Skip to Content
Référence APIPayment Sessions

Payment Sessions

Une Payment Session réserve un numéro Mobile Money Kadryza vers lequel votre client envoie le paiement. La confirmation est asynchrone : Kadryza constate le crédit, le rapproche de la session, puis décide. Suivez le résultat par webhook ou en lisant la session.

  • L’environnement est déduit de la clé (kadryza_test_… → session test, kadryza_live_… → session live). N’envoyez jamais merchant_id, is_test ni environment dans le corps.
  • Portées de clé requises : write_transactions pour créer ou annuler, read_transactions pour lire.

Cycle de vie

StatutSignificationCe que vous faites
AWAITING_PAYMENTEn attente du paiement du clientAfficher le numéro et le compte à rebours
SUCCESSPaiement reçu et confirméValider la commande
UNDER_REVIEWPaiement reçu mais à vérifier, ou issue inconnue après l’expiration. Jamais confirmé automatiquement.Garder la commande en attente, ne pas relancer de second paiement, contacter Kadryza
EXPIREDSession test expirée sans paiementProposer un nouveau paiement
CANCELLEDSession test annulée—

Test et live ne se comportent pas pareil

SituationSession testSession live
Crédit observé et rapprochéSUCCESSUNDER_REVIEW : aujourd’hui, un paiement réel est d’abord vérifié
Aucun paiement à l’échéanceEXPIRED (15 min après expires_at)UNDER_REVIEW : l’absence de preuve n’est pas une preuve d’échec
Paiement arrivé après expires_atUNDER_REVIEWUNDER_REVIEW
AnnulationPossible tant que AWAITING_PAYMENTImpossible (409) : le client a peut-être déjà payé
Mouvement de soldeJamaisUniquement au passage en SUCCESS

Ne créez jamais une seconde session pour la même commande tant que la première est AWAITING_PAYMENT ou UNDER_REVIEW : le client pourrait payer deux fois.

Créer une Payment Session

