このトピックでは、Google Play Billing Library をアプリに統合してアイテムの販売を開始する方法について説明します。このトピックを読む前に、準備するに記載された手順に沿って、必ず Google Play の構成を設定してください。
このトピックには、GitHub の公式サンプルアプリに基づくコード例が含まれています。統合の際に使用できるサンプルアプリとその他のリソースの一覧については、追加リソースをご覧ください。
購入ライフサイクル
1 回限りの購入または定期購入の一般的な購入フローは次のとおりです。
- 購入できるアイテムをユーザーに表示します。
- ユーザーが実際に購入を選択するまでの購入フローを起動します。
- サーバーで購入を確認します。
- ユーザーにコンテンツを配布し、コンテンツが配布されたことを確認します。必要に応じて、アイテムを消費済みとしてマークし、ユーザーがアイテムを再購入できるようにします。
定期購入は、ユーザーがキャンセルするまで自動的に更新されます。定期購入には次のステータスがあります。
- アクティブ: ユーザーは良好な状態にあり、問題なく定期購入を利用できます。
- キャンセル済み: ユーザーは定期購入をキャンセルしましたが、期限切れになるまで利用できます。
- 猶予期間内: 支払いに関する問題が発生しましたが、Google がお支払い方法を再試行している間、ユーザーは定期購入を利用できます。
- 保留中: 支払いに関する問題が発生し、Google がお支払い方法を再試行している間、ユーザーは定期購入を利用できなくなりました。
- 一時停止中: ユーザーは定期購入の利用を一時停止し、再開するまで利用できなくなりました。
- 期限切れ: ユーザーは定期購入をキャンセルし、利用できなくなりました。期限切れになると、ユーザーは解約したものと見なされます。
購入トークンとオーダー ID
Google Play は、購入トークンとオーダー ID を使用して、アイテムと取引をトラッキングします。
- 購入トークンとは、Google Play 上のアイテムに対する購入者の利用権を表す文字列です。Google ユーザーが SKU で表される特定のアイテムの利用権を持つことを示します。購入トークンは Google Play Developer API で使用できます。
- オーダー ID とは、Google Play 上の支払い取引を表す文字列です。この文字列は、購入者にメールで送信される領収書に記載されます。オーダー ID は販売レポートと支払いレポートにも記載され、払い戻しの管理に使用できます。
オーダー ID は、支払い取引が発生するたびに作成されます。購入トークンは、ユーザーが購入フローを完了したときにのみ生成されます。
- 1 回限りのアイテムの場合、購入が発生するたびに新しい購入トークンが作成されます。ほとんどの購入では、新しいオーダー ID も生成されます。ただし、プロモーション コードで説明しているように、ユーザーが代金を請求されない場合は例外です。
- 定期購入の場合、初回購入時に購入トークンとオーダー ID が作成されます。その後は、請求対象期間が更新されるたびに、購入トークンは同じままで新しいオーダー ID が発行されます。アップグレード、ダウングレード、交換、再登録が発生したときは、新しい購入トークンとオーダー ID が作成されます。
定期購入では、次の点に注意してください。
- 定期購入のアップグレードとダウングレード、およびその他の定期購入フローで生成される購入トークンは、前の購入トークンに置き換わる必要があります。Google Play Developer API の
linkedPurchaseToken
フィールドに表示される購入トークンを無効にする必要があります。詳細については、Implementing linkedPurchaseToken correctly to prevent duplicate subscriptions をご覧ください。 - 定期購入の更新のオーダー番号には、何回目の更新かを表す整数が追加されます。たとえば、初回の定期購入のオーダー ID が
GPA.1234-5678-9012-34567
である場合、その後のオーダー ID はGPA.1234-5678-9012-34567..0
(1 回目の更新)、GPA.1234-5678-9012-34567..1
(2 回目の更新)などになります。
エラー処理
Google Play Billing Library は、BillingResult
の形式でエラーを返します。BillingResult
に含まれている BillingResponseCode
は、アプリで発生する可能性がある請求関連のエラーを分類したものです。たとえば、SERVICE_DISCONNECTED
エラーコードが返された場合、アプリは Google Play との接続を再初期化する必要があります。また、BillingResult
に含まれているデバッグ メッセージは、開発中のエラーの診断に役立ちます。
Google Play に接続する
Google Play の課金システムを統合するための最初のステップは、アプリにライブラリを追加して接続を初期化することです。
Google Play Billing Library への依存関係を追加する
次に示すように、Google Play Billing Library への依存関係をアプリの build.gradle
ファイルに追加します。
dependencies { def billing_version = "3.0.0" implementation "com.android.billingclient:billing:$billing_version" }
Kotlin を使用している場合、Google Play Billing Library の KTX モジュールに Kotlin 拡張機能とコルーチンのサポートが含まれているため、Google Play Billing Library を使用する際に慣用的な Kotlin コードを作成できます。これらの拡張機能をプロジェクトに含めるには、アプリの build.gradle
ファイルに次の依存関係を追加します。
dependencies { def billing_version = "3.0.0" implementation "com.android.billingclient:billing-ktx:$billing_version" }
BillingClient を初期化する
Google Play Billing Library への依存関係を追加したら、BillingClient
インスタンスを初期化する必要があります。BillingClient
は、Google Play Billing Library とアプリの他の部分の通信に使用されるメイン インターフェースです。BillingClient
は、多くの一般的な請求処理に役立つコンビニエンス メソッド(同期メソッドと非同期メソッドの両方)を提供します。
BillingClient
を作成するには、newBuilder()
を使用します。また、購入に関する最新情報を受け取るには、setListener()
を呼び出して PurchasesUpdatedListener
への参照を渡す必要があります。このリスナーは、すべてのアプリ内購入に関する最新情報を受信します。
Kotlin
private val purchasesUpdatedListener = PurchasesUpdatedListener { billingResult, purchases -> // To be implemented in a later section. } private var billingClient = BillingClient.newBuilder(activity) .setListener(purchasesUpdatedListener) .enablePendingPurchases() .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(activity) .setListener(purchasesUpdatedListener) .enablePendingPurchases() .build();
Google Play との接続を確立する
BillingClient
を作成したら、Google Play との接続を確立する必要があります。
Google Play に接続するには、startConnection()
を呼び出します。接続プロセスは非同期です。クライアントのセットアップが完了してリクエストを送信する準備ができたら、コールバックを受け取るために BillingClientStateListener
を実装する必要があります。
また、Google Play への接続の切断を処理するための再試行ロジックも実装する必要があります。再試行ロジックを実装するには、onBillingServiceDisconnected()
コールバック メソッドをオーバーライドし、後続のリクエストを送信する前に BillingClient
が startConnection()
メソッドを呼び出して 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 への接続を確立したら、購入可能なアイテムをクエリしてユーザーに表示できます。Google Play でアプリ内アイテムの詳細をクエリするには、querySkuDetailsAsync()
を呼び出します。SKU 詳細のクエリは、ローカライズされた商品情報を返すので、ユーザーにアイテムを表示する前の重要なステップとなります。定期購入の場合は、アイテムの表示がすべての Play ポリシーに従っていることを確認します。
querySkuDetailsAsync()
を呼び出すときは、SkuType
と一緒に、Google Play Console で作成された商品 ID 文字列のリストを指定する SkuDetailsParams
のインスタンスを渡します。SkuType
には、1 回限りのアイテムの場合は SkuType.INAPP
、定期購入の場合は SkuType.SUBS
を指定できます。
非同期操作の結果を処理するには、SkuDetailsResponseListener
インターフェースを実装するリスナーも指定する必要があります。次の例に示すように、クエリの終了時にリスナーに通知する onSkuDetailsResponse()
をオーバーライドできます。
Kotlin
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 -> // Process the result. } } }
Java
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(BillingResult billingResult, List<SkuDetails> skuDetailsList) { // Process the result. } });
Google Play Billing Library は、SkuDetails
オブジェクトの List
にクエリ結果を保存します。したがって、デベロッパーは、リスト内の各 SkuDetails
オブジェクトのさまざまなメソッドを呼び出して、アプリ内アイテムに関する価格や説明などの情報を参照できます。購入可能なアイテムの詳細情報を参照するには、SkuDetails
クラスのメソッドのリストをご覧ください。
販売するアイテムを提示する前に、ユーザーがまだそのアイテムを所有していないことを確認します。ユーザーのアイテム ライブラリに消費可能アイテムがまだある場合、ユーザーはそのアイテムを消費しない限り再購入できません。
定期購入を提示する前に、ユーザーがまだ定期購入を登録していないことを確認します。
購入フローを起動する
アプリから購入リクエストを開始するには、アプリのメインスレッドから launchBillingFlow()
メソッドを呼び出します。このメソッドは、querySkuDetailsAsync()
の呼び出しから取得された関連する SkuDetails
オブジェクトを含む BillingFlowParams
オブジェクトへの参照を受け取ります。BillingFlowParams オブジェクトを作成するには、BillingFlowParams.Builder
クラスを使用します。
Kotlin
// An activity reference from which the billing flow will be launched. val activity : Activity = ...; // Retrieve a value for "skuDetails" by calling querySkuDetailsAsync(). val flowParams = BillingFlowParams.newBuilder() .setSkuDetails(skuDetails) .build() val responseCode = billingClient.launchBillingFlow(activity, flowParams).responseCode
Java
// An activity reference from which the billing flow will be launched. Activity activity = ...; // Retrieve a value for "skuDetails" by calling querySkuDetailsAsync(). BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder() .setSkuDetails(skuDetails) .build(); int responseCode = billingClient.launchBillingFlow(activity, billingFlowParams).getResponseCode(); // Handle the result.
launchBillingFlow()
メソッドは、BillingClient.BillingResponseCode
にリストされているレスポンス コードのいずれかを返します。この結果をチェックして、購入フローの起動時にエラーが発生していないことを確認してください。BillingResponseCode
が OK
であれば、起動は成功です。
launchBillingFlow()
への呼び出しが成功すると、Google Play の購入画面が表示されます。図 1 は、定期購入の購入画面です。

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) { handlePurchase(purchase) } } else if (billingResult.responseCode == BillingResponseCode.USER_CANCELED) { // Handle an error caused by a user cancelling 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) { handlePurchase(purchase); } } else if (billingResult.getResponseCode() == BillingResponseCode.USER_CANCELED) { // Handle an error caused by a user cancelling the purchase flow. } else { // Handle any other error codes. } }
購入が成功すると、図 2 のような Google Play の購入成功画面が生成されます。

購入が成功すると、購入トークンも生成されます。これは、ユーザーとユーザーが購入したアプリ内アイテムの商品 ID を表す一意の識別子です。アプリでは購入トークンをローカルに保存することもできますが、安全なバックエンド サーバーに渡すことをおすすめします。そうすれば、購入を確認して不正行為を防止できます。このプロセスについては、次のセクションで詳しく説明します。
さらに、ユーザーには、オーダー ID(取引の一意の ID)が記載された領収書がメールで送られます。ユーザーへのメールに記載されるオーダー ID は、1 回限りのアイテムでは購入ごとに異なり、定期購入では初回購入とその後の自動更新ごとに異なります。デベロッパーは、Google Play Console でオーダー ID を使用して払い戻しを管理できます。
購入を処理する
ユーザーが購入を完了したら、アプリはその購入を処理する必要があります。ほとんどの場合、アプリは PurchasesUpdatedListener
を通じて購入の通知を受け取ります。ただし、購入をフェッチするで説明しているように、アプリが BillingClient.queryPurchases()
の呼び出しを認識する場合もあります。
アプリは次のようにして購入を処理する必要があります。
- 購入を確認します。
- ユーザーにコンテンツを配布し、コンテンツが配布されたことを確認します。必要に応じて、アイテムを消費済みとしてマークし、ユーザーがアイテムを再購入できるようにします。
購入を確認するには、まず購入ステータスが PURCHASED
かどうかをチェックします。購入が PENDING
である場合は、保留中の取引の処理の説明に従って購入を処理します。onPurchaseUpdated()
または queryPurchases からの購入の場合は、アプリが利用権を付与する前に、購入をさらに確認する必要があります。購入を適切に確認する方法については、利用権を付与する前に購入を確認するをご覧ください。
購入を確認したら、アプリはユーザーに利用権を付与できます。利用権を付与した後、アプリは購入を承認する必要があります。この承認により、購入の利用権を付与したことが Google Play に通知されます。
利用権を付与して購入を承認するプロセスは、消費不可アイテムの購入か、消費可能アイテムの購入か、定期購入かによって異なります。
消費可能アイテムの場合、consumeAsync()
メソッドは承認要件を履行し、アプリがユーザーに利用権を付与したことを示します。このメソッドにより、アプリは 1 回限りのアイテムを再購入可能にすることもできます。
1 回限りのアイテムが消費されたことを示すには、consumeAsync()
を呼び出して、Google Play での再購入を可能にする購入トークンを追加します。ConsumeResponseListener
インターフェースを実装するオブジェクトも渡す必要があります。このオブジェクトは、消費オペレーションの結果を処理します。オペレーションの完了時に Google Play Billing Library が呼び出す onConsumeResponse()
メソッドをオーバーライドできます。
アイテムに関連付けられている購入トークンを使ってアイテムを消費する例を次に示します。
Kotlin
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 entitlement to the user. val consumeParams = ConsumeParams.newBuilder() .setPurchaseToken(purchase.getPurchaseToken()) .build() billingClient.consumeAsync(consumeParams, { billingResult, outToken -> if (billingResult.responseCode == BillingResponseCode.OK) { // Handle the success of the consume operation. } }) }
Java
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 entitlement to the user. 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); }
消費不可アイテムの購入を承認するには、Google Play Billing Library の BillingClient.acknowledgePurchase()
または Google Play Developer API の Product.Purchases.Acknowledge を使用します。購入を承認する前に、アプリは Google Play Billing Library の isAcknowledged()
メソッドまたは Google Developers API の acknowledgementState
フィールドを使用して、購入がすでに承認されていないかをチェックする必要があります。
次の例は、Google Play Billing Library を使用して購入を承認する方法を示しています。
Kotlin
val client: BillingClient = ... val acknowledgePurchaseResponseListener: AcknowledgePurchaseResponseListener = ... fun handlePurchase() { if (purchase.purchaseState === PurchaseState.PURCHASED) { if (!purchase.isAcknowledged) { val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder() .setPurchaseToken(purchase.purchaseToken) val ackPurchaseResult = withContext(Dispatchers.IO) { client.acknowledgePurchase(acknowledgePurchaseParams.build()) } } } }
Java
BillingClient client = ... AcknowledgePurchaseResponseListener acknowledgePurchaseResponseListener = ... void handlePurchase(Purchase purchase) { if (purchase.getPurchaseState() == PurchaseState.PURCHASED) { if (!purchase.isAcknowledged()) { AcknowledgePurchaseParams acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder() .setPurchaseToken(purchase.getPurchaseToken()) .build(); client.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener); } } }
定期購入は、消費不可アイテムと同様に処理されます。Google Play Billing Library の BillingClient.acknowledgePurchase()
または Google Play Developer API の Purchases.Subscriptions.Acknowledge
を使用して、定期購入を承認できます。初回の定期購入では必ず承認が必要です。定期購入の更新では承認は不要です。定期購入を承認する必要があるケースの詳細については、定期購入を販売するトピックをご覧ください。
購入をフェッチする
アプリですべての購入を確実に処理するには、PurchasesUpdatedListener
で購入の更新情報をリッスンするだけでは不十分です。その方法では、ユーザーが行った購入の一部をアプリが認識しない可能性があります。アプリが購入を見失う場合または気づかない場合のシナリオを以下に示します。
- 購入中のネットワーク問題: ユーザーが購入に成功して Google から確認通知を受け取ったが、デバイスが
PurchasesUpdatedListener
を通じて購入通知を受け取る前にネットワーク接続を失った場合。 - 複数のデバイス: ユーザーがあるデバイスでアイテムを購入し、別のデバイスに切り替えたときにそのアイテムが表示されることを期待する場合。
- アプリ外で行われた購入の処理: 一部の購入(販促用クーポンの利用など)はアプリの外部で行われる可能性があります。
このような状況に対処するには、アプリの onResume()
および onCreate()
メソッドで必ず BillingClient.queryPurchases()
を呼び出し、すべての購入が購入を処理するの説明どおりに、正常に処理されるようにします。
アプリ外で行われた購入の処理
販促用クーポンの利用など、一部の購入はアプリの外部で発生することがあります。ユーザーは、アプリの外部で購入を行った場合、アプリ内メッセージが表示されるか、なんらかのメカニズムによりアプリが購入情報を正常に受信して処理したという通知を受け取ることを期待します。これを実現するメカニズムとしては、以下のようなものがあります。
- アプリ内ポップアップを表示する。
- アプリ内メッセージ ボックスにメッセージを配信し、アプリ内メッセージ ボックスに新着メッセージがあることを明示する。
- OS の通知メッセージを使用する。
アプリが購入を認識したとき、アプリはどのような状態でもありうることに留意してください。購入時にアプリがインストールされていなかったことすらありえます。ユーザーは、アプリの状態に関係なく、アプリを再開すれば購入情報を受信できることを期待します。
購入が行われたときのアプリの状態にかかわらず、購入を検出しなければなりません。ただし、アイテムの配布をすぐにユーザーに通知しなくてよい場合もあります。次に例を示します。
- ゲームのアクション パートの最中にメッセージを表示すると、ユーザーの気が散るおそれがあります。この場合は、アクション パートの終了後に通知する必要があります。
- カットシーンでメッセージを表示すると、ユーザーの気が散るおそれがあります。この場合は、カットシーンの終了後に通知する必要があります。
- ゲームの冒頭のチュートリアルとユーザー設定パート。新規ユーザーに特典を通知するタイミングとしては、ゲームを開始した直後またはユーザーによる初期設定時をおすすめします。ただし、メインのゲーム シーケンスをプレイできる状態になるまで、ユーザーへの通知を遅らせてもかまいません。
アプリの外部で行われた購入について、いつどのようにユーザーに通知するかを決定するにあたっては、常にユーザーへの配慮を優先してください。ユーザーはすぐに通知されないと困惑するかもしれません。さらには、アプリの使用をやめたり、ユーザー サポートに問い合わせたり、ソーシャル メディアで不平をこぼしたりする可能性もあります。
保留中の取引の処理
Google Play では、保留中の取引、つまりユーザーが購入を開始してから購入のお支払い方法が処理されるまでに 1 つ以上の追加手順が必要な取引がサポートされています。ユーザーのお支払い方法に従って正常に請求されたことを Google が通知するまで、アプリはこのタイプの購入について利用権を付与してはなりません。
たとえば、ユーザーはお支払い方法として現金を選択でき、これによりアプリ内アイテムの PENDING
の購入が作成されます。次に、ユーザーは取引を完了する実店舗を選択し、通知とメールの両方でコードを受け取ります。ユーザーは実店舗に足を運び、レジでコードを提示して現金による支払いを行います。Google は、デベロッパーとユーザーの両方に現金の受領を通知します。この時点で、アプリはユーザーに利用権を付与できます。
保留中の購入を有効にするには、アプリの初期化の一環として enablePendingPurchases()
を呼び出します。
アプリが PurchasesUpdatedListener
を通して、または queryPurchases()
の呼び出しの結果として新規の購入を受信したら、getPurchaseState()
メソッドを使用して、購入ステータスが PURCHASED
か PENDING
かを判定します。ステータスが PURCHASED
の場合にのみ、利用権を付与してください。ユーザーが購入を完了したときにアプリが実行中であった場合は、PurchasesUpdatedListener
が再度呼び出され、PurchaseState
が PURCHASED
になります。この時点で、アプリは 1 回限りの購入を処理する標準の方法で購入を処理できます。また、アプリの onResume()
メソッドと onCreate()
メソッドで queryPurchases()
を呼び出し、アプリが実行中でなかったときに PURCHASED
ステータスに移行した購入を処理する必要があります。
アプリでは、OneTimeProductNotifications
をリッスンすることにより、保留中の購入に関するリアルタイム デベロッパー通知を使用することもできます。購入が PENDING
から PURCHASED
に移行すると、アプリに ONE_TIME_PRODUCT_PURCHASED
通知が届きます。購入がキャンセルされると、アプリに ONE_TIME_PRODUCT_CANCELED
通知が届きます。これは、要求される期間内にユーザーが支払いを完了しなかった場合に発生することがあります。これらの通知を受け取った場合は、Purchases.products
の PENDING
ステータスを含む Google Play Developer API を使用できます。