# Conexões financeiras

Endpoint, credenciais, recebimentos e diagnóstico dos gateways dos seus sites.

## IDs de assinatura

`pulsant_subscription_id` é um logicalId estável da assinatura e deve acompanhar `pulsant_external_user_id` quando o provedor aceita metadados de contrato. `pulsant_payment_id` identifica uma cobrança específica, inclusive cada renovação; não reutilize o ID de assinatura como ID de pagamento. Quando um pagamento chega com `subscriptionId` e a Pulsant já recebeu a identidade do contrato na mesma conexão, pode herdar `pulsant_external_user_id`. A aquisição da renovação permanece a da primeira venda registrada e não é recalculada pelo último acesso.

```
const subscriptionMetadata = {
  pulsant_external_user_id: user.opaqueAnalyticsId,
  pulsant_subscription_id: subscriptionIdStableInYourSystem,
};
```

## Prepare o recebimento

Em Conexões financeiras, selecione provedor e nome. Preparar endpoint reserva uma URL sem aceitar webhooks. Cadastre essa URL no provedor e então informe o segredo/token e, quando solicitado, a chave privada da API. Stripe e Mercado Pago precisam da chave para consultar o pagamento canônico. Segredos são criptografados e não retornam na listagem.


## IDs para atribuição e retries

A venda só recebe origem quando a cobrança carrega um vínculo determinístico. `pulsant_attribution_id` deve ser o ID retornado por `window.pulsant?.('getAttributionId')` com analytics autorizado; ele representa visitorId ou sessionId, não e-mail. No modo efêmero é um sessionId e não permite atribuição em dias posteriores; para retornos em vários dias, use identificação persistente autorizada ou `pulsant_external_user_id` com `identify()`. `pulsant_external_user_id` é opcional e deve ser o identificador opaco que sua aplicação já usa em `identify()`. `pulsant_payment_id` é um logicalId estável para a mesma cobrança em retries ou notificações de cliente + servidor; gere um novo para cada pagamento de renovação e não reutilize o ID da assinatura. Envie somente os campos necessários, no servidor, antes de criar o pagamento. Sem vínculo confiável, a venda permanece Sem atribuição; não há associação por IP, e-mail aproximado ou horário.

```
const attributionId = window.pulsant?.('getAttributionId');
// O browser envia o identificador opaco somente ao seu próprio backend.
await fetch('/api/checkout', { method: 'POST', body: JSON.stringify({ attributionId }) });

// No seu backend, monte o metadata do gateway após validar a sessão e o site:
const paymentMetadata = {
  ...(typeof input.attributionId === 'string' ? { pulsant_attribution_id: input.attributionId } : {}),
  ...(user?.opaqueAnalyticsId ? { pulsant_external_user_id: user.opaqueAnalyticsId } : {}),
  pulsant_payment_id: paymentIdStableForThisCharge,
};
```

## Stripe, Polar e Lemon Squeezy

Stripe: coloque os IDs em `payment_intent_data.metadata` ao criar Checkout Session e, se também tratar eventos de Checkout, replique-os em `metadata` da Session. A Pulsant lê o PaymentIntent canônico e não usa metadata de Customer como substituto do vínculo da compra. Polar: passe-os no `metadata` do Checkout Session; a documentação do Polar informa que esse objeto é copiado para o Order e/ou Subscription. Lemon Squeezy: passe-os em `checkout_data.custom`; eles chegam como `meta.custom_data` nos webhooks de Order/Subscription. Não use nome ou e-mail como identificador.

