A BitPay ainda não está em produção: aguardamos a certificação da EMIS. Podes criar conta e testar tudo em sandbox — avisamos-te quando os pagamentos reais abrirem.

BitPay

Developers

Referência da API

Tudo o que precisas para integrar a BitPay. Os exemplos são reais — define a tua chave, copia, cola e corre contra o sandbox agora mesmo.

sandbox · https://api-sandbox.bitpay.ao/v1produção · https://api.bitpay.ao/v1

Introdução

A API BitPay é REST/JSON: recursos previsíveis, verbos HTTP padrão e erros consistentes. Uma única integração dá-te os métodos de pagamento de Angola — Referências Multicaixa e Multicaixa Express hoje; Débitos Directos (P3) e UNITEL Money brevemente.

A API é channel-agnostic: o mesmo /v1 serve o teu site, a tua app móvel e os teus sistemas internos. Passar do sandbox para produção é trocar o host e as credenciais — nada mais.

Pré-lançamento: a BitPay aguarda a certificação da EMIS. Até lá só o sandbox está aberto — podes construir e testar a integração completa; nenhum dinheiro real é movimentado.

Envia sempre o corpo em JSON com o header Content-Type: application/json. Todos os montantes são inteiros em AOA (sem casas decimais).

AmbienteBase URLEstado
Sandboxhttps://api-sandbox.bitpay.ao/v1aberto a todos
Produçãohttps://api.bitpay.ao/v1após certificação EMIS e onboarding aprovado
bashO teu primeiro pedido
export BITPAY_SK=sk_test_... # a tua secret key (ver Contas e autenticação)
curl https://api-sandbox.bitpay.ao/v1/payment_methods \
-H "Authorization: Bearer $BITPAY_SK"

Contas e autenticação

POST/v1/sandbox/accounts

Há dois caminhos para obter chaves sandbox — ambos imediatos e gratuitos:

1) Recomendado — cria conta em https://dashboard.bitpay.ao (email e palavra-passe) e copia as chaves em Developers → Chaves. Ganhas o painel: pagamentos, Pay Links, botão "Enviar evento de teste" nos webhooks e logs de cada pedido.

2) Rápido, só API — um único POST /v1/sandbox/accounts devolve pk_test_ e sk_test_ na hora, sem email. Ideal para testes automáticos e assistentes de IA. Esta conta não tem utilizador, por isso não entra no dashboard; tudo o que o dashboard faz tens por API (eventos de teste, entregas de webhooks, eventos e logs).

Autentica cada pedido com a secret key no header Authorization: Bearer. As chaves sandbox começam por sk_test_, as de produção por sk_live_. A secret key vive apenas no teu servidor — nunca numa app móvel, no browser ou num repositório.

bashCriar conta sandbox só-API (sem auth)
curl -X POST https://api-sandbox.bitpay.ao/v1/sandbox/accounts \
-H "Content-Type: application/json" \
-d '{"name":"A Minha Empresa"}'
jsonResposta
{
"object": "sandbox_account",
"id": "acct_…",
"name": "A Minha Empresa",
"pk": "pk_test_…",
"sk": "sk_test_…"
}
bashGuarda a chave para os exemplos seguintes
export BITPAY_SK=sk_test_... # o valor de "sk" acima
curl https://api-sandbox.bitpay.ao/v1/account \
-H "Authorization: Bearer $BITPAY_SK"

Integração com assistente de IA

Usas Claude, Cursor, Copilot ou outro assistente? Dá-lhe as duas fontes oficiais: https://developers.bitpay.ao/llms.txt (guia compacto, escrito para modelos) e https://developers.bitpay.ao/openapi.yaml (contrato completo da API).

Com isso, o assistente consegue criar a conta sandbox, gerar o código de integração e testar todos os cenários sozinho — os exemplos desta página correm literalmente.

Regra de ouro a manter no código gerado: o pagamento só está confirmado com o webhook payment.succeeded (assinatura verificada) — nunca pelo redirect nem pelo estado PENDING.

bashPrompt pronto a colar
Integra o gateway de pagamentos BitPay (Angola) no meu projeto.
Lê primeiro https://developers.bitpay.ao/llms.txt e
https://developers.bitpay.ao/openapi.yaml.
- Usa o sandbox https://api-sandbox.bitpay.ao/v1 (cria uma conta
com POST /v1/sandbox/accounts se eu não tiver chave).
- Guarda a secret key só no servidor (variável BITPAY_SK).
- Cria pagamentos com Idempotency-Key = id da encomenda.
- Confirma a encomenda SÓ no webhook payment.succeeded, com a
assinatura BitPay-Signature verificada e dedupe por event id.
- Testa com os números 923000000 (aprovado), 923000001
(rejeitado) e 923000002 (UNKNOWN → sucesso tardio).

