ÉconomieOperations — Compliance, Anti-abuse, Refunds

Operations — Compliance, Anti-abuse, Refunds

Tout ce qui touche au cadre légal, sécuritaire et opérationnel. Ces décisions sont rarement modifiables sans review légale.

Tout ce qui touche au cadre légal, sécuritaire et opérationnel. Ces décisions sont rarement modifiables sans review légale.


Compliance

Tax

JuridictionOutilDétail
EU VATStripe TaxActivé sur tous les checkouts. MOSS géré automatiquement
US sales taxStripe TaxActivé. Tax registrations à configurer dans Stripe Dashboard par état si on hit nexus
1099-K (US)Stripe ConnectAuto-généré pour sellers/affiliates US > $600/an
GST (UK / AU / CA)Stripe TaxInclus dans Stripe Tax automatic
Tax codes Stripetxcd_10103000SaaS digital services, applicable aux subs et credit purchases

KYC

  • Sellers marketplace : Stripe Connect Express requis avant first listing
  • Affiliates : Stripe Connect Express requis avant first payout
  • Custom plan customers : KYC manuel + contract signé
  • Bounty contributors : KYC requis pour payouts > $200

GDPR

ItemImplémentation
Data Processing Agreement (DPA)À publier sur boostecom.app/dpa
Right to accessEndpoint GET /api/account/export (export complet en JSON)
Right to deletionEndpoint DELETE /api/account — soft delete 30j puis hard delete
Cookie consentBanner avec opt-in granulaire (analytics, marketing)
Data retentionLogs 90j, audit trails 7 ans (compliance), users 12 mois après deletion
Data Processor listVercel, Neon, Stripe, Resend, Anthropic, OpenAI

CCPA (California)

  • Opt-out de la vente des données disponible (boutique on/off dans settings)
  • Pas de "sale" au sens CCPA : clarifier dans la privacy policy

Refunds policy

Subscriptions

CasPolitique
Cancellation < 7j sans aucun message envoyéRefund pro-rata possible sur demande
Cancellation après usageAucun refund, accès jusqu'à fin de cycle
Plan downgrade en milieu de cyclePas de pro-rata, prend effet next cycle
Plan upgrade en milieu de cyclePro-rata immédiat
Disabled account (suspension par BoostEcom)Refund pro-rata si pas de fraude

Credit purchases

CasPolitique
Achat erroné, < 24h, aucun usageRefund manuel possible sur demande support
Crédits non utilisés après 12 moisExpirent, pas de refund
Chargeback StripeClawback des credits, automatique sur charge.dispute.created. Pas de suspension automatique : la seule suspension du code est User.banned, que l'auth lit pour refuser la CONNEXION, et une dispute n'est pas une fraude prouvee — le chargeback le plus courant est un porteur dont la carte a servi a quelqu'un d'autre. La suspension reste une decision operateur
Crédits sur compte suspendu pour fraudeAucun refund

Marketplace purchases

CasPolitique
Item ne correspond pas à la description7j money-back, modération vérifie
Seller flagué fraudeRefund automatique tous buyers + clawback seller
Item retiré par seller après achatRefund proportionnel au temps restant si subscription

Rate limits

MCP rate limits (anti-DoS only)

SurfaceLimitImplémentation
MCP Free per orgId50 calls/min, 2k/jourUpstash Redis sliding window (push upgrade)
MCP Pro per orgId500 calls/min, 50k/jourUpstash Redis sliding window
MCP Max 5x per orgId1000 calls/min, 100k/jourUpstash Redis sliding window
MCP Max 20x per orgId5000 calls/min, illimité jourUpstash Redis sliding window
MCP Custom per orgIdIllimitén/a

Budget-based guards (remplacent les caps msg/5h v2.0)

v3.0 : pas de cap msg artificiel. La friction est uniquement le solde de credits.

GuardImplémentationEffet
Pre-stream estimateCalcul du coût max estimé avant streamText(). Refus 402 si > balanceBloque les calls run-away avant d'exécuter
Mid-stream hard capAbortSignal déclenché si running cost atteint balance pendant streamingStoppe net si l'estimate était sous-évalué
Workflow concurrentsCap par tier (1 / 5 / 20 / illimité, = nombre de stores inclus) via RedisProtection scheduler
Daily bonus idempotencyDailyBonus(orgId, day) @@unique1 bonus / Org / jour, pas de double-claim. Calibration par plan : Pro $1 / Max 5x $3 / Max 20x $5

