このセクションでは、now.gg決済モジュールを開発環境に統合する方法を説明します。
このドキュメントでは、now.ggの公式サンプルアプリやゲームをベースにした様々なコードサンプルを紹介しています。また、now.gg決済モジュールをよりよく理解し実装するために、本ドキュメント全体を通してインラインリファレンスを確認することができます。
1. 圧縮されたnow.gg決済モジュールを展開し、パッケージ内の.aarファイルを見つけます:
Payments.aar
2. ゲームの「build.gradle
」ファイルに以下の依存関係を追加します:
dependencies { implementation fileTree(dir: 'libs', include: ['*.aar']) }
now.gg決済の購入ライフサイクルフローは以下の通りです:
now.gg決済は、PurchaseTokenとOrderIDを使用して、ゲームやアプリ内のプロダクトや取引を追跡します。
エンタイトルメントまたはエンタイトルメント付与という用語は、プロダクト購入後のユーザーへの「アプリ内プロダクトの引き渡し」を意味するために使用されます。
SERVICE_DISCONNECTED
」エラーコードは、now.gg決済サービスとゲームの接続が切断されていることを示唆しています。now.gg決済サービスとの接続を再確立するには、アプリ/ゲームを再度初期化する必要があります。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) Query unconsumed purchases after initialization --> use queryPurchasesAsync()
以下のサンプルは、BillingClient
を作成および初期化する方法を示しています:
private PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() { @Override public void onPurchasesUpdated(int billingResult, List<Purchase> purchases) { if (billingResult == BillingResponse.OK) { if (purchases == null) { return; } for (Purchase purchase: purchases) { String payload = purchase.getDeveloperPayload(); // 開発者ペイロードを取得する // ここで購入成功を処理する } } else if (billingResult == BillingResponse.USER_CANCELED) { Log.i(TAG, "onPurchasesUpdated() - user cancelled the purchase flow - skipping"); } else { Log.w(TAG, "onPurchasesUpdated() got unknown billingResult: " + billingResult); } } }; private BillingClient billingClient = BillingClient.newBuilder(activity) .setListener(purchasesUpdatedListener) .setAppId(PAYMENT_ID) .build();
private val purchasesUpdatedListener = object : PurchasesUpdatedListener { override fun onPurchasesUpdated(billingResult: Int, purchases: List<Purchase>?) { if (billingResult == BillingResponse.OK) { if (purchases == null) { return } for (purchase in purchases) { val payload = purchase.developerPayload // 開発者ペイロードを取得する // ここで購入成功を処理する } } else if (billingResult == BillingResponse.USER_CANCELED) { Log.i(TAG, "onPurchasesUpdated() - user cancelled the purchase flow - skipping") } else { Log.w(TAG, "onPurchasesUpdated() got unknown billingResult: $billingResult") } } } private val billingClient = BillingClient.newBuilder(activity) .setListener(purchasesUpdatedListener) .setAppId(PAYMENT_ID) .build()
PAYMENT_ID
はアプリの固有の識別子です。
PAYMENT_ID
はnowStudioのアプリ詳細セクションで確認することができます。 – 詳細はこちらPAYMENT_ID
は、nowStudioにアプリ/ゲームを追加した後にのみ使用可能になります。now.gg決済サービスは非同期接続プロセスを持っており、クライアントのセットアップ完了時にコールバックをキャプチャするためにBillingClientStateListener
を実装する必要があります。
startConnection()
を呼び出す必要があります。startConnection()
を一度だけ呼び出すようにしてください。queryPurchasesAsync()
メソッドを使用して、未消費の購入分をクエリし、ユーザの以前のゲームセッションで未消費の購入分が適切にそのユーザに割り当てられるようにする必要があります。
※独自のリトライロジックを書くこともできますが、当社ではリトライロジックも提供していますので、そちらを実装することも可能です。
例
billingClient.startConnection(new BillingClientStateListener() { @Override public void onBillingSetupFinished(int billingResult) { if (billingResult == 0) { // BillingClientの準備ができました。ここで購入をクエリできます。 QueryPurchasesParams params = QueryPurchasesParams.newBuilder().setProductType(BillingClient.SkuType.ALL).build(); billingClient.queryPurchasesAsync(params, new PurchasesResponseListener() { @Override public void onQueryPurchasesResponse(BillingResult billingResult, List < Purchase > list) { // 購入したアイテムをユーザーに割り当て、消費/確認を呼び出してください。 } }); } } @Override public void onBillingServiceDisconnected() { // 次のリクエストで支払いモジュールへの接続を再起動するには、startConnection() メソッドを呼び出してください。 } });
billingClient.startConnection(object: BillingClientStateListener { override fun onBillingSetupFinished(billingResult: int) { if (billingResult == 0) { // BillingClientの準備ができました。ここで購入をクエリできます。 val params = QueryPurchasesParams.newBuilder() .setProductType(BillingClient.SkuType.ALL) .build() billingClient.queryPurchasesAsync(params, object: PurchasesResponseListener { override fun onQueryPurchasesResponse(billingResult: BillingResult, list: List) { // 購入したアイテムをユーザーに割り当て、消費/確認を呼び出してください。 } }) } } override fun onBillingServiceDisconnected() { // 次のリクエストで支払いモジュールへの接続を再起動するには、startConnection() メソッドを呼び出してください。 } });
このセクションでは、アプリ/ゲームで利用可能なアプリ内プロダクトの照会と一覧表示、および購入の処理について説明します。
このフローの手順は以下の通りです:
始める前に、以下のことを確認してください:
ここでの最初のステップは、利用可能なプロダクトをクエリし、ユーザーに一覧表示することです。アプリ内のプロダクト詳細のクエリを実行するには、querySkuDetailsAsync()
を呼び出す必要があります。SKUの詳細をクエリすると、now.gg決済モジュールはローカライズされたプロダクト情報を返し、ユーザーに表示します。
※更新された関連するプロダクト情報が返されるため、アプリ内プロダクトをユーザーに表示する前にSKUの詳細をクエリする必要があります。
SKUTypeと注文の文字列
以下は、now.gg決済に関連する<SKUType
です:
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.ALL); 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.ALL) withContext(Dispatchers.IO) { billingClient.querySkuDetailsAsync(params.build()) { billingResult, skuDetailsList -> // Result Processing } } }
SkuDetails
オブジェクトは、アプリ内プロダクトの情報を一覧表示するために、様々なメソッドを使用して呼び出すことができます。SkuDetails
オブジェクトのリストに保存します。購入可能なプロダクトをリストアップした後、アプリ/ゲームから購入フローを開始する必要があります。
購入フローを開始するには、アプリのメインスレッドから launchBillingFlow()
メソッドを呼び出す必要があります。
ここでは、BillingFlowParams
オブジェクトが参照のために呼び出されます。このオブジェクトには、SkuDetails
オブジェクト内に保存されている関連詳細が含まれています。SkuDetailsオブジェクトは、querySkuDetailsAsync()
を呼び出すことによって取得されます。
購入フローを開始するには、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) .setDeveloperPayload("任意の開発者ペイロード") .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) .setDeveloperPayload("任意の開発者ペイロード") .build() billingClient.launchBillingFlow(activity, flowParams) //Result
BillingClient.BillingResponseをリターン・レスポンス・コードのリファレンスとして使用できます。
0
)は、起動が成功したことを示します。launchBillingFlow()
の呼び出しが成功すると、システムによって購入画面が表示されます。onPurchasesUpdated()
を呼び出して、PurchasesUpdatedListener
インターフェースを実装するリスナーに購入結果を伝えます。setListener()
メソッドを使用してBillingClientを初期化する際に指定します。次の例は、onPurchasesUpdated()
メソッドをオーバーライドするプロセスの概要です:
@Override void onPurchasesUpdated(int billingResult, List<Purchase>) { if (billingResult == 0 && purchases != null) { for (Purchase purchase : purchases) { String payload = purchase.getDeveloperPayload(); // get developer payload handlePurchase(purchase); } } else if (billingResult == 1) { // エラーハンドリング - ユーザーが購入フローを終了したことによる原因 } else { // エラーハンドリング - その他の原因(エラーコードに基づく) } }
override fun onPurchasesUpdated(billingResult: int, purchases: List<Purchase>?) { if (billingResult == 0 && purchases != null) { for (purchase in purchases) { val payload: String = purchase.developerPayload // get developer payload handlePurchase(purchase) } } else if (billingResult == 1) { // エラーハンドリング - ユーザーが購入フローを終了したことによる原因 } else { // エラーハンドリング - その他の原因(エラーコードに基づく) } }
※PurchasesUpdated()
メソッドは、可能性のあるレスポンス・コードを処理するために実装する必要があります。
PurchaseToken
が生成されます。
PurchaseToken
は、アプリ内で購入したプロダクトと、購入した対応するユーザーを表す固有の識別子でもあります。OrderID
は、その後の注文ごとに生成され、now.gg決済サービスとの紛争や払い戻しのリファレンスに使用することができます。OrderID
およびその他の関連情報が記載された購入のコピーは、ユーザーにもメールで送信されます。このセクションでは、購入の処理に焦点を当てます。購入フローを開始した後、以下の手順で購入処理を行う必要があります:
次の2つの方法のいずれかを使用して購入を検証することができます:
購入を確認するための最初の方法は、verifyPurchase API を使用することです。この API を使用して、私たちのバックエンドサーバーで購入を確認できます。
購入を確認するには、アプリのバックエンドサーバーから purchaseToken
を使用して verifyPurchase API を呼び出してください。以下のサンプルリクエストコードで説明されています:
import requests url = "https://payments-api.now.gg/v2/sellers/order/verifyPurchase" headers = { "Authorization": "<payment_api_key_here>", "Content-Type": "application/x-www-form-urlencoded" } data = { "purchaseToken": "<purchase_token_here>", "productId": "<product_id_here>", // マルチストアにのみ必要 "currency": "<currency>", // マルチストアにのみ必要 "type": "<store>" # オプション: xiaomi, onestore, amazon, huawei. 存在しない場合、フォールバック値は nowgg になります。 } response = requests.post(url, headers=headers, data=data) print("レスポンスステータスコード:", response.status_code) print("レスポンスボディ:", response.text)
curl --location --request POST 'https://payments-api.now.gg/v2/sellers/order/verifyPurchase' \ --header 'Authorization: <payment_api_key_here>' \ --header 'Content-Type': 'application/x-www-form-urlencoded' \ --data '{ "purchaseToken": "<purchase_token_here>", "productId": "<product_id_here>", // マルチストアにのみ必要 "currency": "<currency>", // マルチストアにのみ必要 "type": "<store>" // xiaomi | onestore | amazon | huawei 存在しない場合、フォールバック値は nowgg になります。 }'
購入を検証する 2 番目の方法は、パブリックキーの使用です。パブリックキーを使用して購入を検証する実装は、デモのbillingManager.java
に示されています。
verifyValidSignature
関数の場所を確認し、次にverifyPurchase
の場所を確認します。デモの billingManager.javaにある上記の関数を使用した実装を参照することで、アプリに購入検証を実装できます。
注意:
購入を確認した後、アプリは購入した商品の権利をユーザーに付与し、その商品を消費済み(消耗品の場合)または承認済み(サブスクリプションの場合)としてマークすることができます。
以下の方法は、ユーザーに購入権利を割り当てる方法を示しています:
購入を消費するために使用できる2つの方法を提供しています:
購入を承認するために使用できる2つの方法を提供しています:
購入したプロダクトを消費するための2つの方法を提供しています:
注意: バックエンドサーバを使用しないクライアントのみのアプリの場合は、このメソッドを使用する必要があります。
エンタイトルメントを付与するには、consumeAsync()
メソッドを呼び出します。このメソッドは、ユーザーにプロダクトのエンタイトルメントを付与し、ユーザーに消費/配信されると、プロダクトを再購入できるようにします。
consumeAsync()の使用:
ConsumeResponseListener
インターフェースを実装するオブジェクトを渡す必要があります。onConsumeResponse()
を呼び出します。ConsumeResponseListener
インターフェースを更新するオブジェクトが更新されない場合に発生する可能性があります。次のコード・スニペットは、購入検証と消費操作を示しています:
void handlePurchase(Purchase purchase) { // Purchase retrieved from BillingClient#queryPurchasesAsync 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#queryPurchasesAsync 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. } }) }
consumePurchase
APIは、バックエンドサーバを持つアプリのサーバサイドでの購入消費に使用する必要があります。
購入を確認した後、アプリは購入した商品の権利をユーザに付与し、商品を消費済みとして処理することができます。
消費購入には以下が含まれます。:
以下のサンプルコードはconsumePurchase
API を使用した関連リクエストの例です。
import requests url = "https://payments-api.now.gg/v2/order/consumePurchase" headers = { "Authorization": "<payment_api_key_here>", "Content-Type": "application/x-www-form-urlencoded" } data = { "purchaseToken": "<purchase_token_here>", "productId": "<product_id_here>", // マルチストアにのみ必要 "developerPayload": "developerPayload", // オプション // マルチストアにのみ必要 "currency": "<currency>", // マルチストアにのみ必要 "type": "<store>" // xiaomi, onestore. 存在しない場合、フォールバック値は nowgg になります。 } response = requests.post(url, headers=headers, data=data) print("レスポンスステータスコード:", response.status_code) print("レスポンスボディ:", response.text)
curl --location --request POST 'https://payments-api.now.gg/v2/order/consumePurchase' \ --header 'Authorization: <payment_api_key_here>' \ --header 'Content-Type': 'application/x-www-form-urlencoded' \ --data '{ "purchaseToken": "<purchase_token_here>", "productId": "<product_id_here>", // マルチストアにのみ必要 "developerPayload" : developerPayload, // オプション, // マルチストアにのみ必要 "currency": "<currency>", // マルチストアにのみ必要 "type": "<store>" // xiaomi | onestore 存在しない場合、フォールバック値は nowgg になります。 }'
注意:
consumePurchase
APIを呼び出す前にユーザーに割り当ててください。Payment API Key
はnowStudioのクレデンシャルセクションで確認することができます。 詳細はこちら購入を承認するために使用できる2つのメソッドを提供しました:
注意: バックエンドサーバーを持たないクライアント専用のアプリの場合、この方法を使用する必要があります。
方法:
acknowledgePurchase()
メソッドを呼び出します。
acknowledgePurchase()
に購入トークンを含めてacknowledgePurchase() を呼び出し、購入した製品のステータスを acknowledged
に更新します。これにより、now.ggの課金サービスが再購入可能にします。onAcknowledgePurchaseResponse()
を呼び出します。このメソッドはオーバーライド可能です。重要な情報:
AcknowledgePurchaseResponseListener
インタフェースを更新しない場合、このような状況が発生する可能性があります。以下のコードスニペットは、購入の検証と承認操作を示しています:
void handlePurchase(Purchase purchase) { // Purchase retrieved from BillingClient#queryPurchasesAsync or your PurchasesUpdatedListener. Purchase purchase = ...; // Verify the purchase. // Ensure entitlement was not already granted for this purchaseToken. // Grant the entitlement to the user. // Acknowledge Subscription final AcknowledgePurchaseResponseListener acknowledgePurchaseResponseListener = new AcknowledgePurchaseResponseListener() { @Override public void onAcknowledgePurchaseResponse(@BillingResponse int responseCode) { if (responseCode == 0) { // Handle the success of the acknowledgement operation. } } }; AcknowledgePurchaseParams acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder() .setPurchaseToken(purchaseToken) .build(); BillingClient.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener); }
fun handlePurchase(purchase: Purchase) { // Purchase retrieved from BillingClient#queryPurchasesAsync or your PurchasesUpdatedListener. val purchase: Purchase = ...; // Verify the purchase. // Ensure entitlement was not already granted for this purchaseToken. // Grant the entitlement to the user. // Acknowledge Subscription val acknowledgePurchaseResponseListener = object : AcknowledgePurchaseResponseListener { override fun onAcknowledgePurchaseResponse(@BillingResponse responseCode: Int) { if (responseCode == 0) { // Handle the success of the acknowledgement operation. } } val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder() .setPurchaseToken(purchaseToken) .build() BillingClient.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener) }
acknowledgePurchase
API は、バックエンドサーバーを持つアプリのためのサーバーサイド購入承認に使用する必要があります。
購入を確認した後、承認済みとしてマークする必要があります。
以下のサンプルコードは、acknowledgePurchase
API を使用した関連するリクエストを示しています。
import requests url = "https://payments-api.now.gg/v2/seller/order/acknowledgepurchase" payload = 'purchaseToken=nowgg-_purchase_token' headers = { 'Authorization': 'payment_api_key_here', 'Content-Type': 'application/x-www-form-urlencoded' } response = requests.request("POST", url, headers=headers, data=payload) print(response.text)
参照:
Payment API Key
はnowStudioのクレデンシャルセクションで確認することができます。 詳細はこちらPurchasesUpdatedListener
は購入結果を提供します。しかし、シナリオによっては、アプリ/ゲームはユーザーが行った成功済みの購入の全てで更新されない場合があります。
これらのシナリオには、以下のようなものがあります:
PurchasesUpdatedListener
から正常な購入通知を受信できない場合があります。上記のシナリオに関連する購入を正常に処理する方法:
onResume()
メソッドとonCreate()
メソッドで、アプリがBillingClient.queryPurchasesAsync(SkuType.ALL)
を呼び出すことを確認してください。以下のセクションでは、未消費のアプリ内購入およびサブスクリプションを確認し、割り当てるプロセスを説明します。
以下のように、queryPurchasesAsyncメソッドを使用すると、ユーザーの未消費購入リストを返すことができます:
QueryPurchasesParams params = QueryPurchasesParams.newBuilder().setProductType(BillingClient.SkuType.ALL).build(); billingClient.queryPurchasesAsync(params, new PurchasesResponseListener() { @Override public void onQueryPurchasesResponse(BillingResult billingResult, List < Purchase > list) { // 購入したアイテムをユーザーに割り当て、消費/確認を呼び出してください。 } });
val params = QueryPurchasesParams.newBuilder() .setProductType(BillingClient.SkuType.ALL) .build() billingClient.queryPurchasesAsync(params, object : PurchasesResponseListener { override fun onQueryPurchasesResponse(billingResult: BillingResult, list: List) { // 購入したアイテムをユーザーに割り当て、消費/確認を呼び出してください。 } })
queryPurchasesAsync()
メソッドを使用して、未消費の購入分をクエリし、ユーザの以前のゲームセッションで未消費の購入分が適切にそのユーザに割り当てられるようにする必要があります。
このセクションでは、SubscriptionStatusCallback
API を提供し、サブスクリプションの状態更新に関する通知を送信するための API 仕様を示しています。
以下は関連するワークフローです:
ドキュメント改訂版 1.0