Google Play Billing Library をアプリに統合する

このトピックでは、Google Play Billing Library をアプリに統合してアイテムの販売を開始する方法について説明します。

購入ライフサイクル

1 回限りの購入または定期購入の一般的な購入フローは次のとおりです。

  1. 購入できるアイテムをユーザーに表示します。
  2. ユーザーが実際に購入を選択するまでの購入フローを起動します。
  3. サーバーで購入を確認します。
  4. ユーザーにコンテンツを提供します。
  5. コンテンツの配信を承認します。消費可能アイテムの場合は、その購入アイテムを消費し、ユーザーがアイテムを再購入できるようにします。

定期購入は、ユーザーがキャンセルするまで自動的に更新されます。定期購入には次のステータスがあります。

  • 有効: ユーザーは良好な状態にあり、問題なく定期購入を利用できます。
  • キャンセル済み: ユーザーは定期購入をキャンセルしましたが、期限切れになるまで利用できます。
  • 猶予期間内: 支払いに関する問題が発生しましたが、Google がお支払い方法を再試行している間、ユーザーは定期購入を利用できます。
  • 保留中: 支払いに関する問題が発生し、Google がお支払い方法を再試行している間、ユーザーが定期購入を利用できなくなっている状態です。
  • 一時停止中: ユーザーは定期購入の利用を一時停止し、再開するまで利用できない状態です。
  • 期限切れ: ユーザーは定期購入をキャンセルし、利用できなくなりました。期限切れになると、ユーザーは解約したものと見なされます。

Google Play への接続を初期化する

Google Play の課金システムを統合するための最初のステップは、アプリに Google Play Billing Library を追加して接続を初期化することです。

Google Play Billing Library への依存関係を追加する

次に示すように、Google Play Billing Library への依存関係をアプリの build.gradle ファイルに追加します。

Groovy

dependencies {
    def billing_version = "7.0.0"

    implementation "com.android.billingclient:billing:$billing_version"
}

Kotlin

dependencies {
    val billing_version = "7.0.0"

    implementation("com.android.billingclient:billing:$billing_version")
}

Kotlin を使用している場合、Google Play Billing Library の KTX モジュールに Kotlin 拡張機能とコルーチンのサポートが含まれているため、Google Play Billing Library を使用する際に慣用的な Kotlin コードを作成できます。これらの拡張機能をプロジェクトに含めるには、アプリの build.gradle ファイルに次の依存関係を追加します。

Groovy

dependencies {
    def billing_version = "7.0.0"

    implementation "com.android.billingclient:billing-ktx:$billing_version"
}

Kotlin

dependencies {
    val billing_version = "7.0.0"

    implementation("com.android.billingclient:billing-ktx:$billing_version")
}

BillingClient を初期化する

Google Play Billing Library への依存関係を追加したら、BillingClient インスタンスを初期化する必要があります。BillingClient は、Google Play Billing Library とアプリの他の部分の通信に使用されるメイン インターフェースです。BillingClient は、多くの一般的な請求処理に役立つコンビニエンス メソッド(同期メソッドと非同期メソッドの両方)を提供します。次の点にご注意ください。

  • 1 つのイベントに対する複数の PurchasesUpdatedListener コールバックを回避するため、一度に開くアクティブな BillingClient 接続は 1 つにすることをおすすめします。
  • アプリの購入がタイムリーに処理されるように、アプリの起動時またはフォアグラウンドに表示されたときに BillingClient の接続を開始することをおすすめします。これは、registerActivityLifecycleCallbacks によって登録された ActivityLifecycleCallbacks を使用して、onActivityResumed をリッスンし、アクティビティが再開されたことを初めて検出したときに接続を初期化することで実現できます。このベスト プラクティスに従う必要がある理由について詳しくは、購入処理のセクションをご覧ください。また、アプリを閉じるときは接続を終了してください。

BillingClient を作成するには、newBuilder を使用します。任意のコンテキストを newBuilder() に渡すと、BillingClient がそれを使用してアプリケーションのコンテキストを取得します。つまり、メモリリークを心配する必要はありません。また、購入に関する最新情報を受け取るには、setListener を呼び出して PurchasesUpdatedListener への参照を渡す必要があります。このリスナーは、すべてのアプリ内購入に関する最新情報を受信します。

Kotlin

private val purchasesUpdatedListener =
   PurchasesUpdatedListener { billingResult, purchases ->
       // To be implemented in a later section.
   }

