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

پلاتفرم را انتخاب کنید: Android iOS Unity Flutter Android (Legacy)

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

این راهنما به شما نشان می‌دهد که چگونه بااستفاده از کتابخانه رمزنگاری شخص ثالث Tink Java Apps، فراخوان‌های برگشتی SSV پاداش‌دار را درستی‌سنجی کنید تا مطمئن شوید که پارامترهای پُرسمان در فراخوان برگشتی مقادیر قانونی هستند. اگرچه 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 رشته داده‌های سفارشی ارائه‌شده توسط customRewardString.

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

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

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

1234567

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

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

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

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

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

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

5970199210771591442
Fluct (پیشنهاد قیمت)8419777862490735710
i-mobile5208827440166355534
Improve Digital (پیشنهاد قیمت)159382223051638006
Index Exchange (پیشنهاد قیمت)4100650709078789802
InMobi7681903010231960328
InMobi (SDK) (bidding)8468954295581492586
InMobi Exchange (bidding)5264320421916134407
ironSource Ads6925240245545091930
ironSource Ads (bidding)1643326773739866623
Liftoff Monetize*

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

1953547073528090325
Liftoff Monetize (مناقصه)*

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

4692500501762622185
LY Ads Network3025503711505004547
LY Ads Network (bidding)2615812619460460513
Magnite DV+ (bidding)3993193775968767067
maio7505118203095108657
Media.net (bidding)2127936450554446159
آگهی‌های خانه‌ای میانجی‌گری‌شده6060308706800320801
Meta Audience Network*

* پیش‌از ۶ ژوئن ۲۰۲۲، این شبکه «Facebook Audience Network» نام داشت.

10568273599589928883
Meta Audience Network (پیشنهاد قیمت)*

* پیش‌از ۶ ژوئن ۲۰۲۲، این شبکه «Facebook Audience Network (bidding)» نام داشت.

11198165126854996598
Mintegral1357746574408896200
Mintegral (پیشنهاد قیمت)6250601289653372374
Mobfox (bidding)3086513548163922365
MobileFuse (bidding)7303547408604090310
Moloco Ads SDK (bidding)8267622065755668722
myTarget8450873672465271579
Nativo (پیشنهاد قیمت دادن)3240503836211327896
Nexxen (bidding)*

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

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

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

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

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

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

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

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

Swift

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

آبجکتیو-سی

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;
              }];

درستی‌سنجی دستی «درستی‌سنجی سمت سرور» پاداش‌دار

مراحل انجام‌شده توسط کلاس 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 منشأ می‌گیرند استفاده کنید.