게임 내 구매

사용자에게 게임 내 구매 옵션을 제공하여 수익을 올릴 수 있습니다. 예를 들어, 레벨을 완료하는 데 필요한 추가 시간이나 캐릭터의 액세서리를 구매할 수 있습니다. 이렇게 하려면 다음과 같이 하세요.

알림

구매를 테스트하는 것은 그들의 소비를 연결한 후에만 가능합니다. 그렇지 않으면 처리되지 않은 결제가 발생할 수 있으며, 이는 심사를 통과하는 것을 불가능하게 만들 수 있습니다.

조건

게임 드래프트를 추가하고 발행한 후에는 구매 연결 요청을 포함한 이메일을 games-partners@yandex-team.com로 보내주시기 바랍니다. 이메일에는 반드시 게임의 이름과 식별자(ID)를 명시해 주세요.

games-partners@yandex-team.com에서 구매가 허용되었다는 확인 회신을 받은 후에, 구매 설정 및 테스트를 진행할 수 있습니다.

초기화

플레이어가 인앱 구매를 할 수 있도록 하려면 payments 객체를 사용하세요. 다음과 같은 방법으로 사용할 수 있습니다:

  • 직접 ysdk.payments에 접근할 수 있습니다. 결제 관련 메서드가 처음 호출될 때 초기화되므로 첫 호출은 약간 느릴 수 있습니다.

  • ysdk.getPayments() 메서드를 사용하여 객체를 초기화할 수 있습니다. 이 방법은 payments 메서드에 필요한 데이터를 미리 로드하므로 첫 호출 시 지연이 발생하지 않습니다.

제한

YaGames.init()ysdk.getPayments()에는 선택적 매개변수 signed: boolean을 전달할 수 있으며, 이는 사기 방지를 위한 목적으로 사용됩니다. 이 값의 선택은 결제 처리가 어디에서 이루어지는지에 따라 달라집니다:

  • 클라이언트 측에서 처리하는 경우 — 매개변수 없이 메서드를 호출하거나 signed: false를 전달하세요. 결제 메서드는 데이터를 공개된 형태로 반환합니다.

  • 서버 측에서 처리하는 경우 — signed: true를 전달하세요. 이 경우 payments.getPurchases()payments.purchase() 메서드의 응답에서 모든 데이터는 _signature 매개변수에 암호화된 형태로만 반환됩니다.

기본값(signed: false)으로 초기화합니다.

방법 1: 간편한 방법

YaGames.init() // 또는 YaGames.init({ signed: false }).
    .then(ysdk => {
        // 결제 메서드는 ysdk.payments에서 사용할 수 있습니다.
        console.log(ysdk.payments);
    }).catch(err => {
        // 결제를 사용할 수 없음.
    });

방법 2: getPayments()를 통한 사전 로드

var payments = null;
YaGames.init()
    .then(ysdk => {
        ysdk.getPayments()
            .then(_payments => {
                // 결제 메서드는 payments에서 사용할 수 있습니다.
                payments = _payments;
                console.log(payments);
            }).catch(err => {
                // 결제를 사용할 수 없음.
            });
    });

signed: true 매개변수로 초기화합니다.

방법 1: SDK 초기화 시

YaGames.init({ signed: true }) // 결제에 대한 서명 작업을 활성화합니다.
    .then(ysdk => {
        // 결제 메서드는 ysdk.payments에서 사용할 수 있습니다.
        console.log(ysdk.payments);
    }).catch(err => {
        // 결제를 사용할 수 없음.
    });

방법 2: getPayments()를 통한 세부 설정

var payments = null;
YaGames.init()
    .then(ysdk => {
        ysdk.getPayments({ signed: true })
            .then(_payments => {
                // 결제 메서드는 payments에서 사용할 수 있습니다.
                payments = _payments;
                console.log(payments);
            }).catch(err => {
                // 결제를 사용할 수 없음.
            });
    });

 

구매 프로세스 활성화

