Обратные вызовы проверки на стороне сервера – это запросы URL с параметрами запроса, которые расширяются Google и отправляются Google во внешнюю систему, чтобы уведомить ее о том, что пользователь должен получить вознаграждение за взаимодействие с объявлением с вознаграждением или межстраничным объявлением с вознаграждением. Обратные вызовы для проверки на стороне сервера при показе объявлений с вознаграждением обеспечивают дополнительную защиту от спуфинга клиентских обратных вызовов для предоставления вознаграждений пользователям.
В этом руководстве рассказывается, как проверять обратные вызовы SSV для объявлений с вознаграждением с помощью сторонней криптографической библиотеки Tink Java Apps, чтобы убедиться, что параметры запроса в обратном вызове являются действительными. В этом руководстве используется библиотека Tink, но вы можете выбрать любую стороннюю библиотеку, которая поддерживает ECDSA. Вы также можете проверить свой сервер с помощью инструмента тестирования в интерфейсе AdMob.
Требования
- Включите проверку на стороне сервера для объявлений с вознаграждением в рекламном блоке.
Как использовать RewardedAdsVerifier из библиотеки приложений Tink Java
В хранилище GitHub Tink Java Apps есть вспомогательный класс RewardedAdsVerifier, который позволяет сократить код, необходимый для проверки обратного вызова при показе объявлений с вознаграждением.
Используя этот класс, вы можете проверить URL обратного вызова с помощью следующего кода.
RewardedAdsVerifier verifier = new RewardedAdsVerifier.Builder()
.fetchVerifyingPublicKeysWith(
RewardedAdsVerifier.KEYS_DOWNLOADER_INSTANCE_PROD)
.build();
String rewardUrl = ...;
verifier.verify(rewardUrl);
Если метод verify() выполняется без исключений, URL обратного вызова успешно проверен. В разделе Вознаграждение пользователей описаны рекомендации о том, когда следует вознаграждать пользователей. Подробное описание действий, выполняемых этим классом для проверки обратных вызовов SSV, приведено в разделе Проверка обратных вызовов SSV вручную.
Параметры обратного вызова для проверки на стороне сервера
Обратные вызовы проверки на стороне сервера содержат параметры запроса, описывающие взаимодействие с объявлением с вознаграждением. Ниже приведены названия, описания и примеры значений параметров. Параметры отправляются в алфавитном порядке.
| Название параметра | Описание | Пример значения |
|---|---|---|
| ad_network | Идентификатор источника объявлений, из которого было получено это объявление. Названия источников объявлений, соответствующие значениям идентификаторов, перечислены в разделе Идентификаторы источников объявлений. | 1953547073528090325 |
| ad_unit | Идентификатор рекламного блока AdMob, который использовался для запроса объявления с вознаграждением. | 2747237135 |
| custom_data | Строка пользовательских данных, предоставленная
customData.
Если приложение не передает строку пользовательских данных, значение этого параметра запроса не будет присутствовать в обратном вызове для проверки на стороне сервера. |
SAMPLE_CUSTOM_DATA_STRING |
| key_id | Ключ, который будет использоваться для проверки обратного вызова SSV. Это значение сопоставляется с открытым ключом, предоставленным сервером ключей AdMob. | 1234567890 |
| reward_amount | Размер вознаграждения, указанный в настройках рекламного блока. | 5 |
| reward_item | Предмет вознаграждения, указанный в настройках рекламного блока. | монеты |
| подпись | Подпись для обратного вызова SSV, созданная AdMob. | MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMtmgJHYYW9k2_icM9LFMY |
| временная метка | Временная метка, указывающая, когда пользователь получил вознаграждение (в миллисекундах). | 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 |
| Rise (назначение ставок) | 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 обратного вызова для проверки на стороне сервера. Если значение данных клиентов не задано, то значение параметра запроса custom_data не будет представлено в обратном вызове для проверки на стороне сервера.
В примере ниже показано, как задать настройки проверки после загрузки объявления с вознаграждением.
Замените SAMPLE_CUSTOM_DATA_STRING своими данными.
Если вы хотите задать строку для специальной награды, это нужно сделать до показа объявления.
Проверка вручную для объявлений с вознаграждением
Ниже описаны действия, которые выполняет класс RewardedAdsVerifier для проверки SSV-запроса. Хотя приведенные фрагменты кода написаны на языке Java и используют стороннюю библиотеку Tink, вы можете выполнить эти действия на любом языке программирования, используя любую стороннюю библиотеку, которая поддерживает ECDSA.
Получение открытых ключей
Чтобы проверить обратный вызов для объявления с вознаграждением, вам потребуется открытый ключ, предоставленный 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;
}
Как получить контент для проверки
Последними двумя параметрами запроса в обратных вызовах для проверки на стороне сервера всегда являются signature и key_id, (именно в таком порядке). Остальные параметры запроса указывают на контент, который нужно проверить. Допустим, вы настроили AdMob для отправки обратных вызовов на URL 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"));
Получите подпись и идентификатор ключа из 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, меняются по переменному расписанию. Чтобы проверка обратных вызовов для вознаграждения на стороне сервера работала корректно, открытые ключи не должны кешироваться дольше чем на 24 часа.
- Что произойдет, если сервер будет недоступен?
- Google ожидает код ответа
HTTP 200 OKдля обратных вызовов SSV. Если ваш сервер недоступен или не возвращает ожидаемый ответ, Google попытается отправить обратные вызовы для проверки на стороне сервера до пяти раз с интервалом в одну секунду. - Как проверить, что обратные вызовы SSV приходят от Google?
- Используйте обратный DNS-запрос, чтобы убедиться, что обратные вызовы для проверки на стороне сервера поступают от Google.