Tích hợp Thư viện Google Play Billing vào ứng dụng

Chủ đề này mô tả cách tích hợp Thư viện Google Play Billing vào ứng dụng để bắt đầu bán sản phẩm.

Vòng đời của một giao dịch mua hàng

Dưới đây là quy trình mua điển hình cho giao dịch mua hàng một lần hoặc một gói thuê bao.

  1. Hiển thị cho người dùng những sản phẩm mà họ có thể mua.
  2. Bắt đầu quy trình mua để người dùng chấp nhận giao dịch mua hàng.
  3. Xác minh giao dịch mua hàng trên máy chủ.
  4. Cung cấp nội dung cho người dùng.
  5. Xác nhận việc phân phối nội dung. Đối với các sản phẩm tiêu dùng, hãy tiến hành giao dịch mua để người dùng có thể mua lại mặt hàng đó.

Các gói thuê bao sẽ tự động gia hạn cho đến khi bị huỷ. Một gói thuê bao có thể trải qua các trạng thái sau:

  • Đang hoạt động: Người dùng hiện ở trạng thái tốt và có quyền truy cập vào gói thuê bao.
  • Đã huỷ: Người dùng đã huỷ nhưng vẫn có quyền truy cập vào gói thuê bao cho đến khi hết hạn.
  • Trong thời gian gia hạn: Người dùng gặp phải vấn đề thanh toán nhưng vẫn có quyền truy cập trong khi Google đang thử lại phương thức thanh toán.
  • Tạm ngưng: Người dùng gặp phải vấn đề thanh toán và không còn quyền truy cập trong khi Google đang thử lại phương thức thanh toán.
  • Đang tạm dừng: Người dùng đã tạm dừng quyền truy cập và không có quyền truy cập cho đến khi họ tiếp tục.
  • Đã hết hạn: Người dùng đã huỷ và mất quyền truy cập vào gói thuê bao. Người dùng được coi là rời bỏ ứng dụng khi gói thuê bao hết hạn.

Khởi động kết nối với Google Play

Bước đầu tiên để tích hợp với hệ thống thanh toán của Google Play là thêm Thư viện Google Play Billing vào ứng dụng của bạn và khởi động kết nối.

Thêm phần phụ thuộc Thư viện Google Play Billing

Thêm phần phụ thuộc Thư viện Google Play Billing vào tệp build.gradle của ứng dụng như dưới đây:

Groovy

