# Lire le compte Source: https://docs.mercy-pay.com/api/account/get GET /api/v1/account Le marchand de la clé, l'environnement de cette clé, vos frais (taux par canal, frais fixe carte, qui les paie), les pays mobile money ouverts, vos limites et vos soldes. Lisez-le plutôt que de recopier ces valeurs dans votre configuration. > [!TIP] > > Essayez avec une clé **sandbox** (`mp_sandbox_…`) : aucun argent réel ne bouge. Voir [Test et production](/start/environments). `key_prefix` ne permet jamais de reconstituer la clé. Il sert seulement à la reconnaître dans votre tableau de bord. Le bloc `fees` donne vos taux réels, négociés ou non, et qui paie les frais. `mobile_money` liste les pays et opérateurs ouverts. Lisez ces valeurs plutôt que de les recopier dans votre configuration : elles changent quand votre contrat ou notre couverture évoluent. # Ouvrir l'espace client Source: https://docs.mercy-pay.com/api/billing-portal/create-session POST /api/v1/billing_portal/sessions Crée un accès d'une heure à l'espace client d'un de vos abonnés. Redirigez-le tout de suite vers `url`, sans la stocker. À ouvrir depuis un bouton « Gérer mon abonnement » de votre site. Voir [Espace client](/subscriptions/billing-portal) pour ce que votre abonné y fait. # Créer une session Source: https://docs.mercy-pay.com/api/checkout-sessions/create POST /api/v1/checkout_sessions Crée une page de paiement hébergée et renvoie son adresse dans `checkout_url`. Redirigez-y votre client. > [!TIP] > > Essayez avec une clé **sandbox** (`mp_sandbox_…`) : aucun argent réel ne bouge. Voir [Test et production](/start/environments). La session dure 30 minutes. Votre client choisit sur la page son moyen de paiement : carte, ou mobile money dans n'importe quel pays proposé, avec conversion automatique. Voir [Accepter un paiement](/payments/accept-a-payment) pour le parcours complet. # Expirer une session Source: https://docs.mercy-pay.com/api/checkout-sessions/expire POST /api/v1/checkout_sessions/{id}/expire Ferme une session que plus personne ne doit payer. Une session déjà payée, ou avec un paiement en cours, renvoie `409`. # Lire une session Source: https://docs.mercy-pay.com/api/checkout-sessions/get GET /api/v1/checkout_sessions/{id} Une session, avec `payment` : la dernière tentative du payeur, ou `null` tant qu'il n'a rien lancé. Le paiement porte la même référence dans son champ `checkout_session`. ## Session ouverte par un lien de paiement Une session ouverte par un [lien de paiement](/payment-links/overview) porte aussi, dans l'événement `checkout_session.completed` : | Champ | Description | | - | - | | `payment_link` | `uuid` du lien d'origine | | `payer_phone` | Téléphone saisi par le payeur, si le lien le demande | | `payer_address` | Objet `line1`, `city`, `country`, si le lien demande l'adresse | | `custom_fields` | Réponses aux champs personnalisés, par `id` de champ. Pour un fichier, son nom | # Lister les sessions Source: https://docs.mercy-pay.com/api/checkout-sessions/list GET /api/v1/checkout_sessions Vos sessions, de la plus récente à la plus ancienne. Chaque session porte `payment`, la dernière tentative de paiement du client. Voir [Pagination](/developers/pagination). # Créer un client Source: https://docs.mercy-pay.com/api/customers/create POST /api/v1/customers Crée un client avant son premier abonnement. Un e-mail déjà utilisé renvoie `409` avec l'id du client existant. # Supprimer un client Source: https://docs.mercy-pay.com/api/customers/delete DELETE /api/v1/customers/{id} Supprime le client et ses moyens enregistrés. Un client avec un abonnement en cours renvoie `409` : résiliez d'abord l'abonnement. # Lire un client Source: https://docs.mercy-pay.com/api/customers/get GET /api/v1/customers/{id} Le client avec ses moyens de paiement. # Lister les clients Source: https://docs.mercy-pay.com/api/customers/list GET /api/v1/customers Vos clients d'abonnement, plus récents en premier. Un client est créé automatiquement à la première inscription d'une adresse e-mail. Voir [Clients et moyens de paiement](/subscriptions/customers). # Moyens de paiement d'un client Source: https://docs.mercy-pay.com/api/customers/payment-methods GET /api/v1/customers/{customer}/payment_methods Les cartes et numéros mobile money enregistrés, du plus ancien au plus récent. Seul votre client ajoute un moyen de paiement, sur la page de paiement ou dans son [espace client](/subscriptions/billing-portal) : il faut sa carte ou son téléphone. # Retirer un moyen de paiement Source: https://docs.mercy-pay.com/api/customers/remove-payment-method DELETE /api/v1/customers/{customer}/payment_methods/{payment_method} Le moyen n'est plus jamais utilisé. Un abonnement qui l'avait choisi repasse au moyen par défaut. Le dernier moyen d'un client qui a un abonnement en cours ne se retire pas (`409`). # Choisir le moyen par défaut Source: https://docs.mercy-pay.com/api/customers/set-default POST /api/v1/customers/{customer}/payment_methods/{payment_method}/default Ce moyen devient celui essayé en premier à chaque échéance. # Modifier un client Source: https://docs.mercy-pay.com/api/customers/update PATCH /api/v1/customers/{id} Seuls les champs envoyés changent. `metadata` se fusionne clé par clé : une clé à `null` est retirée. # Lister les envois d'un événement Source: https://docs.mercy-pay.com/api/events/deliveries GET /api/v1/events/{id}/deliveries Chaque envoi de l'événement à vos endpoints, renvoi compris, du plus récent au plus ancien, avec chaque tentative et le code renvoyé par votre serveur. Pour comprendre depuis votre code pourquoi un événement n'est pas arrivé. Le corps des réponses de votre serveur n'est pas renvoyé. Un événement qui n'est pas arrivé ? Cette liste montre chaque tentative : le code HTTP renvoyé par votre serveur, la durée, ou l'erreur réseau. Corrigez votre serveur, puis [renvoyez l'événement](/api/events/resend). # Lire un événement Source: https://docs.mercy-pay.com/api/events/get GET /api/v1/events/{id} # Lister les événements Source: https://docs.mercy-pay.com/api/events/list GET /api/v1/events Tous les événements du compte, plus récents en premier : de quoi rattraper ceux qu'un serveur indisponible a manqués. # Renvoyer un événement Source: https://docs.mercy-pay.com/api/events/resend POST /api/v1/events/{id}/resend Renvoie un événement à l'un de vos endpoints, avec 8 nouvelles tentatives : pour rattraper ce qu'un serveur indisponible a manqué. Un endpoint désactivé renvoie `409`. # Calculer les frais d'un paiement Source: https://docs.mercy-pay.com/api/fees/quote POST /api/v1/fees/quote Ce qu'un paiement donnerait pour votre compte, sans rien créer ni débiter : le montant débité au client (dans la devise de l'opérateur pour un pays mobile money), les frais et ce qui arrive sur votre solde. Le calcul est celui d'un vrai paiement. Annoncez le prix exact sur votre site avant d'envoyer le client payer. Le calcul est celui d'un vrai paiement : si vous faites payer les frais à vos clients, ils sont ajoutés au montant ; pour un pays mobile money qui utilise une autre devise, le montant est converti au taux du jour. Rien n'est créé ni débité. Un devis n'engage pas : un taux de conversion peut changer entre le devis et le paiement. # Lire une facture Source: https://docs.mercy-pay.com/api/invoices/get GET /api/v1/invoices/{id} Une facture. # Lister les factures Source: https://docs.mercy-pay.com/api/invoices/list GET /api/v1/invoices Les factures de vos abonnements, plus récentes en premier. Mercy Pay encaisse et relance chaque facture tout seul, voir [Rappels et relances](/subscriptions/overview#rappels-et-relances). Ces routes servent à suivre vos factures et à agir sur l'une d'elles sans attendre. # Encaisser une facture Source: https://docs.mercy-pay.com/api/invoices/pay POST /api/v1/invoices/{id}/pay Encaisse tout de suite sur les moyens enregistrés du client, dans l'ordre. Le résultat arrive par `invoice.paid` ou `invoice.payment_failed`. Une carte est débitée, ou une demande part sur le téléphone du client. Répond `422` si la facture n'est pas à payer, si l'abonnement n'est pas en cours, ou si un paiement attend déjà sa confirmation. # Envoyer la facture au client Source: https://docs.mercy-pay.com/api/invoices/send POST /api/v1/invoices/{id}/send Envoie au client un e-mail pour payer : un lien de paiement en mobile money, un lien vers son espace client pour une carte. Une fois toutes les 10 minutes au plus. # Archiver un lien Source: https://docs.mercy-pay.com/api/payment-links/archive POST /api/v1/payment_links/{id}/archive Désactive le lien et le sort de la liste. Ses paiements restent consultables. # Créer un lien Source: https://docs.mercy-pay.com/api/payment-links/create POST /api/v1/payment_links Crée un lien partageable : prix fixe, montant libre, plusieurs tarifs, panier ou inscription à un plan. > [!TIP] > > Essayez avec une clé **sandbox** (`mp_sandbox_…`) : aucun argent réel ne bouge. Voir [Test et production](/start/environments). Un fichier envoyé par un payeur (champ `file`) n'accepte que les images et les PDF. Il reste privé : seuls les membres de votre boutique l'ouvrent, depuis le détail du lien dans le tableau de bord. Voir [Créer un lien par l'API](/payment-links/api-guide) pour des exemples de chaque mode de prix. # Supprimer un lien Source: https://docs.mercy-pay.com/api/payment-links/delete DELETE /api/v1/payment_links/{id} Supprime un lien qui n'a jamais ouvert de session de checkout. Sinon la réponse est `409` : archivez-le. # Lire un lien Source: https://docs.mercy-pay.com/api/payment-links/get GET /api/v1/payment_links/{id} Un lien de paiement. # Lister les liens Source: https://docs.mercy-pay.com/api/payment-links/list GET /api/v1/payment_links Vos liens, plus récents en premier. Les liens archivés sont exclus par défaut. # Paiements d'un lien Source: https://docs.mercy-pay.com/api/payment-links/payments GET /api/v1/payment_links/{id}/payments Les paiements réussis faits par ce lien, plus récents en premier. `status` liste un autre statut. Pour recevoir chaque paiement avec le téléphone, l'adresse et les réponses aux champs, écoutez `checkout_session.completed`. Voir [Événements](/developers/webhooks/events). # Modifier un lien Source: https://docs.mercy-pay.com/api/payment-links/update PATCH /api/v1/payment_links/{id} Ne change que les champs envoyés. Un champ envoyé à `null` est vidé. `type`, `currency` et `plan` ne se modifient pas. Pour `price_options` et `items`, envoyez la liste complète : un élément avec son `id` est mis à jour, un élément sans `id` est ajouté, un élément absent est supprimé. # Encaisser un paiement Source: https://docs.mercy-pay.com/api/payments/create POST /api/v1/payments Encaisse sans page hébergée : votre serveur collecte les informations du client et appelle cette route. En mobile money, une demande part sur le téléphone du client. Le résultat arrive par webhook. > [!TIP] > > Essayez avec une clé **sandbox** (`mp_sandbox_…`) : aucun argent réel ne bouge. Voir [Test et production](/start/environments). `payment_method` choisit le canal : `mobile_money` (par défaut) ou `card`. Pour la carte, le plus simple est la page hébergée, voir [Accepter un paiement](/payments/accept-a-payment). Une carte dans votre propre formulaire se met en place avec notre équipe : écrivez au support. > [!NOTE] > > Un opérateur mobile money ne déplace que la devise de son pays. Si `currency` est différente, le montant est converti avant l'encaissement et la réponse donne le montant réellement prélevé, dans la devise de l'opérateur. Exemple : `1000 XAF` vers `MTN_MOMO_RWA` encaisse en RWF, et la réponse renvoie `"currency": "RWF"`. Sans taux disponible, la requête répond `422`. ## Un paiement qui échoue La création répond toujours `201`, même si le paiement échoue ensuite chez l'opérateur. Vous l'apprenez par le webhook `payment.failed` ou en relisant le paiement : `status` passe à `failed` et `failure_code` donne la raison. ## failure_code Un code stable, en anglais et en minuscules, à tester dans votre code. `failure_reason` est un texte en français à afficher, pas à comparer. | `failure_code` | Signification | | - | - | | `insufficient_balance` | Le payeur n'a pas assez de solde mobile money | | `payment_not_approved` | Le payeur a refusé ou ignoré la demande sur son téléphone | | `payer_not_found` | Le numéro n'est pas associé à l'opérateur choisi | | `payer_limit_reached` | Le payeur a atteint sa limite chez l'opérateur | | `transaction_expired` | Le payeur n'a pas confirmé à temps | | `customer_not_eligible` | Ce client n'est pas éligible à ce type de paiement | | `invalid_phone_number` | Le numéro n'est pas valide pour l'opérateur choisi | | `amount_out_of_bounds` | Le montant est hors des limites autorisées | | `provider_temporarily_unavailable` | L'opérateur est temporairement indisponible | | `unspecified_failure` | Refus sans motif plus précis. C'est le code de tous les refus carte : lisez `failure_reason` | Cette liste peut s'enrichir, mais un code ne change jamais de sens. Traitez un code inconnu comme un échec générique. Une carte refusée peut encore réussir si le client réessaie, voir [L'objet paiement](/payments/payment-object#un-refus-carte-nest-pas-toujours-definitif). ## next_action Toujours présent. `null` quand il n'y a rien de plus à faire (le mobile money aujourd'hui). Avec `payment_method: "card"`, `type` vaut `card_payment` et `client_secret` confirme le paiement depuis votre formulaire. Décidez selon `type` : un futur moyen de paiement pourra ajouter le sien. # Lire un paiement Source: https://docs.mercy-pay.com/api/payments/get GET /api/v1/payments/{id} Un paiement, qu'il vienne d'une session de checkout ou d'une charge directe. `next_action` est toujours `null` ici : il ne sert qu'à la création. # Lister les paiements Source: https://docs.mercy-pay.com/api/payments/list GET /api/v1/payments Vos paiements, du plus récent au plus ancien. Voir [L'objet paiement](/payments/payment-object#statuts) pour le sens de chaque statut et [Pagination](/developers/pagination) pour parcourir les pages. # Demander un retrait Source: https://docs.mercy-pay.com/api/payouts/create POST /api/v1/payouts Demande un versement de votre solde vers un compte mobile money. Clé live seulement. La demande est validée avant d'être envoyée à l'opérateur. > [!NOTE] > > En live, il faut que votre vérification d'identité soit approuvée. En sandbox, le retrait part tout de suite depuis votre solde de test, vers un numéro Mobile Money de test. La demande n'est pas exécutée tout de suite : elle est d'abord validée, puis envoyée à l'opérateur. Suivez-la avec les webhooks `payout.*`. Voir [Demander un retrait](/payouts/request-a-payout). | Erreur | Cause | | - | - | | `422` `Solde disponible insuffisant pour ce montant.` | Le solde du pays et de l'opérateur ne couvre pas le retrait | | `422` `Opérateur Mobile Money invalide pour ce pays.` | Code opérateur incohérent avec le pays | # Lire un retrait Source: https://docs.mercy-pay.com/api/payouts/get GET /api/v1/payouts/{id} Un retrait, avec la même structure que la réponse de création. # Lister les retraits Source: https://docs.mercy-pay.com/api/payouts/list GET /api/v1/payouts Vos retraits, du plus récent au plus ancien. Voir [Retraits](/payouts/overview#statuts) pour le sens de chaque statut. # Archiver un plan Source: https://docs.mercy-pay.com/api/plans/archive POST /api/v1/plans/{id}/archive Le plan n'accepte plus de nouvel abonné. Les abonnés actuels continuent d'être facturés. # Créer un plan Source: https://docs.mercy-pay.com/api/plans/create POST /api/v1/plans Le prix et le rythme d'un abonnement. Le même plan sert les clients carte et mobile money. > [!TIP] > > Essayez avec une clé **sandbox** (`mp_sandbox_…`) : aucun argent réel ne bouge. Voir [Test et production](/start/environments). Le même plan sert un client carte et un client mobile money : ce choix se fait pour chaque abonnement. # Supprimer un plan Source: https://docs.mercy-pay.com/api/plans/delete DELETE /api/v1/plans/{id} Supprime un plan qui n'a jamais eu d'abonné. Sinon la réponse est `409` : archivez-le. # Lire un plan Source: https://docs.mercy-pay.com/api/plans/get GET /api/v1/plans/{id} Un plan. # Lister les plans Source: https://docs.mercy-pay.com/api/plans/list GET /api/v1/plans Vos plans, plus récents en premier. # Réactiver un plan Source: https://docs.mercy-pay.com/api/plans/restore POST /api/v1/plans/{id}/restore Réactive un plan archivé. # Modifier un plan Source: https://docs.mercy-pay.com/api/plans/update PATCH /api/v1/plans/{id} Modifie `name`, `description`, `portal_switchable` ou `metadata`. Le prix, la devise et le rythme ne changent jamais : envoyer `amount`, `currency`, `interval` ou `interval_count` répond `422`. Pour un autre prix, créez un nouveau plan, puis changez le plan de vos abonnés avec [Modifier un abonnement](/api/subscriptions/update). # État des opérateurs Source: https://docs.mercy-pay.com/api/provider-status/get GET /api/v1/provider_status Public, sans clé : appelez-le depuis le navigateur de votre client pour griser un opérateur en panne avant même de tenter un paiement. Chaque statut vaut `operational`, `degraded` ou `down`. * **`down`** : l'opérateur ne traite aucun encaissement. Grisez-le sur votre page de paiement plutôt que de laisser votre client tomber sur un échec. * **`degraded`** : utilisable, avec plus de risques de lenteur ou d'échec ponctuel. La donnée se rafraîchit en continu. Interrogez cette route à chaque affichage de votre page de paiement, sans garder le résultat en cache longtemps. Elle est soumise à la même [limite de débit](/developers/rate-limits) que le reste de l'API. # Annuler un remboursement retardé Source: https://docs.mercy-pay.com/api/refunds/cancel POST /api/v1/refunds/{id}/cancel Annule un remboursement qui attend un opérateur indisponible. # Rembourser un paiement Source: https://docs.mercy-pay.com/api/refunds/create POST /api/v1/payments/{id}/refunds Rend tout le paiement au client. # Lire un remboursement Source: https://docs.mercy-pay.com/api/refunds/get GET /api/v1/refunds/{id} Un remboursement et son statut. # Lister les remboursements Source: https://docs.mercy-pay.com/api/refunds/list GET /api/v1/refunds Les remboursements, du plus récent au plus ancien. # Passer à l'échéance (sandbox) Source: https://docs.mercy-pay.com/api/subscriptions/advance POST /api/v1/subscriptions/{id}/advance Sandbox uniquement. Amène la prochaine échéance à maintenant : la facture est créée et son paiement lancé tout de suite, avec les webhooks `invoice.created` puis `invoice.paid` ou `invoice.payment_failed`. Pour tester un renouvellement sans attendre la vraie date. Répond `422` avec une clé live, ou pour un abonnement sans échéance à venir. # Annuler un abonnement Source: https://docs.mercy-pay.com/api/subscriptions/cancel POST /api/v1/subscriptions/{id}/cancel Tout de suite, ou à la fin de la période payée avec `at_period_end`. # Créer un abonnement Source: https://docs.mercy-pay.com/api/subscriptions/create POST /api/v1/subscriptions Inscrit un client à un plan. L'abonnement reste `incomplete` jusqu'au premier paiement : envoyez à votre client la page `next_action.redirect_url`. > [!TIP] > > Essayez avec une clé **sandbox** (`mp_sandbox_…`) : aucun argent réel ne bouge. Voir [Test et production](/start/environments). Envoyez la page `next_action.redirect_url` à votre client. Il y paie le premier cycle par carte ou mobile money, ou enregistre son moyen de paiement si le plan a un essai encore disponible pour lui. L'abonnement devient `active` (ou `trialing`) et vous recevez `subscription.created`. Sans paiement sous 48 heures, l'inscription est annulée. | Erreur | Cause | | - | - | | `422` sur `customer_email` | Adresse absente : elle est obligatoire | | `422` sur `customer_provider` | Opérateur invalide pour `customer_country`, ou numéro invalide | | `422` sur `plan` | Plan introuvable ou archivé | | `422` sur `subscription` | Le client a déjà un abonnement en cours à ce plan | Voir [Paiement récurrent pas à pas](/subscriptions/api-guide). # Lire un abonnement Source: https://docs.mercy-pay.com/api/subscriptions/get GET /api/v1/subscriptions/{id} Un abonnement, avec son plan. # Factures d'un abonnement Source: https://docs.mercy-pay.com/api/subscriptions/invoices GET /api/v1/subscriptions/{id}/invoices Les factures de cet abonnement, plus récentes en premier. # Lister les abonnements Source: https://docs.mercy-pay.com/api/subscriptions/list GET /api/v1/subscriptions Vos abonnements, plus récents en premier. # Mettre en pause Source: https://docs.mercy-pay.com/api/subscriptions/pause POST /api/v1/subscriptions/{id}/pause Plus aucune facturation pendant la pause. # Retirer une annulation prévue Source: https://docs.mercy-pay.com/api/subscriptions/reactivate POST /api/v1/subscriptions/{id}/reactivate Retire une annulation prévue en fin de période. Répond `422` s'il n'y en a pas. # Reprendre Source: https://docs.mercy-pay.com/api/subscriptions/resume POST /api/v1/subscriptions/{id}/resume Une nouvelle période commence et elle est facturée tout de suite, ou à la fin de l'essai s'il était encore en cours. # Modifier un abonnement Source: https://docs.mercy-pay.com/api/subscriptions/update PATCH /api/v1/subscriptions/{id} Ne change que les champs envoyés. `payment_method` et `customer_email` ne se modifient pas. Une étape impossible dans l'état actuel répond `422` sur `subscription`, avec un message qui explique pourquoi. # Virer entre deux soldes Source: https://docs.mercy-pay.com/api/wallet-transfers/create POST /api/v1/wallet_transfers Déplace un montant entre deux de vos soldes mobile money du même pays et de la même devise, par exemple de MTN vers Orange au Cameroun, pour qu'un retrait puisse partir vers l'un ou l'autre. Immédiat et sans frais. Un solde carte ne peut pas y prendre part. Le solde débité doit couvrir le montant, retraits déjà validés déduits. Utile quand vos clients paient surtout par un opérateur et que vous voulez retirer vers un autre : le montant passe d’un solde à l’autre tout de suite, sans frais. Les deux soldes doivent être du même pays et de la même devise. # Lister les virements entre soldes Source: https://docs.mercy-pay.com/api/wallet-transfers/list GET /api/v1/wallet_transfers # Lire les soldes Source: https://docs.mercy-pay.com/api/wallets/list GET /api/v1/wallets Vos soldes dans l'environnement de la clé, par canal, pays et opérateur. Cette liste n'est pas paginée. Une clé sandbox ne voit que les soldes sandbox, et une clé live que les soldes live. Un encaissement mobile money crédite `available_balance` tout de suite. Un encaissement carte crédite d'abord `pending_balance`, qui passe en disponible en général sous 5 jours. Voir [Soldes](/payouts/balances). # Créer un endpoint Source: https://docs.mercy-pay.com/api/webhook-endpoints/create POST /api/v1/webhook_endpoints Le secret de signature n'apparaît que dans cette réponse : conservez-le pour vérifier les signatures. # Supprimer un endpoint Source: https://docs.mercy-pay.com/api/webhook-endpoints/delete DELETE /api/v1/webhook_endpoints/{id} # Lire un endpoint Source: https://docs.mercy-pay.com/api/webhook-endpoints/get GET /api/v1/webhook_endpoints/{id} # Lister les endpoints Source: https://docs.mercy-pay.com/api/webhook-endpoints/list GET /api/v1/webhook_endpoints # Changer le secret Source: https://docs.mercy-pay.com/api/webhook-endpoints/rotate-secret POST /api/v1/webhook_endpoints/{id}/rotate_secret Crée un nouveau secret de signature, renvoyé dans cette réponse uniquement. Pendant 24 heures, chaque webhook est signé avec l'ancien et le nouveau : déployez le nouveau dans ce délai. # Envoyer un test Source: https://docs.mercy-pay.com/api/webhook-endpoints/test POST /api/v1/webhook_endpoints/{id}/test Envoie un événement `webhook.test` à cet endpoint seulement, pour vérifier que votre serveur reçoit et vérifie les webhooks. Un endpoint désactivé renvoie `409`. # Modifier un endpoint Source: https://docs.mercy-pay.com/api/webhook-endpoints/update PATCH /api/v1/webhook_endpoints/{id} Seuls les champs envoyés changent. `status` à `disabled` met l'envoi en pause. # Journal des changements Source: https://docs.mercy-pay.com/changelog Ce qui change dans l'API et ce que vous devez vérifier dans votre intégration. Les changements les plus récents sont en haut. Filtrez par produit avec les étiquettes. Une entrée marquée **À vérifier** peut demander une modification de votre code. **Couverture et politique de versions, 14 novembre 2026** * Nouveaux pays : Gabon (Airtel, Moov), Kenya (M-Pesa) et Mozambique (M-Pesa, en MZN). Nouveaux opérateurs : Moov en Côte d'Ivoire et Zamtel en Zambie. Le Ghana n'est plus proposé. * L'API `v1` est figée : plus aucun changement cassant sans 90 jours de préavis. Voir [Politique de versions](/developers/versioning). * Nouveau réglage : l'e-mail du contact technique qui reçoit nos annonces de changements. * Nouveau : [`POST /api/v1/fees/quote`](/api/fees/quote) calcule le montant payé par le client, les frais et ce que vous recevez, avant tout paiement. * [`GET /api/v1/account`](/api/account/get) donne vos frais (taux par canal, frais fixe carte, qui les paie), les pays mobile money ouverts et vos limites. * Nouveaux paramètres de session `payment_method_types` et `allowed_countries` pour choisir ce que la page de paiement propose, et `{CHECKOUT_SESSION_ID}` dans `success_url`. Voir [Sessions de paiement](/payments/checkout-sessions#choisir-ce-que-la-page-propose). * Nouvel événement `provider_status.changed` quand un opérateur tombe en panne ou revient, à cocher sur votre endpoint. * Nouveau : [`GET /api/v1/events/{id}/deliveries`](/api/events/deliveries) liste chaque envoi d'un événement et chaque tentative. Renvoyer un événement à un endpoint qui l'avait déjà reçu fonctionne de nouveau. * Limites de débit séparées : 300 lectures et 60 écritures par minute et par clé. Voir [Limites de débit](/developers/rate-limits). * Nouveau : virez entre deux soldes mobile money du même pays (MTN vers Orange au Cameroun, par exemple), depuis la page Soldes ou par [`POST /api/v1/wallet_transfers`](/api/wallet-transfers/create). Immédiat et sans frais. * Remboursement partiel : passez `amount` à [`POST /api/v1/payments/{id}/refunds`](/api/refunds/create), plusieurs fois si besoin. Le paiement reste `succeeded` jusqu'au remboursement complet, avec le nouveau champ `amount_refunded`. `payment.refunded` n'est envoyé qu'une fois tout le paiement rendu. * Nouveaux champs sur un paiement : `fee_paid_by` (qui a payé les frais) et `payer` (nom, e-mail, numéro). Le numéro saisi sur la page de paiement est aussi gardé dans `payer_phone` de la session. * Documentation corrigée : les retraits fonctionnent bien en sandbox, et le statut `refunded` est utilisé depuis le 13 novembre. **Remboursements et retraits, 13 novembre 2026** * Nouveau : remboursez un paiement en entier, depuis la transaction ou par [`POST /api/v1/payments/{id}/refunds`](/api/refunds/create). Frais de 1 %, au moins 100 FCFA, jusqu'à 90 jours après le paiement. Voir [Rembourser un paiement](/payments/refunds). * Nouveaux événements `payment.refunded`, `refund.succeeded`, `refund.failed` et `refund.canceled`. Nouveaux codes d'erreur `insufficient_balance` et `refund_not_allowed`. * Nouveau champ `status_detail` sur un retrait : `delayed` quand l'opérateur est indisponible (le retrait peut alors être annulé), `pending_processing` quand il n'est pas encore parti. * Un retrait vers un opérateur fermé aux retraits est refusé à la demande, sans réserver le montant. **Factures et retraits, 11 novembre 2026** * Nouveau statut de retrait `canceled` : un retrait pas encore examiné peut être annulé depuis le tableau de bord, sans mouvement d'argent. Nouvel événement `payout.canceled`. * Nouvel événement `invoice.canceled` : vous avez annulé une facture impayée, ses relances s'arrêtent et l'abonnement continue. * Un client qui paie avec un e-mail est désormais aussi un client de l'API : il apparaît dans [`GET /api/v1/customers`](/api/customers/list). * Dans les logs de la console, collez un `Request-Id` pour retrouver l'appel exact. **Sandbox, 11 novembre 2026** * Les retraits fonctionnent en sandbox : ils partent tout de suite et passent à `paid` ou `failed` selon le numéro de test. L'erreur `payout_unavailable_in_sandbox` n'est plus renvoyée. * Nouveau : [`POST /api/v1/subscriptions/{id}/advance`](/api/subscriptions/advance) fait passer un abonnement sandbox à son échéance. * La page de paiement sandbox propose une cause d'échec précise. Voir [Tester les cas difficiles](/start/environments#tester-les-cas-difficiles). **Moyens de paiement, 10 novembre 2026** * Apple Pay, Google Pay et Link s'affichent avec la carte quand l'appareil de votre client les propose. * Un paiement en FCFA (XAF ou XOF) débite la carte en euros, au taux fixe de 1 € pour 655,957 FCFA : votre prix et votre solde ne changent pas. Votre client voit le montant en euros avant de payer, et son reçu montre les deux. * En FCFA comme en EUR, USD ou GBP, votre client voit aussi les moyens acceptés en euros : par exemple Bancontact, EPS, MB WAY, Satispay, Klarna ou Amazon Pay. * Rien à changer dans votre intégration : ces paiements restent des paiements `card`. Les abonnements restent en carte. **Tarif carte, 10 novembre 2026** * **À vérifier** : un paiement par carte coûte désormais 6 % + 200 FCFA (0,30 en USD, EUR ou GBP). Le mobile money et les retraits ne changent pas. * Si vous faites payer la commission à votre client, le frais fixe est ajouté à ce qu'il paie : vous recevez toujours votre prix exact. * Le solde d'un paiement carte passe en disponible le jour où les fonds nous sont réellement versés, en général sous 5 jours. **Page de paiement, 9 novembre 2026** * Votre client voit pourquoi son paiement a échoué et quoi faire : un message par cause en mobile money, et le vrai motif d'un refus de carte (solde, code de sécurité, carte expirée…). * « Payer autrement » reste visible sous le formulaire carte. Les champs carte sont toujours en français. * Le nom, l'e-mail et le téléphone donnés à une session de checkout sont pré-remplis. Un lien de paiement accepte aussi `?email=`, `?name=` et `?phone=` : voir [Liens de paiement](/payment-links/overview#pré-remplir-les-informations-du-client). * L'écran de succès propose le reçu à télécharger, puis renvoie vers votre `success_url` après 5 secondes. * Vos liens et vos pages d'abonnement reprennent tout votre kit de marque : couleurs, police, arrondis et vos propres conditions. * Un lien partagé sur WhatsApp ou Facebook s'affiche à vos couleurs, avec votre logo, le titre et le montant. Seul votre nom apparaît dans l'onglet du navigateur, sur l'aperçu et sur les pages d'erreur de paiement. * À l'abonnement aussi, l'opérateur mobile money est trouvé d'après le numéro saisi. Votre client peut le changer. **Webhooks, 9 novembre 2026** Des webhooks plus sûrs et plus fiables. * **À vérifier :** la signature passe dans l'en-tête `MercyPay-Signature`, de la forme `t=…,v1=…`. `v1` est le HMAC-SHA256 de `{t}.{corps brut}`, et une signature de plus de 5 minutes doit être refusée. L'ancien en-tête `X-MercyPay-Signature` n'est plus envoyé. Mettez votre vérification à jour : voir [Sécurité des webhooks](/developers/webhooks/security). * **À vérifier :** `created` d'un événement est une date ISO 8601, comme partout dans l'API, et l'événement porte `object: "event"`. * Nouveaux en-têtes `MercyPay-Event-Id`, `MercyPay-Event-Type` et `MercyPay-Delivery-Id`. `X-MercyPay-Event` est remplacé par `MercyPay-Event-Type`. * Changer de secret ne coupe plus rien : pendant 24 heures, chaque webhook est signé avec l'ancien et le nouveau secret. * 8 tentatives sur 3 jours au lieu de 5 sur 1 heure 20. Un endpoint qui n'accepte rien pendant 3 jours est désactivé, et vos développeurs sont prévenus par e-mail. * **Envoyer un test** et **Relancer les échecs** dans la console. Dans l'API : [`POST /api/v1/webhook_endpoints/{id}/test`](/api/webhook-endpoints/test), [`POST /api/v1/webhook_endpoints/{id}/rotate_secret`](/api/webhook-endpoints/rotate-secret) et [`POST /api/v1/events/{id}/resend`](/api/events/resend). **8 novembre 2026** Une API v1 plus cohérente. **À vérifier :** ces changements modifient la forme des réponses et des chemins. Mettez votre intégration à jour en suivant les points ci-dessous. Identifiants et forme des objets : * **À vérifier :** chaque objet a un `id` préfixé selon son type (`pay_…`, `po_…`, `cus_…`, `pm_…`, `sub_…`, `plan_…`, `in_…`, `plink_…`, `wal_…`, `cs_…`), plus `object`, `livemode` et `created`. `uuid` et `created_at` disparaissent. Pour une session de checkout, `reference` est remplacée par `id`. * Les objets liés renvoient leur `id`. Avec `expand`, vous recevez l'objet entier. Voir [Objets liés](/developers/expand). * **À vérifier :** les montants d'un paiement s'appellent comme ceux d'un retrait : `amount`, `fee_amount`, `net_amount`. `gross_amount`, `psp_fee_amount`, `platform_fee_amount` et `net_amount_to_merchant` disparaissent. Voir [Paiements](/payments/payment-object). * **À vérifier :** le plan s'envoie dans `plan` à la création d'un abonnement ou d'un lien récurrent, et un lien récurrent le renvoie dans `plan`. `subscription_plan_id` et `subscription_plan` disparaissent. * `metadata` sur les paiements, retraits, clients et factures. Un paiement reprend celle de sa session, une facture celle de son abonnement. Voir [Metadata](/developers/metadata). * Nouveau champ `failure_code` sur un retrait échoué, avec les mêmes codes que les paiements. Chemins, listes et erreurs : * **À vérifier :** les chemins passent en snake_case : `/api/v1/checkout_sessions`, `/api/v1/payment_links`, `/api/v1/billing_portal/sessions`, `/api/v1/customers/{id}/payment_methods`, `/api/v1/provider_status`. * **À vérifier :** les listes se parcourent avec un curseur : `limit`, `starting_after`, `ending_before`, `created[gte]` et `created[lte]`. La réponse est `{"object": "list", "url", "has_more", "next_cursor", "data"}`. `page`, `per_page`, `links` et `meta` disparaissent. Voir [Pagination](/developers/pagination). * **À vérifier :** une erreur est un seul objet `error` avec `type`, `code`, `message`, `param`, `doc_url`, `request_id` et, sur une erreur `422`, `errors`. Lisez `error.code` au lieu de `code`. Voir [Erreurs](/developers/errors). * Chaque réponse porte un en-tête `Request-Id`, à donner au support pour désigner un appel précis. Nouveaux endpoints : * [Clients](/api/customers/create) : création, modification et suppression. * [`POST /api/v1/checkout_sessions/{id}/expire`](/api/checkout-sessions/expire) : ferme une session que plus personne ne doit payer. * [`GET /api/v1/events`](/api/events/list) : tous les événements du compte, pour rattraper ceux que votre serveur a manqués. * [`/api/v1/webhook_endpoints`](/api/webhook-endpoints/create) : gérez vos endpoints de webhook depuis votre code. Tableau de bord et e-mails : * Les messages de connexion par clé d'accès et par double authentification, ainsi que les e-mails de réinitialisation du mot de passe et de confirmation d'adresse, sont maintenant en français. * Un retrait échoué indique un motif clair, par exemple un numéro de bénéficiaire non reconnu par l'opérateur. **7 novembre 2026** Paiement avant activation et moyens de paiement du client : * **À vérifier :** un abonnement créé est `incomplete` jusqu'au premier paiement, en carte comme en mobile money. Redirigez votre client vers `next_action.redirect_url` (`/abonnements/{uuid}/payer`) et donnez l'accès à votre service à la réception de `subscription.created`. Sans paiement sous 48 heures, l'inscription est annulée. Voir [Abonnements](/subscriptions/overview). * **À vérifier :** `next_action` est maintenant `{"type": "redirect", "redirect_url": ...}`. L'ancienne page `/abonnements/{uuid}/carte` redirige vers la nouvelle. * `payment_method` devient facultatif à la création : sans lui, le client choisit sur la page de paiement. * Un essai par client et par plan (même e-mail, carte ou numéro). Un client ne peut pas avoir deux abonnements en cours au même plan. * Nouveau statut `unpaid` et événement `subscription.unpaid` : en mobile money, la demande part 2 jours avant l'échéance, et l'abonnement passe à `unpaid` si rien n'est payé à l'échéance. Coupez alors l'accès à votre service. * Chaque client a ses cartes et numéros mobile money de n'importe quel pays, dont un par défaut. À l'échéance, ils sont essayés dans l'ordre. Nouveaux endpoints [Clients](/subscriptions/customers) et champ `customer` dans un abonnement. * Passer à un plan plus cher au même rythme est immédiat, contre la différence au prorata. Un plan moins cher s'applique à la prochaine échéance. * Nouveau champ `portal_switchable` sur les [plans](/api/plans/create) : vos abonnés peuvent passer eux-mêmes à ces plans depuis leur espace client. * Nouveau champ `kind` sur les [factures](/api/invoices/list) : `first`, `cycle` ou `proration`. * L'[espace client](/subscriptions/billing-portal) est refait : une page par abonnement, cartes et numéros, factures, paiement d'avance. * Les e-mails de rappel, d'échec de paiement et de fin d'abonnement affichent la date en toutes lettres, par exemple « le 9 novembre 2026 ». Retraits et plafond : * Un retrait refusé par notre équipe vous est maintenant signalé par e-mail et par le webhook `payout.failed`, avec le motif dans `failure_reason`. * Le plafond mensuel des comptes en votre nom ne compte plus que les paiements réels : vos paiements de test n'entament plus votre plafond. Liens de paiement : * Tout le constructeur du tableau de bord est disponible dans l'API : `PATCH`, archivage, suppression, tarifs, panier, champs personnalisés, métadonnées. Voir [Liens de paiement](/api/payment-links/create). * Nouveaux réglages `collect_phone`, `collect_address` et `after_payment_message`. * Nouveau `GET /api/v1/payment-links/{uuid}/payments` et nouvel événement `checkout_session.completed`. * **À vérifier :** `uses_count` compte les paiements réussis, plus les ouvertures. Ouvrir un lien à prix fixe affiche sa page au lieu de rediriger tout de suite vers le checkout. * **À vérifier :** un lien créé sans `amount` est en montant libre (`pricing_mode: "flexible"`). Abonnements : * **À vérifier :** `customer_email` est obligatoire pour tout abonnement. * En mobile money, chaque échéance envoie une demande de paiement sur le téléphone du client, puis un lien par e-mail s'il ne la valide pas. * Relances à J+1, J+3 et J+7 pour la carte comme pour le mobile money, puis annulation. * Pause et reprise, nombre d'échéances limité (`max_cycles`), annulation en fin de période et retour sur l'annulation (`reactivate`). * Nouvelles [factures](/api/invoices/list) dans l'API, avec encaissement immédiat et renvoi au client. * Nouvel [espace client](/subscriptions/billing-portal) et `POST /api/v1/billing-portal/sessions`. * Nouveaux événements : `subscription.updated`, `subscription.past_due`, `subscription.unpaid`, `subscription.paused`, `subscription.resumed`, `invoice.created` et `invoice.upcoming`. * **À vérifier :** `PATCH /api/v1/plans/{uuid}` modifie le nom, la description, `portal_switchable` et les métadonnées. Pour archiver, appelez `POST /api/v1/plans/{uuid}/archive`. * **À vérifier :** dans un abonnement, `plan` est maintenant l'objet plan complet. Compte, retraits et documentation : * Nouveau parcours de démarrage : code par e-mail à l'inscription, boutique en 5 écrans, premier paiement test guidé. * Une clé sandbox peut être copiée à tout moment depuis la [console Développeurs](/developers/console). Les clés créées avant ce changement n'ont pas ce bouton : générez-en une nouvelle si besoin. * La page de paiement affiche des [données de test](/start/environments#tester-un-paiement) en sandbox. * Nouveau parcours de [vérification d'identité](/start/verify-your-account), avec recto et verso et un envoi explicite du dossier. Les dossiers déjà envoyés gardent leurs règles. * Connexion possible avec un code reçu par e-mail. * `POST /api/v1/payouts` accepte `destination_id`, l'identifiant d'un [compte de retrait enregistré](/payouts/request-a-payout#utiliser-un-compte-enregistré). * Chaque retrait porte un champ `origin` : `dashboard`, `api` ou `automatic`. Il reste vide pour les retraits plus anciens. * Nouveaux [retraits automatiques](/payouts/request-a-payout#retraits-automatiques), réglés solde par solde. * Un compte suspendu ne peut plus demander de retrait, y compris depuis le tableau de bord. * Un dossier refusé peut corriger ses informations avant d'être renvoyé. * Le guide d'intégration de carte dans votre propre formulaire est retiré. Pour cette intégration, écrivez au support. * Nouveaux documents légaux : activités interdites, lutte anti-blanchiment, traitement des données et conditions de l'API. Documentation : * La documentation est rangée par produit, avec pour chacun une partie « Sans code » illustrée de captures en clair et en sombre, et une partie « Développeurs ». * La référence API est générée depuis une spec OpenAPI : chaque route a sa page avec le bouton « Essayer » et des exemples en cURL, PHP, JavaScript et Python. Les anciennes adresses redirigent vers les nouvelles. **6 novembre 2026** * Chaque erreur de l'API porte un champ `code` stable, voir [Erreurs](/developers/errors). Testez ce champ plutôt que `message`. * Chaque événement webhook porte `created` et `livemode`, voir [Événements](/developers/webhooks/events). * **À vérifier :** les messages d'erreur disent « retrait » au lieu de « payout ». Si votre code compare le texte de `message`, passez au champ `code`. Les URL, les champs et les événements de l'API ne changent pas (`/api/v1/payouts`, `payout.paid`). * **À vérifier :** une carte refusée peut réussir ensuite si le client réessaie. Vous recevez alors `payment.failed` puis `payment.succeeded`, voir [Paiements](/payments/payment-object). * Un paiement carte non terminé passe à `expired` après 24 heures, avec un événement `payment.failed` et le `failure_code` `transaction_expired`. * Les numéros mobile money du Congo, de Côte d'Ivoire et du Bénin gardent leur `0`. * Les secrets de webhook peuvent être réaffichés et régénérés depuis la [console Développeurs](/developers/console). **5 novembre 2026** * **À vérifier :** `Idempotency-Key` est obligatoire sur `POST /api/v1/payments`. Sans cet en-tête, l'API répond `400`. * **À vérifier :** le champ `psp_reference` n'est plus renvoyé dans les paiements, les retraits et les webhooks. * Nouveau champ `failure_code` sur les paiements, voir [la liste des codes](/api/payments/create#failure_code). **4 novembre 2026** * **À vérifier :** la commission carte par défaut est de 5,5 %. La [documentation](/payments/overview) indiquait 5 %. * Gestion d'équipe : invitez des membres dans votre boutique et donnez-leur un rôle. * La chronologie d'un paiement ou d'un retrait montre chaque livraison de webhook. * Recherche et filtres sur les listes du tableau de bord. * Les actions sensibles demandent un code de vérification, et un mot de passe connu dans une fuite de données est refusé. **2 novembre 2026** * Les réponses rejouées avec la même `Idempotency-Key` sont protégées contre les requêtes simultanées, et un webhook reçu deux fois n'est traité qu'une fois. * Nouveaux e-mails transactionnels, aux couleurs de votre boutique. **1er novembre 2026** * Vérification d'identité refaite : compte Individuel ou Entreprise, avec un plafond d'encaissement adapté à chacun. **30 octobre 2026** * Nouvelle page **Image de marque** : logo, couleurs, forme des boutons et coordonnées de contact, appliqués au checkout, aux reçus PDF et aux e-mails. **29 octobre 2026** * Liens de paiement refaits : panier, plusieurs tarifs, abonnements, champs personnalisés et image de votre boutique. * La [page de statut](/api/provider-status/get) couvre aussi la plateforme et l'API, avec un taux de disponibilité. **27 octobre 2026** * Les pages publiques ont changé d'adresse. `/checkout/...` devient `/paiement/...` et `/pay/...` devient `/payer/...`. Les anciens liens sont redirigés. * Nouvelle page Tarifs et pages produit pour les paiements, les liens de paiement et les abonnements. **24 octobre 2026** * **À vérifier :** `Idempotency-Key` est obligatoire sur `POST /api/v1/payouts`. Sans cet en-tête, l'API répond `400`. * **À vérifier :** les retraits, les clés API, les webhooks et les paramètres de la boutique sont réservés au propriétaire. * Une URL de webhook vers une adresse privée ou interne est refusée. * Limites de requêtes sur l'inscription, la réinitialisation du mot de passe et les webhooks. * Acceptation des conditions d'utilisation horodatée à l'inscription. **22 octobre 2026** * Premiers abonnements : facturation récurrente par carte et mobile money. * Connexion avec Google ou GitHub. **21 octobre 2026** * Paiement par carte directement depuis l'API, sans session de checkout, avec `POST /api/v1/payments`. * La clé publique live pour la carte n'est fournie qu'après validation de votre identité. **20 octobre 2026** * Nouvel endpoint public [`GET /api/v1/provider-status`](/api/provider-status/get) : la disponibilité réelle de chaque opérateur, à interroger depuis votre site sans clé API. **19 octobre 2026** * Page de statut publique des moyens de paiement, avec 30 jours d'historique. Un opérateur en panne est grisé sur le checkout. * Ajoutez votre logo et votre couleur : ils s'affichent sur le checkout et les liens de paiement. * Reçus PDF envoyés au payeur, à votre nom, à la place du reçu du prestataire carte. * Support : ouvrez une demande depuis le tableau de bord. **18 octobre 2026** * Le checkout propose tous les pays mobile money. Un payeur d'un autre pays paie dans sa monnaie, convertie au taux du jour. Voir [Devises](/payments/amounts-and-currencies). * Option pour répercuter la commission sur votre client. * Soldes en attente et disponibles, et retrait du solde carte. * **À vérifier :** `GET /api/v1/account` ne renvoie que les soldes de l'environnement de la clé utilisée. * E-mails à chaque paiement reçu et à chaque changement de statut d'un retrait. **11 octobre 2026** * Nouveaux [liens de paiement](/api/payment-links/create) : une demande de paiement réutilisable, à partager. * Commission de 3,5 % en mobile money et 5 % par carte. **4 octobre 2026** * Paiement mobile money direct depuis l'API, sans session de checkout. * Commission de 2 % sur les retraits, détaillée dans l'API et le tableau de bord. * Le motif d'un paiement refusé est renvoyé, sans jamais nommer le prestataire. Un code PIN faux est indiqué clairement. **3 octobre 2026** * Mobile money dans tous les pays pris en charge, avec un solde par canal, pays et opérateur. * Retrait depuis le solde de votre choix. * **À vérifier :** les soldes de l'API portent `channel`, `country` et `operator`, et un paiement mobile money porte son pays et son opérateur. * Les montants suivent le nombre de décimales de chaque devise (ISO 4217). Montants minimums carte en USD, EUR et GBP. * Inscription possible depuis n'importe quel pays pris en charge. * Le secret de signature d'un webhook s'affiche une fois, à la création de l'endpoint. **18 juillet 2026** * Inscription des marchands et vérification d'identité. * Bascule entre les environnements sandbox et live. **17 juillet 2026** * Lancement de l'API v1 : sessions de checkout, paiements carte et mobile money, soldes et webhooks signés. * Authentification par `Authorization: Bearer` ou par l'en-tête `X-Api-Key`. # Authentification Source: https://docs.mercy-pay.com/developers/authentication Authentifiez chaque requête avec une clé API sandbox ou live. Toutes les requêtes vers `/api/v1` doivent porter une clé API. Son préfixe seul suffit à identifier l'environnement : `mp_sandbox_9Kd2…` ne déplace jamais d'argent réel, `mp_live_…` oui, et seulement une fois votre KYC approuvé. Ne mettez jamais une clé live côté navigateur. Gardez-la sur votre serveur. ## Envoyer la clé Deux en-têtes sont acceptés, au choix : ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} # En-tête Authorization curl https://mercy-pay.com/api/v1/account \ -H "Authorization: Bearer mp_sandbox_XXXXXXXXXXXXXXXX" # En-tête X-Api-Key curl https://mercy-pay.com/api/v1/account \ -H "X-Api-Key: mp_sandbox_XXXXXXXXXXXXXXXX" ``` ## Erreurs d'authentification | Statut | `error.code` | Message | Cause | | - | - | - | - | | `401` | `api_key_missing` | `Clé API manquante.` | Aucune clé fournie | | `401` | `api_key_invalid` | `Clé API invalide ou révoquée.` | Clé inconnue ou révoquée | | `403` | `merchant_suspended` | `Ce compte marchand est suspendu.` | Marchand suspendu | | `403` | `live_mode_requires_kyc` | `Le mode live nécessite un KYC approuvé…` | Clé live avant approbation du KYC | ## Révoquer une clé Ouvrez l'onglet **Clés API** de la [console Développeurs](/developers/console). L'effet est immédiat : la requête suivante avec cette clé reçoit un `401`. # Console Développeurs Source: https://docs.mercy-pay.com/developers/console Suivez vos requêtes, vos webhooks et vos erreurs, et gérez vos clés API, depuis votre tableau de bord. La console Développeurs est la barre en bas de chaque page de votre tableau de bord. Cliquez dessus pour l'ouvrir. Vous pouvez aussi l'agrandir en plein écran. Elle affiche l'environnement choisi avec le sélecteur **Sandbox / Live** en haut de page : les données des deux environnements ne se mélangent jamais. ![La console Développeurs ouverte sur l'onglet Aperçu](/images/developers/console-overview-light.webp) ![La console Développeurs ouverte sur l'onglet Aperçu](/images/developers/console-overview-dark.webp) ## Les onglets | Onglet | Ce que vous y trouvez | | - | - | | **Aperçu** | Vos requêtes API, vos erreurs, vos événements et le temps de réponse de vos webhooks, sur 24 heures, 7 jours ou 30 jours | | **Webhooks** | Vos endpoints, leur secret et l'historique de leurs livraisons | | **Événements** | Chaque événement émis, avec son contenu exact, même si aucun endpoint ne l'écoute | | **Logs** | Chaque requête reçue par l'API, avec sa réponse | | **Santé** | Vos erreurs regroupées par `code`, pour voir ce qui échoue le plus | | **Clés API** | Vos clés sandbox et live, et votre clé publique carte | ## Gérer un endpoint de webhook ![L'onglet Webhooks : vos destinations, les événements écoutés et leur activité](/images/developers/console-webhooks-light.webp) ![L'onglet Webhooks : vos destinations, les événements écoutés et leur activité](/images/developers/console-webhooks-dark.webp) Depuis l'onglet **Webhooks**, vous pouvez : * ajouter un endpoint, en choisissant ses événements et son environnement ; * réafficher le secret de signature, après avoir confirmé votre mot de passe ; * régénérer le secret. Le nouveau s'applique dès la livraison suivante ; * consulter chaque tentative de livraison et la réponse de votre serveur ; * renvoyer une livraison en échec. ## Déboguer une intégration ![L'onglet Logs : chaque requête reçue par l'API et sa réponse](/images/developers/console-logs-light.webp) ![L'onglet Logs : chaque requête reçue par l'API et sa réponse](/images/developers/console-logs-dark.webp) 1. Ouvrez **Logs** et retrouvez la requête concernée : collez son `Request-Id` (l'en-tête de la réponse, ou `error.request_id`) dans la recherche pour l'ouvrir directement. Vous voyez le statut, le `code` d'erreur et la réponse. 2. Si la requête a réussi, ouvrez **Événements** pour vérifier que l'événement est bien parti. 3. Ouvrez **Webhooks** pour voir ce que votre serveur a répondu, puis renvoyez la livraison une fois le problème corrigé. Les numéros de compte bancaire sont masqués dans les logs et les événements. ## Actions protégées Certaines actions demandent une confirmation : | Action | Confirmation | | - | - | | Créer une clé live, demander un retrait depuis le tableau de bord | Votre code 2FA, ou un code reçu par e-mail si le 2FA n'est pas activé | | Supprimer une clé API ou un webhook | Votre mot de passe ou votre clé d'accès, ou un code reçu par e-mail | | Ajouter un endpoint, réafficher ou régénérer un secret | Votre mot de passe | ## Avant de passer en live L'onglet **Aperçu** affiche ce qui reste à faire avant d'encaisser de vrais paiements : identité vérifiée, un paiement de test réussi, un webhook reçu par votre serveur, la [signature des webhooks](/developers/webhooks/security) vérifiée et une clé API live. Chaque étape se coche d'elle-même, sauf la signature : votre serveur ne nous dit pas s'il la vérifie, cochez-la quand c'est fait. Rien n'est obligatoire : repliez la liste, masquez-la, et elle disparaît d'elle-même une fois tout fait. # Erreurs Source: https://docs.mercy-pay.com/developers/errors Le format des erreurs de l'API, chaque code et comment y répondre. Toutes les erreurs de l'API ont la même forme : un statut HTTP et un objet `error`. ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "error": { "type": "invalid_request_error", "code": "validation_failed", "message": "Le champ amount est obligatoire.", "param": "amount", "doc_url": "https://docs.mercy-pay.com/developers/errors#validation_failed", "request_id": "req_01jc8x4m2v7t9qk3n5w6y8z0ab", "errors": { "amount": [ "Le champ amount est obligatoire." ] } } } ``` | Champ | Contenu | | - | - | | `type` | La famille de l'erreur, voir plus bas | | `code` | Un code stable : c'est lui que votre code doit tester | | `message` | Une phrase en français pour un humain. Son texte peut changer : ne le comparez jamais | | `param` | Le premier champ en cause, ou `null` | | `doc_url` | Le lien vers l'explication de ce code sur cette page | | `request_id` | L'identifiant de la requête, à nous donner si vous contactez le support | Sur une erreur `422`, `errors` liste chaque champ refusé avec ses messages. ## Identifiant de requête Chaque réponse, réussie ou non, porte un en-tête `Request-Id` (`req_…`), et chaque erreur le répète dans `error.request_id`. Gardez-le dans vos journaux : il désigne l'appel exact, et le support retrouve la requête avec lui. ## Types d'erreur | `type` | Quand | | - | - | | `authentication_error` | Clé absente, invalide, compte suspendu ou KYC manquant | | `invalid_request_error` | Requête ou données invalides, objet introuvable ou état incompatible | | `idempotency_error` | Clé d'idempotence absente, en cours ou réutilisée | | `payment_error` | Paiement refusé | | `rate_limit_error` | Trop de requêtes | | `api_error` | Erreur de notre côté | ## Codes Cette liste peut s'enrichir, mais un code existant ne change jamais de nom. Traitez un code inconnu comme une erreur générique de son `type`. ### `api_key_missing` `401` · `authentication_error` Aucune clé n'accompagne la requête. Ajoutez l'en-tête `Authorization: Bearer mp_sandbox_…` (ou `mp_live_…`). Voir [Authentification](/developers/authentication). ### `api_key_invalid` `401` · `authentication_error` La clé est inconnue ou a été révoquée. Copiez une clé active depuis la console développeurs. Vérifiez qu'elle n'a pas d'espace en trop. ### `merchant_suspended` `403` · `authentication_error` Le compte marchand est suspendu. Contactez le support : aucune requête n'aboutit tant que le compte reste suspendu. ### `live_mode_requires_kyc` `403` · `authentication_error` Une clé live est utilisée avant l'approbation de votre vérification d'identité. Terminez votre KYC. En attendant, travaillez avec une clé sandbox. ### `forbidden` `403` · `authentication_error` La clé ne permet pas cette action. Vérifiez l'environnement de la clé et la ressource visée. ### `bad_request` `400` · `invalid_request_error` La requête est mal formée, par exemple un JSON invalide. Envoyez un corps JSON valide avec `Content-Type: application/json`. ### `idempotency_key_missing` `400` · `idempotency_error` `Idempotency-Key` manque sur une opération qui l'exige (paiement direct, retrait). Générez une clé unique par opération et envoyez-la dans l'en-tête. Voir [Idempotence](/developers/idempotency). ### `resource_not_found` `404` · `invalid_request_error` L'objet n'existe pas, ou appartient à un autre marchand ou à l'autre environnement. Vérifiez l'id et l'environnement de la clé : un objet sandbox n'est pas visible avec une clé live. ### `method_not_allowed` `405` · `invalid_request_error` La méthode HTTP n'est pas acceptée sur cette URL. Consultez la référence de l'endpoint pour la bonne méthode. ### `conflict` `409` · `invalid_request_error` L'action contredit l'état de l'objet : un client avec cet e-mail existe déjà, une session est déjà payée… Lisez `message` : il dit quoi faire. Pour un client en double, l'id existant est renvoyé dans `error.customer`. ### `idempotency_key_in_use` `409` · `idempotency_error` Une requête avec la même clé d'idempotence est encore en cours. Attendez quelques secondes puis rejouez la même requête avec la même clé. ### `idempotency_key_reused` `409` · `idempotency_error` La clé d'idempotence a déjà servi pour une requête au contenu différent. Utilisez une nouvelle clé pour une nouvelle opération. ### `validation_failed` `422` · `invalid_request_error` Un ou plusieurs champs sont invalides. `error.param` nomme le premier champ en cause et `errors` détaille chacun. ### `insufficient_balance` `422` · `invalid_request_error` Votre solde disponible, retraits déjà réservés déduits, ne couvre pas le remboursement et ses frais. Attendez de nouveaux encaissements, ou annulez un retrait en attente, puis réessayez. ### `refund_not_allowed` `422` · `invalid_request_error` Ce paiement ne peut pas être remboursé : il n'a pas abouti, il date de plus de 90 jours, ses fonds carte ne sont pas encore disponibles, ou l'opérateur du client refuse les remboursements pour le moment. Le message dit laquelle de ces raisons s'applique. ### `payment_declined` `422` · `payment_error` Le paiement a été refusé dès sa création. Proposez au payeur un autre numéro ou un autre moyen de paiement. ### `rate_limited` `429` · `rate_limit_error` Trop de requêtes sur une courte période. Attendez le délai de l'en-tête `Retry-After`. Voir [Limites de débit](/developers/rate-limits). ### `server_error` `500` · `api_error` Une erreur s'est produite de notre côté. Réessayez avec la même `Idempotency-Key` : l'opération ne sera jamais faite deux fois. Si l'erreur persiste, donnez-nous le `request_id`. ## Motif d'échec d'un paiement ou d'un retrait Un paiement ou un retrait échoué n'est pas une erreur de requête : il porte un `failure_code` et un `failure_reason` en français. | `failure_code` | Signification | | - | - | | `insufficient_balance` | Le solde du payeur ne couvre pas le montant | | `payment_not_approved` | Le payeur a refusé ou n'a pas validé la demande sur son téléphone | | `payer_not_found` | Le numéro n'est pas associé à l'opérateur choisi | | `payer_limit_reached` | Le payeur a atteint la limite fixée par son opérateur | | `transaction_expired` | Le payeur n'a pas confirmé à temps | | `customer_not_eligible` | Le payeur n'a pas accès à ce type de paiement | | `invalid_phone_number` | Le numéro n'est pas valide pour l'opérateur | | `amount_out_of_bounds` | Le montant sort des limites de l'opérateur | | `provider_temporarily_unavailable` | L'opérateur est momentanément indisponible | | `recipient_not_found` | Retrait : le numéro du bénéficiaire n'est pas reconnu | | `payouts_not_allowed` | Retrait : les retraits ne sont pas activés vers cet opérateur | | `unspecified_failure` | Refus sans motif précis | ## Erreurs métier fréquentes | Message | Cause | | - | - | | `Solde disponible insuffisant pour ce montant.` | Le solde du pays et de l'opérateur ne couvre pas le retrait | | `Opérateur Mobile Money invalide pour ce pays.` | Code opérateur incohérent avec le pays | | `Cette devise ne figure pas dans la norme ISO 4217.` | Code devise inconnu | | `Ce moyen de paiement n'est pas disponible pour le moment.` | Aucun taux de conversion disponible vers la devise de l'opérateur mobile money choisi | | `Le montant minimum pour un paiement par carte est de 0.50 USD.` | Montant sous le minimum carte | # Objets liés Source: https://docs.mercy-pay.com/developers/expand Recevez un objet lié en entier plutôt que son id, en une seule requête. Un objet renvoie l'id des objets qui lui sont liés. Avec `expand`, vous recevez l'objet lié en entier, sans seconde requête. ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://mercy-pay.com/api/v1/payments/pay_9d2c4a7e1b3f4c5d8e6f7a0b1c2d3e4f?expand[]=checkout_session" \ -H "Authorization: Bearer mp_sandbox_XXXXXXXXXXXXXXXX" ``` Sans `expand`, `checkout_session` vaut `"cs_01JC8X4M2V7T9QK3N5W6Y8Z0AB"`. Avec, il contient la session complète, avec son `object` à `checkout_session`. Vous pouvez demander plusieurs objets, en répétant `expand[]` ou en les séparant par des virgules : `?expand=subscription,payment`. ## Ce qui peut être déplié | Objet | `expand` | | - | - | | Paiement | `checkout_session` | | Facture | `subscription`, `payment` | | Abonnement | `customer` | Un seul niveau : un objet déplié ne déplie pas ses propres liens. # Idempotence Source: https://docs.mercy-pay.com/developers/idempotency Rejouez une création sans créer de doublon, grâce à l'en-tête Idempotency-Key. Après un timeout, vous ne savez pas si Mercy Pay a traité votre requête. La renvoyer telle quelle pourrait créer un doublon (deux paiements, deux retraits). Avec un en-tête `Idempotency-Key` unique par opération, une même clé ne crée jamais deux fois. ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} curl https://mercy-pay.com/api/v1/payouts \ -H "Authorization: Bearer mp_live_XXXXXXXXXXXXXXXX" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: payout-2026-10-03-001" \ -d '{"amount": 4000, "currency": "XAF", "destination": {"type": "mobile_money", "country": "CMR", "provider": "MTN_MOMO_CMR", "phone": "237680000000"}}' ``` ## Une clé par opération, jamais par tentative La clé identifie l'opération (cette commande, ce retrait), pas la requête HTTP. Générez-la **une seule fois**, au moment où vous décidez de créer le retrait (ou la charge, ou la session), par exemple à partir de l'identifiant de commande déjà présent dans votre propre système. Si l'appel échoue ou time-out, vous **renvoyez la requête à l'identique, avec la même clé**, autant de fois que nécessaire : vous ne générez jamais une nouvelle clé pour « réessayer ». ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} 1. Votre code décide de créer le retrait pour la commande #4521. 2. Il appelle l'API avec Idempotency-Key: commande-4521. 3. Le réseau coupe avant que vous ne receviez la réponse, succès ou échec : vous ne savez pas. 4. Vous rappelez l'API avec EXACTEMENT la même requête et la même clé commande-4521. → Si le premier appel avait réussi : Mercy Pay renvoie la réponse d'origine, rien n'est recréé. → Si le premier appel n'était jamais arrivé : Mercy Pay le traite pour de vrai, cette fois. ``` Dans les deux cas, votre code peut traiter la réponse exactement de la même façon : il n'a pas besoin de savoir si c'était le premier essai ou une relance. > [!WARNING] > > Générer une clé différente à chaque tentative (par exemple un `uuid()` appelé au moment de l'envoi plutôt que mémorisé avec la commande) annule complètement la protection : chaque relance est alors vue comme une opération différente, et un double paiement redevient possible. ## Comportement | Situation | Réponse | | - | - | | Première requête | Création, `201` | | Même clé, même contenu | Réponse d'origine rejouée, `200`, sans nouvelle création | | Même clé, contenu différent | `409`, `error.code` : `idempotency_key_reused` | | Même clé, première requête encore en cours | `409`, `error.code` : `idempotency_key_in_use`. Patientez quelques secondes et renvoyez. | | Clé absente sur une charge directe ou un retrait | `400`, `error.code` : `idempotency_key_missing` | Les clés sont conservées **24 heures**. Au-delà, la même clé n'est plus reconnue et recrée une ressource. ## Conseils * Générez une clé par opération logique (une commande, un retrait, une charge directe), pas une par tentative réseau. * Ne réutilisez jamais une clé pour une autre opération, même après un échec : son rôle est justement d'identifier une seule opération sans ambiguïté. * `Idempotency-Key` est accepté sur toutes les créations : sessions de checkout, charges directes, retraits, liens de paiement, plans et abonnements. Il est **obligatoire** pour une charge directe (`POST /api/v1/payments`) et pour un retrait (`POST /api/v1/payouts`) : la requête est rejetée sans lui. Pour les autres, il reste recommandé mais optionnel. # Metadata Source: https://docs.mercy-pay.com/developers/metadata Attachez vos propres données à un objet, comme un numéro de commande. `metadata` garde vos propres repères sur un objet : un numéro de commande, l'id d'un client dans votre système. Mercy Pay les renvoie tels quels dans les réponses et dans les webhooks. ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} curl https://mercy-pay.com/api/v1/checkout_sessions \ -H "Authorization: Bearer mp_sandbox_XXXXXXXXXXXXXXXX" \ -H "Content-Type: application/json" \ -d '{ "amount": 15000, "currency": "XAF", "metadata": { "order_id": "CMD-2041", "store": "Douala Akwa" } }' ``` ## Où les utiliser `metadata` est accepté sur les sessions de checkout, les paiements, les retraits, les liens de paiement, les plans, les abonnements et les clients. Deux objets reprennent ceux d'un autre : * un paiement reprend la `metadata` de sa session de checkout. Le webhook `payment.succeeded` vous dit donc directement quelle commande est payée ; * une facture reprend celle de son abonnement. ## Limites * 50 clés au plus ; * une clé fait 40 caractères au plus ; * une valeur est un texte, un nombre ou un booléen de 500 caractères au plus. Un objet ou une liste est refusé. Ne mettez jamais de donnée sensible dans `metadata` : ni mot de passe, ni numéro de carte. ## Modifier Sur un client, `PATCH` fusionne clé par clé : une clé envoyée à `null` est retirée, les autres restent. `"metadata": null` retire toutes les clés. ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X PATCH https://mercy-pay.com/api/v1/customers/cus_c71d0e5a3b2f4e8a9c1d5f6a7b8c9d01 \ -H "Authorization: Bearer mp_sandbox_XXXXXXXXXXXXXXXX" \ -H "Content-Type: application/json" \ -d '{ "metadata": { "segment": null, "city": "Douala" } }' ``` # Pagination Source: https://docs.mercy-pay.com/developers/pagination Parcourez une liste avec un curseur, sans jamais sauter ni répéter un objet. Toutes les listes de l'API répondent de la même façon, du plus récent au plus ancien. ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://mercy-pay.com/api/v1/payments?limit=2" \ -H "Authorization: Bearer mp_sandbox_XXXXXXXXXXXXXXXX" ``` ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "object": "list", "url": "/api/v1/payments", "has_more": true, "next_cursor": "pay_5b0e8c1a2f4d4e6a9b7c3d2e1f0a9b8c", "data": [ { "id": "pay_9d2c4a7e1b3f4c5d8e6f7a0b1c2d3e4f", "object": "payment", "amount": 15000, "currency": "XAF", "status": "succeeded" }, { "id": "pay_5b0e8c1a2f4d4e6a9b7c3d2e1f0a9b8c", "object": "payment", "amount": 4500, "currency": "XAF", "status": "failed" } ] } ``` Les objets de `data` sont abrégés ici : chaque liste renvoie les objets complets. ## Paramètres | Paramètre | Effet | | - | - | | `limit` | Nombre d'objets, de 1 à 100. 20 par défaut | | `starting_after` | Un id : la page qui suit cet objet. Passez le `next_cursor` de la page précédente | | `ending_before` | Un id : la page qui précède cet objet, pour revenir en arrière | | `created[gte]` | Objets créés à partir de cette date (ISO 8601 ou horodatage Unix) | | `created[lte]` | Objets créés jusqu'à cette date | Pensez à encoder le `+` d'un fuseau horaire dans l'URL (`%2B`). Un `+` non encodé est tout de même compris. ## Tout parcourir Avancez tant que `has_more` vaut `true`, en passant `next_cursor` dans `starting_after` : ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://mercy-pay.com/api/v1/payments?limit=100&starting_after=pay_5b0e8c1a2f4d4e6a9b7c3d2e1f0a9b8c" \ -H "Authorization: Bearer mp_sandbox_XXXXXXXXXXXXXXXX" ``` Contrairement à des numéros de page, un curseur ne saute ni ne répète jamais un objet quand de nouveaux paiements arrivent pendant que vous parcourez la liste. Un curseur qui ne désigne aucun objet de la liste renvoie une erreur `422` avec `error.param` à `starting_after` ou `ending_before`. # Limites de débit Source: https://docs.mercy-pay.com/developers/rate-limits Nombre de requêtes autorisées par minute, par clé API. Chaque clé API a deux compteurs séparés, par minute : | Requêtes | Limite | | - | - | | Lectures : `GET` et [`POST /api/v1/fees/quote`](/api/fees/quote) | **300 par minute** | | Écritures : toutes les autres `POST`, `PATCH` et `DELETE` | **60 par minute** | Au-delà, l'API répond `429 Too Many Requests`. Une lecture ne consomme jamais votre quota d'écriture. Les compteurs se réinitialisent chaque minute. Cette limite ne concerne que ce que vous envoyez. Les webhooks que vous recevez ne la consomment pas. ## En-têtes de réponse Chaque réponse indique votre quota courant : | En-tête | Description | | - | - | | `X-RateLimit-Limit` | Le plafond du compteur concerné : `300` pour une lecture, `60` pour une écriture | | `X-RateLimit-Remaining` | Requêtes restantes sur la minute en cours | Une réponse `429` ajoute en plus : | En-tête | Description | | - | - | | `Retry-After` | Secondes à attendre avant de réessayer | | `X-RateLimit-Reset` | Timestamp Unix de la réinitialisation du quota | ## Bonnes pratiques * Évitez de sonder `GET /api/v1/payments/{id}` en boucle : écoutez plutôt les webhooks, voir [Webhooks](/developers/webhooks/overview). * Étalez les envois en masse sur plusieurs minutes. # Politique de versions Source: https://docs.mercy-pay.com/developers/versioning Ce qui peut changer dans l'API v1, ce qui ne change jamais, et comment nous vous prévenons. L'API `v1` est figée depuis le 14 novembre 2026. Une intégration qui fonctionne aujourd'hui continue de fonctionner sans modification. ## Ce qui ne change jamais en v1 * Les chemins des endpoints et leurs méthodes. * Le nom, le type et le sens d'un champ existant. * Les valeurs d'un champ à liste fermée, comme `status` d'un paiement ou d'un retrait. Aucune nouvelle valeur n'y apparaît sans préavis. * Le format des erreurs (`error.code`, `error.message`, `error.doc_url`) et les codes d'erreur existants. * La signature des webhooks (en-tête `MercyPay-Signature`) et la forme des événements existants. * Le format des identifiants (`pay_…`, `cs_…`, `po_…`). ## Ce qui peut arriver à tout moment Ces ajouts ne cassent pas une intégration bien écrite. Votre code doit les accepter sans erreur : * un nouvel endpoint ; * un nouveau champ dans une réponse ou dans un événement ; * un nouveau paramètre optionnel dans une requête ; * un nouveau type d'événement webhook. Un endpoint ne le reçoit que si vous l'ajoutez à sa liste d'événements ; * un nouveau code d'erreur pour un cas qui échouait déjà ; * une limite de débit plus large. > [!TIP] > > Ignorez les champs que vous ne connaissez pas au lieu de les refuser, et prévoyez une branche par défaut quand vous lisez un type d'événement. ## Quand un changement cassant est nécessaire Cela reste exceptionnel, par exemple pour corriger une faille de sécurité. Dans ce cas : 1. Nous l'annonçons au moins **90 jours** avant, dans le [journal des changements](/changelog), avec la mention **À vérifier**. 2. Nous écrivons au contact technique de votre boutique. Renseignez-le dans **Réglages**, rubrique **Informations de l'entreprise**. Sans contact technique, nous écrivons au propriétaire. 3. Pendant ces 90 jours, l'ancien et le nouveau comportement coexistent. Seule exception à ce délai : une faille de sécurité grave, corrigée tout de suite et annoncée le jour même. # Événements Source: https://docs.mercy-pay.com/developers/webhooks/events Les événements envoyés aux marchands, avec leur contenu. Vous choisissez les événements que reçoit chaque endpoint. | Événement | Déclencheur | | - | - | | `payment.succeeded` | Un paiement est encaissé. Le solde est crédité. | | `payment.failed` | Un paiement est refusé, annulé ou expiré (une carte non terminée expire après 24 heures). Le motif est dans `failure_code`/`failure_reason`. Pour une carte, un `payment.succeeded` peut suivre si le client réessaie. | | `payment.refunded` | Un paiement est entièrement remboursé. Son statut passe à `refunded`. | | `refund.succeeded` | Un remboursement est arrivé chez le client. | | `refund.failed` | Un remboursement n'a pas pu être envoyé. Le montant et les frais sont revenus sur votre solde, le motif est dans `failure_reason`. | | `refund.canceled` | Un remboursement retardé par un opérateur indisponible a été annulé. Le montant et les frais sont revenus sur votre solde. | | `payout.paid` | Un retrait est versé sur le compte du bénéficiaire. | | `payout.failed` | Un retrait échoue ou est refusé. Le motif est dans `failure_code` et `failure_reason`. | | `payout.canceled` | Un retrait a été annulé : avant son examen, ou pendant qu'il attendait un opérateur indisponible. Aucun argent n'a bougé. | | `checkout_session.completed` | Une session de checkout est payée. Porte le lien de paiement d'origine (`payment_link`), le téléphone et l'adresse du payeur s'ils ont été demandés, et les réponses aux champs personnalisés (`custom_fields`). | | `provider_status.changed` | Un opérateur mobile money tombe en panne, se dégrade ou revient (`provider`, `country`, `status`, `previous_status`). Envoyé seulement aux endpoints qui l'ont coché, pour griser un opérateur sur votre site sans interroger `GET /api/v1/provider_status`. | | `subscription.created` | Un abonnement démarre : premier paiement réussi, ou moyen de paiement enregistré pour un essai. Jamais pour une inscription pas encore payée. | | `subscription.updated` | Un changement de plan est prévu ou retiré, une annulation en fin de période est prévue ou retirée, un abonnement en retard redevient à jour, ou ses informations changent. | | `subscription.past_due` | Une échéance carte n'a pas pu être encaissée. De nouvelles tentatives sont programmées. | | `subscription.unpaid` | Une échéance mobile money est passée sans paiement. Coupez l'accès à votre service : les relances continuent jusqu'à J+7, et `subscription.updated` arrive quand le client paie. | | `subscription.paused` | Un abonnement est mis en pause. | | `subscription.resumed` | Un abonnement en pause reprend. | | `subscription.canceled` | Un abonnement s'arrête : annulation, dernière échéance d'un `max_cycles` atteinte, ou échec de toutes les tentatives. | | `invoice.created` | Une facture est créée : à l'échéance d'un abonnement carte, ou 2 jours avant en mobile money. | | `invoice.upcoming` | Une échéance arrive dans 3 jours. `data` est l'abonnement, avec `next_billing_at`. | | `invoice.paid` | Une facture est payée, par carte ou par mobile money. | | `invoice.canceled` | Le marchand a annulé une facture impayée. Elle ne sera plus relancée, l'abonnement continue. | | `invoice.payment_failed` | Une tentative d'encaissement échoue. Le motif est dans `failure_reason`, la prochaine tentative dans `next_attempt_at`. | | `webhook.test` | Envoyé à la demande, depuis la console ou l'API, pour vérifier votre endpoint. Toujours reçu, même s'il n'est pas coché. `data` contient seulement un `message`. | ## Enveloppe Chaque événement suit la même structure : | Champ | Description | | - | - | | `id` | Identifiant unique, de la forme `evt_…`. Un même événement garde le même `id` sur tous vos endpoints et à chaque nouvel essai. Sert à dédupliquer. | | `object` | Toujours `event`. | | `type` | Nom de l'événement, par exemple `payout.paid`. | | `created` | Date de l'événement, au format ISO 8601. | | `livemode` | `true` en live, `false` en sandbox. | | `data` | Ressource concernée. | ## Exemple `payout.paid` ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "id": "evt_01J9Z6P0E3VW8KZ2RMQ4YTB7XN", "object": "event", "type": "payout.paid", "created": "2026-10-03T10:12:00+00:00", "livemode": true, "data": { "id": "po_a9d4e6f21b3c4e5d8f702c9b6a1e3d48", "object": "payout", "livemode": true, "created": "2026-10-03T10:12:00+00:00", "amount": 4000, "fee_amount": 80, "net_amount": 3920, "currency": "XAF", "destination": { "type": "mobile_money", "country": "CMR", "provider": "MTN_MOMO_CMR", "phone": "237680000000" }, "status": "paid", "failure_code": null, "failure_reason": null, "metadata": null } } ``` ## Exemple `payment.failed` ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "id": "evt_01J9Z6Q1F4WX9L03SNR5ZUC8YP", "object": "event", "type": "payment.failed", "created": "2026-10-04T10:20:00+00:00", "livemode": true, "data": { "id": "pay_5e0c8a7b3d1f4a2e9c648b7f0d2e1a93", "object": "payment", "livemode": true, "created": "2026-10-04T10:20:00+00:00", "checkout_session": null, "amount": 1000, "fee_amount": 50, "net_amount": 950, "currency": "XAF", "payment_method": "mobile_money", "mobile_money_country": "CMR", "mobile_money_provider": "MTN_MOMO_CMR", "status": "failed", "failure_code": "insufficient_balance", "failure_reason": "Le solde du payeur est insuffisant pour effectuer ce paiement.", "metadata": null, "next_action": null } } ``` Testez toujours `failure_code` dans votre code, jamais `failure_reason` : c'est un texte en français fait pour être affiché, pas comparé. Voir [la liste complète des codes](/api/payments/create#failure_code). Les champs de `data` sont décrits dans : * [Paiements](/api/payments/create) pour `payment.*` ; * [Retraits](/api/payouts/create) pour `payout.*` ; * [Sessions de checkout](/api/checkout-sessions/create) pour `checkout_session.completed` ; * [Abonnements](/api/subscriptions/create) pour `subscription.*` et `invoice.upcoming` ; * [Factures](/api/invoices/list) pour `invoice.created`, `invoice.paid`, `invoice.canceled` et `invoice.payment_failed`. # Gérer les webhooks Source: https://docs.mercy-pay.com/developers/webhooks/handle Recevez les événements de paiement et de retrait, vérifiez leur signature et traitez-les une seule fois. Mercy Pay envoie un événement à votre serveur à chaque changement de statut d'un paiement ou d'un retrait. Fiez-vous à cet événement pour confirmer un encaissement, pas à la redirection du navigateur. **1. Déclarez l'endpoint** Dans l'onglet **Webhooks** de la [console Développeurs](/developers/console), ajoutez une URL HTTPS publique et cochez les événements voulus. Mercy Pay affiche alors le **secret de signature** (`whsec_…`). Copiez-le dans vos secrets. Vous pourrez le réafficher plus tard depuis la console, après avoir confirmé votre mot de passe. **2. Vérifiez la signature** Chaque requête porte l'en-tête `MercyPay-Signature`, de la forme `t=1791022800,v1=5257a869…`. `t` est l'heure d'envoi, `v1` le HMAC-SHA256 en hexadécimal de `{t}.{corps brut}`, calculé avec votre secret. Refusez une signature de plus de 5 minutes : elle protège contre le rejeu d'une ancienne requête. Voir [Sécurité des webhooks](/developers/webhooks/security). ```js JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} import crypto from 'node:crypto'; const TOLERANCE_SECONDS = 300; export function isAuthentic(rawBody, signatureHeader, secret) { const parts = (signatureHeader ?? '').split(',').map((part) => part.split('=')); const timestamp = Number(parts.find(([key]) => key === 't')?.[1]); const signatures = parts.filter(([key]) => key === 'v1').map(([, value]) => value); if (!timestamp || Math.abs(Date.now() / 1000 - timestamp) > TOLERANCE_SECONDS) { return false; } const expected = crypto .createHmac('sha256', secret) .update(`${timestamp}.${rawBody}`) .digest('hex'); return signatures.some( (signature) => signature.length === expected.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature)), ); } ``` ```php PHP theme={"theme":{"light":"github-light","dark":"github-dark"}} function isAuthentic(string $rawBody, ?string $signatureHeader, string $secret): bool { $timestamp = null; $signatures = []; foreach (explode(',', $signatureHeader ?? '') as $part) { [$key, $value] = array_pad(explode('=', trim($part), 2), 2, ''); if ($key === 't') { $timestamp = (int) $value; } elseif ($key === 'v1') { $signatures[] = $value; } } if (! $timestamp || abs(time() - $timestamp) > 300) { return false; } $expected = hash_hmac('sha256', $timestamp.'.'.$rawBody, $secret); foreach ($signatures as $signature) { if (hash_equals($expected, $signature)) { return true; } } return false; } ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import hashlib import hmac import time def is_authentic(raw_body: bytes, signature_header: str | None, secret: str) -> bool: timestamp = None signatures = [] for part in (signature_header or '').split(','): key, _, value = part.strip().partition('=') if key == 't' and value.isdigit(): timestamp = int(value) elif key == 'v1': signatures.append(value) if timestamp is None or abs(time.time() - timestamp) > 300: return False signed = f'{timestamp}.'.encode() + raw_body expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest() return any(hmac.compare_digest(expected, signature) for signature in signatures) ``` Vérifiez le corps tel qu'il est reçu, avant tout `JSON.parse`. **3. Répondez vite, traitez après** Répondez `2xx` dès que la signature est validée, avant de lancer un traitement lourd. Mercy Pay n'attend que 10 secondes. Passé ce délai, ou sur une réponse qui n'est pas `2xx`, la livraison est considérée en échec et reprise plus tard. | Tentative | Moment | | - | - | | 1 | immédiate | | 2 | 1 minute après | | 3 | 10 minutes après | | 4 | 1 heure après | | 5 | 3 heures après | | 6 | 8 heures après | | 7 | 1 jour après | | 8 | 3 jours après | Si votre serveur n'a accepté aucun événement pendant ces 3 jours, l'endpoint est désactivé et les développeurs de votre boutique reçoivent un e-mail. Corrigez le serveur, réactivez l'endpoint, puis cliquez sur **Relancer les échecs** dans la console. Les événements restent consultables avec [`GET /api/v1/events`](/api/events/list). **4. Dédupliquez par identifiant** Une même livraison peut arriver deux fois, par exemple si le réseau coupe juste après votre `2xx`. Chaque événement porte un `id` (`evt_…`), répété dans l'en-tête `MercyPay-Event-Id` : enregistrez ceux que vous avez déjà traités et ignorez les doublons. ## Exemple d'événement ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "id": "evt_01J9Z4K2M8TQ5VX3WB7NRY6HDA", "object": "event", "type": "payment.succeeded", "created": "2026-10-03T10:20:00+00:00", "livemode": true, "data": { "id": "pay_5e0c8a7b3d1f4a2e9c648b7f0d2e1a93", "object": "payment", "livemode": true, "created": "2026-10-03T10:20:00+00:00", "checkout_session": "cs_01J9Z3V8QK4M2XW7R6TB5N0HYD", "amount": 15000, "fee_amount": 750, "net_amount": 14250, "currency": "XAF", "payment_method": "mobile_money", "mobile_money_country": "CMR", "mobile_money_provider": "MTN_MOMO_CMR", "status": "succeeded", "failure_code": null, "failure_reason": null, "metadata": null, "next_action": null } } ``` # Webhooks Source: https://docs.mercy-pay.com/developers/webhooks/overview Mercy Pay notifie votre serveur à chaque changement de statut d'un paiement ou d'un retrait. Un **webhook** est une requête `POST` que Mercy Pay envoie à une URL de votre serveur dès qu'un statut change. Vous n'avez donc pas besoin d'interroger l'API en boucle. ## Configurer un endpoint Ouvrez l'onglet **Webhooks** de la [console Développeurs](/developers/console) : 1. Saisissez une URL HTTPS publique qui accepte les requêtes `POST`. 2. Choisissez l'environnement écouté : **Sandbox**, **Live** ou les deux. 3. Cochez les événements à recevoir. 4. Copiez le **secret de signature** (`whsec_…`). Vous pouvez déclarer plusieurs endpoints. Chacun a son propre secret et ses propres événements. ## Format d'une livraison ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} POST /webhooks/mercy-pay HTTP/1.1 Content-Type: application/json MercyPay-Signature: t=1791022800,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd MercyPay-Event-Id: evt_01JC9A2B3C4D5E6F7G8H9J0K1M MercyPay-Event-Type: payment.succeeded MercyPay-Delivery-Id: whd_6d1f3a8c2e5b4d7a9c0e2f4b6a8d1c3e ``` Le corps est un événement : `id`, `object`, `type`, `created`, `livemode` et `data`. Le contenu de `data` reprend la ressource correspondante : un paiement pour `payment.*`, un retrait pour `payout.*`, une session pour `checkout_session.*`, un abonnement pour `subscription.*` et `invoice.upcoming`, une facture pour les autres `invoice.*`. Voir [Événements](/developers/webhooks/events). ## Fiabilité * Mercy Pay attend une réponse `2xx` dans les 10 secondes. * Sinon, Mercy Pay réessaie, jusqu'à 8 tentatives sur 3 jours. * Un endpoint qui n'a accepté aucun événement pendant 3 jours est désactivé, et vos développeurs sont prévenus par e-mail. * Une livraison en échec peut être renvoyée depuis la console, une par une ou toutes celles des 3 derniers jours, ou avec [`POST /api/v1/events/{id}/resend`](/api/events/resend). * **Envoyer un test** dans la console, ou [`POST /api/v1/webhook_endpoints/{id}/test`](/api/webhook-endpoints/test), envoie un événement `webhook.test` pour vérifier votre serveur. * Un même événement peut être livré plusieurs fois : dédupliquez-le avec son `id`. Pour la mise en œuvre pas à pas, voir [Gérer les webhooks](/developers/webhooks/handle). # Sécurité des webhooks Source: https://docs.mercy-pay.com/developers/webhooks/security Vérifiez la signature de chaque requête avant de la traiter. Une URL de webhook est publique : n'importe qui peut lui envoyer une fausse requête. Vérifiez donc la signature avant de traiter un événement. ## Principe Chaque requête porte l'en-tête `MercyPay-Signature` : ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} MercyPay-Signature: t=1791022800,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd ``` * `t` est l'heure d'envoi, en secondes depuis 1970 ; * `v1` est le **HMAC-SHA256** en hexadécimal de la chaîne `{t}.{corps brut}`, calculé avec le secret de l'endpoint. Pour vérifier : 1. Lisez le corps **tel qu'il est reçu**, en octets, sans le parser. 2. Refusez la requête si `t` a plus de 5 minutes d'écart avec votre horloge : une requête ancienne rejouée est ainsi rejetée. 3. Calculez le HMAC-SHA256 de `{t}.{corps brut}` avec votre secret. 4. Comparez-le à temps constant avec chaque valeur `v1` de l'en-tête. Une seule doit correspondre. ## Exemple en PHP ```php theme={"theme":{"light":"github-light","dark":"github-dark"}} hash_equals($expected, $signature)) !== []; if (! $valid) { http_response_code(401); exit; } $event = json_decode($rawBody, true); ``` Le même contrôle en JavaScript et en Python est dans [Gérer les webhooks](/developers/webhooks/handle). ## Autres en-têtes | En-tête | Contenu | | - | - | | `MercyPay-Event-Id` | L'`id` de l'événement (`evt_…`), pour dédupliquer | | `MercyPay-Event-Type` | Son type, par exemple `payment.succeeded` | | `MercyPay-Delivery-Id` | L'identifiant de cette livraison (`whd_…`), le même à chaque nouvelle tentative | ## Changer de secret Régénérez le secret depuis l'onglet **Webhooks** de la [console Développeurs](/developers/console), ou avec [`POST /api/v1/webhook_endpoints/{id}/rotate_secret`](/api/webhook-endpoints/rotate-secret). Pendant 24 heures, chaque webhook porte deux `v1` : un calculé avec le nouveau secret, un avec l'ancien. Votre serveur continue donc d'accepter les webhooks, quel que soit le secret qu'il utilise, le temps de déployer le nouveau. ## Bonnes pratiques * Gardez le secret dans votre gestionnaire de secrets, jamais dans le code. * Refusez tout événement dont la signature ne correspond pas. * Gardez votre horloge à l'heure (NTP) : la tolérance est de 5 minutes. * Ne vous fiez pas à l'ordre d'arrivée : utilisez le `status` de la ressource. * Si un secret fuite, régénérez-le sans attendre. # Bienvenue sur Mercy Pay Source: https://docs.mercy-pay.com/index Encaissez par mobile money et par carte, facturez en récurrent et retirez vos fonds. Avec ou sans une ligne de code. Mercy Pay encaisse vos clients en mobile money dans 13 pays africains, et par carte, Apple Pay ou Google Pay partout dans le monde. Chaque produit se pilote depuis votre tableau de bord, et tout ce qu'il fait existe aussi dans l'API. ![Le tableau de bord Mercy Pay : volume encaissé, derniers paiements et soldes](/images/start/dashboard-light.webp) ![Le tableau de bord Mercy Pay : volume encaissé, derniers paiements et soldes](/images/start/dashboard-dark.webp) ## Vos clients paient comme ils veulent Mobile money, carte, Apple Pay ou Google Pay, depuis le pays de leur choix : le montant est converti pour eux, sur une page à vos couleurs, aussi belle sur téléphone que sur ordinateur. Après le paiement, ils reçoivent un reçu à votre marque. 15 000 FCFA payés depuis le Ghana : 375,00 GHS débités, conversion comprise. > [card] [La page de paiement](/payments/checkout-page) > > Mobile money, carte, confirmation : chaque écran. > [card] [La page d'inscription](/subscriptions/subscribe-page) > > L'abonnement s'active dès le premier paiement. ## Choisissez votre produit > [card] [Paiements](/payments/overview) > > Une page de paiement hébergée, ou l'encaissement direct depuis votre app. Mobile money et carte. > [card] [Liens de paiement](/payment-links/overview) > > Un lien à partager sur WhatsApp ou Instagram. Prix fixe, montant libre, tarifs ou panier. > [card] [Abonnements](/subscriptions/overview) > > Facturez chaque semaine, mois ou année. Relances et espace client compris. > [card] [Soldes et retraits](/payouts/overview) > > Un solde par pays et par opérateur, et des retraits vers le mobile money ou la banque. ## Par où commencer > [card] [Je n'écris pas de code](/payment-links/create-in-dashboard) > > Créez un lien de paiement ou un abonnement depuis le tableau de bord et partagez-le. Chaque section « Sans code » montre les écrans pas à pas. > [card] [Je suis développeur](/start/quickstart) > > Une clé sandbox, une requête, un premier paiement de test. Chaque route a sa page avec le bouton « Essayer ». ## Pays et moyens de paiement Le mobile money fonctionne au Cameroun, au Congo, au Gabon, au Bénin, en Côte d'Ivoire, au Sénégal, en RD Congo, au Rwanda, en Sierra Leone, en Ouganda, en Zambie, au Kenya et au Mozambique, chacun dans sa devise. La carte, Apple Pay et Google Pay fonctionnent partout. Un paiement par carte en FCFA est débité en euros au taux fixe de 655,957 FCFA pour 1 €, ce qui ouvre aussi à vos clients des moyens comme Bancontact, Klarna ou Amazon Pay selon leur pays. Voir [Le parcours d'un encaissement](/payments/overview#le-parcours-dun-encaissement). Votre client paie depuis le pays de son choix : un montant en XAF payé avec un numéro rwandais est converti en RWF au moment du paiement. Voir [Montants et devises](/payments/amounts-and-currencies#payer-depuis-un-autre-pays). > [accordion] Opérateurs par pays > > La liste complète, avec les codes à utiliser dans l'API, est dans [Paiements](/payments/overview#pays-et-operateurs). > [accordion] Ce que coûte Mercy Pay > > 3,5 % par paiement mobile money, 6 % + 200 FCFA par carte et 2 % par retrait. Pas d'abonnement. Voir [Commission](/payments/overview#commission). > [accordion] Tester sans risque > > Le mode test est ouvert dès la création du compte, sans vérification. Aucun argent réel ne bouge. Provoquez un refus, testez un retrait ou faites passer un abonnement à son échéance. Voir [Test et production](/start/environments). # Créer un lien de paiement Source: https://docs.mercy-pay.com/payment-links/api-guide Un lien que vous créez une fois et partagez n'importe où, payable par autant de clients que nécessaire. Ce guide couvre la création d'un lien de paiement réutilisable : la manière la plus rapide d'encaisser sans construire de session pour chaque client. Voir [Liens de paiement](/payment-links/overview) pour le fonctionnement général. **1. Créez le lien** Laissez `amount` de côté si vous voulez que le client choisisse combien payer, utile pour un don ou une collecte. Fixez-le pour vendre à prix fixe. ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl https://mercy-pay.com/api/v1/payment_links \ -H "Authorization: Bearer mp_live_XXXXXXXXXXXXXXXX" \ -H "Content-Type: application/json" \ -d '{ "title": "Collecte caritative", "currency": "XAF", "minimum_amount": 500 }' ``` ```php PHP theme={"theme":{"light":"github-light","dark":"github-dark"}} $ch = curl_init('https://mercy-pay.com/api/v1/payment_links'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Authorization: Bearer mp_live_XXXXXXXXXXXXXXXX', 'Content-Type: application/json', ]); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([ 'title' => 'Collecte caritative', 'currency' => 'XAF', 'minimum_amount' => 500, ])); $response = curl_exec($ch); curl_close($ch); ``` ```js JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const response = await fetch('https://mercy-pay.com/api/v1/payment_links', { method: 'POST', headers: { Authorization: 'Bearer mp_live_XXXXXXXXXXXXXXXX', 'Content-Type': 'application/json', }, body: JSON.stringify({ title: 'Collecte caritative', currency: 'XAF', minimum_amount: 500, }), }); const data = await response.json(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests response = requests.post( 'https://mercy-pay.com/api/v1/payment_links', headers={'Authorization': 'Bearer mp_live_XXXXXXXXXXXXXXXX'}, json={ 'title': 'Collecte caritative', 'currency': 'XAF', 'minimum_amount': 500, }, ) data = response.json() ``` La réponse contient `url` : c'est le lien à partager tel quel, pas besoin de le transformer. **2. Partagez-le** N'importe où : WhatsApp, Instagram, un lien en bio, un SMS. Chaque personne qui l'ouvre peut payer indépendamment des autres. Rien à créer de votre côté pour chaque client. **3. Suivez les paiements comme d'habitude** Chaque paiement effectué via le lien déclenche les mêmes webhooks qu'un paiement classique (`payment.succeeded`, `payment.failed`), et apparaît dans `GET /api/v1/payments` avec la référence de sa session. Voir [Gérer les webhooks](/developers/webhooks/handle). **4. Désactivez-le quand vous n'en avez plus besoin** Un lien désactivé refuse les nouveaux paiements mais garde son historique. Désactivez-le depuis votre tableau de bord, ou avec [`PATCH /api/v1/payment_links/{id}`](/api/payment-links/create) et `active: false`. `POST /api/v1/payment_links/{id}/archive` le retire de votre liste. ## Erreurs fréquentes | Message | Cause | | - | - | | `Ce lien n'est plus disponible.` | Lien désactivé, expiré, ou slug incorrect | | Erreur de validation sur `amount` | Montant sous le `minimum_amount` fixé à la création | # Créer un lien de paiement Source: https://docs.mercy-pay.com/payment-links/create-in-dashboard Un lien à partager sur WhatsApp, Instagram ou par SMS, créé en une minute depuis le tableau de bord. Pas de site, pas de code : vous créez le lien, vous le partagez, vos clients paient. **1. Ouvrez le formulaire** Dans **Liens de paiement**, cliquez sur **Nouveau lien de paiement**. Le même bouton est sur la page d'accueil du tableau de bord. **2. Choisissez le type et le titre** **Paiement ponctuel** pour une vente, **Abonnement** pour inscrire vos clients à un de vos plans. Le titre est ce que verra votre client, par exemple « Consultation » ou « Formation Excel ». **3. Fixez le prix** Sous **Comment fixer le prix** : | Choix | Pour | | - | - | | **Prix fixe** | Un produit ou un service à un prix connu | | **Montant libre** | Un don, une avance, un devis : le client saisit le montant, avec un minimum si vous voulez | | **Plusieurs tarifs** | Un même service en plusieurs formules, par exemple « Solo » et « Équipe » | | **Panier** | Plusieurs articles avec quantités et variantes (taille, couleur) | L'**Aperçu**, à droite, montre la page telle que votre client la verra. **4. Ajoutez ce dont vous avez besoin (facultatif)** * **Pendant et après le paiement** : demander le téléphone ou l'adresse, et le message affiché après le paiement. * **Champs personnalisés à demander au client** : une question en texte libre, un choix, une case à cocher ou un fichier (image ou PDF). * **Options avancées** : une limite d'utilisation, une date d'expiration, et les pages de retour de votre site. **5. Créez et partagez** Cliquez sur **Créer le lien**. Sur la page du lien, **Copier le lien** ou **Télécharger le QR code** à imprimer en boutique. ![Le formulaire Nouveau lien de paiement avec l'aperçu à droite](/images/payment-links/create-light.webp) ![Le formulaire Nouveau lien de paiement avec l'aperçu à droite](/images/payment-links/create-dark.webp) ## Suivre vos liens **Liens de paiement** liste vos liens avec leur état, leurs paiements et leurs visites. Filtrez par état ou par type, ou cherchez un lien par son titre. ![La liste des liens de paiement](/images/payment-links/index-light.webp) ![La liste des liens de paiement](/images/payment-links/index-dark.webp) Ouvrez un lien pour voir ses visites, ses paiements et son taux de conversion. Depuis cette page : **Modifier**, **Désactiver** (puis **Activer**) ou **Archiver**. Un lien désactivé affiche une page « indisponible » mais garde son historique. Un lien qui n'a jamais servi peut aussi être supprimé depuis la liste. ![Le détail d'un lien de paiement avec ses paiements](/images/payment-links/show-light.webp) ![Le détail d'un lien de paiement avec ses paiements](/images/payment-links/show-dark.webp) > [!NOTE] > > Le lien encaisse dans la devise de votre compte. Votre client paie depuis le pays de son choix : le montant est converti vers la devise de son opérateur au moment du paiement. Voir [Ce que voit votre client](/payment-links/customer-page) pour le parcours côté client. # Ce que voit votre client Source: https://docs.mercy-pay.com/payment-links/customer-page La page du lien, puis la page de paiement : mobile money ou carte, depuis n'importe quel pays. Votre client ouvre le lien sur son téléphone ou son ordinateur. Il ne crée aucun compte. **1. La page du lien** Le nom de votre boutique, le titre et le prix. Selon le lien, il saisit un montant, choisit un tarif ou remplit son panier, puis répond à vos questions. ![La page d'un lien de paiement en montant libre](/images/payment-links/public-light.webp) ![La page d'un lien de paiement en montant libre](/images/payment-links/public-dark.webp) **2. La page de paiement** Il choisit **Mobile money** ou **Carte bancaire**. En mobile money, il choisit son pays, son opérateur et saisit son numéro : la demande arrive sur son téléphone et il la valide avec son code. ![La page de paiement : mobile money ou carte bancaire](/images/showcase/checkout-momo-light.webp) ![La page de paiement : mobile money ou carte bancaire](/images/showcase/checkout-momo-dark.webp) Il peut aussi payer depuis un autre pays : le montant est converti pour lui. Voir [La page de paiement](/payments/checkout-page). **3. La confirmation** Le paiement confirmé, il voit votre message de remerciement et peut télécharger son reçu. Il le reçoit aussi par e-mail s'il a donné son adresse. Si vous avez indiqué une page de retour, il y est renvoyé après 5 secondes. ## Sur téléphone La plupart de vos clients paient depuis leur téléphone. Les deux pages s'y adaptent. ![Un lien de paiement sur téléphone](/images/payment-links/public-mobile-light.webp) ![Un lien de paiement sur téléphone](/images/payment-links/public-mobile-dark.webp) ![La page de paiement sur téléphone, avec la conversion](/images/showcase/checkout-convert-mobile-light.webp) ![La page de paiement sur téléphone, avec la conversion](/images/showcase/checkout-convert-mobile-dark.webp) > [!TIP] > > En mode test, un bandeau « Mode test, aucun montant réel n'est débité » s'affiche sur la page de paiement. Il disparaît en live. Les couleurs et le logo de ces pages se règlent dans **Paramètres**, onglet **Image de marque**. # Liens de paiement Source: https://docs.mercy-pay.com/payment-links/overview Un lien que vous créez une fois et que n'importe quel client peut payer, autant de fois que nécessaire, sans site web ni intégration. Une session de checkout est à usage unique : elle représente une demande de paiement précise, pour un client précis. Un **lien de paiement** fonctionne dans l'autre sens : vous le créez une fois, vous le partagez où vous voulez (WhatsApp, Instagram, votre bio), et il reste payable par autant de clients différents que nécessaire, aussi longtemps que vous le laissez actif. Derrière chaque paiement effectué via un lien, Mercy Pay crée une session de checkout ordinaire. Rien ne change pour vos clients ni dans vos webhooks : même page de paiement, mêmes événements, mêmes frais. > [card] [Créer un lien](/payment-links/create-in-dashboard) > > En une minute, depuis le tableau de bord. > [card] [Créer un lien par l'API](/payment-links/api-guide) > > Chaque mode de prix, avec des exemples. ## Les façons de fixer le prix Un lien de paiement peut demander un montant fixe (vous vendez un produit à 5 000 XAF, par exemple), laisser le client choisir combien il veut payer (utile pour une collecte ou un don, avec un minimum si vous voulez), proposer plusieurs tarifs, ou vendre plusieurs articles en panier. Le lien affiche toujours d'abord sa propre page, à vos couleurs, puis la page de paiement. | `pricing_mode` | Ce que voit le client | | - | - | | `fixed` | Le titre, la description et le montant, puis un bouton pour payer | | `flexible` | Un champ pour saisir le montant qu'il veut payer, au moins `minimum_amount` | | `options` | Les tarifs proposés (`price_options`), dont il choisit un | | `cart` | Les articles (`items`), dont il choisit les quantités | Un lien `type: "recurring"` inscrit le client au plan choisi : il paie le premier cycle pour activer son abonnement, voir [Abonnements](/subscriptions/overview). ## Pré-remplir les informations du client Si vous connaissez déjà votre client, ajoutez ses informations à l'adresse du lien avant de la lui envoyer : ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} https://mercy-pay.com/payer/formation-laravel?email=awa@example.com&name=Awa%20Ndiaye&phone=%2B237677123456 ``` | Paramètre | Effet | | - | - | | `email` | Pré-remplit l'adresse e-mail, où il reçoit son reçu | | `name` | Pré-remplit son nom | | `phone` | Pré-remplit son numéro mobile money, au format international (`+237…`), et choisit son pays | Votre client peut toujours modifier ces champs, par exemple pour payer avec un autre numéro. Une valeur mal formée est simplement ignorée. Encodez les caractères spéciaux de l'adresse : `%20` pour un espace, `%2B` pour le `+`. ## Suivi Chaque lien compte ses paiements réussis (`uses_count`) et ses ouvertures (`views_count`). Ouvrir le lien ne consomme rien : un aperçu WhatsApp ou un robot ne fait pas baisser `max_uses`. Une session de checkout en cours réserve une place jusqu'à son expiration, pour qu'un lien limité à 10 paiements n'en encaisse jamais 11. Retrouvez les paiements d'un lien avec [`GET /api/v1/payment_links/{id}/payments`](/api/payment-links/payments), ou recevez chacun avec `checkout_session.completed`, qui porte le lien d'origine et les réponses du payeur. ## Désactiver un lien Un lien désactivé affiche une page "indisponible" à qui le visite, mais son historique (paiements déjà reçus, compteur d'utilisations) reste intact. Contrairement à une suppression, une désactivation se défait. Un lien peut aussi expirer de lui-même si vous lui donnez une date d'expiration à la création. Voir [Créer un lien de paiement](/payment-links/create-in-dashboard) pour le parcours dans le tableau de bord, [Créer un lien par l'API](/payment-links/api-guide) pour le flux développeur, ou [Liens de paiement](/api/payment-links/create) pour la référence API. # Accepter un paiement Source: https://docs.mercy-pay.com/payments/accept-a-payment Créez une session de checkout, redirigez votre client et confirmez le paiement par webhook. Ce guide couvre le flux complet d'un encaissement, du montant jusqu'à la confirmation sur votre serveur, avec la page hébergée : la manière la plus rapide de démarrer. Si vous préférez ne jamais rediriger votre client vers une page Mercy Pay, la charge directe fait la même chose depuis votre propre formulaire : votre serveur appelle `POST /api/v1/payments`, avec le numéro et l'opérateur du client pour le mobile money, voir [Paiements](/api/payments/create), ou pour une carte bancaire avec votre propre formulaire, mis en place avec notre équipe. **1. Créez la session côté serveur** Ne créez jamais de session depuis le navigateur : votre clé API ne doit pas y figurer. ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl https://mercy-pay.com/api/v1/checkout_sessions \ -H "Authorization: Bearer mp_live_XXXXXXXXXXXXXXXX" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: commande-4821" \ -d '{ "amount": 15000, "currency": "XAF", "description": "Commande 4821", "payer_email": "client@example.com", "success_url": "https://boutique-ada.example/commandes/4821/merci", "cancel_url": "https://boutique-ada.example/commandes/4821" }' ``` ```php PHP theme={"theme":{"light":"github-light","dark":"github-dark"}} $ch = curl_init('https://mercy-pay.com/api/v1/checkout_sessions'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Authorization: Bearer mp_live_XXXXXXXXXXXXXXXX', 'Content-Type: application/json', 'Idempotency-Key: commande-4821', ]); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([ 'amount' => 15000, 'currency' => 'XAF', 'description' => 'Commande 4821', 'payer_email' => 'client@example.com', 'success_url' => 'https://boutique-ada.example/commandes/4821/merci', 'cancel_url' => 'https://boutique-ada.example/commandes/4821', ])); $response = curl_exec($ch); curl_close($ch); ``` ```js JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const response = await fetch('https://mercy-pay.com/api/v1/checkout_sessions', { method: 'POST', headers: { Authorization: 'Bearer mp_live_XXXXXXXXXXXXXXXX', 'Content-Type': 'application/json', 'Idempotency-Key': 'commande-4821', }, body: JSON.stringify({ amount: 15000, currency: 'XAF', description: 'Commande 4821', payer_email: 'client@example.com', success_url: 'https://boutique-ada.example/commandes/4821/merci', cancel_url: 'https://boutique-ada.example/commandes/4821', }), }); const data = await response.json(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests response = requests.post( 'https://mercy-pay.com/api/v1/checkout_sessions', headers={ 'Authorization': 'Bearer mp_live_XXXXXXXXXXXXXXXX', 'Idempotency-Key': 'commande-4821', }, json={ 'amount': 15000, 'currency': 'XAF', 'description': 'Commande 4821', 'payer_email': 'client@example.com', 'success_url': 'https://boutique-ada.example/commandes/4821/merci', 'cancel_url': 'https://boutique-ada.example/commandes/4821', }, ) data = response.json() ``` Stockez `reference` dans votre commande. **2. Redirigez votre client** Envoyez le client vers `data.checkout_url`. Sur la page hébergée, il choisit son moyen de paiement : * **Mobile money** : pays, opérateur et numéro, parmi tous les pays disponibles. Même si leur devise diffère de celle de la session, le montant est alors converti automatiquement : voir [Sessions de checkout](/payments/checkout-sessions). * **Carte bancaire** : disponible si le montant atteint le minimum de la devise, voir [Montants et devises](/payments/amounts-and-currencies). **3. Attendez la confirmation par webhook** La redirection vers `success_url` ne prouve rien. Le client peut fermer l'onglet avant qu'elle n'ait lieu. Ne marquez une commande payée qu'à réception de `payment.succeeded`, voir [Gérer les webhooks](/developers/webhooks/handle). **4. Relisez la session si besoin** Pour vérifier l'état à tout moment : ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl https://mercy-pay.com/api/v1/checkout_sessions/cs_01J9Z3V8QK4M2XW7R6TB5N0HYD \ -H "Authorization: Bearer mp_live_XXXXXXXXXXXXXXXX" ``` ```php PHP theme={"theme":{"light":"github-light","dark":"github-dark"}} $ch = curl_init('https://mercy-pay.com/api/v1/checkout_sessions/cs_01J9Z3V8QK4M2XW7R6TB5N0HYD'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Authorization: Bearer mp_live_XXXXXXXXXXXXXXXX', ]); $response = curl_exec($ch); curl_close($ch); ``` ```js JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const response = await fetch( 'https://mercy-pay.com/api/v1/checkout_sessions/cs_01J9Z3V8QK4M2XW7R6TB5N0HYD', { headers: { Authorization: 'Bearer mp_live_XXXXXXXXXXXXXXXX', }, }, ); const data = await response.json(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests response = requests.get( 'https://mercy-pay.com/api/v1/checkout_sessions/cs_01J9Z3V8QK4M2XW7R6TB5N0HYD', headers={'Authorization': 'Bearer mp_live_XXXXXXXXXXXXXXXX'}, ) data = response.json() ``` Le champ `status` passe de `created` à `pending_payment`, puis `completed`. ## Erreurs fréquentes | Message | Cause | | - | - | | `Le montant minimum pour un paiement par carte est de 0.50 USD.` | Montant sous le minimum carte | | `Opérateur Mobile Money invalide pour ce pays.` | Opérateur non disponible pour le pays choisi | | `Une tentative de paiement est déjà en cours pour cette session.` | Deux paiements lancés en même temps | # Montants et devises Source: https://docs.mercy-pay.com/payments/amounts-and-currencies Les montants sont des entiers en unités mineures. Le nombre de décimales dépend de la devise (norme ISO 4217). Tous les montants de l'API sont des **entiers en unités mineures** : la plus petite unité que la devise permet de compter. Vous n'envoyez donc jamais de décimales, et le calcul dépend de la devise. ## Nombre de décimales par devise | Devise | Décimales | Montant `1 000` signifie | | - | - | - | | XAF, XOF (franc CFA) | 0 | 1 000 XAF | | RWF, UGX | 0 | 1 000 RWF, 1 000 UGX | | USD, EUR, GBP | 2 | 10,00 USD (soit 1 000 centimes) | | CDF, GHS, KES, MZN, NGN, SLE, ZMW | 2 | 10,00 (soit 1 000 centimes) | Pour convertir un montant lu par un humain, multipliez-le par `10` à la puissance du nombre de décimales : `10,50 USD` s'envoie `1050`, `5 000 XAF` s'envoie `5000` tel quel puisque le XAF n'a pas de sous-unité. > [!WARNING] > > N'envoyez jamais `5000.00` ou `10.50`. Une valeur décimale est refusée par la validation de l'API. ## Codes devise Le code devise est un code **ISO 4217** de trois lettres (`XAF`, `USD`, `EUR`, la casse n'importe pas : Mercy Pay normalise en majuscules). Un code qui n'existe pas dans la norme, comme `ABC`, est refusé. ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} curl https://mercy-pay.com/api/v1/checkout_sessions \ -H "Authorization: Bearer mp_sandbox_XXXXXXXXXXXXXXXX" \ -H "Content-Type: application/json" \ -d '{"amount": 1050, "currency": "USD", "description": "Abonnement mensuel"}' ``` ## Minimums carte En dessous d'un certain montant, le réseau carte lui-même refuse la transaction. Mercy Pay bloque donc la tentative en amont plutôt que de vous laisser découvrir l'échec après coup : | Devise | Minimum carte | | - | - | | XAF | 500 XAF | | USD | 0,50 USD (`50`) | | EUR | 0,50 EUR (`50`) | | GBP | 0,30 GBP (`30`) | Sous ce seuil, la page de paiement ne propose plus que le mobile money, et une tentative de paiement par carte échoue avec le minimum exprimé en unités que votre client comprend (pas en unités mineures). ## Payer depuis un autre pays Vous fixez un seul prix, dans votre devise. Votre client paie depuis n'importe quel pays pris en charge, sur le checkout, un lien de paiement, la page de paiement d'un abonnement ou par `POST /api/v1/payments` : * **Mobile money** : un opérateur ne déplace que la monnaie de son pays. Si elle diffère de votre devise, le montant est converti au taux du jour, arrondi au-dessus, avant la demande sur le téléphone. Le paiement porte le montant et la devise réellement prélevés, et il crédite le solde de ce pays et de cet opérateur, voir [Soldes](/payouts/balances). * **Carte** : le montant est prélevé tel quel, dans votre devise. Si la carte est dans une autre monnaie, c'est la banque de votre client qui convertit. Pour un abonnement, chaque échéance est convertie de la même façon, quel que soit le pays du numéro enregistré par votre client. # Analyses Source: https://docs.mercy-pay.com/payments/analytics Vos encaissements, votre revenu net et votre taux de réussite, jour après jour. **Analyses** montre comment évoluent vos ventes, sans tableur. ![La page Analyses : volume encaissé, revenu net, taux de réussite, panier moyen et graphiques](/images/payments/analytics-light.webp) ![La page Analyses : volume encaissé, revenu net, taux de réussite, panier moyen et graphiques](/images/payments/analytics-dark.webp) ## Ce que vous y lisez | Indicateur | Ce qu'il mesure | | - | - | | **Volume encaissé** | Le total payé par vos clients sur la période | | **Revenu net** | Ce qui vous revient, frais Mercy Pay déduits | | **Taux de réussite** | La part des paiements tentés qui ont réussi | | **Panier moyen** | Le montant moyen d'un paiement réussi | Chaque indicateur se compare à la période précédente de même durée. * **Rythme des encaissements** : vos encaissements jour par jour, mobile money et carte séparés, avec la période précédente en pointillés. * **Répartition par canal** : la part de chaque opérateur et de la carte dans vos paiements réussis. ## Changer de période et exporter En haut à droite, choisissez **7 j**, **30 j** ou **90 j**. **Exporter** télécharge les chiffres de la période. > [!TIP] > > Un taux de réussite qui baisse vient souvent d'un opérateur en difficulté. Vérifiez la [page d'état des services](https://mercy-pay.com/statut) et le détail des refus dans [Transactions](/payments/transactions). # La page de paiement Source: https://docs.mercy-pay.com/payments/checkout-page Ce que voit votre client : mobile money ou carte, depuis le pays de son choix, avec la conversion faite pour lui. Une seule page sert à tout : les liens de paiement, les sessions créées par votre site et les factures d'abonnement. Elle porte le nom de votre boutique, s'adapte au téléphone comme à l'ordinateur, et suit le thème clair ou sombre de votre client. Votre client saisit son numéro : son opérateur est reconnu tout seul. ## Payer depuis n'importe quel pays Votre client choisit son pays parmi les 11 proposés. Le montant est converti dans la devise de son opérateur et affiché avant qu'il paie : il sait exactement ce qui sera débité de son compte. Une facture de 15 000 FCFA payée depuis le Ghana : 375,00 GHS débités. > [!TIP] > > Vous encaissez toujours dans votre devise. La conversion est faite au moment du paiement, sans réglage de votre côté. Voir [Payer depuis un autre pays](/payments/amounts-and-currencies#payer-depuis-un-autre-pays). ## Tous les moyens, sur la même page > [tab] Mobile money > > Le client choisit son pays et saisit son numéro. La demande arrive sur son téléphone et il la valide avec son code. La page suit le paiement en direct, sans qu'il ait à la recharger. > [tab] Carte bancaire > > Visa et Mastercard, de n'importe quel pays. La vérification de la banque (code par SMS ou appli) s'affiche si elle est demandée. > [tab] Confirmation > > Le paiement confirmé, votre client le voit tout de suite et reçoit son reçu par e-mail s'il a donné son adresse. ## En mode test Le bandeau « Mode test, aucun montant réel n'est débité » est affiché. En mobile money, vous choisissez le résultat (**Paiement réussi** ou **Paiement refusé**). En carte, la carte de test `4242 4242 4242 4242` est proposée. En live, ces aides disparaissent. ## Pour l'ouvrir > [card] [Un lien de paiement](/payment-links/create-in-dashboard) > > Sans code, depuis le tableau de bord. > [card] [Une session par l'API](/payments/accept-a-payment) > > Un appel, puis une redirection. > [card] [Un abonnement](/subscriptions/subscribe-page) > > La page d'inscription, même esprit. # Sessions de checkout Source: https://docs.mercy-pay.com/payments/checkout-sessions Une session de checkout est une demande de paiement hébergée par Mercy Pay, avec un cycle de vie précis. Une **session de checkout** représente une demande de paiement : un montant, une devise et une page hébergée que votre client peut ouvrir pour payer. ## Cycle de vie | Statut | Signification | | - | - | | `created` | Session créée, aucun paiement commencé | | `pending_payment` | Un paiement est en cours de traitement | | `completed` | Un paiement a réussi | | `expired` | Personne n'a payé avant `expires_at` | | `cancelled` | Réservé, jamais renvoyé aujourd'hui. Une session abandonnée ou que vous fermez passe à `expired`. | Une session expire après **30 minutes** sans paiement réussi. Au-delà, créez-en une nouvelle. ## Identifiant Chaque session a un `id` de la forme `cs_01J9Z3V8QK4M2XW7R6TB5N0HYD` : c'est lui que vous utilisez pour relire ou expirer la session ensuite. ## Champs | Champ | Requis | Description | | - | - | - | | `amount` | oui | Entier en unités mineures, supérieur à 0 | | `currency` | oui | Code ISO 4217 | | `description` | non | Libellé affiché au client, 500 caractères maximum | | `payer_name`, `payer_email`, `payer_phone` | non | Pré-remplissent la page de paiement | | `success_url`, `cancel_url` | non | Redirections après paiement ou annulation. `{CHECKOUT_SESSION_ID}` y est remplacé par l'`id` de la session. | | `payment_method_types` | non | Moyens proposés : `["mobile_money"]`, `["card"]` ou les deux. Absent : tous. | | `allowed_countries` | non | Pays mobile money proposés, par exemple `["CMR", "GAB"]`. Absent : tous les pays ouverts. | | `metadata` | non | Objet libre, renvoyé dans chaque réponse | ## Retrouver la session au retour Ajoutez `{CHECKOUT_SESSION_ID}` à votre `success_url` : `https://boutique.example/merci?session={CHECKOUT_SESSION_ID}`. Au retour, votre page reçoit `?session=cs_…` et peut relire la session avec [`GET /api/v1/checkout_sessions/{id}`](/api/checkout-sessions/get), sans paramètre maison. La confirmation reste le webhook `payment.succeeded` : un client peut fermer l'onglet avant d'être redirigé. ## Choisir ce que la page propose Vous encaissez déjà la carte ailleurs ? Passez `payment_method_types: ["mobile_money"]` et la page ne propose plus que le mobile money. `allowed_countries` limite de la même façon les pays proposés. Un moyen ou un pays masqué est aussi refusé si quelqu'un essaie de payer avec lui directement. ## Paiement mobile money dans une autre devise Sur la page hébergée, votre client peut choisir n'importe quel pays mobile money disponible, même si sa devise diffère de celle de la session : le montant est alors converti automatiquement vers la devise de l'opérateur choisi avant l'encaissement. Le paiement qui en résulte (consultable via `GET /api/v1/payments/{id}`) reflète ce montant converti, pas celui de la session d'origine, voir [Paiements](/payments/payment-object). Un paiement par carte, lui, encaisse toujours exactement le montant et la devise de la session, sans conversion. ## Idempotence Après un timeout, vous ne savez pas si votre requête a abouti. Envoyez un en-tête `Idempotency-Key` à la création : si vous rejouez la même requête après un doute, vous récupérez la session déjà créée au lieu d'en ouvrir une deuxième. Voir [Idempotence](/developers/idempotency). # Vue d'ensemble Source: https://docs.mercy-pay.com/payments/overview Le parcours d'un paiement, de la session de checkout au solde marchand, et les acteurs qui interviennent. Mercy Pay relie votre application et vos clients. Un encaissement passe par deux flux possibles, mais finit toujours de la même façon. > [card] [Suivre vos transactions](/payments/transactions) > > Chaque paiement, ses frais et son reçu, dans le tableau de bord. > [card] [Accepter un paiement](/payments/accept-a-payment) > > Une session de paiement ou un encaissement direct, par l'API. ## Le parcours d'un encaissement Avec la page hébergée (la plus simple à mettre en place) : 1. Votre serveur crée une **session de checkout** (`POST /api/v1/checkout_sessions`) et reçoit une `checkout_url`. 2. Votre client ouvre cette page et choisit **Mobile money** ou **Carte bancaire**. Apple Pay, Google Pay et Link s'affichent avec la carte quand l'appareil les propose. Pour un montant en FCFA (XAF ou XOF), la carte est débitée en euros au taux fixe de 1 € pour 655,957 FCFA : votre prix et votre solde restent en FCFA, et votre client voit aussi les moyens acceptés en euros, comme Bancontact ou Klarna. Il en va de même en EUR, USD ou GBP. 3. Mercy Pay crée un **paiement** et le traite. Avec la charge directe (voir [Accepter un paiement](/payments/accept-a-payment)), vous sautez les deux premières étapes : votre serveur appelle `POST /api/v1/payments` avec le numéro et l'opérateur du client, sans jamais le rediriger vers Mercy Pay. Troisième option quand vous ne voulez pas créer de session par client : un [lien de paiement](/payment-links/overview), créé une fois et payable par autant de clients que nécessaire. Chaque paiement y crée une session comme les deux flux ci-dessus. Dans tous les cas, la suite est identique : Mercy Pay confirme le résultat, enregistre le paiement, calcule la commission, crédite le **solde** correspondant, puis vous envoie un événement `payment.succeeded` ou `payment.failed`. ## Les soldes Un solde est rattaché à un **canal**, un **pays** et un **opérateur**, pour une devise donnée : il y en a un pour la carte bancaire, et un par pays et par opérateur mobile money. L'argent reste dans ce circuit jusqu'à ce que vous le retiriez. Un encaissement MTN au Cameroun crédite exactement le solde `CMR · MTN_MOMO_CMR`, jamais le solde carte ni un autre pays, et un retrait ne peut puiser que dans le solde du pays et de l'opérateur choisis. Voir [Soldes](/payouts/balances). ## Pays et opérateurs | Pays | Devise | Opérateurs (`provider`) | | - | - | - | | Cameroun (`CMR`) | XAF | `MTN_MOMO_CMR`, `ORANGE_CMR` | | Congo (`COG`) | XAF | `MTN_MOMO_COG`, `AIRTEL_COG` | | Gabon (`GAB`) | XAF | `AIRTEL_GAB`, `MOOV_GAB` | | Bénin (`BEN`) | XOF | `MTN_MOMO_BEN`, `MOOV_BEN` | | Côte d'Ivoire (`CIV`) | XOF | `MTN_MOMO_CIV`, `ORANGE_CIV`, `MOOV_CIV` | | Sénégal (`SEN`) | XOF | `FREE_SEN`, `ORANGE_SEN` | | RD Congo (`COD`) | CDF | `VODACOM_MPESA_COD`, `AIRTEL_COD`, `ORANGE_COD` | | Rwanda (`RWA`) | RWF | `AIRTEL_RWA`, `MTN_MOMO_RWA` | | Sierra Leone (`SLE`) | SLE | `ORANGE_SLE` | | Ouganda (`UGA`) | UGX | `AIRTEL_OAPI_UGA`, `MTN_MOMO_UGA` | | Zambie (`ZMB`) | ZMW | `MTN_MOMO_ZMB`, `AIRTEL_OAPI_ZMB`, `ZAMTEL_ZMB` | | Kenya (`KEN`) | KES | `MPESA_KEN` | | Mozambique (`MOZ`) | MZN | `VODACOM_MOZ` | Les pays sont notés en ISO 3166-1 alpha-3. Les codes opérateurs sont à transmettre tels quels. ## Commission Mercy Pay prélève une commission sur le montant brut de chaque paiement, et vous crédite du net (`net = brut − commission`). Vous n'avez rien à calculer. Voir [Retraits](/payouts/overview) pour le détail d'un retrait. # Paiements Source: https://docs.mercy-pay.com/payments/payment-object Un paiement est la trace d'un encaissement : montants, commission, moyen de paiement et statut. Un **paiement** est créé lorsqu'un client valide une session de checkout, ou directement via `POST /api/v1/payments`. Son `id` commence par `pay_`. Il vous est consultable via l'API et dans le tableau de bord. ## Montants Pour un paiement de 5 000 XAF réglé par mobile money : | Champ | Valeur | Signification | | - | - | - | | `amount` | 5000 | Montant payé par le client | | `fee_amount` | 175 | Frais retenus | | `net_amount` | 4825 | Ce qui est crédité à votre solde | Ce sont les mêmes noms que sur un retrait. Seul `net_amount` compte pour votre solde. ## Moyen de paiement * `payment_method` : `mobile_money` ou `card`. * Pour le mobile money, `mobile_money_country` et `mobile_money_provider` indiquent le pays et l'opérateur, par exemple `CMR` et `MTN_MOMO_CMR`. Un opérateur mobile money ne déplace que sa propre devise nationale : si le pays choisi a une devise différente de celle demandée, le montant est converti automatiquement avant l'encaissement, et `currency` et `amount` reflètent le montant réellement prélevé dans la devise de l'opérateur. Une carte, elle, encaisse toujours exactement la devise demandée, sans conversion. ## `next_action` Un paiement né d'une session de checkout ou d'une charge directe (`POST /api/v1/payments`) porte toujours un champ `next_action` : `null` quand il n'y a rien de plus à faire côté client (le mobile money aujourd'hui), ou un objet `{"type": "...", ...}` quand votre frontend doit terminer quelque chose pour confirmer le paiement. Pour une carte, `type` est `"card_payment"`. Décidez toujours sur `type`, jamais sur la seule présence du champ : un futur moyen de paiement introduira son propre `type` sans jamais changer la forme de ceux déjà documentés. ## Statuts | Statut | Signification | | - | - | | `pending` | Enregistré, en attente de confirmation | | `processing` | Accepté, en cours de traitement | | `succeeded` | Encaissé : le solde est crédité | | `failed` | Refusé ou en échec, voir `failure_code` (code stable à tester) et `failure_reason` (texte français à afficher), voir [la liste complète](/api/payments/create#failure_code). Une carte peut encore réussir ensuite, voir plus bas. | | `canceled` | Annulé avant encaissement | | `expired` | Délai dépassé sans confirmation. Une carte non terminée expire après 24 heures. | | `refunded` | Remboursé : le montant est reparti vers le client, voir [Rembourser un paiement](/payments/refunds) | | `disputed` | Réservé, non utilisé pour l'instant | Un paiement ne passe à `succeeded` qu'une seule fois. Marquez une commande comme payée à la réception du webhook `payment.succeeded`, pas à la redirection vers `success_url` : le client peut fermer l'onglet ou perdre sa connexion, alors que le webhook est renvoyé tant que votre serveur ne répond pas `2xx`. ## Un refus carte n'est pas toujours définitif Sur le même formulaire, un client peut se tromper de carte puis réessayer avec la bonne. Le paiement passe alors de `failed` à `succeeded`, et vous recevez `payment.failed` puis `payment.succeeded`. Ne bloquez donc pas une commande pour toujours au premier `payment.failed` d'une carte : `payment.succeeded` a toujours le dernier mot. Cela ne concerne que la carte. Un paiement mobile money en `failed` reste en `failed`. # Rembourser un paiement Source: https://docs.mercy-pay.com/payments/refunds Rendez tout ou partie d'un paiement à votre client, depuis le tableau de bord ou par l'API, et suivez son arrivée. Un remboursement rend **tout ou partie** du paiement au client : sur son numéro mobile money, ou sur la carte qui a servi à payer. Plusieurs remboursements partiels peuvent se suivre jusqu'à rendre tout le paiement, un à la fois. ## Les règles * Un paiement se rembourse jusqu'à **90 jours** après lui. * Un paiement par carte se rembourse une fois ses fonds disponibles sur votre solde. Avant, la transaction indique la date à partir de laquelle ce sera possible. * Votre solde est débité du montant rendu et de **frais de remboursement de 1 % de ce montant, au moins 100 FCFA par remboursement**. La commission du paiement n'est pas rendue. * Un remboursement doit être arrivé ou avoir échoué avant d'en demander un autre sur le même paiement. * Le solde disponible, retraits déjà réservés déduits, doit couvrir ce total. Sinon le remboursement est refusé. * Un paiement en FCFA débité en euros sur une carte est remboursé en euros : la même part des euros pour un remboursement partiel, et exactement ce qui reste pour le dernier. ## Depuis le tableau de bord Ouvrez la transaction et choisissez **Rembourser**. Le montant proposé est ce qui reste à rendre : réduisez-le pour un remboursement partiel. Le montant rendu, les frais et le total débité s'affichent avant de confirmer. Pour de l'argent réel, une confirmation de votre identité est demandée, comme pour un retrait. Seuls le propriétaire et les administrateurs de la boutique peuvent rembourser. ## Par l'API ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} curl https://mercy-pay.com/api/v1/payments/pay_0b76919140c74c4c99d52f5c1734b743/refunds \ -H "Authorization: Bearer $MERCY_PAY_KEY" \ -H "Idempotency-Key: refund-commande-1042" \ -H "Content-Type: application/json" \ -d '{"amount": 2500, "reason": "Une partie de la commande annulée"}' ``` Sans `amount`, tout ce qui reste à rendre est remboursé. Le champ `amount_refunded` du paiement donne ce qui a déjà été rendu. L'en-tête `Idempotency-Key` est obligatoire : la même clé renvoie le même remboursement, jamais un second. Voir [Rembourser un paiement](/api/refunds/create) dans la référence. ## Suivre un remboursement | Statut | Signification | | - | - | | `pending` | Envoyé, réponse attendue | | `processing` | En route vers le client | | `succeeded` | Arrivé chez le client. Le paiement passe à `refunded` une fois tout son montant rendu ; avant, il reste `succeeded` avec `amount_refunded` à jour. | | `failed` | Non envoyé : le montant et les frais sont revenus sur votre solde | | `canceled` | Annulé pendant qu'il attendait un opérateur indisponible | Quand l'opérateur du client est indisponible, le remboursement attend son retour (`status_detail` vaut `delayed`). Vous pouvez alors l'annuler depuis la transaction ou par [`POST /api/v1/refunds/{id}/cancel`](/api/refunds/cancel). Les événements `refund.succeeded`, `refund.failed`, `refund.canceled` et `payment.refunded` vous préviennent à chaque étape. Votre client reçoit un e-mail quand son argent revient, si son adresse est connue. # Suivre vos transactions Source: https://docs.mercy-pay.com/payments/transactions Retrouvez chaque paiement, ce que vous recevez après les frais, et le reçu envoyé à votre client. Chaque paiement, qu'il vienne d'un lien, d'un abonnement, de votre site ou de l'API, arrive dans **Transactions**. ![La liste des transactions avec leur montant, leur état et le moyen de paiement](/images/payments/transactions-light.webp) ![La liste des transactions avec leur montant, leur état et le moyen de paiement](/images/payments/transactions-dark.webp) ## Trouver un paiement **1. Filtrez par état** **Tous**, **Réussi**, **En attente** ou **Échoué**, avec le nombre de paiements de chaque état. **2. Affinez** Ajoutez un filtre **Date**, **Moyen de paiement**, **Pays**, **Montant** ou **Origine** (lien de paiement, abonnement, API). La recherche trouve un client par son nom, son e-mail, son téléphone ou la description du paiement. **3. Exportez** **Exporter** télécharge la liste filtrée, pour votre comptabilité. > [!TIP] > > Le sélecteur **Sandbox / Live**, en bas du menu, choisit les paiements affichés. Les paiements de test ne se mélangent jamais aux vrais. ## Le détail d'un paiement Cliquez sur une ligne. Vous voyez ce que le client a payé, les frais Mercy Pay et ce que vous recevez, puis le moyen de paiement, le client et la chronologie du paiement. ![Le détail d'une transaction : montant payé, frais, net pour vous et moyen de paiement](/images/payments/transaction-light.webp) ![Le détail d'une transaction : montant payé, frais, net pour vous et moyen de paiement](/images/payments/transaction-dark.webp) Un paiement refusé affiche sa raison en clair, par exemple « Le solde du payeur est insuffisant ». Pour une carte, une section **Analyse du risque** explique la décision du contrôle anti-fraude et du réseau de la carte. Depuis cette page : * **Télécharger le reçu** : le reçu PDF du paiement ; * **Partager** : envoyer le reçu PDF par WhatsApp, e-mail ou une autre application, depuis un téléphone ou un navigateur qui le permet ; * **Signaler un problème** : ouvrir une demande au support avec ce paiement déjà joint. ## Le reçu de votre client Dès qu'un paiement réussit, votre client reçoit un e-mail de confirmation à vos couleurs, avec le lien vers son reçu. ![L'e-mail de confirmation de paiement reçu par le client](/images/emails/payment-confirmation-light.webp) ![Le reçu PDF du paiement](/images/payments/receipt-light.webp) Le logo, la couleur et les coordonnées affichés se règlent dans **Paramètres**, onglet **Image de marque**. Voir [Analyses](/payments/analytics) pour suivre vos chiffres dans le temps. # Comptes de retrait Source: https://docs.mercy-pay.com/payouts/accounts Enregistrez vos numéros mobile money et vos comptes bancaires une fois, puis retirez en deux clics. Un compte de retrait évite de ressaisir un numéro à chaque retrait, et sert aux retraits automatiques. ![La page Comptes de retrait avec les comptes enregistrés](/images/payouts/accounts-light.webp) ![La page Comptes de retrait avec les comptes enregistrés](/images/payouts/accounts-dark.webp) **1. Ouvrez Comptes de retrait** Dans **Paramètres**, onglet **Comptes de retrait**. **2. Ajoutez un compte** **Ajouter un compte de retrait**, puis choisissez le **Type de compte** : * **Mobile money** : **Pays**, **Opérateur** et **Numéro** ; * **Compte bancaire** : **Titulaire du compte**, **Banque** et **IBAN ou RIB**, pour vider un solde carte. Donnez-lui un nom si vous voulez, par exemple « Compte principal ». **3. Choisissez le compte par défaut** **Mettre par défaut** : ce compte est proposé en premier dans **Demander un retrait**. Un compte se renomme ou se supprime à tout moment. Les retraits déjà faits vers un compte supprimé restent dans votre historique. > [!TIP] > > Par l'API, un compte enregistré s'utilise avec `destination_id` dans [Demander un retrait](/api/payouts/create), à la place de `destination`. # Soldes Source: https://docs.mercy-pay.com/payouts/balances Vos soldes sont séparés par canal, par pays et par opérateur. Chaque encaissement crédite le solde qui lui correspond. Un **solde** (wallet) contient les fonds que Mercy Pay vous doit à un moment donné. Vos soldes ne sont **jamais mélangés** entre eux : un solde MTN du Cameroun ne peut pas financer un retrait Orange de Côte d'Ivoire, même si l'un est largement créditeur et l'autre vide. ## Dans le tableau de bord **Soldes** affiche ce qui est **Disponible** et **En attente**, regroupé par **Pays** ou par **Moyens de paiement**, en cartes ou en liste. ![La page Soldes, avec le disponible et l'en attente par pays et par opérateur](/images/payouts/balances-light.webp) ![La page Soldes, avec le disponible et l'en attente par pays et par opérateur](/images/payouts/balances-dark.webp) ## Structure Chaque solde est identifié par cinq éléments : | Champ | Exemple | Rôle | | - | - | - | | `channel` | `mobile_money` ou `card` | Canal d'encaissement | | `country` | `CMR` | Pays, en mobile money seulement | | `provider` | `MTN_MOMO_CMR` | Opérateur, en mobile money seulement | | `currency` | `XAF` | Devise du solde | | `available_balance`, `pending_balance` | `4825`, `0` | En unités mineures | Les soldes carte ont `country` et `provider` à `null`. ## Disponible et en attente `available_balance` est ce que vous pouvez effectivement verser par retrait dès maintenant. `pending_balance` contient des fonds déjà comptés mais pas encore disponibles. Le mobile money crédite directement `available_balance` : dès qu'un paiement passe à `succeeded`, le solde correspondant est disponible pour un retrait. La carte, elle, crédite d'abord `pending_balance`, le temps que les fonds nous soient versés, en général sous 5 jours, avant que les fonds basculent automatiquement vers `available_balance`, sans action de votre part. ## Exemple Après un encaissement au Cameroun (MTN), déjà disponible, et une carte internationale en USD encaissée hier et toujours en attente, la réponse de [`GET /api/v1/account`](/api/account/get) contient : ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "merchant": { "name": "Boutique Ada" }, "wallets": [ { "id": "wal_3f1a8c2e5b7d4e9a8c610d2f4b9a7e15", "object": "wallet", "livemode": false, "created": "2026-10-02T14:05:00+00:00", "channel": "mobile_money", "country": "CMR", "provider": "MTN_MOMO_CMR", "currency": "XAF", "available_balance": 9500, "pending_balance": 0 }, { "id": "wal_7c4e9b1d2a8f4d3e9b605e1a3c8d2f97", "object": "wallet", "livemode": false, "created": "2026-10-02T14:05:00+00:00", "channel": "card", "country": null, "provider": null, "currency": "USD", "available_balance": 0, "pending_balance": 4650 } ] } } ``` La liste seule est disponible avec [`GET /api/v1/wallets`](/api/wallets/list). ## Virer entre deux opérateurs Un retrait puise dans le solde de l'opérateur qui reçoit. Vos clients paient surtout par MTN et vous voulez retirer vers Orange ? Ouvrez le solde du pays et choisissez **Virer vers un autre opérateur**, ou utilisez [`POST /api/v1/wallet_transfers`](/api/wallet-transfers/create). Le montant passe tout de suite d'un solde à l'autre, sans frais. Les deux soldes doivent être du même pays et de la même devise : un solde carte ne peut pas y prendre part. # Retraits Source: https://docs.mercy-pay.com/payouts/overview Un retrait verse des fonds de votre solde vers un compte mobile money, ou vers un compte bancaire pour un solde carte. Il est servi par le solde choisi. Un **retrait** transfère des fonds de votre solde vers un compte mobile money. Un solde carte peut aussi partir vers un compte bancaire (IBAN ou RIB), depuis le tableau de bord uniquement : l'API n'accepte que des destinations mobile money. C'est la seule façon de sortir de l'argent de Mercy Pay. > [card] [Demander un retrait](/payouts/request-a-payout) > > Depuis le tableau de bord, frais affichés avant de confirmer. > [card] [Retraits par l'API](/api/payouts/create) > > Une demande de retrait depuis votre serveur. ## Ce qui débite le solde Un retrait puise dans le **solde du même pays, du même opérateur et de la même devise** que le bénéficiaire. Pour verser 4 000 XAF vers un numéro MTN du Cameroun, il vous faut un solde `CMR · MTN_MOMO_CMR` en XAF qui couvre ce montant : pas un solde Orange, pas un solde dans une autre devise, même équivalente. Pour retirer vers un autre opérateur du même pays, [virez d'abord entre vos soldes](/payouts/balances#virer-entre-deux-operateurs). ## Conditions En live, deux conditions : votre KYC doit être approuvé, et le solde concerné doit couvrir le montant demandé. En sandbox, un retrait part tout de suite, sans KYC ni validation, depuis votre solde de test et vers un numéro Mobile Money de test. Voir [Environnements](/start/environments). ## Validation En live, notre équipe valide chaque retrait à la main : ceux du tableau de bord, ceux de l'API et les retraits automatiques. Un compte suspendu ne peut plus en demander, et un retrait en attente n'est pas validé tant que la suspension dure. ## Commission Mercy Pay retient **2 %** du montant demandé, le temps du virement. Un retrait de 4 000 XAF débite bien 4 000 XAF de votre solde, mais seuls 3 920 XAF partent effectivement vers le bénéficiaire. | Champ | Exemple | Signification | | - | - | - | | `amount` | `4000` | Montant demandé, débité de votre solde | | `fee_amount` | `80` | Commission Mercy Pay (2 % de `amount`) | | `net_amount` | `3920` | Montant réellement envoyé au bénéficiaire | `fee_amount` et `net_amount` sont figés au moment de la création du retrait. Un changement de taux de commission plus tard dans votre historique ne les recalcule jamais rétroactivement. ## Statuts | Statut | Signification | | - | - | | `requested` | Demande enregistrée, en attente d'approbation | | `approved` | Approuvé par Mercy Pay, envoyé à l'opérateur | | `processing` | Virement en cours chez l'opérateur | | `paid` | Versé sur le compte du bénéficiaire | | `failed` | Échec côté opérateur, le motif est dans `failure_reason` | | `rejected` | Refusé par Mercy Pay, le motif est dans `failure_reason` | | `canceled` | Annulé avant son examen, ou pendant qu'il attendait un opérateur indisponible | Le champ `status_detail` précise un retrait encore en route : | `status_detail` | Signification | | - | - | | `delayed` | L'opérateur est indisponible : le retrait attend son retour. Vous pouvez l'annuler depuis le tableau de bord. | | `pending_processing` | Le retrait n'est pas encore parti. Il partira automatiquement, sans action de votre part. | Le solde n'est débité qu'au passage à `paid`. Un retrait `failed`, `rejected` ou `canceled` ne touche jamais votre solde, même s'il a été approuvé entre-temps. Le montant doit simplement être disponible au moment de la demande. ## Origine Le champ `origin` dit d'où vient le retrait : `dashboard`, `api` ou `automatic`. Tous arrivent dans la même liste du tableau de bord, filtrable par origine. ## Destination ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "type": "mobile_money", "country": "CMR", "provider": "MTN_MOMO_CMR", "phone": "237680000000" } ``` Le numéro s'écrit avec l'indicatif du pays, sans le `+` (`237` pour le Cameroun). Pour le Congo, la Côte d'Ivoire et le Bénin, gardez le `0` qui fait partie du numéro local. # Demander un retrait Source: https://docs.mercy-pay.com/payouts/request-a-payout Versez vos fonds vers un compte mobile money depuis le tableau de bord ou l'API, ou un solde carte vers un compte bancaire depuis le tableau de bord. Un retrait verse des fonds vers un compte mobile money, en débitant le solde du **même pays, du même opérateur et de la même devise** que le bénéficiaire. Voir [Retraits](/payouts/overview) pour le détail de la commission et du cycle de statuts. ## Depuis le tableau de bord Les retraits se font en **Live**, après la vérification de votre identité. Choisissez **Live** en bas du menu, puis ouvrez **Retraits** et **Demander un retrait**. **1. Solde** Le solde à retirer : chaque pays et chaque opérateur a le sien, avec ce qui est disponible. **2. Montant** Le **Montant retiré**. Les **Frais** et ce que **Vous recevez** s'affichent tout de suite. **3. Destination** Un [compte de retrait](/payouts/accounts) enregistré, ou un nouveau numéro. **4. Confirmation** Relisez, puis confirmez. Pour votre sécurité, un code envoyé par e-mail peut vous être demandé. ![Demander un retrait, première étape : le choix du solde](/images/payouts/create-light.webp) ![Demander un retrait, première étape : le choix du solde](/images/payouts/create-dark.webp) **Retraits** liste ensuite chaque retrait avec son état et ce que le bénéficiaire a reçu, filtrable par origine : **Tableau de bord**, **API** ou **Automatiques**. ![La liste des retraits avec leur statut et leur origine](/images/payouts/index-light.webp) ![La liste des retraits avec leur statut et leur origine](/images/payouts/index-dark.webp) Depuis le tableau de bord, un solde carte peut aussi partir vers un compte bancaire. Par l'API, seules les destinations mobile money sont acceptées. ## Par l'API Les mêmes retraits, depuis votre serveur. ### Prérequis En live, il vous faut un KYC approuvé et un solde qui couvre le montant. En sandbox, le retrait part tout de suite depuis votre solde de test, sans KYC ni validation : utilisez les numéros de [la page Environnements](/start/environments). **1. Consultez vos soldes** ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl https://mercy-pay.com/api/v1/wallets \ -H "Authorization: Bearer mp_live_XXXXXXXXXXXXXXXX" ``` ```php PHP theme={"theme":{"light":"github-light","dark":"github-dark"}} $ch = curl_init('https://mercy-pay.com/api/v1/wallets'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Authorization: Bearer mp_live_XXXXXXXXXXXXXXXX', ]); $response = curl_exec($ch); curl_close($ch); ``` ```js JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const response = await fetch('https://mercy-pay.com/api/v1/wallets', { headers: { Authorization: 'Bearer mp_live_XXXXXXXXXXXXXXXX', }, }); const data = await response.json(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests response = requests.get( 'https://mercy-pay.com/api/v1/wallets', headers={'Authorization': 'Bearer mp_live_XXXXXXXXXXXXXXXX'}, ) data = response.json() ``` Repérez le solde `channel: mobile_money` avec `country: "CMR"` et `provider: "MTN_MOMO_CMR"`. Sa `currency` est la devise du retrait. **2. Créez le retrait** ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl https://mercy-pay.com/api/v1/payouts \ -H "Authorization: Bearer mp_live_XXXXXXXXXXXXXXXX" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: payout-2026-10-03-001" \ -d '{ "amount": 4000, "currency": "XAF", "destination": { "type": "mobile_money", "country": "CMR", "provider": "MTN_MOMO_CMR", "phone": "237680000000" } }' ``` ```php PHP theme={"theme":{"light":"github-light","dark":"github-dark"}} $ch = curl_init('https://mercy-pay.com/api/v1/payouts'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Authorization: Bearer mp_live_XXXXXXXXXXXXXXXX', 'Content-Type: application/json', 'Idempotency-Key: payout-2026-10-03-001', ]); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([ 'amount' => 4000, 'currency' => 'XAF', 'destination' => [ 'type' => 'mobile_money', 'country' => 'CMR', 'provider' => 'MTN_MOMO_CMR', 'phone' => '237680000000', ], ])); $response = curl_exec($ch); curl_close($ch); ``` ```js JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const response = await fetch('https://mercy-pay.com/api/v1/payouts', { method: 'POST', headers: { Authorization: 'Bearer mp_live_XXXXXXXXXXXXXXXX', 'Content-Type': 'application/json', 'Idempotency-Key': 'payout-2026-10-03-001', }, body: JSON.stringify({ amount: 4000, currency: 'XAF', destination: { type: 'mobile_money', country: 'CMR', provider: 'MTN_MOMO_CMR', phone: '237680000000', }, }), }); const data = await response.json(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests response = requests.post( 'https://mercy-pay.com/api/v1/payouts', headers={ 'Authorization': 'Bearer mp_live_XXXXXXXXXXXXXXXX', 'Idempotency-Key': 'payout-2026-10-03-001', }, json={ 'amount': 4000, 'currency': 'XAF', 'destination': { 'type': 'mobile_money', 'country': 'CMR', 'provider': 'MTN_MOMO_CMR', 'phone': '237680000000', }, }, ) data = response.json() ``` La réponse (HTTP `201`) a le statut `requested`. `amount` est ce qui est débité de votre solde ; `net_amount`, une fois les 2 % de commission retirés, est ce qui part réellement vers le bénéficiaire. ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "id": "po_a9d4e6f21b3c4e5d8f702c9b6a1e3d48", "object": "payout", "livemode": false, "created": "2026-10-03T10:12:00+00:00", "amount": 4000, "fee_amount": 80, "net_amount": 3920, "currency": "XAF", "destination": { "type": "mobile_money", "country": "CMR", "provider": "MTN_MOMO_CMR", "phone": "237680000000" }, "status": "requested", "failure_code": null, "failure_reason": null, "metadata": null } } ``` **3. Suivez le statut** En live, notre équipe valide chaque retrait à la main, y compris ceux de l'API, puis le transmet à l'opérateur. Prévoyez ce délai si vous versez de l'argent à vos propres clients. Inutile d'interroger `GET /api/v1/payouts/{id}` en boucle : le webhook `payout.paid` ou `payout.failed` vous prévient dès que l'un des deux survient. ## Utiliser un compte enregistré Enregistrez vos numéros dans **Paramètres, Comptes de retrait**, puis envoyez `destination_id` à la place de `destination`. L'API refuse un compte qui n'appartient pas à votre boutique. ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "amount": 4000, "currency": "XAF", "destination_id": "5f1c2a7e-8d34-4b19-a6e2-0c7d9e3b1f42" } ``` ## Retraits automatiques Sur la page **Soldes**, ouvrez un solde et réglez son retrait automatique : * **Fréquence :** chaque jour, chaque semaine ou chaque mois. * **Compte qui reçoit :** un compte enregistré. Un solde mobile money part vers un numéro du même opérateur. * **Minimum :** en dessous de ce montant, le retrait attend la prochaine échéance. * **Réserve :** la somme qui reste sur le solde. À chaque échéance, nous demandons le solde disponible moins la réserve. Rien n'est demandé si un retrait de ce solde est encore en cours. Chaque retrait automatique est validé par notre équipe et porte l'origine `automatic`. ## Erreurs fréquentes | Message | Cause | | - | - | | `Solde disponible insuffisant pour ce montant.` | Le solde du pays et de l'opérateur ne couvre pas le montant | | `Opérateur Mobile Money invalide pour ce pays.` | Le code opérateur ne correspond pas au pays | | `Ce compte de retrait est introuvable.` | Le `destination_id` n'existe pas ou appartient à une autre boutique | # Environnements Source: https://docs.mercy-pay.com/start/environments Sandbox et live sont deux espaces totalement isolés : clés, soldes et webhooks séparés. Mercy Pay sépare **sandbox** et **live** de bout en bout. Chaque clé API appartient à un seul des deux. Un paiement créé en sandbox n'apparaît jamais dans un solde live. ## Ce qui change entre les deux | Élément | Sandbox | Live | | - | - | - | | Clés | `mp_sandbox_…` | `mp_live_…` | | Soldes | Soldes de test | Soldes réels | | Paiements | De test, aucun argent réel ne circule | Réels | | Retraits | De test, versés ou refusés selon le numéro, sans examen | Examinés par notre équipe, une fois le KYC approuvé | | Webhooks | `livemode: false` | `livemode: true` | | Accès | Immédiat dès la création de l'espace | Après approbation du KYC | Le sandbox sert à intégrer et à tester sans attendre d'approbation. Le live ouvre après le KYC, car c'est là que de l'argent réel circule. Dans le tableau de bord, le sélecteur **Sandbox / Live** en haut de page choisit les données affichées. Un endpoint de webhook peut écouter le sandbox, le live ou les deux, voir [Webhooks](/developers/webhooks/overview). ## Clés sandbox et live Une clé sandbox reste lisible : copiez-la quand vous voulez depuis la console Développeurs. Une clé live ne s'affiche qu'une seule fois, à sa création. Gardez-la dans vos secrets serveur. ## Tester un paiement En sandbox, la page de paiement affiche directement les données de test. Choisissez **Paiement réussi** ou **Paiement refusé** pour le mobile money, ou copiez la carte de test. ### Carte | Numéro | Expiration | Code | | - | - | - | | `4242 4242 4242 4242` | `12/34` | `123` | ### Mobile money Ces numéros ne fonctionnent qu'en sandbox. Chacun donne toujours le même résultat. | Pays | Opérateur | Réussite | Échec | | - | - | - | - | | Bénin | MTN Mobile Money | `22951345789` | `22951345029` | | Bénin | Moov Money | `22995345789` | `22995345679` | | Cameroun | MTN Mobile Money | `237653456789` | `237653456019` | | Cameroun | Orange Money | `237693456789` | `237693456049` | | Congo | Airtel Money | `242053456789` | `242053456049` | | Congo | MTN Mobile Money | `242063456789` | `242063456049` | | Côte d'Ivoire | MTN Mobile Money | `2250503456789` | `2250503456029` | | Côte d'Ivoire | Moov Money | `2250103456789` | `2250103456049` | | Côte d'Ivoire | Orange Money | `2250734567890` | `2250734567030` | | Gabon | Airtel Money | `24174345678` | `24174345048` | | Kenya | M-Pesa | `254703456789` | `254703456049` | | Ouganda | Airtel Money | `256753456789` | `256753456049` | | Ouganda | MTN Mobile Money | `256783456789` | `256783456019` | | RD Congo | Airtel Money | `243973456789` | `243973456069` | | RD Congo | M-Pesa | `243813456789` | `243813456049` | | RD Congo | Orange Money | `243893456789` | `243893456049` | | Rwanda | Airtel Money | `250733456789` | `250733456049` | | Rwanda | MTN Mobile Money | `250783456789` | `250783456019` | | Sierra Leone | Orange Money | `23276123456` | Aucun | | Sénégal | Free Money | `221763456789` | `221763456049` | | Sénégal | Orange Money | `221773456789` | `221773456049` | | Zambie | Airtel Money | `260973456789` | `260973456049` | | Zambie | MTN Mobile Money | `260763456789` | `260763456019` | | Zambie | Zamtel Money | `260953456700` | `260953456704` | Moov Money Gabon, M-Pesa Mozambique : pas encore de numéro de test. Testez sur un autre opérateur. ## Tester les cas difficiles ### Une cause d'échec précise Sur la page de paiement sandbox, des boutons remplissent le numéro qui produit chaque cause, quand l'opérateur la propose : **Solde insuffisant**, **Refusé par le client**, **Plafond atteint**, **Numéro inconnu**, ou **Reste en attente** pour un paiement qui ne se conclut pas. Le paiement échoue avec le `failure_code` correspondant, et votre webhook `payment.failed` le reçoit. ### Un retrait Un retrait sandbox part tout de suite, sans examen et sans KYC, depuis votre solde de test. Vers le numéro de réussite de l'opérateur, il passe à `paid`. Pour un échec, utilisez le numéro de retrait refusé : | Opérateur | Retrait refusé | | - | - | | MTN Mobile Money Cameroun | `237653456089` | | Orange Money Cameroun | `237693456119` | | MTN Mobile Money Congo | `242063456089` | | Airtel Money Congo | `242053456089` | | Airtel Money Gabon | `24174345088` | | MTN Mobile Money Bénin | `22951345089` | | Moov Money Bénin | `22995345219` | | MTN Mobile Money Côte d'Ivoire | `2250503456089` | | Orange Money Côte d'Ivoire | `2250734567110` | | Moov Money Côte d'Ivoire | `2250103456119` | | Zamtel Money Zambie | `260953456712` | | M-Pesa Kenya | `254703456089` | Vous recevez `payout.paid` ou `payout.failed`, comme en live. ### Un renouvellement d'abonnement Sans attendre la vraie date : sur un abonnement sandbox, **Gérer** puis **Passer à l'échéance (test)**, ou [`POST /api/v1/subscriptions/{id}/advance`](/api/subscriptions/advance). La facture est créée et son paiement lancé tout de suite. # Déploiement Source: https://docs.mercy-pay.com/start/go-live Mettre une intégration en production : KYC, clé live et endpoints webhooks. Passer en production demande trois étapes. **1. Faites approuver votre KYC** Depuis **KYC**, choisissez votre type de compte et envoyez les documents demandés. Un compte **Individuel** demande une pièce d'identité et son volume mensuel est plafonné (1 000 000 XAF par exemple). Un compte **Entreprise** demande aussi les documents de votre société et n'a pas de plafond. L'équipe Mercy Pay examine le dossier. Tant que le KYC n'est pas approuvé, les clés live sont refusées et les retraits sont indisponibles. **2. Créez une clé live** Une fois le KYC approuvé, ouvrez l'onglet **Clés API** de la [console Développeurs](/developers/console), passez en **Live** et générez une clé `mp_live_…`. Mercy Pay vous demande une confirmation : votre code 2FA si vous l'avez activé, sinon un code reçu par e-mail. Stockez la clé dans le gestionnaire de secrets de votre serveur, jamais dans le dépôt de code. **3. Déclarez votre endpoint webhook** Depuis l'onglet **Webhooks** de la console, ajoutez l'URL HTTPS de votre serveur et les événements à recevoir. Vérifiez la signature de chaque requête, voir [Sécurité des webhooks](/developers/webhooks/security). ## Vérifications avant le premier paiement live 1. Un paiement de test de 100 unités réussit en sandbox. 2. Votre endpoint répond `2xx` à l'événement `payment.succeeded`. 3. Votre clé live est présente dans le gestionnaire de secrets, et la clé sandbox est révoquée si elle n'est plus utilisée. # Démarrage rapide Source: https://docs.mercy-pay.com/start/quickstart Créez votre espace marchand et encaissez un premier paiement de test en sandbox. Ce guide vous mène d'un compte vierge à un paiement de test, en sandbox. Aucun argent réel n'est déplacé. > [!TIP] > > Le tableau de bord vous guide aussi pas à pas. Après la création de votre boutique, la page **Faites votre premier paiement** lance cette requête pour vous, sans terminal, et affiche le résultat en direct. **1. Créez votre espace marchand** Créez votre compte, confirmez le code reçu par e-mail, puis répondez aux 5 écrans de démarrage : nom de la boutique, pays, activité, volume estimé et façon d'encaisser. Mercy Pay génère automatiquement une clé sandbox (`mp_sandbox_…`). Vous la retrouvez à tout moment dans la console Développeurs, onglet **Clés API**, avec le bouton **Copier**. ![L'onglet Clés API de la console Développeurs](/images/developers/console-api-keys-light.webp) ![L'onglet Clés API de la console Développeurs](/images/developers/console-api-keys-dark.webp) **2. Vérifiez votre clé** ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} curl https://mercy-pay.com/api/v1/account \ -H "Authorization: Bearer mp_sandbox_XXXXXXXXXXXXXXXX" ``` La réponse indique votre marchand, l'environnement de la clé et vos soldes : ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "merchant": { "uuid": "0b6c2f1e-6d1a-4a3e-9d0a-2f3c1b7e8a41", "name": "Boutique Ada", "status": "pending", "kyc_status": "not_submitted", "default_currency": "XAF" }, "api_key": { "environment": "sandbox", "key_prefix": "mp_sandbox_XXXXXXXX" }, "wallets": [] } } ``` **3. Créez une session de checkout** Les montants sont des entiers en unités mineures : `5000` représente 5 000 XAF. Voir [Montants et devises](/payments/amounts-and-currencies). ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} curl https://mercy-pay.com/api/v1/checkout_sessions \ -H "Authorization: Bearer mp_sandbox_XXXXXXXXXXXXXXXX" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: commande-2026-0001" \ -d '{ "amount": 5000, "currency": "XAF", "description": "Consultation à domicile", "success_url": "https://boutique-ada.example/merci", "cancel_url": "https://boutique-ada.example/panier" }' ``` La réponse (HTTP `201`) contient l'URL de la page de paiement : ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "id": "cs_01J9Z3V8QK4M2XW7R6TB5N0HYD", "object": "checkout_session", "livemode": false, "created": "2026-10-03T10:00:00+00:00", "amount": 5000, "currency": "XAF", "description": "Consultation à domicile", "status": "created", "checkout_url": "https://mercy-pay.com/paiement/01J9Z3V8QK4M2XW7R6TB5N0HYD", "expires_at": "2026-10-03T10:30:00+00:00" } } ``` **4. Payez en sandbox** Ouvrez `checkout_url`, choisissez **Mobile Money** ou **Carte bancaire**, puis validez. En sandbox, l'encaissement est simulé. Vérifiez ensuite le statut : ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} curl https://mercy-pay.com/api/v1/checkout_sessions/cs_01J9Z3V8QK4M2XW7R6TB5N0HYD \ -H "Authorization: Bearer mp_sandbox_XXXXXXXXXXXXXXXX" ``` ## Étapes suivantes * Recevez les événements sur votre serveur plutôt que d'interroger l'API en boucle : [Webhooks](/developers/webhooks/overview). * Suivez vos requêtes, vos webhooks et vos erreurs dans la [console Développeurs](/developers/console). * Passez en live une fois votre KYC approuvé : [Environnements](/start/environments). * Si vous préférez construire votre propre formulaire de paiement plutôt que rediriger vers la page hébergée, voyez la charge directe dans [Accepter un paiement](/payments/accept-a-payment). # Vérifier votre identité Source: https://docs.mercy-pay.com/start/verify-your-account Ce qu'il faut préparer pour ouvrir le mode live et recevoir de vrais paiements. Le sandbox reste ouvert sans vérification. Pour encaisser pour de vrai et demander des retraits, votre dossier doit être validé par notre équipe. Nous répondons en général sous 24 heures ouvrées. ## Ce qu'il faut préparer | Compte | À fournir | | - | - | | En votre nom | Pièce d'identité : recto et verso, ou la page photo du passeport | | Entreprise | Pièce d'identité, raison sociale, numéro d'enregistrement, registre de commerce et justificatif de domicile | Un compte en votre nom a un plafond mensuel d'encaissement. Un compte entreprise n'en a pas. Les documents sont acceptés en PDF, JPG ou PNG, jusqu'à 10 Mo. Sur téléphone, vous pouvez les prendre en photo directement. ## Le parcours Cliquez sur **Vérifier mon identité** dans le bandeau sandbox, en haut de chaque page du tableau de bord. ![Le premier écran de la vérification : en votre nom ou pour une entreprise](/images/start/kyc-light.webp) ![Le premier écran de la vérification : en votre nom ou pour une entreprise](/images/start/kyc-dark.webp) **1. Choisissez votre type de compte** En votre nom ou pour une entreprise. **2. Décrivez votre activité** Ce que vous vendez et à qui, avec votre site si vous en avez un. **3. Vérifiez votre identité** Date de naissance, adresse et pièce d'identité. Chaque document est enregistré dès son envoi : vous pouvez arrêter et reprendre plus tard. **4. Relisez et envoyez** Votre dossier n'entre en revue qu'à ce moment-là. ## Après l'envoi Vous suivez l'avancement sur la page **KYC** et vous recevez un e-mail à chaque décision. Si un document est refusé, le motif s'affiche à côté : remplacez-le ou utilisez **Corriger mes informations**, puis cliquez sur **Renvoyer mon dossier** pour le remettre en revue. Une fois validé, un e-mail vous mène à votre premier vrai paiement. # Accepter un paiement récurrent Source: https://docs.mercy-pay.com/subscriptions/api-guide Créez un plan, inscrivez un client, laissez Mercy Pay facturer chaque cycle. Ce guide couvre la mise en place d'un abonnement, du plan jusqu'au premier prélèvement. Voir [Abonnements](/subscriptions/overview) pour le fonctionnement général, notamment la différence de comportement entre carte et mobile money. **1. Créez un plan** Un plan fixe le prix, la devise et la fréquence, pas le moyen de paiement, qui se choisit par client à l'étape suivante. Un même plan est donc réutilisable pour vos clients carte comme pour vos clients mobile money. ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl https://mercy-pay.com/api/v1/plans \ -H "Authorization: Bearer mp_live_XXXXXXXXXXXXXXXX" \ -H "Content-Type: application/json" \ -d '{ "name": "Abonnement Pro", "amount": 5000, "currency": "XAF", "interval": "monthly" }' ``` ```php PHP theme={"theme":{"light":"github-light","dark":"github-dark"}} $ch = curl_init('https://mercy-pay.com/api/v1/plans'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Authorization: Bearer mp_live_XXXXXXXXXXXXXXXX', 'Content-Type: application/json', ]); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([ 'name' => 'Abonnement Pro', 'amount' => 5000, 'currency' => 'XAF', 'interval' => 'monthly', ])); $response = curl_exec($ch); curl_close($ch); ``` ```js JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const response = await fetch('https://mercy-pay.com/api/v1/plans', { method: 'POST', headers: { Authorization: 'Bearer mp_live_XXXXXXXXXXXXXXXX', 'Content-Type': 'application/json', }, body: JSON.stringify({ name: 'Abonnement Pro', amount: 5000, currency: 'XAF', interval: 'monthly', }), }); const data = await response.json(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests response = requests.post( 'https://mercy-pay.com/api/v1/plans', headers={'Authorization': 'Bearer mp_live_XXXXXXXXXXXXXXXX'}, json={ 'name': 'Abonnement Pro', 'amount': 5000, 'currency': 'XAF', 'interval': 'monthly', }, ) data = response.json() ``` Vous pouvez aussi créer un plan depuis votre tableau de bord, section Abonnements, sans passer par l'API. **2. Inscrivez votre client, avec son moyen de paiement** ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl https://mercy-pay.com/api/v1/subscriptions \ -H "Authorization: Bearer mp_live_XXXXXXXXXXXXXXXX" \ -H "Content-Type: application/json" \ -d '{ "plan": "plan_9b1e4c2a6d3f4a5e8f6c2d9b1a7e3c48", "payment_method": "card", "customer_name": "Ada Lovelace", "customer_email": "ada@example.com" }' ``` ```php PHP theme={"theme":{"light":"github-light","dark":"github-dark"}} $ch = curl_init('https://mercy-pay.com/api/v1/subscriptions'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Authorization: Bearer mp_live_XXXXXXXXXXXXXXXX', 'Content-Type: application/json', ]); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([ 'plan' => 'plan_9b1e4c2a6d3f4a5e8f6c2d9b1a7e3c48', 'payment_method' => 'card', 'customer_name' => 'Ada Lovelace', 'customer_email' => 'ada@example.com', ])); $response = curl_exec($ch); curl_close($ch); ``` ```js JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const response = await fetch('https://mercy-pay.com/api/v1/subscriptions', { method: 'POST', headers: { Authorization: 'Bearer mp_live_XXXXXXXXXXXXXXXX', 'Content-Type': 'application/json', }, body: JSON.stringify({ plan: 'plan_9b1e4c2a6d3f4a5e8f6c2d9b1a7e3c48', payment_method: 'card', customer_name: 'Ada Lovelace', customer_email: 'ada@example.com', }), }); const data = await response.json(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests response = requests.post( 'https://mercy-pay.com/api/v1/subscriptions', headers={'Authorization': 'Bearer mp_live_XXXXXXXXXXXXXXXX'}, json={ 'plan': 'plan_9b1e4c2a6d3f4a5e8f6c2d9b1a7e3c48', 'payment_method': 'card', 'customer_name': 'Ada Lovelace', 'customer_email': 'ada@example.com', }, ) data = response.json() ``` `customer_email` est obligatoire : votre client y reçoit ses reçus, ses rappels et l'accès à son espace client. `payment_method` est facultatif : sans lui, le client choisit sur la page de paiement. Avec `payment_method: "mobile_money"` et `customer_phone`, `customer_country` et `customer_provider` (par exemple `MTN_MOMO_CMR`), la première demande part tout de suite sur ce téléphone. **3. Faites payer votre client pour activer** L'abonnement démarre `incomplete`. Redirigez votre client vers `next_action.redirect_url` (`https://mercy-pay.com/abonnements/{uuid}/payer`), une page hébergée par Mercy Pay, sans rien à intégrer de votre côté. Il y paie le premier cycle par carte ou mobile money, ou enregistre son moyen de paiement si le plan a un essai. Donnez l'accès à votre service quand vous recevez `subscription.created` : l'abonnement est alors `active` ou `trialing`. Sans paiement sous 48 heures, l'inscription est annulée. **4. Laissez Mercy Pay facturer chaque cycle** Rien à planifier. À chaque échéance, Mercy Pay essaie les moyens enregistrés du client dans l'ordre : il débite la carte, ou envoie une demande de paiement sur son téléphone 2 jours avant l'échéance. Votre client reçoit un rappel 3 jours avant, puis son reçu. En cas d'échec, Mercy Pay relance à J+1, J+3 et J+7, puis annule. Écoutez `invoice.paid` et `invoice.payment_failed` pour suivre chaque facture, et `subscription.unpaid` pour couper l'accès d'un client mobile money qui n'a pas payé à l'échéance. Voir [Gérer les webhooks](/developers/webhooks/handle). **5. Annulez quand nécessaire** ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST https://mercy-pay.com/api/v1/subscriptions/sub_2f8a6c1e4b3d4a5e8f6c2d9b1a7e3c48/cancel \ -H "Authorization: Bearer mp_live_XXXXXXXXXXXXXXXX" ``` ```php PHP theme={"theme":{"light":"github-light","dark":"github-dark"}} $ch = curl_init('https://mercy-pay.com/api/v1/subscriptions/sub_2f8a6c1e4b3d4a5e8f6c2d9b1a7e3c48/cancel'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Authorization: Bearer mp_live_XXXXXXXXXXXXXXXX', ]); $response = curl_exec($ch); curl_close($ch); ``` ```js JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const response = await fetch( 'https://mercy-pay.com/api/v1/subscriptions/sub_2f8a6c1e4b3d4a5e8f6c2d9b1a7e3c48/cancel', { method: 'POST', headers: { Authorization: 'Bearer mp_live_XXXXXXXXXXXXXXXX', }, }, ); const data = await response.json(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests response = requests.post( 'https://mercy-pay.com/api/v1/subscriptions/sub_2f8a6c1e4b3d4a5e8f6c2d9b1a7e3c48/cancel', headers={'Authorization': 'Bearer mp_live_XXXXXXXXXXXXXXXX'}, ) data = response.json() ``` Immédiat par défaut. Passez `{"at_period_end": true}` pour laisser l'abonnement courir jusqu'à la fin du cycle payé. ## Erreurs fréquentes | Message | Cause | | - | - | | Erreur de validation sur `customer_email` | Adresse e-mail absente : elle est obligatoire pour tout abonnement | | Erreur de validation sur `customer_provider` | `payment_method: "mobile_money"` sans opérateur valide pour le pays, ou numéro invalide | | Erreur de validation sur `plan` | Plan inconnu, archivé, ou appartenant à un autre marchand | # Espace client Source: https://docs.mercy-pay.com/subscriptions/billing-portal Vos abonnés gèrent eux-mêmes leurs abonnements, leurs moyens de paiement et leurs factures, sur une page à vos couleurs. L'espace client est une page aux couleurs de votre boutique. Votre abonné y fait seul ce qu'il vous demanderait sinon par message : payer une facture en retard, changer de carte, passer à une autre formule ou arrêter. Vous n'avez rien à construire. ![L'espace client : la liste des abonnements avec leur statut et le prochain paiement](/images/subscriptions/portal-home-light.webp) ![L'espace client : la liste des abonnements avec leur statut et le prochain paiement](/images/subscriptions/portal-home-dark.webp) L'accueil : chaque abonnement, son statut et le prochain paiement. Un bandeau signale un paiement en attente. ## Ce que votre abonné y fait > [tab] Abonnements > > Il ouvre un abonnement pour : > > * voir sa formule, le prochain paiement et ses dernières factures ; > * passer à une autre formule parmi celles que vous proposez. Une formule plus chère s'applique tout de suite contre la différence au prorata, une moins chère à la prochaine échéance ; > * choisir le moyen de paiement de cet abonnement ; > * payer d'avance la période suivante ; > * annuler en fin de période, puis revenir sur sa décision. > > ![Le détail d'un abonnement dans l'espace client, avec les formules proposées](/images/subscriptions/portal-subscription-light.webp) > ![Le détail d'un abonnement dans l'espace client, avec les formules proposées](/images/subscriptions/portal-subscription-dark.webp) > [tab] Moyens de paiement > > Il ajoute des cartes et des numéros mobile money de n'importe quel pays, choisit celui par défaut et retire ceux qu'il n'utilise plus. Un numéro est confirmé par son premier paiement réussi, sans SMS. > > ![Les moyens de paiement enregistrés, avec celui par défaut](/images/subscriptions/portal-methods-light.webp) > ![Les moyens de paiement enregistrés, avec celui par défaut](/images/subscriptions/portal-methods-dark.webp) > [tab] Factures > > Toutes ses factures, filtrables par abonnement, avec le bouton **Payer** pour celles en attente et le reçu de celles déjà payées. > > ![La liste des factures avec leur statut et le reçu](/images/subscriptions/portal-invoices-light.webp) > ![La liste des factures avec leur statut et le reçu](/images/subscriptions/portal-invoices-dark.webp) Sur téléphone, les quatre sections passent en onglets en haut de l'écran. ![L'espace client sur téléphone](/images/subscriptions/portal-home-mobile-light.webp) ![L'espace client sur téléphone](/images/subscriptions/portal-home-mobile-dark.webp) Il ne voit que ses propres abonnements chez vous, reconnus par son adresse e-mail. Une inscription jamais payée n'y apparaît pas. ## Comment votre abonné y accède **1. Par ses e-mails** Chaque e-mail d'abonnement (rappel, reçu, échec de paiement) contient un lien vers son espace, valable 7 jours. **2. Par un lien sur votre site** Dans **Abonnements**, copiez le lien d'accès et mettez-le sur votre site ou dans votre bio. Votre client y saisit son adresse e-mail et reçoit un lien valable une heure. La réponse est la même que l'adresse soit connue ou non. ![La page où le client demande son lien d'accès](/images/subscriptions/portal-request-light.webp) ![La page où le client demande son lien d'accès](/images/subscriptions/portal-request-dark.webp) **3. Par un bouton de votre site (développeurs)** Votre serveur crée un accès d'une heure avec [Ouvrir l'espace client](/api/billing-portal/create-session) et redirige votre client vers l'adresse reçue. Avec `return_url`, l'espace affiche un lien « Retour vers » suivi du nom de votre boutique. ## Choisir les formules proposées Dans **Plans d'abonnement**, cochez **Proposée dans l’espace client** sur chaque formule vers laquelle vos abonnés peuvent passer seuls. Seules les formules de la même devise que la leur leur sont proposées. Par l'API, c'est le champ `portal_switchable` du [plan](/api/plans/update). ## Désactiver l'espace client Dans **Paramètres**, coupez **Activer l'espace client**. Les liens déjà envoyés cessent de fonctionner, les e-mails n'en contiennent plus et [Ouvrir l'espace client](/api/billing-portal/create-session) répond `403`. Vous gérez alors seul les abonnements, depuis le tableau de bord ou l'API. # Clients et moyens de paiement Source: https://docs.mercy-pay.com/subscriptions/customers Retrouvez un abonné et les cartes et numéros mobile money avec lesquels il paie. Un **client** est créé automatiquement à la première inscription d'une adresse e-mail chez vous, dans un environnement. Il porte les moyens de paiement que ses abonnements utilisent : des cartes et des numéros mobile money de n'importe quel pays, dont un par défaut. À chaque échéance, ses moyens sont essayés dans l'ordre : celui choisi pour l'abonnement, sinon celui par défaut, puis les autres. Voir [Abonnements](/subscriptions/overview). Seul votre client ajoute un moyen de paiement, sur la page de paiement d'un abonnement ou depuis son [espace client](/subscriptions/billing-portal), car il faut sa carte ou son téléphone. L'API permet de les consulter, de choisir celui par défaut et d'en retirer un. ## L'objet client ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "id": "cus_c71d0e5a3b2f4e8a9c1d5f6a7b8c9d01", "object": "customer", "livemode": false, "created": "2026-11-07T10:00:00+00:00", "email": "awa@example.com", "name": "Awa Ndiaye", "metadata": null, "default_payment_method": "pm_8e2f1a7c5d4b4c3e9a1f0b6d2e8c4a15", "payment_methods": [ { "id": "pm_8e2f1a7c5d4b4c3e9a1f0b6d2e8c4a15", "object": "payment_method", "livemode": false, "created": "2026-11-07T10:00:00+00:00", "type": "card", "label": "Visa •• 4242", "brand": "visa", "last4": "4242", "exp_month": 9, "exp_year": 2029, "country": null, "provider": null, "phone_last_digits": null, "verified": true }, { "id": "pm_3a9c6e2d1f8b4d7a8e5c2b4f6a9d1c37", "object": "payment_method", "livemode": false, "created": "2026-11-07T10:05:00+00:00", "type": "mobile_money", "label": "MTN Mobile Money •• 12", "brand": null, "last4": null, "exp_month": null, "exp_year": null, "country": "CMR", "provider": "MTN_MOMO_CMR", "phone_last_digits": "12", "verified": false } ], "subscriptions_count": 2 } } ``` | Champ | Description | | - | - | | `default_payment_method` | `uuid` du moyen utilisé en premier, `null` si aucun | | `payment_methods` | Moyens enregistrés, du plus ancien au plus récent. Renvoyé par `GET /api/v1/customers/{id}` | | `verified` | `true` dès qu'un paiement a réussi avec ce moyen. Un numéro mobile money est confirmé ainsi, sans SMS | | `phone_last_digits` | Les 2 derniers chiffres du numéro. Le numéro complet n'est jamais renvoyé | L'`uuid` du client figure aussi dans chaque [abonnement](/api/subscriptions/create), champ `customer`. ## Par l'API > [card] [Lister les clients](/api/customers/list) > > Retrouvez un client par son adresse e-mail. > [card] [Lire un client](/api/customers/get) > > Le client et ses moyens de paiement. > [card] [Choisir le moyen par défaut](/api/customers/set-default) > > Celui essayé en premier à chaque échéance. > [card] [Retirer un moyen](/api/customers/remove-payment-method) > > Il ne sera plus jamais utilisé. # E-mails envoyés à vos abonnés Source: https://docs.mercy-pay.com/subscriptions/emails Rappel, reçu, échec de paiement : ce que reçoit votre client, à vos couleurs, sans que vous ayez rien à écrire. Mercy Pay écrit à vos abonnés à chaque moment important, au nom de votre boutique. Chaque e-mail contient un lien vers leur [espace client](/subscriptions/billing-portal). > [tab] Rappel > > **3 jours avant l'échéance.** En mobile money, la demande de paiement part 2 jours avant, et l'e-mail porte le bouton pour payer. > > ![L'e-mail de rappel avant l'échéance](/images/emails/subscription-upcoming-light.webp) > [tab] Reçu > > **À chaque paiement réussi**, avec le montant, la période payée et le moyen utilisé. > > ![L'e-mail de reçu d'un abonnement](/images/emails/subscription-receipt-light.webp) > [tab] Échec > > **Quand un paiement échoue**, avec la raison et un bouton pour payer. Mercy Pay réessaie aussi seul à J+1, J+3 et J+7. > > ![L'e-mail envoyé quand un paiement échoue](/images/emails/subscription-payment-failed-light.webp) > [tab] Accès > > **Quand votre client demande l'accès à son espace**, ou quand vous le lui envoyez depuis sa fiche avec **Envoyer un accès à son espace**. > > ![L'e-mail avec le lien vers l'espace client](/images/emails/billing-portal-link-light.webp) Votre client reçoit aussi un e-mail quand son abonnement s'arrête : annulation prévue, annulation, ou fin d'un nombre d'échéances limité. Le nom, le logo et la couleur viennent de **Paramètres**, onglet **Image de marque**. # Abonnements Source: https://docs.mercy-pay.com/subscriptions/overview Facturez vos clients en récurrent, cycle après cycle, sans y repenser. Un **abonnement** relie un de vos clients à un **plan** (un prix et un rythme que vous définissez une fois). Il se facture tout seul à chaque échéance jusqu'à son annulation. Un vérificateur interne tourne toutes les cinq minutes : il crée chaque facture, l'encaisse, relance en cas d'échec et prévient votre client. > [card] [Créer un plan](/subscriptions/plans) > > Votre formule et votre lien d'inscription, sans code. > [card] [Paiement récurrent pas à pas](/subscriptions/api-guide) > > Plan, inscription et webhooks, par l'API. ## Le client paie avant d'utiliser Un abonnement créé est `incomplete` tant que le premier paiement n'est pas fait. Le client paie sur une page aux couleurs de votre boutique, `https://mercy-pay.com/abonnements/{uuid}/payer`, renvoyée dans `next_action.redirect_url` : * **Sans essai** : il paie le premier cycle, par carte ou en validant la demande reçue sur son téléphone. L'abonnement passe à `active` au paiement réussi. * **Avec essai** : il enregistre une carte ou un numéro mobile money, sans être débité. L'abonnement passe à `trialing` et le premier paiement a lieu à la fin de l'essai. `subscription.created` part à ce moment-là, pas à la création. Sans paiement, l'inscription est annulée sans prévenir le client : au bout de 48 heures pour un abonnement créé par l'API ou le tableau de bord, de 30 minutes pour une inscription depuis un lien de paiement. Une inscription jamais payée n'apparaît ni dans vos chiffres, ni dans l'espace client. Un client ne peut pas avoir deux abonnements en cours au même plan. L'essai d'un plan n'est accordé qu'une fois par client : même e-mail, même carte ou même numéro. Un client qui l'a déjà eu paie le premier cycle normalement. ## Les moyens de paiement sont ceux du client Un plan fixe le prix, la devise et le rythme, jamais le moyen de paiement. Chaque client a, chez vous, son propre portefeuille : des cartes et des numéros mobile money de n'importe quel pays, dont un par défaut. Un paiement dans une autre devise est converti au moment du prélèvement. Tous les abonnements du client puisent dans ce portefeuille. Voir [Clients](/subscriptions/customers). L'adresse e-mail du client est obligatoire pour tout abonnement : il y reçoit ses reçus, ses rappels et l'accès à son [espace client](/subscriptions/billing-portal). ## Encaisser une échéance À chaque échéance, les moyens du client sont essayés dans l'ordre : d'abord celui choisi pour cet abonnement, sinon celui par défaut, puis tous les autres. Une seule demande mobile money est en cours à la fois. Un numéro mobile money est confirmé par son premier paiement réussi. | | Carte | Mobile money | | - | - | - | | Quand | Le jour de l'échéance | La demande part 2 jours avant l'échéance, avec un lien de paiement par e-mail | | Comment | Prélèvement automatique, sans action du client | Le client valide la demande sur son téléphone avec son code | | Si rien n'est payé à l'échéance | `past_due` (`subscription.past_due`) | `unpaid` (`subscription.unpaid`) : coupez l'accès à votre service jusqu'au paiement | Un opérateur mobile money ne débite jamais un compte sans la validation de son titulaire. Mercy Pay envoie donc la demande au bon moment, au bon numéro, et la renouvelle en cas d'échec. Le client peut aussi payer la période suivante d'avance depuis son espace. ## Rappels et relances * **3 jours avant l'échéance** : le client reçoit un rappel par e-mail, et vous recevez `invoice.upcoming`. * **À l'échéance** : la facture est créée (`invoice.created`) et encaissée. En mobile money, elle est créée 2 jours plus tôt. * **Si le paiement échoue** : le client reçoit un e-mail avec un lien pour payer, et une nouvelle tentative a lieu à J+1, J+3 et J+7 après l'échéance. * **Si la tentative de J+7 échoue** : la facture passe à `failed`, l'abonnement est annulé (`subscription.canceled`) et les propriétaires et administrateurs de la boutique reçoivent un e-mail. Un paiement réussi à n'importe quel moment remet l'abonnement à `active` et envoie `invoice.paid`. Le client reçoit son reçu. ## Plans | Champ | Description | | - | - | | `name` | Nom affiché, par exemple "Abonnement Pro" | | `amount` | Prix par cycle, en unités mineures | | `currency` | Devise ISO 4217 | | `interval` | `daily`, `weekly`, `monthly` ou `yearly` | | `interval_count` | Nombre d'intervalles entre deux échéances (`monthly` et `3` : tous les 3 mois) | | `trial_period_days` | Jours gratuits avant le premier paiement, `0` si aucun | | `portal_switchable` | `true` pour que vos abonnés puissent passer à ce plan depuis leur espace client | Le prix, la devise et le rythme d'un plan ne changent jamais : chaque abonné paie ce qu'il a accepté. Pour un nouveau prix, créez un nouveau plan, puis [changez le plan](#changer-de-plan) de vos abonnés. Un plan sans abonné peut être supprimé, sinon il est archivé et n'accepte plus de nouvel abonné. Pour un rythme mensuel ou annuel, l'échéance revient au même jour du mois dès que le mois le permet : souscrit le 31 janvier, un abonnement se facture le 28 février puis le 31 mars. ## Cycle de vie ```mermaid theme={"theme":{"light":"github-light","dark":"github-dark"}} stateDiagram-v2 direction LR [*] --> incomplete: inscription incomplete --> trialing: moyen enregistré (essai) incomplete --> active: premier paiement incomplete --> canceled: jamais payé trialing --> active: fin de l'essai payée active --> past_due: carte refusée active --> unpaid: mobile money non payé past_due --> active: paiement réussi unpaid --> active: paiement réussi past_due --> canceled: échec à J+7 unpaid --> canceled: échec à J+7 active --> paused: pause paused --> active: reprise active --> canceled: annulation ``` | Statut | Signification | | - | - | | `incomplete` | Inscription pas encore payée. Annulée sans paiement au bout de 48 heures, ou 30 minutes depuis un lien de paiement | | `trialing` | Période d'essai, aucun paiement encore | | `active` | Facturé normalement | | `past_due` | Paiement carte en échec, une nouvelle tentative est programmée | | `unpaid` | Échéance mobile money passée sans paiement : l'accès est à couper, les relances continuent | | `paused` | En pause : aucune facture jusqu'à la reprise | | `canceled` | Statut final | ### Mettre en pause `POST /api/v1/subscriptions/{id}/pause`, avec `resumes_at` facultatif. Aucune facture n'est créée pendant la pause. À la reprise, manuelle ou à `resumes_at`, une nouvelle période commence et elle est facturée tout de suite. ### Changer de plan `PATCH /api/v1/subscriptions/{id}` avec `plan`. Le nouveau plan doit être dans la même devise. * **Plan plus cher, même rythme** : le changement est immédiat. La différence pour les jours restants de la période est facturée tout de suite et encaissée sur les moyens du client. Le nouveau plan s'applique dès que ce paiement réussit. S'il échoue, rien ne change. * **Plan moins cher, ou autre rythme** : le nouveau plan s'applique à la **prochaine échéance**. En attendant, l'abonnement porte `pending_plan`. Vos abonnés peuvent aussi changer de plan eux-mêmes depuis leur espace client, vers les plans marqués `portal_switchable`. ### Nombre d'échéances limité Avec `max_cycles`, l'abonnement s'arrête tout seul une fois ce nombre d'échéances facturé, par exemple pour un paiement en 3 fois. `cycles_billed` compte les échéances déjà facturées. ### Annuler `POST /api/v1/subscriptions/{id}/cancel` : * **Par défaut** : annulation immédiate. Les factures en attente sont annulées et plus rien n'est prélevé. * **Avec `at_period_end: true`** : l'abonnement va jusqu'à la fin de la période payée, puis s'arrête. `cancel_at_period_end` vaut `true` en attendant, et `POST /api/v1/subscriptions/{id}/reactivate` retire l'annulation prévue. ## Espace client Vos abonnés gèrent eux-mêmes leurs abonnements depuis un espace aux couleurs de votre boutique : factures et reçus, paiement d'une facture en retard ou de la période suivante, cartes et numéros mobile money, changement de plan, annulation en fin de période. Le lien figure dans chaque e-mail. Vous pouvez aussi l'ouvrir depuis votre site avec [`POST /api/v1/billing_portal/sessions`](/subscriptions/billing-portal), ou le couper dans **Paramètres**. ## Webhooks `subscription.created`, `subscription.updated`, `subscription.past_due`, `subscription.unpaid`, `subscription.paused`, `subscription.resumed`, `subscription.canceled`, `invoice.created`, `invoice.upcoming`, `invoice.paid` et `invoice.payment_failed`. Voir [Événements](/developers/webhooks/events). Voir [Accepter un paiement récurrent](/subscriptions/api-guide) pour le parcours complet, et [Plans](/api/plans/create), [Abonnements](/api/subscriptions/create) et [Factures](/api/invoices/list) pour la référence. # Créer un plan Source: https://docs.mercy-pay.com/subscriptions/plans Le prix et le rythme de votre abonnement, réglés une fois dans le tableau de bord. Un plan, c'est une formule : un nom, un prix et un rythme. Le même plan sert vos clients carte et mobile money, de n'importe quel pays. ![La page Plans d'abonnement avec ses formules et le nombre d'abonnés](/images/subscriptions/plans-light.webp) ![La page Plans d'abonnement avec ses formules et le nombre d'abonnés](/images/subscriptions/plans-dark.webp) **1. Ouvrez Plans d'abonnement** Dans le menu, sous **Encaisser**, puis **Créer un plan**. **2. Décrivez la formule** * **Nom** : ce que voit votre client, par exemple « Abonnement Pro ». * **Montant** et **Devise** : le prix d'un cycle. * **Rythme** : chaque jour, semaine, mois ou année, et **Tous les** pour espacer (tous les 3 mois, par exemple). * **Essai gratuit (jours)** : laissez vide pour faire payer dès l'inscription. **3. Proposez-la dans l'espace client (facultatif)** Cochez **Proposée dans l’espace client** pour que vos abonnés puissent y passer seuls depuis leur [espace client](/subscriptions/billing-portal). **4. Partagez-la** Dans le menu **…** de la formule, **Créer un lien** ouvre un [lien de paiement](/payment-links/create-in-dashboard) d'abonnement : vos clients s'y inscrivent et paient le premier cycle. ## Modifier, archiver, supprimer Le menu **…** de chaque formule propose : | Action | Effet | | - | - | | **Modifier** | Le nom, la description et la case de l'espace client. Le prix et le rythme ne changent jamais : chaque abonné paie ce qu'il a accepté | | **Archiver** | Plus de nouvel abonné. Les abonnés actuels continuent d'être facturés. **Réactiver** la remet en vente | | **Supprimer** | Seulement pour une formule qui n'a jamais eu d'abonné | Pour changer de prix, créez une nouvelle formule, puis faites passer vos abonnés dessus depuis leur fiche, avec **Gérer**, **Changer de formule**. Voir [Suivre vos abonnés](/subscriptions/subscribers). # La page d'inscription Source: https://docs.mercy-pay.com/subscriptions/subscribe-page Votre client choisit sa carte ou son mobile money, dans le pays de son choix, et son abonnement s'active dès le paiement. Chaque inscription passe par cette page : depuis un lien de paiement d'abonnement, un abonné ajouté dans le tableau de bord, ou un abonnement créé par l'API. Votre client y voit la formule, le prix, le rythme et la date du premier paiement, puis paie pour activer. ![La page d'inscription à un abonnement, en carte bancaire](/images/showcase/subscribe-card-light.webp) ![La page d'inscription à un abonnement, en carte bancaire](/images/showcase/subscribe-card-dark.webp) « Payer et activer » : le premier paiement active l'abonnement. ## Carte ou mobile money, de n'importe quel pays Votre client choisit **Carte bancaire** ou **Mobile Money**. En mobile money, il choisit son pays parmi les 11 proposés, son opérateur et son numéro. Si la devise de son opérateur est différente de celle de la formule, chaque échéance est convertie au moment du prélèvement. ## Ce que la page dit à votre client * **Sans essai** : il paie le premier cycle tout de suite, et l'abonnement s'active. * **Avec essai** : il enregistre sa carte ou son numéro sans être débité, et le premier paiement a lieu à la fin de l'essai. * **Toujours** : ce qui sera prélevé, à quel rythme, et comment annuler depuis son [espace client](/subscriptions/billing-portal). Ensuite, Mercy Pay encaisse chaque échéance, relance si besoin et écrit à votre client. Voir [Abonnements](/subscriptions/overview). # Suivre vos abonnés Source: https://docs.mercy-pay.com/subscriptions/subscribers Qui paie, qui est en retard, et ce que vous pouvez faire pour chacun, depuis le tableau de bord. **Abonnements** montre vos abonnés actifs, votre revenu récurrent par mois, et chaque abonnement avec son statut. ![La liste des abonnements avec les abonnés actifs et le revenu récurrent](/images/subscriptions/index-light.webp) ![La liste des abonnements avec les abonnés actifs et le revenu récurrent](/images/subscriptions/index-dark.webp) Filtrez par statut (**En retard**, **Impayés**…) ou par formule, et cherchez un client par son nom ou son e-mail. ## Inscrire un client vous-même Vos clients s'inscrivent d'habitude seuls, par un [lien de paiement](/payment-links/create-in-dashboard) d'abonnement. Pour inscrire un client que vous connaissez : **1. Ajoutez l'abonné** **Ajouter un abonné**, puis choisissez le **Plan**, le **Nom du client** et son **Adresse e-mail**. Il y reçoit ses reçus, ses rappels et l'accès à son espace. **2. Transmettez le lien d'activation** Mercy Pay affiche un **Lien d'activation à transmettre**. Envoyez-le à votre client : il y paie le premier cycle, ou enregistre son moyen de paiement si la formule a un essai. ![La page où le client active son abonnement en payant](/images/subscriptions/pay-light.webp) ![La page où le client active son abonnement en payant](/images/subscriptions/pay-dark.webp) **3. C'est actif** Dès le paiement, l'abonnement apparaît comme **Active** (ou en essai). Sans paiement sous 48 heures, l'inscription est annulée. ## La fiche d'un abonné Cliquez sur un abonnement : statut, total encaissé, prochain paiement, factures, et les moyens de paiement du client dans l'ordre où ils seront essayés. ![La fiche d'un abonné : statut, factures et moyens de paiement](/images/subscriptions/show-light.webp) ![La fiche d'un abonné : statut, factures et moyens de paiement](/images/subscriptions/show-dark.webp) Le bouton **Gérer** propose : * **Changer de formule** : plus chère, la différence au prorata est payée tout de suite ; moins chère, elle s'applique à la prochaine échéance ; * **Mettre en pause**, puis **Reprendre** ; * **Envoyer un accès à son espace** : le client reçoit par e-mail un lien vers son [espace client](/subscriptions/billing-portal) ; * **Annuler l'abonnement** : **Immédiatement**, ou à la fin de la période payée. **Ne plus annuler** retire une annulation prévue. Sur une facture en attente, **Relancer** tente un nouvel encaissement et **Renvoyer au client** lui envoie de quoi payer. ## Les statuts | Statut | Ce qu'il faut faire | | - | - | | **Active** | Rien, Mercy Pay encaisse chaque échéance | | **Essai en cours** | Rien, le premier paiement aura lieu à la fin de l'essai | | **Échéance impayée** | Une carte a été refusée. Mercy Pay réessaie à J+1, J+3 et J+7 | | **Impayé** | L'échéance mobile money n'est pas payée : coupez l'accès à votre service jusqu'au paiement | | **En pause** | Aucune facture jusqu'à la reprise | | **Annulé** | Terminé | Voir [Abonnements](/subscriptions/overview#rappels-et-relances) pour le calendrier des rappels et [E-mails envoyés](/subscriptions/emails) pour ce que reçoit votre client.