# Ricetta: crediti API

> Vendi crediti prepagati per un’API o una funzione di AI e scalane uno a chiamata. Se il saldo non basta, rispondi 402 con un link di ricarica.

Gli acquirenti pagano in anticipo un pacchetto di crediti; ogni chiamata ne consuma alcuni. Perly Pay tiene il conto: il consumo è una sola chiamata atomica che non va sotto zero, e ogni acquirente ha la sua pagina di ricarica. Niente viene addebitato da solo: quando i crediti finiscono, l’acquirente ricarica. Tutto sui crediti è in [Sell credits](https://pay.perly.io/docs/credits.md); qui, la ricetta intorno.

> **Nota:** Con un assistente: la skill `sell-credits` del plugin Perly Pay realizza questa ricetta nel tuo progetto. L’esempio eseguibile è `examples/api-credits` (MIT) nel repository del plugin.

## Come funziona

1. **Un pacchetto di crediti** — Un prodotto e un prezzo con `model: "credit_pack"`, `unit_amount` in centesimi e `credits`. Una volta per modalità.
2. **I tuoi utenti sono clienti** — Si creano una volta con `external_id` (il tuo id utente); poi `credits.*` accettano quell’id.
3. **Ricariche** — `credits.topUpUrl(userId)` è la pagina dell’acquirente: sceglie un pacchetto e paga. Ogni pacchetto pagato aggiunge crediti.
4. **Consumo a chiamata** — Prima `credits.consume(userId, cost, { idempotencyKey })`, poi il lavoro. Un nuovo tentativo con la stessa chiave restituisce il primo consumo, quindi la chiave di ogni chiamata la genera il tuo server e non la prende mai da chi chiama.
5. **Non basta?** — `insufficient_credits` (409) non consuma nulla: rispondi `402` con la pagina di ricarica, non con 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
```

## Esegui l’esempio

1. **Installa** — `npm install` in `examples/api-credits` (Node 20.12+).
2. **Chiave** — Copia `.env.example` in `.env` e metti una chiave di test (`sk_test_…`) in `PERLY_PAY_SECRET_KEY`.
3. **Prepara** — `npm run setup` crea il prodotto e il prezzo con la tua chiave e stampa i loro id per `.env`.
4. **Avvia** — `npm start` (o `npm run dev`), come dice il suo README.
5. **Paga senza wallet** — Chiama `POST /v1/shout` (402), apri la pagina che restituisce `POST /top-up`, scegli un pacchetto, premi **Simulate payment** sulla fattura e chiama di nuovo: 200 e `X-Credits-Remaining: 499`.

## Errori comuni

- Consumare senza chiave di idempotenza: una chiamata ripetuta scala due volte.
- Prendere la chiave del consumo da ciò che invia il client (un header `Idempotency-Key`): la stessa chiave restituisce il primo consumo come successo e il client riceve di nuovo il lavoro senza pagare.
- Controllare il saldo e poi consumare in due passi. `consume` impedisce già di andare sotto zero; il saldo serve per mostrarlo.
- Prima il lavoro, poi il consumo. Al contrario: prima il consumo.
- Id di test con una chiave live. Prodotti, prezzi e webhook appartengono a una modalità: prima del lancio, ripeti la configurazione con `sk_live_…`.