private var billingClient = BillingClient.newBuilder(context)
   .setListener(purchasesUpdatedListener)
   // Configure other settings.
   .build()

Java

private PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() {
    @Override
    public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
        // To be implemented in a later section.
    }
};

private BillingClient billingClient = BillingClient.newBuilder(context)
    .setListener(purchasesUpdatedListener)
    // Configure other settings.
    .build();

Google Play に接続する

BillingClient を作成したら、Google Play との接続を確立する必要があります。

Google Play に接続するには、startConnection を呼び出します。接続プロセスは非同期です。クライアントのセットアップが完了してリクエストを送信する準備ができたら、コールバックを受け取るために BillingClientStateListener を実装する必要があります。

また、Google Play への接続の切断を処理するための再試行ロジックも実装する必要があります。再試行ロジックを実装するには、onBillingServiceDisconnected() コールバック メソッドをオーバーライドし、後続のリクエストを送信する前に BillingClientstartConnection() メソッドを呼び出して Google Play に再接続するようにします。

次の例は、接続を開始して使用可能かどうかをテストする方法を示しています。

Kotlin

billingClient.startConnection(object : BillingClientStateListener {
    override fun onBillingSetupFinished(billingResult: BillingResult) {
        if (billingResult.responseCode ==  BillingResponseCode.OK) {
            // The BillingClient is ready. You can query purchases here.
        }
    }
    override fun onBillingServiceDisconnected() {
        // Try to restart the connection on the next request to
        // Google Play by calling the startConnection() method.
    }
})

Java

billingClient.startConnection(new BillingClientStateListener() {
    @Override
    public void onBillingSetupFinished(BillingResult billingResult) {
        if (billingResult.getResponseCode() ==  BillingResponseCode.OK) {
            // The BillingClient is ready. You can query purchases here.
        }
    }
    @Override
    public void onBillingServiceDisconnected() {
        // Try to restart the connection on the next request to
        // Google Play by calling the startConnection() method.
    }
});

購入可能なアイテムを表示する

Google Play への接続を確立したら、購入可能なアイテムをクエリしてユーザーに表示できます。

商品の詳細のクエリは、ローカライズされた商品情報を返すので、ユーザーにアイテムを表示する前の重要なステップとなります。定期購入の場合は、アイテムの表示がすべての Play ポリシーに従っていることを確認します。

アプリ内アイテムの詳細をクエリするには、queryProductDetailsAsync を呼び出します。

非同期操作の結果を処理するには、ProductDetailsResponseListener インターフェースを実装するリスナーも指定する必要があります。次の例に示すように、クエリの終了時にリスナーに通知する onProductDetailsResponse をオーバーライドできます。

Kotlin

val queryProductDetailsParams =
    QueryProductDetailsParams.newBuilder()
        .setProductList(
            ImmutableList.of(
                Product.newBuilder()
                    .setProductId("product_id_example")
                    .setProductType(ProductType.SUBS)
                    .build()))
        .build()

billingClient.queryProductDetailsAsync(queryProductDetailsParams) {
    billingResult,
    productDetailsList ->
      // check billingResult
      // process returned productDetailsList
}

Java

QueryProductDetailsParams queryProductDetailsParams =
    QueryProductDetailsParams.newBuilder()
        .setProductList(
            ImmutableList.of(
                Product.newBuilder()
                    .setProductId("product_id_example")
                    .setProductType(ProductType.SUBS)
                    .build()))
        .build();

billingClient.queryProductDetailsAsync(
    queryProductDetailsParams,
    new ProductDetailsResponseListener() {
        public void onProductDetailsResponse(BillingResult billingResult,
                List<ProductDetails> productDetailsList) {
            // check billingResult
            // process returned productDetailsList
        }
    }
)

商品の詳細をクエリするときは、ProductType と一緒に、Google Play Console で作成された商品 ID 文字列のリストを指定する QueryProductDetailsParams のインスタンスを渡します。ProductType には、1 回限りのアイテムの場合は ProductType.INAPP、定期購入の場合は ProductType.SUBS を指定できます。

Kotlin 拡張機能によるクエリ

Kotlin 拡張機能を使用している場合は、queryProductDetails() 拡張関数を呼び出すことでアプリ内アイテムの詳細をクエリできます。

queryProductDetails() は Kotlin コルーチンを使用するため、リスナーを別に定義する必要はありません。関数はクエリが完了するまで一時停止し、その後結果を処理できます。

