meta {
  name: Billing
  seq: 10
}

docs {
  ## Billing — Stripe Checkout, Customer Portal, Subscriptions

  Endpoints qui pilotent la souscription d'une org : passage Free → Pro/Business
  via Stripe Checkout, gestion CB & annulation via Customer Portal, et webhook
  Stripe pour tenir l'org synchro avec l'état Stripe.

  ## Plans

  | Plan | Mensuel | Annuel | Limite factures actives | V2 |
  |---|---|---|---|---|
  | Free | 0 € | — | 5 (post-grace 3 mois) | — |
  | Pro | 19 € | 190 € | illimité | — |
  | Business | 49 € | 490 € | illimité | 5 sièges, reply-from-user |

  ## Flow d'upgrade

  1. SPA → **02 Start checkout** (auth) avec `{plan, cycle}` → reçoit l'URL Stripe
  2. SPA redirect vers l'URL → user paye sur Stripe Checkout (UI hostée)
  3. Stripe redirect vers `?checkout=success` côté SPA
  4. **En parallèle** : Stripe envoie `checkout.session.completed` au webhook
     → `applySubscriptionToOrg` set `org.plan='pro'`, `subscription_status='active'`
  5. SPA repoll **01 Get subscription** → affiche le nouveau plan

  ## Flow d'annulation

  1. SPA → **03 Open portal** (auth) → reçoit l'URL portal
  2. SPA redirect → user click "Cancel plan" + confirme
  3. Stripe pose `cancel_at` (timestamp = period_end) ou `cancel_at_period_end=true`
  4. Stripe envoie `customer.subscription.updated` au webhook
     → `org.cancel_at_period_end = true`
  5. UI affiche bandeau "ANNULÉ — accès jusqu'au DD/MM" + bouton "Réactiver"
  6. À `current_period_end`, Stripe envoie `customer.subscription.deleted`
     → `org.plan = 'free'`

  ## Flow de réactivation

  Si l'user clique "Réactiver" avant la fin de période :
  1. SPA → **04 Reactivate** (auth)
  2. Backend retrieve la sub Stripe pour savoir si `cancel_at` ou
     `cancel_at_period_end` est posé (ils sont mutuellement exclusifs côté
     Stripe API), puis clear le bon
  3. Pas de paiement immédiat — la sub continue son cycle normal

  ## Webhook idempotence

  Stripe peut re-livrer un event plusieurs fois. Notre handler est read-then-write
  (pas d'assumption "1-shot"). Les 4 events pris en charge :
    - checkout.session.completed
    - customer.subscription.created/updated  → applySubscriptionToOrg
    - customer.subscription.deleted          → bascule en Free
    - invoice.payment_failed                 → status past_due

  ## Pour tester en local

  1. `node ace stripe:setup` une fois pour créer Products + Prices test mode
  2. `stripe listen --forward-to localhost:3333/api/v1/billing/webhook`
     → copie le `whsec_...` dans `.env` (STRIPE_WEBHOOK_SECRET)
  3. Lance Bruno **02 Start checkout** → ouvre l'URL → CB test 4242 4242 4242 4242
  4. Reviens sur **01 Get subscription** → tu devrais voir `plan: "pro"`
}
