Ícone do site Blog do Publer

Desenvolvido internamente: como nosso programa de afiliados gerou US$ 1 milhão em receita

Oferecer um programa de afiliados é um dos canais de marketing mais eficazes para expandir seu negócio de SaaS (Software as a Service), principalmente nos estágios iniciais.

A ideia é simples: seus usuários atuais (ou afiliados) indicam seu produto para o público deles por meio de um link exclusivo e, em troca, ganham uma pequena comissão por cada novo cliente pagante que trouxerem.

Benefícios:

As comissões variam, mas, no caso do SaaS, geralmente são recorrentes e ficam em torno de 30% do valor pago pelo cliente indicado.

Muitos provedores terceirizados, como Rewardful, Tolt, FirstPromoter, PartnerStack e Dub Partners, permitem que você lance seu programa de afiliados na hora, mas se você quer criar um programa personalizado internamente, continue lendo.

Por que criar o programa de afiliados internamente

Embora as soluções sem código sejam ótimas, elas geralmente vêm com taxas e muitas limitações.

Um dos principais fatores que nos levou a criar nosso programa de afiliados do zero foi a impossibilidade de adaptar as soluções de terceiros às nossas necessidades.

Vantagens de criar seu próprio programa de afiliados

Contras

As desvantagens não são tão ruins quanto parecem, então vamos lá.

Criando links de afiliados

Pessoalmente, não curto links de afiliados que parecem muito genéricos e/ou pertencem a um (sub)domínio diferente do seu.

Em vez disso, eu queria que nossos links de afiliados parecessem parte do nosso site e pudessem ser personalizados pelo usuário, assim como alguém pode escolher seu nome de usuário no Instagram, no X ou em qualquer outra rede social.

Nomes de usuário específicos que usamos como rotas no nosso site, como /plans ou /blog, são restritos, mas, fora isso, os afiliados podem usar toda a criatividade que quiserem, e o nome de usuário pode ser alterado a qualquer momento.

Alguém escolheu o nome de usuário freetrial -> publer.com/freetrial

Nosso objetivo é transformar nossos clientes atuais em embaixadores da marca, em vez de oferecer mais um programa de afiliados; por isso, tornamos obrigatório ter uma conta no Publer.

Fomos um pouco além e agora aprovamos manualmente o acesso de afiliados para usuários do plano gratuito.

Vários links de afiliado

Compartilhar a página inicial do produto geralmente funciona. Mas, se você tem uma comunidade de nicho, pode ser mais eficaz compartilhar uma página de destino específica ou um artigo que aborde diretamente os pontos fracos da sua comunidade.

Apesar de usarmos tecnologias diferentes, como Framer, Vercel e WordPress, investimos uma quantia significativa para garantir que todos os links do nosso site fossem links de afiliados.

Os afiliados podem compartilhar qualquer link do nosso site como um link de afiliado simplesmente acrescentando /USERNAME no final da URL

Aqui tá o código do arquivo 404.php do WordPress que identifica links do blog que terminam com /USERNAME, verifica se o USERNAME é válido por meio de uma solicitação cURL e redireciona o visitante de volta pro link original, passando o parâmetro ?referral=ID na URL.

<?php
  $url = rtrim(strtok($_SERVER["REQUEST_URI"], '?'), '/');
  $LINK_WITHOUT_AMBASSADOR = "https://publer.com/blog" . substr($url, 0, strrpos($url, '/'));
  
  // Don't honor clicks from ads
  if (!$_GET['gclid'] && !$_GET['gad_source'] && !$_GET['via']) {
    preg_match("/[^\/]+$/", $url, $matches);
    $AMBASSADOR = $matches[0]; 
    $referrer = isset($_GET['referrer']) ? $_GET['referrer'] : $_SERVER['HTTP_REFERER'];
    
    // The data to send to the API
    $postData = array(
      'username' => $AMBASSADOR,
      'link'     => 'https://publer.com/blog' . $LINK_WITHOUT_AMBASSADOR,
      'referrer' => $referrer
    );

    // Setup cURL
    $ch = curl_init('https://app.publer.com/ambassador');
    curl_setopt_array($ch, array(
      CURLOPT_POST           => TRUE,
      CURLOPT_RETURNTRANSFER => TRUE,
      CURLOPT_HTTPHEADER     => array('Content-Type: application/json'),
      CURLOPT_POSTFIELDS     => json_encode($postData)
    ));
    
    $response = curl_exec($ch);
    
    if ($response) {
      $data = json_decode($response, TRUE);
      $ambassador_id = $data['id'];
      header("Location: $LINK_WITHOUT_AMBASSADOR/?referral=$ambassador_id");
      exit();
    } else {
      header("Location: $LINK_WITHOUT_AMBASSADOR/");
      exit(); 
    }
  } else {
    header("Location: $LINK_WITHOUT_AMBASSADOR/");
    exit();
  }

  get_header(); 
