คำอธิบายการเชื่อมต่อฝั่งเซิร์ฟเวอร์
หากเกมของคุณมีเซิร์ฟเวอร์ของตัวเอง และข้อมูลผู้เล่นเกมถูกจัดเก็บและบริหารโดยเซิร์ฟเวอร์ของคุณ คุณจะต้องทำการเชื่อมต่อฝั่งเซิร์ฟเวอร์ดังนี้:
โมดูล | คำอธิบาย | ข้อแนะนำในการเชื่อมต่อ |
---|---|---|
ระบบผู้ใช้ | คุณต้องเชื่อมโยงระบบผู้ใช้ของแพลตฟอร์ม Jogos กับผู้ใช้บนเซิร์ฟเวอร์ของคุณ จำเป็นต้องเชื่อมต่อระบบผู้ใช้จากฝั่งเซิร์ฟเวอร์ | ☑️จำเป็นต้องเชื่อมต่อ |
ระบบซื้อภายในเกม | หากเกมของคุณมีรายการซื้อภายในเกม จำเป็นต้องเชื่อมต่อระบบซื้อภายในเกมของ Jogos เพื่อรับรองความปลอดภัยของการชำระเงินและข้อมูลของผู้ใช้ | ☑️เกมที่มีระบบซื้อภายในจำเป็นต้องเชื่อมต่อ |
การเชื่อมต่อระบบผู้ใช้
1 ดึงโทเค็นผู้ใช้จากฝั่งลูกค้า
- โปรดตรวจสอบเอกสาร
Client SDK การเชื่อมต่อผู้ใช้
ก่อน
2 ภาพรวม
หลังจากได้รับ token และ publickey จาก Jogos แล้ว ส่งกลับไปยังเซิร์ฟเวอร์เกมเพื่อทำการตรวจสอบและดึงข้อมูลผู้ใช้ โปรดทราบว่า ทุกครั้งที่ตรวจสอบโทเค็น ต้องดึง publickey ใหม่ เพราะ publickey อาจมีการเปลี่ยนแปลงได้ตลอดเวลา
3 ลอจิกการตรวจสอบ
- นักพัฒนาเกมจะรับ token จาก Jogos แล้วส่งกลับไปยังเซิร์ฟเวอร์ของตน
- เซิร์ฟเวอร์จะดึง publickey (จาก https://www.jogos.com/publicKey.json)
- ใช้ publickey ในรูปแบบ Base64 เพื่อสร้าง RSA public key
- ใช้ RSA public key เพื่อถอดรหัส JWT token สามารถตรวจสอบผลลัพธ์ของ token ที่ถอดรหัสได้ที่ jwt.io
- หลังจากถอดรหัสแล้ว ดึงข้อมูลผู้ใช้และเชื่อมโยงกับผู้ใช้บนเซิร์ฟเวอร์ของเกม
- หากการถอดรหัสล้มเหลว จะส่งคืนข้อมูลผู้ใช้เป็นค่าว่าง
4 ตัวอย่างโค้ด
นำเข้า jwt ในโปรเจกต์:
<!--JWT-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.2</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.2</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.2</version>
</dependency>
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Map;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import com.alibaba.fastjson.JSONObject;
public class JwtUtils {
public static void main(String[] args) {
/**
* ดึง publickey และ token ของผู้ใช้จาก Jogos SDK แล้วส่งไปยังเซิร์ฟเวอร์ เช่น publickey ดังนี้:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhyUrr/W8bSuC+HK8Rk++BDOGDefGMEBa9jekVwVE3oeqi7QbVzZPAuqb1K3GPJjDBfi44IWzb3w8Xa0P1ZeO2Cnbg1LltanlzFv/EcKseCIkOd8Qo78ARPfmlf4WP6MznYGKwNVGFh/s5Y6ar8QgWX1ttkcwYzHu/gUroO+nPOZkU6bfxHjRrJxk3lSQBZWfTSFd2JFwntq3h45UHymjQZiMtW47G2C4VTtTTt1Iz0VQ9rdtWie/+REqQYoFUm04Yns9jyG3TZzio9vsRrxESLQbBIRxto77cQNtEe9j/2EXNwQabRkiS6zo6i2TIN4O6uthWBVud5WXsBWdiyIOHQIDAQAB
-----END PUBLIC KEY-----
และ token ของผู้ใช้:
eyJhbGciOiJSUzI1NiJ9.eyJqdGkiOiIxOTY3NDIzNTM3NjE1NTgxMTg0Iiwic3ViIjoie1wicHJvZmlsZVBpY3R1cmVVcmxcIjpcImh0dHBzOi8vaW1hZ2Uuam9nb3NwcmUuY29tLzBlZTkyZjViZTRhMzQ4NzBhYjY0YWU1N2FjN2I1YmE0LmpwZ1wiLFwiZ2FtZUlkXCI6XCIxMjIwXCIsXCJ1c2VySWRcIjpcIjI1N1wiLFwidXNlcm5hbWVcIjpcIlBsYXllcjI1N1wifSIsImlzcyI6ImNvbTpqb2dvczpzZGsiLCJpYXQiOjE3NTc5MDUyOTgsImV4cCI6MTc2MTUwNTI5OH0.MKBab0XC1o5AYGosbq1l5m9xCLN1-4xfcr7_dHn-_C8Eh2moTuq9TbQutIajB3dViFW0e1KyIg7UhxNk00rdhT2b5UrvO23tyFsgg9FYyAyCZABURxHyI0lTW9V8YA9k4faycK_gCUMXH_IubseDMz1P7cYmPpo8WxJXZq3R-mL8OhhuKCn8DlpP5BVsd0_gYSTvDUD0gjdINLUNTVrZVkETcDVWW8OXQVzJxdTH0VVlDs4cGvIyko8TJ6g1Bvz4VWj4qy1XoQFfRBH8sgBl3oGJtUodhq3b4bOx4Cr_o-2tK54CetDdmmCPbtcbmmDcBqK5EQ51A2Kws-1cYtsFxg
หมายเหตุ: ทุกครั้งที่ตรวจสอบ token ต้องดึง publickey ใหม่ เพราะ publickey อาจเปลี่ยนแปลงได้
*/
String publickey ="publickey ของคุณ";
String token = "token ของผู้ใช้ของคุณ";
System.out.println("publickey:["+publickey+"]");
System.out.println("token:["+token+"]");
Map<String, Object> userMap = verifyToken(token,publickey);
// หาก Token วิเคราะห์สำเร็จ, userMap จะไม่ว่าง
if (userMap != null) {
System.out.println("ตรวจสอบผ่าน ข้อมูลผู้ใช้คือ:");
System.out.println(JSONObject.toJSONString(userMap));
}else {
System.out.println("การตรวจสอบ jwt ไม่ผ่าน");
}
}
/**
* ตรวจสอบ token
* @param token
* @return
*/
public static Map<String, Object> verifyToken(String token,String publickey){
// พยายามถอดรหัส Token หากล้มเหลวจะคืนค่า null
Map<String, Object> map = null;
try {
map = parseToken(token,publickey);
} catch (Exception e) {
}
return map;
}
/**
* ถอดรหัส token
* @param token
* @return
*/
public static Map<String,Object> parseToken(String token,String publickey){
// ลบส่วนหัวและส่วนท้าย
if(publickey.startsWith("-----BEGIN PUBLIC KEY-----")) {
publickey = publickey.substring(27,publickey.length()-25);
}
Claims claims = (Claims) Jwts.parser()
.setSigningKey(getPublicKey(publickey))
.parse(token)
.getBody();
String parseToken = claims.getSubject();
if (parseToken != null) {
Map<String, Object> map = JSONObject.parseObject(parseToken, Map.class);
return map;
}
return null;
}
/**
* แปลง Base64 string เป็น RSA public key
* @param key Base64 string ของ public key
* @return RSA public key ที่ได้
* <p>
* หมายเหตุ: วิธีนี้ใช้ Base64 decoder แปลง string เป็น byte array จากนั้นใช้ KeyFactory และ X509EncodedKeySpec
* แปลง byte array เป็น PublicKey หากเกิดข้อผิดพลาดจะโยน RuntimeException
*/
private static PublicKey getPublicKey(String key) {
byte[] decode = Base64.getDecoder().decode(key);
PublicKey publicKey;
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decode);
publicKey = keyFactory.generatePublic(keySpec);
} catch (Exception e) {
throw new RuntimeException(e);
}
return publicKey;
}
}
การเชื่อมต่อระบบซื้อภายในเกม
ก่อนเริ่มเชื่อมต่อ โปรดยืนยัน:
คุณกำลังใช้
www.jogospre.com
(สภาพแวดล้อมทดสอบ) หรือwww.jogos.com
(สภาพแวดล้อมจริง); URL ของ POST request ที่ระบุด้านล่าง ต้องปรับเป็น URL ที่ตรงกัน (ตัวอย่าง API สำหรับสภาพแวดล้อมทดสอบ: https://api.jogospre.com/api/gamepay/webhook/arrivedorder)ตรวจสอบว่าคุณได้สร้างเกมแอปพลิเคชันใน Developer Platform และใน “Game Parameters” ได้สร้าง Shared Key ของเกมแล้ว
ตรวจสอบว่าได้เลือกโหมด “In-App Purchase” และใส่ Callback URL ของเซิร์ฟเวอร์ของคุณเรียบร้อยแล้ว
การสื่อสารใช้การเข้ารหัส SHA-1 ตัวอย่างโค้ดดังนี้:
javascriptprivate String sha1(String input) { try { MessageDigest md = MessageDigest.getInstance("SHA-1"); byte[] hashInBytes = md.digest(input.getBytes(StandardCharsets.UTF_8)); StringBuilder sb = new StringBuilder(); for (byte b : hashInBytes) { sb.append(String.format("%02x", b)); } return sb.toString(); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("ไม่พบอัลกอริทึม SHA-1", e); } }
การเชื่อมต่อฝั่งลูกค้า:
- ลูกค้าสร้างคำสั่งซื้อและส่งคำสั่งซื้อ
- โปรดตรวจสอบเอกสาร
Client SDK การเชื่อมต่อระบบซื้อภายใน
การเริ่มเชื่อมต่อฝั่งเซิร์ฟเวอร์:
1. เมื่อผู้ใช้ซื้อสินค้าในเกมสำเร็จ แพลตฟอร์ม Jogos จะส่งการแจ้งชำระเงินสำเร็จ
แพลตฟอร์ม Jogos จะส่ง Callback ไปยัง Callback URL ที่คุณตั้งค่า
Header ของ Request:
ชื่อฟิลด์ ประเภท ว่างได้หรือไม่ คำอธิบาย Authorization string ไม่ Header สำหรับตรวจสอบ sha1(body+SecretKey) โดย SecretKey คือ Shared Key ของเกมที่ตั้งค่าใน Game Parameters Request Body มีฟิลด์ดังนี้:
ชื่อฟิลด์ ประเภท ว่างได้หรือไม่ คำอธิบาย paytype String ไม่ ประเภทการแจ้ง pay: แจ้งชำระเงินสำเร็จ; refund: แจ้งคืนเงินสำเร็จ gameId int ไม่ ID เกม orderId String ไม่ หมายเลขคำสั่งซื้อ arrivedStatus int ไม่ สถานะการจัดส่ง: 0 ยังไม่จัดส่ง, 1 จัดส่งแล้ว productId String ไม่ ID สินค้า createTime long ไม่ เวลาสร้างคำสั่งซื้อ (Unix timestamp) payTime long ไม่ เวลาชำระเงินสำเร็จ (Unix timestamp) userId int ไม่ ID ผู้ใช้ refundStatus int ใช่ สถานะคืนเงิน ถ้ามีการคืนเงินไม่ว่าง 0: กำลังคืนเงิน, 1: คืนเงินสำเร็จ, 2: คืนเงินล้มเหลว, 3: ปฏิเสธคืนเงิน refundTime long ใช่ เวลาคืนเงิน (Unix timestamp) ถ้ามีการคืนเงินไม่ว่าง ตัวอย่าง Request Body:
javascript{ "gameId": 9968, "orderId": "zXhKYRy3genKKh9TgndBpu1gp5tBpA", "arrivedStatus": 0, "productId": "1002", "createTime": 1755657098, "payTime": 1755657115, "userId": 278, "refundStatus": null, "refundTime": null }
- Response JSON (code:200 หมายถึงสำเร็จ)
json{ "code": "200", "msg": "Success" }
รหัสผิดพลาด:
305
พารามิเตอร์จำเป็นว่าง40001
ไม่พบเกมที่ตรงกัน41007
การตรวจสอบล้มเหลว500
ข้อผิดพลาดระบบ
2. หลังจากเซิร์ฟเวอร์เกมจัดส่งสินค้าแล้ว ต้องเรียก “อัปเดตสถานะการจัดส่ง” เพื่อเปลี่ยนคำสั่งซื้อเป็นจัดส่งแล้ว
URL: [POST] https://api.jogos.com/api/gamepay/webhook/arrivedorder
Header:
ชื่อฟิลด์ ประเภท ว่างได้หรือไม่ คำอธิบาย Authorization string ไม่ Header สำหรับตรวจสอบ sha1(body+SecretKey) โดย SecretKey คือ Shared Key ของเกม Request Body:
ชื่อฟิลด์ ประเภท ว่างได้หรือไม่ คำอธิบาย gameId int ไม่ ID เกม orderId int ไม่ หมาย
เลขคำสั่งซื้อ |
ตัวอย่าง Request Body:
{
"gameId": 9943,
"orderId":"gHpGwweTlucXwcM6yIeSMcgKkhFmoO"
}
Response JSON (code:200 หมายถึงสำเร็จ)
json{ "code": "200", "msg": "Success" }
รหัสผิดพลาด:
305
พารามิเตอร์จำเป็นว่าง40001
ไม่พบเกมที่ตรงกัน41007
การตรวจสอบล้มเหลว500
ข้อผิดพลาดระบบ
3. เซิร์ฟเวอร์เกมดึงรายการคำสั่งซื้อ เพื่อตรวจสอบสถานะคำสั่งซื้อและการจัดส่ง
URL: [POST] https://api.jogos.com/api/gamepay/getOrders
Header:
ชื่อฟิลด์ ประเภท ว่างได้หรือไม่ คำอธิบาย Authorization string ไม่ Header สำหรับตรวจสอบ sha1(body+SecretKey) โดย SecretKey คือ Shared Key ของเกม Request Body:
ชื่อฟิลด์ ประเภท ว่างได้หรือไม่ คำอธิบาย gameId int ไม่ ID เกม pageNo int ไม่ หมายเลขหน้า pageSize int ไม่ จำนวนรายการต่อหน้า ตัวอย่าง Request Body:
javascript{ "gameId": 9943, "pageNo":1, "pageSize":20 }
Response JSON (code:200 หมายถึงสำเร็จ)
ชื่อฟิลด์ ประเภท ว่างได้หรือไม่ คำอธิบาย code String ไม่ รหัสสถานะ เช่น: SUCCESS message String ไม่ ข้อความแจ้ง เช่น: ออกจากระบบสำเร็จ total Integer ไม่ จำนวนทั้งหมด pageSize Integer ไม่ จำนวนรายการต่อหน้า currentPage Integer ไม่ หน้าปัจจุบัน totalPage Integer ไม่ จำนวนหน้าทั้งหมด orderId String ไม่ หมายเลขคำสั่งซื้อ arrivedStatus Integer ไม่ สถานะจัดส่ง 0: ยังไม่จัดส่ง, 1: จัดส่งแล้ว payTime long ไม่ เวลาชำระเงิน (Unix timestamp) productId String ไม่ ID สินค้า userId Integer ไม่ ID ผู้ใช้ refundStatus Integer ใช่ สถานะคืนเงิน ถ้ามีการคืนเงินไม่ว่าง 0: กำลังคืนเงิน, 1: คืนเงินสำเร็จ, 2: คืนเงินล้มเหลว, 3: ปฏิเสธคืนเงิน refundTime long ใช่ เวลาคืนเงิน (Unix timestamp) ถ้ามีการคืนเงินไม่ว่าง json{ "code": "200", "msg": "Success", "page": { "total": 31, "totalPage": 7, "currentPage": 1, "pageSize": 5, "content": [ { "orderId": "gHpGwweTlucXwcM6yIeSMcgKkhFmoO", "arrivedStatus": 1, "payTime": 1112211221, "productId": "game_pro1", "userId": 278, "refundStatus": null, "refundTime": null } ] } }
รหัสผิดพลาด:
305
พารามิเตอร์จำเป็นว่าง40001
ไม่พบเกมที่ตรงกัน41007
การตรวจสอบล้มเหลว500
ข้อผิดพลาดระบบ
4. เมื่อผู้ใช้ขอคืนเงิน ส่งแจ้งการคืนเงินสำเร็จไปยังเซิร์ฟเวอร์เกม
- แพลตฟอร์ม Jogos จะส่ง Callback ของคำสั่งคืนเงินสำเร็จไปยัง Callback URL ที่ตั้งค่า
- Request Body เหมือนกับข้อ 1 “แจ้งชำระเงินสำเร็จ”
- แนะนำให้นักพัฒนาตรวจสอบข้อมูลคำสั่งคืนเงิน และลดหรือจำกัดรางวัล/ไอเท็มที่ผู้เล่นได้รับจากการเติมเงินก่อนหน้านี้ตามความเหมาะสม