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.
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).
| Ambiente | Base URL | Estado |
|---|---|---|
| Sandbox | https://api-sandbox.bitpay.ao/v1 | aberto a todos |
| Produção | https://api.bitpay.ao/v1 | após certificação EMIS e onboarding aprovado |
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
/v1/sandbox/accountsHá 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.
curl -X POST https://api-sandbox.bitpay.ao/v1/sandbox/accounts \-H "Content-Type: application/json" \-d '{"name":"A Minha Empresa"}'
{"object": "sandbox_account","id": "acct_…","name": "A Minha Empresa","pk": "pk_test_…","sk": "sk_test_…"}
export BITPAY_SK=sk_test_... # o valor de "sk" acimacurl 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.
Integra o gateway de pagamentos BitPay (Angola) no meu projeto.Lê primeiro https://developers.bitpay.ao/llms.txt ehttps://developers.bitpay.ao/openapi.yaml.- Usa o sandbox https://api-sandbox.bitpay.ao/v1 (cria uma contacom 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 aassinatura 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.
| HTTP | Código | Quando acontece |
|---|---|---|
| 400 | invalid_request | Payload ou parâmetros inválidos (a mensagem diz qual campo) |
| 400 | invalid_json | O corpo não é JSON válido |
| 400 | invalid_phone_number | Número Multicaixa Express em falta ou mal formado |
| 400 | amount_out_of_range | Montante abaixo do mínimo ou acima do máximo do método |
| 400 | method_disabled | Método de pagamento indisponível de momento |
| 401 | authentication_failed | Chave de API em falta, inválida ou revogada |
| 403 | permission_denied | A chave não tem permissão para esta operação |
| 404 | resource_not_found | Objeto ou rota inexistente, ou fora da tua conta |
| 409 | idempotency_conflict | Mesma Idempotency-Key com payload diferente |
| 409 | payment_already_processed | Operação incompatível com o estado atual |
| 410 | payment_link_inactive | Pay Link desativado — já não aceita pagamentos |
| 429 | rate_limit_exceeded | Mais de 120 pedidos/minuto — respeita o Retry-After |
| 500 | internal_error | Erro nosso — repete com a mesma Idempotency-Key |
{"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.
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.
HTTP/1.1 429 Too Many RequestsRetry-After: 17X-RateLimit-Limit: 120X-RateLimit-Remaining: 0X-RateLimit-Reset: 17
Payment Intents
/v1/payment_intents/v1/payment_intents/v1/payment_intents/{id}/v1/payment_intents/{id}/cancelO 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)
| Campo | Tipo | Notas |
|---|---|---|
| amount | inteiro | obrigatório; valor em AOA, sem casas decimais; dentro de min/max do método |
| currency | string | obrigatório; sempre "AOA" |
| payment_method | string | obrigatório; multicaixa_express · multicaixa_reference |
| customer.mobile | string | obrigatório no Express (9 dígitos, começa por 9) |
| merchant_reference | string | opcional; o teu identificador (até 64 caracteres) |
| metadata | objeto | opcional; 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"}'
{"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"}
curl "https://api-sandbox.bitpay.ao/v1/payment_intents?limit=10" \-H "Authorization: Bearer $BITPAY_SK"# troca pay_XXXX pelo id devolvido na criaçãocurl 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
/v1/sandbox/simulate/reference_paidCom 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).
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"}'
{"id": "pay_…","status": "PENDING","payment_method": "multicaixa_reference","reference": {"entity": "90201","number": "221610687","expires_at": "2026-09-30T14:00:00.000Z"}}
# troca 221610687 pelo reference.number que recebestecurl -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.
| Estado | Significado | Evento |
|---|---|---|
| PENDING | criada; à espera do cliente/rail | payment.created |
| PROCESSING | em curso no rail | — |
| SUCCEEDED | pago — o único estado que vale dinheiro | payment.succeeded |
| FAILED | falhou (vê failure_code) | payment.failed |
| UNKNOWN | sem resposta do rail; em reconciliação | payment.unknown |
| EXPIRED | expirou sem pagamento | payment.expired |
| CANCELLED | cancelada pelo comerciante | payment.cancelled |
| PARTIALLY_REFUNDED / REFUNDED | reembolsada em parte/total | refund.succeeded |
"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.mobile | Desfecho |
|---|---|
| 923000000 | PROCESSING → SUCCEEDED (~5s) |
| 923000001 | FAILED · rejected_by_customer (~5s) |
| 923000002 | PROCESSING → UNKNOWN (~10s) → SUCCEEDED tardio (~30s) |
| 923000003 | FAILED · insufficient_funds (~5s) |
| 923000004 | FAILED · provider_unavailable (~2s) |
| qualquer outro 9XXXXXXXX | aprovado (~5s) |
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
/v1/refunds/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 SUCCEEDEDcurl -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}'
Pay by Link
/v1/payment_links/v1/payment_links/v1/payment_links/{id}/v1/payment_links/{id}Cria um link, envia no WhatsApp, recebe. O cliente abre uma página BitPay otimizada para telemóvel, escolhe o método e paga — tu recebes os mesmos webhooks de qualquer pagamento (payment.created, payment.succeeded…), com metadata.payment_link a indicar o link.
DELETE desativa o link e devolve-o com active: false: a página pública passa a responder 410 payment_link_inactive e deixa de aceitar pagamentos; os pagamentos já feitos mantêm-se. GET /v1/payment_links lista os links da conta.
Para testar no sandbox: abre o url devolvido, escolhe Multicaixa Express e paga com 923000000.
curl -X POST https://api-sandbox.bitpay.ao/v1/payment_links \-H "Authorization: Bearer $BITPAY_SK" \-H "Content-Type: application/json" \-d '{"amount":50000,"description":"Consultoria"}'# → { "id": "plink_…", "code": "B7K23P",# "url": "https://pay.bitpay.ao/B7K23P", "active": true }
curl https://api-sandbox.bitpay.ao/v1/payment_links \-H "Authorization: Bearer $BITPAY_SK"# troca plink_XXXX pelo id devolvido na criaçãocurl https://api-sandbox.bitpay.ao/v1/payment_links/plink_XXXX \-H "Authorization: Bearer $BITPAY_SK"curl -X DELETE https://api-sandbox.bitpay.ao/v1/payment_links/plink_XXXX \-H "Authorization: Bearer $BITPAY_SK"
Checkout hospedado
/v1/checkout/sessions/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, … }
{"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
/v1/qr_codes/v1/payment_links/{id}/qrGera 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.
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
/v1/webhook_endpoints/v1/webhook_endpoints/v1/webhook_endpoints/{id}/v1/webhook_endpoints/{id}/test/v1/webhook_endpoints/{id}/deliveriesOs 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.
| Evento | Quando |
|---|---|
| payment.created | intent criada (PENDING) |
| payment.succeeded | pagamento confirmado — entrega a encomenda aqui (uma vez por pagamento) |
| payment.failed | pagamento falhou (vê failure_code) |
| payment.unknown | sem resposta do rail; em reconciliação |
| payment.reconciled | informativo: chega depois de payment.succeeded quando o pagamento esteve em UNKNOWN (sucesso tardio) |
| payment.expired | expirou sem pagamento (ex.: referência não paga em 72h) |
| payment.cancelled | cancelado pelo comerciante |
| reference.paid | referência Multicaixa paga (chega antes de payment.succeeded) |
| refund.succeeded | reembolso concluído (~3s no sandbox) |
| checkout.session.completed | sessão de checkout paga (inclui payment_intent) |
| test.ping | evento 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á
# troca whe_XXXX pelo id do endpointcurl -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"
{"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).
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 rotaapp.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.succeededres.sendStatus(200);});
$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)
/v1/payment_intents/{id}/events/v1/public/payments/{id}/eventsNã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.
# troca pay_XXXX pelo id do pagamentocurl -N https://api-sandbox.bitpay.ao/v1/payment_intents/pay_XXXX/events \-H "Authorization: Bearer $BITPAY_SK"# event: snapshot … event: payment.succeeded
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
/v1/events/v1/logsAlé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.
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.
