Mua trong game

Bạn có thể tạo doanh thu bằng cách cung cấp cho người dùng tùy chọn mua hàng trong trò chơi. Ví dụ: họ có thể mua thêm thời gian để hoàn thành cấp độ hoặc phụ kiện cho nhân vật của mình. Để làm việc này:

Chú ý

Bạn chỉ có thể kiểm tra mua hàng sau khi đã kết nối tiêu thụ của chúng. Nếu không, những thanh toán chưa xử lý có thể xuất hiện, làm cho việc qua mức kiểm duyệt không thể được.

Điều kiện

Sau khi bạn thêm thông tin mua hàng và phát hành bản nháp trò chơi, hãy gửi yêu cầu để bật chế độ mua hàng đến games-partners@yandex-team.com. Hãy bao gồm tên và ID trò chơi của bạn trong email.

Sau khi bạn nhận được xác nhận từ games-partners@yandex-team.com cho biết chế độ mua hàng đã được bật, bạn có thể đặt cấu hình và kiểm tra chúng.

Khởi tạo

Để người chơi có thể thực hiện in-app purchases, hãy sử dụng đối tượng payments. Bạn có thể:

  • Truy cập trực tiếp vào ysdk.payments. Các giao dịch mua hàng sẽ được khởi tạo khi gọi lần đầu tiên bất kỳ phương thức nào của đối tượng, vì vậy lần gọi đầu tiên có thể hơi chậm.

  • Khởi tạo đối tượng bằng phương thức ysdk.getPayments(). Phương thức này sẽ tải trước dữ liệu cần thiết cho các phương thức payments, giúp lần gọi đầu tiên không bị chậm.

Hạn chế

Trong YaGames.init()ysdk.getPayments(), bạn có thể truyền tham số tùy chọn signed: boolean nhằm bảo vệ chống gian lận. Giá trị này phụ thuộc vào nơi xử lý thanh toán:

  • Nếu xử lý phía client — gọi các phương thức không có tham số hoặc truyền signed: false. Các phương thức mua hàng sẽ trả về dữ liệu ở dạng mở.

  • Nếu xử lý phía server — truyền signed: true. Trong trường hợp này, các phương thức payments.getPurchases()payments.purchase() sẽ trả về dữ liệu ở dạng mã hóa trong tham số signature.

Khởi tạo với tham số mặc định (signed: false).

Cách 1: Đơn giản

YaGames.init() // Hoặc YaGames.init({ signed: false }).
    .then(ysdk => {
        // Các phương thức mua hàng có sẵn trong ysdk.payments.
        console.log(ysdk.payments);
    }).catch(err => {
        // Mua hàng không khả dụng.
    });

Cách 2: Tải trước qua getPayments()

var payments = null;
YaGames.init()
    .then(ysdk => {
        ysdk.getPayments()
            .then(_payments => {
                // Các phương thức mua hàng có sẵn trong payments.
                payments = _payments;
                console.log(payments);
            }).catch(err => {
                // Mua hàng không khả dụng.
            });
    });

Khởi tạo với tham số signed: true.

Cách 1: Khi khởi tạo SDK

YaGames.init({ signed: true }) // Bật chế độ ký tên cho mua hàng.
    .then(ysdk => {
        // Các phương thức mua hàng có sẵn trong ysdk.payments.
        console.log(ysdk.payments);
    }).catch(err => {
        // Mua hàng không khả dụng.
    });

Cách 2: Cấu hình chi tiết qua getPayments()

var payments = null;
YaGames.init()
    .then(ysdk => {
        ysdk.getPayments({ signed: true })
            .then(_payments => {
                // Các phương thức mua hàng có sẵn trong payments.
                payments = _payments;
                console.log(payments);
            }).catch(err => {
                // Mua hàng không khả dụng.
            });
    });

 

Kích hoạt quy trình mua hàng

Để kích hoạt mua hàng trong trò chơi, sử dụng phương thức payments.purchase({ id, developerPayload }).

  • id: string — ID sản phẩm được thiết lập trong Bảng điều khiển trò chơi.
  • developerPayload: string — Tham số tùy chọn. Dữ liệu mua bổ sung mà bạn muốn gửi đến máy chủ của mình (được truyền trong tham số signature ).

Phương thức mở một khung với cổng thanh toán.

Khởi tạo với tham số mặc định (signed: false).

Trả về Promise<Purchase>

Purchase: object — bao gồm chi tiết mua hàng. Đối tượng này chứa các thuộc tính sau:

Khởi tạo với tham số signed: true.

Trả về Promise<{ signature }>.

signature: string — dữ liệu mua hàng đã được mã hóa và chữ ký để kiểm tra tính xác thực của người chơi.