suspend fun processPurchases() {
    val productList = listOf(
        QueryProductDetailsParams.Product.newBuilder()
            .setProductId("product_id_example")
            .setProductType(BillingClient.ProductType.SUBS)
            .build()
    )
    val params = QueryProductDetailsParams.newBuilder()
    params.setProductList(productList)

    // leverage queryProductDetails Kotlin extension function
    val productDetailsResult = withContext(Dispatchers.IO) {
        billingClient.queryProductDetails(params.build())
    }

    // Process the result.
}

まれに、一部のデバイスでは Google Play 開発者サービスのバージョンが古いことが原因で、ProductDetailsqueryProductDetailsAsync() に対応していない場合があります。適切にこのシナリオに対応できるようにするには、Play Billing Library 5 移行ガイドで、下位互換性機能の使い方を確認してください。

結果を処理する

Google Play Billing Library は、ProductDetails オブジェクトの List にクエリ結果を保存します。したがって、デベロッパーは、リスト内の各 ProductDetails オブジェクトのさまざまなメソッドを呼び出して、アプリ内アイテムに関する価格や説明などの情報を参照できます。購入可能なアイテムの詳細情報を参照するには、ProductDetails クラスのメソッドのリストをご覧ください。

販売するアイテムを提示する前に、ユーザーがまだそのアイテムを所有していないことを確認します。ユーザーのアイテム ライブラリに消費型アイテムがまだある場合、ユーザーはそのアイテムを消費しない限り再購入できません。

定期購入を提示する前に、ユーザーがまだ定期購入を登録していないことを確認します。 また、次の点に注意してください。

  • queryProductDetailsAsync() は、定期購入アイテムの詳細と、購読ごとに最大 50 件のオファーを返します。
  • queryProductDetailsAsync() は、ユーザーが対象となるオファーのみを返します。対象ではないオファーをユーザーが購入しようとした場合(たとえば、対象となるオファーのリストが古い場合)は、対象外であることを Play がユーザーに通知します。ユーザーは代わりに基本プランを購入することもできます。

購入フローを起動する

アプリから購入リクエストを開始するには、アプリのメインスレッドから launchBillingFlow() メソッドを呼び出します。このメソッドは、queryProductDetailsAsync の呼び出しから取得された関連する ProductDetails オブジェクトを含む BillingFlowParams オブジェクトへの参照を受け取ります。BillingFlowParams オブジェクトを作成するには、BillingFlowParams.Builder クラスを使用します。

Kotlin

// An activity reference from which the billing flow will be launched.
val activity : Activity = ...;

val productDetailsParamsList = listOf(
    BillingFlowParams.ProductDetailsParams.newBuilder()
        // retrieve a value for "productDetails" by calling queryProductDetailsAsync()
        .setProductDetails(productDetails)
        // For One-time products, "setOfferToken" method shouldn't be called.
        // For subscriptions, to get an offer token, call ProductDetails.subscriptionOfferDetails()
        // for a list of offers that are available to the user
        .setOfferToken(selectedOfferToken)
        .build()
)

val billingFlowParams = BillingFlowParams.newBuilder()
    .setProductDetailsParamsList(productDetailsParamsList)
    .build()

// Launch the billing flow
val billingResult = billingClient.launchBillingFlow(activity, billingFlowParams)

Java

// An activity reference from which the billing flow will be launched.
Activity activity = ...;

ImmutableList<ProductDetailsParams> productDetailsParamsList =
    ImmutableList.of(
        ProductDetailsParams.newBuilder()
             // retrieve a value for "productDetails" by calling queryProductDetailsAsync()
            .setProductDetails(productDetails)
            // For one-time products, "setOfferToken" method shouldn't be called.
            // For subscriptions, to get an offer token, call
            // ProductDetails.subscriptionOfferDetails() for a list of offers
            // that are available to the user.
            .setOfferToken(selectedOfferToken)
            .build()
    );

BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
    .setProductDetailsParamsList(productDetailsParamsList)
    .build();

// Launch the billing flow
BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams);

launchBillingFlow() メソッドは、BillingClient.BillingResponseCode にリストされているレスポンス コードのいずれかを返します。この結果をチェックして、購入フローの起動時にエラーが発生していないことを確認してください。BillingResponseCodeOK であれば、起動は成功です。

launchBillingFlow() への呼び出しが成功すると、Google Play の購入画面が表示されます。図 1 は、定期購入の購入画面です。

