> 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/all-out-docs/docs-es/conceptos-centrales-del-motor/purchasing-product-apis.md).

# API de compra/producto

Los jugadores pueden comprar productos (pases de juego + consumibles) por Sparks en tu juego. Cuando ocurre una compra, All Out llama a tu **manejador de compras** para que puedas otorgar el objeto o la característica.

{% hint style="info" %}
Para crear productos y ver analíticas, consulta [Productos dentro del juego](/all-out-docs/docs-es/monetizacion/in-game-products.md).
{% endhint %}

### Tipos de productos (resumen rápido)

* **Pases de juego**: desbloqueos persistentes de una sola vez (pase de admin, habilidad permanente, propiedad de casa)
* **Consumibles**: se pueden comprar varias veces (poción, mejora temporal, paquete de monedas de una sola vez)

Los tipos de productos se configuran en el portal del creador. Consulta [Productos dentro del juego](/all-out-docs/docs-es/monetizacion/in-game-products.md).

### Referencia de la API de compras

```go
Producto :: 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;
}
```

### Solicitar una compra

Para iniciar el flujo de compra, llama a `Purchasing.prompt_purchase` con un ID de producto.

```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" %}
El ID del producto se muestra en la página de monetización/productos en el portal del creador (puedes hacer clic para copiarlo). Consulta [Productos dentro del juego](/all-out-docs/docs-es/monetizacion/in-game-products.md).
{% endhint %}

### Comprobar si un jugador posee un producto (pases de juego)

Para los pases de juego, puedes limitar funciones del juego comprobando la propiedad.

```go
ADMIN_PASS_ID :: "prod_admin";

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

{% hint style="info" %}
Los consumibles se pueden comprar varias veces; no se debe usar owns\_product para otorgarlos. Los consumibles normalmente se conceden en tu manejador de compras (monedas, objetos, mejoras, etc.).
{% endhint %}

### Leer información del producto (nombre/precio/icono)

Si quieres mostrar una interfaz de “Comprar” o registrar información del producto, obtén la definición del producto por ID:

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

### El manejador de compras (concesión)

Cuando un jugador compra un producto en tu juego, All Out llama a:

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

Si tu juego no define `ao_purchase_handler`, los productos no consumibles se conceden automáticamente para que funcionen con `Purchasing.owns_product`. Los productos consumibles siguen requiriendo un manejador y se marcan como fallos de concesión si no hay uno.

Deberías:

* Hacer switch sobre el ID del producto
* Conceder el objeto/la característica
* Devolver `true` si la concesión tuvo éxito
* Devolver `false` si la concesión falló (inventario lleno, prerrequisito faltante, 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: {
            // Ejemplo: conceder monedas (consumible)
            Economy.deposit_currency(player, "Coins", 500);
            Notifier.notify(player, "¡Gracias! +500 monedas");
            return true;
        }
        case DOUBLE_JUMP_ID: {
            // Ejemplo: desbloqueo de pase de juego (persistente)
            // Normalmente solo lo habilitas mediante comprobaciones de owns_product() en otra parte.
            // Aun así, devuelve true para que la compra se considere concedida.
            Notifier.notify(player, "¡Doble salto desbloqueado!");
            return true;
        }
    }

    // ID desconocido
    return false;
}
```

### Fallos de concesión y reintentos

Si tu manejador de compras devuelve `false`, la compra se marca como un **fallo de concesión**.

* Los fallos de concesión se muestran en el portal del creador
* Se reintentan automáticamente cuando el jugador entra hasta que tienen éxito
* Puedes volver a ejecutar manualmente un manejador de compras marcando una compra como “no concedida” en la interfaz de datos del jugador

Consulta:

* [Productos dentro del juego](/all-out-docs/docs-es/monetizacion/in-game-products.md) (“Manejadores de compra / Fallos de concesión”)
* [Editar/ver datos del jugador](/all-out-docs/docs-es/datos-y-persistencia/editing-viewing-player-data.md) (sección “Compras”)

### Probar compras

En un juego iniciado desde el editor (Iniciar juego o `run_tests`), `Purchasing.prompt_purchase` abre un aviso de compra de prueba marcado como "Compra de prueba: no se gastan Sparks". Ofrece **Comprar** según tu saldo de Sparks. Comprar ejecuta tu `ao_purchase_handler` a través del mismo flujo del servidor que una compra real, y un pase de juego concedido entonces pasa `Purchasing.owns_product`.

* El producto ya debe existir para tu juego (consulta [Productos dentro del juego](/all-out-docs/docs-es/monetizacion/in-game-products.md)).
* El aviso dice "Compra completada" incluso cuando tu manejador devuelve `false`, así que revisa la concesión en sí.
* Las compras de prueba no dejan registro de compra. Después de que el jugador vuelva a entrar, `owns_product` vuelve a ser false y un fallo de concesión no se reintenta. Cualquier cosa que tu manejador haya guardado por su cuenta, como la moneda, se mantiene.
* Llamar a `ao_purchase_handler` tú mismo, por ejemplo desde un comando de chat, ejecuta solo tu código de concesión. Omite el flujo del servidor y nunca establece `owns_product`.

En una prueba, pulsa los botones del aviso por nombre:

```go
test_buy_coin_pack :: proc() {
    Test.click_button("coin_pack_button");    // tu UI que llama a Purchasing.prompt_purchase
    Test.click_button("purchase modal/Buy");  // espera hasta ~3 s a que se cargue el producto
    Test.wait_seconds(3);                     // el manejador se ejecuta 1-2 s después de Buy
    Test.click_button("purchase modal/Okay"); // cierra "Compra completada"
    Test.assert_balance("coin pack granted", "Coins", 500);
}
```

### Entre juegos (hub + minijuegos)

Si usas jerarquía de juegos, las compras se pueden compartir entre tus juegos. Consulta [Productos/datos entre juegos](/all-out-docs/docs-es/datos-y-persistencia/cross-game-products-data.md).
