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:
- Onde encontrá-los: Faça login na sua conta Singular e navegue até Ferramentas do desenvolvedor > Integração do SDK > Chaves do SDK.
-
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.
-
O que é: Um nome exclusivo para seu site, de preferência usando o formato DNS reverso (por exemplo,
- 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.
<script src="https://web-sdk-cdn.singular.net/singular-sdk/latest/singular-sdk.js"></script>
Especifique a versão específica no caminho da biblioteca JavaScript do WebSDK, por exemplo: 1.4.3 é anotado aqui:
<script src="https://web-sdk-cdn.singular.net/singular-sdk/1.4.3/singular-sdk.js"></script>
-
Execute
npm i singular-sdkno diretório raiz do seu projeto ou adicione"singular-sdk": "^1.4.3"à seção de dependências no arquivo package.json e, em seguida, executenpm install. -
Adicione o seguinte código nos scripts que deseja usar o SDK.
import {singularSdk, SingularConfig} from "singular-sdk";
Usando com Next.js / React
Next.js é uma estrutura React que fornece renderização do lado do servidor (SSR), geração de site estático (SSG) e componentes do servidor React. Como o Singular WebSDK requer APIs do navegador (DOM, localStorage, cookies), você deve carregá-lo somente do lado do cliente.
IMPORTANTE: Nunca carregue o Singular SDK no código do lado do servidor (por exemplo, getServerSideProps, Componentes do servidor React ou ambientes Node.js). Isso causará erros porque as APIs do navegador não estão disponíveis no servidor.
Método 1: Usando o componente de script Next.js (recomendado)
O componente <Script> do Next.js fornece estratégias de carregamento otimizadas e evita a injeção de scripts duplicados em alterações de rota.
// pages/_app.tsx (Pages Router) or app/layout.tsx (App Router)
import Script from 'next/script'
export default function App({ Component, pageProps }) {
return (
<>
<Script
src="https://web-sdk-cdn.singular.net/singular-sdk/latest/singular-sdk.js"
strategy="afterInteractive"
/>
<Component {...pageProps} />
</>
)
}
Opções de estratégia de script:
-
afterInteractive(recomendado): Carrega depois que a página se torna interativa - ideal para análise e rastreamento. -
lazyOnloadCarregamento durante o tempo ocioso do navegador - use para widgets não críticos.
Método 2: Importação dinâmica para carregamento no nível do componente
Para controlo ao nível dos componentes, utilize as importações dinâmicas Next.js com ssr: false para carregar o script apenas quando necessário:
// pages/index.tsx
import dynamic from 'next/dynamic'
const SingularSDKLoader = dynamic(
() => import('../components/SingularSDKLoader'),
{ ssr: false }
)
export default function Page() {
return (
<div>
<SingularSDKLoader />
<h1>Your Page Content</h1>
</div>
)
}
// components/SingularSDKLoader.tsx
'use client' // Required for Next.js App Router
import { useEffect } from 'react'
export default function SingularSDKLoader() {
useEffect(() => {
// Load Singular SDK script dynamically
const script = document.createElement('script')
script.src = 'https://web-sdk-cdn.singular.net/singular-sdk/latest/singular-sdk.js'
script.async = true
document.body.appendChild(script)
// Cleanup on unmount
return () => {
if (document.body.contains(script)) {
document.body.removeChild(script)
}
}
}, [])
return null
}
Configuração de variáveis de ambiente
DICA! Armazene suas credenciais Singular em variáveis de ambiente prefixadas com NEXT_PUBLIC_ para torná-las disponíveis no navegador:
# .env.local
NEXT_PUBLIC_SINGULAR_SDK_KEY=your_sdk_key_here
NEXT_PUBLIC_SINGULAR_SDK_SECRET=your_sdk_secret_here
NEXT_PUBLIC_SINGULAR_PRODUCT_ID=com.your-website
Essas variáveis de ambiente estarão acessíveis no seu código do lado do cliente e podem ser usadas durante a inicialização na Etapa 2.
Roteador de aplicativos vs. Roteador de páginas
| Tipo de roteador | Principais diferenças | Localização do script |
|---|---|---|
| Roteador de aplicativos (Next.js 13+) |
Os componentes são componentes do servidor por padrão. Adicione a diretiva 'use client' para APIs do browser. |
app/layout.tsx
|
| Roteador de páginas (Next.js 12 e anteriores) | Todos os componentes são componentes de cliente por padrão. |
pages/_app.tsx
|
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.
- Crie uma função de inicialização e chame-a no DOM Ready após o carregamento da página.
- Certifique-se de que a inicialização ocorra antes que qualquer outro evento Singular seja relatado.
- 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.
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 |
|
Navegando para uma nova rota/página |
Chamar |
|
No carregamento inicial no SPA |
Não chamar |
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();
});
Inicialização Next.js / React
Depois de carregar o script do SDK na Etapa 1, inicialize-o quando o DOM estiver pronto. A inicialização deve ocorrer no lado do cliente após o carregamento do script.
Declarações de tipo do TypeScript
Crie um arquivo de declaração de tipo para adicionar suporte ao TypeScript para o SDK Singular:
// types/singular.d.ts
interface SingularConfig {
new (sdkKey: string, sdkSecret: string, productId: string): SingularConfig;
withCustomUserId(userId: string): SingularConfig;
withAutoPersistentSingularDeviceId(domain: string): SingularConfig;
withLogLevel(level: number): SingularConfig;
withSessionTimeoutInMinutes(timeout: number): SingularConfig;
withProductName(productName: string): SingularConfig;
withPersistentSingularDeviceId(singularDeviceId: string): SingularConfig;
withInitFinishedCallback(callback: (params: { singularDeviceId: string }) => void): SingularConfig;
}
interface SingularSDK {
init(config: SingularConfig): void;
event(eventName: string, attributes?: Record<string, any>): void;
conversionEvent(eventName: string): void;
revenue(eventName: string, currency: string, amount: number, attributes?: Record<string, any>): void;
login(userId: string): void;
logout(): void;
pageVisit(): void;
buildWebToAppLink(baseLink: string): string;
getSingularDeviceId(): string;
setGlobalProperties(key: string, value: string, override: boolean): void;
getGlobalProperties(): Record<string, string>;
clearGlobalProperties(): void;
}
interface Window {
singularSdk: SingularSDK;
SingularConfig: typeof SingularConfig;
}
Inicialização básica com o componente de script Next.js
Ao usar o componente Script da Etapa 1, adicione o retorno de chamada onLoadpara inicializar o SDK:
// pages/_app.tsx or app/layout.tsx
import Script from 'next/script'
export default function App({ Component, pageProps }) {
return (
<>
<Script
src="https://web-sdk-cdn.singular.net/singular-sdk/latest/singular-sdk.js"
strategy="afterInteractive"
onLoad={() => {
// Initialize Singular SDK after script loads
const config = new (window as any).SingularConfig(
process.env.NEXT_PUBLIC_SINGULAR_SDK_KEY,
process.env.NEXT_PUBLIC_SINGULAR_SDK_SECRET,
process.env.NEXT_PUBLIC_SINGULAR_PRODUCT_ID
);
window.singularSdk.init(config);
}}
/>
<Component {...pageProps} />
</>
)
}
Inicialização com o Hook useEffect
Se você estiver usando o método de importação dinâmica ou precisar de mais controle, inicialize usando useEffect:
// components/SingularInitializer.tsx
'use client'
import { useEffect } from 'react'
export default function SingularInitializer() {
useEffect(() => {
// Wait for script to load, then initialize
const checkAndInit = () => {
if (window.singularSdk && window.SingularConfig) {
const config = new window.SingularConfig(
process.env.NEXT_PUBLIC_SINGULAR_SDK_KEY!,
process.env.NEXT_PUBLIC_SINGULAR_SDK_SECRET!,
process.env.NEXT_PUBLIC_SINGULAR_PRODUCT_ID!
);
window.singularSdk.init(config);
} else {
// Retry if SDK not loaded yet
setTimeout(checkAndInit, 100);
}
};
checkAndInit();
}, []); // Empty dependency - run once on mount
return null;
}
Inicialização com opções de configuração
Encadeie os métodos .with para ativar recursos adicionais, como rastreamento entre subdomínios ou IDs de usuário personalizados:
const config = new window.SingularConfig(
process.env.NEXT_PUBLIC_SINGULAR_SDK_KEY!,
process.env.NEXT_PUBLIC_SINGULAR_SDK_SECRET!,
process.env.NEXT_PUBLIC_SINGULAR_PRODUCT_ID!
)
.withAutoPersistentSingularDeviceId('your-domain.com')
.withCustomUserId(userId)
.withLogLevel(2); // 0=None, 1=Warn, 2=Info, 3=Debug
window.singularSdk.init(config);
Roteamento de aplicativo de página única (SPA)
Para aplicativos Next.js com roteamento do lado do cliente, é necessário rastrear as alterações de rota separadamente da inicialização inicial.
IMPORTANTE: Inicialize o SDK apenas uma vez no primeiro carregamento da página. Em seguida, chame window.singularSdk.pageVisit()em cada alteração de rota subsequente.
// components/SingularPageTracker.tsx
'use client'
import { useEffect, useRef } from 'react'
import { usePathname } from 'next/navigation' // App Router
// or import { useRouter } from 'next/router' // Pages Router
export default function SingularPageTracker() {
const pathname = usePathname() // App Router
// const router = useRouter(); const pathname = router.pathname // Pages Router
const isInitialized = useRef(false);
// Initialize SDK once on mount
useEffect(() => {
if (!isInitialized.current && window.singularSdk && window.SingularConfig) {
const config = new window.SingularConfig(
process.env.NEXT_PUBLIC_SINGULAR_SDK_KEY!,
process.env.NEXT_PUBLIC_SINGULAR_SDK_SECRET!,
process.env.NEXT_PUBLIC_SINGULAR_PRODUCT_ID!
);
window.singularSdk.init(config);
isInitialized.current = true;
}
}, []); // Empty dependency - runs once
// Track page visits on route changes (NOT on initial mount)
useEffect(() => {
if (isInitialized.current && window.singularSdk) {
window.singularSdk.pageVisit();
}
}, [pathname]); // Runs when pathname changes
return null;
}
Usando um retorno de chamada de inicialização
Se você precisar executar um código depois que o SDK estiver pronto (por exemplo, para obter a ID de dispositivo singular), use .withInitFinishedCallback():
const config = new window.SingularConfig(
process.env.NEXT_PUBLIC_SINGULAR_SDK_KEY!,
process.env.NEXT_PUBLIC_SINGULAR_SDK_SECRET!,
process.env.NEXT_PUBLIC_SINGULAR_PRODUCT_ID!
)
.withInitFinishedCallback((initParams) => {
const singularDeviceId = initParams.singularDeviceId;
console.log('Singular Device ID:', singularDeviceId);
// Store for analytics or pass to other services
});
window.singularSdk.init(config);
Práticas recomendadas para a inicialização do Next.js
- Inicialize o SDK apenas uma vez na montagem do aplicativo, não em cada alteração de rota.
-
Use variáveis de ambiente com o prefixo
NEXT_PUBLIC_para credenciais do SDK. -
Para SPAs, chame
window.singularSdk.pageVisit()em alterações de rota para rastrear a navegação. -
Use
useRefpara rastrear o estado de inicialização e evitar a inicialização duplicada. -
Adicione verificações nulas para
window.singularSdkantes de chamar métodos para evitar erros durante a SSR. - Teste no modo de desenvolvimento para garantir que o SDK seja inicializado corretamente sem erros no lado do servidor.
Lista de verificação do desenvolvedor para Next.js
- Verifique se o script foi carregado com êxito na Etapa 1.
- Configure variáveis de ambiente com suas credenciais do SDK.
- Crie declarações de tipo TypeScript para uma melhor experiência do desenvolvedor.
-
Escolha o método de inicialização: Componente de script
onLoadou ganchouseEffect. -
Para SPAs, implemente o rastreamento de alterações de rota com
pageVisit(). -
Teste a inicialização verificando a consola para o evento
__PAGE_VISIT__.
-
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-namee 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:
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 |
|
|
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.
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);
Rastreamento de eventos de conversão
Rastreie um evento de conversão:
var eventName = 'sng_complete_registration';
window.singularSdk.conversionEvent(eventName);
Rastreamento de eventos de receita
Rastreie eventos de conversão com informações de receita e adicione atributos opcionais para obter mais contexto:
- Passe a moeda da receita como um código de moeda ISO 4217 de três letras, como "USD", "EUR" ou "INR".
- Passe o valor do montante da receita como um valor decimal.
// Revenue event tracking
var revenueEventName = 'sng_ecommerce_purchase';
var revenueCurrency = 'USD';
var revenueAmount = 49.99;
window.singularSdk.revenue(revenueEventName, revenueCurrency, revenueAmount);
// Optional: With attributes for more context
var revenueEventName = 'sng_ecommerce_purchase';
var revenueCurrency = 'USD';
var revenueAmount = 49.99;
var attributes = {
key1: 'value1', // First custom attribute
key2: 'value2' // Second custom attribute
};
window.singularSdk.revenue(revenueEventName, revenueCurrency, revenueAmount, attributes);
Padrões comuns de implementação de eventos
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);
});
Eventos de clique de botão
O mecanismo de rastreamento de eventos de clique em botão usa JavaScript para monitorar quando um usuário clica em um botão com a ID checkout-buttone aciona um evento checkout_started usando o SDK Singular para rastreamento analítico. O código emprega um ouvinte de eventos clickpara detetar a interação do botão, inclui um mecanismo para evitar múltiplos disparos de eventos (utilizando um atributo data-event-fired ) e envia o evento com atributos personalizados (cart_value e currency) para a plataforma Singular. Isto é ideal para análises de comércio eletrónico para rastrear quando os utilizadores iniciam um processo de checkout, garantindo dados precisos ao disparar o evento apenas uma vez por sessão de página.
/**
* Tracks a checkout started event with the Singular SDK when a button is clicked.
* @param {string} eventName - The name of the event to track (e.g., 'checkout_started').
* @param {Object} attributes - A JSON object with custom event attributes.
* @param {number} attributes.cart_value - The total value of the cart (e.g., 99.99).
* @param {string} attributes.currency - The currency of the cart value (e.g., 'USD').
*/
document.getElementById('checkout-button').addEventListener('click', function(e) {
// Prevent multiple fires
if (this.hasAttribute('data-event-fired')) {
return;
}
this.setAttribute('data-event-fired', 'true');
var eventName = 'checkout_started';
var attributes = {
cart_value: 99.99, // Total value of the cart
currency: 'USD' // Currency of the cart value
};
window.singularSdk.event(eventName, attributes);
});
Eventos de envio de formulários
O mecanismo de rastreamento de eventos de envio de formulário usa JavaScript para monitorar quando um usuário envia um formulário com o ID signup-form e dispara um evento signup_completed usando o Singular SDK para rastreamento analítico. O código emprega um ouvinte de eventos submitpara detetar o envio do formulário, inclui um mecanismo para evitar vários disparos de eventos (usando um atributo data-event-fired) e envia o evento com um atributo personalizado (signup_method) para a plataforma Singular. Isso é ideal para rastrear inscrições de usuários em fluxos de trabalho de análise, como monitorar a aquisição de usuários ou métodos de inscrição, garantindo dados precisos ao disparar o evento apenas uma vez por sessão de página.
Abaixo está um exemplo simples que adapta o código original para usar window.singularSdk.event, separando o nome do evento e os atributos para maior clareza.
/**
* Tracks a signup completed event with the Singular SDK when a form is submitted.
* @param {string} eventName - The name of the event to track (e.g., 'signup_completed').
* @param {Object} attributes - A JSON object with custom event attributes.
* @param {string} attributes.signup_method - The method used for signup (e.g., 'email').
* @example
* // Track a signup event with additional attributes
* var eventName = 'signup_completed';
* var attributes = {
* signup_method: 'email',
* email: document.getElementById('email')?.value || 'unknown'
* };
* window.singularSdk.event(eventName, attributes);
*/
document.getElementById('signup-form').addEventListener('submit', function(e) {
// Prevent multiple fires
if (this.hasAttribute('data-event-fired')) {
return;
}
this.setAttribute('data-event-fired', 'true');
var eventName = 'signup_completed';
var attributes = {
signup_method: 'email' // Method used for signup
};
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:
// After user logs in or signs up
var userId = 'user_12345';
window.singularSdk.login(userId);
// After user logs out
window.singularSdk.logout();
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.
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
- Abra seu site em um navegador
- Abra as Ferramentas do desenvolvedor (F12 ou clique com o botão direito do mouse → Inspecionar)
- Vá para a guia Console
- Digite
typeof singularSdke pressione Enter - Você deve ver um objeto "function", não "undefined"
Verificar solicitações de rede
- Abra as Ferramentas do desenvolvedor (F12)
- Vá para o separador Rede
- Recarregue sua página
- Filtre por
singularousdk-api - Procure solicitações para
sdk-api-v1.singular.net - Clique em uma solicitação para exibir detalhes
- Verifique se o código de status é
200 - 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
- Acione um evento em seu site (clique em um botão, envie um formulário, etc.)
- Na guia Rede, procure uma nova solicitação para
sdk-api-v1.singular.net - Clique na solicitação e visualize a guia Carga útil ou Solicitação
-
Verifique se o parâmetro do nome do evento "
n" aparece no pedido
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.
- Na plataforma Singular, vá para Ferramentas de desenvolvedor > Console de teste.
- Clique em Adicionar dispositivo.
- Selecione a plataforma"Web" e insira o ID do dispositivo Singular.
- Obtenha a ID do dispositivo Singular a partir do payload do navegador. Verifique a captura de ecrã acima para localizar o
SDIDnum evento. - 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.
- 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.
- 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.
- 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.
- 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 WebSDKbuildWebToAppLink(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.
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
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
localstorageaté 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().
/**
* 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);
}
/** * Define uma propriedade global Singular antes da inicialização do SDK. * Permite até 5 pares chave/valor. Opcionalmente, substitui o valor existente para uma chave. * @param {string} propertyKey - A chave da propriedade a definir.
* @param {string} propertyValue - O valor da propriedade a definir. * @param {boolean} overrideExisting - Se a propriedade deve ser substituída se já existir. */ // Utilização window.singularSdk.setGlobalProperties(propertyKey, propertyValue, overrideExisting);
// Get the JSON Object of global property values.
// Usage
window.singularSdk.getGlobalProperties();
// Clears all global property values.
// Usage
window.singularSdk.clearGlobalProperties();
Monitorização da 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"wpcncomo "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
-
Verifica o URL da página em busca de parâmetros de campanha conhecidos (Google, Facebook, TikTok, UTMs, etc.).
-
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)
-
-
Actualiza o URL no browser sem recarregar a página.
-
Armazena o URL atual e o referenciador em
localStoragecomosng_urlesng_ref.
Utilização
-
Adicione
setupOrganicSearchTracking.jsao seu site como uma biblioteca. -
Chame a função
setupOrganicSearchTracking()ANTES de inicializar o WebSDK.
// 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 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 |
|
Próximos passos
- Criar links de sites no Singular para suas campanhas na Web
- Siga nossos guias para Google Ads Web, Facebook Web e TikTok Ads Web, se você executar campanhas da Web para inventário móvel.
- Monitorar seus dados em relatórios do Singular