ตรวจสอบการเรียกกลับของการยืนยันฝั่งเซิร์ฟเวอร์ (SSV)

จัดทุกอย่างให้เป็นระเบียบอยู่เสมอด้วยคอลเล็กชัน บันทึกและจัดหมวดหมู่เนื้อหาตามค่ากำหนดของคุณ

การเรียกกลับการยืนยันฝั่งเซิร์ฟเวอร์คือคําขอ URL ที่มีพารามิเตอร์การค้นหาซึ่ง Google ขยายให้ ซึ่ง Google จะส่งไปยังระบบภายนอกเพื่อแจ้งให้ทราบว่าผู้ใช้ควรได้รับรางวัลจากการโต้ตอบกับโฆษณาคั่นระหว่างหน้าที่มีการให้รางวัลหรือโฆษณาที่มีการให้รางวัล โค้ดเรียกกลับ SSV (การยืนยันฝั่งเซิร์ฟเวอร์) ที่มีการให้รางวัลให้การป้องกันอีกชั้นจากการปลอมแปลงโค้ดเรียกกลับฝั่งไคลเอ็นต์เพื่อตอบแทนผู้ใช้

คู่มือนี้แสดงวิธียืนยันโค้ดเรียกกลับ SSV ที่มีการให้รางวัลโดยใช้ไลบรารีการเข้ารหัสของบุคคลที่สามของ Tink Java Apps เพื่อให้แน่ใจว่าพารามิเตอร์การค้นหาในโค้ดเรียกกลับเป็นค่าที่ถูกต้อง แม้ว่า Tink จะใช้เพื่อวัตถุประสงค์ของคู่มือนี้ แต่คุณก็มีตัวเลือกในการใช้ไลบรารีของบุคคลที่สามที่รองรับ ECDSA นอกจากนี้ คุณยังทดสอบเซิร์ฟเวอร์ด้วยเครื่องมือทดสอบใน UI ของ AdMob ได้อีกด้วย

ดูตัวอย่างที่ใช้งานได้จริงนี้โดยใช้ Java Springt-boot

สิ่งที่ต้องดำเนินการก่อน

ใช้ RewardAdsVerifier จากไลบรารี 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

โค้ดเรียกกลับการยืนยันฝั่งเซิร์ฟเวอร์มีพารามิเตอร์การค้นหาที่อธิบายการโต้ตอบกับโฆษณาที่มีการให้รางวัล ชื่อพารามิเตอร์ คําอธิบาย และค่าพารามิเตอร์ จะแสดงไว้ด้านล่างนี้ ระบบจะส่งพารามิเตอร์ตามลําดับตัวอักษร

ชื่อพารามิเตอร์ คำอธิบาย ค่าตัวอย่าง
เครือข่ายโฆษณา ตัวระบุแหล่งที่มาของโฆษณาสําหรับแหล่งที่มาของโฆษณานี้ ชื่อแหล่งที่มาของโฆษณาที่สอดคล้องกับค่ารหัสจะแสดงอยู่ในส่วนตัวระบุแหล่งที่มาของโฆษณา 1953547073528090325
หน่วยโฆษณา รหัสหน่วยโฆษณา AdMob ที่ใช้ในการขอโฆษณาที่มีการให้รางวัล 2747237135
ข้อมูลที่กําหนดเอง สตริงข้อมูลที่กําหนดเองโดย customRewardString

หากไม่มีแอปให้บริการสตริงข้อมูลที่กําหนดเอง ค่าพารามิเตอร์การค้นหานี้จะไม่แสดงในการเรียกกลับ SSV

SAMPLE_CUSTOM_DATA_STRING
รหัสคีย์ คีย์ที่จะใช้เพื่อยืนยันการเรียกกลับ SSV ค่านี้จะแมปกับคีย์สาธารณะที่เซิร์ฟเวอร์คีย์ AdMob ระบุไว้ 1234567890
จํานวนรางวัล จํานวนรางวัลที่ระบุในการตั้งค่าหน่วยโฆษณา 5
รางวัล_สินค้า ได้รางวัลตามที่ระบุไว้ในการตั้งค่าหน่วยโฆษณา เหรียญ
ลายเซ็น ลายเซ็นสําหรับการเรียกกลับ SSV ที่ AdMob สร้างขึ้น MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMtmgJHYYW9k2_icM9LFMY
การประทับเวลา การประทับเวลาที่ผู้ใช้จะได้รับรางวัลเป็นเวลา Epoch ในหน่วยมิลลิวินาที 1507770365237823
transaction_id ตัวระบุที่เข้ารหัสแบบเลขฐานสิบหกที่ไม่ซ้ํากันสําหรับเหตุการณ์การมอบรางวัลแต่ละครั้งที่ AdMob สร้างขึ้น 18fa792de1bca816048293fc71035638
user_id ตัวระบุผู้ใช้จาก userIdentifier

