Uygulama içi satın alımlar

Kullanıcıların oyun içi satın alma yapmasına izin vererek gelir elde edebilirsiniz. Örneğin, bir seviyeyi tamamlamak için ekstra süre veya oyun karakteri için aksesuarlar. Bunun için:

Dikkat

Satın alımları yalnızca tüketimlerini etkinleştirdikten sonra test edebilirsiniz. Aksi takdirde işlenmemiş ödemeler ortaya çıkabilir ve bu da moderasyondan geçmeyi imkansız hale getirebilir.

Etkinleştirme koşulları

SDK ile çalışmadan önce işbirliği şemasını kontrol edin. Bunun için Geliştirici Konsolu'nda Account bölümüne gidin ve Unified licensing model alanının değerini kontrol edin:

Para kazanma ve satın alımları etkinleştirin:

  1. Reklam para kazanmayı etkinleştirin. Yandex Reklam Ağı ortak arayüzünde reklam ve satın alımlar için ödeme bilgilerini belirtin. Veriler doğrulandıktan sonra Yandex Reklam Ağı arayüzünde Extras → Documents bölümündeki sözleşme durumu Offer accepted olarak değişecektir.

  2. Etkinleştirme talebi ile games-partners@yandex-team.com adresine e-posta gönderin. E-postada şunları belirtin:

    • oyun adı
    • oyun kimliği (ID)

    Öneri

    Talebi mümkün olduğunca erken gönderin, bunu oyun arşivini yüklemeden veya satın alımları eklemeden önce yapabilirsiniz.

  3. games-partners@yandex-team.com adresinden satın alımların izin verildiğini onaylayan yanıt e-postasını bekleyin.

Talimatın sonraki adımları için Satın almayı bağlayın bölümüne bakın.

Tüm oyunlarınızda satın alımlar otomatik olarak etkinleştirilmiştir. Başlatmaya geçin.

Başlatma

Oyuncuların uygulama içi satın alımlar yapabilmesi için payments nesnesini kullanın. Şunları yapabilirsiniz:

  • Doğrudan ysdk.payments'a erişin. Satın alımlar, nesnenin herhangi bir metoduna ilk çağrı yapıldığında başlatılır, bu nedenle ilk çağrı biraz daha yavaş olabilir.

  • ysdk.getPayments() metodu ile nesneyi başlatın. Bu metod, payments metodları için gerekli verileri önceden yükler. Böylece ilk çağrıda yavaşlama olmaz.

Dikkat

YaGames.init() ve ysdk.getPayments() metodlarına, sahtekarlığa karşı koruma için tasarlanmış isteğe bağlı signed: boolean parametresi iletilebilir. Değer seçimi, ödemelerin nerede işlendiğine bağlıdır:

  • İstemci tarafında işleniyorsa — metodları parametresiz çağırın veya signed: false iletin. Satın alma metodları verileri açık biçimde döndürür.

  • Sunucu tarafında işleniyorsa — signed: true iletin. Bu durumda payments.getPurchases() ve payments.purchase() metodlarının yanıtlarında tüm veriler yalnızca şifrelenmiş biçimde signature parametresinde döndürülür.

Varsayılan parametre ile başlatma (signed: false).

Yöntem 1: Basitleştirilmiş

1const ysdk = await YaGames.init();
2
3const payments = ysdk.payments;

Yöntem 2: getPayments() ile ön yükleme

1const ysdk = await YaGames.init();
2
3try {
4    const payments = await ysdk.getPayments();
5} catch (err) {
6    // Satın alımlar kullanılamıyor.
7}

signed: true parametresi ile başlatma.

Yöntem 1: SDK başlatılırken

1const ysdk = await YaGames.init({ signed: true });
2
3const payments = ysdk.payments;

Yöntem 2: getPayments() ile daha ayrıntılı yapılandırma

1const ysdk = await YaGames.init();
2
3try {
4    const payments = await ysdk.getPayments({ signed: true });
5} catch (err) {
6    // Satın alımlar kullanılamıyor.
7}

 

Satın alma sürecinin etkinleştirilmesi

Uygulama içi satın almayı etkinleştirmek için payments.purchase() metodunu kullanın. Bu metod, ödeme ağ geçidini içeren bir çerçeve açar.

