原生Android支付模块

本章节介绍如何为您的开发环境集成now.gg支付模块。

本文档基于now.gg的官方示例应用程序和游戏,提供了各种 代码示例 。您可以查看文档中内联的参考资料,以便更好地理解now.gg支付模块。


添加SDK库

首先下载包含支付库的now.gg SDK压缩包,并按照以下步骤将其添加到您的开发环境中:

注意: now.gg SDK压缩包包含支付模块,演示应用程序和示例代码。

1. 解压下载的now.gg支付模块压缩包,并在其中找到.aar文件:

 Payments.aar

2. 添加以下依赖项,到您游戏的 build.gradle 文件中:

dependencies {
         implementation fileTree(dir: 'libs', include: ['*.aar'])
 }


使用计费客户端

购买的生命周期

now.gg支付的购买生命周期流程如下:

  • 列出用户可以购买的可用产品。
  • 购买流程启动,以供用户购买列出的应用内产品。
  • 处理购买:
    • 购买验证。
    • 向用户交付产品/内容。
    • 将购买标记为已消费/已交付,以允许重新购买该产品。

术语介绍

now.gg支付使用购买令牌(Purchase Tokens)订单ID(Order IDs)来跟踪您游戏或应用内的产品和交易。

购买令牌(Purchase Tokens)
  • 购买令牌代表买家对产品的所有权/权利。
  • 它们与特定产品和相关产品的SKU(商品项)相关联。
  • 在用户成功进行应用内购买后,才会生成购买令牌。
  • 对于一次性购买的产品,每次交易都会生成一个新的购买令牌。
订单ID(Order IDs)
  • 订单ID是对now.gg计费服务(Billing Service)中的财务交易的引用。
  • 订单ID是作为跟踪购买、退款或任何争议的参考。
  • 每笔财务交易都会创建唯一的订单ID。
权利(Entitlement)

权利或授予权利一词用于表示在用户购买产品后,向用户“交付应用内产品”。

错误处理(Error Handling)
您的应用/游戏内遇到的错误和响应通过计费响应(Billing Response)代码进行分类。
示例:SERVICE_DISCONNECTED 错误代码表明now.gg支付服务(now.gg Payments Service)与您的游戏之间的连接已断开。解决此问题的方法是重新初始化您的应用/游戏,以重新建立与now.gg支付服务的连接。

1. 创建并初始化BillingClient

BillingClient 接口是 now.gg支付模块和您的应用/游戏之间的主要通信机制。它负责为一般的计费操作提供同步和异步方法。

 BillingClient的创建                               --> 使用 newBuilder()

 设置购买的状态更新                                --> Call setListener()

 通过应用/游戏接收状态更新                          --> 使用 PurchasesUpdatedListener

 为计费设置PAYMENT_ID                             --> 使用 setAppId(PAYMENT_ID)

 使用已有的游戏内用户ID来设置IN_GAME_ID             --> 使用 setInGameId(IN_GAME_ID)

 初始化后查询未消费的购买                           --> 使用 queryPurchasesAsync()
now.gg支付SDK需要使用唯一的userID来识别用户和发起交易。或者用户也可以选择保存付款信息,这样他们将来在您的应用中进行交易时,就不需要再次输入这些详细的信息。

在创建和初始化 BillingClient 的时候,您必须将用户的唯一userID(IN_GAME_ID)传给now.gg支付SDK,如以下代码所示:

private PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() {
    @Override
    public void onPurchasesUpdated(int billingResult, List<Purchase> purchases) {
         if (billingResult == BillingResponse.OK) {
             if (purchases == null) {
                 return;
             }
             for (Purchase purchase: purchases) {
                 String payload = purchase.getDeveloperPayload(); // 获取开发者负载
                 // 在此处理成功购买
             }
         } else if (billingResult == BillingResponse.USER_CANCELED) {
            Log.i(TAG, "onPurchasesUpdated() - user cancelled the purchase flow - skipping");
         } else {
            Log.w(TAG, "onPurchasesUpdated() got unknown billingResult: " + billingResult);
         }
    }
 };

 private BillingClient billingClient = BillingClient.newBuilder(activity)
    .setListener(purchasesUpdatedListener)
    .setAppId(PAYMENT_ID)
    .setInGameId(IN_GAME_ID) // unique ID to identify the user.
    .build();
