6 月 3 日の「#Android11: The Beta Launch Show」にぜひご参加ください。

AIDL から Google Play Billing Library への移行ガイド

このトピックでは、Android インターフェース定義言語(AIDL)を使用する Google Play 請求サービスの統合から移行する方法について説明します。AIDL を使用した Google Play 請求サービスへのアクセスはサポートが終了しているため、今後はすべての統合で Google Play Billing Library を使用する必要があります。

移行手順

Google Play Billing Library をインポートする

まず、Google Play Billing Library に依存関係を追加します。Gradle を使用している場合は、アプリの build.gradle ファイルに次のコードを追加できます。

dependencies {
        implementation 'com.android.billingclient:billing:2.2.0'
    }
    

以前の参照コードからコピーした可能性がある「グルー」コード(IabHelper など)はすべて削除できます。IabHelper が提供する機能は現在、Google Play Billing Library に含まれています。

com.android.vending.BILLING 権限を削除する

com.android.vending.BILLING 権限は Google Play Billing Library のマニフェスト内に埋め込まれています。この権限をアプリのマニフェスト内で明示的に追加する必要はなくなりました。

Google Play 請求サービスに接続する

接続の管理は Google Play Billing Library の BillingClient が行います。移行する場合、アプリを次のように変更します。

次の例は、アプリの変更前と変更後のコードを示しています。

変更前

mServiceConn = new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName name) {
          ...
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
          ...
        }
    };

    Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
    serviceIntent.setPackage("com.android.vending");
    List<ResolveInfo> intentServices = mContext.getPackageManager()
        .queryIntentServices(serviceIntent, 0);
    if (intentServices != null && !intentServices.isEmpty()) {
        mContext.bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);
    } else {
        // Handle errors.
        ...
    }

    ...

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        IabResult result;
        if (requestCode != mRequestCode || data == null) {
            // Handle errors.
            ...
        }

        int responseCode = getResponseCodeFromIntent(data);
        String purchaseData = data.getStringExtra(RESPONSE_INAPP_PURCHASE_DATA);
        String dataSignature = data.getStringExtra(RESPONSE_INAPP_SIGNATURE);

        if (resultCode != Activity.RESULT_OK || responseCode != BILLING_RESPONSE_RESULT_OK) {
            // Handle errors.
            ...
        }

        // Process successful purchase.
        ...

        return true;
    }
    

変更後

Kotlin

    class MyBillingImpl(private var billingClient: BillingClient) : PurchasesUpdatedListener {

        init {
            billingClient = BillingClient.newBuilder(activity).setListener(this).build()
            billingClient.startConnection(object : BillingClientStateListener {
                override fun onBillingSetupFinished(billingResult: BillingResult?) {
                    // Logic from ServiceConnection.onServiceConnected should be moved here.
                }

                override fun onBillingServiceDisconnected() {
                    // Logic from ServiceConnection.onServiceDisconnected should be moved here.
                }
            })
        }

        override fun onPurchasesUpdated(
            billingResult: BillingResult?,
            purchases: MutableList<Purchase>?
        ) {
            // Logic from onActivityResult should be moved here.
        }
    }
    

Java

    public class MyBillingImpl implements PurchasesUpdatedListener {
        private BillingClient billingClient;
        ...

        public void initialize() {
            billingClient = BillingClient.newBuilder(activity).setListener(this).build();
            billingClient.startConnection(new BillingClientStateListener() {
                @Override
                public void onBillingSetupFinished(BillingResult billingResult) {
                    // Logic from ServiceConnection.onServiceConnected should be moved here.
                }

                @Override
                public void onBillingServiceDisconnected() {
                    // Logic from ServiceConnection.onServiceDisconnected should be moved here.
                }
            });
        }

        @Override
        public void onPurchasesUpdated(
            @BillingResponse int responseCode, @Nullable List<Purchase> purchases) {
            // Logic from onActivityResult should be moved here.
        }
    }
    

購入する

購入ダイアログを起動する方法は次のとおりです。

次の例は、アプリの変更前と変更後のコードを示しています。

変更前

// Query Skus
    String skuToSell = "premium_upgrade";
    ArrayList<String> skus = new Arraylist<>();
    skus.add(skuToSell);
    Bundle querySkus = new Bundle();
    querySkus.putStringArrayList(GET_SKU_DETAILS_ITEM_LIST, skus);
    Bundle skuDetails = mService.getSkuDetails(3,
                                               mContext.getPackageName(),
                                               itemType,
                                               querySkus);

    if (!skuDetails.containsKey(RESPONSE_GET_SKU_DETAILS_LIST)) {
        // Handle errors.
        ...
    }

    // Launch Buy Flow
    Bundle buyIntentBundle = mService.getBuyIntent(3,
                                                   mContext.getPackageName(),
                                                   skuToSell,
                                                   "Inapp",
                                                   "");

    int response = getResponseCodeFromBundle(buyIntentBundle);
    if (response != BILLING_RESPONSE_RESULT_OK) {
        // Handle errors.
        ...
    }

    PendingIntent pendingIntent = buyIntentBundle.getParcelable(RESPONSE_BUY_INTENT);
    act.startIntentSenderForResult(pendingIntent.getIntentSender(),
                                   requestCode,
                                   new Intent(),
                                   Integer.valueOf(0),
                                   Integer.valueOf(0),
                                   Integer.valueOf(0));

    // Purchase is handled in onActivityResult illustrated in the previous section.
    