Ces guards protègent contre le pire cas (un user qui consomme $500 d'Opus en 1h) sans imposer de friction sur l'usage normal.

Autres rate limits

SurfaceLimitImplémentation
Chat per userId60 msgs/minIn-memory + Redis fallback
Credit purchase per orgId10 attempts/heureStripe Radar + custom
Affiliate redeem per userId3 attempts/heureAnti-fraud
Account creation per IP5 accounts/jourNextAuth + IP detection

Anti-abuse — checklist consolidée

RisqueSurfaceMesureÉtat
Free user obtient des créditsChat / purchase / daily / redeemrequirePaidPlan gate✅ Implémenté
MCP DoS attack/api/mcp/[storeId]Rate-limit 100/min UpstashÀ coder Phase 1
Self-affiliate (s'auto-référer)AffiliateDétection email + IP + carteÀ coder Phase 3
Self-marketplace (acheter ses items)MarketplaceuserId !== sellerIdÀ coder Phase 4
Self-agent-runs (gonfler stats)Agent runsDétection orgId === creatorOrgIdÀ coder Phase 6
Subscription downgrade exploitSubscriptionsPas de pro-rata sur downgrade✅ Stripe natif
Crédits remboursés via disputeCreditsClawback sur charge.dispute.created, restitution si la dispute est gagnée✅ Stripe webhook (billing/0093)
Multi-account farmingSignupStripe Radar + email/IP détection✅ Partiel
Race condition double-grantCron / webhooksTransactions Prisma + idempotency tables✅ Implémenté
OAuth token leakOAuth tokensHash at rest + 1h TTL + rotation✅ Implémenté
Marketplace item pirateMarketplaceModération première publicationÀ coder Phase 4
Seller fake reviewsMarketplace1 review par buyer vérifiéÀ coder Phase 5

Stripe configuration

Required dashboard setup

  • Stripe Tax activé
  • Tax registrations configurées (US states où on hit nexus, EU VAT)
  • Webhook endpoint configuré : https://www.boostecom.app/api/webhooks/stripe
  • Webhook events souscrits : checkout.session.completed, invoice.payment_succeeded, customer.subscription.updated, customer.subscription.deleted, charge.dispute.created, charge.dispute.closed

    Cette case reste a cocher cote tableau de bord Stripe : le code les traite desormais tous, mais un evenement non souscrit n'est jamais envoye. La ligne du tableau anti-fraude ci-dessus a affirme le contraire (✅) pendant que dispatchStripeEvent n'avait aucun case pour cette famille.

  • Stripe Radar : règles standard activées + rules customs (multi-account same card)
  • Stripe Connect : Express enabled
  • Tax codes : txcd_10103000 configuré comme défaut

Quelle version d'API parle chaque appel

Deux versions distinctes, et les confondre a coute une regression de dunning (billing/0287, constat billing-08).

SensVersion qui s'appliqueOu elle se regle
Sortant (checkout, transfers, refunds, invoices.list)Stripe.API_VERSION, embarquee dans la release du paquet stripepackage.json — bouger le paquet bouge le format de fil
Entrant (webhooks)la version configuree sur l'endpointtableau de bord Stripe, reglage independant du code

src/services/stripe/client.ts ne passe pas d'apiVersion explicite, et son header a longtemps traduit ca par « pas d'apiVersion pinned, Stripe gere la retrocompat ». C'est l'inverse : stripe-node envoie systematiquement son Stripe-Version, donc ne rien passer signifie « la version du SDK installe s'applique ». On ne la fige pas a la main parce que la seule version coherente avec les types TypeScript livres est celle du paquet ; la figer ailleurs, c'est promettre une forme de payload que les types ne decrivent plus.

Consequence operationnelle : apres un bump du paquet stripe, aligner la version de l'endpoint webhook dans le tableau de bord, sinon les deux sens divergent. C'est exactement la divergence qui a fait lire invoice.subscription a un handler pendant que Stripe envoyait parent.subscription_details — le handler de dunning voyait undefined, sautait son bloc, et une carte en echec gardait l'acces paye.

Derive, jamais recopie : src/services/stripe/pinned-api-version.test.ts lit Stripe.API_VERSION et verifie que le client ne l'ecrase pas.

Price IDs à créer

ProduitPrixTypeStripe price_id
Max monthly subscriptionVariablerecurring (metered ou tier)À créer
Max annual subscription (-20%)Variablerecurring annualÀ créer
Stores additional$9metered monthlyÀ créer
Credit purchaseVariableone_timePas de price_id (price_data inline)

Décision pricing UI : utiliser price_data inline sur Checkout pour la flexibilité du slider, plutôt que 50 price_ids fixes (un par palier).


Monitoring & Alerts

Metrics critiques

MetricThresholdAction
Margin négative sur un user< 0%Alert email équipe + audit
Daily revenue drop> -30% J/J-7Alert PagerDuty
Chargeback rate> 1%Stripe Radar tightening
AI Gateway cost spike> 2x baselineInvestigation modèles utilisés
MCP rate-limit hit massif> 100 hits/heureDDoS suspect, geo-block possible
Failed Stripe webhooks> 5/heureAlert + manual reprocess
Affiliate fraud signalPattern detectedSuspension auto + audit

Audit logs

Toute opération financière est tracée dans AuditLog via auditBilling() (voir src/services/audit-billing.ts). Events :

  • purchase.completed
  • monthly.reset
  • affiliate.redeemed
  • subscription.created / upgraded / downgraded / cancelled
  • chargeback.received / clawback.applied
  • marketplace.transaction / payout
  • bounty.paid

Rétention : 7 ans (compliance financière).


Disputes & support

Process standard

  1. User contact support@boostecom.app
  2. Ticket triaged dans 24h
  3. Si dispute pricing/refund → escalade billing team
  4. Si dispute technique → debug avec accès AuditLog
  5. Decision documentée dans le ticket
  6. Action via admin panel (refund, credit adjustment, account action)

Admin actions disponibles

  • Manual credit grant (audit logged comme manual.adjustment)
  • Manual refund Stripe (réversible 30j)
  • Account suspension (soft)
  • Account ban (hard, irréversible)
  • Marketplace item takedown
  • Affiliate suspension

Toutes ces actions requièrent role >= "admin" dans l'org boostecom-admin, audit logged.


Code de référence

DomainePath
Webhooks Stripesrc/services/webhooks.ts
Audit billingsrc/services/audit-billing.ts
Rate limit helpersrc/lib/security/rate-limit.ts
Stripe Radar configStripe Dashboard (pas dans le code)
Idempotency tablesprisma/schema.prisma — StripeEvent, MonthlyReset, DailyBonus