Skip to content

Description des interfaces d'intégration côté serveur

Si votre jeu possède son propre serveur de jeu et que les données des joueurs sont conservées et gérées par votre serveur, une intégration côté serveur est nécessaire :

ModuleDescriptionSuggestion d'intégration
Système utilisateurVous devez associer le système utilisateur de la plateforme Jogos à vos utilisateurs serveur. Cela nécessite une intégration côté serveur du système utilisateur☑️ Intégration nécessaire
Système d'achats intégrésSi votre jeu comporte des achats intégrés, vous devez intégrer le système d'achats intégrés de Jogos pour garantir la sécurité des paiements et des données des utilisateurs☑️ Intégration nécessaire pour les jeux avec achats intégrés

Intégration du système utilisateur

1 Obtention du token utilisateur via le client

2 Aperçu

Après avoir obtenu le token utilisateur et la clé publique depuis Jogos, renvoyez-les à votre serveur de jeu pour vérification et récupération des informations utilisateur. Attention : récupérez la clé publique à chaque vérification de token, car elle peut changer à tout moment.

3 Logique de vérification

  • Le développeur du jeu obtient le token utilisateur depuis Jogos et le renvoie au serveur du jeu.
  • Le serveur récupère ensuite la clé publique (via https://www.jogos.com/publicKey.json).
  • Convertir la clé publique Base64 en clé publique RSA.
  • Décrypter le token JWT avec la clé RSA. Le décryptage peut être testé sur jwt.io.
  • Après décodage, récupérer les informations utilisateur et les associer à vos utilisateurs serveur.
  • Si le décryptage échoue, retourner des informations utilisateur vides.

4 Exemple de code

  Ajouter JWT dans le projet :
  <!--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) {
		/**
		 * Obtenez la clé publique et le token utilisateur depuis le SDK Jogos et envoyez-les au serveur, par exemple :
		  
		  -----BEGIN PUBLIC KEY-----
		  MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhyUrr/W8bSuC+HK8Rk++BDOGDefGMEBa9jekVwVE3oeqi7QbVzZPAuqb1K3GPJjDBfi44IWzb3w8Xa0P1ZeO2Cnbg1LltanlzFv/EcKseCIkOd8Qo78ARPfmlf4WP6MznYGKwNVGFh/s5Y6ar8QgWX1ttkcwYzHu/gUroO+nPOZkU6bfxHjRrJxk3lSQBZWfTSFd2JFwntq3h45UHymjQZiMtW47G2C4VTtTTt1Iz0VQ9rdtWie/+REqQYoFUm04Yns9jyG3TZzio9vsRrxESLQbBIRxto77cQNtEe9j/2EXNwQabRkiS6zo6i2TIN4O6uthWBVud5WXsBWdiyIOHQIDAQAB
		  -----END PUBLIC KEY-----

		et le token utilisateur :
		  eyJhbGciOiJSUzI1NiJ9.eyJqdGkiOiIxOTY3NDIzNTM3NjE1NTgxMTg0Iiwic3ViIjoie1wicHJvZmlsZVBpY3R1cmVVcmxcIjpcImh0dHBzOi8vaW1hZ2Uuam9nb3NwcmUuY29tLzBlZTkyZjViZTRhMzQ4NzBhYjY0YWU1N2FjN2I1YmE0LmpwZ1wiLFwiZ2FtZUlkXCI6XCIxMjIwXCIsXCJ1c2VySWRcIjpcIjI1N1wiLFwidXNlcm5hbWVcIjpcIlBsYXllcjI1N1wifSIsImlzcyI6ImNvbTpqb2dvczpzZGsiLCJpYXQiOjE3NTc5MDUyOTgsImV4cCI6MTc2MTUwNTI5OH0.MKBab0XC1o5AYGosbq1l5m9xCLN1-4xfcr7_dHn-_C8Eh2moTuq9TbQutIajB3dViFW0e1KyIg7UhxNk00rdhT2b5UrvO23tyFsgg9FYyAyCZABURxHyI0lTW9V8YA9k4faycK_gCUMXH_IubseDMz1P7cYmPpo8WxJXZq3R-mL8OhhuKCn8DlpP5BVsd0_gYSTvDUD0gjdINLUNTVrZVkETcDVWW8OXQVzJxdTH0VVlDs4cGvIyko8TJ6g1Bvz4VWj4qy1XoQFfRBH8sgBl3oGJtUodhq3b4bOx4Cr_o-2tK54CetDdmmCPbtcbmmDcBqK5EQ51A2Kws-1cYtsFxg
		     
		  Remarque : récupérez la clé publique à chaque vérification de token, car elle peut changer.
		 */
		
		String publickey ="Votre clé publique";
		String token = "Votre token utilisateur";
		
		System.out.println("publickey:["+publickey+"]");
		System.out.println("token:["+token+"]");
		Map<String, Object> userMap = verifyToken(token,publickey);
		// Si le token est analysé avec succès, userMap n'est pas vide
        if (userMap != null) {
        	System.out.println("Vérification réussie, informations utilisateur :");
        	System.out.println(JSONObject.toJSONString(userMap));
        }else {
        	System.out.println("Échec de la vérification JWT");
        }
	}
	
	
	/**
	 * Vérification du token
	 * @param token
	 * @return
	 */
	public static Map<String, Object> verifyToken(String token,String publickey){  
		
		// Essayer de parser le token, si échec retourner null
		Map<String, Object> map = null;
        try {
        	map = parseToken(token,publickey);
        } catch (Exception e) {
            
        }
        return map;
         
    }
	
	/**
	 * Décryptage du token
	 * @param token
	 * @return
	 */
	public static Map<String,Object> parseToken(String token,String publickey){
		// Supprimer les en-têtes et pieds 
		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;
    }
	
	 /**
     * Obtenir la clé publique RSA depuis une chaîne Base64
     * @param key Chaîne Base64 de la clé publique
     * @return Clé publique RSA
     * <p>
     * Remarque : cette méthode décode la chaîne en tableau d'octets via Base64, puis utilise KeyFactory et X509EncodedKeySpec
     * pour convertir le tableau d'octets en objet PublicKey. En cas d'exception, une RuntimeException est levée.
     */
    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;
    }
}

