Passer au contenu principal
Cette page est la source de vérité sur l’évolution du contrat d’intégration SandPay. Chaque entrée est datée, porte une version d’API, et vous indique clairement si une action est requise de votre côté.
SandPay est la source de vérité. Votre application (Zana ou tout autre consommateur) est un client. Lorsque quelque chose change ici, mettez à jour votre intégration en conséquence — ne forkez pas le contrat.

Comment rester à jour (le mode de mise à jour)

SandPay expose sa version au moment de l’exécution pour que vous n’ayez jamais à deviner ce qu’une instance supporte :

GET /v1/meta

Sans authentification. Retourne la version de l’API, les capacités supportées, la version minimale recommandée du SDK, et un lien vers cette page.

X-SandPay-Api-Version

Chaque réponse /v1/* porte cet en-tête — ainsi n’importe quel appel vous indique déjà quel contrat vous utilisez.
curl https://api.sandpay.dev/v1/meta
# ou, contre une instance locale/auto-hébergée :
curl $SANDPAY_BASE_URL/api/v1/meta
{
  "api_version": "2026-05-31",
  "capabilities": ["payments", "refunds", "disbursements", "webhooks", "test_clients", "multi_environment", "commission_split", "order_linkage", "reference_format"],
  "min_sdk_version": "0.2.0",
  "changelog_url": "https://docs.sandpay.dev/integration-changelog"
}
Détectez les fonctionnalités plutôt que de les supposer. Avant d’appeler un endpoint plus récent, vérifiez que la capacité est présente — votre code fonctionne alors aussi contre les anciennes instances :
const { capabilities } = await sandpay.meta();
if (capabilities.includes("disbursements")) {
  await sandpay.disbursements.create({ /* … */ });
}

La procédure de mise à niveau (chaque fois qu’une entrée dit “action requise”)

1

Lisez l'entrée

Trouvez la dernière entrée datée ci-dessous et sa ligne Action requise ?.
2

Mettez à jour le SDK

npm i @sandpay/node@latest (≥ la min_sdk_version de /v1/meta). Si vous avez un client fait maison (ex. une fonction Deno/Supabase Edge), reproduisez manuellement les nouvelles méthodes/champs.
3

Migrez une instance auto-hébergée

Si vous faites tourner votre propre instance SandPay, appliquez les migrations de base de données : corepack pnpm --filter sandpay-app run db:migrate. (Pas nécessaire contre le service hébergé api.sandpay.dev.)
4

Appliquez la modification de code

Faites ce que la ligne Action requise ? de l’entrée indique (ex. branchez votre handler webhook sur event). Les entrées uniquement additives ne nécessitent rien.
5

Vérifiez

Relancez vos tests d’intégration contre la nouvelle version. GET /v1/meta doit rapporter la version attendue.
Politique de compatibilité. Dans v1, les modifications sont additives — nouveaux endpoints, nouveaux champs optionnels, nouveaux noms d’événements webhook. Les champs existants et la signification des valeurs existantes ne changent jamais sous v1. Un changement cassant serait livré sous un nouveau chemin majeur (v2) avec cette page qui l’indique et une fenêtre de dépréciation sur v1.

Changelog

2026-05-31 — Format de référence canonique api_version: 2026-05-31

Une forme recommandée pour la clé d’idempotence reference. Additif.
  • Nouvelle convention : reference suit le format ORIGIN-OP-TS-CODE (ex. ZANA-PAY-20260531221015-8E2884A47B1C) — ORIGIN = le slug de votre application (SP pour SandPay), OPPAY (collecte) / REF (remboursement) / DIS (décaissement) / ABO (abonnement), TS = date UTC compacte YYYYMMDDHHMMSS (triable), CODE = hex majuscule à 12 caractères (aléatoire pour une nouvelle tentative, ou dérivé d’un identifiant stable — avec un TS stable — pour des reprises idempotentes). Capacité : reference_format. L’analyse est tolérante — les références pré-TS sont toujours reconnues.
  • Modifié (additif) : lorsque vous omettez reference sur un versement, le défaut auto-généré de SandPay utilise désormais ce format (SP-REF-… / SP-DIS-…, un remboursement héritant du ORIGIN de son parent) au lieu des anciens RFND_… / DISB_…. N’affecte que les appelants qui n’envoient pas leur propre reference.
  • SDK : buildReference(), referenceForType(), deriveRefCode(), generateRefCode(), parseReference(), isCanonicalReference() exportés depuis @sandpay/node 0.2.1.
