التحقق من استدعاءات التحقق من جانب الخادم (SSV)

عمليات استدعاء إثبات الملكية من جانب الخادم هي طلبات عناوين URL، مع مَعلمات طلب البحث بواسطة Google، والتي ترسلها Google إلى نظام خارجي وإشعاره بأنه ينبغي أن يحصل المستخدم على مكافأة مقابل التفاعل مع إعلان إعلان بيني يضم مكافأة طلبات معاودة الاتصال الخاصة بميزة "إثبات الملكية من جهة الخادم" بمكافأة توفير طبقة حماية إضافية ضد انتحال المكالمات من جهة العميل لمكافأة المستخدمين.

يوضح لك هذا الدليل كيفية التحقّق من استدعاءات SSV بمكافأة باستخدام Tink Java Apps التابعة لجهات خارجية للتأكد من أن معاملات الاستعلام في عملية الاستدعاء القيم المشروعة. ورغم استخدام Tink لأغراض هذا الدليل، يمكنك استخدام أي مكتبة تابعة لجهة خارجية ECDSA: يمكنك أيضًا اختبار خادمك باستخدام أداة الاختبار في واجهة مستخدم AdMob.

تحقق من هذا البرنامج الذي يعمل بشكل كامل مثال باستخدام حذاء الربيع لـ Java.

المتطلبات الأساسية

استخدام واجهة BonusedAdsVerifier من مكتبة 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
custom_data سلسلة البيانات المخصصة كما هي مقدّمة من setCustomData .

إذا لم يوفّر التطبيق أي سلسلة بيانات مخصّصة، ستكون معلَمة طلب البحث هذه. في استدعاء SSV.

SAMPLE_CUSTOM_DATA_STRING
key_id المفتاح الذي سيتم استخدامه للتحقُّق من معاودة اتصال ميزة "التحقُّق بخطوتين". يتم ربط هذه القيمة بمفتاح عام. التي يقدّمها خادم مفاتيح مفاتيح AdMob. 1234567890
reward_amount مبلغ المكافأة على النحو المحدّد في إعدادات الوحدة الإعلانية 5
reward_item عنصر المكافأة على النحو المحدّد في إعدادات الوحدة الإعلانية. عملات معدنية
توقيع توقيع معاودة الاتصال الخاصة بميزة "التحقُّق بخطوتين" (SSV) التي أنشأها AdMob MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMtmgJHYYW9k2_icM9LFMY
timestamp طابع زمني لوقت مكافأة المستخدم كوقت حقبة بالمللي ثانية 1507770365237823
transaction_id معرّف فريد بترميز سداسي لكل حدث منح مكافأة تنشئه AdMob 18fa792de1bca816048293fc71035638
user_id معرّف المستخدم على النحو المقدَّم من setUserId

وإذا لم يوفّر التطبيق معرّف المستخدم، لن يتم توفير مَعلمة طلب البحث هذه موجودة في معاودة اتصال SSV.

1234567

معرّفات مصادر الإعلانات

أسماء مصادر الإعلانات وأرقام تعريفها

اسم مصدر الإعلان رقم تعريف مصدر الإعلان
Aarki (عروض الأسعار)5240798063227064260
إنشاء الإعلانات (عروض الأسعار)1477265452970951479
AdColony15586990674969969776
AdColony (غير مستندة إلى حزمة تطوير البرامج (SDK)) (عروض الأسعار)4600416542059544716
AdColony (عروض الأسعار)6895345910719072481
AdFalcon3528208921554210682
شبكة AdMob5450213213286189855
العرض الإعلاني بدون انقطاع في شبكة AdMob1215381445328257950
ADResult10593873382626181482
AMoAd17253994435944008978
أبلوفين1063618907739174004
Apploin (عروض الأسعار)1328079684332308356
Chartboost2873236629771172317
منصة الشوكولاتة (عروض الأسعار)6432849193975106527
CrossChannel (MdotM)9372067028804390441
حدث مخصّص18351550913290782395
DT Exchange*
* قبل 21 أيلول (سبتمبر) 2022، كان يُطلق على هذه الشبكة اسم "سوق Fyber".
2179455223494392917
EMX (عروض الأسعار)8497809869790333482
متقلّب (عروض الأسعار)8419777862490735710
Flurry3376427960656545613
Fyber*
* يُستخدَم مصدر الإعلان هذا في إعداد التقارير السابقة.
4839637394546996422
i-mobile5208827440166355534
التحسين الرقمي (عروض الأسعار)159382223051638006
بورصة الفهرس (عروض الأسعار)4100650709078789802
InMobi7681903010231960328
InMobi (عروض الأسعار)6325663098072678541
InMobi Exchange (عروض الأسعار)5264320421916134407
IronSource6925240245545091930
إعلانات ironSource (عروض الأسعار)1643326773739866623
Leadbolt2899150749497968595
LG U+AD18298738678491729107
شبكة LINE Ads3025503711505004547
مايو7505118203095108657
مايو (عروض الأسعار)1343336733822567166
Media.net (عروض الأسعار)2127936450554446159
إعلانات للشركة نفسها المعتمَدة على التوسّط6060308706800320801
Meta Audience Network*
* قبل 6 حزيران (يونيو) 2022، كان يُطلق على هذه الشبكة اسم "Facebook Audience Network".
10568273599589928883
Meta Audience Network (عروض الأسعار)*
* قبل 6 حزيران (يونيو) 2022، كان يُطلق على هذه الشبكة اسم "Facebook Audience Network (عروض الأسعار)".
11198165126854996598
Mintegral1357746574408896200
Mintegral (عروض أسعار)6250601289653372374
MobFox8079529624516381459
MobFox (عروض الأسعار)3086513548163922365
MoPub (متوقّف نهائيًا)10872986198578383917
myTarget8450873672465271579
Nend9383070032774777750
Nexxen (عرض أسعار)*

