Server-to-Server - Guia de implementação da SKAdNetwork 3

Documento

SKAdNetwork 4 disponível: Para obter a versão mais recente da SKAdNetwork, consulte o Guia de implementação da SKAdNetwork 4.

Guia de implementação de SKAdNetwork 3.0 S2S

Implemente a estrutura SKAdNetwork 3.0 da Apple para atribuição iOS compatível com a privacidade, utilizando a integração servidor a servidor para medir o desempenho da campanha de instalação de aplicações sem comprometer os identificadores de utilizadores.


Visão geral

O que é a SKAdNetwork?

SKAdNetwork é a estrutura de atribuição da Apple que preserva a privacidade e permite medir as taxas de conversão de campanhas publicitárias de instalação de aplicativos no iOS sem exigir identificadores no nível do usuário.

A estrutura processa a atribuição através dos servidores da App Store, desligando as informações de atribuição dos identificadores do utilizador e dos dados temporais antes de as enviar para as redes de anúncios.

Documentação da Apple: Referência da estrutura SKAdNetwork


Como funciona a SKAdNetwork

O processo de atribuição ocorre inteiramente através da infraestrutura da Apple, garantindo a privacidade do utilizador e permitindo a medição do desempenho da campanha.

SKAdNetwork Attribution Flow

Fluxo de atribuição:

  1. Clique no anúncio: O utilizador clica no anúncio que contém a assinatura SKAdNetwork com o ID da rede, do editor e da campanha
  2. Abertura da App Store: O dispositivo armazena os dados de atribuição e abre a App Store para instalação
  3. Lançamento do aplicativo: O utilizador instala e inicia a aplicação pela primeira vez
  4. Registo: A aplicação regista-se na estrutura SKAdNetwork para sinalizar uma instalação bem sucedida
  5. Valor de conversão: Opcionalmente, a aplicação actualiza o valor de conversão (0-63) que representa a atividade pós-instalação
  6. Janela do temporizador: O dispositivo aguarda mais de 24 horas após o primeiro lançamento ou a última atualização do valor de conversão
  7. Postback: A App Store envia um postback de atribuição para a rede de anúncios com o ID da campanha, o valor de conversão e a assinatura criptográfica

Privacidade desde a conceção:

  • O postback não contém identificadores de dispositivos ou utilizadores
  • O atraso mínimo de 24 horas impede a correlação temporal
  • A aplicação nunca sabe em que anúncio o utilizador clicou
  • A rede nunca sabe que utilizador específico instalou

Recursos e limitações

SKAdNetwork oferece:

  • Atribuição de último clique: Funciona sem o consentimento do utilizador ou o opt-in da ATT
  • Detalhamento da campanha: Granularidade de origem, campanha e editor
  • Valores de conversão: 64 valores discretos (0-63) para medição pós-instalação
  • Prevenção de fraudes: Assinaturas criptográficas validam a autenticidade da atribuição

Limitações actuais:

  • Sem dados ao nível do utilizador: Não é possível acompanhar os percursos individuais dos utilizadores
  • Sem visualização: Apenas atribuição baseada em cliques
  • Valores de conversão limitados: Valor único de 6 bits (0-63) por instalação
  • Granularidade limitada: Máximo de 100 IDs de campanha, sem discriminação de grupo de anúncios ou criativo
  • Sem coortes longas: Período de tempo limitado para medição de conversão
  • Exposição a fraudes: O valor da conversão em si não é assinado, os postbacks podem ser duplicados

Recursos da Singular

A Singular fornece recursos abrangentes para a implementação e otimização da SKAdNetwork.


Solução Singular SKAdNetwork

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

Componentes da solução

Caraterísticas da plataforma

Suporte abrangente de SKAdNetwork em todo o fluxo de trabalho de atribuição e análise.