?>
    
<div>
  <h3>Oops! That page can’t be found.</h3>
  <p>Try searching or go to the <a href="/blog">homepage</a></p> 
</div>

No arquivo header.php, o ID de referência, se houver, é armazenado como um cookie, que depois é usado para fins de rastreamento.

<?php
  // Don't honor clicks from ads
  if ($_GET['referral'] && !$_GET['gclid'] && !$_GET['gad_source'] && !$_GET['via']) {
    $expiration = 30 * 24 * 60 * 60; // 30 days
    echo "<script>document.cookie = 'referral=" . $_GET['referral'] . ";path=/;domain=.publer.com;max-age=$expiration;samesite=none;secure=true';</script>";
    echo "<script>window.history.replaceState({}, document.title, window.location.href.split('?')[0]);</script>";
  }
?>

As soluções para as rotas do Vercel e do Framer são um pouco diferentes e/ou mais complexas, mas a lógica por trás delas continua a mesma. Se quiser os trechos de código para essas rotas, é só me mandar uma mensagem.

Rastreamento de links de afiliados

A maneira mais fácil de rastrear indicações é por meio de cookies com validade limitada, que geralmente duram 30 ou 60 dias.

Cookies são pequenos arquivos de texto que os sites enviam para o seu navegador para armazenar informações sobre a sua visita.

A gente armazena o cookie de indicação no nível do domínio .domain para que o subdomínio do app possa acessá-lo.

Sempre que alguém se cadastra na nossa plataforma, a gente executa esse método auxiliar em Ruby pra verificar se a pessoa veio de um link de afiliado, ou seja, se o cookie de referência existe.

def check_referral(user)
  referral = cookies[:referral]
  return if referral.blank?

  # Mark referred user as such and offer a sign up commission
  Ambassador::CommissionsService.new(user, referral).registration_commission
  
  cookies.delete(:referral)
end

Rastreamento de instalações de aplicativos móveis

Os cookies são ótimos para rastreamento em plataformas da web, mas a coisa fica complicada se você tiver um aplicativo móvel.

Não dá pra passar parâmetros de rastreamento personalizados pro Google Play ou pra Apple App Store, então essa foi a nossa solução alternativa:

Embora pareça boa no papel, essa solução alternativa não é infalível. O visitante pode copiar outra coisa antes de se cadastrar pelo nosso app móvel, ou o sistema operacional pode não permitir que se copie para a área de transferência sem permissão.

Referências perdidas

É normal que alguém clique em um link de afiliado pelo celular, mas decida se informar melhor e se cadastrar pela web.

Os cookies não funcionam em diferentes dispositivos nem em diferentes navegadores

Para evitar tickets desnecessários no suporte ao cliente e alterações manuais no banco de dados, permitimos que os usuários indiquem, se quiserem, se alguém os indicou para o Publer.

Métricas monitoradas

Embora as análises não tenham sido nosso foco principal, a gente acompanha as seguintes métricas essenciais:

O painel de afiliados foi criado lá em 2016 usando jQuery, Chartist, Bootstrap e DataTables. Nada de mais, mas ainda dá conta do recado 10 anos depois, sem precisar de manutenção.

Comissões e pagamentos

Para cada pagamento feito por um cliente indicado, o afiliado tem direito a uma comissão de aproximadamente 30%.

Para quem paga US$ 10 por mês, o afiliado ganharia US$ 3 por mês