dependencies {
    def billing_version = "7.1.1"

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

Kotlin

dependencies {
    val billing_version = "7.1.1"

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

Nếu bạn đang sử dụng Kotlin, mô-đun KTX của Thư viện Google Play Billing chứa các tiện ích và coroutine của Kotlin, cho phép bạn viết mã Kotlin tương thích khi sử dụng Thư viện Google Play Billing. Để đưa các phần mở rộng này vào dự án, hãy thêm phần phụ thuộc sau vào tệp build.gradle của ứng dụng như sau:

Groovy

dependencies {
    def billing_version = "7.1.1"

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

Kotlin

dependencies {
    val billing_version = "7.1.1"

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

Khởi động BillingClient

Sau khi thêm một phần phụ thuộc vào Thư viện Google Play Billing, bạn cần khởi động một bản sao của BillingClient. BillingClient là giao diện chính để giao tiếp giữa Thư viện Google Play Billing và các phần còn lại của ứng dụng. BillingClient cung cấp các phương thức thuận tiện (cả đồng bộ và không đồng bộ) cho nhiều hoạt động thanh toán phổ biến. Hãy lưu ý những điều sau:

  • Bạn nên mở một kết nối BillingClient đang hoạt động cùng một lúc để tránh tạo ra nhiều lệnh gọi lại PurchasesUpdatedListener cho một sự kiện duy nhất.
  • Bạn nên bắt đầu kết nối cho BillingClient khi ứng dụng của bạn được khởi chạy hoặc chuyển sang nền trước để đảm bảo ứng dụng xử lý các giao dịch mua kịp thời. Bạn có thể thực hiện việc này bằng cách sử dụng ActivityLifecycleCallbacks do registerActivityLifecycleCallbacks đăng ký và theo dõi onActivityResumed để khởi chạy kết nối khi bạn phát hiện một hoạt động đang được tiếp tục lần đầu tiên. Hãy tham khảo phần xử lý giao dịch mua để biết thêm thông tin chi tiết về lý do bạn nên làm theo phương pháp hay nhất này. Ngoài ra, hãy nhớ kết thúc kết nối khi ứng dụng của bạn đóng.

Để tạo BillingClient, hãy sử dụng newBuilder. Bạn có thể truyền bất kỳ ngữ cảnh nào đến hàm newBuilder(), đồng thời BillingClient sử dụng hàm này để nhận ngữ cảnh của ứng dụng. Điều đó có nghĩa là bạn không cần lo lắng về việc rò rỉ bộ nhớ. Để nhận thông tin cập nhật về giao dịch mua, bạn cũng phải gọi hàm setListener bằng cách truyền tham chiếu đến một PurchasesUpdatedListener. Trình nghe này nhận nội dung cập nhật cho tất cả giao dịch mua trong ứng dụng.

Kotlin

private val purchasesUpdatedListener =
   PurchasesUpdatedListener { billingResult, purchases ->
       // To be implemented in a later section.
   }

private var billingClient = BillingClient.newBuilder(context)
   .setListener(purchasesUpdatedListener)
   // Configure other settings.
   .build()

Java

private PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() {
    @Override
    public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
        // To be implemented in a later section.
    }
};

private BillingClient billingClient = BillingClient.newBuilder(context)
    .setListener(purchasesUpdatedListener)
    // Configure other settings.
    .build();

Kết nối với Google Play

Sau khi đã tạo một BillingClient, bạn cần thiết lập kết nối với Google Play.

Để kết nối với Google Play, hãy gọi hàm startConnection. Quá trình kết nối không đồng bộ và bạn phải triển khai BillingClientStateListener để nhận một lệnh gọi lại sau khi ứng dụng được thiết lập và sẵn sàng tạo thêm yêu cầu.

Bạn cũng phải triển khai logic thử lại để xử lý việc mất kết nối với Google Play. Để triển khai logic thử lại, hãy ghi đè phương pháp gọi lại onBillingServiceDisconnected() và đảm bảo rằng BillingClient gọi phương thức startConnection() để kết nối lại với Google Play trước khi tạo yêu cầu tiếp theo.

Ví dụ sau đây minh hoạ cách bắt đầu một kết nối và kiểm thử xem kết nối đã sẵn sàng để sử dụng chưa:

Kotlin

billingClient.startConnection(object : BillingClientStateListener {
    override fun onBillingSetupFinished(billingResult: BillingResult) {
        if (billingResult.responseCode ==  BillingResponseCode.OK) {
            // The BillingClient is ready. You can query purchases here.
        }
    }
    override fun onBillingServiceDisconnected() {
        // Try to restart the connection on the next request to
        // Google Play by calling the startConnection() method.
    }
})

Java

billingClient.startConnection(new BillingClientStateListener() {
    @Override
    public void onBillingSetupFinished(BillingResult billingResult) {
        if (billingResult.getResponseCode() ==  BillingResponseCode.OK) {
            // The BillingClient is ready. You can query purchases here.
        }
    }
    @Override
    public void onBillingServiceDisconnected() {
        // Try to restart the connection on the next request to
        // Google Play by calling the startConnection() method.
    }
});

Hiện các sản phẩm có sẵn để mua

Sau khi thiết lập kết nối với Google Play, bạn có thể truy vấn các sản phẩm mà mình hiện có và cho người dùng thấy các sản phẩm đó.

Truy vấn thông tin chi tiết về sản phẩm là một bước quan trọng trước khi hiển thị sản phẩm cho người dùng vì việc truy vấn này sẽ trả về thông tin sản phẩm đã bản địa hoá. Đối với gói thuê bao, hãy đảm bảo cách sản phẩm xuất hiện phải tuân thủ tất cả các chính sách của Play.

Để truy vấn thông tin chi tiết về sản phẩm trong ứng dụng, hãy gọi queryProductDetailsAsync.

Để xử lý kết quả của hoạt động không đồng bộ, bạn cũng phải chỉ định một trình nghe có chức năng triển khai giao diện ProductDetailsResponseListener. Sau đó, bạn có thể ghi đè onProductDetailsResponse để thông báo cho trình nghe khi truy vấn kết thúc, như minh hoạ trong ví dụ sau:

Kotlin

val queryProductDetailsParams =
    QueryProductDetailsParams.newBuilder()
        .setProductList(
            ImmutableList.of(
                Product.newBuilder()
                    .setProductId("product_id_example")
                    .setProductType(ProductType.SUBS)
                    .build()))
        .build()

billingClient.queryProductDetailsAsync(queryProductDetailsParams) {
    billingResult,
    productDetailsList ->
      // check billingResult
      // process returned productDetailsList
}

Java

QueryProductDetailsParams queryProductDetailsParams =
    QueryProductDetailsParams.newBuilder()
        .setProductList(
            ImmutableList.of(
                Product.newBuilder()
                    .setProductId("product_id_example")
                    .setProductType(ProductType.SUBS)
                    .build()))
        .build();

billingClient.queryProductDetailsAsync(
    queryProductDetailsParams,
    new ProductDetailsResponseListener() {
        public void onProductDetailsResponse(BillingResult billingResult,
                List<ProductDetails> productDetailsList) {
            // check billingResult
            // process returned productDetailsList
        }
    }
)

Khi truy vấn thông tin chi tiết về sản phẩm, hãy chuyển một bản sao của QueryProductDetailsParams để chỉ định danh sách các chuỗi mã sản phẩm được tạo trong Google Play Console cùng với một ProductType. ProductType có thể là ProductType.INAPP đối với các sản phẩm tính phí một lần hoặc ProductType.SUBS đối với các gói thuê bao.

Truy vấn bằng tiện ích Kotlin

Nếu đang sử dụng tiện ích Kotlin, bạn có thể truy vấn thông tin chi tiết về sản phẩm trong ứng dụng bằng cách gọi hàm mở rộng queryProductDetails().

queryProductDetails() tận dụng coroutine của Kotlin để bạn không cần xác định một trình nghe riêng biệt. Thay vào đó, hàm này sẽ tạm ngưng cho đến khi truy vấn hoàn tất, sau đó bạn có thể xử lý kết quả:

suspend fun processPurchases() {
    val productList = listOf(
        QueryProductDetailsParams.Product.newBuilder()
            .setProductId("product_id_example")
            .setProductType(BillingClient.ProductType.SUBS)
            .build()
    )
    val params = QueryProductDetailsParams.newBuilder()
    params.setProductList(productList)

    // leverage queryProductDetails Kotlin extension function
    val productDetailsResult = withContext(Dispatchers.IO) {
        billingClient.queryProductDetails(params.build())
    }

    // Process the result.
}

Trong một số ít trường hợp, một số thiết bị không thể hỗ trợ ProductDetailsqueryProductDetailsAsync(), thường là do các phiên bản Dịch vụ Google Play đã lỗi thời. Để đảm bảo nhận được sự hỗ trợ phù hợp cho trường hợp này, hãy tìm hiểu cách sử dụng tính năng tương thích ngược trong hướng dẫn di chuyển Thư viện Play Billing 5.

Xử lý kết quả

Thư viện Google Play Billing lưu trữ các kết quả truy vấn trong một List các đối tượng ProductDetails. Sau đó, bạn có thể gọi nhiều phương thức cho mỗi đối tượng ProductDetails trong danh sách để xem thông tin liên quan về một sản phẩm trong ứng dụng, chẳng hạn như giá hoặc phần mô tả. Để xem thông tin chi tiết về sản phẩm có sẵn, hãy xem danh sách các phương thức trong lớp ProductDetails.

Trước khi chào bán một mặt hàng, hãy kiểm tra để đảm bảo rằng người dùng chưa sở hữu mặt hàng đó. Nếu người dùng có một sản phẩm tiêu dùng vẫn còn trong thư viện mặt hàng, họ phải dùng mặt hàng đó thì mới có thể mua lại.

Trước khi đưa ra đề nghị về một gói thuê bao, hãy xác minh rằng người dùng chưa đăng ký. Ngoài ra, hãy lưu ý những điều sau:

  • queryProductDetailsAsync() trả về thông tin chi tiết của sản phẩm thuê bao và tối đa 50 lựa chọn cho mỗi thuê bao.
  • queryProductDetailsAsync() chỉ trả về những lựa chọn người dùng đủ điều kiện. Nếu người dùng cố gắng mua một lựa chọn họ không đủ điều kiện (ví dụ: nếu ứng dụng đang đưa ra danh sách lựa chọn đủ điều kiện nhưng đã hết hạn), thì Play sẽ thông báo cho người dùng rằng họ không đủ điều kiện, và họ có thể chuyển sang mua gói cơ bản.

Bắt đầu quy trình mua

Để bắt đầu một yêu cầu mua hàng từ ứng dụng của bạn, hãy gọi phương thức launchBillingFlow() từ luồng chính của ứng dụng. Phương thức này tham chiếu đến một đối tượng BillingFlowParams chứa đối tượng ProductDetails liên quan đã nhận được từ việc gọi queryProductDetailsAsync. Để tạo một đối tượng BillingFlowParams, hãy dùng lớp BillingFlowParams.Builder.

Kotlin

// An activity reference from which the billing flow will be launched.
val activity : Activity = ...;

val productDetailsParamsList = listOf(
    BillingFlowParams.ProductDetailsParams.newBuilder()
        // retrieve a value for "productDetails" by calling queryProductDetailsAsync()
        .setProductDetails(productDetails)
        // For One-time products, "setOfferToken" method shouldn't be called.
        // For subscriptions, to get an offer token, call ProductDetails.subscriptionOfferDetails()
        // for a list of offers that are available to the user
        .setOfferToken(selectedOfferToken)
        .build()
)

val billingFlowParams = BillingFlowParams.newBuilder()
    .setProductDetailsParamsList(productDetailsParamsList)
    .build()

// Launch the billing flow
val billingResult = billingClient.launchBillingFlow(activity, billingFlowParams)

Java

// An activity reference from which the billing flow will be launched.
Activity activity = ...;

ImmutableList<ProductDetailsParams> productDetailsParamsList =
    ImmutableList.of(
        ProductDetailsParams.newBuilder()
             // retrieve a value for "productDetails" by calling queryProductDetailsAsync()
            .setProductDetails(productDetails)
            // For one-time products, "setOfferToken" method shouldn't be called.
            // For subscriptions, to get an offer token, call
            // ProductDetails.subscriptionOfferDetails() for a list of offers
            // that are available to the user.
            .setOfferToken(selectedOfferToken)
            .build()
    );

BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
    .setProductDetailsParamsList(productDetailsParamsList)
    .build();

// Launch the billing flow
BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams);

Phương thức launchBillingFlow() trả về một trong nhiều mã phản hồi được liệt kê trong BillingClient.BillingResponseCode. Hãy kiểm tra kết quả này để đảm bảo không có lỗi khi bắt đầu quy trình mua. BillingResponseCodeOK cho biết đã chạy thành công.

Khi bạn gọi hàm launchBillingFlow() thành công, hệ thống sẽ hiển thị màn hình mua hàng trên Google Play. Hình 1 hiển thị màn hình mua cho một gói thuê bao:

màn hình mua hàng trên Google Play cho thấy gói thuê bao đã sẵn có để mua
Hình 1. Màn hình mua hàng trên Google Play hiện một gói thuê bao mà bạn có thể mua.

Google Play gọi hàm onPurchasesUpdated() để cung cấp kết quả của thao tác mua hàng cho một trình nghe có nhiệm vụ triển khai giao diện PurchasesUpdatedListener. Trình nghe này được chỉ định bằng phương thức setListener() khi bạn khởi động ứng dụng của mình.

Bạn phải thực thi onPurchasesUpdated() để xử lý các mã phản hồi khả thi. Ví dụ sau đây trình bày cách ghi đè onPurchasesUpdated():

Kotlin

override fun onPurchasesUpdated(billingResult: BillingResult, purchases: List<Purchase>?) {
   if (billingResult.responseCode == BillingResponseCode.OK && purchases != null) {
       for (purchase in purchases) {
           // Process the purchase as described in the next section.
       }
   } else if (billingResult.responseCode == BillingResponseCode.USER_CANCELED) {
       // Handle an error caused by a user canceling the purchase flow.
   } else {
       // Handle any other error codes.
   }
}

Java

@Override
void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
    if (billingResult.getResponseCode() == BillingResponseCode.OK
        && purchases != null) {
        for (Purchase purchase : purchases) {
            // Process the purchase as described in the next section.
        }
    } else if (billingResult.getResponseCode() == BillingResponseCode.USER_CANCELED) {
        // Handle an error caused by a user canceling the purchase flow.
    } else {
        // Handle any other error codes.
    }
}

Giao dịch mua thành công sẽ tạo ra một màn hình mua hàng thành công trên Google Play, tương tự như hình 2.

màn hình giao dịch mua thành công trên google play
Hình 2. Màn hình giao dịch mua thành công của Google Play.

Một lượt mua hàng thành công cũng tạo ra một mã mua hàng. Mã này là giá trị nhận dạng duy nhất, đại diện cho người dùng và mã sản phẩm của sản phẩm trong ứng dụng mà họ đã mua. Ứng dụng có thể lưu trữ cục bộ mã thông báo giao dịch mua, nhưng bạn nên truyền mã thông báo này tới máy chủ phụ trợ bảo mật của bạn. Tại máy chủ đó, bạn có thể xác minh và bảo vệ giao dịch mua khỏi hành vi gian lận. Quy trình này được mô tả kỹ hơn trong phần Phát hiện và xử lý giao dịch mua.

Người dùng cũng sẽ nhận được biên nhận giao dịch chứa mã đơn hàng hoặc một mã nhận dạng duy nhất của giao dịch qua email. Người dùng sẽ nhận được một email kèm mã đơn hàng duy nhất cho mỗi lần mua sản phẩm tính phí một lần, cũng như giao dịch mua gói thuê bao lần đầu và những lần gia hạn tự động định kỳ tiếp theo. Bạn có thể sử dụng mã đơn hàng để quản lý việc hoàn tiền trong Google Play Console.

Báo giá dành riêng cho bạn

Nếu bạn có thể phân phối ứng dụng cho người dùng ở Liên minh Châu Âu, hãy sử dụng phương thức setIsOfferPersonalized() khi gọi launchBillingFlow để cho người dùng biết rằng giá của mặt hàng đã được cá nhân hoá bằng tính năng ra quyết định tự động.

Màn hình mua hàng trên Google Play cho biết cho biết rằng giá đã được điều chỉnh để phù hợp với người dùng.
Hình 3. Màn hình mua hàng trên Google Play cho biết giá này được điều chỉnh cho phù hợp với người dùng.

Bạn phải tham khảo qua Art. 6 (1) (ea) CRD của Chỉ thị về quyền của người tiêu dùng 2011/83/EU để xác định xem giá bạn cung cấp cho người dùng có được cá nhân hoá hay không.

setIsOfferPersonalized() nhận giá trị nhập boolean. Khi true, giao diện người dùng Play có bao gồm thông tin công bố. Khi false, giao diện người dùng bỏ qua thông tin công bố. Giá trị mặc định là false.

Hãy truy cập vào Trung tâm trợ giúp người tiêu dùng để biết thêm thông tin.

Đính kèm giá trị nhận dạng người dùng

Khi bạn chạy quy trình mua, ứng dụng của bạn có thể đính kèm bất kỳ giá trị nhận dạng người dùng nào mà bạn có cho người dùng thực hiện giao dịch mua bằng cách sử dụng obfuscatedAccountId hoặc obfuscatedProfileId. Một mã nhận dạng mẫu có thể là phiên bản đã làm rối mã đăng nhập của người dùng trong hệ thống. Việc thiết lập các tham số này có thể giúp Google phát hiện hành vi gian lận. Ngoài ra, tính năng này có thể giúp bạn đảm bảo rằng giao dịch mua được phân bổ cho đúng người dùng như đã thảo luận trong phần cấp quyền cho người dùng.

Phát hiện và xử lý giao dịch mua

Việc phát hiện và xử lý giao dịch mua được mô tả trong phần này áp dụng cho tất cả các loại giao dịch mua, bao gồm cả giao dịch mua bên ngoài ứng dụng như sử dụng mã khuyến mãi.

Ứng dụng của bạn phát hiện giao dịch mua mới và giao dịch mua đang chờ xử lý đã hoàn tất theo một trong các cách sau:

  1. Khi onPurchasesUpdated được gọi do ứng dụng gọi launchBillingFlow (như đã thảo luận trong phần trước) hoặc nếu ứng dụng của bạn đang chạy với kết nối Thư viện thanh toán đang hoạt động khi có giao dịch mua được thực hiện bên ngoài ứng dụng hoặc giao dịch mua đang chờ xử lý đã hoàn tất. Ví dụ: một thành viên gia đình phê duyệt giao dịch mua đang chờ xử lý trên một thiết bị khác.
  2. Khi ứng dụng gọi queryPurchasesAsync để truy vấn giao dịch mua của người dùng.

Đối với trường hợp #1, onPurchasesUpdated sẽ tự động được gọi cho các giao dịch mua mới hoặc đã hoàn tất miễn là ứng dụng của bạn đang chạy và có kết nối Thư viện Google Play Billing đang hoạt động. Nếu ứng dụng của bạn không chạy hoặc không có kết nối thư viện Google Play Billing đang hoạt động, thì onPurchasesUpdated sẽ không được gọi. Hãy nhớ rằng ứng dụng của bạn nên cố gắng duy trì kết nối đang hoạt động miễn là ứng dụng chạy trên nền trước để đảm bảo ứng dụng nhận được thông tin cập nhật kịp thời về giao dịch mua.

Đối với trường hợp #2, bạn phải gọi BillingClient.queryPurchasesAsync() để đảm bảo ứng dụng xử lý tất cả giao dịch mua. Bạn nên thực hiện việc này khi ứng dụng của bạn thiết lập thành công kết nối với Thư viện Google Play Billing (nên thực hiện khi ứng dụng của bạn được khởi chạy hoặc chuyển sang nền trước như đã thảo luận trong phần khởi chạy BillingClient). Bạn có thể thực hiện việc này bằng cách gọi queryPurchasesAsync khi nhận được kết quả thành công cho onServiceConnected. Bạn cần tuân thủ đề xuất này để xử lý các sự kiện và tình huống như:

  • Các vấn đề về mạng trong khi mua hàng: Người dùng có thể mua hàng thành công và nhận được xác nhận của Google, nhưng thiết bị của họ bị mất kết nối mạng trước khi thiết bị và ứng dụng của bạn nhận được thông báo về giao dịch mua thông qua PurchasesUpdatedListener.
  • Nhiều thiết bị: Người dùng có thể mua một mặt hàng trên một thiết bị và kỳ vọng thấy mặt hàng đó khi chuyển đổi thiết bị.
  • Xử lý giao dịch mua thực hiện bên ngoài ứng dụng: Người dùng có thể thực hiện một số giao dịch mua (chẳng hạn như sử dụng các mã khuyến mãi) bên ngoài ứng dụng của bạn.
  • Xử lý quá trình chuyển đổi trạng thái mua hàng: Người dùng có thể hoàn tất thanh toán cho một giao dịch mua PENDING (ĐANG CHỜ) trong khi ứng dụng của bạn không chạy và mong nhận được xác nhận rằng họ đã hoàn tất giao dịch mua khi mở ứng dụng.

Sau khi phát hiện một giao dịch mua mới hoặc đã hoàn tất, ứng dụng của bạn phải:

  • Xác minh giao dịch mua hàng.
  • Cấp nội dung cho người dùng đối với các giao dịch mua đã hoàn tất.
  • Thông báo cho người dùng.
  • Thông báo cho Google rằng ứng dụng của bạn đã xử lý các giao dịch mua đã hoàn tất.

Các bước này được thảo luận chi tiết trong các phần sau, theo sau là một phần tóm tắt tất cả các bước.

Xác minh giao dịch mua

Ứng dụng của bạn phải luôn xác minh các giao dịch mua để đảm bảo chúng hợp lệ trước khi cấp lợi ích cho người dùng. Bạn có thể thực hiện việc này bằng cách làm theo các nguyên tắc được mô tả trong phần Xác minh giao dịch mua trước khi cấp quyền. Chỉ sau khi xác minh giao dịch mua, ứng dụng của bạn mới tiếp tục xử lý giao dịch mua và cấp quyền cho người dùng. Nội dung này sẽ được thảo luận trong phần tiếp theo.

Cấp quyền cho người dùng

Sau khi xác minh giao dịch mua, ứng dụng có thể tiếp tục cấp quyền cho người dùng và thông báo cho người dùng. Trước khi cấp quyền, hãy đảm bảo rằng ứng dụng của bạn đang kiểm tra để đảm bảo trạng thái mua hàngPURCHASED. Nếu giao dịch mua ở trạng thái ĐANG CHỜ, ứng dụng của bạn phải thông báo cho người dùng rằng họ vẫn cần hoàn tất các hành động để hoàn tất giao dịch mua trước khi được cấp quyền. Chỉ cấp quyền khi giao dịch mua chuyển từ trạng thái ĐANG CHỜ XỬ LÝ sang THÀNH CÔNG. Bạn có thể xem thêm thông tin trong phần Xử lý giao dịch đang chờ xử lý.

Nếu đã đính kèm giá trị nhận dạng người dùng vào giao dịch mua như đã thảo luận trong phần đính kèm giá trị nhận dạng người dùng, thì bạn có thể truy xuất và sử dụng các giá trị nhận dạng đó để phân bổ cho đúng người dùng trong hệ thống. Kỹ thuật này hữu ích khi điều chỉnh các giao dịch mua mà ứng dụng của bạn có thể đã mất ngữ cảnh về người dùng thực hiện giao dịch mua. Xin lưu ý rằng các giao dịch mua được thực hiện bên ngoài ứng dụng sẽ không được đặt giá trị nhận dạng này. Trong trường hợp này, ứng dụng của bạn có thể cấp quyền cho người dùng đã đăng nhập hoặc nhắc người dùng chọn một tài khoản ưu tiên.

Thông báo cho người dùng

Sau khi cấp quyền cho người dùng, ứng dụng của bạn sẽ hiển thị thông báo xác nhận giao dịch mua thành công. Điều này giúp đảm bảo người dùng không nhầm lẫn về việc giao dịch mua đã hoàn tất thành công hay chưa, điều này có thể khiến người dùng ngừng sử dụng ứng dụng, liên hệ với bộ phận hỗ trợ người dùng hoặc phàn nàn về việc đó trên mạng xã hội. Xin lưu ý rằng ứng dụng của bạn có thể phát hiện nội dung cập nhật về giao dịch mua bất cứ lúc nào trong vòng đời của ứng dụng. Ví dụ: cha mẹ phê duyệt một giao dịch mua đang chờ xử lý trên một thiết bị khác. Trong trường hợp này, ứng dụng của bạn có thể muốn trì hoãn việc thông báo cho người dùng đến một thời điểm thích hợp. Sau đây là một số ví dụ về trường hợp bạn nên sử dụng độ trễ:

  • Khi họ đang tham gia vào phần hành động của một trò chơi hoặc đoạn phim cắt cảnh, việc hiện thông báo có thể khiến người dùng mất tập trung. Trong trường hợp này, bạn phải thông báo cho người dùng sau khi phần hành động này kết thúc.
  • Trong quá trình hướng dẫn ban đầu và thiết lập người dùng của trò chơi. Ví dụ: người dùng có thể đã mua hàng bên ngoài ứng dụng của bạn trước khi cài đặt ứng dụng đó. Bạn nên thông báo cho người dùng mới về phần thưởng ngay sau khi họ mở trò chơi hoặc trong quá trình thiết lập người dùng ban đầu. Nếu ứng dụng của bạn yêu cầu người dùng tạo tài khoản hoặc đăng nhập trước khi cấp quyền cho người dùng, bạn nên thông báo cho người dùng về các bước cần hoàn tất để xác nhận giao dịch mua. Điều này rất quan trọng vì các giao dịch mua sẽ được hoàn tiền sau 3 ngày nếu ứng dụng của bạn chưa xử lý giao dịch mua.

Khi thông báo cho người dùng về một giao dịch mua, Google Play đề xuất các cơ chế sau:

  • Hiển thị hộp thoại trong ứng dụng.
  • Chuyển thông báo tới hộp thông báo trong ứng dụng và nêu rõ rằng có một tin nhắn mới trong hộp thông báo trong ứng dụng.
  • Sử dụng một nội dung thông báo trên hệ điều hành.

Thông báo phải thông báo cho người dùng về lợi ích mà họ nhận được. Ví dụ: "Bạn đã mua 100 Vàng!". Ngoài ra, nếu giao dịch mua là do một lợi ích của một chương trình như Play Pass, thì ứng dụng của bạn sẽ thông báo điều này cho người dùng. Ví dụ: "Đã nhận mặt hàng! Bạn vừa nhận được 100 viên ngọc bằng Play Pass. Tiếp tục". Mỗi chương trình có thể có hướng dẫn về văn bản đề xuất để hiển thị cho người dùng nhằm truyền đạt các lợi ích.

Thông báo cho Google về việc giao dịch mua đã được xử lý

Sau khi cấp quyền cho người dùng và thông báo cho họ về giao dịch thành công, ứng dụng cần thông báo cho Google rằng giao dịch mua đã được xử lý thành công. Bạn có thể thực hiện việc này bằng cách xác nhận giao dịch mua và phải thực hiện trong vòng 3 ngày để đảm bảo giao dịch mua không bị tự động hoàn tiền và quyền không bị thu hồi. Quy trình xác nhận các loại giao dịch mua được mô tả trong các phần sau.

Sản phẩm tiêu dùng

Đối với sản phẩm tiêu dùng, nếu ứng dụng có phần phụ trợ bảo mật, bạn nên sử dụng Purchases.products:consume để tiêu thụ sản phẩm được mua theo cách đáng tin cậy. Đảm bảo giao dịch mua hàng chưa được sử dụng bằng cách kiểm tra consumptionState trong kết quả của lệnh gọi Purchases.products:get. Nếu ứng dụng của bạn chỉ dành cho máy khách mà không có phần phụ trợ, hãy sử dụng consumeAsync() trong Thư viện Google Play Billing. Cả hai phương thức đều đáp ứng yêu cầu xác nhận và cho biết rằng ứng dụng đã cấp quyền cho người dùng. Các phương thức này cũng cho phép ứng dụng tạo sản phẩm tính phí một lần tương ứng với mã thông báo giao dịch mua hàng đầu vào hiện có để mua lại. Với consumeAsync(), bạn cũng phải truyền một đối tượng sẽ triển khai giao diện ConsumeResponseListener. Đối tượng này xử lý kết quả của hoạt động tiêu thụ. Bạn có thể ghi đè phương thức onConsumeResponse() mà Thư viện Google Play Billing này sẽ gọi khi hoạt động hoàn tất.

Ví dụ sau minh hoạ việc tiêu thụ một sản phẩm bằng Thư viện Google Play Billing khi sử dụng mã mua hàng được liên kết:

Kotlin

    val consumeParams =
        ConsumeParams.newBuilder()
            .setPurchaseToken(purchase.getPurchaseToken())
            .build()
    val consumeResult = withContext(Dispatchers.IO) {
        client.consumePurchase(consumeParams)
    }

Java

    ConsumeParams consumeParams =
            ConsumeParams.newBuilder()
                .setPurchaseToken(purchase.getPurchaseToken())
                .build();

    ConsumeResponseListener listener = new ConsumeResponseListener() {
        @Override
        public void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
            if (billingResult.getResponseCode() == BillingResponseCode.OK) {
                // Handle the success of the consume operation.
            }
        }
    };

    billingClient.consumeAsync(consumeParams, listener);

