メインコンテンツまでスキップ

Unity SDK 集成

概览

为了向 Unity 开发者提供更好且简化的 Billing SDK 集成方式,我们创建了 Aptoide Billing SDK Unity。它由一个供 Unity 项目导入的包组成,其中包含 Aptoide Billing SDK Native 的镜像类和方法。它提供了用于初始化原生 SDK、管理计费操作以及处理来自原生 SDK 回调的方法。此包旨在简化 Unity 应用程序中应用内购买及计费相关功能的集成。
本指南帮助您集成这个 Aptoide Billing SDK Unity Package


在 Unity 中的导入与使用

通过 Unity Package Manager 导入

  1. 打开顶部菜单栏:Window > Package Manager
  2. 点击 + 号并选择 Import via git URL
  3. 粘贴以下链接:
    https://github.com/Aptoide/aptoide-unity-billing-sdk.git
    此处查看可用的发行版本,以了解我们的最新更新。

导入的文件

Runtime/Plugins/Android

此文件夹中的文件必须手动导入到您项目中的 Assets/Plugins/Android 文件夹。

重要
将它们复制到 Assets/Plugins/Android 文件夹后,请从导入的包中移除 Runtime/Plugins/Android 文件夹,以避免插件冲突。

baseProjectTemplate.gradle

此文件用于标识依赖项的仓库和来源。
如果您的项目中还没有 build.gradle 文件,可以使用模板 baseProjectTemplate.gradle

gradleTemplate.properties

此文件包含使用 Aptoide Billing Native SDK 所需的选项。
如果您的项目中还没有 gradle.properties 文件,可以使用模板 gradleTemplate.properties

settingsTemplate.properties

此文件用于标识依赖项的仓库和来源。
如果您的项目中还没有 settings.gradle 文件,可以使用模板 settingsTemplate.gradle

mainTemplate.gradle

此文件包含使用 Unity Billing SDK 所需的依赖项。
如果您的项目中还没有 main.gradle 文件,可以使用模板 mainTemplate.gradle

如果您已经拥有用于依赖项管理的文件,只需添加 Unity Billing SDK 使用的这 2 个依赖项即可。

dependencies {
implementation("com.aptoide:android-aptoide-billing:1.5.0") // Check the version in the mainTemplate.gradle to avoid any failures with the Native Billing SDK versioning
implementation("org.json:json:20210307")
}
AptoideBillingSDKUnityBridge.java

此文件用作 Unity Billing SDK 与 Android Native Billing SDK 之间的主桥接。
它通过调用 AptoideBillingClient 类中的方法,将 Unity 连接到原生 SDK。

Runtime/SDK

AptoideBillingSDKManager.cs

此类用于从您的项目向 Unity Billing SDK 发起调用。它拥有 Native Billing SDK 的镜像方法。


Unity Billing SDK 的功能

初始化

  • 使用所需参数初始化 Aptoide Billing SDK。
  • 建立与原生计费服务的连接。

计费操作

  • 异步查询商品详情。
  • 为应用内购买启动计费流程。
  • 消耗已购买的项目。
  • 确认非消耗型购买(在不消耗的情况下完成)。
  • 查询用户所做的购买。
  • 检查 SDK 是否支持特定功能。

账户管理

  • 让用户登录(及退出)其 Aptoide 账户。
  • 在重新安装和跨设备时恢复待处理和非消耗型购买。
  • 接收登录/退出状态变化的通知。

应用更新管理

  • 检查是否有可用的应用更新。
  • 启动对话框或将用户重定向到商店进行更新。

回调处理

  • 处理来自原生 SDK 的回调,包括计费设置、购买更新、商品详情、消耗响应、确认响应、账户登录结果以及账户状态变化。

集成注意事项

要使用 AptoideBillingSDKManager,您必须创建一个单独的类来调用其中的方法,并应用所需的游戏逻辑。


