Web SDK - Guia de implementação do JavaScript nativo

Documento

Visão geral

INFORMAÇÕES: A Atribuição da Web é uma funcionalidade empresarial. Contacte o seu Gestor de Sucesso do Cliente para ativar esta funcionalidade para a sua conta.

Este guia orienta a implementação do Singular WebSDK usando JavaScript nativo. Este método fornece o rastreio mais fiável e é recomendado para a maioria das implementações porque não é bloqueado por bloqueadores de anúncios comuns.

IMPORTANTE!

  • Não implemente os métodos JavaScript nativo e Gerenciador de tags do Google. Escolha apenas um para evitar o rastreio duplicado.
  • O Singular WebSDK foi concebido para ser executado do lado do cliente no browser de um utilizador. Ele requer acesso aos recursos do navegador, como localStorage e o Modelo de Objeto de Documento (DOM), para funcionar corretamente. Não tente executar o SDK no lado do servidor (por exemplo, via SSR Next.js ou node.js) - isso causará falhas de rastreamento, pois os ambientes de servidor não fornecem acesso às APIs do navegador.

Pré-requisitos

Antes de começar, verifique se você tem:

  • SDK Key & SDK Secret:
  • ID do produto:
    • O que é: Um nome exclusivo para seu site, de preferência usando o formato DNS reverso (por exemplo, com.website-name).
    • Por que é importante: Essa ID associa o site como um aplicativo no Singular e deve corresponder ao ID do pacote de aplicativos da Web listado na sua página de aplicativos no Singular.
  • Permissão para editar o código HTML do seu site.
  • Acesso para adicionar JavaScript na secção <head> das suas páginas.
  • Uma lista de eventos que você deseja rastrear. Veja nossos Eventos padrão do Singular: Lista completa e Eventos recomendados por vertical para obter ideias.

Etapas de implementação


Etapa 1: Adicionar o script da biblioteca SDK

Adicione o seguinte trecho de código à secção <head> de todas as páginas do seu Web site. Coloque-o o mais cedo possível, de preferência perto do topo da tag <head>.

DICA! Adicionar o script antecipadamente garante que a Biblioteca JavaScript Singular esteja disponível para todas as funções Singular no código-fonte da página.

Latest VersionSpecific VersionUsing NPMNext.js / React
<script src="https://web-sdk-cdn.singular.net/singular-sdk/latest/singular-sdk.js"></script>

Etapa 2: inicializar o SDK

  • Sempre inicialize o SDK sempre que uma página for carregada no navegador.
  • A inicialização é necessária para todos os recursos de atribuição e rastreamento de eventos do Singular.
  • A inicialização aciona um evento __PAGE_VISIT__ evento.
  • O evento __PAGE_VISIT__ é usado para gerar uma nova sessão no lado do servidor quando as seguintes condições são atendidas:
    • O utilizador chega com novos dados de publicidade no URL (tais como parâmetros UTM ou WP), ou
    • A sessão anterior expirou (após 30 minutos de inatividade).
    • As sessões são utilizadas para medir a retenção de utilizadores e apoiar a atribuição de reengajamento.
  1. Crie uma função de inicialização e chame-a no DOM Ready após o carregamento da página.
  2. Certifique-se de que a inicialização ocorra antes que qualquer outro evento Singular seja relatado.
  3. Para aplicativos de página única (SPA), inicialize o SDK Singular no primeiro carregamento de página e, em seguida, chame a função Singular Page Visist window.singularSdk.pageVisit() em cada alteração de rota que represente uma nova exibição de página.
Basic InitializationNext.js / React

Inicialização básica pronta para DOM

Adicione um ouvinte de eventos para chamar o initSingularSDK()em DOMContentLoaded

/**
 * Initializes the Singular SDK with the provided configuration.
 * @param {string} sdkKey - The SDK key for Singular.
 * @param {string} sdkSecret - The SDK secret for Singular.
 * @param {string} productId - The product ID for Singular.
 * @example
 * initSingularSDK(); // Initializes SDK with default config
 */
function initSingularSDK() {
  var config = new SingularConfig('sdkKey', 'sdkSecret', 'productId');
  window.singularSdk.init(config);
}

/**
 * Triggers Singular SDK initialization when the DOM is fully parsed.
 */
document.addEventListener('DOMContentLoaded', function() {
  initSingularSDK();
});

Inicialização básica com propriedades globais

Como o SDK do Singular não está sendo inicializado, é necessário implementar a função personalizada para definir as Propriedades globais no localstorage do navegador. Consulte a função personalizada setGlobalPropertyBeforeInit(). Uma vez implementada, você pode definir as propriedades conforme descrito abaixo antes da inicialização do SDK.

/**
 * Initializes the Singular SDK with the provided configuration.
 * @param {string} sdkKey - The SDK key for Singular.
 * @param {string} sdkSecret - The SDK secret for Singular.
 * @param {string} productId - The product ID for Singular.
 * @example
 * initSingularSDK(); // Initializes SDK with default config
 */
function initSingularSDK() {
  var sdkKey = 'sdkKey';
  var sdkSecret = 'sdkSecret';
  var productId = 'productId';
  
  // Set global properties before SDK initialization
  setGlobalPropertyBeforeInit(sdkKey, productId, 'global_prop_1', 'test', false);
  setGlobalPropertyBeforeInit(sdkKey, productId, 'global_prop_2', 'US', true);
  
  // Initialize SDK
  var config = new SingularConfig('sdkKey', 'sdkSecret', 'productId');
  window.singularSdk.init(config);
}

/**
 * Triggers Singular SDK initialization when the DOM is fully parsed.
 */
document.addEventListener('DOMContentLoaded', function() {
  initSingularSDK();
});

Inicialização básica com roteamento de aplicativo de página única (SPA)

Cenário O que fazer

Carregamento da primeira página

Chamar window.singularSdk.init(config)

Navegando para uma nova rota/página

Chamar window.singularSdk.pageVisit()

No carregamento inicial no SPA

Não chamar window.singularSdk.pageVisit() (A inicialização fornecerá o evento de primeira visita à página) .

Exemplo para SPAs (React)

import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';

/**
 * Initializes the Singular SDK with the provided configuration.
 * @param {string} sdkKey - The SDK key for Singular.
 * @param {string} sdkSecret - The SDK secret for Singular.
 * @param {string} productId - The product ID for Singular.
 * @example
 * // Initialize the Singular SDK
 * initSingularSDK();
 */
function initSingularSDK() {
  var config = new SingularConfig('sdkKey', 'sdkSecret', 'productId');
  window.singularSdk.init(config);
}

/**
 * Tracks a page visit event with the Singular SDK on route changes.
 * @example
 * // Track a page visit
 * trackPageVisit();
 */
function trackPageVisit() {
  window.singularSdk.pageVisit();
}

/**
 * A React component that initializes the Singular SDK on mount and tracks page visits on route changes.
 * @returns {JSX.Element} The component rendering the SPA content.
 * @example
 * // Use in a React SPA with react-router-dom
 * function App() {
 *   const location = useLocation();
 *   useEffect(() => {
 *     initSingularSDK();
 *   }, []);
 *   useEffect(() => {
 *     trackPageVisit();
 *   }, [location.pathname]);
 *   return <div>Your SPA Content</div>;
 * }
 */
function App() {
  const location = useLocation();

  useEffect(() => {
    initSingularSDK();
  }, []); // Run once on mount for SDK initialization

  useEffect(() => {
    trackPageVisit();
  }, [location.pathname]); // Run on route changes for page visits

  return (
    <div>Your SPA Content</div>
  );
}

export default App;

Inicialização básica usando um callback de inicialização

Se precisar de executar código depois de o SDK estar pronto (por exemplo, para obter o ID do dispositivo singular), defina um retorno de chamada com .withInitFinishedCallback():

