YYayaw

Vue d'ensemble de la facturation

Architecture de facturation et modèle d'exécution pour les abonnements et les paiements uniques.

Vue d'ensemble

La facturation Yayaw utilise une architecture hybride :

  1. Plugin Better Auth Stripe pour les abonnements récurrents.
  2. Flux de webhook Stripe personnalisé pour les paiements uniques (droit à vie).
  3. Configuration d'exécution répartie entre les valeurs d'environnement et les métadonnées de produits gérées en base de données.

Source de vérité

  • Variables d'environnement :
    • secrets Stripe
    • valeurs Stripe price_id
  • Base de données (system_settings + tables de produits de facturation) :
    • période de grâce dans system_settings
    • limites de fonctionnalités des plans dans billing_plans
    • métadonnées du catalogue produit et état d'activation
    • billing_email d'organisation et stripe_customer_id
    • droits de paiement unique, grants durables d'accès au code et événements d'idempotence des webhooks

Yayaw est la source de vérité de l'email de contact de facturation. L'email du Stripe Customer est synchronisé depuis l'email de facturation de l'organisation, avec repli sur l'email du compte de l'acteur du checkout lorsque l'organisation n'a pas d'email de facturation explicite. Les détails du Stripe Customer ne sont jamais utilisés pour modifier l'email de connexion de l'utilisateur.

Modèle produit V1 actuel

  • Produits d'abonnement :
    • pro_monthly
    • pro_yearly
    • business_monthly
    • business_yearly
  • Produit de paiement unique :
    • pro_lifetime

Règles de priorité à l'exécution

  1. Si l'organisation possède un droit pro_lifetime actif, l'état de facturation est considéré comme actif et le plan effectif est pro.
  2. Sinon, l'état de facturation suit le statut d'abonnement Stripe et les règles de période de grâce.
  3. L'application des sièges continue de s'appliquer avec la limite de sièges du plan effectif.

Modèle UX de facturation d'organisation (V2.3)

L'UX de facturation d'organisation est répartie sur trois surfaces du tableau de bord :

  1. /dashboard/organization/billing (résumé du plan actif + statut/sièges + activité)
  2. /dashboard/organization/plans (sélection du plan et actions de checkout)
  3. /dashboard/organization/code-access (livrables de code source achetés)

Ces surfaces sont pilotées par un view-model serveur dédié :

  • getOrganizationBillingPageModel(organizationId)
  • overview.activeOffer est résolu côté serveur (free, pro_subscription, business_subscription, pro_lifetime)
  • les états des CTA produit sont résolus côté serveur (enabled/disabled + reason)
  • les cartes produit incluent les miroirs de prix Stripe, les intervalles de facturation, les limites de plan, les fonctionnalités marketing et la mise en évidence de l'offre active
  • le client de checkout n'exécute que les types d'action déclarés dans le view-model (subscription_checkout, one_time_checkout, billing_portal)
  • l'action du portail de facturation Stripe reste désactivée jusqu'à ce que l'organisation ait un client Stripe enregistré, ce qui évite d'exposer une action de portail avant le premier checkout réussi
  • les réglages d'organisation exposent une carte de contact de facturation; sa sauvegarde met à jour l'email de facturation stocké et synchronise tout Stripe Customer existant
  • les requêtes de checkout transmettent la locale active à Stripe afin que Checkout et les emails de facturation de suivi puissent utiliser le bon contexte de locale

Cela évite la dérive des règles métier entre serveur et client.

Accès au code acheté

L'accès au code acheté est dérivé côté serveur à partir de l'autorisation et de l'état de facturation, avec des grants durables persistés quand Stripe confirme un achat :

  1. L'accès à la route nécessite code-access:read via le moteur RBAC existant.
  2. Le seed et la migration accordent cette ressource via les rôles d'organisation :
    • Organization Member : code-access:list,read
    • Organization Manager/Admin/Super Admin : code-access:manage
  3. Le checkout Stripe de paiement unique crée ou met à jour une ligne billing_code_access_grants indexée par session de checkout et accorde aussi le droit pro_lifetime.
  4. Les callbacks Better Auth Stripe d'abonnement créent, rafraîchissent, expirent ou révoquent le grant d'organisation indexé par Stripe subscription ID.
  5. La page se déverrouille lorsque l'utilisateur possède code-access:read et soit un état de facturation payant actif, soit un grant durable actif. La facturation en période de grâce continue de déverrouiller les plans non gratuits, tandis que les états inactifs, annulés, restreints, expirés ou révoqués gardent les livrables verrouillés.

Les grants d'accès au code sont des enregistrements d'achat à portée organisation, pas des permissions par utilisateur. L'accès utilisateur reste contrôlé par la ressource authz et les rôles de groupe de l'organisation.

L'accès au dépôt GitHub est provisionné séparément via code_access_github_accounts. Un utilisateur connecté avec code-access:read et un grant d'organisation actif peut soumettre un nom d'utilisateur GitHub depuis /dashboard/organization/code-access. Yayaw invite ensuite ce compte sur le dépôt privé configuré avec l'accès en lecture seule pull. Les identifiants GitHub App sont préférés pour le provisionnement en production ; un fallback par token existe pour les environnements locaux ou de staging.

Les réglages non secrets d'accès au dépôt GitHub sont gérés depuis /dashboard/admin/billing-settings :

  • activé/désactivé
  • nom complet du dépôt
  • GitHub App ID
  • GitHub App installation ID