Metodun imzası:

1function purchase(data: {
2    id: string;
3    developerPayload?: string;
4}) => Promise<IPurchase | ISign> {}

Parametreler alınır:

Parametre

Tür

Açıklama

id

string

Geliştirici Konsolu'nda ayarlanan ürün tanımlayıcısı.

developerPayload

string

İsteğe bağlı parametre. Sunucunuza iletmek istediğiniz satın alma hakkında ek bilgiler içerir (signature parametresinde iletilir).

Varsayılan parametre ile Başlatma (signed: false).

Satın alma bilgilerini içeren Promise<IPurchase> döndürür.

1interface IPurchase {
2    productID: string;
3    purchaseToken: string;
4    developerPayload: string;
5}

İçerir:

Parametre

Tür

Açıklama

productID

string

Ürün tanımlayıcısı.

purchaseToken

string

Satın almayı kullanmak için token.

developerPayload

string

Satın alma hakkında ek bilgiler.

signed: true parametresi ile Başlatma.

Promise<ISign> döndürür.

1interface ISign {
2    signature: string;
3}

İçerir:

Parametre

Tür

Açıklama

signature

string

Oyuncu kimliğini doğrulamak için şifrelenmiş satın alma verileri ve imza.

Satın alma başarıyla tamamlandıktan sonra Promise, fulfilled durumuyla çözümlenir. Oyuncu satın alma yapmadıysa ve pencereyi kapattıysa, Promise, rejected durumuyla reddedilir.

Dikkat

İstikrarsız internet bağlantısı, oyuncunun satın alma yaptığı ancak bunun oyunda işlenmediği durumlara yol açabilir. Bunu önlemek için, satın alımları işlemek üzere İşlenmemiş satın alımların kontrolü ve payments.consumePurchase() bölümlerinde açıklanan metodları uygulayın.

Bu talimatlara uyulmaması, uygulamada uygulama içi satın alımların devre dışı bırakılmasına veya uygulamanın yayından kaldırılmasına neden olabilir.

Kullanıcı oturum açmadan satın alma yapabilir, ancak önceden veya satın alma sırasında hesabına giriş yapmasını önermek önerilir.

Örnek

Genel durumda:

1const ysdk = await YaGames.init();
2
3try {
4    const purchase = await ysdk.payments.purchase({ id: 'gold500' });
5} catch (err) {
6    // Satın alma başarısız oldu: Geliştirici Konsolu'nda bu id'ye sahip ürün eklenmemiş,
7    // kullanıcı oturum açmadı, fikrini değiştirdi ve ödeme penceresini kapattı,
8    // satın alma için ayrılan süre doldu, yeterli para yok vb.
9}

İsteğe bağlı developerPayload parametresini kullanarak:

1const ysdk = await YaGames.init();
2
3try {
4    const purchase = await ysdk.payments.purchase({ id: 'gold500', developerPayload: '{serverId:42}' });
5} catch (err) {
6    // Satın alma hatasının işlenmesi.
7}

Satın alınan ürünlerin listesini alma

payments.getPurchases() metodunu kullanarak:

  • Oyuncunun daha önce hangi satın alımları yaptığını öğrenin.

  • İşlenmemiş satın alımların varlığını kontrol edin.

  • Kalıcı satın alımları işleyin.

Metodun imzası:

function getPurchases(): Promise<IPurchase[] | ISign> {}

Varsayılan parametre ile Başlatma (signed: false).

Satın alımlar dizisini içeren Promise<IPurchase[]> döndürür. Dizinin her öğesi, payments.purchase() metodu tarafından döndürülen satın almayla aynı formata sahiptir.

Örnek

 1const ysdk = await YaGames.init();
 2
 3let SHOW_ADS = true;
 4
 5try {
 6    const purchases = await ysdk.payments.getPurchases();
 7
 8    if (purchases.some(purchase => purchase.productID === 'disable_ads')) {
 9        SHOW_ADS = false;
10    }
11} catch (err) {
12    // Satın alma listesini alırken hata oluştu. PAYMENT_FAILURE istisnası fırlatır.
13}

signed: true parametresi ile Başlatma.

