التحقّق من صحة عمليات معاودة الاتصال في ميزة "إثبات الملكية من جهة الخادم"

عمليات معاودة الاتصال لعملية تحقّق من جهة الخادم هي طلبات عناوين URL، مع معلمات طلب البحث التي توسّعها Google، وترسلها Google إلى نظام خارجي لإعلامه بأنّه يجب منح المستخدم مكافأة مقابل التفاعل مع إعلان بيني أو إعلان بيني مقابل مكافأة. توفّر عمليات معاودة الاتصال من جهة الخادم (SSV) للإعلانات التي تضم مكافأة طبقة إضافية من الحماية ضد انتحال عمليات معاودة الاتصال من جهة العميل لمكافأة المستخدمين.

يوضّح لك هذا الدليل كيفية التحقّق من عمليات رد الاتصال من جهة الخادم (SSV) للإعلانات مقابل مكافآت باستخدام مكتبة التشفير التابعة لجهة خارجية Tink Java Apps للتأكّد من أنّ مَعلمات طلب البحث في عملية رد الاتصال هي قيم صالحة. على الرغم من استخدام Tink لأغراض هذا الدليل، يمكنك استخدام أي مكتبة تابعة لجهة خارجية تتوافق مع ECDSA. يمكنك أيضًا اختبار الخادم باستخدام أداة الاختبار في واجهة مستخدم AdMob.

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

استخدام RewardedAdsVerifier من مكتبة تطبيقات Tink Java

يتضمّن مستودع GitHub تطبيقات Tink Java فئة مساعدة RewardedAdsVerifier لتقليل الرمز البرمجي المطلوب للتحقّق من طلب معاودة الاتصال للتحقّق من جانب الخادم (SSV) بمكافأة. يتيح لك استخدام هذه الفئة التحقّق من صحة عنوان URL لردّ الاتصال باستخدام الرمز التالي.

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

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

مَعلمات معاودة الاتّصال بالتحقّق من صحة العرض من جهة الخادم

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

اسم المَعلمة الوصف مثال على القيمة
ad_network معرّف مصدر الإعلان الذي نفّذ هذا الإعلان. يتم إدراج أسماء مصادر الإعلانات التي تتوافق مع قيم أرقام التعريف في قسم معرّفات مصادر الإعلانات. 1953547073528090325
ad_unit معرّف الوحدة الإعلانية في AdMob الذي تم استخدامه لطلب الإعلان مقابل مكافأة 2747237135
key_id المفتاح الذي سيتم استخدامه للتحقّق من صحة ردّ الاتصال من جهة خادم التحقّق من صحة العرض (SSV). ترتبط هذه القيمة بمفتاح عام تقدّمه خوادم مفاتيح AdMob. 1234567890
reward_amount مبلغ المكافأة كما هو محدّد في إعدادات الوحدة الإعلانية 5
reward_item عنصر المكافأة كما هو محدّد في إعدادات الوحدة الإعلانية عملات معدنية
التوقيع توقيع لعملية معاودة الاتصال للتحقّق من صحة عرض الإعلانات من إنشاء AdMob MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMtmgJHYYW9k2_icM9LFMY
timestamp الطابع الزمني لوقت حصول المستخدم على المكافأة بتنسيق وقت Epoch بالمللي ثانية 1507770365237823
transaction_id معرّف فريد بترميز سداسي عشري لكل حدث منح مكافأة تم إنشاؤه بواسطة AdMob. 18fa792de1bca816048293fc71035638
user_id معرّف المستخدم كما هو مقدَّم من SetUserId

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

1234567

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

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

اسم مصدر الإعلان رقم تعريف مصدر الإعلان
‫Ad Generation (عروض الأسعار)1477265452970951479
شبكة AdMob5450213213286189855
العرض الإعلاني بدون انقطاع في "شبكة AdMob"1215381445328257950
AppLovin1063618907739174004
AppLovin (عروض الأسعار)1328079684332308356
Bidease (عروض الأسعار)3670825090829827805
BidMachine (تقديم عروض الأسعار)7943972370566394673
Chartboost2873236629771172317
‫Chocolate Platform (عروض الأسعار)6432849193975106527
حدث مخصّص18351550913290782395
‫DT Exchange*

* قبل 21 سبتمبر 2022، كانت هذه الشبكة تُعرف باسم "Fyber Marketplace".

2179455223494392917
‫DT Exchange (عروض الأسعار)8189833498765234879
‫Equativ (عروض الأسعار)*

* قبل 12 يناير 2023، كانت هذه الشبكة تُعرف باسم "Smart Adserver".

5970199210771591442
Fluct (عروض الأسعار)8419777862490735710
i-mobile5208827440166355534
Improve Digital (عروض الأسعار)159382223051638006
Index Exchange (عروض الأسعار)4100650709078789802
InMobi7681903010231960328
‫InMobi (حزمة تطوير البرامج) (عروض الأسعار)8468954295581492586
InMobi Exchange (عروض الأسعار)5264320421916134407
ironSource Ads6925240245545091930
‫ironSource Ads (عروض الأسعار)1643326773739866623
‫Liftoff Monetize*

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

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