private val purchasesUpdatedListener = object : PurchasesUpdatedListener {
    override fun onPurchasesUpdated(billingResult: Int, purchases: List<Purchase>?) {
        if (billingResult == BillingResponse.OK) {
            if (purchases == null) {
                return
            }
            for (purchase in purchases) {
                val payload = purchase.developerPayload // 获取开发者负载
                // 在此处理成功购买
            }
        } else if (billingResult == BillingResponse.USER_CANCELED) {
            Log.i(TAG, "onPurchasesUpdated() - user cancelled the purchase flow - skipping")
        } else {
            Log.w(TAG, "onPurchasesUpdated() got unknown billingResult: $billingResult")
        }
    }
}

private val billingClient = BillingClient.newBuilder(activity)
    .setListener(purchasesUpdatedListener)
    .setAppId(PAYMENT_ID)
    .setInGameId(IN_GAME_ID) // unique ID to identify the user.
    .build()

重要信息:

  • IN_GAME_ID 是您用户的唯一标识符。
    • IN_GAME_ID 的唯一性至关重要,因为如果用户决定在购买过程中保存与支付相关的信息,这些信息将与特定的 IN_GAME_ID相关联。请确保您的 IN_GAME_ID 对每个用户都是唯一的。
    • 支付集成测试:如果您想使用特定的IN_GAME_ID进行支付测试,请将其添加为nowStudio内部测试员
  • PAYMENT_ID 是您应用的唯一标识符。
    • 相应的 PAYMENT_ID 可以在nowStudio的“应用详情”(App Details)章节找到 – 更多信息.
    • PAYMENT_ID 只有在您 添加应用/游戏 游戏到 nowStudio 后才可用。
  • 退款处理 – 与 now.gg 支付相关的退款流程在 这里进行了说明
参考资料

2. 建立连接

now.gg支付服务有一个异步连接过程,并且需要您实现 BillingClientStateListener 来捕获客户端设置完成时的回调。

  • 要与 now.gg 支付服务建立连接,您需要调用 startConnection()
  • 要处理丢失的连接,您需要定义一个重试的逻辑。重试逻辑的实现将要求您重写回调方法 onBillingServiceDisconnected()
  • 在重写回调方法 onBillingServiceDisconnected() 时,请确保您的 BillingClient 正在调用 startConnection() 方法以重新建立与 now.gg 支付服务的连接。
  • 在执行任何方法之前,请保持与 BillingClient 的连接。
  • 查询未消费的购买:在初始化后,您必须使用 queryPurchasesAsync() 方法查询未消费的购买,以确保用户之前游戏会话中的任何未消费购买都能正确分配给用户。
    • 这在购买后应用崩溃或购买过程中网络连接丢失的情况下尤为重要。

注意:您可以选择编写自己的重试逻辑。不过,我们也提供了一个重试逻辑供您选择实现。

示例

billingClient.startConnection(new BillingClientStateListener() {
  @Override
  public void onBillingSetupFinished(int billingResult) {
    if (billingResult == 0) {
      // BillingClient已准备好,您可以在此处查询购买。 
      QueryPurchasesParams params = QueryPurchasesParams.newBuilder().setProductType(BillingClient.SkuType.ALL).build();
      billingClient.queryPurchasesAsync(params, new PurchasesResponseListener() {
        @Override
        public void onQueryPurchasesResponse(BillingResult billingResult, List < Purchase > list) {
        // 将购买的物品分配给用户,并调用消耗/确认。
        }
      });
    }
  }
  @Override
  public void onBillingServiceDisconnected() {
      // 调用startConnection()方法,
      // 以在下次向支付模块发起请求时尝试重启连接。
  }
});
billingClient.startConnection(object : BillingClientStateListener {
     override fun onBillingSetupFinished(billingResult: Int) {
         if (billingResult == 0) {
             // The BillingClient is ready. You can query purchases here. 
             val params = QueryPurchasesParams.newBuilder().setProductType(BillingClient.SkuType.ALL).build()
             billingClient.queryPurchasesAsync(params, object : PurchasesResponseListener {
                 override fun onQueryPurchasesResponse(billingResult: BillingResult, list: List< Purchase >) {
                      // 将购买的物品分配给用户,并调用消耗/确认。
                 }
             })
         }
     }

     override fun onBillingServiceDisconnected() {
         // 调用startConnection()方法,
         // 以在下次向支付模块发起请求时尝试重启连接。 
     }
 })
