9a850e2241
Fixes https://github.com/twentyhq/private-issues/issues/432 ## Problem When a user's invoice goes unpaid, Stripe moves their subscription to `unpaid` status, and Twenty suspends the workspace. But if the user pays that invoice while a new billing period has started, Stripe has already generated a new **draft** invoice for that period. Since the draft isn't finalized or paid, Stripe doesn't reactivate the subscription — the workspace stays suspended indefinitely. ## What was missing - No handler for the `invoice.paid` Stripe webhook event - No mechanism to finalize draft invoices that accumulated during the `unpaid` period - No way to reset the workspace deletion countdown (`suspendedAt`) when a user shows payment intent ## What was added ### 1. `StripeInvoiceService` — new Stripe SDK wrapper - `listDraftInvoices(stripeSubscriptionId)` — lists all draft invoices for a subscription - `finalizeInvoice(invoiceId)` — finalizes a draft with `auto_advance: true` so Stripe auto-charges it ### 2. `INVOICE_PAID` enum value Added to `BillingWebhookEvent` so the controller can route it. ### 3. `processInvoicePaid()` in `BillingWebhookInvoiceService` New private handler that: - Fetches all draft invoices for the subscription - Filters to only those whose `period_end` is in the past (already overdue) - Finalizes each one (with error handling per invoice to avoid blocking the webhook) - If the workspace is suspended, resets `suspendedAt` to now (buys time before deletion) ### 4. Controller routing `INVOICE_FINALIZED` and `INVOICE_PAID` are now grouped in the same switch case, both calling `processStripeEvent(data, eventType)`, which forks internally in the service. ## Expected recovery flow User pays overdue invoice → Stripe fires invoice.paid → Handler finalizes past-due draft invoices (auto_advance: true) → Stripe auto-charges them → Subscription becomes active → customer.subscription.updated fires → Existing logic unsuspends workspace → suspendedAt refreshed (resets deletion countdown while payments cascade)