API REST nativa
A API nativa é a interface REST própria da blockwallets: cobranças em centavos de dólar, um ref idempotente por pedido, contas de depósito estáticas que cobrem todas as redes e webhooks assinados com HMAC-SHA256. Ela cria as mesmas cobranças na mesma página de pagamento hospedada que a API compatível com a Cryptomus, com as mesmas taxas e o mesmo saldo.
Gere as suas chaves você mesmo no painel, em “Desenvolvedores › Integração e chaves”: uma chave Live e uma Test com as respectivas chaves de assinatura de webhook. Cada uma é mostrada uma única vez; a lista só mostra o prefixo. Se a sua conta tem a verificação em duas etapas ativa, gerar ou rotacionar uma chave Live pede um código. Ainda não tem conta de lojista? Solicite acesso. Já usa a Cryptomus? Veja a API compatível com a Cryptomus (como escolher).
Chaves e requisições
URL base https://blockwallets.io. Requisições e respostas são em JSON. Toda requisição leva:
Authorization: Bearer bw_live_…ouBearer bw_test_…: o prefixo define o modo, então uma chave Test só enxerga cobranças Test e roda em testnetsContent-Type: application/jsonnas requisiçõesPOST- Guarde as chaves apenas no seu servidor, nunca numa página web ou app; nós armazenamos só o hash delas
Os erros retornam um status HTTP e {"error": {"code": "…", "message": "…"}}: 400 invalid_request (campo ausente ou malformado), 401 unauthorized (chave ausente, desconhecida ou desativada), 404 not_found, 409 conflict (a requisição conflita com o estado atual da cobrança), 422 currency_unsupported.
Endpoints
| Endpoint | O que faz |
|---|---|
GET /v1/networks | Redes que você pode aceitar agora no modo desta chave: mínimo, confirmações, tempo típico de chegada e taxa estimada; as moedas cotadas incluem a cotação atual |
POST /v1/invoices | Cria uma cobrança (201), ou retorna a existente para o mesmo ref (200) |
GET /v1/invoices?ref=… | Consulta uma cobrança pelo seu ref |
GET /v1/invoices/:id | Consulta uma cobrança pelo id |
POST /v1/invoices/:id/network | Escolha você mesmo a rede de uma cobrança (página de pagamento white-label): retorna o address e o pay_amount a exibir |
POST /v1/invoices/:id/close | Fecha uma cobrança pendente que não recebeu nenhuma transferência |
POST /v1/deposit-accounts | Abre (201) ou obtém (200) a conta de depósito de um cliente: um endereço permanente em cada rede |
GET /v1/deposit-accounts?customer_ref=… | Consulta uma conta de depósito pelo customer_ref |
GET /v1/deposit-accounts/:id | Consulta uma conta de depósito pelo id |
GET /v1/deposits | Lista os depósitos de uma conta (account_id ou customer_ref, limit 1–200, padrão 50) |
GET /v1/deposits/:id | Consulta um depósito |
GET /v1/events | Entregas de webhook de uma cobrança ou depósito (invoice_id ou deposit_id) |
POST /v1/events/:id/redeliver | Envia um webhook de novo (202) |
Adicione ?probe=1 à consulta de uma cobrança ou conta de depósito para verificar a blockchain na hora (no máximo uma vez a cada 10 segundos); caso contrário você recebe o último estado do nosso scanner, que verifica a cada minuto.
Criar uma cobrança POST /v1/invoices
Chame POST /v1/invoices quando o cliente finalizar a compra e redirecione-o para a pay_url da resposta.
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}}'| Campo | Obrigatório | Descrição |
|---|---|---|
ref | Sim | O número do seu pedido: 1–64 caracteres entre letras, dígitos e _ . : -. Único por modo; enviar o mesmo ref com os mesmos campos retorna a mesma cobrança, com campos diferentes retorna 409 |
amount | Sim | Valor em centavos de dólar, inteiro: 4200 = $42.00. Não pode ficar abaixo do mínimo da rede depois de escolhida |
title | Sim | O que o cliente vê na página de pagamento, até 64 caracteres |
return_url | Sim | Para onde vai o “Voltar” na página de pagamento; deve estar em um dos domínios do seu site |
success_url | Não | Para onde o cliente vai depois de pagar; o padrão é return_url |
network | Não | Pré-seleciona um id de rede da tabela de redes; omita para o cliente escolher na página de pagamento |
customer_ref | Não | O id do seu cliente, até 64 caracteres, devolvido como está |
lifetime | Não | Duração em segundos, 600 – 43200, padrão 3600 |
amount_rule | Não | "any" = credita o que chegar (recargas): paid assim que confirmar. Omita para usar a “Regra de valor” em “Configurações › Regras de recebimento e taxas” no painel |
accuracy_percent | Não | Quanto de pagamento a menos ainda conta como pago, 0 – 5 (por cento), padrão 0 |
subtract_percent | Não | Parte da taxa da plataforma paga pelo cliente, 0 – 100 (por cento), somada ao pay_amount; omita para usar o padrão do painel |
payment_multiple | Não | Se um pagamento insuficiente pode ser complementado no mesmo endereço, padrão do painel (ligado, a menos que você tenha mudado); false: a primeira transferência insuficiente termina como wrong_amount |
meta | Não | Qualquer objeto JSON de até 2 KB, devolvido como está nas consultas e nos webhooks |
currency | Não | Somente USD (o padrão); qualquer outro retorna 422 |
Toda URL deve estar em um dos domínios do seu site (painel → “Desenvolvedores › Integração e chaves”). A resposta é o objeto cobrança abaixo; network, address e pay_amount são preenchidos assim que uma rede é escolhida.
O objeto cobrança
Os ids começam com inv_. amount está em centavos de dólar; pay_amount e paid_amount são valores da moeda, em strings decimais, na rede escolhida.
{
"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 | Significado | O que você faz |
|---|---|---|
pending | Aguardando pagamento | — |
confirming | Uma transferência foi detectada e está confirmando, ou chegou incompleta e ainda pode ser complementada | — |
paid | Paga | Envie / credite o pedido |
paid_over | Paga, com pagamento a mais | O mesmo; o excedente fica no seu saldo |
wrong_amount | Terminou com menos que o valor total (na expiração, ou na primeira transferência insuficiente quando payment_multiple é false) | Negócios de recarga podem creditar o que chegou (paid_amount; moedas com cotação também trazem paid_cents); lojas pedem ao cliente que pague o restante |
closed | Expirou sem receber nada, ou você a fechou | — (o dinheiro que chegar depois ainda é creditado e notificado) |
Outros campos: pay_url (a página de pagamento hospedada), payments (cada transferência com txid, amount, confirmations e confirmed), expires_at, paid_at, closed_at; BTC, LTC, DOGE, BCH e moedas nativas também trazem rate (dólares por moeda, travado por 30 minutos), rate_locked_until e paid_cents (o que já chegou, em centavos de dólar).
Redes
Passe o id como network. GET /v1/networks retorna as que estão abertas agora no seu modo; uma rede pode ser pausada, então prefira consultar a deixá-la fixa no código.
| network | Moeda | Rede | Mínimo |
|---|---|---|---|
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 |
Contas de depósito (endereços estáticos) POST /v1/deposit-accounts
POST /v1/deposit-accounts com {"customer_ref": "user_42"} dá a esse cliente um endereço permanente em cada rede (todas as redes EVM compartilham um endereço). Tudo o que ele enviar é creditado em dólares americanos, com um webhook deposit.credited por transferência.
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 caracteres entre letras, dígitos e_ . : @ -; o mesmo valor sempre retorna a mesma conta.return_urlopcional nos seus domínios- A resposta traz
deposit_url(uma página hospedada com os endereços, QR codes e depósitos recentes) enetworks(id,address,min_amounteconfirmationsde cada uma) - Os ids de depósito começam com
dep_;amounté em centavos de dólar,amount_tokené o valor da moeda, e as moedas cotadas trazem orateusado
Transferências abaixo do mínimo (geralmente $1, BTC $5) são registradas como below_min e não são creditadas automaticamente; fale com a gente para creditá-las.
Webhooks
Enviamos um POST com JSON para a URL de webhook do painel (“Desenvolvedores › Integração e chaves”; Live e Test têm cada um a sua URL e o seu secret). O corpo é o evento:
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>, ondev1= HMAC-SHA256(webhook secret desse modo,t+.+ corpo bruto)X-BW-Event: o id do evento (ev_…); desduplique por eleX-BW-Delivery: o número da tentativa, começando em 1
| Evento | Quando |
|---|---|
invoice.paid | A cobrança virou paid ou paid_over (inclusive dinheiro que chegou depois de expirar) |
invoice.wrong_amount | A cobrança terminou como wrong_amount |
invoice.closed | A cobrança expirou sem receber nada, ou você a fechou |
deposit.credited | Uma transferência para uma conta de depósito foi creditada |
Verifique contra o corpo bruto antes de interpretá-lo, rejeite um t a mais de 300 segundos do seu relógio e compare em tempo constante. invoice / deposit no corpo é o mesmo objeto que uma consulta retorna.
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));
}Uma resposta 2xx conta como entregue; caso contrário tentamos de novo após 1 minuto, 5 minutos, 30 minutos, 2 horas, 12 horas, 24 horas e 24 horas (8 tentativas no total).
Eventos e reenvio
GET /v1/events?invoice_id=inv_… (ou deposit_id=dep_…) lista os webhooks de uma cobrança ou depósito com state, attempts, last_status e last_error. POST /v1/events/ev_…/redeliver o coloca de novo na fila na hora, por exemplo depois de você corrigir o seu endpoint. “Desenvolvedores › Log de webhooks” no painel mostra cada tentativa com requisição e resposta.
Modo de teste
As cobranças criadas com uma chave bw_test_ rodam em testnets e nunca tocam dinheiro real. A página de pagamento hospedada mostra um botão “Simular pagamento” (total / metade / a mais), para você acompanhar status e webhooks sem moedas de testnet.