หากแอปไม่ได้ให้ตัวระบุผู้ใช้ไว้ พารามิเตอร์การค้นหานี้จะไม่อยู่ในโค้ดเรียกกลับ SSV

1234567

ตัวระบุแหล่งที่มาของโฆษณา

ชื่อและแหล่งที่มาของโฆษณา

ชื่อแหล่งที่มาของโฆษณา รหัสแหล่งที่มาของโฆษณา
Aarki (การเสนอราคา)5240798063227064260
การสร้างโฆษณา (การเสนอราคา)1477265452970951479
AdColony15586990674969969776
AdColony (ไม่ใช่ SDK) (การเสนอราคา)4600416542059544716
AdColony (การเสนอราคา)6895345910719072481
AdFalcon3528208921554210682
เครือข่าย AdMob5450213213286189855
ผลลัพธ์โฆษณา10593873382626181482
โฆษณา AMo17253994435944008978
แอปโลฟิน1063618907739174004
Applovin (การเสนอราคา)1328079684332308356
แผนภูมิ2873236629771172317
แพลตฟอร์มช็อกโกแลต (การเสนอราคา)6432849193975106527
หลายแชแนล (MdotM)9372067028804390441
เหตุการณ์ที่กำหนดเอง18351550913290782395
DT Exchange
ก่อนวันที่ 21 กันยายน 2022 เครือข่ายนี้เรียกว่า "Fyber"
4839637394546996422
Fluct (การเสนอราคา)8419777862490735710
Flurry3376427960656545613
i-mobile5208827440166355534
ปรับปรุงดิจิทัล (การเสนอราคา)159382223051638006
Index Exchange (การเสนอราคา)4100650709078789802
InMobi7681903010231960328
InMobi (การเสนอราคา)6325663098072678541
ธาตุเหล็ก6925240245545091930
Leadbolt2899150749497968595
LG U+โฆษณา18298738678491729107
Maio7505118203095108657
maio (การเสนอราคา)1343336733822567166
Media.net (การเสนอราคา)2127936450554446159
โฆษณาเฮาส์แอ็ดที่ใช้สื่อกลาง6060308706800320801
Meta Audience Network
ก่อนวันที่ 6 มิถุนายน 2022 เครือข่ายนี้เรียกว่า "Facebook Audience Network"
10568273599589928883
Meta Audience Network (การเสนอราคา)
ก่อนวันที่ 6 มิถุนายน 2022 เครือข่ายนี้เรียกว่า "Facebook Audience Network (bidding)"
11198165126854996598
MobFox8079529624516381459
MoPub (เลิกใช้งาน)10872986198578383917
เป้าหมายของฉัน8450873672465271579
Nend9383070032774777750
ONE by AOL (Millennial Media)6101072188699264581
ONE by AOL (Nexage)3224789793037044399
OpenX (การเสนอราคา)4918705482605678398
Pangle (การเสนอราคา)3525379893916449117
PubMatic (การเสนอราคา)3841544486172445473
แคมเปญแบบจองล่วงหน้า7068401028668408324
RhythmOne (การเสนอราคา)2831998725945605450
Rubicon (การเสนอราคา)3993193775968767067
ดาวเคราะห์ SK734341340207269415
ส่วนแบ่งการตลาด (การเสนอราคา)5247944089976324188
Smaato (การเสนอราคา)3362360112145450544
อิเควทิฟ (การเสนอราคา)*

* ก่อนวันที่ 12 มกราคม 2023 เครือข่ายนี้เรียกว่า "Smart Adserver"

5970199210771591442
Sonobi (การเสนอราคา)3270984106996027150
Tapjoy7295217276740746030
Tapjoy (การเสนอราคา)4692500501762622178
GDT สมบูรณ์7007906637038700218
TripleLift (การเสนอราคา)8332676245392738510
โฆษณา Unity4970775877303683148
UnrulyX (การเสนอราคา)2831998725945605450
Verizon Media7360851262951344112
VPN1940957084538325905
การสร้างรายได้จาก Brand Lift*

* ก่อนวันที่ 30 มกราคม 2023 เครือข่ายนี้เรียกว่า "Vungle"

