تأیید اعتبار سمت سرور (SSV) فراخوانی

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

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

این مثال کاملاً کارآمد را با استفاده از Java Spring-boot بررسی کنید.

پیش نیازها

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

مخزن Tink Java Apps GitHub شامل یک کلاس کمکی 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

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

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

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

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

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

1234567

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

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

نام منبع آگهی شناسه منبع آگهی
آرکی (مناقصه) 5240798063227064260
تولید آگهی (مناقصه) 1477265452970951479
AdColony 15586990674969969776
AdColony (غیر SDK) (مناقصه) 4600416542059544716
AdColony (مناقصه) 6895345910719072481
AdFalcon 3528208921554210682
شبکه AdMob 5450213213286189855
آبشار شبکه AdMob 1215381445328257950
نتیجه ADR 10593873382626181482
AMoAd 17253994435944008978
آپلووین 1063618907739174004
Applovin (مناقصه) 1328079684332308356
Chartboost 2873236629771172317
پلت فرم شکلات (مناقصه) 6432849193975106527
CrossChannel (MdotM) 9372067028804390441
رویداد سفارشی 18351550913290782395
صرافی DT*
* تا قبل از 21 سپتامبر 2022، این شبکه "Fyber Marketplace" نام داشت.
2179455223494392917
EMX (مناقصه) 8497809869790333482
نوسان (مناقصه) 8419777862490735710
سر و صدا 3376427960656545613
فیبر*
* این منبع آگهی برای گزارش تاریخی استفاده می شود.
4839637394546996422
آی موبایل 5208827440166355534
بهبود دیجیتال (مناقصه) 159382223051638006
بورس شاخص (مناقصه) 4100650709078789802
InMobi 7681903010231960328
InMobi (مناقصه) 6325663098072678541
تبادل InMobi (مناقصه) 5264320421916134407
منبع آهن 6925240245545091930
IronSource Ads (مناقصه) 1643326773739866623
لیدبولت 2899150749497968595
LG U+AD 18298738678491729107
LINE Ads Network 3025503711505004547
مایو 7505118203095108657
maio (مناقصه) 1343336733822567166
Media.net (مناقصه) 2127936450554446159
تبلیغات خانه با واسطه 6060308706800320801
شبکه متا مخاطبان*
* تا قبل از 6 ژوئن 2022، این شبکه «شبکه مخاطبان فیسبوک» نام داشت.
10568273599589928883
شبکه متا مخاطبان (مناقصه)*
* تا قبل از 6 ژوئن 2022، این شبکه "شبکه مخاطبان فیس بوک (مناقصه)" نام داشت.
11198165126854996598
انتگرال 1357746574408896200
منتگرال (مناقصه) 6250601289653372374
موب فاکس 8079529624516381459
MobFox (مناقصه) 3086513548163922365
MoPub ( منسوخ شده ) 10872986198578383917
myTarget 8450873672465271579
نند 9383070032774777750
Nexxen (مناقصه)*

* قبل از 1 می 2024، این شبکه "UnrulyX" نام داشت.

2831998725945605450
ONE توسط AOL (Millennial Media) 6101072188699264581
ONE توسط AOL (Nexage) 3224789793037044399
تبادل OneTag (مناقصه) 4873891452523427499
OpenX (مناقصه) 4918705482605678398
پنگل 4069896914521993236
Pangle (مناقصه) 3525379893916449117
PubMatic (مناقصه) 3841544486172445473
کمپین رزرو 7068401028668408324
RhythmOne (مناقصه) 2831998725945605450
روبیکون (مناقصه) 3993193775968767067
سیاره SK 734341340207269415
اشتراک گذاری (مناقصه) 5247944089976324188
اسماتو (مناقصه) 3362360112145450544
Equativ (مناقصه)*

* قبل از 12 ژانویه 2023، این شبکه "Smart Adserver" نام داشت.

5970199210771591442
سونوبی (مناقصه) 3270984106996027150
Tapjoy 7295217276740746030
Tapjoy (مناقصه) 4692500501762622178
Tencent GDT 7007906637038700218
TripleLift (مناقصه) 8332676245392738510
تبلیغات یونیتی 4970775877303683148
تبلیغات Unity (مناقصه) 7069338991535737586
Verizon Media 7360851262951344112
گروه Verve (مناقصه) 5013176581647059185
Vpon 1940957084538325905
Liftoff کسب درآمد*

* قبل از 30 ژانویه 2023، این شبکه "Vungle" نام داشت.

1953547073528090325
Liftoff کسب درآمد (مناقصه)*

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

4692500501762622185
بازده (مناقصه) 4193081836471107579
YieldOne (مناقصه) 3154533971590234104
زاکس 5506531810221735863

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

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

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

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

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

نمونه کد زیر نحوه تنظیم گزینه های SSV را پس از بارگیری تبلیغ پاداش نشان می دهد.

جاوا

RewardedAd.load(MainActivity.this, "ca-app-pub-3940256099942544/5354046379",
    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;
  }
});

کاتلین

RewardedAd.load(this, "ca-app-pub-3940256099942544/5354046379",
    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
  }
})

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

تأیید دستی 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 از مرحله قبل، پارامترهای پرس و جوی signature و key_id را از URL برگشتی مطابق شکل زیر تجزیه کنید:

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 همانطور که در نظر گرفته شده کار می‌کند، کلیدهای عمومی نباید بیش از 24 ساعت در حافظه پنهان نگهداری شوند.
اگر دسترسی به سرور من امکان پذیر نباشد چه اتفاقی می افتد؟
Google انتظار دارد یک کد پاسخ وضعیت موفقیت HTTP 200 OK برای تماس های SSV. اگر سرور شما قابل دسترسی نباشد یا پاسخ مورد انتظار را ارائه ندهد، Google مجدداً تلاش می‌کند تا حداکثر پنج بار در بازه‌های زمانی یک ثانیه‌ای تماس‌های SSV ارسال کند.
چگونه می‌توانم تأیید کنم که تماس‌های SSV از Google می‌آیند؟
از جستجوی معکوس DNS برای تأیید اینکه تماس‌های SSV از Google سرچشمه می‌گیرند استفاده کنید.