게임 내 구매를 활성화하려면 payments.purchase({ id, developerPayload }) 메서드를 사용하세요.

  • id: string — 개발자 대시보드에 설정된 제품 ID입니다.
  • developerPayload: string — 선택적 파라미터. 서버로 전송할 추가 구매 데이터(signature 파라미터로 전송됨).

이 메소드는 결제 게이트웨이를 여는 프레임을 엽니다.

초기화 기본 매개변수(signed: false)를 사용하여.

Promise<Purchase>를 반환합니다.

Purchase: object — 구매에 대한 정보. 다음 속성을 포함합니다:

  • productID: string — 제품 ID.
  • purchaseToken: string — 구매자에게 구매를 사용할 수 있는 권한을 부여하는 토큰입니다.
  • developerPayload: string — 추가 구매 데이터.

초기화 매개변수 signed: true를 사용하여.

Promise<{ signature }>를 반환합니다.

signature: string — 구매 데이터 및 플레이어 인증을 위한 서명.

플레이어가 구매에 성공하면 Promise은 "해결됨" 상태로 전환됩니다. 플레이어가 구매를 하지 않고 창을 닫은 경우, Promise 상태는 "거부됨"이 됩니다.

알림

인터넷 연결이 불안정할 경우, 플레이어가 구매를 진행했지만 게임에 등록되지 않는 상황이 발생할 수 있습니다. 이를 방지하기 위해 미처리 구매 확인구매 처리 및 게임 내 재화 적립에 설명된 방법을 사용하여 구매를 처리해 주세요.

이 지침을 따르지 않으면 앱의 게임 내 구매가 비활성화되거나 목록에서 삭제될 수 있습니다.

사용자는 인증 없이 구매할 수 있지만, 사전에 계정 로그인을 권장합니다.

예시

일반:

payments.purchase({ id: 'gold500' })
    .then(purchase => {
        // 구매 성공!
    }).catch(err => {
        // 구매 실패: 개발자 대시보드에 이 ID의 제품이 존재하지 않습니다.
        // 사용자가 로그인하지 않았거나 마음이 바뀌어 결제 창을 닫았습니다.
        // 구매 시간이 초과되었거나, 자금이 부족하거나, 기타 다른 이유가 있습니다.
    });

선택적 파라미터인 developerPayload를 사용합니다.

payments.purchase({ id: 'gold500', developerPayload: '{serverId:42}' })
    .then(purchase => {
        // purchase.developerPayload === '{serverId:42}'
    });

구매한 아이템 목록 가져오기

payments.getPurchases() 메서드를 사용하여:

  • 플레이어가 이미 한 구매를 확인하십시오;

  • 미처리 구매의 존재를 확인하십시오;

  • 지속적인 구매를 처리하십시오.

초기화 기본 매개변수(signed: false)를 사용하여.

Promise<Purchase[]>를 반환합니다.

Purchase[]: array — 플레이어가 한 구매 목록입니다. 각 구매 Purchase는 다음 속성을 포함합니다:

  • productID: string — 제품 ID.
  • purchaseToken: string — 구매자에게 구매를 사용할 수 있는 권한을 부여하는 토큰입니다.
  • developerPayload: string — 추가 구매 데이터.

예시

var SHOW_ADS = true;
payments.getPurchases()
    .then(purchases => {
        if (purchases.some(purchase => purchase.productID === 'disable_ads')) {
          SHOW_ADS = false;
        }
    }).catch(err => {
        // 로그오프한 사용자에 대해 USER_NOT_AUTHORIZED 예외가 발생합니다.
    });

초기화 매개변수 signed: true를 사용하여.

Promise<{ signature }>를 반환합니다.

signature: string — 구매 데이터 및 플레이어 인증을 위한 서명.

예시

payments.getPurchases()
  .then(purchases => {
      fetch('https://your.game.server?purchase', {
          method: 'POST',
          headers: { 'Content-Type': 'text/plain' },
          body: purchases.signature
      });
  });

 

