# Receta: créditos de API

> Vende créditos prepagados para una API o una función de IA y descuenta uno por llamada. Si no alcanza el saldo, responde 402 con un enlace de recarga.

Los compradores pagan por adelantado un paquete de créditos; cada llamada gasta algunos. Perly Pay lleva la cuenta: el gasto es una sola llamada atómica que no baja de cero, y cada comprador tiene su propia página de recarga. Nada se cobra solo: cuando se acaban los créditos, el comprador recarga. Todo sobre los créditos está en [Sell credits](https://pay.perly.io/docs/credits.md); aquí, la receta alrededor.

> **Nota:** Con un asistente: la skill `sell-credits` del plugin de Perly Pay hace esta receta en tu proyecto. El ejemplo ejecutable es `examples/api-credits` (MIT) en el repositorio del plugin.

## Cómo funciona

1. **Un paquete de créditos** — Un producto y un precio con `model: "credit_pack"`, `unit_amount` en centavos y `credits`. Una vez por modo.
2. **Tus usuarios son clientes** — Se crean una vez con `external_id` (tu id de usuario); después, `credits.*` aceptan ese id.
3. **Recargas** — `credits.topUpUrl(userId)` es la página propia del comprador: elige un paquete y paga. Cada paquete pagado suma créditos.
4. **Gasto por llamada** — Primero `credits.consume(userId, cost, { idempotencyKey })`, luego el trabajo. Un reintento con la misma clave devuelve el primer gasto, así que la clave de cada llamada la genera tu servidor y nunca la toma de quien llama.
5. **¿No alcanza?** — `insufficient_credits` (409) no gasta nada: responde `402` con la página de recarga, no un 500.

**Spend one request's credits**

```js
try {
  await pay.credits.consume(userId, cost, { product: PRODUCT, action: "api", idempotencyKey: `req-${userId}-${requestId}` });
} catch (e) {
  if (e.code !== "insufficient_credits") throw e;
  return reply(402, { top_up: await pay.credits.topUpUrl(userId, { product: PRODUCT }) });
}
// now do the work
```

## Ejecuta el ejemplo

1. **Instala** — `npm install` en `examples/api-credits` (Node 20.12+).
2. **Clave** — Copia `.env.example` a `.env` y pon una clave de prueba (`sk_test_…`) en `PERLY_PAY_SECRET_KEY`.
3. **Prepara** — `npm run setup` crea el producto y el precio con tu clave e imprime sus ids para `.env`.
4. **Ejecuta** — `npm start` (o `npm run dev`), según diga su README.
5. **Paga sin billetera** — Llama a `POST /v1/shout` (402), abre la página que da `POST /top-up`, elige un paquete, pulsa **Simulate payment** en la factura y llama otra vez: 200 y `X-Credits-Remaining: 499`.

## Errores comunes

- Gastar sin clave de idempotencia: una llamada repetida descuenta dos veces.
- Tomar la clave del gasto de lo que envía el cliente (una cabecera `Idempotency-Key`): la misma clave devuelve el primer gasto como éxito y el cliente vuelve a recibir el trabajo sin pagar.
- Consultar el saldo y luego gastar en dos pasos. `consume` ya impide bajar de cero; el saldo es para mostrarlo.
- Primero el trabajo, luego el gasto. Al revés: primero el gasto.
- Ids de prueba con una clave real. Productos, precios y webhooks pertenecen a un modo: antes de lanzar, repite la configuración con `sk_live_…`.
