بازخوانهای درستیسنجی سمت سرور درخواستهای نشانی وب با پارامترهای پُرسمان هستند که توسط Google گسترش یافتهاند و Google آنها را به سیستم خارجی ارسال میکند تا به آن اطلاع دهد که کاربر باید برای تعامل با آگهی تمامصفحه پاداشدار یا پاداشدار پاداش دریافت کند. بازخوانهای SSV (درستیسنجی سمت سرور) پاداشدار لایه محافظتی اضافهای دربرابر جعل بازخوانهای سمت کارخواه برای پاداش دادن به کاربران ارائه میدهند.
این راهنما به شما نشان میدهد که چگونه بااستفاده از کتابخانه رمزنگاری شخص ثالث Tink Java Apps، فراخوانهای برگشتی SSV پاداشدار را درستیسنجی کنید تا مطمئن شوید که پارامترهای پُرسمان در فراخوان برگشتی مقادیر قانونی هستند. اگرچه Tink برای اهداف این راهنما استفاده میشود، شما این گزینه را دارید که از هر کتابخانه شخص ثالثی که از ECDSA پشتیبانی میکند استفاده کنید. همچنین میتوانید سرورتان را با ابزار آزمایش در میانای کاربری AdMob آزمایش کنید.
پیشنیازها
- درستیسنجی پاداشدار سمت سرور را در واحد آگهیتان فعال کنید.
استفاده از RewardedAdsVerifier از کتابخانه Tink Java Apps
مخزن GitHub برنامههای Tink Java
شامل یک
RewardedAdsVerifier
کلاس کمکی برای کاهش کد موردنیاز برای درستیسنجی فراخوانی برگشتی SSV پاداشدار است.
بااستفاده از این کلاس میتوانید نشانی وب بازگشت تماس را با کد زیر درستیسنجی کنید.
RewardedAdsVerifier verifier = new RewardedAdsVerifier.Builder()
.fetchVerifyingPublicKeysWith(
RewardedAdsVerifier.KEYS_DOWNLOADER_INSTANCE_PROD)
.build();
String rewardUrl = ...;
verifier.verify(rewardUrl);
اگر روش verify() بدون ایجاد استثنا اجرا شود، نشانی وب فراخوان با موفقیت تأیید شده است. بخش پاداش دادن به کاربر
روالهای مطلوب را درباره زمان پاداش دادن به کاربران بهتفصیل شرح میدهد. برای
تفکیک مراحل انجامشده توسط این کلاس برای درستیسنجی بازخوانیهای SSV پاداشدار،
میتوانید بخش درستیسنجی دستی SSV
پاداشدار را بخوانید.
پارامترهای فراخوانی SSV
فراخوانیهای برگشتی درستیسنجی سمت سرور حاوی پارامترهای پُرسمانی است که تعامل آگهی پاداشدار را شرح میدهد. نام، شرح، و مقدار نمونه پارامترها در زیر فهرست شده است. پارامترها بهترتیب حروف الفبا ارسال میشوند.
| نام پارامتر | شرح | مقدار نمونه |
|---|---|---|
| ad_network | شناسه منبع آگهی برای منبع آگهیای که این آگهی را برآورده کرده است. نامهای منبع آگهی مربوط به مقادیر شناسه در بخش شناسههای منبع آگهی فهرست شدهاند. | 1953547073528090325 |
| ad_unit | شناسه واحد آگهی AdMob که برای درخواست آگهی پاداشدار استفاده شده است. | 2747237135 |
| custom_data | رشته دادههای سفارشی ارائهشده توسط
customRewardString.
اگر رشته داده سفارشی توسط برنامه ارائه نشود، این مقدار پارامتر پُرسمان در فراخوانی SSV وجود نخواهد داشت. |
SAMPLE_CUSTOM_DATA_STRING |
| key_id | کلیدی که برای تأیید فراخوانی SSV استفاده میشود. این مقدار با کلید عمومیای که سرور کلید AdMob ارائه کرده است مطابقت دارد. | 1234567890 |
| reward_amount | مقدار پاداش همانطور که در تنظیمات واحد آگهی مشخص شده است. | 5 |
| reward_item | مورد پاداش همانطور که در تنظیمات واحد آگهی مشخص شده است. | سکه |
| امضا | امضای SSV callback تولیدشده توسط AdMob. | MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMtmgJHYYW9k2_icM9LFMY |
| مُهر زمان | مهر زمان پاداش گرفتن کاربر بهصورت زمان Epoch برحسب میلیثانیه. | 1507770365237823 |
| transaction_id | شناسه کدگذاریشده هگز یکتا برای هر رویداد اعطای پاداش که توسط AdMob تولید شده است. | 18fa792de1bca816048293fc71035638 |
| user_id | شناسه کاربر طبق اطلاعات ارائهشده از سوی
userIdentifier.
اگر شناسه کاربری توسط برنامه ارائه نشود، این پارامتر پُرسمان در فراخوانی SSV وجود نخواهد داشت. |
1234567 |
شناسههای منبع آگهی
نام و شناسه منبع آگهی
| نام منبع آگهی | شناسه منبع آگهی |
|---|---|
| Ad Generation (پیشنهاد قیمت) | 1477265452970951479 |
| شبکه AdMob | 5450213213286189855 |
| آبشار شبکه AdMob | 1215381445328257950 |
| AppLovin | 1063618907739174004 |
| AppLovin (پیشنهاد قیمت) | 1328079684332308356 |
| Bidease (پیشنهاد قیمت) | 3670825090829827805 |
| BidMachine (پیشنهاد قیمت) | 7943972370566394673 |
| Chartboost | 2873236629771172317 |
| Chocolate Platform (مناقصه) | 6432849193975106527 |
| رویداد سفارشی | 18351550913290782395 |
| DT Exchange* * پیشاز ۲۱ سپتامبر ۲۰۲۲، این شبکه «Fyber Marketplace» نام داشت. | 2179455223494392917 |
| DT Exchange (پیشنهاد قیمت) | 8189833498765234879 |
| Equativ (bidding)* * پیشاز ۱۲ ژانویه ۲۰۲۳، این شبکه «Smart Adserver» نام داشت. | 5970199210771591442 |
| Fluct (پیشنهاد قیمت) | 8419777862490735710 |
| i-mobile | 5208827440166355534 |
| Improve Digital (پیشنهاد قیمت) | 159382223051638006 |
| Index Exchange (پیشنهاد قیمت) | 4100650709078789802 |
| InMobi | 7681903010231960328 |
| InMobi (SDK) (bidding) | 8468954295581492586 |
| InMobi Exchange (bidding) | 5264320421916134407 |
| ironSource Ads | 6925240245545091930 |
| ironSource Ads (bidding) | 1643326773739866623 |
| Liftoff Monetize* * پیشاز ۳۰ ژانویه ۲۰۲۳، این شبکه «Vungle» نام داشت. | 1953547073528090325 |
| Liftoff Monetize (مناقصه)* * پیشاز ۳۰ ژانویه ۲۰۲۳، این شبکه «Vungle (پیشنهاد قیمت)» نام داشت. | 4692500501762622185 |
| LY Ads Network | 3025503711505004547 |
| LY Ads Network (bidding) | 2615812619460460513 |
| Magnite DV+ (bidding) | 3993193775968767067 |
| maio | 7505118203095108657 |
| Media.net (bidding) | 2127936450554446159 |
| آگهیهای خانهای میانجیگریشده | 6060308706800320801 |
| Meta Audience Network* * پیشاز ۶ ژوئن ۲۰۲۲، این شبکه «Facebook Audience Network» نام داشت. | 10568273599589928883 |
| Meta Audience Network (پیشنهاد قیمت)* * پیشاز ۶ ژوئن ۲۰۲۲، این شبکه «Facebook Audience Network (bidding)» نام داشت. | 11198165126854996598 |
| Mintegral | 1357746574408896200 |
| Mintegral (پیشنهاد قیمت) | 6250601289653372374 |
| Mobfox (bidding) | 3086513548163922365 |
| MobileFuse (bidding) | 7303547408604090310 |
| Moloco Ads SDK (bidding) | 8267622065755668722 |
| myTarget | 8450873672465271579 |
| Nativo (پیشنهاد قیمت دادن) | 3240503836211327896 |
| Nexxen (bidding)* * پیشاز ۱ مه ۲۰۲۴، این شبکه «UnrulyX» نام داشت. | 2831998725945605450 |
| OneTag Exchange (bidding) | 4873891452523427499 |
| OpenX (bidding) | 4918705482605678398 |
| Pangle | 4069896914521993236 |
| Pangle KR SDK (bidding) | 12171279046073404914 |
| Pangle ROW SDK (bidding) | 3525379893916449117 |
| Pangle US SDK (bidding) | 15999446638585856012 |
| PubMatic (پیشنهاد قیمت) | 3841544486172445473 |
| PubMatic OpenWrap SDK | 7702975372504485373 |
| PubMatic OpenWrap SDK (bidding) | 1234567890123456789 |
| پویش مدل رزرو | 7068401028668408324 |
| افزایش (پیشنهاد قیمت) | 6816468518946650043 |
| Sharethrough (پیشنهاد قیمت) | 5247944089976324188 |
| Smaato (پیشنهاد قیمت) | 3362360112145450544 |
| Sonobi (bidding) | 3270984106996027150 |
| TripleLift (bidding) | 8332676245392738510 |
| Unity Ads | 4970775877303683148 |
| Unity Ads (bidding) | 7069338991535737586 |
| Verve Group (bidding) | 5013176581647059185 |
| Vpon | 1940957084538325905 |
| Yieldmo (پیشنهاد قیمت) | 4193081836471107579 |
| YieldOne (پیشنهاد قیمت) | 3154533971590234104 |
| زاکس | 5506531810221735863 |
پاداش دادن به کاربر
هنگام تصمیمگیری درباره زمان پاداش دادن به کاربر، مهم است که بین تجربه کاربری و اعتبارسنجی پاداش تعادل برقرار کنید. فراخوانیهای برگشتی سمت سرور ممکن است قبلاز رسیدن به سیستمهای خارجی با تأخیر مواجه شوند. بنابراین، بهترین روش توصیهشده این است که از فراخوانی برگشتی سمت کارخواه برای پاداش دادن فوری به کاربر استفاده کنید، و درعینحال اعتبارسنجی همه پاداشها را پساز دریافت فراخوانیهای برگشتی سمت سرور انجام دهید. این رویکرد تجربه کاربری خوبی ارائه میدهد و درعینحال اعتبار پاداشهای اعطاشده را تضمین میکند.
بااینحال، برای برنامههایی که اعتبار پاداش در آنها حیاتی است (برای مثال، پاداش بر اقتصاد درونبازی برنامهتان تأثیر میگذارد) و تأخیر در اعطای پاداش قابلقبول است، انتظار برای فراخوانی برگشتی تأییدشده سمت سرور ممکن است بهترین رویکرد باشد.
دادههای سفارشی
برنامههایی که در بازخوانیهای درستیسنجی سمت سرور به دادههای اضافی نیاز دارند باید از
ویژگی دادههای سفارشی آگهیهای پاداشدار استفاده کنند. هر مقدار رشتهای که در شیء آگهی پاداشدار تنظیم شده باشد به پارامتر پُرسمان custom_data در فراخوان SSV ارسال میشود. اگر هیچ مقدار داده سفارشیای تنظیم نشده باشد، مقدار پارامتر پُرسمان custom_data در فراخوانی SSV وجود نخواهد داشت.
مثال زیر گزینههای SSV را پساز بار شدن آگهی پاداشدار تنظیم میکند:
Swift
RewardedAd.load(with:"AD_UNIT_ID", request: request, completionHandler: { [self] ad, error in if let error != error { rewardedAd = ad let options = ServerSideVerificationOptions() options.customRewardString = "SAMPLE_CUSTOM_DATA_STRING" rewardedAd.serverSideVerificationOptions = options } })
آبجکتیو-سی
GADRequest *request = [GADRequest request]; [GADRewardedAd loadWithAdUnitID:@"AD_UNIT_ID" request:request completionHandler:^(GADRewardedAd *ad, NSError *error) { if (error) { // Handle Error return; } self.rewardedAd = ad; GADServerSideVerificationOptions *options = [[GADServerSideVerificationOptions alloc] init]; options.customRewardString = @"SAMPLE_CUSTOM_DATA_STRING"; ad.serverSideVerificationOptions = options; }];
درستیسنجی دستی «درستیسنجی سمت سرور» پاداشدار
مراحل انجامشده توسط کلاس 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 ×tamp=150777823&transaction_id=12...DEF&user_id=1234567&signature=ME...Z1c&key_id=1268887
کد زیر نشان میدهد که چگونه محتوای موردتأیید را از نشانی وب فراخوان بهعنوان آرایه بایت 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 از نشانی وب پاسخ تماس
بااستفاده از مقدار queryString از مرحله قبلی، پارامترهای پُرسمان signature و
key_id را از نشانی وب بازگشت تماس تجزیه کنید، همانطور که در زیر نشان داده شده است:
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()));
انجام درستیسنجی
مرحله نهایی درستیسنجی محتوای نشانی وب فراخوانی با کلید عمومی مناسب است. نگاشت برگرداندهشده از روش
parsePublicKeysJson را بگیرید و از پارامتر key_id از نشانی وب
فراخوانی برگشتی استفاده کنید تا کلید عمومی را از آن نگاشت دریافت کنید. سپس امضا را با آن کلید عمومی درستیسنجی میکند. این مراحل در زیر در روش 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);
}
}
اگر روش بدون ایجاد استثنا اجرا شود، نشانی وب فراخوانی با موفقیت درستیسنجی شده است.
پرسشگان
- آیا میتوانم کلید عمومی ارائهشده توسط سرور کلید AdMob را در حافظه نهان ذخیره کنم؟
- توصیه میکنیم کلید عمومی ارائهشده توسط سرور کلید AdMob را ذخیره کنید تا تعداد عملیات موردنیاز برای اعتبارسنجی بازخوانیهای SSV کاهش یابد. بااینحال، توجه داشته باشید که کلیدهای عمومی بهطور منظم چرخش میکنند و نباید بیشتر از ۲۴ ساعت در حافظه نهان ذخیره شوند.
- کلیدهای عمومی ارائهشده توسط سرور کلید AdMob هر چند وقت یکبار چرخش میکنند؟
- کلیدهای عمومی ارائهشده توسط سرور کلید AdMob طبق زمانبندی متغیری چرخش میکنند. برای اطمینان از اینکه درستیسنجی فراخوانیهای SSV همچنان طبق انتظار کار میکند، کلیدهای عمومی نباید بیشتر از ۲۴ ساعت ذخیره موقت شوند.
- اگر سرورم دردسترس نباشد چه اتفاقی میافتد؟
- Google انتظار دارد کد پاسخ وضعیت موفقیت
HTTP 200 OKرا برای فراخوانیهای SSV دریافت کند. اگر سرورتان دردسترس نباشد یا پاسخ موردانتظار را ارائه ندهد، Google تا پنج بار در فواصل یک ثانیهای برای ارسال فراخوانهای SSV تلاش مجدد خواهد کرد. - چگونه میتوانم درستیسنجی کنم که بازخوانهای SSV از Google میآیند؟
- از جستجوی ساناد معکوس برای تأیید اینکه بازخوانیهای SSV از Google منشأ میگیرند استفاده کنید.