إنّ عمليات ردّ الاتصال لإثبات الملكية من جانب الخادم هي طلبات عناوين URL، مع معامِلات طلب البحث التي توسّعها Google، وترسلها Google إلى نظام خارجي لإعلامه بأنّه يجب منح المستخدم مكافأة مقابل التفاعل مع إعلان بيني أو إعلان فيديو مقابل مكافأة. توفّر عمليات معاودة الاتصال الخاصة بميزة "التحقّق من جهة الخادم" (SSV) للإعلانات مقابل مكافأة طبقة حماية إضافية ضد انتحال عمليات معاودة الاتصال من جهة العميل لمكافأة المستخدمين.
يوضّح لك هذا الدليل كيفية التحقّق من عمليات رد الاتصال من جهة الخادم (SSV) للإعلانات مقابل مكافآت باستخدام مكتبة التشفير التابعة لجهة خارجية Tink Java Apps للتأكّد من أنّ مَعلمات الطلب في عملية رد الاتصال هي قيم صالحة. على الرغم من أنّ هذا الدليل يستخدم Tink، يمكنك استخدام أي مكتبة تابعة لجهة خارجية تتوافق مع ECDSA. يمكنك أيضًا اختبار الخادم باستخدام أداة الاختبار في واجهة مستخدم AdMob.
يمكنك الاطّلاع على مثال على التحقّق من صحة العرض من جهة الخادم مقابل مكافأة باستخدام Java spring-boot.
المتطلبات الأساسية
- فعِّل التحقّق من صحة الإعلان مقابل مكافأة من جهة الخادم في وحدتك الإعلانية.
استخدام RewardedAdsVerifier من مكتبة تطبيقات Tink Java
يتضمّن مستودع تطبيقات Java في Tink على GitHub
فئة مساعدة RewardedAdsVerifier
لتقليل الرموز البرمجية المطلوبة للتحقّق من ردّ الاتصال الخاص بميزة "التحقّق من صحة العرض من جهة الخادم" للمكافآت.
يتيح لك استخدام هذه الفئة التحقّق من صحة عنوان 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 |
custom_data | سلسلة البيانات المخصّصة المقدَّمة من
setCustomData
إذا لم يقدّم التطبيق سلسلة بيانات مخصّصة، لن تظهر قيمة مَعلمة طلب البحث هذه في معاودة الاتصال من جهة الخادم إلى الخادم. |
SAMPLE_CUSTOM_DATA_STRING |
key_id | المفتاح الذي سيتم استخدامه للتحقّق من صحة معاودة الاتصال من جهة خادم التحقّق من صحة العرض (SSV). ترتبط هذه القيمة بمفتاح عام تقدّمه خوادم مفاتيح AdMob. | 1234567890 |
reward_amount | مبلغ المكافأة كما هو محدّد في إعدادات الوحدة الإعلانية | 5 |
reward_item | عنصر المكافأة كما هو محدّد في إعدادات الوحدة الإعلانية | عملات معدنية |
توقيع | توقيع لعملية ردّ الاتصال من جهة الخادم إلى الخادم (SSV) التي أنشأتها AdMob. | MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMtmgJHYYW9k2_icM9LFMY |
timestamp | الطابع الزمني لوقت حصول المستخدم على المكافأة بتنسيق Epoch time بالمللي ثانية | 1507770365237823 |
transaction_id | معرّف فريد بترميز سداسي عشري لكل حدث منح مكافأة تم إنشاؤه بواسطة AdMob. | 18fa792de1bca816048293fc71035638 |
user_id | معرّف المستخدم كما يقدّمه
setUserId .
إذا لم يقدّم التطبيق معرّفًا للمستخدم، لن تظهر مَعلمة طلب البحث هذه في معاودة الاتصال من جهة خادم التحقّق من صحة العرض. |
1234567 |
معرّفات مصادر الإعلانات
أسماء مصادر الإعلانات وأرقام تعريفها
اسم مصدر الإعلان | رقم تعريف مصدر الإعلان |
---|---|
إنشاء الإعلانات (عروض الأسعار) | 1477265452970951479 |
AdColony | 15586990674969969776 |
AdColony (عرض الأسعار) | 6895345910719072481 |
AdFalcon | 3528208921554210682 |
شبكة AdMob | 5450213213286189855 |
العرض الإعلاني بدون انقطاع في شبكة AdMob | 1215381445328257950 |
Applovin | 1063618907739174004 |
Applovin (عرض الأسعار) | 1328079684332308356 |
Chartboost | 2873236629771172317 |
Chocolate Platform (عروض الأسعار) | 6432849193975106527 |
حدث مخصّص | 18351550913290782395 |
DT Exchange* * قبل 21 سبتمبر 2022، كانت هذه الشبكة تُعرف باسم "Fyber Marketplace". | 2179455223494392917 |
Equativ (عروض الأسعار)* * قبل 12 يناير 2023، كانت هذه الشبكة تُسمّى "Smart Adserver". | 5970199210771591442 |
Fluct (عرض أسعار) | 8419777862490735710 |
Flurry | 3376427960656545613 |
Fyber* * يُستخدَم مصدر الإعلان هذا لإعداد التقارير السابقة. | 4839637394546996422 |
i-mobile | 5208827440166355534 |
Improve Digital (عروض الأسعار) | 159382223051638006 |
Index Exchange (عروض الأسعار) | 4100650709078789802 |
InMobi | 7681903010231960328 |
InMobi (عروض الأسعار) | 6325663098072678541 |
InMobi Exchange (عروض الأسعار) | 5264320421916134407 |
IronSource | 6925240245545091930 |
ironSource Ads (عروض الأسعار) | 1643326773739866623 |
Leadbolt | 2899150749497968595 |
Liftoff Monetize* * قبل 30 يناير 2023، كانت هذه الشبكة تُسمّى "Vungle". | 1953547073528090325 |
Liftoff Monetize (عروض الأسعار)* * قبل 30 يناير 2023، كانت هذه الشبكة تُسمّى "Vungle (المزايدة)". | 4692500501762622185 |
LG U+AD | 18298738678491729107 |
LINE Ads Network | 3025503711505004547 |
Magnite DV+ (عرض الأسعار) | 3993193775968767067 |
maio | 7505118203095108657 |
maio (عرض أسعار) | 1343336733822567166 |
Media.net (عرض أسعار) | 2127936450554446159 |
الإعلانات للشركة نفسها المعتمَدة على التوسّط | 6060308706800320801 |
Meta Audience Network* * قبل 6 يونيو 2022، كانت هذه الشبكة تُعرف باسم "Facebook Audience Network". | 10568273599589928883 |
Meta Audience Network (عروض الأسعار)* * قبل 6 يونيو 2022، كانت هذه الشبكة تُعرف باسم "Facebook Audience Network (عروض الأسعار)". | 11198165126854996598 |
Mintegral | 1357746574408896200 |
Mintegral (عروض الأسعار) | 6250601289653372374 |
MobFox (عرض أسعار) | 3086513548163922365 |
MoPub (متوقّفة نهائيًا) | 10872986198578383917 |
myTarget | 8450873672465271579 |
Nend | 9383070032774777750 |
Nexxen (عروض الأسعار)* * قبل 1 مايو 2024، كانت هذه الشبكة تُسمّى "UnrulyX". | 2831998725945605450 |
OneTag Exchange (عرض أسعار) | 4873891452523427499 |
OpenX (عرض أسعار) | 4918705482605678398 |
Pangle | 4069896914521993236 |
Pangle (عرض أسعار) | 3525379893916449117 |
PubMatic (عرض الأسعار) | 3841544486172445473 |
حملة قائمة على الحجز | 7068401028668408324 |
SK planet | 734341340207269415 |
Sharethrough (عروض الأسعار) | 5247944089976324188 |
Smaato (عرض أسعار) | 3362360112145450544 |
Sonobi (عرض أسعار) | 3270984106996027150 |
Tapjoy | 7295217276740746030 |
Tapjoy (عرض أسعار) | 4692500501762622178 |
Tencent GDT | 7007906637038700218 |
TripleLift (عروض الأسعار) | 8332676245392738510 |
Unity Ads | 4970775877303683148 |
Unity Ads (عروض الأسعار) | 7069338991535737586 |
Verve Group (عروض الأسعار) | 5013176581647059185 |
Vpon | 1940957084538325905 |
Yieldmo (عرض أسعار) | 4193081836471107579 |
YieldOne (عرض الأسعار) | 3154533971590234104 |
Zucks | 5506531810221735863 |
مكافأة المستخدم
من المهم تحقيق التوازن بين تجربة المستخدم والتحقّق من صحة المكافأة عند تحديد وقت منح المكافأة للمستخدم. قد يحدث تأخير في عمليات معاودة الاتصال من جهة الخادم قبل أن تصل إلى الأنظمة الخارجية. لذلك، فإنّ أفضل الممارسات التي ننصح بها هي استخدام دالة الرجوع من جهة العميل لمنح المكافأة للمستخدم على الفور، مع إجراء عملية التحقّق من صحة جميع المكافآت عند تلقّي دوال الرجوع من جهة الخادم. يوفّر هذا الأسلوب تجربة مستخدم جيدة مع ضمان صحة المكافآت الممنوحة.
ومع ذلك، في التطبيقات التي تكون فيها صلاحية المكافأة مهمة (على سبيل المثال، إذا كانت المكافأة تؤثر في اقتصاد اللعبة داخل التطبيق) وكان من المقبول حدوث تأخير في منح المكافآت، قد يكون الانتظار إلى أن يتم تلقّي ردّ من الخادم تم التحقّق منه هو أفضل طريقة.
البيانات المخصّصة
يجب أن تستخدم التطبيقات التي تتطلّب بيانات إضافية في عمليات معاودة الاتصال من جهة الخادم ميزة البيانات المخصّصة في "الإعلانات مقابل مكافأة". يتم تمرير أي قيمة سلسلة تم ضبطها على عنصر إعلان مقابل مكافأة إلى مَعلمة طلب البحث custom_data
في معاودة الاتصال من جهة خادم التحقّق من صحة الإعلان. في حال عدم ضبط قيمة بيانات مخصّصة، لن تظهر قيمة مَعلمة طلب البحث custom_data
في ردّ الاتصال من جهة الخادم.
يضبط المثال التالي خيارات "الفيديوهات أثناء التصفّح" بعد تحميل "الإعلان مقابل مكافأة":
Java
RewardedAd.load(MainActivity.this, "AD_UNIT_ID", 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, "AD_UNIT_ID", 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;
}
الحصول على المحتوى المطلوب التحقّق منه
تكون آخر مَعلمتَين لطلب البحث في عمليات معاودة الاتصال من جهة الخادم في فيديو مقابل مكافأة هما 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 ×tamp=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 مؤقتًا لتقليل عدد العمليات المطلوبة للتحقّق من صحة عمليات ردّ الاتصال الخاصة بالتحقّق من صحة العرض من جهة الخادم. يُرجى العِلم أنّه يتم تغيير المفاتيح العامة بانتظام، ويجب عدم تخزينها مؤقتًا لمدة تزيد عن 24 ساعة.
- كم عدد المرات التي يتم فيها تغيير المفاتيح العامة التي يوفّرها خادم مفاتيح AdMob؟ يتم تدوير المفاتيح العامة التي يوفّرها خادم مفاتيح AdMob وفقًا لجدول زمني متغيّر.
- لضمان استمرار عمل عمليات التحقّق من عمليات ردّ الاتصال من جهة خادم التحقّق من صحة الإعلانات (SSV) على النحو المنشود، يجب عدم تخزين المفاتيح العامة مؤقتًا لمدة تزيد عن 24 ساعة.
- ماذا يحدث إذا تعذّر الوصول إلى الخادم؟
- يتوقّع محرّك بحث Google تلقّي رمز استجابة بحالة النجاح
HTTP 200 OK
لعمليات معاودة الاتصال من جهة الخادم. إذا تعذّر الوصول إلى الخادم أو لم يقدّم الردّ المتوقّع، سيعيد محرّك بحث Google محاولة إرسال عمليات ردّ الاتصال من جهة الخادم خمس مرات كحدّ أقصى على فواصل زمنية مدتها ثانية واحدة. - كيف يمكنني التأكّد من أنّ عمليات معاودة الاتصال من جهة خادم إلى خادم (SSV) واردة من Google؟
- استخدِم بحث نظام أسماء النطاقات العكسي للتحقّق من أنّ عمليات معاودة الاتصال من جهة خادم التحقّق من صحة الإعلان (SSV) مصدرها Google.