计费流程示例

  1. 使用 InitializePlugin 初始化 SDK。
  2. 使用 QueryPurchasesAsync 查询已完成但未消耗的购买,以便消耗它们。
  3. 使用 QueryProductDetailsAsync 查询可用商品,以便向用户显示正确的价格。
  4. 使用 LaunchBillingFlow 为所选商品启动计费流程,并使用在 QueryProductDetailsAsync 中获得的商品结果创建 BillingFlowParams
  5. IPurchasesUpdatedListenerOnPurchasesUpdated 方法中处理收到的购买结果。
  6. 在服务器端验证购买。
  7. 将项目交付给用户。
  8. 使用 ConsumeAsync 消耗购买,以通知购买和交付流程已在您这一端成功完成。

实现指南

1. 服务连接与初始化

Unity 包完全集成后,您需要初始化 AptoideBillingSDKManager
为此,您需要先实现 AptoideBillingSDKManager 中使用的监听器。您可以通过在变量中实现这些监听器,或直接在您的游戏逻辑类中实现它们来完成,如下所示:

public class Logic : MonoBehaviour,
IAptoideBillingClientStateListener,
IConsumeResponseListener,
IPurchasesUpdatedListener,
IPurchasesResponseListener,
IProductDetailsResponseListener,
IAcknowledgeResponseListener,
IAptoideSignInResponseListener,
IAptoideAccountStateListener
{
...
void Start() {
AptoideBillingSDKManager.InitializePlugin(
this, // IAptoideBillingClientStateListener
this, // IConsumeResponseListener
this, // IPurchasesUpdatedListener
this, // IPurchasesResponseListener
this, // IProductDetailsResponseListener
this, // IAcknowledgeResponseListener
this, // IAptoideSignInResponseListener
this, // IAptoideAccountStateListener
"YOUR_PUBLIC_KEY", // Set here the public Key associated to your Billing Integration
gameObject.name
);
}
...
}

要获取公钥,请遵循此文档

完成初始化后,您将在 OnBillingSetupFinishedOnBillingServiceDisconnected 方法中收到与计费服务的连接状态。
连接成功时,您将在 OnBillingSetupFinished 中收到 responseCode0 的调用,并调用必要的方法:

public void OnBillingSetupFinished(BillingResult billingResult)
{
if (billingResult.ResponseCode == 0)
{
// Check pending purchases of Consumables
CheckPendingConsumables();

// Check for pending and active Subscriptions
CheckSubscriptions();

// Query in-app Product details
QueryInapps()

// Query subscriptions Product details
QuerySubs()
}
else
{
Debug.LogError($"Billing setup failed with response code: {billingResult.ResponseCode}");
}
}

2. 查询未消耗的购买和有效的订阅

设置成功完成后,您应立即检查待处理的消耗型购买以及有效或待处理的订阅。如果存在待处理的购买,您应验证其合法性,将项目交付给用户并消耗它们。消耗将在步骤 5 中说明。

消耗型商品(Consumables)
以下示例展示了如何检查待处理的消耗型购买:

public void CheckPendingConsumables()
{
QueryPurchasesParams queryPurchasesParams =
QueryPurchasesParams.NewBuilder()
.SetProductType("inapp")
.Build();
// Query Purchases asynchronously for inapp products
AptoideBillingSDKManager.QueryPurchasesAsync(queryPurchasesParams);
}

public void OnQueryPurchasesResponse(BillingResult billingResult, Purchase[] purchases)
{
if (billingResult.ResponseCode == 0) // Assuming 0 indicates success
{
// Validate the Purchase in a server-to-server request
// follow this page /docs/iap-validators-server-to-server-check-client

// After validating, deliver the product to the User

// Lastly AptoideBillingSDKManager.ConsumeAsync should be called to notify
// Aptoide Services of the Successfull delivery and allow the User to Purchase once again the Item
}
else
{
Debug.LogError($"Failed to update purchases. Response code: {billingResult.ResponseCode}");
}
}

