Os callbacks de verificação do lado do servidor são solicitações de URL, com parâmetros de consulta expandidos pelo Google, que são enviados pelo Google a um sistema externo para notificar que um usuário deve ser recompensado por interagir com um anúncio premiado ou intersticial premiado. Os callbacks de SSV (verificação do lado do servidor) premiada oferecem uma camada extra de proteção contra spoofing de callbacks do lado do cliente para premiar os usuários.
Neste guia, mostramos como verificar os callbacks de SSV premiada 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 fins deste guia, você tem a opção de usar qualquer biblioteca de terceiros compatível com ECDSA. Você também pode testar seu servidor com a ferramenta de teste na interface da AdMob.
Pré-requisitos
- Ative a verificação do lado do servidor premiada no seu bloco de anúncios.
Usar o RewardedAdsVerifier da biblioteca de apps Java do Tink
O repositório do GitHub Tink Java Apps
inclui uma classe auxiliar
RewardedAdsVerifier
para reduzir o código necessário para verificar um callback de SSV premiada.
Com essa classe, é possível 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 com sucesso. A seção Recompensar o usuário
detalha as práticas recomendadas sobre quando os usuários devem ser recompensados. Para uma
análise detalhada das etapas realizadas por essa classe para verificar os callbacks de SSV premiada,
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. Os nomes, as descrições e os valores de exemplo dos parâmetros estão listados abaixo. Os parâmetros são enviados em ordem alfabética.
| Nome do parâmetro | Descrição | Valor de exemplo |
|---|---|---|
| ad_network | Identificador da origem de anúncios que preencheu o anúncio. Os nomes das origens de anúncios correspondentes aos valores de ID estão listados na seção Identificadores de origem de anúncios. | 1953547073528090325 |
| ad_unit | É o ID do bloco de anúncios da AdMob usado para solicitar o anúncio premiado. | 2747237135 |
| key_id | Chave a ser usada para verificar o callback de SSV. Esse valor é associado a uma chave pública fornecida pelo servidor de chaves da AdMob. | 1234567890 |
| reward_amount | Valor da recompensa, conforme especificado nas configurações do bloco de anúncios. | 5 |
| reward_item | Item de recompensa, conforme especificado nas configurações do bloco de anúncios. | moedas |
| assinatura | Assinatura do callback de SSV gerada pela AdMob. | MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMtmgJHYYW9k2_icM9LFMY |
| timestamp | Carimbo de data/hora de quando o usuário recebeu a recompensa como horário de início em ms. | 1507770365237823 |
| transaction_id | Identificador exclusivo codificado em hexadecimal para cada evento de concessão de recompensa gerado pela AdMob. | 18fa792de1bca816048293fc71035638 |
| user_id | Identificador de usuário fornecido por
SetUserId.
Se nenhum identificador de usuário for fornecido pelo app, esse parâmetro de consulta não estará presente no callback de SSV. |
1234567 |
Identificadores de origem de anúncios
Nomes e IDs das origens de anúncios
| Nome da origem do anúncio | ID da origem de anúncios |
|---|---|
| Ad Generation (lances) | 1477265452970951479 |
| Rede da AdMob | 5450213213286189855 |
| Hierarquia da rede da AdMob | 1215381445328257950 |
| AppLovin | 1063618907739174004 |
| AppLovin (lances) | 1328079684332308356 |
| Bidease (lances) | 3670825090829827805 |
| BidMachine (lances) | 7943972370566394673 |
| Chartboost | 2873236629771172317 |
| Chocolate Platform (lances) | 6432849193975106527 |
| Evento personalizado | 18351550913290782395 |
| DT Exchange* * Antes de 21 de setembro de 2022, essa rede era chamada de "Fyber Marketplace". | 2179455223494392917 |
| DT Exchange (lances) | 8189833498765234879 |
| Equativ (lances)* * Antes de 12 de janeiro de 2023, essa rede era chamada de "Smart Adserver". | 5970199210771591442 |
| Fluct (lances) | 8419777862490735710 |
| i-mobile | 5208827440166355534 |
| Improve Digital (lances) | 159382223051638006 |
| Index Exchange (lances) | 4100650709078789802 |
| InMobi | 7681903010231960328 |
| InMobi (SDK) (lances) | 8468954295581492586 |
| InMobi Exchange (lances) | 5264320421916134407 |
| ironSource Ads | 6925240245545091930 |
| ironSource Ads (lances) | 1643326773739866623 |
| Liftoff Monetize* * Antes de 30 de janeiro de 2023, essa rede era chamada de "Vungle". | 1953547073528090325 |
| Liftoff Monetize (lances)* * Antes de 30 de janeiro de 2023, essa rede era chamada de "Vungle (lances)". | 4692500501762622185 |
| LY Ads Network | 3025503711505004547 |
| LY Ads Network (lances) | 2615812619460460513 |
| Magnite DV+ (lances) | 3993193775968767067 |
| maio | 7505118203095108657 |
| Media.net (lances) | 2127936450554446159 |
| Anúncios internos mediados | 6060308706800320801 |
| Audience Network da Meta* * Antes de 6 de junho de 2022, essa rede era chamada de "Facebook Audience Network". | 10568273599589928883 |
| Audience Network da Meta (lances)* * Antes de 6 de junho de 2022, essa rede era chamada de "Facebook Audience Network (lances)". | 11198165126854996598 |
| Mintegral | 1357746574408896200 |
| Mintegral (lances) | 6250601289653372374 |
| Mobfox (lances) | 3086513548163922365 |
| MobileFuse (lances) | 7303547408604090310 |
| SDK de anúncio da Moloco (lances) | 8267622065755668722 |
| myTarget | 8450873672465271579 |
| Nativo (lances) | 3240503836211327896 |
| Nexxen (lances)* * Antes de 1º de maio de 2024, essa rede era chamada de "UnrulyX". | 2831998725945605450 |
| OneTag Exchange (lances) | 4873891452523427499 |
| OpenX (lances) | 4918705482605678398 |
| Pangle | 4069896914521993236 |
| SDK da Pangle KR (lances) | 12171279046073404914 |
| SDK da Pangle ROW (lances) | 3525379893916449117 |
| SDK da Pangle nos EUA (lances) | 15999446638585856012 |
| PubMatic (lances) | 3841544486172445473 |
| SDK OpenWrap da PubMatic | 7702975372504485373 |
| SDK OpenWrap da PubMatic (lances) | 1234567890123456789 |
| Campanha de reserva | 7068401028668408324 |
| Aumento (lances) | 6816468518946650043 |
| Sharethrough (lances) | 5247944089976324188 |
| Smaato (lances) | 3362360112145450544 |
| Sonobi (lances) | 3270984106996027150 |
| TripleLift (lances) | 8332676245392738510 |
| Unity Ads | 4970775877303683148 |
| Unity Ads (lances) | 7069338991535737586 |
| Verve Group (lances) | 5013176581647059185 |
| Vpon | 1940957084538325905 |
| Yieldmo (lances) | 4193081836471107579 |
| YieldOne (lances) | 3154533971590234104 |
| Zucks | 5506531810221735863 |
Como recompensar o usuário
É importante equilibrar a experiência do usuário e a validação da recompensa ao decidir quando recompensar um usuário. Os callbacks do lado do servidor podem sofrer atrasos antes de chegar aos sistemas externos. Portanto, a prática recomendada é usar o callback do lado do cliente para recompensar o usuário imediatamente e, ao mesmo tempo, validar todas as recompensas ao receber os callbacks do lado do servidor. Essa abordagem oferece uma boa experiência do usuário e garante a validade das recompensas concedidas.
No entanto, para aplicativos em que a validade da recompensa é essencial (por exemplo, quando ela afeta a economia do jogo no app) e atrasos na concessão de recompensas são aceitáveis, esperar pelo 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 personalizado 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 depois que o anúncio premiado é carregado:
private void LoadRewardedAd(string adUnitId)
{
// Send the request to load the ad.
AdRequest adRequest = new AdRequest();
RewardedAd.Load(adUnitId, adRequest, (RewardedAd rewardedAd, LoadAdError error) =>
{
// If the operation failed with a reason.
if (error != null)
{
Debug.LogError("Rewarded ad failed to load an ad with error : " + error);
return;
}
var options = new ServerSideVerificationOptions
.Builder()
.SetCustomData("SAMPLE_CUSTOM_DATA_STRING")
.Build()
rewardedAd.SetServerSideVerificationOptions(options);
});
}
Se quiser definir a string de recompensa personalizada, faça isso antes de mostrar 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
usem a biblioteca de terceiros Tink, essas etapas podem ser implementadas por você na
linguagem de sua escolha, usando qualquer biblioteca de terceiros compatível com
ECDSA.
Buscar chaves públicas
Para verificar um callback de SSV premiada, você precisa de uma chave pública fornecida pela AdMob.
Uma lista de chaves públicas a serem usadas para validar os callbacks de SSV premiada pode ser buscada no servidor de chaves da AdMob. A lista de chaves públicas é fornecida como uma representação JSON com um formato semelhante a este:
{
"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 delas. 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 trocadas regularmente. Você vai receber um e-mail para informar sobre uma rotação futura. Se você estiver armazenando chaves públicas em cache, atualize-as ao receber este e-mail.
Depois que as chaves públicas forem buscadas, elas precisarão 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 dos callbacks de SSV premiada são sempre signature
e key_id,, nessa ordem. Os parâmetros de consulta restantes especificam o conteúdo
a ser verificado. Vamos supor que você tenha configurado a AdMob para enviar callbacks de recompensa para
https://www.myserver.com/mypath. O snippet abaixo mostra um exemplo de callback de SSV premiado com o conteúdo a ser verificado em destaque.
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 a assinatura e o 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 verificação
A etapa final é verificar o conteúdo do URL de callback com a
chave pública apropriada. Pegue 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 os callbacks de SSV. No entanto, as chaves públicas são rotacionadas regularmente e não devem 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 em uma programação variável. Para garantir que a verificação de callbacks de SSV continue funcionando como esperado, as chaves públicas não podem ser armazenadas em cache por mais de 24 horas.
- O que acontece se meu servidor não puder ser acessado?
- O Google espera um código de resposta de status de sucesso
HTTP 200 OKpara callbacks de SSV. Se o servidor não puder ser acessado ou não fornecer a resposta esperada, o Google vai tentar enviar os callbacks de SSV até cinco vezes em intervalos de um segundo. - Como posso verificar se os callbacks de SSV são do Google?
- Use a busca DNS reversa para verificar se os callbacks de SSV são do Google.