> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sandpay.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Changelog d'intégration

> Historique daté et versionné de chaque modification du contrat d'intégration SandPay — et exactement ce que vous devez faire (le cas échéant) pour rester à jour.

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

<Note>
  **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.
</Note>

## 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 :

<CardGroup cols={2}>
  <Card title="GET /v1/meta" icon="circle-info">
    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.
  </Card>

  <Card title="X-SandPay-Api-Version" icon="tag">
    Chaque réponse `/v1/*` porte cet en-tête — ainsi n'importe quel appel vous
    indique déjà quel contrat vous utilisez.
  </Card>
</CardGroup>

```bash theme={null}
curl https://api.sandpay.dev/v1/meta
# ou, contre une instance locale/auto-hébergée :
curl $SANDPAY_BASE_URL/api/v1/meta
```

```json theme={null}
{
  "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 :

```ts theme={null}
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")

<Steps>
  <Step title="Lisez l'entrée">
    Trouvez la dernière entrée datée ci-dessous et sa ligne **Action requise ?**.
  </Step>

  <Step title="Mettez à jour le SDK">
    `npm i @drwintech/sandpay@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.
  </Step>

  <Step title="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`.)
  </Step>

  <Step title="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.
  </Step>

  <Step title="Vérifiez">
    Relancez vos tests d'intégration contre la nouvelle version. `GET /v1/meta`
    doit rapporter la version attendue.
  </Step>
</Steps>

<Info>
  **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`.
</Info>

***

## 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), `OP` ∈ `PAY` (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 `@drwintech/sandpay` **`0.2.1`**.

<Note>
  **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](/fr/integration-guide#format-de-référence-origin-op-ts-code).
</Note>

### 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`.**

<Warning>
  **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](/fr/integration-guide#5-remboursements--décaissements).
</Warning>

Voir [§5 du Guide d'intégration](/fr/integration-guide#5-remboursements--décaissements).

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

<Note>
  **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](/fr/integration-guide#4-commission--règlement).
</Note>

### 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

<CardGroup cols={2}>
  <Card title="Mise à niveau & (ré)installation" icon="rotate" href="/integration-upgrade">
    Procédure pas-à-pas pour installer, réinstaller ou mettre à niveau depuis votre application.
  </Card>

  <Card title="Guide d'intégration" icon="book" href="/integration-guide">
    La référence d'intégration complète faisant autorité.
  </Card>

  <Card title="FAQ d'intégration" icon="circle-question" href="/integration-faq">
    Pièges courants + conseils.
  </Card>
</CardGroup>
