اعتبارسنجی کردن برگشتی‌های درستی‌سنجی سمت سرور (SSV)

«بازخوان‌های درستی‌سنجی سمت سرور» درخواست‌های نشانی وب با پارامترهای پُرسمان هستند که Google آن‌ها را ازهم باز می‌کند و برای سیستم خارجی ارسال می‌کند تا به آن اطلاع دهد که کاربر باید برای تعامل با آگهی ویدیویی پاداش‌دار یا آگهی بینابینی پاداش‌دار پاداش دریافت کند. بازخوان‌های «درستی‌سنجی سمت سرور» پاداش‌دار لایه محافظتی اضافه‌ای دربرابر جعل بازخوان‌های سمت کارخواه برای پاداش دادن به کاربران ارائه می‌دهند.

این راهنما نحوه درستی‌سنجی کردن برگشتی‌های SSV پاداش‌دار را بااستفاده از برنامه‌های جاوا Tink کتابخانه رمزنگاری شخص ثالث نشان می‌دهد تا مطمئن شوید که پارامترهای پُرسمان در برگشتی مقادیر قانونی هستند. اگرچه از Tink برای اهداف این راهنما استفاده می‌شود، این گزینه را دارید که از هر کتابخانه طرف سومی که از ECDSA پشتیبانی می‌کند استفاده کنید. همچنین می‌توانید سرورتان را با ابزار آزمایش در واسط کاربر AdMob آزمایش کنید.

پیش‌نیازها

از RewardedAdsVerifier از کتابخانه Tink Java Apps استفاده کنید

مخزن GitHub برنامه‌های Tink Java شامل RewardedAdsVerifier کلاس کمکی برای کاهش کد موردنیاز برای درستی‌سنجی کردن برگشتی SSV پاداش‌دار است. استفاده از این کلاس به شما امکان می‌دهد نشانی وب برگشتی را با کد زیر درستی‌سنجی کنید.

RewardedAdsVerifier verifier = new RewardedAdsVerifier.Builder()
    .fetchVerifyingPublicKeysWith(
        RewardedAdsVerifier.KEYS_DOWNLOADER_INSTANCE_PROD)
    .build();
String rewardUrl = ...;
verifier.verify(rewardUrl);

اگر روش verify() بدون ایجاد استثنا اجرا شود، نشانی وب پاسخ‌به‌تماس باموفقیت درستی‌سنجی شده است. بخش پاداش دادن به کاربر روال‌های مطلوب را درباره زمان پاداش دادن به کاربران شرح می‌دهد. برای جزئیات مراحل انجام‌شده توسط این کلاس برای درستی‌سنجی تماس‌های برگشتی SSV پاداش‌دار، می‌توانید بخش درستی‌سنجی دستی SSV پاداش‌دار را بخوانید.

پارامترهای پاسخ تماس SSV

کاربردهای درستی‌سنجی سمت سرور حاوی پارامترهای پُرسمانی است که تعامل آگهی پاداش‌دار را توصیف می‌کند. نام‌های پارامتر، شرح‌ها، و مقادیر نمونه در زیر فهرست شده است. پارامترها به‌ترتیب حروف الفبا ارسال می‌شوند.

نام پارامتر شرح مقدار نمونه
ad_network شناسه منبع آگهی برای منبع آگهی که این آگهی را اجرا کرده است. نام‌های منبع آگهی متناظر با مقادیر شناسه در بخش شناسه‌های منبع آگهی فهرست شده‌اند. 1953547073528090325
ad_unit شناسه واحد تبلیغات AdMob که برای درخواست آگهی پاداش‌دار استفاده شده است. 2747237135
custom_data رشته داده سفارشی همان‌گونه که توسط customData ارائه شده است.

اگر رشته داده سفارشی توسط برنامه ارائه نشود، این مقدار پارامتر پُرسمان در بازخوان SSV وجود نخواهد داشت.

