Pular para o conteúdo

Webhooks

Webhooks permitem que o OrcaPay notifique sua aplicação quando algo acontece — por exemplo, quando um pagamento é confirmado. Em vez de consultar a API repetidamente, você recebe um POST na URL que cadastrar.

Cadastre seus endpoints no console do OrcaPay, na seção de Webhooks. Para cada endpoint você define um nome, a URL (HTTPS, acessível publicamente) e os eventos que deseja receber. Ao criar o endpoint é gerado um secret, usado para assinar as notificações.

O secret é exibido uma única vez, na confirmação do cadastro. Copie e guarde em local seguro — ele não pode ser consultado depois. Se você perder o secret, exclua o endpoint e cadastre um novo.

EventoDisparado quando
PAYMENT_CREATEDUm pagamento é criado
PAYMENT_CONFIRMEDUm pagamento é confirmado
PAYMENT_FAILEDUm pagamento não é aprovado
PAYMENT_CANCELLEDUm pagamento é cancelado
PAYMENT_REVERSEDO pagamento é estornado integralmente (estorno total ou último estorno parcial)
PAYMENT_PARTIALLY_REVERSEDUm estorno parcial é concluído e o pagamento ainda tem saldo
CHARGEBACK_OPENEDUm chargeback é aberto
CHARGEBACK_UPDATEDO status de um chargeback muda
CHARGEBACK_RESOLVEDUm chargeback é resolvido

Endpoints cadastrados antes do estorno parcial não recebem PAYMENT_PARTIALLY_REVERSED automaticamente: edite o endpoint no console e marque o evento. Os campos extras dos eventos de estorno estão em Estornos.

Cada notificação é um POST com corpo JSON e três headers de identificação:

POST https://sua-aplicacao.com.br/webhooks/orcapay
Content-Type: application/json
X-Orcapay-Signature: sha256=K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols=
X-Orcapay-Event: PAYMENT_CONFIRMED
X-Orcapay-Event-Id: 1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d

O corpo é um envelope com os dados do evento em data. Exemplo de PAYMENT_CONFIRMED de um pagamento com cartão:

{
"eventId": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"eventType": "PAYMENT_CONFIRMED",
"establishmentId": "e1f2a3b4-c5d6-7e8f-9a0b-1c2d3e4f5a6b",
"timestamp": "2026-07-18T14:35:04.402942623Z",
"data": {
"paymentId": "9f8a7b6c-5d4e-4a3b-2c1d-0e9f8a7b6c5d",
"establishmentId": "e1f2a3b4-c5d6-7e8f-9a0b-1c2d3e4f5a6b",
"establishmentName": "Minha Loja",
"receiverId": "c3d4e5f6-a7b8-4c9d-8e0f-1a2b3c4d5e6f",
"externalId": "pedido-1042",
"referenceId": "CARTAO-4d2a9f1e-8b3c-4a7d-9e6f-1c0b2a3d4e5f",
"status": "CONFIRMED",
"paymentMethod": "CREDIT_CARD",
"installments": 3,
"amount": 159.9,
"originalAmount": 159.9,
"interestAmount": 0.0,
"netAmount": 155.44,
"refundedAmount": 0,
"refundableAmount": 159.9,
"description": "Pedido #1042",
"customerName": "Maria Silva",
"customerEmail": "maria@exemplo.com.br",
"cardBrand": "visa",
"cardLastFour": "1111",
"paidAt": "2026-07-18T14:35:02.184529Z",
"expectedSettlementDate": "2026-08-17"
}
}
CampoTipoDescrição
eventIdstring (UUID)Identificador único do evento, o mesmo do header X-Orcapay-Event-Id
eventTypestringTipo do evento, o mesmo do header X-Orcapay-Event
establishmentIdstring (UUID)Estabelecimento a que o evento pertence
timestampstringMomento em que a notificação foi gerada, em ISO 8601 (UTC). Reentregas mantêm o mesmo valor
dataobjetoRecurso no momento do evento. Os campos dependem do tipo de evento, veja abaixo
  • Valores monetários são números em reais, sem quantidade fixa de casas decimais (159.9, 0).
  • Campos sem valor chegam como null.
  • Dentro de data, datas e horários usam o mesmo formato ISO 8601 das respostas da API. Instantes (paidAt, reversedAt, openedAt, disputeDeadline, resolvedAt) vêm como string em UTC (2026-07-18T14:35:02.184529Z). Datas sem hora (expectedSettlementDate) vêm como string AAAA-MM-DD (2026-08-17).
  • Até setembro de 2026 os instantes chegavam como número de segundos desde 1970-01-01 e expectedSettlementDate como array [ano, mês, dia]. Se a sua integração multiplica paidAt por 1000 ou desestrutura esse array, passe a ler as strings ISO 8601.
  • A ordem das chaves não é fixa e novos campos podem ser incluídos. Leia os campos pelo nome e ignore os que não conhecer.

No cartão enviado com allowPending: true, o pagamento em análise antifraude chega primeiro como PAYMENT_CREATED com "status": "PENDING" e é fechado depois por PAYMENT_CONFIRMED, PAYMENT_FAILED ou PAYMENT_CANCELLED. Veja Cartão em análise antifraude.

