Referência da API
Base URL https://api.broski.pt · Autenticação Authorization: Bearer sk_live_… · Valores em cêntimos · Importe o openapi.yaml no Postman ou Insomnia.
POST/v1/orders — Criar pedido
Cria uma cobrança MB WAY ou Multibanco. Header Idempotency-Key obrigatório (1–128 caracteres): reenvio com a mesma chave e o mesmo corpo devolve a resposta original com Idempotent-Replay: true; corpo diferente → 409. Retenção garantida ≥ 24 h.
| Campo | Tipo | Regras |
|---|---|---|
amount | int | Obrigatório, em cêntimos. MB WAY: 50–500000 (€0,50–€5.000). Multibanco: 1–100000000. |
method | string | Obrigatório: mbway | multibanco. |
phone | string | Obrigatório p/ MB WAY. Telemóvel PT: +3519XXXXXXXX. |
external_reference | string | Obrigatório, único por conta: A–Z a–z 0–9 _ - . (1–128). |
currency | string | Opcional; apenas EUR. |
description | string | Opcional (≤140). Nome do produto — aparece no seu painel; não é devolvido pela API. |
customer | objeto | Opcional: name, email, phone, nif (9 dígitos, validado se presente). Armazenado cifrado; nunca devolvido. |
Campo desconhecido no JSON → 400 invalid_json (validação estrita).
Exemplo — MB WAY (201)
curl https://api.broski.pt/v1/orders \
-H "Authorization: Bearer sk_live_..." \
-H "Idempotency-Key: pedido-1042-v1" \
-H "Content-Type: application/json" \
-d '{"amount":1990,"method":"mbway","phone":"+351912345678",
"external_reference":"pedido-1042","description":"Curso de tráfego"}'
{"id":"ord_9f2c1ab34d","status":"pending","amount":1990,"currency":"EUR",
"method":"mbway","external_reference":"pedido-1042","livemode":true,
"created_at":"2026-07-16T14:03:07Z"}
O cliente confirma no app MB WAY; a confirmação chega em segundos via webhook order.paid.
Exemplo — Multibanco (201, com voucher)
{"id":"ord_4c77e0b1aa","status":"awaiting_payment","amount":4990,"currency":"EUR",
"method":"multibanco","external_reference":"pedido-1043","livemode":true,
"created_at":"2026-07-16T14:05:11Z",
"multibanco":{"entity":"11249","reference":"123 456 789","amount":4990,
"expires_at":"2026-07-19T14:05:11Z"}}
Exiba entidade, referência e valor ao cliente — ele paga no homebanking ou numa caixa Multibanco, em geral em até alguns dias. awaiting_payment é um estado normal e duradouro.
Erros específicos: 402 payment_failed (não iniciou; o pedido é desfeito e a external_reference pode ser reutilizada), 409 external_reference_taken, 400 amount_below_fee, 400 currency_not_supported.
GET/v1/orders/{id} — Consultar pedido
Devolve o pedido completo (mesmo shape do POST; paid_at presente quando pago). Pedido inexistente ou de outra conta → 404 resource_not_found.
GET/v1/orders — Listar pedidos
Página de até 25, do mais novo ao mais antigo. Filtros: status (enum de status), method. Paginação por cursor: passe starting_after=<id do último>. Cursor desconhecido é ignorado (devolve a primeira página). Filtro fora do enum → 400.
{"data":[{"id":"ord_…","status":"paid","amount":1990,"method":"mbway",
"external_reference":"pedido-1042","livemode":true,
"created_at":"…","paid_at":"…"}], "has_more":true}
O item da listagem é um resumo (sem currency nem multibanco). Para o objeto completo, consulte o pedido individual.
POST/v1/orders/{id}/refunds — Estornar
Corpo JSON obrigatório — envie {} para estorno total do restante, ou {"amount": 500, "reason": "…"} para parcial (amount 0 ou ausente = total). Parciais são repetíveis até o total (o pedido vira refunded só no total acumulado). Estornos não aceitam Idempotency-Key: após um timeout, confira o pedido antes de repetir — repetir um parcial estorna duas vezes.
201 → {"id":"ref_5ba01cd922","amount":500,"status":"succeeded"}
Erros: 409 order_not_paid, 400 refund_exceeds_amount, 404 resource_not_found.
Webhooks
Cadastre a URL HTTPS no painel (Configurações → Webhooks) — o segredo whbroski_… é exibido uma única vez. Eventos: order.awaiting_payment, order.paid, order.failed, order.expired, order.refunded (lista vazia no cadastro = todos). O endpoint pode receber tipos além destes — responda 2xx e ignore type desconhecido.
POST (sua URL)
Content-Type: application/json
Broski-Signature: t=1750000000,v1=5fb448226598b14dea45ff94ee317c2c…
{"id":"evt_ab12cd34ef","type":"order.paid","created":1750000000,
"data":{"object":{"id":"ord_9f2c1ab34d","status":"paid","amount":1990,
"currency":"EUR","method":"mbway","external_reference":"pedido-1042",
"livemode":true,"created_at":"…","paid_at":"…"}}}
- Verifique sempre a assinatura:
v1 = HMAC-SHA256(secret, t + "." + corpo_cru); rejeite setdivergir do relógio por mais de 5 min (recomendação). Snippets prontos no guia. - Responda 2xx em até 10 s (processe async se precisar). Redirecionamentos contam como falha.
- Entrega at-least-once, sem ordem garantida — deduplique por
iddo evento e confie nostatusdo objeto. - Sem 2xx: retries por ~24 h (imediato, 1m, 5m, 15m, 1h, 3h, 8h, 12h) → aí o evento vai à dead-letter, com reenvio manual no painel (log de entregas incluído).
Códigos de erro
Toda resposta de erro usa o envelope {"error":{"type","code","message","param?"}} com mensagem em PT-BR.
| HTTP | code | Quando |
|---|---|---|
| 400 | validation_failed | Campo inválido (veja param quando presente). |
| 400 | invalid_json | JSON malformado ou campo desconhecido. |
| 400 | currency_not_supported | Moeda diferente de EUR. |
| 400 | amount_below_fee | Valor menor que a tarifa aplicável. |
| 400 | refund_exceeds_amount | Estorno maior que o restante. |
| 401 | unauthorized | Chave ausente, inválida ou revogada. |
| 402 | payment_failed | O pagamento não pôde ser iniciado. |
| 404 | resource_not_found | Recurso inexistente (ou de outra conta). |
| 409 | external_reference_taken | Referência já usada. |
| 409 | idempotency_key_reused | Mesma Idempotency-Key com corpo diferente. |
| 409 | order_not_paid | Estorno de pedido não pago. |
| 413 | body_too_large | Corpo acima de 64 KB. |
| 429 | rate_limited | Acima de 120 req/min por chave. |
| 500 | internal_error | Erro nosso — tente novamente; persiste? fale com o suporte. |