変更後

Kotlin

    val skuToSell = "premium_upgrade"
    val skuList = mutableListOf<String>()
    skuList.add(skuToSell)
    val params = SkuDetailsParams.newBuilder()
    params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP)
    billingClient.querySkuDetailsAsync(params.build(),
      object : SkuDetailsResponseListener {
        override fun onSkuDetailsResponse(
          billingResult: BillingResult?, skuDetailsList: MutableList<SkuDetails>?) {
            // Process the result.
            }
        })

    // SkuDetails object obtained above.
    val skuDetails = ...
    val purchaseParams = BillingFlowParams.newBuilder()
      .setSkuDetails(skuDetails)
       .build()

    billingClient.launchBillingFlow(activity, purchaseParams)

    // Purchase is handled in onPurchasesUpdated illustrated in the previous section
    

Java

    String skuToSell = "premium_upgrade";
    List<String> skuList = new ArrayList<> ();
    skuList.add(skuToSell);
    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.
                ...
            }
        });

    // SkuDetails object obtained above.
    SkuDetails skuDetails = ...;

    BillingFlowParams purchaseParams =
        BillingFlowParams.newBuilder()
                .setSkuDetails(skuDetails)
                .build();

    mBillingClient.launchBillingFlow(mActivity, purchaseParams);

    // Purchase is handled in onPurchasesUpdated illustrated in the previous section.
    

購入アイテムを消費する

Google Play Billing Library を使用して購入アイテムを消費する方法は次のとおりです。

次の例は、アプリの変更前と変更後のコードを示しています。

変更前

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        int responseCode = data.getIntExtra(RESPONSE_CODE);
        JSONObject purchaseData =
            new JSONObject(data.getStringExtra("INAPP_PURCHASE_DATA"));

        String token = purchaseData.get("purchaseToken");

        ...

        // Consume purchase
        int response = mService.consumePurchase(3, mContext.getPackageName(), token);
        if (response != BILLING_RESPONSE_RESULT_OK) {
            // Handle errors.
            ...
        }

        // Handle successful consumption.
    }
    

変更後

Kotlin

    class MyBillingImpl(private val billingClient: BillingClient) :
            ... , ConsumeResponseListener {

      fun consumePurchase(purchaseToken: String) {
        val consumeParams = ConsumeParams
          .newBuilder()
          .setPurchaseToken(purchaseToken)
          .build()
      }

      override fun onConsumeResponse(
        billingResult: BillingResult?,
        purchaseToken: String?) {
          // Handle consumption
        }
     }
    

Java

    public class MyBillingImpl implements ..., ConsumeResponseListener {
        private BillingClient billingClient;
        ...

        public void consumePurchase(String purchaseToken) {
            ConsumeParams consumeParams =
            ConsumeParams.newBuilder()
                    .setPurchaseToken(purchaseToken)
                    .build();
        }

        @Override
        void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
            // Handle consumption.
            ...
        }
    }
    

購入を承認する

Google Play Billing Library のバージョン 2.0 以降では、アプリですべての購入アイテムを消費するか、すべての購入を承認する必要があります。

3 日以内に購入アイテムを消費するか、購入を承認しないと、Google が自動的に購入を取り消し、ユーザーに払い戻しを行います。詳しくは、購入を承認するをご覧ください。

アプリ外購入を認識する

アプリ外購入の処理を Google Play Billing Library に移行する方法は次のとおりです。

  • アプリが BillingClient.queryPurchases() の呼び出しをアプリの onResume() コールバックで行っていることを確認します。
  • com.android.vending.billing.PURCHASES_UPDATED のブロードキャスト レシーバを削除し、対応するコールバックのコードを PurchasesUpdatedListener に移動します。

以前は、Google Play 請求サービスを AIDL と統合する場合、アプリ外購入を処理するには、アプリで com.android.vending.billing.PURCHASES_UPDATED インテントを受け取るリスナーを登録する必要がありました。

アプリが実行されていない間に行われたすべての購入が認識されるようにするには、最初のステップとして、Google Play Billing Library を使用して、queryPurchases() をアプリの onResume() コールバックで呼び出す必要があります。アプリが実行中の間は、Google Play Billing Library がアプリ外購入を自動的にリッスンし、PurchasesUpdatedListener を介して通知します。

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

Google Play Billing Library のバージョン 2.0 以降では、購入してから利用権を付与するまでの間に追加の操作が必要な保留中のトランザクションをアプリで処理する必要があります。たとえば、ユーザーが実店舗でアプリ内アイテムを現金で購入するようなケースがあります。その場合は、トランザクションがアプリの外部で完了することになります。このようなシナリオでは、ユーザーがトランザクションを完了した後にのみ利用権を付与する必要があります。

詳しくは、保留中のトランザクションをサポートするをご覧ください。

デベロッパー ペイロード

デベロッパー ペイロードは、不正行為の防止や、適切なユーザーへの購入のアトリビューションなど、さまざまな目的で使用されてきました。これらのユースケースは Google Play Billing Library でサポートされるようになったため、Google Play Billing Library のバージョン 2.2 以降ではデベロッパー ペイロードがサポートされなくなっています。詳しくは、デベロッパー ペイロードをご覧ください。

詳細なエラー メッセージ

Google Play Billing Library のバージョン 2.0 以降では、すべてのエラーに、対応するデバッグ関連のメッセージが用意されています。これらのメッセージを取得するには、BillingResult.getDebugMessage() を呼び出します。