تأیید تماس‌های تأیید سمت سرور (SSV)، تأیید تماس‌های تأیید سمت سرور (SSV)، تأیید تماس‌های تأیید سمت سرور (SSV)

فراخوانی‌های تأیید سمت سرور، درخواست‌های URL هستند که پارامترهای پرس‌وجو توسط گوگل گسترش یافته‌اند و توسط گوگل به یک سیستم خارجی ارسال می‌شوند تا به آن اطلاع دهند که کاربر باید به دلیل تعامل با یک تبلیغ بینابینی پاداش‌دار یا پاداش‌دار، پاداش دریافت کند. فراخوانی‌های SSV پاداش‌دار (تأیید سمت سرور) یک لایه محافظتی اضافی در برابر جعل فراخوانی‌های سمت کلاینت برای پاداش دادن به کاربران فراهم می‌کنند.

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

پیش‌نیازها

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

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

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

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

پارامترهای فراخوانی SSV

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

نام پارامتر توضیحات مقدار مثال
شبکه_تبلیغاتی شناسه منبع تبلیغاتی برای منبع تبلیغاتی که این تبلیغ را نمایش می‌دهد. نام‌های منبع تبلیغاتی مربوط به مقادیر شناسه در بخش شناسه‌های منبع تبلیغاتی فهرست شده‌اند. ۱۹۵۳۵۴۷۰۷۳۵۲۸۰۹۰۳۲۵
واحد_تبلیغاتی شناسه واحد تبلیغاتی AdMob که برای درخواست تبلیغ جایزه‌دار استفاده شده است. ۲۷۴۷۲۳۷۱۳۵
داده‌های سفارشی رشته داده سفارشی همانطور که توسط customData ارائه شده است.

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

نمونه_رشته_داده_سفارشی
کلید_شناسه کلیدی که برای تأیید فراخوانی SSV استفاده می‌شود. این مقدار به یک کلید عمومی ارائه شده توسط سرور کلید AdMob نگاشت می‌شود. ۱۲۳۴۵۶۷۸۹۰
مبلغ پاداش مبلغ پاداش مطابق با تنظیمات واحد تبلیغات. ۵
reward_item مورد پاداش همانطور که در تنظیمات واحد تبلیغ مشخص شده است. سکه‌ها
امضا امضای مربوط به فراخوانی SSV که توسط AdMob تولید شده است. MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMtmgJHYYW9k2_icM9LFMY
مهر زمانی مهر زمانی که کاربر به عنوان زمان Epoch بر حسب میلی‌ثانیه پاداش دریافت کرده است. ۱۵۰۷۷۷۰۳۶۵۲۳۷۸۲۳
شناسه تراکنش شناسه منحصر به فرد کدگذاری شده با هگز برای هر رویداد اعطای پاداش تولید شده توسط AdMob. 18fa792de1bca816048293fc71035638
شناسه_کاربر شناسه کاربر که توسط userId ارائه شده است.

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

۱۲۳۴۵۶۷

شناسه‌های منبع تبلیغات

نام‌ها و شناسه‌های منبع تبلیغات

نام منبع تبلیغات شناسه منبع تبلیغ
تولید آگهی (مناقصه) 1477265452970951479
ادکولونی 15586990674969969776
ادکولونی (مناقصه) 6895345910719072481
ادفالکون 3528208921554210682
شبکه ادموب 5450213213286189855
آبشار شبکه ادموب 1215381445328257950
آپلووین 1063618907739174004
اپلوین (مناقصه) 1328079684332308356
چارتبوست 2873236629771172317
پلتفرم شکلات (مناقصه) 6432849193975106527
رویداد سفارشی 18351550913290782395
صرافی دی‌تی*
* قبل از ۲۱ سپتامبر ۲۰۲۲، این شبکه «بازار فیبر» نام داشت.
2179455223494392917
اکواتیو (مناقصه)*

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

5970199210771591442
نوسان (مناقصه) 8419777862490735710
تپش 3376427960656545613
فیبر*
* این منبع آگهی برای گزارش‌های تاریخی استفاده می‌شود.
4839637394546996422
آی-موبایل 5208827440166355534
بهبود دیجیتال (مناقصه) 159382223051638006
بورس شاخص (مناقصه) 4100650709078789802
این‌موبی 7681903010231960328
InMobi (مناقصه) 6325663098072678541
صرافی InMobi (پیشنهاد قیمت) 5264320421916134407
آیرون سورس 6925240245545091930
تبلیغات ironSource (مناقصه) 1643326773739866623
لیدبولت 2899150749497968595
کسب درآمد از لیفت‌آف*

* قبل از 30 ژانویه 2023، این شبکه «وانگِل» (Vungle) نام داشت.

1953547073528090325
کسب درآمد از لیفت‌آف (مناقصه)*

* قبل از 30 ژانویه 2023، این شبکه «Vungle (مناقصه)» نامیده می‌شد.

4692500501762622185
ال‌جی یو+ای‌دی 18298738678491729107
شبکه تبلیغات لاین 3025503711505004547
مگنیت دی‌وی+ (مناقصه) 3993193775968767067
مایو 7505118203095108657
مایو (مناقصه) 1343336733822567166
Media.net (مناقصه) 2127936450554446159
تبلیغات خانه با واسطه 6060308706800320801
شبکه مخاطبان متا*
* پیش از ۶ ژوئن ۲۰۲۲، این شبکه «شبکه مخاطبان فیسبوک» نام داشت.
10568273599589928883
شبکه مخاطبان متا (مناقصه)*
* پیش از ۶ ژوئن ۲۰۲۲، این شبکه «شبکه مخاطبان فیسبوک (مناقصه)» نامیده می‌شد.
11198165126854996598
انتگرال مینت 1357746574408896200
انتگرال منهای (مناقصه) 6250601289653372374
موب‌فاکس (مناقصه‌دهی) 3086513548163922365
MoPub ( منسوخ شده ) 10872986198578383917
هدف من 8450873672465271579
نند 9383070032774777750
نکسن (مناقصه)*

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

2831998725945605450
صرافی وان‌تگ (پیشنهاد قیمت) 4873891452523427499
OpenX (مناقصه) 4918705482605678398
پنگل 4069896914521993236
پنگل (مناقصه) 3525379893916449117
پاب‌ماتیک (مناقصه) 3841544486172445473
کمپین رزرو 7068401028668408324
سیاره SK 734341340207269415
اشتراک‌گذاری (مناقصه) 5247944089976324188
سماتو (مناقصه) 3362360112145450544
سونوبی (مناقصه) 3270984106996027150
تاپجوی 7295217276740746030
تاپ‌جوی (مناقصه) 4692500501762622178
تنسنت جی‌دی‌تی 7007906637038700218
تریپل لیفت (مناقصه) 8332676245392738510
تبلیغات یونیتی 4970775877303683148
تبلیغات یونیتی (مناقصه) 7069338991535737586
گروه ورو (مناقصه دهنده) 5013176581647059185
ویپون 1940957084538325905
ییلدمو (مناقصه) 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 با داده‌های سفارشی خود جایگزین کنید.

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

تأیید دستی SSV پاداش داده شده

مراحل انجام شده توسط کلاس RewardedAdsVerifier برای تأیید SSV پاداش داده شده در زیر شرح داده شده است. اگرچه قطعه کدهای موجود در این کد به زبان جاوا هستند و از کتابخانه شخص ثالث 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

کد زیر نحوه تجزیه محتوایی که قرار است از یک URL فراخوانی شده تأیید شود را به عنوان یک آرایه بایتی 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 از URL فراخوانی شده

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

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

انجام تأیید

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

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

سوالات متداول

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