Skip to main content

Updating from a previous AppCoins Unity Plugin version

This guide covers migrating from the previous AppCoins Unity Plugin to the current version. The previous plugin exposed a direct API through a singleton (AppCoinsSDK.Instance) for fetching products, initiating purchases, and consuming them. The new plugin is built on top of Unity IAP v5 as a custom store provider. All billing operations now go through the standard Unity IAP StoreController events and methods. The old direct API no longer exists.

Overview

The new plugin does not wrap the old one — it replaces it entirely. Remove the old SDK files from your project and rewrite your IAP code to use the Unity IAP v5 pattern. The migration is mechanical: each old method has a direct equivalent in the new API, and the purchase lifecycle maps cleanly from one to the other.

Breaking Changes at a Glance

Old APINew API
await AppCoinsSDK.Instance.IsAvailable()AppCoinsStoreMode.Automatic handles this automatically
await AppCoinsSDK.Instance.GetProducts(skus)controller.OnProductsFetched event
await AppCoinsSDK.Instance.Purchase(sku, payload)controller.PurchaseProduct(productId) + OnPurchasePending event
await AppCoinsSDK.Instance.ConsumePurchase(sku)controller.ConfirmPurchase(order)
await AppCoinsSDK.Instance.GetUnfinishedPurchases()controller.ProcessPendingOrdersOnPurchasesFetched(true) + controller.FetchPurchases() → re-fires OnPurchasePending
await AppCoinsSDK.Instance.GetAllPurchases()Transaction.all (not yet exposed in the Unity plugin) — as a workaround, use controller.FetchPurchases() to recover unfinished purchases. Full purchase history is not currently available in the Unity Plugin.
AppCoinsPurchaseManager.OnPurchaseUpdatedcontroller.OnPurchasePending
AppCoinsSDKPurchaseResult.State stringsUnity IAP v5 PendingOrder / ConfirmedOrder / FailedOrder types
AppCoinsSDK.PURCHASE_STATE_SUCCESScontroller.OnPurchaseConfirmed event
AppCoinsSDK.PURCHASE_STATE_FAILEDcontroller.OnPurchaseFailed event
await AppCoinsSDK.Instance.ConfirmPurchaseIntent()Removed — PurchaseIntent is gone entirely

Step-by-Step Migration

1. Remove the Old Plugin

Delete the old AppCoins SDK files from your Assets folder. This typically includes:

  • AppCoinsSDK.cs
  • AppCoinsPurchaseManager.cs
  • Any AppCoinsSDKPurchaseResult, AppCoinsSDKResult, or AppCoinsSDKError files
  • The old native iOS framework or .xcframework bundle if it was added manually

Remove all using directives that reference the old SDK namespace from your scripts.

2. Add the New Plugin

Download the latest .unitypackage from the GitHub releases page and import it:

  1. In Unity, go to Assets → Import Package → Custom Package...
  2. Select the downloaded .unitypackage file.
  3. In the Import Unity Package dialog, ensure all items are selected and click Import.

The plugin files will appear under Assets/Plugins/iOS/AppCoinsSDKPlugin/.

3. Add Unity IAP via Package Manager

The new plugin requires Unity IAP v5. Open Window → Package Manager, search for In App Purchasing (com.unity.purchasing), and install version 5.0 or later.

⚠️
Warning: The new plugin requires com.unity.purchasing 5.0 or later. It will fail to compile if the package is missing.

4. Replace Purchase Calls with the Unity IAP v5 Flow

Replace every call to AppCoinsSDK.Instance.Purchase(sku, payload) and AppCoinsPurchaseManager.OnPurchaseUpdated with the Unity IAP v5 PurchaseProduct / OnPurchasePending pattern:

Before:

// 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);
}
}

After:

// 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. Replace ConsumePurchase with ConfirmPurchase

Replace every call to AppCoinsSDK.Instance.ConsumePurchase(sku) with controller.ConfirmPurchase(order). Pass the PendingOrder object rather than a SKU string.

Before:

var result = await AppCoinsSDK.Instance.ConsumePurchase("gas");
if (result.IsSuccess) { /* done */ }

After:

_controller.ConfirmPurchase(order); // order is the PendingOrder from OnPurchasePending
⚠️
Call ConfirmPurchase after every successful purchase. Purchases that are not confirmed are automatically refunded after 24 hours.

6. Remove PurchaseIntent Handling

Remove any code that references PurchaseIntent, ConfirmPurchaseIntent, and RejectPurchaseIntent. These types no longer exist. The new API has no intent step — the purchase either succeeds (arriving in OnPurchasePending) or fails (arriving in OnPurchaseFailed).

Before / After Full Example

Before — old direct 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}");
}
}

After — new Unity IAP v5 adapter:

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}");
}
}