Componente Funcionalidade
Código do lado do cliente Amostras de código iOS nativo para registro de estrutura SKAdNetwork e gerenciamento de valor de conversão
Processamento de postbacks Validação e agregação de postbacks de todas as redes de anúncios com relatórios unificados
Proteção contra fraudes Validação de assinatura criptográfica, deduplicação de ID de transação e verificação segura de parâmetros
Gestão de conversões Configuração dinâmica do painel de controlo para codificar actividades pós-instalação em valores de conversão
Relatórios Tradução de ID de campanha e enriquecimento com parâmetros de marketing para análise granular
Postbacks de parceiros Valores de conversão descodificados enviados como eventos e receitas para otimização do parceiro

Arquitetura de integração S2S

Componentes de implementação

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

1. Implementação no lado do cliente (obrigatório):

  • Registo da estrutura SKAdNetwork no lançamento da aplicação
  • Gestão inteligente do valor de conversão com base na atividade pós-instalação
  • Essencial para a otimização de campanhas utilizando a atribuição de SKAdNetwork

2. Atualização da integração S2S (opcional):

  • Enriquece os eventos e sessões do Singular S2S com metadados da SKAdNetwork
  • Permite a validação da implementação e a resolução de problemas
  • Fornece carimbos de data e hora do valor de conversão para depuração

Implementação no lado do cliente

Implemente o registro da estrutura SKAdNetwork e o gerenciamento do valor de conversão usando os exemplos de código iOS nativos do Singular para otimizar a medição da campanha.

Responsabilidades de implementação

Funcionalidade principal

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

  1. Registro de SKAdNetwork: Registra o aplicativo com a estrutura imediatamente após o lançamento para permitir a atribuição
  2. Gerenciamento do valor de conversão:
    • Comunica-se de forma síncrona com o endpoint Singular para receber o próximo valor de conversão
    • Relata sessões, eventos e receita para a Singular
    • Recebe o valor de conversão codificado que representa a atividade pós-instalação configurada
    • Recolhe metadados SKAdNetwork (timestamp da primeira chamada, timestamp da última chamada, valor atual)

Interface SKAdNetwork

Definições de métodos

A interface inclui métodos para registo de framework, acompanhamento de sessões, acompanhamento de eventos e medição de receitas.

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 registo

registerAppForAdNetworkAttribution regista a aplicação com a estrutura SKAdNetwork, iniciando o temporizador de atribuição de 24 horas.

Comportamento do temporizador:

  • O dispositivo envia uma notificação de instalação 0-24 horas após a expiração do temporizador
  • A atualização do valor de conversão com um valor mais elevado reinicia o temporizador para um novo intervalo de 24 horas
  • Referência da Apple: updateConversionValue Documentação

Métodos de valor de conversão

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

Actividades suportadas:

  • Sessões: Crítico para a medição de retenção
  • Eventos de conversão: Crítico para a medição de eventos pós-instalação
  • Eventos de receita: Crítico para a medição de receitas

Implementação de interface

Código de implementação completo

A implementação completa lida com o registo, gestão de conversões e comunicação com a API 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

Gestão de metadados

Os métodos utilitários gerem o armazenamento de metadados da SKAdNetwork em UserDefaults, essencial para o cálculo do valor de conversão.

Implementação crítica: Estes utilitários mantêm os metadados necessários para o cálculo exato do valor de conversão. Não modificar, exceto se necessário.

