אימות קריאות חוזרות (callback) לצורך אימות בצד השרת

קריאות חוזרות (callback) לאימות בצד השרת הן בקשות לכתובות URL, עם פרמטרים של שאילתות מורחבים על ידי Google, שנשלחים על ידי Google למערכת חיצונית כדי ליידע את המשתמש שצריך לקבל תגמול על אינטראקציה עם מודעה מתגמלת או מודעת מעברון מתגמלת. קריאה חוזרת (callback) של SSV מתגמל (אימות בצד השרת) מספקים שכבה נוספת של הגנה מפני זיוף של קריאות חוזרות (callback) בצד הלקוח כדי לתגמל משתמשים.

במדריך הזה מוסבר איך לאמת התקשרות חזרה של SSV מתגמל. Tink Java Apps של צד שלישי ספרייה קריפטוגרפית כדי לוודא שהפרמטרים של השאילתה בקריאה החוזרת והערכים הלגיטימיים. על אף ש-Tink משמש למדריך זה, יש לך אפשרות משתמשים בכל ספרייה של צד שלישי שתומכת ECDSA. אפשר גם לבדוק את השרת באמצעות הבדיקה בממשק המשתמש של AdMob.

רוצה לראות איך זה עובד? דוגמה באמצעות Spring-boot.

דרישות מוקדמות

שימוש ב-RewardedAdsVerifier מספריית האפליקציות של Tink ל-Java

מאגר Tink Java Apps ב-GitHub כוללת RewardedAdsVerifier Assistant מסוג 'עוזר דיגיטלי' כדי להקטין את הקוד שנדרש לאימות קריאה חוזרת של SSV מתגמל. שימוש בכיתה הזו מאפשר לכם לאמת כתובת URL לקריאה חוזרת (callback) באמצעות הקוד הבא.

RewardedAdsVerifier verifier = new RewardedAdsVerifier.Builder()
    .fetchVerifyingPublicKeysWith(
        RewardedAdsVerifier.KEYS_DOWNLOADER_INSTANCE_PROD)
    .build();
String rewardUrl = ...;
verifier.verify(rewardUrl);

אם ה-method verify() פועלת בלי להעלות חריג, הקריאה החוזרת (callback) כתובת ה-URL אומתה בהצלחה. האפשרות תגמול המשתמש בקטע 'שיטות מומלצות' לגבי המקרים שבהם המשתמשים צריכים לקבל תגמול. עבור פירוט השלבים שבוצעו על ידי הכיתה הזו כדי לאמת התקשרות חזרה של SSV מתגמל, ניתן לקרוא את האימות הידני של מודעות מתגמלות SSV.

פרמטרים של קריאה חוזרת ל-SSV

קריאות חוזרות לאימות בצד השרת מכילות פרמטרים של שאילתה שמתארים את אינטראקציה עם מודעות מתגמלות. שמות הפרמטרים, התיאורים והערכים לדוגמה הם שמפורטות בהמשך. הפרמטרים נשלחים בסדר אלפביתי.

שם פרמטר תיאור ערך לדוגמה
ad_network המזהה של מקור המודעות שהגיש את המודעה. מקור המודעה שמות התואמים לערכי המזהה מופיעים בעמודה מודעה מזהי מקורות. 1953547073528090325
ad_unit מזהה יחידת המודעות ב-AdMob שדרכו נשלחה הבקשה להצגת המודעה המתגמלת. 2747237135
custom_data מחרוזת נתונים מותאמת אישית כפי שסופקה על ידי ServerSideVerificationOptions::custom_data .

אם האפליקציה לא מספקת מחרוזת נתונים מותאמת אישית, פרמטר השאילתה הזה לא יופיע בקריאה החוזרת של ה-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
חותמת זמן חותמת הזמן של מועד התגמול של המשתמש כזמן של תקופה של זמן מערכת באלפיות השנייה. 1507770365237823
transaction_id מזהה ייחודי בקידוד הקסדצימלי לכל אירוע של הענקת פרס שנוצר על ידי AdMob. 18fa792de1bca816048293fc71035638
user_id מזהה המשתמש כפי שסופק על ידי ServerSideVerificationOptions::user_id.

אם האפליקציה לא מספקת מזהה משתמש, פרמטר השאילתה הזה לא יהיה בקריאה החוזרת של ה-SSV.

1234567

מזהים של מקורות מודעות

שמות ומזהים של מקורות מודעות

