Guia de Implementação S2S do SKAdNetwork 3.0 (Legado)

Legado — SKAdNetwork 3. Este guia cobre a implementação legada do SKAdNetwork 3.0 e é mantido apenas para integrações existentes. Para todas as implementações novas e atuais, use o Guia de Implementação do SKAdNetwork 4 .

Guia de Implementação S2S do SKAdNetwork 3.0 (Legado)

Implemente o framework SKAdNetwork 3.0 da Apple para atribuição iOS em conformidade com a privacidade usando integração server-to-server para medir o desempenho de campanhas de instalação de apps sem comprometer os identificadores dos usuários.


Visão Geral

O que é o SKAdNetwork?

O SKAdNetwork é o framework de atribuição da Apple que preserva a privacidade e permite a medição das taxas de conversão de campanhas de anúncios de instalação de apps no iOS sem exigir identificadores em nível de usuário.

O framework processa a atribuição por meio dos servidores da App Store, desvinculando as informações de atribuição dos identificadores de usuário e dos dados temporais antes de enviá-las às redes de anúncios.

Documentação da Apple: SKAdNetwork Framework Reference


Como o SKAdNetwork Funciona

O processo de atribuição ocorre inteiramente por meio da infraestrutura da Apple, garantindo a privacidade do usuário e ao mesmo tempo permitindo a medição do desempenho de campanhas.

Fluxo de Atribuição do SKAdNetwork

Fluxo de Atribuição:

  1. Clique no Anúncio: O usuário clica em um anúncio que contém a assinatura do SKAdNetwork com o ID de rede, publisher e campanha
  2. Abertura da App Store: O dispositivo armazena os dados de atribuição e abre a App Store para a instalação
  3. Inicialização do App: O usuário instala e inicia o app pela primeira vez
  4. Registro: O app se registra no framework do SKAdNetwork para sinalizar uma instalação bem-sucedida
  5. Conversion Value: O app opcionalmente atualiza o conversion value (0-63) representando a atividade pós-instalação
  6. Janela do Timer: O dispositivo aguarda mais de 24 horas após a primeira inicialização ou a última atualização do conversion value
  7. Postback: A App Store envia o postback de atribuição à rede de anúncios com o ID da campanha, o conversion value e a assinatura criptográfica

Privacidade por Design:

  • O postback não contém identificadores de dispositivo ou de usuário
  • O atraso mínimo de 24 horas impede a correlação temporal
  • O app nunca sabe em qual anúncio o usuário clicou
  • A rede nunca sabe qual usuário específico fez a instalação

Capacidades e Limitações

O SKAdNetwork Oferece:

  • Atribuição Last-Click: Funciona sem o consentimento do usuário ou opt-in de ATT
  • Detalhamento de Campanha: Granularidade por origem, campanha e publisher
  • Conversion Values: 64 valores discretos (0-63) para medição pós-instalação
  • Prevenção de Fraude: Assinaturas criptográficas validam a autenticidade da atribuição

Limitações Atuais:

  • Sem Dados em Nível de Usuário: Não é possível rastrear jornadas individuais de usuários
  • Sem View-Through: Apenas atribuição baseada em clique
  • Conversion Values Limitados: Um único valor de 6 bits (0-63) por instalação
  • Granularidade Limitada: Máximo de 100 IDs de campanha, sem detalhamento por grupo de anúncios ou criativo
  • Sem Coortes Longas: Prazo limitado para a medição de conversão
  • Exposição a Fraude: O conversion value em si não é assinado, e os postbacks podem ser duplicados

Recursos da Singular

A Singular oferece recursos abrangentes para a implementação e otimização do SKAdNetwork.


Solução SKAdNetwork da Singular

A solução SKAdNetwork da Singular oferece gerenciamento de atribuição de ponta a ponta, desde a implementação no lado do cliente até o processamento de postbacks e a otimização de campanhas.

Componentes da Solução

Recursos da Plataforma

Suporte abrangente ao SKAdNetwork em todo o fluxo de trabalho de atribuição e analytics.

Componente Funcionalidade
Código do Lado do Cliente Exemplos de código iOS nativo para o registro no framework do SKAdNetwork e o gerenciamento de conversion values
Processamento de Postbacks Validação e agregação de postbacks de todas as redes de anúncios com relatórios unificados
Proteção Contra Fraude Validação de assinatura criptográfica, deduplicação de transaction ID e verificação segura de parâmetros
Gerenciamento de Conversão Configuração dinâmica do dashboard para codificar atividades pós-instalação em conversion values
Relatórios Tradução do ID da campanha e enriquecimento com parâmetros de marketing para uma análise granular
Postbacks de Parceiros Conversion values decodificados enviados como eventos e receita para a otimização de parceiros

