Обратные вызовы проверки на стороне сервера – это запросы URL с параметрами запроса, которые расширяются Google и отправляются Google во внешнюю систему, чтобы уведомить ее о том, что пользователь должен получить вознаграждение за взаимодействие с объявлением с вознаграждением или межстраничным объявлением с вознаграждением. Обратные вызовы для проверки на стороне сервера (SSV) объявлений с вознаграждением обеспечивают дополнительную защиту от подделки обратных вызовов на стороне клиента, предназначенных для выдачи вознаграждения пользователям.
В этом руководстве рассказывается, как проверять обратные вызовы SSV для объявлений с вознаграждением с помощью сторонней криптографической библиотеки Tink Java Apps, чтобы убедиться, что параметры запроса в обратном вызове являются действительными. В этом руководстве используется библиотека Tink, но вы можете выбрать любую стороннюю библиотеку, которая поддерживает ECDSA. Вы также можете протестировать свой сервер с помощью инструмента тестирования в интерфейсе AdMob.
Требования
- Включите проверку на стороне сервера для объявлений с вознаграждением в рекламном блоке.
Как использовать RewardedAdsVerifier из библиотеки Tink Java Apps
В репозитории GitHub Tink Java Apps есть вспомогательный класс RewardedAdsVerifier, который позволяет сократить объем кода, необходимого для проверки обратного вызова SSV с вознаграждением.
Используя этот класс, вы можете проверить URL обратного вызова с помощью следующего кода:
RewardedAdsVerifier verifier = new RewardedAdsVerifier.Builder()
.fetchVerifyingPublicKeysWith(
RewardedAdsVerifier.KEYS_DOWNLOADER_INSTANCE_PROD)
.build();
String rewardUrl = ...;
verifier.verify(rewardUrl);
Если метод verify() выполняется без исключений, URL обратного вызова успешно проверен. В разделе Вознаграждение пользователей описаны рекомендации о том, когда следует вознаграждать пользователей. Чтобы узнать, как этот класс проверяет обратные вызовы SSV для объявлений с вознаграждением, ознакомьтесь с разделом Проверка обратных вызовов SSV для объявлений с вознаграждением вручную.
Параметры обратного вызова SSV
Обратные вызовы проверки на стороне сервера содержат параметры запроса, описывающие взаимодействие с объявлением с вознаграждением. Ниже приведены названия, описания и примеры значений параметров. Параметры отправляются в алфавитном порядке.
| Название параметра | Описание | Пример значения |
|---|---|---|
| ad_network | Идентификатор источника объявлений, из которого было получено это объявление. Названия источников объявлений, соответствующие значениям идентификаторов, перечислены в разделе Идентификаторы источников объявлений. | 1953547073528090325 |
| ad_unit | Идентификатор рекламного блока AdMob, который использовался для запроса объявления с вознаграждением. | 2747237135 |
| custom_data | Специальная строка данных, предоставленная
customData.
Если приложение не предоставляет строку пользовательских данных, значение этого параметра запроса не будет передано в обратном вызове SSV. |
SAMPLE_CUSTOM_DATA_STRING |
| key_id | Ключ, который будет использоваться для проверки обратного вызова SSV. Это значение сопоставляется с открытым ключом, предоставленным сервером ключей AdMob. | 1234567890 |
| reward_amount | Размер вознаграждения, указанный в настройках рекламного блока. | 5 |
| reward_item | Предмет вознаграждения, указанный в настройках рекламного блока. | монеты |
| подпись | Подпись для обратного вызова SSV, созданная AdMob. | MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMtmgJHYYW9k2_icM9LFMY |
| временная метка | Временная метка, когда пользователь получил награду, в формате Epoch в миллисекундах. | 1507770365237823 |
| transaction_id | Уникальный идентификатор в шестнадцатеричном коде для каждого события предоставления вознаграждения, сгенерированного AdMob. | 18fa792de1bca816048293fc71035638 |
| user_id | Идентификатор пользователя, предоставленный userId.
Если приложение не предоставляет идентификатор пользователя, этот параметр запроса не будет присутствовать в обратном вызове SSV. |
1234567 |
Идентификаторы источников объявлений
Названия и идентификаторы источников объявлений
| Название источника объявления | Идентификатор источника объявлений |
|---|---|
| Ad Generation (назначение ставок) | 1477265452970951479 |
| Сеть AdMob | 5450213213286189855 |
| Каскад сети AdMob | 1215381445328257950 |
| AppLovin | 1063618907739174004 |
| AppLovin (назначение ставок) | 1328079684332308356 |
| Bidease (назначение ставок) | 3670825090829827805 |
| BidMachine (назначение ставок) | 7943972370566394673 |
| Chartboost | 2873236629771172317 |
| Chocolate Platform (назначение ставок) | 6432849193975106527 |
| Пользовательское событие | 18351550913290782395 |
| DT Exchange* * До 21 сентября 2022 г. эта сеть называлась Fyber Marketplace. | 2179455223494392917 |
| DT Exchange (назначение ставок) | 8189833498765234879 |
| Equativ (назначение ставок)* * До 12 января 2023 г. эта сеть называлась Smart Adserver. | 5970199210771591442 |
| Fluct (назначение ставок) | 8419777862490735710 |
| i-mobile | 5208827440166355534 |
| Improve Digital (назначение ставок) | 159382223051638006 |
| Index Exchange (назначение ставок) | 4100650709078789802 |
| InMobi | 7681903010231960328 |
| InMobi (SDK) (назначение ставок) | 8468954295581492586 |
| InMobi Exchange (назначение ставок) | 5264320421916134407 |
| ironSource Ads | 6925240245545091930 |
| ironSource Ads (назначение ставок) | 1643326773739866623 |
| Liftoff Monetize* * До 30 января 2023 г. эта сеть называлась Vungle. | 1953547073528090325 |
| Liftoff Monetize (ставки)* * До 30 января 2023 года эта сеть называлась "Vungle (назначение ставок)". | 4692500501762622185 |
| LY Ads Network | 3025503711505004547 |
| LY Ads Network (назначение ставок) | 2615812619460460513 |
| Magnite DV+ (назначение ставок) | 3993193775968767067 |
| maio. | 7505118203095108657 |
| Media.net (аукционы) | 2127936450554446159 |
| Собственные объявления, агрегируемые с помощью медиации | 6060308706800320801 |
| Meta Audience Network* * До 6 июня 2022 г. эта сеть называлась Facebook Audience Network. | 10568273599589928883 |
| Meta Audience Network (назначение ставок)* * До 6 июня 2022 г. эта сеть называлась "Facebook Audience Network (назначение ставок)". | 11198165126854996598 |
| Mintegral | 1357746574408896200 |
| Mintegral (назначение ставок) | 6250601289653372374 |
| Mobfox (ставки) | 3086513548163922365 |
| MobileFuse (назначение ставок) | 7303547408604090310 |
| Moloco Ads SDK (назначение ставок) | 8267622065755668722 |
| myTarget | 8450873672465271579 |
| Nativo (назначение ставок) | 3240503836211327896 |
| Nexxen (ставки)* * До 1 мая 2024 года эта сеть называлась UnrulyX. | 2831998725945605450 |
| OneTag Exchange (назначение ставок) | 4873891452523427499 |
| OpenX (ставки) | 4918705482605678398 |
| Pangle | 4069896914521993236 |
| Pangle KR SDK (назначение ставок) | 12171279046073404914 |
| Pangle ROW SDK (назначение ставок) | 3525379893916449117 |
| Pangle US SDK (назначение ставок) | 15999446638585856012 |
| PubMatic (назначение ставок) | 3841544486172445473 |
| PubMatic OpenWrap SDK | 7702975372504485373 |
| PubMatic OpenWrap SDK (назначение ставок) | 1234567890123456789 |
| Кампания с резервированием | 7068401028668408324 |
| Повышение ставок | 6816468518946650043 |
| Sharethrough (назначение ставок) | 5247944089976324188 |
| Smaato (назначение ставок) | 3362360112145450544 |
| Sonobi (назначение ставок) | 3270984106996027150 |
| TripleLift (назначение ставок) | 8332676245392738510 |
| Unity Ads | 4970775877303683148 |
| Unity Ads (назначение ставок) | 7069338991535737586 |
| Verve Group (назначение ставок) | 5013176581647059185 |
| Vpon | 1940957084538325905 |
| Yieldmo (назначение ставок) | 4193081836471107579 |
| YieldOne (ставки) | 3154533971590234104 |
| Zucks | 5506531810221735863 |
Вознаграждение для пользователя
При определении момента, когда пользователь должен получить вознаграждение, важно соблюдать баланс между удобством и проверкой. При использовании обратных вызовов на стороне сервера возможны задержки при передаче данных во внешние системы. Поэтому мы рекомендуем использовать клиентский обратный вызов, чтобы сразу же начислять пользователю вознаграждение, и выполнять проверку всех вознаграждений при получении серверных обратных вызовов. Такой подход позволяет обеспечить удобство для пользователей и гарантировать действительность полученных вознаграждений.
Однако если срок действия награды критически важен (например, она влияет на экономику игры) и задержка в ее предоставлении допустима, то лучше дождаться подтвержденного обратного вызова на стороне сервера.
Специальные данные
Если приложению требуются дополнительные данные в обратных вызовах для проверки на стороне сервера, используйте функцию пользовательских данных в объявлениях с вознаграждением. Любое строковое значение, заданное в объекте объявления с вознаграждением, передается в параметр запроса custom_data обратного вызова SSV. Если значение специальных данных не задано, значение параметра запроса custom_data не будет присутствовать в обратном вызове SSV.
В примере ниже показано, как задать параметры SSV после загрузки объявления с вознаграждением:
Замените SAMPLE_CUSTOM_DATA_STRING собственными данными.
Если вы хотите задать строку для специальной награды, это нужно сделать до показа объявления.
Проверка SSV для объявлений с вознаграждением вручную
Ниже описаны действия, которые выполняет класс RewardedAdsVerifier для проверки SSV с вознаграждением. Хотя приведенные фрагменты кода написаны на языке Java и используют стороннюю библиотеку Tink, вы можете выполнить эти действия на любом языке программирования, используя любую стороннюю библиотеку, которая поддерживает ECDSA.
Получение открытых ключей
Чтобы проверить обратный вызов SSV для объявлений с вознаграждением, вам понадобится открытый ключ, предоставленный AdMob.
Список открытых ключей, которые используются для проверки обратных вызовов SSV для объявлений с вознаграждением, можно получить с сервера ключей AdMob. Список открытых ключей предоставляется в виде объекта JSON в следующем формате:
{
"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=="
},
],
}
Чтобы получить открытые ключи, подключитесь к серверу ключей AdMob и скачайте ключи. Приведенный ниже код выполняет эту задачу и сохраняет представление ключей в формате JSON в переменной 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();
}
Обратите внимание, что открытые ключи регулярно меняются. Вы получите электронное письмо с информацией о предстоящей ротации. Если вы кешируете открытые ключи, то после получения этого письма вам нужно обновить их.
После получения открытые ключи необходимо проанализировать. Приведенный ниже метод parsePublicKeysJson принимает на вход строку JSON, например из примера выше, и создает сопоставление значений key_id с открытыми ключами, которые инкапсулируются как объекты ECPublicKey из библиотеки 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;
}
Как получить контент для проверки
Последние два параметра запроса в обратных вызовах SSV для объявлений с вознаграждением всегда имеют значения signature и key_id, (в указанном порядке). Остальные параметры запроса указывают на контент, который нужно проверить. Предположим, вы настроили AdMob так, чтобы обратные вызовы с информацией о вознаграждении отправлялись в https://www.myserver.com/mypath. Во фрагменте кода ниже показан пример обратного вызова SSV для объявлений с вознаграждением, в котором выделен контент, требующий проверки.
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
В приведенном ниже коде показано, как проанализировать контент, который нужно проверить, из URL обратного вызова в виде массива байтов 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"));
Как получить подпись и key_id из URL обратного вызова
Используя значение queryString из предыдущего шага, извлеките параметры запроса signature и key_id из URL обратного вызова, как показано ниже.
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()));
Как пройти проверку
На последнем этапе необходимо проверить содержимое URL обратного вызова с помощью подходящего открытого ключа. Возьмите сопоставление, возвращенное методом parsePublicKeysJson, и используйте параметр key_id из URL обратного вызова, чтобы получить открытый ключ из этого сопоставления. Затем проверьте подпись с помощью этого открытого ключа. Ниже описаны шаги для метода 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);
}
}
Если метод выполняется без исключений, URL обратного вызова успешно проверен.
Часто задаваемые вопросы
- Можно ли кешировать открытый ключ, предоставленный сервером ключей AdMob?
- Рекомендуем кешировать открытый ключ, предоставленный сервером ключей AdMob, чтобы уменьшить количество операций, необходимых для проверки обратных вызовов SSV. Однако обратите внимание, что открытые ключи регулярно меняются, поэтому их не следует кешировать более чем на 24 часа.
- Как часто меняются открытые ключи, предоставляемые сервером ключей AdMob?
- Открытые ключи, предоставленные сервером ключей AdMob, меняются по переменному расписанию. Чтобы проверка обратных вызовов SSV работала корректно, не кешируйте открытые ключи дольше 24 часов.
- Что произойдет, если сервер будет недоступен?
- Google ожидает, что в ответ на обратные вызовы SSV будет возвращаться код статуса
HTTP 200 OK. Если сервер недоступен или не предоставляет ожидаемый ответ, Google повторит попытку отправить обратные вызовы SSV до пяти раз с интервалом в одну секунду. - Как проверить, что обратные вызовы SSV приходят от Google?
- Используйте обратный DNS-запрос, чтобы убедиться, что обратные вызовы SSV поступают от Google.