購入可能な定期購入が表示された Google Play の購入画面
図 1. 購入可能な定期購入が表示された Google Play の購入画面

Google Play は onPurchasesUpdated() を呼び出して、PurchasesUpdatedListener インターフェースを実装するリスナーに購入オペレーションの結果を通知します。リスナーは、クライアントを初期化するときに setListener() メソッドを使用して指定します。

onPurchasesUpdated() を実装して、想定されるレスポンス コードを処理する必要があります。次の例は、onPurchasesUpdated() をオーバーライドする方法を示しています。

Kotlin

override fun onPurchasesUpdated(billingResult: BillingResult, purchases: List<Purchase>?) {
   if (billingResult.responseCode == BillingResponseCode.OK && purchases != null) {
       for (purchase in purchases) {
           // Process the purchase as described in the next section.
       }
   } else if (billingResult.responseCode == BillingResponseCode.USER_CANCELED) {
       // Handle an error caused by a user canceling the purchase flow.
   } else {
       // Handle any other error codes.
   }
}

Java

@Override
void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
    if (billingResult.getResponseCode() == BillingResponseCode.OK
        && purchases != null) {
        for (Purchase purchase : purchases) {
            // Process the purchase as described in the next section.
        }
    } else if (billingResult.getResponseCode() == BillingResponseCode.USER_CANCELED) {
        // Handle an error caused by a user canceling the purchase flow.
    } else {
        // Handle any other error codes.
    }
}

購入が成功すると、図 2 のような Google Play の購入成功画面が生成されます。

Google Play の購入成功画面
図 2. Google Play の購入成功画面

購入が成功すると、購入トークンも生成されます。これは、ユーザーとユーザーが購入したアプリ内アイテムの商品 ID を表す一意の識別子です。アプリでは購入トークンをローカルに保存することもできますが、安全なバックエンド サーバーに渡すことを強くおすすめします。そうすれば、購入を確認して不正行為を防止できます。このプロセスの詳細については、購入の検出と処理をご覧ください。

さらに、ユーザーには、オーダー ID(取引の一意の ID)が記載された領収書がメールで送られます。ユーザーへのメールに記載されるオーダー ID は、1 回限りのアイテムでは購入ごとに異なり、定期購入では初回購入とその後の自動更新ごとに異なります。デベロッパーは、Google Play Console でオーダー ID を使用して払い戻しを管理できます。

カスタマイズされた価格であることを提示する

アプリを欧州連合のユーザーに配信できる場合は、launchBillingFlow を呼び出すときに setIsOfferPersonalized() メソッドを使用して、アイテムの価格が自動意思決定によりカスタマイズされていることを開示する通知をユーザーに表示します。

価格がユーザーに合わせてカスタマイズされていることを開示する Google Play の購入画面。
図 3. 価格がユーザーに合わせてカスタマイズされていることを開示する Google Play の購入画面。

必ず、消費者権利指令(2011/83/EU)の第 6 条(1)(ea)CRD に照らして、提供する価格がユーザーにカスタマイズされているかどうかを判断してください。

setIsOfferPersonalized() はブール値の入力を受け取ります。true の場合は、Play UI に開示を表示します。false の場合は、UI に開示を表示しません。デフォルト値は false です。

詳しくは、消費者向けヘルプセンターをご覧ください。

ユーザー ID を関連付ける

購入フローを開始するときに、アプリは obfuscatedAccountId または obfuscatedProfileId を使用して、購入したユーザーのユーザー ID を付加できます。識別子の例としては、システム内のユーザーのログインを難読化したバージョンなどがあります。これらのパラメータを設定すると、Google が不正行為を検出しやすくなります。また、ユーザーに利用資格を付与するで説明したように、購入が適切なユーザーに関連付けられるようにすることもできます。

購入を検出して処理する

このセクションで説明する購入の検出と処理は、プロモーションの利用など、アプリ外での購入を含むすべての種類の購入に適用されます。

アプリは、次のいずれかの方法で新規購入と完了した保留中の購入を検出します。

  1. アプリが launchBillingFlow を呼び出した結果として onPurchasesUpdated が呼び出されたとき(前のセクションで説明したとおり)、またはアプリ外で購入が行われた場合や保留中の購入が完了したときに、アプリがアクティブな Billing Library 接続で実行されている場合。たとえば、家族が別のデバイスで保留中の購入を承認します。
  2. アプリが queryPurchasesAsync を呼び出してユーザーの購入を照会する。