שם מקור המודעה מזהה של מקור מודעות
ארקי (בידינג)5240798063227064260
יצירת מודעות (בידינג)1477265452970951479
AdColony15586990674969969776
AdColony (ללא SDK) (בידינג)4600416542059544716
AdColony (בידינג)6895345910719072481
AdFalcon3528208921554210682
רשת AdMob5450213213286189855
Waterfall של רשת AdMob1215381445328257950
ADResult10593873382626181482
AMoAd17253994435944008978
אפלובין1063618907739174004
אפלובין (בידינג)1328079684332308356
Chartboost2873236629771172317
פלטפורמת שוקולד (בידינג)6432849193975106527
CrossChannel (MdotM)9372067028804390441
אירוע מותאם אישית18351550913290782395
DT Exchange*
* לפני 21 בספטמבר 2022 הרשת הזו נקראה 'Fyber Marketplace'.
2179455223494392917
EMX (בידינג)8497809869790333482
תנודות (בידינג)8419777862490735710
סערה3376427960656545613
Fyber*
* מקור המודעות הזה משמש לדיווחים היסטוריים.
4839637394546996422
i-mobile5208827440166355534
שיפור הדיגיטל (בידינג)159382223051638006
החלפת אינדקס (בידינג)4100650709078789802
InMobi7681903010231960328
InMobi (בידינג)6325663098072678541
InMobi Exchange (בידינג)5264320421916134407
IronSource6925240245545091930
מודעות ironSource (בידינג)1643326773739866623
Leadbolt2899150749497968595
LG U+AD18298738678491729107
רשת המודעות של LINE3025503711505004547
Maio7505118203095108657
maio (בידינג)1343336733822567166
Media.net (בידינג)2127936450554446159
מודעות הבית שמשתתפות בתהליך בחירת הרשת6060308706800320801
Meta Audience Network*
* הרשת הזו נקראה לפני 6 ביוני 2022 'Facebook Audience Network'.
10568273599589928883
Meta Audience Network (בידינג)*
* הרשת הזו נקראה Facebook Audience Network (בידינג) לפני 6 ביוני 2022.
11198165126854996598
Mintegral1357746574408896200
Mintegral (בידינג)6250601289653372374
MobFox8079529624516381459
MobFox (בידינג)3086513548163922365
MoPub (הוצאה משימוש)10872986198578383917
myTarget8450873672465271579
Nend9383070032774777750
Nexxen (בידינג)*

* לפני 1 במאי 2024, הרשת הזו נקראה UnrulyX.

2831998725945605450
ONE by AOL (Millennial Media)6101072188699264581
ONE by AOL (Nexage)3224789793037044399
OneTag Exchange (בידינג)4873891452523427499
OpenX (בידינג)4918705482605678398
Pangle4069896914521993236
Pangle (בידינג)3525379893916449117
PubMatic (בידינג)3841544486172445473
קמפיין בהזמנה7068401028668408324
RhythmOne (בידינג)2831998725945605450
סמל רובי (בידינג)3993193775968767067
כוכב SK734341340207269415
שיתוף המרות (בידינג)5247944089976324188
Smaato (בידינג)3362360112145450544
Equativ (בידינג)*

* לפני 12 בינואר 2023, הרשת הזו נקראה 'Smart Adserver'.

5970199210771591442
Sonobi (בידינג)3270984106996027150
Tapjoy7295217276740746030
Tapjoy (בידינג)4692500501762622178
Tencent GDT7007906637038700218
TripleLift (בידינג)8332676245392738510
מודעות Unity4970775877303683148
מודעות Unity (בידינג)7069338991535737586
Verizon Media7360851262951344112
קבוצת Verve (בידינג)5013176581647059185
Vpon1940957084538325905
Liftoff Monetize*

* לפני 30 בינואר 2023 הרשת הזו נקראה Vungle.

1953547073528090325
Liftoff Monetize (בידינג)*

* לפני 30 בינואר 2023 הרשת הזו נקראה Vungle (בידינג).

4692500501762622185
תפוקה (בידינג)4193081836471107579
YieldOne (בידינג)3154533971590234104
זקס5506531810221735863

תגמול למשתמשים

כשמקבלים החלטה, חשוב לאזן בין חוויית המשתמש לבין אימות התגמול מתי לתגמל משתמש. ייתכנו עיכובים בקריאות חוזרות בצד השרת להגיע למערכות חיצוניות. לכן, השיטה המומלצת היא להשתמש הקריאה החוזרת בצד הלקוח כדי לתגמל את המשתמש באופן מיידי, תוך כדי ביצוע אימות של כל התגמולים עם קבלת הקריאות החוזרות בצד השרת. הזה מספקת חוויית משתמש טובה תוך הבטחת תקפות בפרסים.