参考资料


产品和购买

本章节将引导您完成在应用/游戏中查询和列出可用的应用内产品以及处理购买的过程。

此流程的步骤如下:

  1. 列出可用产品。
  2. 购买流程启动。
  3. 处理付款。

在开始之前,请检查您是否已经:

1. 列出可用产品

第一步是查询可用的产品并将它们列出给您的用户。要查询应用内产品的详细信息,您需要调用 querySkuDetailsAsync()。当您查询 SKU的详细信息时,now.gg 付款模块将返回本地化的产品信息以便展示给您的用户。

注意:在向用户展示应用内产品之前,您应该先查询 SKU的详细信息,因为它会返回最新相关的产品信息。

SKU类型和订单字符串(Order Strings)

以下是 SKUType 与now.gg支付相关的内容:

  • SkuType.SUBS – 订阅
  • SkuType.INAPP – 应用内购买
  • SkuType.ALL – 所有的SKU

当您调用 querySkuDetailsAsync(),记得传递一个 SkuDetailsParams 的实例,该实例指定了在 nowStudio 中配置的 SKUType 以及产品ID。

结果

查询可用的产品后,必须分配一个侦听器来捕获传递的异步操作的结果。

以下的示例展示了 SkuDetailsResponseListener 接口的实现,并且允许您重写 onSkuDetailsResponse(),以便在查询完成时通知侦听器。

List < String > skuList = new ArrayList < > ();
 skuList.add("premium_upgrade");
 skuList.add("gas");
 SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
 params.setSkusList(skuList).setType(SkuType.ALL);
 billingClient.querySkuDetailsAsync(params.build(),
   new SkuDetailsResponseListener() {
     @Override
     public void onSkuDetailsResponse(int billingResult,
       List < SkuDetails > skuDetailsList) {
       // 在此处处理结果。
     }
   });
val skuList = mutableListOf<String>()
 skuList.add("premium_upgrade")
 skuList.add("gas")
 val params = SkuDetailsParams.newBuilder()
     .setSkusList(skuList)
     .setType(SkuType.ALL)
 billingClient.querySkuDetailsAsync(params.build(),
     object : SkuDetailsResponseListener {
         override fun onSkuDetailsResponse(billingResult: Int, skuDetailsList: List<SkuDetails>) {
             // 在此处处理结果。
         }
     })

更多信息

  • 只能在完成上一次购买并将产品交付给用户后,用户才能重新购买可消费物品。
  • 可以使用各种方法调用 SkuDetails 对象,以列出有关应用内产品的信息。
  • now.gg支付模块将所有的查询结果存储在 SkuDetails 对象列表中。
参考资料

2. 购买流程启动

在列出可供用户购买的可用产品后,您必须从应用/游戏启动购买流程。

要开始购买流程,您需要从应用程序的主线程调用 launchBillingFlow() 方法。

在此,调用 BillingFlowParams 对象作为参考,因为它包含了储存在 SkuDetails 对象中的相关的详细信息。SkuDetails 对象是通过调用 querySkuDetailsAsync() 获得的。

创建 BillingFlowParams 对象

要开始购买流程,你需要使用 BillingFlowParams.Builder 类创建 BillingFlowParams 对象。请使用以下代码片段作为参考:

