提醒:从 2022 年 8 月 2 日起,所有新应用都必须使用结算库版本 4 或更高版本。自 2022 年 11 月 1 日起,现有应用的所有新版本都必须使用结算库版本 4 或更高版本。了解详情

从 AIDL 迁移到 Google Play 结算库的迁移指南

使用集合让一切井井有条 根据您的偏好保存内容并对其进行分类。

本主题介绍如何脱离使用 Android 接口定义语言 (AIDL) 的结算服务集成。使用 AIDL 访问 Google Play 结算系统这种方式已被弃用,日后所有集成都必须使用 Google Play 结算库。

迁移步骤

导入 Google Play 结算库

首先,为 Google Play 结算库添加依赖项。如果您使用的是 Gradle,则可将以下内容添加到应用的 build.gradle 文件中:

Groovy

dependencies {
    def billing_version = "5.0.0"

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

Kotlin

dependencies {
    val billing_version = "5.0.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 intent,用于处理应用外购买交易。

使用 Google Play 结算库时,您应始终先在应用的 onResume() 回调中调用 queryPurchasesAsync(),以确保识别出在应用未运行期间完成的所有购买交易。在应用运行时,Google Play 结算库会自动监听应用外购买交易,并通过 PurchasesUpdatedListener 通知您。

处理待处理的交易

从 Google Play 结算库 2.0 版开始,应用必须处理某些待处理的交易,这些交易需要在购买后执行额外操作,然后才能授予使用权。例如,用户可能会选择使用现金在实体店购买您的应用内商品。也就是说,交易是在应用外部完成的。在这种情况下,只有在用户完成交易后,您才能授予使用权。

如需了解详情,请参阅支持待处理的交易

开发者载荷

开发者载荷向来被用于各种不同用途,包括防欺诈以及将购买交易归因于正确的用户。由于 Google Play 结算库现已支持这些用例,因此从 Google Play 结算库 2.2 版开始,我们已弃用开发者载荷。如需了解详情,请参阅开发者载荷

详细的错误消息

从 Google Play 结算库 2.0 版开始,所有错误都包含相应的调试相关消息。这些消息可以通过调用 BillingResult.getDebugMessage() 获得。