Aller au contenu principal
Tous les articles
Technique12 min de lecture

Relier un paiement à sa commande : orderId, externalId, metadata et les autres identifiants

Relier un paiement à sa commande : qui crée orderId, externalId, metadata ou operationId, où chacun revient, jusqu'au remboursement et à l'export CSV.

Pour relier un paiement à sa commande, placez votre propre référence dans le champ metadata du paiement : il vous revient tel quel dans l'événement de paiement, et, pour une session, l'événement porte aussi votre orderId. L'externalId, lui, sert à ne jamais créer deux fois la même ressource ; il ne revient pas dans l'événement d'une session ou d'un lien. Ce guide range les sept identifiants d'un paiement — qui les crée, à quoi ils servent, où ils reviennent —, puis les applique à la session, au lien, à l'abonnement et à la boutique hébergée, jusqu'au remboursement et à la ligne d'export CSV.

Relier un paiement à sa commande : la table des identifiants

ChariPay est une plateforme de paiement marocaine, un PSP opéré par Chari Money, établissement de paiement agréé par Bank Al-Maghrib. Un paiement y traverse plusieurs systèmes — votre site, la page de paiement, nos webhooks, l'export comptable —, et chacun a son identifiant. Les confondre, c'est fabriquer des paiements « orphelins » : l'argent est là, la commande attend encore. Voici les sept à connaître.

IdentifiantCréé parSert àRevient où
orderIdvous, à la création d'une sessionvotre référence métier, sans contrainte d'unicitédans l'événement de paiement de la session
externalIdvous, sur un lien, une session, un abonnement ou un produitne jamais créer deux fois la même ressource ; unique par compte et par environnementdans les lectures du lien ; dans l'événement d'un abonnement ; pas dans l'événement d'une session ni d'un lien
metadatavousporter vos références libres : panier, commande, canaltel quel, sur les lectures et dans l'événement de paiement
reference (pl_…)ChariPay, à la création d'un lienidentifier le lien et lire son étatréponse de création ; GET /v1/payment-links/{reference}
sessionId (ps_…)ChariPay, à la création d'une sessionidentifier la session et lire son statutréponse de création ; GET /v1/payment-sessions/{sessionId}
operationIdChariPay, au paiementidentifier le paiement, notamment pour le rembourserGET /v1/transactions ; GET /v1/transactions/{operationId}
refundReferencevous, à la demande de remboursementrendre le remboursement rejouable sans double débitGET /v1/refunds/{reference}, qui accepte votre refundReference

Deux règles résument la table. La première : retrouvez votre commande par votre metadata, renvoyé tel quel dans l'événement ; pour une session, l'événement porte aussi votre orderId. La seconde : l'externalId d'une session ou d'un lien ne revient pas dans l'événement — il sert à l'anti-doublon, pas au rattachement ; seul l'abonnement fait exception. Ce que l'externalId protège, et comment, est expliqué dans l'idempotence des paiements.

La metadata de paiement : plate, légère, renvoyée telle quelle

Le champ metadata est un objet que vous joignez à la création d'un lien, d'une session, d'un abonnement ou d'un produit. Ses règles sont strictes :

  • plat : des chaînes, des nombres, des booléens ; ni objet imbriqué ni tableau, sinon la création échoue en 400 ;
  • léger : 4 Ko au plus ;
  • à la racine : envoyé une fois, au niveau racine de la requête, pas dans config ;
  • renvoyé tel quel : sur les lectures et dans l'événement de paiement, sans transformation.
À mettreÀ éviter
vos références internes : cartId, orderNumberle nom, le téléphone ou l'e-mail du client, qui ont leurs propres champs
le canal de vente : "source": "web" ou "source": "whatsapp"un numéro de carte, un CVV, toute donnée de carte
un identifiant de magasin ou de caisseun secret, un jeton, une clé
un indicateur métier booléen : "gift": trueun objet imbriqué ou une liste

Pourquoi ces précautions ? Parce que votre metadata revient tel quel, dans des événements qui transitent par vos journaux, vos files de messages et parfois vos outils d'analyse. N'y mettez rien que vous ne voudriez pas lire dans un journal : des identifiants opaques, que seule votre base sait relier à une personne.

