تماسهای تأیید سمت سرور، درخواستهای URL هستند، با پارامترهای جستجوی گسترشیافته توسط Google، که توسط Google به یک سیستم خارجی ارسال میشوند تا به آن اطلاع دهند که کاربر باید برای تعامل با یک تبلیغ بینالمللی پاداش یا پاداش دریافت کند. تماسهای SSV پاداش (تأیید سمت سرور) یک لایه حفاظتی اضافی در برابر جعل تماسهای سمت مشتری برای پاداش دادن به کاربران ارائه میکنند.
این راهنما به شما نشان میدهد که چگونه میتوانید با استفاده از کتابخانه رمزنگاری شخص ثالث Tink Java Apps تماسهای SSV پاداش را تأیید کنید تا اطمینان حاصل کنید که پارامترهای پرس و جو در پاسخ به تماس مقادیر قانونی هستند. اگرچه Tink برای اهداف این راهنما استفاده میشود، شما میتوانید از هر کتابخانه شخص ثالثی که از ECDSA پشتیبانی میکند استفاده کنید. همچنین میتوانید سرور خود را با ابزار تست در رابط کاربری AdMob آزمایش کنید.
این مثال کاملاً کارآمد را با استفاده از Java Spring-boot بررسی کنید.
پیش نیازها
تبلیغات پاداش را در برنامه تلفن همراه خود ادغام کنیدنسخه 11.6.0 یا بالاتر از Google Mobile Ads SDK.
تأیید اعتبار سمت سرور را در واحد تبلیغات خود فعال کنید.
از 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 ×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 میآیند؟
- از جستجوی معکوس DNS برای تأیید اینکه تماسهای SSV از Google سرچشمه میگیرند استفاده کنید.