Sau khi người chơi mua hàng thành công, Promise sẽ chuyển sang trạng thái "đã giải quyết". Nếu người chơi không mua hàng và đóng cửa sổ, trạng thái Promise sẽ trở thành "bị từ chối".

Chú ý

Kết nối internet không ổn định có thể khiến trò chơi không đăng ký được các giao dịch mua hàng thành công. Để tránh điều này, hãy sử dụng các phương thức được mô tả trong các phần Kiểm tra các giao dịch mua hàng chưa được xử lýXử lý giao dịch mua hàng và ghi có số tiền trong trò chơi.

Việc không làm theo các hướng dẫn này có thể dẫn đến việc tắt chế độ mua hàng trong ứng dụng hoặc hủy phát hành ứng dụng.

Người dùng có thể thực hiện mua hàng mà không cần đăng nhập, nhưng chúng tôi khuyên bạn nên đề xuất họ đăng nhập vào tài khoản trước hoặc trong khi thực hiện mua hàng.

Ví dụ

Tổng quát:

payments.purchase({ id: 'gold500' })
    .then(purchase => {
        // Mua hàng thành công!
    }).catch(err => {
        // Mua hàng không thành công: không có sản phẩm nào có id này tồn tại trong Bảng điều khiển trò chơi,
        // người dùng không đăng nhập, thay đổi ý định và đóng cửa sổ thanh toán,
        // việc mua hàng đã hết thời gian chờ, không có đủ tiền hoặc vì bất kỳ lý do nào khác.
    });

Sử dụng tham số tùy chọn developerPayload:

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

Lấy danh sách các mặt hàng đã mua

Sử dụng phương thức payments.getPurchases() để:

Khởi tạo với tham số mặc định (signed: false).

Trả về Promise<Purchase[]>.

Purchase[]: array — danh sách các giao dịch mua hàng do người chơi thực hiện. Mỗi Purchase mua sắm có các thuộc tính sau:

Ví dụ

var SHOW_ADS = true;
payments.getPurchases()
    .then(purchases => {
        if (purchases.some(purchase => purchase.productID === 'disable_ads')) {
          SHOW_ADS = false;
        }
    }).catch(err => {
        // Trả về lỗi USER_NOT_AUTHORIZED đối với người dùng chưa đăng nhập.
    });

Khởi tạo với tham số signed: true.

Trả về Promise<{ signature }>.

signature: string — dữ liệu mua hàng đã được mã hóa và chữ ký để kiểm tra tính xác thực của người chơi.

Ví dụ

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

 

Lấy danh mục tất cả các sản phẩm

Để lấy danh sách các mua hàng có sẵn và giá của chúng, hãy sử dụng phương thức payments.getCatalog().

Phương thức trả về Promise<Product[]>.

Product[]: array — danh sách các sản phẩm có sẵn cho người dùng. Danh sách này được tạo từ bảng trong In-app purchases của Bảng điều khiển trò chơi. Mỗi Product chứa các thuộc tính sau:

  • id: string — ID sản phẩm.
  • title: string — tên sản phẩm.
  • description: string — mô tả sản phẩm.
  • imageURI: string — URL hình ảnh.
  • price: string — giá sản phẩm ở định dạng <price> <currency code>.
  • priceValue: string — Giá sản phẩm ở định dạng <price>.
  • priceCurrencyCode: string — Mã tiền tệ.

Product.getPriceCurrencyImage(size) — phương thức lấy địa chỉ của biểu tượng tiền tệ.

  • size: string — tham số tùy chọn. Các giá trị có thể có:

    • small (mặc định) — lấy biểu tượng nhỏ.

    • medium — lấy biểu tượng kích cỡ vừa.

    • svg — lấy biểu tượng dạng vector.

Phương pháp này trả về string — địa chỉ biểu tượng, ví dụ //yastatic.net/s3/games-static/static-data/images/payments/sdk/currency-icon-s@2x.png.

Ví dụ

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

Hiển thị đơn vị tiền tệ của cổng thông tin

Để hiển thị tên và biểu tượng đơn vị tiền tệ của cổng thông tin trong trò chơi, hãy sử dụng các tính năng của SDK:

  • Product.price (string): Giá cùng với mã tiền tệ.
  • Product.priceCurrencyCode (string): Mã tiền tệ.
  • Product.getPriceCurrencyImage() (string): URL của biểu tượng tiền tệ.

Để biết thêm chi tiết về đối tượng Product, hãy xem Lấy danh mục của tất cả các sản phẩm.

Xử lý giao dịch mua hàng và ghi có số tiền trong trò chơi