Erros

Todos os erros têm o mesmo formato: um código estável (para o teu código tratar), uma mensagem clara (para humanos) e um request_id (para diagnóstico nos logs). Os códigos nunca mudam de significado entre versões.

Indisponibilidade do rail não é um erro HTTP: o pedido é aceite (201) e o desfecho chega no próprio intent como status FAILED com failure_code "provider_unavailable" — por webhook (payment.failed), consulta ou SSE. No sandbox, o número 923000004 simula este cenário.

Os limites de montante (min_amount / max_amount) de cada método estão em GET /v1/payment_methods — por exemplo, mínimo de 100 AOA.

HTTPCódigoQuando acontece
400invalid_requestPayload ou parâmetros inválidos (a mensagem diz qual campo)
400invalid_jsonO corpo não é JSON válido
400invalid_phone_numberNúmero Multicaixa Express em falta ou mal formado
400amount_out_of_rangeMontante abaixo do mínimo ou acima do máximo do método
400method_disabledMétodo de pagamento indisponível de momento
401authentication_failedChave de API em falta, inválida ou revogada
403permission_deniedA chave não tem permissão para esta operação
404resource_not_foundObjeto ou rota inexistente, ou fora da tua conta
409idempotency_conflictMesma Idempotency-Key com payload diferente
409payment_already_processedOperação incompatível com o estado atual
410payment_link_inactivePay Link desativado — já não aceita pagamentos
429rate_limit_exceededMais de 120 pedidos/minuto — respeita o Retry-After
500internal_errorErro nosso — repete com a mesma Idempotency-Key
jsonFormato de erro
{
"error": {
"code": "invalid_phone_number",
"message": "'customer.mobile' é obrigatório…",
"request_id": "req_9f1b2c…"
}
}

Idempotência

Redes falham e retries acontecem — a idempotência garante que nunca cobras duas vezes. O header Idempotency-Key (8 a 128 caracteres) é obrigatório em POST /v1/payment_intents e POST /v1/refunds.

Repetir o pedido com a mesma chave e o mesmo corpo devolve a resposta original — mesmo código HTTP (ex.: 201) e mesmo objeto — com o header Idempotency-Replayed: true. O corpo é comparado pelo conteúdo JSON (a ordem das chaves não conta). A mesma chave com um corpo diferente devolve 409 idempotency_conflict.

Usa um identificador do teu domínio (o número da encomenda, por exemplo) ou um UUID guardado antes do envio.

bashRetry seguro
curl -i -X POST https://api-sandbox.bitpay.ao/v1/payment_intents \
-H "Authorization: Bearer $BITPAY_SK" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: encomenda-1042" \
-d '{"amount":25000,"currency":"AOA","payment_method":"multicaixa_express","customer":{"mobile":"923000000"}}'
# repetir exatamente o mesmo comando → mesmo pay_…, HTTP 201,
# header Idempotency-Replayed: true — sem dupla cobrança

Limites de pedidos

Cada chave pode fazer até 120 pedidos por minuto. As respostas trazem X-RateLimit-Limit, X-RateLimit-Remaining e X-RateLimit-Reset (segundos até a janela reabrir). Acima do limite recebes 429 rate_limit_exceeded com o header Retry-After — espera esse número de segundos e repete.

Para acompanhar pagamentos prefere webhooks ou o stream SSE a polling. Se precisares de consultar, faz no máximo 1 pedido a cada 2 segundos por pagamento, com backoff.

bashCabeçalhos de limite
HTTP/1.1 429 Too Many Requests
Retry-After: 17
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 17

Payment Intents

POST/v1/payment_intents
GET/v1/payment_intents
GET/v1/payment_intents/{id}
POST/v1/payment_intents/{id}/cancel

O payment intent é o objeto central da BitPay: representa uma cobrança, do pedido à confirmação, em qualquer método. Cria-lo devolve 201 em milissegundos com estado PENDING; o desfecho chega por webhook e em tempo real por SSE.

No Multicaixa Express indica o número do cliente — ele confirma na app. Nas Referências recebes entidade, número e validade (ver Referências Multicaixa).