Promise<ISign> döndürür.

İçerir:

Parametre

Tür

Açıklama

signature

string

Oyuncu kimliğini doğrulamak için şifrelenmiş satın alma verileri ve imza.

Örnek

 1const ysdk = await YaGames.init({ signed: true });
 2
 3try {
 4    const purchases = await ysdk.payments.getPurchases();
 5    // Satın alma listesini sunucuya gönderiyoruz.
 6    const response = await fetch('https://your.game.server/handlePurchases', {
 7        method: 'POST',
 8        headers: { 'Content-Type': 'text/plain' },
 9        body: purchases.signature
10    });
11} catch (err) {
12    // Satın alma listesini alma veya işleme hatası.
13}

 

Tüm ürünlerin kataloğunu alma

Mevcut satın almaların ve fiyatlarının listesini almak için payments.getCatalog() metodunu kullanın.

Metodun imzası:

 1interface IProduct {
 2    id: string;
 3    title: string;
 4    description: string;
 5    imageURI: string;
 6    price: string;
 7    priceValue: string;
 8    priceCurrencyCode: string;
 9    getPriceCurrencyImage(size: 'small' | 'medium' | 'svg'): string;
10}
11
12function getCatalog(): Promise<IProduct[]> {}

Metod, kullanıcı için mevcut ürünlerin listesini döndürür. Geliştirici Konsolu'nun In-app purchases sekmesindeki tablodan oluşturulur. Her IProduct şu özellikleri içerir:

Özellik

Tür

Açıklama

id

string

Ürün tanımlayıcısı.

title

string

Ürün adı.

description

string

Ürün açıklaması.

imageURI

string

Ürün görselinin URL'si.

price

string

Ürün fiyatı <fiyat> <para birimi kodu> formatında.

priceValue

string

Ürün fiyatı <fiyat> formatında.

priceCurrencyCode

string

Para birimi kodu.

getPriceCurrencyImage(size)

string

Simge boyutu parametresine bağlı olarak para birimi simgesinin adresini alma yöntemi. Olası değerler:

  • small (varsayılan) — küçük simge alma.

  • medium — orta boyutlu simge alma.

  • svg — vektör formatında simge alma.

Önemli

Portal para birimi otomatik olarak belirlenmeli (madde 3.8)Bunun için adını ve simgesini IProduct özelliklerinden alın. Daha fazla bilgi için Portal Para Birimi Otomatik Tespiti bölümüne bakın.

Örnek

 1const ysdk = await YaGames.init();
 2
 3let gameShop = [];
 4
 5try {
 6    const purchases = await ysdk.payments.getPurchases();
 7
 8    gameShop = purchases;
 9} catch (err) {
10    // Satın alma listesini alma hatası.
11}

Satın almanın işlenmesi ve oyun içi para biriminin yüklenmesi

İki tür satın alma vardır:

  • Kalıcı (örneğin, reklamları devre dışı bırakma). Bunları işlemek için payments.getPurchases() metodunu uygulayın.
  • Tüketilebilir (örneğin, oyun içi para birimi). Bunları işlemek için payments.consumePurchase() metodunu uygulayın.

payments.consumePurchase()

Dikkat

payments.consumePurchase() metodu çağrıldıktan sonra, işlenen satın alma geri alınamayacak şekilde silinir. Bu nedenle önce oyuncu verilerini player.setData(), player.setStats() veya player.incrementStats() metodlarıyla değiştirin, ardından satın almayı işleyin.

Metodun imzası:

function consumePurchase(purchaseToken: string): Promise<void> {}

payments.purchase() ve payments.getPurchases() metodları tarafından döndürülen purchaseToken'ı kabul eder. İşlem başarılı olduysa, Promise fulfilled durumuyla çözümlenir, hata oluştuysa rejected durumuyla reddedilir.

Örnek

 1const ysdk = await YaGames.init();
 2
 3function addGold(value) {
 4    return ysdk.player.incrementStats({ gold: value });
 5}
 6
 7try {
 8    const purchase = await ysdk.payments.purchase({ id: 'gold500' });
 9
10    await addGold(500);
11
12    await ysdk.payments.consumePurchase(purchase.purchaseToken);
13} catch (err) {
14    // Tüketilebilir satın alma işleme hatasının işlenmesi.
15}

