Como transmitir parâmetros de rastreamento de sites móveis para instalações na loja? A transmissão de parâmetros de rastreamento de um site móvel para um aplicativo recém-instalado envolve o carregamento do Web JS SDK da OpoInstall, a captura de parâmetros de URL permitidos (allowlisted) e a restauração de dados de atribuição compatíveis por meio do SDK nativo no primeiro lançamento do aplicativo.
A união de parâmetros entre downloads na loja de aplicativos é o processo de preservar parâmetros de URL da web elegíveis quando um usuário transita de uma landing page móvel para a loja de aplicativos e, subsequentemente, inicia um aplicativo recém-instalado. Um SDK web captura o contexto de campanha ou referência, enquanto um mecanismo suportado pela plataforma ou um serviço de restauração adiada pode disponibilizar os parâmetros associados ao aplicativo nativo. A restauração depende da plataforma, da configuração do SDK, dos sinais de correspondência disponíveis e dos requisitos de privacidade aplicáveis.
| Termo | Definição | Entidade Relacionada | Papel da Intenção de Busca |
|---|---|---|---|
| Parâmetros de Rastreamento | Pares chave-valor anexados a URLs para identificar contexto de tráfego, fontes de campanha ou informações de referência. | String de Consulta de URL | Informativo / Técnico |
| Instalação de Parâmetros do App | Um fluxo de trabalho de restauração de parâmetros adiados que disponibiliza contexto web elegível após a instalação. | SDK Nativo Mobile | Técnico / Informativo |
| Google Play Install Referrer | Uma API oficial do Android para recuperar metadados de referência associados a uma instalação na Google Play. | SDK Android | Técnico |
A lacuna de atribuição Web-to-Store: Barreiras de plataforma entre as lojas de aplicativos

A barreira de desconexão: Como os sandboxes do sistema operacional isolam o contexto do navegador
As análises da web comumente usam strings de consulta de URL, incluindo tags UTM (utm_source, utm_medium, utm_campaign), identificadores de clique de publicidade como gclid e fbclid, e parâmetros de referência personalizados.
Dentro de um site participante, APIs de navegador e sistemas de análise first-party podem capturar esses valores para fluxos de trabalho permitidos de análise e campanha.
O desafio surge quando um visitante deixa o navegador e entra em um processo de instalação na loja de aplicativos. O navegador e o aplicativo nativo recém-instalado operam em ambientes separados. Cookies de navegador, armazenamento de sessão e parâmetros de URL de página não se tornam automaticamente disponíveis dentro do aplicativo após a instalação.
Consequentemente, os desenvolvedores precisam de um mecanismo de referência suportado pela loja ou um fluxo de trabalho de restauração de parâmetros adiados para conectar o contexto pré-instalação elegível com uma sessão do aplicativo pós-instalação.
Especificidades da plataforma: Ingestão na Apple App Store vs. Google Play Store