Sản phẩm không phải hàng tiêu dùng

Để xác nhận giao dịch mua sản phẩm không phải hàng tiêu dùng, nếu ứng dụng có phần phụ trợ bảo mật, bạn nên sử dụng Purchases.products:acknowledge để xác nhận giao dịch mua hàng một cách đáng tin cậy. Đảm bảo giao dịch mua hàng chưa được xác nhận trước đó bằng cách kiểm tra acknowledgementState trong kết quả của lệnh gọi Purchases.products:get.

Nếu ứng dụng của bạn chỉ dành cho máy khách, hãy dùng BillingClient.acknowledgePurchase() từ Thư viện Google Play Billing trong ứng dụng của bạn. Trước khi xác nhận giao dịch mua hàng, ứng dụng của bạn phải kiểm tra xem giao dịch đã được xác nhận hay chưa bằng cách dùng phương thức isAcknowledged() trong Thư viện Google Play Billing.

Ví dụ sau cho biết cách xác nhận một giao dịch mua hàng bằng cách sử dụng Thư viện Google Play Billing:

Kotlin

val client: BillingClient = ...
val acknowledgePurchaseResponseListener: AcknowledgePurchaseResponseListener = ...

val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
    .setPurchaseToken(purchase.purchaseToken)
val ackPurchaseResult = withContext(Dispatchers.IO) {
     client.acknowledgePurchase(acknowledgePurchaseParams.build())
}

