Guia de implementação da SKAdNetwork 4
Caso de uso de SKAdNetwork
SKAdNetwork (SKAN) é a estrutura de atribuição focada na privacidade da Apple que permite a medição de campanhas publicitárias de instalação de aplicações iOS, protegendo ao mesmo tempo a privacidade do utilizador. A implementação Server-to-Server (S2S) oferece uma maneira robusta de validar e rastrear o desempenho da campanha, enviando dados de SKAdNetwork diretamente entre servidores, garantindo uma atribuição precisa e o rastreamento de conversões.
A estrutura lida com todos os aspectos críticos da atribuição, mantendo a privacidade do utilizador através dos métodos prescritos pela Apple, tornando-a uma ferramenta essencial para os profissionais de marketing móvel que operam no cenário pós-iOS 14.5.
Pontos principais
- Validação e agregação robustas de postbacks de todas as redes com proteção contra fraudes incorporada
- Gestão dinâmica do valor de conversão através da configuração do painel de controlo
- Capacidades melhoradas de elaboração de relatórios com parâmetros de marketing enriquecidos e informações granulares sobre os dados
- Sistema seguro de postback de parceiros para valores de conversão descodificados e controlo de receitas
- Validação abrangente da implementação do lado do cliente através do rastreio enriquecido de eventos e sessões
- Gestão automatizada de carimbos de data/hora em várias janelas de postback (0-2 dias, 3-7 dias, 8-35 dias)
- Suporte para monetização de anúncios e rastreamento de receita regular com especificações de moeda
Pré-requisitos
- Saiba mais sobre a SKAdNetwork 4.0 no FAQ da SKAN 4.0.
- Para obter mais informações sobre a SKAdNetwork, consulte o Guia de implementação S2S da SKAdNetwork 3.0.
Componentes principais
A solução SKAdNetwork da Singular consiste nos seguintes componentes:
-
Código do lado do cliente para implementar a SKAdNetwork
Está disponível uma abordagem alternativa do lado do servidor, utilizando o endpoint API de valor de conversão. - Validação e agregação de postback de todas as redes
-
Proteção contra fraudes:
- Validação de assinaturas e deduções de ID de transação.
- Configuração segura com redes para verificar parâmetros que não são assinados (valor de conversão e geodados).
- Gestão do valor de conversão: permite configurar dinamicamente no dashboard da Singular qual a atividade pós-instalação que deve ser codificada no valor de conversão SKAdNetwork.
- Relatórios: Traduzir o ID limitado da campanha SKAdNetwork e enriquecer os dados com mais parâmetros e granularidades de marketing
- Postbacks de parceiros: Fornece o envio de postbacks SKAdNetwork com um valor de conversão descodificado em eventos e receitas, o que é fundamental para a sua otimização.
A implementação de SKAdNetwork no lado do cliente consiste em duas partes principais:
- Implementação do lado do cliente de SKAdNetwork: Esta parte é fundamental para registar as suas aplicações na SKAdNetwork e gerir o valor de conversão da SKAdNetwork de forma inteligente. Isto significa que, ao implementá-la, será capaz de otimizar as suas campanhas com base nas atribuições de SKAdNetwork e nas actividades pós-instalação associadas.
- Atualização da integração do lado do servidor: Esta parte é importante para validar e resolver problemas da implementação do lado do cliente. Ao enriquecer eventos e sessões enviados para Singular através dos endpoints Session e Event com metadados de SKAdNetwork, Singular pode validar que a implementação foi feita corretamente no lado do aplicativo.
Primeiros passos
Implementação do lado do cliente de SKAdNetwork
O Singular fornece trechos de código da interface SKAdNetwork que suportam o registro na SKAdNetwork e o gerenciamento do valor de conversão. Esses exemplos de código são responsáveis pelas seguintes partes:
- Suporte e registo na SKAdNetwork
- Gestão do valor de conversão:
- O código comunica de forma síncrona com o endpoint da Singular para receber o próximo valor de conversão com base no modelo de conversão configurado. Comunica eventos/sessões/receitas e, em resposta, obtém o próximo valor de conversão, que é um número codificado que representa a atividade pós-instalação que foi configurada para medição no dashboard da Singular.
- O código também recolhe metadados SKAdnetwork por período de medição. Os metadados são usados tanto para validação quanto para o cálculo do próximo valor de conversão:
- O carimbo de data/hora da primeira chamada para a estrutura SKAdNetwork subjacente
- O carimbo de data/hora da última chamada para a estrutura SKAdNetwork subjacente
- O último valor de postbacks atualizado (tanto Coarse como Fine)
- Receita total e receita total de Admon geradas pelo dispositivo
Fluxo de integração
O diagrama acima ilustra o fluxo de SKAdNetwork para um cliente S2S:
- Em primeiro lugar, o código na aplicação comunica com um servidor Singular (através de um endpoint dedicado para SKAdNetwork) para obter o valor de conversão mais recente de forma síncrona com base em eventos/sessões/receitas que acontecem na aplicação e atualizar a estrutura SKAdNetwork com este valor.
- Em segundo lugar, a aplicação enriquece os eventos e sessões existentes com dados SKAdNetwork, que serão posteriormente utilizados para validações.
- Quando a aplicação terminar de atualizar a estrutura SKAdNetwork com novos valores de conversão e o temporizador SKAdNetwork expirar, o postback SKAdNetwork será enviado para a rede.
- A rede encaminhará o postback para a Singular (através da configuração segura ou da configuração normal).
- A Singular processará o postback da seguinte forma
- Validando a sua assinatura
- Decodificando o valor de conversão com base no modelo de conversão configurado
- Enriquecer o postback com informações da rede. Os dados são recolhidos a partir de integrações com parceiros, juntando o SKAdNetwork e o Network campaign ID.
- Envio dos postbacks descodificados para o BI e para os parceiros
Uma nota importante é que a informação da SKAdNetwork, incluindo instalações e eventos descodificados, estará acessível através de um conjunto diferente de relatórios/APIs/tabelas ETL e postbacks para evitar misturá-la com os conjuntos de dados existentes. Isso é especialmente importante durante as próximas semanas para permitir que você meça e teste a SKAdNetwork lado a lado com a sua campanha existente.
Interface de SKAdNetwork
Este arquivo de cabeçalho define a interface pública para a integração de SKAdNetwork (SKAN), fornecendo métodos para rastreamento de atribuição, atualizações de valores de conversão e gerenciamento de receita em aplicativos iOS.
Registo de atribuição
Inicializa o rastreamento de atribuição SKAN na primeira inicialização do aplicativo, definindo o valor de conversão inicial como 0 e estabelecendo carimbos de data/hora de linha de base. O método subjacente da API da Apple gera uma notificação se o dispositivo tiver dados de atribuição para esse aplicativo.
+ (void)registerAppForAdNetworkAttribution;
Gerenciamento do valor de conversão
- O valor de conversão é calculado com base na atividade pós-instalação de um dispositivo que é capturado pelos métodos abaixo e por um modelo de conversão selecionado que pode ser configurado dinamicamente pelo utilizador.
- Os métodos nesta secção são responsáveis por extrair o próximo valor de conversão do endpoint da Singular de acordo com o modelo de conversão selecionado e a atividade pós-instalação reportada (ver documentação acima).
- Os métodos abaixo atualizam o valor de conversão com base nas seguintes atividades pós-instalação:
- Sessão: essencial para a medição da retenção de um utilizador com SKAdNetwork
- Conversion Event: essencial para a medição de eventos de conversão pós-instalação com SKAdNetwork
- Evento de receita: Crítico para a medição de receita com SKAdNetwork
Rastreamento de sessão
Gerencia o rastreamento baseado em sessões para retenção e análise de cohort, com um manipulador de conclusão opcional para ações pós-atualização.
+ (void)updateConversionValuesAsync:(void(^)(NSNumber *, NSNumber *, BOOL, NSError *))handler;
Rastreamento de eventos
Trata o rastreamento de eventos de conversão antes de enviar dados para Singular, atualizando os valores de conversão com base no contexto do evento.
+ (void)updateConversionValuesAsync:(NSString *)eventName
withCompletionHandler:(void(^)(NSNumber *, NSNumber *, BOOL, NSError *))handler;
Gestão de receitas
Rastreia eventos de receita, mantendo totais separados para monetização de anúncios e receita regular, exigindo chamadas antes das atualizações do valor da conversão.
+ (void)updateRevenue:(double)amount
andCurrency:(NSString *)currency
isAdMonetization:(BOOL)admon;
Recuperação de dados
Retorna dados SKAN abrangentes, incluindo:
- Valores de conversão refinados actuais e anteriores
- Valores grosseiros em diferentes janelas de postback
- Carimbos de data e hora de bloqueio de janela
- Controlo de receitas por moeda
- Acompanhamento separado para monetização de anúncios e receita regular
+ (NSDictionary *)getSkanDetails;
Notas de implementação
- Os métodos utilizam padrões assíncronos para evitar o bloqueio da thread principal
- O acompanhamento das receitas deve preceder as actualizações do valor de conversão
- Suporta valores de conversão de granularidade fina (0-63) e grosseira (Baixa/Média/Alta)
- Mantém um acompanhamento separado para diferentes janelas de postback
- Implementa um tratamento de erros abrangente através de manipuladores de conclusão
Código da interface SKANSnippet.h
//SKANSnippet.h
#import <Foundation/Foundation.h>
@interface SKANSnippet : NSObject
// Register for SKAdNetwork attribution.
// You should call this method as soon as possible once the app is launched for the first time.
// This function sets the conversion value to be 0 and updates the timestamp for additional processing.
+ (void)registerAppForAdNetworkAttribution;
// To track retention and cohorts you need to call this method for each app open.
// It reports the session details and updates the conversion value due to this session if needed.
// The callback passed to the method is optional, you can use it to run code once the conversion value is updated.
+ (void)updateConversionValuesAsync:(void(^)(NSNumber *, NSNumber *, BOOL, NSError *))handler;
// To track conversion events with SKAdNetwork you need to call this method after each event and before this event is sent to Singular.
// It reports the event details and updates the conversion value due to this event if needed.
// The callback passed to the method is optional, you can use it to run code once the conversion value is updated.
+ (void)updateConversionValuesAsync:(NSString *)eventName withCompletionHandler:(void(^)(NSNumber *, NSNumber *, BOOL, NSError *))handler;
// To track revenue with SKAdNetwork you need to call this method before every revenue event.
// It will update the total revenue, so when you call 'updateConversionValuesAsync', the new conversion value will be determined according to the total amout of revenue.
// Note:
// 1. Call this method before calling 'updateConversionValuesAsync' to make sure that revenue is updated.
// 2. In case of retrying an event, avoid calling this method so the same revenue will not be counted twice.
+ (void)updateRevenue:(double)amount andCurrency:(NSString *)currency isAdMonetization:(BOOL)admon;
// Gets current fine, coarse, window locked values. saved in the dictionary under "fineValue", "coarseValue", "windowLock".
// In addition, contains all other relevant values for SKAN purposes.
// e.g.
// {
// "skan_current_conversion_value": 3,
// "prev_fine_value": 2,
// "skan_first_call_to_skadnetwork_timestamp": 167890942,
// "skan_last_call_to_skadnetwork_timestamp": 167831134,
// "skan_total_revenue_by_currency": { "USD": 1.2 },
// "skan_total_admon_revenue_by_currency": { "USD": 0.8 },
// "p0_coarse": 0,
// "p1_coarse": 1,
// "p2_coarse": nil,
// "p0_window_lock": 167890942,
// "p1_window_lock": nil,
// "p2_window_lock": nil,
// "p0_prev_coarse_value": 0,
// "p1_prev_coarse_value": 0,
// "p2_prev_coarse_value": nil,
// "p0_total_iap_revenue": nil,
// "p1_total_iap_revenue": nil,
// "p2_total_iap_revenue": nil,
// "p0_total_admon_revenue": nil,
// "p1_total_admon_revenue": nil,
// "p2_total_admon_revenue": nil
// }
+ (NSDictionary *)getSkanDetails;
@end
Implementação da interface SKAdNetwork
O código implementa a interface SKAdNetwork (SKAN) da Apple para aplicações iOS, gerindo o acompanhamento da atribuição, os valores de conversão e os relatórios de receitas em várias janelas de postback.
Constantes e configuração
A implementação define três janelas de postback distintas para rastrear a atividade do utilizador e as conversões.
static NSInteger firstSkan4WindowInSec = 3600 * 24 * 2; // 48 hours
static NSInteger secondSkan4WindowInSec = 3600 * 24 * 7; // 7 days
static NSInteger thirdSkan4WindowInSec = 3600 * 24 * 35; // 35 days
Principais recursos
- Registo de atribuição:Trata da configuração inicial da atribuição da aplicação e do rastreio do valor de conversão pela primeira vez
- Gerenciamento do valor de conversão: Atualiza e rastreia os valores de conversão em várias janelas de postback.
- Rastreamento de receita: Mantém um rastreamento separado para monetização de anúncios e eventos regulares de receita.
- Persistência de dados: Usa NSUserDefaults para armazenar dados relacionados ao SKAN nas sessões do aplicativo.
- Segurança de thread: Implementa NSLock para operações thread-safe durante chamadas de rede.
Estrutura de armazenamento de dados
- Valores de conversão de granulação fina (0-63)
- Valores grosseiros (baixo/médio/alto)
- Controlo de receitas por moeda
- Gestão de carimbos temporais para janelas de postback
- Controlo de valores anteriores para conversões finas e grosseiras
Considerações sobre privacidade
- Implementa funcionalidades específicas do iOS 15.4+ e iOS 16.1+
- Lida com actualizações de valores de conversão de postback de acordo com as diretrizes de privacidade da Apple
- Mantém o rastreamento separado para diferentes tipos de receita para garantir uma atribuição precisa
Notas técnicas
- Utiliza operações assíncronas para chamadas de rede e actualizações de valores
- Implementa tratamento de erros e validação para valores de conversão
- Suporta o controlo de valores de conversão tradicional e grosseiro
- Gerencia várias janelas de postback com diferentes durações e requisitos
Atualização da integração S2S (recomendada)
Actualize a sua integração S2S com os seguintes metadados SKAdNetwork.
Estes metadados devem ser transmitidos em todas as sessões e em todos os eventos comunicados à Singular através do Session Notification Endppint e do Event Notification Endppoint. Estes dados são utilizados para a validação da implementação da SKAdNetwork.
Estrutura dos metadados
Valores de conversão
Utilize o método Data Retrieval para extrair o dicionário de metadados e reencaminhe-o para o seu servidor para anexar como parâmetros de consulta nos pedidos de API dos pontos finais Session e Event.
NSDictionary *values = [SKANSnippet getSkanDetails];
Código de implementação SKANSnippet.m
// SKANSnippet.m
#import "SKANSnippet.h"
#import <StoreKit/SKAdNetwork.h>
#import <UIKit/UIKit.h>
#define SESSION_EVENT_NAME @"__SESSION__"
#define SINGULAR_API_URL @"https://sdk-api-v1.singular.net/api/v2/conversion_value"
// SKAN Keys for NSUserDefaults persistency and requests
#define CONVERSION_VALUE_KEY @"skan_current_conversion_value"
#define FIRST_SKAN_CALL_TIMESTAMP @"skan_first_call_to_skadnetwork_timestamp"
#define LAST_SKAN_CALL_TIMESTAMP @"skan_last_call_to_skadnetwork_timestamp"
#define TOTAL_REVENUE_BY_CURRENCY @"skan_total_revenue_by_currency"
#define TOTAL_ADMON_REVENUE_BY_CURRNECY @"skan_total_admon_revenue_by_currency"
#define SKAN_UPDATED_CONVERSION_VALUE @"conversion_value"
#define SKAN_UPDATED_COARSE_VALUE @"skan_updated_coarse_value"
#define SKAN_UPDATED_LOCK_WINDOW_VALUE @"skan_updated_lock_window_value"
#define P0_COARSE @"p0_coarse"
#define P1_COARSE @"p1_coarse"
#define P2_COARSE @"p2_coarse"
#define P0_WINDOW_LOCK_TS @"p0_window_lock"
#define P1_WINDOW_LOCK_TS @"p1_window_lock"
#define P2_WINDOW_LOCK_TS @"p2_window_lock"
#define P0_PREV_FINE_VALUE @"prev_fine_value"
#define P0_PREV_COARSE_VALUE @"p0_prev_coarse_value"
#define P1_PREV_COARSE_VALUE @"p1_prev_coarse_value"
#define P2_PREV_COARSE_VALUE @"p2_prev_coarse_value"
#define TOTAL_REVENUE_P0 @"p0_total_iap_revenue"
#define TOTAL_REVENUE_P1 @"p1_total_iap_revenue"
#define TOTAL_REVENUE_P2 @"p2_total_iap_revenue"
#define TOTAL_ADMON_REVENUE_P0 @"p0_total_admon_revenue"
#define TOTAL_ADMON_REVENUE_P1 @"p1_total_admon_revenue"
#define TOTAL_ADMON_REVENUE_P2 @"p2_total_admon_revenue"
@implementation SKANSnippet
static NSInteger firstSkan4WindowInSec = 3600 * 24 * 2; //48 hours in sec
static NSInteger secondSkan4WindowInSec = 3600 * 24 * 7;
static NSInteger thirdSkan4WindowInSec = 3600 * 24 * 35;
static NSLock *lockObject;
+ (void)registerAppForAdNetworkAttribution {
if ([SKANSnippet getFirstSkanCallTimestamp] != 0) {
return;
}
if (@available(iOS 15.4, *)) {
[SKAdNetwork updatePostbackConversionValue:0 completionHandler:nil];
[SKANSnippet setFirstSkanCallTimestamp];
[SKANSnippet setLastSkanCallTimestamp];
[SKANSnippet valuesHasBeenUpdated:@(0) coarseValue:nil lockWindow:NO];
}
}
+ (void)updateConversionValuesAsync:(void(^)(NSNumber *, NSNumber *, BOOL, NSError *))handler {
[SKANSnippet updateConversionValuesAsync:SESSION_EVENT_NAME withCompletionHandler:handler];
}
+ (void)updateConversionValuesAsync:(NSString *)eventName withCompletionHandler:(void(^)(NSNumber *, NSNumber *, BOOL, NSError *))handler {
if ([SKANSnippet isSkanWindowOver]) {
return;
}
[SKANSnippet getConversionValueFromServer:eventName withCompletionHandler:handler];
}
+ (void)updateRevenue:(double)amount andCurrency:(NSString *)currency isAdMonetization:(BOOL)admon {
// Update total revenues
if (amount == 0 || !currency ) {
return;
}
[SKANSnippet addToTotalRevenue:@(amount) withCurrency:currency isAdmon:admon];
}
+ (NSDictionary *)getSkanDetails {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *res = [NSMutableDictionary dictionary];
//current fine
[res setValue:[[userDefaults valueForKey:CONVERSION_VALUE_KEY] stringValue] forKey:CONVERSION_VALUE_KEY];
//prev fine
[res setValue:[[userDefaults valueForKey:P0_PREV_FINE_VALUE] stringValue] forKey:P0_PREV_FINE_VALUE];
//current coarse
[res setValue:[[userDefaults valueForKey:P0_COARSE] stringValue] forKey:P0_COARSE];
[res setValue:[[userDefaults valueForKey:P1_COARSE] stringValue] forKey:P1_COARSE];
[res setValue:[[userDefaults valueForKey:P2_COARSE] stringValue] forKey:P2_COARSE];
//prev coarse
[res setValue:[[userDefaults valueForKey:P0_PREV_COARSE_VALUE] stringValue] forKey:P0_PREV_COARSE_VALUE];
[res setValue:[[userDefaults valueForKey:P1_PREV_COARSE_VALUE] stringValue] forKey:P1_PREV_COARSE_VALUE];
[res setValue:[[userDefaults valueForKey:P2_PREV_COARSE_VALUE] stringValue] forKey:P2_PREV_COARSE_VALUE];
//lock windows ts
[res setValue:[[userDefaults valueForKey:P0_WINDOW_LOCK_TS] stringValue] forKey:P0_WINDOW_LOCK_TS];
[res setValue:[[userDefaults valueForKey:P1_WINDOW_LOCK_TS] stringValue] forKey:P1_WINDOW_LOCK_TS];
[res setValue:[[userDefaults valueForKey:P2_WINDOW_LOCK_TS] stringValue] forKey:P2_WINDOW_LOCK_TS];
//total revenues
[res setValue:[userDefaults valueForKey:TOTAL_REVENUE_BY_CURRENCY] forKey:TOTAL_REVENUE_BY_CURRENCY];
[res setValue:[userDefaults valueForKey:TOTAL_ADMON_REVENUE_BY_CURRNECY] forKey:TOTAL_ADMON_REVENUE_BY_CURRNECY];
//revenue per window
[res setValue:[userDefaults valueForKey:TOTAL_REVENUE_P0] forKey:TOTAL_REVENUE_P0];
[res setValue:[userDefaults valueForKey:TOTAL_REVENUE_P1] forKey:TOTAL_REVENUE_P1];
[res setValue:[userDefaults valueForKey:TOTAL_REVENUE_P2] forKey:TOTAL_REVENUE_P2];
[res setValue:[userDefaults valueForKey:TOTAL_ADMON_REVENUE_P0] forKey:TOTAL_ADMON_REVENUE_P0];
[res setValue:[userDefaults valueForKey:TOTAL_ADMON_REVENUE_P1] forKey:TOTAL_ADMON_REVENUE_P1];
[res setValue:[userDefaults valueForKey:TOTAL_ADMON_REVENUE_P2] forKey:TOTAL_ADMON_REVENUE_P2];
//skan TS
[res setValue:[[userDefaults valueForKey:LAST_SKAN_CALL_TIMESTAMP] stringValue] forKey:LAST_SKAN_CALL_TIMESTAMP];
[res setValue:[[userDefaults valueForKey:FIRST_SKAN_CALL_TIMESTAMP] stringValue] forKey:FIRST_SKAN_CALL_TIMESTAMP];
return res;
}
#pragma mark - internal
+ (BOOL)validateValues:(NSNumber *)conversionValue coarse:(NSNumber *)coarseValue{
if ([conversionValue intValue] < 0 || 63 < [conversionValue intValue]) {
return NO;
}
if (coarseValue) {
if ([coarseValue intValue] > 2 || [coarseValue intValue] < 0) {
return NO;
}
}
return YES;
}
+ (NSURLComponents *)prepareQueryParams:(NSString *)bundleIdentifier eventName:(NSString *)eventName {
NSURLComponents *components = [NSURLComponents componentsWithString:SINGULAR_API_URL];
NSString *API_KEY = @"YOUR API KEY";
NSString *APP_VERSION = @"YOUR APP VERSION";
NSString *IDFV = @"IDFV";
NSString *IDFA = @"IDFA";
NSMutableArray *queryItems = [@[
[NSURLQueryItem queryItemWithName:@"a" value:API_KEY],
[NSURLQueryItem queryItemWithName:@"v" value:[[UIDevice currentDevice] systemVersion]],
[NSURLQueryItem queryItemWithName:@"i" value:bundleIdentifier],
[NSURLQueryItem queryItemWithName:@"app_v" value:APP_VERSION],
[NSURLQueryItem queryItemWithName:@"n" value:eventName],
[NSURLQueryItem queryItemWithName:@"p" value:@"iOS"],
[NSURLQueryItem queryItemWithName:@"idfv" value:IDFV],
[NSURLQueryItem queryItemWithName:@"idfa" value:IDFA]
] mutableCopy];
NSDictionary *skanValues = [SKANSnippet getSkanDetails];
[skanValues enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
if ([obj isKindOfClass:[NSDictionary class]]) {
[queryItems addObject:[NSURLQueryItem queryItemWithName:key value:[SKANSnippet dictionaryToJsonString:obj]]];
} else {
[queryItems addObject:[NSURLQueryItem queryItemWithName:key value:obj]];
}
}];
components.queryItems = queryItems;
return components;
}
+ (void)getConversionValueFromServer:(NSString*)eventName withCompletionHandler:(void(^)(NSNumber *, NSNumber *, BOOL, NSError*))handler {
if (!lockObject) {
lockObject = [NSLock new];
}
@try {
// Making the lock async so it will not freeze the calling thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
[lockObject lock];
NSString *bundleIdentifier = @"YOUR BUNDLE IDENTIFIER";
NSURLComponents *components = [SKANSnippet prepareQueryParams:bundleIdentifier eventName:eventName];
[[[NSURLSession sharedSession] dataTaskWithURL:components.URL
completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error) {
[lockObject unlock];
if (handler) {
handler(nil, nil, NO, error);
}
return;
}
NSDictionary *parsedResponse = [SKANSnippet jsonDataToDictionary:data];
if (!parsedResponse) {
[lockObject unlock];
if (handler) {
handler(nil,nil, NO, [NSError errorWithDomain:bundleIdentifier
code:0
userInfo:@{NSLocalizedDescriptionKey:@"Failed parsing server response"}]);
}
return;
}
NSNumber *conversionValue = [parsedResponse objectForKey:SKAN_UPDATED_CONVERSION_VALUE];
NSNumber *coarseValue = [parsedResponse objectForKey:SKAN_UPDATED_COARSE_VALUE];
BOOL lockWindow = [[parsedResponse objectForKey:SKAN_UPDATED_LOCK_WINDOW_VALUE] boolValue];
if (!conversionValue) {
[lockObject unlock];
NSString *status = [parsedResponse objectForKey:@"status"];
if (!status || ![status isEqualToString:@"ok"]) {
if (handler) {
NSString *reason = [parsedResponse objectForKey:@"reason"];
if (!reason) {
reason = @"Got error from server";
}
handler(nil, nil, NO, [NSError errorWithDomain:bundleIdentifier
code:0
userInfo:@{NSLocalizedDescriptionKey:reason}]);
}
}
return;
}
if(![SKANSnippet validateValues:conversionValue coarse:coarseValue]) {
if (handler) {
handler(nil,nil, NO, [NSError errorWithDomain:bundleIdentifier
code:0
userInfo:@{NSLocalizedDescriptionKey:@"Illegal values recieved"}]);
}
return;
}
if (![SKANSnippet getFirstSkanCallTimestamp]) {
[SKANSnippet setFirstSkanCallTimestamp];
}
[SKANSnippet setConversionValues:conversionValue coarseValue:coarseValue lockWindow:lockWindow handler:handler];
[lockObject unlock];
}] resume];
});
} @catch (id exception) {
NSLog(@"%@", exception);
}
}
+ (void)setFirstSkanCallTimestamp {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setInteger:[SKANSnippet getCurrentUnixTimestamp] forKey:FIRST_SKAN_CALL_TIMESTAMP];
[userDefaults synchronize];
}
+ (void)setLastSkanCallTimestamp {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setInteger:[SKANSnippet getCurrentUnixTimestamp] forKey:LAST_SKAN_CALL_TIMESTAMP];
[userDefaults synchronize];
}
+ (NSString*)dictionaryToJsonString:(NSDictionary*)dictionary {
if (!dictionary || [dictionary count] == 0){
return @"{}";
}
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dictionary
options:0
error:&error];
if (error || !jsonData) {
return @"{}";
}
return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
+ (NSInteger)getFirstSkanCallTimestamp {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
return [userDefaults integerForKey:FIRST_SKAN_CALL_TIMESTAMP];
}
+ (NSInteger)getLastSkanCallTimestamp {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
return [userDefaults integerForKey:LAST_SKAN_CALL_TIMESTAMP];
}
+ (NSInteger)getCurrentUnixTimestamp {
return [[NSDate date] timeIntervalSince1970];
}
+ (void)setConversionValues:(NSNumber *)conversionValue coarseValue:(NSNumber *)coarse lockWindow:(BOOL)lockWindow handler:(void(^)(NSNumber *, NSNumber *, BOOL, NSError*))handler {
@try {
__block void(^skanResultHandler)(NSError * _Nullable error) = ^(NSError * _Nullable error) {
if (handler) {
if (error) {
handler(nil, nil, NO, error);
} else {
handler(conversionValue, coarse, lockWindow, nil);
}
}
[SKANSnippet valuesHasBeenUpdated:conversionValue coarseValue:coarse lockWindow:lockWindow];
};
if (@available(iOS 16.1, *)) {
[SKAdNetwork updatePostbackConversionValue:[conversionValue integerValue] coarseValue:[SKANSnippet resolveCoarseValueFrom:coarse] lockWindow:lockWindow completionHandler:^(NSError * _Nullable error) {
skanResultHandler(error);
}];
} else {
if (@available(iOS 15.4, *)) {
[SKAdNetwork updatePostbackConversionValue:[conversionValue integerValue] completionHandler:^(NSError * _Nullable error) {
skanResultHandler(error);
}];
}
}
} @catch (id exception) {
NSLog(@"%@", exception);
}
}
+ (NSNumber *)getConversionValue {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
if (![userDefaults objectForKey:CONVERSION_VALUE_KEY]) {
return @(0);
}
return @([userDefaults integerForKey:CONVERSION_VALUE_KEY]);
}
+ (NSDictionary*)jsonDataToDictionary:(NSData*)jsonData {
if (!jsonData) {
return nil;
}
NSError *error;
NSDictionary *parsedData = [NSJSONSerialization JSONObjectWithData:jsonData
options:kNilOptions error:&error];
if (error || !parsedData) {
return nil;
}
return parsedData;
}
+ (NSInteger)getCurrentSkanWindow {
NSInteger timeDiff = [SKANSnippet getCurrentUnixTimestamp] - [SKANSnippet getFirstSkanCallTimestamp];
if (timeDiff < firstSkan4WindowInSec) { return 0; }
if (timeDiff < secondSkan4WindowInSec) { return 1; }
if (timeDiff < thirdSkan4WindowInSec) { return 2; }
return -1;
}
// persist updated conversion values based on the active skan window.
+ (void)valuesHasBeenUpdated:(NSNumber *)fineValue coarseValue:(NSNumber *)coarseValue lockWindow:(BOOL)lockWindow {
NSNumber *currentPersistedFineValue;
NSNumber *currentPersistedCoarseValue;
NSInteger window = [SKANSnippet getCurrentSkanWindow];
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
switch (window) {
case 0:
currentPersistedFineValue = [userDefaults objectForKey:CONVERSION_VALUE_KEY];
currentPersistedCoarseValue = [userDefaults objectForKey:P0_COARSE];
[userDefaults setValue:fineValue forKey:CONVERSION_VALUE_KEY];
[userDefaults setValue:currentPersistedFineValue forKey:P0_PREV_FINE_VALUE];
[userDefaults setValue:coarseValue forKey:P0_COARSE];
[userDefaults setValue:currentPersistedCoarseValue forKey:P0_PREV_COARSE_VALUE];
if (lockWindow) {
[userDefaults setObject:@([SKANSnippet getCurrentUnixTimestamp]) forKey:P0_WINDOW_LOCK_TS];
}
break;
case 1:
currentPersistedCoarseValue = [userDefaults objectForKey:P1_COARSE];
[userDefaults setValue:coarseValue forKey:P1_COARSE];
[userDefaults setValue:currentPersistedCoarseValue forKey:P1_PREV_COARSE_VALUE];
if (lockWindow) {
[userDefaults setObject:@([SKANSnippet getCurrentUnixTimestamp]) forKey:P1_WINDOW_LOCK_TS];
}
break;
case 2:
currentPersistedCoarseValue = [userDefaults objectForKey:P2_COARSE];
[userDefaults setValue:coarseValue forKey:P2_COARSE];
[userDefaults setValue:currentPersistedCoarseValue forKey:P2_PREV_COARSE_VALUE];
if (lockWindow) {
[userDefaults setValue:@([SKANSnippet getCurrentUnixTimestamp]) forKey:P2_WINDOW_LOCK_TS];
}
break;
}
[SKANSnippet setLastSkanCallTimestamp];
}
+ (BOOL)isSkanWindowOver {
NSInteger timeDiff = [SKANSnippet getCurrentUnixTimestamp] - [SKANSnippet getFirstSkanCallTimestamp];
return thirdSkan4WindowInSec <= timeDiff;
}
// Revenues are being accumulated and saved by Ad monetization and non ad monetization events, total sum and break down by skan windows.
+ (void)addToTotalRevenue:(NSNumber *)newRevenue withCurrency:(NSString *)currency isAdmon:(BOOL)isAdmon {
NSString *key = isAdmon ? TOTAL_ADMON_REVENUE_BY_CURRNECY : TOTAL_REVENUE_BY_CURRENCY;
[SKANSnippet addToTotalRevenue:newRevenue withCurrency:currency forKey:key];
NSInteger window = [SKANSnippet getCurrentSkanWindow];
switch (window) {
case 0:
key = isAdmon ? TOTAL_ADMON_REVENUE_P0 : TOTAL_REVENUE_P0 ;
break;
case 1:
key = isAdmon ? TOTAL_ADMON_REVENUE_P1 : TOTAL_REVENUE_P1 ;
break;
case 2:
key = isAdmon ? TOTAL_ADMON_REVENUE_P2 : TOTAL_REVENUE_P2 ;
break;
case -1:
key = nil;
return;
}
[SKANSnippet addToTotalRevenue:newRevenue withCurrency:currency forKey:key];
}
// Coarse value is being sent on requests and responses as an Int and being translated into the system defined coarse value upon API execution.
+ (NSString *)resolveCoarseValueFrom:(NSNumber *)value {
if(@available(iOS 16.1, *)) {
if (!value) {
return nil;
}
switch ([value integerValue]) {
case 0:
return SKAdNetworkCoarseConversionValueLow;
case 1:
return SKAdNetworkCoarseConversionValueMedium;
case 2:
return SKAdNetworkCoarseConversionValueHigh;
default:
return nil;
}
}
return nil;
}
+ (NSDictionary*)getTotalRevenue:(NSString *)revenueKey {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
if (![userDefaults objectForKey:revenueKey]){
[userDefaults setObject:[NSDictionary dictionary] forKey:revenueKey];
}
return [userDefaults objectForKey:revenueKey];
}
+ (void)addToTotalRevenue:(NSNumber *)newRevenue withCurrency:(NSString *)currency forKey:(NSString *)revenueKey {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *revenues = [[SKANSnippet getTotalRevenue:revenueKey] mutableCopy];
NSNumber *currentRevenue = 0;
if ([revenues objectForKey:currency]) {
currentRevenue = [revenues objectForKey:currency];
}
currentRevenue = @([currentRevenue floatValue] + [newRevenue floatValue]);
[revenues setObject:currentRevenue forKey:currency];
[userDefaults setObject:revenues forKey:revenueKey];
[userDefaults synchronize];
}
@end
Exemplo de fluxo do ciclo de vida da aplicação
Este código demonstra os principais pontos de integração para a atribuição de SKAdNetwork (SKAN) num ciclo de vida de uma aplicação iOS, lidando com lançamentos de aplicações, sessões, eventos e rastreio de receitas.
Notas de implementação
- O código utiliza métodos assíncronos para actualizações de valores de conversão para evitar o bloqueio da thread principal
- Todos os dados relacionados com o SKAN são recolhidos num formato de dicionário antes de serem enviados para o servidor
- A implementação segue a abordagem de privacidade da Apple, permitindo ao mesmo tempo o controlo de atribuição essencial
- O controlo de receitas inclui o valor monetário e a especificação da moeda para relatórios financeiros precisos
Primeiro lançamento da aplicação
Este código regista a aplicação na SKAdNetwork para rastreio de atribuição e envia os dados da sessão inicial para o endpoint da Singular. Isso só é executado na primeira inicialização do aplicativo para estabelecer o rastreamento de atribuição.
Isso só é executado na primeira inicialização do aplicativo para estabelecer o rastreamento de atribuição.
[SKANSnippet registerAppForAdNetworkAttribution];
NSDictionary *skanValues = [SKANSnippet getSkanDetails];
[self sendSessionToServer:skanValues] //to Singular launch EP
Gerenciamento de sessão
Esta secção actualiza os valores de conversão após cada sessão e envia os detalhes SKAN actualizados para acompanhar o envolvimento do utilizador.
[SKANSnippet updateConversionValuesAsync:handler];
NSDictionary *skanValues = [SKANSnippet getSkanDetails];
[self sendSessionToServer:skanValues]
Acompanhamento de eventos
Esse código lida com eventos sem receita atualizando os valores de conversão e enviando dados de eventos para o endpoint de eventos do Singular.
[SKANSnippet updateConversionValuesAsync:@"event_name" withCompletionHandler:handler];
NSDictionary *skanValues = [SKANSnippet getSkanDetails];
[self sendEventToServer:skanValues eventName:@"event_name"]
Rastreamento de receita
Esta seção gerencia eventos de receita atualizando o valor da receita com a moeda e os valores de conversão associados. Os dados são então enviados para o ponto de extremidade de evento do Singular para rastrear atividades relacionadas a compras.
[SKANSnippet updateRevenue:15.53 andCurrency:@"KRW"];
[SKANSnippet updateConversionValuesAsync:@"revenue_event_name" withCompletionHandler:handler];
NSDictionary *skanValues = [SKANSnippet getSkanDetails];
[self sendEventToServer:skanValues eventName:@"revenue_event_name"]
API de valores de conversão
O valor de conversão da SKAdNetwork pode ser reportado através de dois métodos:
- Implementação direta da interface SKAdNetwork no lado do cliente - ver acima
- Integração do lado do servidor utilizando o ponto final da API de valor de conversão
Ambos os métodos mantêm o mesmo fluxo de dados e integridade de relatório, permitindo-lhe escolher a implementação que melhor se adapta à sua arquitetura técnica. O ponto de extremidade da API Conversion Value aceita parâmetros idênticos aos da interface do lado do cliente, garantindo um acompanhamento consistente da atribuição.
Conteúdo
- Ponto de extremidade da API de valor de conversão
- Parâmetros necessários
- Corpo do pedido
- Exemplos de pedidos
- Resposta ao pedido
- Parâmetros de resposta
- Possíveis erros de resposta
- Parâmetros opcionais
Ponto final da API do valor de conversão
Método HTTP e ponto final do valor de conversão
GET https://sdk-api-v1.singular.net/api/v2/conversion_value
Parâmetros necessários
A tabela seguinte lista os parâmetros necessários e opcionais para suportar a API de conversão a partir do seu servidor. Todos os parâmetros listados são parâmetros de consulta.
Parâmetros necessários | |
---|---|
Chave da API | |
Parâmetro | Descrição |
|
O parâmetro a especifica a chave do SDK Singular. Recupere a Chave SDK na IU do Singular, em Ferramentas de Desenvolvedor no Menu Principal. Nota: Não use a chave da API de relatório, pois isso resultará em dados rejeitados. Exemplo de valor:
|
Parâmetros do identificador do dispositivo | |
Parâmetro | Descrição |
|
O parâmetro idfa especifica o Identificador para Anunciantes (IDFA), que ajuda os anunciantes a acompanhar e atribuir acções do utilizador (por exemplo, cliques em anúncios, instalações de aplicações) a campanhas específicas, permitindo uma segmentação precisa dos anúncios e a otimização das campanhas. A partir do iOS 14.5, os utilizadores devem aderir através da estrutura App Tracking Transparency (ATT) para que as aplicações possam aceder ao IDFA. Se os utilizadores não optarem por aderir ao IDFA, este não estará disponível, o que resulta na limitação das capacidades de rastreio.
Exemplo de valor:
|
Parâmetro | Descrição do parâmetro |
|
O parâmetro idfv especifica o Identificador para Fornecedores (IDFV), um identificador único atribuído pela Apple a um dispositivo, que é específico de um determinado fornecedor ou programador. Mantém-se consistente em todas as aplicações do mesmo fornecedor num determinado dispositivo, permitindo ao fornecedor seguir o comportamento e as interações do utilizador no seu ecossistema de aplicações sem identificar o utilizador pessoalmente.
Exemplo de valor:
|
Parâmetros do dispositivo | |
Parâmetro | Descrição |
|
O parâmetro p especifica a plataforma da aplicação. Uma vez que esta API só é utilizada para iOS, este valor tem de ser iOS. Exemplo de valor:
|
Parâmetro | Descrição |
|
O parâmetro v especifica a versão do sistema operativo do dispositivo no momento da sessão. Exemplo de valor:
|
Parâmetros da aplicação | |
Parâmetro | Descrição |
|
O parâmetro i especifica o identificador da aplicação. Este é o ID do pacote para a aplicação iOS. (sensível a maiúsculas e minúsculas) Exemplo de valor:
|
Parâmetro | Descrição |
|
O parâmetro app_v indica a versão da aplicação. Exemplos:
|
Parâmetros de eventos | |
Parâmetro | Descrição |
|
O parâmetro n indica o nome do acontecimento que está a ser controlado.
Exemplo de valor:
|
Parâmetros de valor de conversão | |
Parâmetro | Descrição do parâmetro |
Plataformas suportadas:
|
O valor de conversão SKAdNetwork mais recente, no momento da notificação da sessão/evento anterior. Trata-se de um número inteiro entre (0-63).
Exemplo de valor:
|
Parâmetro | Descrição do parâmetro |
Plataformas suportadas:
|
O valor mais recente de conversão grosseira da SKAdNetwork para postback_sequence 1, no momento da notificação da sessão/evento anterior. Este é um número inteiro entre (0-2).
Exemplo de valor:
|
Parâmetro | Descrição do parâmetro |
Plataformas suportadas:
|
O valor mais recente de conversão grosseira da SKAdNetwork para postback_sequence 2, no momento da notificação da sessão/evento anterior. Trata-se de um número inteiro entre (0-2).
Exemplo de valor:
|
Parâmetros de rastreamento de receita | |
Parâmetro | Descrição do parâmetro |
Plataformas suportadas:
|
Necessário se estiver a utilizar modelos IAP ou Todas as receitas. Total agregado atual das receitas de IAP geradas pelo dispositivo, excluindo quaisquer receitas de monetização de anúncios.
Exemplo de valor:
|
Parâmetro | Descrição |
Plataformas suportadas:
|
Necessário se estiver usando Admon ou Todos os modelos de conversão de receita. Total agregado atual da receita de monetização de anúncios gerada pelo dispositivo.
Exemplo de valor:
|
Parâmetros de carimbo de data/hora | |
Parâmetro | Descrição |
Plataformas suportadas:
|
Carimbo de data/hora Unix da primeira chamada à API SKAdNetwork subjacente.
Exemplo de valor:
|
Parâmetro | Descrição do parâmetro |
Plataformas suportadas:
|
Carimbo de data/hora Unix da última chamada para a API SKAdNetwork subjacente, no momento desta notificação de sessão.
Exemplo de valor:
|
Corpo do pedido
Não forneça um corpo de pedido quando chamar este método. O pedido deve ser enviado utilizando o método GET com parâmetros de consulta.
Exemplos de pedidos
Os seguintes exemplos de código podem não representar todos os parâmetros suportados. Ao implementar o pedido, certifique-se de que inclui todos os parâmetros necessários, conforme listado acima, e valide se os valores corretos estão a ser passados antes de enviar dados de uma instância de produção. Aconselha-se a utilização de um parâmetro único `i` (identificador da aplicação) para desenvolvimento e teste.
PYTHON
import requests
params = {
'a': 'sdk_key_here',
'p': 'iOS',
'i': 'com.singular.app',
'v': '16.1',
'idfa': 'DFC5A647-9043-4699-B2A5-76F03A97064B',
'idfv': '21DB6612-09B3-4ECC-84AC-B353B0AF1334',
'n': '__SESSION__',
'app_v': '1.2.3',
'skan_current_conversion_value': 7,
'p1_coarse': 0,
'p2_coarse': 1,
'skan_total_revenue_by_currency': {"USD":9.99},
'skan_total_admon_revenue_by_currency': {"USD":1.2},
'skan_first_call_to_skadnetwork_timestamp': 1510090877,
'skan_last_call_to_skadnetwork_timestamp': 1510090877
}
response = requests.get('https://sdk-api-v1.singular.net/api/v2/conversion_value', params=params)
print(response.json())
CURL
curl -G 'https://sdk-api-v1.singular.net/api/v2/conversion_value' \
--data-urlencode 'a=sdk_key_here' \
--data-urlencode 'p=iOS' \
--data-urlencode 'i=com.singular.app' \
--data-urlencode 'v=16.1' \
--data-urlencode 'idfa=DFC5A647-9043-4699-B2A5-76F03A97064B' \
--data-urlencode 'idfv=21DB6612-09B3-4ECC-84AC-B353B0AF1334' \
--data-urlencode 'n=__SESSION__' \
--data-urlencode 'app_v=1.2.3' \
--data-urlencode 'skan_current_conversion_value=7' \
--data-urlencode 'p1_coarse=0' \
--data-urlencode 'p2_coarse=1' \
--data-urlencode 'skan_total_revenue_by_currency={"USD":9.99}' \
--data-urlencode 'skan_total_admon_revenue_by_currency={"USD":1.2}' \
--data-urlencode 'skan_first_call_to_skadnetwork_timestamp=1510040127' \
--data-urlencode 'skan_last_call_to_skadnetwork_timestamp=1510090877'
HTTP
GET /api/v2/conversion_value
?a=sdk_key_here
&p=iOS
&i=com.singular.app
&v=16.1
&idfa=DFC5A647-9043-4699-B2A5-76F03A97064B
&idfv=21DB6612-09B3-4ECC-84AC-B353B0AF1334
&n=__SESSION__
&app_v=1.2.3
&skan_current_conversion_value=7
&p1_coarse=0
&p2_coarse=1
&skan_total_revenue_by_currency=%7B%22USD%22%3A9.99%7D
&skan_total_admon_revenue_by_currency=%7B%22USD%22%3A1.2%7D
&skan_first_call_to_skadnetwork_timestamp=1510090877
&skan_last_call_to_skadnetwork_timestamp=1510090877 HTTP/1.1
Host: sdk-api-v1.singular.net
Accept: application/json
Exemplo JAVA
// Base URL
String baseUrl = "https://sdk-api-v1.singular.net/api/v2/conversion_value";
// Parameters
Map < String, String > params = new HashMap < > ();
params.put("a", "sdk_key_here");
params.put("p", "iOS");
params.put("i", "com.singular.app");
params.put("v", "16.1");
params.put("idfa", "DFC5A647-9043-4699-B2A5-76F03A97064B");
params.put("idfv", "21DB6612-09B3-4ECC-84AC-B353B0AF1334");
params.put("n", "__SESSION__");
params.put("app_v", "1.2.3");
params.put("skan_current_conversion_value", "7");
params.put("p1_coarse", "0");
params.put("p2_coarse", "1");
params.put("skan_first_call_to_skadnetwork_timestamp", "1510040127");
params.put("skan_last_call_to_skadnetwork_timestamp", "1510090877");
// JSON objects
JSONObject revenueJson = new JSONObject().put("USD", 9.99);
params.put("skan_total_revenue_by_currency", revenueJson.toString());
JSONObject admonJson = new JSONObject().put("USD", 1.2);
params.put("skan_total_admon_revenue_by_currency", admonJson.toString());
// Build URL with encoded parameters
StringBuilder urlBuilder = new StringBuilder(baseUrl);
urlBuilder.append('?');
for (Map.Entry < String, String > entry: params.entrySet()) {
if (urlBuilder.length() > baseUrl.length() + 1) {
urlBuilder.append('&');
}
urlBuilder.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8))
.append('=')
.append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8));
}
// Create connection
URL url = new URL(urlBuilder.toString());
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Accept", "application/json");
// Get response
int responseCode = conn.getResponseCode();
BufferedReader in = new BufferedReader(
new InputStreamReader(conn.getInputStream())
);
String inputLine;
StringBuilder response = new StringBuilder();
while ((inputLine = in .readLine()) != null) {
response.append(inputLine);
} in .close();
// Check application-level status
System.out.println("HTTP Status Code: " + responseCode);
System.out.println("Response: " + response.toString());
// Disconnect
conn.disconnect();
Resposta ao pedido
O seguinte identifica uma resposta API bem sucedida com um novo valor de conversão devolvido.
Resposta HTTP | |
---|---|
|
O 200 - ok sem qualquer erro ou razão no corpo da resposta significa que o pedido foi enviado para a fila de espera para processamento. Resposta:
|
Parâmetros de resposta
A tabela seguinte define os parâmetros de resposta.
Chave | Descrição | Exemplo Valor |
---|---|---|
|
Novo valor de conversão fina |
|
|
Novo valor de conversão grosseira |
|
|
Corresponde aos períodos de medição do postback SKAN:
Indica que chave de valor de conversão grosseira deve ser actualizada: ou seja, p0_coarse, p1_coarse, p2_coarse |
|
|
Ok para processado com êxito |
|
Possíveis erros de resposta
- Passaram mais de 24 horas desde a última atualização de conversão (28032 horas), a janela de atualização está fechada:
- Horas calculadas como:
(skan_last_call_to_skadnetwork_timestamp) - (skan_first_call_to_skadnetwork_timestamp)
- Horas calculadas como:
- Erro de plataforma desconhecida - Plataforma não iOS
- Gestão de conversões: Parâmetro inválido %x fornecido
- Gestão de Conversões: Modelo de conversão não encontrado para a aplicação: %x
- Período de medição inválido: %x
- Gestão de Conversões: Não é possível encontrar a moeda do proprietário: %customer
Parâmetros opcionais
A tabela seguinte lista os parâmetros opcionais utilizados para suportar a versão 4 da SKAdNetwork. Todos os parâmetros listados são parâmetros de consulta.
Parâmetros opcionais | |
---|---|
Parâmetros de valor de conversão | |
Parâmetro | Descrição |
Plataformas suportadas:
|
Não é necessário. Esta chave é mapeada a partir da chave skan_current_conversion_value. Por outras palavras, o modelo não utiliza p0_coarse quando avalia o skan_updated_coarse_value para devolver na resposta - utiliza o skan_current_conversion_value. Este é um número inteiro entre (0-2).
Exemplo de valor:
|
Parâmetro | Descrição do parâmetro |
Plataformas suportadas:
|
O valor grosseiro anterior para p0. Trata-se de um número inteiro entre (0-2).
Exemplo de valor:
|
Parâmetro | Descrição do parâmetro |
Plataformas suportadas:
|
O valor grosseiro anterior para p1. Trata-se de um número inteiro entre (0-2).
Exemplo de valor:
|
Plataformas suportadas:
|
O valor grosseiro anterior para p2. Trata-se de um número inteiro entre (0-2).
Exemplo de valor:
|
Controlo de receitas | |
Plataformas suportadas:
|
Total da receita de IAP para p0, excluindo a receita de monetização de anúncios.
Exemplo de valor:
|
Plataformas suportadas:
|
Total de receitas de IAP para p1, excluindo as receitas de monetização de anúncios.
Exemplo de valor:
|
Plataformas suportadas:
|
Total de receitas de IAP para p2, excluindo as receitas de monetização de anúncios.
Exemplo de valor:
|
Plataformas suportadas:
|
Total da receita de monetização de anúncios para p0.
Exemplo de valor:
|
Plataformas suportadas:
|
Total das receitas de monetização de anúncios para p1.
Exemplo de valor:
|
Plataformas suportadas:
|
Total das receitas de monetização de anúncios para p2.
Exemplo de valor:
|
Parâmetros de carimbo de data/hora | |
Parâmetro | Descrição |
Plataformas suportadas:
|
Carimbo de data/hora Unix da última atualização com bloqueio de janela para p0 Nota - o modelo de conversão singular não tem atualmente em conta o bloqueio de janela, mas poderá vir a ter
Exemplo de valor:
|
Parâmetro | Descrição do parâmetro |
Plataformas suportadas:
|
Carimbo de data/hora Unix da última atualização com bloqueio de janela para p1 Nota - o modelo de conversão singular não tem atualmente em conta o bloqueio de janela, mas poderá vir a ter
Exemplo de valor:
|
Parâmetro | Descrição do parâmetro |
Plataformas suportadas:
|
Carimbo de data/hora Unix da última atualização com bloqueio de janela para p2 Nota - o modelo de conversão singular não tem atualmente em conta o bloqueio de janela, mas poderá vir a ter
Exemplo de valor:
|