SAMPLE_CUSTOM_DATA_STRING
key_id کلیدی که برای تأیید تماس برگشتی SSV استفاده می‌شود. این مقدار به کلید عمومی ارائه‌شده توسط سرور کلید AdMob نگاشت می‌شود. 1234567890
reward_amount مقدار پاداش همان‌طور که در تنظیمات واحد آگهی مشخص شده است. 5
reward_item مورد پاداش همان‌طور که در تنظیمات واحد آگهی مشخص شده است. سکه
امضا امضای تماس برگشتی SSV تولیدشده توسط AdMob. MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMtmgJHYYW9k2_icM9LFMY
مُهر زمان مهر زمان زمانی که کاربر پاداش دریافت کرده است به‌عنوان زمان Epoch در میلی‌ثانیه. 1507770365237823
transaction_id شناسه یکتای کدگذاری‌شده با هگز برای هر رویداد اعطای پاداش تولیدشده توسط AdMob. 18fa792de1bca816048293fc71035638
user_id شناسه کاربر ارائه‌شده توسط userId.

اگر برنامه شناسه کاربری ارائه نکند، این پارامتر پُرسمان در بازخوان SSV وجود نخواهد داشت.

1234567

شناسه‌های منبع آگهی

نام‌ها و شناسه‌های منبع آگهی

نام منبع آگهی شناسه منبع آگهی
تولید آگهی (پیشنهاد قیمت)1477265452970951479
شبکه AdMob5450213213286189855
آبشار شبکه AdMob1215381445328257950
AppLovin1063618907739174004
AppLovin (پیشنهاد قیمت)1328079684332308356
Bidease (پیشنهاد قیمت)3670825090829827805
BidMachine (پیشنهاد قیمت)7943972370566394673
Chartboost2873236629771172317
Chocolate Platform (پیشنهاد قیمت)6432849193975106527
رویداد سفارشی18351550913290782395
DT Exchange*

* قبل‌از ۲۱ سپتامبر ۲۰۲۲، این شبکه «Fyber Marketplace» نامیده می‌شد.

2179455223494392917
DT Exchange (پیشنهاد قیمت)8189833498765234879
Equativ (پیشنهاد قیمت)*

* قبل‌از ۱۲ ژانویه ۲۰۲۳، این شبکه «Smart Adserver» نامیده می‌شد.

5970199210771591442
نوسان (پیشنهاد قیمت دادن)8419777862490735710
i-mobile5208827440166355534
بهبود دیجیتال (پیشنهاد قیمت)159382223051638006
Index Exchange (پیشنهاد قیمت)4100650709078789802
InMobi7681903010231960328
InMobi (کیت توسعه نرم‌افزار) (پیشنهاد قیمت)8468954295581492586
InMobi Exchange (پیشنهاد قیمت)5264320421916134407
آگهی‌های ironSource6925240245545091930
آگهی‌های ironSource (پیشنهاد قیمت)1643326773739866623
درآمدزایی پس‌از پرتاب*

* پیش‌از ۳۰ ژانویه ۲۰۲۳، این شبکه «Vungle» نامیده می‌شد.

1953547073528090325
درآمدزایی Liftoff (پیشنهاد قیمت)*

* قبل‌از ۳۰ ژانویه ۲۰۲۳، این شبکه «Vungle (پیشنهاد قیمت)» نامیده می‌شد.

4692500501762622185
LY Ads Network3025503711505004547
شبکه آگهی LY (پیشنهاد قیمت)2615812619460460513
Magnite DV+‎ (پیشنهاد قیمت)3993193775968767067
مه7505118203095108657
Media.net (پیشنهاد قیمت)2127936450554446159
آگهی‌های خانه‌ای واسطه‌ای6060308706800320801
شبکه مخاطبان Meta*

* قبل‌از ۶ ژوئن ۲۰۲۲، این شبکه «شبکه مخاطبان Facebook» نامیده می‌شد.

10568273599589928883
شبکه مخاطبان Meta (پیشنهاد قیمت)*

* قبل‌از ۶ ژوئن ۲۰۲۲، این شبکه «شبکه مخاطبان Facebook (پیشنهاد قیمت)» نامیده می‌شد.