PAYMENT_CREATED, PAYMENT_CONFIRMED, PAYMENT_FAILED e PAYMENT_CANCELLED trazem em data os campos abaixo. PAYMENT_REVERSED e PAYMENT_PARTIALLY_REVERSED trazem os mesmos campos e mais os dados do estorno, descritos em Estornos.

CampoTipoDescrição
paymentIdstring (UUID)Identificador do pagamento no OrcaPay. É o id retornado na criação e usado em GET /payments/{id}
establishmentIdstring (UUID)Estabelecimento dono do pagamento
establishmentNamestringNome fantasia do estabelecimento. Omitido quando indisponível
receiverIdstring (UUID)Recebedor do pagamento
externalIdstringIdentificador enviado por você na criação do pagamento
referenceIdstringCódigo de referência da cobrança na operadora (PIX-... ou CARTAO-...), o mesmo da coluna “Código de Referência” no relatório do PagBank. Veja Identificadores do pagamento
statusstringStatus do pagamento após o evento, veja Status do pagamento
paymentMethodstringPIX, CREDIT_CARD ou DEBIT_CARD
installmentsinteiroQuantidade de parcelas (1 no Pix e no débito)
amountnúmeroValor cobrado do cliente, incluindo juros de parcelamento repassados
originalAmountnúmeroValor da venda antes dos juros repassados
interestAmountnúmeroJuros de parcelamento pagos pelo cliente (amount = originalAmount + interestAmount)
netAmountnúmeroValor líquido após as taxas
refundedAmountnúmeroTotal já estornado
refundableAmountnúmeroSaldo que ainda pode ser estornado
descriptionstringDescrição do pagamento
customerNamestringNome do cliente
customerEmailstringE-mail do cliente
cardBrandstringBandeira do cartão como informada pela operadora (ex.: visa). null no Pix
cardLastFourstringÚltimos quatro dígitos do cartão. null no Pix
paidAtstringMomento da confirmação, em ISO 8601 (UTC). null antes da confirmação
expectedSettlementDatestringData prevista de liquidação, no formato AAAA-MM-DD

CHARGEBACK_OPENED, CHARGEBACK_UPDATED e CHARGEBACK_RESOLVED trazem em data o chargeback, e não o pagamento:

CampoTipoDescrição
chargebackIdstring (UUID)Identificador do chargeback no OrcaPay
paymentIdstring (UUID)Pagamento contestado. null enquanto o chargeback não estiver vinculado a um pagamento
establishmentIdstring (UUID)Estabelecimento dono do pagamento
providerstringOperadora que notificou o chargeback (ex.: PAGBANK)
providerChargebackIdstringIdentificador do chargeback na operadora
amountnúmeroValor contestado
currencystringMoeda (BRL)
statusstringRECEIVED, AWAITING_EVIDENCE, EVIDENCE_SUBMITTED, UNDER_GATEWAY_REVIEW, SUBMITTED_TO_ISSUER, ACCEPTED_BY_MERCHANT, EXPIRED, WON ou LOST
statusDescriptionstringDescrição do status em português
classificationstringNOT_RECOGNIZED, DISAGREEMENT, FRAUD_REPORT ou OTHER
reasonCodestringCódigo do motivo da contestação
cyclestringCiclo da disputa informado pela operadora
openedAtstringAbertura, em ISO 8601 (UTC)
disputeDeadlinestringPrazo para enviar a defesa, em ISO 8601 (UTC)
resolvedAtstringResolução, em ISO 8601 (UTC). null enquanto não resolvido
matchStatusstringVínculo com o pagamento: MATCHED, UNMATCHED ou MANUAL

Toda notificação é assinada com o secret do endpoint, exibido uma única vez no cadastro. O header X-Orcapay-Signature contém sha256= seguido do HMAC-SHA256 do corpo bruto da requisição, calculado com o secret e codificado em Base64. Recalcule a assinatura do seu lado e compare com o header antes de processar:

const crypto = require('crypto');
function isValidSignature(rawBody, signatureHeader, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(rawBody, 'utf8')
.digest('base64');
const received = Buffer.from(signatureHeader ?? '');
const expectedBuffer = Buffer.from(expected);
return (
received.length === expectedBuffer.length &&
crypto.timingSafeEqual(received, expectedBuffer)
);
}

Dois cuidados:

  • Use o corpo exatamente como recebido. Se o framework desserializar o JSON e você serializar de novo, a assinatura não vai bater — em Express, por exemplo, capture o corpo bruto com express.raw({ type: 'application/json' }) na rota do webhook.
  • Compare em tempo constante (como crypto.timingSafeEqual acima) em vez de ===, para não vazar informação por tempo de resposta.

Rejeite qualquer notificação com assinatura inválida — ela não veio do OrcaPay.

  • Uma entrega é considerada bem-sucedida quando seu endpoint responde 2xx em até 10 segundos.
  • Em caso de falha, o OrcaPay tenta novamente até 5 vezes, com intervalos de 1 minuto, 5 minutos, 15 minutos, 1 hora e 24 horas.
  • Após esgotar as tentativas, a entrega é marcada como esgotada e não é reenviada.
  • Responda 2xx o mais rápido possível e processe o evento de forma assíncrona.
  • Trate eventos de forma idempotente usando o eventId: o mesmo evento pode ser entregue mais de uma vez.
  • Não confie apenas no webhook para decisões críticas. Confirme o status com GET /payments/{id}, usando o paymentId recebido, quando necessário.