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)