# API REST native de blockwallets

> L’API REST native de blockwallets : clés Bearer, factures en cents américains avec refs idempotents, comptes de dépôt, webhooks signés HMAC-SHA256, événements et renvoi.
>
> HTML: https://blockwallets.io/docs/api?lang=fr · English: https://blockwallets.io/docs/api.md?lang=en

## API REST native

L’API native est l’interface REST propre à blockwallets : des factures tarifées en cents américains, un `ref` idempotent par commande, des comptes de dépôt statiques couvrant tous les réseaux et des webhooks signés HMAC-SHA256. Elle crée les mêmes factures sur la même page de paiement hébergée que l’[API compatible Cryptomus](https://blockwallets.io/docs/cryptomus?lang=fr), avec les mêmes frais et le même solde.

Générez vos clés vous-même dans le tableau de bord, sous « Développeurs › Intégration et clés » : une clé Live et une clé Test avec leurs clés de signature de webhook. Chacune n’est affichée qu’une seule fois ; la liste n’en montre que le préfixe. Si la double authentification est activée sur votre compte, générer ou renouveler une clé Live demande un code. Pas encore de compte marchand ? [Demandez l’accès](https://blockwallets.io/?lang=fr#join). Vous êtes déjà sur Cryptomus ? Voyez l’[API compatible Cryptomus](https://blockwallets.io/docs/cryptomus?lang=fr) ([comment choisir](https://blockwallets.io/docs?lang=fr#which)).

### Clés et requêtes

URL de base `https://blockwallets.io`. Les requêtes et les réponses sont en JSON. Chaque requête porte :

- `Authorization: Bearer bw_live_…` ou `Bearer bw_test_…` : le préfixe détermine le mode, donc une clé Test ne voit que les factures Test et fonctionne sur les testnets

- `Content-Type: application/json` sur les requêtes `POST`

- Gardez les clés uniquement sur votre serveur, jamais dans une page web ou une appli ; nous n’en stockons que le hash

Les erreurs renvoient un statut HTTP et `{"error": {"code": "…", "message": "…"}}` : 400 `invalid_request` (un champ manquant ou mal formé), 401 `unauthorized` (clé manquante, inconnue ou désactivée), 404 `not_found`, 409 `conflict` (la requête entre en conflit avec l’état actuel de la facture), 422 `currency_unsupported`.

### Endpoints

| Endpoint | Ce qu’il fait |
| --- | --- |
| `GET /v1/networks` | Réseaux que vous pouvez accepter en ce moment dans le mode de cette clé : minimum, confirmations, délai d’arrivée typique et estimation des frais ; les cryptos cotées incluent le taux actuel |
| `POST /v1/invoices` | Crée une facture (201), ou renvoie l’existante pour le même `ref` (200) |
| `GET /v1/invoices?ref=…` | Consulte une facture par votre `ref` |
| `GET /v1/invoices/:id` | Consulte une facture par id |
| `POST /v1/invoices/:id/network` | Choisissez vous-même le réseau d’une facture (page de paiement en marque blanche) : renvoie l’`address` et le `pay_amount` à afficher |
| `POST /v1/invoices/:id/close` | Ferme une facture en attente qui n’a reçu aucun transfert |
| `POST /v1/deposit-accounts` | Ouvre (201) ou récupère (200) le compte de dépôt d’un client : une adresse permanente sur chaque réseau |
| `GET /v1/deposit-accounts?customer_ref=…` | Consulte un compte de dépôt par `customer_ref` |
| `GET /v1/deposit-accounts/:id` | Consulte un compte de dépôt par id |
| `GET /v1/deposits` | Liste les dépôts d’un compte (`account_id` ou `customer_ref`, `limit` 1–200, 50 par défaut) |
| `GET /v1/deposits/:id` | Consulte un dépôt |
| `GET /v1/events` | Envois de webhook pour une facture ou un dépôt (`invoice_id` ou `deposit_id`) |
| `POST /v1/events/:id/redeliver` | Renvoie un webhook (202) |

Ajoutez `?probe=1` à la consultation d’une facture ou d’un compte de dépôt pour vérifier la chaîne au passage (au plus une fois toutes les 10 secondes) ; sinon vous obtenez le dernier état de notre scanner, qui vérifie chaque minute.

### Créer une facture POST /v1/invoices

Appelez `POST /v1/invoices` quand le client passe sa commande et redirigez-le vers la `pay_url` de la réponse.

```curl
curl -X POST https://blockwallets.io/v1/invoices \
  -H "Authorization: Bearer $BW_KEY" -H "Content-Type: application/json" \
  -d '{"ref":"order_1042","amount":4200,"title":"Order #1042",
       "return_url":"https://shop.example/cart","success_url":"https://shop.example/thanks?order=1042",
       "meta":{"user_id":42}}'
```

| Champ | Obligatoire | Description |
| --- | --- | --- |
| `ref` | Oui | Votre numéro de commande : 1–64 caractères parmi lettres, chiffres et `_ . : -`. Unique par mode ; renvoyer le même `ref` avec les mêmes champs renvoie la même facture, avec des champs différents il renvoie 409 |
| `amount` | Oui | Montant en cents américains, entier : `4200` = $42.00. Pas en dessous du minimum du réseau une fois celui-ci choisi |
| `title` | Oui | Ce que le client voit sur la page de paiement, 64 caractères au maximum |
| `return_url` | Oui | Où mène « Retour » sur la page de paiement ; doit se trouver sur l’un des domaines de votre site |
| `success_url` | Non | Où va le client après avoir payé ; par défaut `return_url` |
| `network` | Non | Présélectionne un id de réseau du [tableau des réseaux](https://blockwallets.io/docs/api?lang=fr#networks) ; omettez-le pour laisser le client choisir sur la page de paiement |
| `customer_ref` | Non | L’identifiant de votre client, 64 caractères au maximum, renvoyé tel quel |
| `lifetime` | Non | Durée de vie en secondes, 600 – 43200, 3600 par défaut |
| `amount_rule` | Non | `"any"` = crédite tout ce qui arrive (recharges) : `paid` dès la confirmation. Omettez-le pour appliquer la « Règle de montant » de « Réglages › Règles d’encaissement et tarifs » du tableau de bord |
| `accuracy_percent` | Non | Le montant manquant encore accepté comme payé, 0 – 5 (pour cent), 0 par défaut |
| `subtract_percent` | Non | Part des frais de la plateforme payée par le client, 0 – 100 (pour cent), ajoutée à `pay_amount` ; omettez-la pour utiliser la valeur par défaut du tableau de bord |
| `payment_multiple` | Non | Indique si un paiement insuffisant peut être complété à la même adresse, valeur par défaut du tableau de bord (activé sauf si vous l’avez changé) ; `false` : le premier transfert insuffisant se termine en `wrong_amount` |
| `meta` | Non | N’importe quel objet JSON jusqu’à 2 Ko, renvoyé tel quel dans les consultations et les webhooks |
| `currency` | Non | Seulement `USD` (la valeur par défaut) ; toute autre valeur renvoie 422 |

Chaque URL doit se trouver sur l’un des domaines de votre site (tableau de bord → « Développeurs › Intégration et clés »). La réponse est l’objet facture ci-dessous ; `network`, `address` et `pay_amount` sont renseignés une fois un réseau choisi.

### L’objet facture

Les ids commencent par `inv_`. `amount` est en cents américains ; `pay_amount` et `paid_amount` sont des montants en crypto, sous forme de chaînes décimales, sur le réseau choisi.

```json
{
  "id": "inv_01J8…",
  "ref": "order_1042",
  "mode": "live",
  "status": "pending",
  "amount": 4200,
  "currency": "USD",
  "title": "Order #1042",
  "meta": { "user_id": 42 },
  "network": null,
  "address": null,
  "pay_amount": null,
  "paid_amount": null,
  "payments": [],
  "pay_url": "https://blockwallets.io/pay/inv_01J8…",
  "return_url": "https://shop.example/cart",
  "success_url": "https://shop.example/thanks?order=1042",
  "created_at": "2026-11-03T08:00:00.000Z",
  "expires_at": "2026-11-03T09:00:00.000Z",
  "paid_at": null,
  "closed_at": null
}
```

| status | Signification | Ce que vous faites |
| --- | --- | --- |
| `pending` | En attente de paiement | — |
| `confirming` | Un transfert a été vu et se confirme, ou il est arrivé incomplet et peut encore être complété | — |
| `paid` | Payée | Expédiez / créditez la commande |
| `paid_over` | Payée, avec un trop-perçu | Pareil ; le surplus est sur votre solde |
| `wrong_amount` | Terminée avec moins que le montant total (à l’expiration, ou au premier transfert insuffisant quand `payment_multiple` est false) | Les activités de recharge peuvent créditer ce qui est arrivé (`paid_amount` ; les cryptos à taux ont aussi `paid_cents`) ; les boutiques demandent au client de payer le reste |
| `closed` | Expirée sans rien recevoir, ou fermée par vous | — (l’argent qui arrive plus tard est quand même crédité et notifié) |

Autres champs : `pay_url` (la page de paiement hébergée), `payments` (chaque transfert avec `txid`, `amount`, `confirmations` et `confirmed`), `expires_at`, `paid_at`, `closed_at` ; BTC, LTC, DOGE, BCH et les cryptos natives portent aussi `rate` (dollars par crypto, verrouillé 30 minutes), `rate_locked_until` et `paid_cents` (ce qui est arrivé jusqu’ici, en cents américains).

### Réseaux

Passez l’id comme `network`. `GET /v1/networks` renvoie ceux ouverts en ce moment dans votre mode ; un réseau peut être suspendu, mieux vaut donc le lire que le coder en dur.

| network | Crypto | Chaîne | Minimum |
| --- | --- | --- | --- |
| `USDT_TRC20` | USDT | TRON | $1 |
| `USDT_BEP20` | USDT | BNB Chain | $1 |
| `USDC_BEP20` | USDC | BNB Chain | $1 |
| `USDT_ERC20` | USDT | Ethereum | $5 |
| `USDC_ERC20` | USDC | Ethereum | $5 |
| `USDC_BASE` | USDC | Base | $1 |
| `USDT_POLYGON` | USDT | Polygon | $1 |
| `USDC_POLYGON` | USDC | Polygon | $1 |
| `USDT_ARBITRUM` | USDT | Arbitrum | $1 |
| `USDC_ARBITRUM` | USDC | Arbitrum | $1 |
| `USDT_OPTIMISM` | USDT | Optimism | $1 |
| `USDC_OPTIMISM` | USDC | Optimism | $1 |
| `USDT_AVALANCHE` | USDT | Avalanche | $1 |
| `USDC_AVALANCHE` | USDC | Avalanche | $1 |
| `USDT_XLAYER` | USDT | X Layer | $1 |
| `USDC_XLAYER` | USDC | X Layer | $1 |
| `USD1_BEP20` | USD1 | BNB Chain | $1 |
| `FDUSD_BEP20` | FDUSD | BNB Chain | $1 |
| `DAI_ERC20` | DAI | Ethereum | $5 |
| `DAI_ARBITRUM` | DAI | Arbitrum | $1 |
| `DAI_BASE` | DAI | Base | $1 |
| `BTC` | BTC | Bitcoin | $5 |
| `LTC` | LTC | Litecoin | $1 |
| `DOGE` | DOGE | Dogecoin | $1 |
| `BCH` | BCH | Bitcoin Cash | $1 |
| `TRX` | TRX | TRON | $1 |
| `BNB` | BNB | BNB Chain | $1 |
| `ETH` | ETH | Ethereum | $5 |
| `ETH_BASE` | ETH | Base | $1 |
| `ETH_ARBITRUM` | ETH | Arbitrum | $1 |
| `ETH_OPTIMISM` | ETH | Optimism | $1 |
| `POL` | POL | Polygon | $1 |
| `AVAX` | AVAX | Avalanche | $1 |
| `OKB` | OKB | X Layer | $1 |
| `USDT_SOL` | USDT | Solana | $1 |
| `USDC_SOL` | USDC | Solana | $1 |
| `SOL` | SOL | Solana | $1 |
| `USDT_TON` | USDT | TON | $1 |
| `TON` | TON | TON | $1 |

### Comptes de dépôt (adresses statiques) POST /v1/deposit-accounts

`POST /v1/deposit-accounts` avec `{"customer_ref": "user_42"}` donne à ce client une adresse permanente sur chaque réseau (toutes les chaînes EVM partagent une adresse). Tout ce qu’il envoie est crédité en dollars américains, avec un webhook `deposit.credited` par transfert.

```curl
curl -X POST https://blockwallets.io/v1/deposit-accounts \
  -H "Authorization: Bearer $BW_KEY" -H "Content-Type: application/json" \
  -d '{"customer_ref":"user_42"}'
```

```json
{
  "id": "dac_01J8…",
  "mode": "live",
  "customer_ref": "user_42",
  "deposit_url": "https://blockwallets.io/deposit/dac_01J8…",
  "min_amount": 100,
  "currency": "USD",
  "networks": [
    { "id": "USDT_TRC20", "chain": "tron", "token": "USDT", "address": "TXy4…8kQ2", "min_amount": 100, "confirmations": 1, … },
    { "id": "USDC_BASE", "chain": "base", "token": "USDC", "address": "0x5aE1…", "min_amount": 100, "confirmations": 20, … }
  ]
}
```

- `customer_ref` : 1–128 caractères parmi lettres, chiffres et `_ . : @ -` ; la même valeur renvoie toujours le même compte. `return_url` facultatif sur vos domaines

- La réponse contient `deposit_url` (une page hébergée avec les adresses, les QR codes et les dépôts récents) et `networks` (`id`, `address`, `min_amount` et `confirmations` pour chacun)

- Les ids de dépôt commencent par `dep_` ; `amount` est en cents américains, `amount_token` le montant en crypto, et les cryptos cotées portent le `rate` utilisé

Les transferts inférieurs au minimum (généralement $1, BTC $5) sont enregistrés comme `below_min` et ne sont pas crédités automatiquement ; contactez-nous pour les faire créditer.

### Webhooks

Nous envoyons un POST JSON à l’URL de webhook du tableau de bord (« Développeurs › Intégration et clés » ; Live et Test ont chacun leur URL et leur secret). Le corps est l’événement :

```http
POST /your/webhook
X-BW-Event: ev_01J8…
X-BW-Delivery: 1
X-BW-Signature: t=1793693052,v1=5f0c9b…

{"id":"ev_01J8…","type":"invoice.paid","created_at":"2026-11-03T08:04:12.000Z","invoice":{"id":"inv_01J8…","ref":"order_1042","status":"paid","amount":4200,…}}
```

- `X-BW-Signature` : `t=<unix seconds>,v1=<hex>`, où `v1` = HMAC-SHA256(webhook secret de ce mode, `t` + `.` + corps brut)

- `X-BW-Event` : l’id de l’événement (`ev_…`) ; dédupliquez avec lui

- `X-BW-Delivery` : le numéro de la tentative, à partir de 1

| Événement | Quand |
| --- | --- |
| `invoice.paid` | La facture est passée à `paid` ou `paid_over` (y compris l’argent arrivé après l’expiration) |
| `invoice.wrong_amount` | La facture s’est terminée en `wrong_amount` |
| `invoice.closed` | La facture a expiré sans rien recevoir, ou vous l’avez fermée |
| `deposit.credited` | Un transfert vers un compte de dépôt a été crédité |

Vérifiez sur le corps brut avant de l’analyser, rejetez un `t` éloigné de plus de 300 secondes de votre horloge et comparez en temps constant. `invoice` / `deposit` dans le corps est le même objet que renvoie une consultation.

```nodejs
import crypto from "node:crypto";

// rawBody: the request body exactly as received (a string), header: X-BW-Signature
function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.trim().split("=")));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false;
  const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
  return typeof parts.v1 === "string" && parts.v1.length === expected.length
    && crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
}
```

Une réponse 2xx compte comme livrée ; sinon nous réessayons après 1 minute, 5 minutes, 30 minutes, 2 heures, 12 heures, 24 heures et 24 heures (8 tentatives au total).

### Événements et renvoi

`GET /v1/events?invoice_id=inv_…` (ou `deposit_id=dep_…`) liste les webhooks d’une facture ou d’un dépôt avec leur `state`, `attempts`, `last_status` et `last_error`. `POST /v1/events/ev_…/redeliver` le remet aussitôt en file, par exemple après correction de votre endpoint. La page « Développeurs › Journal des webhooks » du tableau de bord montre chaque tentative avec la requête et la réponse.

### Mode test

Les factures créées avec une clé `bw_test_` tournent sur les testnets et ne touchent jamais d’argent réel. La page de paiement hébergée affiche un bouton « Simuler un paiement » (complet / moitié / trop-perçu), pour suivre les statuts et les webhooks sans coins de testnet.
