# Complete a test purchase

> Run a simulated purchase end to end to verify your In-App Purchasing integration before you connect a payment provider.

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](../dashboard-catalog/add-catalog-item) in the Unity Dashboard, or author one in the Unity Editor and [upload it to the Remote Catalog service](./configure-remote-catalog.md#upload-your-product-catalog-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](./workflow.md#prerequisites).
* An environment with test mode enabled and no payment provider connected. Refer to [Turn test mode on or off](./test-mode.md#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 `StoreController` for D2C payments alongside your native store.

## Run a simulated purchase

To run a simulated purchase, follow these steps:

1. **Initialize services**

   Create a startup script that initializes Unity Gaming Services, signs the player in, and then calls `InitializeIAP` on the `PurchaseManager` script you create in the next step.

   Set your Unity Editor environment to an environment that has test mode enabled. For more information, refer to [Configure your environment in the Unity Editor](./workflow.md#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](./initialize-services.md).

   ```csharp
   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);
           }
       }
   }
   ```

   > **Note:**
   >
   > This script signs in anonymously to keep the test short. If the session token is lost, for example when a player reinstalls your game, the player can't recover an unlinked anonymous account or the purchases tied to it. Before a player makes a purchase, link their anonymous account to a platform account or choose another approach. Refer to [Approaches to authentication](/authentication/approaches-to-authentication.md).

2. **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 [`StoreController`](https://docs.unity3d.com/Packages/com.unity.purchasing@latest?subfolder=/api/UnityEngine.Purchasing.StoreController.html) to the D2C payment provider integration, fetches the catalog, and confirms orders as they arrive.

   Pass `PaymentProvider.Name` 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.

   Attach this script to the same GameObject as `ServiceOrchestrator`. For the complete implementation, refer to [Fetch your Remote Catalog](./fetch-remote-catalog.md).

   ```csharp
   using 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 `catalogListing.definition.id`. Purchase calls require the catalog listing ID, so use the ID that this script logs.

   Unity IAP invokes the [`OnPurchasePending`](https://docs.unity3d.com/Packages/com.unity.purchasing@latest?subfolder=/api/UnityEngine.Purchasing.IPurchaseService.html#UnityEngine_Purchasing_IPurchaseService_OnPurchasePending) callback when an order is paid and waiting for fulfillment. The handler you registered on your `StoreController` grants the player their items, then calls [`ConfirmPurchase`](https://docs.unity3d.com/Packages/com.unity.purchasing@latest?subfolder=/api/UnityEngine.Purchasing.IPurchaseService.html#UnityEngine_Purchasing_IPurchaseService_ConfirmPurchase_UnityEngine_Purchasing_PendingOrder_). 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](./fulfill-purchases.md).

3. **Start the purchase from your game**

   Add the following method to `PurchaseManager` and call it from your buy button, passing one of the catalog listing IDs that the previous step logs:

   ```csharp
   public 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);
   }
   ```

   > **Note:**
   >
   > The uGUI Purchase Options UI adds an `EventSystem` with a `StandaloneInputModule` if your scene doesn't already have one. The Editor's Fake Store does the same. Because `StandaloneInputModule` uses the legacy `UnityEngine.Input` API, it logs an `InvalidOperationException` every frame when **Active Input Handling** is set to **Input System Package (New)**. To check this setting, go to **Edit** > **Project Settings** > **Player** > **Other Settings** > **Configuration**. If it is set to **Input System Package (New)**, add an `EventSystem` with an `InputSystemUIInputModule` to your scene before starting a purchase. A pre-existing `EventSystem` prevents either UI from adding an additional `EventSystem`.

   When you call `BuyProduct`, what you see first depends on whether your game also has a native store set up:

   * **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 `StoreController` and fetched the same catalog listings, the Purchase Options UI presents the native option alongside **Pay with mock**. In the Editor, the [Fake Store](../stores#fake-store) stands in for the native store as **Pay with Mock Store**. Select **Pay with mock** to open the simulated checkout page.
   * **Native store only**: A `StoreController` that you didn't create with `PaymentProvider.Name` has no `PaymentProviderStoreExtendedService`, so `paymentOptionProvider` is null. The sample logs a warning and returns instead of starting a purchase.

   For the rules that decide when the UI appears, refer to [When the Purchase Options UI doesn't appear](./initiate-purchase.md#disable-the-ui-for-a-build). To present your own purchase interface instead of the Purchase Options UI, refer to [Build your own purchase UI](./initiate-purchase.md#build-your-own-purchase-ui).

4. **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.


   **Frame:**
   ![The mock checkout page in two states. Before payment, a Checkout summary shows the order ID, a coins.pack.small consumable at $4.99, and a $4.99 total, with Confirm Payment and Cancel options. After payment, the same page shows the "Payment confirmed, waiting for your backend to fulfill"  message above Return to Game, Fulfill Order, Refund, and Dispute. Both states display the following banner: This is a mock payment provider for testing purposes. Do not use in production."](/api/media?file=/iap/media/images/mock-checkout.png)

   To complete the purchase, follow these steps:

   1. Select **Confirm Payment**. The page reports that the payment is confirmed and waiting for your backend to fulfill the order.
   2. 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 `OnPurchasePending`, and your handler calls `ConfirmPurchase`.

   > **Note:**
   >
   > Don't select **Fulfill Order** when you test from your game. The button represents a backend that fulfills orders server-side, such as a [webhook](./implement-backend.md) or a [Cloud Code module](./cloud-code-fulfillment.md). It closes the order on the server, but your game doesn't receive a successful callback. If you select it before returning to your game, your game receives `OnPurchaseFailed` with the `OrderStateChanged` reason.

   The 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](./refunds-and-disputes.md).

5. **Verify that the simulated purchase flow succeeded**

   The console indicates that the test purchase succeeds if it shows the following messages in order:

   1. `Catalog listing ID: <your listing ID>` appears when the catalog loads.
   2. `Purchase pending. Confirming the order.`, appears after you confirm the payment and return your game.
   3. `Granted the player their items for <your listing ID>.`, when your fulfillment code runs.
   4. `Purchase confirmed.`, when Unity IAP confirms the order.

   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 `NoEligibleStore` 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 `ShowPurchaseOption` returns `null` and starts no purchase. Pass a value that the `Catalog listing ID` log shows, and check your [Editor environment](./workflow.md#configure-your-environment-in-the-unity-editor).
   * **The Console fills with `InvalidOperationException` errors about the `UnityEngine.Input` class**: Your project uses the Input System package only. Add an `EventSystem` with an `InputSystemUIInputModule` to your scene.
   * **`OnPurchasePending` doesn't run after checkout**: Your game hasn't regained focus. Switch back to your game, or select the **Game** view in the Editor.
   * **`OnPurchaseFailed` runs with the `OrderStateChanged` reason**: 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**.

> **Important:**
>
> While test mode is on, Unity IAP simulates every payment in the environment. Make sure not to release with an environment that has test mode enabled. To turn off test mode and ensure you can take real payments, connect at least one payment provider.

## Additional resources

* [Test mode for D2C payments](./test-mode.md) to understand what test mode simulates and the rules that govern it.
* [Initiate a purchase](./initiate-purchase.md) to learn which payment options the UI presents.
* [Configure your payment provider](./configure-payment-provider.md) to connect a provider when you're ready to take real payments.
