Complete a test purchase
Run a simulated purchase end to end to verify your In-App Purchasing integration before you connect a payment provider.
Read time 10 minutesLast updated 5 days ago
A test purchase uses the simulated payment provider that Unity In-App Purchasing (IAP) applies when an environment is in test mode. For test purchases, Unity IAP creates and tracks the order, your fulfilment code runs, and your webhook or Cloud Code module receives the purchase events. No money moves through the simulated provider, and you don't need a provider account, credentials, routing rule, or redirect URL. A completed test purchase proves that your catalog, your purchase call, and your purchase handling work before you start onboarding with a provider.
Prerequisites
To complete a test purchase, you need the following:
- At least one catalog listing in your Remote Catalog. Add a catalog listing in the Unity Dashboard, or author one in the Unity Editor and upload it to the Remote Catalog service.
- A Unity Cloud project connected to your Unity project, with IAP v5.4 and Unity Authentication installed. For more information, refer to Integrate D2C payment providers prerequisites.
- An environment with test mode enabled and no payment provider connected. Refer to Turn test mode on or off.
- A way to run your game and trigger a purchase. You can test a purchase in the Editor or in a player build. Unity opens the simulated checkout page in a browser outside of your game.
If your game already sells through a native store such as the Apple App Store or Google Play, keep your existing setup. The scripts on this page add a second for D2C payments alongside your native store.
StoreControllerRun a simulated purchase
To run a simulated purchase, follow these steps:
Initialize services
Create a startup script that initializes Unity Gaming Services, signs the player in, and then calls on the script you create in the next step.
InitializeIAPPurchaseManagerSet your Unity Editor environment to an environment that has test mode enabled. For more information, refer to Configure your environment in the Unity Editor.
The following script is a short example for testing. For the full startup sequence, refer to Initialize services to handle the startup sequence.
using System;using Unity.Services.Authentication;using Unity.Services.Core;using UnityEngine;public class ServiceOrchestrator : MonoBehaviour{ // Assign this in the Inspector. public PurchaseManager purchaseManager; async void Awake() { try { await UnityServices.InitializeAsync(); await AuthenticationService.Instance.SignInAnonymouslyAsync(); await purchaseManager.InitializeIAP(); } catch (Exception e) { Debug.LogException(e); } }}
Fetch your catalog and handle the order
A purchase needs the catalog listings from your project's Remote Catalog, and your game needs to confirm each order it receives. Create a script that connects a to the D2C payment provider integration, fetches the catalog, and confirms orders as they arrive.
StoreControllerPass as the store name to connect the controller to your D2C integration. Unity IAP decides which provider serves an order when the purchase starts, and when test mode is on that's always the simulated provider. All D2C orders arrive on this controller, so subscribe to its purchase events even if your game already handles purchase events on a native store controller.
PaymentProvider.NameAttach this script to the same GameObject as . For the complete implementation, refer to Fetch your Remote Catalog.
ServiceOrchestratorusing System.Collections.Generic;using System.Threading.Tasks;using UnityEngine;using UnityEngine.Purchasing;public class PurchaseManager : MonoBehaviour{ StoreController m_StoreController; // Called by ServiceOrchestrator after sign-in completes. public async Task InitializeIAP() { m_StoreController = UnityIAPServices.StoreController(PaymentProvider.Name); m_StoreController.OnProductsFetched += OnProductsFetched; m_StoreController.OnPurchasePending += OnPurchasePending; m_StoreController.OnPurchaseConfirmed += OnPurchaseConfirmed; m_StoreController.OnPurchaseFailed += OnPurchaseFailed; await m_StoreController.Connect(); RemoteCatalogProvider catalogProvider = new RemoteCatalogProvider(); var fetchRemoteCatalogResult = await catalogProvider.FetchRemoteCatalog(); if (!fetchRemoteCatalogResult.Success) { throw fetchRemoteCatalogResult.Exception!; } m_StoreController.FetchProducts(catalogProvider.GetProducts()); } void OnProductsFetched(List<Product> products) { // Log the catalog listing IDs that you can buy in the next step. foreach (var product in products) { foreach (var catalogListing in product.catalogListings.Values) { Debug.Log($"Catalog listing ID: {catalogListing.id}"); } } } void OnPurchasePending(PendingOrder order) { // Grant the player their items before you confirm the order. GrantPlayerItems(order); Debug.Log("Purchase pending. Confirming the order."); m_StoreController.ConfirmPurchase(order); } void GrantPlayerItems(PendingOrder order) { // Replace this with your own fulfillment code, for example adding currency // or unlocking content. foreach (var cartItem in order.CartOrdered.Items()) { Debug.Log($"Granted the player their items for {cartItem.CatalogListingId}."); } } void OnPurchaseConfirmed(Order order) { if (order is FailedOrder failedOrder) { Debug.LogWarning($"Confirmation failed: {failedOrder.FailureReason} {failedOrder.Details}"); return; } Debug.Log("Purchase confirmed."); } void OnPurchaseFailed(FailedOrder order) { Debug.LogWarning($"Purchase failed: {order.FailureReason} {order.Details}"); }}
A catalog listing ID might differ from the product ID in . Purchase calls require the catalog listing ID, so use the ID that this script logs.
catalogListing.definition.idUnity IAP invokes the callback when an order is paid and waiting for fulfillment. The handler you registered on your grants the player their items, then calls . Unity IAP invokes the callback for a simulated purchase exactly as it does for a purchase through a connected provider, so your test exercises the same fulfillment code that your released game uses. For the fulfillment options in full, refer to Fulfill purchases.
OnPurchasePendingStoreControllerConfirmPurchaseStart the purchase from your game
Add the following method to and call it from your buy button, passing one of the catalog listing IDs that the previous step logs:
PurchaseManagerpublic async void BuyProduct(string catalogListingId){ var extendedService = m_StoreController.PaymentProviderStoreExtendedService; // Use GetPaymentOptionProviderUITK for UI Toolkit projects. var paymentOptionProvider = extendedService?.GetPaymentOptionProviderUGUI(); if (paymentOptionProvider == null) { // The Purchase Options UI isn't available. Debug.LogWarning("Show your own purchase UI instead."); return; } // Show the Purchase Options UI with the eligible payment options. await paymentOptionProvider.ShowPurchaseOption(catalogListingId);}
When you call , what you see first depends on whether your game also has a native store set up:
BuyProduct- D2C payments only: Only the simulated provider has your catalog listing, so Unity IAP skips the Purchase Options UI and opens the simulated checkout page directly.
- Native store and D2C payments: If your game has already connected a native and fetched the same catalog listings, the Purchase Options UI presents the native option alongside Pay with mock. In the Editor, the Fake Store stands in for the native store as Pay with Mock Store. Select Pay with mock to open the simulated checkout page.
StoreController - Native store only: A that you didn't create with
StoreControllerhas noPaymentProvider.Name, soPaymentProviderStoreExtendedServiceis null. The sample logs a warning and returns instead of starting a purchase.paymentOptionProvider
For the rules that decide when the UI appears, refer to When the Purchase Options UI doesn't appear. To present your own purchase interface instead of the Purchase Options UI, refer to Build your own purchase UI.
Complete the simulated checkout
The simulated checkout page stands in for a payment provider's hosted checkout and doesn't ask for card details. The page displays the order ID, the catalog listing, and the total, so you can verify that your purchase request includes the expected listing.

To complete the purchase, follow these steps:
- Select Confirm Payment. The page reports that the payment is confirmed and waiting for your backend to fulfill the order.
- Return to your game. If your environment has a success redirect URL, select Return to Game. If it doesn't, the page shows a terminal screen instead, so close the browser and switch back to your game yourself. Unity IAP checks the order when your game regains focus, so in the Editor, select the Game view.
Your game receives the order through , and your handler calls .
OnPurchasePendingConfirmPurchaseThe page also offers Refund and Dispute actions. These actions move the order into the corresponding states and send the related purchase events to your webhook or Cloud Code module. Use them to test how your game and backend handle these order statuses. For more information, refer to Refunds and disputes for D2C payments.
Verify that the simulated purchase flow succeeded
The console indicates that the test purchase succeeds if it shows the following messages in order:
- appears when the catalog loads.
Catalog listing ID: <your listing ID> - , appears after you confirm the payment and return your game.
Purchase pending. Confirming the order. - , when your fulfillment code runs.
Granted the player their items for <your listing ID>. - , when Unity IAP confirms the order.
Purchase confirmed.
The Console messages only prove that your handler ran in the right order. Also check that the player received what they bought, because Unity IAP confirms the order whether or not your fulfillment code granted anything.
If you configured a webhook or Cloud Code module, verify that it receives the purchase event.
If the test doesn't display those messages, it might be due to one of following causes:
- The checkout page doesn't open, and the Console shows a warning: You passed an ID that isn't a catalog listing ID, or the Editor's environment doesn't have test mode on. In both cases no store has the listing cached, so
NoEligibleStorereturnsShowPurchaseOptionand starts no purchase. Pass a value that thenulllog shows, and check your Editor environment.Catalog listing ID - The Console fills with errors about the
InvalidOperationExceptionclass: Your project uses the Input System package only. Add anUnityEngine.Inputwith anEventSystemto your scene.InputSystemUIInputModule - doesn't run after checkout: Your game hasn't regained focus. Switch back to your game, or select the Game view in the Editor.
OnPurchasePending - runs with the
OnPurchaseFailedreason: You selected Fulfill Order before you returned to your game, so the order was already closed. Start a new purchase and don't select Fulfill Order.OrderStateChanged
Additional resources
- Test mode for D2C payments to understand what test mode simulates and the rules that govern it.
- Initiate a purchase to learn which payment options the UI presents.
- Configure your payment provider to connect a provider when you're ready to take real payments.