모든 제품 카탈로그 가져오기

사용 가능한 구매 목록과 해당 비용을 가져오려면 payments.getCatalog() 메서드를 사용합니다.

이 메서드는 Promise<Product[]>를 반환합니다.

Product[]: array — 사용자가 사용할 수 있는 제품 목록입니다. 개발자 대시보드의 In-app purchases 탭에 있는 표를 기반으로 생성됩니다. 각 Product에는 속성이 있습니다:

  • id: string — 제품 ID.
  • title: string — 제품 이름.
  • description: string — 제품 설명.
  • imageURI: string — 제품 이미지의 URL.
  • price: string — <price> <currency code> 형식의 제품 가격입니다.
  • priceValue: string — <price> 형식의 제품 가격입니다.
  • priceCurrencyCode: string — 통화 코드.

Product.getPriceCurrencyImage(size) — 화폐 아이콘 주소 획득 방법.

  • size: string — 선택 사항입니다. 가능한 값:

    • small (기본값) — 작은 아이콘을 받아옵니다.

    • medium — 중간 크기 아이콘 가져오기.

    • svg — 벡터 아이콘 반환.

이 방법은 string을 반환합니다 — 아이콘 주소, 예를 들면 //yastatic.net/s3/games-static/static-data/images/payments/sdk/currency-icon-s@2x.png.

예시

var gameShop = []
payments.getCatalog()
    .then(products => {
        gameShop = products;
    });

포털 화폐의 표시

게임 내에서 포털 화폐의 이름과 아이콘을 표시하기 위해 SDK의 기능을 사용하세요:

  • Product.price: string — 화폐 코드가 포함된 가격.
  • Product.priceCurrencyCode: string — 화폐 코드.
  • Product.getPriceCurrencyImage()string을 반환 — 화폐 아이콘의 주소.

Product 객체에 대한 자세한 내용은 모든 상품 카탈로그 받기 섹션을 참조하세요.

구매 처리 및 게임 내 통화 적립하기

구매에는 영구 구매(예: 광고 비활성화)와 소모성 구매(예: 게임 내 화폐)의 두 가지 유형이 있습니다.

영구 구매를 처리하려면 payments.getPurchases() 메서드를 사용합니다.

소모성 구매를 처리하려면 payments.consumePurchase() 메서드를 사용합니다.

payments.consumePurchase()

이 메서드는 "해결됨" 상태(처리가 성공한 경우) 또는 "거부됨" 상태(오류가 발생한 경우)의 Promise를 반환합니다.

알림

payments.consumePurchase() 메서드를 호출하면 처리된 구매는 영구 삭제됩니다. 따라서 먼저 player.setStats() 또는 player.incrementStats() 메서드를 사용하여 플레이어 데이터를 수정한 후 구매를 처리해야 합니다.

예시

payments.purchase({ id: 'gold500' })
    .then(purchase => {
        // 구매 성공!
        // 계정에 500 골드를 추가하고 구매를 사용합니다.
        addGold(500).then(() => payments.consumePurchase(purchase.purchaseToken));
    });

function addGold(value) {
    return player.incrementStats({ gold: value });
    // 자세한 내용은 플레이어 데이터를 참조하세요.
}

purchaseToken: string — payments.purchase()payments.getPurchases() 메서드가 반환한 토큰입니다.

처리되지 않은 구매 확인하기

게임 내 구매 중에 사용자의 인터넷 연결이 실패하거나 서버가 오프라인 상태가 되면 구매가 처리되지 않은 상태로 남아있을 수 있습니다. 이를 방지하려면 payments.getPurchases() 메서드를 사용하여 처리되지 않은 구매가 있는지 확인하세요(예: 게임을 시작할 때마다).

알림

구매를 추가하기 전에, 미처리 결제 검사를 설정하세요.