Arquitetura de Integração S2S

Componentes de Implementação

A integração server-to-server do SKAdNetwork consiste em duas principais áreas de implementação.

1. Implementação no Lado do Cliente (Obrigatória):

  • Registro no framework do SKAdNetwork na inicialização do app
  • Gerenciamento inteligente do conversion value com base na atividade pós-instalação
  • Essencial para a otimização de campanhas usando a atribuição do SKAdNetwork

2. Atualização da Integração S2S (Opcional):

  • Enriquece os eventos e sessões S2S da Singular com metadados do SKAdNetwork
  • Permite a validação da implementação e a solução de problemas
  • Fornece timestamps de conversion value para depuração

Implementação no Lado do Cliente

Implemente o registro no framework do SKAdNetwork e o gerenciamento de conversion values usando os exemplos de código iOS nativo da Singular para uma medição de campanha ideal.

Responsabilidades de Implementação

Funcionalidade Principal

O código do lado do cliente lida com duas funções críticas para a medição do SKAdNetwork.

  1. Registro no SKAdNetwork: Registra o app no framework imediatamente após a inicialização para habilitar a atribuição
  2. Gerenciamento do Conversion Value:
    • Comunica-se de forma síncrona com o endpoint da Singular para receber o próximo conversion value
    • Reporta sessões, eventos e receita à Singular
    • Recebe um conversion value codificado que representa a atividade pós-instalação configurada
    • Coleta metadados do SKAdNetwork (timestamp da primeira chamada, timestamp da última chamada, valor atual)

Interface do SKAdNetwork

Definições de Métodos

A interface inclui métodos para registro no framework, rastreamento de sessão, rastreamento de eventos e medição de receita.

SKANSnippet.h
//  SKANSnippet.h

#import <Foundation/Foundation.h>

@interface SKANSnippet : NSObject

// Register for SKAdNetwork attribution.
// Call this method as soon as possible once app is launched
+ (void)registerAppForAdNetworkAttribution;

// Track retention and cohorts by calling after each session.
// Reports session details and updates conversion value if needed.
// Conversion value updates only if new value greater than previous.
// Optional callback runs once conversion value updated.
+ (void)updateConversionValueAsync:(void(^)(int, NSError*))handler;

// Track conversion events by calling after each event.
// Reports event details and updates conversion value if needed.
// Optional callback runs once conversion value updated.
+ (void)updateConversionValueAsync:(NSString*)eventName
               withCompletionHandler:(void(^)(int, NSError*))handler;

// Track revenue by calling before each revenue event.
// Updates total revenue for next conversion value calculation.
// Note:
// 1. Call before 'updateConversionValueAsync' to ensure revenue included
// 2. Avoid calling on event retries to prevent double-counting
+ (void)updateRevenue:(double)amount andCurrency:(NSString*)currency;

// Gets current conversion value (nil if none set)
+ (NSNumber *)getConversionValue;

@end

Método de Registro

registerAppForAdNetworkAttribution registra o app no framework do SKAdNetwork, iniciando o timer de atribuição de 24 horas.

Comportamento do Timer:

  • O dispositivo envia a notificação de instalação de 0 a 24 horas após a expiração do timer
  • Atualizar o conversion value com um valor mais alto reinicia o timer para um novo intervalo de 24 horas
  • Referência da Apple: updateConversionValue Documentation

Métodos de Conversion Value

Os métodos calculam e atualizam os conversion values com base na atividade pós-instalação e no modelo de conversão configurado.

Atividades Suportadas:

  • Sessões: Críticas para a medição de retenção
  • Eventos de Conversão: Críticos para a medição de eventos pós-instalação
  • Eventos de Receita: Críticos para a medição de receita

Implementação da Interface

Código de Implementação Completo

A implementação completa lida com o registro, o gerenciamento de conversão e a comunicação com a API da Singular.

SKANSnippet.m
//  SKANSnippet.m

#import "SKANSnippet.h"
#import <StoreKit/SKAdNetwork.h>

#define SESSION_EVENT_NAME @"__SESSION__"
#define SINGULAR_API_URL @"https://sdk-api-v1.singular.net/api/v1/conversion_value"

// Keys for UserDefaults storage
#define CONVERSION_VALUE_KEY @"skan_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_KEY @"skan_total_revenue_by_currency"
#define SECONDS_PER_DAY 86400

static NSLock *lockObject;

@implementation SKANSnippet

