Lưu ý: Kể từ ngày 2 tháng 8 năm 2022, tất cả các ứng dụng mới phải sử dụng Thư viện Billing phiên bản 4 trở lên. Chậm nhất vào ngày 1 tháng 11 năm 2022, tất cả các bản cập nhật cho ứng dụng hiện có đều phải sử dụng Thư viện Billing phiên bản 4 trở lên. Tìm hiểu thêm.

Hướng dẫn di chuyển AIDL sang Thư viện Google Play Billing

Sử dụng bộ sưu tập để sắp xếp ngăn nắp các trang Lưu và phân loại nội dung dựa trên lựa chọn ưu tiên của bạn.

Chủ đề này mô tả cách di chuyển khỏi công cụ thanh toán tích hợp bằng Ngôn ngữ định nghĩa giao diện Android (AIDL). Phương thức sử dụng AIDL để truy cập vào hệ thống thanh toán của Google Play không còn được dùng nữa và tất cả công cụ tích hợp sau này đều phải sử dụng Thư viện Google Play Billing.

Các bước di chuyển

Nhập Thư viện Google Play Billing

Trước tiên, hãy thêm phần phụ thuộc vào Thư viện Google Play Billing. Nếu đang sử dụng Gradle, bạn có thể thêm những thành phần sau vào tệp build.gradle của ứng dụng:

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")
}

Bạn có thể xoá bất kỳ mã "keo" (glue) nào, chẳng hạn như IabHelper mà có thể bạn đã sao chép từ mã tham chiếu trước đó. Chức năng do IabHelper cung cấp hiện có trong Thư viện Google Play Billing.

Xoá quyền com.android.vending.BILLING

Thư viện Google Play Billing nhúng quyền com.android.vending.BILLING vào tệp kê khai. Bạn không cần phải thêm quyền này một cách rõ ràng trong tệp kê khai của ứng dụng nữa.

Kết nối với Google Play Billing

Phương thức Thư viện Google Play Billing BillingClient sẽ giúp bạn quản lý kết nối. Để di chuyển, hãy thực hiện các thay đổi sau đây trong ứng dụng của bạn:

Các ví dụ sau cho bạn thấy ứng dụng trước và sau khi thực hiện các thay đổi này:

Trước

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;
}

Sau

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.
    }
}

Mua hàng

Để mở hộp thoại mua hàng, hãy làm như sau:

Các ví dụ sau cho bạn thấy ứng dụng trước và sau khi thực hiện các thay đổi này:

Trước

// 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.

Sau

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.

Tiêu thụ giao dịch mua

Để tiêu thụ giao dịch mua bằng Thư viện Google Play Billing, hãy làm như sau:

Các ví dụ sau cho bạn thấy ứng dụng trước và sau khi thực hiện các thay đổi này:

Trước

@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.
}

Sau

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.
        ...
    }
}

Xác nhận giao dịch mua

Bắt đầu từ phiên bản 2.0 của Thư viện Google Play Billing, ứng dụng của bạn phải tiêu thụ hoặc xác nhận tất cả giao dịch mua.

Nếu bạn không tiêu thụ hoặc xác nhận giao dịch mua trong vòng 3 ngày, thì Google sẽ tự động thu hồi giao dịch mua và hoàn lại tiền cho người dùng. Để biết thêm thông tin, hãy xem phần Xác nhận giao dịch mua.

Nhận biết các giao dịch mua ngoài ứng dụng

Để di chuyển tính năng xử lý giao dịch mua ngoài ứng dụng sang Thư viện Google Play Billing, hãy làm như sau:

Trước đây khi tích hợp Google Play Billing bằng AIDL, ứng dụng của bạn cần đăng ký một trình nghe để nhận ý định com.android.vending.billing.PURCHASES_UPDATED nhằm xử lý các giao dịch mua được thực hiện bên ngoài ứng dụng.

Với Thư viện Google Play Billing, ứng dụng của bạn luôn phải gọi queryPurchasesAsync() trong lệnh gọi lại onResume(). Đây là bước đầu tiên để đảm bảo rằng tất cả giao dịch mua được thực hiện trong khi ứng dụng không chạy đều được nhận dạng. Khi ứng dụng của bạn đang chạy, Thư viện Google Play Billing sẽ tự động lắng nghe các giao dịch mua bên ngoài ứng dụng, thông báo cho bạn thông qua PurchasesUpdatedListener.

Xử lý các giao dịch đang chờ

Bắt đầu từ phiên bản 2.0 của Thư viện Google Play Billing, trước khi cấp quyền, ứng dụng của bạn phải thực hiện các giao dịch đang chờ xử lý cần thao tác bổ sung sau khi mua. Chẳng hạn như người dùng có thể chọn mua sản phẩm trong ứng dụng của bạn tại cửa hàng thực tế bằng tiền mặt. Điều này có nghĩa là giao dịch sẽ được hoàn tất bên ngoài ứng dụng. Trong trường hợp này, bạn chỉ nên cấp quyền sau khi người dùng đã hoàn tất giao dịch.

Để biết thêm thông tin, hãy xem phần Hỗ trợ giao dịch đang chờ xử lý.

Trọng tải dành cho nhà phát triển

Trọng tải dành cho nhà phát triển trước đây được sử dụng cho nhiều mục đích như ngăn chặn lừa đảo và phân bổ giao dịch mua cho đúng người dùng. Vì Thư viện Google Play Billing hiện đã hỗ trợ các trường hợp sử dụng này nên bắt đầu từ phiên bản 2.2 của Thư viện, chúng tôi không dùng trọng tải dành cho nhà phát triển nữa. Để biết thêm thông tin, hãy xem bài viết về Trọng tải dành cho nhà phát triển.

Thông báo lỗi chi tiết

Bắt đầu từ phiên bản 2.0 của Thư viện Google Play Billing, tất cả lỗi đều có thông báo liên quan đến cách gỡ lỗi tương ứng. Bạn có thể xem các thông báo này bằng lệnh gọi BillingResult.getDebugMessage().