@hotfyllc/sdk
v1.7.4
Published
Hotfy SDK unificado para React Native — wrapper config (ads), CDP (eventos + atribuição + revenue) e device token registration em um único init.
Readme
@hotfyllc/sdk
SDK unificado da Hotfy para React Native. Em um único pacote:
- Wrapper — orquestração remota de anúncios (interstitial, app open, rewarded, banner, native) integrada com AdMob/GAM, com attribution capture, daysSinceInstall e event system.
- CDP — Customer Data Platform: eventos custom, screen views, identify, atribuição, ad revenue (ILAR), push events.
- Device token — registro automático do FCM/APNS no Console e no CDP, com change-detection, retry e safety re-sync.
Instalação
npm install @hotfyllc/sdkPeer dependencies (obrigatórias)
npm install \
@react-native-async-storage/async-storage \
react-native-google-mobile-ads \
react-native-play-install-referrerVersões mínimas:
react-native >= 0.70.0react-native-google-mobile-ads >= 14.0.0react-native-play-install-referrer >= 1.1.0(Android)@react-native-async-storage/async-storage >= 1.17.0
Onde pegar as API keys
O SDK usa duas chaves distintas (sistemas diferentes):
Hotfy Console API key
Identidade do app no Hotfy Console (mesma chave usada nos endpoints /v1/wrapper/config, /v1/devices e /v1/push-events — vai no header x-api-key).
Onde pegar: painel do Hotfy Console → seu app → Settings → "API Key". É um UUID.
Hotfy CDP API key
Identidade do app no Customer Data Platform (sistema separado do Console — eventos, atribuição, ad revenue).
Onde pegar: painel do Hotfy CDP → seu app → Settings → "API Keys". É um hash hex de 64 caracteres.
Env vars recomendadas
# .env do seu app (não commitar)
EXPO_PUBLIC_HOTFY_APP_API_KEY=your-console-api-key
EXPO_PUBLIC_HOTFY_CDP_API_KEY=your-cdp-api-keyInicialização
Chame Hotfy.init(...) uma vez no boot do app (ex: dentro de um useEffect em _layout.tsx).
import { Hotfy } from '@hotfyllc/sdk'
import AsyncStorage from '@react-native-async-storage/async-storage'
await Hotfy.init({
cdp: { apiKey: process.env.EXPO_PUBLIC_HOTFY_CDP_API_KEY! },
wrapper: {
apiKey: process.env.EXPO_PUBLIC_HOTFY_APP_API_KEY!,
storage: AsyncStorage,
},
deviceToken: {}, // herda apiKey do wrapper
})O init() é bloqueante até o wrapper resolver a config (cache local, fetch fresco, ou response default empty). CDP roda em paralelo. Device token aguarda o app chamar setNativeToken(...) — não bloqueia.
Notas sobre defaults:
cdp.baseUrldefault:https://api.cdp.hotfy.com(override em dev/staging)wrapper.baseUrldefault:https://wrapper.hotfy.com(idem)wrapper.platformdetectado automaticamente viaPlatform.OSdo RNdeviceToken.apiKeyherda dewrapper.apiKeyse omitido — mesma key serve
Se você não precisa de push notification via Hotfy Console, omita o bloco deviceToken inteiro.
Mostrar anúncios
import {
Hotfy,
loadAndShowBootAd,
showInterstitial,
showRewarded,
startAdPreload,
WrapperBanner,
useNativeAd,
} from '@hotfyllc/sdk'
// Splash / cold start
await loadAndShowBootAd()
// Pré-carrega pool de interstitials (chame depois do init)
startAdPreload()
// Transição entre telas
await showInterstitial('home_to_offers')
// "Desbloqueio" — premiado
await showRewarded({ screenKey: 'premium_unlock' })Todos os helpers resolvem sem lançar mesmo em falha (sem fill, user fechou cedo, ads off no segmento, ad unit não configurado pra essa tela). O caller pode navegar logo após — não bloqueia UX.
Banner
<WrapperBanner screenKey="home" size="banner" />Native ad (hook)
const { nativeAd, error } = useNativeAd({ screenKey: 'home' })Os ad units são resolvidos pelo Hotfy Console por (screenKey, format) — mude no painel sem rebuild do app.
Tracking de eventos (CDP)
// Evento custom
Hotfy.track('signup', { plan: 'pro', source: 'home_cta' })
// Screen view
Hotfy.screen('Home')
// Identify — vincula anonymousId ao userId autenticado
await Hotfy.identify('user-123', { email: '[email protected]' })
// Flush manual da queue (raramente necessário — auto-flush a cada 30s ou 20 eventos)
await Hotfy.flush()O anonymousId é gerado no primeiro boot e persiste cross-install via attribution.
Ad revenue tracking (ILAR)
Encaminhar PAID events do AdMob pro CDP automaticamente:
Hotfy.on('impression', (e) => {
Hotfy.trackAdImpression({
revenueMicros: e.revenueMicros ?? 0,
currency: e.currency,
precision: e.precision,
adUnitId: e.adUnitId,
adSource: e.network,
adFormat: e.format,
})
})revenueMicros precisa estar em micros (USD * 1.000.000). O precision ('precise' / 'estimated' / 'publisher_provided' / 'unknown') vem do AdMob.
Integração com outros analytics
Hotfy.on(...) permite encaminhar eventos do wrapper pra qualquer outro destino (Meta Events, Firebase Analytics, Appsflyer, etc.). Eventos disponíveis: show, close, error, impression, click, load, skip.
import { Hotfy } from '@hotfyllc/sdk'
import { AppEventsLogger } from 'react-native-fbsdk-next'
import { getAnalytics, logEvent } from '@react-native-firebase/analytics'
// Impressão — Hotfy + Meta + Firebase
Hotfy.on('impression', (e) => {
// Hotfy CDP — revenue tracking
Hotfy.trackAdImpression({
revenueMicros: e.revenueMicros ?? 0,
currency: e.currency,
precision: e.precision,
adUnitId: e.adUnitId,
adSource: e.network,
adFormat: e.format,
})
// Meta App Events (só impressões pagas)
if (typeof e.value === 'number' && typeof e.currency === 'string') {
AppEventsLogger.logEvent('AdImpression', e.value, {
ad_type: e.format,
ad_platform: e.network,
currency: e.currency,
})
}
// Firebase Analytics
logEvent(getAnalytics(), 'ad_impression', {
ad_platform: e.network,
ad_source: e.network,
ad_format: e.format,
ad_unit_name: e.adUnitId,
currency: e.currency,
value: e.value,
})
})
// Clique
Hotfy.on('click', (e) => {
AppEventsLogger.logEvent('AdClick', { ad_format: e.format })
logEvent(getAnalytics(), 'ad_click', {
ad_platform: e.network,
ad_format: e.format,
})
})
// Ad mostrado em fullscreen — supressão de AppState 'background' falso no Android
Hotfy.on('show', (e) => {
AppEventsLogger.logEvent('AdShown', { ad_format: e.format })
})Cada call de Hotfy.on(...) retorna uma função pra unsubscribe — chame em cleanup se subscrever dentro de hook:
useEffect(() => {
const unsub = Hotfy.on('impression', handler)
return unsub
}, [])Push notification token
Após o usuário conceder permissão, pegue o token FCM/APNS e passe ao SDK:
import { Hotfy } from '@hotfyllc/sdk'
import * as Notifications from 'expo-notifications'
import { Platform } from 'react-native'
const { data: token } = await Notifications.getDevicePushTokenAsync()
Hotfy.setNativeToken(token, Platform.OS === 'ios' ? 'ios' : 'android')O SDK cuida de:
- Change-detection — skip se o token não mudou e foi sincronizado nos últimos 30 dias.
- Retry idempotente — marca
pending=trueem falha e re-tenta no próximo boot. - Sync dual — registra no Hotfy Console (
/v1/devices) E no CDP, em paralelo.
Status pra UI:
const status = Hotfy.getPushTokenStatus()
// {
// platform: 'android' | 'ios',
// hasToken: boolean,
// console: { synced, lastSyncedAt, pending },
// cdp: { synced, lastSyncedAt, pending },
// }Attribution
O wrapper captura automaticamente no primeiro install:
- Android — Google Play Install Referrer (autoritativo, com
installBeginTimestamp). - iOS — deep link inicial via
Linking.getInitialURL().
Acesso síncrono após o init():
const attribution = Hotfy.getAttribution()
// { utmSource?, utmMedium?, utmCampaign?, gclid?, gbraid?, wbraid?, metaCampaignGroupId?, networkClickId?, ... }
const days = Hotfy.getDaysSinceInstall()
// number — dias inteiros desde o install
// Repassa attribution crua pro CDP (enrich + storage)
await Hotfy.captureAttribution(attribution)Logout
await Hotfy.reset()Reseta o CDP — gera novo anonymousId e limpa userId. Não afeta wrapper config nem device token.
API completa
Init
| Método | Descrição |
|---|---|
| Hotfy.init(config) | Bootstrap — bloqueante no wrapper, CDP em paralelo |
CDP — tracking, identity, revenue
| Método | Descrição |
|---|---|
| Hotfy.track(event, props?) | Evento custom |
| Hotfy.screen(name, props?) | Screen view |
| Hotfy.identify(userId, traits?) | Vincula anonymous → user ID |
| Hotfy.getAnonymousId() | Anonymous ID atual (string) |
| Hotfy.getUserId() | User ID setado via identify (ou undefined) |
| Hotfy.captureAttribution(params?) | Envia attribution pro backend CDP |
| Hotfy.registerPushToken(token, meta?) | (uso direto raro — prefira setNativeToken) |
| Hotfy.trackPushDelivered(sendId) | Reporta delivery de push |
| Hotfy.trackPushOpened(sendId) | Reporta tap em push |
| Hotfy.trackAdImpression(data) | Reporta impressão paga (ILAR) |
| Hotfy.flush() | Flush manual da queue |
| Hotfy.getDeviceContext() | OS, advertising_id, app version, locale, timezone |
| Hotfy.getAdvertisingId() | IDFA/AAID (Promise, requer permissão iOS) |
| Hotfy.reset() | Logout — novo anonymousId |
| Hotfy.shutdown() | Para CDP graciosamente |
Wrapper — config + eventos + attribution
| Método | Descrição |
|---|---|
| Hotfy.on(event, handler) | Subscreve evento (retorna unsub fn) |
| Hotfy.off(event, handler) | Remove listener manualmente |
| Hotfy.getConfig() | WrapperConfig resolvida ou null |
| Hotfy.getAttribution() | AttributionData capturada (sync) |
| Hotfy.getDaysSinceInstall() | Dias desde install (number) |
| Hotfy.getAdUnit(screen, format) | Ad unit por (screen, format) ou null |
| Hotfy.getFallbackAdUnit(screen, format) | Ad unit fallback |
| Hotfy.getAppOpen() | Ad unit do slot app_open |
| Hotfy.getAppOpenType() | 'app_open' \| 'interstitial' |
| Hotfy.getAppOpenFallback() | Ad unit fallback do app_open |
| Hotfy.getAppOpenFallbackType() | Tipo do fallback |
| Hotfy.isActive() | True se ads ativos no segmento |
| Hotfy.isReady() | True quando wrapper resolveu |
| Hotfy.refresh() | Força refetch (limpa cache local) |
| Hotfy.disableAppOpenOnForeground() | Opt-out manual do App Open ao voltar do background |
Device token
| Método | Descrição |
|---|---|
| Hotfy.setNativeToken(token, platform) | Registra FCM/APNS — dispara sync em background |
| Hotfy.syncIfNeeded() | Manual, idempotente |
| Hotfy.forceSyncNow() | Ignora change-detection (debug) |
| Hotfy.getPushTokenStatus() | Status dos sync alvos (console + CDP) |
Helpers de ad orchestration
| Helper | Descrição |
|---|---|
| loadAndShowBootAd() | App Open ad na splash |
| showInterstitial(screenKey) | Interstitial via pool pré-carregado |
| showRewarded({ screenKey }) | Rewarded ad |
| startAdPreload() | Pré-carrega pool de interstitials |
| WrapperBanner | Componente banner |
| useNativeAd | Hook pra native ads |
| NativeAdView, NativeAsset, NativeAssetType | Primitivos de render do native (re-exports do react-native-google-mobile-ads) |
Eventos do wrapper
| Evento | Payload (resumo) | Quando |
|---|---|---|
| load | { format, adUnitId, network } | Ad carregou |
| show | { format, adUnitId, network } | Ad apresentado fullscreen / em tela |
| impression | { format, adUnitId, network, value, currency, precision, revenueMicros } | PAID event do AdMob (ILAR) |
| click | { format, adUnitId, network } | User clicou no ad |
| close | { format, adUnitId, network } | Ad fechou (rewarded / interstitial) |
| error | { format, adUnitId, error } | Falha de load / show |
| skip | { format, screenKey, reason } | Helper decidiu não mostrar (cooldown, segmento off, sem unit, etc.) |
License
UNLICENSED
