> For the complete documentation index, see [llms.txt](https://docs.allout.game/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.allout.game/core-engine-concepts/purchasing-product-apis.md).

# Purchasing/Product APIs

Players can buy products (game passes + consumables) for Sparks in your game. When a purchase happens, All Out calls your **purchase handler** so you can grant the item or feature.

{% hint style="info" %}
For creating products and viewing analytics, see [In Game Products](/monetization/in-game-products.md).
{% endhint %}

### Product types (quick overview)

* **Game passes**: one-time, persistent unlocks (admin pass, permanent ability, house ownership)
* **Consumables**: can be bought multiple times (potion, temporary buff, one-time coin pack)

Product types are configured in the creator portal. See [In Game Products](/monetization/in-game-products.md).

### Purchasing API reference

```go
Product :: struct {
    id: string;
    name: string;
    description: string;
    price: s64;
    consumable: bool;
    icon: Texture_Asset;
}

Purchasing :: struct {
    prompt_purchase :: proc(player: Player, id: string);
    owns_product    :: proc(player: Player, id: string) -> bool;
    get_product     :: proc(id: string) -> Product;
}
```

### Prompting a purchase

To start the purchase flow, call `Purchasing.prompt_purchase` with a product ID.

```go
BUY_DOUBLE_JUMP_ID :: "prod_double_jump";

try_prompt_double_jump :: proc(player: Player) {
    Purchasing.prompt_purchase(player, BUY_DOUBLE_JUMP_ID);
}
```

{% hint style="info" %}
The product ID is shown in the monetization/products page in the creator portal (you can click to copy it). See [In Game Products](/monetization/in-game-products.md).
{% endhint %}

### Checking if a player owns a product (game passes)

For game passes, you can gate gameplay features by checking ownership.

```go
ADMIN_PASS_ID :: "prod_admin";

is_admin :: proc(player: Player) -> bool {
    return Purchasing.owns_product(player, ADMIN_PASS_ID);
}
```

{% hint style="info" %}
Consumables can be bought multiple times, owns\_product shouldn't be used to award them. Consumables are usually granted in your purchase handler (coins, items, buffs, etc).
{% endhint %}

### Reading product info (name/price/icon)

If you want to show a “Buy” UI or log product info, fetch the product definition by ID:

```go
product := Purchasing.get_product("prod_double_jump");
log_info("Product % costs % sparks", {product.name, product.price});
```

### The purchase handler (granting)

When a player buys a product in your game, All Out calls:

```go
ao_purchase_handler :: proc(player: Player, id: string) -> bool
```

If your game does not define `ao_purchase_handler`, non-consumable products are granted automatically so they work with `Purchasing.owns_product`. Consumable products still require a handler and are marked as grant failures without one.

You should:

* Switch on the product ID
* Grant the item/feature
* Return `true` if the grant succeeded
* Return `false` if the grant failed (inventory full, missing prerequisite, etc)

```go
COIN_PACK_ID :: "prod_coin_pack_small";
DOUBLE_JUMP_ID :: "prod_double_jump";

ao_purchase_handler :: proc(player: Player, id: string) -> bool {
    switch id {
        case COIN_PACK_ID: {
            // Example: grant coins (consumable)
            Economy.deposit_currency(player, "Coins", 500);
            Notifier.notify(player, "Thanks! +500 coins");
            return true;
        }
        case DOUBLE_JUMP_ID: {
            // Example: game pass unlock (persistent)
            // You typically just enable it via owns_product() checks elsewhere.
            // Still return true so the purchase is considered granted.
            Notifier.notify(player, "Double Jump unlocked!");
            return true;
        }
    }

    // Unknown ID
    return false;
}
```

### Grant failures & retries

If your purchase handler returns `false`, the purchase is marked as a **grant failure**.

* Grant failures are shown in the creator portal
* They're automatically retried when the player joins until they succeed
* You can manually re-run a purchase handler by marking a purchase as “ungranted” in the player data UI

See:

* [In Game Products](/monetization/in-game-products.md) (“Purchase Handlers / Grant Failures”)
* [Editing/Viewing Player Data](/data-and-persistence/editing-viewing-player-data.md) (“Purchases” section)

### Cross-game (hub + minigames)

If you use game parenting, purchases can be shared across your games. See [Cross-Game Products/Data](/data-and-persistence/cross-game-products-data.md).
