קריאות חוזרות (callbacks) במסגרת אימות בצד השרת הן בקשות לכתובות URL, עם פרמטרים של שאילתה שמורחבים על ידי Google, שנשלחות על ידי Google למערכת חיצונית כדי להודיע לה שצריך לתגמל משתמש על אינטראקציה עם מודעה מתגמלת או עם מודעה מתגמלת מסוג מודעה מעברון. קריאות חוזרות (callbacks) של SSV (אימות בצד השרת) עם תגמול מספקות שכבת הגנה נוספת מפני זיוף של קריאות חוזרות בצד הלקוח כדי לתגמל משתמשים.
במדריך הזה מוסבר איך לאמת קריאות חוזרות (callbacks) של SSV עם תגמול באמצעות ספריית הצפנה של צד שלישי, Tink Java Apps, כדי לוודא שפרמטרים של שאילתות בקריאה החוזרת הם ערכים חוקיים. למרות שנעשה שימוש ב-Tink למטרות המדריך הזה, יש לכם אפשרות להשתמש בכל ספרייה של צד שלישי שתומכת ב-ECDSA. אפשר גם לבדוק את השרת באמצעות כלי הבדיקה בממשק המשתמש של AdMob.
כדאי לעיין בדוגמה ל-SSV עם פרסים באמצעות Java spring-boot.
דרישות מוקדמות
- מפעילים את אימות המודעות המתגמלות בצד השרת ביחידת המודעות.
שימוש ב-RewardedAdsVerifier מספריית האפליקציות של Tink ל-Java
המאגר ב-GitHub של Tink Java Apps כולל את הכיתה המסייעת RewardedAdsVerifier
כדי לצמצם את הקוד הנדרש לאימות קריאה חוזרת מסוג SSV עם פרס.
השימוש בכיתה הזו מאפשר לאמת כתובת URL להודעת חזרה באמצעות הקוד הבא.
RewardedAdsVerifier verifier = new RewardedAdsVerifier.Builder()
.fetchVerifyingPublicKeysWith(
RewardedAdsVerifier.KEYS_DOWNLOADER_INSTANCE_PROD)
.build();
String rewardUrl = ...;
verifier.verify(rewardUrl);
אם השיטה verify()
פועלת בלי להעלות חריגה, המשמעות היא שהכתובת של ה-callback אומתה בהצלחה. בקטע מתן פרסים למשתמשים מפורטות שיטות מומלצות לגבי הזמנים שבהם כדאי לתת למשתמשים פרסים. בקטע אימות ידני של קריאות חוזרות (callbacks) מסוג SSV עם תגמול מפורט פירוט של השלבים שבהם מבצעת הכיתה הזו את האימות של קריאות חוזרות מסוג SSV עם תגמול.
פרמטרים של קריאה חוזרת (callback) של SSV
קריאות חוזרות (callbacks) במסגרת האימות בצד השרת מכילות פרמטרים של שאילתות שמתארים את האינטראקציה של המשתמש עם מודעת הפרסום המניבה תגמול. בהמשך מפורטים השמות, התיאורים והערכים לדוגמה של הפרמטרים. הפרמטרים נשלחים בסדר אלפביתי.
שם פרמטר | תיאור | ערך לדוגמה |
---|---|---|
ad_network | מזהה מקור המודעות של מקור המודעות שסיפק את המודעה הזו. השמות של מקורות המודעות שתואמים לערכי המזהים מפורטים בקטע מזהי מקורות מודעות. | 1953547073528090325 |
ad_unit | מזהה יחידת המודעות ב-AdMob ששימש לבקשת המודעה המתגמלת. | 2747237135 |
custom_data | מחרוזת נתונים בהתאמה אישית כפי שסופקה על ידי
setCustomData .
אם האפליקציה לא מספקת מחרוזת נתונים מותאמת אישית, הערך של פרמטר השאילתה הזה לא יופיע בקריאה החוזרת של SSV. |
SAMPLE_CUSTOM_DATA_STRING |
key_id | המפתח שישמש לאימות קריאה חוזרת של SSV. הערך הזה ממופה למפתח ציבורי שמסופק על ידי שרת המפתחות של AdMob. | 1234567890 |
reward_amount | סכום התגמול כפי שצוין בהגדרות של יחידת המודעות. | 5 |
reward_item | פריט התגמול כפי שצוין בהגדרות של יחידת המודעות. | מטבעות |
signature | חתימה לקריאה חוזרת של SSV שנוצרה על ידי AdMob. | MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMtmgJHYYW9k2_icM9LFMY |
חותמת זמן | חותמת הזמן של המועד שבו המשתמש קיבל את התגמול, לפי תקופת זמן המערכת (Epoch) באלפיות שנייה. | 1507770365237823 |
transaction_id | מזהה ייחודי בקידוד הקסדצימלי לכל אירוע הענקת פרס שנוצר על ידי AdMob. | 18fa792de1bca816048293fc71035638 |
user_id | מזהה המשתמש כפי שסופק על ידי setUserId .
אם האפליקציה לא מספקת מזהה משתמש, פרמטר השאילתה הזה לא יופיע בקריאה החוזרת של SSV. |
1234567 |
מזהים של מקורות למודעות
שמות ומזהים של מקורות למודעות
שם מקור המודעה | מזהה מקור המודעות |
---|---|
Aarki (בידינג) | 5240798063227064260 |
יצירת מודעות (בידינג) | 1477265452970951479 |
AdColony | 15586990674969969776 |
AdColony (ללא SDK) (בידינג) | 4600416542059544716 |
AdColony (בידינג) | 6895345910719072481 |
AdFalcon | 3528208921554210682 |
רשת AdMob | 5450213213286189855 |
Waterfall של רשת 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 |
שיפור הביצועים בדיגיטל (בידינג) | 159382223051638006 |
Index Exchange (בידינג) | 4100650709078789802 |
InMobi | 7681903010231960328 |
InMobi (בידינג) | 6325663098072678541 |
InMobi Exchange (בידינג) | 5264320421916134407 |
IronSource | 6925240245545091930 |
ironSource Ads (בידינג) | 1643326773739866623 |
Leadbolt | 2899150749497968595 |
LG U+AD | 18298738678491729107 |
LINE Ads Network | 3025503711505004547 |
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 | 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 planet | 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 |
מתגמלים את המשתמש
חשוב לאזן בין חוויית המשתמש לבין אימות התגמול כשמחליטים מתי לתגמל משתמש. יכול להיות שיחולו עיכובים לפני שהקריאות החוזרות (callbacks) בצד השרת יגיעו למערכות חיצוניות. לכן, השיטה המומלצת היא להשתמש בקריאה החוזרת (callback) בצד הלקוח כדי לתגמל את המשתמש באופן מיידי, ולבצע אימות של כל התגמולים עם קבלת הקריאות החוזרות בצד השרת. הגישה הזו מספקת חוויית משתמש טובה תוך שמירה על תקינות התגמולים.
עם זאת, באפליקציות שבהן תקינות הפרס קריטית (לדוגמה, הפרס משפיע על הכלכלה במשחק של האפליקציה) ועיכובים במתן הפרסים מקובלים, ייתכן שהגישה הטובה ביותר היא להמתין להודעת החזרה (callback) המאומתת בצד השרת.
נתונים בהתאמה אישית
באפליקציות שדורשות נתונים נוספים בקריאות חוזרות (callbacks) לאימות בצד השרת, צריך להשתמש בתכונה 'נתונים מותאמים אישית' של מודעות מתגמלות. כל ערך מחרוזת שמוגדר באובייקט של מודעה עם תגמול מועבר לפרמטר השאילתה custom_data
של קריאה חוזרת (callback) של SSV. אם לא מגדירים ערך נתונים מותאם אישית, ערך פרמטר השאילתה custom_data
לא יופיע בקריאה החוזרת של SSV.
בדוגמה הבאה מגדירים את האפשרויות של 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 } })
אם רוצים להגדיר מחרוזת תגמול בהתאמה אישית, צריך לעשות זאת לפני הצגת המודעה.
אימות ידני של מודעות מתגמלות עם מודעות וידאו
בהמשך מפורטים השלבים שבהם מבצעת הכיתה RewardedAdsVerifier
את האימות של SSV עם פרס. קטעי הקוד שכלולים כאן הם ב-Java ומשתמשים בספריית הצד השלישי Tink, אבל אפשר להטמיע את השלבים האלה בשפה שבוחרים, באמצעות כל ספריית צד שלישי שתומכת ב-ECDSA.
אחזור מפתחות ציבוריים
כדי לאמת קריאה חוזרת (callback) מסוג SSV עם תגמול, צריך מפתח ציבורי שמסופק על ידי AdMob.
אפשר לאחזר רשימה של מפתחות ציבוריים שישמשו לאימות הקריאות החוזרות (callbacks) של 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 של קריאה חוזרת (callback) כמערך בייטים של 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 של הקריאה החוזרת (callback)
בעזרת הערך 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 של קריאת החזרה (callback) כדי לקבל את המפתח הציבורי מהמיפוי הזה. לאחר מכן מאמתים את החתימה באמצעות המפתח הציבורי הזה. השלבים האלה מוצגים בהמשך בשיטה 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 מתבצעת לפי לוח זמנים משתנה. כדי לוודא שהאימות של קריאות החזרה (callbacks) של SSV ימשיך לפעול כמצופה, אסור לשמור במטמון מפתחות ציבוריים למשך יותר מ-24 שעות.
- מה קורה אם לא ניתן לגשת לשרת?
- Google מצפה לקוד תגובה עם סטטוס הצלחה
HTTP 200 OK
בקריאות חזרה של SSV. אם אי אפשר ליצור קשר עם השרת או שהוא לא מספק את התשובה הצפויה, Google תנסה שוב לשלוח קריאות חזרה מסוג SSV עד חמש פעמים במרווחי זמן של שנייה אחת. - איך אפשר לוודא שהשיחות החוזרות של SSV מגיעות מ-Google?
- משתמשים בשאילתת DNS הפוכה כדי לוודא שההחזרות של קריאות SSV מגיעות מ-Google.