As lojas de aplicativos móveis suportam diferentes abordagens para transmitir contexto de referência.
- Apple App Store (iOS): Uma instalação padrão da App Store não encaminha automaticamente parâmetros de consulta de URL externos arbitrários para o ambiente de primeiro lançamento de um aplicativo recém-instalado. Serviços de restauração adiada ou fluxos de trabalho explicitamente mediados pelo usuário podem ajudar a recuperar contexto elegível, sujeitos aos requisitos de privacidade da Apple.
- Google Play Store (Android): O Google fornece a API oficial Google Play Install Referrer. Onde um fluxo de referência da Google Play devidamente construído é usado, o aplicativo instalado pode recuperar metadados de referência suportados, incluindo uma string de referência e timestamps relevantes.
Esses mecanismos não devem ser tratados como intercambiáveis. A Google Play oferece uma interface de referência oficial ao nível da loja, enquanto a restauração de parâmetros adiada arbitrária no iOS exige um mecanismo projetado separadamente e em conformidade com as políticas.
O impacto comercial do tráfego de instalação não atribuído no cálculo de ROI de canal
Se os parâmetros de campanha forem perdidos entre uma visita à web e uma instalação de aplicativo, os sistemas de análise podem não associar corretamente a instalação à sua campanha original.
Isso pode levar a instalações não atribuídas, relatórios de aquisição incompletos e cálculos imprecisos de Retorno sobre Investimento (ROI) ou Custo de Aquisição de Cliente (CAC).
A perda de parâmetros também pode afetar as experiências de integração (onboarding). Por exemplo, um link de referência web pode incluir um identificador de convidador ou código promocional:
https://example.com/download?inviter_id=USR_1024
https://example.com/download?promo_code=WELCOME50
Se o aplicativo não conseguir recuperar esses valores, os usuários podem precisar inseri-los manualmente.
No entanto, restaurar um valor de referência ou promocional não autoriza automaticamente uma recompensa. O backend do aplicativo deve verificar independentemente a elegibilidade, limites de resgate e regras de negócio aplicáveis.
Mecanismos suportados pela loja vs. mecanismos de parâmetros adiados: Apple App Store vs. Google Play
Mecanismos da Google Play Store: Aproveitando a API nativa Install Referrer
No Android, a API Google Play Install Referrer oferece um mecanismo oficial para recuperar metadados de referência de instalação suportados.
Uma URL de download da Google Play pode incluir um parâmetro referrer codificado:
https://play.google.com/store/apps/details?id=com.example.app&referrer=utm_source%3Dfacebook%26inviter_id%3DUSR_1024
Nesta URL ilustrativa, o valor de referência codificado representa:
utm_source=facebook&inviter_id=USR_1024
Após a instalação, um aplicativo Android nativo pode usar o Install Referrer Client para recuperar informações de referência disponíveis na Google Play.
Se uma versão específica do Web SDK da OpoInstall gera, modifica ou gerencia tal URL automaticamente, isso deve ser verificado em relação à sua documentação e comportamento implantados. A existência da API oficial do Google não estabelece a implementação interna de um SDK de terceiros.
Fronteiras da Apple App Store: Lidando com redirecionamentos padrão de loja sem parâmetros de consulta nativos
A Apple App Store não transmite diretamente parâmetros de consulta web arbitrários para o callback de primeiro lançamento de um aplicativo recém-instalado.
Um fluxo de trabalho de restauração adiada pode, portanto, envolver a captura de informações de campanha permitidas antes do redirecionamento para a loja e a solicitação de contexto associado quando o aplicativo nativo for iniciado.
Para um fluxo de trabalho baseado em SDK, os desenvolvedores devem distinguir entre a funcionalidade documentada pelo fornecedor e os métodos de correspondência usados pela versão do SDK implantada.
Qualquer método de correspondência ou coleta de dados deve estar em conformidade com as políticas de privacidade da Apple. Em particular, uma implementação de atribuição não deve depender de impressão digital de dispositivo (fingerprinting) proibida ou assumir que a permissão de ATT torna aceitável um método de correspondência de outra forma proibido.
Links Universais são úteis para rotear links suportados para um aplicativo já instalado, mas não garantem, por si só, que parâmetros de consulta web arbitrários sobreviverão a uma instalação nova na App Store.
Divergência arquitetônica entre plataformas: Construindo contratos de parâmetros unificados
Embora Android e iOS usem mecanismos de plataforma diferentes, um aplicativo pode normalizar valores recuperados em um esquema compartilhado ao nível do aplicativo.
Por exemplo:
{
"utm_source": "facebook",
"utm_medium": "paid_social",
"utm_campaign": "summer_sale",
"inviter_id": "USR_1024",
"promo_code": "WELCOME50"
}
Este objeto JSON representa um contrato de parâmetro normalizado definido pelo aplicativo, não um formato de resposta literal garantido de cada lançamento do SDK da OpoInstall.
O aplicativo deve separar esses campos em duas categorias:
- Atribuição de marketing:
utm_source,utm_mediumeutm_campaign. - Ações de referência e promocionais:
inviter_idepromo_code.
A atribuição de marketing pode ser enviada para um pipeline de análise autorizado. Ações de referência e promocionais exigem autorização independente do lado do servidor antes que o aplicativo conceda qualquer benefício.
O papel do cache de carga contextual na ponte de redirecionamento de loja
Serviços de restauração adiada geralmente exigem um período de retenção limitado para o contexto de instalação.
A janela de retenção aplicável, comportamento de restauração e disponibilidade de correspondência dependem do serviço implantado, configuração do SDK, condições de rede, limitações da plataforma e requisitos de privacidade.
As equipes de engenharia devem medir os resultados da restauração usando seus próprios coortes de instalação em vez de adotar alegações de precisão não suportadas.
Arquiteturas de contexto adiado conscientes da privacidade e limites de plataforma
Limites da área de transferência (pasteboard) do sistema no iOS: Banners de transparência e mecânicas de intenção do UIPasteControl
Algumas implementações históricas de links adiados usavam a área de transferência do sistema para carregar contexto temporário entre aplicativos.
No entanto, o iOS restringe e expõe o acesso à área de transferência entre aplicativos aos usuários.
- iOS 14: Ler conteúdo copiado de outro aplicativo pode acionar uma notificação de acesso à área de transferência.
- iOS 16 e posterior: Certas operações de colagem programáticas podem acionar um prompt de permissão. Controles como
UIPasteControlsuportam interações de colagem iniciadas pelo usuário.
Consequentemente, o acesso geral à área de transferência não deve ser tratado como um canal de transporte de parâmetros de instalação invisível ou irrestrito.
Aplicativos que usam funcionalidade relacionada à área de transferência devem tornar a interação apropriada às expectativas do usuário e lidar com a permissão negada ou conteúdo ausente de forma elegante.
Restrições da área de transferência em primeiro plano no Android: Restrições de navegação e limpeza automática
O Android também aplica restrições ao acesso à área de transferência.
- Android 10 e posterior: O acesso à área de transferência é restrito para aplicativos que não estão em um contexto de primeiro plano elegível, sujeito a exceções definidas pela plataforma.
- Android 13 e posterior: O Android fornece feedback de interface do usuário relacionado à área de transferência e comportamento de limpeza automática para conteúdo da área de transferência.
Os aplicativos não devem assumir que os dados da área de transferência podem ser recuperados silenciosamente ou que permanecem disponíveis indefinidamente.
O acesso à área de transferência deve ser opcional e limitado a fluxos de trabalho apropriados voltados ao usuário, em vez de servir como um canal de atribuição primário presumido.
Adesão estrita às políticas de privacidade e uso de dados do usuário da Apple: Proibição de fingerprinting de dispositivo
A Apple proíbe o fingerprinting de dispositivo usado para identificar usuários ou dispositivos por meio de combinações de características do dispositivo e do ambiente.
Uma implementação não deve construir identificadores persistentes ou probabilísticos a partir de combinações de endereços IP, informações de agente de usuário, características de exibição ou outros sinais de dispositivo em violação às políticas da Apple.
Conceder permissão de App Tracking Transparency não torna admissíveis métodos de impressão digital proibidos.
Para qualquer serviço de restauração de parâmetros adiados de terceiros, os desenvolvedores devem revisar a coleta real de dados, comportamento de correspondência, divulgações de privacidade e requisitos de política de plataforma do SDK implantado.
Uma integração consciente da privacidade deve priorizar a minimização de dados, interações transparentes com o usuário e mecanismos de plataforma suportados.
Limites de segurança e confiança: Gerenciando ciclos de vida de parâmetros e autorização de servidor