+ (void)registerAppForAdNetworkAttribution {
    if ([SKANSnippet getFirstSkanCallTimestamp] != 0) {
        return;
    }

    if (@available(iOS 11.3, *)) {
        [SKAdNetwork registerAppForAdNetworkAttribution];
        [SKANSnippet setFirstSkanCallTimestamp];
        [SKANSnippet setLastSkanCallTimestamp];
    }
}

+ (void)updateConversionValueAsync:(void(^)(int, NSError*))handler {
    [SKANSnippet updateConversionValueAsync:SESSION_EVENT_NAME withCompletionHandler:handler];
}

+ (void)updateConversionValueAsync:(NSString*)eventName withCompletionHandler:(void(^)(int, NSError*))handler {
    if (@available(iOS 14, *)) {
        if ([SKANSnippet isSkanUpdateWindowOver]) {
            return;
        }

        [SKANSnippet getConversionValueFromServer:eventName withCompletionHandler:handler];
    }
}

+ (void)updateRevenue:(double)amount andCurrency:(NSString*)currency {
    if (@available(iOS 14, *)) {

        // Update total revenues
        if (amount != 0 && currency) {
            NSMutableDictionary* revenues = [[SKANSnippet getTotalRevenue] mutableCopy];
            NSNumber* currentRevenue = @(0);

            if ([revenues objectForKey:currency]) {
                currentRevenue = [revenues objectForKey:currency];
            }

            currentRevenue = @([currentRevenue floatValue] + amount);
            [revenues setObject:currentRevenue forKey:currency];

            [SKANSnippet setTotalRevenue:revenues];
        }
    }
}

+ (NSNumber *)getConversionValue {
    NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
    if (![userDefaults objectForKey:CONVERSION_VALUE_KEY]) {
        return nil;
    }
    return @([userDefaults integerForKey:CONVERSION_VALUE_KEY]);
}

+ (void)getConversionValueFromServer:(NSString*)eventName withCompletionHandler:(void(^)(int, NSError*))handler {
    if (!lockObject) {
        lockObject = [NSLock new];
    }

    // 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];

        NSURLComponents *components = [NSURLComponents componentsWithString:SINGULAR_API_URL];

        NSString* bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];

        components.queryItems = @[
            [NSURLQueryItem queryItemWithName:@"a" value:@"YOUR_SDK_KEY"],
            [NSURLQueryItem queryItemWithName:@"i" value:bundleIdentifier],
            [NSURLQueryItem queryItemWithName:@"app_v" value:@"YOUR_APP_VERSION"],
            [NSURLQueryItem queryItemWithName:@"n" value:eventName],
            [NSURLQueryItem queryItemWithName:@"p" value:@"iOS"],
            [NSURLQueryItem queryItemWithName:@"idfv" value:@"YOUR_IDFV"],
            [NSURLQueryItem queryItemWithName:@"idfa" value:@"YOUR_IDFA"],
            [NSURLQueryItem queryItemWithName:@"conversion_value"  value:[[SKANSnippet getConversionValue] stringValue]],
            [NSURLQueryItem queryItemWithName:@"total_revenue_by_currency"  value:[SKANSnippet dictionaryToJsonString:[SKANSnippet getTotalRevenue]]],
            [NSURLQueryItem queryItemWithName:@"first_call_to_skadnetwork_timestamp"  value:[NSString stringWithFormat:@"%ld", [SKANSnippet getFirstSkanCallTimestamp]]],
            [NSURLQueryItem queryItemWithName:@"last_call_to_skadnetwork_timestamp"  value:[NSString stringWithFormat:@"%ld", [SKANSnippet getLastSkanCallTimestamp]]],
        ];

        [[[NSURLSession sharedSession] dataTaskWithURL:components.URL
                                     completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            if (error) {
                [lockObject unlock];
                if (handler) {
                    handler(-1, error);
                }
                return;
            }

            NSDictionary* parsedResponse = [SKANSnippet jsonDataToDictionary:data];

            if (!parsedResponse) {
                [lockObject unlock];
                if (handler) {
                    handler(-1, [NSError errorWithDomain:bundleIdentifier
                                                    code:0
                                                userInfo:@{NSLocalizedDescriptionKey:@"Failed parsing server response"}]);
                }
                return;
            }

            NSNumber *conversionValue = [parsedResponse objectForKey:@"conversion_value"];

            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(-1, [NSError errorWithDomain:bundleIdentifier
                                                        code:0
                                                    userInfo:@{NSLocalizedDescriptionKey:reason}]);
                    }
                }
                return;
            }

            NSNumber* currentValue = [SKANSnippet getConversionValue];
            if ([conversionValue intValue] <= [currentValue intValue]) {
                [lockObject unlock];
                return;
            }

            [SKANSnippet setConversionValue:[conversionValue intValue]];
            [SKANSnippet setLastSkanCallTimestamp];

            if (![SKANSnippet getFirstSkanCallTimestamp]) {
                [SKANSnippet setFirstSkanCallTimestamp];
            }

            [lockObject unlock];

            if (handler) {
                handler([conversionValue intValue], error);
            }
        }] resume];
    });
}