/**
 * Initializes the Singular SDK with a callback to handle initialization completion.
 * @param {string} sdkKey - The SDK key for Singular.
 * @param {string} sdkSecret - The SDK secret for Singular.
 * @param {string} productId - The product ID for Singular.
 * @example
 * initSingularSDK(); // Initializes SDK and logs device ID
 */
function initSingularSDK() {
  var config = new SingularConfig('sdkKey', 'sdkSecret', 'productId')
    .withInitFinishedCallback(function(initParams) {
      var singularDeviceId = initParams.singularDeviceId;
      // Example: Store device ID for analytics
      console.log('Singular Device ID:', singularDeviceId);
      // Optionally store in localStorage or use for event tracking
      // localStorage.setItem('singularDeviceId', singularDeviceId);
    });

  window.singularSdk.init(config);
}

/**
 * Triggers Singular SDK initialization when the DOM is fully parsed.
 */
document.addEventListener('DOMContentLoaded', function() {
  initSingularSDK();
});
  • Substitua 'sdkKey' pela sua chave SDK atual.
  • Substitua 'sdkSecret' pelo seu SDK Secret atual.
  • Substitua 'productId' pela sua ID de produto real. Deve ser semelhante a: com.website-name e deve corresponder ao valor BundleID na página Apps na plataforma Singular.

Opções de configuração

Aprimore a configuração do WebSDK encadeando os métodos .with para habilitar recursos extras.

Por exemplo, para suportar o rastreamento entre subdomínios persistindo a ID de dispositivo Singular (SDID) em um cookie ou para incluir uma ID de usuário personalizada para um visitante do site que retorna com um estado de login ativo:

Config with Options
var domain = 'website-name.com';
var config = new SingularConfig('sdkKey','sdkSecret','productId')
  .withAutoPersistentSingularDeviceId(domain)
  .withCustomUserId(userId);

Referência do método SingularConfig

Aqui estão todos os métodos ".with" disponíveis.

Método Descrição dométodo Saber mais
.withCustomUserId(customId) Enviar o ID de utilizador para o Singular Definir o ID do utilizador
.withProductName(productName) Um nome de apresentação opcional para o produto
.withLogLevel(logLevel) Configurar o nível de registo: 0 - Nenhum (predefinição); 1 - Aviso; 2 - Informação; 3 - Depurar.
.withSessionTimeoutInMinutes(timeout) Definir o tempo limite da sessão em minutos (predefinição: 30 minutos)

.withAutoPersistentSingularDeviceId(domain) Ativar o rastreio automático entre subdomínios Persistir automaticamente usando cookies

.withPersistentSingularDeviceId(singularDeviceId)

Ativar o rastreio manual entre subdomínios Definir manualmente o ID de dispositivo único
.withInitFinishedCallback(callback) Invocar um retorno de chamada quando a inicialização do SDK estiver concluída Invocar uma função de retorno de chamada quando a inicialização estiver concluída

Lista de verificação do desenvolvedor

  • Reúna as credenciais do SDK e a ID do produto.
  • Decida se você precisa de alguma configuração personalizada (ID de usuário, tempo limite, etc.).
  • Construa uma função Singular Intitialization e um objeto SingularConfig usando os exemplos acima.
  • Sempre teste para garantir que a inicialização seja acionada apenas uma vez no carregamento da página.

DICA! Para configurações avançadas, como o rastreamento da Pesquisa orgânica, talvez seja necessário implementar JavaScript personalizado - por exemplo, para ajustar os parâmetros de consulta - antes da inicialização do SDK Singular. Certifique-se de que seu código personalizado seja executado antes da função de inicialização do Singularpara que as alterações sejam capturadas corretamente. Mais detalhes sobre como implementar o rastreamento de pesquisa orgânica podem ser encontrados aqui.


Etapa 3: rastreamento de eventos

Depois de inicializar o SDK, é possível rastrear eventos personalizados quando os usuários realizam ações importantes no seu site.

IMPORTANTE! O Singular não bloqueia eventos duplicados! É responsabilidade dos desenvolvedores adicionar proteções contra atualizações de página ou duplicação. Recomenda-se a incorporação de algum método de deduplicação especificamente para eventos de receita para evitar dados de receita erróneos. Consulte a "Etapa 5: Prevenção de eventos duplicados" abaixo para obter um exemplo.

Basic EventConversion EventRevenue Event

Rastreamento básico de eventos

Rastreie um evento simples ou adicione atributos personalizados usando JSON válido para fornecer mais contexto sobre o evento:

// Basic event tracking
var eventName = 'page_view';
window.singularSdk.event(eventName);
      
// Optional: With attributes for more context
var eventName = 'sng_content_view';
var attributes = {
  key1: 'value1', // First custom attribute
  key2: 'value2'  // Second custom attribute
};
window.singularSdk.event(eventName, attributes);

Padrões comuns de implementação de eventos

Page Load EventsButton Click EventsForm Submission Events

Eventos de carregamento de página

O mecanismo de rastreamento de eventos de carregamento de página usa JavaScript para monitorar quando uma página da Web é totalmente carregada e, em seguida, aciona um evento de análise usando o SDK Singular. Especificamente, aproveita o método window.addEventListener('load', ...) para detetar quando todos os recursos da página (por exemplo, HTML, imagens, scripts) foram carregados. Após este evento, a função event do Singular SDK é chamada para registar um evento personalizado com atributos associados, permitindo o rastreio das interações do utilizador para fins analíticos, como a monitorização de visualizações de páginas ou acções do utilizador como registos. O mecanismo é normalmente utilizado na análise da Web para capturar dados sobre o comportamento do utilizador, como visitas a páginas ou acções específicas, com atributos personalizáveis para obter informações detalhadas.

O mecanismo pode ser adaptado para rastrear um evento específico (por exemplo, um evento page_view ) com atributos personalizados, como mostrado abaixo. A estrutura do código permanece limpa e modular, com o nome do evento e os atributos definidos separadamente para maior clareza e facilidade de manutenção.

/**
 * Tracks a registration event with the Singular SDK when the page fully loads.
 * @param {string} eventName - The name of the event to track (e.g., 'page_view').
 * @param {Object} attributes - A JSON object with custom event attributes.
 * @param {string} attributes.key1 - First custom attribute (e.g., 'value1').
 * @param {string} attributes.key2 - Second custom attribute (e.g., 'value2').
 */
window.addEventListener('load', function() {
  var eventName = 'page_view';
  var attributes = {
    key1: 'value1', // First custom attribute
    key2: 'value2'  // Second custom attribute
  };
  window.singularSdk.event(eventName, attributes);
});

Etapa 4: Definir o ID de usuário do cliente

Você pode enviar seu ID de usuário interno para a Singular usando um método do SDK da Singular.

OBSERVAÇÃO: Se você usar a solução Cross-Device da Singular, deverá coletar a ID de usuário em todas as plataformas.

  • A ID de usuário pode ser qualquer identificador e não deve expor PII (Informações de identificação pessoal). Por exemplo, você não deve usar o endereço de e-mail, nome de usuário ou número de telefone de um usuário. A Singular recomenda o uso de um valor com hash exclusivo apenas para seus dados primários.
  • O valor da ID de usuário passado para a Singular também deve ser a mesma ID de usuário interna que você captura em todas as plataformas (Web/Mobile/PC/Console/Offline).
  • A Singular incluirá o ID de utilizador nas exportações ao nível do utilizador, ETL e postbacks do BI interno (se configurado). O ID do usuário é um dado primário e a Singular não o compartilha com outras partes.
  • O valor da ID de utilizador, quando definido com o método Singular SDK, persistirá até ser anulado utilizando o método logout() ou até que o armazenamento local do browser seja eliminado. Fechar ou atualizar o sítio Web não anula a definição do ID de utilizador.
  • No modo privado/incógnito, o SDK não pode manter o ID de utilizador porque o browser elimina automaticamente o armazenamento local quando é fechado.

Para definir o ID de utilizador, utilize o método login(). Para o anular (por exemplo, se o utilizador "sair" da conta), chame o método logout().

