Referência pública dos endpoints REST de pagamento, integração e carteira pessoal do OrionPay. Base URL: https://payapi.orion.moe
An LLM-friendly index lives at /llms.txt and the full inline spec at /llms-full.txt. The OpenAPI 3.1 spec is at payapi.orion.moe/openapi.json. Product & API release notes live at /changelog.
A Streamable HTTP MCP server with 18 tools is live at payapi.orion.moe/mcp:
claude mcp add orionpay --transport http https://payapi.orion.moe/mcp --header "X-API-Key: $ORIONPAY_API_KEY"Generate an API key at Dashboard > API & Webhooks > MCP / LLM.
Todo endpoint público exige uma API Key enviada no header X-API-Key (ou Authorization: Bearer). Existem dois tipos:
productId = null. Controla a carteira do próprio usuário (saldo, saques, swap). Não gera cobranças de venda.
Endpoints: /api/v1/personal/*.
Vinculada a um produto. Necessária para vender via API: gera cobranças e dispara os webhooks de venda.
Endpoints: /api/v1/pix/generate, /integration/*.
X-API-Key: opay_abc123def456...// Gerar PIX via API
const response = await fetch('https://payapi.orion.moe/api/v1/pix/generate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'opay_sua_chave_aqui'
},
body: JSON.stringify({
amount: 199.90,
name: 'João da Silva',
email: '[email protected]'
})
})
const { data } = await response.json()
console.log(data.purchaseId) // ID interno da compra
console.log(data.pixCode) // PIX copia-e-cola
console.log(data.qrCode) // QR Code (mesmo valor)
console.log(data.expiresAt) // ISO 8601Para vender via API
Cobranças de checkout (/api/v1/pix/generate) e webhooks de venda (purchase.created, payment.success) exigem uma API Key de Produto — crie um produto no dashboard e gere a chave dele. A API Key Pessoal acessa apenas a sua carteira (/api/v1/personal/*) e não gera cobranças. Depósitos na sua própria conta são feitos pelo dashboard, não pela API.
/api/v1/pix/generateAPI Key de ProdutoGera um QR Code PIX vinculado a um produto. Usa o preço do produto ou o valor informado. O CPF/CNPJ do pagador (campo cpf) é obrigatório desde 30/06/2026 — alternativa: euid (identificação do pagador no DePix).
Request body
{
"amount": 199.90,
"description": "Venda de produto",
"name": "João da Silva",
"email": "[email protected]",
"cpf": "52998224725",
"phone": "+5511999999999"
}Response (200)
{
"success": true,
"data": {
"purchaseId": 12345,
"productId": 8,
"productTitle": "Exemplo Produto",
"id": "550e8400-e29b-41d4-a716-446655440000",
"pixCode": "00020126580014br.gov.bcb.pix...",
"qrCode": "00020126580014br.gov.bcb.pix...",
"amount": 199.90,
"description": "Venda de produto: Exemplo Produto",
"expiresAt": "2026-04-24T14:30:00.000Z",
"status": "PENDING",
"coProducers": [
{ "userId": 5, "name": "Co-produtor Name", "percentage": 15.50 }
]
}
}/api/v1/pix/status/:idQualquer API Key60 req/minAceita purchaseId (numérico), transactionId (numérico) ou eulenDepositId (UUID).
Response (200)
{
"success": true,
"type": "product_purchase",
"id": 12345,
"eulenDepositId": "550e8400-e29b-41d4-a716-446655440000",
"status": "PAID",
"paidAt": "2026-04-24T12:00:00.000Z",
"accessToken": "abcd1234efgh5678ijkl9012mnop3456",
"productId": 8,
"productTitle": "Exemplo Produto",
"amount": 199.90,
"buyerEmail": "[email protected]",
"buyerName": "João Silva",
"createdAt": "2026-04-24T10:00:00.000Z"
}/integration/verify-accessAPI Key de Produto60 req/minQuery: email ou accessToken (ao menos um obrigatório).
Response (200)
{
"hasAccess": true,
"purchaseId": 12345,
"accessToken": "abcd1234efgh5678ijkl9012mnop3456",
"buyerEmail": "[email protected]",
"buyerName": "João Silva",
"productTitle": "Exemplo Produto",
"purchaseDate": "2026-04-20T10:00:00.000Z",
"expiresAt": "2026-05-20T10:00:00.000Z"
}/integration/grant-accessAPI Key de ProdutoCria uma compra já paga para o email informado. Útil para cortesias e suporte.
Request body
{
"buyerEmail": "[email protected]",
"buyerName": "Maria Santos",
"price": 199.90,
"expiresAt": "2026-05-24T00:00:00.000Z"
}Response (200)
{
"success": true,
"message": "Acesso concedido com sucesso",
"purchaseId": 12347,
"accessToken": "abcd1234efgh5678ijkl9012mnop3456",
"buyerEmail": "[email protected]",
"buyerName": "Maria Santos",
"expiresAt": "2026-05-24T00:00:00.000Z"
}/integration/buyersAPI Key de Produto60 req/minQuery opcional: status (PENDING, PAID, EXPIRED).
Response (200)
{
"total": 2,
"buyers": [
{
"purchaseId": 12345,
"buyerEmail": "[email protected]",
"buyerName": "João Silva",
"price": 199.90,
"paymentStatus": "PAID",
"accessToken": "abcd1234efgh5678ijkl9012mnop3456",
"purchaseDate": "2026-04-20T10:00:00.000Z",
"paidAt": "2026-04-20T10:05:00.000Z",
"expiresAt": "2026-05-20T10:00:00.000Z",
"paymentLink": "Link de Pagamento Exemplo"
}
]
}/integration/buyers/:purchaseIdAPI Key de ProdutoRetorna dados completos de uma compra pelo ID.
Response (200)
{
"purchaseId": 12345,
"buyerEmail": "[email protected]",
"buyerName": "João Silva",
"price": 199.90,
"paymentStatus": "PAID",
"accessToken": "abcd1234efgh5678ijkl9012mnop3456",
"purchaseDate": "2026-04-20T10:00:00.000Z",
"paidAt": "2026-04-20T10:05:00.000Z",
"expiresAt": "2026-05-20T10:00:00.000Z",
"product": { "id": 8, "title": "Exemplo Produto", "productType": "COURSE" },
"paymentLink": { "id": 1, "title": "Link de Pagamento Exemplo" }
}/integration/contentAPI Key de Produto60 req/minQuery: accessToken (obrigatório). Valida o token e retorna os conteúdos liberados.
Response (200)
{
"productId": 8,
"buyerEmail": "[email protected]",
"buyerName": "João Silva",
"contents": [
{
"id": 1,
"type": "video",
"title": "Aula 1 - Introdução",
"url": "https://storage.example.com/video1.mp4",
"fileSize": 524288000,
"mimeType": "video/mp4",
"order": 1,
"metadata": "{\"duration\": 3600}"
}
]
}/integration/revoke-access/:purchaseIdAPI Key de ProdutoInvalida o accessToken da compra informada.
Response (200)
{
"success": true,
"message": "Acesso revogado com sucesso",
"purchaseId": 12345
}/api/v1/integration/deposit/pixAPI Key de Produto10 req/minCria QR Code PIX para o produto. O CPF/CNPJ do pagador (campo cpf, apenas dígitos) é obrigatório desde 30/06/2026 — alternativa: euid (identificação do pagador no DePix). A identificação do lojista (merchantId) é automática (resolvida pela sua API key); não precisa ser enviada.
Request body
{
"cpf": "52998224725",
"amount": 199.90,
"buyerEmail": "[email protected]",
"buyerName": "João da Silva"
}Response (200)
{
"purchaseId": 12348,
"productId": 8,
"productTitle": "Exemplo Produto",
"id": "550e8400-e29b-41d4-a716-446655440000",
"pixCode": "00020126580014br.gov.bcb.pix...",
"qrCode": "00020126580014br.gov.bcb.pix...",
"amount": 199.90,
"expiresAt": "2026-04-24T14:30:00.000Z",
"status": "created"
}/api/v1/personal/balanceAPI Key Pessoal60 req/hRetorna saldo por moeda, incluindo bloqueado e pendente.
Response (200)
{
"success": true,
"wallets": [
{
"currency": "BRL",
"balance": 5000.00,
"blockedBalance": 500.00,
"pendingBalance": 0.00,
"availableBalance": 4500.00
},
{
"currency": "L-BTC",
"balance": 0.02,
"blockedBalance": 0.00,
"pendingBalance": 0.00,
"availableBalance": 0.02
}
]
}/api/v1/personal/limitsAPI Key Pessoal30 req/hRetorna limites diários/mensais e o consumo atual.
Response (200)
{
"success": true,
"limits": {
"dailyWithdrawBRL": 10000.00,
"monthlyWithdrawBRL": 100000.00,
"dailyWithdrawCrypto": 2.00,
"monthlyWithdrawCrypto": 10.00,
"dailyUsed": 2000.00,
"monthlyUsed": 15000.00
}
}/api/v1/personal/transactionsAPI Key Pessoal60 req/hQuery: limit, offset, type (DEPOSIT, WITHDRAWAL, SWAP).
Response (200)
{
"success": true,
"transactions": [
{
"id": 789,
"type": "DEPOSIT",
"status": "COMPLETED",
"amount": 500.00,
"description": "Depósito PIX",
"blockchainTxId": null,
"createdAt": "2026-04-24T10:00:00.000Z"
}
],
"pagination": { "total": 50, "limit": 10, "offset": 0, "hasMore": true }
}/api/v1/personal/withdraw/quoteAPI Key Pessoal120 req/hSimula um saque com a mesma matemática do débito real. Envie exatamente um: ?amount= (bruto a debitar) OU ?netAmount= (líquido desejado na conta destino). grossAmount − totalFee = netAmount fecha exato.
Response (200)
{
"success": true,
"quote": { "grossAmount": 78.31, "totalFee": 3.31, "netAmount": 75.00, "requestedNetAmount": 75.00 },
"limits": { "minAmount": 4.00, "maxAmount": 50000.00 },
"validation": { "withinLimits": true }
}/api/v1/personal/withdraw/pixAPI Key Pessoal10 req/hEnvie exatamente um: amount (1–100.000, bruto a debitar) OU netAmount (líquido que deve chegar na conta — o débito é calculado automaticamente). fee é a taxa TOTAL e amount − fee = netAmount. pixKeyType é detectado automaticamente (EMAIL, CPF, CNPJ, PHONE, RANDOM).
Request body
{
"netAmount": 75.00,
"pixKey": "12345678000123",
"pixKeyType": "CPF",
"description": "Saque via API",
"taxNumber": "52998224725"
}Response (200)
{
"success": true,
"message": "Saque PIX em processamento",
"queueId": 88,
"transactionId": 791,
"status": "PENDING",
"requiresApproval": false,
"amount": 78.31,
"fee": 3.31,
"total": 78.31,
"netAmount": 75.00,
"requestedNetAmount": 75.00,
"estimatedProcessingTime": "1-2 minutos"
}/api/v1/personal/withdraw/cryptoAPI Key Pessoal10 req/hSuporta L-BTC, BTC-PEGOUT, DePix, USDt, USDC. memo obrigatório para TON.
Request body
{
"amount": 0.01,
"toAddress": "bc1q6y3kz2c8s4n9p1m0l2k3j4h5g6f7e8d9c0b1a",
"assetTicker": "BTC-PEGOUT",
"network": "bitcoin"
}Response (200)
{
"success": true,
"transactionId": 792,
"status": "PENDING",
"amount": 0.01,
"assetTicker": "BTC-PEGOUT",
"toAddress": "bc1q6y3kz2c8s4n9p1m0l2k3j4h5g6f7e8d9c0b1a",
"blockchainTxId": null,
"createdAt": "2026-04-24T14:00:00.000Z"
}/api/v1/personal/withdraw/status/:idAPI Key Pessoal120 req/hRetorna estado atual, taxa total e valor líquido. netAmount vira o payout confirmado pelo provedor assim que disponível (payoutConfirmed: true). Para saques cripto, toAddress/asset são preenchidos no lugar de pixKey.
Response (200)
{
"success": true,
"transaction": {
"id": 791,
"status": "COMPLETED",
"amount": 78.31,
"fee": 3.31,
"netAmount": 75.00,
"payoutConfirmed": true,
"requestedNetAmount": 75.00,
"pixKey": "12345678000123",
"description": "P-API: Saque PIX",
"blockchainTxId": null,
"errorCode": null,
"errorMessage": null,
"createdAt": "2026-04-24T14:00:00.000Z",
"updatedAt": "2026-04-24T14:05:00.000Z"
}
}/api/v1/personal/swap/quoteAPI Key Pessoal120 req/hMoedas suportadas: DePix, L-BTC, USDt, EURx. Cotação válida por 5 minutos.
Request body
{
"fromCurrency": "BRL",
"toCurrency": "BTC",
"amount": 1000.00
}Response (200)
{
"success": true,
"fromCurrency": "BRL",
"toCurrency": "BTC",
"fromAmount": 1000.00,
"toAmount": 0.0195,
"rate": 0.0000195,
"fee": 5.00,
"feeCurrency": "BRL",
"expiresAt": "2026-04-24T14:15:00.000Z"
}/api/v1/personal/swap/executeAPI Key Pessoal20 req/hDebita fromCurrency e credita toCurrency ao preço vigente no momento da execução.
Request body
{
"fromCurrency": "BRL",
"toCurrency": "BTC",
"amount": 1000.00
}Response (200)
{
"success": true,
"transactionId": 793,
"fromCurrency": "BRL",
"toCurrency": "BTC",
"fromAmount": 1000.00,
"toAmount": 0.0195,
"status": "COMPLETED",
"executedAt": "2026-04-24T14:05:00.000Z"
}/payment-links/purchase/:purchaseId/statusPúblico (sem auth)60 req/minÚnico endpoint sem auth. Aceita ID numérico (compra) ou UUID (eulenDepositId). Pensado para polling no checkout.
Response (200)
{
"purchaseId": 12345,
"paymentStatus": "PAID",
"paidAt": "2026-04-24T12:00:00.000Z",
"accessToken": "abcd1234efgh5678ijkl9012mnop3456",
"productTitle": "Exemplo Produto",
"price": 199.90
}Ao exceder o limite, o backend responde com HTTP 429. Os limites são por API Key + rota e resetam na janela indicada.
Valores possíveis do campo status no recurso Transaction (GET /transactions/:id)
PIX gerado, aguardando pagamento. Saldo ainda não foi movimentado.
Eulen processando. Pode estar em under_review (anti-fraude), delayed (Eulen segurando o DePix antes do approved — sem efeito financeiro ainda) ou em delay-depix HOLD após o approved (PIX confirmado e blockedBalance já incrementado; sub-fase identificada por metadata.holdSource = "delay-depix" + metadata.approvedAt + availableAt). Webhook desta sub-fase: deposit.approved_delay.
DePix entregue na reserva. Saldo creditado em balance (ou em pendingBalance se isOnHold=true). Webhook emitido: deposit.success ou deposit.on_hold.
PIX falhou (não pago, expirado, recusado pela Eulen ou banco). Nenhum saldo movimentado.
Transação cancelada manualmente (admin) ou por TTL (PIX não pago dentro da janela).
Estorno: MED (PIX chargeback) detectado durante carência ou estorno manual feito por admin. Saldo retirado de blockedBalance ou pendingBalance conforme metadata.holdSource.
Transação em análise manual pelo time. Aguardando aprovação ou rejeição.
Receba notificacoes automaticas quando eventos importantes acontecem
Todo webhook traz assinatura HMAC-SHA256 no header X-Webhook-Signature, calculada sobre o corpo bruto (raw body) com o segredo do webhook. Valide sempre antes de processar.
X-Webhook-Signature: a1b2c3d4e5f6...payment.successPagamento confirmado e creditado.
{
"event": "payment.success",
"productId": 8,
"productTitle": "Exemplo Produto",
"webhookName": "notificar_sistema",
"timestamp": "2026-04-24T12:00:00.000Z",
"data": {
"purchaseId": 12345,
"transactionId": "txn_abc123",
"productId": 8,
"buyerEmail": "[email protected]",
"buyerName": "João Silva",
"accessToken": "abcd1234efgh5678ijkl9012mnop3456",
"price": 199.90,
"netAmount": 179.91,
"platformFee": 19.99,
"timestamp": "2026-04-24T12:00:00.000Z"
}
}purchase.createdCompra criada, aguardando pagamento.
{
"event": "purchase.created",
"productId": 8,
"timestamp": "2026-04-24T10:00:00.000Z",
"data": {
"purchaseId": 12345,
"transactionId": "txn_abc123",
"amount": 199.90,
"status": "pending",
"createdAt": "2026-04-24T10:00:00.000Z",
"buyerEmail": "[email protected]",
"buyerName": "João Silva"
}
}access.grantedAcesso liberado ao comprador após confirmação de pagamento.
{
"event": "access.granted",
"productId": 8,
"timestamp": "2026-04-24T12:00:05.000Z",
"data": {
"purchaseId": 12345,
"buyerEmail": "[email protected]",
"accessToken": "abcd1234efgh5678ijkl9012mnop3456",
"expiresAt": "2026-05-24T12:00:05.000Z",
"grantedAt": "2026-04-24T12:00:05.000Z"
}
}payment.refunded_medPagamento reembolsado por MED (chargeback) durante o hold antifraude.
{
"event": "payment.refunded_med",
"productId": 8,
"timestamp": "2026-04-24T15:00:00.000Z",
"data": {
"transactionId": 789,
"amount": 199.90,
"reference": "550e8400-e29b-41d4-a716-446655440000",
"status": "refunded",
"type": "DEPOSIT",
"currency": "BRL",
"reason": "med_during_antifraud_hold",
"medDetectedAt": "2026-04-24T14:50:00.000Z",
"refundedAt": "2026-04-24T15:00:00.000Z"
}
}payment.refundedDepósito reembolsado pela Eulen ao pagador (PIX devolvido). O valor retido é estornado (estorno MED de valor já creditado sai como payment.refunded_med). O campo opcional refundReasonDetail traz o motivo inferido da devolução (med_chargeback — pagador abriu MED e o valor foi devolvido durante a carência —, cpf_mismatch, cnpj_mismatch, spam_like, user_blocked, payer_refused, indeterminate_payer — entre 28/07/2026 e 01/08/2026 o provedor não revelou a conta pagadora nas devoluções: motivo indeterminado, sem nenhum campo de pagador; desde 01/08/2026 a identidade real do pagador voltou e as devoluções classificam normalmente; valores legados em dados antigos: no_typed_cpf, cpf_exempt, no_payer_data, payer_company). Desde 06/08/2026 os motivos mismatch não ocorrem para totais estritamente abaixo de R$ 10,00: um CPF digitado nessa faixa é validado e registrado, mas NÃO é repassado ao provedor — o QR não fica travado no documento e o pagamento de qualquer conta completa normalmente (devoluções nessa faixa classificam pelo resto da cascata). Em todos os motivos exceto indeterminate_payer o payload inclui payerEuid — id pseudônimo e estável da conta que pagou (ex.: EU015562310201680), útil para correlacionar/bloquear pagadores reincidentes sem expor identidade. Apenas nos motivos mismatch o payload inclui também payerName e payerTaxNumber da conta que pagou, para conferência com o documento digitado no checkout — CPF sempre mascarado (***.999.999-**, o CPF completo nunca é enviado); CNPJ de conta PJ/PSP vai completo. Nos demais motivos vão só o enum e o payerEuid, sem identidade do pagador.
{
"event": "payment.refunded",
"productId": 8,
"timestamp": "2026-04-24T15:00:00.000Z",
"data": {
"transactionId": 789,
"amount": 199.90,
"reference": "550e8400-e29b-41d4-a716-446655440000",
"status": "refunded",
"type": "DEPOSIT",
"currency": "BRL",
"reason": "eulen_refund",
"refundReasonDetail": "cpf_mismatch",
"payerEuid": "EU015562310201680",
"payerName": "João da Silva",
"payerTaxNumber": "***.456.789-**",
"eulenStatus": "refunded",
"refundedAt": "2026-04-24T15:00:00.000Z",
"credited": false
}
}payment.refund_pendingAviso PRÉVIO: a Eulen anunciou que vai devolver um PIX que já recebeu (risco/compliance). Ainda não é o terminal — o payment.refunded vem depois (em média ~2h; até 3 dias quando o provedor retém o valor antes, informado em expectedRefundAt). Na prática é irreversível: dos 187 avisos recebidos, 182 terminaram em devolução e nenhum chegou a liquidar. O campo opcional rejectionReasons traz o código cru do provedor (PAYER_MISMATCH — documento da conta pagadora diferente do travado no QR —, BLOCKED_USER; o vocabulário é dele e pode crescer, trate código desconhecido como recusa genérica de risco). Se o comprador já tinha recebido acesso (vendas com delay DePix liberam acesso na confirmação do PIX, antes da liquidação), a OrionPay revoga e envia accessRevoked: true — reverta a entrega do seu lado também.
{
"event": "payment.refund_pending",
"productId": 8,
"timestamp": "2026-04-24T15:00:00.000Z",
"data": {
"transactionId": 789,
"amount": 199.90,
"reference": "550e8400-e29b-41d4-a716-446655440000",
"status": "refund_pending",
"type": "DEPOSIT",
"currency": "BRL",
"reason": "eulen_will_refund",
"rejectionReasons": ["PAYER_MISMATCH"],
"expectedRefundAt": "2026-04-27T15:00:00.000Z",
"payerEuid": "EU015562310201680",
"eulenStatus": "will_refund",
"credited": false,
"accessRevoked": true,
"purchaseId": 12345,
"productId": 8,
"productTitle": "Exemplo Produto",
"buyerEmail": "[email protected]",
"buyerName": "Cliente Exemplo"
}
}med.createdMED (chargeback) detectado em depósito Eulen.
{
"event": "med.created",
"timestamp": "2026-04-24T14:50:00.000Z",
"data": {
"qrId": "550e8400-e29b-41d4-a716-446655440000",
"bankTxId": "E00000000202604241450",
"amount": 199.90,
"currency": "BRL",
"medDetectedAt": "2026-04-24T14:50:00.000Z",
"depositStatus": "depix_sent",
"payerTaxNumber": "52998224725",
"transactionId": 789,
"purchaseId": 12345,
"productId": 8,
"productTitle": "Exemplo Produto"
}
}withdrawal.completedSaque transmitido à blockchain ou ao banco.
{
"event": "withdrawal.completed",
"timestamp": "2026-04-24T14:05:00.000Z",
"data": {
"withdrawalId": 792,
"amount": 0.01,
"fee": 0.00001,
"totalDeducted": 0.01001,
"asset": "L-BTC",
"toAddress": "lq1qqw...",
"txHash": "abc123...",
"network": "liquid",
"isSideShift": false,
"completedAt": "2026-04-24T14:05:00.000Z"
}
}withdrawal.settledSaque totalmente confirmado na rede de destino (inclui Peg-Out BTC).
{
"event": "withdrawal.settled",
"timestamp": "2026-04-24T14:30:00.000Z",
"data": {
"withdrawalId": 792,
"amount": 0.01,
"asset": "BTC-PEGOUT",
"toAddress": "bc1q...",
"liquidTxHash": "abc123...",
"settleTxHash": "def456...",
"settleNetwork": "bitcoin",
"completedAt": "2026-04-24T14:30:00.000Z"
}
}withdrawal.failedSaque falhou (saldo insuficiente, endereço inválido, etc). Valores já refundidos ao saldo.
{
"event": "withdrawal.failed",
"timestamp": "2026-04-24T14:05:00.000Z",
"data": {
"withdrawalId": 792,
"amount": 0.01,
"asset": "L-BTC",
"toAddress": "lq1qqw...",
"network": "liquid",
"error": "Insufficient hotwallet balance",
"failedAt": "2026-04-24T14:05:00.000Z"
}
}swap.completedSwap atômico executado com sucesso.
{
"event": "swap.completed",
"timestamp": "2026-04-24T14:05:00.000Z",
"data": {
"txid": "abc123...",
"fromCurrency": "L-BTC",
"toCurrency": "DePix",
"fromAmount": 0.01,
"toAmountGross": 3500.00,
"toAmountNet": 3482.50,
"feePercent": 0.5,
"feeAmount": 17.50,
"completedAt": "2026-04-24T14:05:00.000Z"
}
}swap.failedSwap falhou ou foi reembolsado.
{
"event": "swap.failed",
"timestamp": "2026-04-24T14:05:00.000Z",
"data": {
"transactionId": 793,
"fromCurrency": "L-BTC",
"toCurrency": "DePix",
"fromAmount": 0.01,
"step": "REFUNDED",
"reason": "Manual refund by admin",
"refundedAt": "2026-04-24T14:05:00.000Z"
}
}webhook.testEvento disparado manualmente pela dashboard para testar a URL.
{
"event": "webhook.test",
"timestamp": "2026-04-24T14:05:00.000Z",
"data": {
"message": "Webhook de teste",
"testId": "test_abc123"
}
}// Validar assinatura de webhook (Node.js)
const crypto = require('crypto')
function validateWebhook(rawBody, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex')
return crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expected, 'hex')
)
}
app.post('/webhooks/orionpay', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-webhook-signature']
const rawBody = req.body.toString('utf8')
if (!validateWebhook(rawBody, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).json({ error: 'Invalid signature' })
}
const { event, data } = JSON.parse(rawBody)
switch (event) {
case 'payment.success':
grantAccess(data.buyerEmail, data.accessToken)
break
case 'payment.refunded_med':
case 'payment.refunded':
case 'payment.refund_pending':
revokeAccess(data.transactionId)
break
case 'withdrawal.settled':
markWithdrawalSettled(data.withdrawalId)
break
}
res.status(200).json({ received: true })
})