Aller au contenu

Refunds

Un Refund permet de restituer tout ou partie du montant d’une Transaction réussie à l’acheteur. Le remboursement est effectué via la même méthode de paiement que le paiement original — vous ne pouvez pas rembourser sur un autre canal.

Vous pouvez effectuer plusieurs remboursements partiels sur une même transaction, tant que le total remboursé ne dépasse pas le montant original.

Délais de remboursement opérateur

Les délais de restitution des fonds dépendent de l’opérateur : Mobile Money (Airtel, Moov) : 24–72h. Carte bancaire : 3–10 jours ouvrés selon la banque émettrice. PayPal : instantané à 24h. Sangho envoie un webhook refund.succeeded dès que l’opérateur confirme.

Cycle de vie

pendingprocessingsucceeded / failed / cancelled / expired
StatutSignification
pendingRemboursement créé, en attente de traitement.
processingTransmis à l'opérateur, en cours de traitement.
succeededFonds restitués à l'acheteur.
failedÉchec du remboursement côté opérateur. Les fonds ne sont pas débités.
cancelledRemboursement annulé avant traitement (via /cancel/).
expiredDélai de traitement dépassé — les fonds sont retournés à votre solde.

Endpoints

MéthodeEndpointDescription
POST/refunds/Créer un remboursement
GET/refunds/Lister les remboursements
GET/refunds/{id}/Récupérer un remboursement
POST/refunds/{id}/cancel/Annuler un remboursement pending

Schéma de l’objet

Response Objet Refund
json
{
  "id": "refd_xxxxxxxxxxxx",
  "object": "refund",
  "transaction": "trans_3Nx8mLKZ2eZvKYlo28m",
  "amount": 5000,
  "currency": "XAF",
  "status": "succeeded",
  "reason": "customer_request",
  "description": "Remboursement partiel — article indisponible",
  "failure_reason": null,
  "payment_method_type": "mobile_money",
  "refunded_at": "2026-03-16T09:00:00Z",
  "metadata": {
    "ticket": "SUPPORT-00412"
  },
  "livemode": true,
  "created_at": "2026-03-15T14:30:00Z",
  "updated_at": "2026-03-16T09:00:00Z"
}

Créer un remboursement

Initie un remboursement pour une transaction existante. La transaction doit être en statut completed. Un remboursement partiel ne modifie pas le statut de la transaction originale — seul un remboursement total passe la transaction en refunded.

POST Corps de la requête
transaction Requis
string trans_... ex : trans_3Nx8mLKZ2eZvKYlo28m

ID de la Transaction à rembourser. La transaction doit être en statut completed.

Une transaction en statut pending, failed ou disputed ne peut pas être remboursée. Pour annuler un paiement non encore traité, utilisez POST /payment-intents/{id}/cancel/.

amount Optionnel
integer centimes

Montant à rembourser en centimes. Si omis, rembourse la totalité du montant remboursable de la transaction.

En savoir plus

Le montant ne peut pas dépasser transaction.amount - montants_déjà_remboursés. Vous pouvez cumuler plusieurs remboursements partiels. Exemple : transaction de 15 000 XAF → remboursement de 5 000, puis un second de 3 000 est accepté (reste : 7 000).

reason Optionnel
string

Motif du remboursement. Utilisé pour les rapports internes et la conformité.

En savoir plus

Valeurs acceptées : duplicate (paiement en double), fraudulent (transaction frauduleuse), customer_request (demande client), product_issue (problème produit), service_not_rendered (service non fourni). Le motif est inclus dans le webhook refund.succeeded.

description Optionnel
string

Note interne sur le remboursement. Non visible par l’acheteur.

En savoir plus

Idéal pour référencer un ticket de support, une décision interne ou une communication avec le client.

metadata Optionnel
object

Données libres associées au remboursement. Retournées dans le webhook refund.succeeded.

Toujours utiliser une Idempotency-Key

Un double appel sans Idempotency-Key crée deux remboursements distincts. Avec une clé identique, Sangho retourne le remboursement existant sans en créer un nouveau. Utilisez un UUID unique par tentative de remboursement.

Créer un remboursement

bash
curl -X POST https://api.sangho.com/v1/refunds/ \
  -H "Authorization: Bearer sk_live_xxxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: refund-trans_xxx-$(date +%s)" \
  -d '{
    "transaction": "trans_3Nx8mLKZ2eZvKYlo28m",
    "amount": 5000,
    "reason": "customer_request",
    "description": "Remboursement partiel — article indisponible",
    "metadata": { "ticket": "SUPPORT-00412" }
  }'
Response 201 Created — Remboursement initié
Le remboursement est créé en statut processing. Le webhook refund.succeeded confirmera la restitution des fonds.
json
{
  "id": "refd_xxxxxxxxxxxx",
  "object": "refund",
  "transaction": "trans_3Nx8mLKZ2eZvKYlo28m",
  "amount": 5000,
  "currency": "XAF",
  "status": "processing",
  "reason": "customer_request",
  "description": "Remboursement partiel — article indisponible",
  "failure_reason": null,
  "metadata": { "ticket": "SUPPORT-00412" },
  "created_at": "2026-03-15T14:30:00Z",
  "updated_at": "2026-03-15T14:30:00Z"
}
Response 400 — Montant excédentaire
Le montant demandé dépasse le remboursable. Vérifiez le montant déjà remboursé avant de soumettre.
Récupérez la transaction via GET /transactions/{id}/ et vérifiez amount_refunded pour connaître le montant restant remboursable.
json
{
  "error": {
    "type": "validation_error",
    "code": "refund_amount_exceeds_available",
    "message": "Le montant du remboursement dépasse le montant remboursable (10000 XAF restants).",
    "param": "amount"
  }
}
Response 409 — Transaction non remboursable
La transaction est dans un état qui ne permet pas le remboursement (failed, pending, disputed).
Seules les transactions en statut completed peuvent être remboursées.
json
{
  "error": {
    "type": "conflict_error",
    "code": "transaction_not_refundable",
    "message": "Cette transaction est en statut failed et ne peut pas être remboursée."
  }
}