Skip to main content

Adding AppCoins alongside Unity IAP v4 or earlier

This guide covers adding AppCoins Billing to a Unity game that uses Unity IAP v4 or earlier. The AppCoins plugin requires Unity IAP v5 — you need to upgrade first. The Unity IAP v4→v5 upgrade changes how purchase completion works, which affects your existing code before AppCoins enters the picture.

If your project is already on Unity IAP v5 or later, see Adding AppCoins alongside Unity IAP v5 instead.


Step 1: Upgrade Unity IAP to v5

Open Window → Package Manager, find In App Purchasing, and upgrade to 5.0 or later.

⚠️
After upgrading, your project will have compile errors until you apply the v5 API changes below. This is expected.

Step 2: Apply Unity IAP v5 API Changes

Unity IAP v5 replaced the IStoreListener callback interface with event subscriptions on IStoreController. The purchase-completion pattern changed most significantly — ProcessPurchase is gone.

Purchase completion

Before (v4):

public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
{
GiveItemToUser(args.purchasedProduct.definition.id);
return PurchaseProcessingResult.Complete;
}

After (v5):

// Subscribe in your initialization code
_controller.OnPurchasePending += OnPurchasePending;

private void OnPurchasePending(PendingOrder order)
{
var product = order.CartOrdered.Items().FirstOrDefault()?.Product;
if (product != null) GiveItemToUser(product.definition.id);
_controller.ConfirmPurchase(order); // replaces returning PurchaseProcessingResult.Complete
}

Store initialization

Before (v4):

public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
{
_controller = controller;
foreach (var product in controller.products.all)
Debug.Log(product.definition.id);
}

After (v5):

// Subscribe before Connect()
_controller.OnProductsFetched += OnProductsFetched;

private void OnProductsFetched(List<Product> products)
{
foreach (var product in products)
Debug.Log(product.definition.id);
}

Initialization failure

Before (v4):

public void OnInitializeFailed(InitializationFailureReason error) { }
public void OnInitializeFailed(InitializationFailureReason error, string message) { }

After (v5):

_controller.OnStoreDisconnected += failure =>
Debug.LogError($"Store connection failed: {failure.Message}");

Purchase failure

Before (v4):

public void OnPurchaseFailed(Product product, PurchaseFailureReason reason) { }

After (v5):

_controller.OnPurchaseFailed += OnPurchaseFailed;

private void OnPurchaseFailed(FailedOrder order)
{
Debug.LogError($"Purchase failed: {order.FailureReason}");
}

IStoreListener is no longer needed

Once you have migrated to the event-subscription pattern, your class does not need to implement IStoreListener. Remove the interface declaration and any remaining stub methods.

Initializing the store

Before (v4):

var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
builder.AddProduct("gas", ProductType.Consumable);
UnityPurchasing.Initialize(this, builder); // synchronous, IStoreListener callback

After (v5):

_controller = UnityIAPServices.StoreController();
_controller.OnProductsFetched += OnProductsFetched;
_controller.OnPurchasePending += OnPurchasePending;

await _controller.Connect();

// Replaces builder.AddProduct — define product definitions and fetch after Connect
_controller.FetchProducts(new List<ProductDefinition>
{
new ProductDefinition("gas", ProductType.Consumable)
});

Step 3: Add the AppCoins 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/.


Step 4: Add AppCoins

Once the project compiles cleanly on Unity IAP v5, adding AppCoins is one line. Before your Connect() call, add:

using AppCoins.Unity;

// Add this before Connect()
await AppCoinsIAP.ConfigureStoreAsync(AppCoinsStoreMode.Automatic);

await _controller.Connect();

AppCoinsStoreMode.Automatic activates AppCoins Billing on qualifying installs and falls back to Apple Billing otherwise — see How Runtime Detection Works. No other code changes are needed.


Before / After Full Example

Before — Unity IAP v4:

using UnityEngine;
using UnityEngine.Purchasing;

public class IAPManager : MonoBehaviour, IStoreListener
{
private IStoreController _controller;

void Start()
{
var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
builder.AddProduct("gas", ProductType.Consumable);
UnityPurchasing.Initialize(this, builder);
}

public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
{
_controller = controller;
}

public void OnInitializeFailed(InitializationFailureReason error) { }

public void BuyProduct(string productId)
{
_controller.InitiatePurchase(productId);
}

public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
{
GiveItemToUser(args.purchasedProduct.definition.id);
return PurchaseProcessingResult.Complete;
}

public void OnPurchaseFailed(Product product, PurchaseFailureReason reason)
{
Debug.LogError($"Purchase failed: {reason}");
}

private void GiveItemToUser(string productId)
{
Debug.Log($"Delivering: {productId}");
}
}

After — Unity IAP v5 with AppCoins:

using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Purchasing;
using AppCoins.Unity;

public class IAPManager : MonoBehaviour
{
private StoreController _controller;

private readonly List<ProductDefinition> _products = new List<ProductDefinition>
{
new ProductDefinition("gas", ProductType.Consumable)
};

private async void Start()
{
// AppCoins: configure before Connect
await AppCoinsIAP.ConfigureStoreAsync(AppCoinsStoreMode.Automatic);

_controller = UnityIAPServices.StoreController();
_controller.OnProductsFetched += OnProductsFetched;
_controller.OnPurchasePending += OnPurchasePending;
_controller.OnPurchaseFailed += OnPurchaseFailed;

// Re-fire OnPurchasePending for any unfinished purchases found by FetchPurchases()
_controller.ProcessPendingOrdersOnPurchasesFetched(true);

await _controller.Connect();

// Fetch product metadata (replaces builder.AddProduct + UnityPurchasing.Initialize)
_controller.FetchProducts(_products);

// 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.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;

GiveItemToUser(product.definition.id);
_controller.ConfirmPurchase(order);
}

private void OnPurchaseFailed(FailedOrder order)
{
Debug.LogError($"Purchase failed: {order.FailureReason}");
}

private void GiveItemToUser(string productId)
{
Debug.Log($"Delivering: {productId}");
}
}

The key changes from v4 to the final result:

  1. IStoreListener removed — no more interface to implement
  2. UnityPurchasing.Initialize(this, builder)await _controller.Connect() + _controller.FetchProducts(products)
  3. ProcessPurchaseOnPurchasePending + controller.ConfirmPurchase(order)
  4. OnInitializedOnProductsFetched event subscription
  5. await AppCoinsIAP.ConfigureStoreAsync(AppCoinsStoreMode.Automatic) added before Connect()