Weryfikowanie wywołań zwrotnych weryfikacji po stronie serwera

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 w ramach weryfikacji po stronie serwera (SSV) w przypadku reklam z nagrodą stanowią dodatkową warstwę ochrony przed fałszowaniem wywołań zwrotnych po stronie klienta w celu przyznania nagród użytkownikom.

Z tego przewodnika dowiesz się, jak weryfikować wywołania zwrotne w ramach weryfikacji po stronie serwera (SSV) w przypadku reklam z nagrodą za pomocą biblioteki kryptograficznej innej firmy Tink Java Apps, aby mieć pewność, że parametry zapytania w wywołaniu zwrotnym mają prawidłowe wartości. Chociaż w tym przewodniku używamy biblioteki Tink, możesz użyć dowolnej biblioteki innej firmy, która obsługuje ECDSA. Możesz też przetestować swój serwer za pomocą narzędzia do testowania w interfejsie AdMob.

Zapoznaj się z przykładem weryfikacji po stronie serwera (SSV) w przypadku reklam z nagrodą za pomocą Java Spring-Boot.

Wymagania wstępne

Używanie klasy RewardedAdsVerifier z biblioteki Tink Java Apps

Repozytorium Tink Java Apps na GitHubie zawiera RewardedAdsVerifier klasę pomocniczą, która zmniejsza ilość kodu wymaganego do weryfikacji wywołania zwrotnego w ramach weryfikacji po stronie serwera (SSV) w przypadku reklam 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łoszenia wyjątku, adres URL wywołania zwrotnego zostanie zweryfikowany. W sekcji Przyznawanie nagród użytkownikom znajdziesz sprawdzone metody dotyczące tego, kiedy należy przyznawać nagrody użytkownikom. Aby dowiedzieć się, jakie kroki wykonuje ta klasa w celu weryfikacji wywołań zwrotnych w ramach weryfikacji po stronie serwera (SSV) w przypadku reklam z nagrodą, przeczytaj sekcję Ręczna weryfikacja wywołań zwrotnych w ramach weryfikacji po stronie serwera (SSV) w przypadku reklam z nagrodą.

Parametry wywołania zwrotnego w ramach weryfikacji po stronie serwera (SSV)

Wywołania zwrotne w ramach weryfikacji po stronie serwera (SSV) zawierają parametry zapytania, które opisują 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, z którego pochodzi ta reklama. Nazwy źródeł reklam odpowiadające wartościom identyfikatorów znajdziesz 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 Ciąg znaków danych niestandardowych podany przez setCustomData.

Jeśli aplikacja nie poda ciągu znaków danych niestandardowych, wartość tego parametru zapytania nie będzie obecna w wywołaniu zwrotnym w ramach weryfikacji po stronie serwera (SSV).

SAMPLE_CUSTOM_DATA_STRING
key_id Klucz, który ma być używany do weryfikacji wywołania zwrotnego w ramach weryfikacji po stronie serwera (SSV). Ta wartość jest mapowana na klucz publiczny udostępniony przez serwer kluczy AdMob. 1234567890
reward_amount Wysokość nagrody określona w ustawieniach jednostki reklamowej. 5
reward_item Przedmiot nagrody określony w ustawieniach jednostki reklamowej. monety
signature Podpis wywołania zwrotnego w ramach weryfikacji po stronie serwera (SSV) wygenerowany przez AdMob. MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMtmgJHYYW9k2_icM9LFMY
timestamp Sygnatura czasowa momentu przyznania nagrody użytkownikowi w formacie czasu epoki w milisekundach. 1507770365237823
transaction_id Unikalny identyfikator zakodowany w systemie szesnastkowym każdego zdarzenia przyznania nagrody wygenerowanego przez AdMob. 18fa792de1bca816048293fc71035638
user_id Identyfikator użytkownika podany przez setUserId.

Jeśli aplikacja nie poda identyfikatora użytkownika, ten parametr zapytania nie będzie obecny w wywołaniu zwrotnym w ramach weryfikacji po stronie serwera (SSV).

1234567

Identyfikatory źródeł reklam

Nazwy i identyfikatory źródeł reklam

Nazwa źródła reklamy Identyfikator źródła reklamy
Ad Generation (określanie stawek)1477265452970951479
Sieć AdMob5450213213286189855
Kaskada sieci AdMob1215381445328257950
AppLovin1063618907739174004
AppLovin (określanie stawek)1328079684332308356
Bidease (określanie stawek)3670825090829827805
BidMachine (określanie stawek)7943972370566394673
Chartboost2873236629771172317
Chocolate Platform (określanie stawek)6432849193975106527
Zdarzenie niestandardowe18351550913290782395
DT Exchange*

* Do 21 września 2022 r. ta sieć nazywała się „Fyber Marketplace”.

2179455223494392917
DT Exchange (określanie stawek)8189833498765234879
Equativ (określanie stawek)*

* Do 12 stycznia 2023 r. ta sieć nazywała się „Smart Adserver”.

