Procédure opérationnelle de test de la facturation
Stratégie de validation manuelle et automatisée pour les abonnements, le checkout de paiement unique, les webhooks et l'application des règles.
Objectif
Cette procédure opérationnelle définit comment valider la facturation de manière sûre avant une release.
Elle combine :
- tests unitaires (logique pure)
- vérifications d'intégration scriptées (flux au niveau contrat)
- validation manuelle des webhooks Stripe en staging
Source de vérité de la configuration
La facturation utilise un modèle de sources hybride :
- Les variables d'environnement restent la source de vérité pour les secrets Stripe.
billing_productsstocke les prix produit gérés par opérateur, les Stripe price IDs gérés en interne et les métadonnées Stripe Product/Price en miroir.billing_stripe_couponsetbilling_stripe_promotion_codesstockent un miroir en lecture des remises Stripe pour les usages CMS/MCP.- La clé
billing.config.v1desystem_settingsstocke la configuration globale de facturation non secrète :- jours de période de grâce
billing_plansstocke les limites de fonctionnalités des plans :- limites d'upload et de stockage média par plan
- limites de sièges par plan
- Le fallback d'exécution est déterministe :
- un payload DB valide gagne pour les champs non secrets
- un payload DB manquant/invalide retombe sur les valeurs par défaut d'env
- les anciens payloads
billing.config.v1avec des réglages de plan embarqués sont normalisés vers la forme actuelle limitée à la période de grâce
Vérifications automatisées
Tests unitaires
Exécutez :
bun testCes tests couvrent :
- mapping du statut Stripe vers le statut produit
- comportement de la période de grâce
- calculs de sièges et éligibilité des invitations
- règles de permission de checkout par rôle et organisation active
- comportement des indicateurs de facturation (
billing-enabled,billing-enforcement-enabled) - règles du page-model de facturation d'organisation :
- résolution de l'offre active (
free,pro_subscription,business_subscription,pro_lifetime) - raisons de désactivation des CTA
- agrégation de l'activité interne
- résolution de l'offre active (
- verrouillage de l'accès au code acheté depuis authz + état de facturation payant actif
- cycle de vie des grants durables d'accès au code pour les achats en paiement unique et les changements de statut d'abonnement
- normalisation des noms d'utilisateur GitHub et configuration de provisionnement du dépôt
Vérifications d'intégration scriptées
Exécutez :
bun run testLe runner de tests exécute tous les scripts dans
src/lib/scripts/tests/test-*.ts, notamment :
test-billing-checkout-permissions.tstest-billing-webhook-contract.tstest-billing-seat-limits.tstest-billing-one-time-lifetime.tstest-billing-products-admin.tstest-billing-organization-view.ts
Miroir du catalogue Stripe
Lors de la mise à jour des prix produit via admin ou MCP, vérifiez que :
- Yayaw crée ou réutilise le Stripe Product attendu.
- Le type du Stripe Price correspond au type du produit Yayaw (
recurringpour les abonnements,one_timepour le checkout à vie). - Les prix récurrents correspondent à l'intervalle attendu (
monthouyear). - Un nouveau Stripe Price est créé lorsque le montant, la devise ou l'intervalle change, et le Price précédent est archivé pour les nouveaux checkouts.
- Le montant, la devise, le nom Product, l'état actif et l'horodatage de synchronisation en miroir sont persistés.
yayaw_stripe_discounts_syncréplique les coupons et codes promotionnels sans traiter Yayaw comme source de vérité.
Validation manuelle de replay d'événement échoué
Après avoir forcé un échec de webhook de paiement unique en staging, validez le replay :
bun run billing:replay-webhooksRésultat attendu :
- L'événement échoué est réessayé une fois.
- Le statut de l'événement passe de
failedàprocessed. - Le droit reste idempotent (aucun effet de bord dupliqué).
Validation Stripe manuelle (staging)
Prérequis
- Environnement de staging configuré avec les clés Stripe.
- App déployée et accessible.
- Stripe CLI installé et authentifié.
Démarrer le forwarding des webhooks
Utilisez Stripe CLI pour transférer les événements d'abonnement vers l'endpoint Better Auth :
stripe listen --forward-to https://<staging-domain>/api/auth/stripe/webhookSi votre endpoint Better Auth Stripe est routé sous la route auth catch-all, gardez cette cible de forwarding alignée avec votre configuration de plugin.
Transférez les événements de paiement unique vers l'endpoint de webhook personnalisé :
stripe listen --forward-to https://<staging-domain>/api/billing/stripe/webhookDéclencher les événements clés
Exécutez ces commandes (ou des actions équivalentes dans Dashboard) :
stripe trigger checkout.session.completed
stripe trigger invoice.payment_failed
stripe trigger invoice.paid
stripe trigger customer.subscription.deletedValider les résultats attendus
checkout.session.completedmet l'abonnement à l'état actif.invoice.payment_failedplace l'organisation en période de grâce.- Après le seuil de grâce, l'organisation devient restreinte pour les actions premium.
invoice.paidrestaure l'état actif.customer.subscription.deleteddéfinit l'état annulé.- La livraison dupliquée de webhook est idempotente (aucun effet de bord dupliqué).
- Le
checkout.session.completedde paiement unique sur/api/billing/stripe/webhookaccorde le droitpro_lifetime. - Le checkout de paiement unique crée aussi une ligne
billing_code_access_grantsindexée par session de checkout Stripe. - Le checkout d'abonnement crée ou rafraîchit une ligne
billing_code_access_grantsindexée par Stripe subscription ID. - Une annulation d'abonnement programmée garde le grant actif jusqu'à la fin de période, et la suppression d'abonnement révoque le grant.
- Une organisation avec le droit
pro_lifetimereste active même si le statut d'abonnement est en défaut. - Les événements de paiement unique échoués peuvent être rejoués avec
billing:replay-webhookset récupérés. - Un checkout réussi redirige vers
/dashboard/organization/code-access, où les livrables sont déverrouillés uniquement lorsquecode-access:readet l'état de facturation payant actif ou un grant durable actif passent. - Avec le provisionnement GitHub configuré, un utilisateur déverrouillé peut
soumettre un nom d'utilisateur GitHub et créer une ligne
code_access_github_accountspour le dépôt configuré.
Validation de la timeline d'activité interne
La liste d'activité de facturation d'organisation n'est volontairement pas un registre financier Stripe complet. Vérifiez que la timeline UI reflète uniquement :
- le dernier instantané d'abonnement
- les grants de droits
- les grants d'accès au code
- les enregistrements d'accès au dépôt GitHub
- les événements de webhook de paiement unique persistés et bornés à l'organisation active
Validez aussi les surfaces UX séparées :
/dashboard/organization/billingaffiche le résumé du plan actif et l'activité/dashboard/organization/planshéberge les actions de checkout/dashboard/organization/code-accessaffiche les livrables achetés après un checkout réussi
Checklist de régression
Avec billing-enabled = false :
- Les flux auth existants fonctionnent toujours.
- Les pages de réglages d'organisation et de membres fonctionnent toujours.
- La navigation ne régresse pas.
Avec billing-enabled = true et billing-enforcement-enabled = false :
- L'UI de facturation et les chemins de checkout sont disponibles.
- Aucun blocage dur pour les invitations.
Avec les deux indicateurs activés :
- Les restrictions de sièges et de statut sont appliquées côté serveur.
- Le blocage d'invitation correspond aux tests de contrat.
Dépanner les blocages esbuild / Drizzle
Si des commandes locales sont bloquées sur la génération docs ou Drizzle :
- Utilisez les commandes sûres avec timeouts :
bun run docs:generatebun run db:generatebun run db:push
- Si le timeout persiste, réinstallez les dépendances et relancez :
rm -rf node_modulesbun install
Gate de release
Avant merge :
bun run check
bunx tsc --noEmit
bun run test
bun run docs:generate
bun run docs:check-links
bun run docs:check-translations
bun run docs:llm:check
bun run build