O relatório de pagamentos mostra 150,00. A exportação de analytics também. Ambos contêm três identificadores de pedido distintos. Parece razoável considerar a integração saudável.

Essas verificações podem passar mesmo quando os relatórios representam pedidos diferentes.

Este pequeno contraexemplo pode ser executado. Ele usa dados inventados, não um incidente de cliente nem uma afirmação sobre qualquer fornecedor de analytics. O objetivo é mostrar o que uma comparação de totais não comprova e tornar concreta a próxima verificação.

Somas iguais não comprovam registros iguais

Uma loja tem três pedidos pagos: O101 de 40,00, O102 de 60,00 e O103 de 50,00. Uma exportação de eventos brutos de compra contém O101 duas vezes, O102 uma vez e um O999 sem correspondência de 10,00. Todos os valores usam a mesma moeda fictícia com duas casas decimais.

ID do pedido Valor do pedido pago Valores dos eventos brutos Linhas de evento
O101 40,00 40,00 + 40,00 2
O102 60,00 60,00 1
O103 50,00 Nenhum evento 0
O999 Sem pedido neste exemplo 10,00 1
Total 150,00 150,00 4

A duplicata acrescenta 40,00. O registro sem correspondência soma outros 10,00. Juntos, compensam exatamente os 50,00 ausentes. Há também três IDs distintos de cada lado, mas apenas dois são compartilhados.

A contagem de linhas de evento encontra uma diferença neste exemplo. A soma e a contagem de IDs distintos não. Nenhum desses agregados identifica qual pedido está ausente.

Os dois relatórios sintéticos somam 150,00 apesar de conterem pedidos diferentes.

Defina o que as duas exportações devem representar

Um evento de compra do navegador registra o que a instrumentação informou. Um pedido pago representa o estado de negócio mantido pelo sistema de pedidos ou pagamentos. São úteis para perguntas diferentes. Um não deve substituir o outro silenciosamente.

Antes da comparação, defina o contrato: loja ou conta, moeda, identificador compartilhado, estado de pagamento incluído, definição de valor e período. Escolha se a comparação será por pedido, tentativa de pagamento ou item. Um pedido pode legitimamente ter vários pagamentos ou itens.

Valor bruto de pedidos, pagamentos capturados, receita líquida de reembolsos e repasses após taxas não são intercambiáveis. Horários de evento, pagamento, ingestão e repasse também não. Combine um corte e considere o atraso real de processamento. Um registro recente que ainda não chegou não está necessariamente perdido.

O exemplo tem um contrato propositalmente restrito: um retrato sintético completo, três pedidos já pagos, um evento esperado por pedido, sem reembolsos, impostos, taxas, pagamentos divididos ou conversão de moeda. São dados brutos, não uma simulação das regras de deduplicação de um fornecedor.

Agrupe os eventos antes de fazer o join

Execute com Python 3 e seu módulo sqlite3. O exemplo cria um banco em memória, sem solicitações de rede nem alterações em produção. Os valores são inteiros na menor unidade da moeda fictícia, portanto a comparação não depende de aritmética binária de ponto flutuante.

import sqlite3

db = sqlite3.connect(":memory:")
db.executescript("""
CREATE TABLE orders (order_id TEXT PRIMARY KEY, cents INTEGER NOT NULL);
CREATE TABLE events (event_id TEXT PRIMARY KEY, order_id TEXT NOT NULL,
                     cents INTEGER NOT NULL);
INSERT INTO orders VALUES ('O101', 4000), ('O102', 6000), ('O103', 5000);
INSERT INTO events VALUES ('E1', 'O101', 4000), ('E2', 'O101', 4000),
                          ('E3', 'O102', 6000), ('E4', 'O999', 1000);
""")

for table in ("orders", "events"):
    total, ids = db.execute(
        f"SELECT SUM(cents), COUNT(DISTINCT order_id) FROM {table}"
    ).fetchone()
    print(f"{table}: cents={total}, distinct_order_ids={ids}")