1953547073528090325
การสร้างรายได้ที่เพิ่มขึ้น (การเสนอราคา)*

* ก่อนวันที่ 30 มกราคม 2023 เครือข่ายนี้เรียกว่า "Vungle"

4692500501762622185
Yieldmo (การเสนอราคา)4193081836471107579
YieldOne (การเสนอราคา)3154533971590234104
ซูกส์5506531810221735863

การให้รางวัลผู้ใช้

การสร้างสมดุลระหว่างประสบการณ์ของผู้ใช้กับการตรวจสอบรางวัลมีความสําคัญเมื่อตัดสินใจว่าจะให้รางวัลผู้ใช้เมื่อใด โค้ดเรียกกลับฝั่งเซิร์ฟเวอร์อาจทํางานล่าช้าก่อนที่จะเข้าถึงระบบภายนอก ดังนั้นแนวทางปฏิบัติแนะนําคือให้ใช้โค้ดเรียกกลับฝั่งไคลเอ็นต์เพื่อตอบแทนผู้ใช้ทันที โดยอาจใช้การตรวจสอบรางวัลทั้งหมดเมื่อได้รับการเรียกกลับฝั่งเซิร์ฟเวอร์ วิธีการนี้มอบประสบการณ์ที่ดีแก่ผู้ใช้ไปพร้อมๆ กับการตรวจสอบความถูกต้องของรางวัลที่มอบให้

อย่างไรก็ตาม สําหรับแอปพลิเคชันที่มีความถูกต้องของรางวัล (เช่น รางวัลจะมีผลกับเศรษฐกิจในเกมของแอป) และความล่าช้าในการให้รางวัลเป็นสิ่งที่ยอมรับได้ การรอโค้ดเรียกกลับจากฝั่งเซิร์ฟเวอร์ที่ยืนยันแล้วอาจเหมาะสมที่สุด

ข้อมูลที่กำหนดเอง

แอปที่ต้องมีข้อมูลเพิ่มเติมในการเรียกกลับสําหรับการยืนยันฝั่งเซิร์ฟเวอร์ควรใช้ฟีเจอร์ข้อมูลที่กําหนดเองของโฆษณาที่มีการให้รางวัล ระบบจะส่งค่าสตริงที่กําหนดไว้ในออบเจ็กต์โฆษณาที่มีการให้รางวัลไปยังพารามิเตอร์การค้นหา custom_data ของโค้ดเรียกกลับ SSV หากไม่ได้กําหนดค่าข้อมูลที่กําหนดเองไว้ ค่าพารามิเตอร์การค้นหา custom_data จะไม่แสดงในโค้ดเรียกกลับ SSV

ตัวอย่างโค้ดต่อไปนี้แสดงวิธีตั้งค่าตัวเลือก SSV หลังจากที่โหลดโฆษณาที่มีการให้รางวัลแล้ว

Swift

GADRewardedAd.load(withAdUnitID:"ca-app-pub-3940256099942544/1712485313",
                       request: request,
                       completionHandler: { [self] ad, error in
      if let error != error {
      rewardedAd = ad
      let options = GADServerSideVerificationOptions()
      options.customRewardString = "SAMPLE_CUSTOM_DATA_STRING"
      rewardedAd.serverSideVerificationOptions = options
    }

Objective-C

GADRequest *request = [GADRequest request];
[GADRewardedAd loadWithAdUnitID:@"ca-app-pub-3940256099942544/1712485313"
                        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;
              }];

การยืนยัน 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();
}

โปรดทราบว่าระบบจะหมุนเวียนคีย์สาธารณะเป็นประจํา คุณจะได้รับอีเมลแจ้งการหมุนเวียนที่กําลังจะเกิดขึ้น หากคุณกําลังแคชคีย์สาธารณะ คุณควรอัปเดตคีย์เมื่อได้รับอีเมลนี้

เมื่อดึงข้อมูลคีย์สาธารณะแล้ว จะต้องแยกวิเคราะห์คีย์ดังกล่าว เมธอด 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;
}

รับเนื้อหาที่จะได้รับการยืนยัน

พารามิเตอร์การค้นหา 2 รายการสุดท้ายของการเรียกกลับ 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
&timestamp=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 อีกครั้งสูงสุด 5 ครั้งในระยะเวลา 1 วินาที
ฉันจะยืนยันได้อย่างไรว่าการติดต่อกลับ SSV มาจาก Google
ใช้การค้นหา DNS แบบย้อนกลับเพื่อยืนยันว่าการเรียกกลับ SSV มาจาก Google