# Verify Xbox in-app purchases

> Verify Xbox in-app purchases in a Cloud Code module before granting entitlements.

Use Cloud Code modules to validate Xbox purchases on the server before you grant entitlements. This prevents spoofed or tampered clients from granting items without confirmed ownership with the [Microsoft Store Collections API](https://learn.microsoft.com/en-us/gaming/gdk/docs/store/commerce/service-to-service/microsoft-store-apis/xstore-v9-query-for-products?view=gdk-2604) (Microsoft) on the server side.

The instructions on this page use the [`Com.Unity.Services.CloudCode.IAP`](https://www.nuget.org/packages/Com.Unity.Services.CloudCode.IAP) NuGet package, which wraps Microsoft Store Collections API calls and handles Microsoft Entra ID (formerly Azure AD) service authentication.

## Verification process overview

The verification process described on this page works as follows:

1. The game client completes a purchase in the Xbox Store.
2. The game client obtains a Collections ID (business-to-business) token from the Microsoft GDK.
3. The game client calls a Cloud Code function, and passes the token and product IDs for verification.
4. The Cloud Code module uses your service credentials to authenticate with Entra ID.
5. The module calls the Microsoft Store Collections API, which returns which products the player owns.
6. The module grants entitlements only for products that Microsoft confirms as owned.

The Collections ID token is a client-to-server handoff token. The player's Microsoft account credentials never reach the module. The module caches Entra ID access tokens in memory for the lifetime of the worker, minus a five-minute refresh buffer. This buffer means most requests skip the token fetch entirely.

## Prerequisites

Before you begin, make sure you meet the following requirements:

* A Cloud Code module project that targets .NET 9.
* An Entra ID app registration linked to your Partner Center account with access to the Microsoft Store Collections API and the `https://onestore.microsoft.com/.default` scope. Refer to [Register an application in Microsoft Entra ID](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app) (Microsoft) for full setup instructions.
* Cloud Code secrets configured for Entra ID credentials.

Use the following secret keys:

| Secret key                 | Description                         |
| -------------------------- | ----------------------------------- |
| `XBOX_AZURE_TENANT_ID`     | Entra ID directory (tenant) ID.     |
| `XBOX_AZURE_CLIENT_ID`     | Entra ID application (client) ID.   |
| `XBOX_AZURE_CLIENT_SECRET` | Entra ID application client secret. |

## Install the package

> **Note:**
>
> NuGet hosts the `Com.Unity.Services.CloudCode.IAP` package publicly. You don't need a private feed or authentication.

To install the Xbox verification package to your Cloud Code module, run the following command from your Cloud Code module project using .NET CLI:

```bash
dotnet add package Com.Unity.Services.CloudCode.IAP
```

## Register the Xbox verification client

To register the Xbox verification client in your module setup:

1. Add the verification client in your `ICloudCodeSetup` implementation:

   ```csharp
   using Unity.Services.CloudCode.Apis.Extensions;
   using Unity.Services.CloudCode.Core;
   using Unity.Services.CloudCode.IAP.Extensions;

   public class ModuleConfig : ICloudCodeSetup
   {
       public void Setup(ICloudCodeConfig config)
       {
           config.AddGameApiClient()
                 .AddIapXboxVerificationClient();
       }
   }
   ```

2. If your secrets use different key names, pass a configuration callback:

   ```csharp
   config.AddIapXboxVerificationClient(options =>
   {
       options.TenantIdKey = "MY_TENANT_ID";
       options.ClientIdKey = "MY_CLIENT_ID";
       options.ClientSecretKey = "MY_CLIENT_SECRET";
   });
   ```

## Validate purchase ownership

To validate purchase ownership in a Cloud Code function before you grant entitlements:

1. Inject `IXboxVerificationApi` into your function.
2. Call `ValidateOwnershipAsync` to verify a purchase before granting entitlements. The method returns which products the player owns and which are missing.

```csharp
using System.Collections.Generic;
using System.Threading.Tasks;
using Unity.Services.CloudCode.Core;
using Unity.Services.IAP.Api;
using Unity.Services.IAP.Model;

public class XboxPurchaseVerifier
{
    [CloudCodeFunction("ValidateXboxPurchases")]
    public async Task<XboxOwnershipValidationResult> ValidateXboxPurchases(
        IExecutionContext context,
        IXboxVerificationApi xboxVerificationApi,
        string collectionsIdToken,
        List<string> productIds,
        string? sandboxId = null)
    {
        return await xboxVerificationApi.ValidateOwnershipAsync(
            context,
            collectionsIdToken,
            productIds,
            sandboxId);
    }
}
```

The `collectionsIdToken` value is the business-to-business token the game client obtains from the Microsoft GDK. The `sandboxId` parameter is optional and typically omitted for retail purchases.

The validation result contains the following fields:

| Field               | Type                   | Description                                                             |
| ------------------- | ---------------------- | ----------------------------------------------------------------------- |
| `AllOwned`          | `bool`                 | Returns true when the player owns every requested product.              |
| `OwnedItems`        | `List<CollectionItem>` | Owned products, including status, acquisition date, and other metadata. |
| `MissingProductIds` | `List<string>`         | Requested product IDs not found in the player's collection.             |

## Query entitlements for a single product

Use `QueryEntitlementsAsync` when you need the raw Collections API response for a single product, including SKU details:

```csharp
var response = await xboxVerificationApi.QueryEntitlementsAsync(
    context,
    collectionsIdToken,
    productId: "9NBLGGH4NNS1",
    skuId: "0010", // Optional
    sandboxId: null); // Null means retail

foreach (var item in response.Items)
{
    Console.WriteLine($"{item.ProductId} - {item.Status} (acquired {item.AcquiredDate})");
}
```

## Get the Collections ID token on the game client

Generate the `collectionsIdToken` on the game client with the Microsoft GDK before you call your Cloud Code module.

For token request details, refer to [To create a User Collections ID key for the Microsoft Store Collections service](https://learn.microsoft.com/en-us/gaming/gdk/docs/store/commerce/service-to-service/xstore-requesting-a-userstoreid?view=gdk-2604#to-create-a-user-collections-id-key-for-the-microsoft-store-collections-service) (Microsoft).

## Verify the module

To verify the module after deployment:

1. Trigger `ValidateXboxPurchases` from your game client or from Cloud Code in the [Unity Dashboard](https://cloud.unity.com/).
2. Confirm that the function response sets `AllOwned: true` for products the player owns.
3. Confirm that unknown product IDs appear in `MissingProductIds`.
4. Check module logs in the Unity Dashboard for successful Microsoft Store collection API calls.

Authentication failures appear as Entra ID errors. Invalid or expired `collectionsIdToken` values appear as `401 Unauthorized` responses from Microsoft Store Collections API. After you verify the module works correctly, you can use it in your game client's purchase flow to protect against spoofed or tampered purchases.