Vérifiez-le vous-même : créez dans la sandbox une session porteuse d'un metadata, payez-la avec la carte de test, et lisez l'événement qui arrive sur votre endpoint. Commencer en mode test : trois champs — nom, e-mail professionnel, entreprise —, un lien d'activation reçu par e-mail pour choisir votre mot de passe, puis le portail, où vous créez vous-même votre clé de test après une ré-authentification. Le mode test est gratuit, sans limite de durée, et la vérification de votre entreprise ne conditionne que la production.

Session de paiement : orderId et metadata dans l'événement

Une session porte vos trois identifiants dès sa création :

bash
curl -X POST 'https://api-psp.charipay.ma/v1/payment-sessions' \
  -H 'X-CHARI-PAY-API-KEY: chari_sk_test_...' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: order-2026-0421-session' \
  -d '{
    "amount": 250.00,
    "orderId": "ORD-2026-0421",
    "externalId": "order-2026-0421",
    "config": {
      "customer": {
        "email": "amine.bennani@example.com",
        "firstName": "Amine",
        "lastName": "Bennani",
        "phone": "+212600000000"
      },
      "urls": {
        "accept": "https://votre-boutique.ma/paiement/succes",
        "decline": "https://votre-boutique.ma/paiement/echec",
        "notification": "https://votre-boutique.ma/webhooks/charipay"
      }
    },
    "metadata": { "cartId": "c_987", "source": "web" }
  }'

ORD-2026-0421 est votre référence de commande, order-2026-0421 votre clé anti-doublon, et cartId et source vos références libres. La réponse ne contient que cinq champs — sessionId, sessionToken, verifyKey, expiresAt, checkoutUrl — : enregistrez le sessionId en face de la commande, il vous servira à relire la session. Les champs de création sont décrits dans le module « Sessions de paiement » de la documentation.

Quand le paiement aboutit, l'événement payment.succeeded vous apporte votre metadata, tel quel, et votre orderId — ou, si vous l'avez renseignée, la valeur de config.urls.externalReference, qui en tient lieu. L'identifiant de l'événement et son type ne se lisent pas dans le corps mais dans les en-têtes Chari-Event-Id et Chari-Event-Type. Dans votre récepteur, la ligne qui touche à votre commande ressemble à ceci :

javascript
await orders.markPaid(event.metadata.cartId);   // your metadata, echoed verbatim

Elle s'insère dans un récepteur complet — vérification de la signature sur le corps brut, dédoublonnage, accusé de réception — décrit dans réussir son intégration des webhooks. Le corps de l'événement est un JSON plat dont le site ne publie pas la liste des champs : appuyez votre rattachement sur votre metadata, et cherchez l'identifiant de l'événement dans Chari-Event-Id, jamais ailleurs.

Lien de paiement : la reference et la metadata du lien

Un lien suit la même logique, avec ses propres identifiants :

bash
curl -X POST 'https://api-psp.charipay.ma/v1/payment-links' \
  -H 'X-CHARI-PAY-API-KEY: chari_sk_test_...' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: order-1042-link' \
  -d '{
    "amount": 149.90,
    "description": "Commande #1042",
    "singleUse": true,
    "externalId": "order-1042",
    "customerEmail": "amine.bennani@example.com",
    "customerPhone": "+212600000000",
    "metadata": { "cartId": "c_987" }
  }'

La réponse — 201, ou 200 si vous rejouez le même externalId — vous donne la reference du lien (pl_…) et son payUrl, le lien à partager. Enregistrez la reference en face de la commande : c'est elle qui permet de lire l'état du lien par GET /v1/payment-links/{reference}.

Quand le client paie par carte, l'événement de paiement porte le metadata du lien — ici cartId —, pas son externalId. Votre rattachement repose donc sur le metadata, ou sur la reference que vous avez gardée. Pour un lien à usage unique réglé en espèces en agence, suivez l'état du lien : dès que le réseau confirme le dépôt, le lien passe « Payé » et vous êtes notifié dans le portail ; côté serveur, l'état du lien se lit par GET /v1/payment-links/{reference}. Le parcours du client est détaillé dans encaisser en espèces en agence.

Abonnements et boutique hébergée : deux exceptions à connaître

Abonnements. Pour un abonnement, votre externalId et votre metadata reviennent dans les événements subscription.payment_succeeded et subscription.payment_failed : c'est documenté, et c'est la seule exception à la règle de l'externalId. Chaque échéance se rattache donc directement à l'abonnement de votre système, sans table de correspondance. Les appels et les événements sont détaillés dans intégrer les abonnements par l'API.