이 검사는 모더레이션을 통과하기 위해 필수적입니다, 그러므로 테스트 구매를 위해서라도 설정하는 것이 중요합니다. 게임에 구매를 추가하고 소비 설정을 하기 전에 테스트를 진행한다면, 테스트 후에 처리되지 않은 결제가 남아 모더레이션을 통과하는 것이 불가능해질 수 있습니다.

초기화 기본 매개변수(signed: false)를 사용하여.

예시

payments.getPurchases().then(purchases => purchases.forEach(consumePurchase));

function consumePurchase(purchase) {
    if (purchase.productID === 'gold500') {
        player.incrementStats({ gold: 500 })
            .then(() => {
                payments.consumePurchase(purchase.purchaseToken)
            });
    }
}

초기화 매개변수 signed: true를 사용하여.

예시

payments.getPurchases()
    .then(purchases => {
        fetch('https://your.game.server?purchase', {
            method: 'POST',
            headers: { 'Content-Type': 'text/plain' },
            body: purchases.signature
        });
    });

 

사기 방지

게임 내에서 가능한 지표 조작을 방지하기 위해 서버 측에서 구매 처리를 하세요:

  1. Initialize YaGames.init() or ysdk.getPayments() with the { signed: true } parameter.
  2. payments.purchase()payments.getPurchases() 응답에서 받은 서명을 자신의 서버로 전송하고, 비밀 키를 사용하여 이를 해독합니다.
  3. 자신의 서버에서 플레이어에게 게임 내에서 얻은 아이템을 지급하세요.
// 구매가 { signed: true } 파라미터를 사용하여 초기화되었는지 확인합니다

payments.purchase({ id: 'gold500' })
    .then(purchase => {
         // 구매 성공!
         // 서버에 500골드 추가...
         serverPurchase(purchase.signature); // 서명을 확인하고 플레이어에게 데이터를 지급합니다.
    });

function serverPurchase(signature) {
    return fetch('https://your.game.server?purchase', {
            method: 'POST',
            headers: { 'Content-Type': 'text/plain' },
            body: signature
        });
}

서버로 전송되는 요청의 signature 파라미터에는 구매 데이터와 서명이 포함됩니다. base64 인코딩의 두 문자열입니다: <signature>.<JSON with the purchase data>.

서명 예시

hQ8adIRJWD29Nep+0P36Z6edI5uzj6F3tddz6Dqgclk=.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImlzc3VlZEF0IjoxNTcxMjMzMzcxLCJyZXF1ZXN0UGF5bG9hZCI6InF3ZSIsImRhdGEiOnsidG9rZW4iOiJkODVhZTBiMS05MTY2LTRmYmItYmIzOC02ZDJhNGNhNDQxNmQiLCJzdGF0dXMiOiJ3YWl0aW5nIiwiZXJyb3JDb2RlIjoiIiwiZXJyb3JEZXNjcmlwdGlvbiI6IiIsInVybCI6Imh0dHBzOi8veWFuZGV4LnJ1L2dhbWVzL3Nkay9wYXltZW50cy90cnVzdC1mYWtlLmh0bWwiLCJwcm9kdWN0Ijp7ImlkIjoibm9hZHMiLCJ0aXRsZSI6ItCR0LXQtyDRgNC10LrQu9Cw0LzRiyIsImRlc2NyaXB0aW9uIjoi0J7RgtC60LvRjtGH0LjRgtGMINGA0LXQutC70LDQvNGDINCyINC40LPRgNC1IiwicHJpY2UiOnsiY29kZSI6IlJVUiIsInZhbHVlIjoiNDkifSwiaW1hZ2VQcmVmaXgiOiJodHRwczovL2F2YXRhcnMubWRzLnlhbmRleC5uZXQvZ2V0LWdhbWVzLzE4OTI5OTUvMmEwMDAwMDE2ZDFjMTcxN2JkN2EwMTQ5Y2NhZGM4NjA3OGExLyJ9fX0=

전송된 구매 데이터 예시(JSON형식)