+ (BOOL)isSkanUpdateWindowOver {
    NSInteger timeDiff = [SKANSnippet getCurrentUnixTimestamp] - [SKANSnippet getLastSkanCallTimestamp];
    return SECONDS_PER_DAY <= timeDiff;
}

@end

Métodos Utilitários

Gerenciamento de Metadados

Os métodos utilitários gerenciam o armazenamento de metadados do SKAdNetwork no UserDefaults, essencial para o cálculo do conversion value.

Implementação Crítica: Estes utilitários mantêm os metadados necessários para o cálculo preciso do conversion value. Não os modifique a menos que seja necessário.

Métodos Utilitários
+ (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];
}

+ (NSDictionary*)getTotalRevenue {
    NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
    if (![userDefaults objectForKey:TOTAL_REVENUE_BY_CURRENCY_KEY]) {
        return [NSDictionary new];
    }
    return [userDefaults objectForKey:TOTAL_REVENUE_BY_CURRENCY_KEY];
}

+ (NSInteger)getCurrentUnixTimestamp {
    return [[NSDate date]timeIntervalSince1970];
}

+ (void)setConversionValue:(int)value {
    if (@available(iOS 14.0, *)) {
        if (value <= [[SKANSnippet getConversionValue] intValue]) {
            return;
        }

        [SKAdNetwork updateConversionValue:value];

        NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
        [userDefaults setInteger:value forKey:CONVERSION_VALUE_KEY];
        [userDefaults synchronize];
    }
}

+ (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]; } + (void)setTotalRevenue:(NSDictionary *)values { NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults]; [userDefaults setObject:values forKey:TOTAL_REVENUE_BY_CURRENCY_KEY]; [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]; } + (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; } @end

Atualização da Integração S2S

Opcionalmente, aprimore a integração server-to-server com metadados do SKAdNetwork para validação da implementação e solução de problemas.

Atualizações Obrigatórias

Resposta de Conversion Value

Após o modelo de conversão ser ativado, os endpoints S2S retornam o campo inteiro conversion_value que contém o próximo valor para a atualização no lado do cliente.

Tratamento da Resposta: Faça o parse do conversion_value das respostas do endpoint S2S e aplique-o ao framework do SKAdNetwork por meio do código do lado do cliente.


Aprimoramentos Opcionais

Parâmetros de Metadados

Enriqueça as requisições de sessão e de evento S2S com metadados do SKAdNetwork para validar a integração e solucionar problemas de implementação.

Parâmetro Descrição
skan_conversion_value Conversion value mais recente no momento da requisição
skan_first_call_timestamp Timestamp Unix da primeira chamada à API do SKAdNetwork
skan_last_call_timestamp Timestamp Unix da chamada mais recente à API do SKAdNetwork

Código de Extração de Metadados

Extraia os valores de metadados do SKAdNetwork usando os métodos utilitários para a transmissão S2S.

Objective-C
NSDictionary *skanMetadata = @{
    @"skan_conversion_value":
        [[SKANSnippet getConversionValue] stringValue],
    @"skan_first_call_timestamp":
        [NSString stringWithFormat:@"%ld", [SKANSnippet getFirstSkanCallTimestamp]],
    @"skan_last_call_timestamp":
        [NSString stringWithFormat:@"%ld", [SKANSnippet getLastSkanCallTimestamp]]
};

// Forward skanMetadata to your server for S2S API enrichment

Encaminhe os parâmetros para o lado do servidor e anexe-os às requisições da API S2S da Singular. Documentação completa dos parâmetros: S2S API Reference


Fluxo de Integração

Fluxo completo do SKAdNetwork para clientes S2S, desde o gerenciamento de conversion value no lado do cliente até o processamento de postbacks.

Fluxo de Integração S2S do SKAdNetwork

Etapas do Fluxo