1 の場合、アプリが実行中で、Google Play Billing Library の接続が有効である限り、新規購入または完了した購入に対して onPurchasesUpdated が自動的に呼び出されます。アプリが実行されていない場合や、アプリに有効な Google Play Billing Library 接続がない場合、onPurchasesUpdated は呼び出されません。アプリがフォアグラウンドにある限り、アプリでアクティブな接続を維持して、購入の最新情報をタイムリーに取得することをおすすめします。

2 の場合、BillingClient.queryPurchasesAsync() を呼び出して、アプリですべての購入が処理されるようにする必要があります。アプリが Google Play Billing Library との接続を確立したときに、この処理を行うことをおすすめします(BillingClient を初期化するで説明したように、アプリの起動時やフォアグラウンドに表示されたときに行うことをおすすめします)。これは、onServiceConnected で成功した結果を受信したときに queryPurchasesAsync を呼び出すことで実現できます。次のようなイベントや状況を処理するには、この推奨事項に従うことが重要です。

  • 購入中のネットワーク問題: ユーザーが購入に成功して Google から確認通知を受け取ったが、デバイスとアプリが PurchasesUpdatedListener を通じて購入通知を受け取る前にネットワーク接続を失った場合。
  • 複数のデバイス: ユーザーがあるデバイスでアイテムを購入し、別のデバイスに切り替えたときにそのアイテムが表示されることを期待する場合。
  • アプリ外で行われた購入の処理: 一部の購入(販促用クーポンの利用など)はアプリの外部で行われる可能性があります。
  • 購入ステータスの遷移を処理する: ユーザーがアプリの実行中に保留中の購入のお支払いを完了し、アプリを開いたときに購入が完了したことの確認を受け取ることを想定している場合。

アプリが新規購入または完了した購入を検出したら、アプリは次の処理を行う必要があります。

  • 購入を確認します。
  • 完了した購入に対してユーザーにコンテンツを付与します。
  • お客様に通知します。
  • アプリが完了した購入を処理したことを Google に通知します。

これらの手順については、以降のセクションで詳しく説明します。その後、すべての手順をまとめたセクションがあります。

購入を確認する

ユーザーに特典を付与する前に、アプリは常に購入を検証して正当であることを確認する必要があります。これは、利用権を付与する前に購入を確認するに記載されているガイドラインに沿って行えます。購入を確認した後にのみ、アプリは購入処理を続行し、ユーザーに利用資格を付与する必要があります。これについては、次のセクションで説明します。

ユーザーに利用資格を付与する

アプリが購入を確認すると、引き続きユーザーに利用権を付与して通知できます。利用資格を付与する前に、アプリが購入ステータスPURCHASED であることを確認していることを確認します。購入が保留中の場合、利用資格が付与される前に購入を完了するためのアクションを完了する必要があることをユーザーに通知する必要があります。購入ステータスが PENDING から SUCCESS に移行した場合にのみ利用資格を付与します。詳しくは、保留中の取引の処理をご覧ください。

ユーザー ID の付加で説明したように、購入にユーザー ID を付加している場合は、その ID を取得して、システム内の正しいユーザーに関連付けることができます。この手法は、購入の対象ユーザーに関するコンテキストがアプリで失われた可能性がある購入を調整する際に役立ちます。アプリ外で行われた購入には、これらの ID は設定されません。この場合、アプリはログイン中のユーザーに利用資格を付与するか、ユーザーに優先アカウントを選択するよう求めるメッセージを表示できます。

お客様に通知する

ユーザーに利用資格を付与した後、アプリは購入が成功したことを示す通知を表示する必要があります。これにより、購入が正常に完了したかどうかについてユーザーが混乱し、アプリの使用を中止したり、ユーザー サポートに問い合わせたり、ソーシャル メディアで不満を漏らしたりすることを防ぐことができます。アプリは、アプリケーションのライフサイクルの任意の時点で購入の更新を検出する可能性があります。たとえば、保護者が別のデバイスで保留中の購入を承認した場合、アプリは適切なタイミングでユーザーに通知を遅らせることができます。遅延が適切な場合の例を次に示します。

  • ゲームのアクション パートやカットシーンでメッセージを表示すると、ユーザーの気が散るおそれがあります。この場合は、アクション パートの終了後に通知する必要があります。
  • ゲームの冒頭のチュートリアルとユーザー設定パート。たとえば、ユーザーがアプリをインストールする前にアプリ外で購入した可能性があります。特典を新しいユーザーに通知するタイミングとしては、ゲームを開いた直後やユーザーによる初期設定時をおすすめします。アプリで、ユーザーに利用資格を付与する前にアカウントの作成またはログインを要求する場合は、購入を申請するために完了する必要がある手順をユーザーに伝えることをおすすめします。アプリが購入を処理しなかった場合、3 日後に購入は払い戻されるため、これは重要です。