5970199210771591442
Fluct (określanie stawek)8419777862490735710
i-mobile5208827440166355534
Improve Digital (określanie stawek)159382223051638006
Index Exchange (określanie stawek)4100650709078789802
InMobi7681903010231960328
InMobi (SDK) (określanie stawek)8468954295581492586
InMobi Exchange (określanie stawek)5264320421916134407
ironSource Ads6925240245545091930
ironSource Ads (określanie stawek)1643326773739866623
Liftoff Monetize*

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

1953547073528090325
Liftoff Monetize (określanie stawek)*

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

4692500501762622185
LY Ads Network3025503711505004547
LY Ads Network (określanie stawek)2615812619460460513
Magnite DV+ (określanie stawek)3993193775968767067
maio7505118203095108657
Media.net (określanie stawek)2127936450554446159
Zapośredniczone autoreklamy6060308706800320801
Meta Audience Network*

* Do 6 czerwca 2022 r. ta sieć nazywała się „Facebook Audience Network”.

10568273599589928883
Meta Audience Network (określanie stawek)*

* Do 6 czerwca 2022 r. ta sieć nazywała się „Facebook Audience Network (określanie stawek)”.

11198165126854996598
Mintegral1357746574408896200
Mintegral (określanie stawek)6250601289653372374
Mobfox (określanie stawek)3086513548163922365
MobileFuse (określanie stawek)7303547408604090310
Moloco Ads SDK (określanie stawek)8267622065755668722
myTarget8450873672465271579
Nativo (określanie stawek)3240503836211327896
Nexxen (określanie stawek)*

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

2831998725945605450
OneTag Exchange (określanie stawek)4873891452523427499
OpenX (określanie stawek)4918705482605678398
Pangle4069896914521993236
Pangle KR SDK (określanie stawek)12171279046073404914
Pangle ROW SDK (określanie stawek)3525379893916449117
Pangle US SDK (określanie stawek)15999446638585856012
PubMatic (określanie stawek)3841544486172445473
PubMatic OpenWrap SDK7702975372504485373
PubMatic OpenWrap SDK (określanie stawek)1234567890123456789
Kampania z rezerwacją7068401028668408324
Rise (określanie stawek)6816468518946650043
Sharethrough (określanie stawek)5247944089976324188
Smaato (określanie stawek)3362360112145450544
Sonobi (określanie stawek)3270984106996027150
TripleLift (określanie stawek)8332676245392738510
Unity Ads4970775877303683148
Unity Ads (określanie stawek)7069338991535737586
Verve Group (określanie stawek)5013176581647059185
Vpon1940957084538325905
Yieldmo (określanie stawek)4193081836471107579
YieldOne (określanie stawek)3154533971590234104
Zucks5506531810221735863

Przyznawanie nagród użytkownikom

Przy podejmowaniu decyzji o tym, kiedy przyznać nagrodę użytkownikowi, ważne jest, aby zachować równowagę między wygodą użytkownika a weryfikacją nagrody. Wywołania zwrotne po stronie serwera mogą docierać do systemów zewnętrznych z opóźnieniem. Dlatego zalecamy używanie wywołania zwrotnego po stronie klienta do natychmiastowego przyznawania nagród użytkownikom, a jednocześnie przeprowadzanie weryfikacji wszystkich nagród po otrzymaniu wywołań zwrotnych po stronie serwera. Takie podejście zapewnia wygodę użytkowników, 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) i dopuszczalne są opóźnienia w przyznawaniu nagród, najlepszym rozwiązaniem może być poczekanie na zweryfikowane wywołanie zwrotne po stronie serwera.

Dane niestandardowe

Aplikacje, które wymagają dodatkowych danych w wywołaniach zwrotnych w ramach weryfikacji po stronie serwera (SSV), 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 w ramach weryfikacji po stronie serwera (SSV). Jeśli nie ustawisz wartości danych niestandardowych, wartość parametru zapytania custom_data nie będzie obecna w wywołaniu zwrotnym w ramach weryfikacji po stronie serwera (SSV).

Poniższy przykład ustawia opcje weryfikacji po stronie serwera (SSV) po wczytaniu reklamy z nagrodą:

Java

RewardedAd.load(MainActivity.this, "AD_UNIT_ID",
    new AdRequest.Builder().build(),  new RewardedAdLoadCallback() {
  @Override
  public void onAdLoaded(RewardedAd ad) {
    Log.d(TAG, "Ad was loaded.");
    rewardedAd = ad;
    ServerSideVerificationOptions options = new ServerSideVerificationOptions
        .Builder()
        .setCustomData("SAMPLE_CUSTOM_DATA_STRING")
        .build();
    rewardedAd.setServerSideVerificationOptions(options);
  }
  @Override
  public void onAdFailedToLoad(LoadAdError loadAdError) {
    Log.d(TAG, loadAdError.toString());
    rewardedAd = null;
  }
});

Kotlin

