# Comply with Apple and Google external purchase requirements

> Understand your compliance responsibilities for external purchases on Apple and Google, and the SDK and webhook tools Unity provides to help you meet them.

An external purchase sells digital content through your own web checkout (such as a payment provider) instead of Apple's or Google's in-app billing. Apple and Google allow this only in select regions and only under their own program rules, such as the [External Purchase program](https://developer.apple.com/documentation/storekit/external-purchase) (Apple) and the [external offers programs](https://developer.android.com/google/play/billing/externalcontentlinks) (Google Play). Eligibility, fees, disclosures, and transaction reporting vary by platform, region, and app. In some regions, you might need little or none of these purchase requirements.

> **Important:**
>
> Unity doesn't perform platform compliance on your behalf. You're responsible for determining your eligibility, meeting each platform's technical and policy requirements, and reporting transactions to Apple and Google where required. Consult Apple and Google directly, and review the [pre-release terms](./_index.md), before you ship external purchases.

To help you build your own compliance flow, Unity provides SDK hooks and webhook data. This page explains the division of responsibilities, the order model your compliance steps build on, and the specific actions you can take.

This page is part of the [Integrate Direct to Consumer (D2C) payment providers workflow](./workflow.md#platform-compliance-for-external-purchases). Set up your [payment provider](./configure-payment-provider.md) and [purchase flow](./purchases-sdk.md) before you add the compliance handling described here.

## Division of responsibilities

Unity provides only technical facilitation. Your payment provider acts as merchant of record for payment processing, tax, and disputes, but it doesn't handle Apple or Google platform reporting. The platform compliance is your responsibility, and Unity doesn't enroll you in any platform program.

### Unity's technical facilitation

When a Payment Providers purchase proceeds, Unity opens the checkout URL on your behalf. By default it opens in the device's external browser. If you opt into in-app presentation (`CheckoutPresentationMode.WebView`), Unity opens it in an in-app browser instead. To further understand how Unity works with orders, refer to [how Unity creates and opens an order](#how-unity-creates-and-opens-an-order).

Unity doesn't call Apple's external-purchase notice or Google's external-link APIs for you, and Unity doesn't decide your eligibility. You invoke those platform APIs yourself, as described in [your responsibilities](#your-responsibilities).

### Your responsibilities

Unity provides only technical facilitation. You are responsible for the following tasks:

* Determine whether your app and region are eligible for each platform's external purchase program.
* Run your own eligibility and disclosure checks before a purchase starts, with Apple's `ExternalPurchaseClient` and Google's `ExternalBillingProgramClient`.
* Acquire and report any transaction tokens that Apple or Google requires, with each platform's own APIs.

## How Unity creates and opens an order

You normally start a Payment Providers purchase with a single call, `PurchaseProduct(catalogListingId)`, where `catalogListingId` identifies the offer in your [Remote Catalog](./configure-remote-catalog.md). A purchase has two underlying steps, and you can run the first one yourself:

* `GenerateURL(catalogListingId, tokens)` creates the order and returns its checkout URL, without opening it.
* `PurchaseProduct(catalogListingId)` opens the checkout. If you already called `GenerateURL` for the same listing, it reuses that order. If you didn't, it creates the order itself, with no tokens, and then opens it.

You only need `GenerateURL` first when you need the URL up front (to [disclose or render it](#disclose-or-render-the-checkout-url)) or when you need to [attach reporting tokens](#attach-and-report-transaction-tokens) to the order. Otherwise, call `PurchaseProduct` directly.

## Decide what your compliance flow needs

The remaining sections are independent. Use the following table to find the steps that apply to your app and platforms:

| If you need to                                              | Do this                                                                      | Section                                                                           |
| :---------------------------------------------------------- | :--------------------------------------------------------------------------- | :-------------------------------------------------------------------------------- |
| Enforce your own eligibility rules in-app                   | Register a compliance callback that runs before each purchase                | [Gate purchases with a compliance check](#gate-purchases-with-a-compliance-check) |
| Show or disclose the checkout URL before the purchase opens | Call `GenerateURL` to get the URL, then `PurchaseProduct` to open it         | [Disclose or render the checkout URL](#disclose-or-render-the-checkout-url)       |
| Report external transactions to Apple or Google             | Attach tokens with `GenerateURL`, then read them back from the order webhook | [Attach and report transaction tokens](#attach-and-report-transaction-tokens)     |

## Gate purchases with a compliance check

This step is optional. Add it only if you enforce your own eligibility rules in-app.

Register a compliance callback to apply your own eligibility rules before a purchase proceeds. When you call `PurchaseProduct` (or `Purchase`), Unity runs your callback first and passes a `PaymentProviderComplianceContext` that exposes the `Cart` being purchased. If the callback returns `false` (or throws), the purchase fails with `PurchasingUnavailable` and no order is created.

1. Define a function that returns a `Task<bool>` indicating whether the player passed your compliance check:

   ```csharp
   Task<bool> RunComplianceCheckAsync(PaymentProviderComplianceContext context)
   {
       const bool allow = true;
       return Task.FromResult(allow);
   }
   ```

2. Register the function on the Payment Providers purchase service so Unity calls the function when a purchase begins:

   ```csharp
   m_PurchasingService.PaymentProviders?.SetComplianceCheck(RunComplianceCheckAsync);
   ```

3. Use the platform clients inside your check to call the APIs each platform requires for compliance:

   * **Apple**: use `ExternalPurchaseClient` to access Apple's external purchase APIs.
   * **Google**: use `ExternalBillingProgramClient` to access Google's external billing APIs.

> **Note:**
>
> The compliance callback runs only for `PurchaseProduct` and `Purchase`. `GenerateURL` isn't gated by it and creates the order immediately, so if you call `GenerateURL` (for disclosure or tokens), run your eligibility checks before that call.

## Disclose or render the checkout URL

A platform might require you to display or disclose the external purchase URL before the player proceeds, or you might want to render the checkout in your own web view. In either case, call `GenerateURL` to get the URL without opening it:

```csharp
string url = await m_PurchasingService.PaymentProviders.GenerateURL(catalogListingId);
// Display or disclose the URL, then call PurchaseProduct to open the checkout.
```

`PurchaseProduct(catalogListingId)` then reuses the order `GenerateURL` already created instead of creating a duplicate.

> **Note:**
>
> Don't call `GenerateURL` only to prepare a purchase in advance. If you don't need the URL or tokens, call `PurchaseProduct` directly. It creates and opens the order for you. A redundant `GenerateURL` (especially with a forgotten `await`) only adds a race window.

## Attach and report transaction tokens

This step is required when Apple or Google requires you to report external transactions (this varies by program and region).

Apple and Google require you to report each external transaction back to them through their server APIs. You get an opaque transaction token from the platform, attach it to the Unity order, and receive it back in the order webhook so you can file your report. You generate the tokens yourself: Apple tokens come from `ExternalPurchaseClient`, and the Google token comes from `ExternalBillingProgramClient`.

`PurchaseProduct` doesn't accept tokens, so a purchase that creates its own order attaches no tokens. To get your tokens onto the order, call `GenerateURL(catalogListingId, tokens)` first. The subsequent `PurchaseProduct` for the same listing then adopts that token-bearing order:

```csharp
await m_PurchasingService.PaymentProviders.GenerateURL(catalogListingId, tokenList);
m_PurchasingService.PurchaseProduct(catalogListingId);
```

Unity stores the tokens with the order without inspecting or validating them, and surfaces them in order webhooks. They're not returned in the order API response.

> **Note:**
>
> If you attach reporting tokens, the order webhook is the only place Unity returns the tokens. Refer to [Configure a webhook or Cloud Code module for purchase fulfillment](./configure-fulfilment.md).

### Tokens in the webhook payload

Order webhooks include an `externalTransactionTokens` array under `data`. Each entry carries the token and the store it targets:

```json
{
  "eventType": "order.paid",
  "data": {
    "id": "...",
    "playerId": "...",
    "status": "paid",
    "externalTransactionTokens": [
      { "store": "apple", "token": "opaque-apple-transaction-token", "type": "acquisition" },
      { "store": "google", "token": "opaque-google-transaction-token" }
    ]
  }
}
```

**store** (string, required): The app store the token targets. One of `apple` or `google`. Determines where you report the transaction.**token** (string, required): The external purchase or transaction token. Unity stores and forwards it without inspecting it.**type** (string): The Apple-only token category. One of `acquisition`, `services`, or `linkOut`. Omitted for Google.

> **Note:**
>
> You can supply up to two tokens per order, because a single Apple external purchase can require two token types depending on the region, such as an `acquisition` token (EU) and a `linkOut` token (Japan). Report each token according to the relevant program. Unity doesn't act on the `type`.

Use these tokens to self-report the transaction through Apple's External Purchase Server API or Google's `externalTransactions` API, as required by each program.

## Next steps

This page is part of a workflow to set up D2C payment providers with IAP. To continue this workflow, choose one of the following options:

[Integrate D2C payment providers](./workflow.md#platform-compliance-for-external-purchases): Return to the Integrate D2C payment providers with IAP workflow page.
[Fulfill purchases through the SDK](./purchases-sdk.md): Review the normal purchase flow that these compliance steps build on.
[Configure a webhook for fulfillment](./configure-fulfilment.md): Configure a webhook to receive order events, including external transaction tokens.
