Skip to main content
A ingestão é a porta de entrada de fatos financeiros (uma assinatura criada, uma cobrança recorrente, um estorno, um cancelamento) vindos de provedores de pagamento para dentro do Repass. Cada fato é traduzido para um evento normalizado, único e agnóstico de gateway, e encaminhado para o processamento de Conversão e Comissão. Por isso, atribuição, deduplicação e cálculo de comissão são herdados integralmente desses fluxos: a ingestão não grava conversões nem comissões por conta própria. Existem duas superfícies de entrada:
  • POST /ingest/stripe/{organizationId}: webhook público do Stripe, autenticado pela assinatura HMAC do próprio Stripe (não pela sua API key).
  • POST /ingest/custom: endpoint server-to-server (S2S), autenticado pela sua API key, para enviar o evento normalizado diretamente quando você não usa um gateway suportado.
E uma superfície de configuração: GET / PUT /settings/stripe, para gravar e ler (mascarado) o segredo de assinatura do webhook do Stripe da sua conta.
A ingestão é a forma recomendada de registrar cobranças recorrentes. Para entender o que acontece depois (atribuição ao Afiliado, criação da Comissão e tratamento de fraude), veja Conversões e fraude.

O evento normalizado

Todo gateway é apenas um tradutor (payload do provedor → evento normalizado) mais uma verificação de assinatura. O Repass conhece um único contrato, uma união discriminada por kind: Os eventos charge, refund e cancellation não trazem dados de atribuição: apontam para uma Conversão existente por conversionId (ULID com prefixo conv_) ou por customerId (o id do cliente no gateway). Pelo menos um dos dois é obrigatório.
Dinheiro sempre em centavos (inteiro), nunca em float. Apenas BRL é suportado: eventos em outra moeda são ignorados na tradução.

Deduplicação por sourceEventId

A idempotência da ingestão é de negócio e permanente, indexada pelo sourceEventId de cada evento. As rotas de ingestão não usam o header Idempotency-Key: você não precisa enviá-lo. Em vez disso, todo evento carrega um sourceEventId estável, e o processamento de Conversão/Comissão garante que o mesmo sourceEventId produza um único efeito. Isso significa que reentregas do gateway (o Stripe reenvia o mesmo webhook várias vezes, por exemplo) são absorvidas sem duplicar conversões ou comissões: o desfecho é simplesmente replayed, apontando para o efeito original.
O sourceEventId deve identificar o fato financeiro, não a entrega do webhook. No tradutor do Stripe ele é o id do objeto financeiro, a invoice (in_...), o refund (re_...) ou <sub.id>:deleted, justamente para que reentregas do mesmo fato sejam dedupadas e para que um refund referencie a cobrança original.

Desfechos da ingestão

A ingestão não tem máquina de estados própria; ela apenas encaminha o evento. Cada chamada produz exatamente um desfecho terminal (outcome), que descreve como o evento foi tratado:
O evento gerou efeito novo: Conversão criada e atribuída, cobrança registrada com Comissão nova, refund/clawback aplicado, ou cancelamento que anulou comissões. Um cancelamento que não tinha nada a anular continua processed, com reason: "nothing_to_void" (idempotente).
O mesmo sourceEventId já havia sido processado; nenhum efeito novo. É a reentrega idempotente.
Uma Conversão não foi atribuída a nenhum Afiliado (no_attribution), ou um charge/refund/cancellation por customerId não achou Conversão ativa (customer_not_attributed). Nada é persistido.
Uma regra de negócio impede o efeito de forma legítima: cliente com mais de uma Conversão ativa (ambiguous_customer), chargeback contra Conversão sem comissões (no_commissions), ou um motivo herdado da cobrança (no_rule: nenhuma regra de comissão aplicável; no_eligible_affiliate: nenhum afiliado elegível; recurrence_exhausted: recorrência da regra já esgotada).
O tradutor decidiu não mapear o evento (tipo não tratado, moeda diferente de BRL, cliente ausente). O reason carrega o motivo. Só ocorre no webhook: o /ingest/custom não aceita esse kind.

Webhook do Stripe

O webhook do Stripe é autenticado pela assinatura HMAC do próprio Stripe, não pela sua API key. A sua conta é identificada no path (organizationId), e a verificação usa o segredo (whsec_...) que você gravou em /settings/stripe.

Comportamento de status

