Описание интерфейсов для интеграции на стороне сервера
Если ваша игра имеет собственный игровой сервер и данные игроков хранятся и управляются вашим сервером, требуется интеграция на стороне сервера:
Модуль | Описание | Рекомендация по интеграции |
---|---|---|
Система пользователей | Вам необходимо связать систему пользователей платформы Jogos с пользователями вашего сервера. Требуется интеграция системы пользователей на стороне сервера. | ☑️ Обязательная интеграция |
Система внутриигровых покупок | Если ваша игра имеет внутриигровые покупки, необходимо интегрировать систему внутриигровых покупок Jogos для обеспечения безопасности платежей и данных пользователей. | ☑️ Обязательна для игр с покупками |
Интеграция системы пользователей
1 Получение токена пользователя на стороне клиента
- Подробности см. в документации:
Интеграция SDK пользователя на стороне клиента
.
2 Обзор
После получения токена пользователя (token) и открытого ключа (publickey) от Jogos, они передаются на игровой сервер для проверки и получения информации о пользователе. Важно: при каждой проверке токена необходимо получать актуальный открытый ключ (publickey), так как он может измениться в любой момент.
3 Логика проверки
- Разработчик игры получает токен пользователя от Jogos и передает его на свой сервер.
- Сервер получает открытый ключ (publickey) (через https://www.jogos.com/publicKey.json).
- Получает RSA публичный ключ из строки publickey, закодированной в Base64.
- Расшифровывает JWT токен с помощью RSA публичного ключа. Декодирование токена можно проверить на 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 и токен пользователя из jogos sdk и передайте их на сервер, как показано ниже для publickey:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhyUrr/W8bSuC+HK8Rk++BDOGDefGMEBa9jekVwVE3oeqi7QbVzZPAuqb1K3GPJjDBfi44IWzb3w8Xa0P1ZeO2Cnbg1LltanlzFv/EcKseCIkOd8Qo78ARPfmlf4WP6MznYGKwNVGFh/s5Y6ar8QgWX1ttkcwYzHu/gUroO+nPOZkU6bfxHjRrJxk3lSQBZWfTSFd2JFwntq3h45UHymjQZiMtW47G2C4VTtTTt1Iz0VQ9rdtWie/+REqQYoFUm04Yns9jyG3TZzio9vsRrxESLQbBIRxto77cQNtEe9j/2EXNwQabRkiS6zo6i2TIN4O6uthWBVud5WXsBWdiyIOHQIDAQAB
-----END PUBLIC KEY-----
и токен пользователя:
eyJhbGciOiJSUzI1NiJ9.eyJqdGkiOiIxOTY3NDIzNTM3NjE1NTgxMTg0Iiwic3ViIjoie1wicHJvZmlsZVBpY3R1cmVVcmxcIjpcImh0dHBzOi8vaW1hZ2Uuam9nb3NwcmUuY29tLzBlZTkyZjViZTRhMzQ4NzBhYjY0YWU1N2FjN2I1YmE0LmpwZ1wiLFwiZ2FtZUlkXCI�XCIxMjIwXCIsXCJ1c2VySWRcIjpcIjI1N1wiLFwidXNlcm5hbWVcIjpcIlBsYXllcjI1N1wifSIsImlzcyI6ImNvbTpqb2dvczpzZGsiLCJpYXQiOjE3NTc5MDUyOTgsImV4cCI6MTc2MTUwNTI5OH0.MKBab0XC1o5AYGosbq1l5m9xCLN1-4xfcr7_dHn-_C8Eh2moTuq9TbQutIajB3dViFW0e1KyIg7UhxNk00rdhT2b5UrvO23tyFsgg9FYyAyCZABURxHyI0lTW9V8YA9k4faycK_gCUMXH_IubseDMz1P7cYmPpo8WxJXZq3R-mL8OhhuKCn8DlpP5BVsd0_gYSTvDUD0gjdINLUNTVrZVkETcDVWW8OXQVzJxdTH0VVlDs4cGvIyko8TJ6g1Bvz4VWj4qy1XoQFfRBH8sgBl3oGJtUodhq3b4bOx4Cr_o-2tK54CetDdmmCPbtcbmmDcBqK5EQ51A2Kws-1cYtsFxg
Примечание: Получайте ключ publickey при каждой проверке токена, так как он может измениться.
*/
String publickey ="Ваш publickey";
String token = "Ваш токен пользователя";
System.out.println("publickey:["+publickey+"]");
System.out.println("token:["+token+"]");
Map<String, Object> userMap = verifyToken(token,publickey);
// Если токен успешно распарсен, userMap не будет пустым
if (userMap != null) {
System.out.println("Проверка пройдена, информация о пользователе:");
System.out.println(JSONObject.toJSONString(userMap));
}else {
System.out.println("JWT проверка не пройдена");
}
}
/**
* Проверить токен
* @param token
* @return
*/
public static Map<String, Object> verifyToken(String token,String publickey){
// Попытаться распарсить токен, в случае неудачи вернуть null
Map<String, Object> map = null;
try {
map = parseToken(token,publickey);
} catch (Exception e) {
}
return map;
}
/**
* Расшифровать токен
* @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;
}
/**
* Получить RSA публичный ключ из строки, закодированной в Base64
* @param key Строка публичного ключа в кодировке Base64
* @return преобразованный RSA публичный ключ
* <p>
* Примечание: Этот метод использует декодер Base64 для декодирования входной строки в массив байтов, затем использует KeyFactory и X509EncodedKeySpec
* для преобразования массива байтов в объект 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-запросов, упомянутые ниже, необходимо изменить на соответствующие адреса. (Например, API подтверждения доставки в тестовой среде: https://api.jogospre.com/api/gamepay/webhook/arrivedorder)Вы создали игровое приложение в панели разработчика и сгенерировали общий секретный ключ для вашей игры в разделе "Параметры игры".
В настройках вашей игры активирован режим "Использовать внутриигровые покупки" и указан адрес обратного вызова для уведомлений вашего сервера.
Для связи используется шифрование 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 algorithm is not available", e); } }
Интеграция на стороне клиента:
- Создание и инициирование заказа осуществляется на стороне клиента.
- Подробности см. в документации:
Интеграция SDK покупок на стороне клиента
.
Начало интеграции на стороне сервера:
1. После успешной покупки товара в игре платформа Jogos отправляет уведомление об успешной оплате
Платформа Jogos отправит информацию об успешном платеже на настроенный вами адрес обратного вызова для уведомлений.
Заголовки запроса:
Имя поля Тип Может быть пустым Описание Authorization string Нет Заголовок аутентификации, sha1(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), не пусто при возврате Пример тела запроса:
javascript{ "gameId": 9968, "orderId": "zXhKYRy3genKKh9TgndBpu1gp5tBpA", "arrivedStatus": 0, "productId": "1002", "createTime": 1755657098, "payTime": 1755657115, "userId": 278, "refundStatus": null, "refundTime": null }
- Возвращаемый JSON-объект содержит следующие поля: (code:200 означает успех)
{ "code": "200", "msg": "Success" }
Коды ошибок:
305
Обязательный параметр пуст40001
Соответствующая игра не найдена41007
Проверка не пройдена500
Системная ошибка
2. После завершения отгрузки игровым сервером необходимо вызвать интерфейс «Обновление статуса доставки», чтобы пометить заказ как доставленный
URL запроса: [POST] https://api.jogos.com/api/gamepay/webhook/arrivedorder
Заголовки запроса:
Имя поля Тип Может быть пустым Описание Authorization string Нет Заголовок аутентификации, sha1(body+ключ), где ключ - общий секрет игры, установленный в параметрах игры Тело запроса:
Имя поля Тип Может быть пустым Описание gameId int Нет ID игры orderId int Нет Номер заказа Пример тела запроса:
javascript{ "gameId": 9943, "orderId":"gHpGwweTlucXwcM6yIeSMcgKkhFmoO" }
Возвращаемый JSON-объект содержит следующие поля: (code:200 означает успех)
{ "code": "200", "msg": "Success" }
Коды ошибок:
305
Обязательный параметр пуст40001
Соответствующая игра не найдена41007
Проверка не пройдена500
Системная ошибка
3. Игровой сервер запрашивает список заказов для синхронизации статусов заказов и доставки
URL запроса: [POST] https://api.jogos.com/api/gamepay/getOrders
Заголовки запроса:
Имя поля Тип Может быть пустым Описание Authorization string Нет Заголовок аутентификации, sha1(body+ключ), где ключ - общий секрет игры, установленный в параметрах игры Тело запроса:
Имя поля Тип Может быть пустым Описание gameId int Нет ID игры pageNo int Нет Номер страницы pageSize int Нет Размер страницы Пример тела запроса:
javascript{ "gameId": 9943, "pageNo":1, "pageSize":20 }
Возвращаемый 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 отправит информацию о successfully refunded order этого пользователя на настроенный вами адрес обратного вызова для уведомлений.
- Тело запроса такое же, как в пункте 1 «Уведомление об успешной оплате».
- Разработчикам рекомендуется на основе информации о заказе на возвратПри необходимости вычитается или ограничивается вознаграждение, полученное игроком до пополнения.. (Сократить или ограничить награды/предметы, полученные игроком за предыдущие пополнения, по своему усмотрению на основе информации о заказе на возврат).