提醒:自 2021 年 11 月 1 日起,現有應用程式的所有更新都必須採用帳款服務程式庫 3.0 以上版本。瞭解詳情

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

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

移轉步驟

匯入 Google Play 帳款服務程式庫

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

Groovy

dependencies {
    def billing_version = "4.1.0"

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

Kotlin

dependencies {
    val billing_version = "4.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() 即可取得這些訊息。