feat: maximus-api — Stripe webhooks (license auto-generate + subscriptions) #136

Open
opened 2026-04-24 00:59:02 +00:00 by maximus · 0 comments
Owner

Contexte

Issue scindée depuis #49 (license server API). Les endpoints Stripe (génération automatique via webhook Checkout, subscriptions status/webhook) sont extraits car le compte Stripe n'est pas encore créé.

Dépend de : #49 (core licence livré) + compte Stripe configuré.

Implémenté dans le même repo maximus-api (voir #49 pour scaffolding, PG, core Ed25519).

Tâches

  • Créer le compte Stripe (TPS/TVQ via Stripe Tax, configuration Canada)
  • Configurer Stripe Checkout pour l'achat unique Base
  • Configurer Stripe Billing pour l'abonnement Premium
  • Configurer Stripe Customer Portal
  • Endpoints maximus-api :
    • POST /licenses/generate — automatisé via webhook checkout.session.completed (auth : API key interne, pas public). Génère JWT Ed25519 signé avec exp obligatoire, format SR-BASE-<JWT> ou SR-PREMIUM-<JWT>.
    • POST /subscriptions/webhook — webhooks Stripe Billing. Événements : checkout.session.completed, invoice.payment_succeeded, customer.subscription.deleted, customer.subscription.updated, charge.refunded (révocation auto).
    • GET /subscriptions/status — lecture statut pour l'app desktop (auth : JWT Logto).
  • Vérification signature Stripe webhook (Stripe-Signature header) OBLIGATOIRE (CWE-345)
  • Idempotency keys sur les webhooks (Stripe retry jusqu'à 72h)
  • Email de confirmation avec clé de licence (Stripe email ou service dédié)
  • Multi-produit : le webhook doit router vers le bon produit (simpl-resultat, plus tard simpl-liste) selon line_items ou metadata
  • Tests intégration avec Stripe test mode

Notes de sécurité

  • Vérification signature webhook = critique (empêche la forge de licences gratuites)
  • Rate limiting non nécessaire sur webhooks (Stripe-only), mais obligatoire sur /subscriptions/status
  • Secret webhook stocké en env var, rotable

Critères d'acceptation

  • Un achat Stripe Checkout test génère automatiquement une licence SR-BASE-<JWT> envoyée par email
  • Le webhook rejette les requêtes non signées (HTTP 400)
  • Le webhook est idempotent (rejouer un event ne crée pas 2 licences)
  • charge.refunded révoque la licence dans les 5 secondes
  • /subscriptions/status retourne le statut active|past_due|canceled|expired pour l'utilisateur Logto authentifié
  • TPS/TVQ collectée automatiquement via Stripe Tax sur les ventes au Canada

Références

## Contexte Issue scindée depuis #49 (license server API). Les endpoints Stripe (génération automatique via webhook Checkout, subscriptions status/webhook) sont extraits car le compte Stripe n'est pas encore créé. Dépend de : #49 (core licence livré) + compte Stripe configuré. Implémenté dans le même repo `maximus-api` (voir #49 pour scaffolding, PG, core Ed25519). ## Tâches - [ ] Créer le compte Stripe (TPS/TVQ via Stripe Tax, configuration Canada) - [ ] Configurer Stripe Checkout pour l'achat unique Base - [ ] Configurer Stripe Billing pour l'abonnement Premium - [ ] Configurer Stripe Customer Portal - [ ] Endpoints `maximus-api` : - `POST /licenses/generate` — automatisé via webhook `checkout.session.completed` (auth : API key interne, pas public). Génère JWT Ed25519 signé avec `exp` obligatoire, format `SR-BASE-<JWT>` ou `SR-PREMIUM-<JWT>`. - `POST /subscriptions/webhook` — webhooks Stripe Billing. Événements : `checkout.session.completed`, `invoice.payment_succeeded`, `customer.subscription.deleted`, `customer.subscription.updated`, `charge.refunded` (révocation auto). - `GET /subscriptions/status` — lecture statut pour l'app desktop (auth : JWT Logto). - [ ] **Vérification signature Stripe webhook** (`Stripe-Signature` header) OBLIGATOIRE (CWE-345) - [ ] Idempotency keys sur les webhooks (Stripe retry jusqu'à 72h) - [ ] Email de confirmation avec clé de licence (Stripe email ou service dédié) - [ ] Multi-produit : le webhook doit router vers le bon produit (`simpl-resultat`, plus tard `simpl-liste`) selon `line_items` ou metadata - [ ] Tests intégration avec Stripe test mode ## Notes de sécurité - Vérification signature webhook = critique (empêche la forge de licences gratuites) - Rate limiting non nécessaire sur webhooks (Stripe-only), mais obligatoire sur `/subscriptions/status` - Secret webhook stocké en env var, rotable ## Critères d'acceptation - [ ] Un achat Stripe Checkout test génère automatiquement une licence `SR-BASE-<JWT>` envoyée par email - [ ] Le webhook rejette les requêtes non signées (HTTP 400) - [ ] Le webhook est idempotent (rejouer un event ne crée pas 2 licences) - [ ] `charge.refunded` révoque la licence dans les 5 secondes - [ ] `/subscriptions/status` retourne le statut `active|past_due|canceled|expired` pour l'utilisateur Logto authentifié - [ ] TPS/TVQ collectée automatiquement via Stripe Tax sur les ventes au Canada ## Références - Parent spec : `spec-monetisation.md` — Phase 2, Issue 5 - Contrat client : voir #49 (core licence) - Stripe Tax Canada : https://docs.stripe.com/tax/supported-countries/canada
maximus added this to the spec-monetisation milestone 2026-04-24 00:59:02 +00:00
maximus added the
status:blocked
type:feature
source:human
labels 2026-04-24 00:59:02 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: maximus/Simpl-Resultat#136
No description provided.