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

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

«بازخوان‌های درستی‌سنجی سمت سرور» درخواست‌های نشانی وب با پارامترهای پُرسمان هستند که 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 رشته داده‌های سفارشی ارائه‌شده توسط customData.

اگر رشته داده سفارشی توسط برنامه ارائه نشود، این مقدار پارامتر پُرسمان در بازخوان 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 شناسه کاربر ارائه‌شده توسط userId.

اگر شناسه کاربری توسط برنامه ارائه نشود، این پارامتر پُرسمان در فراخوانی 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
نوسان (پیشنهاد قیمت دادن)8419777862490735710
i-mobile5208827440166355534
Improve Digital (پیشنهاد قیمت)159382223051638006
Index Exchange (پیشنهاد قیمت)4100650709078789802
InMobi7681903010231960328
InMobi (کیت توسعه نرم‌افزار) (پیشنهاد قیمت)8468954295581492586
InMobi Exchange (پیشنهاد قیمت)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» نامیده می‌شد.

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

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

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

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

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

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

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

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

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

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

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

RewardedAd.load(
  adUnitId: "_adUnitId",
  request: AdRequest(),
  rewardedAdLoadCallback: RewardedAdLoadCallback(
    onAdLoaded: (ad) {
      ServerSideVerificationOptions _options =
          ServerSideVerificationOptions(
            customData: 'SAMPLE_CUSTOM_DATA_STRING',
          );
      ad.setServerSideOptions(_options);
      _rewardedAd = ad;
    },
    onAdFailedToLoad: (error) {},
  ),
);

‫SAMPLE_CUSTOM_DATA_STRING را با داده‌های سفارشی خودتان جایگزین کنید.

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

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

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