購入についてユーザーに通知する場合は、次のメカニズムをおすすめします。

  • アプリ内ダイアログを表示する。
  • アプリ内メッセージ ボックスにメッセージを配信し、アプリ内メッセージ ボックスに新着メッセージがあることを明示する。
  • OS の通知メッセージを使用する。

通知には、受け取った特典についてユーザーに伝える内容を含める必要があります。たとえば、「金貨 100 枚を購入しました」などです。また、購入が Play Pass などのプログラムの特典によるものである場合は、アプリがユーザーにその旨を通知します。例: 「アイテムを受け取りました。Play Pass で宝石を 100 個獲得しました。続行」各プログラムには、ユーザーに表示してメリットを伝える推奨テキストに関するガイダンスが用意されている場合があります。

購入が処理されたことを Google に通知する

アプリがユーザーに利用資格を付与し、取引が成功したことをユーザーに通知したら、購入が正常に処理されたことを Google に通知する必要があります。これは、購入を承認することで行われます。購入が自動的に払い戻され、利用資格が取り消されないようにするには、3 日以内に行う必要があります。さまざまな種類の購入を確認するプロセスについては、以降のセクションで説明します。

消費可能アイテム

アプリに安全なバックエンドがある場合は、消費型アイテムには Purchases.products:consume を使用して、購入品が確実に消費されるようにすることをおすすめします。Purchases.products:get の呼び出し結果にある consumptionState をチェックして、購入品がまだ消費されていないことを確かめます。アプリがバックエンドのないクライアント専用である場合は、Google Play Billing Library の consumeAsync() を使用します。どちらのメソッドも承認の要件を満たし、アプリがユーザーに利用権を付与したことを示します。これらのメソッドにより、入力された購入トークンに対応する 1 回限りのアイテムを、アプリで再び購入可能にすることもできます。consumeAsync() では、ConsumeResponseListener インターフェースを実装するオブジェクトも渡す必要があります。このオブジェクトは、消費オペレーションの結果を処理します。オペレーションの完了時に Google Play Billing Library が呼び出す onConsumeResponse() メソッドをオーバーライドできます。

アイテムに関連付けられている購入トークンを使って Google Play Billing Library でアイテムを消費する例を次に示します。

Kotlin

    val consumeParams =
        ConsumeParams.newBuilder()
            .setPurchaseToken(purchase.getPurchaseToken())
            .build()
    val consumeResult = withContext(Dispatchers.IO) {
        client.consumePurchase(consumeParams)
    }

Java

    ConsumeParams consumeParams =
            ConsumeParams.newBuilder()
                .setPurchaseToken(purchase.getPurchaseToken())
                .build();

    ConsumeResponseListener listener = new ConsumeResponseListener() {
        @Override
        public void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
            if (billingResult.getResponseCode() == BillingResponseCode.OK) {
                // Handle the success of the consume operation.
            }
        }
    };

    billingClient.consumeAsync(consumeParams, listener);

消費不可アイテム

アプリに安全なバックエンドがある場合、非消費型アイテムの購入には、確実に承認できるように Purchases.products:acknowledge を使用することをおすすめします。Purchases.products:get の呼び出し結果にある acknowledgementState をチェックして、その購入が以前に承認されていないことを確かめます。

アプリがクライアント専用の場合は、Google Play Billing Library の BillingClient.acknowledgePurchase() を使用します。購入を承認する前に、アプリは Google Play Billing Library の isAcknowledged() メソッドを使用して、購入がすでに承認されていないかチェックする必要があります。

次の例は、Google Play Billing Library を使用して購入を承認する方法を示しています。

Kotlin

val client: BillingClient = ...
val acknowledgePurchaseResponseListener: AcknowledgePurchaseResponseListener = ...

val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
    .setPurchaseToken(purchase.purchaseToken)
val ackPurchaseResult = withContext(Dispatchers.IO) {
     client.acknowledgePurchase(acknowledgePurchaseParams.build())
}

Java

