150 euros à encaisser pour une réservation d’atelier, un produit unique, un prix fixe : ce genre de besoin revient régulièrement sur des sites vitrines qui n’ont besoin ni de catalogue, ni de panier, ni de gestion de stock. Installer WooCommerce pour un unique produit à prix fixe alourdit inutilement le site : tables supplémentaires en base, plugins d’extension à maintenir, mises à jour à surveiller en continu.
Stripe Elements, intégré directement dans un template de thème, permet d’encaisser ce paiement ponctuel avec une empreinte technique bien plus légère. Ce tutoriel construit ce template pas à pas. Il ne traite pas les paiements récurrents ni la facturation automatisée : uniquement l’encaissement d’un montant fixe, une fois.
Préparer le terrain côté serveur
Avant d’afficher le moindre champ de carte bancaire, il faut créer une intention de paiement côté serveur. C’est elle qui porte le montant, la devise, et qui renvoie le client_secret nécessaire pour finaliser le paiement côté navigateur.
- Installer le SDK PHP de Stripe via
composer require stripe/stripe-php - Créer un endpoint REST WordPress dédié à la création de l’intention de paiement
- Créer le template de thème qui charge Stripe.js et affiche le formulaire
- Confirmer le paiement côté client puis vérifier le succès côté serveur via webhook
add_action( 'rest_api_init', function () {
register_rest_route( 'atelier/v1', '/creer-intention', array(
'methods' => 'POST',
'callback' => 'atelier_creer_intention_paiement',
'permission_callback' => '__return_true',
) );
} );
function atelier_creer_intention_paiement( WP_REST_Request $request ) {
\Stripe\Stripe::setApiKey( getenv( 'STRIPE_SECRET_KEY' ) );
$intent = \Stripe\PaymentIntent::create( array(
'amount' => 15000, // 150,00 € en centimes
'currency' => 'eur',
'automatic_payment_methods' => array( 'enabled' => true ),
) );
return new WP_REST_Response( array(
'clientSecret' => $intent->client_secret,
), 200 );
}
Le template de paiement
Un template dédié, par exemple template-paiement-atelier.php, ne charge Stripe.js que lorsqu’il est affiché — inutile d’alourdir chaque page du site avec cette dépendance. Le chargement conditionnel se fait via wp_enqueue_script() conditionné à is_page_template().
function atelier_charger_scripts_paiement() {
if ( is_page_template( 'template-paiement-atelier.php' ) ) {
wp_enqueue_script( 'stripe-js', 'https://js.stripe.com/v3/', array(), null, true );
wp_enqueue_script(
'atelier-paiement',
get_stylesheet_directory_uri() . '/assets/js/paiement.js',
array( 'stripe-js' ),
'1.0',
true
);
wp_localize_script( 'atelier-paiement', 'atelierPaiement', array(
'restUrl' => esc_url_raw( rest_url( 'atelier/v1/creer-intention' ) ),
'clePublique' => getenv( 'STRIPE_PUBLIC_KEY' ),
) );
}
}
add_action( 'wp_enqueue_scripts', 'atelier_charger_scripts_paiement' );
Le formulaire côté client
Le fichier paiement.js initialise Stripe Elements, monte le composant de paiement, puis confirme le paiement au clic. La confirmation renvoie l’utilisateur vers une page de retour, mais cette page ne fait qu’afficher un message d’attente — elle ne valide jamais elle-même le paiement.
const stripe = Stripe( atelierPaiement.clePublique );
let elements;
async function initialiserPaiement() {
const reponse = await fetch( atelierPaiement.restUrl, { method: 'POST' } );
const { clientSecret } = await reponse.json();
elements = stripe.elements( { clientSecret } );
const paymentElement = elements.create( 'payment' );
paymentElement.mount( '#element-paiement' );
}
document.getElementById( 'formulaire-paiement' ).addEventListener( 'submit', async ( e ) => {
e.preventDefault();
const { error } = await stripe.confirmPayment( {
elements,
confirmParams: { return_url: window.location.href.split( '?' )[0] + '?statut=en-attente' },
} );
if ( error ) {
document.getElementById( 'message-erreur' ).textContent = error.message;
}
} );
initialiserPaiement();

Ne jamais faire confiance à la page de retour
C’est le point qui piège le plus souvent les développeurs venant d’un contexte de paiement simplifié : l’URL de retour prouve seulement que le navigateur a été redirigé, pas que le paiement a réussi. La seule source de vérité reste le webhook Stripe, exactement comme pour un don ou tout autre encaissement.
Ne validez jamais une réservation ou un accès sur la seule base d’un paramètre d’URL après redirection Stripe. Le webhook signé est le seul événement qui prouve un encaissement réel.
Le webhook déclenche l’envoi de la confirmation de réservation par email, l’enregistrement en base du statut « payé », et éventuellement l’ajout d’une entrée au calendrier de l’atelier. La page de retour, elle, peut simplement afficher : « Votre paiement est en cours de confirmation, vous recevrez un email sous quelques instants. »
Comparer le coût réel des deux approches
| Critère | Template + Stripe Elements | WooCommerce |
|---|---|---|
| Tables ajoutées en base | 0 (ou 1 si journal des paiements) | Plus de 10 |
| Plugins à maintenir | 0 | 1 core + extensions |
| Temps de mise en place pour un produit unique | Une demi-journée | Une journée complète (configuration + habillage) |
| Évolutivité vers un vrai catalogue | Faible, à réécrire | Native |
Cette comparaison n’est valable que pour un besoin de paiement ponctuel à prix fixe. Dès qu’apparaît un second produit, des variantes, ou un panier multi-articles, WooCommerce redevient le choix rationnel.
Notre verdict
Pour un acompte, une inscription à un événement, ou tout paiement à montant fixe et non récurrent, un template dédié avec Stripe Elements évite l’installation d’un système e-commerce complet. La discipline à respecter est simple mais non négociable : créer l’intention de paiement côté serveur, ne jamais valider sur la seule redirection, et laisser le webhook signé être l’unique déclencheur des actions consécutives au paiement.