GET /v1/payment_intents lista os mais recentes primeiro (?limit=, por omissão 25, máximo 500). O cancelamento só é possível em PENDING/PROCESSING e emite payment.cancelled.

Parâmetros do corpo (POST)

CampoTipoNotas
amountinteiroobrigatório; valor em AOA, sem casas decimais; dentro de min/max do método
currencystringobrigatório; sempre "AOA"
payment_methodstringobrigatório; multicaixa_express · multicaixa_reference
customer.mobilestringobrigatório no Express (9 dígitos, começa por 9)
merchant_referencestringopcional; o teu identificador (até 64 caracteres)
metadataobjetoopcional; pares chave-valor livres
curl -X POST https://api-sandbox.bitpay.ao/v1/payment_intents \
-H "Authorization: Bearer $BITPAY_SK" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: encomenda-1042" \
-d '{
"amount": 25000,
"currency": "AOA",
"payment_method": "multicaixa_express",
"customer": { "mobile": "923000000" },
"merchant_reference": "ENC-1042"
}'
json201 Created
{
"id": "pay_7af191ff28b7…",
"object": "payment_intent",
"amount": 25000,
"currency": "AOA",
"payment_method": "multicaixa_express",
"status": "PENDING",
"failure_code": null,
"customer": { "mobile": "923000000" },
"merchant_reference": "ENC-1042",
"reference": null,
"amount_refunded": 0,
"metadata": {},
"livemode": false,
"created_at": "2026-09-27T14:00:00.000Z"
}
bashListar · Consultar · Cancelar
curl "https://api-sandbox.bitpay.ao/v1/payment_intents?limit=10" \
-H "Authorization: Bearer $BITPAY_SK"
# troca pay_XXXX pelo id devolvido na criação
curl https://api-sandbox.bitpay.ao/v1/payment_intents/pay_XXXX \
-H "Authorization: Bearer $BITPAY_SK"
curl -X POST https://api-sandbox.bitpay.ao/v1/payment_intents/pay_XXXX/cancel \
-H "Authorization: Bearer $BITPAY_SK"
# só em PENDING/PROCESSING → CANCELLED + evento payment.cancelled

Referências Multicaixa

POST/v1/sandbox/simulate/reference_paid

Com payment_method "multicaixa_reference" a resposta traz o objeto reference: entidade, número de referência e validade. Mostra os três ao cliente — ele paga no ATM ou no internet banking.

A referência é válida por 72 horas (3 dias). Se não for paga, o intent passa a EXPIRED e recebes payment.expired. Quando é paga recebes reference.paid e payment.succeeded.

No sandbox simulas o pagamento no "ATM" com POST /v1/sandbox/simulate/reference_paid, indicando o número da referência (o valor de reference.number).

bashCriar uma referência
curl -X POST https://api-sandbox.bitpay.ao/v1/payment_intents \
-H "Authorization: Bearer $BITPAY_SK" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: encomenda-2001" \
-d '{"amount":15000,"currency":"AOA","payment_method":"multicaixa_reference","merchant_reference":"ENC-2001"}'
jsonResposta (excerto)
{
"id": "pay_…",
"status": "PENDING",
"payment_method": "multicaixa_reference",
"reference": {
"entity": "90201",
"number": "221610687",
"expires_at": "2026-09-30T14:00:00.000Z"
}
}
bashSandbox: pagar a referência no "ATM"
# troca 221610687 pelo reference.number que recebeste
curl -X POST https://api-sandbox.bitpay.ao/v1/sandbox/simulate/reference_paid \
-H "Authorization: Bearer $BITPAY_SK" \
-H "Content-Type: application/json" \
-d '{"reference":"221610687"}'
# → intent SUCCEEDED + eventos reference.paid e payment.succeeded

Estados

A máquina de estados é curta e sem surpresas. Regra absoluta: PENDING não é pago — só SUCCEEDED conta como dinheiro confirmado.

Timeout nunca vira falha: se o rail não responder, o pagamento entra em UNKNOWN (evento payment.unknown) e a reconciliação da BitPay resolve. No sucesso tardio recebes payment.succeeded e também payment.reconciled para o mesmo pagamento — entrega a encomenda no payment.succeeded, de forma idempotente pelo id do pagamento. Nunca lances uma segunda cobrança por tua conta: repete com a mesma Idempotency-Key.