Java

BillingClient client = ...
AcknowledgePurchaseResponseListener acknowledgePurchaseResponseListener = ...

AcknowledgePurchaseParams acknowledgePurchaseParams =
                AcknowledgePurchaseParams.newBuilder()
                    .setPurchaseToken(purchase.getPurchaseToken())
                    .build();
 client.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener);

Gói thuê bao

Gói thuê bao được xử lý tương tự như sản phẩm không phải hàng tiêu dùng. Nếu có thể, hãy sử dụng Purchases.subscriptions.acknowledge từ API Nhà phát triển Google Play để xác nhận một cách đáng tin cậy giao dịch mua hàng từ phần phụ trợ bảo mật của bạn. Xác minh rằng giao dịch mua hàng chưa được xác nhận trước đó bằng cách kiểm tra acknowledgementState trong tài nguyên giao dịch mua hàng từ Purchases.subscriptions:get. Nếu không, bạn có thể xác nhận gói thuê bao bằng cách sử dụng BillingClient.acknowledgePurchase() từ Thư viện Google Play Billing sau khi kiểm tra isAcknowledged(). Bạn cần xác nhận tất cả các giao dịch mua gói thuê bao ban đầu. Bạn không cần xác nhận việc gia hạn gói thuê bao. Để biết thêm thông tin về thời điểm cần xác nhận gói thuê bao, hãy xem chủ đề Bán gói thuê bao.

