Обратные вызовы проверки на стороне сервера — это URL-запросы с параметрами запроса, расширенными Google, которые отправляются Google во внешнюю систему, чтобы уведомить ее о том, что пользователь должен быть вознагражден за взаимодействие с вознагражденной или межстраничной рекламой с вознаграждением. Обратные вызовы SSV (проверка на стороне сервера) с вознаграждением обеспечивают дополнительный уровень защиты от подделки обратных вызовов на стороне клиента для вознаграждения пользователей.
В этом руководстве показано, как проверить вознагражденные обратные вызовы SSV с помощью сторонней криптографической библиотеки Tink Java Apps, чтобы убедиться, что параметры запроса в обратном вызове являются допустимыми значениями. Хотя для целей данного руководства используется Tink, у вас есть возможность использовать любую стороннюю библиотеку, поддерживающую ECDSA . Вы также можете протестировать свой сервер с помощью инструмента тестирования в пользовательском интерфейсе AdMob.
Предварительные условия
- Включите вознагражденную проверку на стороне сервера в своем рекламном блоке.
Используйте RewardedAdsVerifier из библиотеки Java-приложений Tink.
Репозиторий 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 |
key_id | Ключ, который будет использоваться для проверки обратного вызова SSV. Это значение соответствует открытому ключу, предоставленному сервером ключей AdMob . | 1234567890 |
вознаграждение_сумма | Сумма вознаграждения указана в настройках рекламного блока. | 5 |
награда_предмет | Элемент вознаграждения, указанный в настройках рекламного блока. | монеты |
подпись | Подпись для обратного вызова SSV, созданная AdMob. | MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-panNDbSCzWGMtmgJHYYW9k2_icM9LFMY |
временная метка | Временная метка того, когда пользователь был вознагражден, как время эпохи в мс. | 1507770365237823 |
идентификатор_транзакции | Уникальный шестнадцатеричный идентификатор для каждого события предоставления вознаграждения, созданного AdMob. | 18fa792de1bca816048293fc71035638 |
ID пользователя | Идентификатор пользователя, предоставленный SetUserId .Если приложение не предоставляет идентификатор пользователя, этот параметр запроса не будет присутствовать в обратном вызове SSV. | 1234567 |
Идентификаторы источников объявлений
Названия и идентификаторы источников объявлений
Название источника объявлений | Идентификатор источника объявления |
---|---|
Генерация рекламы (торги) | 1477265452970951479 |
Адколония | 15586990674969969776 |
AdColony (торги) | 6895345910719072481 |
АдФалькон | 3528208921554210682 |
Сеть Рекламы в приложении | 5450213213286189855 |
Водопад сети AdMob | 1215381445328257950 |
Аппловин | 1063618907739174004 |
Аппловин (торг) | 1328079684332308356 |
Чартбуст | 2873236629771172317 |
Шоколадная платформа (торги) | 6432849193975106527 |
Пользовательское событие | 18351550913290782395 |
Обмен ДТ* * До 21 сентября 2022 года эта сеть называлась «Fyber Marketplace». | 2179455223494392917 |
Экватив (торг)* * До 12 января 2023 года эта сеть называлась «Смарт-адсервер». | 5970199210771591442 |
Флукт (торги) | 8419777862490735710 |
Шквал | 3376427960656545613 |
Оптоволокно* * Этот источник объявлений используется для исторических отчетов. | 4839637394546996422 |
я-мобильный | 5208827440166355534 |
Улучшение цифровых технологий (назначение ставок) | 159382223051638006 |
Индексная биржа (торги) | 4100650709078789802 |
ИнМоби | 7681903010231960328 |
ИнМоби (торги) | 6325663098072678541 |
Биржа InMobi (торги) | 5264320421916134407 |
ЖелезоИсточник | 6925240245545091930 |
Объявления IronSource (торги) | 1643326773739866623 |
Свинцовый болт | 2899150749497968595 |
Стартовая монетизация* * До 30 января 2023 года эта сеть называлась «Vungle». | 1953547073528090325 |
Монетизация Liftoff (торги)* * До 30 января 2023 года данная сеть называлась «Вунгл (торг)». | 4692500501762622185 |
LG U+AD | 18298738678491729107 |
Рекламная сеть LINE | 3025503711505004547 |
Магнит ДВ+ (торг) | 3993193775968767067 |
Майо | 7505118203095108657 |
майо (торг) | 1343336733822567166 |
Медиа.нет (торги) | 2127936450554446159 |
Медиированные собственные объявления | 6060308706800320801 |
Сеть метааудиторий* * До 6 июня 2022 г. эта сеть называлась «Facebook Audience Network». | 10568273599589928883 |
Сеть метааудитории (торги)* * До 6 июня 2022 года эта сеть называлась «Сеть аудитории Facebook (торги)». | 11198165126854996598 |
Минтеграл | 1357746574408896200 |
Минтеграл (торг) | 6250601289653372374 |
МобФокс (торги) | 3086513548163922365 |
MoPub ( устарело ) | 10872986198578383917 |
моя цель | 8450873672465271579 |
Ненд | 9383070032774777750 |
Некссен (торг)* * До 1 мая 2024 года эта сеть называлась «UnrulyX». | 2831998725945605450 |
OneTag Exchange (торги) | 4873891452523427499 |
OpenX (торги) | 4918705482605678398 |
Пангл | 4069896914521993236 |
Пэнгл (торг) | 3525379893916449117 |
ПабМатик (торги) | 3841544486172445473 |
Кампания по бронированию | 7068401028668408324 |
СК планета | 734341340207269415 |
Долевое участие (торги) | 5247944089976324188 |
Смаато (торг) | 3362360112145450544 |
Соноби (торг) | 3270984106996027150 |
Тапджой | 7295217276740746030 |
Тапджой (торги) | 4692500501762622178 |
Тенсент ГДТ | 7007906637038700218 |
TripleLift (торги) | 8332676245392738510 |
Юнити-реклама | 4970775877303683148 |
Unity Ads (торги) | 7069338991535737586 |
Verve Group (торг) | 5013176581647059185 |
Впон | 1940957084538325905 |
Доходность (торги) | 4193081836471107579 |
YieldOne (торги) | 3154533971590234104 |
Цукс | 5506531810221735863 |
Награждение пользователя
При принятии решения о том, когда вознаграждать пользователя, важно сбалансировать пользовательский опыт и проверку вознаграждения. Обратные вызовы на стороне сервера могут испытывать задержки перед достижением внешних систем. Поэтому рекомендуется использовать обратный вызов на стороне клиента для немедленного вознаграждения пользователя, одновременно выполняя проверку всех вознаграждений при получении обратных вызовов на стороне сервера. Такой подход обеспечивает хороший пользовательский опыт, гарантируя при этом действительность предоставленных вознаграждений.
Однако для приложений, где действительность вознаграждения имеет решающее значение (например, вознаграждение влияет на внутриигровую экономику вашего приложения) и задержки в предоставлении вознаграждений приемлемы, лучшим подходом может быть ожидание проверенного обратного вызова на стороне сервера.
Пользовательские данные
Приложения, которым требуются дополнительные данные в обратных вызовах проверки на стороне сервера, должны использовать функцию пользовательских данных объявлений с вознаграждением. Любое строковое значение, установленное для объекта рекламы с вознаграждением, передается в параметр запроса custom_data
обратного вызова SSV. Если значение настраиваемых данных не установлено, значение параметра запроса custom_data
не будет присутствовать в обратном вызове SSV.
В следующем примере кода показано, как настроить параметры SSV после загрузки объявления с вознаграждением.
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);
});
}
Если вы хотите установить специальную строку вознаграждения, это необходимо сделать до показа объявления.
Ручная проверка награжденных 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.
Обратные вызовы проверки на стороне сервера — это URL-запросы с параметрами запроса, расширенными Google, которые отправляются Google во внешнюю систему, чтобы уведомить ее о том, что пользователь должен быть вознагражден за взаимодействие с вознагражденной или межстраничной рекламой с вознаграждением. Обратные вызовы SSV (проверка на стороне сервера) с вознаграждением обеспечивают дополнительный уровень защиты от подделки обратных вызовов на стороне клиента для вознаграждения пользователей.
В этом руководстве показано, как проверить вознагражденные обратные вызовы SSV с помощью сторонней криптографической библиотеки Tink Java Apps, чтобы убедиться, что параметры запроса в обратном вызове являются допустимыми значениями. Хотя для целей данного руководства используется Tink, у вас есть возможность использовать любую стороннюю библиотеку, поддерживающую ECDSA . Вы также можете протестировать свой сервер с помощью инструмента тестирования в пользовательском интерфейсе AdMob.
Предварительные условия
- Включите вознагражденную проверку на стороне сервера в своем рекламном блоке.
Используйте RewardedAdsVerifier из библиотеки Java-приложений Tink.
Репозиторий 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 |
key_id | Ключ, который будет использоваться для проверки обратного вызова SSV. Это значение соответствует открытому ключу, предоставленному сервером ключей AdMob . | 1234567890 |
вознаграждение_сумма | Сумма вознаграждения указана в настройках рекламного блока. | 5 |
награда_предмет | Элемент вознаграждения, указанный в настройках рекламного блока. | монеты |
подпись | Подпись для обратного вызова SSV, созданная AdMob. | MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-panNDbSCzWGMtmgJHYYW9k2_icM9LFMY |
временная метка | Временная метка того, когда пользователь был вознагражден, как время эпохи в мс. | 1507770365237823 |
идентификатор_транзакции | Уникальный шестнадцатеричный идентификатор для каждого события предоставления вознаграждения, созданного AdMob. | 18fa792de1bca816048293fc71035638 |
ID пользователя | Идентификатор пользователя, предоставленный SetUserId .Если приложение не предоставляет идентификатор пользователя, этот параметр запроса не будет присутствовать в обратном вызове SSV. | 1234567 |
Идентификаторы источников объявлений
Названия и идентификаторы источников объявлений
Название источника объявлений | Идентификатор источника объявления |
---|---|
Генерация рекламы (торги) | 1477265452970951479 |
Адколония | 15586990674969969776 |
AdColony (торги) | 6895345910719072481 |
АдФалькон | 3528208921554210682 |
Сеть Рекламы в приложении | 5450213213286189855 |
Водопад сети AdMob | 1215381445328257950 |
Аппловин | 1063618907739174004 |
Аппловин (торг) | 1328079684332308356 |
Чартбуст | 2873236629771172317 |
Шоколадная платформа (торги) | 6432849193975106527 |
Пользовательское событие | 18351550913290782395 |
Обмен ДТ* * До 21 сентября 2022 года эта сеть называлась «Fyber Marketplace». | 2179455223494392917 |
Экватив (торг)* * До 12 января 2023 года эта сеть называлась «Смарт-адсервер». | 5970199210771591442 |
Флукт (торги) | 8419777862490735710 |
Шквал | 3376427960656545613 |
Оптоволокно* * Этот источник объявлений используется для исторических отчетов. | 4839637394546996422 |
я-мобильный | 5208827440166355534 |
Улучшение цифровых технологий (назначение ставок) | 159382223051638006 |
Индексная биржа (торги) | 4100650709078789802 |
ИнМоби | 7681903010231960328 |
ИнМоби (торги) | 6325663098072678541 |
Биржа InMobi (торги) | 5264320421916134407 |
ЖелезоИсточник | 6925240245545091930 |
Объявления IronSource (торги) | 1643326773739866623 |
Свинцовый болт | 2899150749497968595 |
Стартовая монетизация* * До 30 января 2023 года эта сеть называлась «Vungle». | 1953547073528090325 |
Монетизация Liftoff (торги)* * До 30 января 2023 года данная сеть называлась «Вунгл (торг)». | 4692500501762622185 |
LG U+AD | 18298738678491729107 |
Рекламная сеть LINE | 3025503711505004547 |
Магнит ДВ+ (торг) | 3993193775968767067 |
Майо | 7505118203095108657 |
майо (торг) | 1343336733822567166 |
Медиа.нет (торги) | 2127936450554446159 |
Медиированные собственные объявления | 6060308706800320801 |
Сеть метааудиторий* * До 6 июня 2022 г. эта сеть называлась «Сеть аудитории Facebook». | 10568273599589928883 |
Сеть метааудитории (торги)* * До 6 июня 2022 года эта сеть называлась «Сеть аудитории Facebook (торги)». | 11198165126854996598 |
Минтеграл | 1357746574408896200 |
Минтеграл (торг) | 6250601289653372374 |
МобФокс (торги) | 3086513548163922365 |
MoPub ( устарело ) | 10872986198578383917 |
моя цель | 8450873672465271579 |
Ненд | 9383070032774777750 |
Некссен (торг)* * До 1 мая 2024 года эта сеть называлась «UnrulyX». | 2831998725945605450 |
OneTag Exchange (торги) | 4873891452523427499 |
OpenX (торги) | 4918705482605678398 |
Пангл | 4069896914521993236 |
Пэнгл (торг) | 3525379893916449117 |
ПабМатик (торги) | 3841544486172445473 |
Кампания по бронированию | 7068401028668408324 |
СК планета | 734341340207269415 |
Долевое участие (торги) | 5247944089976324188 |
Смаато (торг) | 3362360112145450544 |
Соноби (торг) | 3270984106996027150 |
Тапджой | 7295217276740746030 |
Тапджой (торги) | 4692500501762622178 |
Тенсент ГДТ | 7007906637038700218 |
TripleLift (торги) | 8332676245392738510 |
Юнити-реклама | 4970775877303683148 |
Unity Ads (торги) | 7069338991535737586 |
Verve Group (торг) | 5013176581647059185 |
Впон | 1940957084538325905 |
Доходность (торги) | 4193081836471107579 |
YieldOne (торги) | 3154533971590234104 |
Цукс | 5506531810221735863 |
Награждение пользователя
При принятии решения о том, когда вознаграждать пользователя, важно сбалансировать пользовательский опыт и проверку вознаграждения. Обратные вызовы на стороне сервера могут испытывать задержки перед достижением внешних систем. Поэтому рекомендуется использовать обратный вызов на стороне клиента для немедленного вознаграждения пользователя, одновременно выполняя проверку всех вознаграждений при получении обратных вызовов на стороне сервера. Такой подход обеспечивает хороший пользовательский опыт, обеспечивая при этом действительность предоставленных вознаграждений.
Однако для приложений, где действительность вознаграждения имеет решающее значение (например, вознаграждение влияет на внутриигровую экономику вашего приложения) и задержки в предоставлении вознаграждений приемлемы, лучшим подходом может быть ожидание проверенного обратного вызова на стороне сервера.
Пользовательские данные
Приложения, которым требуются дополнительные данные в обратных вызовах проверки на стороне сервера, должны использовать функцию пользовательских данных объявлений с вознаграждением. Любое строковое значение, установленное для объекта рекламы с вознаграждением, передается в параметр запроса custom_data
обратного вызова SSV. Если значение настраиваемых данных не установлено, значение параметра запроса custom_data
не будет присутствовать в обратном вызове SSV.
В следующем примере кода показано, как настроить параметры SSV после загрузки объявления с вознаграждением.
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);
});
}
Если вы хотите установить специальную строку вознаграждения, это необходимо сделать до показа объявления.
Ручная проверка награжденных 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.
Обратные вызовы проверки на стороне сервера — это URL-запросы с параметрами запроса, расширенными Google, которые отправляются Google во внешнюю систему, чтобы уведомить ее о том, что пользователь должен быть вознагражден за взаимодействие с вознагражденной или межстраничной рекламой с вознаграждением. Обратные вызовы SSV (проверка на стороне сервера) с вознаграждением обеспечивают дополнительный уровень защиты от подделки обратных вызовов на стороне клиента для вознаграждения пользователей.
В этом руководстве показано, как проверить вознагражденные обратные вызовы SSV с помощью сторонней криптографической библиотеки Tink Java Apps, чтобы убедиться, что параметры запроса в обратном вызове являются допустимыми значениями. Хотя для целей данного руководства используется Tink, у вас есть возможность использовать любую стороннюю библиотеку, поддерживающую ECDSA . Вы также можете протестировать свой сервер с помощью инструмента тестирования в пользовательском интерфейсе AdMob.
Предварительные условия
- Включите вознагражденную проверку на стороне сервера в своем рекламном блоке.
Используйте RewardedAdsVerifier из библиотеки Java-приложений Tink.
Репозиторий 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 |
key_id | Ключ, который будет использоваться для проверки обратного вызова SSV. Это значение соответствует открытому ключу, предоставленному сервером ключей AdMob . | 1234567890 |
вознаграждение_сумма | Сумма вознаграждения указана в настройках рекламного блока. | 5 |
награда_предмет | Элемент вознаграждения, указанный в настройках рекламного блока. | монеты |
подпись | Подпись для обратного вызова SSV, созданная AdMob. | MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-panNDbSCzWGMtmgJHYYW9k2_icM9LFMY |
временная метка | Временная метка того, когда пользователь был вознагражден, как время эпохи в мс. | 1507770365237823 |
идентификатор_транзакции | Уникальный шестнадцатеричный идентификатор для каждого события предоставления вознаграждения, созданного AdMob. | 18fa792de1bca816048293fc71035638 |
ID пользователя | Идентификатор пользователя, предоставленный SetUserId .Если приложение не предоставляет идентификатор пользователя, этот параметр запроса не будет присутствовать в обратном вызове SSV. | 1234567 |
Идентификаторы источников объявлений
Названия и идентификаторы источников объявлений
Название источника объявлений | Идентификатор источника объявления |
---|---|
Генерация рекламы (торги) | 1477265452970951479 |
Адколония | 15586990674969969776 |
AdColony (торги) | 6895345910719072481 |
АдФалькон | 3528208921554210682 |
Сеть Рекламы в приложении | 5450213213286189855 |
Водопад сети AdMob | 1215381445328257950 |
Аппловин | 1063618907739174004 |
Аппловин (торг) | 1328079684332308356 |
Чартбуст | 2873236629771172317 |
Шоколадная платформа (торги) | 6432849193975106527 |
Пользовательское событие | 18351550913290782395 |
Обмен ДТ* * До 21 сентября 2022 года эта сеть называлась «Fyber Marketplace». | 2179455223494392917 |
Экватив (торг)* * До 12 января 2023 года эта сеть называлась «Смарт-адсервер». | 5970199210771591442 |
Флукт (торги) | 8419777862490735710 |
Шквал | 3376427960656545613 |
Оптоволокно* * Этот источник объявлений используется для исторических отчетов. | 4839637394546996422 |
я-мобильный | 5208827440166355534 |
Улучшение цифровых технологий (назначение ставок) | 159382223051638006 |
Индексная биржа (торги) | 4100650709078789802 |
ИнМоби | 7681903010231960328 |
ИнМоби (торги) | 6325663098072678541 |
Биржа InMobi (торги) | 5264320421916134407 |
ЖелезоИсточник | 6925240245545091930 |
Объявления IronSource (торги) | 1643326773739866623 |
Свинцовый болт | 2899150749497968595 |
Стартовая монетизация* * До 30 января 2023 года эта сеть называлась «Vungle». | 1953547073528090325 |
Монетизация Liftoff (торги)* * До 30 января 2023 года данная сеть называлась «Вунгл (торг)». | 4692500501762622185 |
LG U+AD | 18298738678491729107 |
Рекламная сеть LINE | 3025503711505004547 |
Магнит ДВ+ (торг) | 3993193775968767067 |
Майо | 7505118203095108657 |
майо (торг) | 1343336733822567166 |
Медиа.нет (торги) | 2127936450554446159 |
Медиированные собственные объявления | 6060308706800320801 |
Сеть метааудиторий* * До 6 июня 2022 г. эта сеть называлась «Сеть аудитории Facebook». | 10568273599589928883 |
Сеть метааудитории (торги)* * До 6 июня 2022 года эта сеть называлась «Сеть аудитории Facebook (торги)». | 11198165126854996598 |
Минтеграл | 1357746574408896200 |
Минтеграл (торг) | 6250601289653372374 |
МобФокс (торги) | 3086513548163922365 |
MoPub ( устарело ) | 10872986198578383917 |
моя цель | 8450873672465271579 |
Ненд | 9383070032774777750 |
Некссен (торг)* * До 1 мая 2024 года эта сеть называлась «UnrulyX». | 2831998725945605450 |
OneTag Exchange (торги) | 4873891452523427499 |
OpenX (торги) | 4918705482605678398 |
Пангл | 4069896914521993236 |
Пэнгл (торг) | 3525379893916449117 |
ПабМатик (торги) | 3841544486172445473 |
Кампания по бронированию | 7068401028668408324 |
СК планета | 734341340207269415 |
Долевое участие (торги) | 5247944089976324188 |
Смаато (торг) | 3362360112145450544 |
Соноби (торг) | 3270984106996027150 |
Тапджой | 7295217276740746030 |
Тапджой (торги) | 4692500501762622178 |
Тенсент ГДТ | 7007906637038700218 |
TripleLift (торги) | 8332676245392738510 |
Юнити-реклама | 4970775877303683148 |
Unity Ads (торги) | 7069338991535737586 |
Verve Group (торг) | 5013176581647059185 |
Впон | 1940957084538325905 |
Доходность (торги) | 4193081836471107579 |
YieldOne (торги) | 3154533971590234104 |
Цукс | 5506531810221735863 |
Награждение пользователя
При принятии решения о том, когда вознаграждать пользователя, важно сбалансировать пользовательский опыт и проверку вознаграждения. Обратные вызовы на стороне сервера могут испытывать задержки перед достижением внешних систем. Поэтому рекомендуется использовать обратный вызов на стороне клиента для немедленного вознаграждения пользователя, одновременно выполняя проверку всех вознаграждений при получении обратных вызовов на стороне сервера. Такой подход обеспечивает хороший пользовательский опыт, обеспечивая при этом действительность предоставленных вознаграждений.
Однако для приложений, где достоверность вознаграждения имеет решающее значение (например, вознаграждение влияет на внутриигровую экономику вашего приложения) и задержки в предоставлении вознаграждений приемлемы, лучшим подходом может быть ожидание проверенного обратного вызова на стороне сервера.
Пользовательские данные
Приложения, которым требуются дополнительные данные в обратных вызовах проверки на стороне сервера, должны использовать функцию пользовательских данных объявлений с вознаграждением. Любое строковое значение, установленное для объекта рекламы с вознаграждением, передается в параметр запроса custom_data
обратного вызова SSV. Если значение настраиваемых данных не установлено, значение параметра запроса custom_data
не будет присутствовать в обратном вызове SSV.
В следующем примере кода показано, как настроить параметры SSV после загрузки объявления с вознаграждением.
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);
});
}
Если вы хотите установить специальную строку вознаграждения, это необходимо сделать до показа объявления.
Ручная проверка награжденных 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.
Обратные вызовы проверки на стороне сервера — это URL-запросы с параметрами запроса, расширенными Google, которые отправляются Google во внешнюю систему, чтобы уведомить ее о том, что пользователь должен быть вознагражден за взаимодействие с вознагражденной или межстраничной рекламой с вознаграждением. Обратные вызовы SSV (проверка на стороне сервера) с вознаграждением обеспечивают дополнительный уровень защиты от подделки обратных вызовов на стороне клиента для вознаграждения пользователей.
В этом руководстве показано, как проверить вознагражденные обратные вызовы SSV с помощью сторонней криптографической библиотеки Tink Java Apps, чтобы убедиться, что параметры запроса в обратном вызове являются допустимыми значениями. Хотя для целей данного руководства используется Tink, у вас есть возможность использовать любую стороннюю библиотеку, поддерживающую ECDSA . Вы также можете протестировать свой сервер с помощью инструмента тестирования в пользовательском интерфейсе AdMob.
Предварительные условия
- Включите вознагражденную проверку на стороне сервера в своем рекламном блоке.
Используйте RewardedAdsVerifier из библиотеки Java-приложений Tink.
Репозиторий 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 |
ad_unit | Идентификатор рекламы Admob, который использовался для запроса вознагражденного объявления. | 2747237135 |
key_id | Ключ, который будет использоваться для проверки обратного вызова SSV. Это значение отображается с открытым ключом, предоставленным сервером ключей Admob . | 1234567890 |
reward_amount | Сумма вознаграждения, как указано в настройках блока рекламы. | 5 |
reward_item | Награда, как указано в настройках блока рекламы. | монеты |
подпись | Подпись для обратного вызова SSV, сгенерированного Admob. | Meuciqcljs_s4ia_sn06hqzew7wc3nhzi4rlw3qv0oo-6aiydqiggjeh-rzkreo-pandbsczwgmtmgjhyyw9k2_icm9lfmy |
временная метка | Временная метка, когда пользователь был вознагражден как время эпоха в MS. | 1507770365237823 |
transaction_id | Уникальный HEX -закодированный идентификатор для каждого мероприятия гранта вознаграждения, сгенерированного Admob. | 18FA792DE1BCA816048293FC71035638 |
ID пользователя | Пользовательский идентификатор, как это предоставлено SetUserId .Если приложение не предоставляется идентификатор пользователя, этот параметр запроса не будет присутствовать в обратном вызове SSV. | 1234567 |
Идентификаторы источника рекламы
Имена источников рекламы и идентификаторы
Имя источника рекламы | Идентификатор источника рекламы |
---|---|
Генерация рекламы (торги) | 1477265452970951479 |
Адколония | 15586990674969969776 |
Адколония (торги) | 6895345910719072481 |
Adfalcon | 3528208921554210682 |
Admob Network | 5450213213286189855 |
Admob Network Waterfall | 1215381445328257950 |
Applevin | 1063618907739174004 |
Applovin (торги) | 1328079684332308356 |
Chartboost | 2873236629771172317 |
Шоколадная платформа (торги) | 6432849193975106527 |
Пользовательское событие | 18351550913290782395 |
DT Exchange* * До 21 сентября 2022 года эта сеть называлась «Fyber Marketplace». | 2179455223494392917 |
Equativ (ткани)* * До 12 января 2023 года эта сеть называлась «Smart Adserver». | 5970199210771591442 |
Колебаться (торги) | 8419777862490735710 |
Шквал | 3376427960656545613 |
Фибер* * Этот источник рекламы используется для исторической отчетности. | 4839637394546996422 |
I-Mobile | 5208827440166355534 |
Улучшить цифровые (ставки) | 159382223051638006 |
Индексная обмена (торги) | 4100650709078789802 |
Inmobi | 7681903010231960328 |
Inmobi (торги) | 6325663098072678541 |
INMOBI Exchange (торги) | 5264320421916134407 |
Ironsource | 6925240245545091930 |
Aronsource Ads (торги) | 1643326773739866623 |
Свинцовый болт | 2899150749497968595 |
Поднять монетизируется* * До 30 января 2023 года эта сеть называлась «Vungle». | 1953547073528090325 |
Liftoff Monetize (ткани)* * До 30 января 2023 года эта сеть называлась «Vungle (ставки)». | 4692500501762622185 |
LG U+AD | 18298738678491729107 |
Линейная реклама сеть | 3025503711505004547 |
Magnite DV+ (торги) | 3993193775968767067 |
Майо | 7505118203095108657 |
Maio (торги) | 1343336733822567166 |
Media.net (ткани) | 2127936450554446159 |
Опосредованные дома рекламы | 6060308706800320801 |
Мета -аудитория сеть* * До 6 июня 2022 года эта сеть называлась «Сеть аудитории Facebook». | 10568273599589928883 |
Мета -аудитория сеть (ставки)* * До 6 июня 2022 года эта сеть называлась «Сеть аудитории Facebook (ставки)». | 11198165126854996598 |
Mintegral | 1357746574408896200 |
Mintegral (торги) | 6250601289653372374 |
Mobfox (торги) | 3086513548163922365 |
Mopub ( устаревший ) | 10872986198578383917 |
Mytarget | 8450873672465271579 |
Нерешительный | 9383070032774777750 |
Nexxen (торги)* * До 1 мая 2024 года эта сеть называлась «Unreulyx». | 2831998725945605450 |
Обмен Onetag (торги) | 4873891452523427499 |
Openx (торги) | 4918705482605678398 |
Панг | 4069896914521993236 |
Пангл (Торга) | 3525379893916449117 |
Pubmatic (ткани) | 3841544486172445473 |
Кампания резервирования | 7068401028668408324 |
SK Planet | 734341340207269415 |
Sharethrough (торги) | 5247944089976324188 |
Smaato (торги) | 3362360112145450544 |
Соноби (торги) | 3270984106996027150 |
Tapjoy | 7295217276740746030 |
Tapjoy (ткани) | 4692500501762622178 |
Tencent GDT | 7007906637038700218 |
TripleLift (торги) | 8332676245392738510 |
Unity Ads | 4970775877303683148 |
Реклама Unity (торги) | 7069338991535737586 |
Verve Group (торги) | 5013176581647059185 |
Vpon | 1940957084538325905 |
Доходность (торги) | 4193081836471107579 |
Доход (торги) | 3154533971590234104 |
Зукс | 5506531810221735863 |
Вознаграждение пользователя
Важно сбалансировать пользовательский опыт и проверку вознаграждения при принятии решения о том, когда поощрять пользователя. Образцы на стороне сервера могут испытывать задержки перед достижением внешних систем. Таким образом, рекомендуемая лучшая практика состоит в том, чтобы использовать обратный вызов на стороне клиента, чтобы немедленно поощрять пользователя, выполняя валидацию во всех вознаграждениях при получении обратных вызовов на стороне сервера. Этот подход обеспечивает хороший пользовательский опыт при обеспечении достоверности выданных вознаграждений.
Однако для приложений, где достоверность вознаграждения имеет решающее значение (например, вознаграждение влияет на внутреннюю экономику вашего приложения) и задержки при предоставлении вознаграждений являются приемлемыми, ожидание проверенного обратного вызова на стороне сервера может быть лучшим подходом.
Пользовательские данные
Приложения, которые требуют дополнительных данных в проверке проверки на стороне сервера, должны использовать функцию пользовательских данных вознагражденной рекламы. Любое строковое значение, установленное на вознагражденном объекте AD, передается в параметр запроса custom_data
обратного вызова SSV. Если не установлено пользовательское значение данных, значение параметра запроса custom_data
не присутствует в обратном вызове SSV.
Следующий образец кода демонстрирует, как установить параметры SSV после загрузки вознаграждения.
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);
});
}
Если вы хотите установить пользовательскую строку вознаграждения, вы должны сделать это, прежде чем показывать объявление.
Ручная проверка вознагражденного SSV
Шаги, выполненные классом RewardedAdsVerifier
для проверки вознагражденного SSV, описаны ниже. Хотя включенные фрагменты кода находятся в Java и используют стороннюю библиотеку Tink, эти шаги могут быть реализованы вами на языке по вашему выбору, используя любую стороннюю библиотеку, которая поддерживает ECDSA .
Принесите общественные ключи
Чтобы проверить вознагражденный обратный вызов SSV, вам нужен открытый ключ, предоставленный Admob.
Список общедоступных ключей, которые будут использоваться для проверки вознагражденных обратных вызовов SSV, можно получить с сервера Admob Key . Список публичных ключей предоставляется в качестве представления 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 Key?
- Мы рекомендуем вам кэшировать открытый ключ, предоставленный сервером Admob, чтобы уменьшить количество операций, необходимых для проверки обратных вызовов SSV. Тем не менее, обратите внимание, что общественные ключи регулярно вращаются и не должны кэшироваться в течение более 24 часов.
- Как часто вращаются общедоступные ключи, предоставляемые сервером Admob Key?
- Общественные ключи, предоставленные сервером ключей Admob, вращаются по графику переменной. Чтобы гарантировать, что проверка обратных вызовов SSV продолжает работать так, как и предполагалось, общественные ключи не должны кэшироваться в течение более 24 часов.
- Что произойдет, если мой сервер не может быть достигнут?
- Google ожидает кода ответа на статус успеха
HTTP 200 OK
для вызовов SSV. Если ваш сервер не может быть достигнут или не предоставит ожидаемый ответ, Google повторно ограничен на отправку обратных вызовов SSV до пяти раз с интервалами в одну секунду. - Как я могу проверить, что обратные вызовы SSV поступают из Google?
- Используйте обратный поиск DNS, чтобы убедиться, что обратные вызовы SSV происходят из Google.