EstadoSignificadoEvento
PENDINGcriada; à espera do cliente/railpayment.created
PROCESSINGem curso no rail—
SUCCEEDEDpago — o único estado que vale dinheiropayment.succeeded
FAILEDfalhou (vê failure_code)payment.failed
UNKNOWNsem resposta do rail; em reconciliaçãopayment.unknown
EXPIREDexpirou sem pagamentopayment.expired
CANCELLEDcancelada pelo comerciantepayment.cancelled
PARTIALLY_REFUNDED / REFUNDEDreembolsada em parte/totalrefund.succeeded
jsonfailure_code
"rejected_by_customer" // cliente recusou
"insufficient_funds" // saldo insuficiente
"provider_unavailable" // operador em baixo

Números de teste

O sandbox é determinístico: o mesmo número produz sempre o mesmo desfecho, com os tempos reais de um rail (2–30 segundos). Testa o caminho feliz e todos os difíceis antes de ires para produção.

Os mesmos números funcionam nas páginas de Pay Link e de Checkout: abre o url devolvido, escolhe Multicaixa Express e usa 923000000 para aprovar. Para Referências, vê Referências Multicaixa.

customer.mobileDesfecho
923000000PROCESSING → SUCCEEDED (~5s)
923000001FAILED · rejected_by_customer (~5s)
923000002PROCESSING → UNKNOWN (~10s) → SUCCEEDED tardio (~30s)
923000003FAILED · insufficient_funds (~5s)
923000004FAILED · provider_unavailable (~2s)
qualquer outro 9XXXXXXXXaprovado (~5s)
bashTestar um cenário difícil
curl -X POST https://api-sandbox.bitpay.ao/v1/payment_intents \
-H "Authorization: Bearer $BITPAY_SK" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: teste-unknown-001" \
-d '{"amount":5000,"currency":"AOA","payment_method":"multicaixa_express","customer":{"mobile":"923000002"}}'

Reembolsos

POST/v1/refunds
GET/v1/refunds/{id}

Reembolsa pagamentos SUCCEEDED (ou PARTIALLY_REFUNDED), no total ou em parte. Omitir amount reembolsa o restante. O reembolso conclui em segundos: o intent passa a PARTIALLY_REFUNDED ou REFUNDED e recebes refund.succeeded por webhook. Idempotency-Key obrigatório.

# troca pay_XXXX pelo id de um pagamento SUCCEEDED
curl -X POST https://api-sandbox.bitpay.ao/v1/refunds \
-H "Authorization: Bearer $BITPAY_SK" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: refund-1042" \
-d '{"payment_intent":"pay_XXXX","amount":5000}'

Checkout hospedado

POST/v1/checkout/sessions
GET/v1/checkout/sessions/{id}

A forma mais rápida de aceitar pagamentos num site: cria uma sessão, redireciona o cliente para o url do checkout BitPay, e ele volta ao teu success_url depois de pagar. A confirmação de verdade chega sempre pelo webhook — nunca confies só no redirect.

Sessões expiram em 30 minutos e fecham (status COMPLETED) quando o pagamento entra: recebes checkout.session.completed e payment.succeeded. O objeto da sessão inclui payment_intent (id do pagamento; null até haver um), merchant_reference, success_url e cancel_url — usa-os para ligar a sessão à tua encomenda.

Para testar no sandbox: abre o url, escolhe Multicaixa Express e paga com 923000000.

curl -X POST https://api-sandbox.bitpay.ao/v1/checkout/sessions \
-H "Authorization: Bearer $BITPAY_SK" \
-H "Content-Type: application/json" \
-d '{
"amount": 75000,
"merchant_reference": "ENC-1042",
"success_url": "https://aloja.ao/obrigado",
"cancel_url": "https://aloja.ao/carrinho"
}'
# → { "id": "cs_…", "url": "https://checkout.bitpay.ao/cs_…",
# "status": "OPEN", "payment_intent": null, … }
jsonConsultar a sessão (depois de paga)
{
"id": "cs_…",
"object": "checkout_session",
"url": "https://checkout.bitpay.ao/cs_…",
"amount": 75000,
"status": "COMPLETED",
"payment_intent": "pay_…",
"merchant_reference": "ENC-1042",
"success_url": "https://aloja.ao/obrigado",
"cancel_url": "https://aloja.ao/carrinho",
"expires_at": "…"
}

QR Codes

POST/v1/qr_codes
GET/v1/payment_links/{id}/qr

