AIDL 移轉至 Google Play 帳款服務程式庫說明指南

本主題說明如何從使用 Android 介面定義語言 (AIDL) 的整合帳款服務中移轉出來。使用 AIDL 存取 Google Play 的帳款服務系統這種方式已淘汰,未來所有整合作業皆須使用 Google Play 帳款服務程式庫。

移轉步驟

匯入 Google Play 帳款服務程式庫

首先,在 Google Play 帳款服務程式庫中新增一個依附元件。如果您是使用 Gradle,可以將下列程式碼新增至應用程式的 build.gradle 檔中:

Groovy

dependencies {
    def billing_version = "6.1.0"

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

Kotlin

dependencies {
    val billing_version = "6.1.0"

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

您可以刪除任何「附著」的程式碼,例如 IabHelper,這些程式碼可能是您從之前的參照代碼複製而來。IabHelper 提供的功能現已併入 Google Play 帳款服務程式庫。

移除 com.android.vending.BILLING 權限

Google Play 帳款服務程式庫會在資訊清單中嵌入 com.android.vending.BILLING 權限。再也不需要在應用程式的資訊清單中明確新增這項權限。

連線至 Google Play 帳款服務

Google Play 帳款服務程式庫的 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 帳款服務程式庫消耗購買產品,請按照下列步驟操作:

以下範例為應用程式在變更前後的可能內容:

之前

@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 帳款服務程式庫 2.0 版開始,應用程式必須消耗或確認所有購買交易

如果您未在三天內消耗或確認購買交易,Google 會自動撤銷該購買交易並退款給使用者。詳情請參閱確認購買交易

辨識應用程式外購

如要將應用程式外購交易的處理程序遷移至 Google Play 帳款服務程式庫,請按照下列步驟操作:

之前當 Google Play 帳款服務與 AIDL 整合時,應用程式必須註冊一個事件監聽器才能接收 com.android.vending.billing.PURCHASES_UPDATED 意圖,以便處理應用程式外的購買交易。

使用 Google Play 帳款服務程式庫時,首先讓應用程式一律在其 onResume() 回呼中呼叫 queryPurchasesAsync(),以確保未執行應用程式時進行的所有購買交易都能獲得確認。應用程式執行時,Google Play 帳款服務程式庫會自動監聽應用程式外購,並透過 PurchasesUpdatedListener 通知您。

處理未完成交易

自 Google Play 帳款服務程式庫 2.0 版開始,應用程式必須先處理「未完成交易」,這些交易必須在購買後執行其他操作才能授予權限。舉例來說,使用者可能會選擇在實體商店以現金購買應用程式內產品。這代表交易是在應用程式外完成。在這種情況下,必須在使用者完成交易後再授予相關權限。

詳情請參閱支援未完成交易

開發人員酬載

過去開發人員酬載用於多種目的,包括防範詐欺行為以及將購買交易認定為來自正確的使用者。Google Play 帳款服務程式庫現在支援這些用途,因此從 Google Play 帳款服務程式庫 2.2 版開始已將開發人員酬載功能淘汰。詳情請參閱開發人員酬載

詳細錯誤訊息

從 Google Play 帳款服務程式庫 2.0 版開始,所有錯誤都會有對應的偵錯相關訊息。呼叫 BillingResult.getDebugMessage() 即可取得這些訊息。