NOTA: Se vários Utilizadores utilizarem um único dispositivo, recomendamos a implementação de um fluxo de fim de sessão para definir e anular a definição do ID de Utilizador para cada início e fim de sessão.

Se você já sabe a ID do usuário quando o Singular SDK é inicializado no site, defina a ID do usuário no objeto de configuração. Dessa forma, o Singular pode ter a ID de usuário desde a primeira sessão. No entanto, o ID de usuário normalmente não está disponível até que o usuário se registre ou faça um login. Nesse caso, chame login() após a conclusão do fluxo de registo.

DICA! Utilize o mesmo ID de utilizador do cliente que utiliza nos seus SDKs móveis. Isto permite a atribuição entre dispositivos e fornece uma visão completa do comportamento do utilizador em todas as plataformas.

Melhores práticas para o ID de utilizador do cliente:

  • Defina-o assim que o utilizador iniciar sessão ou se inscrever
  • Utilizar o mesmo ID nas plataformas Web e móveis
  • Não utilizar informações de identificação pessoal (e-mail, número de telefone)
  • Utilize o seu ID de utilizador interno ou o ID da base de dados
  • Enviar como uma cadeia de caracteres, mesmo que seja numérica:
Set the User IDUnset the User ID
// After user logs in or signs up
var userId = 'user_12345';
window.singularSdk.login(userId);

Etapa 5: Evitar eventos duplicados

IMPORTANTE! Este é um dos erros de implementação mais comuns. Sem as devidas salvaguardas, os eventos podem ser disparados várias vezes, inflacionando as suas métricas.

A prevenção de eventos de receita duplicados numa aplicação Web é importante para uma análise precisa e para evitar a comunicação excessiva de conversões. O objetivo é garantir que cada evento único é ativado apenas uma vez por sessão ou visita à página, mesmo que o utilizador recarregue a página ou accione inadvertidamente várias submissões.

  • É gerada uma chave única para cada evento (com base nos detalhes do evento e nos atributos personalizados opcionais).
  • Essa chave é armazenada no sessionStorage do navegador (ou localStorage para deduplicação persistente).
  • Antes de enviar, o código verifica se um evento de receita com a mesma carga útil já foi disparado.
  • Caso contrário, envia o evento e guarda a chave.
  • Em caso afirmativo, bloqueia a repetição e notifica o utilizador ou o programador.
Using Session Storage Method

Utilização do método de armazenamento de sessão

// Sends a revenue event to Singular WebSDK, preventing duplicates in the same session
// @param {string} eventName - Name of the revenue event (defaults to "web_purchase")
// @param {number} amount - Revenue amount (defaults to 0)
// @param {string} currency - Currency code (defaults to "USD", normalized to uppercase)
// @param {object} [attributes] - Optional key-value pairs for additional event data
function sendRevenueEvent(eventName, amount, currency, attributes) {
  // Normalize inputs: set defaults and ensure currency is uppercase
  eventName = eventName ? eventName : "web_purchase";
  currency = currency ? currency.toUpperCase() : "USD";
  amount = amount ? amount : 0;

  // Create a payload object to standardize event data
  var payload = {
    eventName: eventName,
    amount: amount,
    currency: currency,
    attributes: attributes ? attributes : null // Null if no attributes provided
  };

  // Generate a unique key for sessionStorage by hashing the payload
  // This ensures deduplication of identical events in the same session
  var storageKey = 'singular_revenue_' + btoa(JSON.stringify(payload));

  // Check if the event was already sent in this session to prevent duplicates
  if (!sessionStorage.getItem(storageKey)) {
    // Mark event as sent in sessionStorage
    sessionStorage.setItem(storageKey, 'true');

    // Send revenue event to Singular SDK, including attributes if provided and valid
    if (attributes && typeof attributes === 'object' && Object.keys(attributes).length > 0) {
      window.singularSdk.revenue(eventName, currency, amount, attributes);
    } else {
      // Fallback to basic revenue event without attributes
      window.singularSdk.revenue(eventName, currency, amount);
    }
    // Log event details for debugging
    console.log("Revenue event sent:", payload);
  } else {
    // Log and alert if a duplicate event is detected
    console.log("Duplicate revenue event prevented:", payload);
    alert("Duplicate revenue event prevented!");
  }
}

Passo 6: Testar a sua implementação

Depois de implementar o SDK, verifique se ele está funcionando corretamente usando as ferramentas de desenvolvedor do seu navegador.

Verificar se o SDK está carregado

  1. Abra seu site em um navegador
  2. Abra as Ferramentas do desenvolvedor (F12 ou clique com o botão direito do mouse → Inspecionar)
  3. Vá para a guia Console
  4. Digite typeof singularSdk e pressione Enter
  5. Você deve ver um objeto "function", não "undefined"

Verificar solicitações de rede

  1. Abra as Ferramentas do desenvolvedor (F12)
  2. Vá para o separador Rede
  3. Recarregue sua página
  4. Filtre por singular ou sdk-api
  5. Procure solicitações para sdk-api-v1.singular.net
  6. Clique em uma solicitação para exibir detalhes
  7. Verifique se o código de status é 200
  8. Verifique se a carga útil do pedido contém a ID do produto. Este estará no parâmetro "i" do payload.

SUCESSO! Se vir pedidos para sdk-api-v1.singular.net com o código de estado 200, o seu SDK está a enviar dados com êxito para a Singular.

Verificar eventos

  1. Acione um evento em seu site (clique em um botão, envie um formulário, etc.)
  2. Na guia Rede, procure uma nova solicitação para sdk-api-v1.singular.net
  3. Clique na solicitação e visualize a guia Carga útil ou Solicitação
  4. Verifique se o parâmetro do nome do evento "n" aparece no pedido

    event_test.png

Para testes de ponta a ponta, use a Console de testes

Você pode testar a integração do Web SDK usando o Console do SDK disponível no painel do Singular.

  1. Na plataforma Singular, vá para Ferramentas de desenvolvedor > Console de teste.
  2. Clique em Adicionar dispositivo.
  3. Selecione a plataforma"Web" e insira o ID do dispositivo Singular.
  4. Obtenha a ID do dispositivo Singular a partir do payload do navegador. Verifique a captura de ecrã acima para localizar o SDID num evento.
  5. A consola de testes tem de permanecer no estado ativo enquanto faz o teste numa janela separada. A consola não apresentará quaisquer eventos que sejam acionados enquanto estiver fechada ou offline.
  6. Assim que o SDID for adicionado, pode recarregar a página Web e acionar alguns eventos. O "__PAGE_VISIT__" e outros eventos (se acionados) serão mostrados na Consola SDK.
  7. Utilizar os registos de exportação(Relatórios e registos de exportação de insights) é outra forma de verificar os resultados dos testes. Este conjunto de dados tem um atraso de 1 hora.

Etapa 7: implementar o encaminhamento da Web para o aplicativo

Encaminhamento de atribuição de Web para aplicativo

