Cartes enregistrées
Proposer le paiement en un clic à vos clients qui reviennent.
La règle à retenir
La page de paiement affiche les cartes enregistrées d'un client si et seulement si la session porte un customerId au moment où elle est créée. Cette condition est évaluée à l'ouverture de la page : rien de ce qui se passe ensuite ne peut la rattraper.
Comprendre le cycle de vie
Une carte enregistrée appartient à un client (objet customer, identifiant cus_...), lui-même rattaché à votre compte marchand. Le portefeuille est cloisonné : les cartes d'un de vos clients ne sont jamais visibles d'un autre marchand Mobupay.
Deux façons d'obtenir ce client, et elles ne se valent pas :
| Vous envoyez | Le client est créé | Le portefeuille s'affiche |
|---|---|---|
| saveCustomer: true | à la fin du paiement | non, jamais |
| customerId: "cus_..." | déjà créé | oui |
saveCustomer n'est donc pas une alternative à customerId : c'est la façon d'en obtenir un, à la première commande. Un intégrateur qui s'appuie sur saveCustomer seul, commande après commande, enregistre des cartes que son client ne verra jamais, et son portefeuille se remplit de doublons de la même carte.
Déclarer votre intention, pas vos paramètres
Le paramétrage d'un acquéreur carte est complexe, et il n'y a aucune raison qu'il le soit pour vous. Vous déclarez ce que vous voulez faire ; Mobupay traduit, et assume les paramètres les plus favorables. Trois intentions couvrent tous les cas, et elles se déclarent par le champ intention à la création de la session.
one_offEncaisser une foisVotre client saisit sa carte et paie. Sa carte n'est pas conservée : il la ressaisira à la prochaine commande.
Quand la choisir : Un achat ponctuel, un visiteur de passage, ou simplement quand vous ne voulez rien conserver.
one_clickLe client repaie en un clicLa carte est conservée. Votre client repaie depuis votre application en la choisissant, et confirme chaque paiement auprès de sa banque.
Quand la choisir : Une application où le client est devant l'écran au moment de payer : commande, réservation, recharge.
unscheduledVous débitez quand vous le décidezLa carte est conservée et vous la débitez selon l'usage de votre client, sans qu'il ait à valider. Sa banque l'accepte parce qu'il vous y a autorisé en enregistrant sa carte.
Quand la choisir : Une consommation facturée après coup, un abonnement, un solde à régulariser. Votre client n'est pas devant l'écran.
POST /api/v1/payments/sessions
{
"order": { "reference": "CMD-1042", "amount": 4500, "currency": "XPF" },
"customerId": "cus_2KmBz9aQp4nT8R",
"intention": "one_click",
"redirectUrl": "https://boutique.nc/merci",
"notificationUrl": "https://boutique.nc/webhooks/mobupay"
}Le champ est facultatif, et son absence ne change rien à vos intégrations existantes : une session qui présentera un portefeuille relève du un clic, les autres du paiement unique.
Ce que l'intention change, et qui ne se voit pas
En one_click, votre client est devant l'écran : la page de paiement ouvre une authentification pour ce paiement, et sa banque peut lui demander de le confirmer. Le débit direct ne l'ouvre que si vous lui transmettez l'empreinte du navigateur de votre client (browser, étape 6). En unscheduled, il est absent : le prélèvement est présenté comme initié par vous, en s'appuyant sur l'autorisation qu'il a donnée en enregistrant sa carte. Déclarer l'une pour l'autre expose à un refus pour suspicion de fraude, et c'est la raison d'être de ce champ.
L'intention se déclare aussi depuis votre espace marchand, à la création d'un lien de paiement et à l'enregistrement d'une carte, avec les mêmes trois choix.
Récupérer le customerId (sans appel supplémentaire)
Le webhook payment.authorized que vous recevez déjà porte toujours le customerId :
{
"type": "payment.authorized",
"data": {
"paymentId": "pay_xxxxxxxx",
"customerId": "cus_2KmBz9aQp4nT8R",
"customerCreated": true,
"paymentMethodId": "pm_xxxxxxxx",
"cardBrand": "VISA",
"cardLast4": "4242"
}
}Écrivez data.customerId sur l'utilisateur concerné dans votre base, à la réception. Vous n'avez rien d'autre à appeler : la correspondance se construit toute seule au fil des commandes.
paymentMethodId vous dit en prime qu'une carte vient d'être enregistrée, et customerCreated distingue une fiche créée d'une fiche retrouvée.
Créer vos clients à l'avance (recommandé)
Plus robuste que d'attendre le premier paiement : créez la fiche au moment où l'utilisateur ouvre un compte chez vous.
curl -X POST https://api.mobupay.nc/api/v1/customers \
-H "Authorization: Bearer sk_live_VOTRE_CLE" \
-H "Content-Type: application/json" \
-d '{
"email": "client@example.com",
"externalId": "votre-identifiant-utilisateur",
"firstName": "Prénom",
"lastName": "Nom"
}'externalId est le champ important : c'est votre identifiant, celui que vous maîtrisez. Il vous permet de retrouver la fiche sans dépendre de l'adresse de courriel, qu'un client peut changer.
Si l'adresse ou l'externalId existe déjà, l'API répond 409 et ne renvoie pas la fiche. Retrouvez-la alors par une recherche :
GET /api/v1/customers?externalId=votre-identifiant-utilisateur
GET /api/v1/customers?email=client@example.comPasser le customerId à chaque session
Première commande, client encore inconnu :
{
"order": { "amount": 3500, "currency": "XPF", "reference": "CMD-1042" },
"email": "client@example.com",
"saveCustomer": true,
"allowCustomerToSavePaymentMethod": true,
"redirectUrl": "https://example.com/retour",
"notificationUrl": "https://example.com/webhook"
}Commandes suivantes, une fois que vous avez son cus_... :
{
"order": { "amount": 2800, "currency": "XPF", "reference": "CMD-1108" },
"customerId": "cus_2KmBz9aQp4nT8R",
"allowCustomerToSavePaymentMethod": true,
"redirectUrl": "https://example.com/retour",
"notificationUrl": "https://example.com/webhook"
}customerIdetsaveCustomerne s'emploient pas ensemble : quand vous connaissez le client, la fiche existe déjà.- Gardez
allowCustomerToSavePaymentMethodàtrue: c'est ce qui autorise le client à enregistrer une nouvelle carte, y compris quand il en a déjà. - La case est présentée déjà cochée. Si votre parcours exige un consentement explicite, passez
savePaymentMethodCheckedByDefaultàfalse: la case s'affiche alors vide, et seul un geste de votre client l'active. - Pour un utilisateur non connecté, gardez le premier format. Le mélange des deux régimes est prévu et sans danger.
- Un
cus_de l'environnement de test ne fonctionne pas en production, et réciproquement : chaque environnement a ses propres fiches. Un identifiant qui n'appartient pas à votre compte donne une erreur400 CUSTOMER_NOT_FOUND.
Client présent ou absent : trois chemins
Une carte enregistrée se débite par trois chemins, et ils ne sont pas interchangeables.
- Votre client est devant l'écran, sur une page Mobupay : créez une session de paiement avec son
customerId(étape 5). La page lui présente sa carte enregistrée, sans ressaisie ; elle mène l'authentification 3D Secure et demande l'exemption (étape 8). - Votre client est devant l'écran, sur votre page :
POST /api/v1/payments/directavec l'objetbrowser, l'empreinte du navigateur de votre client. Mobupay l'authentifie et demande l'exemption ; si sa banque exige un défi, vous l'affichez dans votre page (ci-dessous). - Votre client est absent (échéance, solde de fin de mois) :
POST /api/v1/payments/directavecinitiator: "merchant", sansbrowser. Le débit présente l'authentification faite à l'enregistrement de la carte, quand celle-ci a été enregistrée avec authentification.
Le débit direct avec votre client présent
Relevez l'empreinte dans la page de votre client et transmettez-la avec son adresse IP : sur un appel de serveur à serveur, l'adresse que Mobupay voit est celle de votre serveur.
{
"order": { "reference": "CMD-1042", "amount": 2500, "currency": "XPF" },
"paymentInstrument": { "customerId": "cus_...", "paymentMethodRank": 1 },
"intention": "one_click",
"browser": {
"ip": "203.0.113.7",
"acceptHeader": "text/html,application/xhtml+xml",
"userAgent": "<navigator.userAgent>",
"language": "<navigator.language>",
"colorDepth": 24,
"screenHeight": 844,
"screenWidth": 390,
"timeZoneOffset": -660,
"javaEnabled": false,
"javascriptEnabled": true
},
"returnUrl": "https://votre-site.nc/commande/1042"
}- 201 ou 402 : la banque n'a rien demandé (exemption accordée ou authentification sans défi). Le paiement est conclu, comme un débit direct ordinaire.
- 202,
status: "requires_action": afficheznextAction.urldans un cadre de votre page (au moins 390 x 600 pixels), ou redirigez-y votre client. La page Mobupay mène l'authentification et le défi, puis débite. L'adresse vaut jusqu'ànextAction.expiresAt; passé ce délai, le paiement expire (payment.expired). - À la fin, la page envoie au cadre parent le message
{ mobupayPayment: { paymentId, status } }, ou renvoie votre client versreturnUrlavecpaymentIdetstatusquand elle n'est pas dans un cadre. Ce message sert à fermer le cadre : le résultat qui fait foi est le webhook (payment.authorizedoupayment.failed) ouGET /api/v1/payments/:id. - Si l'authentification ne peut pas être menée (incident de l'acquéreur), Mobupay débite sans authentification, comme sans
browser: la banque accepte ou refuse avec le code 01131, et un paiement accepté ne bénéficie pas du transfert de responsabilité. Le blocauthenticationde la lecture du paiement le dit.
// L'origine de la page est celle de nextAction.url.
const origine = new URL(nextAction.url).origin;
window.addEventListener("message", (e) => {
if (e.origin !== origine) return;
const r = e.data && e.data.mobupayPayment;
if (!r) return;
fermerLeCadre();
// Le statut définitif vient du webhook ou de GET /payments/{r.paymentId}.
});Sans browser, un débit avec initiator: "customer" part sans aucune authentification et sans demande d'exemption. Beaucoup de banques le refusent avec le code 01131, authentification exigée.
Après un refus 01131
- Avec
browser, il n'y a rien à faire : quand l'exemption a été refusée, Mobupay relance une fois l'authentification sans exemption. - Sans
browser, la réponse 402 portenextAction.type: "checkout_session": créez une session avec lecustomerId, ou rejouez le débit avecbrowsersi votre client est toujours sur votre page. - Ne bloquez pas la carte : le 01131 est une décision de la banque, prise paiement par paiement, et la même carte peut passer sans défi le lendemain.
- Ne déclarez pas
initiator: "merchant"un paiement que votre client vient de demander pour échapper à l'authentification : c'est une déclaration inexacte, que les réseaux sanctionnent par des refus.
Vérifier
Sur une clé de test, dans cet ordre :
- Une première commande avec
email+saveCustomer, en cochant l'enregistrement de la carte. Relevez lecustomerIddu webhook. - Une seconde commande pour le même utilisateur, avec
customerId. La page doit afficher la carte enregistrée et un bouton de paiement en un clic, et non un formulaire vierge. - Une troisième en enregistrant une deuxième carte : les deux doivent apparaître, et la carte par défaut être présélectionnée.
Si l'étape 2 affiche encore un formulaire vierge, le customerId n'est pas parti dans le corps de la requête : vérifiez vos journaux d'appel avant toute autre piste.
Exemption 3D Secure gérée par Mobupay
Sur un paiement en un clic, Mobupay peut demander à la banque du porteur de ne pas l'authentifier. C'est Mobupay qui décide, paiement par paiement, dans les limites que vous avez réglées : votre intégration n'a rien à envoyer, et aucun champ de l'API ne déclenche l'exemption.
Une exemption n'est demandée que si toutes ces conditions sont réunies :
- le porteur paie lui-même avec une carte enregistrée (intention
one_click), sur la page de paiement ou par un débit direct avecbrowser(étape 6) ; - la carte a déjà été authentifiée chez vous ;
- le montant ne dépasse pas votre seuil : 100 € par défaut, 250 € au plus ;
- la carte n'a pas atteint votre nombre de paiements exemptés sur 24 heures : 3 par défaut, 5 au plus ;
- la carte est une carte CB, si vous gardez ce critère ;
- aucun remboursement après encaissement ni aucune contestation sur cette carte chez vous depuis 180 jours (une autorisation annulée avant capture ne compte pas) ;
- au plus 5 % de vos paiements remboursés sur les 90 derniers jours.
Ne sont jamais exemptés : l'enregistrement d'une carte et le premier paiement qui l'enregistre, les prélèvements faits sans le porteur (intention unscheduled), tout débit direct sans browser (étape 6), et les paiements encaissés sur le contrat d'encaissement Mobupay.
La banque garde le dernier mot : elle peut accorder l'exemption, authentifier le porteur sans défi, ou lui en présenter un. Si elle refuse l'exemption, Mobupay relance une fois l'authentification, sans exemption, sur la même carte.
Le bloc authentication de la lecture d'un paiement dit ce que la banque a répondu : exempted vaut true quand elle a accordé l'exemption, et liabilityShift dit si la responsabilité a été transférée. Ces champs valent null tant que la réponse n'est pas connue.
La précaution qui compte
N'envoyez un customerId que pour un utilisateur authentifié chez vous. Il donne accès aux cartes masquées du titulaire et permet de payer avec : c'est à vous, et à vous seul, de savoir que la personne devant l'écran est bien celle-là.