BillingClient client = ...
AcknowledgePurchaseResponseListener acknowledgePurchaseResponseListener = ...

AcknowledgePurchaseParams acknowledgePurchaseParams =
                AcknowledgePurchaseParams.newBuilder()
                    .setPurchaseToken(purchase.getPurchaseToken())
                    .build();
 client.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener);

定期購入

定期購入は、非消費型アイテムと同様に処理されます。可能であれば、購入を安全なバックエンドから確実に承認できるように、Google Play Developer API の Purchases.subscriptions.acknowledge を使用してください。Purchases.subscriptions:get の購入リソースの acknowledgementState をチェックして、その購入が以前に承認されていないことを確かめます。バックエンドがない場合は、Google Play Billing Library の BillingClient.acknowledgePurchase() を使用し、isAcknowledged() をチェックした後、定期購入を承認できます。初回の定期購入では必ず承認が必要です。定期購入の更新は、承認の必要はありません。定期購入の承認が必要な場合の詳細については、定期購入を販売するトピックをご覧ください。

まとめ

次のコード スニペットは、これらの手順の概要を示しています。

Kotlin

fun handlePurchase(Purchase purchase) {
    // Purchase retrieved from BillingClient#queryPurchasesAsync or your
    // onPurchasesUpdated.
    Purchase purchase = ...;

    // Step 1: Send the purchase to your secure backend to verify the purchase
    // following
    // https://developer.android.com/google/play/billing/security#verify
.
    // Step 2: Update your entitlement storage with the purchase. If purchase is
    // in PENDING state then ensure the entitlement is marked as pending and the
    // user does not receive benefits yet. It is recommended that this step is
    // done on your secure backend and can combine in the API call to your
    // backend in step 1.

    // Step 3: Notify the user using appropriate messaging (delaying
    // notification if needed as discussed above).

    // Step 4: Notify Google the purchase was processed using the steps
    // discussed in the processing purchases section.
}

Java

void handlePurchase(Purchase purchase) {
    // Purchase retrieved from BillingClient#queryPurchasesAsync or your
    // onPurchasesUpdated.
    Purchase purchase = ...;

    // Step 1: Send the purchase to your secure backend to verify the purchase
    // following
    // https://developer.android.com/google/play/billing/security#verify

    // Step 2: Update your entitlement storage with the purchase. If purchase is
    // in PENDING state then ensure the entitlement is marked as pending and the
    // user does not receive benefits yet. It is recommended that this step is
    // done on your secure backend and can combine in the API call to your
    // backend in step 1.

    // Step 3: Notify the user using appropriate messaging (delaying
    // notification if needed as discussed above).

    // Step 4: Notify Google the purchase was processed using the steps
    // discussed in the processing purchases section.
}

アプリにこれらの手順が正しく実装されていることを確認するには、テストガイドに沿って操作します。

保留中のトランザクションを処理する

Google Play では、保留中の取引がサポートされています。これは、ユーザーが購入を開始してから購入のお支払い方法が処理されるまでに 1 つ以上の追加手順が必要な取引です。このタイプの購入では、ユーザーのお支払い方法への請求が正常に処理されたことを Google が通知するまで、アプリは利用権を付与してはなりません。

たとえば、ユーザーは、後で現金で支払う実店舗を選択して取引を開始できます。ユーザーは通知とメールの両方でコードを受け取ります。ユーザーは実店舗に足を運び、レジでコードを提示して現金による支払いを行います。Google は、デベロッパーとユーザーの両方に支払いの受領を通知します。この時点で、アプリはユーザーに利用権を付与できます。

BillingClient の初期化の一環として enablePendingPurchases() を呼び出して、アプリの保留中の取引を有効にします。アプリは、1 回限りのアイテムの保留中の取引を有効にしてサポートする必要があります。サポートを追加する前に、保留中の取引の購入ライフサイクルを理解してください。

アプリが PurchasesUpdatedListener を通して、または queryPurchasesAsync の呼び出しの結果として新規の購入を受信したら、getPurchaseState() メソッドを使用して、購入ステータスが PURCHASEDPENDING かを判定します。ステータスが PURCHASED の場合にのみ、利用権を付与してください。

ユーザーが購入を完了したときにアプリが実行中であり、Play Billing Library の接続が有効な場合は、PurchasesUpdatedListener が再度呼び出され、PurchaseStatePURCHASED になります。この時点で、アプリは 購入の検出と処理の標準方法で購入を処理できます。また、アプリの onResume() メソッドで queryPurchasesAsync() を呼び出し、アプリが実行中でなかったときに PURCHASED ステータスに移行した購入を処理する必要があります。

