Обратные вызовы проверки на стороне сервера — это URL-запросы с параметрами запроса, расширенными Google, которые Google отправляет внешней системе, чтобы уведомить ее о том, что пользователь должен быть вознагражден за взаимодействие с вознаграждаемой или вознаграждаемой промежуточной рекламой. Вознаграждаемые SSV-вызовы (проверка на стороне сервера) обеспечивают дополнительный уровень защиты от подмены обратных вызовов на стороне клиента для вознаграждения пользователей.
В этом руководстве показано, как проверить вознагражденные обратные вызовы SSV с помощью сторонней криптографической библиотеки Tink Java Apps, чтобы гарантировать, что параметры запроса в обратном вызове являются допустимыми значениями. Хотя в этом руководстве используется Tink, у вас есть возможность использовать любую стороннюю библиотеку, которая поддерживает ECDSA . Вы также можете протестировать свой сервер с помощью инструмента тестирования в пользовательском интерфейсе AdMob.
Предпосылки
- Включите проверку на стороне сервера с вознаграждением для вашего рекламного блока.
Используйте RewardedAdsVerifier из библиотеки Tink Java Apps
Репозиторий Tink Java Apps GitHub включает вспомогательный класс 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
Обратные вызовы проверки на стороне сервера содержат параметры запроса, которые описывают взаимодействие с вознагражденной рекламой. Имена параметров, описания и примеры значений приведены ниже. Параметры отправляются в алфавитном порядке.
Имя параметра | Описание | Пример значения |
---|---|---|
рекламная_сеть | Идентификатор источника рекламы для источника рекламы, который выполнил эту рекламу. Названия источников рекламы, соответствующие значениям идентификаторов, перечислены в разделе Идентификаторы источников рекламы . | 1953547073528090325 |
рекламный_блок | Идентификатор рекламного блока AdMob, который использовался для запроса вознагражденной рекламы. | 2747237135 |
пользовательские_данные | Пользовательская строка данных, предоставленная customRewardString .Если приложение не предоставляет пользовательскую строку данных, это значение параметра запроса не будет присутствовать в обратном вызове SSV. | ОБРАЗЕЦ_ПОЛЬЗОВАТЕЛЬСКОЙ_СТРОКИ_ДАННЫХ |
key_id | Ключ, который будет использоваться для проверки обратного вызова SSV. Это значение сопоставляется с открытым ключом, предоставленным сервером ключей AdMob . | 1234567890 |
сумма_вознаграждения | Сумма вознаграждения указана в настройках рекламного блока. | 5 |
предмет_награды | Элемент вознаграждения, указанный в настройках рекламного блока. | монеты |
подпись | Подпись для обратного вызова SSV, сгенерированная AdMob. | MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMtmgJHYYW9k2_icM9LFMY |
временная метка | Временная метка, указывающая, когда пользователь был вознагражден в виде времени эпохи в мс. | 1507770365237823 |
транзакция_id | Уникальный шестнадцатеричный идентификатор для каждого события предоставления вознаграждения, сгенерированного AdMob. | 18fa792de1bca816048293fc71035638 |
ID пользователя | Идентификатор пользователя, предоставленный userIdentifier .Если приложение не предоставило идентификатор пользователя, этот параметр запроса не будет присутствовать в обратном вызове SSV. | 1234567 |
Идентификаторы источника рекламы
Названия и идентификаторы источников рекламы
Название источника рекламы | Идентификатор источника рекламы |
---|---|
Генерация рекламы (торги) | 1477265452970951479 |
AdColony | 15586990674969969776 |
AdColony (торги) | 6895345910719072481 |
AdFalcon | 3528208921554210682 |
Сеть AdMob | 5450213213286189855 |
Водопад сети AdMob | 1215381445328257950 |
Аппловин | 1063618907739174004 |
Applovin (торги) | 1328079684332308356 |
Chartboost | 2873236629771172317 |
Шоколадная платформа (торги) | 6432849193975106527 |
Пользовательское событие | 18351550913290782395 |
Обмен ДТ* * До 21 сентября 2022 года эта сеть называлась «Fyber Marketplace». | 2179455223494392917 |
Equativ (торги)* * До 12 января 2023 года эта сеть называлась «Smart Adserver». | 5970199210771591442 |
Fluct (торги) | 8419777862490735710 |
Шквал | 3376427960656545613 |
Фибер* * Этот источник рекламы используется для исторических отчетов. | 4839637394546996422 |
i-мобильный | 5208827440166355534 |
Улучшение цифровых технологий (торги) | 159382223051638006 |
Индексная биржа (торги) | 4100650709078789802 |
InMobi | 7681903010231960328 |
InMobi (торги) | 6325663098072678541 |
InMobi Exchange (торги) | 5264320421916134407 |
Источник железа | 6925240245545091930 |
ironSource Ads (торги) | 1643326773739866623 |
Свинцовый болт | 2899150749497968595 |
Подъем монетизации* * До 30 января 2023 года эта сеть называлась «Vungle». | 1953547073528090325 |
Liftoff Monetize (торги)* * До 30 января 2023 года эта сеть называлась «Vungle (биддинг)». | 4692500501762622185 |
LG U+AD | 18298738678491729107 |
Рекламная сеть LINE | 3025503711505004547 |
Магнит DV+ (торги) | 3993193775968767067 |
майо | 7505118203095108657 |
майо (торги) | 1343336733822567166 |
Media.net (торги) | 2127936450554446159 |
Посреднические домашние объявления | 6060308706800320801 |
Сеть метааудитории* * До 6 июня 2022 года эта сеть называлась «Facebook Audience Network». | 10568273599589928883 |
Сеть метааудитории (торги)* * До 6 июня 2022 года эта сеть называлась «Facebook Audience Network (биддинг)». | 11198165126854996598 |
Минтеграл | 1357746574408896200 |
Mintegral (торги) | 6250601289653372374 |
MobFox (торги) | 3086513548163922365 |
MoPub ( устарело ) | 10872986198578383917 |
мояЦель | 8450873672465271579 |
Ненд | 9383070032774777750 |
Nexxen (торги)* * До 1 мая 2024 года эта сеть называлась «UnrulyX». | 2831998725945605450 |
OneTag Exchange (торги) | 4873891452523427499 |
OpenX (торги) | 4918705482605678398 |
Пангл | 4069896914521993236 |
Пэнгл (торги) | 3525379893916449117 |
PubMatic (торги) | 3841544486172445473 |
Кампания по бронированию | 7068401028668408324 |
СК планета | 734341340207269415 |
Sharethrough (торги) | 5247944089976324188 |
Смаато (торги) | 3362360112145450544 |
Соноби (торги) | 3270984106996027150 |
Тапджой | 7295217276740746030 |
Tapjoy (торги) | 4692500501762622178 |
Tencent GDT | 7007906637038700218 |
TripleLift (торги) | 8332676245392738510 |
Реклама Unity | 4970775877303683148 |
Unity Ads (торги) | 7069338991535737586 |
Verve Group (торги) | 5013176581647059185 |
Впон | 1940957084538325905 |
Yieldmo (торги) | 4193081836471107579 |
YieldOne (торги) | 3154533971590234104 |
Закс | 5506531810221735863 |
Награждение пользователя
Важно сбалансировать пользовательский опыт и проверку вознаграждения при принятии решения о том, когда вознаграждать пользователя. Обратные вызовы на стороне сервера могут испытывать задержки перед достижением внешних систем. Поэтому рекомендуемая лучшая практика — использовать обратный вызов на стороне клиента для немедленного вознаграждения пользователя, выполняя проверку всех вознаграждений при получении обратных вызовов на стороне сервера. Такой подход обеспечивает хороший пользовательский опыт, гарантируя при этом действительность предоставленных вознаграждений.
Однако для приложений, где действительность вознаграждения имеет решающее значение (например, вознаграждение влияет на внутриигровую экономику вашего приложения) и задержки в предоставлении вознаграждений приемлемы, ожидание проверенного обратного вызова на стороне сервера может быть наилучшим подходом.
Пользовательские данные
Приложения, которым требуются дополнительные данные в обратных вызовах проверки на стороне сервера, должны использовать функцию пользовательских данных объявлений с вознаграждением. Любое строковое значение, заданное для объекта объявления с вознаграждением, передается в параметр запроса custom_data
обратного вызова SSV. Если не задано никакого значения пользовательских данных, значение параметра запроса custom_data
не будет присутствовать в обратном вызове SSV.
В следующем примере задаются параметры SSV после загрузки вознагражденной рекламы:
Быстрый
RewardedAd.load(with:"AD_UNIT_ID", request: request, completionHandler: { [self] ad, error in if let error != error { rewardedAd = ad let options = ServerSideVerificationOptions() options.customRewardString = "SAMPLE_CUSTOM_DATA_STRING" rewardedAd.serverSideVerificationOptions = options } })
Objective-C
GADRequest *request = [GADRequest request]; [GADRewardedAd loadWithAdUnitID:@"AD_UNIT_ID" request:request completionHandler:^(GADRewardedAd *ad, NSError *error) { if (error) { // Handle Error return; } self.rewardedAd = ad; GADServerSideVerificationOptions *options = [[GADServerSideVerificationOptions alloc] init]; options.customRewardString = @"SAMPLE_CUSTOM_DATA_STRING"; ad.serverSideVerificationOptions = options; }];
Ручная проверка вознагражденных 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 ожидает код ответа
HTTP 200 OK
для обратных вызовов SSV. Если ваш сервер недоступен или не предоставляет ожидаемый ответ, Google попытается повторно отправить обратные вызовы SSV до пяти раз с интервалом в одну секунду. - Как проверить, что обратные вызовы SSV поступают от Google?
- Используйте обратный поиск DNS, чтобы убедиться, что обратные вызовы SSV исходят от Google.