Todos os parâmetros restaurados devem ser tratados como entrada não confiável.
Tags de marketing e tokens de referência podem se originar de URLs editáveis ou solicitações de cliente não confiáveis. Sua presença isolada não estabelece que uma campanha é legítima ou que um usuário é elegível para uma recompensa.
Os aplicativos devem seguir estes princípios:
- Valores permitidos (Allowlisted): Aceite apenas chaves explicitamente suportadas e valores limitados.
- Separe atribuição de autorização: Valores UTM podem descrever a origem do tráfego; eles não devem autorizar independentemente benefícios promocionais.
- Registros pendentes com limite de tempo: Reter apenas as informações necessárias para o processamento, sujeitas a uma janela de expiração definida pelo aplicativo.
- Armazenamento protegido: Use armazenamento apropriado à sensibilidade dos tokens pendentes, como um design baseado em Android Keystore ou iOS Keychain Services, quando justificado.
- Resultados de entrega independentes: Para registros contendo parâmetros de marketing e recompensa, o aplicativo deve rastrear os resultados necessários independentemente.
- Validação de recompensa no servidor: Créditos de referência, descontos promocionais e direitos semelhantes devem ser aprovados por um backend de aplicativo autenticado.
- Aplicação de recompensa idempotente: O backend deve aplicar idempotência ao nível da transação e restrições de unicidade ao nível do negócio para que solicitações repetidas do cliente não possam duplicar recompensas de uso único.
O código abaixo ilustra o limite de parâmetros e as responsabilidades de integração do aplicativo host. Ele não tenta fornecer um sistema completo de armazenamento, análise ou transação de recompensa de nível de produção.
Implementação ponta a ponta: Capturando parâmetros da Web para restauração nativa
Higienizando parâmetros de consulta da Web: Bootstrap HTML, análise de lista de permitidos e configuração do Web JS SDK
Uma landing page pode capturar parâmetros de consulta explicitamente suportados e passá-los para o Web SDK como uma carga útil delimitada.
O exemplo a seguir demonstra:
- Carregar o SDK de sua fonte configurada.
- Preservar um link de fallback de download HTTPS normal.
- Aceitar apenas chaves de parâmetro suportadas.
- Rejeitar valores fora de uma política restrita de caracteres e comprimento.
- Conectar uma ação de download do SDK ao botão de download da página.
O construtor do SDK, as chaves de configuração e os métodos são representativos. Verifique suas assinaturas em relação ao lançamento exato do Web SDK da OpoInstall implantado antes da publicação ou implantação.
<!-- CODE_BLOCK_01: Bootstrap HTML representativo e Integração Web SDK -->
<!-- File: landing_page.html -->
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Download do Aplicativo</title>
</head>
<body>
<!-- Substitua por um link real de download HTTPS ou roteamento de loja. -->
<a
id="download-app-btn"
href="https://app.example.com/store-download"
>
Baixar Aplicativo
</a>
<!-- Verifique a URL oficial da CDN com a versão do SDK implantada. -->
<script src="https://web.cdn.opoinstallcloud.com/openinstall.js"></script>
<script>
(function () {
"use strict";
function getSanitizedQueryParams() {
var allowedKeys = new Set([
"utm_source",
"utm_medium",
"utm_campaign",
"inviter_id",
"promo_code"
]);
var validPattern = /^[A-Za-z0-9_.-]{1,64}$/;
var result = Object.create(null);
try {
var query = new URLSearchParams(window.location.search);
query.forEach(function (value, key) {
if (
allowedKeys.has(key) &&
validPattern.test(value) &&
!Object.prototype.hasOwnProperty.call(result, key)
) {
result[key] = value;
}
});
} catch (error) {
console.warn("Não foi possível analisar os parâmetros da URL.");
return Object.create(null);
}
return result;
}
var downloadButton = document.getElementById(
"download-app-btn"
);
// Mantenha o href da âncora como o fallback normal do navegador.
if (!downloadButton) {
return;
}
if (typeof OpenInstall === "undefined") {
console.warn(
"Web SDK indisponível; usando o link de download."
);
return;
}
var customParams = getSanitizedQueryParams();
try {
new OpenInstall({
appKey: "OPOINSTALL_APPKEY_PLACEHOLDER",
data: customParams,
onready: function () {
var sdkClient = this;
downloadButton.addEventListener(
"click",
function (event) {
try {
// Método de SDK representativo.
// Verifique seu comportamento e tratamento de falha assíncrona
// para a versão implantada.
sdkClient.wakeupOrInstall();
event.preventDefault();
} catch (error) {
console.warn(
"Ação do SDK falhou; o navegador usará o fallback."
);
// Não impeça a ação padrão da âncora.
}
}
);
}
});
} catch (error) {
console.warn(
"Inicialização do Web SDK falhou; usando o link de fallback."
);
}
})();
</script>
</body>
</html>
A política de valores aceita intencionalmente apenas letras, dígitos, sublinhados, hifens e pontos, com um máximo de 64 caracteres.
Valores contendo espaços ou outros caracteres são excluídos nesta implementação de referência. Equipes com requisitos mais amplos de nomenclatura UTM devem definir e testar um esquema específico de campo apropriado, em vez de assumir silenciosamente que todos os valores de URL são suportados.
Da mesma forma, identificadores de clique de publicidade como gclid e fbclid são discutidos como conceitos comuns de atribuição web, mas não estão incluídos nesta lista de permitidos de exemplo. Suportá-los requer uma coleta de dados, retenção e revisão de privacidade separadas.
A âncora de fallback permanece disponível quando o SDK não carrega ou não consegue inicializar. Como as operações do SDK podem falhar de forma assíncrona, uma integração de produção deve seguir adicionalmente o tratamento de erro e o comportamento de redirecionamento documentados pelo SDK; um try/catch síncrono por si só não é uma estratégia completa de fallback assíncrono.
Implementação nativa Android: Configuração do SDK, Validação de Parâmetros e Contratos de Aplicativo Host
Após a instalação, o aplicativo Android pode solicitar parâmetros suportados do SDK nativo e normalizá-los em um registro definido pelo aplicativo.
O exemplo abaixo mantém deliberadamente as responsabilidades separadas:
- O SDK da OpoInstall inicializa no processo principal do aplicativo.
- O callback nativo fornece uma carga útil candidata.
- O aplicativo valida e normaliza a carga útil.
- O aplicativo salva o registro por meio de uma interface de armazenamento fornecida pelo host.
- Um componente de processamento fornecido pelo host coordena a atribuição de marketing e a autorização de recompensa.
- O aplicativo marca o registro como completo apenas após esse componente relatar um resultado de processamento verificado.
Importante: As interfaces de host são contratos definidos pelo aplicativo, não APIs da OpoInstall. Este exemplo exige que o aplicativo host forneça implementações. Ele não inclui armazenamento de fallback inseguro, entrega de análise simulada ou uma resposta de autorização de recompensa simulada.
// CODE_BLOCK_02: Integração Android Representativa (Kotlin)
// File: CustomApplication.kt e MainActivity.kt
//
// Verifique as importações e callbacks da OpoInstall em relação ao AAR implantado.
// Implementações de host devem ser fornecidas antes do processamento de parâmetros.
package com.example.app
import android.app.ActivityManager
import android.app.Application
import android.content.Context
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.Process
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import com.opoinstall.api.OpoInstall
import com.opoinstall.api.OpoData
import com.opoinstall.api.OpoError
import com.opoinstall.api.ResultCallBack
import org.json.JSONObject
import java.util.UUID
enum class ParamFetchState {
NOT_STARTED,
FETCHING,
RECEIVED,
APPLIED,
REJECTED,
NO_MATCH,
RETRYABLE_FAILURE
}
data class PendingAttributionRecord(
val utmSource: String?,
val utmMedium: String?,
val utmCampaign: String?,
val inviterId: String?,
val promoCode: String?,
val idempotencyKey: String,
val createdAt: Long,
val expiresAt: Long
) {
val hasAttributionData: Boolean
get() = listOf(
utmSource, utmMedium, utmCampaign
).any { !it.isNullOrEmpty() }
val hasRewardData: Boolean
get() = !inviterId.isNullOrEmpty() ||
!promoCode.isNullOrEmpty()
val hasAnyData: Boolean
get() = hasAttributionData || hasRewardData
fun isValid(): Boolean {
val now = System.currentTimeMillis()
val pattern = Regex("^[A-Za-z0-9_.-]{1,64}$")
val values = listOf(
utmSource, utmMedium, utmCampaign,
inviterId, promoCode
)
val validFields = values.all {
it == null || pattern.matches(it)
}
val validWindow =
expiresAt > createdAt &&
expiresAt - createdAt <= 48L * 60 * 60 * 1000 &&
now >= createdAt &&
now <= expiresAt
val validId = try {
UUID.fromString(idempotencyKey)
true
} catch (_: IllegalArgumentException) {
false
}
return hasAnyData &&
validFields &&
validWindow &&
validId
}
}
// Contrato de persistência segura definido pelo host.
// A implementação deve distinguir dados ausentes de
// falhas de armazenamento e lidar com vinculação de conta quando necessário.
interface PendingTransactionStore {
fun save(record: PendingAttributionRecord): Boolean
fun load(): PendingAttributionRecord?
fun clear(): Boolean
}
// Coordenador definido pelo host. Sua implementação é responsável
// por telemetria independente/reconhecimentos de recompensa,
// idempotência, tratamento de tentativas, e verificações de identidade de transação.
interface InstallParameterProcessor {
fun process(
record: PendingAttributionRecord,
onComplete: (ProcessingOutcome) -> Unit
)
}
enum class ProcessingOutcome {
COMPLETED,
REJECTED,
RETRYABLE_FAILURE
}
// Estas são dependências do aplicativo, NÃO APIs da OpoInstall.
object HostAppDependencies {
var transactionStore: PendingTransactionStore? = null
var parameterProcessor: InstallParameterProcessor? = null
val isConfigured: Boolean
get() = transactionStore != null &&
parameterProcessor != null
}
class CustomApplication : Application() {
override fun onCreate() {
super.onCreate()
if (isMainProcess()) {
OpoInstall.initialize(this)
// O aplicativo host deve configurar as dependências aqui
// usando seu mecanismo real de DI/bootstrap.
//
// HostAppDependencies.transactionStore =
// hostProvidedProtectedStore
//
// HostAppDependencies.parameterProcessor =
// hostProvidedInstallParameterProcessor
}
}
private fun isMainProcess(): Boolean {
val manager = getSystemService(
Context.ACTIVITY_SERVICE
) as ActivityManager
val currentPid = Process.myPid()
return manager.runningAppProcesses?.any {
it.pid == currentPid &&
it.processName == packageName
} == true
}
}
class MainActivity : AppCompatActivity() {
private val mainHandler = Handler(Looper.getMainLooper())
private var state = ParamFetchState.NOT_STARTED
private var activeRecord: PendingAttributionRecord? = null
private var processingInFlight = false
private val transactionTtlMs =
24L * 60 * 60 * 1000
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
startParameterFlow()
}
private fun startParameterFlow() {
val store = HostAppDependencies.transactionStore
val processor = HostAppDependencies.parameterProcessor
if (store == null || processor == null) {
Log.w(
"OpoInstall",
"Dependências do host indisponíveis; processamento suspenso"
)
state = ParamFetchState.RETRYABLE_FAILURE
return
}
// Recuperação de registro representativa.
// O armazenamento de produção deve relatar falhas de leitura separadamente
// de uma ausência confirmada de um registro pendente.
val existingRecord = try {
store.load()
} catch (error: Exception) {
state = ParamFetchState.RETRYABLE_FAILURE
return
}
if (existingRecord != null) {
if (existingRecord.isValid()) {
activeRecord = existingRecord
state = ParamFetchState.RECEIVED
processRecord(existingRecord)
} else {
// Registros inválidos ou expirados não são processados.
if (!store.clear()) {
state = ParamFetchState.RETRYABLE_FAILURE
return
}
requestInstallParameters()
}
} else {
requestInstallParameters()
}
}
private fun requestInstallParameters() {
if (!HostAppDependencies.isConfigured) {
state = ParamFetchState.RETRYABLE_FAILURE
return
}
if (state == ParamFetchState.FETCHING ||
state == ParamFetchState.APPLIED ||
state == ParamFetchState.NO_MATCH ||
processingInFlight
) {
return
}
state = ParamFetchState.FETCHING
// Callback do SDK representativo.
// Valide a assinatura em relação ao AAR implantado.
OpoInstall.getInstance().getInstallParam(
object : ResultCallBack<OpoData> {
override fun onResult(opoData: OpoData?) {
mainHandler.post {
if (state != ParamFetchState.FETCHING) {
return@post
}
val rawData: Any? = opoData?.data
val payload = when (rawData) {
is String -> rawData
is JSONObject -> rawData.toString()
is Map<*, *> -> try {
JSONObject(rawData).toString()
} catch (_: Exception) {
null
}
else -> null
}
val record = payload?.let {
parseAndSanitizeRawPayload(it)
}
if (record == null) {
// Nenhum dado elegível foi recuperado.
// Isso não estabelece uma instalação orgânica.
state = ParamFetchState.NO_MATCH
return@post
}
val store =
HostAppDependencies.transactionStore
if (store == null ||
!HostAppDependencies.isConfigured
) {
state = ParamFetchState.RETRYABLE_FAILURE
return@post
}
activeRecord = record
// Não reivindique recuperação durável se o salvamento falhar.
val saved = try {
store.save(record)
} catch (_: Exception) {
false
}
if (!saved) {
Log.w(
"OpoInstall",
"Registro pendente não pôde ser persistido"
)
state = ParamFetchState.RETRYABLE_FAILURE
return@post
}
state = ParamFetchState.RECEIVED
processRecord(record)
}
}
override fun onError(error: OpoError?) {
mainHandler.post {
if (state == ParamFetchState.FETCHING) {
Log.w(
"OpoInstall",
"Recuperação de parâmetro de instalação falhou"
)
state = ParamFetchState.RETRYABLE_FAILURE
}
}
}
}
)
}
private fun parseAndSanitizeRawPayload(
rawJson: String
): PendingAttributionRecord? {
return try {
if (rawJson.length > 4096) return null
val json = JSONObject(rawJson)
val pattern = Regex("^[A-Za-z0-9_.-]{1,64}$")
fun field(key: String): String? =
(json.opt(key) as? String)?.takeIf {
pattern.matches(it)
}
val now = System.currentTimeMillis()
val record = PendingAttributionRecord(
utmSource = field("utm_source"),
utmMedium = field("utm_medium"),
utmCampaign = field("utm_campaign"),
inviterId = field("inviter_id"),
promoCode = field("promo_code"),
idempotencyKey = UUID.randomUUID().toString(),
createdAt = now,
expiresAt = now + transactionTtlMs
)
record.takeIf { it.isValid() }
} catch (_: Exception) {
null
}
}
private fun processRecord(
record: PendingAttributionRecord
) {
val processor = HostAppDependencies.parameterProcessor
val store = HostAppDependencies.transactionStore
if (processor == null || store == null ||
!record.isValid()
) {
state = ParamFetchState.RETRYABLE_FAILURE
return
}
if (processingInFlight) return
processingInFlight = true
// O processador host não deve relatar COMPLETED até que
// todos os resultados específicos de canal necessários sejam confirmados.
processor.process(record) { outcome ->
mainHandler.post {
// Ignore transações substituídas ou obsoletas.
if (activeRecord?.idempotencyKey !=
record.idempotencyKey
) {
return@post
}
processingInFlight = false
when (outcome) {
ProcessingOutcome.COMPLETED -> {
finalizeRecord(
record,
ParamFetchState.APPLIED
)
}
ProcessingOutcome.REJECTED -> {
finalizeRecord(
record,
ParamFetchState.REJECTED
)
}
ProcessingOutcome.RETRYABLE_FAILURE -> {
state =
ParamFetchState.RETRYABLE_FAILURE
}
}
}
}
}
private fun finalizeRecord(
record: PendingAttributionRecord,
terminalState: ParamFetchState
) {
val store = HostAppDependencies.transactionStore
?: run {
state = ParamFetchState.RETRYABLE_FAILURE
return
}
if (activeRecord?.idempotencyKey !=
record.idempotencyKey
) {
return
}
// Este exemplo deixa intencionalmente o diário terminal durável
// e a limpeza atômica para o armazenamento do host.
// clear() deve relatar se a limpeza foi bem-sucedida.
val cleared = try {
store.clear()
} catch (_: Exception) {
false
}
if (!cleared) {
state = ParamFetchState.RETRYABLE_FAILURE
return
}
activeRecord = null
state = terminalState
Log.i(
"OpoInstall",
"Processamento de parâmetro de instalação resolvido"
)
}
}
O exemplo Android intencionalmente não inclui uma implementação padrão de PendingTransactionStore ou InstallParameterProcessor.
O aplicativo host deve fornecê-los antes de invocar o fluxo de trabalho.
Em particular, o processador não deve retornar COMPLETED meramente porque uma instrução de log foi executada ou uma solicitação assíncrona foi iniciada. Um resultado concluído deve corresponder aos requisitos de processamento para a carga útil real:
- Carga útil apenas UTM: O evento de atribuição necessário é aceito de forma confiável pelo sistema de análise configurado ou fila de eventos duráveis.
- Carga útil apenas recompensa: O backend autenticado retorna um resultado de autorização terminal aceito.
- Carga útil mista UTM e Recompensa: Ambos os canais necessários atingem seus resultados definidos independentemente, e um canal não pode remover prematuramente o trabalho exigido pelo outro.
Se o aplicativo host exigir recuperação entre processos, o processador e o armazenamento protegido devem persistir adicionalmente progresso suficiente específico do canal e preservar identificadores de transação estáveis.
A atividade acima demonstra o limite de integração; ela não é um substituto para essas implementações específicas do aplicativo.
Implementação nativa iOS: Restauração de SDK e Contratos de Integração de Aplicativo
A integração iOS segue o mesmo limite.
O SDK nativo fornece parâmetros de instalação candidatos. O aplicativo valida a carga útil, persiste um registro pendente através de um componente de armazenamento fornecido pelo host e delega o processamento de negócios para um coordenador de propriedade do aplicativo.
O código é representativo e deve ser adaptado ao nome do módulo, protocolo de delegado e assinaturas de callback da versão do SDK iOS da OpoInstall instalada.
// CODE_BLOCK_03: Integração iOS Representativa (Swift)
// File: AppDelegate.swift
//
// Verifique o módulo e os métodos do SDK em relação à versão
// do framework da OpoInstall implantada.
import UIKit
import libOpoInstallSDK
enum ParamFetchState: String {
case notStarted = "NOT_STARTED"
case fetching = "FETCHING"
case received = "RECEIVED"
case applied = "APPLIED"
case rejected = "REJECTED"
case noMatch = "NO_MATCH"
case retryableFailure = "RETRYABLE_FAILURE"
}
struct PendingAttributionRecord: Codable {
let utmSource: String?
let utmMedium: String?
let utmCampaign: String?
let inviterId: String?
let promoCode: String?
let idempotencyKey: String
let createdAt: TimeInterval
let expiresAt: TimeInterval
var hasAttributionData: Bool {
return [
utmSource, utmMedium, utmCampaign
].contains { !$0.isNilOrEmpty }
}
var hasRewardData: Bool {
return !inviterId.isNilOrEmpty ||
!promoCode.isNilOrEmpty
}
var hasAnyData: Bool {
return hasAttributionData || hasRewardData
}
var isValid: Bool {
let now = Date().timeIntervalSince1970
let window = expiresAt - createdAt
let pattern = "^[A-Za-z0-9_.-]{1,64}$"
let fields = [
utmSource, utmMedium, utmCampaign,
inviterId, promoCode
]
let validFields = fields.allSatisfy { field in
guard let field = field else { return true }
return field.range(
of: pattern,
options: .regularExpression
) != nil
}
return hasAnyData &&
validFields &&
UUID(uuidString: idempotencyKey) != nil &&
window > 0 &&
window <= 48 * 60 * 60 &&
now >= createdAt &&
now <= expiresAt
}
}
private extension Optional where Wrapped == String {
var isNilOrEmpty: Bool {
return self?.isEmpty ?? true
}
}
// Contrato de persistência protegida fornecido pelo host.
protocol PendingTransactionStore {
func save(_ record: PendingAttributionRecord) -> Bool
func load() throws -> PendingAttributionRecord?
func clear() -> Bool
}
enum ProcessingOutcome {
case completed
case rejected
case retryableFailure
}
// Coordenador fornecido pelo host para processamento de análise e recompensa.
protocol InstallParameterProcessor {
func process(
record: PendingAttributionRecord,
completion: @escaping (ProcessingOutcome) -> Void
)
}
// Estes são contratos de aplicativo, não APIs da OpoInstall.
struct HostAppDependencies {
static var transactionStore: PendingTransactionStore?
static var parameterProcessor: InstallParameterProcessor?
static var isConfigured: Bool {
return transactionStore != nil &&
parameterProcessor != nil
}
}
@UIApplicationMain
class AppDelegate:
UIResponder,
UIApplicationDelegate,
OpoInstallDelegate {
var window: UIWindow?
private var state: ParamFetchState = .notStarted
private var activeRecord: PendingAttributionRecord?
private var processingInFlight = false
private let transactionTTL: TimeInterval =
24 * 60 * 60
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions:
[UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Inicialização do SDK representativa.
OpoInstallSDK.initWith(self)
// O aplicativo host deve registrar suas dependências reais
// antes de iniciar o processamento de parâmetros.
//
// HostAppDependencies.transactionStore =
// hostProvidedProtectedStore
//
// HostAppDependencies.parameterProcessor =
// hostProvidedInstallParameterProcessor
startParameterFlow()
return true
}
private func startParameterFlow() {
guard HostAppDependencies.isConfigured,
let store = HostAppDependencies.transactionStore
else {
print(
"OpoInstall: Dependências do host obrigatórias indisponíveis"
)
state = .retryableFailure
return
}
do {
if let saved = try store.load() {
if saved.isValid {
activeRecord = saved
state = .received
processRecord(saved)
return
}
guard store.clear() else {
state = .retryableFailure
return
}
}
} catch {
// Um erro de leitura não é equivalente a um armazenamento vazio.
state = .retryableFailure
return
}
requestInstallParameters()
}
private func requestInstallParameters() {
guard HostAppDependencies.isConfigured,
state != .fetching,
state != .applied,
state != .noMatch,
!processingInFlight
else {
return
}
guard let manager = OpoInstallSDK.defaultManager()
else {
state = .retryableFailure
return
}
state = .fetching
// Assinatura de callback representativa.
manager.getInstallParmsCompleted({
[weak self] (appData: OpoinstallData?) in
DispatchQueue.main.async {
guard let self = self,
self.state == .fetching
else {
return
}
let rawData: Any? = appData?.data
guard let record =
self.parseAndSanitizeRawPayload(rawData)
else {
// Nenhum parâmetro personalizado elegível recuperado.
// Isso não é prova definitiva de tráfego orgânico.
self.state = .noMatch
return
}
guard HostAppDependencies.isConfigured,
let store =
HostAppDependencies.transactionStore
else {
self.state = .retryableFailure
return
}
self.activeRecord = record
guard store.save(record) else {
// Não reivindique um registro pendente durável
// quando a persistência falhar.
self.state = .retryableFailure
return
}
self.state = .received
self.processRecord(record)
}
})
}
private func parseAndSanitizeRawPayload(
_ rawData: Any?
) -> PendingAttributionRecord? {
guard let rawData = rawData else {
return nil
}
var dictionary: [String: Any]?
if let map = rawData as? [String: Any] {
dictionary = map
} else if let jsonString = rawData as? String,
jsonString.count <= 4096,
let data = jsonString.data(using: .utf8),
let parsed = try? JSONSerialization.jsonObject(
with: data
) as? [String: Any] {
dictionary = parsed
}
guard let payload = dictionary else {
return nil
}
let pattern = "^[A-Za-z0-9_.-]{1,64}$"
func field(_ key: String) -> String? {
guard let value = payload[key] as? String,
value.range(
of: pattern,
options: .regularExpression
) != nil
else {
return nil
}
return value
}
let now = Date().timeIntervalSince1970
let record = PendingAttributionRecord(
utmSource: field("utm_source"),
utmMedium: field("utm_medium"),
utmCampaign: field("utm_campaign"),
inviterId: field("inviter_id"),
promoCode: field("promo_code"),
idempotencyKey: UUID().uuidString,
createdAt: now,
expiresAt: now + transactionTTL
)
return record.isValid ? record : nil
}
private func processRecord(
_ record: PendingAttributionRecord
) {
guard record.isValid,
let processor =
HostAppDependencies.parameterProcessor
else {
state = .retryableFailure
return
}
guard !processingInFlight else {
return
}
processingInFlight = true
// O processador host deve confirmar os resultados
// de atribuição e recompensa necessários independentemente.
processor.process(record: record) {
[weak self] outcome in
DispatchQueue.main.async {
guard let self = self,
self.activeRecord?.idempotencyKey ==
record.idempotencyKey
else {
return
}
self.processingInFlight = false
switch outcome {
case .completed:
self.finalizeRecord(
record,
terminalState: .applied
)
case .rejected:
self.finalizeRecord(
record,
terminalState: .rejected
)
case .retryableFailure:
self.state = .retryableFailure
}
}
}
}
private func finalizeRecord(
_ record: PendingAttributionRecord,
terminalState: ParamFetchState
) {
guard activeRecord?.idempotencyKey ==
record.idempotencyKey,
let store =
HostAppDependencies.transactionStore
else {
state = .retryableFailure
return
}
// O armazenamento host deve coordenar a conclusão durável
// e a limpeza para as necessidades de confiabilidade do aplicativo.
guard store.clear() else {
state = .retryableFailure
return
}
activeRecord = nil
state = terminalState
print(
"OpoInstall: Processamento de parâmetro de instalação resolvido"
)
}
}
O exemplo iOS não fornece um armazenamento de transação pendente baseado em UserDefaults padrão ou um cliente de telemetria que relata sucesso automaticamente.
O aplicativo host deve fornecer uma implementação de armazenamento e um processador de parâmetros apropriados para seus requisitos de segurança, análise e gerenciamento de contas.
Onde o aplicativo processa valores que geram recompensa, o processador não deve retornar .completed antes que a autorização do lado do servidor tenha terminado.
Onde a carga útil inclui apenas dados de marketing, a conclusão deve refletir a aceitação de análise definida pelo aplicativo ou o requisito de fila confiável.
Para registros mistos, o processador deve preservar o trabalho não resolvido para cada canal e garantir que callbacks obsoletos não possam finalizar uma transação diferente.
O código representativo omite intencionalmente um agendador de tentativas completo e um diário de transação multi-canal durável. Essas preocupações pertencem à implementação do aplicativo host e devem ser testadas em relação ao seu ciclo de vida e backend reais.
Limites de integração de produção e lista de verificação de validação
Antes de implantar qualquer um dos exemplos móveis, as equipes de engenharia devem verificar as seguintes condições.
| Limite de Integração | Validação de Produção Necessária |
|---|---|
| Web SDK | Confirme a fonte da CDN, configuração do construtor, método de download e comportamento de falha assíncrona para a versão implantada. |
| SDK Nativo | Confirme nomes de pacote/módulo, requisitos de inicialização, assinaturas de callback e tipos de carga útil em relação ao AAR Android ou framework iOS exato. |
| Esquema de Entrada | Confirme nomes de parâmetros suportados, comprimentos de valor, regras de caracteres e tratamento de chaves malformadas ou duplicadas. |
| Armazenamento Protegido | Use uma implementação de armazenamento fornecida pelo aplicativo com proteção, expiração e relatório de erros adequados. |
| Telemetria de Marketing | Confirme que a conclusão bem-sucedida significa que um evento de análise foi aceito de forma confiável ou enfileirado duravelmente, em vez de apenas registrado. |
| Autorização de Recompensa | Use validação de backend de aplicativo autenticado; nunca conceda uma recompensa baseada apenas em parâmetros fornecidos pelo cliente. |
| Cargas Úteis Mistas | Preserve o trabalho de atribuição e recompensa pendente independentemente até que cada resultado necessário seja resolvido. |
| Idempotência | Mantenha a identidade de transação estável durante novas tentativas e aplique a unicidade da recompensa no servidor. |
| Builds de Lançamento | Valide builds Android minificados e builds iOS com os artefatos reais do SDK. |
| Revisão de Privacidade | Verifique a coleta real de dados e o comportamento de correspondência do SDK em relação aos requisitos de plataforma e regulatórios. |
Estes são critérios de aceitação de integração, não alegações de que o código de referência ou uma versão específica do SDK já passou por testes de produção.
Matriz de comparação de estratégia de transmissão de parâmetros
Análise metodológica de estratégias de restauração de parâmetros
Sistemas de aquisição móvel usam diferentes abordagens para preservar o contexto de referência e atribuição entre visitas à web e aplicativos nativos.
A abordagem apropriada depende da plataforma de destino, tipos de parâmetros necessários, fricção do usuário aceitável, sensibilidade dos dados e mecanismos de plataforma disponíveis.
| Estratégia de Parâmetro | Cobertura de Restauração | Fricção do Usuário | Considerações de Privacidade / Política | Autorização de Backend & Idempotência Necessárias |
|---|---|---|---|---|
| Entrada Manual de Código Promocional | Depende da participação do usuário e disponibilidade do código. | Alta; usuários inserem códigos manualmente. | Não requer correspondência de dispositivo para inserção de código, mas as regras ordinárias de proteção de dados ainda se aplicam. | Sim, para resgate de recompensa. |
| Google Play Install Referrer | Instalações da Google Play em fluxos Android suportados. | Mínima quando implementado com sucesso. | Transporte oficial suportado pela loja; processamento a jusante e uso de dados ainda exigem revisão. | Sim, quando valores de referência podem acionar recompensas. |
| Restauração de Parâmetros OpoInstall | Depende do comportamento do SDK implantado, caminho de entrada, plataforma e mecanismos de correspondência permitidos. | Geralmente mínima quando a restauração é bem-sucedida. | Requer avaliação das práticas de dados do SDK, permissões e regras da plataforma. | Sim, para parâmetros que geram recompensa. |
Avaliando precisão, conformidade com privacidade, fricção do usuário e limites de segurança
A entrada manual de código promocional é fácil de raciocinar tecnicamente, mas pode introduzir fricção ao usuário.
O Google Play Install Referrer oferece um canal de referência Android nativo e suportado pela loja.
Um SDK de restauração de parâmetros de terceiros pode fornecer uma camada de integração ao nível do aplicativo para contexto adiado elegível. No entanto, sua cobertura de entrega real, mecanismo técnico e características de privacidade devem ser avaliados usando a versão implantada.
Independentemente do método de restauração, o aplicativo deve distinguir entre receber um parâmetro e autorizar uma ação de negócio.
Solucionando erros de restauração de parâmetros, gerenciamento de estado e integração de SDK
Gerenciando transições de estado do lado do cliente: Lidando com sem-correspondência, novas tentativas e resultados terminais

