Kupony (pule kodów)

Kupon w Zevio to pula kodów (cpn_…) należąca do organizacji. Każdy kod (code_…) ma własne limity użyć. Tworzenie: Promocje → Kupony → Nowa pula (/dashboard/promotions/coupons/create). Szczegóły: /dashboard/promotions/coupons/[couponId]. Po utworzeniu pulę przypinasz do oferty w Oferta → Promocje. Rabat jest grantowany po SUCCESS.

Nazwa puli

Wymagana, 1-120 znaków. Widoczna tylko w panelu i przy multipicku na ofercie - klient widzi sam kod, nie nazwę puli.

Typ rabatu

  • Procent (`percent`) - wartość całkowita 0-100. W podglądzie checkoutu staje się generalDiscount i zastępuje zniżkę ogólną oferty; trial oferty zwykle zostaje.
  • Stała cena brutto (`fixed_gross`) - kwota ≥ 0 z dokładnością do 0,01. Mapuje się na trialAccess-podobne zachowanie w merge i przy aktywnym grancie wypiera bazowe promocje oferty na cyklach.
Typ rabatu i czas trwania są niemutowalne po utworzeniu puli. Edytujesz później: nazwę, status ACTIVE/INACTIVE, daty ważności oraz kody.

Typ trwania rabatu

  • Jednorazowo (`once`) - tylko płatność, na której użyto kodu; grant consumed na tym pay_….
  • Liczba płatności (`repeating_payment_count`) - N cykli (1-999); po starcie paymentsApplied rośnie z każdym SUCCESS.
  • Powtarzający się interwał (`repeating_interval`) - od daty startu subskrypcji przez token 1D…1Y lub własny 1-999 + D/W/M/Y. Domyślnie w UI: repeating_interval.
  • Bezterminowo (`forever`) - do dezaktywacji / zastąpienia wpisu.

Okres ważności puli

Opcjonalne validFrom / expiresAt (datetime). Poza oknem realizacja kończy się errors.promotion.code_expired, nawet jeśli kod ma wolne użycia.

Sposób dodania kodów

  • Wygeneruj automatycznie - autoGenerateCount 1-500 (domyślnie 10). Kody: 8 znaków uppercase.
  • Wgraj własne - lista 1-500 kodów; każdy 1-64 znaków; separatory: nowa linia, przecinek, średnik. Kody trim + uppercase. Kolizja unikalności w organizacji kończy się błędem zapisu.

Limity użyć

  • Maks. użyć na kod - domyślnie 1; min. 1 gdy przełącznik nielimitowanych jest wyłączony.
  • Nielimitowane użycia kodu - zapisuje sentinel 999_999_999 (UNLIMITED_CODE_REDEMPTIONS).
  • Maks. użyć całej puli (`maxRedemptions`) - dostępne w API (nie w podstawowym UI create); po wyczerpaniu errors.promotion.pool_exhausted.

Włączenie na ofercie

W Oferta → Promocje: Akceptuj kody kuponów + wybór co najmniej jednej puli ACTIVE. REST: PUT /promotions/coupons/{qrId} z { enabled, couponId: string[] } (tablica wymagana gdy enabled). Pule wewnętrzne powiązane z programem poleceń (coupon.referralId) nie są wybieralne jako zwykłe kupony.

Lista, szczegóły, kody

  • Lista: status, liczba kodów, użycia, daty; filtr zarchiwizowanych.
  • Szczegóły: podsumowanie limitów per kod (none | unlimited | uniform | range | mixed), dodawanie kolejnych kodów, usuwanie kodów bez użyć i bez referralLinkId.
  • Eksport kodów z panelu.
  • Usuwanie kodu z redemptions lub powiązanego z linkiem polecającym jest pomijane / blokowane (code_not_deletable).

Archiwizacja, przywracanie, usuwanie

  • Archiwizacja → ARCHIVED + archivedAt; w tym stanie nie dodasz/usuniesz kodów ani nie zaktualizujesz puli jak aktywnej.
  • Przywracanie (restore) → ACTIVE (tylko kupony; programy poleceń nie mają restore).
  • Usunięcie puli tylko gdy redemptionCount === 0; inaczej errors.promotion.coupon_in_use.
  • INACTIVE blokuje nowe realizacje bez archiwizacji.

Przebieg realizacji

  • Walidacja publiczna: POST /public/promotions/validate oraz pricing POST /public/promotions/payment-pricing (rate limit).
  • Po SUCCESS: PromotionRedemption + wpis na subskrypcji; webhook promotion.redeemed z side: "coupon".
  • Idempotency grantu spina płatność z redeem (redeem_{paymentId} / równoważny klucz).

Klucze błędów

  • errors.promotion.code_not_found | code_expired | code_exhausted | pool_exhausted
  • errors.promotion.pool_inactive | pool_archived | coupon_in_use | code_not_deletable
  • errors.promotion.code_redemption_disabled | qr_not_eligible

Identyfikatory

  • Pula: cpn_ + 12.
  • Kod: code_ + 12 (lub własny string uppercase jako wartość kodu widoczna dla klienta).
  • Redemption: pred_….