Aller au contenu

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.

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

POSThttps://api.wajub.com/payments/{id}
channelstringobligatoire
Où débiter : un identifiant de canal renvoyé par GET /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.phonestringfacultatif
Le numéro Mobile Money à débiter. Le format international est le plus sûr ; un numéro local est lu dans le pays du canal.
data.operatorstringfacultatif
Avec cm.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.ibanstringfacultatif
Pour un canal bancaire. data.account_number est accepté à la place.
data.wallet_addressstringfacultatif
Pour un canal crypto.
data.save_payment_methodbooleanfacultatif
Enregistre ce moyen de paiement sur le client, pour le lui proposer la prochaine fois. Uniquement avec l'accord du payeur. Vaut false 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 :

Réponse · 202 Accepted
{
"code": 202,
"status": "Accepted",
"message": "Confirm the transaction on your phone.",
"transaction": {
"id": "trx_test_CSUGajfv9xh0XQ5wu2lx",
"reference": "order-4172",
"amount": 5000,
"currency": "XAF",
"status": "processing",
"channel": "cm.mtn",
"sandbox": true
}
}

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.

CodeCe qu'il signifieQue faire
200Le canal a confirmé tout de suite. Le paiement est succeededVérifiez, puis livrez
202En attente du payeur, ou d'une étape suivante indiquée dans actionAffichez message, terminez l'étape indiquée dans action s'il y en a une, attendez le webhook
400Le paiement ne peut plus être débité : il a déjà réussi, expiré ou été annuléCréez un nouveau paiement si besoin
402L'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 coursLaissez le payeur réessayer, voir plus bas. Pour merchant_not_verified, terminez votre vérification
403Refusé 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_referenceNe relancez pas la même tentative
404Aucun paiement avec cet id, dans cet environnementVérifiez l'id et la clé
409Un 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éeAttendez l'issue de la tentative en cours, par webhook ou en relisant le paiement. Ne débitez à nouveau que s'il est revenu à pending
422Le 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êteCorrigez 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.

actionCe que fait le payeurCe que porte la réponse
redirectIl approuve le paiement sur la page du prestataire ou dans son applicationaction_url, l'adresse où envoyer le payeur
confirm_otpIl 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_addressIl donne une information de plus au prestatairepin_id et pin_instruction, ou la même paire au nom de phone, birthday ou address
client_sessionIl paie dans une fenêtre que le prestataire gère lui-mêmeclient_session, avec son id et, quand le prestataire a sa propre page, hosted_url
confirmIl suit la consigne donnée dans messageRien de plus en live. En sandbox, un canal crypto ajoute crypto.deposit
confirm_3dsIl s'authentifie auprès de l'émetteur de la carteRien à 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.

Réponse · 202 Accepted, avec une redirection
{
"code": 202,
"status": "Accepted",
"message": "Open Wave to confirm your payment.",
"action": "redirect",
"transaction": {
"id": "trx_CSUGajfv9xh0XQ5wu2lx",
"amount": 5000,
"currency": "XOF",
"status": "processing",
"channel": "ci.wave"
},
"action_url": "https://pay.wave.com/c/cos-1c2rs2wsg10q8"
}

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.

Confirmer le code reçu par le payeur
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.

actionCorps de POST /payments/{id}/confirm
confirm_otpotp_id, code
confirm_pinpin_id, pin
confirm_phonephone_id, phone
confirm_birthdaybirthday_id, birthday au format YYYY-MM-DD
confirm_addressaddress_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é :

ChampCe qu'il contient
card.numberUne carte de test. Le numéro décide du résultat
card.holderLe nom sur la carte. card_holder est accepté aussi
expL'expiration au format MM/AA ou MM/AAAA, une année de celle-ci à trente ans plus tard
cvcTrois 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.

Que pensez-vous de ce contenu ?