Descrição
O evento conversion rastreia quando uma venda é concretizada através de um afiliado. Este é o evento mais importante do sistema pois:
Gera comissões para os afiliados
Calcula o ROI do programa de afiliados
Determina os pagamentos (payouts)
Mede o sucesso real dos afiliados
Vincula receita à origem do tráfego
Conversões devem ter order_id único e order_value obrigatório . O
sistema automaticamente calcula a comissão baseado nas configurações da
campanha.
Quando Usar
Pagamento Confirmado Quando um pagamento é confirmado pelo gateway
Webhook Recebido Ao receber webhook de Stripe, Mercado Pago, etc
Assinatura Ativada Quando uma assinatura é criada/ativada
Pagamento Recebido Ao confirmar recebimento do pagamento
NUNCA envie conversões antes da confirmação do pagamento. Aguarde sempre o
webhook ou confirmação do gateway.
Propriedades Obrigatórias
ID único do pedido/transação (usado para idempotência ) Exemplo:
"ORDER-12345", "ch_1234abcd", "TXN-2024-001" Conversões com mesmo order_id serão ignoradas (deduplicação
automática)
Valor total da venda em reais (deve ser positivo ) Exemplo: 99.90,
299.00, 1499.99 A comissão será calculada automaticamente: Percentage → (order_value * commission_value / 100) | Fixed → commission_value fixo
Propriedades Opcionais
Nome do produto ou plano vendido Exemplo: "Plano Premium Anual",
"Produto X"
Nome do cliente que comprou Exemplo: "Maria Santos"
ID do cliente no seu sistema Exemplo: "user_abc123", "cus_1234abcd"
Método de pagamento usado Exemplo: "credit_card", "boleto", "pix",
"stripe"
ID da assinatura (para modelos recorrentes) Exemplo: "sub_abc123",
"subscription_456"
URL da página de checkout/confirmação Exemplo:
"https://seu-site.com/checkout/success"
Data/hora ISO 8601 da conversão Exemplo: "2024-01-15T10:45:00Z"
Exemplos de Implementação
SDK JavaScript - Página de Sucesso
<! DOCTYPE html >
< html >
< head >
< title > Compra Confirmada! </ title >
< script src = "https://cdn.affiliatus.io/latest/affiliatus.min.js" ></ script >
</ head >
< body >
< h1 >< Icon icon = "party-horn" /> Obrigado pela sua compra! </ h1 >
< p > Seu pedido foi confirmado com sucesso. </ p >
< script >
const affiliatus = new Affiliatus ( 'seu-campaign-id' );
// Dados do pedido (vindos do backend via template)
const orderData = {
order_id: '<?= $order->id ?>' ,
order_value: <?= $order -> total ?> ,
product : '<?= $order->product_name ?>' ,
customer_email : '<?= $customer->email ?>' ,
customer_name: '<?= $customer->name ?>' ,
payment_method: '<?= $order->payment_method ?>'
};
// Rastrear conversão
affiliatus . trackConversion ( orderData );
console . log ( 'Conversão rastreada:' , orderData );
</ script >
</ body >
</ html >
Webhook Stripe
const stripe = require ( "stripe" )( process . env . STRIPE_SECRET_KEY );
const express = require ( "express" );
const app = express ();
// Endpoint para webhook do Stripe
app . post (
"/webhook/stripe" ,
express . raw ({ type: "application/json" }),
async ( req , res ) => {
const sig = req . headers [ "stripe-signature" ];
let event ;
try {
// Verificar assinatura do webhook
event = stripe . webhooks . constructEvent (
req . body ,
sig ,
process . env . STRIPE_WEBHOOK_SECRET
);
} catch ( err ) {
console . error ( "Erro ao verificar webhook:" , err . message );
return res . status ( 400 ). send ( `Webhook Error: ${ err . message } ` );
}
// Processar evento de checkout completo
if ( event . type === "checkout.session.completed" ) {
const session = event . data . object ;
// Buscar affiliate_id e session_id dos metadata
const affiliateId = session . metadata . affiliate_id ;
const affiliateSessionId = session . metadata . session_id ;
if ( affiliateId ) {
console . log ( "Rastreando conversão para afiliado:" , affiliateId );
try {
await fetch ( "https://api.affiliatus.io/v1/events" , {
method: "POST" ,
headers: {
"Content-Type" : "application/json" ,
"X-API-Key" : process . env . AFFILIATUS_API_KEY ,
},
body: JSON . stringify ({
events: [
{
event_type: "conversion" ,
campaign_id: process . env . CAMPAIGN_ID ,
affiliate_id: affiliateId ,
session_id: affiliateSessionId ,
properties: {
order_id: session . id ,
order_value: session . amount_total / 100 , // Stripe usa centavos
product: session . metadata . product_name || "Produto" ,
customer_email: session . customer_email ,
customer_id: session . customer ,
payment_method: "stripe" ,
subscription_id: session . subscription ,
url: session . success_url ,
timestamp: new Date (). toISOString (),
},
device_info: {
ip: req . ip ,
user_agent: req . headers [ "user-agent" ],
},
},
],
}),
});
console . log ( "Conversão rastreada com sucesso" );
} catch ( error ) {
console . error ( "Erro ao rastrear conversão:" , error );
}
}
}
res . json ({ received: true });
}
);
app . listen ( 3000 , () => console . log ( "Webhook server running on port 3000" ));
// No checkout, adicione affiliate_id aos metadata
const session = await stripe . checkout . sessions . create ({
payment_method_types: [ "card" ],
line_items: [
{
price: "price_abc123" ,
quantity: 1 ,
},
],
mode: "subscription" ,
success_url: "https://seu-site.com/success" ,
cancel_url: "https://seu-site.com/cancel" ,
metadata: {
affiliate_id: affiliateId , // ← Importante!
session_id: sessionId , // ← Importante!
product_name: "Plano Premium" ,
},
});
Webhook Mercado Pago
from flask import Flask, request, jsonify
import requests
import os
app = Flask( __name__ )
@app.route ( '/webhook/mercadopago' , methods = [ 'POST' ])
def mercadopago_webhook ():
"""Processar notificação de pagamento aprovado do Mercado Pago"""
data = request.json
# Verificar se é notificação de pagamento aprovado
if data.get( 'type' ) == 'payment' and data.get( 'action' ) == 'payment.approved' :
payment_id = data[ 'data' ][ 'id' ]
# Buscar detalhes do pagamento na API do Mercado Pago
payment = get_mercadopago_payment(payment_id)
# Buscar affiliate_id armazenado nos metadata
affiliate_id = payment.get( 'metadata' , {}).get( 'affiliate_id' )
session_id = payment.get( 'metadata' , {}).get( 'session_id' )
if affiliate_id:
# Enviar conversão para Affiliatus
payload = {
'events' : [{
'event_type' : 'conversion' ,
'campaign_id' : os.getenv( 'CAMPAIGN_ID' ),
'affiliate_id' : affiliate_id,
'session_id' : session_id,
'properties' : {
'order_id' : payment[ 'external_reference' ] or str (payment_id),
'order_value' : float (payment[ 'transaction_amount' ]),
'product' : payment[ 'description' ],
'customer_email' : payment[ 'payer' ][ 'email' ],
'customer_name' : payment[ 'payer' ][ 'name' ],
'customer_id' : str (payment[ 'payer' ][ 'id' ]),
'payment_method' : 'mercadopago' ,
'timestamp' : payment[ 'date_approved' ]
},
'device_info' : {
'ip' : request.remote_addr
}
}]
}
response = requests.post(
'https://api.affiliatus.io/v1/events' ,
headers = {
'Content-Type' : 'application/json' ,
'X-API-Key' : os.getenv( 'AFFILIATUS_API_KEY' )
},
json = payload
)
print ( f "Conversão rastreada: { response.json() } " )
return jsonify({ 'status' : 'ok' })
def get_mercadopago_payment ( payment_id ):
"""Buscar detalhes do pagamento no Mercado Pago"""
access_token = os.getenv( 'MERCADOPAGO_ACCESS_TOKEN' )
response = requests.get(
f 'https://api.mercadopago.com/v1/payments/ { payment_id } ' ,
headers = { 'Authorization' : f 'Bearer { access_token } ' }
)
return response.json()
if __name__ == '__main__' :
app.run( port = 3000 )
Webhook PagSeguro
<? php
// Webhook PagSeguro
if ( $_POST [ 'notificationCode' ]) {
$notificationCode = $_POST [ 'notificationCode' ];
// Consultar transação no PagSeguro
$transaction = consultarTransacaoPagSeguro ( $notificationCode );
// Verificar se pagamento foi aprovado (status 3 = Paga)
if ( $transaction [ 'status' ] == 3 ) {
// Buscar affiliate_id armazenado na referência
// Você deve ter salvo isso ao criar o pagamento
$reference = $transaction [ 'reference' ];
list ( $orderId , $affiliateId , $sessionId ) = explode ( '_' , $reference );
if ( $affiliateId ) {
// Preparar dados para Affiliatus
$data = [
'events' => [[
'event_type' => 'conversion' ,
'campaign_id' => $_ENV [ 'CAMPAIGN_ID' ],
'affiliate_id' => $affiliateId ,
'session_id' => $sessionId ,
'properties' => [
'order_id' => $orderId ,
'order_value' => floatval ( $transaction [ 'grossAmount' ]),
'product' => $transaction [ 'items' ][ 0 ][ 'description' ],
'customer_email' => $transaction [ 'sender' ][ 'email' ],
'customer_name' => $transaction [ 'sender' ][ 'name' ],
'payment_method' => 'pagseguro' ,
'timestamp' => date ( 'c' )
],
'device_info' => [
'ip' => $_SERVER [ 'REMOTE_ADDR' ]
]
]]
];
// Enviar para Affiliatus
$ch = curl_init ( 'https://api.affiliatus.io/v1/events' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [
'Content-Type: application/json' ,
'X-API-Key: ' . $_ENV [ 'AFFILIATUS_API_KEY' ]
]);
curl_setopt ( $ch , CURLOPT_POSTFIELDS , json_encode ( $data ));
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
$response = curl_exec ( $ch );
$httpCode = curl_getinfo ( $ch , CURLINFO_HTTP_CODE );
curl_close ( $ch );
if ( $httpCode == 200 ) {
error_log ( "Conversão rastreada com sucesso: $orderId " );
} else {
error_log ( "Erro ao rastrear conversão: $response " );
}
}
}
}
function consultarTransacaoPagSeguro ( $notificationCode ) {
$email = $_ENV [ 'PAGSEGURO_EMAIL' ];
$token = $_ENV [ 'PAGSEGURO_TOKEN' ];
$url = "https://ws.pagseguro.uol.com.br/v3/transactions/notifications/ $notificationCode ?email= $email &token= $token " ;
$ch = curl_init ( $url );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
$xml = curl_exec ( $ch );
curl_close ( $ch );
// Converter XML para array
$transaction = simplexml_load_string ( $xml );
return json_decode ( json_encode ( $transaction ), true );
}
?>
Fluxo de Processamento
Quando uma conversão é recebida, o sistema executa os seguintes passos:
1. Validação
Verifica se campaign, affiliate e domain são válidos e ativos
2. Idempotência
Checa se order_id já existe no banco de dados (evita duplicatas)
3. Limites do Plano
Verifica se o plano do usuário suporta mais conversões no mês
4. Salvar Evento
Registra o evento na tabela events
5. Criar Conversão
Cria registro na tabela conversions com status pending
6. Calcular Comissão
Calcula comissão baseado em commission_type e commission_value
Cálculo de Comissão
O sistema calcula automaticamente a comissão baseado no tipo configurado na campanha:
Comissão Percentual (percentage)
commission_value = (order_value * campaign.commission_value) / 100
Exemplo:
order_value: R$ 100,00
commission_value: 10%
Comissão calculada: R$ 10,00
Comissão Fixa (fixed)
commission_value = campaign.commission_value
Exemplo:
order_value: R$ 100,00
commission_value: R$ 15,00
Comissão calculada: R$ 15,00 (independente do valor)
Status da Conversão
Após criação, a conversão passa pelos seguintes status:
pending
Pendente - Aguardando aprovação manual do merchant
approved
Aprovada - Conversão aprovada, comissão confirmada
rejected
Recusada - Conversão recusada (chargeback, fraude, cancelamento)
paid
Paga - Comissão foi paga ao afiliado
Métricas Geradas
Total de Conversões Número de vendas por afiliado
Receita Gerada Soma de order_value por afiliado
Comissões a Pagar Total de comissões calculadas
Taxa de Conversão Conversões / Visitas (%)
Ticket Médio Receita total / Número de conversões
ROI do Programa Receita gerada vs Comissões pagas
Boas Práticas
SEMPRE use order_id único
Use o ID da transação do seu sistema ou gateway. Nunca gere IDs aleatórios ou reutilize. // <Icon icon="check" color="#db2777" /> BOM - ID do Stripe
order_id : session . id // "cs_1234abcd"
// <Icon icon="check" color="#db2777" /> BOM - ID do seu banco de dados
order_id : `ORDER- ${ order . id } ` // "ORDER-12345"
// <Icon icon="x" /> RUIM - Aleatório
order_id : Math . random (). toString ()
// <Icon icon="x" /> RUIM - Timestamp
order_id : Date . now (). toString ()
Envie APENAS após confirmação
Aguarde webhook ou confirmação do gateway. Nunca envie no clique do botão de compra.// <Icon icon="x" /> ERRADO - No clique do botão
button . addEventListener ( 'click' , () => {
affiliatus . trackConversion ({ /* ... */ });
checkout ();
});
// <Icon icon="check" color="#db2777" /> CORRETO - No webhook após pagamento confirmado
app . post ( '/webhook/payment' , ( req , res ) => {
if ( req . body . status === 'paid' ) {
trackConversion ({ /* ... */ });
}
});
Envie o valor que o cliente realmente pagou (após descontos, mas antes de taxas). // <Icon icon="check" color="#db2777" /> CORRETO - Valor que o cliente pagou
order_value : 99.90 // Após desconto de R$ 10
// <Icon icon="x" /> ERRADO - Valor antes do desconto
order_value : 109.90
// <Icon icon="x" /> ERRADO - Valor após taxas do gateway
order_value : 94.90
Teste em ambiente sandbox
Use webhooks de teste dos gateways e uma campanha de testes antes de produção. Stripe: Use test keys e stripe trigger CLIMercado Pago: Use ambiente de testesPagSeguro: Use sandbox
Implemente retry com idempotência
Troubleshooting
Conversões não aparecem no dashboard
Verifique se order_value é positivo
O valor deve ser maior que zero // <Icon icon="check" color="#db2777" /> Válido
order_value : 99.90
// <Icon icon="x" /> Inválido
order_value : 0
order_value : - 50
Confirme se não é duplicata
Cheque se o order_id já foi usado anteriormente Query no dashboard ou logs do sistema
Valide limites do plano
Veja se não atingiu o limite mensal de conversões Dashboard → Configurações → Plano
Verifique logs de erro
Cheque resposta da API para mensagens de erro const response = await fetch ( /* ... */ );
const data = await response . json ();
if ( ! response . ok ) {
console . error ( 'Erro:' , data );
}
Conversões não geram comissão
Verifique se affiliate está ativo
Status deve ser active, não inactive
Confirme configuração da campanha
commission_type e commission_value devem estar configurados
Aprove manualmente no dashboard
Conversões ficam pending até aprovação manual Dashboard → Conversões →
Ações → Aprovar
Erro “order_id already exists”
Isso significa que você está tentando enviar uma conversão com order_id que já existe.
Soluções:
Se é uma tentativa de reenvio → Está tudo bem, o sistema já registrou
Se é uma nova venda → Use um order_id diferente e único
Próximos Passos