ردود الاتصال لإثبات الملكية من جهة الخادم هي طلبات عناوين URL تتضمّن مَعلمات طلب بحث توسعها Google، وتُرسِلها Google إلى نظام خارجي لإعلامه بأنّه يجب منح المستخدم مكافأة مقابل التفاعل مع إعلان بيني يضمّ مكافأة أو إعلان بيني يضمّ مكافأة. توفّر طلبات إعادة الاتصال في ميزة "إثبات الملكية من جهة الخادم" (SSV) التي تضمّ مكافأة طبقة حماية إضافية ضد انتحال هوية طلبات إعادة الاتصال من جهة العميل لمكافأة المستخدمين.
يوضّح لك هذا الدليل كيفية التحقّق من عمليات ردّ الاتصال في عملية التحقق من جهة الخادم (SSV) التي تضمّ مكافأة باستخدام مكتبة التشفير التابعة لجهة خارجية Tink Java Apps لضمان أنّ مَعلمات طلب البحث في ردّ الاتصال هي قيم شرعية. على الرغم من أنّه يتم استخدام Tink لأغراض هذا الدليل، يمكنك استخدام أي مكتبة تابعة لجهة خارجية تتيح ECDSA. يمكنك أيضًا اختبار الخادم باستخدام أداة الاختبار في واجهة مستخدم AdMob.
اطّلِع على مثال على نموذج SSV للإعلانات التي تضم مكافآت باستخدام Java spring-boot.
لمكافأة المستخدمين.المتطلبات الأساسية
- فعِّل التحقّق من الإعلانات التي تضم مكافأة من جهة الخادم في وحدتك الإعلانية.
استخدام RewardedAdsVerifier من مكتبة تطبيقات Java في Tink
يتضمّن مستودع GitHub الخاص بتطبيقات Tink Java
فئة مساعدة
RewardedAdsVerifier
لتقليل الرمز البرمجي المطلوب للتحقّق من طلب إعادة الاتصال في ميزة "إثبات الهوية من خلال الفيديو" التي تضم مكافأة.
يتيح لك استخدام هذه الفئة التحقّق من عنوان 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 |
AdColony | 15586990674969969776 |
AdColony (غير حزمة تطوير البرامج (SDK)) (عروض الأسعار) | 4600416542059544716 |
AdColony (عروض الأسعار) | 6895345910719072481 |
AdFalcon | 3528208921554210682 |
شبكة AdMob | 5450213213286189855 |
العرض الإعلاني بدون انقطاع في "شبكة AdMob" | 1215381445328257950 |
ADResult | 10593873382626181482 |
AMoAd | 17253994435944008978 |
Applovin | 1063618907739174004 |
Applovin (عروض الأسعار) | 1328079684332308356 |
Chartboost | 2873236629771172317 |
Chocolate Platform (عروض الأسعار) | 6432849193975106527 |
CrossChannel (MdotM) | 9372067028804390441 |
حدث مخصّص | 18351550913290782395 |
DT Exchange* * قبل 21 أيلول (سبتمبر) 2022، كانت هذه الشبكة تُعرف باسم "Fyber Marketplace". | 2179455223494392917 |
EMX (عروض الأسعار) | 8497809869790333482 |
Fluct (عروض الأسعار) | 8419777862490735710 |
Flurry | 3376427960656545613 |
Fyber* * يُستخدَم مصدر الإعلان هذا لإعداد التقارير السابقة. | 4839637394546996422 |
i-mobile | 5208827440166355534 |
Improve Digital (bidding) | 159382223051638006 |
Index Exchange (عروض الأسعار) | 4100650709078789802 |
InMobi | 7681903010231960328 |
InMobi (عروض الأسعار) | 6325663098072678541 |
InMobi Exchange (عروض الأسعار) | 5264320421916134407 |
IronSource | 6925240245545091930 |
ironSource Ads (عروض الأسعار) | 1643326773739866623 |
Leadbolt | 2899150749497968595 |
LG U+AD | 18298738678491729107 |
شبكة إعلانات LINE | 3025503711505004547 |
مايو | 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 | 8079529624516381459 |
MobFox (عروض الأسعار) | 3086513548163922365 |
MoPub (متوقّفة نهائيًا) | 10872986198578383917 |
myTarget | 8450873672465271579 |
Nend | 9383070032774777750 |
Nexxen (عروض الأسعار)* * قبل 1 أيار (مايو) 2024، كانت هذه الشبكة تُعرف باسم "UnrulyX". | 2831998725945605450 |
ONE by AOL (Millennial Media) | 6101072188699264581 |
ONE by AOL (Nexage) | 3224789793037044399 |
OneTag Exchange (عروض الأسعار) | 4873891452523427499 |
OpenX (عرض الأسعار) | 4918705482605678398 |
Pangle | 4069896914521993236 |
Pangle (عروض الأسعار) | 3525379893916449117 |
PubMatic (عروض الأسعار) | 3841544486172445473 |
حملة قائمة على الحجز | 7068401028668408324 |
RhythmOne (عروض الأسعار) | 2831998725945605450 |
Rubicon (عروض الأسعار) | 3993193775968767067 |
كوكب SK | 734341340207269415 |
Sharethrough (عروض الأسعار) | 5247944089976324188 |
Smaato (عروض الأسعار) | 3362360112145450544 |
Equativ (عروض الأسعار)* * قبل 12 كانون الثاني (يناير) 2023، كان اسم هذه الشبكة هو "Smart Adserver". | 5970199210771591442 |
Sonobi (عروض الأسعار) | 3270984106996027150 |
Tapjoy | 7295217276740746030 |
Tapjoy (عروض الأسعار) | 4692500501762622178 |
Tencent GDT | 7007906637038700218 |
TripleLift (عروض الأسعار) | 8332676245392738510 |
Unity Ads | 4970775877303683148 |
Unity Ads (عروض الأسعار) | 7069338991535737586 |
Verizon Media | 7360851262951344112 |
Verve Group (عروض الأسعار) | 5013176581647059185 |
Vpon | 1940957084538325905 |
Liftoff Monetize* * قبل 30 كانون الثاني (يناير) 2023، كانت هذه الشبكة تُعرف باسم "Vungle". | 1953547073528090325 |
Liftoff Monetize (عروض الأسعار)* * قبل 30 كانون الثاني (يناير) 2023، كانت هذه الشبكة تُعرف باسم "Vungle (عروض الأسعار)". | 4692500501762622185 |
Yieldmo (عروض الأسعار) | 4193081836471107579 |
YieldOne (عروض الأسعار) | 3154533971590234104 |
Zucks | 5506531810221735863 |
مكافأة المستخدم
من المهمّ موازنة تجربة المستخدم والتحقّق من صحة المكافأة عند تحديد وقت منح المكافأة للمستخدم. قد تحدث تأخيرات في عمليات الاستدعاء من جهة الخادم قبل الوصول إلى الأنظمة الخارجية. لذلك، فإنّ أفضل الممارسات المقترَحة هي استخدام مكالمة الاسترجاع من جهة العميل لمكافأة المستخدم على الفور، مع إجراء عمليات التحقّق من جميع المكافآت عند تلقّي مكالمات الاسترجاع من جهة الخادم. يقدّم هذا الأسلوب تجربة مستخدم جيدة مع ضمان صلاحية المكافآت التي يتم منحها.
ومع ذلك، بالنسبة إلى التطبيقات التي تكون فيها صلاحية المكافأة ضرورية (على سبيل المثال، إذا كانت المكافأة تؤثر في اقتصاد اللعبة داخل تطبيقك) وكان التأخير في منح المكافآت مقبولًا، قد يكون الانتظار إلى أن يتم التحقّق من طلب إعادة الاتصال من جهة الخادم هو أفضل نهج.
البيانات المخصّصة
يجب أن تستخدم التطبيقات التي تتطلّب بيانات إضافية في عمليات ردّ الاتصال لإثبات الملكية من جهة الخادم
ميزة البيانات المخصّصة للإعلانات التي تضمّ مكافأة. يتمّ تمرير أيّ قيمة سلسلة تمّ ضبطها على عنصر إعلان مكافأة
إلى مَعلمة طلب البحث custom_data
في طلب استدعاء SSV. في حال عدم تحديد قيمة data custom، لن تكون قيمة مَعلمة طلب البحث custom_data
متوفرة في دالة SSV callback.
يحدِّد المثال التالي خيارات SSV بعد تحميل الإعلان الذي يقدّم مكافأة:
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
للتحقّق من صحة ملف تعريف الارتباط لعرض الإعلانات أثناء التشغيل الذي يتضمّن مكافأة. على الرغم من أنّ مقتطفات الرموز البرمجية المضمّنة مكتوبة بلغة 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
. يعرض المقتطف أدناه مثالاً على طلب تأكيد هوية العميل في عملية شراء مدعومة بالإعلانات
مع تمييز المحتوى المطلوب إثبات ملكيته.
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 للحد من عدد العمليات المطلوبة للتحقّق من صحة طلبات إعادة الاتصال SSV. يُرجى العلم أنّه يتم تبديل المفاتيح العامة بانتظام، ويجب عدم تخزينها مؤقتًا لأكثر من 24 ساعة.
- ما هي وتيرة تبديل المفاتيح العامة التي يوفّرها خادم مفاتيح AdMob؟
- يتم تبديل المفاتيح العامة التي يوفّرها خادم مفاتيح AdMob وفقًا لجدول زمني متبدّل. لضمان استمرار عمل عمليات التحقّق من عمليات تسجيل الإحالات الناجحة باستخدام SSV على النحو المقصود، يجب عدم تخزين المفاتيح العامة في ذاكرة التخزين المؤقت لأكثر من 24 ساعة.
- ماذا يحدث إذا تعذّر الوصول إلى خادمنا؟
- تتوقع Google تلقّي رمز استجابة حالة النجاح
HTTP 200 OK
لطلبات SSV المعاد الاتصال بها. إذا تعذّر الوصول إلى خادمك أو لم يقدّم الردّ المتوقّع، ستحاول Google إعادة إرسال طلبات إعادة الاتصال في SSV حتى خمس مرات بفواصل زمنية تبلغ ثانية واحدة. - كيف يمكنني التأكّد من أنّ عمليات تسجيل المكالمات في SSV تأتي من Google؟
- استخدِم بحث نظام أسماء النطاقات العكسي للتحقّق من أنّ عمليات استدعاء SSV تأتي من Google.