* قبل 30 يناير 2023، كانت هذه الشبكة تُعرف باسم "Vungle (المزايدة)".

4692500501762622185
LY Ads Network3025503711505004547
‫شبكة إعلانات LY (عروض الأسعار)2615812619460460513
‫Magnite DV+ (عروض الأسعار)3993193775968767067
maio7505118203095108657
‫Media.net (عروض أسعار)2127936450554446159
الإعلانات الذاتية المعتمَدة على التوسّط6060308706800320801
‫Meta Audience Network*

* قبل 6 يونيو 2022، كانت تُعرف هذه الشبكة باسم "شبكة جمهور Facebook".

10568273599589928883
‫Meta Audience Network (عروض الأسعار)*

* قبل 6 يونيو 2022، كانت هذه الشبكة تُعرف باسم "Facebook Audience Network (عروض الأسعار)".

11198165126854996598
Mintegral1357746574408896200
Mintegral (عروض الأسعار)6250601289653372374
Mobfox (عروض أسعار)3086513548163922365
MobileFuse (عروض الأسعار)7303547408604090310
Moloco Ads SDK (عروض الأسعار)8267622065755668722
myTarget8450873672465271579
Nativo (عروض أسعار)3240503836211327896
Nexxen (عروض الأسعار)*

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

2831998725945605450
‫OneTag Exchange (عروض الأسعار)4873891452523427499
‫OpenX (تقديم عروض الأسعار)4918705482605678398
Pangle4069896914521993236
حزمة تطوير البرامج (SDK) من Pangle KR (عروض الأسعار)12171279046073404914
حزمة تطوير البرامج (SDK) في Pangle ROW (عروض الأسعار)3525379893916449117
حزمة تطوير البرامج (SDK) في الولايات المتحدة من Pangle (عروض الأسعار)15999446638585856012
PubMatic (تقديم عروض الأسعار)3841544486172445473
حزمة تطوير البرامج (SDK) الخاصة بمنصة PubMatic OpenWrap7702975372504485373
حزمة تطوير البرامج (SDK) الخاصة بمنصة PubMatic OpenWrap (عروض الأسعار)1234567890123456789
حملة قائمة على الحجز7068401028668408324
Rise (عروض الأسعار)6816468518946650043
Sharethrough (عروض الأسعار)5247944089976324188
Smaato (عروض أسعار)3362360112145450544
‫Sonobi (عروض أسعار)3270984106996027150
TripleLift (عروض الأسعار)8332676245392738510
Unity Ads4970775877303683148
Unity Ads (عروض الأسعار)7069338991535737586
‫Verve Group (عروض الأسعار)5013176581647059185
Vpon1940957084538325905
Yieldmo (تقديم عروض الأسعار)4193081836471107579
‫YieldOne (عروض أسعار)3154533971590234104
Zucks5506531810221735863

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

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

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

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

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

يوضّح نموذج الرمز البرمجي التالي كيفية ضبط خيارات التحقّق من صحة عرض الإعلان من جهة الخادم بعد تحميل "الإعلان مقابل مكافأة":

private void LoadRewardedAd(string adUnitId)
{
  // Send the request to load the ad.
  AdRequest adRequest = new AdRequest();
  RewardedAd.Load(adUnitId, adRequest, (RewardedAd rewardedAd, LoadAdError error) =>
  {
    // If the operation failed with a reason.
    if (error != null)
    {
        Debug.LogError("Rewarded ad failed to load an ad with error : " + error);
        return;
    }

    var options = new ServerSideVerificationOptions
                          .Builder()
                          .SetCustomData("SAMPLE_CUSTOM_DATA_STRING")
                          .Build()
    rewardedAd.SetServerSideVerificationOptions(options);
  });
}

إذا أردت ضبط سلسلة المكافآت المخصّصة، عليك إجراء ذلك قبل عرض الإعلان.

التحقّق يدويًا من ميزة "التحقّق من جهة الخادم" (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;
}

الحصول على محتوى للتحقّق منه

تكون آخر مَعلمتَين لطلب البحث في عمليات معاودة الاتصال من جهة الخادم التي تتضمّن مكافآت دائمًا 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 لعمليات معاودة الاتصال من جهة الخادم. إذا تعذّر الوصول إلى الخادم أو لم يقدّم الاستجابة المتوقّعة، ستعيد Google محاولة إرسال عمليات معاودة الاتصال للتحقّق من صحة الجلسة حتى خمس مرات على فترات زمنية مدتها ثانية واحدة.
كيف يمكنني التأكّد من أنّ عمليات معاودة الاتصال من جهة خادم التحقّق من صحة العرض (SSV) تأتي من Google؟
استخدِم بحث نظام أسماء النطاقات العكسي للتحقّق من أنّ عمليات معاودة الاتصال من جهة خادم التحقّق من صحة الإعلانات (SSV) مصدرها Google.