Callbacks de verificação do lado do servidor são solicitações de URL, com parâmetros de consulta expandidos e enviados a um sistema externo para notificar que um usuário precisa ser recompensado por interagir com um anúncio intersticial premiado ou premiado. Os callbacks de SSV (verificação do lado do servidor) premiados oferecem uma camada extra de proteção contra spoofing de callbacks do lado do cliente para recompensar os usuários.
Neste guia, mostramos como verificar os callbacks de SSV premiados usando a biblioteca criptográfica de terceiros Tink Java Apps para garantir que os parâmetros de consulta no callback sejam valores legítimos. Embora o Tink seja usado para os propósitos deste guia, você tem a opção de usar qualquer biblioteca de terceiros com suporte à ECDSA. Você também pode testar seu servidor com a ferramenta de teste na interface da AdMob.
Confira este exemplo totalmente funcional usando o Java Spring-boot.
Pré-requisitos
Integre anúncios premiados ao seu app para dispositivos móveis com av11.6.0 ou mais recente do SDK dos anúncios para dispositivos móveis do Google.
Ative a verificação premiada do lado do servidor no seu bloco de anúncios.
Usar o PremAdsVerifier da biblioteca de apps do Java do Tink
O repositório do GitHub Apps Java do Tink
inclui uma classe auxiliar
RewardedAdsVerifier
para reduzir o código necessário para verificar um callback de SSV premiado.
O uso dessa classe permite verificar um URL de callback com o código a seguir.
RewardedAdsVerifier verifier = new RewardedAdsVerifier.Builder()
.fetchVerifyingPublicKeysWith(
RewardedAdsVerifier.KEYS_DOWNLOADER_INSTANCE_PROD)
.build();
String rewardUrl = ...;
verifier.verify(rewardUrl);
Se o método verify()
for executado sem gerar uma exceção, o URL de callback foi verificado. A seção Recompensar o usuário detalha as práticas recomendadas sobre quando os usuários devem ser recompensados. Para um detalhamento das etapas realizadas por essa classe para verificar callbacks de SSV premiados, leia a seção Verificação manual de SSV premiada.
Parâmetros de callback de SSV
Os callbacks de verificação do lado do servidor contêm parâmetros de consulta que descrevem a interação com o anúncio premiado. Nomes, descrições e exemplos de valores dos parâmetros estão listados abaixo. Eles são enviados em ordem alfabética.
Nome do parâmetro | Descrição | Valor de exemplo |
---|---|---|
ad_network | É o identificador da origem de anúncios que preencheu o anúncio. Os nomes das origens de anúncios correspondentes aos valores de ID são listados na seção Identificadores de origens de anúncios. | 1953547073528090325 |
ad_unit | ID do bloco de anúncios da AdMob que foi usado para solicitar o anúncio premiado. | 2747237135 |
custom_data | String de dados personalizada fornecida por
setCustomData
.
Se o app não fornecer uma string de dados personalizada, o valor desse parâmetro de consulta não estará presente no retorno de chamada de SSV. |
SAMPLE_CUSTOM_DATA_STRING |
key_id | Chave a ser usada para verificar o callback de SSV. Esse valor é mapeado para uma chave pública fornecida pelo servidor de chaves da AdMob. | 1234567890 |
reward_amount | Valor do prêmio conforme especificado nas configurações do bloco de anúncios. | 5 |
reward_item | Item do prêmio conforme especificado nas configurações do bloco de anúncios. | moedas |
assinatura | Assinatura para o callback de SSV gerado pela AdMob. | MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMtmgJHYYW9k2_icM9LFMY |
carimbo de data/hora | Carimbo de data/hora de quando o usuário foi recompensado como tempo de época em ms. | 1507770365237823 |
transaction_id | Identificador codificado em hexadecimal exclusivo para cada evento de concessão de prêmio gerado pela AdMob. | 18fa792de1bca816048293fc71035638 |
user_id | Identificador do usuário conforme fornecido por
setUserId .
Se nenhum identificador de usuário for fornecido pelo app, esse parâmetro de consulta não estará presente no retorno de chamada de SSV. |
1234567 |
Identificadores de origens de anúncios
Nomes e IDs das origens de anúncios
Nome da origem do anúncio | ID da origem de anúncios |
---|---|
Aarki (lances) | 5240798063227064260 |
Ad Generation (lances) | 1477265452970951479 |
AdColony | 15586990674969969776 |
AdColony (não SDK) (lances) | 4600416542059544716 |
AdColony (lances) | 6895345910719072481 |
AdFalcon | 3528208921554210682 |
Rede da AdMob | 5450213213286189855 |
ADResult | 10593873382626181482 |
AMoAd | 17253994435944008978 |
AppLovin | 1063618907739174004 |
AppLovin (lances) | 1328079684332308356 |
Chartboost | 2873236629771172317 |
Plataforma de chocolate (lances) | 6432849193975106527 |
Cross-channel (MdotM) | 9372067028804390441 |
Evento personalizado | 18351550913290782395 |
DT Exchange* * Antes de 21 de setembro de 2022, essa rede era chamada de "Fyber Marketplace". | 2179455223494392917 |
EMX (lances) | 8497809869790333482 |
Flutuação (lances) | 8419777862490735710 |
Flurry | 3376427960656545613 |
Fyber* * Essa origem de anúncios é usada para relatórios históricos. | 4839637394546996422 |
i-mobile | 5208827440166355534 |
Melhore o conteúdo digital (lances) | 159382223051638006 |
Index Exchange (lances) | 4100650709078789802 |
InMobi | 7681903010231960328 |
InMobi (lances) | 6325663098072678541 |
IronSource | 6925240245545091930 |
Anúncios da ironSource (lances) | 1643326773739866623 |
Leadbolt | 2899150749497968595 |
LG U+AD | 18298738678491729107 |
Rede de anúncios LINE | 3025503711505004547 |
maio | 7505118203095108657 |
maio (lances) | 1343336733822567166 |
Media.net (lances) | 2127936450554446159 |
Anúncios internos mediados | 6060308706800320801 |
Meta Audience Network* * Até 6 de junho de 2022, essa rede era chamada de "Facebook Audience Network". | 10568273599589928883 |
Meta Audience Network (lances)* * Antes de 6 de junho de 2022, essa rede era chamada de "Facebook Audience Network (lances)". | 11198165126854996598 |
Mintegral | 1357746574408896200 |
Mintegral (lances) | 6250601289653372374 |
MobFox | 8079529624516381459 |
MobFox (lances) | 3086513548163922365 |
MoPub (descontinuado) | 10872986198578383917 |
myTarget | 8450873672465271579 |
Nend | 9383070032774777750 |
Nexxen (lances)* * Antes de 1o de maio de 2024, essa rede era chamada de "UnrulyX". | 2831998725945605450 |
ONE by AOL (Millennial Media) | 6101072188699264581 |
ONE da AOL (Nexage) | 3224789793037044399 |
Troca da OneTag (lances) | 4873891452523427499 |
OpenX (lances) | 4918705482605678398 |
Pangle (lances) | 3525379893916449117 |
PubMatic (lances) | 3841544486172445473 |
Campanha de reserva | 7068401028668408324 |
RhythmOne (lances) | 2831998725945605450 |
Rubicon (lances) | 3993193775968767067 |
Planeta SK | 734341340207269415 |
Compartilhamento (lances) | 5247944089976324188 |
Smaato (lances) | 3362360112145450544 |
Equativ (lances)* * Até 12 de janeiro de 2023, essa rede era chamada de "Smart AdServer". | 5970199210771591442 |
Sonobi (lances) | 3270984106996027150 |
Tapjoy | 7295217276740746030 |
Tapjoy (lances) | 4692500501762622178 |
Tencent GDT | 7007906637038700218 |
TripleLift (lances) | 8332676245392738510 |
Unity Ads | 4970775877303683148 |
Anúncios do Unity (lances) | 7069338991535737586 |
Vivo Media | 7360851262951344112 |
Verve Group (lances) | 5013176581647059185 |
Vpon | 1940957084538325905 |
Liftoff Monetize* * Antes de 30 de janeiro de 2023, essa rede era chamada de "Vungle". | 1953547073528090325 |
Liftoff Monetize (lances)* * Até 30 de janeiro de 2023, essa rede era chamada de "Vungle (lances)". | 4692500501762622185 |
Yieldmo (lances) | 4193081836471107579 |
YieldOne (lances) | 3154533971590234104 |
Zucks | 5506531810221735863 |
Recompensa pelo usuário
É importante equilibrar a experiência do usuário e a validação de recompensas ao decidir quando recompensar um usuário. Os callbacks do lado do servidor podem sofrer atrasos antes de chegarem aos sistemas externos. Portanto, a prática recomendada é usar o callback do lado do cliente para recompensar o usuário imediatamente, enquanto realiza a validação de todos os prêmios ao receber callbacks do lado do servidor. Essa abordagem oferece uma boa experiência do usuário, garantindo a validade das recompensas concedidas.
No entanto, para aplicativos em que a validade da recompensa é essencial (por exemplo, a recompensa afeta a economia do app no jogo) e atrasos na concessão são aceitáveis, aguardar o callback verificado do lado do servidor pode ser a melhor abordagem.
Dados personalizados
Os apps que exigem dados extras em callbacks de verificação do lado do servidor precisam usar
o recurso de dados personalizados dos anúncios premiados. Qualquer valor de string definido em um objeto de anúncio
premiado é transmitido ao parâmetro de consulta custom_data
do callback de SSV. Se nenhum
valor de dados personalizados for definido, o valor do parâmetro de consulta custom_data
não estará
presente no callback de SSV.
O exemplo de código a seguir demonstra como definir as opções de SSV após o carregamento do anúncio premiado.
Java
RewardedAd.load(MainActivity.this, "ca-app-pub-3940256099942544/5354046379", new AdRequest.Builder().build(), new RewardedAdLoadCallback() { @Override public void onAdLoaded(RewardedAd ad) { Log.d(TAG, "Ad was loaded."); rewardedAd = ad; ServerSideVerificationOptions options = new ServerSideVerificationOptions .Builder() .setCustomData("SAMPLE_CUSTOM_DATA_STRING") .build(); rewardedAd.setServerSideVerificationOptions(options); } @Override public void onAdFailedToLoad(LoadAdError loadAdError) { Log.d(TAG, loadAdError.toString()); rewardedAd = null; } });
Kotlin
RewardedAd.load(this, "ca-app-pub-3940256099942544/5354046379", AdRequest.Builder().build(), object : RewardedAdLoadCallback() { override fun onAdLoaded(ad: RewardedAd) { Log.d(TAG, "Ad was loaded.") rewardedInterstitialAd = ad val options = ServerSideVerificationOptions.Builder() .setCustomData("SAMPLE_CUSTOM_DATA_STRING") .build() rewardedAd.setServerSideVerificationOptions(options) } override fun onAdFailedToLoad(adError: LoadAdError) { Log.d(TAG, adError?.toString()) rewardedAd = null } })
Se você quiser definir a string de prêmio personalizada, faça isso antes de exibir o anúncio.
Verificação manual da SSV premiada
As etapas realizadas pela classe RewardedAdsVerifier
para verificar uma SSV premiada estão descritas abaixo. Embora os snippets de código incluídos estejam em Java e
aproveitem a biblioteca de terceiros do Tink, essas etapas podem ser implementadas na
linguagem que você escolher, usando qualquer biblioteca de terceiros com suporte a
ECDSA.
Buscar chaves públicas
Para verificar um callback de SSV premiado, você precisa de uma chave pública fornecida pela AdMob.
É possível buscar uma lista de chaves públicas a serem usadas para validar os callbacks de SSV premiados no servidor de chaves da AdMob. A lista de chaves públicas é fornecida como uma representação JSON com um formato semelhante ao seguinte:
{
"keys": [
{
keyId: 1916455855,
pem: "-----BEGIN PUBLIC KEY-----\nMF...YTPcw==\n-----END PUBLIC KEY-----"
base64: "MFkwEwYHKoZIzj0CAQYI...ltS4nzc9yjmhgVQOlmSS6unqvN9t8sqajRTPcw=="
},
{
keyId: 3901585526,
pem: "-----BEGIN PUBLIC KEY-----\nMF...aDUsw==\n-----END PUBLIC KEY-----"
base64: "MFYwEAYHKoZIzj0CAQYF...4akdWbWDCUrMMGIV27/3/e7UuKSEonjGvaDUsw=="
},
],
}
Para recuperar as chaves públicas, conecte-se ao servidor de chaves da AdMob e faça o download das
chaves. O código a seguir realiza essa tarefa e salva a representação JSON das chaves na variável data
.
String url = ...;
NetHttpTransport httpTransport = new NetHttpTransport.Builder().build();
HttpRequest httpRequest =
httpTransport.createRequestFactory().buildGetRequest(new GenericUrl(url));
HttpResponse httpResponse = httpRequest.execute();
if (httpResponse.getStatusCode() != HttpStatusCodes.STATUS_CODE_OK) {
throw new IOException("Unexpected status code = " + httpResponse.getStatusCode());
}
String data;
InputStream contentStream = httpResponse.getContent();
try {
InputStreamReader reader = new InputStreamReader(contentStream, UTF_8);
data = readerToString(reader);
} finally {
contentStream.close();
}
As chaves públicas são alternadas regularmente. Você vai receber um e-mail informando sobre uma próxima rotação. Se você estiver armazenando chaves públicas em cache, atualize as chaves assim que receber este e-mail.
Depois que as chaves públicas são buscadas, elas precisam ser analisadas. O método parsePublicKeysJson
abaixo usa uma string JSON, como o exemplo acima, como entrada e cria um mapeamento de valores key_id
para chaves públicas, que são encapsuladas como objetos ECPublicKey
da biblioteca Tink.
private static Map<Integer, ECPublicKey> parsePublicKeysJson(String publicKeysJson)
throws GeneralSecurityException {
Map<Integer, ECPublicKey> publicKeys = new HashMap<>();
try {
JSONArray keys = new JSONObject(publicKeysJson).getJSONArray("keys");
for (int i = 0; i < keys.length(); i++) {
JSONObject key = keys.getJSONObject(i);
publicKeys.put(
key.getInt("keyId"),
EllipticCurves.getEcPublicKey(Base64.decode(key.getString("base64"))));
}
} catch (JSONException e) {
throw new GeneralSecurityException("failed to extract trusted signing public keys", e);
}
if (publicKeys.isEmpty()) {
throw new GeneralSecurityException("No trusted keys are available.");
}
return publicKeys;
}
Receber conteúdo para verificação
Os dois últimos parâmetros de consulta de callbacks de SSV premiados são sempre signature
e key_id,
, nessa ordem. Os demais parâmetros de consulta especificam o conteúdo a ser verificado. Vamos supor que você configurou a AdMob para enviar callbacks de prêmios a
https://www.myserver.com/mypath
. O snippet abaixo mostra um exemplo de callback de SSV premiado com o conteúdo a ser verificado destacado.
https://www.myserver.com/path?ad_network=54...55&ad_unit=12345678&reward_amount=10&reward_item=coins ×tamp=150777823&transaction_id=12...DEF&user_id=1234567&signature=ME...Z1c&key_id=1268887
O código abaixo demonstra como analisar o conteúdo a ser verificado de um URL de callback como uma matriz de bytes UTF-8.
public static final String SIGNATURE_PARAM_NAME = "signature=";
...
URI uri;
try {
uri = new URI(rewardUrl);
} catch (URISyntaxException ex) {
throw new GeneralSecurityException(ex);
}
String queryString = uri.getQuery();
int i = queryString.indexOf(SIGNATURE_PARAM_NAME);
if (i == -1) {
throw new GeneralSecurityException("needs a signature query parameter");
}
byte[] queryParamContentData =
queryString
.substring(0, i - 1)
// i - 1 instead of i because of & in the query string
.getBytes(Charset.forName("UTF-8"));
Receber assinatura e key_id do URL de callback
Usando o valor queryString
da etapa anterior, analise os parâmetros de consulta signature
e
key_id
do URL de callback, conforme mostrado abaixo:
public static final String KEY_ID_PARAM_NAME = "key_id=";
...
String sigAndKeyId = queryString.substring(i);
i = sigAndKeyId.indexOf(KEY_ID_PARAM_NAME);
if (i == -1) {
throw new GeneralSecurityException("needs a key_id query parameter");
}
String sig =
sigAndKeyId.substring(
SIGNATURE_PARAM_NAME.length(), i - 1 /* i - 1 instead of i because of & */);
int keyId = Integer.valueOf(sigAndKeyId.substring(i + KEY_ID_PARAM_NAME.length()));
Fazer a verificação
A etapa final é verificar o conteúdo do URL de callback com a chave pública apropriada. Use o mapeamento retornado do método parsePublicKeysJson
e use o parâmetro key_id
do URL de callback para receber a chave pública desse mapeamento. Em seguida, verifique a assinatura
com essa chave pública. Essas etapas são demonstradas abaixo no método verify
.
private void verify(final byte[] dataToVerify, int keyId, final byte[] signature)
throws GeneralSecurityException {
Map<Integer, ECPublicKey> publicKeys = parsePublicKeysJson();
if (publicKeys.containsKey(keyId)) {
foundKeyId = true;
ECPublicKey publicKey = publicKeys.get(keyId);
EcdsaVerifyJce verifier = new EcdsaVerifyJce(publicKey, HashType.SHA256, EcdsaEncoding.DER);
verifier.verify(signature, dataToVerify);
} else {
throw new GeneralSecurityException("cannot find verifying key with key ID: " + keyId);
}
}
Se o método for executado sem gerar uma exceção, o URL de callback foi verificado.
Perguntas frequentes
- Posso armazenar em cache a chave pública fornecida pelo servidor de chaves da AdMob?
- Recomendamos que você armazene em cache a chave pública fornecida pelo servidor de chaves da AdMob para reduzir o número de operações necessárias para validar callbacks de SSV. No entanto, as chaves públicas são alternadas regularmente e não podem ser armazenadas em cache por mais de 24 horas.
- Com que frequência as chaves públicas fornecidas pelo servidor de chaves da AdMob são alternadas?
- As chaves públicas fornecidas pelo servidor de chaves da AdMob são alternadas de acordo com uma programação variável. Para garantir que a verificação de callbacks de SSV continue funcionando conforme o esperado, as chaves públicas não podem ser armazenadas em cache por mais de 24 horas.
- O que acontece se não for possível acessar meu servidor?
- O Google espera um código de resposta de status de sucesso
HTTP 200 OK
para callbacks SSV. Se o servidor não puder ser acessado ou não fornecer a resposta esperada, o Google vai tentar enviar callbacks das SSVs até cinco vezes em intervalos de um segundo. - Como posso verificar se os callbacks de SSV estão vindo do Google?
- Use a busca DNS reversa para verificar se os callbacks de SSV são provenientes do Google.