Boutique hébergée. Une commande payée par carte sur la boutique hébergée s'accompagne de deux événements : order.paid, qui signale la commande payée, et le payment.succeeded compagnon, qui porte le metadata du produit. Si votre rattachement repose sur le metadata — un identifiant d'entrepôt, une référence de catalogue —, lisez-le dans payment.succeeded, pas dans order.paid. L'outil lui-même est présenté dans boutique hébergée.

Du paiement au remboursement : retrouver l'operationId

Pour rembourser, il faut désigner le paiement d'origine. Préférez son operationId, l'identifiant du paiement côté ChariPay. Il se lit dans GET /v1/transactions — liste filtrable par status, type, method, channel, from, to et search, parcourue avec le curseur limit / cursor / hasMore — ou dans GET /v1/transactions/{operationId}, qui détaille une transaction ; GET /v1/transactions/{operationId}/timeline en retrace la chronologie. Chaque mouvement porte la référence de la vente qui l'explique : identifiez le paiement de votre commande une fois, puis conservez son operationId en face d'elle pour ne plus avoir à le chercher.

À défaut d'operationId, la demande de remboursement accepte un externalId : il désigne alors la référence métier du paiement — pour une session, la valeur de votre orderId, ou celle de config.urls.externalReference si vous l'avez renseignée —, jamais l'externalId d'idempotence que vous aviez donné à la session. Le piège est là : le même nom de champ désigne deux choses différentes selon l'appel.

Votre refundReference, enfin, se dérive de la commande — refund-ORD-2026-0421-1 —, jamais d'un tirage aléatoire : c'est elle qui rend le remboursement rejouable, avec 202 à la création et 200 au rejeu, puis refund.succeeded ou refund.failed à l'issue. La procédure complète, depuis le portail ou par l'API, est dans rembourser un client.

Jusqu'à la ligne d'export CSV

