Weryfikowanie wywołań zwrotnych weryfikacji po stronie serwera

Wywołania zwrotne w ramach weryfikacji po stronie serwera to żądania URL z rozwiniętymi przez Google parametrami zapytania, które Google wysyła do systemu zewnętrznego, aby powiadomić go, że użytkownik powinien otrzymać nagrodę za interakcję z reklamą przejściową z nagrodą lub reklamą przejściową z nagrodą. Wywołania zwrotne po stronie serwera (weryfikacja po stronie serwera) w przypadku reklam z nagrodą zapewniają dodatkową ochronę przed podszywaniem wywołań zwrotnych po stronie klienta w celu nagradzania użytkowników.

Z tego przewodnika dowiesz się, jak weryfikować wywołania zwrotne SSV z nagrodą za pomocą zewnętrznej biblioteki kryptograficznej Tink Java Apps, aby upewnić się, że parametry zapytania w wywołaniu zwrotnym to prawidłowe wartości. Chociaż w tym przewodniku używamy biblioteki Tink, możesz użyć dowolnej biblioteki zewnętrznej, która obsługuje ECDSA. Możesz też przetestować serwer za pomocą narzędzia do testowania w interfejsie AdMob.

Wymagania wstępne

Używanie biblioteki RewardedAdsVerifier z biblioteki aplikacji Tink w języku Java

Repozytorium GitHub Tink Java Apps zawiera klasę pomocniczą RewardedAdsVerifier, która pozwala ograniczyć kod wymagany do weryfikacji wywołania zwrotnego SSV z nagrodą. Za pomocą tej klasy możesz zweryfikować adres 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 wywołania wyjątku, adres URL wywołania zwrotnego został pomyślnie zweryfikowany. W sekcji Nagrywanie użytkownika znajdziesz sprawdzone metody dotyczące tego, kiedy należy nagradzać użytkowników. Szczegóły dotyczące czynności wykonywanych przez tę klasę w celu weryfikacji wywołań zwrotnych SSV z nagrodą znajdziesz w sekcji Ręczne sprawdzanie wywołań zwrotnych SSV z nagrodą.

Parametry wywołania zwrotnego SSV

Wywołania zwrotne w ramach weryfikacji po stronie serwera zawierają parametry zapytania opisujące interakcję z reklamą wideo z nagrodą. Nazwy, opisy i przykładowe wartości parametrów znajdziesz poniżej. Parametry są wysyłane w kolejności alfabetycznej.

Nazwa parametru Opis Przykładowa wartość
ad_network Identyfikator źródła reklam, które dostarczyło tę reklamę. Nazwy źródeł reklam odpowiadające wartościom identyfikatorów są wymienione w sekcji Identyfikatory źródeł reklam. 1953547073528090325
ad_unit Identyfikator jednostki reklamowej AdMob, która została użyta do wysłania żądania reklamy z nagrodą. 2747237135
custom_data Niestandardowy ciąg danych podany przez: customRewardString.

Jeśli aplikacja nie podaje ciągu danych niestandardowych, ta wartość parametru zapytania nie będzie obecna w wywołaniu zwrotnym SSV.

SAMPLE_CUSTOM_DATA_STRING
key_id Klucz służący 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 Nagroda określona 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, w którym użytkownik otrzymał nagrodę, w milisekundach. 1507770365237823
transaction_id Unikalny identyfikator zakodowany w szesnastkowym kodzie dziesiętnym dla każdego zdarzenia przyznania nagrody wygenerowanego przez AdMob. 18fa792de1bca816048293fc71035638
user_id Identyfikator użytkownika podany przez użytkownika: userIdentifier.

Jeśli aplikacja nie poda identyfikatora użytkownika, ten parametr zapytania nie będzie obecny w wywołaniu zwrotnym SSV.

1234567

Identyfikatory źródeł reklam

Nazwy i identyfikatory źródeł reklam