POST/v1/payment-sessionsX-API-Key
ChampTypeRequisDescription
referencestringOuiVotre identifiant de commande, unique par environnement
amountintegerOuiMontant en XAF, entier strictement positif
currencystringOuiXAF
operatorstringOuiAIRTEL ou MOOV
customer_phonestringLive : oui · Test : nonTéléphone du payeur, au format international ; il renforce le rapprochement
descriptionstringNonDescription courte
metadataobjectNonStockée, mais jamais renvoyée dans les réponses ni les webhooks
ttl_minutesintegerNonDurée de validité, de 1 à 60 minutes (défaut 10)
return_urlstringNonAdresse de votre site où renvoyer le client une fois le paiement réglé. URL absolue https (http://localhost accepté avec une clé de test), 2048 caractères au plus, sans identifiants. Voir Retour sur votre site
pricing_versionintegerNon1 si votre intégration sait afficher le détail des frais et le total à payer. Requis si le tarif serveur ajoute une part de frais au payeur.

Montant original, frais et total à payer

La tarification est calculée par le backend et figée à la création. Votre intégration ne fournit jamais un taux, un montant de commission ou un mode de prise en charge. amount dans la réponse et les instructions reste le total à payer. Si présent, pricing.original_amount représente le montant original de la commande, pricing.customer_fee les frais ajoutés au payeur, et pricing.merchant_net_amount le net commercial avant autres coûts.

Une réponse peut inclure pricing même quand les frais sont désactivés. Les valeurs calculées ne sont pas un solde disponible. fee_collection_status distingue cotation non comptabilisée, commission comptabilisée, exemption, statut inconnu et simulation sans argent réel. Utilisez les champs *_text pour afficher exactement les montants dépassant la précision des nombres JS.

Sans pricing_version: 1, une création ajoutant des frais au payeur est refusée avec 422 PRICING_VERSION_REQUIRED, avant toute réservation. Le mode marchand et les frais désactivés ne demandent pas cette nouvelle capacité. Une modification de tarif n’affecte jamais une session déjà créée. Ne recalculez pas ses frais localement et ne considérez pas une cotation comme un paiement confirmé.

Le checkout public anonyme reçoit une projection payeur distincte : montant original, frais client, total à payer, devise et leurs représentations exactes *_text. Il ne reçoit ni taux interne, ni commission Kadryza, ni net marchand, ni preuve de comptabilisation. Ces détails restent réservés aux réponses authentifiées du marchand et à ses webhooks.

curl -X POST https://api.kadryza.app/v1/payment-sessions \ -H "X-API-Key: $KADRYZA_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "reference": "order_2026_001", "amount": 5000, "currency": "XAF", "operator": "AIRTEL", "customer_phone": "+23566000000" }'
201 Created
{ "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "reference": "order_2026_001", "ticket": "KDRZ-8F3K2", "amount": 5000, "currency": "XAF", "operator": "AIRTEL", "status": "AWAITING_PAYMENT", "environment": "live", "assigned_collection_number": "074000001", "expires_at": "2026-06-05T12:40:00Z", "created_at": "2026-06-05T12:30:00Z", "instructions": "Envoyez 5000 XAF vers le numéro 074000001 (AIRTEL), puis conservez la référence KDRZ-8F3K2. La session expire si le paiement n'est pas reçu à temps.", "checkout_url": "https://checkout.kadryza.com/pay/payment-sessions/a1b2c3d4-e5f6-7890-abcd-ef1234567890" }
Champ de réponseDescription
assigned_collection_numberLe numéro Mobile Money que votre client doit payer
ticketRéférence lisible à communiquer au client (KDRZ- + 5 caractères)
expires_atFin de la fenêtre de paiement
completed_atPrésent une fois la session conclue
payment_proofPreuve tirée du SMS de l’opérateur, dès que Kadryza le détient : operator_reference (référence de la transaction, le TID chez Airtel), payer_name (titulaire du compte, tel qu’écrit par l’opérateur), payer_phone_masked (+235 66 •• •• 01), received_at. Voir Preuve de paiement
instructionsPhrase prête à afficher
checkout_urlPage de paiement hébergée pour cette session. Lien de commodité, pas un secret. Le domaine peut être checkout.kadryza.com ou checkout.kadryza.app : n’en figez pas un seul dans une liste blanche.

Les réponses n’exposent ni le téléphone complet du payeur (seulement sa forme masquée dans payment_proof), ni metadata, ni aucun identifiant interne.

Preuve de paiement

Quand l’opérateur confirme un transfert, la passerelle Kadryza reçoit son SMS. Kadryza en extrait payment_proof :

"payment_proof": { "operator_reference": "PP260101.1200.A00001", "payer_name": "IDRISS DEDE", "payer_phone_masked": "+235 66 •• •• 01", "received_at": "2026-10-01T18:54:10Z" }
  • operator_reference : votre client retrouve la même valeur dans son propre SMS. C’est la référence à donner à l’opérateur en cas de contestation.
  • payer_name : le titulaire du compte qui a payé, tel qu’écrit par l’opérateur. Il peut différer de votre client si un proche a payé pour lui.
  • Les champs absents ne sont pas devinés : un SMS d’un format non vérifié ne donne que ce qui est sûr.
  • Le SMS brut n’est jamais exposé.

Erreurs de création

HTTPcodeCause
400INVALID_BODY, INVALID_TTLCorps illisible, ttl_minutes hors 1–60
400INVALID_RETURN_URLreturn_url qui n’est pas une adresse https absolue valide
400INVALID_AMOUNT, INVALID_CURRENCY, INVALID_OPERATOR, REFERENCE_REQUIREDChamp invalide
400CUSTOMER_PHONE_REQUIREDSession live sans customer_phone
400PAYMENT_SESSION_AMOUNT_TOO_LOW, PAYMENT_SESSION_AMOUNT_TOO_HIGHMontant hors des bornes de votre compte
403LIVE_MODE_NOT_ALLOWEDClé live, mais le mode live n’est pas activé pour votre compte
403PAYMENT_SESSION_DAILY_LIMIT_EXCEEDEDPlafond journalier atteint
409DUPLICATE_REFERENCEreference déjà utilisée dans cet environnement
501PROVIDER_NOT_IMPLEMENTEDOpérateur sans fournisseur implémenté
503PROVIDER_RAIL_NOT_READYRéseau d’encaissement de l’opérateur indisponible en live ; voir failed_checks
503NO_COLLECTION_NUMBER_AVAILABLEAucun numéro disponible pour cet opérateur et cet environnement
503PROVIDER_UNAVAILABLEAucun fournisseur actif pour cet opérateur

Plus les erreurs communes d’authentification et de débit : Codes d’erreur.

503 Service Unavailable
{ "error": "payment provider rail is not ready for live payment sessions", "code": "PROVIDER_RAIL_NOT_READY", "operator": "AIRTEL", "environment": "live", "failed_checks": [ { "name": "gateway_connected", "ready": false, "reason": "no matching gateway connected to hub", "count": 0 } ] }

Face à PROVIDER_RAIL_NOT_READY ou NO_COLLECTION_NUMBER_AVAILABLE, expliquez au client que cet opérateur est momentanément indisponible et proposez l’autre opérateur, ou réessayez plus tard. N’affichez pas failed_checks tel quel : il sert au diagnostic.

Récupérer une Payment Session

GET/v1/payment-sessions/:idX-API-Key

Renvoie la même vue que la création. Une session d’un autre compte ou d’un autre environnement renvoie 404 NOT_FOUND.

curl https://api.kadryza.app/v1/payment-sessions/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \ -H "X-API-Key: $KADRYZA_API_KEY"

Si vous interrogez l’API en complément des webhooks : au plus une requête toutes les 5 secondes pendant AWAITING_PAYMENT, et arrêt dès que le statut change.

Retrouver une session par référence

GET/v1/payment-sessions/by-reference?reference=…X-API-Key

Le chemin de récupération après une création dont vous n’avez pas reçu la réponse (coupure réseau, timeout). reference est obligatoire (400 REFERENCE_REQUIRED) et limitée à 200 caractères (400 INVALID_REFERENCE).

curl "https://api.kadryza.app/v1/payment-sessions/by-reference?reference=order_2026_001" \ -H "X-API-Key: $KADRYZA_API_KEY"

SDK : kadryza.paymentSessions.retrieveByReference('order_2026_001').

Historique des livraisons webhook

GET/v1/payment-sessions/:id/webhook-deliveriesX-API-Key

Diagnostic des webhooks envoyés pour une session, sans URL, corps ni réponse.

200 OK
{ "deliveries": [ { "event_id": "3f1c2b9a-7d4e-5a61-9b2f-0c8d7e6f5a41", "endpoint_id": "d0fb9df9-0a9b-4c2f-84f8-e28d1a2b1e7c", "event_type": "payment_session.under_review", "status": "delivered", "attempts": 1, "last_http_status": 200, "last_attempt_at": "2026-06-05T12:33:11Z", "delivered_at": "2026-06-05T12:33:11Z", "created_at": "2026-06-05T12:33:10Z" } ] }

status : pending, retrying, delivered, failed, skipped (endpoint désactivé), obsolete (la session a changé d’état avant l’envoi) ou unknown.

SDK : kadryza.paymentSessions.listWebhookDeliveries(id).

Annuler une Payment Session

POST/v1/payment-sessions/:id/cancelTest uniquementX-API-Key

Uniquement pour une session test encore AWAITING_PAYMENT. Toute autre session répond 409 NOT_CANCELLABLE, y compris une session live : elle a peut-être déjà été payée.

curl -X POST https://api.kadryza.app/v1/payment-sessions/a1b2c3d4-e5f6-7890-abcd-ef1234567890/cancel \ -H "X-API-Key: $KADRYZA_API_KEY"

Votre propre interface de paiement

Vous pouvez garder votre interface et ne jamais afficher la page hébergée. Affichez alors :

  • amount, currency, operator ;
  • assigned_collection_number : le numéro à payer ;
  • ticket, et un compte à rebours jusqu’à expires_at.

Kadryza reste le moteur de paiement : constat du crédit, rapprochement et décision côté serveur. Rien de ce que le navigateur affiche ne fait foi.

Webhooks émis

ÉvénementQuand
payment_session.succeededLa session passe en SUCCESS
payment_session.under_reviewLa session passe en UNDER_REVIEW
payment_session.expiredUne session test passe en EXPIRED

Payload, en-têtes, signature et retries : Webhooks.