# Przepis: kredyty API

> Sprzedawaj przedpłacone kredyty do API lub funkcji AI i pobieraj jeden za zapytanie. Gdy saldo nie wystarcza — odpowiedź 402 z linkiem do doładowania.

Kupujący z góry opłacają pakiet kredytów; każde zapytanie zużywa kilka. Rozliczenie prowadzi Perly Pay: zużycie to jedno atomowe wywołanie, które nie schodzi poniżej zera, a każdy kupujący ma własną stronę doładowania. Nic nie jest pobierane samo — gdy kredyty się skończą, kupujący doładowuje. Wszystko o kredytach jest w [Sell credits](https://pay.perly.io/docs/credits.md); tutaj przepis wokół nich.

> **Uwaga:** Z asystentem: ten przepis realizuje w Twoim projekcie skill `sell-credits` wtyczki Perly Pay. Działający przykład to `examples/api-credits` (MIT) w repozytorium wtyczki.

## Jak to działa

1. **Pakiet kredytów** — Produkt i cena z `model: "credit_pack"`, `unit_amount` w centach i `credits`. Raz na tryb.
2. **Twoi użytkownicy to klienci** — Tworzeni raz z `external_id` (Twoim identyfikatorem użytkownika); potem `credits.*` przyjmują ten identyfikator.
3. **Doładowanie** — `credits.topUpUrl(userId)` to własna strona kupującego: wybiera pakiet i płaci. Każdy opłacony pakiet dodaje kredyty.
4. **Zużycie za zapytanie** — Najpierw `credits.consume(userId, cost, { idempotencyKey })`, potem praca. Ponowienie z tym samym kluczem zwraca pierwsze pobranie, więc klucz dla każdego zapytania tworzy Twój serwer i nigdy nie bierze go od wywołującego.
5. **Brakuje?** — `insufficient_credits` (409) niczego nie pobiera: odpowiedz `402` ze stroną doładowania, a nie 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
```

## Uruchom przykład

1. **Zainstaluj** — `npm install` w `examples/api-credits` (Node 20.12+).
2. **Klucz** — Skopiuj `.env.example` do `.env` i wpisz klucz testowy (`sk_test_…`) w `PERLY_PAY_SECRET_KEY`.
3. **Przygotuj** — `npm run setup` tworzy produkt i cenę Twoim kluczem i drukuje ich identyfikatory do `.env`.
4. **Uruchom** — `npm start` (lub `npm run dev`), jak mówi jego README.
5. **Zapłać bez portfela** — Wywołaj `POST /v1/shout` (402), otwórz stronę, którą daje `POST /top-up`, wybierz pakiet, kliknij **Simulate payment** na fakturze i wywołaj ponownie: 200 i `X-Credits-Remaining: 499`.

## Pułapki

- Zużycie bez klucza idempotencji: powtórzone zapytanie pobiera dwa razy.
- Klucz pobrania z tego, co wysyła klient (nagłówek `Idempotency-Key`): ten sam klucz zwraca pierwsze pobranie jako sukces i klient znowu dostaje pracę bez zapłaty.
- Sprawdzanie salda, a potem pobieranie w dwóch krokach. `consume` sam nie schodzi poniżej zera; saldo służy do wyświetlania.
- Najpierw praca, potem zużycie. Odwrotnie: najpierw zużycie.
- Testowe identyfikatory z kluczem produkcyjnym. Produkty, ceny i webhooki należą do jednego trybu: przed startem powtórz konfigurację z `sk_live_…`.
