Faturalama, planlar ve kota (Faz 13)
Plan matrisi, deneme, Stripe/iyzico/mock sağlayıcıları, kullanım raporu, kota ve özellik kapıları, uyarı e-postaları. Tasarım kararları: ADR-0020. Ortak kod: packages/billing (plan matrisi, sağlayıcı adaptörleri, kota…
Plan matrisi, deneme, Stripe/iyzico/mock sağlayıcıları, kullanım raporu, kota ve özellik kapıları, uyarı e-postaları. Tasarım kararları: docs/adr/0020-faturalama-kota-admin.md. Ortak kod: packages/billing (plan matrisi, sağlayıcı adaptörleri, kota yardımcıları, DNS doğrulaması, tek kullanımlık e-posta listesi).
1. Planlar
plans tablosu migration 0021 ile tohumlanır; packages/billing/src/plans.ts tek kaynaktır (ensurePlans eksik satırı tamamlar). Dashboard Organizasyon → Faturalama ve GET /internal/v1/billing/plans aynı veriyi okur.
| Plan | Aylık | Abone | Mesaj/ay | API rps | Üye | App | Saklama | Journey | Segment | A/B varyant | Özellikler |
|---|---|---|---|---|---|---|---|---|---|---|---|
| free | 0 | 10k | 100k | 20 | 3 | 1 | 30 gün | 1 | 10 | 2 | push, e-posta, in-app, journey (A/B, SMS, onay akışı, composer yok) |
| starter | $49 | 50k | 1M | 100 | 10 | 3 | 90 gün | 10 | 50 | 4 | + A/B, SMS, audit export, composer |
| growth | $199 | 500k | 10M | 500 | 50 | 10 | 365 gün | 100 | 500 | 10 | + onay akışı |
| enterprise | özel | ∞ | ∞ | 2000 | ∞ | ∞ | 730 gün | ∞ | ∞ | 10 | + SSO |
Diğer limitler: tags_per_user, aliases_per_user, subscriptions_per_user, report_history_days, webhooks, api_keys. null = sınırsız. Rol kapısı: features.roles dışındaki rol atanamaz (HS_PLAN_FEATURE_REQUIRED, details.feature = "roles").
Etkin plan (effectivePlanCode): ödemesi geçerli abonelik planı (active, trialing, past_due; PAID_PLAN_STATUSES) > deneme sürüyorsa starter (yalnızca yükseltir: platform yöneticisinin atadığı daha yüksek organizations.plan_code denemede düşürülmez) > organizations.plan_code > free. incomplete/incomplete_expired abonelik planı açmaz. Yeni organizasyon 14 gün deneme alır (trial_ends_at); 3 gün kala ve bitince e-posta gönderilir.
2. Sağlayıcılar
| Sağlayıcı | Seçim | Notlar |
|---|---|---|
stripe | STRIPE_SECRET_KEY + STRIPE_WEBHOOK_SECRET | REST (SDK yok): Checkout Session (subscription modu, client_reference_id = org_id, metadata.plan_code), Customer Portal, cancel_at_period_end, fatura listesi. Webhook POST /hooks/billing/stripe — Stripe-Signature (t/v1 HMAC-SHA256, 5 dk tolerans). Plan fiyatı plans.stripe_price_id ile eşlenir (Stripe fiyatının metadata.plan_code alanı da okunur). |
iyzico | IYZICO_API_KEY + IYZICO_SECRET_KEY + IYZICO_MERCHANT_ID + billing.iyzico feature flag (ya da BILLING_PROVIDER=iyzico) | Abonelik Checkout Form (IYZWSv2 imzalı). Akış: POST …/billing/checkout → subscription/checkoutform/initialize (müşteri adı, e-posta, GSM — fatura bilgisindeki telefon +90… biçimine normalize edilir) → form içeriği Redis hs:iyzico:form:<token> (30 dk) → kullanıcı API'nin barındırdığı GET /hooks/billing/iyzico/form/:token sayfasına gider (sayfaya özel CSP) → iyzico POST /hooks/billing/iyzico/checkout (token) → GET /v2/subscription/checkoutform/{token} ile sonuç sorgulanır, abonelik/plan senkronlanır ve dashboard'a 303 ile dönülür. Kart güncelleme POST /hooks/billing/iyzico/card. Webhook POST /hooks/billing/iyzico: X-IYZ-SIGNATURE-V3 = hex HMAC-SHA256(secretKey, merchantId + secretKey + iyziEventType + subscriptionReferenceCode + orderReferenceCode + customerReferenceCode); olaylar `subscription.order.success |
mock | anahtar yok (üretim dışı) | Checkout …/settings/billing?checkout=success&mock_checkout=<id>&plan=<kod>&customer=<cus> adresine döner; dashboard POST …/billing/mock/complete çağırır → Stripe biçiminde checkout.session.completed + customer.subscription.created + `invoice.paid |
BILLING_PROVIDER=auto (varsayılan): Stripe anahtarı varsa stripe, iyzico bayrağı + anahtar varsa iyzico, aksi halde mock.
none durumu — üretimde (NODE_ENV=production) Stripe/iyzico yapılandırılmamışsa okuma uçları çalışır: GET …/billing provider: "none" döner, GET …/billing/plans ve GET /internal/v1/billing/plans tüm planları purchasable: false verir; faturalar ve kullanım raporu etkilenmez. Checkout ve portal 503 HS_SERVICE_UNAVAILABLE (details.reason = billing_provider_not_configured, "Ödeme sağlayıcısı yapılandırılmamış"). Dashboard mevcut planı, limitleri ve kullanımı gösterir; satın alma satırı yerine "Plan değişikliği için destek ile iletişime geçin" kartı (markanın destek e-postası, yoksa support@pushizo.com) çıkar. Plan değişikliği platform admin panelinden yapılır.
Webhook işleme (BillingService.applyEvent): billing_events ile idempotent (işleme hata verirse olay kaydı silinir, sağlayıcının yeniden denemesi tekrar işler); abonelik olayı billing_subscriptions upsert + organizations.plan_code senkronu (iptal → free) + plan önbelleği geçersiz; fatura olayı invoices upsert, invoice.payment_failed → past_due + payment_failed e-postası (owner/admin/billing).
3. Uçlar
| Yol | İzin | Açıklama |
|---|---|---|
GET /internal/v1/billing/plans | oturum | Plan matrisi (purchasable: sağlayıcı fiyatı tanımlı ya da mock) |
GET /internal/v1/orgs/:orgId/billing | billing.manage | Durum: sağlayıcı, plan, deneme, abonelik, askıya alma, fatura bilgisi |
PUT …/billing/info | billing.manage | Fatura/vergi bilgileri (settings.billing) |
POST …/billing/checkout {plan_code} | billing.manage | Checkout URL'si; aktif abonelik varsa portal kullanılmalı (409) |
POST …/billing/portal | billing.manage | Müşteri portalı |
POST …/billing/cancel {cancel_at_period_end} | billing.manage | Dönem sonunda iptal / geri al |
GET …/billing/invoices?sync=1 | billing.manage | Faturalar (sync sağlayıcıdan çeker) |
GET …/usage?days=30 | analytics.read | Kullanım raporu |
GET …/features | oturum | Plan özellikleri/limitleri + feature flag değerleri |
POST …/billing/mock/complete | billing.manage | Mock checkout simülasyonu (üretim dışı) |
GET /v1/organization/usage?days= | org geneli analytics:read/admin anahtarı | Aynı kullanım raporu (public; Node hs.organization.usage(), PHP $hs->organization->usage(), diğer SDK'lar organization.usage) |
POST /hooks/billing/:provider | imza | Sağlayıcı webhook'u |
Kullanım raporu: metrics[] (metric, used, limit, percent, hard), apps[] (messages_sent, active_subscriptions, mau), daily[] (son N gün messages_sent / api_requests / events_ingested), trialing, trial_ends_at.
4. Kullanım ve kota
- Rollup (
usage-rollup, saatte bir;apps/worker/src/processors/billing.ts): dün + bugün içinusage_daily(org × app × metrik;app_id = ''toplam):messages_sent(deliveries.sent_at),events_ingested,api_requests(api_request_logs),active_subscriptions,mau(son 30 günlast_active_at). - Aylık mesaj sayacı Redis
hs:usage:{org}:{YYYYMM}:messages: API/kampanya gönderimleri oluşturma anında tahmini alıcı sayısı kadar rezerve edilir (QuotaService.assertMessages,options.quota_reserved); worker kitle çözümlemede gerçek hedef sayısıyla farkı bir kez düzeltir (hs:usage:adj:<mesaj>), kalan kotayı aşan teslimatlarsuppressed/quota_exceededolur. Zamanlanmış/tekrarlı kampanyalar zamanlamada ön kontrol edilir (rezervasyonsuz). Rollup sayacı aşağı çekmez, yukarı uzlaştırır. - Sert sınırlar (
HS_QUOTA_EXCEEDED429,details { metric, limit, used, add, plan }): aylık mesaj, app, üye + bekleyen davet, aktif journey (yayında), segment, webhook, API anahtarı, A/B varyant sayısı, kullanıcı başına tag/alias/abonelik; yeni organizasyon ilk 24 saat günde 1000 mesaj (new_org_daily_messages). Abone sayısı yumuşak sınırdır (uyarı). Worker kitle çözümlemede sayaç limiti aşmışsa teslimatlarısuppressed/quota_exceededyazar (son savunma). - Özellik kapıları (
HS_PLAN_FEATURE_REQUIRED403,details { feature, plan }):sms/emailkanal kurulumu,in_appmesaj oluşturma,journeysyayını,ab_testing(varyantlar),roles(rol ataması). - Plan bazlı rate limit: API anahtarı varsayılan kapasitesi planın
api_rpsdeğeri (RateLimitGuard; org plan önbelleği 60 sn). - Uyarılar (
usage_alerts, dönem başına bir kez): %80 ve %100 (messages_per_month,subscriptions) e-postaları owner/admin/billing üyelerine; deneme bitişi; ödeme başarısız.
Etkin plan önbelleği Redis hs:plan:{org} (60 sn); plan/abonelik değişiminde QuotaService.invalidate.
5. Askıya alma
organizations.status = suspended (platform admin): API anahtarı istekleri 403 HS_ORG_SUSPENDED, dashboard yalnızca faturalama/durum okur, SDK uçları 403, worker teslimatları suppressed/org_suspended. Kaldırılınca org_unsuspended e-postası. Ayrıntı: platform-admin.md.
6. Testler
Stripe test modu (Faz 13 kabulü): tests/e2e/billing-stripe.spec.ts kayıt → deneme → kota aşımı 429 → Stripe Checkout (yayımlanmış test kartı) → webhook → Growth → fatura → gönderim akışını doğrular. Yalnızca E2E_STRIPE=1 iken koşar; CI'da e2e.yml → e2e-stripe işi depo değişkeni STRIPE_E2E=true ve gizli anahtarlar STRIPE_TEST_SECRET_KEY (yalnızca sk_test_/rk_test_ kabul edilir), STRIPE_TEST_PRICE_GROWTH tanımlıyken çalışır; webhook'lar stripe listen --forward-to http://localhost:4000/hooks/billing/stripe ile yönlendirilir ve STRIPE_WEBHOOK_SECRET iş içinde üretilir. Yerelde: test anahtarlarıyla API'yi BILLING_PROVIDER=stripe başlatın, plans.stripe_price_id değerini Growth test fiyatına ayarlayın, stripe listen çalıştırın ve E2E_STRIPE=1 npx playwright test tests/e2e/billing-stripe.spec.ts.
packages/billing/src/__tests__/billing.test.ts (plan matrisi, etkin plan, kota/eşik/itibar yardımcıları, Stripe imza/normalizasyon/REST, iyzico yetki/webhook, mock, DNS, tek kullanımlık e-posta), apps/api/src/__tests__/phase13.int.test.ts (planlar/deneme/kullanım, kota ve özellik kapıları, mock checkout → abonelik → fatura, webhook imzası, askıya alma 403, support session, admin uçları, status page, OAuth, DNS), apps/worker/src/__tests__/billing.int.test.ts (rollup, uyarılar, bastırmalar, itibar, DNS, kota rezervasyonu, şikâyet incelemesi), apps/api/src/__tests__/iyzico.int.test.ts (sahte iyzico sunucusuyla form → sonuç → webhook), E2E tests/e2e/billing-admin.spec.ts (deneme → kota aşımı 429 → mock yükseltme → gönderim açılır) ve tests/e2e/billing-stripe.spec.ts.
Sunucu tarafı hızlı başlangıç
API anahtarı oluşturun, ilk POST /v1/notifications isteğini cURL ile gönderin, sonra sunucu SDK'larından birini (Node, PHP, Python, Go, Java, .NET, Ruby) kullanın.
Kanallara genel bakış
Pushizo'nun desteklediği kanallar (Web Push, iOS, Android, Huawei, e-posta, SMS, WhatsApp, RCS, in-app, bildirim kutusu, Live Activities), sağlayıcıları ve kurulum gereksinimleri.