订阅(Subscriptions)
要验证有效/待处理的订阅,请使用 QueryPurchasesAsync 方法。结果由待处理的订阅(待消耗)和有效的订阅组成。为了正确地从用户处移除已过期的订阅,您应将结果中缺失但当前对用户可用的订阅进行匹配。

以下示例展示了如何检查订阅:

public void CheckSubscriptions()
{
QueryPurchasesParams queryPurchasesParams =
QueryPurchasesParams.NewBuilder()
.SetProductType("subs")
.Build();
// Query Purchases asynchronously for subscription products
AptoideBillingSDKManager.QueryPurchasesAsync(queryPurchasesParams);
}

public void OnQueryPurchasesResponse(BillingResult billingResult, Purchase[] purchases)
{
if (billingResult.ResponseCode == 0) // Assuming 0 indicates success
{
// Validate the Purchase in a server-to-server request
// follow this page /docs/iap-validators-server-to-server-check-client

// After validating, deliver the product to the User

// Lastly AptoideBillingSDKManager.ConsumeAsync should be called to notify
// Aptoide Services of the Successfull delivery and allow the User to Purchase once again the Item

// Remove Subscriptions from the User when not present in this list
}
else
{
Debug.LogError($"Failed to update purchases. Response code: {billingResult.ResponseCode}");
}
}

📘
注意

要向用户提供有关其订阅状态的实时信息,请使用 RTDN

3. 查询商品

启动连接后,您应查询可供购买的商品,以便以来自 Aptoide Connect 的正确价格向用户显示。此查询不仅包含商品的标题,还包含描述、价格等。
要查询商品,您可以使用 AptoideBillingSDKManager.QueryProductDetailsAsync,它会返回结果以处理 Aptoide Connect 的响应。

创建监听器后,您可以将其连同参数一起传递给 QueryProductDetailsAsync,如下所示:

private static List<QueryProductDetailsParams.Product> inappProducts =
new List<QueryProductDetailsParams.Product>() {
QueryProductDetailsParams.Product.NewBuilder()
.SetProductId("my_inapp_sku")
.SetProductType("inapp")
.Build()
};
private static List<QueryProductDetailsParams.Product> subsProducts =
new List<QueryProductDetailsParams.Product>() {
QueryProductDetailsParams.Product.NewBuilder()
.SetProductId("my_sub_sku")
.SetProductType("subs")
.Build()
};

public void QueryInapps()
{
// inappProducts is a list of in-app QueryProductDetailsParams
QueryProductDetailsParams queryProductDetails =
QueryProductDetailsParams.NewBuilder()
.SetProductList(inappProducts)
.Build();

AptoideBillingSDKManager.QueryProductDetailsAsync(queryProductDetails);
}

public void QuerySubs()
{
// Check if subscriptions are supported
if (AptoideBillingSDKManager.IsFeatureSupported(0).ResponseCode == 0)
{
Debug.Log("Subscriptions are supported.");
// subsProducts is a list of subscription QueryProductDetailsParams
QueryProductDetailsParams queryProductDetails =
QueryProductDetailsParams.NewBuilder()
.SetProductList(subsProducts)
.Build();

AptoideBillingSDKManager.QueryProductDetailsAsync(queryProductDetails);
}
else
{
Debug.LogWarning("Subscriptions are not supported by the Billing Service.");
}
}

public void OnProductDetailsResponse(BillingResult billingResult, QueryProductDetailsResult productDetailsResult)
{
if (billingResult.ResponseCode == 0)
{
foreach (var productDetails in productDetailsResult.ProductDetailsList)
{
// Apply the Product details to the UI or perform any action
// based on the Product type (inapp or subs)
}
foreach(var unfetchedProduct in productDetailsResult.UnfetchedProductList)
{
// Apply the logic for the unfetched Products here
}
}
else
{
Debug.LogError($"Failed to receive SKU details. Response code: {responseCode}");
}
}

4. 启动计费流程

要开始购买流程,请使用 AptoideBillingSDKManager.LaunchBillingFlow 函数。它接收包含在 QueryProductDetailsAsync 中获取的商品的 BillingFlowParams 以及供开发者使用的数据。以下代码片段展示了一个可能与"购买"按钮关联的函数:

