Remboursements
Remboursez un paiement Orange Money réussi, totalement ou partiellement, en un seul appel API.
Fonctionnement
- L'argent est renvoyé sur le numéro Orange Money du payeur (un crédit Orange Money vers le wallet qui a payé). Le paiement doit donc porter un numéro de téléphone du payeur ; sinon le remboursement est refusé avec
refund_payer_phone_missing. - Le montant remboursé est débité de votre solde SOAMTAR.
- Les remboursements partiels sont autorisés : vous pouvez rembourser un paiement plusieurs fois jusqu'à ce que la somme des remboursements atteigne le montant du paiement.
- Le remboursement est soumis à Orange Money dans la même requête, mais le crédit est asynchrone : en live, le remboursement revient en général
processinget son statut final vous parvient par les webhooksrefund.success/refund.failed(ouGET/v1/refunds/{id}). - Votre clé API doit avoir le scope
can_refund, et les remboursements doivent être activés sur votre compte. - Utilisez une clé du même environnement que le paiement : une clé
sk_test_ne rembourse que les paiements sandbox, une clésk_live_que les paiements live.
Créer un remboursement
POST/v1/refunds
Headers
Authorization: Bearer sk_(test|live)_…Idempotency-Key: <uuid>(fortement recommandé : une requête réessayée ne doit jamais rembourser deux fois ; voir Idempotence)Content-Type: application/json
Corps
{
"transaction_id": "0e85b3c2-7c4f-4f1d-9b3a-1c8a2d4e5f6a",
"amount": "40.00",
"reason": "customer_request",
"description": "Article retourné"
}| Champ | Type | Requis | Notes |
|---|---|---|---|
transaction_id | string | oui | id (UUID) ou référence (TXN-…) du paiement. payment_id est accepté comme alias. |
amount | décimal (string) | optionnel | Montant à rembourser, deux décimales au plus. Omettez-le pour rembourser tout le montant restant. |
reason | string, max 100 | optionnel | Votre code motif, par ex. customer_request, duplicate, fraudulent. |
description | string, max 500 | optionnel | Texte libre conservé avec le remboursement. |
Réponse 201
La réponse porte le statut courant du remboursement :
- sandbox (
sk_test_) :success— le simulateur règle immédiatement ; - Orange Money (
sk_live_) :processing— le crédit est en cours ; attendezrefund.success/refund.failed; failedquand Orange Money a refusé le crédit (voirfailure_reason). Le montant est libéré et vous pouvez réessayer.
{
"data": {
"id": "9b1f4c2e-5d6a-4e8b-a1c3-2f4e6a8b0c1d",
"object": "refund",
"reference": "REF-7K2M9Q4XW8T1B3NC",
"transaction_id": "0e85b3c2-7c4f-4f1d-9b3a-1c8a2d4e5f6a",
"amount": "40.00",
"currency": "SLE",
"reason": "customer_request",
"description": "Article retourné",
"status": "processing",
"fee_refunded": false,
"fee_refunded_amount": "0.60",
"tax_refunded_amount": "0.00",
"failure_reason": null,
"processed_at": null,
"failed_at": null,
"created_at": "2026-10-22T10:04:58+00:00",
"updated_at": "2026-10-22T10:04:59+00:00"
}
}curl -X POST https://app.so-amtar.com/v1/refunds \
-H "Authorization: Bearer sk_test_..." \
-H "Idempotency-Key: refund-ORDER-001-1" \
-H "Content-Type: application/json" \
-d '{"transaction_id":"TXN-1A2B3C4D-ABCDEF123456","amount":"40.00","reason":"customer_request"}'Consulter un remboursement
GET/v1/refunds/{id_ou_reference}
Accepte l'id (UUID) du remboursement ou sa référence (REF-…). Renvoie {"data": Refund}, ou 404 refund_not_found.
Lister les remboursements
GET/v1/refunds?status=success&transaction_id=0e85b3c2-...&per_page=25&page=1
| Paramètre | Notes |
|---|---|
status | pending, processing, success ou failed |
transaction_id | Seulement les remboursements de ce paiement (UUID) |
per_page | 1 à 100, 25 par défaut |
page | Numéro de page, à partir de 1 |
Tri par created_at décroissant.
{
"data": [ { "id": "9b1f4c2e-...", "object": "refund", "reference": "REF-7K2M9Q4XW8T1B3NC", "status": "success", "...": "..." } ],
"pagination": { "total": 3, "per_page": 25, "current_page": 1, "last_page": 1 }
}L'objet remboursement
| Champ | Type | Description |
|---|---|---|
id | UUID | Identifiant du remboursement |
object | string | Toujours refund |
reference | string | REF- suivi de 16 caractères. Sert aussi d'identifiant de transaction Orange Money du crédit. |
transaction_id | UUID | Le paiement remboursé |
amount | décimal (string) | Montant remboursé, par ex. "400.00" |
currency | string | Devise du paiement (SLE) |
reason / description | string | null | Tels qu'envoyés à la création |
status | enum | pending, processing, success, failed |
fee_refunded | booléen | Indique si la commission SOAMTAR sur la part remboursée vous a été restituée |
fee_refunded_amount | décimal (string) | Quote-part de la commission SOAMTAR du paiement pour ce remboursement |
tax_refunded_amount | décimal (string) | Quote-part de la taxe du paiement pour ce remboursement |
failure_reason | string | null | Raison de l'échec |
processed_at | ISO 8601 | null | Date de réussite |
failed_at | ISO 8601 | null | Date d'échec |
created_at / updated_at | ISO 8601 | Horodatages |
Statuts
| Statut | Sens |
|---|---|
pending | Créé, pas encore soumis à Orange Money |
processing | Crédit soumis à Orange Money, en attente de sa confirmation |
success | Le wallet du payeur a été crédité (final) |
failed | Refusé ou non abouti (final) ; le montant est libéré |
Un remboursement qui reste processing est vérifié automatiquement auprès d'Orange Money ; un crédit qu'Orange Money n'a jamais enregistré passe failed au bout de 60 minutes.
Quand un remboursement réussit, le paiement passe partially_refunded, ou refunded dès que les remboursements couvrent tout le montant.
Webhooks
Chaque remboursement produit ses propres événements :
| Résultat | Événements |
|---|---|
| Réussite | refund.success + payment.partially_refunded (ou payment.refunded si le paiement est désormais totalement remboursé) |
| Échec | refund.failed |
Les événements refund.* portent l'objet remboursement complété de transaction_reference, merchant_reference et is_test ; les événements payment.*refunded portent le payload de la transaction avec le remboursement dans data.refund. Voir Webhooks → Événements de remboursement. Abonnez votre endpoint à ces événements (ils ne font pas partie de l'abonnement par défaut des SDK).
Erreurs
| Code | HTTP | Cause |
|---|---|---|
transaction_not_found | 404 | Id ou référence de paiement inconnu pour ce marchand |
refunds_disabled | 422 | Les remboursements ne sont pas activés sur votre compte (contactez le support) |
refund_environment_mismatch | 422 | Clé de test sur un paiement live, ou clé live sur un paiement sandbox |
transaction_not_refundable | 422 | Le paiement n'est ni success ni partially_refunded |
non_payment_transaction_refund | 422 | Seuls les paiements peuvent être remboursés (pas les paiements sortants) |
provider_refund_unsupported | 422 | Le moyen de paiement ne prend pas en charge les remboursements |
refund_payer_phone_missing | 422 | Le paiement n'a pas de numéro de payeur à créditer |
amount_must_be_positive | 422 | amount est nul ou négatif |
transaction_already_fully_refunded | 422 | Plus rien à rembourser sur ce paiement |
refund_exceeds_refundable | 422 | amount dépasse le montant restant remboursable |
refund_rejected | 422 | Le remboursement n'a pas pu être créé (autre règle métier) |
scope_insufficient | 403 | La clé API n'a pas le scope can_refund |
refund_not_found | 404 | GET/v1/refunds/{id} : id ou référence de remboursement inconnu |
SDK
Chaque SDK serveur expose refunds.create, refunds.retrieve et refunds.list (nommés selon le langage). Par exemple en Node.js :
const { data: refund } = await soamtar.refunds.create({
transaction_id: payment.id, // omettez amount pour tout rembourser
amount: '40.00',
reason: 'customer_request',
}, { idempotencyKey: `refund-${order.id}-1` });
// refund.status : 'success' (sandbox) | 'processing' (Orange Money) | 'failed'Voir les pages SDK pour les autres langages, et les plugins CMS pour rembourser depuis WooCommerce, Magento, PrestaShop, Drupal Commerce et Odoo.