Pharos Docs
Cobranças

Webhooks de Collections

Verifique eventos de checkout e confirme compras apenas após o sucesso de pagamento autorizado.

Webhooks notificam seu backend sobre resultados de links de pagamento e Cart Checkout. Um redirecionamento não comprova pagamento: o navegador pode fechar ou uma forma de pagamento assíncrona terminar mais tarde.

Eventos principais

EventoSignificadoConfirmar compra
checkout_session.completedO fluxo terminou; o pagamento pode continuar em processamentoNão
checkout_session.expiredSessão não pode mais ser usadaNão
invoice.paidA obrigação de pagamento foi paga de forma autoritativaSim, após verificar e deduplicar
credit_note.created, credit_note.updated, credit_note.voidedCiclo de vida de uma nota de créditoNão

invoice.paid é o sinal autoritativo de fulfillment. checkout_session.completed pode chegar enquanto o pagamento ainda está em processamento.

As linhas de fatura vinculadas ao catálogo incluem productId e productPlanPriceId em data.related.invoice.items, para que seu sistema de fulfillment identifique o item comprado. Linhas manuais omitem esses campos.

Configurar e receber

Configure um endpoint nos ajustes de integração e confira sua ativação para a conta e o ambiente escolhidos. Você precisa de acesso à configuração e um receptor no servidor.

  1. O Pharos salva o estado comercial e envia o evento JSON ao endpoint configurado.
  2. Seu servidor lê o corpo original e verifica Pharos-Signature com o segredo do endpoint.
  3. Faça o parse apenas do evento verificado e confira se já processou seu id.
  4. Registre a identidade e execute a ação comercial de forma atômica ou por fluxo idempotente equivalente.
  5. Responda com sucesso apenas após aceitar a entrega. Entregas que falharam podem ser repetidas.

Verificar a assinatura

O Pharos envia Pharos-Signature: t=<unix>,v1=<hmac_sha256>. Calcule HMAC SHA-256 sobre <timestamp>.<raw_body>, rejeite timestamps com mais de cinco minutos e compare em tempo constante.

import { createHmac, timingSafeEqual } from 'node:crypto'

export function verifyPharosSignature(
  rawBody: Buffer,
  signatureHeader: string,
  signingSecret: string,
) {
  const fields = Object.fromEntries(signatureHeader.split(',').map(part => part.split('=', 2)))
  const timestamp = Number(fields.t)
  if (!Number.isSafeInteger(timestamp) || Math.abs(Date.now() / 1000 - timestamp) > 300) return false
  const received = fields.v1 ?? ''
  const expected = createHmac('sha256', signingSecret)
    .update(`${timestamp}.`)
    .update(rawBody)
    .digest('hex')

  const receivedBuffer = Buffer.from(received, 'hex')
  const expectedBuffer = Buffer.from(expected, 'hex')

  return receivedBuffer.length === expectedBuffer.length
    && timingSafeEqual(receivedBuffer, expectedBuffer)
}

Rejeite assinaturas ausentes, malformadas ou inválidas. Não verifique um JSON serializado novamente: espaços e ordem de chaves podem mudar os bytes.

Evitar efeitos duplicados

Trate entregas como pelo menos uma vez, não exatamente uma vez. Salve id com restrição única. Uma entrega repetida deve responder sucesso sem duplicar pedidos, acesso ao serviço, emails ou outras ações.

async function handleVerifiedEvent(event: PharosEvent) {
  if (await events.has(event.id)) return

  await database.transaction(async (tx) => {
    await tx.events.insert({
      eventId: event.id,
    })

    if (event.type === 'invoice.paid') {
      await tx.orders.markFulfilled({ invoiceId: event.data.object.id })
    }
  })
}

O exemplo é ilustrativo: adapte tipos, transações e armazenamento ao seu sistema. A identidade única deve proteger também contra duas entregas concorrentes.

Meios assíncronos

Transferências, boletos, dinheiro ou redirecionamentos podem concluir após o checkout. Uma sessão pode estar complete enquanto o pagamento continua em processamento. Mantenha o pedido pendente até receber invoice.paid; não deduza sucesso pelo tempo decorrido.

Se falhar depois, atualize o pedido pelo evento verificado correspondente. Reutilize a sessão apenas se seu estado permitir outra tentativa.

Histórico e novas tentativas

Endpoints devem usar HTTPS público, sem credenciais na URL. Destinos privados ou inseguros são rejeitados e redirecionamentos não são seguidos. A entrega tem prazo de dez segundos.

A entrega automática admite até oito tentativas em no máximo 24 horas. Novas tentativas mantêm ID e corpo assinado. A configuração de integração leva ao histórico; uma repetição manual afeta aquela entrega e não reenvia o evento a email ou análise.

Notificações comerciais exigem ativação. Reconciliar pagamentos antigos não envia automaticamente notificações históricas. Confira ativação e configuração antes de depender de eventos em produção.

Verificar e resolver problemas

  • Teste sucesso imediato e demorado, falha, expiração e entregas duplicadas no Sandbox.
  • Mantenha o segredo fora do código e use a configuração compatível para rotacionar.
  • Se a assinatura falhar, confira corpo original, header e segredo antes de processar.
  • Se eventos não chegam, confira ativação, histórico e resposta do endpoint.
  • Responda rapidamente e delegue trabalho lento a um fluxo idempotente; alerte sobre falhas repetidas.
  • Registre identidades de evento e correlação sem salvar segredos ou dados sensíveis de pagamento.

Continuar