RATE = 0.3 # 30% commission

def payment_commission(payment, next_bill_date)
  monthly_price = payment.unit_price.to_f
  commission = (monthly_price * RATE).round(2)
  
  Ambassador::Commission.create(created_at: Time.current, commission: commission, user: @user, ambassador: @ambassador, payment: payment)
  
  Notification.create(reason: 'commission_pending', user_id: @ambassador.user.id.to_s)
end

As comissões ganhas geralmente ficam retidas por 30 dias para dar tempo de lidar com eventuais pedidos de reembolso que possam surgir.

Essa é uma tarefa diária que marca automaticamente as comissões como “aprovadas” ou “recusadas”, dependendo do status do pagamento, e depois que os 30 dias se passam.

scheduler.cron '7 18 * * *' do # Every day at 18:07 UTC
  Ambassador::CommissionsTask.new.execute
end

module Ambassador
  class CommissionsTask
    def execute
      pipeline = [
        { '$match': { state: 'pending', created_at: { '$lte': 30.days.ago.beginning_of_day } } },
        { '$project': { _id: 1 } }
      ]

      ids = Ambassador::Commission.collection.aggregate(pipeline).map { |oid| oid.as_json['_id']['$oid'] }
      ids.each_slice(BATCH_SIZE) do |bulk|
        # Mark as 'approved' or 'declined' depending on the payment status
        Ambassador::CommissionsWorker.perform_async(bulk)
      end
    end
  end
end

As comissões aprovadas são então agrupadas e pagas assim que o valor total atingir um limite específico, geralmente US$ 20 ou US$ 50.

Acompanhamento de comissões

Os afiliados podem ver em tempo real todas as comissões ganhas e todos os clientes pagantes que trouxeram, além de alguns detalhes sobre o plano deles.

Pagamento aos afiliados

Essa é provavelmente a maior desvantagem quando se trata de administrar um programa de afiliados interno:

No entanto, usar serviços online como o FirstBase e o Online Taxman facilita muito a constituição de uma empresa nos Estados Unidos e a manutenção de sua situação legal.

👉 Como abrir uma empresa e abrir uma conta bancária nos Estados Unidos

Os pagamentos podem ser totalmente automatizados mensalmente ou a cada duas semanas.

scheduler.cron '7 13 1 * *' # First day of the month at 13:07 UTC
  Ambassador::PayoutsTask.new.execute
end

Pagamentos via PayPal

O PayPal é o método mais simples, já que o afiliado só precisa fornecer seu endereço de e-mail do PayPal.

module Ambassador
  class PaypalPayoutsWorker
    include Sidekiq::Worker

    def perform(params)
      commission_ids = []
      payout_items = params.map do |item|
        ambassador = Ambassador::Detail.find(item['ambassador'])
        receiver = ambassador.payout_method&.[](:email)
        next unless receiver

        commission_ids.concat(item['commissions'])

        {
          recipient_type: 'EMAIL',
          amount: {
            value: item['total'].round(2),
            currency: 'USD'
          },
          note: <<~NOTE,
            Thank you for being an Ambassador of #PublerNation.
            Login to your dashboard: <a href='https://app.publer.com/ambassador'>https://app.publer.com/ambassador</a><br>
          NOTE
          sender_item_id: ambassador.username,
          receiver: receiver
        }
      end

      payout_params = {
        sender_batch_header: {
          sender_batch_id: SecureRandom.hex(8),
          email_subject: 'Your payment from the Publer Ambassador program!'
        },
        items: payout_items.compact
      }

      payout = PayPal::SDK::REST::DataTypes::Payout.new(payout_params)
      Ambassador::Commission.in(id: commission_ids.map { |id| BSON::ObjectId(id) }).update_all(state: 'sending')
      payout.create
    rescue => ex
      Sentry.capture_exception(ex, extra: { params: params })
    end
  end
end

A única desvantagem do PayPal é que você precisa configurar um endpoint e ficar de olho nos webhooks do PayPal, que te avisam se os pagamentos deram certo ou não.

Stripe Connect

O Stripe é outra excelente alternativa para quem não pode ou prefere não usar o PayPal.

