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

ResponseObjet 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.

POSTCorps de la requête
transactionRequis
stringtrans_...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/.

amountOptionnel
integercentimes

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).

reasonOptionnel
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.

descriptionOptionnel
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.

metadataOptionnel
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.ga/v1/refunds/ \
  -H "Authorization: Bearer sk_prod_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" }
  }'
Response201 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"
}
Response400 — 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"
  }
}
Response409 — 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."
  }
}