Use o Singular WebSDK para rastrear as jornadas do usuário do seu site para seu aplicativo móvel, permitindo a atribuição precisa de campanhas da Web a instalações e reengajamentos de aplicativos móveis. Siga estes passos para configurar o reencaminhamento da web para a aplicação, incluindo suporte de código QR para utilizadores de desktop.

  1. Siga nosso Guia de Encaminhamento de Atribuição de Website para Aplicativo Móvel para configurar o Singular WebSDK para atribuição de web móvel.
  2. Para rastreamento Web-to-App para desktop:
    • Conclua a configuração no guia acima.
    • Use o Singular Mobile Web-to-App Link (por exemplo, https://yourlink.sng.link/...) e a função WebSDK buildWebToAppLink(link) com uma biblioteca de código QR dinâmica como QRCode.js.
    • Gere um código QR na sua página Web que codifica a ligação. Os utilizadores de desktop podem digitalizá-lo com o seu dispositivo móvel para abrir a aplicação, passando os dados da campanha para atribuição.
    • Explore a nossa Demonstração Singular WebSDK para obter um exemplo funcional da geração de códigos QR e do tratamento de ligações web-to-app.

DICA! As visualizações da web do navegador in-app móvel (como as usadas pelo Facebook, Instagram e TikTok) podem fazer com que o Singular Device ID mude se um usuário mudar para o navegador nativo do dispositivo, interrompendo a atribuição.

Para evitar isso, use sempre o formato de link de rastreamento Singular adequado para cada rede de anúncios:


Demonstração do Singular WebSDK

Abaixo está uma implementação simples, mas abrangente, usando a API do Singular WebSDK documentada. Esta amostra fornece uma separação clara para eventos personalizados, eventos de conversão, eventos de receita e suporte a links web-to-app usando o encaminhamento Web-to-App suportado pelo WebSDK.

É possível executar esse código em um arquivo HTML local ou modificá-lo em seu site para integração avançada e solução de problemas.

Demonstração do Singular WebSDK (código-fonte)
#

Copie e cole o código abaixo como um ficheiro HTML e abra-o no seu browser.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  
  <title>Singular WebSDK Demo</title>
  <script src="https://cdn.jsdelivr.net/npm/qrcodejs/qrcode.min.js"></script>
  <script src="https://web-sdk-cdn.singular.net/singular-sdk/latest/singular-sdk.js"></script>

  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }

    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', Roboto, sans-serif;
      line-height: 1.6;
      color: #1a1d29;
      background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
      padding: 40px 20px;
      min-height: 100vh;
    }

    .container {
      max-width: 800px;
      margin: 0 auto;
      background: white;
      padding: 48px;
      border-radius: 16px;
      box-shadow: 0 4px 24px rgba(30, 41, 59, 0.08);
      border: 1px solid rgba(148, 163, 184, 0.1);
    }

    .header-brand {
      display: flex;
      align-items: center;
      margin-bottom: 32px;
      padding-bottom: 24px;
      border-bottom: 2px solid #f1f5f9;
    }

    .brand-dot {
      width: 12px;
      height: 12px;
      background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
      border-radius: 50%;
      margin-right: 12px;
    }

    h1 {
      font-size: 32px;
      font-weight: 700;
      color: #0f172a;
      letter-spacing: -0.025em;
    }

    h2 {
      font-size: 24px;
      font-weight: 600;
      margin: 48px 0 24px 0;
      color: #1e293b;
      padding-bottom: 12px;
      border-bottom: 2px solid #e2e8f0;
      position: relative;
    }

    h2:before {
      content: '';
      position: absolute;
      bottom: -2px;
      left: 0;
      width: 60px;
      height: 2px;
      background: linear-gradient(90deg, #6366f1, #8b5cf6);
    }

    p {
      margin-bottom: 18px;
      color: #475569;
      font-size: 15px;
    }

    strong {
      color: #1e293b;
      font-weight: 600;
    }

    .form-group {
      margin-bottom: 24px;
    }

    .radio-group {
      display: flex;
      gap: 24px;
      margin-bottom: 24px;
      padding: 16px;
      background: #f8fafc;
      border-radius: 12px;
      border: 1px solid #e2e8f0;
    }

    .radio-group label {
      display: flex;
      align-items: center;
      gap: 10px;
      cursor: pointer;
      font-size: 14px;
      font-weight: 500;
      color: #475569;
      transition: color 0.2s ease;
    }

    .radio-group label:hover {
      color: #1e293b;
    }

    input[type="radio"] {
      width: 20px;
      height: 20px;
      cursor: pointer;
      accent-color: #6366f1;
    }

    input[type="text"] {
      width: 100%;
      padding: 14px 18px;
      font-size: 15px;
      font-weight: 400;
      border: 2px solid #e2e8f0;
      border-radius: 12px;
      transition: all 0.2s ease;
      background: #fafbfc;
      color: #1e293b;
    }

    input[type="text"]:focus {
      outline: none;
      border-color: #6366f1;
      background: white;
      box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.1);
    }

    input[type="text"]::placeholder {
      color: #94a3b8;
      font-weight: 400;
    }

    button {
      padding: 14px 32px;
      font-size: 15px;
      font-weight: 700;
      color: #fff;
      background: linear-gradient(90deg, #2363f6 0%, #1672fe 100%);
      border: none;
      border-radius: 4px;  /* Pill shape */
      cursor: pointer;
      box-shadow: 0 2px 8px rgba(35, 99, 246, 0.12);
      transition: background 0.2s, transform 0.2s;
      letter-spacing: 0.03em;
    }
    button:hover {
      background: linear-gradient(90deg, #1672fe 0%, #2363f6 100%);
      transform: translateY(-2px);
      box-shadow: 0 8px 24px rgba(22, 114, 254, 0.18);
    }
    button:active {
      transform: translateY(0);
    }

    a {
      color: #6366f1;
      text-decoration: none;
      font-weight: 600;
      transition: all 0.2s ease;
      position: relative;
    }

    a:hover {
      color: #4f46e5;
    }

    a:after {
      content: '';
      position: absolute;
      width: 0;
      height: 2px;
      bottom: -2px;
      left: 0;
      background: #6366f1;
      transition: width 0.2s ease;
    }

    a:hover:after {
      width: 100%;
    }

    ol {
      margin-left: 10px;
      margin-bottom: 18px;
    }

    ol li {
      margin-left: 10px;
    }

    .info-box {
      background: linear-gradient(135deg, #eff6ff 0%, #f0f9ff 100%);
      border-left: 4px solid #6366f1;
      padding: 20px;
      margin: 24px 0;
      border-radius: 12px;
      border: 1px solid rgba(99, 102, 241, 0.1);
    }

    .info-box p {
      color: #1e40af;
      margin-bottom: 0;
    }

    .section-label {
      font-size: 12px;
      font-weight: 700;
      color: #6b7280;
      margin-bottom: 10px;
      text-transform: uppercase;
      letter-spacing: 1px;
    }

    .button-group {
      display: flex;
      align-items: center;
      gap: 16px;
      flex-wrap: wrap;
    }

    .analytics-badge {
      display: inline-flex;
      align-items: center;
      gap: 8px;
      background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
      color: white;
      padding: 6px 12px;
      border-radius: 20px;
      font-size: 12px;
      font-weight: 600;
      margin-bottom: 16px;
    }

    .feature-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
      gap: 16px;
      margin: 24px 0;
    }

    .feature-item {
      padding: 16px;
      background: #f8fafc;
      border-radius: 10px;
      border: 1px solid #e2e8f0;
      text-align: center;
    }

    .feature-item strong {
      display: block;
      color: #6366f1;
      font-size: 14px;
      margin-bottom: 4px;
    }

    .metric-dot {
      width: 8px;
      height: 8px;
      background: #10b981;
      border-radius: 50%;
      display: inline-block;
      margin-right: 8px;
      animation: pulse 2s infinite;
    }

    @keyframes pulse {
      0%, 100% { opacity: 1; }
      50% { opacity: 0.5; }
    }
  </style>

</head>

<body>
  <div class="container">
    <div class="header-brand">
      <div class="brand-dot"></div>
      <h1>Singular WebSDK Demo</h1>
    </div>

    <p>
      Below is a simple but comprehensive implementation using the documented Singular WebSDK API. 
      This sample provides clear separation for <strong>custom events</strong>, <strong>conversion events</strong>, <strong>revenue events</strong>, and <strong>web-to-app link support using WebSDK supported Web-to-App forwarding</strong>. </p>
      <p>You can run this code in a local HTML file or modify it in your site for advanced integration and troubleshooting.
    </p>

    <h2>SDK Initialization Testing</h2>
    <ol>
      <li>Open your browsers developer tools.</li>
      <li>Inspect the Network tab and filter for "sdk-api-v1.singular.net"</li>
      <li>Refresh this page, and examin the payload of the request.</li>
    </ol>


    <hr>

    <h2>User Authentication</h2>
    <p>User authentication is key to supporting cross-device attribution. Pass the common User ID value that would also be sent to Singular from mobile SDKs for cross-device continuity.</p>
    <div class="form-group">
      <div class="section-label">Simulate User Authentication</div>
      <input type="text" id="userId" placeholder="Enter a User ID"/>
    </div>
    <div class="form-group">
      <div class="button-group">
        <button onclick="setUserId()">Set the User ID (Login)</button>
        <button onclick="unsetUserId()">Unset the User ID (Logout)</button>
      </div>
    </div>

    <hr>

    <h2>SDK Event Testing</h2>
    <div class="form-group">
      <div class="section-label">Event Type Selection</div>
      <div class="radio-group">
        <label>
          <input type="radio" name="eventType" value="custom" checked onchange="handleRevenueFields(this)">
          <span>Custom Event</span>
        </label>
        <label>
          <input type="radio" name="eventType" value="conversion" onchange="handleRevenueFields(this)">
          <span>Conversion Event</span>
        </label>
        <label>
          <input type="radio" name="eventType" value="revenue" onchange="handleRevenueFields(this)">
          <span>Revenue Event</span>
        </label>
      </div>
    </div>

    <div class="form-group">
      <div class="section-label">Event Configuration</div>
      <input type="text" id="eventName" placeholder="Enter event name"/>
    </div>

    <div class="form-group" id="currencyGroup" style="display:none">
      <input type="text" id="currency" placeholder="Currency code (e.g., USD, EUR)"/>
    </div>

    <div class="form-group" id="amountGroup" style="display:none">
      <input type="text" id="amount" placeholder="Revenue amount (e.g., 29.99)"/>
    </div>

    <div class="form-group" id="attributesGroup" style="display:none">
      <input type="text" id="attributes" placeholder='Optional attributes (JSON, e.g. {"color":"blue"})'/>
    </div>

    <div class="form-group">
      <button id="send" onclick="sendCustomEvent()">Send Event to Singular</button>
    </div>

    <hr>
    <h2>Web-to-App Forwarding</h2>
    <div class="form-group">
    <ol>
      <li><a href="https://support.singular.net/hc/pt-br/articles/360042283811" target="_blank">Learn more about Web-to-App Forwarding</a></li>
    <li>In the page source code you will find the mobile web-to-app base link "<strong>https://seteam.sng.link/D0mvs/y3br?_smtype=3</strong>" used for demonstration purposes.</li>
    <li>Replace all occurances of the link with your own Singular Mobile Web-to-App link to test functionality.</li>
    <li>Add Singular Web Parameters to the URL and refresh the page. (example: "<strong>?wpsrc=SingularTest&wpcn=MyCampaign</strong>").</li>
  </ol>

    <hr>
    <div class="section-label">Mobile Web-to-App Test</div>
    <div class="form-group">
      <div class="button-group">
        <button onclick="displayWebLink()">Display Constructed Web-to-App Link</button>
        <button onclick="testWebLink()">Test Web-to-App Link</button>
      </div>
    </div>
  </div>
    <hr>
    <div class="section-label">Desktop Web-to-App Configuration</div>
    <div class="form-group">
    <p>Use a dynamic QR Code generation library like <a href="https://davidshimjs.github.io/qrcodejs/" target="_blank">QRCode.js</a> to build and display a QR code using the Singular Mobile Web-to-App link with constructed campaign parameters.</p>
    <div id="qrcode"></div>
    </div>

  </div>
  
  <script>
    /**
     * Initializes the Singular SDK with the provided configuration.
     * @param {string} sdkKey - The SDK key for Singular (e.g., 'se_team_9b3431b0').
     * @param {string} sdkSecret - The SDK secret for Singular (e.g., 'bcdee06e8490949422c071437da5c5ed').
     * @param {string} productId - The product ID for Singular (e.g., 'com.website').
     * @example
     * // Initialize the Singular SDK
     * initSingularSDK('se_team_9b3431b0', 'bcdee06e8490949422c071437da5c5ed', 'com.website');
     */
    function initSingularSDK() {
      var sdkKey = 'se_team_9b3431b0';
      var sdkSecret = 'bcdee06e8490949422c071437da5c5ed';
      var productId = 'com.website';
      
      var config = new SingularConfig(sdkKey, sdkSecret, productId)
        .withInitFinishedCallback(function(initParams) {
          var singularDeviceId = initParams.singularDeviceId;
          console.log('Singular Device ID:', singularDeviceId);
          // Optionally store for use in events
          // sessionStorage.setItem('singularDeviceId', singularDeviceId);
        });

      window.singularSdk.init(config);
      generateDesktopWebToAppQRCode(); // Generate QR code after initialization
    }

    /**
     * Triggers Singular SDK initialization when the DOM is fully parsed.
     * @example
     * document.addEventListener('DOMContentLoaded', function() {
     *   initSingularSDK();
     * });
     */
    document.addEventListener('DOMContentLoaded', function() {
      initSingularSDK();
    });


    // Fires a custom event using the Singular SDK
    function sendCustomEvent() {
      var eventName = document.getElementById('eventName').value;
      window.singularSdk.event(eventName);
    }

    // Fires a conversion event using the Singular SDK
    function sendConversionEvent() {
      var eventName = document.getElementById('eventName').value;
      window.singularSdk.conversionEvent(eventName);
    }

    // Fires a revenue event only once per session with deduplication
    // Parameters:
    //   - eventName: string (name of event), default "web_purchase" if blank
    //   - amount: revenue amount, default 0 if blank
    //   - currency: string, defaults to "USD" if blank
    //   - attributes: optional object with extra payload
    function sendRevenueEvent(eventName, amount, currency, attributes) {
      // Fill in defaults and normalize values
      eventName = eventName ? eventName : "web_purchase";
      currency = currency ? currency.toUpperCase() : "USD";
      amount = amount ? amount : 0;

      // Create a unique key based on event data for deduplication
      // btoa(JSON.stringify(...)) ensures order consistency in local/sessionStorage
      var payload = {
        eventName: eventName,
        amount: amount,
        currency: currency,
        attributes: attributes ? attributes : null
      };
      var storageKey = 'singular_revenue_' + btoa(JSON.stringify(payload));

      // Only fire event if no identical event has already fired in this tab/session
      if (!sessionStorage.getItem(storageKey)) {
        sessionStorage.setItem(storageKey, 'true');
        if (attributes && typeof attributes === 'object' && Object.keys(attributes).length > 0) {
          // Fire event with attributes payload
          window.singularSdk.revenue(eventName, currency, amount, attributes);
        } else {
          // Fire simple revenue event
          window.singularSdk.revenue(eventName, currency, amount);
        }
        console.log("Revenue event sent:", payload);
      } else {
        // Duplicate detected, don't send to SDK
        console.log("Duplicate revenue event prevented:", payload);
        alert("Duplicate revenue event prevented!");
      }
    }

    // Collects form values, validates attributes JSON, and calls sendRevenueEvent()
    // Ensures only valid values are sent and blocks on malformed JSON
    function fireFormRevenueEvent() {
      var eventName = document.getElementById('eventName').value;
      var currency = document.getElementById('currency').value;
      var amount = document.getElementById('amount').value;
      
      var attributesRaw = document.getElementById('attributes').value;
      var attributes = null;
      if (attributesRaw) {
        try {
          // Try to parse the optional attributes field from JSON string
          attributes = JSON.parse(attributesRaw);
        } catch (e) {
          alert("Optional attributes must be valid JSON.");
          return; // Stop if invalid JSON
        }
      }

      // Calls main revenue logic for deduplication and event sending
      sendRevenueEvent(eventName, amount, currency, attributes);
    }

    // Controls which form fields are visible depending on selected event type
    // Revenue events require currency, amount, and attributes fields
    function handleRevenueFields(sender) {
      var isRevenue = sender.value === "revenue";
      document.getElementById("currencyGroup").style.display = isRevenue ? "block" : "none";
      document.getElementById("amountGroup").style.display = isRevenue ? "block" : "none";
      document.getElementById("attributesGroup").style.display = isRevenue ? "block" : "none";

      // Dynamically assign the event button's onclick handler
      var send = document.getElementById("send");
      send.onclick = (sender.value === "custom") ? sendCustomEvent
        : (sender.value === "conversion") ? sendConversionEvent
        : fireFormRevenueEvent; // Only fires revenue logic for "revenue"
    }

    // Opens demo Singular web-to-app link in a new tab/window
    function testWebLink() {
      var singularWebToAppBaseLink = 'https://seteam.sng.link/D0mvs/y3br?_smtype=3';
      window.open(singularWebToAppBaseLink);
    }

    // Displays constructed Singular web-to-app link with campaign parameters
    function displayWebLink() {
      var singularWebToAppBaseLink = 'https://seteam.sng.link/D0mvs/y3br?_smtype=3';
      var builtLink = window.singularSdk.buildWebToAppLink(singularWebToAppBaseLink);
      console.log("Singular Web-to-App Link: ", builtLink);
      alert("Singular Web-to-App Link: " + builtLink);
    }

    // Generates QR code for desktop deep linking using Singular Mobile Web-to-App link
    function generateDesktopWebToAppQRCode() {
      var singularWebToAppBaseLink = 'https://seteam.sng.link/D0mvs/y3br?_smtype=3';
      const value = window.singularSdk.buildWebToAppLink(singularWebToAppBaseLink);
      new QRCode(document.getElementById("qrcode"), {
        text: value,
        width: 128,
        height: 128,
        colorDark: "#000",
        colorLight: "#fff",
        correctLevel: QRCode.CorrectLevel.H
      });
    }

    // Simulate user authentication and send login event
    function setUserId() {
      var userId = document.getElementById('userId').value;
      window.singularSdk.login(userId);
      console.log("Singular User ID is Set to: " + userId);
      window.singularSdk.event("sng_login");
    }

    // Simulate user logout and unset Singular user ID
    function unsetUserId() {
      window.singularSdk.logout();
      console.log("Singular User ID is Unset");
    }
  </script>
</body>
</html>

Tópicos avançados

Banners singulares

Ativar banners singulares
#

INFORMAÇÃO: Os Singular Banners são uma funcionalidade empresarial. Para saber mais sobre esta funcionalidade, contacte o seu Gestor de Sucesso do Cliente.

Os Singular Banners podem ser apresentados no seu Web site móvel para conduzir os utilizadores da Web sem problemas para a sua aplicação e apresentar o conteúdo mais relevante da aplicação. Depois de ativar os Singular Banners no seu website, a sua organização pode facilmente desenhar, implementar e manter banners através da UI dos Singular Banners.

Artigos relacionados

Guia de implementação passo a passo

  1. Adicione o Script WebSDK JavaScript Nativo Singular ao seu site.

    Siga o guia de integração acima para adicionar o Singular Native JavaScript WebSDK ao seu site antes de prosseguir com a implementação dos Banners.

  2. Dar permissão ao Singular para acessar dados de dicas do cliente

    Com as limitações dos dados do agente do utilizador nos navegadores Web baseados no Chromium, lançadas em fevereiro-março de 2023, os anunciantes precisam agora de obter dados de dicas do cliente e dar permissão ao Singular WebSDK para receber esses dados. Para mais informações, consulte as FAQ sobre Banners.

    O site precisa de:

    • Começar a pedir Client Hints (accept-ch header).
    • Conceder permissão à Singular (como um terceiro) para que o navegador envie à Singular as dicas do cliente nas solicitações de banner de busca (permissions-policy header).

    Adicione os seguintes cabeçalhos de resposta à resposta de carregamento da página:

    accept-ch:
    sec-ch-ua-model,
    sec-ch-ua-platform-version,
    sec-ch-ua-full-version-list
    
    permissions-policy:
    ch-ua-model=("https://sdk-api-v1.singular.net"),
    ch-ua-platform-version=("https://sdk-api-v1.singular.net"),
    ch-ua-full-version-list=("https://sdk-api-v1.singular.net")
  3. Adicione a opção de configuração do banner à inicialização do WebSDK

    Os Singular Banners exibem avisos de download de aplicativos inteligentes em seu site móvel, preservando a atribuição da fonte de marketing original. Quando ativado com suporte web-to-app, o SDK rastreia qual a rede de anúncios ou campanha que trouxe o utilizador para o seu site, atribuindo depois a instalação subsequente da aplicação a essa fonte original.

    /**
     * Initialize Singular SDK with Banner support and web-to-app attribution.
     * This enables smart banners that direct mobile web users to download your app
     * while preserving the original marketing source (UTM parameters, ad network, etc.)
     * for proper attribution when the app is installed.
     * @param {string} sdkKey - Your Singular SDK Key
     * @param {string} sdkSecret - Your Singular SDK Secret
     * @param {string} productId - Your Product ID (e.g., com.your-website)
     */
    function initSingularSDK() {
      // Enable web-to-app tracking to preserve attribution data
      var bannersOptions = new BannersOptions().withWebToAppSupport();
      
      // Configure SDK with banner support
      var config = new SingularConfig(sdkKey, sdkSecret, productId)
        .withBannersSupport(bannersOptions);
      
      // Initialize the SDK
      window.singularSdk.init(config);
      
      // Display the smart banner
      window.singularSdk.showBanner();
    }
    
    // Call on DOM ready
    document.addEventListener('DOMContentLoaded', function() {
      initSingularSDK();
    });
  4. Mostrar novamente o banner na rota da página (somente aplicativos de página única)

    Se o seu aplicativo é um aplicativo de página única, é necessário ocultar e mostrar novamente o banner em cada rota de página. Isso garante que o Singular forneça o banner apropriado para sua experiência na web.

    Para ocultar e mostrar novamente o banner, use o seguinte código:

    window.singularSdk.hideBanner();
    window.singularSdk.showBanner();
  5. [Opção avançada] Personalizar configurações de link

    O Singular fornece uma maneira de personalizar os links no banner através do código.

    Para personalizar os links:

    • Crie um objeto LinkParams e utilize uma ou mais das funções abaixo. Faça isso antes de chamar window.singularSdk.showBanner().
    • Em seguida, passe o objeto LinkParams quando chamar window.singularSdk.showBanner().

    Exemplo:

    // Define a LinkParams object
    let bannerParams = new LinkParams();
    
    // Configure link options (see details on each option below)
    bannerParams.withAndroidRedirect("androidRedirectValue");
    bannerParams.withAndroidDL("androidDLParamValue");
    bannerParams.withAndroidDDL("androidDDLparamValue");
    bannerParams.withIosRedirect("iosRedirectValue");
    bannerParams.withIosDL("iosDLValue");
    bannerParams.withIosDDL("iosDDLValue");
    
    // Show the banner with the defined options
    window.singularSdk.showBanner(bannerParams);

    Lista de opções:

    Método Descrição
    withAndroidRedirect Passa uma ligação de redireccionamento para a página de transferência da sua aplicação Android, normalmente uma página da Play Store.
    withAndroidDL Passar um deep link para uma página dentro do seu aplicativo Android.
    withAndroidDDL Passar uma ligação direta diferida, ou seja, uma ligação para uma página na sua aplicação Android que o utilizador ainda não instalou.
    withIosRedirect Passe um link de redirecionamento para a página de download do seu aplicativo iOS, geralmente uma página da App Store.
    withIosDL Passe um link direto para uma página dentro do seu aplicativo iOS.
    withIosDDL Passar um link direto diferido, ou seja, um link para uma página no seu aplicativo iOS que o usuário ainda não instalou.
  6. [Opção avançada] Usar código para forçar a ocultação/exibição de banners

    Tal como mencionado no passo 3, se tiver uma aplicação de página única, tem de utilizar os métodos hideBanner() e showBanner() em cada rota de página para garantir que o banner adequado é entregue (ver acima).

    hideBanner() Os métodos showBanner()e também estão disponíveis para serem utilizados em todo o seu código, caso pretenda ocultar um banner que, de outra forma, seria apresentado, ou voltar a apresentar um banner que ocultou.

    Método Descrição
    singularSdk.hideBanner() Oculta um banner visível da sua página.
    singularSdk.showBanner() Mostrar o banner pré-configurado.
    singularSdk.showBanner(params) Mostrar o banner pré-configurado mas substituir os links com as opções definidas no objeto linkParams (ver passo 4).

Propriedades globais

Propriedades globais
#

O Singular SDK permite definir propriedades personalizadas a serem enviadas para os servidores Singular juntamente com cada sessão e evento enviado pela aplicação. Essas propriedades podem representar qualquer informação que você queira sobre o usuário, o modo/status do aplicativo ou qualquer outra coisa.

  • É possível definir até 5 propriedades globais como um objeto JSON válido. As propriedades globais são mantidas nos navegadores localstorage até que sejam apagadas ou o contexto do navegador seja alterado.

  • Cada nome e valor de propriedade pode ter até 200 caracteres. Se passar um nome ou valor de propriedade mais longo, este será truncado para 200 caracteres.

  • As propriedades globais são atualmente reflectidas nos registos de eventos ao nível do utilizador do Singular (consulte Exportar registos de atribuição) e em postbacks.

  • As propriedades globais estão disponíveis para serem enviadas em postbacks do Singular para um terceiro para fins de correspondência, se necessário.

Para suportar a definição de Propriedades Globais antes da inicialização do WebSDK, é necessário implementar a função personalizada setGlobalPropertyBeforeInite chamá-la antes da Inicialização do SDK. Se não for necessária a definição de Propriedades Globais antes da Inicialização, pode omitir este código personalizado.

Para tratar as Propriedades Globais após a inicialização, tem de utilizar as funções SDK: setGlobalProperties(), getGlobalProperties(), clearGlobalProperties().

setGlobalPropertyBeforeInit()setGlobalProperties()getGlobalProperties()clearGlobalProperties()
/**
 * Set a Singular global property before SDK initialization.
 * Allows up to 5 key/value pairs. Optionally overwrites existing value for a key.
 * 
 * @param {string} sdkKey - The Singular SDK key.
 * @param {string} productId - The Singular product ID.
 * @param {string} propertyKey - The property key to set.
 * @param {string} propertyValue - The property value to set.
 * @param {boolean} overrideExisting - Whether to overwrite the property if it already exists.
 */
function setGlobalPropertyBeforeInit(sdkKey, productId, propertyKey, propertyValue, overrideExisting) {
  const storageKey = `${sdkKey}_${productId}_global_properties`;
  let properties = {};

  // Try to load any existing properties
  const existing = localStorage.getItem(storageKey);
  if (existing) {
    try {
      properties = JSON.parse(existing) || {};
    } catch (e) {
      // If parsing fails, reset properties
      properties = {};
    }
  }

  // Only allow up to 5 keys
  const propertyExists = Object.prototype.hasOwnProperty.call(properties, propertyKey);
  const numKeys = Object.keys(properties).length;
  if (!propertyExists && numKeys >= 5) {
    console.warn("You can only set up to 5 Singular global properties.");
    return;
  }

  // Apply logic for overwrite or skip
  if (propertyExists && !overrideExisting) {
    console.log(`Global property '${propertyKey}' exists and overrideExisting is false; property not changed.`);
    return;
  }

  properties[propertyKey] = propertyValue;
  localStorage.setItem(storageKey, JSON.stringify(properties));
  console.log("Singular Global Properties set:", storageKey, properties);
}

Monitorização da pesquisa orgânica

Exemplo de rastreamento de pesquisa orgânica
#

IMPORTANTE! Este exemplo é fornecido como uma solução alternativa para ativar o rastreio da Pesquisa orgânica. O código deve ser utilizado apenas como exemplo e atualizado e mantido pelo programador Web com base nas necessidades do seu departamento de marketing. O rastreio da Pesquisa orgânica pode ter significados diferentes consoante o anunciante. Reveja o exemplo e ajuste-o de acordo com as suas necessidades.

Porquê utilizar isto?

  • Assegura que as visitas de pesquisa orgânica são corretamente controladas, mesmo que não estejam presentes parâmetros de campanha.

  • Anexa o parâmetro Singular "Source" wpsrccom o valor do (referrer) e o parâmetro "Campaign Name" wpcn como "OrganicSearch" ao URL para uma atribuição clara.

  • Armazena o URL atual e o referenciador em localStoragepara utilização posterior.

  • JavaScript puro, zero dependências e fácil de integrar.

Como funciona

  1. Verifica o URL da página em busca de parâmetros de campanha conhecidos (Google, Facebook, TikTok, UTMs, etc.).

  2. Se nenhum parâmetro de campanha estiver presente e o referenciador for um mecanismo de pesquisa, anexa:

    • wpsrc (com o referenciador como valor)
    • wpcn (com OrganicSearch como valor)
  3. Actualiza o URL no browser sem recarregar a página.

  4. Armazena o URL atual e o referenciador em localStoragecomo sng_url e sng_ref.

Utilização

  1. Adicione setupOrganicSearchTracking.js ao seu site como uma biblioteca.
  2. Chame a função setupOrganicSearchTracking() ANTES de inicializar o WebSDK.
setupOrganicSearchTracking.js
// singular-web-organic-search-tracking: setupOrganicSearchTracking.js
// Tracks organic search referrals by appending wpsrc and wpcn to the URL if no campaign parameters exist and the referrer is a search engine.

// Configuration for debugging (set to true to enable logs)
const debug = true;

// List of campaign parameters to check for exclusion
const campaignParams = [
    'gclid', 'fbclid', 'ttclid', 'msclkid', 'twclid', 'li_fat_id',
    'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'wpsrc'
];

// Whitelist of legitimate search engine domains (prevents false positives)
const legitimateSearchEngines = new Set([
    // Google domains
    'google.com', 'google.co.uk', 'google.ca', 'google.com.au', 'google.de', 
    'google.fr', 'google.it', 'google.es', 'google.co.jp', 'google.co.kr',
    'google.com.br', 'google.com.mx', 'google.co.in', 'google.ru', 'google.com.sg',
    
    // Bing domains  
    'bing.com', 'bing.co.uk', 'bing.ca', 'bing.com.au', 'bing.de',
    
    // Yahoo domains
    'yahoo.com', 'yahoo.co.uk', 'yahoo.ca', 'yahoo.com.au', 'yahoo.de',
    'yahoo.fr', 'yahoo.it', 'yahoo.es', 'yahoo.co.jp',
    
    // Other search engines
    'baidu.com', 'duckduckgo.com', 'yandex.com', 'yandex.ru',
    'ask.com', 'aol.com', 'ecosia.org', 'startpage.com', 
    'qwant.com', 'seznam.cz', 'naver.com', 'daum.net'
]);

// Extract main domain from hostname (removes subdomains)
function getMainDomain(hostname) {
    if (!hostname) return '';
    
    const lowerHost = hostname.toLowerCase();
    
    // Handle special cases for known search engines with country codes
    const searchEnginePatterns = {
        'google': (host) => {
            // Match google.TLD patterns more precisely
            if (host.includes('google.co.') || host.includes('google.com')) {
                const parts = host.split('.');
                const googleIndex = parts.findIndex(part => part === 'google');
                if (googleIndex !== -1 && googleIndex < parts.length - 1) {
                    return parts.slice(googleIndex).join('.');
                }
            }
            return null;
        },
        'bing': (host) => {
            if (host.includes('bing.co') || host.includes('bing.com')) {
                const parts = host.split('.');
                const bingIndex = parts.findIndex(part => part === 'bing');
                if (bingIndex !== -1 && bingIndex < parts.length - 1) {
                    return parts.slice(bingIndex).join('.');
                }
            }
            return null;
        },
        'yahoo': (host) => {
            if (host.includes('yahoo.co') || host.includes('yahoo.com')) {
                const parts = host.split('.');
                const yahooIndex = parts.findIndex(part => part === 'yahoo');
                if (yahooIndex !== -1 && yahooIndex < parts.length - 1) {
                    return parts.slice(yahooIndex).join('.');
                }
            }
            return null;
        }
    };
    
    // Try specific patterns for major search engines
    for (const [engine, patternFn] of Object.entries(searchEnginePatterns)) {
        if (lowerHost.includes(engine)) {
            const result = patternFn(lowerHost);
            if (result) return result;
        }
    }
    
    // Handle other known engines with simple mapping
    const otherEngines = {
        'baidu.com': 'baidu.com',
        'duckduckgo.com': 'duckduckgo.com', 
        'yandex.ru': 'yandex.ru',
        'yandex.com': 'yandex.com',
        'ask.com': 'ask.com',
        'aol.com': 'aol.com',
        'ecosia.org': 'ecosia.org',
        'startpage.com': 'startpage.com',
        'qwant.com': 'qwant.com',
        'seznam.cz': 'seznam.cz',
        'naver.com': 'naver.com',
        'daum.net': 'daum.net'
    };
    
    for (const [domain, result] of Object.entries(otherEngines)) {
        if (lowerHost.includes(domain)) {
            return result;
        }
    }
    
    // Fallback: Extract main domain by taking last 2 parts (for unknown domains)
    const parts = hostname.split('.');
    if (parts.length >= 2) {
        return parts[parts.length - 2] + '.' + parts[parts.length - 1];
    }
    
    return hostname;
}

// Get query parameter by name, using URL.searchParams with regex fallback for IE11
function getParameterByName(name, url) {
    if (!url) url = window.location.href;
    try {
        return new URL(url).searchParams.get(name) || null;
    } catch (e) {
        if (debug) console.warn('URL API not supported, falling back to regex:', e);
        name = name.replace(/[\[\]]/g, '\\$&');
        const regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)');
        const results = regex.exec(url);
        if (!results) return null;
        if (!results[2]) return '';
        return decodeURIComponent(results[2].replace(/\+/g, ' '));
    }
}

// Check if any campaign parameters exist in the URL
function hasAnyParameter(url, params) {
    return params.some(param => getParameterByName(param, url) !== null);
}

// Improved search engine detection - only checks hostname, uses whitelist
function isSearchEngineReferrer(referrer) {
    if (!referrer) return false;
    
    let hostname = '';
    try {
        hostname = new URL(referrer).hostname.toLowerCase();
    } catch (e) {
        // Fallback regex for hostname extraction
        const match = referrer.match(/^(?:https?:\/\/)?([^\/\?#]+)/i);
        hostname = match ? match[1].toLowerCase() : '';
    }
    
    if (!hostname) return false;
    
    // First check: exact match against whitelist
    if (legitimateSearchEngines.has(hostname)) {
        if (debug) console.log('Exact match found for:', hostname);
        return true;
    }
    
    // Second check: subdomain of legitimate search engine
    const hostParts = hostname.split('.');
    if (hostParts.length >= 3) {
        // Try domain.tld combination (e.g., google.com from www.google.com)
        const mainDomain = hostParts[hostParts.length - 2] + '.' + hostParts[hostParts.length - 1];
        if (legitimateSearchEngines.has(mainDomain)) {
            if (debug) console.log('Subdomain match found for:', hostname, '-> main domain:', mainDomain);
            return true;
        }
        
        // Try last 3 parts for country codes (e.g., google.co.uk from www.google.co.uk)
        if (hostParts.length >= 3) {
            const ccDomain = hostParts[hostParts.length - 3] + '.' + hostParts[hostParts.length - 2] + '.' + hostParts[hostParts.length - 1];
            if (legitimateSearchEngines.has(ccDomain)) {
                if (debug) console.log('Country code domain match found for:', hostname, '-> cc domain:', ccDomain);
                return true;
            }
        }
    }
    
    if (debug) {
        console.log('Hostname not recognized as legitimate search engine:', hostname);
    }
    
    return false;
}

// Main function to update URL with organic search tracking parameters
function setupOrganicSearchTracking() {
    const url = window.location.href;
    const referrer = document.referrer || '';

    // Store URL and referrer in localStorage
    try {
        localStorage.setItem('sng_url', url);
        localStorage.setItem('sng_ref', referrer);
    } catch (e) {
        if (debug) console.warn('localStorage not available:', e);
    }

    if (debug) {
        console.log('Current URL:', url);
        console.log('Referrer:', referrer);
    }

    // Skip if campaign parameters exist or referrer is not a search engine
    const hasCampaignParams = hasAnyParameter(url, campaignParams);
    if (hasCampaignParams || !isSearchEngineReferrer(referrer)) {
        if (debug) console.log('Skipping URL update: Campaign params exist or referrer is not a legitimate search engine');
        return;
    }

    // Extract and validate referrer hostname
    let referrerHostname = '';
    try {
        referrerHostname = new URL(referrer).hostname;
    } catch (e) {
        if (debug) console.warn('Invalid referrer URL, falling back to regex:', e);
        referrerHostname = referrer.match(/^(?:https?:\/\/)?([^\/]+)/i)?.[1] || '';
    }

    // Extract main domain from hostname
    const mainDomain = getMainDomain(referrerHostname);
    
    if (debug) {
        console.log('Full hostname:', referrerHostname);
        console.log('Main domain:', mainDomain);
    }

    // Only proceed if main domain is valid and contains safe characters
    if (!mainDomain || !/^[a-zA-Z0-9.-]+$/.test(mainDomain)) {
        if (debug) console.log('Skipping URL update: Invalid or unsafe main domain');
        return;
    }

    // Update URL with wpsrc and wpcn parameters
    const urlObj = new URL(url);
    
    // Set wpsrc to the main domain (e.g., google.com instead of tagassistant.google.com)
    urlObj.searchParams.set('wpsrc', mainDomain);
    
    // Set wpcn to 'Organic Search' to identify the campaign type
    urlObj.searchParams.set('wpcn', 'Organic Search');

    // Update the URL without reloading (check if history API is available)
    if (window.history && window.history.replaceState) {
        try {
            window.history.replaceState({}, '', urlObj.toString());
            if (debug) console.log('Updated URL with organic search tracking:', urlObj.toString());
        } catch (e) {
            if (debug) console.warn('Failed to update URL:', e);
        }
    } else {
        if (debug) console.warn('History API not supported, cannot update URL');
    }
}

Rastreamento entre subdomínios

Por padrão, o Singular WebSDK gera uma ID de dispositivo Singular e a mantém usando o armazenamento do navegador. Como esse armazenamento não pode ser compartilhado entre subdomínios, o SDK acaba gerando uma nova ID para cada subdomínio.

Se você quiser manter a ID de Dispositivo Singular em todos os subdomínios, poderá usar uma das seguintes opções:

Método B (Avançado): Definir manualmente o ID de dispositivo singular
#

Método B (Avançado): Definir manualmente a ID do dispositivo Singular

Se não quiser que o Singular SDK mantenha a ID do dispositivo automaticamente, é possível manter a ID manualmente entre domínios - por exemplo, usando um cookie de domínio de nível superior ou um cookie do lado do servidor. O valor deve ser uma ID gerada anteriormente pelo Singular, no formato uuid4 válido.

NOTA: É possível ler a ID do dispositivo Singular usando singularSdk.getSingularDeviceId() depois de chamar o método init ou usando InitFinishedCallback.

Método withPersistentSingularDeviceId

Descrição

Inicializa o SDK com opções de configuração, incluindo o Singular Device Id que pretende manter.

Assinatura withPersistentSingularDeviceId(singularDeviceId)
Exemplo de uso
function initSingularSDK() {
  const config = new SingularConfig(sdkKey, sdkSecret, productId)
    .withPersistentSingularDeviceId(singularDeviceId);
  window.singularSdk.init(config);
}

Próximos passos

Artigos relacionados