עם זאת, באפליקציות שבהן תוקף התגמולים הוא קריטי (לדוגמה, הפרס משפיע על כלכלת האפליקציה במשחק) והעיכובים בהענקת פרסים הם סביר, המתנה לקריאה חוזרת (callback) מאומתת בצד השרת .

נתונים בהתאמה אישית

אפליקציות שדורשות נתונים נוספים בקריאות חוזרות (callback) של אימות בצד השרת פיצ'ר הנתונים בהתאמה אישית במודעות המתגמלות. כל ערך מחרוזת שמוגדר במודעה מתגמלת האובייקט מועבר אל פרמטר השאילתה custom_data של הקריאה החוזרת ל-SSV. אם לא מוגדר ערך של נתונים מותאמים אישית, ערך הפרמטר custom_data של השאילתה לא יהיה נמצאים בקריאה החוזרת של ה-SSV.

דוגמת הקוד הבאה מראה איך מגדירים נתונים בהתאמה אישית במודעה מתגמלת לפני בקשת מודעה.

  firebase::gma::RewardedAd* rewarded_ad;
  rewarded_ad = new firebase::gma::RewardedAd();

  firebase::gma::RewardedAd::ServerSideVerificationOptions options;
  options.custom_data = "SAMPLE_CUSTOM_DATA_STRING";
  rewarded_ad->SetServerSideVerificationOptions(options);

כדי להגדיר את מחרוזת התגמולים המותאמת אישית, עליך לעשות זאת לפני ההצגה את המודעה.

אימות ידני של אימות דו-שלבי (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();
}

חשוב לזכור שמפתחות ציבוריים עוברים רוטציה באופן קבוע. יישלח אליך אימייל כדי להודיע על כך לגבי סבב קרוב. אם המפתחות הציבוריים נשמרים במטמון, צריך לעדכן אותם המפתחות.

אחרי אחזור המפתחות הציבוריים, יש לנתח אותם. ה-method 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 לשליחת קריאה חוזרת (callback) של תגמולים אל https://www.myserver.com/mypath בקטע הקוד הבא מוצגת דוגמה מתגמלת קריאה חוזרת של SSV עם הדגשה של התוכן שצריך לאמת.

https://www.myserver.com/path?ad_network=54...55&ad_unit=12345678&reward_amount=10&reward_item=coins
&timestamp=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 לקריאה חוזרת

תוך שימוש בערך queryString מהשלב הקודם, מנתחים את signature key_id פרמטרים של שאילתה מכתובת ה-URL לקריאה חוזרת (callback), כפי שמוצג בהמשך:

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);
  }
}

אם ה-method פועלת מבלי להקפיץ הודעת שגיאה, כתובת ה-URL של הקריאה החוזרת (callback) הייתה אומת בהצלחה.

שאלות נפוצות

האם אפשר לשמור במטמון את המפתח הציבורי שסופק על ידי שרת המפתחות של AdMob?
מומלץ לשמור במטמון את המפתח הציבורי שסופק על ידי מפתח AdMob שרת לצמצום מספר הפעולות הנדרשות לאימות SSV קריאה חוזרת (callback). עם זאת, חשוב לדעת שמפתחות ציבוריים עוברים רוטציה באופן קבוע, יישמרו במטמון למשך יותר מ-24 שעות.
באיזו תדירות מתבצעת רוטציה של המפתחות הציבוריים שמסופקים על ידי שרת המפתחות של AdMob?
המפתחות הציבוריים שמסופקים על ידי שרת המפתחות של AdMob מוחלפים במשתנה לוח זמנים. כדי להבטיח שהאימות של קריאה חוזרת (callback) של SSV ימשיך לפעול כמו אין לשמור במטמון מפתחות ציבוריים למשך יותר מ-24 שעות.
מה קורה אם לא ניתן להגיע לשרת שלי?
Google מצפה לקוד תגובה של HTTP 200 OK לסטטוס הצלחה עבור SSV קריאה חוזרת (callback). אם לא ניתן להגיע לשרת או שהוא לא מספק את הצפי תגובה, Google תנסה לשלוח שוב התקשרות חזרה של SSV עד חמש פעמים במרווחי זמן של שנייה אחת.
איך אפשר לוודא שהקריאות החוזרות של בצד שלישי מגיעות מ-Google?
להשתמש בשאילתת DNS הפוכה כדי לאמת שהקריאות החוזרות של SSV מגיעות מ-Google.