API directe
Débitez un paiement depuis votre serveur, sur le canal choisi, sans page Wajub.
Avec l'API directe, c'est vous qui affichez chaque écran. Votre application recueille le numéro de téléphone, votre serveur débite le paiement sur le canal que vous avez choisi, et vous affichez vous-même l'attente et le résultat. Wajub envoie la demande de confirmation sur le téléphone du payeur et vous communique le résultat.
Il faut deux appels. POST /payments enregistre le paiement, exactement comme dans les autres modes,
et POST /payments/{id} le débite. Cette page traite du second.
Cet appel a sa place sur votre serveur
Envoyez-le avec votre clé privée, depuis votre backend. N'utilisez jamais une clé sk.… depuis un
navigateur : Wajub la refuse avec un 403. Si votre écran de paiement vit dans un
navigateur, utilisez plutôt les Composants intégrés.
1. Créez le paiement
Créez le paiement comme le montre le guide de démarrage des
paiements. Conservez transaction.id dans la réponse.
Vous n'utiliserez pas authorization_url, qui sert à la page hébergée. Conservez aussi
authorization_token si vous débitez un canal dont le prestataire demande un code au payeur : c'est
l'identifiant qui confirme cette étape, et il n'est renvoyé qu'à la
création.
Le paiement est pending, et personne n'a encore été débité.
2. Débitez-le
https://api.wajub.com/payments/{id}channelstringobligatoireGET /channels, comme cm.mtn ou cm.orange. Envoyez mobile, ou cm.mobile pour le Cameroun, pour laisser Wajub trouver l'opérateur à partir du numéro. Ce sont les deux seuls agrégats.data.phonestringfacultatifdata.operatorstringfacultatifcm.mobile uniquement : l'opérateur choisi par le payeur, par exemple orange. Utilisé quand le numéro seul ne permet pas à Wajub de savoir à quel opérateur il appartient, un numéro porté par exemple.data.ibanstringfacultatifdata.account_number est accepté à la place.data.wallet_addressstringfacultatifdata.save_payment_methodbooleanfacultatiffalse par défaut.Chaque champ de data est aussi accepté à la racine du corps. Envoyez un en-tête Idempotency-Key
propre à ce débit, pour qu'une nouvelle tentative après un délai dépassé ne débite jamais deux fois.
curl https://api.wajub.com/payments/trx_test_CSUGajfv9xh0XQ5wu2lx \
-H "Authorization: sk_test.xxxxxxxxxxxxxxxx…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: charge-order-4172-1" \
-d '{
"channel": "cm.mtn",
"data": { "phone": "+237670000000" }
}'La réponse
Pour le Mobile Money, la réponse est presque toujours 202 Accepted. La demande est sur le téléphone
du payeur, le paiement est passé en processing, et message est la phrase à afficher pendant
l'attente :
Le résultat arrive plus tard, sous la forme d'un webhook payment.succeeded ou payment.failed.
Avant de livrer la commande, récupérez le paiement
et lisez son status, comme dans tous les autres modes.
| Code | Ce qu'il signifie | Que faire |
|---|---|---|
200 | Le canal a confirmé tout de suite. Le paiement est succeeded | Vérifiez, puis livrez |
202 | En attente du payeur, ou d'une étape suivante indiquée dans action | Affichez message, terminez l'étape indiquée dans action s'il y en a une, attendez le webhook |
400 | Le paiement ne peut plus être débité : il a déjà réussi, expiré ou été annulé | Créez un nouveau paiement si besoin |
402 | L'opérateur ou l'émetteur de la carte a refusé. Le paiement repasse en pending. Aussi error_code: merchant_not_verified tant que la vérification de votre compte est en cours | Laissez le payeur réessayer, voir plus bas. Pour merchant_not_verified, terminez votre vérification |
403 | Refusé par une restriction sur votre compte, ou par Shield. Un blocage Shield porte error_code: shield_blocked et sa référence BLK-… dans block_reference | Ne relancez pas la même tentative |
404 | Aucun paiement avec cet id, dans cet environnement | Vérifiez l'id et la clé |
409 | Un débit est déjà en cours sur ce paiement (error_code: payment_in_progress), un appel avec la même Idempotency-Key est encore en cours, ou le paiement a été préparé et sa commande n'est pas encore validée | Attendez l'issue de la tentative en cours, par webhook ou en relisant le paiement. Ne débitez à nouveau que s'il est revenu à pending |
422 | Le canal n'est pas accepté, le numéro ne correspond pas à l'opérateur, le montant dépasse le plafond du canal, ou l'Idempotency-Key a déjà servi pour une autre requête | Corrigez la requête. Une clé réutilisée demande une nouvelle clé pour un nouveau débit |
Un 402 porte error_code, un motif stable comme insufficient_funds que vous pouvez traduire pour
le payeur, et limit_exceeded quand le paiement dépasserait l'un des plafonds de votre compte.
Quand action est renseigné
La plupart des débits Mobile Money n'attendent rien de plus de votre part : action est absent et
vous attendez le webhook. Quand le prestataire derrière le canal veut d'abord que le payeur fasse
quelque chose, la réponse reste un 202, action nomme cette étape, et la réponse porte ce dont
l'étape a besoin.
action | Ce que fait le payeur | Ce que porte la réponse |
|---|---|---|
redirect | Il approuve le paiement sur la page du prestataire ou dans son application | action_url, l'adresse où envoyer le payeur |
confirm_otp | Il saisit le code que le prestataire lui a envoyé | otp_id, et otp_masked_contact quand le prestataire dit où le code est parti |
confirm_pin, confirm_phone, confirm_birthday, confirm_address | Il donne une information de plus au prestataire | pin_id et pin_instruction, ou la même paire au nom de phone, birthday ou address |
client_session | Il paie dans une fenêtre que le prestataire gère lui-même | client_session, avec son id et, quand le prestataire a sa propre page, hosted_url |
confirm | Il suit la consigne donnée dans message | Rien de plus en live. En sandbox, un canal crypto ajoute crypto.deposit |
confirm_3ds | Il s'authentifie auprès de l'émetteur de la carte | Rien à gérer de votre côté : cette étape se passe dans la page de paiement ou les composants, une raison de plus d'y traiter les cartes |
L'étape dépend du prestataire vers lequel votre compte route le canal : lisez donc action à chaque
202, au lieu d'en supposer une par canal.
Une redirection
Wave fonctionne ainsi : le payeur approuve dans l'application Wave, pas sur une demande reçue sur son téléphone. Les canaux crypto aussi en live, où la page du prestataire affiche l'adresse et le montant à envoyer.
Envoyez le payeur vers action_url : une redirection dans un navigateur, un lien ou un bouton dans
une application. Quand il a terminé, le prestataire le ramène vers le callback du paiement, si vous
en avez défini un. Le résultat arrive toujours par webhook, et vous vérifiez toujours le paiement
avant de livrer.
Un code, ou une autre information
Avec confirm_otp, le prestataire a envoyé un code au payeur. Recueillez-le sur votre propre écran,
puis envoyez-le depuis votre serveur à POST /payments/{id}/confirm, avec
votre clé secrète, l'otp_id de la réponse et le code.
curl https://api.wajub.com/payments/trx_CSUGajfv9xh0XQ5wu2lx/confirm \
-H "Authorization: sk.xxxxxxxxxxxxxxxx…" \
-H "Idempotency-Key: confirm-order-1042" \
-H "Content-Type: application/json" \
-d '{
"otp_id": "prc_2b4l4EObTOnOa501JXNi",
"code": "482913"
}'La réponse est celle du débit. 200 quand le paiement est payé. 202 quand le prestataire demande
une étape de plus : action la nomme, avec ce qu'il lui faut, comme sur le débit. 402 quand le
prestataire a refusé ce qui a été envoyé : error_code dit pourquoi (invalid_otp, ou
max_attempts_exceeded une fois les trois essais de l'étape épuisés) et attempts_remaining compte
les essais restants. Quand il n'en reste aucun, débitez le paiement à nouveau. Un 422 avec
otp_expired signifie qu'aucune étape de ce paiement ne porte cet otp_id. Envoyez une
Idempotency-Key : un appel rejoué répond alors ce qu'a répondu le premier, sans envoyer le code deux
fois au prestataire.
Les quatre autres étapes passent par le même appel, avec leurs propres champs. Chaque champ
…_instruction de la réponse du débit est la formulation du prestataire lui-même pour ce qu'il
demande.
action | Corps de POST /payments/{id}/confirm |
|---|---|
confirm_otp | otp_id, code |
confirm_pin | pin_id, pin |
confirm_phone | phone_id, phone |
confirm_birthday | birthday_id, birthday au format YYYY-MM-DD |
confirm_address | address_id, address, city, state, zip_code |
Chaque étape permet trois essais, qu'elle soit confirmée depuis votre intégration ou depuis la page de paiement.
La fenêtre du prestataire
Avec client_session, le prestataire encaisse le paiement lui-même. Quand
client_session.hosted_url est présent, envoyez-y le payeur comme pour une redirection. À son retour,
appelez POST /payments/{id}/confirm avec client_session_id égal à client_session.id : Wajub
vérifie tout de suite le résultat auprès du prestataire, sans attendre son webhook, et répond comme le
débit, 202 tant que le prestataire n'a rien de définitif. Un prestataire qui ne propose qu'un widget
dans la page, sans hosted_url, est fait pour les Composants intégrés, qui
l'ouvrent pour vous.
Laisser Wajub trouver l'opérateur
Avec channel à mobile, ou à cm.mobile pour le Cameroun, Wajub lit l'opérateur à partir du
numéro et débite dessus : +23767… part sur cm.mtn. Ce sont les deux seuls agrégats : un autre
pays se débite sur le canal de son opérateur, comme ci.orange. Quand le numéro ne peut pas être
attribué, un numéro porté par exemple, Wajub utilise data.operator si vous l'avez envoyé avec
cm.mobile, et répond 422 sinon.
Avec un canal précis comme cm.mtn, le numéro doit appartenir à cet opérateur. Un numéro Orange
envoyé sur cm.mtn est refusé avec un 422 qui indique le bon canal, au lieu d'être débité sur le
mauvais réseau.
Certains canaux portent le nom d'un portefeuille, pas celui d'un opérateur. Wave (ci.wave,
sn.wave), Express Union (cm.eu), Djamo (ci.djamo, sn.djamo) et Capitec Pay (za.capitec) se
détiennent quel que soit l'opérateur du payeur : tout numéro y est accepté. Les autres portefeuilles
appartiennent à un opérateur et prennent un numéro de cet opérateur : ke.mpesa un numéro
Safaricom, tz.mpesa et mz.mpesa un numéro Vodacom, tg.tmoney un numéro Togocel, gh.vodafone
un numéro Telecel. Un agrégat envoie un tel numéro sur le portefeuille.
Formats de numéros de téléphone liste chaque
portefeuille et l'opérateur derrière lui.
Sur un paiement créé avec accept, un
agrégat est jugé d'après l'opérateur que donne le numéro : cm.mobile avec un numéro MTN est accepté
partout où cm.mtn l'est.
Réessayer après un refus
Un 402 laisse le paiement en pending : le même paiement peut donc être débité de nouveau, avec un
autre numéro, un autre opérateur, ou le même une fois que le payeur a rechargé son compte. Utilisez
une nouvelle Idempotency-Key pour cette nouvelle tentative. Renvoyer la première clé renvoie la
première réponse, le 402, sans rien retenter.
La même clé reste la bonne après un délai dépassé ou une erreur réseau, quand vous ne savez pas si le premier appel est passé.
Cartes
Le mode live refuse les données de carte brutes : un numéro de carte ou un code de sécurité dans le
corps reçoit un 422 avec error_code: raw_card_data_not_accepted, avant tout débit. Les cartes
se saisissent dans le Checkout hébergé, les Composants
intégrés ou un SDK mobile, qui les transmettent aux champs sécurisés du
prestataire de paiement.
La sandbox accepte channel: card avec les cartes de test publiées uniquement, pour que vous
puissiez dérouler tout le parcours. Tout autre numéro de carte y est refusé aussi. Un débit par carte
en sandbox prend quatre champs dans data, chacun refusé par un 422 à son nom s'il manque ou est
mal formé :
| Champ | Ce qu'il contient |
|---|---|
card.number | Une carte de test. Le numéro décide du résultat |
card.holder | Le nom sur la carte. card_holder est accepté aussi |
exp | L'expiration au format MM/AA ou MM/AAAA, une année de celle-ci à trente ans plus tard |
cvc | Trois ou quatre chiffres |
Les deux clés card. sont des noms qui contiennent un point, pas un objet imbriqué :
"data": {"card.number": "4242424242424242", "card.holder": "Amina Diallo", "exp": "12/30", "cvc": "123"}.
En sandbox
Les six derniers chiffres du numéro de téléphone décident du résultat, comme sur la page hébergée.
…000000 réussit ; …000001 est refusé tout de suite comme le live refuse : un 402 avec
error_code: insufficient_funds, et le paiement reste pending. Le tableau complet est dans Scénarios de
test.
La sandbox ne débite aucun prestataire : elle ne répond donc jamais redirect, confirm_otp ni
client_session, ces étapes viennent des prestataires en live. Elle répond sans action pour le
Mobile Money et les canaux bancaires, confirm_3ds pour une carte de test 3-D Secure, et confirm
avec crypto.deposit, l'adresse et le montant à envoyer, pour un canal crypto.
L'API directe est disponible sur tous les plans, en sandbox comme en live. Le découpage d'un paiement
en plusieurs versements, avec POST /payments/{id}/splits, fonctionne dans
les deux, sur les canaux Mobile Money en live : Paiements fractionnés le
décrit.
Pages associées
- Choisir son intégrationLes quatre modes comparés, et quand l'API directe est le bon choix.
- Numéros de téléphoneLes formats qu'un numéro peut prendre, et comment l'opérateur est trouvé.
- Cycle de vie d'un paiementChaque statut que traverse un paiement, et le webhook de chacun.
- Référence APIChaque champ de POST /payments/{id}.