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

从旧版 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.OnPurchaseUpdatedcontroller.OnPurchasePending
AppCoinsSDKPurchaseResult.State 字符串Unity IAP v5 的 PendingOrder / ConfirmedOrder / FailedOrder 类型
AppCoinsSDK.PURCHASE_STATE_SUCCESScontroller.OnPurchaseConfirmed 事件
AppCoinsSDK.PURCHASE_STATE_FAILEDcontroller.OnPurchaseFailed 事件
await AppCoinsSDK.Instance.ConfirmPurchaseIntent()已移除——PurchaseIntent 已被完全取消

分步迁移

1. 移除旧插件

从 Assets 文件夹中删除旧的 AppCoins SDK 文件,通常包括:

  • AppCoinsSDK.cs
  • AppCoinsPurchaseManager.cs
  • 所有 AppCoinsSDKPurchaseResultAppCoinsSDKResultAppCoinsSDKError 文件
  • 如果曾手动添加,还包括旧的原生 iOS 框架或 .xcframework

请从脚本中删除所有引用旧 SDK 命名空间的 using 指令。

2. 添加新插件

GitHub releases 页面 下载最新的 .unitypackage 并导入:

  1. 在 Unity 中依次选择 Assets → Import Package → Custom Package...
  2. 选择已下载的 .unitypackage 文件。
  3. Import Unity Package 对话框中确认所有项目均已选中,然后点击 Import

插件文件将出现在 Assets/Plugins/iOS/AppCoinsSDKPlugin/ 下。

3. 通过 Package Manager 添加 Unity IAP

新插件需要 Unity IAP v5。请打开 Window → Package Manager,搜索 In App Purchasingcom.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 相关逻辑

请删除所有引用 PurchaseIntentConfirmPurchaseIntentRejectPurchaseIntent 的代码。这些类型已不再存在。新 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}");
}
}