Diferente do PayPal, os afiliados que usam o Stripe precisam passar por algumas etapas de cadastro para fornecer suas informações pessoais e os dados da conta bancária.

Depois que o afiliado concluir a integração, o pagamento das comissões aprovadas é um processo de apenas uma etapa.

module Ambassador
  class StripePayoutsWorker
    include Sidekiq::Worker
    include AmbassadorHelpers

    def perform(payout)
      ambassador = Ambassador::Detail.find(payout['ambassador'])
      commissions = Ambassador::Commission.where(_id: { '$in': payout['commissions'] })
      commissions.update_all(state: 'sending')

      params = { currency: 'usd',
                 amount: (payout['total'] * 100).to_i,
                 destination: ambassador.payout_method[:id],
                 description: 'Thank you for being an Ambassador of #PublerNation!' }
      
      transfer = Stripe::Transfer.create(params)

      notify_payout(payout['total'], transfer.id, ambassador, :stripe)
    rescue => ex
      commissions&.update_all(state: 'approved')
      Sentry.capture_exception(ex, extra: { payout: payout })
    end
  end
end

Enquanto os pagamentos do PayPal são enviados pelo próprio PayPal, os pagamentos do Stripe são depositados diretamente na conta bancária vinculada do afiliado.

Incentivando o programa de afiliados

Se você acha que basta criar um programa de afiliados para que influenciadores e clientes comecem a promover seu produto, você vai se decepcionar.

Veja abaixo alguns dos incentivos personalizados que implementamos para o nosso programa de afiliados.

Taxas de comissão progressivas

As taxas de comissão padrão são as seguintes:

E, com base nas vendas que o afiliado fez no mês anterior, as taxas de comissão podem aumentar:

Para os grandes influenciadores, oferecemos taxas de comissão fixas, mas mais altas

Acesso antecipado a novos recursos

Quem promove ativamente o Publer, independentemente do sucesso que tenha, pode testar novos recursos antes de todo mundo, dar feedback e, assim, passar a fazer parte diretamente do processo de tomada de decisão.

Frequência de pagamento

Assim como no acesso antecipado a novos recursos, os afiliados mais dedicados recebem seus pagamentos a cada duas semanas, em vez de mensalmente.

Prêmios

Reservamos um orçamento mensal de US$ 175 para os três melhores afiliados (com base na receita mensal gerada por novos clientes pagantes).

Janela pop-up de depoimentos

As pessoas compram de pessoas (ou marcas) que conhecem, de quem gostam e em quem confiam, então queríamos que nossos afiliados fizessem parte do nosso site — literalmente.

Dica: as janelas pop-up aceitam Markdown, então os afiliados podem colocar um link para o site da empresa ou pessoal no depoimento deles, pra aumentar um pouco mais o reconhecimento da marca.

Compartilhamento de descontos

Por que alguém se inscreveria em um serviço usando um link de afiliado em vez de simplesmente acessar o site diretamente? Descontos são sempre um ótimo motivo!

Criando uma comunidade em torno do programa de afiliados

Por fim, você precisa estabelecer canais de comunicação entre você e seus afiliados.

E é sempre uma ótima ideia preparar imagens prontas, tutoriais e posts.

Dicas de SEO

Ter links de afiliados como parte do seu domínio principal é uma vantagem significativa para o SEO, mas também pode acabar sendo contraproducente para você.

Indexação

Você não quer que links de afiliados apareçam nos resultados de busca do Google sempre que alguém pesquisar pelo seu produto, né?

Você precisa garantir que os mecanismos de busca não indexem os links de afiliados.

<Header image={image} description={description} title={title} url={url}>
  <meta name="robots" content="noindex" />
</Header>

URL canônica

Você também não quer ter vários links para a mesma página de destino, pois isso levaria à canibalização de palavras-chave (links competindo entre si pelo tráfego).

Em vez disso, a URL canônica deve ser sempre aquela que não está vinculada a outras.

<link rel="canonical" href="https://publer.com"> ✅
<link rel="canonical" href="https://publer.com/affiliate"> ❌
<link rel="canonical" href="https://publer.com/anotherone"> ❌
<link rel="canonical" href="https://publer.com/random123"> ❌

