Access Google payment 4.0 (Kotlin)

Keywords: Android Google kotlin


Many game R & D students may need to access Google to pay. We all know the Google Documents. Sometimes it seems that they have grasped something but haven't grasped anything, resulting in the access work sometimes falling into a bottleneck. I sorted out the next Google payment interface, and added some explanatory text and extension code, hoping to help you. Finally, stick it Official website address of Google payment


Google's background project introduces white packets. It needs to bother the operating students to configure goods in the background. White packets can have no functions, but Google must pay SDK, because Google needs to have payment related permissions in your package. At the beginning of 4.0, there is no need to make a separate statement in the manifest and directly rely on Google payment package. The method of dependence is as follows:

dependencies {
	//The version may be updated. It is recommended to use the latest version
    val billing_version = "4.0.0"

Only after white packets are passed in can you query goods and pull up payment in the next process.

Start integration

Note: the goods mentioned below are only for consumer goods!!! Non expendable goods are basically the same except for the final consumption steps.

Initialize and connect to Google Play

Google payment needs to establish a connection with Google play before querying goods and so on, so it is best to call Google payment initialization, initialization and connection code example after the project is initialized.

private lateinit var billingClient: BillingClient
billingClient = BillingClient.newBuilder(activity)
//Google payment callback will be used after pulling up payment, which will be explained in detail later
billingClient.startConnection(object : BillingClientStateListener {
   override fun onBillingSetupFinished(billingResult: BillingResult) {
       if (billingResult.responseCode ==  BillingClient.BillingResponseCode.OK) {
           //Perform operations related to successful initialization
   override fun onBillingServiceDisconnected() {
       //Do a good job in disconnecting and reconnecting

Query commodity information

Generally, the game will be configured with a list of purchased goods to define the name, price, and other parameters of the goods. However, if multiple countries are issued, you can use this interface to query the currency, corresponding price, country short code and other information of the player's location. The result returned after successful query is also a necessary parameter for payment. In addition, officials also suggest whether there are goods to be consumed before querying the commodity information. If so, you have to consume the goods first and then pull up the payment, otherwise it will lead to payment failure. Example of querying Product Code:

//Product information requested by the process
private var paySkuDetails: ArrayList<SkuDetails> = ArrayList()
private suspend fun queryProductInfo(productIds: List<String>) {
    val params = SkuDetailsParams.newBuilder()

    // leverage querySkuDetails Kotlin extension function
    val skuDetailsResult = withContext(Dispatchers.IO) {
    if (skuDetailsResult.billingResult.responseCode == BillingClient.BillingResponseCode.OK && !skuDetailsResult.skuDetailsList.isNullOrEmpty()){
        //Query success callback
        paySkuDetails = ArrayList()
        for (skuDetailList in skuDetailsResult.skuDetailsList){
    } else{
        //Query failure callback

Start the purchase process

The purchase method needs to be called in the main thread. The parameters required for payment are the details of the goods queried successfully in the previous phase. If the query of goods fails, please do not call the payment interface. Example of pull up payment code:

val flowParams = BillingFlowParams.newBuilder()
            .setObfuscatedAccountId("Optional, Google payment detection mechanism, the only one associated with the game account ID,For example, account number MD5 Value. Note that it cannot be empty")
            .setObfuscatedProfileId("Optional, similar to the above functions, but many domestic developers use it to transparently transmit the order number of their own platform. Use it with caution")
val responseCode = billingClient.launchBillingFlow(mActivity, flowParams).responseCode

At the beginning of Google initialization, a payment callback interface was mentioned to monitor payment status. Google payment callback code example:

private val purchasesUpdatedListener = PurchasesUpdatedListener {
        billingResult, purchases ->
    if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && purchases != null){
        //Payment succeeded, processing consumption logic
        for (purchase in purchases){
            mPurchase = purchase
            GlobalScope.launch {
    }else if(billingResult.responseCode == BillingClient.BillingResponseCode.USER_CANCELED){
        //User cancels payment callback
        //Other payment errors

Consume purchased goods

The most important thing is that you must consume the goods after each successful payment. If you don't consume the test account for 5 minutes, normal players will receive a refund from Google in three days. If the goods are shipped in the game, you really lose your wife and your soldiers, so you must consume!!! As for the actual consumption before or after delivery, it depends on your own needs. Consumption code example:

private suspend fun consume(purchase: Purchase) {
    val consumeParams = ConsumeParams.newBuilder()
    val consumeResult = withContext(Dispatchers.IO) {
    if (consumeResult.billingResult.responseCode == BillingClient.BillingResponseCode.OK){
       	//Logic for successfully handling commodity consumption
     	//Logic of consumption failure when processing commodity purchase

Unconsumed goods query

In some abnormal purchase processes, the callback of successful purchase may not be received, such as multiple devices; Network interruption during internal purchase; Users are trading outside the application, so Google recommends calling BillingClient.queryPurchasesAsync() in onResume and onCreate to find out if there is any commodity that needs to be consumed. I feel that it is best to query every time before pulling up the payment, then consume it first, and then directly pull up the payment.

GlobalScope.launch {
   val purchasesResult = billingClient.queryPurchasesAsync(BillingClient.SkuType.INAPP)
   if (purchasesResult.billingResult.responseCode == BillingClient.BillingResponseCode.OK){
           //Processing commodity consumption logic
           for (purchase in purchasesResult.purchasesList){
           		//Consumer goods
       } else{
           //There are no commodities that need to be replenished
       //Failed to query Google order

Test payment

At this point, the payment part is almost finished. Finally, the test payment is needed. This part is also the head of the brain. Here are some pits I stepped on. I hope it can help you.

  1. Ensure that the signature, package name and VersionCode of your test APK are consistent with the white package you uploaded;
  2. Click payment to display the Google payment pop-up window, but there are errors such as unable to purchase. Generally, it is a configuration problem. Please carefully check the first item and confirm whether the test account is correctly added;
  3. Make sure that your Google Play account is a test account. It's easier to keep only one test account on your mobile phone;
  4. If you add a test account and the Google login account is also correct, but you still prompt that you can't buy when you pull up the payment, you can clear the Google Play data and try. Google cache knows everything;
  5. The test account must be added to the test plan. If you do not click to add the plan, it will not take effect even if it is added in the background.

Posted by ronz on Mon, 25 Oct 2021 01:46:17 -0700