Utility Methods
+ (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, melhora a integração servidor a servidor com metadados SKAdNetwork para validação da implementação e resolução de problemas.

Actualizações obrigatórias

Resposta do valor de conversão

Uma vez ativado o modelo de conversão, os endpoints S2S devolvem conversion_value campo inteiro contendo o próximo valor para atualização do lado do cliente.

Tratamento da resposta: Analisar conversion_valuea partir das respostas do ponto final S2S e aplicar à estrutura SKAdNetwork através de código do lado do cliente.


Melhorias opcionais

Parâmetros de metadados

Enriquecer os pedidos de sessões e eventos S2S com metadados SKAdNetwork para validar a integração e resolver problemas de implementação.

Parâmetro Descrição
skan_conversion_value Valor de conversão mais recente no momento do pedido
skan_first_call_timestamp Carimbo de data/hora Unix da primeira chamada à API SKAdNetwork
skan_last_call_timestamp Registo de data e hora Unix da última chamada à API SKAdNetwork

Código de extração de metadados

Extrai valores de metadados SKAdNetwork utilizando métodos utilitários para 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

Reencaminhar parâmetros para o lado do servidor e anexar aos pedidos da API S2S Singular. Documentação completa dos parâmetros: Referência da API S2S


Fluxo de integração

Fluxo completo de SKAdNetwork para clientes S2S, desde a gestão da conversão no lado do cliente até ao processamento de postback.

SKAdNetwork S2S Integration Flow

Fases do fluxo

Processo de ponta a ponta

  1. Solicitação de valor de conversão: O código do aplicativo se comunica com o endpoint Singular de forma síncrona para obter o valor de conversão mais recente com base em sessões, eventos e receita
  2. Atualização do framework: o aplicativo atualiza o framework SKAdNetwork com o valor de conversão recebido
  3. Enriquecimento de metadados: A aplicação enriquece os eventos e sessões S2S com metadados SKAdNetwork para validação
  4. Expiração do temporizador: Após mais de 24 horas da última atualização, SKAdNetwork envia postback para a rede de anúncios
  5. Encaminhamento de postback: A rede encaminha o postback para a Singular (configuração segura ou regular)
  6. Processamento do postback: A Singular processa o postback por
    • Validação da assinatura criptográfica
    • Decodificação do valor de conversão usando o modelo configurado
    • Enriquecimento com parâmetros de marketing baseados em integrações de parceiros
    • Envio de dados descodificados para o BI e parceiros através de postbacks

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


Implementação do ciclo de vida da aplicação

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

Exemplo de implementação

Colocação de 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 de implementação

Valide a implementação de SKAdNetwork usando o endpoint de teste da Singular antes da implementação na 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 aplicativo.

URL do ponto de extremidade de teste:

https://skadnetwork-testing.singular.net/api/v1/conversion_value
  • Nenhuma chave de API necessária para o ponto de extremidade de teste
  • Retorna valores de conversão 0-2 sequencialmente
  • Devolve uma cadeia de caracteres vazia após a terceira chamada

Procedimento de teste

Etapas de validação

  1. Atualizar o ponto final: Substituir a constante SINGULAR_API_URLpelo URL do ponto de extremidade de teste
  2. Inicializar valor de conversão: Garantir que o valor de conversão padrão seja inicializado em 0
  3. Gerar eventos: Acionar 3 eventos diferentes no aplicativo
  4. Verificar atualizações: Confirmar updateConversionValueAsync chamado após cada evento
  5. Registar valores: Registar os valores de conversão devolvidos para verificar a progressão correta (0 → 1 → 2)
  6. Confirmar a conclusão: Após a terceira chamada, verificar a resposta vazia e a retenção do valor final

Comportamento esperado:

  • Primeiro evento: Recebe o valor de conversão 0
  • Segundo evento: Recebe o valor de conversão 1
  • Terceiro evento: Recebe o valor de conversão 2
  • Quarto + eventos: Resposta vazia, o valor 2 persiste

Registro de alterações de código

Acompanhe as atualizações de exemplos de código e 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ÍTICO]: Mecanismo de bloqueio na inicialização de getConversionValueFromServer corrigido
  • Melhorado: O motivo da falha na resposta do servidor agora é retornado no tratamento de erros
23 de setembro de 2020
  • Correção [CRÍTICO]: setConversionValue agora chama corretamente updateConversionValue
  • Melhorado: Tratamento de erros aprimorado ao recuperar valores de conversão do servidor
15 de setembro de 2020
  • Alterado [IMPORTANTE]:getConversionValuevalor predefinido alterado de nulo para 0 quando não definido
  • Melhorado: Melhorias no relatório e no tratamento de receitas
  • Melhorado: Implementação da recuperação do valor de conversão assíncrona