Intégration du système d'achats intégrés

Avant de commencer l'intégration, assurez-vous de :

  • Utiliser www.jogospre.com (environnement de test) ou www.jogos.com (environnement de production) ; les URL POST ci-dessous doivent être adaptées en conséquence. (Par exemple, l'API de test : https://api.jogospre.com/api/gamepay/webhook/arrivedorder)

  • Avoir créé l'application de jeu sur la plateforme développeur et généré la clé partagée dans les paramètres du jeu.

  • Avoir activé le mode “Achats intégrés” dans les options de votre jeu et renseigné l'URL de callback serveur.

  • La communication utilise un chiffrement SHA-1, exemple d'utilisation :

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

Intégration côté client :

Intégration côté serveur :

1. Notification de paiement réussi après achat dans le jeu

  • La plateforme Jogos envoie la notification de paiement réussi à l'URL de callback configurée.

  • En-têtes de requête :

    Nom du champTypeObligatoireDescription
    AuthorizationstringOuiEn-tête de validation, sha1(body+clé), où la clé est la clé partagée du jeu
  • Corps de la requête :

    Nom du champTypeObligatoireDescription
    paytypeStringOuiType de notification : pay = paiement réussi ; refund = remboursement réussi
    gameIdintOuiID du jeu
    orderIdStringOuiNuméro de commande
    arrivedStatusintOuiStatut de livraison : 0 non livré, 1 livré
    productIdStringOuiID du produit
    createTimelongOuiDate de création de la commande (timestamp Unix)
    payTimelongOuiDate de paiement réussi (timestamp Unix)
    userIdintOuiID utilisateur
    refundStatusintNonStatut de remboursement : 0 en cours, 1 réussi, 2 échoué, 3 refusé
    refundTimelongNonDate du remboursement (timestamp Unix), si applicable

    Exemple de corps de requête :

    javascript
    {
        "gameId"9968,
        "orderId""zXhKYRy3genKKh9TgndBpu1gp5tBpA",
        "arrivedStatus"0,
        "productId""1002",
        "createTime"1755657098,
        "payTime"1755657115,
        "userId"278,
        "refundStatus"null,
        "refundTime"null
    }
  • Réponse JSON (code 200 = succès) :

    json
    {
        "code""200",
        "msg""Success"
    }
  • Codes d'erreur :

    • 305 Paramètre obligatoire manquant
    • 40001 Jeu non trouvé
    • 41007 Vérification échouée
    • 500 Erreur système
    *Remarque générale*
    • Si le serveur du jeu ne retourne pas 200, le service de paiement réessaiera plusieurs fois sur 3 heures, jusqu'à 5 fois maximum.
    • Lors de la réception de la notification, il faut vérifier les paramètres de cette manière :
    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); // Skip "Signature "
        String serverSignature = sha1(body + your_key);//your_key = clé partagée spécifique du jeu, définie dans les paramètres du jeu
    
        return clientSignature.equals(serverSignature);
    }