Limpeza de URLs

Depois de salvar o cookie de afiliado no navegador, é sempre uma boa ideia limpar os parâmetros de rastreamento da URL na barra de endereços.

removeURLParameter('username');
removeURLParameter('referrer');

const removeURLParameter = useCallback(
  (parameter) => {
    let params = new URLSearchParams(window.location.search);
    params.delete(parameter);

    const url = constructURL(params.toString());
    window.history.replaceState({}, document.title, url);
  },
  [constructURL]
);

Caso contrário, os visitantes podem acabar compartilhando o link de afiliado sem querer e gerar comissões desnecessárias pra você, a menos que você esteja oferecendo um programa de afiliados em vários níveis (uma estrutura de comissão em que o afiliado ganha não só com as vendas diretas, mas também com as vendas geradas por essas vendas diretas).

Prevenção de anúncios

Em quase todos os programas de afiliados, o primeiro termo que você precisa aceitar é o seguinte:

Ao continuar, você concorda em não usar anúncios pagos para promover seus links de afiliado, nosso site ou as palavras-chave da nossa marca

Isso porque você não quer que os afiliados façam lances por tráfego de palavras-chave para o seu site ou marca.

Você também não quer que anúncios aleatórios de afiliados apareçam no Google quando alguém pesquisar pelo seu produto. Você sai perdendo tanto com os lances em palavras-chave quanto com as comissões dos afiliados.

Mas regras são feitas para serem quebradas, então a única maneira de impedir totalmente que os afiliados veiculem anúncios é não reconhecer os cliques e as comissões desses anúncios.

const router = useRouter();
const { ambassador, referrer, gclid, gad_source, via } = router.query;

const isAffiliateMarketingNetwork = (referrer) => {
  return /shareasale\.com|googleadservices\.com|google\.com/.test(referrer || '');
};

useEffect(() => {
  // Don't honor clicks from ads
  router.push({
    pathname: '/',
    query:
      gclid || gad_source || via || isAffiliateMarketingNetwork(referrer)
        ? {}
        : { username: ambassador, referrer },
  });
}, [ambassador, referrer, gclid, gad_source, via, router]);

Transformar o programa de afiliados em um programa de recompensas para funcionários

Não importa quanto você pague em comissões, os melhores embaixadores de uma marca sempre serão seus funcionários

Seria uma pena não oferecer comissões parecidas para sua equipe de marketing e suporte, principalmente se você já tiver a infraestrutura necessária.

Sempre que alguém decide fazer um upgrade do plano no Publer, esse modal aparece.

O funcionário escolhido, que não sou eu, vai receber uma comissão de 10% sobre os pagamentos por até um ano, um incentivo excelente pra qualquer um se esforçar um pouco mais.

Assim como qualquer outro afiliado, usando o mesmo painel, os funcionários também podem compartilhar seu link exclusivo para atrair novos clientes nas redes sociais e em outros lugares.

E tem um prêmio extra de US$ 50 para o melhor funcionário do mês (com base na receita gerada por novos clientes pagantes)

Retorno sobre o investimento

Embora esse programa de afiliados esteja completando 10 anos, seu desenvolvimento inicial levou apenas algumas semanas, com algumas pequenas atualizações feitas ao longo dos anos. É uma máquina de gerar receita totalmente autônoma.

Até agora, já pagamos aos nossos afiliados, incluindo os funcionários, mais de US$ 208 mil em comissões.

Em troca, tivemos um lucro de US$ 742 mil (sem contar a receita da minha marca pessoal).

Com um ROI de 355%, esse programa de afiliados supera, por uma larga margem, todos os outros canais de marketing que testamos até agora

Planos para o futuro

Dado o sucesso dessas poucas linhas de código, tô pensando seriamente em oferecer o que a gente criou como um produto SaaS independente.

Seria uma ótima contribuição para a nossa missão de fortalecer a sua presença online.

Me avisa se isso for algo que te interesse.

E se você leu até aqui, espero que tenha valido a pena.

Obrigado!

Outras novidades que talvez você tenha perdido:

Sair da versão mobile