購入が PENDING から PURCHASED に移行すると、real_time_developer_notifications クライアントに ONE_TIME_PRODUCT_PURCHASED または SUBSCRIPTION_PURCHASED 通知が届きます。購入がキャンセルされると、ONE_TIME_PRODUCT_CANCELED または SUBSCRIPTION_PENDING_PURCHASE_CANCELED の通知が届きます。これは、要求される期間内にユーザーが支払いを完了しなかった場合に発生することがあります。Google Play Developer API を使用して、購入の現在のステータスを確認できます。

複数数量の購入を処理する

Google Play Billing Library のバージョン 4.0 以降では、購入カートから数量を指定して、1 回の取引で同じアプリ内アイテムを複数購入できます。アプリは複数数量の購入を処理し、指定された購入数量に基づいて利用権を付与することが想定されています。

複数数量の購入に対応するには、アプリのプロビジョニング ロジックでアイテムの数量を確認する必要があります。quantity フィールドにアクセスするには、次のいずれかの API を使用します。

複数数量の購入を処理するロジックを追加したら、Google Play Console のアプリ内アイテム管理ページで、対応するアイテムについて複数数量の購入機能を有効にする必要があります。

ユーザーの請求構成をクエリする

getBillingConfigAsync(): ユーザーが Google Play で使用する国を提供します。

BillingClient作成した後に、ユーザーの請求構成をクエリできます。次のコード スニペットは、getBillingConfigAsync() を呼び出す方法を示しています。BillingConfigResponseListener を実装してレスポンスを処理します。このリスナーは、アプリから開始されたすべての請求構成クエリの更新を受け取ります。

返された BillingResult にエラーがない場合は、BillingConfig オブジェクトの countryCode フィールドを確認すると、ユーザーの Google Play の国を取得できます。

Kotlin

// Use the default GetBillingConfigParams.
val getBillingConfigParams = GetBillingConfigParams.newBuilder().build()
billingClient.getBillingConfigAsync(getBillingConfigParams,
    object : BillingConfigResponseListener {
        override fun onBillingConfigResponse(
            billingResult: BillingResult,
            billingConfig: BillingConfig?
        ) {
            if (billingResult.responseCode == BillingResponseCode.OK
                && billingConfig != null) {
                val countryCode = billingConfig.countryCode
                ...
            } else {
                // TODO: Handle errors
            }
        }
    })

Java

// Use the default GetBillingConfigParams.
GetBillingConfigParams getBillingConfigParams = GetBillingConfigParams.newBuilder().build();
billingClient.getBillingConfigAsync(getBillingConfigParams,
    new BillingConfigResponseListener() {
      public void onBillingConfigResponse(
          BillingResult billingResult, BillingConfig billingConfig) {
        if (billingResult.getResponseCode() == BillingResponseCode.OK
            && billingConfig != null) {
            String countryCode = billingConfig.getCountryCode();
            ...
         } else {
            // TODO: Handle errors
        }
      }
    });

Google Play Games のホーム画面のカート放棄リマインダー(デフォルトで有効)

IAP で収益化しているゲーム デベロッパーの場合、Google Play Console で有効になっている商品アイテム(SKU)をアプリの外部で販売する方法の一つとして、カート放棄リマインダー機能があります。この機能は、Google Play ストアをブラウジングしているときに、以前に放棄した購入を完了するようユーザーに促すものです。これらの購入は、アプリの外部、Google Play ストアの Google Play Games ホームから行われます。

ユーザーが中断したところから再開できるようにし、デベロッパーが販売を最大化できるようにするため、この機能はデフォルトで有効になっています。ただし、カートの放棄リマインダー機能のオプトアウト フォームを送信することで、アプリでこの機能を無効にできます。Google Play Console で SKU を管理する際のベスト プラクティスについては、アプリ内アイテムを作成するをご覧ください。

次の画像は、Google Play ストアに表示されるカート放棄リマインダーを示しています。

以前に放棄した購入について、Google Play ストアの画面に購入プロンプトが表示される
図 2. Google Play ストアの画面に、以前に中断した購入の購入プロンプトが表示される。

以前に放棄した購入について、Google Play ストアの画面に購入プロンプトが表示される
図 3. Google Play ストアの画面に、以前に中断した購入の購入プロンプトが表示される。