Tóm tắt

Đoạn mã sau đây tóm tắt các bước này.

Kotlin

fun handlePurchase(Purchase purchase) {
    // Purchase retrieved from BillingClient#queryPurchasesAsync or your
    // onPurchasesUpdated.
    Purchase purchase = ...;

    // Step 1: Send the purchase to your secure backend to verify the purchase
    // following
    // https://developer.android.com/google/play/billing/security#verify
.
    // Step 2: Update your entitlement storage with the purchase. If purchase is
    // in PENDING state then ensure the entitlement is marked as pending and the
    // user does not receive benefits yet. It is recommended that this step is
    // done on your secure backend and can combine in the API call to your
    // backend in step 1.

    // Step 3: Notify the user using appropriate messaging (delaying
    // notification if needed as discussed above).

    // Step 4: Notify Google the purchase was processed using the steps
    // discussed in the processing purchases section.
}

Java

void handlePurchase(Purchase purchase) {
    // Purchase retrieved from BillingClient#queryPurchasesAsync or your
    // onPurchasesUpdated.
    Purchase purchase = ...;

    // Step 1: Send the purchase to your secure backend to verify the purchase
    // following
    // https://developer.android.com/google/play/billing/security#verify

    // Step 2: Update your entitlement storage with the purchase. If purchase is
    // in PENDING state then ensure the entitlement is marked as pending and the
    // user does not receive benefits yet. It is recommended that this step is
    // done on your secure backend and can combine in the API call to your
    // backend in step 1.

    // Step 3: Notify the user using appropriate messaging (delaying
    // notification if needed as discussed above).

    // Step 4: Notify Google the purchase was processed using the steps
    // discussed in the processing purchases section.
}

