Callback verifikasi sisi server adalah permintaan URL, dengan parameter kueri diperluas oleh Google, yang dikirim oleh Google ke sistem eksternal untuk memberi tahu bahwa pengguna harus diberikan reward karena telah berinteraksi dengan iklan interstisial reward. Callback SSV (verifikasi sisi server) reward memberikan lapisan perlindungan tambahan terhadap spoofing callback sisi klien untuk memberikan reward kepada pengguna.
Panduan ini menunjukkan cara memverifikasi callback SSV yang diberi reward menggunakan Tink Aplikasi Java pihak ketiga kriptografis untuk memastikan bahwa parameter kueri di callback nilai yang sah. Meskipun Tink digunakan untuk tujuan panduan ini, Anda memiliki opsi untuk menggunakan library pihak ketiga yang mendukung ECDSA. Anda juga dapat menguji server dengan pengujian di UI AdMob.
Lihat aplikasi berikut yang berfungsi sepenuhnya contoh menggunakan Spring-boot Java.
Prasyarat
Mengintegrasikan iklan reward ke aplikasi seluler dengan Google Mobile Ads SDK v11.6.0 atau versi yang lebih tinggi.
Aktifkan sisi server reward verifikasi di unit iklan Anda.
Menggunakan RewardAdsVerifier dari library Aplikasi Java Tink
Repositori GitHub Tink Java Apps
berisi
RewardedAdsVerifier
untuk mengurangi kode yang diperlukan untuk memverifikasi callback SSV yang diberi reward.
Menggunakan class ini memungkinkan Anda memverifikasi URL callback dengan kode berikut.
RewardedAdsVerifier verifier = new RewardedAdsVerifier.Builder()
.fetchVerifyingPublicKeysWith(
RewardedAdsVerifier.KEYS_DOWNLOADER_INSTANCE_PROD)
.build();
String rewardUrl = ...;
verifier.verify(rewardUrl);
Jika metode verify()
dieksekusi tanpa memunculkan pengecualian, callback
URL berhasil diverifikasi. Memberikan reward kepada pengguna
menjelaskan praktik terbaik terkait kapan pengguna harus diberi reward. Untuk
perincian langkah-langkah yang dilakukan oleh class ini untuk memverifikasi callback SSV yang diberi reward,
Anda dapat membaca Verifikasi
manual iklan reward
SSV.
Parameter callback SSV
Callback verifikasi sisi server berisi parameter kueri yang menjelaskan interaksi iklan reward. Nama, deskripsi, dan nilai contoh parameter yang tercantum di bawah ini. Parameter dikirim dalam urutan abjad.
Nama Parameter | Deskripsi | Nilai contoh |
---|---|---|
ad_network | ID sumber iklan untuk sumber iklan yang memenuhi iklan ini. Sumber iklan yang sesuai dengan nilai ID tercantum di kolom ID sumber. | 1953547073528090325 |
ad_unit | ID unit iklan AdMob yang digunakan untuk meminta iklan reward. | 2747237135 |
custom_data | String data kustom seperti yang disediakan oleh
setCustomData
.
Jika tidak ada string data kustom yang disediakan oleh aplikasi, parameter kueri ini nilai tersebut tidak akan ada dalam callback SSV. |
SAMPLE_CUSTOM_DATA_STRING |
key_id | Kunci yang akan digunakan untuk memverifikasi callback SSV. Nilai ini dipetakan ke kunci publik yang disediakan oleh server kunci AdMob. | 1234567890 |
reward_amount | Jumlah reward seperti yang ditetapkan dalam setelan unit iklan. | 5 |
reward_item | Item reward seperti yang ditentukan dalam setelan unit iklan. | koin |
tanda tangan | Tanda tangan untuk callback SSV yang dibuat oleh AdMob. | MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMtmgJHYYW9k2_icM9LFMY |
timestamp | Stempel waktu saat pengguna mendapatkan reward sebagai waktu Epoch dalam milidetik. | 1507770365237823 |
transaction_id | ID unik yang dienkode dengan heksadesimal untuk setiap peristiwa pemberian reward yang dihasilkan oleh AdMob. | 18fa792de1bca816048293fc71035638 |
user_id | ID pengguna seperti yang disediakan oleh
setUserId .
Jika tidak ada ID pengguna yang disediakan oleh aplikasi, parameter kueri ini tidak akan ada di callback SSV. |
1234567 |
ID sumber iklan
Nama dan ID sumber iklan
广告来源名称 | 广告来源 ID |
---|---|
Aarki(出价) | 5240798063227064260 |
Ad Generation(出价) | 1477265452970951479 |
AdColony | 15586990674969969776 |
AdColony(非 SDK)(出价) | 4600416542059544716 |
AdColony(出价) | 6895345910719072481 |
AdFalcon | 3528208921554210682 |
AdMob 广告联盟 | 5450213213286189855 |
AdMob 广告联盟广告瀑布流 | 1215381445328257950 |
ADResult | 10593873382626181482 |
AMoAd | 17253994435944008978 |
AppLovin | 1063618907739174004 |
AppLovin(出价) | 1328079684332308356 |
Chartboost | 2873236629771172317 |
Chocolate Platform(出价) | 6432849193975106527 |
跨渠道 (MdotM) | 9372067028804390441 |
自定义事件 | 18351550913290782395 |
DT Exchange* * 在 2022 年 9 月 21 日之前,该广告联盟称为“Fyber Marketplace”。 | 2179455223494392917 |
EMX(出价) | 8497809869790333482 |
Fluct(出价) | 8419777862490735710 |
小风 | 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 广告联盟 | 3025503711505004547 |
maio | 7505118203095108657 |
maio(出价) | 1343336733822567166 |
Media.net(出价) | 2127936450554446159 |
参与中介的自家广告 | 6060308706800320801 |
Meta Audience Network* * 在 2022 年 6 月 6 日之前,该广告联盟称为“Facebook Audience Network”。 | 10568273599589928883 |
Meta Audience Network(出价)* * 在 2022 年 6 月 6 日之前,该广告联盟称为“Facebook Audience Network(出价)”。 | 11198165126854996598 |
Mintegral | 1357746574408896200 |
Mintegral(出价) | 6250601289653372374 |
MobFox | 8079529624516381459 |
MobFox(出价) | 3086513548163922365 |
MoPub(已弃用) | 10872986198578383917 |
myTarget | 8450873672465271579 |
Nend | 9383070032774777750 |
Nexxen(出价)* * 在 2024 年 5 月 1 日之前,该广告联盟称为“UnrulyX”。 | 2831998725945605450 |
ONE by AOL (Millennial Media) | 6101072188699264581 |
ONE by AOL (Nexage) | 3224789793037044399 |
OneTag Exchange(出价) | 4873891452523427499 |
OpenX(出价) | 4918705482605678398 |
邦格尔 | 4069896914521993236 |
Pangle(出价) | 3525379893916449117 |
PubMatic(出价) | 3841544486172445473 |
预订型广告系列 | 7068401028668408324 |
RhythmOne(出价) | 2831998725945605450 |
Rubicon(出价) | 3993193775968767067 |
SK 星球 | 734341340207269415 |
Sharethrough(出价) | 5247944089976324188 |
Smaato(出价) | 3362360112145450544 |
Equativ(出价)* * 在 2023 年 1 月 12 日之前,该广告联盟称为“Smart Adserver”。 | 5970199210771591442 |
Sonobi(出价) | 3270984106996027150 |
Tapjoy | 7295217276740746030 |
Tapjoy(出价) | 4692500501762622178 |
Tencent GDT | 7007906637038700218 |
TripleLift(出价) | 8332676245392738510 |
Unity 广告 | 4970775877303683148 |
Unity Ads(出价) | 7069338991535737586 |
Verizon Media | 7360851262951344112 |
Verve Group(出价) | 5013176581647059185 |
Vpon | 1940957084538325905 |
Liftoff Monetize* * 在 2023 年 1 月 30 日之前,该广告联盟称为“Vungle”。 | 1953547073528090325 |
Liftoff Monetize(出价)* * 在 2023 年 1 月 30 日之前,该广告联盟称为“Vungle(出价)”。 | 4692500501762622185 |
Yieldmo(出价) | 4193081836471107579 |
YieldOne(出价) | 3154533971590234104 |
Zucks | 5506531810221735863 |
Memberikan reward kepada pengguna
Penting untuk menyeimbangkan pengalaman pengguna dan validasi penghargaan saat memutuskan kapan perlu memberikan reward kepada pengguna. Callback sisi server mungkin mengalami penundaan sebelum menjangkau sistem eksternal. Oleh karena itu, praktik terbaik yang direkomendasikan adalah menggunakan callback sisi klien untuk segera memberi reward kepada pengguna, saat melakukan dan validasi semua reward setelah menerima callback sisi server. Ini pendekatan ini memberikan pengalaman pengguna yang baik sambil memastikan validitas reward.
Namun, untuk aplikasi yang menekankan validitas reward (misalnya, reward memengaruhi ekonomi dalam game aplikasi) dan keterlambatan dalam memberikan reward masih dapat diterima, menunggu callback sisi server yang terverifikasi mungkin adalah yang terbaik pendekatan.
Data kustom
Aplikasi yang memerlukan data tambahan di callback verifikasi sisi server harus menggunakan
fitur data kustom iklan reward. Nilai string apa pun yang ditetapkan pada iklan reward
diteruskan ke parameter kueri custom_data
dari callback SSV. Jika tidak
nilai data kustom ditetapkan, nilai parameter kueri custom_data
tidak akan
ada di callback SSV.
Contoh kode berikut menunjukkan cara mengatur opsi SSV setelah iklan reward dimuat.
Java
RewardedAd.load(MainActivity.this, "ca-app-pub-3940256099942544/5354046379", 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, "ca-app-pub-3940256099942544/5354046379", 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 } })
Jika ingin menetapkan string reward kustom, Anda harus melakukannya sebelum menampilkan iklan.
Verifikasi manual SSV reward
Langkah-langkah yang dilakukan oleh class RewardedAdsVerifier
untuk memverifikasi reward
SSV diuraikan di bawah ini. Meskipun cuplikan kode yang disertakan ada di Java dan
memanfaatkan library pihak ketiga Tink, langkah-langkah ini dapat Anda terapkan
bahasa pilihan Anda, menggunakan library pihak ketiga yang mendukung
ECDSA.
Mengambil kunci publik
Untuk memverifikasi callback SSV reward, Anda memerlukan kunci publik yang disediakan oleh AdMob.
Daftar kunci publik yang akan digunakan untuk memvalidasi callback SSV yang diberi reward dapat diambil dari kunci AdMob server. Daftar kunci publik disediakan sebagai representasi JSON dengan format yang mirip dengan berikut ini:
{
"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=="
},
],
}
Untuk mengambil kunci publik, hubungkan ke server kunci AdMob dan download
tombol. Kode berikut akan menyelesaikan tugas ini dan menyimpan JSON
representasi kunci untuk variabel 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();
}
Perlu diingat bahwa kunci publik dirotasi secara berkala. Anda akan menerima email untuk menginformasikan tentang rotasi yang akan datang. Jika Anda menyimpan kunci publik dalam cache, Anda harus memperbarui kunci setelah menerima email ini.
Setelah kunci publik diambil, kunci tersebut harus diurai. Tujuan
Metode parsePublicKeysJson
di bawah mengambil string JSON, seperti contoh
di atas, sebagai input, dan membuat pemetaan dari nilai key_id
ke kunci publik,
yang dienkapsulasi sebagai objek ECPublicKey
dari library 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;
}
Meminta verifikasi konten
Dua parameter kueri terakhir dari callback SSV reward selalu signature
dan key_id,
dalam urutan tersebut. Parameter kueri yang tersisa menentukan konten
diverifikasi. Anggaplah Anda mengonfigurasi AdMob untuk mengirim callback reward ke
https://www.myserver.com/mypath
. Cuplikan di bawah menampilkan contoh iklan reward
Callback SSV dengan konten yang akan diverifikasi ditandai.
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
Kode di bawah ini menunjukkan cara mengurai konten yang akan diverifikasi dari sebuah URL callback sebagai array byte 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"));
Mendapatkan signature dan key_id dari URL callback
Dengan menggunakan nilai queryString
dari langkah sebelumnya, urai signature
dan
key_id
parameter kueri dari URL callback seperti yang ditunjukkan di bawah ini:
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()));
Melakukan verifikasi
Langkah terakhir adalah memverifikasi konten URL callback dengan
kunci publik yang sesuai. Ambil pemetaan yang dikembalikan dari
Metode parsePublicKeysJson
dan gunakan parameter key_id
dari callback
untuk mendapatkan kunci publik dari pemetaan tersebut. Kemudian memverifikasinya
dengan
kunci publik tersebut. Langkah-langkah ini ditunjukkan di bawah dalam metode 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);
}
}
Jika metode dieksekusi tanpa menampilkan pengecualian, URL callback-nya adalah berhasil diverifikasi.
FAQ
- Dapatkah saya menyimpan kunci publik yang disediakan oleh server kunci AdMob ke dalam cache?
- Sebaiknya simpan kunci publik yang disediakan oleh kunci AdMob ke dalam cache server untuk mengurangi jumlah operasi yang diperlukan untuk memvalidasi SSV callback. Namun, perhatikan bahwa kunci publik dirotasi secara berkala dan tidak boleh disimpan di cache selama lebih dari 24 jam.
- Seberapa sering kunci publik yang disediakan oleh server kunci AdMob dirotasi?
- Kunci publik yang disediakan oleh server kunci AdMob dirotasi pada variabel jadwal proyek. Untuk memastikan bahwa verifikasi callback SSV terus berfungsi sebagai kunci publik tidak boleh di-cache lebih dari 24 jam.
- Apa yang terjadi jika server saya tidak dapat dijangkau?
- Google mengharapkan kode respons status sukses
HTTP 200 OK
untuk SSV callback. Jika server tidak dapat dijangkau atau tidak memberikan respons Google akan mencoba kembali mengirimkan panggilan balik SSV hingga lima kali dengan interval satu detik. - Bagaimana cara memverifikasi bahwa callback SSV berasal dari Google?
- Gunakan pencarian balik DNS untuk memverifikasi bahwa callback SSV berasal dari Google.