Integração, do clique à primeira venda
Instale a captura da indicação, preserve a referência durante o login e envie-a no pagamento. Depois, confira a venda atribuída e a comissão no IndicaSaaS.
Este guia é para quem desenvolve ou mantém o checkout do SaaS. Você precisa alterar o site e o backend que cria a cobrança. Conectar o gateway, receber cliques ou carregar um script não conclui essa integração.
Antes de começar#
- Cadastre o SaaS e aceite o contrato. No portal do anunciante, confirme o SaaS e a organização que você está administrando. O login do IndicaSaaS é independente do login do seu produto.
- Comprove o domínio. Em Detalhes do SaaS, publique o TXT solicitado e use Verificar DNS. Configure uma URL de destino sob seu controle e preserve a query string nos redirecionamentos.
- Crie, ative e publique o programa. Anote o
programId, escolha primeiro/último clique, janela de atribuição, comissão, recorrência, retenção e se aceita apenas clientes novos. O ID do programa não é o código do link. - Tenha uma afiliação ativa. O afiliado precisa aceitar os termos e atender aos requisitos de perfil/canal; em aprovação manual, aguarde a aprovação do anunciante. Gere o link pelo portal ou pelo SDK autenticado.
- Conecte a conta correta do gateway. Em Ferramentas → Gateways de pagamento, use OAuth para Stripe/Mercado Pago ou a chave solicitada para Asaas/AbacatePay/Pagar.me. A conta que recebe o dinheiro precisa ser a mesma conexão que entrega os eventos.
- Separe homologação e operação. Combine conta, credenciais, produtos, clientes e webhooks do mesmo ambiente. Veja o roteiro de teste antes de criar cobranças.
O motor trabalha com BRL e valor efetivamente pago positivo. Não converte moeda. Cupom de 100%, trial gratuito, cadastro e autorização sem captura não criam comissão por si só. O IndicaSaaS registra comissões e repasses; o SaaS paga o afiliado diretamente.
O que fica em cada lado
| Local | Responsabilidade | Segredo? |
|---|---|---|
| Navegador do comprador | Carregar afs.js, ler AFS.getRef(programId), enviar a referência ao próprio backend. | Nenhuma chave. O ID do programa é público. |
| Backend do SaaS | Autenticar o comprador, calcular preço/desconto, preservar o ref e criar a cobrança. | Credencial do seu gateway; chave SDK IndicaSaaS somente se usar widget/cupom. |
| IndicaSaaS | Autenticar eventos, validar ref, regras, conversão, comissão e estornos. | Credenciais da conexão ficam no servidor. |
Do clique à comissão#
- Link do afiliado:
https://go.indicasaas.com/SEU_CODIGO. Parâmetrossubeutm_*são opcionais para origem/campanha. - Redirect: o destino recebe
?ref=afs1.…, assinado e datado. Preserve o valor inteiro; não o substitua pelo código do afiliado, e-mail, ID do cliente ou UTM. - Persistência antes do login: o
afs.jslê a URL ao executar e guarda o ref por programa no navegador. Um login posterior não deve apagar essa indicação. - Backend: o checkout envia o ref escolhido pelo SDK. O servidor associa-o ao pedido que vai criar, sem confiar em preço, identidade ou desconto enviados pelo navegador.
- Pagamento: o backend coloca o ref no campo aceito pelo gateway. Cada fornecedor usa um nome diferente.
- Webhook: o gateway entrega o evento autenticado. O IndicaSaaS o registra e processa em fila, consultando o gateway quando necessário.
- Conversão e comissão: pagamento, atribuição e regras elegíveis produzem a venda e o lançamento financeiro. Duplicatas não devem produzir outra comissão.
A assinatura do afs1 é validada pelo motor do IndicaSaaS, não pelo JavaScript do navegador. O SDK apenas seleciona uma referência com formato e prazo compatíveis. Um ref presente no navegador ainda não prova elegibilidade financeira.
O redirect pode emitir um ref assinado mesmo sem snapshot recuperável do clique. Nesse caso, a atribuição ainda depende das regras e as subtags podem ficar indisponíveis; subtags_available=false no postback não significa que subtags foram recuperadas.
Instalar em HTML, SPA, React e Next.js#
HTML e sites com páginas tradicionais
Inclua o script nas páginas de entrada e no checkout. Carregue-o antes de remover ref da URL ou encaminhar o visitante para login. O arquivo público não exige API key.
<script defer src="https://indicasaas.com/sdk/v1/afs.js"></script>
<form id="checkout" action="/api/checkout" method="post">
<!-- Inclua aqui a proteção CSRF exigida pelo seu backend. -->
<input type="hidden" name="ref" id="ref-indicacao">
<button type="submit">Continuar para pagamento</button>
</form>
<script>
document.querySelector('#checkout').addEventListener('submit', function (event) {
if (!window.AFS) {
event.preventDefault();
alert('A integração ainda não carregou. Tente novamente.');
return;
}
document.querySelector('#ref-indicacao').value =
window.AFS.getRef('SEU_PROGRAM_ID') || '';
});
</script>/api/checkout é uma rota que você implementa no seu SaaS, não uma rota do IndicaSaaS. Neste exemplo ela recebe formulário; se usar JSON, ajuste o parser. Um null legítimo permite compra orgânica, sem indicação; falha de carregamento do SDK merece diagnóstico separado.
SPA e React
Carregue o mesmo script no HTML de entrada antes da inicialização do checkout. Leia o ref no momento de enviar a compra; não use um valor capturado no primeiro render se o checkout puder ocorrer depois. Em TypeScript, declare apenas a API utilizada:
declare global {
interface Window {
AFS?: { getRef(programId?: string): string | null };
}
}
export async function abrirCheckout(csrfToken: string) {
if (!window.AFS) throw new Error('Tracking ainda não carregado');
const response = await fetch('/api/checkout', {
method: 'POST',
credentials: 'same-origin',
headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrfToken },
body: JSON.stringify({ ref: window.AFS.getRef('SEU_PROGRAM_ID') }),
});
if (!response.ok) throw new Error('Não foi possível abrir o checkout');
const { url } = await response.json();
// Seu backend retorna apenas a URL obtida do gateway autorizado.
window.location.assign(url);
}O componente deve mostrar estado de envio e erro e impedir duplo envio. A chave idempotente e o preço pertencem ao pedido no backend. Este trecho é um exemplo de transporte, não implementa seu carrinho, autenticação ou proteção CSRF.
pushState/replaceState. Chamar AFS.save() depois não captura um novo ref. Para uma nova entrada por indicação, preserve a query e use navegação completa para carregar a página e o SDK novamente.Next.js (App Router)
No layout raiz, use next/script com beforeInteractive para a captura inicial. Leia window.AFS em um Client Component, por exemplo no handler do botão; não durante o render do servidor. Referência: componente Script do Next.js.
import Script from 'next/script';
import type { ReactNode } from 'react';
export default function RootLayout({ children }: { children: ReactNode }) {
return (
<html lang="pt-BR">
<body>
<Script src="https://indicasaas.com/sdk/v1/afs.js"
strategy="beforeInteractive" />
{children}
</body>
</html>
);
}No Pages Router, carregue o script conforme o contrato do seu _document/_app, garantindo captura antes do redirect de login. Middleware que redireciona antes de renderizar precisa preservar a query ou guardar a referência no servidor. O exemplo não intercepta redirects HTTP. Se houver CSP, autorize a origem do script e use o nonce exigido pela sua aplicação.
Persistência, login, domínios e prazo#
Primeiro e último clique
O SDK guarda afs_ref:PROGRAM_ID no localStorage. Em primeiro clique, mantém o primeiro válido; em último clique, mantém o mais recente pela data do clique. Reload e retorno à mesma URL não reiniciam o prazo. A janela é definida pelo programa (7, 14 ou 30 dias); o motor confere a data do pagamento, não a hora em que o webhook chegou.
Use sempre AFS.getRef('SEU_PROGRAM_ID'). Sem argumento, AFS.getRef() escolhe o ref mais recente entre os programas guardados e pode selecionar o programa errado num site com mais de um. Se expirou ou não existe, retorna null. Ref legado sem data no storage não recupera uma indicação válida.
Antes e depois do login
Instale a captura na landing pública. Não espere o usuário entrar no painel. Na mesma origem, a referência pode sobreviver ao login e a novas páginas. Para preservar durante OAuth, guarde o ref selecionado na sessão temporária do seu servidor e vincule-o à mesma sessão após autenticação; use o mecanismo de state do seu provedor, sem substituir suas proteções de login. Persistir no banco do SaaS é uma decisão da sua integração e não cria um lead ou cliente no IndicaSaaS.
Se gravar o ref no seu backend antes da compra, mantenha a data original e a política do programa: não sobrescreva primeiro clique válido nem ressuscite referência vencida. A HMAC, o programa e o prazo ainda serão conferidos no pagamento. O SDK não oferece endpoint público de identificação de leads.
Landing e app em origens diferentes
www.exemplo.com, app.exemplo.com, HTTP e HTTPS têm armazenamentos separados. O cookie do redirect em go.indicasaas.com também não cruza para seu site. O localStorage é separado por origem; não existe sincronização automática entre domínios, dispositivos ou navegadores.
Transporte o ref escolhido para um destino fixo sob seu controle e execute o SDK no destino, ou use a sessão do backend. Exemplo de passagem da landing para outro subdomínio:
function entrarNoApp() {
if (!window.AFS) throw new Error('Tracking ainda não carregado');
const destino = new URL('https://app.exemplo.com/cadastro');
const ref = window.AFS.getRef('SEU_PROGRAM_ID');
if (ref) destino.searchParams.set('ref', ref);
window.location.assign(destino.href); // navegação completa
}Não aceite URL de destino arbitrária enviada pelo usuário. Verifique se login, encurtador, CDN ou canonical redirect remove parâmetros. Depois da captura, você pode remover apenas o ref da barra com history.replaceState, preservando os demais parâmetros e o hash. Evite registrar a URL completa em analytics/logs desnecessários.
Storage bloqueado e navegação privada
Se o navegador negar armazenamento, o SDK mantém o ref somente na memória da página atual. Fechar a aba, recarregar ou sair dela pode perder a indicação. Navegação privada e limpeza de dados também limitam a persistência. Transporte ao backend antes de sair quando necessário; não prometa recuperação por e-mail ou fingerprint.
Levar a referência ao backend e ao pedido#
O comprador envia apenas o ref selecionado e os identificadores necessários à compra. O backend resolve o programa, a conta do gateway, o produto, o preço, o cliente e qualquer desconto a partir da sessão autenticada e do catálogo interno. Nunca aceite isNewCustomer, total pago ou couponApplied como fatos vindos do navegador.
// Valida o transporte, NÃO a assinatura ou a elegibilidade financeira.
function referralFromBody(body) {
if (!body || typeof body !== 'object' || Array.isArray(body)) {
throw new Error('invalid_body');
}
const ref = body.ref;
if (ref == null || ref === '') return undefined; // compra orgânica
if (typeof ref !== 'string' || ref.length > 512 ||
!/^afs1\.[A-Za-z0-9_.-]+$/.test(ref)) {
throw new Error('invalid_ref');
}
return ref; // preserve exatamente, sem cortar ou reescrever
}Adapte os erros para HTTP 400 e limite o tamanho do corpo. Autentique, valide CSRF/origem conforme sua sessão, autorize a compra e persista pedido/ref antes da chamada ao gateway. Repetir uma requisição do mesmo pedido deve reutilizar a mesma chave idempotente e o mesmo ref. Se a chamada ao gateway expirar sem resposta, consulte a cobrança existente antes de criar outra.
A verificação acima não autoriza desconto, comissão nem conta. Não copie o segredo de assinatura do IndicaSaaS para seu SaaS e não tente fabricar afs1. Para um campo de referência que você já usa como número do pedido, preserve esse número no seu banco ou em metadata própria; não concatene pedido|ref, pois o motor espera o token inteiro.
| Gateway | Campo | Observação |
|---|---|---|
| Stripe Checkout | client_reference_id | Na assinatura, também subscription_data.metadata.aff_ref. |
| Mercado Pago | external_reference | Pagamento aceita fallback metadata.aff_ref; comprove a propagação ao recurso pago. |
| Asaas | externalReference | Cobrança/assinatura; confira a cobrança paga. |
| AbacatePay | externalId | Não é externalReference; metadata arbitrária não substitui. |
| Pagar.me Core v5 | metadata.aff_ref | Pedido/assinatura e recursos autenticados precisam concordar. |
Stripe: assinatura e pagamento único#
Conecte sua conta pelo portal. O recebimento central de eventos Connect é configuração do operador da plataforma; autorizar OAuth não prova entrega. Confirme conta conectada e modo com uma transação de teste no ambiente combinado. Não é necessário compartilhar a chave Stripe do seu SaaS no frontend.
Os exemplos abaixo usam o cliente stripe já configurado no seu servidor, com a conta que recebe o pagamento. ref vem do contrato anterior; order e isNewCustomer vêm do seu banco. São trechos de criação da sessão, não um servidor completo.
Assinatura
const metadata = {
...(ref ? { aff_ref: ref } : {}),
aff_new: isNewCustomer ? '1' : '0', // comprovado pelo seu backend
};
const session = await stripe.checkout.sessions.create({
mode: 'subscription',
customer: order.stripeCustomerId,
line_items: [{ price: order.recurringPriceId, quantity: 1 }],
...(ref ? { client_reference_id: ref } : {}),
metadata,
subscription_data: { metadata },
success_url: 'https://app.exemplo.com/compra/retorno',
cancel_url: 'https://app.exemplo.com/planos',
}, { idempotencyKey: order.id });
// Retorne session.url ao comprador; não marque a venda pela success_url.O checkout da assinatura estabelece a indicação; a comissão vem das invoices pagas. Grave metadata na assinatura para que invoice.paid possa recuperá-la mesmo se chegar antes do evento do Checkout. Metadata da sessão não é copiada automaticamente para todos os objetos. Se não consegue provar cliente novo, omita aff_new; não envie '1' por padrão.
Pagamento único
const metadata = {
...(ref ? { aff_ref: ref } : {}),
aff_new: isNewCustomer ? '1' : '0',
};
const session = await stripe.checkout.sessions.create({
mode: 'payment',
customer: order.stripeCustomerId,
line_items: [{ price: order.oneTimePriceId, quantity: 1 }],
...(ref ? { client_reference_id: ref } : {}),
metadata,
success_url: 'https://app.exemplo.com/compra/retorno',
cancel_url: 'https://app.exemplo.com/planos',
}, { idempotencyKey: order.id });Não envie subscription_data em mode: 'payment'. Para métodos assíncronos, completar a sessão sem pagamento ainda não gera comissão; o adapter também trata checkout.session.async_payment_succeeded. Uma sessão com pagamento zero continua inelegível.
Para créditos de uso, adicione aff_revenue_type: 'credits' à metadata da sessão. Se habilitar invoice_creation, repita aff_ref, aff_new e a classificação em invoice_creation.invoice_data.metadata. Assinatura e créditos com taxas diferentes devem ser cobranças separadas; esta versão não rateia uma invoice mista por item.
Para cupons, habilite allow_promotion_codes: true ou aplique o Promotion Code confirmado pelo backend no campo discounts, conforme seu checkout. Não misture exemplos dos dois métodos sem conferir a API. O desconto tem que estar na mesma conta e modo da conexão. Veja cupons e a referência oficial de criação da sessão.
O contrato implementado usa consultas Stripe com versão 2025-03-31.basil. Não trate uma versão futura ou outro produto Stripe como automaticamente homologado. Invoices sem assinatura, Payment Links e cobranças criadas por outro fluxo precisam demonstrar que a referência chega a um evento suportado.
Mercado Pago#
Conecte via OAuth no IndicaSaaS. O adapter recebe tópicos payment, subscription_authorized_payment e subscription_preapproval; autentica a notificação e consulta o recurso na conta conectada. Um pagamento pendente não equivale a aprovação.
// Mescle no payload válido do produto Mercado Pago que você já usa.
const atribuicao = ref ? {
external_reference: ref,
metadata: { aff_ref: ref },
} : {};
// Ao mesclar metadata, preserve as chaves internas do seu pedido.Em Checkout Pro, configure a referência ao criar a preferência e confira o external_reference na consulta do pagamento final. Em assinatura, configure a referência no fluxo de preapproval e confira cada cobrança autorizada. O fragmento não é um request completo e não promete paridade com a nova API Orders ou todos os produtos Mercado Pago.
O vínculo de assinatura confirmado pode recuperar renovações sem ref. Não estime o ciclo por número de webhooks: regras de primeiras N cobranças exigem ordinal comprovado. O adapter não implementa cupom de indicação nativo nem recomposição por vitória de disputa. Configuração detalhada e teste oficial do Checkout Pro.
Asaas#
Informe a chave em Ferramentas → Gateways, depois cadastre no Asaas a URL e o secret devolvidos. O secret de webhook vai no mecanismo asaas-access-token do fornecedor; não é sua API key. A conexão verifica a conta e deve manter sua identidade em atualizações de chave.
const cobranca = {
customer: order.asaasCustomerId,
billingType: 'PIX',
value: order.totalCents / 100, // Asaas usa reais neste campo
dueDate: order.dueDate, // AAAA-MM-DD
...(ref ? { externalReference: ref } : {}),
};
// Envie pela integração Asaas do seu servidor, no ambiente correto.Para assinatura, inclua externalReference no payload de assinatura e confira sua presença na cobrança paga. PAYMENT_CONFIRMED e PAYMENT_RECEIVED podem representar a mesma cobrança; a idempotência evita comissão dupla. A API de cobrança e seu desconto não criam um cupom exclusivo de afiliado.
A chave de homologação com prefixo $aact_hmlg seleciona sandbox nas consultas do adapter. Seu próprio backend também precisa apontar para sandbox. Reembolso solicitado não é reembolso concluído: confirme estado DONE, inclusive para parcial. A fila do Asaas pode exigir reativação depois de falhas; acompanhe-a no painel do fornecedor. Criar cobrança, sandbox e eventos aceitos.
AbacatePay#
Conecte por API key e cadastre a URL inteira retornada, inclusive ?webhookSecret=…. A ingestão exige esse segredo e a verificação de assinatura prevista pelo adapter. Trate a URL como credencial. Autenticar a chave não comprova por si só a identidade externa da loja.
// Mescle no payload válido de checkout/assinatura da sua API AbacatePay.
const atribuicao = ref ? { externalId: ref } : {};
// Preserve o token inteiro. Não troque por externalReference ou metadata.Confirme externalId no recurso pago e no evento. O adapter trata checkout.completed, transparent.completed, subscription.completed e subscription.renewed, além de refund, disputa aberta e cancelamento. Os detalhes do payload completo dependem da API/produto contratado; o fragmento acima não cria cobrança sozinho.
A reconciliação é limitada pelos campos que a API de listagem fornece: pode faltar assinatura, ordinal, método de pagamento ou valor de refund parcial. Não substitua o webhook por suposições sobre esses campos. O desfecho de disputa não é recomposto automaticamente. Veja criação de checkout, eventos de checkout e o contrato separado de cupom.
Pagar.me Core v5: suporte e limites#
Existe adaptador Core v5 para cobranças, créditos, assinatura, cancelamento e reversões. APIs antigas não estão cobertas. Conecte a chave do ambiente correto e copie a URL completa de webhook; o segredo da URL é separado da chave da API.
const metadata = {
...(ref ? { aff_ref: ref } : {}),
// Somente numa compra separada de créditos de uso:
...(order.isCredits ? { aff_revenue_type: 'credits' } : {}),
};O corpo público do webhook fornece o hook_id. O IndicaSaaS consulta esse hook pela API da conta para comprovar identidade, cobrança, valor pago e metadata. Metadados contraditórios entre pedido, invoice, assinatura ou charge pedem nova tentativa; não se escolhe uma referência arbitrária. Configure charge.paid, charge.refunded, charge.partial_canceled, charge.chargedback, charge.updated e subscription.canceled.
chargeback.received ainda não tem processamento financeiro completo. Ele gera erro explícito para conferência e pode impedir o avanço da reconciliação da janela. Não anuncie cobertura integral de chargeback ou disputa ganha. Confira a lista oficial de eventos e os limites da integração.Não há cupom de indicação habilitado no Pagar.me. Cliente novo não é inferido por metadata inventada. Regras de primeiras cobranças precisam do ciclo comprovado pela API; múltiplas charges numa invoice exigem conferência. Dois refunds parciais, conta, retenção de hooks e permissões precisam ser homologados no ambiente contratado. Testes locais não provam essa homologação.
Cupons: desconto e atribuição são contratos diferentes#
| Gateway | Desconto | Atribuição sem clique |
|---|---|---|
| Stripe | Criação explícita ou verificação de Promotion Code existente. | Sim, quando confirmado e devolvido no pagamento da conexão configurada. |
| AbacatePay | Criação explícita pelo anunciante, percentual inteiro ou fixo BRL. | Somente pelo backend do checkout próprio com o contrato abaixo. Digitação apenas no checkout hospedado não basta. |
| Mercado Pago / Asaas / Pagar.me | Eventuais descontos do gateway pertencem à sua cobrança. | Sem suporte local de cupom de indicação. Transporte o ref do link. |
Escolha conexão, modo e desconto no programa. Reservar o nome não aplica desconto: o código é imutável, com reserva permanente; somente a confirmação no gateway habilita o uso prometido. Na integração Stripe, respeite couponUsable=true. A validade e a duração do desconto são diferentes da janela de clique e da recorrência da comissão.
Em programas Stripe com autoatendimento habilitado, o afiliado pode solicitar o cupom conforme as regras expostas pelo programa, inclusive a modalidade de primeira mensalidade quando disponível. Não estenda essa promessa à compra avulsa ou a outros gateways. Desativar cupons no IndicaSaaS interrompe emissão/atribuição locais; não revoga automaticamente um desconto já criado no gateway.
AbacatePay: cupom confirmado no checkout próprio
O backend do SaaS valida o código, aplica o desconto e calcula o total líquido. Só então chama o SDK com sua chave secreta. O exemplo usa dados fictícios; substitua pelos dados do pedido já conferidos no servidor.
POST /sdk/v1/coupon/attribution HTTP/1.1
Host: indicasaas.com
Authorization: Bearer SUA_CHAVE_SDK_NO_SERVIDOR
Content-Type: application/json
{
"programId": "SEU_PROGRAM_ID",
"gatewayConnectionId": "SUA_CONEXAO_ABACATEPAY",
"code": "AFILIADO10",
"gatewayCustomerId": "cust_EXEMPLO",
"amountPaidCents": 9000,
"couponApplied": true
}A resposta traz ref: 'afc1.…', attributionSource: 'merchant_checkout' e paymentRequired: true. Persista esse ref no pedido e envie-o inteiro no externalId; reutilize-o ao retentar o mesmo pedido. Não o passe ao afs.js e não crie clique artificial. Ele ocupa o lugar do ref de clique naquele pagamento.
A rota não aplica desconto, não cria cobrança, não incrementa resgate remoto e não cria comissão. O pagamento precisa comprovar a mesma conexão, cliente, BRL, valor e prazo. couponApplied=true é declaração do backend, nunca um campo confiável do navegador. A lista de cupons permitidos num checkout hospedado não comprova qual foi resgatado. As flags de capacidade nativa continuam conservadoras; não confunda desconto confirmado com atribuição automática do gateway.
Afiliação automática, manual e widget#
No programa, autoApproveAffiliates decide se uma adesão elegível fica ativa ou pendente. Na aprovação manual, o anunciante decide na área de afiliados. Pendente/rejeitado não equivale a afiliado liberado para comissão. A aprovação não instala tracking nem cria uma venda.
O widget “Indique e ganhe” atende o afiliado que divulga seu SaaS. O tracking atende o comprador indicado. Você pode instalar a captura sem exibir widget; renderizar o widget não identifica automaticamente todos os compradores.
Para embarcar a adesão/link, seu backend chama o SDK autenticado com a identidade do usuário logado e entrega ao frontend apenas o link e os dados públicos necessários. Nunca exponha Bearer sk_…. O SDK tem seus próprios contratos de afiliação e requisitos de aprovação; não use identidade livremente informada no browser.
Clientes indicados são derivados de conversões atribuídas. Esta integração não cria uma lista de leads, cadastros ou usuários logados antes do pagamento. Se precisa medir esses passos, use o analytics do seu SaaS e mantenha essa medição separada da atribuição financeira.
Webhook, renovação, reembolso e disputa#
Cadastre a URL e os eventos indicados na página do gateway. URLs com token/secret não devem ir para logs públicos, prints ou frontend de compradores. Stripe usa assinatura, Mercado Pago usa manifesto HMAC e consulta do recurso; Asaas usa token; AbacatePay combina segredo e HMAC; Pagar.me consulta o hook autenticado depois de validar o segredo de roteamento. “Webhook assinado” não descreve da mesma forma todos eles.
O recebimento grava o evento para processamento em fila. HTTP 200/aceite indica ingestão ou reconhecimento do evento, não garante comissão. Se houver falha técnica, o fluxo permite retry e diagnóstico; não altere payload, valor ou ref para forçar sucesso. No Asaas, confirme HTTP 200 para a ingestão durável e monitore a fila do fornecedor.
| Caso | Comportamento |
|---|---|
| Renovação | Usa referência nativa ou vínculo da assinatura já atribuído na mesma conexão. A expiração do clique não apaga automaticamente esse vínculo; as regras do programa continuam valendo. |
| Recompra sem assinatura | Recuperação pelo cliente exige ID autenticado, venda anterior atribuída, mesma conexão, uma única afiliação histórica e janela válida. E-mail isolado não atribui. |
| Primeira compra / primeiras N | Conta ciclo real do gateway, não quantidade de eventos/comissões. Ciclo desconhecido pode impedir comissão. |
| Apenas clientes novos | Exige prova positiva; histórico ausente não prova novidade. Stripe aceita aff_new definido pelo backend. Não presuma o mesmo campo nos demais. |
| Reembolso | Gera reversão vinculada ao pagamento. Parcial precisa de valor confirmado; replay não deve debitar duas vezes. |
| Disputa | Debita a comissão conforme o evento suportado. Vitória identificada pode recompor a parcela devida em contratos que a suportam, sem apagar refunds. MP, AbacatePay e Pagar.me têm limites descritos acima. |
| Cancelamento | Preserva comissões passadas e impede cobranças posteriores à data efetiva conforme as regras; não equivale a reembolso. |
Comissões ficam retidas pelo prazo do programa antes de liberar. Os padrões são 120 dias para cartão e 7 para Pix; confira a configuração e o método comprovado do pagamento. Comissão disponível não significa Pix enviado. Registre o repasse depois do pagamento ao afiliado; se apenas o comprovante falhar, não repita o pagamento.
Validar sem cobrança real#
| Gateway | Preparação |
|---|---|
| Stripe | Sandbox/test keys, produtos e conta Connect do mesmo modo, signing secret correspondente. Use apenas meios de pagamento de teste da documentação oficial. |
| Mercado Pago | Contas comprador/vendedor e credenciais de teste do produto integrado. Siga o fluxo oficial de teste; não misture dados de produção. |
| Asaas | Conta/chave sandbox e base sandbox também no seu backend. Guia oficial. |
| AbacatePay | Chave e recurso em Dev mode, simulação prevista para a API que você utiliza. Confira devMode no recurso; guia oficial de sandbox. |
| Pagar.me | Chave sk_test_* na Core v5 e dados de teste; nunca substitua por uma chave live para “ver se funciona”. Autenticação e ambientes. |
- Abra um perfil limpo de teste. Use um afiliado de teste diferente do comprador; autocompra pode ser inelegível. Limpe apenas o storage do site de teste, nunca todos os dados do usuário.
- Entre pelo link gerado. Confira o redirect, a query
ref=afs1.…e o script carregado sem erro. UmHEAD, preview de link ou request técnico não substitui essa navegação. - Confira a seleção. No console,
AFS.getRef('SEU_PROGRAM_ID')deve devolver o ref elegível. Navegue, faça login, recarregue e atravesse os domínios reais do fluxo. - Confira o pedido no backend. O ref selecionado deve chegar intacto e persistido antes de criar a cobrança. O pedido deve ter total, cliente, produto e ambiente determinados no servidor.
- Simule um pagamento positivo em BRL. Use os mecanismos oficiais de sandbox. Confira no objeto do gateway a referência e os IDs do pedido/pagamento; retorno à página de sucesso não é recibo financeiro.
- Confira o evento e a conversão. No diagnóstico do IndicaSaaS, localize a conexão e o resultado. Abra vendas/comissões e confira afiliação, valor pago, taxa, retenção e origem. Evento recebido sem venda exige investigação.
- Exercite a repetição e a reversão. Reentregue o mesmo evento de teste pelo gateway e confirme uma única comissão. Simule refund, renovação e cancelamento quando aplicáveis.
Acrescente casos negativos: sem ref (orgânico), ref vencido/adulterado/de outro programa, afiliação pendente, moeda diferente, valor zero, cliente antigo quando a regra exige novo e dois afiliados na política de primeiro/último clique. Para cupom Stripe, teste sem clicar no link; para AbacatePay, teste o contrato autenticado do checkout próprio.
Guarde evidência mínima e sanitizada: ambiente, IDs fictícios de teste, data, etapa, resultado esperado e observado. Não salve chaves, URL secreta ou payload integral de comprador. Uma rodada local com mocks prova código; sandbox prova o caminho naquele ambiente; nenhum deles comprova venda real em produção.
Diagnóstico: onde a indicação parou?#
Cliques observados e cliques validados
Observados são registros de navegação no redirect. Validados pelo filtro de borda são o subconjunto marcado is_trusted=1. is_trusted=0 significa sem essa validação: pode incluir teste técnico, automação ou visita sem sinal suficiente. Nem um estado nem o outro certifica uma pessoa humana ou única.
observedClicks informa observados; clicks mantém os aprovados pelo filtro; unverifiedClicks é a diferença. EPC e taxa de conversão usam o contrato de cliques validados existente. A lista de navegações e esses indicadores podem ter totais diferentes, inclusive por filtro/período/limite da tela.
É possível ter cliques observados, zero validados e zero conversões. Isso não prova que o script falhou, nem prova que foi instalado. Webhooks antigos também não comprovam venda indicada atual. O checklist só conclui Validar a primeira venda atribuída quando existe conversão; sua indicação parcial de cliques é uma etapa anterior.
Cadastro gratuito ou meses com desconto de 100%: enquanto o valor pago for zero, não nasce uma conversão financeira nem um cliente indicado nessa lista. No Stripe, o checkout pode estabelecer o vínculo da assinatura; o motor também pode recuperar um cupom confirmado de uma invoice inicial gratuita quando comprova a mesma assinatura e cliente. A cobrança paga seguinte ainda precisa cumprir atribuição, ciclo e regras. Gratuidade não prova falha geral de tracking nem garante comissão futura.
Recebimento e atribuição
Em Ferramentas → Gateways, o diagnóstico mostra os últimos 7 dias, com última reconciliação e motivos por conexão. Pela API, GET /api/v1/advertiser/diagnostics usa a sessão do anunciante, não a chave do SDK. Um histórico anterior à janela pode explicar checklist de webhook concluído e diagnóstico recente vazio.
Na API do afiliado, GET /api/v1/me/affiliate/clicks preserva o padrão de registros validados. O parâmetro opcional includeUnverified=1 inclui observados sem validação; confira isTrusted por linha. Isso não altera os indicadores financeiros nem autoriza atribuição.
| Estado / motivo | Interpretação e ação |
|---|---|
received | Recebido, aguardando processamento. Confira fila e horário antes de reenviar. |
error | Falha técnica recuperável: credencial, consulta ou gravação. Corrija a causa e acompanhe retry/reconciliação. |
processed | Processamento concluído ou registro legado; leia o outcome. Não significa comissão. |
attributed | Resultado atribuído; confira commission_created, estorno ou recomposição e o lançamento financeiro correspondente. |
unattributed / no_valid_reference | Compra orgânica ou referência sem validade. Compare ref no navegador, pedido e recurso autenticado. |
ineligible | Referência/relação não basta para comissionar: confira a regra e o reason. |
ignored | Evento sem efeito financeiro ou já contabilizado. Leia o motivo antes de concluir que há falha. |
subscription_attribution_seeded | Assinatura identificada; ainda aguarda pagamento elegível. |
unknown_customer_status / existing_customer | Novidade não comprovada ou cliente antigo. Revise a regra e a prova do backend. |
unknown_billing_cycle / recurrence_limit | Ciclo desconhecido ou limite atingido. Confira invoice/ciclo na conta do gateway. |
unsupported_currency / zero_or_invalid_payment | Moeda não BRL ou valor pago sem elegibilidade. Não substitua por valor nominal. |
self_referral / self_referral_identity | Autocompra identificada. Não altere e-mail para contornar a regra. |
duplicate_payment | Pagamento já contabilizado; procure a conversão existente. |
Checklist de investigação rápida
- O ref sumiu antes do login: confira redirect HTTP, router, CSP, bloqueador e ordem de carregamento.
- Existe na landing, mas não no app: compare protocolo, host e porta; transporte entre origens explicitamente.
- Existe no browser, mas não na cobrança: confira parser do backend, persistência do pedido, campo correto e metadata de assinatura.
- Gateway conectado, sem eventos: confira conta/modo, URL completa, tópicos, assinatura, resposta HTTP e fila no gateway.
- Evento presente, sem comissão: leia outcome/reason, pagamento confirmado, BRL, prazo, afiliação, cliente novo, ciclo e autocompra.
- Comissão retida: confira retenção e repasse; não é falha de tracking.
Perguntas frequentes#
Instalei o script. Por que o checklist continua pendente?
O visitante se cadastrou. Ele já aparece como cliente indicado?
Tenho cliques na lista e zero no indicador. É perda de venda?
Preciso usar o widget para rastrear a compra?
Posso copiar só o código do afiliado ou enviar o e-mail?
O prazo venceu antes de eu receber o webhook?
Um cupom de primeira mensalidade grátis cria comissão?
O mesmo usuário comprou em outro dispositivo. O IndicaSaaS descobre?
Posso simular a venda enviando JSON direto ao webhook?
Troquei a chave ou reconectei. Perco o histórico?
Referências e contratos#
Os nomes de campos, estados e limites acima correspondem à implementação do IndicaSaaS. Exemplos com order, IDs e rotas do seu SaaS são pontos de adaptação. Documentação do fornecedor prova o contrato externo; não comprova que sua conta foi homologada.
- Referência interativa da API e OpenAPI: parâmetros, autenticação e respostas dos endpoints IndicaSaaS.
- Atribuição, SDK/widget, webhooks e comissões e repasses.
- Metadata Stripe e testes de Billing: objetos distintos e ciclos de assinatura.
- Hook Pagar.me v5: consulta autenticada que sustenta o adapter.