Skip to content

คำอธิบายการเชื่อมต่อฝั่งเซิร์ฟเวอร์

หากเกมของคุณมีเซิร์ฟเวอร์ของตัวเอง และข้อมูลผู้เล่นเกมถูกจัดเก็บและบริหารโดยเซิร์ฟเวอร์ของคุณ คุณจะต้องทำการเชื่อมต่อฝั่งเซิร์ฟเวอร์ดังนี้:

โมดูลคำอธิบายข้อแนะนำในการเชื่อมต่อ
ระบบผู้ใช้คุณต้องเชื่อมโยงระบบผู้ใช้ของแพลตฟอร์ม Jogos กับผู้ใช้บนเซิร์ฟเวอร์ของคุณ จำเป็นต้องเชื่อมต่อระบบผู้ใช้จากฝั่งเซิร์ฟเวอร์☑️จำเป็นต้องเชื่อมต่อ
ระบบซื้อภายในเกมหากเกมของคุณมีรายการซื้อภายในเกม จำเป็นต้องเชื่อมต่อระบบซื้อภายในเกมของ Jogos เพื่อรับรองความปลอดภัยของการชำระเงินและข้อมูลของผู้ใช้☑️เกมที่มีระบบซื้อภายในจำเป็นต้องเชื่อมต่อ

การเชื่อมต่อระบบผู้ใช้

1 ดึงโทเค็นผู้ใช้จากฝั่งลูกค้า

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>
java
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 ตัวอย่างโค้ดดังนี้:

    javascript
    private 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);
        }
    }

การเชื่อมต่อฝั่งลูกค้า:

การเริ่มเชื่อมต่อฝั่งเซิร์ฟเวอร์:

