Skip to content

서버 측 연동 인터페이스 설명

만약 귀하의 게임이 자체 게임 서버를 보유하고 있으며, 게임 플레이어 데이터가 귀하의 서버에 저장 및 관리되는 경우, 서버 측 연동이 필요합니다:

모듈설명연동 권고사항
사용자 시스템Jogos 플랫폼의 사용자 시스템과 귀하의 서버 사용자를 연동해야 합니다. 서버 측에서 사용자 시스템을 연동해야 합니다.☑️ 필수 연동
인앱 결제 시스템게임에 인앱 결제 항목이 있는 경우, 사용자 결제 및 데이터 보안을 위해 Jogos 인앱 결제 시스템을 연동해야 합니다.☑️ 인앱 결제 게임 필수 연동

사용자 시스템 연동

1 클라이언트 측에서 사용자 토큰 획득

2 개요

Jogos에서 사용자 token 및 publickey를 획득한 후, 게임 서버 측으로 전달하여 검증 및 사용자 정보 획득에 사용합니다. 주의: 매번 토큰을 검증할 때마다 publickey를 획득해야 합니다. 이는 언제든지 변경될 수 있기 때문입니다.

3 검증 로직

  • 게임 개발자는 Jogos에서 사용자 token을 획득하여 게임 개발자 서버로 전달합니다.
  • 서버 측에서 publickey를 획득한 후 (https://www.jogos.com/publicKey.json 통해 획득)
  • Base64로 인코딩된 publickey 문자열을 기반으로 RSA 공개 키를 획득합니다.
  • RSA 공개 키를 사용하여 JWT 토큰을 복호화합니다. 반환된 토큰 디코딩은 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) {
		/**
		 * Jogos SDK에서 publickey와 사용자 token을 얻어 서버로 전달합니다. 아래는 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
		     
		  참고: 매번 토큰을 검증할 때마다 publickey 키를 획득해야 합니다. 변경될 수 있기 때문입니다.
		 */
		
		String publickey ="귀하의 publickey";
		String token = "귀하의 사용자 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;
    }
	
	 /**
     * Base64로 인코딩된 문자열로 RSA 공개 키 획득
     * @param key Base64로 인코딩된 공개 키 문자열
     * @return 변환된 RSA 공개 키
     * <p>
     * 참고: 이 메서드는 Base64 디코더를 사용하여 입력 문자열을 바이트 배열로 디코딩한 다음 KeyFactory 및 X509EncodedKeySpec를 사용합니다.
     * 바이트 배열을 PublicKey 객체로 변환합니다. 변환 과정에서 예외가 발생하면 RuntimeException이 throw됩니다.
     */
    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(운영 환경)을 사용 중인지 확인하십시오; 아래에서 언급된 POST 요청 URL을 해당 주소로 변경해야 합니다. (예: 테스트 환경 도착 API: https://api.jogospre.com/api/gamepay/webhook/arrivedorder)

  • 개발자 플랫폼에서 게임 애플리케이션을 생성하고 "게임 매개변수"에서 게임의 공유 비밀키를 생성했는지 확인하십시오.

  • 게임 옵션에서 "인앱 구매 사용" 모드를 선택하고 서버가 수신하는 콜백 알림 주소를 입력했는지 확인하십시오.

  • 통신에는 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 algorithm is not available", e);
        }
    }

클라이언트 측 선행 연동:

서버 측 연동 시작:

1. 사용자가 게임 내에서 상품 구매 성공 후, Jogos 플랫폼이 결제 성공 알림 전송

  • Jogos 플랫폼은 결제 성공 콜백 정보를 귀하가 구성한 콜백 알림 주소로 전송합니다.

  • 요청 헤더:

    필드 이름유형빈값 허용 여부설명
    Authorizationstring아니오검증 헤더, sha1(body+비밀키), 여기서 비밀키는 해당 게임의 공유 비밀키이며 게임 매개변수에서 설정됩니다.
  • 요청 본문에는 다음 필드가 포함됩니다:

    필드 이름유형빈값 허용 여부설명
    paytypeString아니오알림 유형 pay: 결제 성공 알림; refund: 환불 성공 알림
    gameIdint아니오게임 ID
    orderIdString아니오주문 번호
    arrivedStatusint아니오도착 상태: 0 미도착, 1 도착
    productIdString아니오제품 ID
    createTimelong아니오주문 생성 시간 (Unix 타임스탬프)
    payTimelong아니오주문 결제 성공 시간 (Unix 타임스탬프)
    userIdint아니오사용자 ID
    refundStatusint환불 상태, 환불 발생 시 비어 있지 않음 0: 환불 중, 1: 환불 성공; 2: 환불 실패; 3: 환불 거부
    refundTimelong환불 시간 (Unix 타임스탬프), 환불 발생 시 비어 있지 않음

    요청 본문 예시:

    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 시스템 예외
    *일반 알림*
    • 게임 서버가 200을 반환하지 않을 경우, 결제 서비스는 3시간 후에 여러 번 재전송을 시도하며, 최대 5회까지 시도합니다.
    • 결제 서버 알림을 수신할 때, 다음 방식으로 매개변수를 검증해야 합니다:
    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); // "Signature " 건너뜀
        String serverSignature = sha1(body + your_key);//your_key는 게임 특정 공유 비밀키이며 게임 매개변수에서 설정됩니다.
    
        return clientSignature.equals(serverSignature);
    }

2. 게임 서버 배송 완료 후, "도착 상태 업데이트" 인터페이스를 호출하여 주문을 발송 완료 상태로 표시해야 함

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

  • 요청 헤더:

    필드 이름유형빈값 허용 여부설명
    Authorizationstring아니오검증 헤더, sha1(body+비밀키), 여기서 비밀키는 해당 게임의 공유 비밀키이며 게임 매개변수에서 설정됩니다.
  • 요청 본문:

    필드 이름유형빈값 허용 여부설명
    gameIdint아니오게임 ID
    orderIdint아니오주문 번호

    요청 본문 예시:

    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

  • 요청 헤더:

    필드 이름유형빈값 허용 여부설명
    Authorizationstring아니오검증 헤더, sha1(body+비밀키), 여기서 비밀키는 해당 게임의 공유 비밀키이며 게임 매개변수에서 설정됩니다.
  • 요청 본문:

    필드 이름유형빈값 허용 여부설명
    gameIdint아니오게임 ID
    pageNoint아니오페이지 번호
    pageSizeint아니오페이지 당 항목 수

    요청 본문 예시 :

    javascript
    {
        "gameId"9943,
        "pageNo":1,
        "pageSize":20
    }
  • 반환된 JSON 객체는 다음 필드를 포함합니다: (code:200 성공을 나타냄)

    필드 이름유형빈값 허용 여부설명
    codeString아니오상태 코드, 요청 결과를 나타냄 (예: SUCCESS)
    messageString아니오알림 메시지 (예: 로그아웃 성공)
    totalInteger아니오총계
    pageSizeInteger아니오페이지 당 항목 수
    currentPageInteger아니오현재 페이지
    totalPageInteger아니오총 페이지 수
    orderIdString아니오주문 번호
    arrivedStatusInteger아니오도착 상태; 0 미도착, 1 도착
    payTimelong아니오결제 시간 (Unix 타임스탬프)
    productIdString아니오제품 ID
    userIdInteger아니오사용자 id
    refundStatusInteger환불 상태, 환불 발생 시 비어 있지 않음: 0: 환불 중, 1: 환불 성공, 2 환불 실패, 3 환불 거부
    refundTimelong환불 시간 (Unix 타임스탬프), 환불 발생 시 비어 있지 않음
    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 플랫폼은 해당 사용자의 환불 신청 성공 주문 콜백 정보를 귀하가 구성한 콜백 알림 주소로 전송합니다.
  • 요청 본문은 제1항 "결제 성공 알림"과 동일합니다.
  • 개발자는 환불 주문 정보에 따라酌情扣减或限制玩家之前充值所得的奖励道具. (플레이어가 이전에 충전으로 얻은 보상 아이템을 상황에 따라 공제 또는 제한하는 것이 좋습니다.)