2. Mise à jour du statut de livraison côté serveur après traitement de la commande

  • URL de requête : [POST] https://api.jogos.com/api/gamepay/webhook/arrivedorder

  • En-têtes de requête :

    Nom du champTypeObligatoireDescription
    AuthorizationstringOuiEn-tête de validation, sha1(body+clé), où la clé est la clé partagée du jeu
  • Corps de requête :

    Nom du champTypeObligatoireDescription
    gameIdintOuiID du jeu
    orderIdintOuiNuméro de commande

    Exemple :

    javascript
    {
        "gameId"9943,
        "orderId":"gHpGwweTlucXwcM6yIeSMcgKkhFmoO"
    }
  • Réponse JSON (code 200 = succès) :

    json
    {
        "code""200",
        "msg""Success"
    }
  • Codes d'erreur :

    • 305 Paramètre obligatoire manquant
    • 40001 Jeu non trouvé
    • 41007 Vérification échouée
    • 500 Erreur système

3. Consultation des commandes côté serveur et synchronisation du statut de livraison

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

  • En-têtes de requête :

    Nom du champTypeObligatoireDescription
    AuthorizationstringOuiEn-tête de validation, sha1(body+clé), où la clé est la clé partagée du jeu
  • Corps de requête :

    Nom du champTypeObligatoireDescription
    gameIdintOuiID du jeu
    pageNointOuiNuméro de page
    pageSizeintOuiNombre d'éléments par page

    Exemple :

    javascript
    {
        "

gameId": 9943,     "pageNo":1,     "pageSize":20 }


- Réponse JSON (code 200 = succès) :

| Nom du champ     | Type    | Obligatoire | Description |
| ---------------- | ------- | ----------- | ----------- |
| code             | String  | Oui         | Code de statut (ex : SUCCESS) |
| message          | String  | Oui         | Message d'information (ex : Déconnexion réussie) |
| total            | Integer | Oui         | Nombre total |
| pageSize         | Integer | Oui         | Nombre par page |
| currentPage      | Integer | Oui         | Page actuelle |
| totalPage        | Integer | Oui         | Nombre total de pages |
| orderId          | String  | Oui         | Numéro de commande |
| arrivedStatus    | Integer | Oui         | Statut de livraison : 0 non livré, 1 livré |
| payTime          | long    | Oui         | Heure de paiement (timestamp Unix) |
| productId        | String  | Oui         | ID du produit |
| userId           | Integer | Oui         | ID utilisateur |
| refundStatus     | Integer | Non         | Statut remboursement si applicable : 0 en cours, 1 réussi, 2 échoué, 3 refusé |
| refundTime       | long    | Non         | Timestamp remboursement si applicable |

```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
      }
    ]
  }
}
  • Codes d'erreur :

    • 305 Paramètre obligatoire manquant
    • 40001 Jeu non trouvé
    • 41007 Vérification échouée
    • 500 Erreur système

4. Notification de remboursement côté serveur

  • La plateforme Jogos envoie les notifications de remboursement à l'URL de callback configurée.
  • Le corps de la requête est identique à celui de la notification de paiement réussi.
  • Il est recommandé de déduire ou limiter les récompenses obtenues précédemment par l'utilisateur lors d'un remboursement.

Ce document est entièrement prêt à être utilisé en Markdown dans VSCode, avec **tous les conteneurs, en-têtes, code blocks et syntaxes conservés**.  

Si tu veux, je peux aussi préparer une **version PDF ou Word technique** formatée à partir de ce Markdown, pratique pour les équipes de dev. Veux-tu que je fasse ça ?