Aller au contenu principal
Tous les articles
·4 min de lecture

Intégrer Stripe dans un SaaS français : TVA, factures, webhooks

Guide complet pour intégrer Stripe dans un SaaS B2B français — gestion de la TVA, factures conformes, webhooks sécurisés et abonnements.

stripepaymentssaasfrance

Stripe en France — ce qui change

Intégrer Stripe dans un SaaS français n'est pas juste "copier-coller la doc US". Il y a la TVA à 20%, les factures conformes à générer, et des règles spécifiques pour les abonnements B2B.

Étape 1 — Configuration Stripe

// src/lib/stripe.ts
import Stripe from "stripe";
 
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
  apiVersion: "2025-02-24.acacia",
  typescript: true,
});

Produits et prix

Créez vos plans dans le dashboard Stripe avec les bons taux de TVA :

// Exemple de création programmatique
const product = await stripe.products.create({
  name: "HeartCo Pro",
  tax_code: "txcd_10103001", // SaaS - Software as a Service
});
 
const price = await stripe.prices.create({
  product: product.id,
  unit_amount: 3400, // 34€ HT
  currency: "eur",
  recurring: { interval: "month" },
  tax_behavior: "exclusive", // TVA ajoutée en plus
});

Étape 2 — Checkout Session

// src/server/api/routers/billing.ts
export const billingRouter = createTRPCRouter({
  createCheckout: staffProcedure
    .input(z.object({ priceId: z.string() }))
    .mutation(async ({ ctx, input }) => {
      const session = await stripe.checkout.sessions.create({
        customer_email: ctx.session.user.email,
        mode: "subscription",
        line_items: [{ price: input.priceId, quantity: 1 }],
        automatic_tax: { enabled: true },
        tax_id_collection: { enabled: true },
        success_url: `${env.NEXT_PUBLIC_APP_URL}/dashboard/billing?success=true`,
        cancel_url: `${env.NEXT_PUBLIC_APP_URL}/dashboard/billing`,
        metadata: {
          organizationId: ctx.session.user.organizationId,
        },
      });
 
      return { url: session.url };
    }),
});

Points clés pour la France

  • automatic_tax: { enabled: true } — Stripe calcule la TVA automatiquement selon le pays du client
  • tax_id_collection — Permet au client de saisir son numéro de TVA intracommunautaire
  • metadata — Toujours inclure l'organizationId pour le webhook

Étape 3 — Webhooks sécurisés

Le webhook est le point critique. C'est lui qui met à jour votre base de données quand un paiement est confirmé.

// src/app/api/webhooks/stripe/route.ts
import { headers } from "next/headers";
import crypto from "crypto";
 
export async function POST(req: Request) {
  const body = await req.text();
  const headersList = await headers();
  const signature = headersList.get("stripe-signature")!;
 
  // Vérification HMAC — OBLIGATOIRE
  let event: Stripe.Event;
  try {
    event = stripe.webhooks.constructEvent(
      body,
      signature,
      process.env.STRIPE_WEBHOOK_SECRET!,
    );
  } catch {
    return new Response("Invalid signature", { status: 400 });
  }
 
  switch (event.type) {
    case "checkout.session.completed": {
      const session = event.data.object;
      const orgId = session.metadata?.organizationId;
      if (!orgId) break;
 
      await db.subscription.update({
        where: { organizationId: orgId },
        data: {
          plan: "PRO",
          stripeSubscriptionId: session.subscription as string,
          status: "ACTIVE",
        },
      });
      break;
    }
 
    case "customer.subscription.deleted": {
      // Downgrade vers FREE
      const sub = event.data.object;
      await db.subscription.updateMany({
        where: { stripeSubscriptionId: sub.id },
        data: { plan: "FREE", status: "CANCELED" },
      });
      break;
    }
  }
 
  return new Response("OK", { status: 200 });
}

Règle de sécurité critique

Ne jamais comparer les signatures avec === :

// ❌ VULNÉRABLE — timing attack
if (computedSignature === receivedSignature) { ... }
 
// ✅ SÉCURISÉ — comparaison en temps constant
crypto.timingSafeEqual(
  Buffer.from(computedSignature),
  Buffer.from(receivedSignature),
);

Stripe SDK le fait pour vous via constructEvent, mais si vous vérifiez manuellement, utilisez toujours timingSafeEqual.

Étape 4 — Factures conformes

Pour un SaaS B2B français, vos factures doivent inclure :

  • Numéro séquentiel (FACT-2026-001)
  • TVA ventilée (HT + TVA + TTC)
  • Mentions légales (SIRET, numéro TVA)
  • Date d'émission et de paiement

Stripe génère des factures automatiquement pour les abonnements. Activez Stripe Invoicing et configurez vos informations légales dans le dashboard.

Checklist Stripe France

  • automatic_tax activé sur toutes les Checkout Sessions
  • tax_id_collection activé pour les clients B2B
  • Webhook vérifié par signature HMAC
  • Informations légales dans Stripe Dashboard (SIRET, TVA)
  • Gestion du downgrade à l'expiration de l'abonnement
  • Emails de confirmation configurés (Stripe ou custom)
  • Mode test validé avant passage en production

Le paiement est la partie la plus sensible de votre SaaS. Prenez le temps de bien le configurer — vos clients (et votre comptable) vous remercieront.