Remboursements
Un remboursement rend au payeur la totalité d’un encaissement réussi, sur son canal d’origine. C’est une opération distincte, avec son propre état.
L’objet Remboursement
idUUIDrequis- Identifiant du remboursement.
environmentchaîne- Environnement de la clé d'origine :
sandbox(test) oulive(argent réel). Une clé ne lit et n'agit que dans son environnement.défaut : live statuschaînerequis- État : CREATED, PENDING, SUCCEEDED, FAILED ou UNKNOWN (non confirmé, à réconcilier). SUCCEEDED et FAILED sont définitifs.
amount_minorentierrequis- Montant rendu au payeur : le brut de l'encaissement d'origine.
currencychaînerequis- Devise : CDF ou USD.
railchaînerequis- Canal de paiement.valeurs : mpesa
refund_ofUUID ou null- Encaissement remboursé.
created_atdate-heurerequis- Date de création (UTC, ISO 8601).
{
"id": "13ebce52-d863-430c-ba0e-361d80e073ae",
"environment": "sandbox",
"status": "CREATED",
"amount_minor": 2500000,
"currency": "CDF",
"rail": "mpesa",
"refund_of": "e2662139-ce99-426d-86b3-d498a05fef8e",
"created_at": "2026-09-24T13:05:19.101548Z"
}Rembourser un encaissement
POST/v1/refunds
Rembourse en totalité un encaissement au statut SUCCEEDED, sur son canal d'origine. Les frais de l'encaissement d'origine restent acquis. Un seul remboursement par encaissement : une seconde demande répond 409, comme une demande sur un encaissement retenu par un litige (en cours ou perdu). Exige un en-tête Idempotency-Key.
En-tête Idempotency-Key requis, et inclus dans la chaîne signée.
Corps de la requête
charge_idUUIDrequis- Encaissement à rembourser, au statut SUCCEEDED. Remboursé en totalité.
Réponse
Code 201, avec l’objet Remboursement.
Erreurs
400En-tête Idempotency-Key manquant.401Authentification manquante ou invalide.404Encaissement à rembourser introuvable.409Déjà remboursé, retenu par un litige, ou Idempotency-Key réutilisée avec un corps différent.422Encaissement non remboursable / corps invalide.429Limite de débit dépassée (voir Retry-After).
Forme commune et conduite à tenir : les erreurs.
KEY="ak_sandbox"
SECRET="sk_sandbox_demo"
P="/v1/refunds"
BODY='{"charge_id":"e2662139-ce99-426d-86b3-d498a05fef8e"}'
IDEM=$(uuidgen)
TS=$(date +%s)
SIG=$(printf '%s' "$TS.POST.$P.$IDEM.$BODY" \
| openssl dgst -sha256 -hmac "$SECRET" | sed 's/^.*= //')
curl "https://sandbox.inopas.net$P" \
-H "X-Inopas-Key: $KEY" \
-H "X-Inopas-Timestamp: $TS" \
-H "X-Inopas-Signature: sha256=$SIG" \
-H "Idempotency-Key: $IDEM" \
-H "Content-Type: application/json" \
-d "$BODY"import { createHmac, randomUUID } from "node:crypto";
const KEY = "ak_sandbox";
const SECRET = "sk_sandbox_demo";
const path = "/v1/refunds"; // signé sans la query string
const body = JSON.stringify({
"charge_id": "e2662139-ce99-426d-86b3-d498a05fef8e"
});
const idem = randomUUID();
const ts = String(Math.floor(Date.now() / 1000));
const sig = createHmac("sha256", SECRET)
.update(`${ts}.POST.${path}.${idem}.${body}`)
.digest("hex");
const res = await fetch(`https://sandbox.inopas.net${path}`, {
method: "POST",
headers: {
"X-Inopas-Key": KEY,
"X-Inopas-Timestamp": ts,
"X-Inopas-Signature": `sha256=${sig}`,
"Idempotency-Key": idem,
"Content-Type": "application/json",
},
body,
});
console.log(res.status, await res.json());import hashlib, hmac, json, time, urllib.error, urllib.request, uuid
KEY = "ak_sandbox"
SECRET = b"sk_sandbox_demo"
path = "/v1/refunds" # signé sans la query string
body = json.dumps({
"charge_id": "e2662139-ce99-426d-86b3-d498a05fef8e",
}, separators=(",", ":")).encode()
idem = str(uuid.uuid4())
ts = str(int(time.time()))
sig = hmac.new(SECRET, f"{ts}.POST.{path}.{idem}.".encode() + body, hashlib.sha256).hexdigest()
entetes = {"X-Inopas-Key": KEY, "X-Inopas-Timestamp": ts, "X-Inopas-Signature": "sha256=" + sig}
entetes |= {"Idempotency-Key": idem, "Content-Type": "application/json"}
req = urllib.request.Request("https://sandbox.inopas.net" + path, headers=entetes, data=body, method="POST")
try:
with urllib.request.urlopen(req) as resp:
print(resp.status, json.load(resp))
except urllib.error.HTTPError as e:
print(e.code, json.load(e))HTTP/2 201
{
"id": "13ebce52-d863-430c-ba0e-361d80e073ae",
"environment": "sandbox",
"status": "CREATED",
"amount_minor": 2500000,
"currency": "CDF",
"rail": "mpesa",
"refund_of": "e2662139-ce99-426d-86b3-d498a05fef8e",
"created_at": "2026-09-24T13:05:19.101548Z"
}Lire un remboursement
GET/v1/refunds/{refund_id}
Lit l'état courant d'un remboursement.
Paramètres
refund_idUUID, dans le cheminrequis- Identifiant renvoyé à la création.
Réponse
Code 200, avec l’objet Remboursement.
Erreurs
401Authentification manquante ou invalide.404Remboursement introuvable.422Paramètre invalide (validation_error).
Forme commune et conduite à tenir : les erreurs.
KEY="ak_sandbox"
SECRET="sk_sandbox_demo"
P="/v1/refunds/13ebce52-d863-430c-ba0e-361d80e073ae"
TS=$(date +%s)
SIG=$(printf '%s' "$TS.GET.$P.." \
| openssl dgst -sha256 -hmac "$SECRET" | sed 's/^.*= //')
curl "https://sandbox.inopas.net$P" \
-H "X-Inopas-Key: $KEY" \
-H "X-Inopas-Timestamp: $TS" \
-H "X-Inopas-Signature: sha256=$SIG"import { createHmac } from "node:crypto";
const KEY = "ak_sandbox";
const SECRET = "sk_sandbox_demo";
const path = "/v1/refunds/13ebce52-d863-430c-ba0e-361d80e073ae"; // signé sans la query string
const ts = String(Math.floor(Date.now() / 1000));
const sig = createHmac("sha256", SECRET)
.update(`${ts}.GET.${path}..`)
.digest("hex");
const res = await fetch(`https://sandbox.inopas.net${path}`, {
headers: {
"X-Inopas-Key": KEY,
"X-Inopas-Timestamp": ts,
"X-Inopas-Signature": `sha256=${sig}`,
},
});
console.log(res.status, await res.json());import hashlib, hmac, json, time, urllib.error, urllib.request
KEY = "ak_sandbox"
SECRET = b"sk_sandbox_demo"
path = "/v1/refunds/13ebce52-d863-430c-ba0e-361d80e073ae" # signé sans la query string
body = b""
idem = ""
ts = str(int(time.time()))
sig = hmac.new(SECRET, f"{ts}.GET.{path}.{idem}.".encode() + body, hashlib.sha256).hexdigest()
entetes = {"X-Inopas-Key": KEY, "X-Inopas-Timestamp": ts, "X-Inopas-Signature": "sha256=" + sig}
req = urllib.request.Request("https://sandbox.inopas.net" + path, headers=entetes)
try:
with urllib.request.urlopen(req) as resp:
print(resp.status, json.load(resp))
except urllib.error.HTTPError as e:
print(e.code, json.load(e))HTTP/2 200
{
"id": "13ebce52-d863-430c-ba0e-361d80e073ae",
"environment": "sandbox",
"status": "CREATED",
"amount_minor": 2500000,
"currency": "CDF",
"rail": "mpesa",
"refund_of": "e2662139-ce99-426d-86b3-d498a05fef8e",
"created_at": "2026-09-24T13:05:19.101548Z"
}