- [Stripe metadata](https://docs.stripe.com/api/metadata)
- [Stripe Checkout Sessions](https://docs.stripe.com/api/checkout/sessions)
- [Polar Create Checkout Session](https://polar.sh/docs/api-reference/checkouts/create-session)
- [Lemon Squeezy custom checkout data](https://docs.lemonsqueezy.com/help/checkout/passing-custom-data)


## Paddle e Dodo Payments

Paddle: envie os IDs em `custom_data` da Transaction ou do Checkout. A Pulsant lê `data.custom_data`; webhooks de assinatura também aceitam custom data. Dodo Payments: envie os IDs em `metadata` do Checkout Session; o webhook de pagamento devolve esse campo. Para contratos recorrentes, preserve `pulsant_subscription_id` e `pulsant_external_user_id` em Subscription metadata; pagamentos de renovação podem herdar a identidade observada no contrato. Os detalhes são dos contratos mapeados no código da Pulsant; valide o campo no recurso/payload ativo da conta antes de habilitar a conexão.

- [Paddle custom data](https://developer.paddle.com/build/transactions/custom-data/)
- [Dodo metadata guide](https://docs.dodopayments.com/api-reference/metadata)


## Asaas e Mercado Pago

Mercado Pago: envie os IDs como propriedades string do objeto `metadata` ao criar o pagamento; a Pulsant consulta o pagamento canônico e lê esse campo. Asaas: `externalReference` é texto livre; o adaptador Pulsant reconhece nele uma string JSON compacta com as chaves aprovadas. Por ser um único campo de busca, mantenha o conteúdo curto, confira o limite da API e confirme que a resposta/webhook o devolve. Caso o provedor ou seu checkout não preserve os IDs, use a API privada de receita da Pulsant após a confirmação server-side; não registre pagamento confirmado no navegador.

```
// Mercado Pago: metadata é um objeto JSON.
metadata: {
  pulsant_attribution_id: attributionId,
  pulsant_external_user_id: opaqueUserId,
  pulsant_payment_id: logicalPaymentId
}

// Asaas: externalReference é uma string; envie só os IDs necessários.
externalReference: JSON.stringify({
  pulsant_attribution_id: attributionId,
  pulsant_payment_id: logicalPaymentId
})
```
- [Mercado Pago payment metadata](https://www.mercadopago.com.br/developers/en/docs/checkout-api-payments/how-tos/improve-payment-approval/industry-data/applications-online-platforms)
- [Asaas create payment](https://docs.asaas.com/reference/create-new-payment)


## Woovi: cobrança Pix e Pix Automático

Cobrança Pix avulsa: use `charge.additionalInfo` como array `{ key, value }` com as chaves `pulsant_attribution_id`, `pulsant_external_user_id` e, se necessário, `pulsant_payment_id`. Pix Automático: `PIX_AUTOMATIC_COBR_COMPLETED` não documenta `additionalInfo` na parcela; para a atribuição do pagamento use `correlationID` no formato `p1.<attributionId>.<sufixo-único>`. O sufixo diferencia assinaturas do mesmo visitante; use somente o ID opaco retornado pelo tracker, nunca PII. A Pulsant reconhece a versão `p1` no webhook da parcela. `additionalInfo` no recurso canônico da assinatura pode complementar o vínculo de `externalUserId` quando a API o devolver. Notificações fora de ordem e sem esses campos permanecem sem atribuição; não são associadas por coincidência.

```
additionalInfo: [
  { key: 'pulsant_attribution_id', value: attributionId },
  { key: 'pulsant_external_user_id', value: opaqueUserId },
  { key: 'pulsant_payment_id', value: logicalPaymentId }
]

// Gere o sufixo no servidor uma única vez por assinatura.
correlationID: `p1.${attributionId}.${serverGeneratedUniqueSuffix}`
```
- [Woovi additionalInfo em cobranças](https://developers.woovi.com/docs/charge/how-charge-additional-info)
- [Woovi Create Pix Automatic](https://developers.woovi.com/docs/pix-automatic/pix-automatic-how-to-create)
- [Woovi Pix Automatic webhooks](https://developers.woovi.com/docs/pix-automatic/webhooks/pix-automatic-webhooks)


## URL pública e ambiente

API_PUBLIC_URL precisa apontar para uma API acessível ao provedor, normalmente por HTTPS. Localhost permite testes de contrato locais, mas o servidor do gateway não consegue chamar o seu localhost. O produto aceita somente pagamentos reais. O modo sandbox foi removido; registros legados não são convertidos em receita real.


## O que foi validado

Há verificadores de autenticação/assinatura para Stripe, Polar, Lemon Squeezy, Paddle, Dodo Payments, Asaas, Mercado Pago e Woovi, com mapeamentos iniciais de pagamentos e ajustes. Stripe pode consultar a taxa da Charge por meio do Balance Transaction, apenas quando origem e moeda correspondem; se essa consulta não estiver disponível, o pagamento continua e a taxa fica desconhecida. Asaas pode derivar a taxa de value − netValue somente quando originalValue não indica diferença e desconto, juros e multa não explicam o valor; casos ambíguos ficam desconhecidos. Woovi pode reportar a taxa de uma cobrança; Mercado Pago soma somente fee_details que o collector paga; Paddle informa a taxa original em details.totals.fee quando a moeda corresponde à da venda e pode completá-la em transaction.updated. Valores convertidos para moeda de repasse não são misturados à venda. A soma e o líquido após taxas informadas cobrem somente pagamentos cuja taxa foi reportada; não representam saldo/repasse do gateway. Taxas e ajustes ainda não cobertos permanecem desconhecidos, sem estimativa. Consulte a matriz de implementação. As validações externas em sandbox foram canceladas; nenhum gateway foi validado ao vivo. Os fixtures financeiros atuais ainda aguardam os checks finais.


## Rotação e desconexão

Na atualização, campos vazios mantêm o segredo/chave existentes. Uma troca de segredo pode aceitar o anterior por uma janela explícita de até 24 horas; zero encerra a validade anterior. Desconectar remove credenciais locais e recusa novos webhooks, preservando histórico. Recebimentos pendentes exigem reconfiguração para serem processados novamente. A conexão preserva sua data original após reconectar, sem importar histórico anterior à primeira configuração.


## Recebimento e recuperação

Um webhook válido recebido comprova autenticação e entrega, não necessariamente que seu tipo já foi mapeado. O diagnóstico separa processamento, evento ainda não mapeado, falha, espera por franquia e coleta pausada. Reprocessar exige motivo e mantém o payload original; IDs de pagamentos e ajustes evitam duplicação. Nova receita de um site desativado/pausado fica duravelmente pendente. Ajustes de vendas já registradas continuam permitidos, sem unidade adicional por mudança de estado.


## Mercado Pago: pagamentos, assinaturas e faturas

O tópico payment consulta o ID assinado em /v1/payments/{id}. subscription_authorized_payment consulta primeiro /authorized_payments/{id}, resolve o pagamento associado e consulta seu recurso canônico. A fatura e a notificação de pagamento usam a mesma identidade da venda, sem duas unidades; preapproval_id vincula deterministicamente a assinatura quando estiver disponível. subscription_preapproval consulta /preapproval/{id} e preserva estado/data last_modified. subscription_preapproval_plan consulta /preapproval_plan/{id}, grava o preço recorrente conhecido com last_modified e busca assinaturas vinculadas em páginas autenticadas de /preapproval/search. Se o plano anteceder a conexão, sua data efetiva começa em first_configured_at, sem retroagir receita. O reconciliador só associa IDs já registrados na mesma conexão, workspace e site; dados de pagador/cartão da resposta de busca não são persistidos. Quando o provedor devolve um valor individual, ele precisa corresponder ao plano; divergências permanecem sem MRR até a confirmação. Relatórios resolvem o preço efetivo na data consultada, sem reescrever transações ou fabricar histórico anterior à conexão. MRR ainda exige contrato ativo e período confirmado; planos com valor/ciclo desconhecido, consultas pendentes ou estados sem última cobrança e próximo vencimento permanecem excluídos. Fatura sem pagamento associado permanece reprocessável, sem presumir confirmação. Chargebacks por caso estao implementados para Mercado Pago, mas aguardam validacao final; o gateway permanece sem validacao autenticada externa.


## Paddle: chargebacks e avisos

Paddle envia adjustment.created para chargebacks e avisos. chargeback registra o valor disputado na venda existente; chargeback_reverse reverte quando Paddle contesta com sucesso. chargeback_warning e chargeback_warning_reverse representam um reembolso preventivo e sua correção, separados de disputas formais. transaction.completed/updated podem reportar a taxa da venda em sua moeda original; a Pulsant não usa o valor convertido da moeda de repasse. Casos simultâneos na mesma transação, taxas de chargeback e reconciliação de payout não são conciliados automaticamente. Código e fixtures aguardam checks finais.


## Stripe: chargebacks

Os eventos charge.dispute.created, updated, closed, funds_withdrawn e funds_reinstated disparam a consulta canonica da disputa. A Pulsant usa o valor parcial e a moeda informados pelo Stripe, vinculando por PaymentIntent ou pela cobranca canonica. needs_response/under_review e lost mantem o valor deduzido; won continua pendente ate funds_reinstated confirmar a devolucao. Avisos preventivos nao sao registrados como chargeback e campos de evidencia ou cliente nao entram no ledger. O fluxo esta implementado, mas aguarda validacao final.


## Asaas: chargebacks

A Pulsant acompanha PAYMENT_CHARGEBACK_REQUESTED, PAYMENT_CHARGEBACK_DISPUTE e PAYMENT_AWAITING_CHARGEBACK_REVERSAL pelo webhook e consulta o estado canonico pela API server-side. REQUESTED/IN_DISPUTE e o periodo de transferencia permanecem deduzidos; DISPUTE_LOST permanece como chargeback. REVERSED so volta a receita quando PAYMENT_CONFIRMED ou PAYMENT_RECEIVED confirma o retorno. DONE sem sinal monetario conclusivo fica para revisao. O valor usa a cobranca integral; chargeback parcial nao e suportado. PAYMENT_REFUNDED depois de chargeback concluido nao e lancado uma segunda vez como reembolso. Este fluxo esta implementado mas aguarda validacao final, sem chamadas autenticadas ao Asaas.


## Mercado Pago: chargebacks

O topico topic_chargebacks_wh busca o caso canonico /v1/chargebacks/{id}, autenticado com o token da conexao e X-Caller-Id do seller. coverage_applied nulo mantem a disputa pendente; false registra chargeback separado de reembolso; true reverte um chargeback anteriormente aplicado. A data canonica ordena mudancas e retries nao duplicam ajustes ou unidades. Apenas casos com um pagamento sao aplicados; os demais ficam para diagnostico. O status charged_back isolado nao e tratado como reembolso. Fixtures e codigo ainda aguardam validacao final, sem chamada autenticada ao gateway. Veja Chargebacks Mercado Pago.


## Consultas ao provedor e retries

As consultas de Mercado Pago e Stripe usam somente origens oficiais fixas, caminhos validados, timeout de 10 segundos e resposta JSON limitada a 512 KiB. Stripe resolve a taxa pela sequência PaymentIntent → Charge → Balance Transaction e valida IDs, captura, origem e moeda antes de guardar a taxa; uma falha nessa consulta deixa a taxa desconhecida sem impedir a venda. Um charge.updated posterior só completa uma taxa de pagamento que já exista. A busca de assinaturas por plano usa filtro de plan ID, páginas curtas e cursor PostgreSQL durável; uma página já projetada pode ser repetida sem duplicar o vínculo. Falhas de credencial, limite temporário, recurso ausente, resposta inválida e ID divergente ficam no diagnóstico, sem copiar corpos de erro privados. O primeiro resultado financeiro validado fica criptografado antes de aplicar o ledger, inclusive quando faltar franquia. Se a franquia acabar antes do registro, o recebimento permanece criptografado e pendente, sem criar receita nem perder o webhook; a tentativa automática ocorre após a regularização, e a conexão mostra esse estado. Retry reaplica o mesmo resultado congelado; quando o snapshot da assinatura faltou, o retry autorizado pode completá-lo sem alterar o pagamento. A persistência contém valores, IDs técnicos e metadados de atribuição permitidos; a resposta completa com dados de pagador/cartão não é copiada. Após 90 dias do processamento concluído, o payload é substituído por marcador cifrado; pagamentos e ajustes permanecem. Recebimentos pendentes, inclusive por quota esgotada, são conservados até a resolução.


## Confirmação e datas financeiras

Checkout Stripe exige payment_status paid e PaymentIntent succeeded, além de ambiente coerente no recurso consultado. Checkout pendente não vira receita por consultar um estado posterior usando sua data antiga; a confirmação deve chegar pelo evento apropriado do provedor. No Mercado Pago, pagamentos usam date_approved e ajustes cumulativos usam date_last_updated do recurso consultado. Essa data descreve a atualização fornecida pelo provedor, não uma promessa de instante exato de cada estorno. Reembolso sem pagamento confirmado no histórico permanece diagnosticável para recuperação; a Pulsant não inventa uma venda anterior para fechar a conta.