İşlenmemiş satın alımların kontrolü

Dikkat

Bu kontrol, moderasyondan geçmek için zorunludur (madde 1.13.1), bu nedenle test satın alımları için bile yapılandırmak önemlidir. Oyuna satın alımlar eklenip tüketim yapılandırılmadan önce test edilirse, testlerden sonra işlenmemiş ödemeler kalabilir ve bu da moderasyondan geçmeyi imkansız hale getirebilir.

Uygulama içi satın alma sırasında kullanıcının interneti kesilirse veya sunucunuz kullanılamazsa, satın alma işlenmemiş kalabilir. Bunu önlemek için, örneğin oyunu her başlattığınızda payments.getPurchases() metodunu kullanarak işlenmemiş satın alımların varlığını kontrol edin.

Varsayılan parametre ile Başlatma (signed: false).

Örnek

 1const ysdk = await YaGames.init();
 2
 3async function handlePurchase(purchase) {
 4    if (purchase.productID === 'gold500') {
 5        await ysdk.player.incrementStats({ gold: 500 });
 6
 7        await ysdk.payments.consumePurchase(purchase.purchaseToken);
 8    }
 9}
10
11const purchases = await ysdk.payments.getPurchases().then(purchases => purchases.forEach(consumePurchase));
12
13for (let purchase of purchases) {
14    await handlePurchase(purchase);
15}

signed: true parametresi ile Başlatma.

Örnek

 1const ysdk = await YaGames.init({ signed: true });
 2
 3try {
 4    const purchases = await ysdk.payments.getPurchases();
 5    // Satın alma listesini sunucuya gönderiyoruz.
 6    const response = await fetch('https://your.game.server/handlePurchases', {
 7        method: 'POST',
 8        headers: { 'Content-Type': 'text/plain' },
 9        body: purchases.signature
10    });
11} catch (err) {
12    // Satın alma listesini alma veya işleme hatası.
13}

 

Sahtekarlığa karşı koruma

Oyunda olası istatistik manipülasyonlarından korunmak için, satın alımları sunucu tarafında işleyin:

  1. YaGames.init() veya ysdk.getPayments() metodlarını { signed: true } parametresiyle başlatın.
  2. payments.purchase() ve payments.getPurchases() yanıtlarında alınan imzayı kendi sunucunuza iletin ve gizli anahtar kullanarak şifresini çözün.
  3. Kendi sunucunuzda, oyuncuya oyunda kazandığı öğeleri tahsis edin.
 1function serverPurchase(signature) {
 2    return fetch('https://your.game.server/handlePurchase', {
 3        method: 'POST',
 4        headers: { 'Content-Type': 'text/plain' },
 5        body: signature
 6    });
 7}
 8
 9// Satın alımların { signed: true } parametresiyle başlatıldığından emin olun.
10const ysdk = await YaGames.init({ signed: true });
11
12try {
13    const purchase = await ysdk.payments.purchase({ id: 'gold500' });
14
15    // Sunucuda 500 altın yüklüyoruz...
16    await serverPurchase(purchase.signature);
17} catch (err) {
18    // Satın alma hatası.
19}

Sunucuya iletilen isteğin signature parametresi, satın alma verilerini ve imzayı içerir. base64 kodlamasında iki dizeyi temsil eder: <imza>.<satın alma verileriyle JSON>.

Signature örneği

