Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Monthly/yearly purchases were one-time charges: our "subscription" was just an end_date stamp, access silently died at period end and no renewal machinery existed (no auto-charge, no reminder) — every paying customer had to notice the lockout and re-buy by hand. - Checkout now mode:"subscription" with inline recurring price_data; the Stripe customer is stored on first purchase and reused (saved card + invoice history on one record, with a stale-customer retry guard) - invoice.paid webhook: extends end_date to the billing-line period end, records a completed payment (deduped on stripe_invoice_id against webhook retries), captures subscription_renewed with $revenue, mails the receipt; late dunning recovery re-activates the row and re-grants Full-plan brands - invoice.payment_failed webhook: dunning mail with Stripe's next retry date; access is NOT cut — end_date governs and the nightly cron closes it if every retry fails. Product rule: mail on success, mail on failure, never a pre-charge reminder - customer.subscription.deleted: stamps cancelledAt; renewals stop and access runs out at end_date naturally - cancel()/resume() sync cancel_at_period_end to Stripe (forwardRef pair) — an in-app cancel that leaves the card being charged was unacceptable - subscription_create invoices only enrich the checkout's payment row (payment intent + invoice id for receipts/panel refunds); activation, revenue and the receipt stay on checkout.session.completed - migration 0020: users.stripe_customer_id, user_subscriptions.stripe_subscription_id (+idx), payments.stripe_invoice_id (+idx) Legacy one-time subs (3 live payers) are untouched: they expire at their end_date as before and board recurring on their next manual checkout. Promote checklist: add invoice.paid / invoice.payment_failed / customer.subscription.deleted to the prod webhook endpoint; verify Stripe "Customer emails" upcoming-renewal reminders stay OFF. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
18 lines
1.1 KiB
SQL
18 lines
1.1 KiB
SQL
-- Stripe recurring billing (Checkout mode:"subscription") bağlantı kolonları.
|
||
-- users.stripe_customer_id → sonraki checkout'lar aynı Stripe Customer'da
|
||
-- toplanır (kayıtlı kart + fatura geçmişi tek kayıtta).
|
||
-- user_subscriptions.stripe_subscription_id → yenileme faturaları
|
||
-- (invoice.paid/payment_failed webhook'ları) bizim abonelik satırına bu
|
||
-- kolondan çözülür; trial ve eski tek-seferlik satırlarda NULL kalır.
|
||
-- payments.stripe_invoice_id → invoice.paid webhook retry'larında mükerrer
|
||
-- gelir kaydını önleyen dedupe anahtarı.
|
||
ALTER TABLE "users" ADD COLUMN "stripe_customer_id" text;
|
||
--> statement-breakpoint
|
||
ALTER TABLE "user_subscriptions" ADD COLUMN "stripe_subscription_id" text;
|
||
--> statement-breakpoint
|
||
ALTER TABLE "payments" ADD COLUMN "stripe_invoice_id" text;
|
||
--> statement-breakpoint
|
||
CREATE INDEX "user_subscriptions_stripe_sub_idx" ON "user_subscriptions" USING btree ("stripe_subscription_id");
|
||
--> statement-breakpoint
|
||
CREATE INDEX "payments_stripe_invoice_id_idx" ON "payments" USING btree ("stripe_invoice_id");
|