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 = "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") }
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:
- Tạo một thực thể của
BillingClient
. - Triển khai một
BillingClientStateListener
để nhận các lệnh gọi lại về trạng thái dịch vụ. - Gọi
startConnection()
trên thực thểBillingClient
của bạn. - Xoá mã
onActivityResult()
liên quan đến giao dịch mua trong ứng dụng và chuyển sangPurchasesUpdatedListener
.
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:
- Chuyển đổi thông tin chi tiết về SKU
Bundle
thànhSkuDetailsParams
. - Chuyển lệnh gọi sang
BillingClient.querySkuDetailsAsync()
thay vì sử dụngmService.getSkuDetails()
. - Chuyển đổi ý định mua
Bundle
thành đối tượngBillingFlowParams
. - Chuyển lệnh gọi sang
BillingClient.launchBillingFlow()
thay vì sử dụngmService.getBuyIntent()
. - Xoá mọi mã liên quan đến giao dịch mua trong ứng dụng khỏi
onActivityResult()
, rồi di chuyển mã này sangPurchasesUpdatedListener
.
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:
- Thay vì gọi
consumePurchase()
, hãy gọiBillingClient.consumeAsync()
. - Triển khai
ConsumeResponseListener
.
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:
- Đảm bảo ứng dụng của bạn gọi
BillingClient.queryPurchasesAsync()
trong lệnh gọi lạionResume()
của ứng dụng. - Xoá broadcast receiver cho
com.android.vending.billing.PURCHASES_UPDATED
và chuyển mã lệnh gọi lại tương ứng sangPurchasesUpdatedListener
.
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()
.