public void StartPurchase(ProductDetails productDetails, string obfuscatedAccountId) 
{
if (AptoideBillingSDKManager.IsReady())
{
// Verify if the purchase of Subscription type you are making is a Free Trial or not
bool isFreeTrial = IsFreeTrialSubscription(productDetails, obfuscatedAccountId);

List<BillingFlowParams.ProductDetailsParams> productDetailsParamsList =
new List<BillingFlowParams.ProductDetailsParams> ()
{
BillingFlowParams.ProductDetailsParams.NewBuilder()
// Use the ProductDetails obtained via QueryProductDetailsAsync() method
.SetProductDetails(productDetails)
.Build()
};

BillingFlowParams billingFlowParams =
BillingFlowParams.NewBuilder()
.SetProductDetailsParamsList(productDetailsParamsList)
.SetObfuscatedAccountId(obfuscatedAccountId)
.SetFreeTrial(isFreeTrial)
.Build();

BillingResult billingResult =
AptoideBillingSDKManager.LaunchBillingFlow(billingFlowParams);
}
}

使用 obfuscatedAccountId 参数有助于防止计费系统的欺诈和滥用。此标识符使我们能够将计费活动准确地关联到您应用程序中的特定用户,从而增强安全性和可靠性。如果没有用户账户逻辑,此参数可以为 null

启动免费试用

要为用户启用免费试用,请在 LaunchBillingFlow 方法中将 freeTrial 参数设置为 true。这将根据提供的 obfuscatedAccountId 启动免费试用付款。

注意: 启动免费试用付款时,obfuscatedAccountId必填项

如果用户已经使用过该订阅的免费试用,则后续任何订阅尝试都将自动回退到常规付款流程。这确保了流畅的付款体验,并允许您在默认情况下安全地将 freeTrial 设置为 true 而不会导致错误。

public bool IsFreeTrialSubscription(
ProductDetails productDetails,
string obfuscatedAccountId
)
{
// First verify if the features Free Trial and Obfucasted Account Id are available
if (AptoideBillingSDKManager.IsFeatureSupported(2).ResponseCode != 0)
{
return false;
}

if (AptoideBillingSDKManager.IsFeatureSupported(1).ResponseCode != 0)
{
return false;
}

// Verify if the Sku Type is a Subscription
if (!productDetails.getProductType() != "subs")
{
return false;
}

// Apply your internal App Logic to verify if the User should receive a Free Trial or not
/* Example:
return obfuscatedAccountId == "123" && productDetails.getProductId() == "trial_dice";
*/

return false;
}

5. 处理购买并将项目交付给用户

public void OnPurchasesUpdated(BillingResult billingResult, Purchase[] purchases)
{
if (billingResult.ResponseCode == 0)
{
foreach (var purchase in purchases)
{
string purchaseToken = purchase.PurchaseToken;
// Validate the Purchase in a server-to-server request
// follow this page /docs/iap-validators-server-to-server-check-client

// After validating, deliver the product to the User

// Lastly ConsumeAsync should be called to allow the user to purchase the
// item again and change the purchase's state.
// Also consume subscriptions to make them active, there will be no issue in consuming more than once
ConsumeParams consumeParams = ConsumeParams.NewBuilder()
.SetPurchaseToken(purchaseToken)
.Build();

AptoideBillingSDKManager.ConsumeAsync(consumeParams);
}
}
else
{
// Handle the error
Debug.Log("Error: " + billingResult.ResponseCode);
}
}

验证购买

为确保购买的合法性并防止欺诈,您的应用程序应始终在服务器间请求中验证购买。要进行此验证,请遵循应用内购买验证页面中的指南。只有在成功验证购买后,您才应继续将商品交付给用户。

消耗购买

购买完成后,需要对其进行消耗。要消耗购买,请使用 AptoideBillingSDKManager.ConsumeAsync 函数。消耗的结果将在 OnConsumeResponse 方法中获取。