Les variables d'environnement restent disponibles comme fallbacks pour ces valeurs non secrètes, mais les secrets GitHub restent toujours dans les variables d'environnement. Voir Configuration de l'environnement de déploiement pour le workflow complet de création de GitHub App et de récupération des variables d'environnement.

Le workflow de grants n'est volontairement pas encore exposé via MCP, car les grants sont dérivés d'événements de facturation plutôt que d'enregistrements gérés par opérateur. Les invitations au dépôt GitHub ont désormais un état opérationnel et devraient être exposées par des outils de plan de contrôle (control plane) lorsque les workflows retry/revoke/reconcile deviendront des actions opérateur.

Configurez les URLs de livrables avec :

  • BILLING_CODE_ACCESS_REPOSITORY_URL
  • BILLING_CODE_ACCESS_DOWNLOAD_URL
  • BILLING_CODE_ACCESS_DOCUMENTATION_URL
  • BILLING_CODE_ACCESS_SUPPORT_URL

Configurez les secrets GitHub avec :

  • BILLING_CODE_ACCESS_GITHUB_APP_PRIVATE_KEY
  • BILLING_CODE_ACCESS_GITHUB_TOKEN (fallback local/staging optionnel)

Fallbacks GitHub non secrets optionnels :

  • BILLING_CODE_ACCESS_GITHUB_REPOSITORY
  • BILLING_CODE_ACCESS_GITHUB_APP_ID
  • BILLING_CODE_ACCESS_GITHUB_APP_INSTALLATION_ID

Les redirections de succès de checkout pointent vers /dashboard/organization/code-access afin que le client arrive sur la surface d'accès après confirmation de l'achat.

Emails transactionnels de facturation

Les webhooks de facturation envoient des emails transactionnels via des modèles système gérés en base de données lorsque RESEND_API_KEY et des modèles actifs sont disponibles. Une configuration email manquante ou des modèles manquants sont journalisés et ne font pas échouer le traitement du webhook.

Slugs de modèles de facturation intégrés :

  • billing-purchase-confirmation : envoyé après un checkout d'abonnement réussi et après un checkout à vie de paiement unique réussi.
  • billing-subscription-updated : envoyé lorsqu'une annulation d'abonnement est programmée ou lorsque Stripe confirme la suppression d'un abonnement.
  • billing-invoice-available : envoyé après l'événement Stripe invoice.paid, lorsqu'une facture payée peut être liée depuis un e-mail Yayaw.

Les modèles de facturation déclarent des variables d'achat détaillées, notamment :

  • billing.productName, billing.planName, billing.productType
  • billing.amountFormatted, billing.currency, billing.billingInterval
  • billing.optionsSummary, billing.lineItemsSummary, billing.discountSummary
  • billing.status, billing.periodStart, billing.periodEnd
  • billing.checkoutSessionId, billing.stripeSubscriptionId, billing.stripePaymentIntentId
  • billing.invoiceId, billing.invoiceNumber, billing.invoiceUrl, billing.invoicePdfUrl, billing.billingReason
  • billing.manageUrl, billing.accessUrl, billing.deliverablesSummary

Les achats en paiement unique envoient d'abord à l'acteur du checkout, puis à l'email de checkout Stripe, puis au premier propriétaire/admin/manager fallback de l'organisation. Les emails d'achat d'abonnement utilisent d'abord la métadonnée userId du checkout Better Auth Stripe, puis l'email de checkout Stripe, puis le même fallback d'organisation. Les emails de facture résolvent l'organisation depuis les métadonnées de facture d'abord, les métadonnées d'abonnement ensuite, puis les lignes locales subscription/customer en dernier. Le Checkout one-time active la création de facture Stripe et stocke les métadonnées d'organisation, acteur, produit, plan et locale sur les factures générées. Si les reçus ou e-mails de facture payée natifs Stripe sont activés, les clients peuvent recevoir à la fois l'e-mail Stripe et l'e-mail Yayaw pour la même facture.

Périmètre de la timeline d'activité

La timeline expose volontairement l'activité interne de facturation, pas un registre complet des factures Stripe :

  1. Instantané d'abonnement actuel
  2. Droits d'organisation (billing_entitlements)
  3. Grants d'accès au code (billing_code_access_grants)
  4. Enregistrements d'accès au dépôt GitHub (code_access_github_accounts)
  5. Événements de webhook de paiement unique (billing_webhook_events, provider stripe-one-time)

Les éléments de webhook de paiement unique sont filtrés par organizationId à partir des métadonnées persistées de payload Stripe.

Règles de sécurité

  • Le checkout de paiement unique est bloqué lorsqu'une organisation possède déjà un abonnement actif Stripe-backed non annulé avec un Stripe subscription ID enregistré.
  • Le checkout de paiement unique crée ou réutilise le Stripe Customer de l'organisation et transmet customer à Stripe Checkout. Quand le Customer a un email valide, Checkout préremplit et verrouille le champ e-mail.
  • Le traitement des webhooks de paiement unique est idempotent via des event IDs persistés.
  • Les upserts de grants d'accès au code sont idempotents par session de checkout Stripe ou Stripe subscription ID.
  • Les demandes d'accès au dépôt GitHub sont idempotentes par organisation, utilisateur et dépôt configuré, et n'accordent que l'accès en lecture seule pull.
  • Les valeurs Stripe price_id manquantes marquent les produits comme non checkoutables au lieu de faire planter l'exécution.