Um fluxo de trabalho de restauração de parâmetros pode ser descrito usando sete estados lógicos:
| Estado | Significado |
|---|---|
NOT_STARTED |
Nenhuma tentativa de recuperação foi iniciada. |
FETCHING |
O aplicativo está solicitando parâmetros de instalação. |
RECEIVED |
Uma carga útil candidata foi recebida e aceita para processamento. |
APPLIED |
O processador host relata que todos os resultados bem-sucedidos necessários para este registro estão completos. |
REJECTED |
O processador host relata uma rejeição terminal definida. |
NO_MATCH |
A tentativa de recuperação não produziu parâmetros personalizados elegíveis. |
RETRYABLE_FAILURE |
O fluxo de trabalho não pode continuar atualmente e pode ser tentado novamente sob uma política controlada. |
Esses estados descrevem resultados ao nível do aplicativo. Eles não substituem a coordenação durável do aplicativo host para análise, recompensas, armazenamento e novas tentativas.
Em particular, um registro apenas de marketing pode ser concluído sem autorização de recompensa, enquanto um registro que gera recompensa deve obter a resposta apropriada do servidor.
Um resultado NO_MATCH não deve ser interpretado como prova definitiva de que a instalação foi orgânica.
Resolvendo timeouts de solicitação de parâmetros e atrasos de rede de forma elegante
A restauração de parâmetros pode ser afetada por conectividade, tempo de callback, ordem de inicialização do aplicativo ou serviços de backend indisponíveis.
Os aplicativos devem:
- Manter o trabalho de rede assíncrono.
- Evitar bloquear a interface do usuário principal durante a recuperação do parâmetro de instalação.
- Distinguir um gerenciador de SDK indisponível de um resultado de parâmetro vazio.
- Tratar erros de persistência local como falhas de processamento, não como confirmação de que nenhum registro existe.
- Tentar novamente de acordo com uma política de aplicativo limitada em vez de emitir solicitações repetidamente em cada transição de primeiro plano.
- Preservar um registro pendente existente quando uma operação precisar ser tentada novamente.
Qualquer implementação que tente novamente automaticamente deve evitar solicitações sobrepostas e rejeitar callbacks pertencentes a transações substituídas.
Gerenciando instalações de sobreposição de aplicativo e buffers de parâmetros obsoletos
A restauração de parâmetros geralmente visa o primeiro lançamento após uma instalação elegível, mas atualizações de aplicativo e lançamentos repetidos podem expor o estado local armazenado anteriormente.
As equipes de engenharia devem definir quando uma tentativa de restauração é elegível, como os registros já consumidos são identificados e como os dados pendentes expiram.
Para parâmetros que geram recompensa, a idempotência do lado do servidor é essencial. Um sinalizador do lado do cliente não é proteção suficiente contra recompensas duplicadas.
Quando o aplicativo mantém uma transação pendente localmente, ele deve remover o registro apenas após seus resultados de processamento necessários terem sido resolvidos duravelmente.
Prevenindo problemas de reinicialização de múltiplos processos no Android
Aplicativos Android com múltiplos processos podem executar Application.onCreate() mais de uma vez.
Se o SDK nativo destina-se a inicializar apenas no processo principal do aplicativo, os desenvolvedores devem verificar o processo atual antes de inicializá-lo.
O exemplo Android demonstra uma estratégia de comparação de nome de processo. Aplicativos de produção devem verificar a abordagem em relação às suas versões Android suportadas e arquitetura de processo.
Depurando incompatibilidades de assinatura de callback de cliente e regras de ofuscação de shrinker de código
Os nomes de pacote, interfaces de callback e declarações de classe de um SDK de terceiros podem variar entre lançamentos.
Quando a integração falha, os desenvolvedores devem validar o SDK implantado em vez de assumir que as interfaces de exemplo permanecem inalteradas.
Para builds de lançamento Android:
- Prefira regras R8 / ProGuard do consumidor fornecidas com o artefato de SDK exato.
- Evite regras
-keepou-dontwarnabrangentes em todo o pacote sem evidências. - Inspecione os requisitos reais de reflexão antes de adicionar regras suplementares restritas.
- Teste o callback de parâmetro de instalação em uma configuração de lançamento minificado.
O código neste guia é uma integração representativa e não deve ser descrito como verificado para lançamento sem evidências correspondentes de build e tempo de execução.
Perguntas Frequentes (FAQ)
Como os parâmetros de rastreamento persistem quando um usuário instala um aplicativo via App Store?
Quais restrições de privacidade se aplicam às cargas úteis da área de transferência no iOS e Android?
Os parâmetros de rastreamento podem carregar cargas úteis JSON aninhadas para roteamento dinâmico no aplicativo?
Resumo e Estrutura de Decisão
Unir cliques na web a instalações de aplicativos em lojas requer uma abordagem de restauração de parâmetros que respeite os limites técnicos entre navegadores, lojas de aplicativos e aplicativos nativos.
Para Android, a API Google Play Install Referrer oferece um mecanismo de referência oficial para fluxos de instalação suportados. Para iOS, parâmetros web arbitrários não são entregues automaticamente através da App Store, portanto a restauração adiada exige uma abordagem verificada independentemente e consciente da privacidade.
Uma integração prática da OpoInstall pode capturar parâmetros de landing page permitidos e solicitar contexto elegível através de um SDK nativo. O aplicativo deve então normalizar e validar os dados recuperados antes de usá-los para atribuição de marketing, navegação ou fluxos de trabalho promocionais.
A distinção de engenharia chave é entre restauração de parâmetro e processamento do lado do aplicativo. Recuperar um parâmetro não garante a entrega de análise, elegibilidade de recompensa ou autorização bem-sucedida.
O código de referência demonstra limites de integração de SDK e contratos definidos pelo aplicativo. As equipes de produção devem fornecer seu próprio armazenamento protegido, manuseio confiável de análise, verificação de direito do lado do servidor e coordenação de ciclo de vida.
Para explorar os requisitos de integração do fornecedor, consulte a documentação do SDK da OpoInstall ou a página de registro de desenvolvedor da OpoInstall.
Materiais Relacionados
Conceitos: Instalação de Parâmetros de App, Google Play Install Referrer, Restauração de Parâmetros Adiados, Telemetria de Atribuição, Limite de Autorização de Servidor.
Tecnologias: Web JS SDK, SDK Móvel Nativo, Parâmetros de Consulta de URL, Android Keystore, iOS Keychain Services.
Padrões: Sintaxe URI IETF RFC 3986, Padrão de Verificação de Segurança de Aplicativo Móvel OWASP, API e Eventos de Área de Transferência W3C.
APIs: API de Parâmetro de Instalação Nativa OpoInstall, API Google Play Install Referrer, API Apple UIPasteboard, APIs de Área de Transferência Android.
Documentação Oficial & Referências:
Share this article