Có hai loại giao dịch mua hàng: hàng không tiêu hao (chẳng hạn như để tắt quảng cáo) và hàng tiêu hao (chẳng hạn như để mua tiền trong trò chơi).

Để xử lý các giao dịch mua hàng không tiêu hao, hãy sử dụng phương thức payments.getPurchases().

Để xử lý các giao dịch mua hàng tiêu hao, hãy sử dụng phương thức payments.consumePurchase().

payments.consumePurchase()

Phương thức trả về Promise ở trạng thái "đã giải quyết" (nếu quá trình xử lý thành công) hoặc trạng thái "bị từ chối" (nếu xảy ra lỗi).

Chú ý

Sau khi gọi phương thức payments.consumePurchase(), giao dịch mua hàng đã xử lý sẽ bị xóa vĩnh viễn. Để thực hiện việc này, trước tiên bạn nên sửa đổi dữ liệu người chơi thông qua phương thức player.setStats() hoặc player.incrementStats() và sau đó xử lý giao dịch mua hàng.

Ví dụ

payments.purchase({ id: 'gold500' })
    .then(purchase => {
        // Mua hàng thành công!
        // Thêm 500 đồng vàng vào tài khoản và tiêu hao giao dịch mua hàng.
        addGold(500).then(() => payments.consumePurchase(purchase.purchaseToken));
    });

function addGold(value) {
    return player.incrementStats({ gold: value });
    // Để tìm hiểu thêm, hãy xem dữ liệu Player.
}

purchaseToken: string — mã thông báo được trả về từ các phương thức payments.purchase()payments.getPurchases().

Kiểm tra các giao dịch mua hàng chưa được xử lý

Nếu người dùng mất kết nối internet khi mua hàng trong trò chơi hoặc máy chủ của bạn không hoạt đọng, giao dịch mua hàng có thể vẫn chưa được xử lý. Để tránh điều này, hãy kiểm tra các giao dịch mua hàng chưa được xử lý bằng phương thức payments.getPurchases() (ví dụ: mỗi lần trò chơi khởi động).

Chú ý

Trước khi thêm mua sắm, hãy thiết lập kiểm tra các thanh toán chưa xử lý.

Việc kiểm tra này là bắt buộc để vượt qua quá trình kiểm duyệt, vì vậy hãy thiết lập nó ngay cả đối với các giao dịch mua hàng thử nghiệm. Nếu thêm mua sắm vào trò chơi và kiểm thử chúng trước khi thiết lập tiêu thụ, sau các bài kiểm thử có thể còn lại các giao dịch chưa được xử lý, điều này sẽ làm quá trình kiểm duyệt không thể thực hiện được.

Khởi tạo với tham số mặc định (signed: false).

Ví dụ

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

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

Khởi tạo với tham số signed: true.

Ví dụ

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

 

Phòng chống gian lận

Để bảo vệ bản thân khỏi khả năng tăng số liệu trong game một cách không chính thống, hãy xử lý mua hàng trên server:

  1. Khởi tạo YaGames.init() hoặc ysdk.getPayments() với tham số { signed: true }.
  2. Chuyển chữ ký đã nhận trong các phản hồi từ payments.purchase()payments.getPurchases() lên server của bạn, và giải mã nó bằng khóa bí mật.
  3. Trên server của mình, cộng dồn các vật phẩm mà người chơi đã nhận được trong game.
// Đảm bảo rằng các giao dịch mua hàng được khởi tạo bằng tham số { signed: true }

payments.purchase({ id: 'gold500' })
    .then(purchase => {
        // Mua hàng thành công!
        // Thêm 500 đồng vàng trên máy chủ...
        serverPurchase(purchase.signature); // Kiểm tra chữ ký và cộng dữ liệu cho người chơi.
    });

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

Tham số signature của yêu cầu được gửi đến máy chủ chứa dữ liệu mua hàng và chữ ký. Đó là hai chuỗi mã hóa base64: <signature>.<JSON with the purchase data>.

Ví dụ về chữ ký