Để xác minh ứng dụng của bạn đã triển khai đúng các bước này, bạn có thể làm theo hướng dẫn kiểm thử.

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

Google Play hỗ trợ các giao dịch đang chờ xử lý hoặc các giao dịch yêu cầu một hoặc nhiều bước bổ sung giữa thời điểm người dùng thực hiện giao dịch mua và thời điểm phương thức thanh toán cho giao dịch mua được xử lý. Ứng dụng không được cấp quyền cho các loại giao dịch mua này cho đến khi Google thông báo cho bạn rằng đã tính phí thành công phương thức thanh toán của người dùng.

Ví dụ: người dùng có thể bắt đầu một giao dịch bằng cách chọn một cửa hàng thực tế mà họ sẽ thanh toán bằng tiền mặt sau. Người dùng sẽ nhận được một mã qua cả thông báo lẫn email. Khi đến cửa hàng thực tế, người dùng có thể sử dụng mã này và thanh toán cho thu ngân bằng tiền mặt. Sau đó, Google sẽ thông báo cho cả bạn và người dùng rằng đã nhận được khoản thanh toán. Sau đó, ứng dụng có thể cấp quyền cho người dùng.

Gọi enablePendingPurchases() trong quá trình khởi chạy BillingClient để bật các giao dịch đang chờ xử lý cho ứng dụng. Ứng dụng của bạn phải bật và hỗ trợ các giao dịch đang chờ xử lý cho các sản phẩm tính phí một lần. Trước khi thêm tính năng hỗ trợ, hãy đảm bảo bạn hiểu rõ vòng đời giao dịch mua đối với các giao dịch đang chờ xử lý.

