@hotfyllc/hotfy-sdk-rn
v1.0.0
Published
Hotfy CDP SDK for React Native — event tracking, identity, attribution, push, ad revenue (AdMob ILAR).
Readme
@hotfyllc/hotfy-sdk-rn
SDK React Native para o Hotfy CDP (Customer Data Platform). Rastreamento de eventos, identidade de usuario, atribuicao de instalacao, receita de anuncios e notificacoes push.
Indice
- Instalacao
- Quick Start
- Configuracao
- Event Tracking
- Identity
- Atribuicao
- Push Notifications
- Ad Revenue (AdMob ILAR)
- Offline Queue
- Contexto Automatico
- Referencia da API
- Eventos Padrao
- Troubleshooting
- Exemplos Completos
1. Instalacao
Dependencia principal
npm install @hotfyllc/hotfy-sdk-rnPeer dependency obrigatoria
O SDK usa @react-native-async-storage/async-storage para persistir a fila de eventos e o ID anonimo entre sessoes.
npm install @react-native-async-storage/async-storagePara projetos com Expo:
npx expo install @react-native-async-storage/async-storagePara projetos React Native puro, execute o link nativo:
npx pod-install # iOSPeer dependency opcional
Para obter device_model e app_version automaticamente, instale:
npm install react-native-device-info
npx pod-install # iOSSem essa dependencia, o SDK usa valores de fallback (Platform.OS para modelo, "0.0.0" para versao).
2. Quick Start
Inicialize o SDK uma unica vez na entrada do seu app e comece a rastrear eventos em segundos:
import { HotfyCdp } from '@hotfyllc/hotfy-sdk-rn';
// 1. Inicializar (uma vez, no App.tsx ou _layout.tsx)
await HotfyCdp.init({
apiKey: 'sua-api-key',
baseUrl: 'https://api.cdp.hotfy.com',
});
// 2. Rastrear um evento
HotfyCdp.track('app_open');
// 3. Rastrear tela
HotfyCdp.screen('Home');
// 4. Identificar usuario
await HotfyCdp.identify('user-123', { plan: 'premium' });
// 5. Receita de anuncio
HotfyCdp.trackAdImpression({ revenueMicros: 1500, adFormat: 'rewarded' });3. Configuracao
O metodo HotfyCdp.init(config) aceita o objeto CdpConfig:
| Opcao | Tipo | Obrigatorio | Padrao | Descricao |
|------------------|-----------|-------------|-----------|---------------------------------------------------------------------------|
| apiKey | string | Sim | — | Chave de API para autenticacao (header X-Api-Key). |
| baseUrl | string | Sim | — | URL base do backend (ex: https://api.cdp.hotfy.com). Barra final removida automaticamente. |
| appId | string | Nao | undefined | UUID do app. Opcional — inferido pela apiKey no backend. |
| flushInterval | number | Nao | 30000 | Intervalo em milissegundos entre flushes automaticos. |
| flushAt | number | Nao | 20 | Numero de eventos que dispara flush automatico. |
| maxQueueSize | number | Nao | 1000 | Tamanho maximo da fila. Eventos mais antigos sao descartados se excedido. |
| debug | boolean | Nao | false | Habilita logs de debug no console ([HotfyCdp] ...). |
Exemplo completo de configuracao
await HotfyCdp.init({
apiKey: 'hcdp_live_abc123xyz',
baseUrl: 'https://api.cdp.hotfy.com',
appId: '550e8400-e29b-41d4-a716-446655440000',
flushInterval: 15000, // flush a cada 15 segundos
flushAt: 10, // flush ao acumular 10 eventos
maxQueueSize: 500, // fila maxima de 500 eventos
debug: __DEV__, // debug apenas em desenvolvimento
});4. Event Tracking
track(eventName, properties?)
Rastreia um evento customizado. O evento e colocado na fila e enviado em batch ao backend.
// Evento simples
HotfyCdp.track('button_clicked');
// Evento com propriedades
HotfyCdp.track('purchase', {
product_id: 'premium_monthly',
price_usd: 9.99,
currency: 'USD',
});
// Evento de login
HotfyCdp.track('login', {
method: 'google',
});screen(screenName, properties?)
Rastreia uma visualizacao de tela. Internamente gera um evento screen_view com screen_name como propriedade.
// Tela simples
HotfyCdp.screen('Home');
// Tela com propriedades adicionais
HotfyCdp.screen('ProductDetail', {
product_id: 'abc-123',
category: 'games',
});Batch via fila
Todos os eventos track e screen sao enfileirados localmente antes de serem enviados ao backend em batch. O envio ocorre automaticamente quando:
- A fila atinge
flushAteventos (padrao: 20) - O timer de
flushIntervaldispara (padrao: 30 segundos) - O app vai para background (lifecycle event)
HotfyCdp.flush()e chamado manualmente
O endpoint de batch e POST /v1/batch com o payload:
{
"events": [ ...array de CdpEvent... ],
"sent_at": "2026-02-18T10:00:00.000Z"
}5. Identity
identify(userId, traits?)
Vincula o ID anonimo gerado pelo SDK ao ID real do usuario no seu sistema. Chame apos o login.
await HotfyCdp.identify('user-123', {
email: '[email protected]',
name: 'Joao Silva',
plan: 'premium',
created_at: '2026-01-15T00:00:00Z',
});O metodo persiste o userId no storage local e envia um POST /v1/identify ao backend com:
{
"anonymous_id": "01950f3e-7d2a-...",
"user_id": "user-123",
"traits": { "email": "[email protected]", "plan": "premium" },
"timestamp": "2026-02-18T10:00:00.000Z"
}getAnonymousId()
Retorna o ID anonimo UUID v7 gerado na primeira instalacao. Persistido entre sessoes via AsyncStorage.
const anonId = HotfyCdp.getAnonymousId();
console.log(anonId); // "01950f3e-7d2a-7000-b4f2-..."getUserId()
Retorna o userId identificado, ou undefined se o usuario ainda nao foi identificado.
const userId = HotfyCdp.getUserId();
if (userId) {
console.log('Usuario logado:', userId);
}reset()
Faz flush de todos os eventos pendentes, gera um novo ID anonimo e limpa o userId. Use no logout.
await HotfyCdp.reset();
// Novo anonymousId gerado automaticamente6. Atribuicao
captureAttribution(params?)
Captura os dados de atribuicao da instalacao. Chame apenas uma vez, no primeiro app_open. O SDK persiste internamente um flag e ignora chamadas subsequentes.
await HotfyCdp.captureAttribution(params);O endpoint e POST /v1/attribution.
Exemplo com Install Referrer (Google Ads / gclid)
import { InstallReferrer, InstallReferrerResponse } from 'react-native-install-referrer';
async function captureGoogleAdsAttribution() {
try {
const referrer: InstallReferrerResponse = await InstallReferrer.getReferrer();
// referrer.installReferrer => "gclid=Cj0KCQiA..."
const params = new URLSearchParams(referrer.installReferrer);
const gclid = params.get('gclid');
await HotfyCdp.captureAttribution({
sourceType: 'google_ads',
touch: 'last',
gclid: gclid ?? undefined,
});
} catch (err) {
// Install Referrer nao disponivel (ex: sideload)
await HotfyCdp.captureAttribution();
}
}Exemplo com Meta Referrer (MIR)
async function captureMetaAttribution(metaEncryptedData: string) {
await HotfyCdp.captureAttribution({
sourceType: 'meta',
touch: 'last',
metaEncryptedData, // dados criptografados AES-256-GCM
metaPublisherPlatform: 'facebook',
});
}Exemplo com UTM params (afiliados / owned media)
async function captureUtmAttribution(referrerString: string) {
// referrerString => "utm_source=newsletter&utm_medium=email&utm_campaign=re-engage"
const params = new URLSearchParams(referrerString);
await HotfyCdp.captureAttribution({
sourceType: 'affiliate',
touch: 'last',
utmSource: params.get('utm_source') ?? undefined,
utmMedium: params.get('utm_medium') ?? undefined,
utmCampaign: params.get('utm_campaign') ?? undefined,
utmContent: params.get('utm_content') ?? undefined,
utmTerm: params.get('utm_term') ?? undefined,
});
}Parametros de AttributionParams
| Campo | Tipo | Descricao |
|-------------------------|-----------------------|--------------------------------------------------------|
| sourceType | string | Tipo da fonte: google_ads, meta, affiliate, etc. |
| touch | 'first' \| 'last' | Modelo de atribuicao. |
| gclid | string | Google Click ID. |
| utmSource | string | UTM source. |
| utmMedium | string | UTM medium. |
| utmCampaign | string | UTM campaign. |
| utmContent | string | UTM content. |
| utmTerm | string | UTM term. |
| networkClickId | string | Click ID de outras redes (ex: Kwai). |
| matchedGaid | string | GAID para match com tracking links. Preenchido automaticamente pelo SDK se nao fornecido. |
| googleCampaignName | string | Nome da campanha Google Ads. |
| googleAdgroupName | string | Nome do ad group Google Ads. |
| googleKeyword | string | Palavra-chave Google Ads. |
| googleNetworkType | string | Tipo de rede Google Ads. |
| metaCampaignGroupName | string | Nome do campaign group Meta. |
| metaCampaignName | string | Nome da campanha Meta. |
| metaAdgroupName | string | Nome do ad group Meta. |
| metaPublisherPlatform | string | Plataforma Meta (facebook, instagram, etc.). |
| metaEncryptedData | string | Dados criptografados do Meta Install Referrer (MIR). |
7. Push Notifications
registerPushToken(token, meta?)
Registra o token FCM do dispositivo no backend. Chame sempre que o token mudar (onTokenRefresh).
import messaging from '@react-native-firebase/messaging';
async function setupPush() {
const token = await messaging().getToken();
await HotfyCdp.registerPushToken(token, {
platform: Platform.OS as 'android' | 'ios',
deviceModel: 'Pixel 7', // opcional
appVersion: '2.1.0', // opcional
locale: 'pt_BR', // opcional
timezone: 'America/Sao_Paulo', // opcional
});
// Escutar refresh de token
messaging().onTokenRefresh(async (newToken) => {
await HotfyCdp.registerPushToken(newToken, {
platform: Platform.OS as 'android' | 'ios',
});
});
}trackPushDelivered(sendId)
Rastreia que uma notificacao push foi recebida pelo dispositivo. Gera o evento push_received.
// No handler de mensagem em background/foreground
messaging().onMessage(async (remoteMessage) => {
const sendId = Number(remoteMessage.data?.send_id);
if (sendId) {
HotfyCdp.trackPushDelivered(sendId);
}
});trackPushOpened(sendId)
Rastreia que o usuario abriu a notificacao push. Gera o evento push_opened.
// Quando o app e aberto via notificacao
messaging().onNotificationOpenedApp((remoteMessage) => {
const sendId = Number(remoteMessage.data?.send_id);
if (sendId) {
HotfyCdp.trackPushOpened(sendId);
}
});
// Verificar se o app foi aberto por notificacao (cold start)
const initialMessage = await messaging().getInitialNotification();
if (initialMessage) {
const sendId = Number(initialMessage.data?.send_id);
if (sendId) {
HotfyCdp.trackPushOpened(sendId);
}
}8. Ad Revenue (AdMob ILAR)
trackAdImpression(data)
Rastreia uma impressao de anuncio com receita por impressao (Impression-Level Ad Revenue). Integra diretamente com o callback onPaidEvent do AdMob.
A receita e armazenada em micros (1 micro = $0.000001) para evitar problemas de ponto flutuante.
HotfyCdp.trackAdImpression({
revenueMicros: 1500, // $0.0015
currency: 'USD',
precision: 'ESTIMATED', // ESTIMATED | PUBLISHER_PROVIDED | PRECISE
adUnitId: 'ca-app-pub-xxx/yyy',
adSource: 'AdMob',
adFormat: 'rewarded', // banner | interstitial | rewarded | native
});Interface AdImpressionData
| Campo | Tipo | Obrigatorio | Descricao |
|-----------------|----------|-------------|------------------------------------------------------------------|
| revenueMicros | number | Sim | Receita em micros. 1.000.000 micros = $1.00. |
| currency | string | Nao | Codigo da moeda. Padrao: "USD". |
| precision | string | Nao | Precisao: ESTIMATED, PUBLISHER_PROVIDED ou PRECISE. Padrao: "ESTIMATED". |
| adUnitId | string | Nao | ID da unidade de anuncio AdMob. |
| adSource | string | Nao | Rede de anuncio (ex: "AdMob", "IronSource", "AppLovin"). |
| adFormat | string | Nao | Formato: banner, interstitial, rewarded ou native. |
Exemplo completo com react-native-google-mobile-ads
import {
RewardedAd,
RewardedAdEventType,
AdEventType,
TestIds,
} from 'react-native-google-mobile-ads';
const AD_UNIT_ID = __DEV__ ? TestIds.REWARDED : 'ca-app-pub-XXXX/YYYY';
export function useRewardedAd() {
const [rewarded, setRewarded] = React.useState<RewardedAd | null>(null);
React.useEffect(() => {
const ad = RewardedAd.createForAdRequest(AD_UNIT_ID, {
requestNonPersonalizedAdsOnly: true,
});
// Rastrear receita quando o anuncio for pago (ILAR)
ad.addAdEventListener(AdEventType.PAID, (revenue) => {
// revenue.value em unidades de moeda (ex: 0.0015 para $0.0015)
// Converter para micros multiplicando por 1.000.000
HotfyCdp.trackAdImpression({
revenueMicros: Math.round(revenue.value * 1_000_000),
currency: revenue.currencyCode,
precision: revenue.precisionType,
adUnitId: AD_UNIT_ID,
adSource: ad.adSourceName ?? 'AdMob',
adFormat: 'rewarded',
});
});
ad.addAdEventListener(RewardedAdEventType.LOADED, () => {
setRewarded(ad);
});
ad.load();
return () => ad.destroy();
}, []);
const show = () => rewarded?.show();
return { show, loaded: rewarded !== null };
}9. Offline Queue
Como funciona
O SDK implementa uma fila de eventos offline com persistencia automatica:
- Enqueue:
track()escreen()adicionam eventos a fila em memoria e persistem noAsyncStoragede forma assincrona (fire-and-forget). - Auto-flush por volume: quando a fila atinge
flushAteventos (padrao: 20), o flush e disparado automaticamente. - Auto-flush por tempo: a cada
flushIntervalms (padrao: 30 segundos), o SDK tenta enviar os eventos acumulados. - Flush no background: o
LifecycleManagerescuta oAppStatedo React Native e faz flush quando o app vai para background (active -> background). - Retry automatico: se o flush falhar (rede indisponivel, erro do servidor), os eventos sao recolocados no inicio da fila e tentados no proximo ciclo.
- Restauracao ao inicializar: no
HotfyCdp.init(), eventos persistidos de sessoes anteriores sao restaurados para a fila em memoria.
Limites
| Limite | Valor | Comportamento ao exceder |
|-------------------|---------|----------------------------------------------|
| maxQueueSize | 1000 | Eventos mais antigos sao descartados (shift). |
| Batch por flush | flushAt (20) | Flush processa ate flushAt eventos por vez. |
Flush manual no background
Se voce usa AppState diretamente ou tem um handler proprio de background, pode forcar o flush:
import { AppState } from 'react-native';
AppState.addEventListener('change', async (nextState) => {
if (nextState === 'background') {
await HotfyCdp.flush();
}
});10. Contexto Automatico
O SDK coleta automaticamente um contexto de dispositivo em cada evento. Os campos sao coletados uma unica vez e cacheados (resetado no reset()).
| Campo | Tipo | Fonte | Exemplo |
|----------------|----------|-----------------------------------------------------|----------------------------|
| os | string | Platform.OS | "android", "ios" |
| os_version | string | Platform.Version | "34", "17.2" |
| device_model | string | react-native-device-info (opcional) ou fallback | "Pixel 7", "iPhone" |
| app_version | string | react-native-device-info (opcional) ou "0.0.0" | "2.1.0" |
| locale | string | NativeModules (iOS: AppleLocale, Android: I18nManager) | "pt_BR" |
| timezone | string | Intl.DateTimeFormat().resolvedOptions().timeZone | "America/Sao_Paulo" |
| screen_width | number | Dimensions.get('window').width | 390 |
| screen_height| number | Dimensions.get('window').height | 844 |
| sdk_name | string | Constante interna | "hotfy-cdp-rn" |
| sdk_version | string | Constante interna | "1.0.0" |
| advertising_id | string? | react-native-advertising-id ou NativeModules.HotfyCdp | "38400000-8cf0-..." |
Google Advertising ID (GAID)
O SDK coleta o GAID automaticamente durante o init() (Android only).
- Coleta via:
react-native-advertising-id(peer dep opcional) ouNativeModules.HotfyCdp(fallback) - Respeita opt-out: se
isLimitAdTrackingEnabled, retornaundefined - iOS: retorna
undefined(IDFA nao coletado por enquanto) - O GAID aparece em
context.advertising_idem todos os eventos captureAttribution()auto-preenchematched_gaidcom o GAID se nao fornecido
Para melhor suporte, instale a peer dep:
npm install react-native-advertising-idAcessar o contexto
const ctx = HotfyCdp.getDeviceContext();
console.log(ctx.os); // "android"
console.log(ctx.app_version); // "2.1.0"
console.log(ctx.timezone); // "America/Sao_Paulo"11. Referencia da API
Todos os metodos publicos do singleton HotfyCdp:
Inicializacao e controle
// Inicializar o SDK (chamar uma vez antes de qualquer outro metodo)
HotfyCdp.init(config: CdpConfig): Promise<void>
// Forcar envio de todos os eventos em fila
HotfyCdp.flush(): Promise<void>
// Encerrar o SDK (flush, parar timers, limpar instancia)
HotfyCdp.shutdown(): Promise<void>Rastreamento de eventos
// Rastrear evento customizado
HotfyCdp.track(eventName: string, properties?: Record<string, unknown>): void
// Rastrear visualizacao de tela
HotfyCdp.screen(screenName: string, properties?: Record<string, unknown>): voidIdentidade
// Vincular usuario anonimo a ID real
HotfyCdp.identify(userId: string, traits?: Record<string, unknown>): Promise<void>
// Obter ID anonimo atual
HotfyCdp.getAnonymousId(): string
// Obter ID do usuario identificado
HotfyCdp.getUserId(): string | undefined
// Resetar identidade (novo ID anonimo, limpar userId)
HotfyCdp.reset(): Promise<void>Atribuicao
// Capturar dados de atribuicao (executado apenas uma vez por instalacao)
HotfyCdp.captureAttribution(params?: AttributionParams): Promise<void>Push Notifications
// Registrar token FCM
HotfyCdp.registerPushToken(
token: string,
meta?: {
platform?: 'android' | 'ios';
deviceModel?: string;
appVersion?: string;
locale?: string;
timezone?: string;
}
): Promise<void>
// Rastrear push recebido (evento: push_received)
HotfyCdp.trackPushDelivered(sendId: number): void
// Rastrear push aberto (evento: push_opened)
HotfyCdp.trackPushOpened(sendId: number): voidAd Revenue
// Rastrear impressao de anuncio (evento: ad_impression)
HotfyCdp.trackAdImpression(data: AdImpressionData): voidContexto
// Obter contexto de dispositivo coletado automaticamente
HotfyCdp.getDeviceContext(): DeviceContext12. Eventos Padrao
Tabela de todos os eventos gerados pelo SDK (automaticamente ou por chamada explicita):
| event_name | Tipo | Como disparar | Propriedades principais | Descricao |
|--------------------|-----------|----------------------------------------|-----------------------------------------------------------------------|-----------------------------------------------------|
| app_open | track | Manual: track('app_open') | — | App aberto pelo usuario. |
| app_close | track | Automatico (lifecycle, background) | — | App enviado para background. |
| screen_view | screen | screen('NomeDaTela') | screen_name: string | Visualizacao de tela. |
| ad_impression | track | trackAdImpression(data) | revenue_micros, currency, precision, ad_unit_id, ad_source, ad_format | Impressao de anuncio com receita ILAR. |
| ad_click | track | Manual: track('ad_click', {...}) | ad_unit_id, ad_format | Clique em anuncio. |
| purchase | track | Manual: track('purchase', {...}) | product_id, price_usd, currency | Compra in-app ou assinatura. |
| sign_up | track | Manual: track('sign_up', {...}) | method (ex: "google", "email") | Novo cadastro de usuario. |
| login | track | Manual: track('login', {...}) | method | Login de usuario existente. |
| push_received | track | trackPushDelivered(sendId) | send_id: number | Notificacao push recebida pelo dispositivo. |
| push_opened | track | trackPushOpened(sendId) | send_id: number | Notificacao push aberta pelo usuario. |
Todos os eventos incluem automaticamente os campos event_id (UUID v7), anonymous_id, user_id (se identificado), timestamp e context (contexto do dispositivo).
13. Troubleshooting
Modo debug
Ative o modo debug para ver todos os logs internos do SDK no console:
await HotfyCdp.init({
apiKey: 'sua-api-key',
baseUrl: 'https://api.cdp.hotfy.com',
debug: __DEV__, // true em desenvolvimento, false em producao
});Os logs seguem o padrao [HotfyCdp] mensagem e incluem:
- Inicializacao e ID anonimo gerado/restaurado
- Eventos enfileirados e flushes
- Erros de rede com detalhes
- Registro de token push
- Captura de atribuicao
- Reset de identidade
Erros comuns
[HotfyCdp] SDK not initialized. Call HotfyCdp.init() first.
Voce chamou um metodo antes de HotfyCdp.init(). Garanta que o init seja awaitado antes de qualquer outra chamada.
// Errado
HotfyCdp.track('app_open'); // SDK nao inicializado ainda
// Correto
await HotfyCdp.init({ apiKey: '...', baseUrl: '...' });
HotfyCdp.track('app_open');[HotfyCdp] Already initialized.
HotfyCdp.init() foi chamado mais de uma vez. Use HotfyCdp.shutdown() antes de reinicializar, ou estruture o codigo para chamar init apenas uma vez (ex: no root component).
Eventos nao aparecem no backend
- Verifique se
apiKeyebaseUrlestao corretos. - Ative
debug: truee observe os logs de flush. - Chame
await HotfyCdp.flush()manualmente para forcar o envio.
device_model retornando "Android" ou "iPhone"
O pacote react-native-device-info nao esta instalado ou nao foi linkado corretamente. Instale-o para obter o modelo real:
npm install react-native-device-info
npx pod-install # iOSAtribuicao nao e capturada mais de uma vez
Esse comportamento e intencional. O SDK persiste um flag no AsyncStorage apos a primeira captura bem-sucedida. Para resetar em desenvolvimento:
// Apenas para testes - nao use em producao
import AsyncStorage from '@react-native-async-storage/async-storage';
await AsyncStorage.removeItem('hotfy_cdp_attr_captured');Fila de eventos crescendo sem flush
Verifique se ha conectividade de rede. O SDK nao descarta eventos em caso de falha de rede — eles ficam na fila ate o proximo ciclo de flush. Se a fila atingir maxQueueSize (1000), os eventos mais antigos serao descartados.
14. Exemplos Completos
Exemplo 1: app_open com atribuicao e ciclo de vida
// App.tsx (Expo Router ou React Native puro)
import React, { useEffect } from 'react';
import { AppState, Platform } from 'react-native';
import { HotfyCdp } from '@hotfyllc/hotfy-sdk-rn';
export default function App() {
useEffect(() => {
initializeSdk();
}, []);
return <YourNavigator />;
}
async function initializeSdk() {
await HotfyCdp.init({
apiKey: 'hcdp_live_abc123',
baseUrl: 'https://api.cdp.hotfy.com',
debug: __DEV__,
});
// Rastrear abertura do app
HotfyCdp.track('app_open');
// Capturar atribuicao (so executa na primeira vez)
await captureAttribution();
}
async function captureAttribution() {
try {
// Simulando Install Referrer
const referrerString = 'utm_source=google-ads&utm_medium=cpc&gclid=Cj0KCQ...';
const params = new URLSearchParams(referrerString);
const gclid = params.get('gclid');
if (gclid) {
await HotfyCdp.captureAttribution({
sourceType: 'google_ads',
touch: 'last',
gclid,
});
} else {
await HotfyCdp.captureAttribution({
sourceType: 'organic',
utmSource: params.get('utm_source') ?? undefined,
utmMedium: params.get('utm_medium') ?? undefined,
utmCampaign: params.get('utm_campaign') ?? undefined,
});
}
} catch {
await HotfyCdp.captureAttribution();
}
}Exemplo 2: ad_impression com rewarded ad (ciclo completo)
// hooks/useRewardedAd.ts
import React from 'react';
import {
RewardedAd,
RewardedAdEventType,
AdEventType,
TestIds,
} from 'react-native-google-mobile-ads';
import { HotfyCdp } from '@hotfyllc/hotfy-sdk-rn';
const AD_UNIT_ID = __DEV__
? TestIds.REWARDED
: 'ca-app-pub-1234567890/0987654321';
export function useRewardedAd(onReward: () => void) {
const [ad, setAd] = React.useState<RewardedAd | null>(null);
const [loaded, setLoaded] = React.useState(false);
React.useEffect(() => {
const rewardedAd = RewardedAd.createForAdRequest(AD_UNIT_ID, {
requestNonPersonalizedAdsOnly: true,
});
// Receita por impressao (ILAR) - chamado quando o anuncio e pago
rewardedAd.addAdEventListener(AdEventType.PAID, (revenue) => {
HotfyCdp.trackAdImpression({
revenueMicros: Math.round(revenue.value * 1_000_000),
currency: revenue.currencyCode || 'USD',
precision: revenue.precisionType || 'ESTIMATED',
adUnitId: AD_UNIT_ID,
adSource: rewardedAd.adSourceName ?? 'AdMob',
adFormat: 'rewarded',
});
});
rewardedAd.addAdEventListener(RewardedAdEventType.LOADED, () => {
setLoaded(true);
setAd(rewardedAd);
});
rewardedAd.addAdEventListener(RewardedAdEventType.EARNED_REWARD, (reward) => {
// Usuario ganhou a recompensa
HotfyCdp.track('reward_earned', {
reward_type: reward.type,
reward_amount: reward.amount,
ad_unit_id: AD_UNIT_ID,
});
onReward();
});
rewardedAd.addAdEventListener(AdEventType.CLOSED, () => {
setLoaded(false);
setAd(null);
// Carregar proximo anuncio
rewardedAd.load();
});
rewardedAd.load();
return () => rewardedAd.destroy();
}, []);
const show = () => {
if (ad && loaded) {
ad.show();
}
};
return { show, loaded };
}
// Uso no componente
function WatchAdButton() {
const { show, loaded } = useRewardedAd(() => {
console.log('Usuario ganhou recompensa!');
});
return (
<Button
title={loaded ? 'Assistir anuncio (+50 moedas)' : 'Carregando...'}
onPress={show}
disabled={!loaded}
/>
);
}Exemplo 3: identify + attribution flow (login completo)
// screens/LoginScreen.tsx
import { HotfyCdp } from '@hotfyllc/hotfy-sdk-rn';
async function handleLogin(email: string, password: string) {
try {
// 1. Autenticar no seu backend
const response = await fetch('https://api.seuapp.com/auth/login', {
method: 'POST',
body: JSON.stringify({ email, password }),
});
const { userId, plan, createdAt } = await response.json();
// 2. Identificar usuario no CDP
await HotfyCdp.identify(userId, {
email,
plan,
created_at: createdAt,
});
// 3. Rastrear evento de login
HotfyCdp.track('login', {
method: 'email',
plan,
});
// 4. Registrar token push (se disponivel)
const pushToken = await getPushToken();
if (pushToken) {
await HotfyCdp.registerPushToken(pushToken, {
platform: Platform.OS as 'android' | 'ios',
});
}
navigateTo('Home');
} catch (err) {
HotfyCdp.track('login_failed', { method: 'email', error: String(err) });
}
}
async function handleLogout() {
// Rastrear logout
HotfyCdp.track('logout');
// Forcar flush antes de resetar
await HotfyCdp.flush();
// Resetar identidade (novo anonymousId, limpar userId)
await HotfyCdp.reset();
navigateTo('Login');
}
async function handleSignUp(email: string, password: string) {
const response = await fetch('https://api.seuapp.com/auth/signup', {
method: 'POST',
body: JSON.stringify({ email, password }),
});
const { userId } = await response.json();
// Identificar imediatamente apos cadastro
await HotfyCdp.identify(userId, { email });
// Rastrear cadastro (preserva o anonymousId para atribuicao)
HotfyCdp.track('sign_up', {
method: 'email',
});
// Atribuicao ja capturada anteriormente vincula ao anonymousId
// O backend correlaciona anonymousId -> userId automaticamente
}