Le WordPress d'aujourd'hui, décodé pour les développeurs

Headless & API

Un e-commerce headless à 200 000 références : Stripe Elements et Next.js

Intégrer Stripe Elements dans un tunnel d'achat entièrement découplé change de nature à l'échelle d'un catalogue de 200 000 références. Étapes concrètes.

Par WordPress Développement • 17 septembre 2022 • 5 min de lecture • Aucun commentaire
Un e-commerce headless à 200 000 références : Stripe Elements et Next.js

Un tunnel d’achat classique et un tunnel d’achat sur un catalogue de 200 000 références se ressemblent en apparence, mais divergent dès qu’il s’agit de valider un panier : impossible de recharger en mémoire l’intégralité du stock à chaque requête, ni de faire confiance aveuglément aux prix envoyés par le navigateur au moment du paiement. Ce guide détaille l’intégration de Stripe Elements dans ce contexte, sans entrer dans la logique de calcul des taxes, qui reste un sujet distinct côté Stripe Tax ou un service tiers dédié.

L’architecture repose sur WooCommerce comme source de vérité du catalogue et des stocks, exposé via l’API REST du plugin, et un front Next.js qui orchestre le tunnel d’achat sans jamais faire confiance aux données qu’il reçoit du navigateur pour calculer un montant à débiter.

Étape 1 : construire le panier côté client, valider côté serveur

Le panier vit dans le state du front, mais chaque montant affiché avant paiement doit être recalculé côté serveur au moment de créer le PaymentIntent. Sur un catalogue de cette taille, cette validation interroge directement l’API WooCommerce par lot d’identifiants de produit plutôt que produit par produit, pour limiter le nombre d’allers-retours réseau :

// pages/api/panier/valider.js
export default async function handler(req, res) {
  const { lignes } = req.body; // [{ id, quantite }]
  const ids = lignes.map((l) => l.id).join(',');

  const produits = await fetch(
    `${process.env.WC_API_URL}/products?include=${ids}&per;_page=100`,
    { headers: { Authorization: authWooCommerce() } }
  ).then((r) => r.json());

  const total = lignes.reduce((somme, ligne) => {
    const produit = produits.find((p) => p.id === ligne.id);
    if (!produit || produit.stock_quantity < ligne.quantite) {
      throw new Error(`Rupture de stock: ${ligne.id}`);
    }
    return somme + parseFloat(produit.price) * ligne.quantite;
  }, 0);

  res.json({ total_valide: total });
}

Étape 2 : créer le PaymentIntent au bon moment

Créer un PaymentIntent trop tôt dans le parcours — dès l’ajout au panier, par exemple — multiplie les intentions de paiement abandonnées et complique leur réconciliation. Le moment le plus fiable reste l’affichage de la page de paiement elle-même, une fois le panier validé côté serveur à l’étape précédente :

L'essentiel à retenir : Un PaymentIntent doit être créé au bon moment du parcours, pas trop tôt ; Le webhook Stripe reste la source de vérité, jamais la réponse du navigateur ; Un gros catalogue impose de paginer la validation du panier côté serveur
const paymentIntent = await stripe.paymentIntents.create({
  amount: Math.round(totalValide * 100),
  currency: 'eur',
  automatic_payment_methods: { enabled: true },
  metadata: { commande_panier_id: panierId },
});

Étape 3 : intégrer Stripe Elements côté front

import { Elements, PaymentElement, useStripe, useElements } from '@stripe/react-stripe-js';
import { loadStripe } from '@stripe/stripe-js';

const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_KEY);

function FormulairePaiement({ clientSecret }) {
  const stripe = useStripe();
  const elements = useElements();

  async function handleSubmit(e) {
    e.preventDefault();
    const { error } = await stripe.confirmPayment({
      elements,
      confirmParams: { return_url: `${window.location.origin}/commande/confirmation` },
    });
    if (error) {
      afficherErreur(error.message);
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      <PaymentElement />
      <button type="submit">Payer</button>
    </form>
  );
}

Étape 4 : ne jamais valider la commande depuis le navigateur

Le retour de confirmPayment côté navigateur ne doit jamais servir à valider la commande côté WooCommerce : un webhook Stripe (payment_intent.succeeded), reçu directement de Stripe côté serveur, reste la seule source fiable pour déclencher la création effective de la commande et la décrémentation du stock.

add_action('rest_api_init', function () {
    register_rest_route('paiement/v1', '/webhook', [
        'methods'  => 'POST',
        'callback' => 'traiter_webhook_stripe',
        'permission_callback' => '__return_true',
    ]);
});

function traiter_webhook_stripe(WP_REST_Request $request) {
    $event = verifier_signature_stripe($request);
    if ($event->type === 'payment_intent.succeeded') {
        creer_commande_woocommerce($event->data->object->metadata->commande_panier_id);
    }
    return rest_ensure_response(['received' => true]);
}

Étape 5 : surveiller les ruptures de stock de dernière minute

Sur un catalogue de 200 000 références à forte rotation, un produit peut passer en rupture entre l’affichage du panier et la confirmation du paiement. Le webhook de succès doit revérifier le stock avant de créer la commande, et déclencher un remboursement automatique via l’API Stripe si la vérification échoue — un cas rare mais qu’il faut absolument prévoir à cette échelle.

  • Ne jamais faire confiance à un prix ou une quantité envoyés depuis le navigateur.
  • Toujours revalider stock et prix juste avant la création du PaymentIntent.
  • Traiter la commande uniquement depuis le webhook Stripe signé, jamais depuis le retour du navigateur.

Sur un gros catalogue, le tunnel d’achat n’est pas seulement une question d’ergonomie : c’est d’abord une question de cohérence entre ce qui a été affiché, ce qui a été payé, et ce qui reste réellement en stock.

En résumé

L’intégration de Stripe Elements dans un front headless ne change pas fondamentalement selon la taille du catalogue, mais l’échelle impose une discipline supplémentaire : validation systématique côté serveur, création tardive du PaymentIntent, et confirmation de commande exclusivement pilotée par webhook. Le calcul des taxes applicables selon la destination reste, lui, un chantier à part entière.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Partager :

À propos de l'auteur

WordPress Développement

Développeur WordPress, passionné par Elementor, le FSE et l’automatisation par IA.

Voir tous ses articles

Dans la même veine

À lire aussi