11198165126854996598
Mintegral1357746574408896200
Mintegral (پیشنهاد قیمت)6250601289653372374
Mobfox (پیشنهاد قیمت)3086513548163922365
MobileFuse (پیشنهاد قیمت)7303547408604090310
کیت توسعه نرم‌افزار آگهی‌های Moloco (پیشنهاد قیمت)8267622065755668722
myTarget8450873672465271579
بومی (پیشنهاد قیمت دادن)3240503836211327896
‫Nexxen (پیشنهاد قیمت)*

* قبل‌از ۱ مه ۲۰۲۴، این شبکه «UnrulyX» نامیده می‌شد.

2831998725945605450
OneTag Exchange (پیشنهاد قیمت)4873891452523427499
OpenX (پیشنهاد قیمت)4918705482605678398
Pangle4069896914521993236
کیت توسعه نرم‌افزار Pangle KR (پیشنهاد قیمت)12171279046073404914
کیت توسعه نرم‌افزار Pangle ROW (پیشنهاد قیمت)3525379893916449117
کیت توسعه نرم‌افزار Pangle US (پیشنهاد قیمت)15999446638585856012
PubMatic (پیشنهاد قیمت)3841544486172445473
PubMatic OpenWrap SDK7702975372504485373
کیت توسعه نرم‌افزار PubMatic OpenWrap (پیشنهاد قیمت)1234567890123456789
پویش مدل رزرو7068401028668408324
افزایش (پیشنهاد قیمت دادن)6816468518946650043
Sharethrough (پیشنهاد قیمت)5247944089976324188
Smaato (پیشنهاد قیمت)3362360112145450544
Sonobi (پیشنهاد قیمت)3270984106996027150
TripleLift (پیشنهاد قیمت)8332676245392738510
Unity Ads4970775877303683148
‫Unity Ads (پیشنهاد قیمت)7069338991535737586
گروه Verve (پیشنهاد قیمت)5013176581647059185
Vpon1940957084538325905
‫Yieldmo (پیشنهاد قیمت)4193081836471107579
‫YieldOne (پیشنهاد قیمت)3154533971590234104
زاکس5506531810221735863

درحال پاداش دادن به کاربر

هنگام تصمیم‌گیری درباره زمان پاداش دادن به کاربر، مهم است که بین تجربه کاربر و تأییدیه پاداش تعادل برقرار کنید. کاربرگ‌های سمت سرور ممکن است قبل‌از رسیدن به سیستم‌های خارجی با تأخیر مواجه شوند. بنابراین، روال مطلوب توصیه‌شده این است که از بازخوانی سمت مشتری برای پاداش دادن فوری به کاربر استفاده کنید، و درعین‌حال پس‌از دریافت بازخوانی‌های سمت سرور، همه پاداش‌ها را اعتبارسنجی کنید. این رویکرد ضمن تضمین اعتبار پاداش‌های اعطاشده، تجربه کاربری خوبی ارائه می‌دهد.

بااین‌حال، برای برنامه‌هایی که اعتبار پاداش در آن‌ها حیاتی است (برای مثال، پاداش بر اقتصاد درون‌بازی برنامه شما تأثیر می‌گذارد) و تأخیر در اعطای پاداش قابل‌قبول است، ممکن است بهترین رویکرد انتظار برای تماس برگشتی سمت سرور تأییدشده باشد.

داده‌های سفارشی

برنامه‌هایی که در تماس‌های برگشتی درستی‌سنجی سمت سرور به داده‌های اضافی نیاز دارند باید از ویژگی داده‌های سفارشی آگهی‌های پاداش‌دار استفاده کنند. هر مقدار رشته‌ای که روی یک شیء آگهی پاداش‌دار تنظیم شده باشد، به پارامتر پُرسمان custom_data در برگشت تماس SSV منتقل می‌شود. اگر مقدار داده سفارشی تنظیم نشده باشد، مقدار پارامتر پُرسمان custom_data در بازخوانی SSV وجود نخواهد داشت.

مثال زیر گزینه‌های SSV را پس‌از بار شدن آگهی پاداش‌دار تنظیم می‌کند:

Kotlin

