# Rezept: API-Credits

> Verkaufen Sie vorausbezahlte Credits für eine API oder eine AI-Funktion und buchen Sie pro Anfrage einen ab. Reicht das Guthaben nicht, gibt es eine 402 mit Aufladelink.

Käufer bezahlen ein Credit-Paket im Voraus; jede Anfrage verbraucht ein paar. Die Buchhaltung führt Perly Pay: Ein Verbrauch ist ein atomarer Aufruf, der nie ins Minus geht, und jeder Käufer hat eine eigene Aufladeseite. Nichts wird von selbst abgebucht – gehen die Credits aus, lädt der Käufer auf. Alles über Credits steht unter [Sell credits](https://pay.perly.io/docs/credits.md); hier geht es um das Rezept drumherum.

> **Hinweis:** Mit einem Assistenten: Dieses Rezept setzt der Skill `sell-credits` des Perly-Pay-Plugins in Ihrem Projekt um. Ein lauffähiges Beispiel ist `examples/api-credits` (MIT) im Repository des Plugins.

## So funktioniert es

1. **Ein Credit-Paket** — Ein Produkt und ein Preis mit `model: "credit_pack"`, `unit_amount` in Cent und `credits`. Einmal pro Modus.
2. **Ihre Nutzer sind Kunden** — Einmal mit `external_id` (Ihre Nutzer-ID) angelegt; danach nehmen `credits.*` diese ID an.
3. **Aufladen** — `credits.topUpUrl(userId)` ist die eigene Seite des Käufers: Er wählt ein Paket und zahlt. Jedes bezahlte Paket fügt Credits hinzu.
4. **Verbrauch pro Anfrage** — Erst `credits.consume(userId, cost, { idempotencyKey })`, dann die Arbeit. Eine Wiederholung mit demselben Schlüssel liefert die erste Abbuchung zurück, daher erzeugt Ihr Server den Schlüssel pro Anfrage und nimmt ihn nie vom Aufrufer.
5. **Zu wenig?** — `insufficient_credits` (409) bucht nichts ab: Antworten Sie mit `402` und der Aufladeseite, nicht mit 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
```

## Beispiel starten

1. **Installieren** — `npm install` in `examples/api-credits` (Node 20.12+).
2. **Schlüssel** — Kopieren Sie `.env.example` nach `.env` und tragen Sie einen Testschlüssel (`sk_test_…`) in `PERLY_PAY_SECRET_KEY` ein.
3. **Vorbereiten** — `npm run setup` legt mit Ihrem Schlüssel Produkt und Preis an und gibt ihre IDs für `.env` aus.
4. **Starten** — `npm start` (oder `npm run dev`), wie seine README sagt.
5. **Ohne Wallet bezahlen** — Rufen Sie `POST /v1/shout` auf (402), öffnen Sie die Seite, die `POST /top-up` liefert, wählen Sie ein Paket, klicken Sie auf der Rechnung auf **Simulate payment** und rufen Sie erneut auf: 200 und `X-Credits-Remaining: 499`.

## Fallstricke

- Verbrauch ohne Idempotenzschlüssel: Eine wiederholte Anfrage bucht doppelt ab.
- Den Abbuchungsschlüssel aus dem nehmen, was der Client sendet (Header `Idempotency-Key`): Derselbe Schlüssel liefert die erste Abbuchung als Erfolg zurück, und der Client bekommt die Arbeit erneut ohne Bezahlung.
- Erst das Guthaben prüfen, dann in zwei Schritten abbuchen. `consume` geht selbst nie ins Minus; das Guthaben ist zum Anzeigen da.
- Erst die Arbeit, dann der Verbrauch. Umgekehrt: erst der Verbrauch.
- Test-IDs mit einem Live-Schlüssel. Produkte, Preise und Webhooks gehören zu einem Modus: Wiederholen Sie die Einrichtung vor dem Start mit `sk_live_…`.