Nazwa źródła reklamy Identyfikator źródła reklamy
Aarki (określanie stawek)5240798063227064260
Generowanie reklam (określanie stawek)1477265452970951479
AdColony15586990674969969776
AdColony (bez pakietu SDK) (określanie stawek)4600416542059544716
AdColony (określanie stawek)6895345910719072481
AdFalcon3528208921554210682
Sieć AdMob5450213213286189855
Kaskada sieci AdMob1215381445328257950
ADResult10593873382626181482
AMoAd17253994435944008978
AppLovin1063618907739174004
AppLovin (określanie stawek)1328079684332308356
Chartboost2873236629771172317
Chocolate Platform (określanie stawek)6432849193975106527
Cross-Channel (MdotM)9372067028804390441
Zdarzenie niestandardowe18351550913290782395
DT Exchange*
* Przed 21 września 2022 r. sieć ta nazywała się „Fyber Marketplace”.
2179455223494392917
EMX (określanie stawek)8497809869790333482
Fluct (określanie stawek)8419777862490735710
Druzgocąca seria3376427960656545613
Fyber*
* To źródło reklam służy do raportowania historycznego.
4839637394546996422
i-mobile5208827440166355534
Improve Digital (bidding)159382223051638006
Index Exchange (ustalanie stawek)4100650709078789802
InMobi7681903010231960328
InMobi (określanie stawek)6325663098072678541
InMobi Exchange (ustalanie stawek)5264320421916134407
IronSource6925240245545091930
ironSource Ads (ustalanie stawek)1643326773739866623
Leadbolt2899150749497968595
LG U+AD18298738678491729107
Sieć LINE Ads3025503711505004547
maio7505118203095108657
maio (określanie stawek)1343336733822567166
Media.net (określanie stawek)2127936450554446159
Zapośredniczone autoreklamy6060308706800320801
Meta Audience Network*
* Do 6 czerwca 2022 r. sieć nosiła nazwę „Facebook Audience Network”.
10568273599589928883
Meta Audience Network (ustalanie stawek)*
* Przed 6 czerwca 2022 r. sieć ta nosiła nazwę „Facebook Audience Network (ustalanie stawek)”.
11198165126854996598
Mintegral1357746574408896200
Mintegral (określanie stawek)6250601289653372374
MobFox8079529624516381459
MobFox (określanie stawek)3086513548163922365
MoPub (wycofany)10872986198578383917
myTarget8450873672465271579
Nend9383070032774777750
Nexxen (określanie stawek)*

* Przed 1 maja 2024 r. sieć ta nazywała się „UnrulyX”.

2831998725945605450
ONE by AOL (Millennial Media)6101072188699264581
ONE by AOL (Nexage)3224789793037044399
OneTag Exchange (ustalanie stawek)4873891452523427499
OpenX (określanie stawek)4918705482605678398
Pangle4069896914521993236
Pangle (określanie stawek)3525379893916449117
PubMatic (określanie stawek)3841544486172445473
Kampania z rezerwacją7068401028668408324
RhythmOne (określanie stawek)2831998725945605450
Rubicon (określanie stawek)3993193775968767067
SK planet734341340207269415
Sharethrough (określanie stawek)5247944089976324188
Smaato (określanie stawek)3362360112145450544
Equativ (określanie stawek)*

* Przed 12 stycznia 2023 r. sieć ta nazywała się „Inteligentny serwer reklamowy”.

5970199210771591442
Sonobi (określanie stawek)3270984106996027150
Tapjoy7295217276740746030
Tapjoy (określanie stawek)4692500501762622178
Tencent GDT7007906637038700218
TripleLift (określanie stawek)8332676245392738510
Unity Ads4970775877303683148
Unity Ads (określanie stawek)7069338991535737586
Verizon Media7360851262951344112
Verve Group (określanie stawek)5013176581647059185
Vpon1940957084538325905
Liftoff Monetize*

* Przed 30 stycznia 2023 r. sieć ta nazywała się „Vungle”.

1953547073528090325
Liftoff Monetize (ustalanie stawek)*

* Przed 30 stycznia 2023 r. sieć ta nazywała się „Vungle (określanie stawek)”.

4692500501762622185
Yieldmo (określanie stawek)4193081836471107579
YieldOne (określanie stawek)3154533971590234104
Zucks5506531810221735863

Nagrywanie użytkownika

Decydując, kiedy przyznać nagrodę użytkownikowi, należy zachować równowagę między jego wrażeniami a weryfikacją nagrody. Wywołania zwrotne po stronie serwera mogą być opóźnione przed dotarciem do systemów zewnętrznych. Dlatego zalecamy, aby używać wywołania zwrotnego po stronie klienta, aby natychmiast nagrodzić użytkownika, a po otrzymaniu wywołań zwrotnych po stronie serwera przeprowadzić weryfikację wszystkich nagród. Takie podejście zapewnia użytkownikom dobre wrażenia, a zarazem gwarantuje ważność przyznanych nagród.

W przypadku aplikacji, w których ważność nagrody jest kluczowa (np. nagroda wpływa na ekonomię w grze) i opóźnienia w przyznawaniu nagród są dopuszczalne, najlepszym rozwiązaniem może być oczekiwanie na zweryfikowany wywołanie zwrotne po stronie serwera.

Dane niestandardowe

Aplikacje, które wymagają dodatkowych danych w wywołaniach weryfikacji po stronie serwera, powinny korzystać z funkcji niestandardowych danych reklam z nagrodą. Każda wartość ciągu 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, parametr zapytania custom_data nie będzie obecny w zwrotnym wywołaniu SSV.

W tym przykładzie opcje SSV są ustawiane po wczytaniu reklamy z nagrodą:

Swift

