Las devoluciones de llamada de verificación del servidor son solicitudes de URL, con parámetros de consulta expandidos por Google, que Google envía a un sistema externo para notificarle que se debe recompensar a un usuario por interactuar con un anuncio recompensado o un anuncio intersticial recompensado. Las devoluciones de llamada de SSV (verificación del servidor) de anuncios recompensados proporcionan una capa adicional de protección contra la suplantación de devoluciones de llamada del cliente para recompensar a los usuarios.
En esta guía, se muestra cómo verificar las devoluciones de llamada de SSV de anuncios recompensados con la biblioteca criptográfica de terceros Tink Java Apps para garantizar que los parámetros de consulta de la devolución de llamada sean valores legítimos. Aunque Tink se usa para los fines de esta guía, tienes la opción de usar cualquier biblioteca de terceros que admita ECDSA. También puedes probar tu servidor con la herramienta de prueba en la IU de AdMob.
Consulta el ejemplo de SSV de anuncios recompensados con Java spring-boot.
Requisitos previos
Usa RewardedAdsVerifier de la biblioteca Tink Java Apps
El repositorio de GitHub de Tink Java Apps
incluye una
RewardedAdsVerifier
clase auxiliar para reducir el código necesario para verificar una devolución de llamada de SSV de anuncios recompensados.
El uso de esta clase te permite verificar una URL de devolución de llamada con el siguiente código.
RewardedAdsVerifier verifier = new RewardedAdsVerifier.Builder()
.fetchVerifyingPublicKeysWith(
RewardedAdsVerifier.KEYS_DOWNLOADER_INSTANCE_PROD)
.build();
String rewardUrl = ...;
verifier.verify(rewardUrl);
Si el método verify() se ejecuta sin generar una excepción, la URL de devolución de llamada se verificó correctamente. En la sección Recompensa al usuario
, se detallan las prácticas recomendadas sobre cuándo se debe recompensar a los usuarios. Para obtener un
desglose de los pasos que realiza esta clase para verificar las devoluciones de llamada de SSV de anuncios recompensados,
puedes leer la sección Verificación manual de SSV de anuncios
recompensados.
Parámetros de devolución de llamada de SSV
Las devoluciones de llamada de verificación del servidor contienen parámetros de consulta que describen la interacción con el anuncio recompensado. A continuación, se indican los nombres de los parámetros, las descripciones y los valores de ejemplo. Los parámetros se envían en orden alfabético.
| Nombre del parámetro | Descripción | Valor de ejemplo |
|---|---|---|
| ad_network | Identificador de la fuente de anuncios que publicó este anuncio. Los nombres de las fuentes de anuncios correspondientes a los valores de ID se indican en la sección Identificadores de fuentes de anuncios. | 1953547073528090325 |
| ad_unit | ID de la unidad de anuncios de AdMob que se usó para solicitar el anuncio recompensado. | 2747237135 |
| custom_data | Cadena de datos personalizados que proporciona
setCustomData.
Si la app no proporciona una cadena de datos personalizados, este valor de parámetro de consulta no estará presente en la devolución de llamada de SSV. |
SAMPLE_CUSTOM_DATA_STRING |
| key_id | Clave que se usará para verificar la devolución de llamada de SSV. Este valor se asigna a una clave pública que proporciona el servidor de claves de AdMob. | 1234567890 |
| reward_amount | Cantidad de recompensa especificada en la configuración de la unidad de anuncios. | 5 |
| reward_item | Elemento de recompensa especificado en la configuración de la unidad de anuncios. | monedas |
| signature | Firma para la devolución de llamada de SSV generada por AdMob. | MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMtmgJHYYW9k2_icM9LFMY |
| timestamp | Marca de tiempo de cuando se recompensó al usuario como hora de la época en ms. | 1507770365237823 |
| transaction_id | Identificador único codificado en hexadecimal para cada evento de concesión de recompensa generado por AdMob. | 18fa792de1bca816048293fc71035638 |
| user_id | Identificador de usuario que proporciona
setUserId.
Si la app no proporciona un identificador de usuario, este parámetro de consulta no estará presente en la devolución de llamada de SSV. |
1234567 |
Identificadores de fuentes de anuncios
Nombres y IDs de fuentes de anuncios
| Nombre de la fuente de anuncios | ID de la fuente de anuncios |
|---|---|
| Ad Generation (ofertas) | 1477265452970951479 |
| AdColony | 15586990674969969776 |
| AdColony (ofertas) | 6895345910719072481 |
| AdFalcon | 3528208921554210682 |
| Red de AdMob | 5450213213286189855 |
| Cascada de la red de AdMob | 1215381445328257950 |
| Applovin | 1063618907739174004 |
| Applovin (ofertas) | 1328079684332308356 |
| Chartboost | 2873236629771172317 |
| Chocolate Platform (ofertas) | 6432849193975106527 |
| Evento personalizado | 18351550913290782395 |
| DT Exchange* * Antes del 21 de septiembre de 2022, esta red se llamaba "Fyber Marketplace". | 2179455223494392917 |
| Equativ (ofertas)* * Antes del 12 de enero de 2023, esta red se llamaba "Smart Adserver". | 5970199210771591442 |
| Fluct (ofertas) | 8419777862490735710 |
| Ráfaga | 3376427960656545613 |
| Fyber* * Esta fuente de anuncios se usa para la generación de informes históricos. | 4839637394546996422 |
| i-mobile | 5208827440166355534 |
| Improve Digital (ofertas) | 159382223051638006 |
| Index Exchange (ofertas) | 4100650709078789802 |
| InMobi | 7681903010231960328 |
| InMobi (ofertas) | 6325663098072678541 |
| InMobi Exchange (ofertas) | 5264320421916134407 |
| IronSource | 6925240245545091930 |
| ironSource Ads (ofertas) | 1643326773739866623 |
| Leadbolt | 2899150749497968595 |
| Liftoff Monetize* * Antes del 30 de enero de 2023, esta red se llamaba "Vungle". | 1953547073528090325 |
| Liftoff Monetize (ofertas)* * Antes del 30 de enero de 2023, esta red se llamaba "Vungle (ofertas)". | 4692500501762622185 |
| LG U+AD | 18298738678491729107 |
| LINE Ads Network | 3025503711505004547 |
| Magnite DV+ (ofertas) | 3993193775968767067 |
| maio | 7505118203095108657 |
| maio (ofertas) | 1343336733822567166 |
| Media.net (ofertas) | 2127936450554446159 |
| Anuncios propios mediados | 6060308706800320801 |
| Meta Audience Network* * Antes del 6 de junio de 2022, esta red se llamaba "Facebook Audience Network". | 10568273599589928883 |
| Meta Audience Network (ofertas)* * Antes del 6 de junio de 2022, esta red se llamaba "Facebook Audience Network (ofertas)". | 11198165126854996598 |
| Mintegral | 1357746574408896200 |
| Mintegral (ofertas) | 6250601289653372374 |
| MobFox (ofertas) | 3086513548163922365 |
| MoPub (obsoleto) | 10872986198578383917 |
| myTarget | 8450873672465271579 |
| Nend | 9383070032774777750 |
| Nexxen (ofertas)* * Antes del 1 de mayo de 2024, esta red se llamaba "UnrulyX". | 2831998725945605450 |
| OneTag Exchange (ofertas) | 4873891452523427499 |
| OpenX (ofertas) | 4918705482605678398 |
| Pangle | 4069896914521993236 |
| Pangle (ofertas) | 3525379893916449117 |
| PubMatic (ofertas) | 3841544486172445473 |
| Campaña de reservación | 7068401028668408324 |
| SK planet | 734341340207269415 |
| Sharethrough (ofertas) | 5247944089976324188 |
| Smaato (ofertas) | 3362360112145450544 |
| Sonobi (ofertas) | 3270984106996027150 |
| Tapjoy | 7295217276740746030 |
| Tapjoy (ofertas) | 4692500501762622178 |
| Tencent GDT | 7007906637038700218 |
| TripleLift (ofertas) | 8332676245392738510 |
| Unity Ads | 4970775877303683148 |
| Unity Ads (ofertas) | 7069338991535737586 |
| Verve Group (ofertas) | 5013176581647059185 |
| Vpon | 1940957084538325905 |
| Yieldmo (ofertas) | 4193081836471107579 |
| YieldOne (ofertas) | 3154533971590234104 |
| Zucks | 5506531810221735863 |
Recompensa al usuario
Es importante equilibrar la experiencia del usuario y la validación de la recompensa cuando se decide cuándo recompensar a un usuario. Las devoluciones de llamada del servidor pueden experimentar demoras antes de llegar a los sistemas externos. Por lo tanto, la práctica recomendada es usar la devolución de llamada del cliente para recompensar al usuario de inmediato, mientras se realiza la validación de todas las recompensas cuando se reciben las devoluciones de llamada del servidor. Este enfoque proporciona una buena experiencia del usuario y, al mismo tiempo, garantiza la validez de las recompensas otorgadas.
Sin embargo, para las aplicaciones en las que la validez de la recompensa es fundamental (por ejemplo, la recompensa afecta la economía del juego de tu app) y las demoras en el otorgamiento de recompensas son aceptables, esperar la devolución de llamada del servidor verificada puede ser el mejor enfoque.
Datos personalizados
Las apps que requieren datos adicionales en las devoluciones de llamada de verificación del servidor deben usar la función de datos personalizados de los anuncios recompensados. Cualquier valor de cadena establecido en un objeto de anuncio recompensado se pasa al parámetro de consulta custom_data de la devolución de llamada de SSV. Si no se establece ningún valor de datos personalizados, el valor del parámetro de consulta custom_data no estará presente en la devolución de llamada de SSV.
En el siguiente ejemplo, se establecen las opciones de SSV después de que se carga el anuncio recompensado:
Java
RewardedAd.load(MainActivity.this, "AD_UNIT_ID", 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, "AD_UNIT_ID", 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 } })
Si deseas establecer la cadena de recompensa personalizada, debes hacerlo antes de mostrar el anuncio.
Verificación manual de SSV de anuncios recompensados
A continuación, se describen los pasos que realiza la clase RewardedAdsVerifier para verificar una SSV de anuncios recompensados. Aunque los fragmentos de código incluidos están en Java y
aprovechan la biblioteca de terceros Tink, puedes implementar estos pasos en
el idioma que elijas con cualquier biblioteca de terceros que admita
ECDSA.
Recupera claves públicas
Para verificar una devolución de llamada de SSV de anuncios recompensados, necesitas una clave pública que proporcione AdMob.
Se puede recuperar una lista de claves públicas que se usará para validar las devoluciones de llamada de SSV de anuncios recompensados desde el servidor de claves de AdMob. La lista de claves públicas se proporciona como una representación JSON con un formato similar al siguiente:
{
"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 las claves públicas, conéctate al servidor de claves de AdMob y descarga las claves. El siguiente código realiza esta tarea y guarda la representación JSON de las claves en la variable 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();
}
Ten en cuenta que las claves públicas se rotan con regularidad. Recibirás un correo electrónico para informarte sobre una rotación próxima. Si almacenas en caché las claves públicas, debes actualizarlas cuando recibas este correo electrónico.
Una vez que se recuperan las claves públicas, se deben analizar. El siguiente método parsePublicKeysJson toma una cadena JSON, como el ejemplo anterior, como entrada y crea una asignación de valores key_id a claves públicas, que se encapsulan como objetos ECPublicKey de la 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;
}
Obtén el contenido que se verificará
Los dos últimos parámetros de consulta de las devoluciones de llamada de SSV de anuncios recompensados siempre son signature y key_id, en ese orden. Los parámetros de consulta restantes especifican el contenido que se verificará. Supongamos que configuraste AdMob para enviar devoluciones de llamada de recompensa a https://www.myserver.com/mypath. En el siguiente fragmento, se muestra un ejemplo de devolución de llamada de SSV de anuncios recompensados con el contenido que se verificará 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
En el siguiente código, se muestra cómo analizar el contenido que se verificará desde una URL de devolución de llamada como un array 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"));
Obtén la firma y key_id de la URL de devolución de llamada
Con el valor queryString del paso anterior, analiza los parámetros de consulta signature y key_id de la URL de devolución de llamada como se muestra a continuación:
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()));
Realiza la verificación
El paso final es verificar el contenido de la URL de devolución de llamada con la clave pública adecuada. Toma la asignación que muestra el método parsePublicKeysJson y usa el parámetro key_id de la URL de devolución de llamada para obtener la clave pública de esa asignación. Luego, verifica la firma con esa clave pública. Estos pasos se muestran a continuación en el 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);
}
}
Si el método se ejecuta sin generar una excepción, la URL de devolución de llamada se verificó correctamente.
Preguntas frecuentes
- ¿Puedo almacenar en caché la clave pública que proporciona el servidor de claves de AdMob?
- Te recomendamos que almacenes en caché la clave pública que proporciona el servidor de claves de AdMob para reducir la cantidad de operaciones necesarias para validar las devoluciones de llamada de SSV. Sin embargo, ten en cuenta que las claves públicas se rotan con regularidad y no se deben almacenar en caché por más de 24 horas.
- ¿Con qué frecuencia se rotan las claves públicas que proporciona el servidor de claves de AdMob?
- Las claves públicas que proporciona el servidor de claves de AdMob se rotan según un cronograma variable. Para garantizar que la verificación de las devoluciones de llamada de SSV siga funcionando según lo previsto, las claves públicas no se deben almacenar en caché por más de 24 horas.
- ¿Qué sucede si no se puede acceder a mi servidor?
- Google espera un código de respuesta de estado de éxito
HTTP 200 OKpara las devoluciones de llamada de SSV. Si no se puede acceder a tu servidor o no proporciona la respuesta esperada, Google volverá a intentar enviar devoluciones de llamada de SSV hasta cinco veces en intervalos de un segundo. - ¿Cómo puedo verificar que las devoluciones de llamada de SSV provienen de Google?
- Usa la búsqueda de DNS inversa para verificar que las devoluciones de llamada de SSV provengan de Google.