このセクションでは、now.gg決済モジュールを開発環境に統合する方法を説明します。
このドキュメントでは、now.ggの公式サンプルアプリやゲームをベースにした様々なコードサンプルを紹介しています。また、now.gg決済モジュールをよりよく理解し実装するために、本ドキュメント全体を通してインラインリファレンスを確認することができます。
SDKライブラリの追加
まず、決済ライブラリを含むnow.gg SDKパッケージをダウンロードし、以下の手順で開発環境に追加します:
※now.gg SDKパッケージには、決済モジュール、デモアプリ、サンプルコードが含まれています。
1. 圧縮されたnow.gg決済モジュールを展開し、パッケージ内の.aarファイルを見つけます:
2. ゲームの「build.gradle
」ファイルに以下の依存関係を追加します:
dependencies {
implementation fileTree(dir: 'libs', include: ['*.aar'])
}
BillingClientの使用
購入ライフサイクル
now.gg決済の購入ライフサイクルフローは以下の通りです:
- ユーザーが購入可能なプロダクトをリストアップします。
- ユーザーがリストアップされたアプリ内プロダクトを購入するための購入フローを開始します。
- 購入の処理:
- 購入を確認する。
- ユーザーにプロダクト/コンテンツを配信する。
- プロダクトの再購入を可能にするために、購入したプロダクトを消費/配信済みとしてマークする。
用語の理解
now.gg決済は、PurchaseTokenとOrderIDを使用して、ゲームやアプリ内のプロダクトや取引を追跡します。
トークンの購入
- PurchaseTokenは、購入者のプロダクトの所有権/権利を表します。
- 特定のプロダクトと関連するプロダクトSKUに関連付けられています。
- PurchaseTokenは、ユーザーによるアプリ内購入が成功した場合にのみ生成されます。
- 1回限りの購入の場合、PurchaseTokenは取引ごとに生成されます。
- 取引ごとに新しいPurchaseTokenが生成されます。
OrderID
- OrderID は、now.gg課金サービスとの金融取引への参照です。
- OrderIDは購入、払い戻し、または紛争を追跡するための参照として使用されます。
- 固有のOrderIDは、すべての金融取引に対して作成されます。
エンタイトルメント
エンタイトルメントまたはエンタイトルメント付与という用語は、プロダクト購入後のユーザーへの「アプリ内プロダクトの引き渡し」を意味するために使用されます。
エラー処理
アプリ/ゲーム内で発生したエラーと応答は、
課金応答コードを使用して分類されます。
例: 「SERVICE_DISCONNECTED
」エラーコードは、now.gg決済サービスとゲームの接続が切断されていることを示唆しています。now.gg決済サービスとの接続を再確立するには、アプリ/ゲームを再度初期化する必要があります。
1. BillingClientの作成と初期化
BillingClientインターフェースは、now.gg決済モジュールと自身のアプリ/ゲーム間の主要な通信メカニズムとして機能します。一般的な課金操作のための同期および非同期メソッドを提供します。
BillingClient Creation --> Use newBuilder()
Setup Updates of Purchase --> Call setListener()
Receive updates throughout app/game --> PurchasesUpdatedListener
Set PAYMENT_ID for billing --> use setAppId(PAYMENT_ID)
Set IN_GAME_ID to use existing in-game UserID --> use setInGameId(IN_GAME_ID)
now.gg Payments moduleは、ユーザを識別し、トランザクションを開始するために一意のuserIDを必要とします。オプションとして、ユーザーは支払い情報を保存することを選択でき、アプリで将来取引を行う際にこれらの詳細を入力する必要がなくなります。
次のサンプルコードに示すように、BillingClient
を作成し初期化する際に、ユーザ固有のユーザID(IN_GAME_ID
)をnow.gg Payments moduleに渡す必要があります:
private PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() {
@Override
public void onPurchasesUpdated(int billingResult, List<Purchase> purchases) {
// To be implemented in a later section.
}
};
private BillingClient billingClient = BillingClient.newBuilder(activity)
.setListener(purchasesUpdatedListener)
.setAppId(PAYMENT_ID)
.setInGameId(IN_GAME_ID) // unique ID to identify the user.
.build();
private val purchasesUpdatedListener =
PurchasesUpdatedListener { billingResult, purchases ->
// To be implemented in a later section.
}
private var billingClient = BillingClient.newBuilder(activity)
.setListener(purchasesUpdatedListener)
.setAppId(PAYMENT_ID)
.setInGameId(IN_GAME_ID) // unique ID to identify the user.
.build()
重要な情報:
IN_GAME_ID
は、ユーザーの固有識別子です。
IN_GAME_ID
がユーザーごとに固有であることを確認してください。
IN_GAME_ID
が一意であることは重要です。それは、ユーザが購入時に支払い関連情報を保存するように決定した場合、その情報は特定のIN_GAME_IDにリンクされるからです。
PAYMENT_ID
はアプリの固有の識別子です。
- 対応する
PAYMENT_ID
はnowStudioのアプリ詳細セクションで確認することができます。 – 詳細はこちら
PAYMENT_ID
は、nowStudioにアプリ/ゲームを追加した後にのみ使用可能になります。
- 払い戻し処理– ご注文の払い戻し手続きは、こちらの手順に従って行ってください。
参照
2. 接続の確立
now.gg決済サービスは非同期接続プロセスを持っており、クライアントのセットアップ完了時にコールバックをキャプチャするためにBillingClientStateListener
を実装する必要があります。
- now.gg決済サービスとの接続を確立するには、
startConnection()
を呼び出す必要があります。
- 失われた接続を処理するには、再試行ロジックを定義する必要があります。
- 再試行ロジックの実装には、コールバックメソッド
onBillingServiceDisconnected()
をオーバーライドする必要があります。
- コールバックメソッド
onBillingServiceDisconnected()
をオーバーライドしている間、BillingClientがstartConnection()
メソッドを呼び出してnow.gg決済サービスとの接続を再確立することを確認します。
- メソッドを実行する前に、BillingClientとの接続を維持します。
※独自のリトライロジックを書くこともできますが、当社ではリトライロジックも提供していますので、そちらを実装することも可能です。
例
billingClient.startConnection(new BillingClientStateListener() {
@Override
public void onBillingSetupFinished(int billingResult) {
if (billingResult == 0) {
// The BillingClient is ready. You can query purchases here.
}
}
@Override
public void onBillingServiceDisconnected() {
// Try to restart the connection on the next request to
// Payments module by calling the startConnection() method.
}
});
billingClient.startConnection(object: BillingClientStateListener {
override fun onBillingSetupFinished(billingResult: int) {
if (billingResult == 0) {
// The BillingClient is ready. You can query purchases here.
}
}
override fun onBillingServiceDisconnected() {
// Try to restart the connection on the next request to
// Payments module by calling the startConnection() method.
}
});
参照
プロダクトと購入
このセクションでは、アプリ/ゲームで利用可能なアプリ内プロダクトの照会と一覧表示、および購入の処理について説明します。
このフローの手順は以下の通りです:
- 利用可能なプロダクトをリストアップ。
- 購入フローの開始。
- 購入の処理。
始める前に、以下のことを確認してください:
1. 利用可能なプロダクトのリストアップ
ここでの最初のステップは、利用可能なプロダクトをクエリし、ユーザーに一覧表示することです。アプリ内のプロダクト詳細のクエリを実行するには、querySkuDetailsAsync()
を呼び出す必要があります。SKUの詳細をクエリすると、now.gg決済モジュールはローカライズされたプロダクト情報を返し、ユーザーに表示します。
※更新された関連するプロダクト情報が返されるため、アプリ内プロダクトをユーザーに表示する前にSKUの詳細をクエリする必要があります。
SKUTypeと注文の文字列
以下は、now.gg決済に関連する<SKUType
です:
- SkuType.SUBS – サブスクリプション(近日公開予定)
- SkuType.INAPP – アプリ内課金
querySkuDetailsAsync()
を呼び出す際に、SkuDetailsParams
のインスタンスを渡すことを忘れないでください。SkuDetailsParams
は、nowStudioで設定したプロダクトIDとSKUType
を指定します。
結果
利用可能なプロダクトをクエリした後、渡された非同期操作の結果をキャプチャするリスナーを割り当てる必要があります。
以下の例は、SkuDetailsResponseListener
インターフェースの実装を示しており、onSkuDetailsResponse()
をオーバーライドして、クエリの終了時にリスナーに通知できるようにします。
List<String> skuList = new ArrayList<> ();
skuList.add("premium_upgrade");
skuList.add("gas");
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(skuList).setType(SkuType.INAPP);
billingClient.querySkuDetailsAsync(params.build(),
new SkuDetailsResponseListener() {
@Override
public void onSkuDetailsResponse(int billingResult,
List<SkuDetails> skuDetailsList) {
// Process the result.
}
});
fun querySkuDetails() {
val skuList = ArrayList<String>()
skuList.add("premium_upgrade")
skuList.add("gas")
val params = SkuDetailsParams.newBuilder()
params.setSkusList(skuList).setType(SkuType.INAPP)
withContext(Dispatchers.IO) {
billingClient.querySkuDetailsAsync(params.build()) { billingResult, skuDetailsList ->
// Result Processing
}
}
}
追加情報
- ユーザーが消耗品を再購入できるのは、最初の購入注文が完了し、プロダクトがユーザーに届けられた後です。
SkuDetails
オブジェクトは、アプリ内プロダクトの情報を一覧表示するために、様々なメソッドを使用して呼び出すことができます。
- now.gg決済モジュールは、すべてのクエリ結果を
SkuDetails
オブジェクトのリストに保存します。
参照
2. 購入フローの開始
購入可能なプロダクトをリストアップした後、アプリ/ゲームから購入フローを開始する必要があります。
購入フローを開始するには、アプリのメインスレッドから launchBillingFlow()
メソッドを呼び出す必要があります。
ここでは、BillingFlowParams
オブジェクトが参照のために呼び出されます。このオブジェクトには、SkuDetails
オブジェクト内に保存されている関連詳細が含まれています。SkuDetailsオブジェクトは、querySkuDetailsAsync()
を呼び出すことによって取得されます。
BillingFlowParamsオブジェクトの作成
購入フローを開始するには、BillingFlowParams.Builderクラスを使用してBillingFlowParams
オブジェクトを作成する必要があります。以下のコード・スニペットを参考にしてください:
// An activity reference for billing flow initiation
Activity activity = ...;
// Call querySkuDetailsAsync() to retrieve a value for 'skuDetails'.
BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
.setSkuDetails(skuDetails)
.build();
billingClient.launchBillingFlow(activity, billingFlowParams);
//Result
// An activity reference for billing flow initiation
val activity : Activity = ...;
// Call querySkuDetailsAsync() to retrive a value for 'skuDetails'.
val flowParams = BillingFlowParams.newBuilder()
.setSkuDetails(skuDetails)
.build()
billingClient.launchBillingFlow(activity, flowParams)
//Result
参照:
結果の評価
BillingClient.BillingResponseをリターン・レスポンス・コードのリファレンスとして使用できます。
- 応答コードOK (
0
)は、起動が成功したことを示します。
launchBillingFlow()
の呼び出しが成功すると、システムによって購入画面が表示されます。
- now.gg決済モジュールは
onPurchasesUpdated()
を呼び出して、PurchasesUpdatedListener
インターフェースを実装するリスナーに購入結果を伝えます。
- このリスナーは、
setListener()
メソッドを使用してBillingClientを初期化する際に指定します。
次の例は、onPurchasesUpdated()
メソッドをオーバーライドするプロセスの概要です:
@Override
void onPurchasesUpdated(int billingResult, List<Purchase>) {
if (billingResult == 0 && purchases != null) {
for (Purchase purchase : purchases) {
handlePurchase(purchase);
}
} else if (billingResult == 1) {
// Error Handling - Caused due to user terminating the purchase flow
} else {
// Error Handling - Any other causes (based on error code)
}
}
override fun onPurchasesUpdated(billingResult: int, purchases: List<Purchase>?) {
if (billingResult == 0 && purchases != null) {
for (purchase in purchases) {
handlePurchase(purchase)
}
} else if (billingResult == 1) {
// Error Handling - Caused due to the user terminating the purchase flow.
} else {
// Error Handling - Any other causes (based on error code)
}
}
※PurchasesUpdated()
メソッドは、可能性のあるレスポンス・コードを処理するために実装する必要があります。
追加情報
- 購入が成功すると、
PurchaseToken
が生成されます。
PurchaseToken
は、アプリ内で購入したプロダクトと、購入した対応するユーザーを表す固有の識別子でもあります。
OrderID
は、その後の注文ごとに生成され、now.gg決済サービスとの紛争や払い戻しのリファレンスに使用することができます。
OrderID
およびその他の関連情報が記載された購入のコピーは、ユーザーにもメールで送信されます。
参照:
3. 購入の処理
このセクションでは、購入の処理に焦点を当てます。購入フローを開始した後、以下の手順で購入処理を行う必要があります:
- 購入の検証
- エンタイトルメントの付与
- 購入の結果
- 未消費購入の取得
1. 購入の検証
購入を検証することで、購入の真正性を確立することができます。不正行為を減らすためのベストプラクティスは、ユーザーにプロダクトを割り当てる前に、すべての購入を認証することです。
次の2つの方法のいずれかを使用して購入を検証することができます:
- 決済検証APIを使用する。
- パブリックキーを使用する。
A. 決済検証APIの使用
購入を確認する最初の方法は、決済検証APIを使用することです。このAPIを使用して、バックエンドサーバーで購入を検証することができます。
購入を検証するには、アプリのバックエンドサーバーから、次のサンプルリクエストコードに示されているように、PurchaseToken
とOrderID
を指定して決済検証APIを呼び出します。
curl --location 'https://payments.now.gg/v1/console/order/verifyPayment'\
--header 'publisherToken: <your_publisherToken_here>'\
--header 'Content-Type: application/json'\
--data '{
"purchaseToken": "<your_purchaseToken_here>",
"orderId": "<your_orderId_here>"
}'
import requests
import json
url = "https://payments.now.gg/v1/console/order/verifyPayment"
payload = json.dumps(
{
"purchaseToken": "<your_purchaseToken_here>",
"orderId": "<your_orderId_here>",
}
)
headers = {
"publisherToken": "<your_publisherToken_here>",
"Content-Type": "application/json",
}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)
重要な情報
-
- 決済検証APIは、アプリのバックエンドサーバーから呼び出す必要があります。
- 購入が成功すると、PurchaseTokenとOrderIDが返されます。詳細については、以下の参考ドキュメントを参照してください:
B. パブリックキーの使用
購入を検証する 2 番目の方法は、パブリックキーの使用です。パブリックキーを使用して購入を検証する実装は、デモのbillingManager.java
に示されています。
- billingManager.javaを開きます。
verifyValidSignature
関数の場所を確認し、次にverifyPurchase
の場所を確認します。
デモの billingManager.javaにある上記の関数を使用した実装を参照することで、アプリに購入検証を実装できます。
注意:
- こちらに記載されている手順に従って、パブリックキーを生成することができます。
- アプリにバックエンドサーバーがある場合は、バックエンドサーバーで購入を検証することをお勧めします。
- ユーザーにプロダクトのエンタイトルメントを付与する前に、すべての購入を検証することをお勧めします。
2. エンタイトルメントの付与
購入を検証した後、アプリは購入したプロダクトのエンタイトルメントをユーザーに付与し、プロダクトを消費済みとしてマークすることができます。
エンタイトルメントを付与するには、consumeAsync()
メソッドを呼び出します。このメソッドは、ユーザーにプロダクトのエンタイトルメントを付与し、ユーザーに消費/配信されると、プロダクトを再購入できるようにします。
consumeAsync()の使用:
- consumeAsync()にPurchaseTokenを含めると、購入したプロダクトのステータスが消費済みに更新され、now.gg課金サービスが再購入できるようになります。
- 消費操作の結果を更新するには、
ConsumeResponseListener
インターフェースを実装するオブジェクトを渡す必要があります。
- 消費操作が完了すると、now.gg課金サービスは
onConsumeResponse()
を呼び出します。
重要な情報
- PurchaseTokenを使用した同じ購入に対する複数のエンタイトルメント付与をチェックする必要があります。これは、消費要求が失敗し、
ConsumeResponseListener
インターフェースを更新するオブジェクトが更新されない場合に発生する可能性があります。
次のコード・スニペットは、購入検証と消費操作を示しています:
void handlePurchase(Purchase purchase) {
// Purchase retrieved from BillingClient#queryPurchases or your PurchasesUpdatedListener.
Purchase purchase = ...;
// Verify the purchase.
// Ensure entitlement was not already granted for this purchaseToken.
// Grant the entitlement to the user.
ConsumeResponseListener listener = new ConsumeResponseListener() {
@Override
public void onConsumeResponse(int billingResult, String purchaseToken) {
if (billingResult == 0) {
// Handle the success of the consume operation.
}
}
};
billingClient.consumeAsync(purchase.getPurchaseToken(), listener);
}
fun handlePurchase(purchase: Purchase) {
// Purchase retrieved from BillingClient#queryPurchases or your PurchasesUpdatedListener.
val purchase: Purchase = ...;
// Verify the purchase.
// Ensure entitlement was not already granted for this purchaseToken.
// Grant the entitlement to the user.
billingClient.consumeAsync(purchase.getPurchaseToken(), {
billingResult,
outToken - >
if (billingResult == 0) {
// Handle the success of the consume operation.
}
})
}
参照:
3. 購入結果
PurchasesUpdatedListener
は購入結果を提供します。しかし、シナリオによっては、アプリ/ゲームはユーザーが行った成功済みの購入の全てで更新されない場合があります。
これらのシナリオには、以下のようなものがあります:
- 複数のデバイス
このシナリオでは、ユーザーが1つのデバイスでプロダクトを購入し、購入したプロダクトが更新されていない別のデバイスで使用したい場合です。
- 接続/ネットワーク損失
接続/ネットワークの損失により、アプリ/ゲームは PurchasesUpdatedListener
から正常な購入通知を受信できない場合があります。
- アプリ外での購入
このシナリオでは、アプリ/ゲームの外で行われた購入は、ユーザーが使用できるように処理する必要があります。
上記のシナリオに関連する購入を正常に処理する方法:
onResume()
メソッドとonCreate()
メソッドで、アプリがBillingClient.queryPurchases(SkuType.INAPP)
を呼び出すことを確認してください。
4. 未消費購入の取得
以下のように、queryPurchasesメソッドを使用すると、ユーザーの未消費購入リストを返すことができます:
Purchase.PurchasesResult result = BillingClient.queryPurchases(BillingClient.SkuType.INAPP);
if (result.getResponseCode() == BillingClient.BillingResponse.OK) {
List < Purchase > purchases = result.getPurchasesList();
}
val result = BillingClient.queryPurchases(BillingClient.SkuType.INAPP)
if (result.responseCode == BillingClient.BillingResponse.OK) {
val purchases = result.purchasesList
}