GADRewardedAd.load(withAdUnitID:"AD_UNIT_ID",
                       request: request,
                       completionHandler: { [self] ad, error in
      if let error != error {
      rewardedAd = ad
      let options = GADServerSideVerificationOptions()
      options.customRewardString = "SAMPLE_CUSTOM_DATA_STRING"
      rewardedAd.serverSideVerificationOptions = options
    }

Objective-C

GADRequest *request = [GADRequest request];
[GADRewardedAd loadWithAdUnitID:@"AD_UNIT_ID"
                        request:request
              completionHandler:^(GADRewardedAd *ad, NSError *error) {
                if (error) {
                  // Handle Error
                  return;
                }
                self.rewardedAd = ad;
                GADServerSideVerificationOptions *options =
                    [[GADServerSideVerificationOptions alloc] init];
                options.customRewardString = @"SAMPLE_CUSTOM_DATA_STRING";
                ad.serverSideVerificationOptions = options;
              }];

Ręczne sprawdzanie reklam z ręcznie przyznawaną nagrodą po stronie serwera

Poniżej opisano czynności wykonywane przez klasę RewardedAdsVerifier w celu weryfikacji nagradzanego SSV. Chociaż załączone fragmenty kodu są w języku Java i korzystają z biblioteki zewnętrznej Tink, możesz je zaimplementować w dowolnym języku, używając dowolnej biblioteki zewnętrznej obsługującej ECDSA.

Pobieranie kluczy publicznych

Aby zweryfikować wywołanie zwrotne SSV z reklamą z nagrodą, potrzebujesz klucza publicznego udostępnionego przez AdMob.

Listę kluczy publicznych służących do weryfikowania wywołań zwrotnych reklam z nagrodą po stronie serwera można pobrać z serwera kluczy AdMob. Lista kluczy publicznych jest dostarczana w postaci reprezentacji JSON w formacie 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ę kluczy w formacie JSON 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 rotowane. Otrzymasz e-maila z informacją o nadchodzącej rotacji. Jeśli używasz pamięci podręcznej kluczy publicznych, po otrzymaniu tego e-maila zaktualizuj te klucze.

Po pobraniu kluczy publicznych należy je przeanalizować. Metoda parsePublicKeysJson poniżej przyjmuje jako dane wejściowe ciąg znaków JSON, taki jak przykład powyżej, i tworzy mapowanie wartości key_id na klucze publiczne, które są opakowane 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;
}

Przesyłanie treści do weryfikacji

Ostatnie 2 parametry zapytania w przypadku wywołań zwrotnych SSV z nagrodą to zawsze signaturekey_id, w tej kolejności. Pozostałe parametry zapytania określają treści, które mają zostać zweryfikowane. Załóżmy, że skonfigurowałeś/skonfigurowałaś AdMob tak, aby wysyłać wywołania zwrotne dotyczące nagrody do adresu https://www.myserver.com/mypath. Fragment kodu poniżej pokazuje przykład wywołania metody SSV z wyświetlonym treścią, która ma zostać zweryfikowana.

https://www.myserver.com/path?ad_network=54...55&ad_unit=12345678&reward_amount=10&reward_item=coins
&timestamp=150777823&transaction_id=12...DEF&user_id=1234567&signature=ME...Z1c&key_id=1268887

Poniższy kod pokazuje, jak zanalizować treści do weryfikacji z adresu URL wywołania zwrotnego jako tablicy bajtów w formacie 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 klucza_id z adresu URL wywołania zwrotnego

Korzystając z wartości queryString z poprzedniego kroku, przeanalizuj parametry zapytania signaturekey_id z adresu URL połączenia 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()));

Przeprowadź weryfikację

Ostatnim krokiem jest zweryfikowanie zawartości adresu URL wywołania zwrotnego za pomocą odpowiedniego klucza publicznego. Użyj mapowania zwróconego przez metodę parsePublicKeysJson i 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 czynności są opisane poniżej w metodach 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 wywołania wyjątku, adres URL wywołania zwrotnego został zweryfikowany.

Najczęstsze pytania

Czy mogę przechowywać w pamięci podręcznej klucz publiczny udostępniony przez serwer kluczy AdMob?
Zalecamy zapisanie w pamięci podręcznej klucza publicznego udostępnionego przez serwer kluczy AdMob, aby zmniejszyć liczbę operacji wymaganych do sprawdzania wywołań zwrotnych SSV. Pamiętaj jednak, że klucze publiczne są regularnie rotowane 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ą rotowane?
Klucze publiczne udostępniane przez serwer kluczy AdMob są rotowane zgodnie z zmiennym harmonogramem. Aby mieć pewność, że weryfikacja wywołań zwrotnych SSV działa prawidłowo, nie przechowuj kluczy publicznych w pamięci podręcznej dłużej niż 24 godziny.
Co się stanie, jeśli nie uda się połączyć z moim serwerem?
Google oczekuje kodu odpowiedzi o stanie powodzenia HTTP 200 OK w przypadku wywołań zwrotnych SSV. Jeśli serwer jest niedostępny lub nie odpowiada zgodnie z oczekiwaniami, Google będzie próbować wysyłać wywołania zwrotne SSV maksymalnie 5 razy w odstępach co 1 sekundę.
Jak mogę sprawdzić, czy wywołania zwrotne SSV pochodzą od Google?
Aby sprawdzić, czy wywołania zwrotne SSV pochodzą z Google, użyj odwrotnego wyszukiwania DNS.