hQ8adIRJWD29Nep+0P36Z6edI5uzj6F3tddz6Dqgclk=.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImlzc3VlZEF0IjoxNTcxMjMzMzcxLCJyZXF1ZXN0UGF5bG9hZCI6InF3ZSIsImRhdGEiOnsidG9rZW4iOiJkODVhZTBiMS05MTY2LTRmYmItYmIzOC02ZDJhNGNhNDQxNmQiLCJzdGF0dXMiOiJ3YWl0aW5nIiwiZXJyb3JDb2RlIjoiIiwiZXJyb3JEZXNjcmlwdGlvbiI6IiIsInVybCI6Imh0dHBzOi8veWFuZGV4LnJ1L2dhbWVzL3Nkay9wYXltZW50cy90cnVzdC1mYWtlLmh0bWwiLCJwcm9kdWN0Ijp7ImlkIjoibm9hZHMiLCJ0aXRsZSI6ItCR0LXQtyDRgNC10LrQu9Cw0LzRiyIsImRlc2NyaXB0aW9uIjoi0J7RgtC60LvRjtGH0LjRgtGMINGA0LXQutC70LDQvNGDINCyINC40LPRgNC1IiwicHJpY2UiOnsiY29kZSI6IlJVUiIsInZhbHVlIjoiNDkifSwiaW1hZ2VQcmVmaXgiOiJodHRwczovL2F2YXRhcnMubWRzLnlhbmRleC5uZXQvZ2V0LWdhbWVzLzE4OTI5OTUvMmEwMDAwMDE2ZDFjMTcxN2JkN2EwMTQ5Y2NhZGM4NjA3OGExLyJ9fX0=

İletilen satın alma verileri örneği (JSONformatında)

Önemli

serverPurchase(signature) fonksiyonundaki signature parametresinin veri formatı, payments.getPurchases() metodunda kullanılandan farklıdır.

payments.getPurchases() metodunda, signature parametresi data alanında bir satın alma nesneleri dizisi içerir. serverPurchase(signature) fonksiyonunda ise bir satın alma nesnesi içerir.

 1{
 2  "algorithm": "HMAC-SHA256",
 3  "issuedAt": 1571233371,
 4  "requestPayload": "qwe",
 5  "data": {
 6    "token": "d85ae0b1-9166-4fbb-bb38-6d2a4ca4416d",
 7    "status": "waiting",
 8    "errorCode": "",
 9    "errorDescription": "",
10    "url": "https://yandex.ru/games/sdk/payments/trust-fake.html",
11    "product": {
12      "id": "noads",
13      "title": "Reklamsız",
14      "description": "Oyunda reklamı devre dışı bırak",
15      "price": {
16        "code": "YAN",
17        "value": "49"
18      },
19      "imagePrefix": "https://avatars.mds.yandex.net/get-games/1892995/2a0000016d1c1717bd7a0149ccadc86078a1/"
20    },
21    "developerPayload": "TEST DEVELOPER PAYLOAD"
22  }
23}

Gizli anahtar örneği

t0p$ecret

İmza doğrulama için gizli anahtar oyuna özgüdür. Geliştirici Konsolu'nda satın alımlar oluşturulurken otomatik olarak oluşturulur. Anahtar, In-app purchasesSettings sekmesinde görüntülenir.

Sunucuda imza doğrulama örneği

 1import hashlib
 2import hmac
 3import base64
 4import json
 5
 6usedTokens = {}
 7
 8key = 't0p$ecret' # Anahtarı gizli tutun.
 9secret = bytes(key, 'utf-8')