// 计费流程启动的 activity 引用。 
 Activity activity = ...; 
      
 // 调用 querySkuDetailsAsync() 以获取 'skuDetails' 的值。
 BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder() 
    .setSkuDetails(skuDetails) 
    .setDeveloperPayload("可选开发者负载")
    .build(); 
 billingClient.launchBillingFlow(activity, billingFlowParams); 
      
     //结果
// 计费流程启动的 activity 引用。 
 val activity : Activity = ...; 
  
 // 调用 querySkuDetailsAsync() 以获取 'skuDetails' 的值。
 val flowParams = BillingFlowParams.newBuilder() 
         .setSkuDetails(skuDetails) 
         .setDeveloperPayload("可选开发者负载")
         .build() 
 billingClient.launchBillingFlow(activity, flowParams)
 //结果
参考资料

结果评估

您可以使用 BillingClient.BillingResponse 作为返回的响应代码的参考。

  • 响应代码 OK(0)表示成功启动。
  • 成功调用 launchBillingFlow() 后,系统将显示购买页面。
  • now.gg 支付模块调用 onPurchasesUpdated() 将购买结果传达给实现的接口监听器 PurchasesUpdatedListener
  • 此监听器是您在使用 setListener() 方法 初始化计费客户端 时指定的。

以下示例概述了重写 onPurchasesUpdated() 方法的过程:

@Override 
 void onPurchasesUpdated(int billingResult, List<Purchase>) { 
     if (billingResult == 0 && purchases != null) { 
         for (Purchase purchase : purchases) { 
             String payload = purchase.getDeveloperPayload(); // 获取开发者负载
             handlePurchase(purchase); 
         } 
     } else if (billingResult == 1) { 
             // 错误处理 - 由于用户终止购买流程 
     } else { 
             // 错误处理 - 其他原因(基于错误码)
     } 
 }
override fun onPurchasesUpdated(billingResult: int, purchases: List<Purchase>?) { 
     if (billingResult == 0 && purchases != null) { 
         for (purchase in purchases) {  
                 val payload: String = purchase.developerPayload // 获取开发者负载
                 handlePurchase(purchase)
         } 
     } else if (billingResult == 1) { 
             // 错误处理 - 由于用户终止购买流程 
     } else { 
             // 错误处理 - 其他原因(基于错误码) 
     } 
 }

注意:要处理各种响应代码,应实现 onPurchasesUpdated() 方法。

更多信息

  • 成功购买后会生成购买令牌。
    • 购买令牌是一个唯一标识符,代表应用内购买的产品和进行购买的相应用户。
  • 每个后续订单都会生成一个订单 ID,可用于与 now.gg 支付服务相关的任何争议或退款参考。
  • 每次购买的凭证,包括订单 ID 和其他相关信息也会通过电子邮件发送给用户。
参考资料

3. 处理购买

本章节重点介绍如何处理购买。开始购买流程后,您需要按以下方式处理购买:

  1. 购买验证
  2. 授予权利
  3. 购买结果
  4. 获取未消费的购买项目和订阅

3.1 购买验证

通过验证购买,您可以确定购买的真实性。减少欺诈的最佳做法是在将产品分配给用户之前对每次购买进行验证。

您可以使用以下两种方法之一来验证购买:

  • 使用验证购买API(Verify Purchase API)。
  • 使用公钥(Public key)。
3.1.1 使用 VerifyPurchase API

验证购买的第一种方法是使用 verifyPurchase API。您可以使用此 API 与我们的后端服务器验证购买。

要验证购买,请从您的应用程序后端服务器调用 verifyPurchase API,并使用 purchaseToken,如下示例请求代码所示:

curl --location 'https://cloud-api.bluestacks.cn/v2/seller/order/verifyPurchase' \
 --header 'Authorization: <payment_api_key_here>' \
 --header 'Content-Type: application/x-www-form-urlencoded' \
 --data-urlencode 'purchaseToken=<nowgg_purchase_token>'
