Intégrer MonCash avec Next.js
Ce guide vous amène de zéro à une intégration complète : créer un paiement via Server Action, gérer le retour client et recevoir les confirmations par webhook signé.
Commencez en Sandbox
Avant d'implémenter le code ci-dessous avec votre clé live (sk_proj_…), nous recommandons de tester d'abord en mode Sandbox avec sk_test_proj_…. Le code est identique — vous ne changez que la valeur de la variable d'environnement. Lisez le Guide Sandbox avant de continuer.
Prérequis
- Node.js 18+
- Next.js 14+ avec App Router
- Un projet MonCashConnect avec une clé secrète — créer un projet
- Un webhook secret configuré dans le tableau de bord
Installation
npm install @moncashconnect/sdkLe SDK est zéro dépendance (utilise node:crypto natif) et exporte des types TypeScript complets.
Variables d'environnement
Ajoutez ces variables dans votre fichier .env.local :
MCC_SECRET_KEY=sk_proj_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
MCC_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
NEXT_PUBLIC_APP_URL=https://votresite.comMCC_SECRET_KEY avec NEXT_PUBLIC_ — cela l'exposerait au navigateur. La clé secrète ne doit jamais quitter le serveur.Server Action — créer un paiement
Créez le fichier app/actions/payment.ts :
"use server";
import { MonCashClient } from "@moncashconnect/sdk";
import { redirect } from "next/navigation";
const client = new MonCashClient(process.env.MCC_SECRET_KEY!);
export async function initiatePayment(orderId: string, amount: number) {
const payment = await client.createPayment(amount, orderId, {
returnUrl: `${process.env.NEXT_PUBLIC_APP_URL}/payment/success?ref=${orderId}`,
});
// redirect() ne retourne jamais — elle lance une exception de navigation
redirect(payment.paymentUrl);
}Appelez l'action depuis un formulaire dans votre page de paiement (app/checkout/page.tsx) :
import { initiatePayment } from "@/app/actions/payment";
export default function CheckoutPage() {
return (
<form
action={async () => {
"use server";
await initiatePayment("order_" + Date.now(), 500);
}}
>
<button type="submit">Payer 500 HTG avec MonCash</button>
</form>
);
}Route Handler webhook
Créez app/api/webhooks/moncash/route.ts. Utilisez req.text() et non req.json() — la signature est calculée sur le corps brut.
import { constructEvent, MonCashError } from "@moncashconnect/sdk";
import { NextRequest, NextResponse } from "next/server";
export async function POST(req: NextRequest) {
const rawBody = await req.text();
const signature = req.headers.get("x-mcc-signature") ?? "";
const timestamp = req.headers.get("x-mcc-timestamp") ?? "";
let event;
try {
event = constructEvent(
rawBody, signature, timestamp,
process.env.MCC_WEBHOOK_SECRET!,
);
} catch (err) {
if (err instanceof MonCashError) {
return NextResponse.json(
{ error: err.message },
{ status: err.statusCode },
);
}
return NextResponse.json({ error: "Internal error" }, { status: 500 });
}
if (event.event === "payment.completed") {
// Mettez à jour votre BDD ici
// await db.order.update({
// where: { id: event.reference },
// data: { status: "paid" },
// });
console.log("✅ Paiement confirmé :", event.reference);
}
if (event.event === "payment.failed") {
console.log("❌ Paiement échoué :", event.reference);
}
return NextResponse.json({ received: true });
}https://votresite.com/api/webhooks/moncash comme URL webhook dans votre projet MonCashConnect (onglet Webhooks).Page de retour client
Après le paiement, MonCash redirige vers votre returnUrl. Lisez le statut réel depuis l'API — ne faites pas confiance à des paramètres d'URL pour confirmer un paiement.
// app/payment/success/page.tsx
import { MonCashClient } from "@moncashconnect/sdk";
const client = new MonCashClient(process.env.MCC_SECRET_KEY!);
export default async function SuccessPage({
searchParams,
}: {
searchParams: { ref?: string };
}) {
const ref = searchParams.ref;
if (!ref) return <p>Référence manquante.</p>;
const tx = await client.getPaymentStatus(ref);
if (tx.status === "completed") {
return <p>✅ Paiement confirmé ! Montant reçu : {tx.netAmount} HTG</p>;
}
if (tx.status === "pending") {
return <p>⏳ Paiement en cours de traitement…</p>;
}
return <p>❌ Paiement échoué ou annulé.</p>;
}pending sur la page de retour est normal — MonCash peut prendre quelques secondes à confirmer. Attendez le webhook payment.completed avant de créditer définitivement la commande.Liste de contrôle avant mise en production
MCC_SECRET_KEY et MCC_WEBHOOK_SECRET sont dans les variables d'environnement du serveur (Vercel, Railway, etc.) — pas seulement en local
La route /api/webhooks/moncash est publiquement accessible (pas derrière un middleware d'auth)
Vous utilisez req.text() avant toute désérialisation dans le webhook handler
Votre handler de webhook est idempotent (peut recevoir le même événement plusieurs fois)
Vous vérifiez le statut via getPaymentStatus() côté serveur sur la page de retour — pas via des paramètres URL
L'URL webhook est configurée dans le tableau de bord MonCashConnect
Autres guides d'intégration