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, 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. Vous êtes déjà sur Cryptomus ? Voyez l’API compatible Cryptomus (comment choisir).
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_…ouBearer bw_test_…: le préfixe détermine le mode, donc une clé Test ne voit que les factures Test et fonctionne sur les testnetsContent-Type: application/jsonsur les requêtesPOST- 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 -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 ; 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.
{
"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 -X POST https://blockwallets.io/v1/deposit-accounts \
-H "Authorization: Bearer $BW_KEY" -H "Content-Type: application/json" \
-d '{"customer_ref":"user_42"}'{
"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_urlfacultatif 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) etnetworks(id,address,min_amountetconfirmationspour chacun) - Les ids de dépôt commencent par
dep_;amountest en cents américains,amount_tokenle montant en crypto, et les cryptos cotées portent lerateutilisé
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 :
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 luiX-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.
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.