# Antipattern : appeler Stripe directement depuis un front headless

> Ce qu'on observe quand une clé secrète Stripe fuite côté client, pourquoi c'est un problème de sécurité et de cohérence, et comment faire transiter les paiements par WordPress.

- Auteur : WordPress Développement
- Publié le : 2023-07-11
- Mis à jour le : 2023-07-11
- Catégorie : Headless &amp; API
- URL : https://www.wpmoderne.fr/headless/antipattern-stripe-directement-front-headless/

## L’essentiel

- Une clé secrète Stripe dans le bundle JavaScript est lisible par n'importe quel visiteur en quelques secondes
- WordPress doit rester l'unique point d'entrée qui initie et confirme une transaction
- Un paiement initié côté client sans validation serveur peut être manipulé avant confirmation

Que se passe-t-il en ouvrant les outils de développement du navigateur sur certains fronts headless pressés par un délai de livraison serré ? Une recherche rapide dans le bundle JavaScript révèle parfois une chaîne commençant par `sk_live_` : la clé secrète Stripe, censée ne jamais quitter un serveur, livrée en clair à chaque visiteur du site.

Cet antipattern revient régulièrement sur les projets où l'équipe front, pressée de livrer un tunnel de paiement fonctionnel, appelle directement l'API Stripe depuis le composant React ou Vue du panier, sans passer par WordPress. Le paiement fonctionne effectivement en démonstration, ce qui masque un problème de sécurité qui ne se révèle qu'au premier incident.

## Ce qu'on observe

Le code incriminé ressemble généralement à ceci, une intégration Stripe appelée directement depuis un composant client sans aucun intermédiaire serveur :

```
// Antipattern : clé secrète appelée depuis le navigateur
const stripe = require('stripe')(process.env.NEXT_PUBLIC_STRIPE_SECRET_KEY);

async function payer(montant) {
  const paiement = await stripe.paymentIntents.create({
    amount: montant,
    currency: 'eur',
  });
  return paiement;
}
```

Le préfixe `NEXT_PUBLIC_` dans Next.js indique explicitement une variable d'environnement injectée dans le bundle client, visible par tout visiteur. Utiliser ce préfixe avec une clé secrète Stripe, plutôt qu'avec la clé publique prévue à cet effet, expose immédiatement l'intégralité du compte Stripe à quiconque inspecte le code source livré au navigateur.

## Pourquoi c'est un problème de sécurité

Une clé secrète Stripe permet de créer des remboursements, de lister l'historique complet des transactions, de modifier des clients existants et, bien sûr, de créer des paiements arbitraires. Une fois exposée, elle doit être révoquée immédiatement depuis le tableau de bord Stripe, ce qui interrompt temporairement l'intégralité du tunnel de paiement en production le temps de déployer la clé de remplacement partout où elle est utilisée.

## Pourquoi c'est aussi un problème de cohérence des données

> L'essentiel à retenir : Une clé secrète Stripe dans le bundle JavaScript est lisible par n'importe quel visiteur en quelques secondes ; WordPress doit rester l'unique point d'entrée qui initie et confirme une transaction ; Un paiement initié côté client sans validation serveur peut être manipulé avant confirmation

Au-delà du risque de sécurité, cette architecture pose un second problème, plus insidieux : si le paiement est initié et confirmé uniquement côté Stripe sans jamais transiter par un point de contrôle WordPress, rien ne garantit que la commande correspondante existe réellement dans le système de gestion de contenu. Un visiteur pourrait, en théorie, modifier les paramètres de la requête avant l'appel à Stripe (montant, devise, métadonnées), créant un paiement qui ne correspond à aucune commande cohérente côté back-office.

## Quoi faire à la place

Le paiement doit systématiquement transiter par un point d'entrée WordPress qui valide la commande avant de dialoguer avec Stripe, en utilisant uniquement la clé secrète côté serveur :

```
add_action('rest_api_init', function () {
    register_rest_route('boutique/v1', '/creer-paiement', [
        'methods' => 'POST',
        'callback' => 'creer_intention_paiement',
        'permission_callback' => '__return_true',
    ]);
});

function creer_intention_paiement(WP_REST_Request $request) {
    $commande_id = absint($request->get_param('commande_id'));
    $commande = get_post($commande_id);

    if (!$commande || $commande->post_type !== 'commande') {
        return new WP_Error('commande_invalide', 'Commande introuvable', ['status' => 404]);
    }

    $montant = (int) get_post_meta($commande_id, 'montant_centimes', true);

    $intention = \Stripe\PaymentIntent::create([
        'amount' => $montant, // montant recalculé côté serveur, jamais reçu du client
        'currency' => 'eur',
        'metadata' => ['commande_id' => $commande_id],
    ]);

    return new WP_REST_Response(['client_secret' => $intention->client_secret], 200);
}
```

Le front ne reçoit que le `client_secret` nécessaire pour finaliser le paiement via le SDK Stripe Elements, jamais la clé secrète elle-même. Le montant, en particulier, est systématiquement recalculé côté serveur à partir de la commande enregistrée, sans jamais faire confiance à une valeur transmise par le client.

## Ce que ce correctif ne couvre pas

La question de la performance de cet endpoint de création de paiement, notamment sous forte charge lors d'un lancement de produit, relève d'un travail d'optimisation distinct, non abordé dans ce correctif de sécurité.

## En résumé

Une clé secrète Stripe appelée directement depuis un front headless est un antipattern qui combine deux risques distincts : l'exposition pure et simple des identifiants de paiement, et la perte de tout contrôle serveur sur la cohérence des montants réellement facturés. WordPress doit rester l'unique point d'entrée qui décide, valide et initie chaque transaction avant de la transmettre à Stripe.