RewardedAd.load(
    AdRequest.Builder("AD_UNIT_ID").build(),
    object : AdLoadCallback<RewardedAd> {
      override fun onAdLoaded(ad: RewardedAd) {
        // Rewarded ad loaded.
        rewardedAd = ad;
        rewardedAd.setServerSideVerificationOptions(
          ServerSideVerificationOptions("userId", "customData")
        )
      }

      override fun onAdFailedToLoad(adError: LoadAdError) {
        // Rewarded ad failed to load.
        rewardedAd = null
      }
    },
  )

جاوا

RewardedAd.load(
      new AdRequest.Builder("AD_UNIT_ID").build(),
      new AdLoadCallback<RewardedAd>() {
        @Override
        public void onAdLoaded(@NonNull RewardedAd ad) {
          // Rewarded ad loaded.
          rewardedAd = ad;
          rewardedAd.setServerSideVerificationOptions(
              new ServerSideVerificationOptions("userId", "customData")
          );
        }

        @Override
        public void onAdFailedToLoad(@NonNull LoadAdError adError) {
          // Rewarded ad failed to load.
          rewardedAd = null;
        }
      }
  );

اگر می‌خواهید رشته پاداش سفارشی را تنظیم کنید، باید این کار را قبل‌از نمایش آگهی انجام دهید.

درستی‌سنجی دستی 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
&timestamp=150777823&transaction_id=12...DEF&user_id=1234567&signature=ME...Z1c&key_id=1268887

کد زیر نشان می‌دهد که چگونه محتوای موردتأیید را از نشانی وب بازخوانی به‌عنوان آرایه بایت 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 از نشانی وب پاسخ تماس

بااستفاده از مقدار queryString از مرحله قبلی، پارامترهای پُرسمان signature و key_id را از نشانی وب برگشتی تجزیه کنید، همان‌طور که در زیر نشان داده شده است:

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()));

انجام درستی‌سنجی

مرحله آخر درستی‌سنجی محتوای نشانی وب تماس برگشتی با کلید عمومی مناسب است. نگاشت برگشتی از روش parsePublicKeysJson را بگیرید و از پارامتر key_id از نشانی وب بازخوان برای دریافت کلید عمومی از آن نگاشت استفاده کنید. سپس امضا را با آن کلید عمومی به‌تأیید برسانید. این مراحل در روش 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);
  }
}

اگر روش بدون ایجاد استثنا اجرا شود، نشانی وب برگشتی با موفقیت درستی‌سنجی شده است.

پرسشگان

آیا می‌توانم کلید عمومی ارائه‌شده توسط سرور کلید AdMob را در حافظه نهان ذخیره کنم؟
توصیه می‌کنیم کلید عمومی ارائه‌شده توسط سرور کلید AdMob را ذخیره کنید تا تعداد عملیات لازم برای اعتبارسنجی تماس‌های برگشتی SSV کاهش یابد. بااین‌حال، توجه داشته باشید که کلیدهای عمومی به‌طور منظم چرخش می‌کنند و نباید بیشتر از ۲۴ ساعت ذخیره شوند.
کلیدهای عمومی ارائه‌شده توسط سرور کلید AdMob هر چند وقت یک‌بار چرخش می‌کنند؟
کلیدهای عمومی ارائه‌شده توسط سرور کلید AdMob در یک برنامه زمانی متغیر چرخانده می‌شوند. برای اطمینان از اینکه درستی‌سنجی برگشتی‌های SSV همچنان طبق هدف کار می‌کند، کلیدهای عمومی نباید بیش‌از ۲۴ ساعت در حافظه نهان ذخیره شوند.
اگر سرور من دردسترس نباشد چه اتفاقی می‌افتد؟
‫Google انتظار دارد کد پاسخ وضعیت موفقیت HTTP 200 OK برای بازخوان‌های SSV دریافت کند. اگر سرورتان دردسترس نباشد یا پاسخ موردانتظار را ارائه ندهد، Google تا پنج بار در فواصل یک ثانیه‌ای تلاش می‌کند تا برگشتی‌های SSV را ارسال کند.
چگونه می‌توانم درستی‌سنجی کنم که برگشتی‌های SSV از Google می‌آیند؟
از جستجوی ساناد معکوس برای تأیید اینکه تماس‌های برگشتی SSV از Google منشأ می‌گیرند استفاده کنید.