SSV Callback คือคำขอ URL ที่มีพารามิเตอร์การค้นหาซึ่ง Google ขยายและส่งไปยังระบบภายนอกเพื่อแจ้งให้ทราบว่าผู้ใช้ควรได้รับรางวัลสำหรับการโต้ตอบกับโฆษณาที่มีการให้รางวัลหรือโฆษณาคั่นระหว่างหน้าที่มีการให้รางวัล Rewarded SSV Callback จะช่วยเพิ่มการป้องกันอีกชั้นจากการปลอมแปลง Callback ทางฝั่งไคลเอ็นต์เพื่อมอบรางวัลให้แก่ผู้ใช้
คู่มือนี้แสดงวิธียืนยัน Rewarded SSV Callback โดยใช้ไลบรารีการเข้ารหัสของบุคคลที่สาม Tink Java Apps เพื่อให้มั่นใจว่าพารามิเตอร์การค้นหาใน Callback เป็นค่าที่ถูกต้อง แม้ว่าเราจะใช้ Tink เพื่อวัตถุประสงค์ของคู่มือนี้ แต่คุณก็มีตัวเลือกในการ ใช้ไลบรารีของบุคคลที่สามที่รองรับ ECDSA นอกจากนี้ คุณยังทดสอบเซิร์ฟเวอร์ด้วยเครื่องมือ ทดสอบใน UI ของ AdMob ได้ด้วย
ข้อกำหนดเบื้องต้น
- เปิดใช้การยืนยันฝั่งเซิร์ฟเวอร์ที่มีการให้รางวัลในหน่วยโฆษณา
ใช้ RewardedAdsVerifier จากไลบรารี Tink Java Apps
ที่เก็บ Tink Java Apps ใน GitHub มีคลาสตัวช่วย RewardedAdsVerifier เพื่อลดโค้ดที่จำเป็นในการยืนยัน Rewarded SSV Callback
การใช้คลาสนี้ช่วยให้คุณยืนยัน URL เรียกกลับด้วยโค้ดต่อไปนี้ได้
RewardedAdsVerifier verifier = new RewardedAdsVerifier.Builder()
.fetchVerifyingPublicKeysWith(
RewardedAdsVerifier.KEYS_DOWNLOADER_INSTANCE_PROD)
.build();
String rewardUrl = ...;
verifier.verify(rewardUrl);
หากเมธอด verify() ทำงานโดยไม่แสดงข้อยกเว้น แสดงว่าระบบยืนยัน URL เรียกกลับสำเร็จแล้ว ส่วนการให้รางวัลแก่ผู้ใช้
จะอธิบายแนวทางปฏิบัติแนะนำเกี่ยวกับเวลาที่ควรให้รางวัลแก่ผู้ใช้ หากต้องการดูรายละเอียดขั้นตอนที่คลาสนี้ดำเนินการเพื่อยืนยัน Rewarded SSV Callback คุณสามารถอ่านส่วนการยืนยัน Rewarded SSV ด้วยตนเอง
พารามิเตอร์ SSV Callback
SSV Callback มีพารามิเตอร์การค้นหาที่อธิบายการโต้ตอบกับโฆษณาที่มีการให้รางวัล ชื่อ คำอธิบาย และค่าตัวอย่างของพารามิเตอร์ แสดงอยู่ด้านล่าง ระบบจะส่งพารามิเตอร์ตามลำดับตัวอักษร
| ชื่อพารามิเตอร์ | คำอธิบาย | ค่าตัวอย่าง |
|---|---|---|
| ad_network | ตัวระบุแหล่งที่มาของโฆษณาสําหรับแหล่งที่มาของโฆษณาที่แสดงโฆษณานี้ ชื่อแหล่งที่มาของโฆษณา ที่สอดคล้องกับค่ารหัสจะแสดงอยู่ในส่วนตัวระบุแหล่งที่มาของโฆษณา | 1953547073528090325 |
| ad_unit | รหัสหน่วยโฆษณา AdMob ที่ใช้ขอโฆษณาที่มีการให้รางวัล | 2747237135 |
| key_id | คีย์ที่จะใช้เพื่อยืนยัน SSV Callback ค่านี้จะแมปกับคีย์สาธารณะ ที่ได้รับจากเซิร์ฟเวอร์คีย์ของ AdMob | 1234567890 |
| reward_amount | จำนวนรางวัลตามที่ระบุในการตั้งค่าหน่วยโฆษณา | 5 |
| reward_item | ไอเทมรางวัลตามที่ระบุในการตั้งค่าหน่วยโฆษณา | เหรียญ |
| ลายเซ็น | ลายเซ็นสำหรับ SSV Callback ที่ AdMob สร้างขึ้น | MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMtmgJHYYW9k2_icM9LFMY |
| การประทับเวลา | การประทับเวลาเมื่อผู้ใช้ได้รับรางวัลเป็นเวลา Epoch ในหน่วยมิลลิวินาที | 1507770365237823 |
| transaction_id | ตัวระบุที่ไม่ซ้ำกันที่เข้ารหัสฐาน 16 สำหรับเหตุการณ์การให้รางวัลแต่ละรายการที่ AdMob สร้างขึ้น | 18fa792de1bca816048293fc71035638 |
| user_id | ตัวระบุผู้ใช้ตามที่
SetUserId ระบุ
หากแอปไม่ได้ระบุตัวระบุผู้ใช้ พารามิเตอร์การค้นหานี้จะไม่อยู่ใน SSV Callback |
1234567 |
ตัวระบุแหล่งที่มาของโฆษณา
ชื่อและรหัสแหล่งที่มาของโฆษณา
| ชื่อแหล่งที่มาของโฆษณา | รหัสแหล่งที่มาของโฆษณา |
|---|---|
| Ad Generation (การเสนอราคา) | 1477265452970951479 |
| AdColony | 15586990674969969776 |
| AdColony (การเสนอราคา) | 6895345910719072481 |
| AdFalcon | 3528208921554210682 |
| เครือข่าย AdMob | 5450213213286189855 |
| การแสดงโฆษณาตามลำดับขั้นของเครือข่าย AdMob | 1215381445328257950 |
| Applovin | 1063618907739174004 |
| Applovin (การเสนอราคา) | 1328079684332308356 |
| Chartboost | 2873236629771172317 |
| Chocolate Platform (การเสนอราคา) | 6432849193975106527 |
| เหตุการณ์ที่กำหนดเอง | 18351550913290782395 |
| DT Exchange* * ก่อนวันที่ 21 กันยายน 2022 เครือข่ายนี้มีชื่อว่า "Fyber Marketplace" | 2179455223494392917 |
| Equativ (การเสนอราคา)* * ก่อนวันที่ 12 มกราคม 2023 เครือข่ายนี้มีชื่อว่า "Smart Adserver" | 5970199210771591442 |
| Fluct (การเสนอราคา) | 8419777862490735710 |
| Flurry | 3376427960656545613 |
| Fyber* * แหล่งที่มาของโฆษณานี้ใช้สําหรับการรายงานข้อมูลย้อนหลัง | 4839637394546996422 |
| i-mobile | 5208827440166355534 |
| Improve Digital (การเสนอราคา) | 159382223051638006 |
| Index Exchange (การเสนอราคา) | 4100650709078789802 |
| InMobi | 7681903010231960328 |
| InMobi (การเสนอราคา) | 6325663098072678541 |
| InMobi Exchange (การเสนอราคา) | 5264320421916134407 |
| IronSource | 6925240245545091930 |
| ironSource Ads (การเสนอราคา) | 1643326773739866623 |
| Leadbolt | 2899150749497968595 |
| Liftoff Monetize* * ก่อนวันที่ 30 มกราคม 2023 เครือข่ายนี้มีชื่อว่า "Vungle" | 1953547073528090325 |
| Liftoff Monetize (การเสนอราคา)* * ก่อนวันที่ 30 มกราคม 2023 เครือข่ายนี้มีชื่อว่า "Vungle (การเสนอราคา)" | 4692500501762622185 |
| LG U+AD | 18298738678491729107 |
| LINE Ads Network | 3025503711505004547 |
| Magnite DV+ (การเสนอราคา) | 3993193775968767067 |
| 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 (การเสนอราคา) | 3086513548163922365 |
| MoPub (เลิกใช้งานแล้ว) | 10872986198578383917 |
| myTarget | 8450873672465271579 |
| Nend | 9383070032774777750 |
| Nexxen (การเสนอราคา)* * ก่อนวันที่ 1 พฤษภาคม 2024 เครือข่ายนี้มีชื่อว่า "UnrulyX" | 2831998725945605450 |
| OneTag Exchange (การเสนอราคา) | 4873891452523427499 |
| OpenX (การเสนอราคา) | 4918705482605678398 |
| Pangle | 4069896914521993236 |
| Pangle (การเสนอราคา) | 3525379893916449117 |
| PubMatic (การเสนอราคา) | 3841544486172445473 |
| แคมเปญแบบจองล่วงหน้า | 7068401028668408324 |
| SK planet | 734341340207269415 |
| Sharethrough (การเสนอราคา) | 5247944089976324188 |
| Smaato (การเสนอราคา) | 3362360112145450544 |
| Sonobi (การเสนอราคา) | 3270984106996027150 |
| Tapjoy | 7295217276740746030 |
| Tapjoy (การเสนอราคา) | 4692500501762622178 |
| Tencent GDT | 7007906637038700218 |
| TripleLift (การเสนอราคา) | 8332676245392738510 |
| Unity Ads | 4970775877303683148 |
| Unity Ads (การเสนอราคา) | 7069338991535737586 |
| Verve Group (การเสนอราคา) | 5013176581647059185 |
| Vpon | 1940957084538325905 |
| Yieldmo (การเสนอราคา) | 4193081836471107579 |
| YieldOne (การเสนอราคา) | 3154533971590234104 |
| Zucks | 5506531810221735863 |
การให้รางวัลแก่ผู้ใช้
คุณควรสร้างสมดุลระหว่างประสบการณ์ของผู้ใช้และการตรวจสอบความถูกต้องของรางวัลเมื่อตัดสินใจ ว่าจะให้รางวัลแก่ผู้ใช้เมื่อใด Callback ฝั่งเซิร์ฟเวอร์อาจมีความล่าช้าก่อนที่จะไปถึงระบบภายนอก ดังนั้น แนวทางปฏิบัติแนะนำที่ดีที่สุดคือการใช้ Callback ฝั่งไคลเอ็นต์เพื่อให้รางวัลแก่ผู้ใช้ทันที ขณะที่ทำการตรวจสอบรางวัลทั้งหมดเมื่อได้รับ Callback ฝั่งเซิร์ฟเวอร์ แนวทางนี้ช่วยให้ผู้ใช้ได้รับประสบการณ์การใช้งานที่ดีพร้อมทั้งรับประกันความถูกต้องของรางวัลที่มอบให้
อย่างไรก็ตาม สำหรับแอปพลิเคชันที่ความถูกต้องของรางวัลมีความสำคัญอย่างยิ่ง (เช่น รางวัลส่งผลต่อเศรษฐกิจในเกมของแอป) และการให้รางวัลล่าช้าเป็นสิ่งที่ยอมรับได้ การรอ Callback ฝั่งเซิร์ฟเวอร์ที่ยืนยันแล้วอาจเป็นแนวทางที่ดีที่สุด
ข้อมูลที่กำหนดเอง
แอปที่ต้องการข้อมูลเพิ่มเติมใน SSV Callback ควรใช้ฟีเจอร์ข้อมูลที่กำหนดเองของโฆษณาที่มีการให้รางวัล ค่าสตริงที่ตั้งค่าไว้ในออบเจ็กต์โฆษณาที่มีการให้รางวัลจะส่งไปยังพารามิเตอร์การค้นหา custom_data ของ SSV Callback หากไม่ได้ตั้งค่าข้อมูลที่กำหนดเอง ค่าพารามิเตอร์การค้นหาcustom_data จะไม่อยู่ใน SSV Callback
ตัวอย่างโค้ดต่อไปนี้แสดงวิธีตั้งค่าตัวเลือก SSV หลังจากโหลดโฆษณาที่มีการให้รางวัลแล้ว
private void LoadRewardedAd(string adUnitId)
{
// Send the request to load the ad.
AdRequest adRequest = new AdRequest();
RewardedAd.Load(adUnitId, adRequest, (RewardedAd rewardedAd, LoadAdError error) =>
{
// If the operation failed with a reason.
if (error != null)
{
Debug.LogError("Rewarded ad failed to load an ad with error : " + error);
return;
}
var options = new ServerSideVerificationOptions
.Builder()
.SetCustomData("SAMPLE_CUSTOM_DATA_STRING")
.Build()
rewardedAd.SetServerSideVerificationOptions(options);
});
}
หากต้องการตั้งค่าสตริงรางวัลที่กำหนดเอง คุณต้องตั้งค่าก่อนแสดงโฆษณา
การยืนยัน Rewarded SSV ด้วยตนเอง
ขั้นตอนที่RewardedAdsVerifierคลาสใช้เพื่อยืนยัน Rewarded SSV มีดังนี้ แม้ว่าข้อมูลโค้ดที่รวมไว้จะเป็นภาษา Java และใช้ประโยชน์จากไลบรารีของบุคคลที่สามอย่าง Tink แต่คุณก็สามารถทำตามขั้นตอนเหล่านี้ในภาษาที่ต้องการได้โดยใช้ไลบรารีของบุคคลที่สามที่รองรับ ECDSA
ดึงข้อมูลคีย์สาธารณะ
หากต้องการยืนยัน Rewarded SSV Callback คุณต้องมีคีย์สาธารณะที่ AdMob ให้ไว้
คุณสามารถดึงรายการคีย์สาธารณะที่จะใช้เพื่อตรวจสอบความถูกต้องของ Callback 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 รายการสุดท้ายของ Rewarded SSV Callback จะเป็น signature และ key_id, ตามลำดับเสมอ พารามิเตอร์การค้นหาที่เหลือจะระบุเนื้อหา
ที่จะยืนยัน สมมติว่าคุณกำหนดค่า AdMob ให้ส่ง Rewarded Callback ไปยัง https://www.myserver.com/mypath ข้อมูลโค้ดด้านล่างแสดงตัวอย่าง Rewarded SSV Callback พร้อมไฮไลต์เนื้อหาที่จะยืนยัน
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 Callback อย่างไรก็ตาม โปรดทราบว่าระบบจะหมุนเวียนคีย์สาธารณะเป็นประจำและไม่ควรแคชคีย์นานเกิน 24 ชั่วโมง
- เซิร์ฟเวอร์คีย์ของ AdMob หมุนเวียนคีย์สาธารณะบ่อยเพียงใด
- คีย์สาธารณะที่เซิร์ฟเวอร์คีย์ของ AdMob จัดเตรียมให้จะหมุนเวียนตาม กำหนดเวลาที่เปลี่ยนแปลงได้ โปรดอย่าแคชคีย์สาธารณะนานเกิน 24 ชั่วโมงเพื่อให้การยืนยัน SSV Callback ยังคงทำงานได้ตามที่ต้องการ
- จะเกิดอะไรขึ้นหากเข้าถึงเซิร์ฟเวอร์ของฉันไม่ได้
- Google คาดหวังรหัสการตอบกลับสถานะความสำเร็จ
HTTP 200 OKสำหรับ SSV Callback หากเข้าถึงเซิร์ฟเวอร์ไม่ได้หรือเซิร์ฟเวอร์ไม่ส่งการตอบกลับตามที่คาดไว้ Google จะพยายามส่ง SSV Callback สูงสุดอีก 5 ครั้งโดยเว้นช่วงครั้งละ 1 วินาที - ฉันจะยืนยันได้อย่างไรว่า SSV Callback มาจาก Google
- ใช้การค้นหา DNS แบบย้อนกลับเพื่อยืนยันว่า SSV Callback มาจาก Google