Gera um QR pronto a imprimir para a loja física, delivery ou eventos — cada leitura abre a página de pagamento do link associado. POST /v1/qr_codes aceita amount (cria um link novo) ou payment_link (o code de um link existente) e devolve o SVG inline no campo svg. GET /v1/payment_links/{id}/qr devolve a imagem SVG diretamente.

bashGerar um QR
curl -X POST https://api-sandbox.bitpay.ao/v1/qr_codes \
-H "Authorization: Bearer $BITPAY_SK" \
-H "Content-Type: application/json" \
-d '{"amount":25000,"description":"Mesa 4"}'
# → { "payment_link": "B7K23P", "url": "https://pay.bitpay.ao/B7K23P", "svg": "<svg…" }
# QR de um link existente, gravado em ficheiro (troca plink_XXXX):
curl https://api-sandbox.bitpay.ao/v1/payment_links/plink_XXXX/qr \
-H "Authorization: Bearer $BITPAY_SK" -o qr.svg

Webhooks

POST/v1/webhook_endpoints
GET/v1/webhook_endpoints
DELETE/v1/webhook_endpoints/{id}
POST/v1/webhook_endpoints/{id}/test
GET/v1/webhook_endpoints/{id}/deliveries

Os webhooks são a fonte de verdade da tua integração: cada mudança de estado chega ao teu endpoint como um POST JSON assinado. O campo events aceita a lista de eventos que queres ou ["*"] para todos (é o valor por omissão).

A entrega é at-least-once com retries (10s, 60s, 5min): o mesmo evento pode chegar mais do que uma vez — deduplica pelo header BitPay-Event-Id (= id do evento) e torna o teu fulfillment idempotente pelo id do pagamento. Responde 2xx depressa e processa depois.

Para testar sem esperar por um pagamento: POST /v1/webhook_endpoints/{id}/test envia um evento test.ping (responde 202 com o event_id). GET /v1/webhook_endpoints/{id}/deliveries mostra as últimas entregas com event_type, state, attempts, last_status (HTTP), last_ms (latência), last_attempt_at, request_body e signature_header — dá para depurar a verificação da assinatura sem adivinhar. No dashboard tens o mesmo no botão "Enviar evento de teste".

Precisas de um URL público: em desenvolvimento usa um túnel (ngrok, cloudflared) ou um serviço tipo webhook.site.

EventoQuando
payment.createdintent criada (PENDING)
payment.succeededpagamento confirmado — entrega a encomenda aqui (uma vez por pagamento)
payment.failedpagamento falhou (vê failure_code)
payment.unknownsem resposta do rail; em reconciliação
payment.reconciledinformativo: chega depois de payment.succeeded quando o pagamento esteve em UNKNOWN (sucesso tardio)
payment.expiredexpirou sem pagamento (ex.: referência não paga em 72h)
payment.cancelledcancelado pelo comerciante
reference.paidreferência Multicaixa paga (chega antes de payment.succeeded)
refund.succeededreembolso concluído (~3s no sandbox)
checkout.session.completedsessão de checkout paga (inclui payment_intent)
test.pingevento de teste enviado por /test ou pelo dashboard
curl -X POST https://api-sandbox.bitpay.ao/v1/webhook_endpoints \
-H "Authorization: Bearer $BITPAY_SK" \
-H "Content-Type: application/json" \
-d '{"url":"https://aloja.ao/webhooks/bitpay","events":["*"]}'
# → { "id": "whe_…", "secret": "whsec_…" } ← guarda o secret já
bashEnviar evento de teste · ver entregas
# troca whe_XXXX pelo id do endpoint
curl -X POST https://api-sandbox.bitpay.ao/v1/webhook_endpoints/whe_XXXX/test \
-H "Authorization: Bearer $BITPAY_SK"
curl https://api-sandbox.bitpay.ao/v1/webhook_endpoints/whe_XXXX/deliveries \
-H "Authorization: Bearer $BITPAY_SK"
jsonCorpo de um evento
{
"id": "evt_…",
"object": "event",
"type": "payment.succeeded",
"livemode": false,
"created_at": "2026-09-27T14:00:05.000Z",
"data": { "id": "pay_…", "object": "payment_intent", "status": "SUCCEEDED", … }
}

Verificar a assinatura

Cada entrega traz o header BitPay-Signature no formato t=<unix>,v1=<hex>, onde v1 é o HMAC-SHA256, em hexadecimal, da string "<t>.<corpo>" com o teu segredo whsec_ como chave. Usa o corpo CRU recebido (bytes exatos), antes de qualquer JSON.parse.