RewardedAd.load(this, "AD_UNIT_ID",
    AdRequest.Builder().build(), object : RewardedAdLoadCallback() {
  override fun onAdLoaded(ad: RewardedAd) {
    Log.d(TAG, "Ad was loaded.")
    rewardedInterstitialAd = ad
    val options = ServerSideVerificationOptions.Builder()
        .setCustomData("SAMPLE_CUSTOM_DATA_STRING")
        .build()
    rewardedAd.setServerSideVerificationOptions(options)
  }

  override fun onAdFailedToLoad(adError: LoadAdError) {
    Log.d(TAG, adError?.toString())
    rewardedAd = null
  }
})

Jeśli chcesz ustawić niestandardowy ciąg znaków nagrody, musisz to zrobić przed wyświetleniem reklamy.

Ręczna weryfikacja wywołań zwrotnych w ramach weryfikacji po stronie serwera (SSV) w przypadku reklam z nagrodą

Poniżej znajdziesz opis czynności wykonywanych przez klasę RewardedAdsVerifier w celu weryfikacji wywołania zwrotnego w ramach weryfikacji po stronie serwera (SSV) w przypadku reklam z nagrodą. Chociaż fragmenty kodu są napisane w języku Java i korzystają z biblioteki innej firmy Tink, możesz zaimplementować te kroki w wybranym języku, używając dowolnej biblioteki innej firmy, która obsługuje ECDSA.

Pobieranie kluczy publicznych

Aby zweryfikować wywołanie zwrotne w ramach weryfikacji po stronie serwera (SSV) w przypadku reklam z nagrodą, potrzebujesz klucza publicznego udostępnionego przez AdMob.

Listę kluczy publicznych, które mają być używane do weryfikacji wywołań zwrotnych w ramach weryfikacji po stronie serwera (SSV) w przypadku reklam z nagrodą, można pobrać z serwera kluczy AdMob. Lista kluczy publicznych jest udostępniana 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ę 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 zbliżającej się zmianie. Jeśli przechowujesz klucze publiczne w pamięci podręcznej, po otrzymaniu tego e-maila zaktualizuj klucze.

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 powyższym przykładzie, 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;
}

Pobieranie treści do zweryfikowania

Ostatnie 2 parametry zapytania wywołań zwrotnych w ramach weryfikacji po stronie serwera (SSV) w przypadku reklam z nagrodą to zawsze signature i key_id, w tej kolejności. Pozostałe parametry zapytania określają treść do zweryfikowania. Załóżmy, że skonfigurowano AdMob tak, aby wysyłał wywołania zwrotne nagród na adres https://www.myserver.com/mypath. Poniższy fragment kodu przedstawia przykład wywołania zwrotnego w ramach weryfikacji po stronie serwera (SSV) w przypadku reklam z nagrodą z wyróżnioną treścią do zweryfikowania.

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 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

Używając wartości queryString z poprzedniego kroku, przeanalizuj parametry zapytania signature i key_id z adresu URL wywołania zwrotnego w sposób pokazany 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 treś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ą pokazane 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łoszenia wyjątku, adres URL wywołania zwrotnego zostanie zweryfikowany.

Najczęstsze pytania

Czy mogę przechowywać w pamięci podręcznej klucz publiczny udostępniony przez serwer kluczy AdMob?
Zalecamy przechowywanie w pamięci podręcznej klucza publicznego udostępnionego przez serwer kluczy AdMob, aby zmniejszyć liczbę operacji wymaganych do weryfikacji wywołań zwrotnych w ramach weryfikacji po stronie serwera (SSV). Pamiętaj jednak, że klucze publiczne są regularnie zmieniane i nie należy ich przechowywać w pamięci podręcznej dłużej niż 24 godziny.
Jak często są zmieniane klucze publiczne udostępniane przez serwer kluczy AdMob?
Klucze publiczne udostępniane przez serwer kluczy AdMob są zmieniane według zmiennego harmonogramu. Aby mieć pewność, że weryfikacja wywołań zwrotnych w ramach weryfikacji po stronie serwera (SSV) nadal działa zgodnie z oczekiwaniami, 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 nawiązać połączenia z moim serwerem?
W przypadku wywołań zwrotnych w ramach weryfikacji po stronie serwera (SSV) Google oczekuje kodu stanu odpowiedzi HTTP 200 OK. Jeśli nie można nawiązać połączenia z Twoim serwerem lub nie zapewnia on oczekiwanej odpowiedzi, Google podejmie do 5 prób wysłania wywołań zwrotnych w ramach weryfikacji po stronie serwera (SSV) w odstępach 1-sekundowych.
Jak mogę sprawdzić, czy wywołania zwrotne w ramach weryfikacji po stronie serwera (SSV) pochodzą od Google?
Aby sprawdzić, czy wywołania zwrotne w ramach weryfikacji po stronie serwera (SSV) pochodzą od Google, użyj odwrotnego wyszukiwania DNS.