serverPurchase(signature) 함수에 사용되는 signature 파라미터의 데이터 형식은 payments.getPurchases() 메서드에서 사용되는 형식과 다르므로 주의하시기 바랍니다.

payments.getPurchases() 메서드에서 signature 파라미터는 data 필드에 구매 개체 배열을 포함합니다. serverPurchase(signature) 함수에서는 구매 객체입니다.

{
  "algorithm": "HMAC-SHA256",
  "issuedAt": 1571233371,
  "requestPayload": "qwe",
  "data": {
    "token": "d85ae0b1-9166-4fbb-bb38-6d2a4ca4416d",
    "status": "waiting",
    "errorCode": "",
    "errorDescription": "",
    "url": "https://yandex.ru/games/sdk/payments/trust-fake.html",
    "product": {
      "id": "noads",
      "title": "No ads",
      "description": "Disable ads in the game",
      "가격": {
        "코드": "YAN",
        "값": "49"
      },
      "imagePrefix": "https://avatars.mds.yandex.net/get-games/1892995/2a0000016d1c1717bd7a0149ccadc86078a1/"
    },
    "developerPayload": "TEST DEVELOPER PAYLOAD"
  }
}

비밀 키 예시

t0p$ecret

서명을 확인하는 데 사용되는 비밀 키는 게임마다 고유합니다. 개발자 대시보드에서 구매 생성 시 자동으로 생성됩니다. 구매 표 아래에서 찾을 수 있습니다.

서버의 서명 확인 예제

import hashlib
import hmac
import base64
import json

usedTokens = {}

key = 't0p$ecret' # 키는 비밀로 유지하세요.
secret = bytes(key, 'utf-8')
signature = 'hQ8adIRJWD29Nep+0P36Z6edI5uzj6F3tddz6Dqgclk=.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImlzc3VlZEF0IjoxNTcxMjMzMzcxLCJyZXF1ZXN0UGF5bG9hZCI6InF3ZSIsImRhdGEiOnsidG9rZW4iOiJkODVhZTBiMS05MTY2LTRmYmItYmIzOC02ZDJhNGNhNDQxNmQiLCJzdGF0dXMiOiJ3YWl0aW5nIiwiZXJyb3JDb2RlIjoiIiwiZXJyb3JEZXNjcmlwdGlvbiI6IiIsInVybCI6Imh0dHBzOi8veWFuZGV4LnJ1L2dhbWVzL3Nkay9wYXltZW50cy90cnVzdC1mYWtlLmh0bWwiLCJwcm9kdWN0Ijp7ImlkIjoibm9hZHMiLCJ0aXRsZSI6ItCR0LXQtyDRgNC10LrQu9Cw0LzRiyIsImRlc2NyaXB0aW9uIjoi0J7RgtC60LvRjtGH0LjRgtGMINGA0LXQutC70LDQvNGDINCyINC40LPRgNC1IiwicHJpY2UiOnsiY29kZSI6IlJVUiIsInZhbHVlIjoiNDkifSwiaW1hZ2VQcmVmaXgiOiJodHRwczovL2F2YXRhcnMubWRzLnlhbmRleC5uZXQvZ2V0LWdhbWVzLzE4OTI5OTUvMmEwMDAwMDE2ZDFjMTcxN2JkN2EwMTQ5Y2NhZGM4NjA3OGExLyJ9fX0='

sign, data = signature.split('.')
message = base64.b64decode(data)

purchaseData = json.loads(message)
result = base64.b64encode(hmac.new(secret, message, digestmod=hashlib.sha256).digest())
if result.decode('utf-8') == sign:
  print('Signature check ok!')

  if not purchaseData['data']['token'] in usedTokens:
    usedTokens[purchaseData['data']['token']] = True; # 데이터베이스를 사용합니다.
    print('Double spend check ok!')

    print('Apply purchase:', purchaseData['data']['product'])
    # 여기서 안전하게 구매를 신청하실 수 있습니다.
const crypto = require('crypto');

