Set up and initialize In-App Purchasing
Install the In-App Purchasing package and write the startup script that signs the player in, connects to your stores, and fetches your product catalog.
Read time 14 minutesLast updated 17 hours ago
Unity In-App Purchasing (IAP) sells products through platform-native stores, such as the Apple App Store and Google Play, and through Direct-to-Consumer (D2C) payment providers, such as Stripe and Coda. Both paths share one setup: install the package, enable the service, and run a startup script that connects your application to the stores you sell through.
You can enable platform-native stores, D2C payment providers, or both. When you enable both, players choose how to pay through the Purchase Options UI.
Prerequisites
Before you begin, make sure you meet the following requirements:
- A Unity project that runs on a supported Unity version. Unity recommends Unity 6.0 or later, and supports Unity 2022.3 as the minimum version.
- Build support modules installed for each platform you target, such as Android Build Support or iOS Build Support.
Install the In-App Purchasing package
To install the IAP package, follow these steps:
- In the Unity Editor, select Window > Package Management > Package Manager.
- Select the Unity Registry tab.
- Search for In-App Purchasing, or .
com.unity.purchasing - Select the package, then select Install.
Link your project and enable the IAP service
When you link your Unity project to a cloud project, the project gets the Unity Project ID that UGS requires at runtime.
Complete this task if you use the Remote Catalog, D2C payment providers, Unity Authentication, IAP analytics, a Google Play license key, or any other Unity service. A project that sells only through platform-native stores and defines its products in a local catalog can skip this step.
To link your project and enable IAP, follow these steps:
- In the Unity Editor, select Services > General Settings. Unity opens the Services General Settings page.
- Set Organization to your organization.
- Link a cloud project in one of the following ways:
- To use an existing cloud project, set Cloud project to that project, then select Link Unity project to cloud project.
- To create a new cloud project, select Create a new cloud project and follow the steps.
- Confirm that the Unity Project ID appears.
- Select Services > In-App Purchasing.
- Enable In-App Purchases.
- If you sell through Google Play, add your Google license key. For more information, refer to Add a Google license key to your project.
Linking your project to a cloud project and adding your Google license key lets Unity verify your Google Play transactions, so your revenue reporting counts them as verified. From In-App Purchasing 5.4.4, Android builds log a warning until you complete both.
Set your environment
The environment you select in the Unity Editor determines where IAP runs after you initialize UGS in code.
To set your environment, follow these steps:
- In the Unity Editor, select Edit > Project Settings > Services > Environments.
- Select the environment you want IAP to run in.
Choose how you define your products
Before you write your startup script, decide where your product definitions come from. Your choice determines which steps the script performs.
Catalog | When to use it | What the startup script does |
|---|---|---|
| Remote Catalog | Recommended for all projects, and required for D2C payment providers. You can update your products without shipping a build. | Signs the player in, connects to your stores, and fetches the catalog at runtime. |
| Local catalog | Projects that sell only through platform-native stores and ship a fixed product list with each build. | Connects to the store and fetches products from the catalog in the build. No sign-in. |
A local catalog still works. If you use one, you don't need the Remote Catalog steps on this page, and you can skip signing the player in. Unity recommends that you import or migrate your catalog to the Remote Catalog service, because it lets you change prices and products between releases and is the only option that supports D2C payment providers.
Write the startup script
Your startup script performs the following steps in order:
- Initialize Unity Gaming Services.
- Set up Authentication event handlers.
- Sign the player in.
- Get a for each store you sell through, and connect to it.
StoreController - Fetch the Remote Catalog and pass its products to each store.
The following script initializes a project for platform-native stores and D2C payment providers at the same time. Each store has its own toggle, so you can enable either one or both. Attach the script to a GameObject in your first scene.
using System;using System.Collections.Generic;using System.Threading.Tasks;using Unity.Services.Authentication;using Unity.Services.Core;using UnityEngine;using UnityEngine.Purchasing;using UnityEngine.Purchasing.Extension;public class IapInitializer : MonoBehaviour{ // Native stores (App Store, Google Play) and IAP D2C Capabilities can run on their own or // together. With both on, the Purchase Options UI lets the player choose how to pay. [SerializeField] bool m_UseNative = true; [SerializeField] bool m_UseD2C = true; StoreController m_NativeStore; StoreController m_D2CStore; IPaymentOptionProvider m_PurchaseOptions; readonly IRemoteCatalogProvider m_Catalog = new RemoteCatalogProvider(); async void Start() { try { await SetUpAsync(); } catch (Exception e) { Debug.LogError($"In-App Purchasing setup failed: {e.Message}"); } } async Task SetUpAsync() { // 1. Initialize Unity Gaming Services. Skip steps 1 to 3 if your project // already does this. await UnityServices.InitializeAsync(); // 2. Attach the Authentication handlers before you sign the player in, so // that no event is missed. AuthenticationService.Instance.SignedIn += OnSignedIn; AuthenticationService.Instance.SignInFailed += OnSignInFailed; AuthenticationService.Instance.SignedOut += OnSignedOut; // 3. Sign in. The Remote Catalog and IAP D2C Capabilities need a signed-in player. // Replace anonymous sign-in with your own authentication: an anonymous player loses // their purchase history when the app is reinstalled or its data is cleared. if (!AuthenticationService.Instance.IsSignedIn) { await AuthenticationService.Instance.SignInAnonymouslyAsync(); } // 4. Get one StoreController per kind of store. Subscribe to every event // before you call Connect(), or you lose the events that fire first. if (m_UseNative) { m_NativeStore = UnityIAPServices.StoreController(); Subscribe(m_NativeStore); m_NativeStore.OnPurchasePending += OnNativePurchasePending; await m_NativeStore.Connect(); } if (m_UseD2C) { m_D2CStore = UnityIAPServices.StoreController(PaymentProvider.Name); Subscribe(m_D2CStore); m_D2CStore.OnPurchasePending += OnD2CPurchasePending; await m_D2CStore.Connect(); m_PurchaseOptions = m_D2CStore.PaymentProviderStoreExtendedService?.GetPaymentOptionProviderUGUI(); } // 5. One Remote Catalog works for every store. var result = await m_Catalog.FetchRemoteCatalog(); if (!result.Success) { Debug.LogError($"Could not fetch the Remote Catalog: {result.Exception?.Message}"); return; } if (m_NativeStore != null) { m_Catalog.FetchProducts(m_NativeStore.FetchProductsWithNoRetries, UnityIAPServices.GetDefaultStore()); } if (m_D2CStore != null) { m_Catalog.FetchProducts(m_D2CStore.FetchProductsWithNoRetries, PaymentProvider.Name); } } // Call this from your buy button. public async void Buy(string catalogListingId) { if (m_PurchaseOptions != null) { // This offers every store that has this listing: native, D2C, // and webshop. With a single option, it buys directly without // showing the picker. await m_PurchaseOptions.ShowPurchaseOption(catalogListingId); return; } // No D2C store, so buy from the native store. if (m_NativeStore != null) { m_NativeStore.PurchaseProduct(catalogListingId); } else { Debug.LogWarning($"No way to pay for {catalogListingId}."); } } // A purchase arrives on the controller of the store the player paid with, // so each controller confirms its own orders. void OnNativePurchasePending(PendingOrder order) { // Grant the content for the order here, then confirm it. m_NativeStore.ConfirmPurchase(order); } void OnD2CPurchasePending(PendingOrder order) { // Grant the content for the order here, then confirm it. m_D2CStore.ConfirmPurchase(order); } void Subscribe(StoreController store) { store.OnStoreDisconnected += OnStoreDisconnected; store.OnProductsFetched += OnProductsFetched; store.OnProductsFetchFailed += OnProductsFetchFailed; } void Unsubscribe(StoreController store) { store.OnStoreDisconnected -= OnStoreDisconnected; store.OnProductsFetched -= OnProductsFetched; store.OnProductsFetchFailed -= OnProductsFetchFailed; } void OnProductsFetched(List<Product> products) => Debug.Log($"{products.Count} products are ready to display."); void OnProductsFetchFailed(ProductFetchFailed failure) => Debug.LogError($"Product fetch failed: {failure.FailureReason}"); void OnStoreDisconnected(StoreConnectionFailureDescription description) => Debug.LogError($"Store disconnected: {description.message}"); void OnSignedIn() => Debug.Log($"Signed in as {AuthenticationService.Instance.PlayerId}."); void OnSignInFailed(RequestFailedException e) => Debug.LogError($"Sign-in failed: {e.Message}"); void OnSignedOut() => Debug.Log("Signed out."); void OnDestroy() { if (m_NativeStore != null) { Unsubscribe(m_NativeStore); m_NativeStore.OnPurchasePending -= OnNativePurchasePending; } if (m_D2CStore != null) { Unsubscribe(m_D2CStore); m_D2CStore.OnPurchasePending -= OnD2CPurchasePending; } AuthenticationService.Instance.SignedIn -= OnSignedIn; AuthenticationService.Instance.SignInFailed -= OnSignInFailed; AuthenticationService.Instance.SignedOut -= OnSignedOut; }}
After the script runs, your application is connected to each store you enabled and holds the products from your catalog, so it can start a purchase.
The following details of the script might not be clear from the code sample alone:
- The script needs both and
using UnityEngine.Purchasing;.using UnityEngine.Purchasing.Extension;andRemoteCatalogProviderare in different namespaces, and if you omit either directive, the type doesn't resolve.IRemoteCatalogProvider - The script initializes IAP in rather than
Start, because it awaits UGS initialization and sign-in, which can't complete inAwake.Awake - The guard covers a sign-in that's already complete, but not one that's still in progress.
IsSignedInthrows in both cases, so a project that signs players in elsewhere must remove steps 1 to 3 instead of relying on the guard.SignInAnonymouslyAsync - The script doesn't cover a change of signed-in player, because anonymous sign-in keeps the same player for the session. If players can switch accounts while your application runs, attach an handler to each store and re-fetch from it. Refer to Handle an account change.
OnAuthAccountChanged
Connect to each store
StoreControllerUnityIAPServices.StoreController- For platform-native stores, call with no argument to get the controller for the default store.
UnityIAPServices.StoreController() - For D2C payment providers, call .
UnityIAPServices.StoreController(PaymentProvider.Name)
Each kind of store needs its own . A project that sells through both keeps two controllers for the lifetime of the application.
StoreControllerAttach handlers to the events before you call , or you lose the events that fire during connection. Attach handlers to the following events at minimum:
StoreControllerConnectConnectTaskOnStoreDisconnectedStoreControllerFetch the Remote Catalog
Instantiate a and call to retrieve your products from the Remote Catalog service. One provider serves every store: the Remote Catalog maps each listing to the native store IDs it carries, so the same fetch supplies a native store and a D2C payment provider in the same application.
RemoteCatalogProviderFetchRemoteCatalogPass the catalog's products to each store with , along with the name of the store that receives them. Use for a platform-native store and for a D2C payment provider.
FetchProductsUnityIAPServices.GetDefaultStore()PaymentProvider.NameYou can call more than once at runtime, but wait for each call to finish before you make the next one. appends its results to the list that returns, so the two methods aren't interchangeable. Call during initialization.
FetchProductsFetchProductsGetProductsFetchProductsFetch products from a catalog created in the Editor
If you define your products with the IAP Catalog window instead of the Remote Catalog, replace the in the script with a built from the local catalog:
RemoteCatalogProviderCatalogProvider// Load the catalog created in the Editor.ProductCatalog catalog = ProductCatalog.LoadDefaultCatalog();CatalogProvider catalogProvider = CodelessCatalogProvider.PopulateCatalogProvider(catalog);
ProductCatalog.LoadDefaultCatalogCodelessCatalogProvider.PopulateCatalogProviderCatalogProviderFetchProductsA local catalog doesn't support D2C payment providers.
Fetch purchases
Call to request the player's active orders, which include pending, confirmed, and deferred orders. Add the call to , so that it runs after the store returns your products.
StoreController.FetchPurchasesOnProductsFetchedThis step isn't required to start using IAP, but Unity recommends that you fetch and handle existing purchases before you initiate new ones. Fetch purchases again whenever you fetch additional products at runtime.
FetchPurchasesOnPurchasesFetchedOrdersOnPurchasesFetchFailedOnPurchasePendingOnPurchasePendingFetchProductsUnlike , overwrites the list that returns rather than appending to it. For more information, refer to Purchases.
FetchProductsFetchPurchasesGetPurchasesHandle an account change
When the signed-in player changes while your application runs, Unity IAP raises on each of your stores. This event requires the Unity Authentication package () version 3.0.0 or later.
OnAuthAccountChangedcom.unity.services.authenticationA project that sells through both a platform-native store and a direct-to-consumer payment provider receives the event once for each store.
From your handler, fetch products and purchases for that store again in the same order as at startup:
- Fetch your catalog.
- Call .
FetchProducts - If your application fetches purchases, call .
FetchPurchases
Don't read or in the handler, because Unity IAP has already emptied both.
GetProductsGetPurchasesStart a purchase
Call from your buy button, with the of the listing you want to sell.
BuycatalogListingIdWhen you enable D2C payment providers, shows the Purchase Options UI, which presents every way the player can pay for that listing: the platform-native store, an eligible D2C payment provider, and a webshop if the listing has one. If only one option is available, the Purchase Options UI skips the choice and completes the purchase through that option. When you don't enable D2C payment providers, sends the purchase to the platform-native store.
BuyBuyA completed purchase arrives on the of the store the player paid through, so each controller handles and confirms its own orders. Grant the player their content in , then call to close the order.
StoreControllerOnPurchasePendingConfirmPurchaseFor more information, refer to Fulfill purchases through the SDK.