* قبل 1 مايو 2024، كان يُطلق على هذه الشبكة اسم "UnrulyX".

2831998725945605450
ONE by AOL (Millennial Media)6101072188699264581
ONE by AOL (Nexage)3224789793037044399
OneTag Exchange (عروض الأسعار)4873891452523427499
OpenX (عروض الأسعار)4918705482605678398
عدسة Pangle4069896914521993236
Pangle (عروض الأسعار)3525379893916449117
PubMatic (عروض الأسعار)3841544486172445473
حملة إعلانية قائمة على الحجوزات7068401028668408324
RhythmOne (عروض الأسعار)2831998725945605450
Rubicon (عروض الأسعار)3993193775968767067
كوكب ساسكاتشوان734341340207269415
نسبة مشاركة الإعلان بالكامل (عروض الأسعار)5247944089976324188
Smaato (عروض الأسعار)3362360112145450544
Equativ (عروض الأسعار)*

* قبل 12 كانون الثاني (يناير) 2023، كان يُطلق على هذه الشبكة اسم "خادم الإعلانات الذكي".

5970199210771591442
Sonobi (عروض الأسعار)3270984106996027150
Tapjoy7295217276740746030
Tapjoy (عروض الأسعار)4692500501762622178
تينسنت GDT7007906637038700218
TripleLift (عروض الأسعار)8332676245392738510
إعلانات Unity4970775877303683148
Unity Ads (عروض الأسعار)7069338991535737586
شركة Verizon Media7360851262951344112
مجموعة Verve (عروض الأسعار)5013176581647059185
Vpon1940957084538325905
Liftoff Monetize*

* قبل 30 كانون الثاني (يناير) 2023، كان يُطلق على هذه الشبكة اسم "Vungle".

1953547073528090325
Liftoff Monetize (عروض الأسعار)*

* قبل 30 كانون الثاني (يناير) 2023، كان يُطلق على هذه الشبكة اسم "التفاعل (عروض الأسعار)".

4692500501762622185
الأرباح (عروض الأسعار)4193081836471107579
YieldOne (عروض الأسعار)3154533971590234104
Zucks5506531810221735863

مكافأة المستخدم

من المهم تحقيق التوازن بين تجربة المستخدم ومكافأة التحقق من الصحة عند اتخاذ القرار ومتى يتم مكافأة المستخدم. قد تواجه عمليات معاودة الاتصال من جهة الخادم تأخيرات قبل الوصول إلى الأنظمة الخارجية. وبالتالي، فإن أفضل ممارسة موصى بها هي استخدام معاودة الاتصال من جانب العميل لمكافأة المستخدم على الفور، أثناء إجراء التحقق من جميع المكافآت عند استلام استدعاءات من جانب الخادم. هذا النمط تجربة مستخدم جيدة مع ضمان صلاحية والمكافآت.

ومع ذلك، بالنسبة إلى التطبيقات التي تكون فيها صلاحية المكافأة أمرًا بالغ الأهمية (على سبيل المثال، تؤثر المكافآت في اقتصاد تطبيقك داخل اللعبة) ويحدث تأخير في منح المكافآت. مقبول، فقد يكون انتظار معاودة الاتصال التي تم التحقق منها من جانب الخادم هو الأفضل الخاص بك.

البيانات المخصّصة

يجب استخدام التطبيقات التي تتطلب بيانات إضافية في عمليات معاودة الاتصال لإثبات الملكية من جهة الخادم ميزة البيانات المخصّصة للإعلانات التي تضم مكافأة. أي قيمة سلسلة تمّ تحديدها على إعلان يضم مكافأة يتم تمرير الكائن إلى معلَمة طلب البحث custom_data لمعاودة اتصال SSV. إذا كانت الإجابة "لا" البيانات المخصّصة، ولن يتم تغيير قيمة معلَمة طلب البحث custom_data الموجودة في معاودة اتصال SSV.

يوضح نموذج التعليمة البرمجية التالي كيفية تعيين خيارات SSV بعد تم تحميل إعلان يضم مكافأة.

Java

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

Kotlin

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" أدناه. وعلى الرغم من أن مقتطفات التعليمات البرمجية المضمنة بلغة 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

يوضح الرمز أدناه كيفية تحليل المحتوى للتحقق من عنوان 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 على متغيّر. زمني للمشروع. لضمان استمرار عمل التحقُّق من عمليات معاودة الاتصال باستخدام ميزة "التحقُّق بخطوتين" المقصود، يجب ألّا يتم تخزين المفاتيح العامة مؤقتًا لمدة تزيد عن 24 ساعة.
ماذا يحدث إذا تعذر الوصول إلى خادمي؟
تتوقع Google رمز استجابة لحالة النجاح HTTP 200 OK من أجل إثبات الملكية من جانب الخادم (SSV) مع معاودة الاتصال. إذا تعذر الوصول إلى الخادم أو لم يقدم ستحاول Google إرسال استدعاءات SSV ما يصل إلى خمس مرات في فواصل مدتها ثانية واحدة
كيف يمكنني التحقّق من أنّ طلبات معاودة الاتصال من خلال ميزة إثبات الملكية (SSV) يتم إرسالها من Google؟
يمكنك استخدام بحث نظام أسماء النطاقات العكسي للتحقّق من أنّ عمليات معاودة الاتصال بميزة SSV مصدرها Google.