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
| Juridiction | Outil | Détail |
|---|---|---|
| EU VAT | Stripe Tax | Activé sur tous les checkouts. MOSS géré automatiquement |
| US sales tax | Stripe Tax | Activé. Tax registrations à configurer dans Stripe Dashboard par état si on hit nexus |
| 1099-K (US) | Stripe Connect | Auto-généré pour sellers/affiliates US > $600/an |
| GST (UK / AU / CA) | Stripe Tax | Inclus dans Stripe Tax automatic |
| Tax codes Stripe | txcd_10103000 | SaaS 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
| Item | Implémentation |
|---|---|
| Data Processing Agreement (DPA) | À publier sur boostecom.app/dpa |
| Right to access | Endpoint GET /api/account/export (export complet en JSON) |
| Right to deletion | Endpoint DELETE /api/account — soft delete 30j puis hard delete |
| Cookie consent | Banner avec opt-in granulaire (analytics, marketing) |
| Data retention | Logs 90j, audit trails 7 ans (compliance), users 12 mois après deletion |
| Data Processor list | Vercel, 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
| Cas | Politique |
|---|---|
| Cancellation < 7j sans aucun message envoyé | Refund pro-rata possible sur demande |
| Cancellation après usage | Aucun refund, accès jusqu'à fin de cycle |
| Plan downgrade en milieu de cycle | Pas de pro-rata, prend effet next cycle |
| Plan upgrade en milieu de cycle | Pro-rata immédiat |
| Disabled account (suspension par BoostEcom) | Refund pro-rata si pas de fraude |
Credit purchases
| Cas | Politique |
|---|---|
| Achat erroné, < 24h, aucun usage | Refund manuel possible sur demande support |
| Crédits non utilisés après 12 mois | Expirent, pas de refund |
| Chargeback Stripe | Clawback 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 fraude | Aucun refund |
Marketplace purchases
| Cas | Politique |
|---|---|
| Item ne correspond pas à la description | 7j money-back, modération vérifie |
| Seller flagué fraude | Refund automatique tous buyers + clawback seller |
| Item retiré par seller après achat | Refund proportionnel au temps restant si subscription |
Rate limits
MCP rate limits (anti-DoS only)
| Surface | Limit | Implémentation |
|---|---|---|
| MCP Free per orgId | 50 calls/min, 2k/jour | Upstash Redis sliding window (push upgrade) |
| MCP Pro per orgId | 500 calls/min, 50k/jour | Upstash Redis sliding window |
| MCP Max 5x per orgId | 1000 calls/min, 100k/jour | Upstash Redis sliding window |
| MCP Max 20x per orgId | 5000 calls/min, illimité jour | Upstash Redis sliding window |
| MCP Custom per orgId | Illimité | 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.
| Guard | Implémentation | Effet |
|---|---|---|
| Pre-stream estimate | Calcul du coût max estimé avant streamText(). Refus 402 si > balance | Bloque les calls run-away avant d'exécuter |
| Mid-stream hard cap | AbortSignal déclenché si running cost atteint balance pendant streaming | Stoppe net si l'estimate était sous-évalué |
| Workflow concurrents | Cap par tier (1 / 5 / 20 / illimité, = nombre de stores inclus) via Redis | Protection scheduler |
| Daily bonus idempotency | DailyBonus(orgId, day) @@unique | 1 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
| Surface | Limit | Implémentation |
|---|---|---|
| Chat per userId | 60 msgs/min | In-memory + Redis fallback |
| Credit purchase per orgId | 10 attempts/heure | Stripe Radar + custom |
| Affiliate redeem per userId | 3 attempts/heure | Anti-fraud |
| Account creation per IP | 5 accounts/jour | NextAuth + IP detection |
Anti-abuse — checklist consolidée
| Risque | Surface | Mesure | État |
|---|---|---|---|
| Free user obtient des crédits | Chat / purchase / daily / redeem | requirePaidPlan gate | ✅ Implémenté |
| MCP DoS attack | /api/mcp/[storeId] | Rate-limit 100/min Upstash | À coder Phase 1 |
| Self-affiliate (s'auto-référer) | Affiliate | Détection email + IP + carte | À coder Phase 3 |
| Self-marketplace (acheter ses items) | Marketplace | userId !== sellerId | À coder Phase 4 |
| Self-agent-runs (gonfler stats) | Agent runs | Détection orgId === creatorOrgId | À coder Phase 6 |
| Subscription downgrade exploit | Subscriptions | Pas de pro-rata sur downgrade | ✅ Stripe natif |
| Crédits remboursés via dispute | Credits | Clawback sur charge.dispute.created, restitution si la dispute est gagnée | ✅ Stripe webhook (billing/0093) |
| Multi-account farming | Signup | Stripe Radar + email/IP détection | ✅ Partiel |
| Race condition double-grant | Cron / webhooks | Transactions Prisma + idempotency tables | ✅ Implémenté |
| OAuth token leak | OAuth tokens | Hash at rest + 1h TTL + rotation | ✅ Implémenté |
| Marketplace item pirate | Marketplace | Modération première publication | À coder Phase 4 |
| Seller fake reviews | Marketplace | 1 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.closedCette 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
dispatchStripeEventn'avait aucuncasepour cette famille. - Stripe Radar : règles standard activées + rules customs (multi-account same card)
- Stripe Connect : Express enabled
- Tax codes :
txcd_10103000configuré 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).
| Sens | Version qui s'applique | Ou elle se regle |
|---|---|---|
| Sortant (checkout, transfers, refunds, invoices.list) | Stripe.API_VERSION, embarquee dans la release du paquet stripe | package.json — bouger le paquet bouge le format de fil |
| Entrant (webhooks) | la version configuree sur l'endpoint | tableau 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
| Produit | Prix | Type | Stripe price_id |
|---|---|---|---|
| Max monthly subscription | Variable | recurring (metered ou tier) | À créer |
| Max annual subscription (-20%) | Variable | recurring annual | À créer |
| Stores additional | $9 | metered monthly | À créer |
| Credit purchase | Variable | one_time | Pas 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
| Metric | Threshold | Action |
|---|---|---|
| Margin négative sur un user | < 0% | Alert email équipe + audit |
| Daily revenue drop | > -30% J/J-7 | Alert PagerDuty |
| Chargeback rate | > 1% | Stripe Radar tightening |
| AI Gateway cost spike | > 2x baseline | Investigation modèles utilisés |
| MCP rate-limit hit massif | > 100 hits/heure | DDoS suspect, geo-block possible |
| Failed Stripe webhooks | > 5/heure | Alert + manual reprocess |
| Affiliate fraud signal | Pattern detected | Suspension auto + audit |
Audit logs
Toute opération financière est tracée dans AuditLog via auditBilling() (voir src/services/audit-billing.ts). Events :
purchase.completedmonthly.resetaffiliate.redeemedsubscription.created/upgraded/downgraded/cancelledchargeback.received/clawback.appliedmarketplace.transaction/payoutbounty.paid
Rétention : 7 ans (compliance financière).
Disputes & support
Process standard
- User contact support@boostecom.app
- Ticket triaged dans 24h
- Si dispute pricing/refund → escalade billing team
- Si dispute technique → debug avec accès AuditLog
- Decision documentée dans le ticket
- 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
| Domaine | Path |
|---|---|
| Webhooks Stripe | src/services/webhooks.ts |
| Audit billing | src/services/audit-billing.ts |
| Rate limit helper | src/lib/security/rate-limit.ts |
| Stripe Radar config | Stripe Dashboard (pas dans le code) |
| Idempotency tables | prisma/schema.prisma — StripeEvent, MonthlyReset, DailyBonus |