Chaque mouvement du compte porte la référence de la vente, du remboursement ou du versement qui l'explique ; le portail distingue montant payé, frais et net, et l'export CSV reprend ces références — depuis le portail ou par GET /v1/transactions/export.csv (jusqu'à 10 000 lignes ; au-delà, découpez par période avec from/to). Les horodatages sont en UTC ; le Maroc est en UTC+1 : votre outil de rapprochement doit convertir.

Trois conséquences pour votre rattachement :

  • un remboursement est un mouvement distinct, lié à la transaction d'origine, et il peut réduire le versement suivant sans ligne séparée sur le relevé bancaire ;
  • l'export par l'API exige une clé dotée de la permission `transaction:export` : sans elle, l'appel répond 403 FORBIDDEN ;
  • un paiement passé juste après minuit, heure du Maroc, figure à la veille dans l'export brut, tant que vous ne convertissez pas.

La méthode quotidienne — frais, décalage de versement, remboursements — appartient à rapprocher ses encaissements : ce guide s'arrête aux données qui la rendent possible. Les paramètres de la liste et de l'export sont dans le module « Transactions » de la documentation.

Cinq erreurs de rattachement à éviter

  1. 1Attendre l'`externalId` dans l'événement. Pour une session ou un lien, il n'y revient pas : rattachez par metadata, et par orderId pour une session.
  2. 2Imbriquer un objet dans `metadata`. La création échoue en 400 : gardez un objet plat, fait de chaînes, de nombres et de booléens.
  3. 3Réutiliser un même `orderId` pour deux commandes. L'API l'accepte, puisque l'orderId n'a pas de contrainte d'unicité ; votre rattachement, lui, devient ambigu.
  4. 4Rembourser avec l'`externalId` d'idempotence d'une session. Utilisez l'operationId, ou à défaut la référence métier du paiement : votre orderId, ou config.urls.externalReference si vous l'avez renseignée.
  5. 5Dédupliquer sur `Chari-Webhook-Id`. Dédupliquez sur Chari-Event-Id, pas sur Chari-Webhook-Id — ce dernier identifie la livraison, pas l'événement.

Les erreurs 1 et 5 ont la même racine : prendre un identifiant pour un autre — la clé anti-doublon pour la référence de commande, l'identifiant de la livraison pour celui de l'événement. La différence entre éviter un doublon et retrouver une commande est développée dans l'idempotence des paiements.

Questions fréquentes

Quelle différence entre orderId et externalId ?

L'orderId est votre référence métier pour une session : il n'a pas de contrainte d'unicité et l'événement de paiement le porte. L'externalId est une clé anti-doublon, unique par compte et par environnement : une création avec un externalId existant renvoie la ressource existante en 200. Pour une session ou un lien, l'externalId ne revient pas dans l'événement.

L'externalId revient-il dans le webhook de paiement ?

Pas pour une session ni pour un lien : retrouvez votre commande par votre metadata, renvoyé tel quel dans l'événement ; pour une session, l'événement porte aussi votre orderId. La seule exception est l'abonnement : votre externalId et votre metadata reviennent dans subscription.payment_succeeded et subscription.payment_failed, ce que le contrat de l'API documente.

Que peut-on mettre dans la metadata d'un paiement ?

Un objet plat — chaînes, nombres, booléens —, de 4 Ko au plus, sans objet imbriqué ni tableau, sous peine de 400. Mettez-y des références opaques : identifiant de panier, numéro de commande, canal de vente, magasin. N'y mettez jamais de donnée de carte ni de secret, et évitez les données personnelles, puisque le contenu revient tel quel dans les événements.

Comment retrouver l'operationId d'un paiement ?

Par GET /v1/transactions, en filtrant sur la période, le statut, le type ou le moyen de paiement, et en parcourant la liste avec le curseur ; ou par GET /v1/transactions/{operationId} pour le détail. Chaque mouvement porte la référence de la vente qui l'explique : identifiez le paiement une fois, puis gardez son operationId en face de la commande.

Comment savoir quelle commande a été payée par un lien de paiement ?

Gardez la reference du lien (pl_…) en face de la commande dès sa création. L'événement de paiement porte le metadata du lien, pas son externalId, et l'état du lien se lit à tout moment par GET /v1/payment-links/{reference}. Pour un paiement en espèces en agence, le lien passe « Payé » et vous êtes notifié dans le portail.

Où lire l'identifiant d'un événement webhook ?

Dans l'en-tête Chari-Event-Id, stable sur toutes les tentatives et sur tous vos endpoints ; le type de l'événement est dans Chari-Event-Type. Ne le cherchez pas dans le corps. Chari-Webhook-Id, lui, identifie la livraison : il ne sert pas à dédupliquer. C'est sur Chari-Event-Id que votre table de dédoublonnage doit porter sa contrainte d'unicité.

Faut-il un orderId différent pour chaque session ?

Pas forcément. L'orderId n'a pas de contrainte d'unicité : une nouvelle session créée pour la même commande, après une expiration ou une annulation, peut garder le même orderId. Son externalId, en revanche, doit changer, sinon l'API renvoie l'ancienne session ; changez aussi l'Idempotency-Key, sinon l'API répond 409 IDEMPOTENCY_CONFLICT. Deux commandes différentes ne doivent jamais partager un orderId, sous peine de rattachement ambigu.

Prochaine étape

Créez dans la sandbox une session et un lien porteurs d'un metadata, payez-les avec la carte de test, puis retrouvez chaque identifiant dans l'événement, dans GET /v1/transactions et dans l'export : Commencer en mode test. La méthode comptable qui prend le relais est dans rapprocher ses encaissements, et la procédure de remboursement dans rembourser un client.

Écrit par Équipe ChariPay.

À lire ensuite

Technique5 min de lecture

L'idempotence : ne facturez jamais deux fois

L'idempotence des paiements : comment l'en-tête Idempotency-Key et l'externalId évitent de débiter deux fois un client quand le réseau coupe.

Guide5 min de lecture

Rapprocher ses encaissements avec sa comptabilité

Rapprocher ses encaissements avec sa comptabilité : les trois écarts entre montant encaissé et versement en banque, et la méthode quotidienne qui tombe juste.

Prêt à essayer par vous-même ?

Créez votre compte et encaissez en mode test dès aujourd'hui : c'est gratuit, sans validation à attendre. Une question ? Notre équipe répond aux commerçants comme aux développeurs.

  • Gratuit, sans engagement
  • Mode test dès l'inscription
  • Aucune validation à attendre