请注意,如果您未在 48 小时内消耗购买,它将被自动退款。

以下是 OnConsumeResponse 实现的示例:

public void OnConsumeResponse(BillingResult billingResult, string purchaseToken)
{
if (billingResult.ResponseCode == 0)
{
Debug.Log($"Purchase with token {purchaseToken} consumed successfully.");
}
else
{
Debug.LogError($"Failed to consume purchase with token {purchaseToken}. Response code: {billingResult.ResponseCode}");
}
}

6. 确认非消耗型购买

非消耗型商品只购买一次即永久拥有(例如去广告或永久性装饰)。这些商品不得被消耗——消耗购买会将其从 QueryPurchasesAsync 中移除,并使其可再次购买。相反,在验证并交付商品后,请使用 AptoideBillingSDKManager.AcknowledgeAsync 确认购买。

确认会完成付款(因此不会在 48 小时后自动退款),同时保持购买处于已拥有状态,使其继续由 QueryPurchasesAsync 返回,并且同一用户无法再次购买。

📘
功能可用性

确认功能通过远程切换,且默认关闭。在依赖它之前,请使用 AptoideBillingSDKManager.IsFeatureSupported(4).ResponseCode == 0 验证其是否可用(响应代码为 0 表示已启用)。

AcknowledgeAsync 方法与 ConsumeAsync 相似:它接收包含购买令牌的 AcknowledgeParams,结果会交付到 IAcknowledgeResponseListenerOnAcknowledgeResponse 方法。请为您的非消耗型商品调用它——在 OnPurchasesUpdated 中以及检查待处理购买时——而不是 ConsumeAsync

public void Acknowledge(string purchaseToken)
{
AcknowledgeParams acknowledgeParams = AcknowledgeParams.NewBuilder()
.SetPurchaseToken(purchaseToken)
.Build();

AptoideBillingSDKManager.AcknowledgeAsync(acknowledgeParams);
}

public void OnAcknowledgeResponse(BillingResult billingResult, string purchaseToken)
{
if (billingResult.ResponseCode == 0)
{
Debug.Log($"Purchase with token {purchaseToken} acknowledged successfully.");
}
else
{
Debug.LogError($"Failed to acknowledge purchase with token {purchaseToken}. Response code: {billingResult.ResponseCode}");
}
}
📘
恢复非消耗型商品

由于已确认的非消耗型商品保持已拥有状态,用户可以通过登录其 Aptoide 账户,在新设备上或重新安装后恢复它们。有关详细信息,请参阅登录 Aptoide 服务文档页面。

故障排查

常见问题

  1. 计费未初始化

    • 检查网络连接
  2. 购买未完成

    • 确保公钥正确
  3. 验证失败

    • 验证服务器端点
    • 检查购买令牌的有效性

最佳实践

  • 始终在服务器端验证购买
  • 实现适当的错误处理
  • 在沙盒环境中进行充分测试

常见问题解答

推荐使用哪个 Unity 版本?

我们的 Unity 封装器支持 JDK-11,因此任何高于 2022.2 的 Unity 版本都适用。

是否有可供我参考的 Unity 集成示例?

有的,Aptoide Unity Diceroll 集成了 Unity Billing SDK。可使用它来解答任何现有的疑问。

如何获取有关基于 Native Billing SDK 集成流程的更多信息?

要获取有关 Native Billing SDK 流程和过程的更多信息,请查看我们的 Native Billing SDK 集成指南。它包含成功集成所需的所有必要信息。遵循该指南,您可以调用 AptoideBillingSDKManager.cs 类中的镜像方法并获得相同的结果。

我仍在使用旧版 AppCoins Unity Billing SDK,在哪里可以找到旧版文档?

如果您仍在使用旧版 AppCoins Unity Billing SDK,强烈建议您过渡到新的 Aptoide Unity Billing SDK,以便在功能和安全补丁方面保持最新。尽管如此,旧版文档仍可在此处查阅。