Skip to content

Hướng dẫn tích hợp giao diện máy chủ

Nếu trò chơi của bạn có máy chủ riêng và dữ liệu người chơi được lưu trữ, quản lý bởi máy chủ của bạn, bạn cần tích hợp máy chủ như sau:

ModuleMô tảKhuyến nghị tích hợp
Hệ thống người dùngBạn cần liên kết hệ thống người dùng của Jogos với người dùng trên máy chủ của bạn, cần tích hợp từ phía máy chủ☑️ Cần thiết
Hệ thống mua hàng trong ứng dụngNếu trò chơi của bạn có các mục mua trong ứng dụng, cần tích hợp hệ thống mua hàng của Jogos để đảm bảo thanh toán và bảo mật dữ liệu☑️ Cần thiết cho trò chơi có mua hàng

Tích hợp hệ thống người dùng

1. Lấy token người dùng từ phía client

2. Tổng quan

Sau khi nhận token người dùng và publickey từ Jogos, gửi về máy chủ trò chơi để xác thực và lấy thông tin người dùng. Lưu ý: mỗi lần xác thực token đều phải lấy publickey vì nó có thể thay đổi bất cứ lúc nào.

3. Logic xác thực

  • Nhà phát triển trò chơi lấy token người dùng từ Jogos và gửi về máy chủ của mình.
  • Máy chủ lấy publickey (thông qua https://www.jogos.com/publicKey.json).
  • Chuyển chuỗi publickey mã hóa Base64 thành khóa công khai RSA.
  • Sử dụng khóa RSA để giải mã JWT token. Token đã giải mã có thể kiểm tra trên jwt.io.
  • Sau khi giải mã, lấy thông tin người dùng và liên kết với người dùng trên máy chủ trò chơi.
  • Nếu giải mã thất bại, trả về thông tin người dùng rỗng.

4. Ví dụ code

  Thêm thư viện jwt vào project:
  <!--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) {
		/**
		 * Lấy publickey và token người dùng từ Jogos SDK và gửi về server, ví dụ publickey:
		  
		  -----BEGIN PUBLIC KEY-----
		  MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhyUrr/W8bSuC+HK8Rk++BDOGDefGMEBa9jekVwVE3oeqi7QbVzZPAuqb1K3GPJjDBfi44IWzb3w8Xa0P1ZeO2Cnbg1LltanlzFv/EcKseCIkOd8Qo78ARPfmlf4WP6MznYGKwNVGFh/s5Y6ar8QgWX1ttkcwYzHu/gUroO+nPOZkU6bfxHjRrJxk3lSQBZWfTSFd2JFwntq3h45UHymjQZiMtW47G2C4VTtTTt1Iz0VQ9rdtWie/+REqQYoFUm04Yns9jyG3TZzio9vsRrxESLQbBIRxto77cQNtEe9j/2EXNwQabRkiS6zo6i2TIN4O6uthWBVud5WXsBWdiyIOHQIDAQAB
		  -----END PUBLIC KEY-----

		và token người dùng:
		  eyJhbGciOiJSUzI1NiJ9.eyJqdGkiOiIxOTY3NDIzNTM3NjE1NTgxMTg0Iiwic3ViIjoie1wicHJvZmlsZVBpY3R1cmVVcmxcIjpcImh0dHBzOi8vaW1hZ2Uuam9nb3NwcmUuY29tLzBlZTkyZjViZTRhMzQ4NzBhYjY0YWU1N2FjN2I1YmE0LmpwZ1wiLFwiZ2FtZUlkXCI6XCIxMjIwXCIsXCJ1c2VySWRcIjpcIjI1N1wiLFwidXNlcm5hbWVcIjpcIlBsYXllcjI1N1wifSIsImlzcyI6ImNvbTpqb2dvczpzZGsiLCJpYXQiOjE3NTc5MDUyOTgsImV4cCI6MTc2MTUwNTI5OH0.MKBab0XC1o5AYGosbq1l5m9xCLN1-4xfcr7_dHn-_C8Eh2moTuq9TbQutIajB3dViFW0e1KyIg7UhxNk00rdhT2b5UrvO23tyFsgg9FYyAyCZABURxHyI0lTW9V8YA9k4faycK_gCUMXH_IubseDMz1P7cYmPpo8WxJXZq3R-mL8OhhuKCn8DlpP5BVsd0_gYSTvDUD0gjdINLUNTVrZVkETcDVWW8OXQVzJxdTH0VVlDs4cGvIyko8TJ6g1Bvz4VWj4qy1XoQFfRBH8sgBl3oGJtUodhq3b4bOx4Cr_o-2tK54CetDdmmCPbtcbmmDcBqK5EQ51A2Kws-1cYtsFxg
		     
		  Lưu ý: mỗi lần xác thực token đều phải lấy publickey vì nó có thể thay đổi
		 */
		
		String publickey ="publickey của bạn";
		String token = "token người dùng của bạn";
		
		System.out.println("publickey:["+publickey+"]");
		System.out.println("token:["+token+"]");
		Map<String, Object> userMap = verifyToken(token,publickey);
		// Nếu token giải mã thành công, userMap không rỗng
        if (userMap != null) {
        	System.out.println("Xác thực thành công, thông tin người dùng:");
        	System.out.println(JSONObject.toJSONString(userMap));
        }else {
        	System.out.println("Xác thực JWT thất bại");
        }
	}
	
	
	/**
	 * Xác thực token
	 * @param token
	 * @return
	 */
	public static Map<String, Object> verifyToken(String token,String publickey){  
		
		// Thử giải mã token, nếu thất bại trả về null
		Map<String, Object> map = null;
        try {
        	map = parseToken(token,publickey);
        } catch (Exception e) {
            
        }
        return map;
         
    }
	
	/**
	 * Giải mã token
	 * @param token
	 * @return
	 */
	public static Map<String,Object> parseToken(String token,String publickey){
		// Loại bỏ phần đầu và cuối
		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;
    }
	
	 /**
     * Chuyển chuỗi Base64 thành RSA public key
     * @param key Chuỗi public key mã hóa Base64
     * @return Khóa RSA công khai
     * <p>
     * Lưu ý: Phương thức này sử dụng Base64 decoder để giải mã thành mảng byte, sau đó sử dụng KeyFactory và X509EncodedKeySpec
     * để chuyển byte array thành đối tượng PublicKey. Nếu xảy ra lỗi sẽ ném 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;
    }
}

Tích hợp hệ thống mua hàng trong ứng dụng

Trước khi bắt đầu:

  • Xác nhận bạn đang dùng www.jogospre.com (môi trường test) hoặc www.jogos.com (môi trường production); các URL POST dưới đây phải thay bằng địa chỉ tương ứng (ví dụ API test về hàng: https://api.jogospre.com/api/gamepay/webhook/arrivedorder)

  • Đảm bảo bạn đã tạo trò chơi trên Developer Platform và đã tạo Shared Key trong “Game Parameters”.

  • Đảm bảo trò chơi của bạn bật chế độ “Mua trong ứng dụng” và nhập địa chỉ callback của server.

  • Giao tiếp sử dụng SHA-1, ví dụ:

    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 algorithm is not available", e);
        }
    }

Tích hợp phía client:

Tích hợp phía server:

1. Khi người dùng mua thành công, Jogos gửi thông báo thanh toán thành công

  • Jogos gửi callback về địa chỉ callback đã cấu hình.

  • Header:

    Tên trườngLoạiCó thể trốngMô tả
    AuthorizationstringKhôngHeader xác thực, sha1(body+shared_key), shared_key là key của trò chơi
  • Body chứa các trường:

    Tên trườngLoạiCó thể trốngMô tả
    paytypeStringKhôngLoại thông báo: pay = thanh toán thành công; refund = hoàn tiền thành công
    gameIdintKhôngID trò chơi
    orderIdStringKhôngMã đơn hàng
    arrivedStatusintKhôngTrạng thái nhận hàng: 0 = chưa nhận, 1 = đã nhận
    productIdStringKhôngID sản phẩm
    createTimelongKhôngThời gian tạo đơn (Unix timestamp)
    payTimelongKhôngThời gian thanh toán thành công (Unix timestamp)
    userIdintKhôngID người dùng
    refundStatusintTrạng thái hoàn tiền: 0=đang hoàn,1=thành công,2=thất bại,3=bị từ chối
    refundTimelongThời gian hoàn tiền (Unix timestamp)

    Ví dụ body:

    javascript
    {
        "gameId"9968,
        "orderId""zXhKYRy3genKKh9TgndBpu1gp5tBpA",
        "arrivedStatus"0,
        "productId""1002",
        "createTime"1755657098,
        "payTime"1755657115,
        "userId"278,
        "refundStatus"null,
        "refundTime"null
    }
    • Response JSON:
    json
    {
        "code""200",
        "msg""Success"
    }
  • Mã lỗi:

    • 305 Tham số bắt buộc trống
    • 40001 Không tìm thấy trò chơi
    • 41007 Xác thực thất bại
    • 500 Lỗi hệ thống
    *Lưu ý chung*
    • Nếu server trò chơi không trả về 200, hệ thống thanh toán sẽ thử lại nhiều lần trong 3 giờ, tối đa 5 lần.
    • Khi nhận thông báo, cần xác thực tham số bằng cách:
    javascript
    public boolean verifySignature(String body, String authorizationHeader) throws Exception
    {
        if (authorizationHeader == null || !authorizationHeader.startsWith("Signature "))
        {
            throw new Exception("\"Authorization\" header not found or invalid in Jogos webhook request.");
        }
    
        String clientSignature = authorizationHeader.substring(10); // Bỏ "Signature "
        String serverSignature = sha1(body + your_key);//your_key là shared key của trò chơi
    
        return clientSignature.equals(serverSignature);
    }

2. Khi server trò chơi giao hàng xong, gọi API “Cập nhật trạng thái nhận hàng”

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

  • Header:

    Tên trườngLoạiCó thể trốngMô tả
    AuthorizationstringKhôngHeader xác thực, sha1(body+shared_key)
  • Body:

    Tên trườngLoạiCó thể trốngMô tả
    gameIdintKhôngID trò chơi
    orderIdintKhôngMã đơn hàng

    Ví dụ body:

    javascript
    {
        "gameId"9943,
        "orderId":"gHpGwweTlucXwcM6yIeSMcgKkhFmoO"
    }
  • Response JSON:

    json
    {
        "code""200",
        "msg""Success"
    }
  • Mã lỗi:

    • 305 Tham số bắt buộc trống
    • 40001 Không tìm thấy trò chơi
    • 41007 Xác thực thất bại
    • 500 Lỗi hệ thống

3. Server trò chơi truy vấn danh sách đơn hàng và đồng bộ trạng thái nhận hàng

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

  • Header:

    Tên trườngLoạiCó thể trốngMô tả
    AuthorizationstringKhôngHeader xác thực, sha1(body+shared_key)
  • Body:

    Tên trườngLoạiCó thể trốngMô tả
    gameIdintKhôngID trò chơi
    pageNointKhôngSố trang
    pageSizeintKhôngSố mục mỗi trang

    Ví dụ body:

    javascript
    {
        "gameId"9943,
        "pageNo":1,
        "pageSize":20
    }
  • Response JSON (code:200 = thành công):

    | Tên trường | Loại | Có thể trống | Mô tả |

| ------------- | ------- | ------------ | ----- | | code | String | Không | Mã trạng thái (ví dụ: SUCCESS) | | message | String | Không | Thông báo (ví dụ: Đăng xuất thành công) | | total | Integer | Không | Tổng số | | pageSize | Integer | Không | Số mục mỗi trang | | currentPage | Integer | Không | Trang hiện tại | | totalPage | Integer | Không | Tổng số trang | | orderId | String | Không | Mã đơn hàng | | arrivedStatus | Integer | Không | Trạng thái nhận hàng: 0 chưa nhận, 1 đã nhận | | payTime | long | Không | Thời gian thanh toán (Unix timestamp) | | productId | String | Không | ID sản phẩm | | userId | Integer | Không | ID người dùng | | refundStatus | Integer | Có | Trạng thái hoàn tiền: 0=đang hoàn,1=thành công,2=thất bại,3=bị từ chối | | refundTime | long | Có | Thời gian hoàn tiền (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
      }
    ]
  }
}
  • Mã lỗi:

    • 305 Tham số bắt buộc trống
    • 40001 Không tìm thấy trò chơi
    • 41007 Xác thực thất bại
    • 500 Lỗi hệ thống

4. Khi người dùng hoàn tiền, gửi thông báo về server trò chơi

  • Jogos gửi callback về địa chỉ callback cho đơn hàng hoàn tiền thành công.
  • Body giống với thông báo “Thanh toán thành công”.
  • Khuyến nghị nhà phát triển căn cứ thông tin đơn hoàn tiền để giảm hoặc hạn chế các vật phẩm đã được người chơi nhận từ lần nạp trước.