10signature = 'hQ8adIRJWD29Nep+0P36Z6edI5uzj6F3tddz6Dqgclk=.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImlzc3VlZEF0IjoxNTcxMjMzMzcxLCJyZXF1ZXN0UGF5bG9hZCI6InF3ZSIsImRhdGEiOnsidG9rZW4iOiJkODVhZTBiMS05MTY2LTRmYmItYmIzOC02ZDJhNGNhNDQxNmQiLCJzdGF0dXMiOiJ3YWl0aW5nIiwiZXJyb3JDb2RlIjoiIiwiZXJyb3JEZXNjcmlwdGlvbiI6IiIsInVybCI6Imh0dHBzOi8veWFuZGV4LnJ1L2dhbWVzL3Nkay9wYXltZW50cy90cnVzdC1mYWtlLmh0bWwiLCJwcm9kdWN0Ijp7ImlkIjoibm9hZHMiLCJ0aXRsZSI6ItCR0LXQtyDRgNC10LrQu9Cw0LzRiyIsImRlc2NyaXB0aW9uIjoi0J7RgtC60LvRjtGH0LjRgtGMINGA0LXQutC70LDQvNGDINCyINC40LPRgNC1IiwicHJpY2UiOnsiY29kZSI6IlJVUiIsInZhbHVlIjoiNDkifSwiaW1hZ2VQcmVmaXgiOiJodHRwczovL2F2YXRhcnMubWRzLnlhbmRleC5uZXQvZ2V0LWdhbWVzLzE4OTI5OTUvMmEwMDAwMDE2ZDFjMTcxN2JkN2EwMTQ5Y2NhZGM4NjA3OGExLyJ9fX0='
11
12sign, data = signature.split('.')
13message = base64.b64decode(data)
14
15purchaseData = json.loads(message)
16result = base64.b64encode(hmac.new(secret, message, digestmod=hashlib.sha256).digest())
17if result.decode('utf-8') == sign:
18  print('Signature check ok!')
19
20  if not purchaseData['data']['token'] in usedTokens:
21    usedTokens[purchaseData['data']['token']] = True # Veritabanını kullanın.
22    print('Double spend check ok!')
23
24    print('Apply purchase:', purchaseData['data']['product'])
25    # Burada satın alınanları güvenle tahsis edebilirsiniz.
 1const crypto = require('crypto');
 2
 3const usedTokens = {};
 4
 5const key = 't0p$ecret'; // Anahtarı gizli tutun.
 6const signature = 'hQ8adIRJWD29Nep+0P36Z6edI5uzj6F3tddz6Dqgclk=.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImlzc3VlZEF0IjoxNTcxMjMzMzcxLCJyZXF1ZXN0UGF5bG9hZCI6InF3ZSIsImRhdGEiOnsidG9rZW4iOiJkODVhZTBiMS05MTY2LTRmYmItYmIzOC02ZDJhNGNhNDQxNmQiLCJzdGF0dXMiOiJ3YWl0aW5nIiwiZXJyb3JDb2RlIjoiIiwiZXJyb3JEZXNjcmlwdGlvbiI6IiIsInVybCI6Imh0dHBzOi8veWFuZGV4LnJ1L2dhbWVzL3Nkay9wYXltZW50cy90cnVzdC1mYWtlLmh0bWwiLCJwcm9kdWN0Ijp7ImlkIjoibm9hZHMiLCJ0aXRsZSI6ItCR0LXQtyDRgNC10LrQu9Cw0LzRiyIsImRlc2NyaXB0aW9uIjoi0J7RgtC60LvRjtGH0LjRgtGMINGA0LXQutC70LDQvNGDINCyINC40LPRgNC1IiwicHJpY2UiOnsiY29kZSI6IlJVUiIsInZhbHVlIjoiNDkifSwiaW1hZ2VQcmVmaXgiOiJodHRwczovL2F2YXRhcnMubWRzLnlhbmRleC5uZXQvZ2V0LWdhbWVzLzE4OTI5OTUvMmEwMDAwMDE2ZDFjMTcxN2JkN2EwMTQ5Y2NhZGM4NjA3OGExLyJ9fX0=';
 7
 8const [sign, data] = signature.split('.');
 9const purchaseDataString = Buffer.from(data, 'base64').toString('utf8');
10const hmac = crypto.createHmac('sha256', key);
11
12hmac.update(purchaseDataString);
13
14const purchaseData = JSON.parse(purchaseDataString);
15
16if (sign === hmac.digest('base64')) {
17  console.log('Signature check ok!');
18
19  if (!usedTokens[purchaseData.data.token]) {
20    usedTokens[purchaseData.data.token] = true; // Veritabanını kullanın.
21    console.log('Double spend check ok!');
22
23    console.log('Apply purchase:', purchaseData.data.product);
24    // Burada satın alınanları güvenle tahsis edebilirsiniz.
25  }
26}

Not

Destek ekibi, hazır oyunu Yandex Oyun platformuna yerleştirmenize yardımcı olur. Geliştirme ve test etme konularındaki uygulamalı sorulara ise diğer geliştiriciler Discord Topluluğu yanıt verir.

Yandex Games SDK kullanırken sorunla karşılaşırsanız veya sorunuz varsa, lütfen destek ekibiyle iletişime geçin:

Sohbete yaz

Sunucuya gönderilen isteğin signature parametresi, satın alma hakkındaki verileri ve imzayı içerir. base64 kodlamasında iki dizeden oluşur: <imza>.<Satın alma hakkında JSON verileri>.