Khi ứng dụng nhận được một giao dịch mua mới, thông qua PurchasesUpdatedListener hoặc từ việc gọi hàm queryPurchasesAsync, hãy sử dụng phương thức getPurchaseState() để xác định trạng thái mua hàng là PURCHASED hay PENDING. Bạn chỉ nên cấp quyền khi trạng thái là PURCHASED.

Nếu ứng dụng đang chạy và bạn có kết nối Thư viện Play Billing đang hoạt động khi người dùng hoàn tất giao dịch mua, PurchasesUpdatedListener sẽ được gọi lại và PurchaseState hiện là PURCHASED. Tại thời điểm này, ứng dụng có thể xử lý giao dịch mua hàng bằng cách sử dụng phương thức tiêu chuẩn để Phát hiện và xử lý giao dịch mua hàng. Ứng dụng cũng nên gọi queryPurchasesAsync() trong phương thức onResume() của ứng dụng để xử lý những giao dịch mua đã chuyển sang trạng thái PURCHASED trong khi ứng dụng không chạy.

Khi giao dịch mua chuyển từ trạng thái PENDING sang PURCHASED, ứng dụng real_time_developer_notifications sẽ nhận được thông báo ONE_TIME_PRODUCT_PURCHASED hoặc SUBSCRIPTION_PURCHASED. Nếu giao dịch mua bị huỷ, bạn sẽ nhận được thông báo ONE_TIME_PRODUCT_CANCELED hoặc SUBSCRIPTION_PENDING_PURCHASE_CANCELED. Điều này có thể xảy ra nếu khách hàng không hoàn tất việc thanh toán trong khung thời gian yêu cầu. Xin lưu ý rằng bạn luôn có thể sử dụng API Nhà phát triển Google Play để kiểm tra trạng thái hiện tại của một giao dịch mua.