import requests

  url = "https://cloud-api.bluestacks.cn/v2/seller/order/verifyPurchase"

  payload = 'purchaseToken=<nowgg_purchase_token>'
  headers = {
  'Authorization': '<payment_api_key_here>',
  'Content-Type': 'application/x-www-form-urlencoded'
  }

  response = requests.request("POST", url, headers=headers, data=payload)

  print(response.text)

重要信息


3.1.2 使用公钥

验证购买的第二种方法就是使用公钥。使用公钥来验证购买的实现方式可以参考演示应用程序中的 billingManager.java 代码文件。

  • 打开源文件 billingManager.java
  • 找到 verifyValidSignature 函数,再定位 verifyPurchase 函数。

通过参考我们演示应用程序中的上述函数的实现源文件 billingManager.java,您可以在您的应用中实现购买验证。

注意:

  • 您可以根据 此步骤 来生成您的 public key
  • 如果您的应用具有后端服务器,我们建议您在后端服务器上验证购买。
  • 建议在授予用户产品权利之前,对每一次购买进行验证。

3.2 授予权利

在您验证购买之后,您的应用可以授予用户所购买产品的权利,并标记此产品为“已消费”(consumed,于可消耗型产品而言)或者“已确认”(Acknowledged,于订阅型产品而言)。
如何分配购买的权利给用户,见如下方式:

1. 可消耗型产品

我们提供了两种方式可来实现购买:

2. 订阅

我们提供了两种方法来授权确认购买的订阅:


3.2.1 可消耗型产品

我们提供了两种方法来实现购买:

A. 客户端 – 使用consumeAsync
下列流程可用于在客户端实现购买项目的消费。

注意:如果您的应用仅有客户端,并无后端服务器,那么您应该使用这一方法。

如何授予权利:

  • 调用 consumeAsync() 方法。
    • 此方法将会给予用户产品的权限,且在此产品被购买后使它能够再次被购买。
  • 将购买令牌包含在方法 consumeAsync()  之内,以此将被购买的产品状态更新为“已消费”(Consumed),这样一来,通过now.gg计费服务使得该产品变为可再次购买的状态。.
  • 要更新消费操作的结果状态,您应传递一个实现了 ConsumeResponseListener 接口的对象。
  • 消费操作结束时,now.gg计费服务会调用函数 onConsumeResponse(),这个函数您可以进行重写。

重要信息:

  • 您应该注意检查是否有使用购买令牌来进行同一购买的多次权利的授予。
  • 这种情况可能发生在:如果消费请求失败了,负责更新 ConsumeResponseListener 接口的对象没有被更新。
  • 如果产品权限没有在购买的三日之内授予,那么交易将被取消,并为用户发起退款。

下列代码片段展示了如何实现购买验证消费操作:

void handlePurchase(Purchase purchase) {
    // 从 BillingClient#queryPurchasesAsync 或者 您的 PurchasesUpdatedListener 获取的购买。
    Purchase purchase = ...;

    // 验证购买。
    // 确保还未将purchaseToken的权利分配给用户后,
    // 将权利分配给用户。

    // 将购买消费掉
    ConsumeResponseListener listener = new ConsumeResponseListener() {
        @Override
        public void onConsumeResponse(int billingResult, String purchaseToken) {
            if (billingResult == 0) {
                // 消费操作成功进行的处理。
            }
        }
    };

    billingClient.consumeAsync(purchase.getPurchaseToken(), listener);
}
fun handlePurchase(purchase: Purchase) {
    // 从 BillingClient#queryPurchasesAsync 或者 您的 PurchasesUpdatedListener 获取的购买。
    val purchase: Purchase = ...;

    // 验证购买。
    // 确保还未将purchaseToken的权利分配给用户后,
    // 将权利分配给用户。

    // 将购买消费掉
    billingClient.consumeAsync(purchase.getPurchaseToken(), {
        billingResult,
        outToken - >
        if (billingResult == 0) {
            // 消费操作成功进行的处理。
        }
    })
}
参考资料

B. 服务器端 – 使用 consumePurchase API – 可消耗型产品

