从旧版 AppCoins Unity 插件升级
本指南介绍如何从旧版 AppCoins Unity 插件迁移到当前版本。旧插件通过单例(AppCoinsSDK.Instance)暴露了一套直接 API,用于获取商品、发起购买和消耗购买。新插件则以自定义商店提供方的形式构建在 Unity IAP v5 之上,所有计费操作现在都通过标准的 Unity IAP StoreController 事件与方法完成。旧的直接 API 已不复存在。
概览
新插件不是对旧插件的封装,而是完全取代它。请从工程中移除旧 SDK 文件,并按 Unity IAP v5 的模式重写您的 IAP 代码。迁移过程是机械性的:旧的每个方法在新 API 中都有直接对应项,购买生命周期也能一一对应。
破坏性变更一览
| 旧 API | 新 API |
|---|---|
await AppCoinsSDK.Instance.IsAvailable() | 由 AppCoinsStoreMode.Automatic 自动处理 |
await AppCoinsSDK.Instance.GetProducts(skus) | controller.OnProductsFetched 事件 |
await AppCoinsSDK.Instance.Purchase(sku, payload) | controller.PurchaseProduct(productId) + OnPurchasePending 事件 |
await AppCoinsSDK.Instance.ConsumePurchase(sku) | controller.ConfirmPurchase(order) |
await AppCoinsSDK.Instance.GetUnfinishedPurchases() | controller.ProcessPendingOrdersOnPurchasesFetched(true) + controller.FetchPurchases() → 重新触发 OnPurchasePending |
await AppCoinsSDK.Instance.GetAllPurchases() | Transaction.all(Unity 插件中尚未暴露)——作为变通方案,可使用 controller.FetchPurchases() 恢复未完成的购买。Unity 插件当前不提供完整的购买历史。 |
AppCoinsPurchaseManager.OnPurchaseUpdated | controller.OnPurchasePending |
AppCoinsSDKPurchaseResult.State 字符串 | Unity IAP v5 的 PendingOrder / ConfirmedOrder / FailedOrder 类型 |
AppCoinsSDK.PURCHASE_STATE_SUCCESS | controller.OnPurchaseConfirmed 事件 |
AppCoinsSDK.PURCHASE_STATE_FAILED | controller.OnPurchaseFailed 事件 |
await AppCoinsSDK.Instance.ConfirmPurchaseIntent() | 已移除——PurchaseIntent 已被完全取消 |
分步迁移
1. 移除旧插件
从 Assets 文件夹中删除旧的 AppCoins SDK 文件,通常包括:
AppCoinsSDK.csAppCoinsPurchaseManager.cs- 所有
AppCoinsSDKPurchaseResult、AppCoinsSDKResult或AppCoinsSDKError文件 - 如果曾手动添加,还包括旧的原生 iOS 框架或
.xcframework包
请从脚本中删除所有引用旧 SDK 命名空间的 using 指令。
2. 添加新插件
从 GitHub releases 页面 下载最新的 .unitypackage 并导入:
- 在 Unity 中依次选择 Assets → Import Package → Custom Package...
- 选择已下载的
.unitypackage文件。 - 在 Import Unity Package 对话框中确认所有项目均已选中,然后点击 Import。
插件文件将出现在 Assets/Plugins/iOS/AppCoinsSDKPlugin/ 下。
3. 通过 Package Manager 添加 Unity IAP
新插件需要 Unity IAP v5。请打开 Window → Package Manager,搜索 In App Purchasing(com.unity.purchasing),并安装 5.0 或更高版本。
com.unity.purchasing 5.0 或更高版本。缺少该包将导致编译失败。4. 将购买调用替换为 Unity IAP v5 流程
请将所有对 AppCoinsSDK.Instance.Purchase(sku, payload) 和 AppCoinsPurchaseManager.OnPurchaseUpdated 的调用,替换为 Unity IAP v5 的 PurchaseProduct / OnPurchasePending 模式:
变更前:
// Old: subscribe to OnPurchaseUpdated and call Purchase directly
AppCoinsPurchaseManager.OnPurchaseUpdated += OnPurchaseUpdated;
await AppCoinsSDK.Instance.Purchase("gas", "optional_payload");
private void OnPurchaseUpdated(AppCoinsSDKPurchaseResult result)
{
if (result.State == AppCoinsSDK.PURCHASE_STATE_SUCCESS)
{
GiveItemToUser(result.Sku);
StartCoroutine(ConsumePurchase(result.Sku));
}
else if (result.State == AppCoinsSDK.PURCHASE_STATE_FAILED)
{
Debug.LogError("Purchase failed: " + result.Error);
}
}
变更后:
// New: use Unity IAP v5 PurchaseProduct and subscribe to events
_controller.OnPurchasePending += OnPurchasePending;
_controller.OnPurchaseConfirmed += order => Debug.Log("Confirmed: " + order.Info.TransactionID);
_controller.OnPurchaseFailed += failure => Debug.LogError("Failed: " + failure.FailureReason);
_controller.PurchaseProduct("gas");
private void OnPurchasePending(PendingOrder order)
{
var product = order.CartOrdered.Items().FirstOrDefault()?.Product;
if (product == null) return;
GiveItemToUser(product.definition.id);
_controller.ConfirmPurchase(order);
}
5. 将 ConsumePurchase 替换为 ConfirmPurchase
请将所有对 AppCoinsSDK.Instance.ConsumePurchase(sku) 的调用替换为 controller.ConfirmPurchase(order),并传入 PendingOrder 对象而非 SKU 字符串。
变更前:
var result = await AppCoinsSDK.Instance.ConsumePurchase("gas");
if (result.IsSuccess) { /* done */ }
变更后:
_controller.ConfirmPurchase(order); // order is the PendingOrder from OnPurchasePending
ConfirmPurchase。未确认的购买将在 24 小时后自动退款。6. 移除 PurchaseIntent 相关逻辑
请删除所有引用 PurchaseIntent、ConfirmPurchaseIntent 和 RejectPurchaseIntent 的代码。这些类型已不再存在。新 API 没有意图步骤——购买要么成功(进入 OnPurchasePending),要么失败(进入 OnPurchaseFailed)。
变更前后的完整示例
变更前 —— 旧的直接 API:
using UnityEngine;
using AppCoins; // old namespace
public class OldIAPManager : MonoBehaviour
{
private async void Start()
{
// 1. Check availability manually
var availability = await AppCoinsSDK.Instance.IsAvailable();
if (!availability.IsSuccess || !availability.Value)
{
Debug.Log("AppCoins not available, falling back to Apple");
return;
}
// 2. Subscribe to purchase events
AppCoinsPurchaseManager.OnPurchaseUpdated += OnPurchaseUpdated;
// 3. Fetch products
var productsResult = await AppCoinsSDK.Instance.GetProducts(new[] { "gas", "premium_pack" });
if (productsResult.IsSuccess)
{
foreach (var product in productsResult.Value)
{
Debug.Log($"Product: {product.Sku} — {product.PriceLabel}");
}
}
// 4. Handle unfinished purchases
var unfinished = await AppCoinsSDK.Instance.GetUnfinishedPurchases();
if (unfinished.IsSuccess)
{
foreach (var purchase in unfinished.Value)
{
GiveItemToUser(purchase.Sku);
await AppCoinsSDK.Instance.ConsumePurchase(purchase.Sku);
}
}
}
public async void BuyProduct(string sku)
{
await AppCoinsSDK.Instance.Purchase(sku, "optional_payload");
}
private async void OnPurchaseUpdated(AppCoinsSDKPurchaseResult result)
{
if (result.State == AppCoinsSDK.PURCHASE_STATE_SUCCESS)
{
GiveItemToUser(result.Sku);
var consumeResult = await AppCoinsSDK.Instance.ConsumePurchase(result.Sku);
if (!consumeResult.IsSuccess)
{
Debug.LogError("Consume failed: " + consumeResult.Error);
}
}
else if (result.State == AppCoinsSDK.PURCHASE_STATE_FAILED)
{
Debug.LogError("Purchase failed: " + result.Error);
}
}
private void GiveItemToUser(string sku)
{
Debug.Log($"Granting item: {sku}");
}
}
变更后 —— 新的 Unity IAP v5 适配器:
using System.Linq;
using UnityEngine;
using UnityEngine.Purchasing;
using UnityEngine.Purchasing.Extension;
using AppCoins.Unity; // new namespace
public class NewIAPManager : MonoBehaviour
{
private StoreController _controller;
private async void Start()
{
// 1. Configure the store — replaces manual IsAvailable() check
var selectedStore = await AppCoinsIAP.ConfigureStoreAsync(AppCoinsStoreMode.Automatic);
Debug.Log("Active store: " + selectedStore);
// 2. Get the controller and subscribe to events
_controller = UnityIAPServices.StoreController();
_controller.OnProductsFetched += OnProductsFetched;
_controller.OnPurchasePending += OnPurchasePending;
_controller.OnPurchaseConfirmed += order => Debug.Log("Confirmed: " + order.Info.TransactionID);
_controller.OnPurchaseFailed += f => Debug.LogError("Failed: " + f.FailureReason);
// 3. Re-fire OnPurchasePending for any unfinished purchases found by FetchPurchases()
_controller.ProcessPendingOrdersOnPurchasesFetched(true);
// 4. Connect — triggers OnProductsFetched when products are available
await _controller.Connect();
// 5. Trigger recovery of unfinished purchases — results arrive via OnPurchasePending
_controller.FetchPurchases();
}
private void OnProductsFetched(List<Product> products)
{
foreach (var product in products)
{
Debug.Log($"Product: {product.definition.id} — {product.metadata.localizedPriceString}");
}
}
public void BuyProduct(string productId)
{
_controller.PurchaseProduct(productId);
}
private void OnPurchasePending(PendingOrder order)
{
var product = order.CartOrdered.Items().FirstOrDefault()?.Product;
if (product == null) return;
// Deliver the item before confirming
GiveItemToUser(product.definition.id);
// Confirm (consume) the purchase
_controller.ConfirmPurchase(order);
}
private void GiveItemToUser(string productId)
{
Debug.Log($"Granting item: {productId}");
}
}