Wywołania zwrotne w ramach weryfikacji po stronie serwera to żądania URL z parametrami zapytania rozszerzonymi przez Google, które są wysyłane przez Google do systemu zewnętrznego, aby powiadomić go, że użytkownik powinien otrzymać nagrodę za interakcję z reklamą z nagrodą lub reklamą pełnoekranową z nagrodą. Wywołania zwrotne SSV (weryfikacja po stronie serwera) w przypadku reklam z nagrodą zapewniają dodatkową warstwę ochrony przed fałszowaniem wywołań zwrotnych po stronie klienta w celu przyznawania nagród użytkownikom.
Z tego przewodnika dowiesz się, jak weryfikować wywołania zwrotne SSV dotyczące reklam z nagrodą za pomocą biblioteki kryptograficznej Tink Java Apps, aby mieć pewność, że parametry zapytania w wywołaniu zwrotnym mają prawidłowe wartości. W tym przewodniku używamy biblioteki Tink, ale możesz użyć dowolnej biblioteki innej firmy, która obsługuje ECDSA. Możesz też przetestować serwer za pomocą narzędzia do testowania w interfejsie AdMob.
Wymagania wstępne
- Włącz weryfikację po stronie serwera w przypadku reklam z nagrodą w jednostce reklamowej.
Używanie klasy RewardedAdsVerifier z biblioteki Tink Java Apps
Repozytorium GitHub Tink Java Apps zawiera RewardedAdsVerifier klasę pomocniczą, która zmniejsza ilość kodu potrzebnego do zweryfikowania wywołania zwrotnego SSV z nagrodą.
Użycie tej klasy umożliwia weryfikację adresu URL wywołania zwrotnego za pomocą tego kodu.
RewardedAdsVerifier verifier = new RewardedAdsVerifier.Builder()
.fetchVerifyingPublicKeysWith(
RewardedAdsVerifier.KEYS_DOWNLOADER_INSTANCE_PROD)
.build();
String rewardUrl = ...;
verifier.verify(rewardUrl);
Jeśli metoda verify() zostanie wykonana bez zgłaszania wyjątku, adres URL wywołania zwrotnego zostanie zweryfikowany. W sekcji Nagradzanie użytkownika znajdziesz szczegółowe informacje o sprawdzonych metodach dotyczących tego, kiedy należy nagradzać użytkowników. Szczegółowe informacje o czynnościach wykonywanych przez tę klasę w celu weryfikacji wywołań zwrotnych SSV w przypadku reklam z nagrodą znajdziesz w sekcji Ręczna weryfikacja SSV w przypadku reklam z nagrodą.
Parametry wywołania zwrotnego SSV
Wywołania zwrotne w ramach weryfikacji po stronie serwera zawierają parametry zapytania opisujące interakcję z reklamą z nagrodą. Poniżej znajdziesz nazwy parametrów, ich opisy i przykładowe wartości. Parametry są wysyłane w kolejności alfabetycznej.
| Nazwa parametru | Opis | Przykładowa wartość |
|---|---|---|
| ad_network | Identyfikator źródła reklam, które wyświetliło tę reklamę. Nazwy źródeł reklam odpowiadające wartościom identyfikatorów są podane w sekcji Identyfikatory źródeł reklam. | 1953547073528090325 |
| ad_unit | Identyfikator jednostki reklamowej AdMob, który został użyty do wysłania żądania reklamy z nagrodą. | 2747237135 |
| custom_data | Niestandardowy ciąg danych podany przez
customData.
Jeśli aplikacja nie podaje niestandardowego ciągu danych, ten parametr zapytania nie będzie obecny w wywołaniu zwrotnym SSV. |
SAMPLE_CUSTOM_DATA_STRING |
| key_id | Klucz używany do weryfikacji wywołania zwrotnego SSV. Ta wartość jest mapowana na klucz publiczny udostępniany przez serwer kluczy AdMob. | 1234567890 |
| reward_amount | Kwota nagrody określona w ustawieniach jednostki reklamowej. | 5 |
| reward_item | Przedmiot nagrody określony w ustawieniach jednostki reklamowej. | monety |
| podpis | Podpis wywołania zwrotnego SSV wygenerowany przez AdMob. | MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMtmgJHYYW9k2_icM9LFMY |
| sygnatura czasowa | Sygnatura czasowa momentu przyznania użytkownikowi nagrody w formacie czasu epoki w milisekundach. | 1507770365237823 |
| transaction_id | Unikalny identyfikator zakodowany w systemie szesnastkowym dla każdego zdarzenia przyznania nagrody wygenerowanego przez AdMob. | 18fa792de1bca816048293fc71035638 |
| user_id | Identyfikator użytkownika podany przez userId.
Jeśli aplikacja nie podaje identyfikatora użytkownika, ten parametr zapytania nie będzie obecny w wywołaniu zwrotnym SSV. |
1234567 |
Identyfikatory źródła reklam
Nazwy i identyfikatory źródeł reklam
| Nazwa źródła reklamy | Identyfikator źródła reklamy |
|---|---|
| Ad Generation (bidding) | 1477265452970951479 |
| Sieć AdMob | 5450213213286189855 |
| Kaskada sieci AdMob | 1215381445328257950 |
| AppLovin | 1063618907739174004 |
| AppLovin (określanie stawek) | 1328079684332308356 |
| Bidease (określanie stawek) | 3670825090829827805 |
| BidMachine (określanie stawek) | 7943972370566394673 |
| Chartboost | 2873236629771172317 |
| Chocolate Platform (licytowanie) | 6432849193975106527 |
| Zdarzenie niestandardowe | 18351550913290782395 |
| DT Exchange* * Przed 21 września 2022 r. ta sieć nosiła nazwę „Fyber Marketplace”. | 2179455223494392917 |
| DT Exchange (określanie stawek) | 8189833498765234879 |
| Equativ (określanie stawek)* * Przed 12 stycznia 2023 r. ta sieć nazywała się „Smart Adserver”. | 5970199210771591442 |
| Fluct (określanie stawek) | 8419777862490735710 |
| i-mobile | 5208827440166355534 |
| Improve Digital (określanie stawek) | 159382223051638006 |
| Index Exchange (ustalanie stawek) | 4100650709078789802 |
| InMobi | 7681903010231960328 |
| InMobi (SDK) (określanie stawek) | 8468954295581492586 |
| InMobi Exchange (określanie stawek) | 5264320421916134407 |
| ironSource Ads | 6925240245545091930 |
| ironSource Ads (licytowanie) | 1643326773739866623 |
| Liftoff Monetize* * Przed 30 stycznia 2023 r. ta sieć nosiła nazwę „Vungle”. | 1953547073528090325 |
| Liftoff Monetize (licytowanie)* * Przed 30 stycznia 2023 r. ta sieć nosiła nazwę „Vungle (licytowanie)”. | 4692500501762622185 |
| LY Ads Network | 3025503711505004547 |
| LY Ads Network (określanie stawek) | 2615812619460460513 |
| Magnite DV+ (określanie stawek) | 3993193775968767067 |
| maio | 7505118203095108657 |
| Media.net (określanie stawek) | 2127936450554446159 |
| Zapośredniczone autoreklamy | 6060308706800320801 |
| Meta Audience Network* * Przed 6 czerwca 2022 r. ta sieć nosiła nazwę „Facebook Audience Network”. | 10568273599589928883 |
| Meta Audience Network (określanie stawek)* * Przed 6 czerwca 2022 r. ta sieć nosiła nazwę „Facebook Audience Network (licytowanie)”. | 11198165126854996598 |
| Mintegral | 1357746574408896200 |
| Mintegral (określanie stawek) | 6250601289653372374 |
| Mobfox (określanie stawek) | 3086513548163922365 |
| MobileFuse (określanie stawek) | 7303547408604090310 |
| Moloco Ads SDK (określanie stawek) | 8267622065755668722 |
| myTarget | 8450873672465271579 |
| Nativo (określanie stawek) | 3240503836211327896 |
| Nexxen (licytowanie)* * Przed 1 maja 2024 r. ta sieć nosiła nazwę „UnrulyX”. | 2831998725945605450 |
| OneTag Exchange (określanie stawek) | 4873891452523427499 |
| OpenX (określanie stawek) | 4918705482605678398 |
| Pangle | 4069896914521993236 |
| Pakiet SDK Pangle KR (określanie stawek) | 12171279046073404914 |
| Pakiet SDK Pangle ROW (określanie stawek) | 3525379893916449117 |
| Pangle US SDK (określanie stawek) | 15999446638585856012 |
| PubMatic (określanie stawek) | 3841544486172445473 |
| PubMatic OpenWrap SDK | 7702975372504485373 |
| PubMatic OpenWrap SDK (określanie stawek) | 1234567890123456789 |
| Kampania z rezerwacją | 7068401028668408324 |
| Wzrost (licytacja) | 6816468518946650043 |
| Sharethrough (określanie stawek) | 5247944089976324188 |
| Smaato (określanie stawek) | 3362360112145450544 |
| Sonobi (określanie stawek) | 3270984106996027150 |
| TripleLift (określanie stawek) | 8332676245392738510 |
| Unity Ads | 4970775877303683148 |
| Unity Ads (określanie stawek) | 7069338991535737586 |
| Verve Group (określanie stawek) | 5013176581647059185 |
| Vpon | 1940957084538325905 |
| Yieldmo (określanie stawek) | 4193081836471107579 |
| YieldOne (określanie stawek) | 3154533971590234104 |
| Zucks | 5506531810221735863 |
Nagradzanie użytkownika
Decydując, kiedy przyznać użytkownikowi nagrodę, musisz zachować równowagę między wygodą użytkowników a weryfikacją nagród. Wywołania zwrotne po stronie serwera mogą być opóźnione, zanim dotrą do systemów zewnętrznych. Dlatego zalecamy używanie wywołania zwrotnego po stronie klienta, aby natychmiast przyznawać użytkownikowi nagrodę, a jednocześnie weryfikować wszystkie nagrody po otrzymaniu wywołań zwrotnych po stronie serwera. Takie podejście zapewnia użytkownikom wygodę, a jednocześnie gwarantuje ważność przyznanych nagród.
W przypadku aplikacji, w których ważność nagrody ma kluczowe znaczenie (np. nagroda wpływa na ekonomię w grze), a opóźnienia w przyznawaniu nagród są dopuszczalne, najlepszym rozwiązaniem może być oczekiwanie na zweryfikowane wywołanie zwrotne po stronie serwera.
Dane niestandardowe
Aplikacje, które wymagają dodatkowych danych w wywołaniach zwrotnych weryfikacji po stronie serwera, powinny korzystać z funkcji danych niestandardowych w reklamach z nagrodą. Każda wartość ciągu znaków ustawiona w obiekcie reklamy z nagrodą jest przekazywana do parametru zapytania custom_data wywołania zwrotnego SSV. Jeśli nie ustawisz wartości danych niestandardowych, wartość parametru zapytania custom_data nie będzie obecna w wywołaniu zwrotnym SSV.
W tym przykładzie opcje SSV są ustawiane po wczytaniu reklamy z nagrodą:
Zastąp SAMPLE_CUSTOM_DATA_STRING swoimi danymi niestandardowymi.
Jeśli chcesz ustawić niestandardowy ciąg znaków nagrody, musisz to zrobić przed wyświetleniem reklamy.
Ręczna weryfikacja SSV w przypadku reklam z nagrodą
Poniżej opisujemy czynności wykonywane przez klasę RewardedAdsVerifier w celu weryfikacji nagrodzonej SSV. Chociaż dołączone fragmenty kodu są napisane w języku Java i korzystają z biblioteki innej firmy Tink, możesz wykonać te czynności w wybranym języku, używając dowolnej biblioteki innej firmy, która obsługuje ECDSA.
Pobieranie kluczy publicznych
Aby zweryfikować wywołanie zwrotne SSV w przypadku reklamy z nagrodą, potrzebujesz klucza publicznego udostępnionego przez AdMob.
Listę kluczy publicznych, które mają być używane do weryfikowania wywołań zwrotnych SSV dotyczących reklam z nagrodą, można pobrać z serwera kluczy AdMob. Lista kluczy publicznych jest podawana w formacie JSON podobnym do tego:
{
"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=="
},
],
}
Aby pobrać klucze publiczne, połącz się z serwerem kluczy AdMob i pobierz klucze. Poniższy kod wykonuje to zadanie i zapisuje reprezentację JSON kluczy w zmiennej 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();
}
Pamiętaj, że klucze publiczne są regularnie zmieniane. Otrzymasz e-maila z informacją o nadchodzącej rotacji. Jeśli buforujesz klucze publiczne, po otrzymaniu tego e-maila musisz je zaktualizować.
Po pobraniu kluczy publicznych należy je przeanalizować. Poniższa metoda parsePublicKeysJson przyjmuje jako dane wejściowe ciąg znaków JSON, taki jak w przykładzie powyżej, i tworzy mapowanie wartości key_id na klucze publiczne, które są hermetyzowane jako obiekty ECPublicKey z biblioteki 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;
}
Uzyskiwanie treści do weryfikacji
Ostatnie 2 parametry zapytania w wywołaniach zwrotnych SSV z nagrodą to zawsze signature i key_id, w tej kolejności. Pozostałe parametry zapytania określają treść, która ma zostać zweryfikowana. Załóżmy, że skonfigurowano AdMob tak, aby wysyłać wywołania zwrotne dotyczące nagród na adres https://www.myserver.com/mypath. Poniższy fragment kodu pokazuje przykładowe wywołanie zwrotne SSV z nagrodą, w którym wyróżniono treść do zweryfikowania.
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
Poniższy kod pokazuje, jak przeanalizować treść do zweryfikowania z adresu URL wywołania zwrotnego jako tablicę bajtów 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"));
Pobieranie podpisu i identyfikatora klucza z adresu URL wywołania zwrotnego
Korzystając z wartości queryString z poprzedniego kroku, przeanalizuj parametry zapytania signature i key_id z adresu URL wywołania zwrotnego, jak pokazano poniżej:
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()));
Przeprowadzanie weryfikacji
Ostatnim krokiem jest zweryfikowanie zawartości adresu URL wywołania zwrotnego za pomocą odpowiedniego klucza publicznego. Pobierz mapowanie zwrócone przez metodę parsePublicKeysJson i użyj parametru key_id z adresu URL wywołania zwrotnego, aby uzyskać klucz publiczny z tego mapowania. Następnie zweryfikuj podpis za pomocą tego klucza publicznego. Te kroki są przedstawione poniżej w metodzie 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);
}
}
Jeśli metoda zostanie wykonana bez zgłaszania wyjątku, adres URL wywołania zwrotnego zostanie zweryfikowany.
Najczęstsze pytania
- Czy mogę buforować klucz publiczny udostępniany przez serwer kluczy AdMob?
- Zalecamy zapisywanie w pamięci podręcznej klucza publicznego udostępnianego przez serwer kluczy AdMob, aby zmniejszyć liczbę operacji wymaganych do weryfikacji wywołań zwrotnych SSV. Pamiętaj jednak, że klucze publiczne są regularnie zmieniane i nie powinny być przechowywane w pamięci podręcznej dłużej niż 24 godziny.
- Jak często klucze publiczne udostępniane przez serwer kluczy AdMob są zmieniane?
- Klucze publiczne udostępniane przez serwer kluczy AdMob są zmieniane zgodnie z różnym harmonogramem. Aby weryfikacja wywołań zwrotnych SSV nadal działała zgodnie z założeniami, klucze publiczne nie powinny być przechowywane w pamięci podręcznej dłużej niż 24 godziny.
- Co się stanie, jeśli nie będzie można się połączyć z moim serwerem?
- Google oczekuje kodu odpowiedzi o stanie
HTTP 200 OKw przypadku wywołań zwrotnych SSV. Jeśli serwer jest niedostępny lub nie zapewnia oczekiwanej odpowiedzi, Google podejmie do 5 prób wysłania wywołań zwrotnych SSV w odstępach 1-sekundowych. - Jak mogę sprawdzić, czy wywołania zwrotne SSV pochodzą z Google?
- Użyj odwrotnego wyszukiwania DNS, aby sprawdzić, czy wywołania zwrotne SSV pochodzą z Google.