O webhook responde 200 sempre que a assinatura é válida, mesmo para eventos não tratados, sem match ou rejeitados por regra de negócio. Isso é deliberado: um 4xx faria o Stripe reentregar o evento indefinidamente. O campo outcome no corpo da resposta diz o que realmente aconteceu.
Conta inexistente e conta sem configuração retornam o mesmo 401 que uma assinatura inválida: decisão de segurança para não revelar quais contas existem.
Exemplo de resposta:

Eventos do Stripe suportados

O tradutor é determinístico: tudo que não casa com uma regra explícita vira ignored, e o webhook responde 200 para o Stripe parar de reentregar. A atribuição de uma assinatura vem da metadata do objeto Stripe. O tradutor procura repass_cid (clickId) e repass_pid (programId) na metadata da invoice, do line item e do subscription_details (incluindo o layout da API Stripe 2025+). O clickId só é aceito se prefixado clk_; o programId, se prefixado prog_.
Conectar o webhook não atribui afiliados sozinho. Seu checkout precisa propagar repass_cid (o clk_... capturado no tracking) — e, opcionalmente, repass_pid — na metadata da assinatura (subscription_data.metadata) ou da session (mode: payment). Sem isso, o desfecho é unmatched. Guia completo com snippets: Integrar Stripe.

Ingestão custom (S2S)

Quando você não usa um gateway suportado, envie o evento normalizado diretamente para POST /ingest/custom. Diferente do webhook, este endpoint é autenticado pela sua API key e propaga erros de negócio como 4xx normais (404/409) em vez de mascará-los como skipped. O body é a mesma união discriminada por kind, sem a variante ignored.
Erros do /ingest/custom:
Chargebacks via charge.dispute.created entram pelo webhook quando a secretKey (sk_…) está configurada. Sem ela, o evento é ignored (dispute_requires_api_lookup) e você pode registrar o chargeback pelo /ingest/custom com "chargeback": true. Veja Refund e clawback e Integrar Stripe.

Resolução de Conversão e casos de borda

Para charge, refund e cancellation, a Conversão é resolvida assim:
1

conversionId explícito

Busca por id. Se não existir, retorna 404: a referência enviada está errada.
2

customerId

Busca Conversões ativas do cliente: zero ⇒ unmatched (customer_not_attributed); exatamente uma ⇒ usa essa; mais de uma ⇒ skipped (ambiguous_customer). Nunca chuta.
Quando uma “nova assinatura” chega para um cliente que já tem Conversão ativa no mesmo Programa, a ingestão não cria Conversão nova: trata o evento como uma cobrança de ciclo subsequente da Conversão original.

Configuração do Stripe

O segredo de assinatura do webhook (whsec_...) é gravado por conta. Ele é usado apenas para a verificação HMAC dos webhooks recebidos do Stripe.
A resposta é sempre mascarada, nunca devolve o segredo completo:
O webhookSecret deve começar com whsec_ e ter no máximo 255 caracteres, senão a gravação falha com 400. O segredo nunca é devolvido em claro nas respostas nem aparece no histórico de eventos, onde é registrado apenas de forma mascarada (whsec_***<last4>).

Suporte a outros gateways

O modelo do Repass é deliberadamente agnóstico de gateway: o contrato normalizado e o processamento de Conversão/Comissão são únicos, e cada provedor entra apenas como uma tradução do seu payload para o evento normalizado mais a verificação de assinatura do webhook. Na prática, isso significa que:
  • A atribuição, o cálculo de comissão, o clawback e o void de comissões funcionam da mesma forma, independentemente de qual gateway originou o fato financeiro.
  • Enquanto o seu gateway não tiver um webhook nativo no Repass, você pode integrá-lo hoje enviando os mesmos eventos normalizados (conversion / charge / refund / cancellation) pelo /ingest/custom. Use o id do fato financeiro do provedor como sourceEventId para herdar a deduplicação.

Próximos passos

Integrar Stripe

Metadata repass_cid, Connect vs manual e checklist de atribuição.

Conversões e fraude

O que acontece depois da ingestão: atribuição, criação da Conversão e filtros antifraude.

Comissões

Como uma cobrança vira Comissão, e como refund vira void ou clawback.

Conversões server-to-server

Guia prático de envio de eventos pelo /ingest/custom.

Refund e clawback

Registrar estornos e chargebacks de ponta a ponta.