Xử lý giao dịch mua số lượng nhiều

Được hỗ trợ trong phiên bản 4.0 trở lên của Thư viện Google Play Billing, Google Play cho phép khách hàng mua nhiều lần cùng một sản phẩm trong ứng dụng trong một giao dịch bằng cách chỉ định số lượng trong giỏ hàng. Ứng dụng cần xử lý các giao dịch mua hàng số lượng nhiều và cấp quyền dựa trên số lượng mặt hàng đã chỉ định.

Để cho phép xử lý các giao dịch mua với số lượng nhiều, logic cấp phép của ứng dụng cần kiểm tra số lượng mặt hàng. Bạn có thể truy cập trường quantity qua một trong các API sau:

Sau khi thêm logic để xử lý giao dịch mua số lượng nhiều, bạn cần bật tính năng mua số lượng nhiều cho sản phẩm tương ứng trên trang quản lý sản phẩm trong ứng dụng trong Google Play Console.

Truy vấn cấu hình thanh toán của người dùng

getBillingConfigAsync() cung cấp quốc gia mà người dùng sử dụng trên Google Play.

Bạn có thể truy vấn cấu hình thanh toán của người dùng sau khi tạo một BillingClient. Đoạn mã sau đây mô tả cách thực hiện lệnh gọi đến getBillingConfigAsync(). Hãy xử lý phản hồi bằng cách triển khai BillingConfigResponseListener. Trình nghe này nhận nội dung cập nhật cho tất cả các truy vấn cấu hình thanh toán được bắt đầu từ ứng dụng của bạn.

Nếu BillingResult được trả về không có lỗi nào, bạn có thể kiểm tra trường countryCode trong đối tượng BillingConfig để lấy thông tin Quốc gia trên Play của người dùng.

Kotlin

// Use the default GetBillingConfigParams.
val getBillingConfigParams = GetBillingConfigParams.newBuilder().build()
billingClient.getBillingConfigAsync(getBillingConfigParams,
    object : BillingConfigResponseListener {
        override fun onBillingConfigResponse(
            billingResult: BillingResult,
            billingConfig: BillingConfig?
        ) {
            if (billingResult.responseCode == BillingResponseCode.OK
                && billingConfig != null) {
                val countryCode = billingConfig.countryCode
                ...
            } else {
                // TODO: Handle errors
            }
        }
    })

Java

// Use the default GetBillingConfigParams.
GetBillingConfigParams getBillingConfigParams = GetBillingConfigParams.newBuilder().build();
billingClient.getBillingConfigAsync(getBillingConfigParams,
    new BillingConfigResponseListener() {
      public void onBillingConfigResponse(
          BillingResult billingResult, BillingConfig billingConfig) {
        if (billingResult.getResponseCode() == BillingResponseCode.OK
            && billingConfig != null) {
            String countryCode = billingConfig.getCountryCode();
            ...
         } else {
            // TODO: Handle errors
        }
      }
    });

Thông báo nhắc về giỏ hàng bị bỏ ngang trên trang chủ Google Play Games (được bật theo mặc định)

Đối với nhà phát triển trò chơi kiếm tiền thông qua giao dịch mua hàng trong ứng dụng, tính năng Nhắc nhở hoàn tất giao dịch là một cách để bán các đơn vị giữ hàng (SKU) đang hoạt động trong Google Play Console bên ngoài ứng dụng của bạn. Tính năng này sẽ nhắc người dùng hoàn tất giao dịch mua bị bỏ dở trước đó trong khi duyệt xem Cửa hàng Google Play. Các giao dịch mua này diễn ra bên ngoài ứng dụng của bạn, từ trang chủ Google Play Games trong Cửa hàng Google Play.

Tính năng này được bật theo mặc định để giúp người dùng tiếp tục từ nơi họ dừng lại và giúp nhà phát triển tăng tối đa doanh số bán hàng. Tuy nhiên, bạn có thể chọn không sử dụng tính năng này cho ứng dụng của mình bằng cách gửi biểu mẫu chọn không sử dụng tính năng Nhắc nhở hoàn tất giao dịch. Để biết các phương pháp hay nhất về cách quản lý SKU trong Google Play Console, hãy xem bài viết Tạo sản phẩm trong ứng dụng.

Hình ảnh sau đây cho thấy lời nhắc về việc bỏ dở giỏ hàng xuất hiện trên Cửa hàng Google Play:

màn hình Cửa hàng Google Play hiện lời nhắc mua hàng cho một giao dịch mua bị bỏ dở trước đó
Hình 2. Màn hình Cửa hàng Google Play hiện lời nhắc mua hàng cho một giao dịch mua bị bỏ dở trước đó.

màn hình Cửa hàng Google Play hiện lời nhắc mua hàng cho một giao dịch mua bị bỏ dở trước đó
Hình 3. Màn hình Cửa hàng Google Play hiện lời nhắc mua hàng cho một giao dịch mua bị bỏ dở trước đó.