{"openapi":"3.1.0","info":{"title":"Provento API","version":"1.0.0","description":"API REST do gateway de pagamentos Provento. Pix, cartão e boleto sob um contrato único, com checkout hospedado, idempotência e webhooks assinados."},"servers":[{"url":"https://pay.provento.tech","description":"Produção"},{"url":"http://localhost:3000","description":"Desenvolvimento local"}],"tags":[{"name":"Cobranças","description":"O ciclo de vida de um pagamento. Você cria uma cobrança, redireciona o cliente para a `checkout_url` (ou confirma via API) e acompanha o status."},{"name":"Checkout","description":"Endpoints internos do checkout hospedado, servidos sob `/api/`. Não exigem chave — são escopados pelo id da cobrança, que funciona como capability. Para integrar do seu servidor, use `/v1/charges/{id}/confirm`."},{"name":"Webhooks","description":"Notificações de mudança de status, assinadas com HMAC-SHA256. Inclui os endpoints que simulam a confirmação assíncrona de Pix e boleto."}],"components":{"securitySchemes":{"chaveSecreta":{"type":"http","scheme":"bearer","description":"Sua chave secreta no header `Authorization: Bearer sk_live_...`. Gere em Painel → Desenvolvedores. Nunca exponha a chave `live` no front-end."},"segredoCron":{"type":"http","scheme":"bearer","description":"Segredo do worker (`PROVENTO_CRON_SECRET`). Uso interno."}},"schemas":{"Cobranca":{"type":"object","description":"Resumo de uma cobrança.","required":["id","status","amount","currency","methods","checkout_url","created_at"],"properties":{"id":{"type":"string","format":"uuid","description":"Identificador da cobrança."},"status":{"type":"string","enum":["criada","processando","paga","parcialmente_paga","falha","expirada","cancelada","estornada"],"description":"Estado atual."},"amount":{"type":"integer","description":"Valor total em centavos.","example":42390},"paid_amount":{"type":"integer","description":"Valor já pago, em centavos."},"currency":{"type":"string","enum":["BRL"],"description":"Moeda (ISO 4217)."},"methods":{"type":"array","description":"Métodos aceitos nesta cobrança.","items":{"type":"string","enum":["pix","cartao_credito","cartao_debito","boleto"]}},"order_ref":{"type":"string","nullable":true,"description":"Sua referência de pedido."},"checkout_url":{"type":"string","format":"uri","description":"Página de checkout hospedada. Redirecione seu cliente para cá."},"created_at":{"type":"string","format":"date-time","description":"Criação (ISO 8601)."}}},"Tentativa":{"type":"object","description":"Uma tentativa de pagar a cobrança num adquirente. Uma cobrança pode ter várias — cartão recusado gera nova tentativa, possivelmente noutro provedor.","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["criada","processando","autorizada","capturada","recusada","erro","timeout","cancelada"]},"method":{"type":"string","enum":["pix","cartao_credito","cartao_debito","boleto"]},"provider_external_id":{"type":"string","nullable":true,"description":"NSU / referência do adquirente."},"card_brand":{"type":"string","nullable":true,"description":"Bandeira."},"card_last4":{"type":"string","nullable":true,"description":"4 últimos dígitos."},"pix_qr_code":{"type":"string","nullable":true,"description":"Payload do QR Code Pix."},"boleto_line":{"type":"string","nullable":true,"description":"Linha digitável."},"error_code":{"type":"string","nullable":true,"description":"Código de erro normalizado, quando recusada."},"created_at":{"type":"string","format":"date-time"}}},"Lancamento":{"type":"object","description":"Uma linha do razão. O sinal vai embutido no valor: `cobranca` é positivo, taxas são negativas. Sua margem é a soma de `taxa_provento` menos `taxa_provedor`.","properties":{"type":{"type":"string","enum":["cobranca","taxa_provedor","taxa_provento","estorno","estorno_taxa_provedor","estorno_taxa_provento","chargeback","repasse_lojista","cobranca_provento","ajuste_manual","correcao_taxa"]},"amount":{"type":"integer","description":"Valor com sinal, em centavos."},"competency_date":{"type":"string","format":"date","description":"Data de competência."}}},"CobrancaDetalhada":{"type":"object","description":"A cobrança com suas tentativas e os lançamentos gerados no razão.","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["criada","processando","paga","parcialmente_paga","falha","expirada","cancelada","estornada"]},"amount":{"type":"integer"},"paid_amount":{"type":"integer"},"currency":{"type":"string","enum":["BRL"]},"methods":{"type":"array","items":{"type":"string","enum":["pix","cartao_credito","cartao_debito","boleto"]}},"order_ref":{"type":"string","nullable":true},"discount":{"type":"integer","description":"Desconto do cupom aplicado pelo comprador, em centavos."},"merchant_discount":{"type":"integer","description":"Abatimento dado pelo lojista sobre o total, em centavos. Já descontado de `amount`."},"merchant_discount_percent":{"type":"number","nullable":true,"description":"O percentual, quando o abatimento do lojista foi em %."},"checkout_url":{"type":"string","format":"uri"},"created_at":{"type":"string","format":"date-time"},"attempts":{"type":"array","items":{"$ref":"#/components/schemas/Tentativa"}},"ledger":{"type":"array","items":{"$ref":"#/components/schemas/Lancamento"}}}},"Erro":{"type":"object","description":"Formato único de erro da API.","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_error","unauthorized","not_found","conflict","internal_error"]},"message":{"type":"string","description":"Mensagem legível."},"fields":{"type":"object","description":"Erros por campo, quando `validation_error`."}}}}}}},"paths":{"/v1/charges":{"post":{"operationId":"criarCobranca","summary":"Criar cobrança","tags":["Cobranças"],"security":[{"chaveSecreta":[]}],"description":"Cria uma cobrança e devolve a `checkout_url`. Envie o header `Idempotency-Key` com um valor único por pedido: repetir a mesma chave devolve a cobrança original em vez de criar outra — é a sua proteção contra cobrança em duplicidade quando a rede falha.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Chave única por operação (recomendado um UUID). Reenvios com a mesma chave retornam a cobrança já criada.","schema":{"type":"string","example":"b1f4c0de-2a77-4f31-9d0c-7c1a3e5b8a90"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["amount"],"properties":{"amount":{"type":"integer","description":"Valor total em centavos. R$ 423,90 → `42390`.","example":42390},"currency":{"type":"string","enum":["BRL"],"description":"Padrão `BRL`."},"methods":{"type":"array","description":"Métodos aceitos. Padrão `[\"pix\"]`.","items":{"type":"string","enum":["pix","cartao_credito","cartao_debito","boleto"]}},"order_ref":{"type":"string","description":"Sua referência de pedido — aparece no painel e na conciliação.","example":"Pedido #1234"},"customer_id":{"type":"string","description":"Id do cliente final, se você já o cadastrou."}}},"example":{"amount":42390,"currency":"BRL","methods":["pix","cartao_credito"],"order_ref":"Pedido #1234"}}}},"responses":{"201":{"description":"Cobrança criada.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Cobranca"},"example":{"id":"3f1c9a2e-8b4d-4f77-9a10-2c5e6d8b1f04","status":"criada","amount":42390,"paid_amount":0,"currency":"BRL","methods":["pix"],"order_ref":"Pedido #1234","checkout_url":"https://pay.provento.tech/checkout/3f1c9a2e-8b4d-4f77-9a10-2c5e6d8b1f04","created_at":"2026-08-11T12:00:00.000Z"}}}},"401":{"description":"Chave de API ausente ou inválida.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Erro"}}}},"422":{"description":"Corpo inválido.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Erro"}}}}}}},"/v1/charges/{id}":{"get":{"operationId":"consultarCobranca","summary":"Consultar cobrança","tags":["Cobranças"],"security":[{"chaveSecreta":[]}],"description":"Devolve a cobrança com todas as tentativas e os lançamentos que ela gerou no razão. É o endpoint de polling se você ainda não configurou webhooks.","parameters":[{"name":"id","in":"path","required":true,"description":"Id da cobrança.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"A cobrança.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CobrancaDetalhada"},"example":{"id":"3f1c9a2e-8b4d-4f77-9a10-2c5e6d8b1f04","status":"paga","amount":42390,"paid_amount":42390,"currency":"BRL","methods":["pix"],"order_ref":"Pedido #1234","checkout_url":"https://pay.provento.tech/checkout/3f1c9a2e-8b4d-4f77-9a10-2c5e6d8b1f04","created_at":"2026-08-11T12:00:00.000Z","attempts":[{"id":"9c2f7b10-4e51-4a03-8f6d-1b2c3d4e5f60","status":"capturada","method":"pix","provider_external_id":"psp_8f21ac","card_brand":null,"card_last4":null,"pix_qr_code":"00020126580014BR.GOV.BCB.PIX...","boleto_line":null,"error_code":null,"created_at":"2026-08-11T12:00:04.000Z"}],"ledger":[{"type":"cobranca","amount":42390,"competency_date":"2026-08-11"},{"type":"taxa_provedor","amount":-335,"competency_date":"2026-08-11"},{"type":"taxa_provento","amount":-420,"competency_date":"2026-08-11"}]}}}},"404":{"description":"Cobrança não encontrada para esta chave.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Erro"}}}}}}},"/v1/charges/{id}/confirm":{"post":{"operationId":"confirmarCobranca","summary":"Confirmar pagamento","tags":["Cobranças"],"security":[{"chaveSecreta":[]}],"description":"Dispara uma tentativa de pagamento — o caminho server-to-server, para quem tem checkout próprio em vez de usar a `checkout_url`. Para cartão, envie o `card_token`: **o número do cartão nunca trafega pela sua aplicação**.","parameters":[{"name":"id","in":"path","required":true,"description":"Id da cobrança.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["method"],"properties":{"method":{"type":"string","enum":["pix","cartao_credito","cartao_debito","boleto"],"description":"Método escolhido."},"card_token":{"type":"string","description":"Token do cartão, obrigatório nos métodos de cartão."},"installments":{"type":"integer","description":"Número de parcelas. Padrão `1`.","example":1}}},"example":{"method":"cartao_credito","card_token":"tok_1a2b3c","installments":3}}}},"responses":{"200":{"description":"Cobrança após a tentativa.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CobrancaDetalhada"}}}},"422":{"description":"Método inválido ou token ausente.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Erro"}}}}}}},"/api/checkout/{id}":{"get":{"operationId":"consultarCheckout","summary":"Dados do checkout","tags":["Checkout"],"security":[],"description":"Público — não exige chave. O id da cobrança já é o segredo que autoriza o acesso. É o que a página de checkout hospedada consome.","parameters":[{"name":"id","in":"path","required":true,"description":"Id da cobrança.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"A cobrança.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CobrancaDetalhada"}}}},"404":{"description":"Cobrança inexistente.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Erro"}}}}}}},"/api/checkout/{id}/confirm":{"post":{"operationId":"pagarCheckout","summary":"Pagar (cliente final)","tags":["Checkout"],"security":[],"description":"Chamado pela página de checkout quando o cliente conclui o pagamento. Ao confirmar, o webhook de saída é despachado em seguida.","parameters":[{"name":"id","in":"path","required":true,"description":"Id da cobrança.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["method"],"properties":{"method":{"type":"string","enum":["pix","cartao_credito","cartao_debito","boleto"]},"card_token":{"type":"string","description":"Token do cartão."},"installments":{"type":"integer","description":"Padrão `1`."}}},"example":{"method":"pix"}}}},"responses":{"200":{"description":"Cobrança após o pagamento.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CobrancaDetalhada"}}}}}}},"/api/checkout/{id}/simulate":{"post":{"operationId":"simularCheckout","summary":"Simular confirmação","tags":["Checkout"],"security":[],"description":"Simula a confirmação assíncrona de Pix e boleto sem esperar o banco — para você testar o fluxo completo em desenvolvimento.","parameters":[{"name":"id","in":"path","required":true,"description":"Id da cobrança.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Cobrança confirmada.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CobrancaDetalhada"}}}}}}},"/v1/webhooks/simulate":{"post":{"operationId":"simularWebhook","summary":"Simular webhook de entrada","tags":["Webhooks"],"security":[{"chaveSecreta":[]}],"description":"Simula o adquirente confirmando um Pix ou boleto. Use para exercitar seu handler de webhook sem depender de um pagamento real.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["charge_id"],"properties":{"charge_id":{"type":"string","format":"uuid","description":"Id da cobrança."}}},"example":{"charge_id":"3f1c9a2e-8b4d-4f77-9a10-2c5e6d8b1f04"}}}},"responses":{"200":{"description":"Cobrança após a confirmação.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CobrancaDetalhada"}}}}}}},"/v1/webhooks/receive-test":{"post":{"operationId":"testarRecebimentoWebhook","summary":"Conferir assinatura","tags":["Webhooks"],"security":[],"description":"Receptor de verificação: recomputa o HMAC do corpo cru e responde se a assinatura confere. Serve para você validar sua própria implementação de verificação antes de apontar o webhook para produção.","parameters":[{"name":"X-Provento-Signature","in":"header","required":true,"description":"Assinatura no formato `t=<unix>,v1=<hmac_sha256_hex>`.","schema":{"type":"string","example":"t=1786452000,v1=5d48f102..."}}],"responses":{"200":{"description":"Assinatura válida.","content":{"application/json":{"schema":{"type":"object","properties":{"received":{"type":"boolean"}}},"example":{"received":true}}}},"400":{"description":"Assinatura inválida ou fora da tolerância de 5 minutos.","content":{"application/json":{"schema":{"type":"object","properties":{"received":{"type":"boolean"},"reason":{"type":"string"}}},"example":{"received":false,"reason":"assinatura inválida"}}}}}}},"/v1/webhooks/dispatch":{"post":{"operationId":"despacharWebhooks","summary":"Despachar fila (interno)","tags":["Webhooks"],"security":[{"segredoCron":[]}],"description":"Worker que drena a fila de saída com backoff. Acionado por Vercel Cron e protegido por `PROVENTO_CRON_SECRET` — não faz parte da API pública.","responses":{"200":{"description":"Resumo do despacho.","content":{"application/json":{"schema":{"type":"object","properties":{"dispatched":{"type":"integer"},"failed":{"type":"integer"}}},"example":{"dispatched":3,"failed":0}}}},"401":{"description":"Segredo ausente ou incorreto.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Erro"}}}}}}}}}