query = """
WITH observed AS (
  SELECT order_id, COUNT(*) AS event_rows, SUM(cents) AS event_cents
  FROM events GROUP BY order_id
), ids AS (
  SELECT order_id FROM orders UNION SELECT order_id FROM events
)
SELECT ids.order_id, o.cents, COALESCE(e.event_rows, 0), e.event_cents
FROM ids
LEFT JOIN orders o ON o.order_id = ids.order_id
LEFT JOIN observed e ON e.order_id = ids.order_id
ORDER BY ids.order_id
"""
for row in db.execute(query):
    print(row)
db.close()

A saída é:

orders: cents=15000, distinct_order_ids=3
events: cents=15000, distinct_order_ids=3
('O101', 4000, 2, 8000)
('O102', 6000, 1, 6000)
('O103', 5000, 0, None)
('O999', None, 1, 1000)

A consulta agrupa primeiro os eventos em uma linha por ID de pedido. Caso contrário, juntar diretamente um pedido a várias linhas de eventos pode repetir seu valor e distorcer a própria conciliação. A união de todos os IDs também preserva registros sem correspondência dos dois lados. Um inner join os esconderia.

O103 não tem linha de evento. O999 não tem pedido correspondente no exemplo. O101 tem duas linhas de evento. São três observações diferentes, não uma única diferença inexplicada no total.

O103 está sem evento e O999 não tem pedido correspondente no exemplo.

Em uma exportação real, verifique também os identificadores ausentes antes de agrupar. Não os transforme silenciosamente em um valor comum como unknown: registros independentes pareceriam pertencer ao mesmo pedido. Delimite os IDs pela loja, conta e ambiente corretos. Se as exportações usam IDs diferentes, estabeleça um mapeamento em vez de juntar por valores iguais ou horários próximos.

Uma divergência inicia a investigação, não é um diagnóstico

Neste exemplo construído, conhecemos os defeitos porque os inserimos. Em produção, um ID sem correspondência também pode resultar de outro limite diário, ambiente de teste, exportação incompleta ou estado legítimo de negócio excluído de um dos relatórios. Preserve essas possibilidades até examinar os registros.

O envio pelo navegador e a confirmação de pagamento têm modos de falha diferentes. O guia da Stripe para atender pedidos do Checkout explica por que um pagamento bem-sucedido não garante uma visita à página de retorno. Um evento nessa página, sozinho, não é um mecanismo completo de confirmação de pagamento.

O envio pelo servidor também exige cuidado. A Stripe documenta entregas duplicadas de webhooks. Receber uma notificação não é o mesmo que aplicar seu efeito de negócio uma única vez com segurança. O ID de evento do provedor e o ID do pedido resolvem problemas distintos: vários eventos diferentes podem tratar do mesmo pedido. Este artigo não implementa um handler de pagamentos nem prescreve uma chave universal de deduplicação.

Da mesma forma, sendBeacon() retornar true significa que o navegador enfileirou os dados para envio, não que a aplicação de analytics aceitou e armazenou uma compra. Esse é o contrato documentado do retorno, não uma prova de que determinado coletor perdeu um evento.

Agrupe eventos por pedido antes de juntá-los aos pedidos pagos.

Concilie as identidades antes de interpretar a atribuição

Uma conciliação útil mantém listas separadas de pedidos pagos sem o evento esperado, eventos sem um pedido pago correspondente, linhas repetidas e divergências de valor ou moeda. Acompanhe essas categorias com regras estáveis. Mantenha visível a receita sem correspondência, em vez de inventar um canal para fechar um gráfico de atribuição.

Corrigir a cobertura da coleta não comprova que um canal causou uma compra. A atribuição faz outra pergunta: como distribuir o crédito. Vale separá-la da questão de saber se os registros de compra concordam.

A lição não é que todo dashboard esteja errado. Totais iguais são evidências mais fracas do que parecem. Mantenha a comparação agregada, mas acrescente uma verificação por registro antes de considerar conciliada uma integração de receita.