Action requise : aucune. reference reste votre clé d’idempotence et toute chaîne fonctionne toujours — c’est une convention recommandée, pas une contrainte imposée. L’adopter rend le tableau de bord cohérent et les références auto-descriptives. Elle est indépendante de order_ref (la clé de regroupement) — continuez à l’envoyer aussi. Voir Guide d’intégration §5 → Format de référence.

2026-05-30 — Remboursements & décaissements api_version: 2026-05-30

La direction sortante de l’argent (marchand → client). Additif.
  • Nouveau : POST /v1/payments/{id}/refund — rembourser tout ou partie d’une collecte SUCCESS (lié via parentTxId).
  • Nouveau : POST /v1/disbursements — versement libre à un msisdn.
  • Nouveau : les transactions portent type (collection | refund | disbursement) et parentTxId ; filtrez avec GET /v1/payments?type=….
  • Nouveaux événements webhook : payment.refunded, disbursement.completed (chacun portant le status terminal). Les collectes déclenchent toujours payment.completed sans changement.
  • Nouveau : GET /v1/meta + en-tête X-SandPay-Api-Version (ce mode de mise à jour).
  • Nouveau (lien de commande) : order_ref + order_url optionnels sur POST /v1/payments (et /v1/disbursements). order_ref est la clé de regroupement qui lie une collecte, ses remboursements et toute charge récurrente ensemble ; un remboursement hérite de celui de son parent. Les deux sont renvoyés sur Payment (orderRef/orderUrl) et le webhook (order_ref/order_url). Le tableau de bord gagne une page Commandes (regroupement par commande) + un filtre order sur l’Historique. Capacité : order_linkage. is_url_protected optionnel (booléen) marque order_url comme protégé par permission → une mention ”⚠ URL protégée” dans le tableau de bord.
  • SDK : sandpay.payments.refund(), sandpay.disbursements.*, sandpay.meta(), PaymentInput.order_ref/order_url. SDK min : 0.2.0.
Action requise (consommateurs de webhook) : branchez-vous sur event. Une fois que vous adoptez les remboursements/décaissements, votre endpoint recevra payment.refunded / disbursement.completed en plus de payment.completed. Switchez sur le champ event (ou type) avant de traiter une livraison comme un règlement de collecte, pour qu’un remboursement ne soit pas confondu avec un nouveau paiement. Si vous n’appelez jamais les endpoints de versement, aucun webhook de versement n’est déclenché et aucune action n’est nécessaire pour l’instant — mais ajouter le branchement maintenant est la décision prudente. Voir Guide d’intégration §5.
Voir §5 du Guide d’intégration.

2026-05-15 — Répartition de commission configurable

  • Nouveaux champs (optionnels) sur Payment + le webhook : commission, netAmount, customerTotal, merchantAbsorptionPct, merchantShare, customerShare, commissionMode.
  • Taux de commission par environnement (commission_bps) + pourcentage d’absorption marchand.
Action requise : réconciliez votre grand livre marchand sur netAmount, pas sur amount. Avec la configuration par défaut (le marchand absorbe 100%) customerTotal est toujours égal à amount, donc la réconciliation existante lisant amount n’est pas fausse — mais netAmount est le bon champ. Voir Guide d’intégration §4.

2026-05-01 — Payload raw natif de l’opérateur

  • Nouveau champ (optionnel) raw sur Payment + le webhook — la forme de réponse native de l’opérateur (synthétisée en sandbox avec _simulated: true ; verbatim en production). Additif — aucune action requise.

2026-04-20 — Lister & récupérer les paiements

  • Nouveau : GET /v1/payments (pagination par curseur) et GET /v1/payments/{id}. Additif — aucune action requise.

Voir aussi

Mise à niveau & (ré)installation

Procédure pas-à-pas pour installer, réinstaller ou mettre à niveau depuis votre application.

Guide d'intégration

La référence d’intégration complète faisant autorité.

FAQ d'intégration

Pièges courants + conseils.