const usedTokens = {};

const key = 't0p$ecret'; // 키는 비밀로 유지하세요.
const signature = 'hQ8adIRJWD29Nep+0P36Z6edI5uzj6F3tddz6Dqgclk=.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImlzc3VlZEF0IjoxNTcxMjMzMzcxLCJyZXF1ZXN0UGF5bG9hZCI6InF3ZSIsImRhdGEiOnsidG9rZW4iOiJkODVhZTBiMS05MTY2LTRmYmItYmIzOC02ZDJhNGNhNDQxNmQiLCJzdGF0dXMiOiJ3YWl0aW5nIiwiZXJyb3JDb2RlIjoiIiwiZXJyb3JEZXNjcmlwdGlvbiI6IiIsInVybCI6Imh0dHBzOi8veWFuZGV4LnJ1L2dhbWVzL3Nkay9wYXltZW50cy90cnVzdC1mYWtlLmh0bWwiLCJwcm9kdWN0Ijp7ImlkIjoibm9hZHMiLCJ0aXRsZSI6ItCR0LXQtyDRgNC10LrQu9Cw0LzRiyIsImRlc2NyaXB0aW9uIjoi0J7RgtC60LvRjtGH0LjRgtGMINGA0LXQutC70LDQvNGDINCyINC40LPRgNC1IiwicHJpY2UiOnsiY29kZSI6IlJVUiIsInZhbHVlIjoiNDkifSwiaW1hZ2VQcmVmaXgiOiJodHRwczovL2F2YXRhcnMubWRzLnlhbmRleC5uZXQvZ2V0LWdhbWVzLzE4OTI5OTUvMmEwMDAwMDE2ZDFjMTcxN2JkN2EwMTQ5Y2NhZGM4NjA3OGExLyJ9fX0=';

const [sign, data] = signature.split('.');
const purchaseDataString = Buffer.from(data, 'base64').toString('utf8');
const hmac = crypto.createHmac('sha256', key);

hmac.update(purchaseDataString);

const purchaseData = JSON.parse(purchaseDataString);

if (sign === hmac.digest('base64')) {
  console.log('Signature check ok!');

  if (!usedTokens[purchaseData.data.token]) {
    usedTokens[purchaseData.data.token] = true; // 데이터베이스를 사용합니다.
    console.log('Double spend check ok!');

    console.log('Apply purchase:', purchaseData.data.product);
    // 여기서 안전하게 구매를 신청하실 수 있습니다.
  }
}

참고

지원팀은 완성된 게임을 Yandex Games에 게시하는 데 도움을 드릴 수 있습니다. 개발이나 테스트에 대해 궁금한 점이 있다면, Discord 채널에서 질문해 주세요.

지원 서비스는 얀덱스 게임에서 완성 된 게임을 게시 할 수 있습니다. 개발 또는 테스트에 대한 질문이 있는 경우

Yandex Games SDK 사용과 관련하여 문제가 발생하거나 질문이 있는 경우 다음 방법으로 지원팀에 문의하세요.

채팅 상담

id: string — 개발자 대시보드에 설정된 제품 ID입니다.

developerPayload: string — 선택적 파라미터. 서버로 전송할 추가 구매 데이터(signature 파라미터로 전송).

서버로 전송되는 요청의 signature 파라미터에는 구매 데이터와 서명이 포함됩니다. base64 인코딩의 두 문자열입니다: <signature>.<JSON with the purchase data>.

purchaseToken: string — payments.purchase()payments.getPurchases() 메서드가 반환한 토큰입니다.

ysdk.getPayments()로 결제를 초기화하지 않은 경우, 직접 ysdk.payments를 호출하세요.

  • 수익화 설정
  • 개발자 콘솔에서 In-app purchases 탭으로 이동 후 다음 사항을 확인하세요:
    • 인앱 상품이 하나 이상 등록된 테이블이 존재하는지
    • Purchases are enabled 문구가 표시되는지
Previous