Vérification d'e-mails avant un paiement Stripe
Les adresses fausses et jetables sont un vecteur classique de transactions Stripe frauduleuses. Ce tutoriel ajoute la vérification Mailbeam avant l'appel à stripe.customers.create, ce qui bloque les adresses invalides avant la création de la moindre ressource Stripe.
Prérequis
- Un compte Stripe avec ses clés d'API
- Un projet Node.js 18+
- Une clé d'API Mailbeam (inscription gratuite)
Mise en place
npm install stripe# .env
MAILBEAM_KEY=mb_live_xxxxxxxxxxxxxxxxxxxx
STRIPE_SECRET_KEY=sk_test_xxxxxxxxxxxxxxxxxxxxL'endpoint de paiement
// server/routes/checkout.ts
import Stripe from "stripe";
import { Router } from "express";
// Pas encore de SDK : voici le wrapper de 12 lignes de /docs/quickstart.
import { verifyEmail } from "./lib/mailbeam";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
const router = Router();
router.post("/api/checkout/create-session", async (req, res) => {
const { email, priceId } = req.body;
if (!email || !priceId) {
return res.status(400).json({ error: "email and priceId are required" });
}
// Étape 1 : vérifier l'adresse avant de toucher à Stripe
try {
const { valid, score, reason } = await verifyEmail(email);
if (!valid || score < 60) {
return res.status(422).json({
error:
reason === "disposable_domain"
? "Merci d'utiliser une adresse e-mail permanente pour finaliser votre achat."
: "Merci de fournir une adresse e-mail valide.",
code: reason,
});
}
} catch {
// Échec permissif — ne bloquez pas un paiement sur une erreur Mailbeam
console.error("[Mailbeam] vérification échouée, on poursuit le paiement");
}
// Étape 2 : créer ou récupérer le Customer Stripe
let customerId: string;
const existingCustomers = await stripe.customers.list({
email,
limit: 1,
});
if (existingCustomers.data.length > 0) {
customerId = existingCustomers.data[0].id;
} else {
const customer = await stripe.customers.create({ email });
customerId = customer.id;
}
// Étape 3 : créer la Checkout Session
const session = await stripe.checkout.sessions.create({
customer: customerId,
payment_method_types: ["card"],
line_items: [{ price: priceId, quantity: 1 }],
mode: "subscription",
success_url: `${req.headers.origin}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${req.headers.origin}/pricing`,
});
res.json({ url: session.url });
});
export default router;Intégration côté front-end
// components/CheckoutButton.tsx
"use client";
import { useState } from "react";
export function CheckoutButton({ email, priceId }: { email: string; priceId: string }) {
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
async function handleCheckout() {
setLoading(true);
setError(null);
const res = await fetch("/api/checkout/create-session", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, priceId }),
});
const data = await res.json();
if (!res.ok) {
setError(data.error);
setLoading(false);
return;
}
// Redirection vers Stripe Checkout
window.location.href = data.url;
}
return (
<div>
{error && (
<p className="mb-3 text-sm text-destructive" role="alert">{error}</p>
)}
<button
onClick={handleCheckout}
disabled={loading}
className="rounded-lg bg-primary px-4 py-2 text-sm font-medium text-primary-foreground disabled:opacity-60"
>
{loading ? "Redirection…" : "S'abonner →"}
</button>
</div>
);
}Bonnes pratiques
- Vérifiez l'adresse avant
stripe.customers.create, pour éviter les clients Stripe orphelins - Échouez en mode permissif sur une erreur Mailbeam : ne bloquez pas un revenu à cause d'un souci d'API
- Cherchez un client Stripe existant par adresse, pour éviter les doublons
- Servez-vous du champ
reasonpour donner des messages d'erreur précis