Rejeita (responde 400) se o header estiver mal formado, se a assinatura não bater ou se o timestamp tiver mais de 10 minutos (600 s) de diferença — protege-te contra replays. Os SDKs oficiais trazem esta verificação pronta (verifyWebhook).

jsVerificar a assinatura (Node + Express)
const crypto = require("node:crypto");
function verifyBitPay(rawBody, header, secret, toleranceSec = 600) {
const m = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header || "");
if (!m) return false;
const t = Number(m[1]);
if (Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
const expected = crypto
.createHmac("sha256", secret)
.update(`${t}.${rawBody}`)
.digest("hex");
const a = Buffer.from(expected, "hex");
const b = Buffer.from(m[2], "hex");
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
// Express: lê o corpo CRU nesta rota
app.post("/webhooks/bitpay", express.raw({ type: "*/*" }), (req, res) => {
const raw = req.body.toString("utf8");
if (!verifyBitPay(raw, req.get("BitPay-Signature"), process.env.BITPAY_WHSEC))
return res.sendStatus(400);
const evt = JSON.parse(raw);
// dedupe por evt.id; entrega a encomenda em payment.succeeded
res.sendStatus(200);
});
phpVerificar a assinatura (PHP)
$raw = file_get_contents("php://input");
$hdr = $_SERVER["HTTP_BITPAY_SIGNATURE"] ?? "";
if (!preg_match('/^t=(\d+),v1=([0-9a-f]{64})$/', $hdr, $m)
|| abs(time() - (int)$m[1]) > 600
|| !hash_equals(hash_hmac("sha256", $m[1] . "." . $raw, getenv("BITPAY_WHSEC")), $m[2])) {
http_response_code(400); exit;
}
$evt = json_decode($raw, true);
http_response_code(200);

Tempo real (SSE)

GET/v1/payment_intents/{id}/events
GET/v1/public/payments/{id}/events

Não faças polling agressivo — liga-te ao stream de um pagamento e recebe o estado no instante em que muda. A ligação envia primeiro um evento snapshot com o estado atual (ideal para retomar páginas fechadas) e fecha sozinha em estados terminais.

Server-side, acompanha qualquer intent com GET /v1/payment_intents/{id}/events e a tua secret key (ex.: curl -N).

No browser usa o endpoint público GET /v1/public/payments/{id}/events — sem credenciais, para pagamentos criados via Pay by Link ou Checkout. O EventSource não envia o header Authorization, por isso o endpoint autenticado responde 401 no browser.

bashServidor (curl)
# troca pay_XXXX pelo id do pagamento
curl -N https://api-sandbox.bitpay.ao/v1/payment_intents/pay_XXXX/events \
-H "Authorization: Bearer $BITPAY_SK"
# event: snapshot … event: payment.succeeded
jsBrowser (EventSource)
const es = new EventSource(
`https://api-sandbox.bitpay.ao/v1/public/payments/${id}/events`);
es.addEventListener("payment.succeeded", (e) => {
const pi = JSON.parse(e.data);
mostrarSucesso(pi);
es.close();
});

Eventos & Logs

GET/v1/events
GET/v1/logs

Além dos webhooks, podes consultar o histórico de eventos da conta e os logs de cada pedido HTTP que fizeste — método, caminho, estado, código de erro e latência, identificados pelo request_id. É a tua primeira paragem quando algo não bate certo.

bashConsultar
curl "https://api-sandbox.bitpay.ao/v1/events?limit=20" \
-H "Authorization: Bearer $BITPAY_SK"
curl "https://api-sandbox.bitpay.ao/v1/logs?limit=20" \
-H "Authorization: Bearer $BITPAY_SK"

SDKs e plugins

SDKs oficiais para Node.js, PHP, Python, Java e Flutter/Dart — zero dependências, já apontados para o sandbox (https://api-sandbox.bitpay.ao/v1), com Content-Type e Idempotency-Key automáticos e verificação de webhooks incluída.

Ainda não estão publicados no npm, Composer ou PyPI: descarrega o .zip na página de developers e copia-o para o teu projeto — o README de cada SDK tem um quickstart que corre tal como está.

Para lojas WooCommerce há o plugin BitPay for WooCommerce (beta): instala, cola a secret key e grava — o webhook configura-se sozinho.

Referência da API — BitPay