Processo de Ponta a Ponta

  1. Requisição de Conversion Value: O código do app se comunica de forma síncrona com o endpoint da Singular para obter o conversion value mais recente com base em sessões, eventos e receita
  2. Atualização do Framework: O app atualiza o framework do SKAdNetwork com o conversion value recebido
  3. Enriquecimento de Metadados: O app enriquece os eventos e sessões S2S com metadados do SKAdNetwork para validação
  4. Expiração do Timer: Após mais de 24 horas da última atualização, o SKAdNetwork envia o postback à rede de anúncios
  5. Encaminhamento do Postback: A rede encaminha o postback à Singular (setup seguro ou regular)
  6. Processamento do Postback: A Singular processa o postback ao:
    • Validar a assinatura criptográfica
    • Decodificar o conversion value usando o modelo configurado
    • Enriquecer com parâmetros de marketing com base nas integrações de parceiros
    • Enviar os dados decodificados para BI e parceiros por meio de postbacks

Separação de Dados: Os dados do SKAdNetwork (instalações e eventos decodificados) ficam acessíveis por meio de relatórios, APIs, tabelas ETL e postbacks separados, para evitar a mistura com os conjuntos de dados existentes durante os testes e a validação.


Implementação no Ciclo de Vida do App

Integre os métodos do SKAdNetwork nos pontos apropriados do ciclo de vida do app para uma cobertura de atribuição completa.

Exemplo de Implementação

Posicionamento dos Métodos

Objective-C
// On app launch (in applicationDidFinishLaunching)
[SKANSnippet registerAppForAdNetworkAttribution];

// After each session handled
[SKANSnippet updateConversionValueAsync:^(int value, NSError *error) {
    if (error) {
        NSLog(@"Conversion value update failed: %@", error);
    } else {
        NSLog(@"Conversion value updated to: %d", value);
    }
}];

// After handling non-revenue events
[SKANSnippet updateConversionValueAsync:@"event_name"
                   withCompletionHandler:^(int value, NSError *error) {
    if (error) {
        NSLog(@"Event conversion value update failed: %@", error);
    } else {
        NSLog(@"Event conversion value updated to: %d", value);
    }
}];

// After handling revenue events
[SKANSnippet updateRevenue:15.53 andCurrency:@"USD"];
[SKANSnippet updateConversionValueAsync:@"revenue_event_name"
                   withCompletionHandler:^(int value, NSError *error) {
    if (error) {
        NSLog(@"Revenue conversion value update failed: %@", error);
    } else {
        NSLog(@"Revenue conversion value updated to: %d", value);
    }
}];

Teste da Implementação

Valide a implementação do SKAdNetwork usando o endpoint de teste da Singular antes da implantação em produção.

Ambiente de Teste

Configuração do Endpoint de Teste

Substitua o endpoint de gerenciamento de conversão de produção pelo endpoint de teste para simular o fluxo do app.

URL do Endpoint de Teste:

https://skadnetwork-testing.singular.net/api/v1/conversion_value
  • Nenhuma API key necessária para o endpoint de teste
  • Retorna os conversion values de 0 a 2 sequencialmente
  • Retorna uma string vazia após a terceira chamada

Procedimento de Teste

Etapas de Validação

  1. Atualizar o Endpoint: Substitua a constante SINGULAR_API_URL pela URL do endpoint de teste
  2. Inicializar o Conversion Value: Garanta que o conversion value padrão seja inicializado como 0
  3. Gerar Eventos: Dispare 3 eventos diferentes dentro do app
  4. Verificar as Atualizações: Confirme que updateConversionValueAsync foi chamado após cada evento
  5. Registrar os Valores: Registre nos logs os conversion values retornados para verificar a progressão correta (0 → 1 → 2)
  6. Confirmar a Conclusão: Após a terceira chamada, verifique a resposta vazia e a retenção do valor final

Comportamento Esperado:

  • Primeiro evento: Recebe o conversion value 0
  • Segundo evento: Recebe o conversion value 1
  • Terceiro evento: Recebe o conversion value 2
  • Quarto evento em diante: Resposta vazia, o valor 2 persiste

Changelog do Código

Acompanhe as atualizações dos exemplos de código e as correções críticas aplicadas ao longo do tempo.

Histórico de Versões

Data Alterações
1 de outubro de 2020
  • Correção [CRÍTICA]: Mecanismo de lock na getConversionValueFromServer inicialização corrigida
  • Melhoria: O motivo da falha na resposta do servidor agora é retornado no tratamento de erro
23 de setembro de 2020
  • Correção [CRÍTICA]: setConversionValue agora chama corretamente updateConversionValue
  • Melhoria: Tratamento de erro aprimorado ao recuperar conversion values do servidor
15 de setembro de 2020
  • Alterado [IMPORTANTE]: getConversionValue valor padrão alterado de null para 0 quando não definido
  • Melhoria: Aprimoramentos no reporte e tratamento de receita
  • Melhoria: Implementação de recuperação assíncrona do conversion value