1. เมื่อผู้ใช้ซื้อสินค้าในเกมสำเร็จ แพลตฟอร์ม Jogos จะส่งการแจ้งชำระเงินสำเร็จ

  • แพลตฟอร์ม Jogos จะส่ง Callback ไปยัง Callback URL ที่คุณตั้งค่า

  • Header ของ Request:

    ชื่อฟิลด์ประเภทว่างได้หรือไม่คำอธิบาย
    Authorizationstringไม่Header สำหรับตรวจสอบ sha1(body+SecretKey) โดย SecretKey คือ Shared Key ของเกมที่ตั้งค่าใน Game Parameters
  • Request Body มีฟิลด์ดังนี้:

    ชื่อฟิลด์ประเภทว่างได้หรือไม่คำอธิบาย
    paytypeStringไม่ประเภทการแจ้ง pay: แจ้งชำระเงินสำเร็จ; refund: แจ้งคืนเงินสำเร็จ
    gameIdintไม่ID เกม
    orderIdStringไม่หมายเลขคำสั่งซื้อ
    arrivedStatusintไม่สถานะการจัดส่ง: 0 ยังไม่จัดส่ง, 1 จัดส่งแล้ว
    productIdStringไม่ID สินค้า
    createTimelongไม่เวลาสร้างคำสั่งซื้อ (Unix timestamp)
    payTimelongไม่เวลาชำระเงินสำเร็จ (Unix timestamp)
    userIdintไม่ID ผู้ใช้
    refundStatusintใช่สถานะคืนเงิน ถ้ามีการคืนเงินไม่ว่าง 0: กำลังคืนเงิน, 1: คืนเงินสำเร็จ, 2: คืนเงินล้มเหลว, 3: ปฏิเสธคืนเงิน
    refundTimelongใช่เวลาคืนเงิน (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 ข้อผิดพลาดระบบ
    *คำเตือนทั่วไป*
    • หากเซิร์ฟเวอร์เกมไม่ตอบกลับ 200 บริการชำระเงินจะลองส่งซ้ำหลายครั้งใน 3 ชั่วโมง สูงสุด 5 ครั้ง
    • เมื่อได้รับแจ้งจากบริการชำระเงิน ต้องตรวจสอบพารามิเตอร์โดยวิธีดังนี้:
    javascript
    public boolean verifySignature(String body, String authorizationHeader) throws Exception
    {
        if (authorizationHeader == null || !authorizationHeader.startsWith("Signature "))
        {
            throw new Exception("\"Authorization\" header ไม่พบหรือไม่ถูกต้องในคำขอ Jogos webhook.");
        }
    
        String clientSignature = authorizationHeader.substring(10); // ข้าม "Signature "
        String serverSignature = sha1(body + your_key);//your_key คือ Shared Key ของเกมที่ตั้งค่าใน Game Parameters
    
        return clientSignature.equals(serverSignature);
    }

2. หลังจากเซิร์ฟเวอร์เกมจัดส่งสินค้าแล้ว ต้องเรียก “อัปเดตสถานะการจัดส่ง” เพื่อเปลี่ยนคำสั่งซื้อเป็นจัดส่งแล้ว

  • URL: [POST] https://api.jogos.com/api/gamepay/webhook/arrivedorder

  • Header:

    ชื่อฟิลด์ประเภทว่างได้หรือไม่คำอธิบาย
    Authorizationstringไม่Header สำหรับตรวจสอบ sha1(body+SecretKey) โดย SecretKey คือ Shared Key ของเกม
  • Request Body:

    ชื่อฟิลด์ประเภทว่างได้หรือไม่คำอธิบาย
    gameIdintไม่ID เกม
    orderIdintไม่หมาย

เลขคำสั่งซื้อ |

ตัวอย่าง Request Body:

javascript
{
    "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:

    ชื่อฟิลด์ประเภทว่างได้หรือไม่คำอธิบาย
    Authorizationstringไม่Header สำหรับตรวจสอบ sha1(body+SecretKey) โดย SecretKey คือ Shared Key ของเกม
  • Request Body:

    ชื่อฟิลด์ประเภทว่างได้หรือไม่คำอธิบาย
    gameIdintไม่ID เกม
    pageNointไม่หมายเลขหน้า
    pageSizeintไม่จำนวนรายการต่อหน้า

    ตัวอย่าง Request Body:

    javascript
    {
        "gameId"9943,
        "pageNo":1,
        "pageSize":20
    }
  • Response JSON (code:200 หมายถึงสำเร็จ)

    ชื่อฟิลด์ประเภทว่างได้หรือไม่คำอธิบาย
    codeStringไม่รหัสสถานะ เช่น: SUCCESS
    messageStringไม่ข้อความแจ้ง เช่น: ออกจากระบบสำเร็จ
    totalIntegerไม่จำนวนทั้งหมด
    pageSizeIntegerไม่จำนวนรายการต่อหน้า
    currentPageIntegerไม่หน้าปัจจุบัน
    totalPageIntegerไม่จำนวนหน้าทั้งหมด
    orderIdStringไม่หมายเลขคำสั่งซื้อ
    arrivedStatusIntegerไม่สถานะจัดส่ง 0: ยังไม่จัดส่ง, 1: จัดส่งแล้ว
    payTimelongไม่เวลาชำระเงิน (Unix timestamp)
    productIdStringไม่ID สินค้า
    userIdIntegerไม่ID ผู้ใช้
    refundStatusIntegerใช่สถานะคืนเงิน ถ้ามีการคืนเงินไม่ว่าง 0: กำลังคืนเงิน, 1: คืนเงินสำเร็จ, 2: คืนเงินล้มเหลว, 3: ปฏิเสธคืนเงิน
    refundTimelongใช่เวลาคืนเงิน (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 “แจ้งชำระเงินสำเร็จ”
  • แนะนำให้นักพัฒนาตรวจสอบข้อมูลคำสั่งคืนเงิน และลดหรือจำกัดรางวัล/ไอเท็มที่ผู้เล่นได้รับจากการเติมเงินก่อนหน้านี้ตามความเหมาะสม