hQ8adIRJWD29Nep+0P36Z6edI5uzj6F3tddz6Dqgclk=.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImlzc3VlZEF0IjoxNTcxMjMzMzcxLCJyZXF1ZXN0UGF5bG9hZCI6InF3ZSIsImRhdGEiOnsidG9rZW4iOiJkODVhZTBiMS05MTY2LTRmYmItYmIzOC02ZDJhNGNhNDQxNmQiLCJzdGF0dXMiOiJ3YWl0aW5nIiwiZXJyb3JDb2RlIjoiIiwiZXJyb3JEZXNjcmlwdGlvbiI6IiIsInVybCI6Imh0dHBzOi8veWFuZGV4LnJ1L2dhbWVzL3Nkay9wYXltZW50cy90cnVzdC1mYWtlLmh0bWwiLCJwcm9kdWN0Ijp7ImlkIjoibm9hZHMiLCJ0aXRsZSI6ItCR0LXQtyDRgNC10LrQu9Cw0LzRiyIsImRlc2NyaXB0aW9uIjoi0J7RgtC60LvRjtGH0LjRgtGMINGA0LXQutC70LDQvNGDINCyINC40LPRgNC1IiwicHJpY2UiOnsiY29kZSI6IlJVUiIsInZhbHVlIjoiNDkifSwiaW1hZ2VQcmVmaXgiOiJodHRwczovL2F2YXRhcnMubWRzLnlhbmRleC5uZXQvZ2V0LWdhbWVzLzE4OTI5OTUvMmEwMDAwMDE2ZDFjMTcxN2JkN2EwMTQ5Y2NhZGM4NjA3OGExLyJ9fX0=

Ví dụ về dữ liệu mua hàng được truyền đi (ở định dạng JSON)

Lưu ý rằng định dạng dữ liệu của tham số signature được sử dụng trong hàm serverPurchase (signature) khác với định dạng được sử dụng trong phương thức payments.getPurchases().

Trong phương thức payments.getPurchases(), tham số signature chứa một mảng các đối tượng giao dịch mua hàng trong trường data. Trong hàm serverPurchase(signature), đó là đối tượng purchase.

{
  "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": "Không có quảng cáo",
      "description": "Tắt quảng cáo trong trò chơi",
      "price": {
        "code": "YAN",
        "value": "49"
      },
      "imagePrefix": "https://avatars.mds.yandex.net/get-games/1892995/2a0000016d1c1717bd7a0149ccadc86078a1/"
    },
    "developerPayload": "KIỂM TRA PAYLOAD CỦA NHÀ PHÁT TRIỂN"
  }
}

Ví dụ về khóa bí mật

t0p$ecret

Khóa bí mật để xác minh chữ ký là duy nhất cho trò chơi. Khóa được tạo tự động khi tạo giao dịch mua hàng trong Bảng điều khiển trò chơi. Bạn có thể tìm thấy khóa trong bảng giao dịch mua hàng.

Ví dụ về xác minh chữ ký trên máy chủ

import hashlib
import hmac
import base64
import json

usedTokens = {}

key = 't0p$ecret' # Giữ khóa secret.
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('Kiểm tra chữ ký ok!')

  if not purchaseData['data']['token'] in usedTokens:
    usedTokens[purchaseData['data']['token']] = True; # Sử dụng cơ sở dữ liệu.
    print('Kiểm tra chi tiêu gấp đôi ok!')

    print('Áp dụng giao dịch mua hàng:', purchaseData['data']['product'])
    # Bạn có thể áp dụng mua hàng một cách an toàn tại đây.
const crypto = require('crypto');

const usedTokens = {};

const key = 't0p$ecret'; // Giữ khóa secret.
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('Kiểm tra chữ ký ok!');

  if (!usedTokens[purchaseData.data.token]) {
    usedTokens[purchaseData.data.token] = true; // Sử dụng cơ sở dữ liệu.
    console.log('Kiểm tra chi tiêu gấp đôi ok!');

    console.log('Áp dụng mua hàng:', purchaseData.data.product);
    // Bạn có thể áp dụng mua hàng một cách an toàn tại đây.
  }
}

Ghi chú

Nhân viên hỗ trợ sẽ giúp bạn đăng bài trò chơi đã hoàn thiện lên nền tảng trò chơi của Yandex. Để đặt các câu hỏi về việc phát triển và kiểm thử, các nhà phát triển khác sẽ trả lời chuyên sâu trong Kênh Discord.

Nếu bạn đang gặp phải vấn đề hoặc có câu hỏi liên quan đến việc sử dụng Yandex Games SDK, vui lòng liên hệ với bộ phận hỗ trợ:

Viết trong mục trò chuyện

id: string — ID sản phẩm được thiết lập trong Bảng điều khiển trò chơi.

developerPayload: string — tham số tùy chọn. Dữ liệu mua hàng bổ sung mà bạn muốn gửi đến máy chủ của mình (được truyền trong tham số signature).

Tham số signature trong yêu cầu được gửi đến máy chủ chứa dữ liệu mua hàng và chữ ký. Đó là hai chuỗi mã hóa base64: <signature>.<JSON with the purchase data>.

purchaseToken: string — mã thông báo được trả về từ các phương thức payments.purchase()payments.getPurchases().

Truy cập trực tiếp vào ysdk.payments nếu bạn chưa khởi tạo giao dịch mua hàng bằng ysdk.getPayments().