对于具有后端服务器的应用, consumePurchase API 应被用于服务器端的购买消费。

在您验证购买之后,您应该标记该产品为“已消费”。

实现购买的消费包括:

  • 分配给用户其所购买的产品。
  • 通知now.gg该产品已被消费。
购买消费的代码范本

下列代码范本展示了使用 consumePurchase API 的相关请求。

import requests

  url = "https://cloud-api.bluestacks.cn/v2/order/consumePurchase"

  payload = 'purchaseToken=nowgg-_purchase_token'
  headers = {
    'Authorization': 'payment_api_key_here',
    'Content-Type': 'application/x-www-form-urlencoded'
  }

  response = requests.request("POST", url, headers=headers, data=payload)

  print(response.text)

参考资料

重要信息

  • 在调用consumePurchase API之前,请确保将购买的产品分配给用户。
  • API Key – 在 nowStudio 的证书章节中可以找到支付API密钥(Payment API Key)。 更多信息
  • 在一次成功的购买行为后,将会返回 purchaseToken。 详细信息可参考以下相关文件:


3.2.2 订阅

重要信息

  • 确认订阅型产品购买是必不可少的一步,因为这将会授予用户其购买的权利。
  • 订阅型产品的有效期自被确认的当天起生效。
  • 如果自购买当天起的三日之内,该购买行为未得到确认,那么该购买将会被取消,并会为用户发起退款。

我们提供了两种方式来实现订阅型产品的确认:

A. 客户端 – 使用 acknowledgePurchase
以下流程用于在客户端确认购买的物品。

注意:如果您的应用仅包含供客户端,没有后端服务器,则应使用此方法。

如何授予权利:

  • 调用acknowledgePurchase() 方法。
    • 此方法将向用户授予产品的权利,并在已确认购买之后可重新购买该产品。
  • 将购买令牌包含在 acknowledgePurchase() 中,以将已购买产品的状态更新为“已确认”(acknowledged),并允许now.gg 计费服务将产品置为可重新购买的状态。
  • 为更新确认操作的结果,需传递一个实现了 AcknowledgePurchaseResponseListener 接口的对象。
  • 确认操作完成时,now.gg计费服务会调用函数:onAcknowledgePurchaseResponse(),您可以重写此函数。

重要信息:

  • 您应该注意检查是否有使用购买令牌来进行同一购买的多次权利的授予。
    • 这个情况会发生在确认请求失败,并且负责更新 AcknowledgePurchaseResponseListener 接口状态的对象未被更新。

以下代码演示了购买验证确认操作:

void handlePurchase(Purchase purchase) {
    // 从 BillingClient#queryPurchasesAsync 或者您的 PurchasesUpdatedListener 获取的购买。
    Purchase purchase = ...;

    // 验证购买。
    // 确保还未将purchaseToken的权利分配给用户后,
    // 将权利分配给用户。
   
    // 确认订阅
    final AcknowledgePurchaseResponseListener acknowledgePurchaseResponseListener = new AcknowledgePurchaseResponseListener() {
          @Override
          public void onAcknowledgePurchaseResponse(@BillingResponse int responseCode) {
              if (responseCode == 0) {
                       // 确认操作成功进行的处理。
                   }

          }
    };


   AcknowledgePurchaseParams acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
          .setPurchaseToken(purchaseToken)
          .build();
   BillingClient.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener);
}
fun handlePurchase(purchase: Purchase) {
    // 从 BillingClient#queryPurchasesAsync 或者您的 PurchasesUpdatedListener 获取的购买。
    val purchase: Purchase = ...;

    // 验证购买。
    // 确保还未将purchaseToken的权利分配给用户后,
    // 将权利分配给用户。

   // 确认订阅
   val acknowledgePurchaseResponseListener = object : AcknowledgePurchaseResponseListener {
    override fun onAcknowledgePurchaseResponse(@BillingResponse responseCode: Int) {
        if (responseCode == 0) {
            // 确认操作成功进行的处理。
        }
    }


   val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
       .setPurchaseToken(purchaseToken)
       .build()
   BillingClient.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener)
}
参考资料

