Jèko

Escrow (séquestre opérationnel)

Bloquer un encaissement Partner jusqu'à libération par la clé plateforme du Service Provider

Produit Service Provider

L'escrow n'est pas un encaissement instantané. Seule la clé plateforme (clé API marchand émise par le Service Provider) peut créer un pot, lancer son encaissement, et plus tard le libérer ou le rembourser. Le marchand ne peut pas se débloquer lui-même.

Tout le cycle de vie passe par : vous créez le pot, vous lancez son encaissement avec , puis vous le libérez ou le remboursez. Le pot montre lui-même où en est l'encaissement, les montants et les frais. Ce n'est pas un séquestre réglementé : les fonds retenus n'apparaissent ni dans le solde du marchand ni dans jusqu'à la libération.

escrowId n'est plus accepté sur les paiements

et avec escrowId répondent 400 escrow_id_not_supported, et rien n'est créé. Les encaissements d'un pot ne sont plus lisibles sur ni (404). Le pot ne contient plus paymentRequestId ni paymentLinkId. Lisez le bloc collect du pot à la place.

Clés

CléEncaissement instantané/escrows
Clé plateforme (émise par le SP pour ce marchand)OuiOui
Clé Owner / Dashboard du marchandOui403
Clé entreprise du SP (/service_providers/*)Non (pas d'argent)403

La clé plateforme est scoped au marchand. Elle ne peut pas toucher les escrows d'un autre marchand (404). Vous l'obtenez via Clés et magasin dédié.

Il n'y a pas d'activation par marchand. Dès que la clé plateforme existe, est disponible pour ce marchand.

De la création à la libération

POST /partner_api/escrows                 { storeId, holdUntil? }        → pending
POST /partner_api/escrows/{id}/collect    { type, data }                 → pending, collect.status: pending
  → le client paie (redirect / in-app / soundbox / lien)
  → held (ESCROW_HELD) ; GET /transactions reste vide pour ce job
  ou l'encaissement échoue → failed (ESCROW_COLLECT_FAILED) → relancez /collect
POST /partner_api/escrows/{id}/release    → released (ESCROW_RELEASED)
  ou /refund                              → refunded (ESCROW_REFUNDED)
  ou holdUntil passe sans paiement        → expired  (ESCROW_EXPIRED)
StatutSignificationWebhook
pendingPot créé, ou encaissement en coursaucun
failedL'encaissement (demande de paiement) a échoué ou expiré. Le pot peut être relancéESCROW_COLLECT_FAILED
heldLe client a payé, les fonds sont retenusESCROW_HELD
releasedFonds crédités au marchand (manuel ou à holdUntil)ESCROW_RELEASED
refundedRemboursement au payeur lancéESCROW_REFUNDED
expiredholdUntil est passé sans paiementESCROW_EXPIRED

1. Créer le pot

Create escrow (Partner API)
curl -X POST "https://api.jeko.africa/partner_api/escrows" \
  -H "X-API-KEY: platform_key" \
  -H "X-API-KEY-ID: platform_key_id" \
  -H "Content-Type: application/json" \
  -d '{
    "storeId": "59ae202a-f583-4a15-970f-9e99bd1e0baa"
  }'

Corps : uniquement storeId et, optionnellement, holdUntil (ISO 8601 UTC).

  • holdUntil omis → 7 jours. Doit être dans le futur, au plus 30 jours.
  • holdUntil court dès la création du pot, pas dès le paiement. Il ne peut être ni prolongé ni désactivé.
  • Pot held à holdUntil → Jèko le libère (pas un remboursement au payeur).
  • Pot encore pending ou failed à holdUntil → il passe expired, et son encaissement en cours est arrêté (demande expirée, lien désactivé).

Réponse (pending) :

{
  "id": "7c2d1a90-0b1e-4c3a-9f11-2e8c4d6a1b20",
  "storeId": "59ae202a-f583-4a15-970f-9e99bd1e0baa",
  "status": "pending",
  "holdUntil": "2026-09-14T20:00:00.000Z",
  "amount": null,
  "createdAt": "2026-09-07T20:00:00.000Z",
  "collect": null,
  "release": null,
  "refund": null
}

2. Lancer l'encaissement

Collect escrow (Partner API)

type vaut payment_request ou payment_link. data reprend le corps habituel de Paiement en ligne ou de Liens de paiement, sans storeId (c'est celui du pot) :

  • payment_request : amountCents, currency, reference, paymentDetails.
  • payment_link : title, amountCents, currency. Le lien est toujours à paiement unique.
curl -X POST "https://api.jeko.africa/partner_api/escrows/7c2d1a90-0b1e-4c3a-9f11-2e8c4d6a1b20/collect" \
  -H "X-API-KEY: platform_key" \
  -H "X-API-KEY-ID: platform_key_id" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "payment_request",
    "data": {
      "amountCents": 50000,
      "currency": "XOF",
      "reference": "ORDER-1042",
      "paymentDetails": {
        "type": "redirect",
        "data": {
          "paymentMethod": "wave",
          "successUrl": "https://example.com/success",
          "errorUrl": "https://example.com/error"
        }
      }
    }
  }'

Réponse : le pot, avec le bloc collect.

{
  "id": "7c2d1a90-0b1e-4c3a-9f11-2e8c4d6a1b20",
  "status": "pending",
  "collect": {
    "type": "payment_request",
    "status": "pending",
    "errorReason": null,
    "reference": "ORDER-1042",
    "redirectUrl": "https://pay.wave.com/c/…",
    "link": null,
    "amount": { "amount": 50000, "currency": "XOF" },
    "fees": null,
    "paymentMethod": "wave",
    "counterpartIdentifier": null
  },
  "release": null,
  "refund": null
}
  • redirectUrl n'est renvoyé que dans cette réponse. Conservez-le : le renvoie à null.
  • Pour un lien, collect.link est l'URL à partager, reference et paymentMethod restent null jusqu'au paiement.
  • Tous les moyens de paiement mobile money sont acceptés : Wave, Orange, MTN, Moov, Djamo.
  • Un pot = un encaissement à la fois. /collect sur un pot pending qui a déjà un encaissement → 409 escrow_already_bound.
  • /collect sur un pot held, released, refunded, expired, ou après holdUntil → 409 escrow_not_collectable.
  • Si l'appel à l'opérateur échoue (400 third_party_payment_provider_error), le pot n'est pas consommé : vous pouvez rappeler /collect.

Suivre l'encaissement

Pollez , ou écoutez les webhooks. collect.status :

collect.statusQuand
pendingEn attente du paiement
successPayé : le pot est held
failedL'opérateur a refusé le paiement. errorReason donne sa raison
expiredLa demande a expiré sans paiement (lien : holdUntil passé)

Échec et nouvelle tentative

Une demande de paiement qui échoue ou expire fait passer le pot à failed, et ESCROW_COLLECT_FAILED part. Rappelez /collect sur le même pot : il repasse pending et le nouveau collect remplace l'ancien. Les tentatives précédentes n'apparaissent plus.

Tant que la tentative précédente peut encore être payée chez l'opérateur, /collect répond 409 escrow_not_collectable. Réessayez quand elle est terminée.

Un lien ne fait jamais passer le pot à failed : le payeur peut réessayer sur le même lien. Le pot reste pending jusqu'au paiement ou jusqu'à holdUntil.

3. Après le paiement (held)

  • : status: "held", collect.status: "success", avec ce que le payeur a payé, les frais, son moyen de paiement et son numéro.
  • : vide pour ce job. Le solde magasin () ne change pas.
  • Webhook ESCROW_HELD, pas TRANSACTION_COMPLETED. Ne créditez pas le marchand comme si le paiement était dépensable. TRANSACTION_COMPLETED arrive plus tard, à la libération.
{
  "id": "7c2d1a90-0b1e-4c3a-9f11-2e8c4d6a1b20",
  "storeId": "59ae202a-f583-4a15-970f-9e99bd1e0baa",
  "status": "held",
  "holdUntil": "2026-09-14T20:00:00.000Z",
  "amount": { "amount": 50000, "currency": "XOF" },
  "createdAt": "2026-09-07T20:00:00.000Z",
  "collect": {
    "type": "payment_request",
    "status": "success",
    "errorReason": null,
    "reference": "ORDER-1042",
    "redirectUrl": null,
    "link": null,
    "amount": { "amount": 50000, "currency": "XOF" },
    "fees": { "amount": 800, "currency": "XOF" },
    "paymentMethod": "wave",
    "counterpartIdentifier": "+2250700000000"
  },
  "release": null,
  "refund": null
}

collect.amount est ce que le payeur a payé : le montant demandé si le marchand paie les frais, montant + frais si le payeur les paie (50800 ici). amount en haut du pot reste le montant demandé.

Un abonnement events: ["TRANSACTION_COMPLETED"] ne reçoit pas les webhooks escrow. events: null (défaut) les reçoit. Lisez d'abord l'en-tête Jeko-Event. Qui reçoit l'appel est dans Événements. L'abonnement se crée avec la clé Service Provider : Webhooks marchand.

Libération, remboursement, expiration

Un pot held se termine par une libération (manuelle ou automatique) ou un remboursement. Chacun porte sur tout le pot : pas de montant partiel.

Libération

crédite le marchand. Résultat : status: "released", le bloc release, et une ligne type=escrow sur . Deux webhooks partent : ESCROW_RELEASED et TRANSACTION_COMPLETED, dont le corps est cette ligne.

"release": { "amount": { "amount": 49200, "currency": "XOF" }, "fees": { "amount": 800, "currency": "XOF" }, "autoRelease": false }

release.amount est le montant crédité au marchand, égal à celui de la ligne type=escrow.

Libération automatique à holdUntil

Si le pot est encore held à holdUntil, Jèko le libère exactement comme un release : mêmes montants, même ligne type=escrow, mêmes webhooks, avec release.autoRelease: true.

Remboursement

rembourse le payeur. Le marchand ne reçoit rien et ne paie rien. Résultat : status: "refunded", le bloc refund, et ESCROW_REFUNDED, sans TRANSACTION_COMPLETED ni ligne sur .

"refund": {
  "status": "success",
  "amount": { "amount": 49000, "currency": "XOF" },
  "fees": { "amount": 200, "currency": "XOF" },
  "paymentMethod": "wave",
  "counterpartIdentifier": "+2250700000000"
}

ESCROW_REFUNDED part quand le remboursement est lancé. Avec Wave, refund.status vaut tout de suite success. Avec les autres opérateurs, il vaut pending jusqu'à la réponse de l'opérateur, puis success ou failed. Le pot reste refunded dans les deux cas : lisez refund.status sur . counterpartIdentifier peut être null quand le numéro du payeur n'est pas connu.

Expiration

Un pot pending ou failed dont holdUntil passe devient expired, et ESCROW_EXPIRED part. Son encaissement en cours est arrêté. Si un paiement de cet encaissement est malgré tout confirmé par l'opérateur après coup, le pot passe held (ESCROW_HELD), puis il est libéré automatiquement, car holdUntil est déjà passé.

Qui reçoit combien

Exemple : un paiement Wave de 500 XOF, avec 8 XOF de frais et 2 XOF de frais de remboursement.

Frais payés par le marchandFrais payés par le payeur
Le payeur paie (collect.amount)500 XOF508 XOF
À la libération, le marchand reçoit (release.amount)492 XOF (frais 8)500 XOF (frais 8)
Au remboursement, le payeur reçoit (refund.amount)490 XOF (500 − 8 − 2)498 XOF (508 − 8 − 2)

Votre commission de Service Provider vous est versée à la libération ou au remboursement.

/escrows vs /transactions

SurfaceRôle
Cycle de vie du pot et de son encaissement, avec montants et frais (collect, release, refund)
Portefeuille opérable seulement. Rien pendant le hold. Après libération : une ligne type=escrow avec le net crédité, les frais et le moyen de paiement du payeur
Disponible opérable seulement

Erreurs fréquentes

CodeQuand
400holdUntil invalide ; escrowId envoyé à /payment_requests ou /payment_links (escrow_id_not_supported)
422data qui ne correspond pas à type
403Clé Owner / Dashboard ou clé entreprise du SP
404Magasin ou escrow introuvable, ou autre marchand
409escrow_already_bound : un encaissement est déjà en cours. escrow_not_collectable : statut qui ne permet pas d'encaisser, holdUntil passé, ou tentative précédente encore payable. payment_request_exists_with_reference : reference déjà utilisée

Avant de détacher un marchand

Libérez ou remboursez ses pots held. Le détachement révoque les clés plateforme, et plus aucune clé ne peut agir sur ces pots.

On this page