B. 服务器端 – 使用 acknowledgePurchase API

acknowledgePurchase API 应被用于具有后端服务器的应用的服务器端的购买确认。

当您验证购买之后,应将其标记为已确认。

示例代码

以下示例代码演示了使用 acknowledgePurchase API 的关联请求。

import requests

  url = "https://cloud-api.bluestacks.cn/v2/seller/order/acknowledgepurchase"

  payload = 'purchaseToken=nowgg-_purchase_token'
  headers = {
    'Authorization': 'payment_api_key_here',
    'Content-Type': 'application/x-www-form-urlencoded'
  }

  response = requests.request("POST", url, headers=headers, data=payload)

  print(response.text)

参考资料

重要信息

  • API Key – 支付API密钥(Payment API Key)可以在 nowStudio 的证书章节中找到。 更多信息
  • 购买成功后,将返回 purchaseToken。详细信息可参考以下相关文件:


3.3 购买结果

PurchasesUpdatedListener 为您提供购买结果,但是在某些情况下,您的应用/游戏可能不会及时更新用户所有成功的购买。

其中一些情况包括:

  • 多个设备
    在此情况中,用户在一台设备上购买了商品,并希望在另一台设备上使用该商品,而购买的产品还未更新。
  • 连接/网络丢失
    由于连接/网络丢失,应用程序/游戏可能无法收到来自m PurchasesUpdatedListener,的成功购买通知。
  • 在应用外的购买
    在这种情况下,需要为用户处理应用/游戏外的购买,以便用户能够正常使用它们。

要成功处理与上述情况相关的购买,请执行以下操作:

  • 在您的 onResume() 和 onCreate() 方法中,确保您的应用调用了 BillingClient.queryPurchasesAsync(SkuType.ALL)

4. 处理未消费的购买

以下部分说明了检查和分配未消费的应用内购买和订阅的过程。

您可以使用 queryPurchasesAsync 方法,以返回用户的未消费购买列表,如下所示:

QueryPurchasesParams params = QueryPurchasesParams.newBuilder().setProductType(BillingClient.SkuType.ALL).build();
 billingClient.queryPurchasesAsync(params, new PurchasesResponseListener() {
   @Override
   public void onQueryPurchasesResponse(BillingResult billingResult, List < Purchase > list) {
      // 将购买的物品分配给用户,并调用消耗/确认。
   }
 });
val params = QueryPurchasesParams.newBuilder()
    .setProductType(BillingClient.SkuType.ALL)
    .build()

billingClient.queryPurchasesAsync(params, object : PurchasesResponseListener {
    override fun onQueryPurchasesResponse(billingResult: BillingResult, list: List) {
        // 将购买的物品分配给用户,并调用消耗/确认。
    }
})

重要信息

  • 在初始化后,您必须使用 queryPurchasesAsync() 方法查询未消费的购买,以确保用户之前游戏会话中的任何未消费购买都能正确分配给用户。
    • 这在购买后应用崩溃或购买过程中网络连接丢失的情况下尤为重要。
  • 有关更多信息,请参阅建立连接部分。

5. 订阅状态回调API

(由开发者提供)

本章节说明了 API 规范,以便您为我们提供 SubscriptionStatusCallback API,用于发送订阅状态的更新。

以下是相关的工作流程:

  • nowStudio 将使用此 API 将请求数据发送到您的游戏后端。
  • 您的游戏后端可以使用我们提供的 API 密钥(Webhook API Key)对请求进行认证。
  • 您可以根据订阅的当前状态(定义于此处)将购买权利分配给用户。
  • 在我们使用您提供的回调 URL 发送请求数据后,我们将等待您 API 的响应。
    • 如果我们没有收到状态代码为 200 且成功为真的响应,我们将在一段时间内继续重试。

参考资料: Subscription Status Callback API 章节。


重要信息

×
文本已复制到剪贴板
copyLinkText
有疑问?请通过以下方式联系我们: dev-support@now.gg