Callback verifikasi sisi server adalah permintaan URL, dengan parameter kueri yang diperluas oleh Google, yang dikirim oleh Google ke sistem eksternal untuk memberi tahu bahwa pengguna harus diberi reward karena berinteraksi dengan iklan interstisial reward atau 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 reward menggunakan library kriptografi pihak ketiga Aplikasi Java Tink untuk memastikan bahwa parameter kueri dalam callback adalah 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 alat pengujian di UI AdMob.
Lihat Contoh SSV Berimbalan menggunakan Java spring-boot.
Prasyarat
- Aktifkan verifikasi sisi server reward di unit iklan Anda.
Menggunakan RewardedAdsVerifier dari library Aplikasi Java Tink
Repositori GitHub Aplikasi Java Tink
menyertakan
class helper
RewardedAdsVerifier
untuk mengurangi kode yang diperlukan guna memverifikasi callback SSV reward.
Dengan menggunakan class ini, Anda dapat 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()
dijalankan tanpa memunculkan pengecualian, URL callback
berhasil diverifikasi. Bagian Memberi reward kepada pengguna
menjelaskan praktik terbaik terkait kapan pengguna harus diberi reward. Untuk
mengetahui perincian langkah-langkah yang dilakukan oleh class ini untuk memverifikasi callback SSV reward,
Anda dapat membaca bagian Verifikasi manual SSV
reward.
Parameter callback SSV
Callback verifikasi sisi server berisi parameter kueri yang mendeskripsikan interaksi iklan reward. Nama, deskripsi, dan contoh nilai parameter tercantum di bawah. Parameter dikirim dalam urutan abjad.
Nama Parameter | Deskripsi | Nilai contoh |
---|---|---|
ad_network | ID sumber iklan untuk sumber iklan yang memenuhi iklan ini. Nama sumber iklan yang sesuai dengan nilai ID dicantumkan di bagian ID sumber iklan. | 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 diberikan oleh aplikasi, nilai parameter kueri ini 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 ditentukan di setelan unit iklan. | 5 |
reward_item | Item reward seperti yang ditentukan dalam setelan unit iklan. | koin |
tanda tangan | Tanda tangan untuk callback SSV yang dihasilkan oleh AdMob. | MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMtmgJHYYW9k2_icM9LFMY |
timestamp | Stempel waktu saat pengguna diberi reward sebagai waktu Epoch dalam md. | 1507770365237823 |
transaction_id | ID unik yang dienkode hex untuk setiap peristiwa pemberian reward yang dihasilkan oleh AdMob. | 18fa792de1bca816048293fc71035638 |
user_id | ID pengguna seperti yang diberikan oleh
setUserId .
Jika tidak ada ID pengguna yang diberikan oleh aplikasi, parameter kueri ini tidak akan ada dalam callback SSV. |
1234567 |
ID sumber iklan
Nama dan ID sumber iklan
Nama sumber iklan | ID sumber iklan |
---|---|
Aarki (bidding) | 5240798063227064260 |
Pembuatan Iklan (bidding) | 1477265452970951479 |
AdColony | 15586990674969969776 |
AdColony (non-SDK) (bidding) | 4600416542059544716 |
AdColony (bidding) | 6895345910719072481 |
AdFalcon | 3528208921554210682 |
Jaringan AdMob | 5450213213286189855 |
Waterfall Jaringan AdMob | 1215381445328257950 |
ADResult | 10593873382626181482 |
AMoAd | 17253994435944008978 |
AppLovin | 1063618907739174004 |
Applovin (bidding) | 1328079684332308356 |
Chartboost | 2873236629771172317 |
Chocolate Platform (bidding) | 6432849193975106527 |
CrossChannel (MdotM) | 9372067028804390441 |
Peristiwa Kustom | 18351550913290782395 |
DT Exchange* * Sebelum 21 September 2022, jaringan ini disebut "Fyber Marketplace". | 2179455223494392917 |
EMX (bidding) | 8497809869790333482 |
Fluct (bidding) | 8419777862490735710 |
Flurry | 3376427960656545613 |
Fyber* * Sumber iklan ini digunakan untuk pelaporan historis. | 4839637394546996422 |
i-mobile | 5208827440166355534 |
Improve Digital (bidding) | 159382223051638006 |
Index Exchange (bidding) | 4100650709078789802 |
InMobi | 7681903010231960328 |
InMobi (bidding) | 6325663098072678541 |
InMobi Exchange (bidding) | 5264320421916134407 |
IronSource | 6925240245545091930 |
ironSource Ads (bidding) | 1643326773739866623 |
Leadbolt | 2899150749497968595 |
LG U+AD | 18298738678491729107 |
LINE Ads Network | 3025503711505004547 |
maio | 7505118203095108657 |
maio (bidding) | 1343336733822567166 |
Media.net (bidding) | 2127936450554446159 |
Iklan internal termediasi | 6060308706800320801 |
Meta Audience Network* * Sebelum 6 Juni 2022, jaringan ini disebut "Facebook Audience Network". | 10568273599589928883 |
Meta Audience Network (bidding)* * Sebelum 6 Juni 2022, jaringan ini disebut "Facebook Audience Network (bidding)". | 11198165126854996598 |
Mintegral | 1357746574408896200 |
Mintegral (bidding) | 6250601289653372374 |
MobFox | 8079529624516381459 |
MobFox (bidding) | 3086513548163922365 |
MoPub (tidak digunakan lagi) | 10872986198578383917 |
myTarget | 8450873672465271579 |
Nend | 9383070032774777750 |
Nexxen (bidding)* * Sebelum 1 Mei 2024, jaringan ini bernama "UnrulyX". | 2831998725945605450 |
ONE by AOL (Millennial Media) | 6101072188699264581 |
ONE by AOL (Nexage) | 3224789793037044399 |
OneTag Exchange (bidding) | 4873891452523427499 |
OpenX (bidding) | 4918705482605678398 |
Pangle | 4069896914521993236 |
Pangle (bidding) | 3525379893916449117 |
PubMatic (bidding) | 3841544486172445473 |
Kampanye reservasi | 7068401028668408324 |
RhythmOne (bidding) | 2831998725945605450 |
Rubicon (bidding) | 3993193775968767067 |
Planet SK | 734341340207269415 |
Sharethrough (bidding) | 5247944089976324188 |
Smaato (bidding) | 3362360112145450544 |
Equativ (bidding)* * Sebelum 12 Januari 2023, jaringan ini bernama "Smart Adserver". | 5970199210771591442 |
Sonobi (bidding) | 3270984106996027150 |
Tapjoy | 7295217276740746030 |
Tapjoy (bidding) | 4692500501762622178 |
Tencent GDT | 7007906637038700218 |
TripleLift (bidding) | 8332676245392738510 |
Unity Ads | 4970775877303683148 |
Unity Ads (bidding) | 7069338991535737586 |
Verizon Media | 7360851262951344112 |
Verve Group (bidding) | 5013176581647059185 |
Vpon | 1940957084538325905 |
Liftoff Monetize* * Sebelum 30 Januari 2023, jaringan ini bernama "Vungle". | 1953547073528090325 |
Liftoff Monetize (bidding)* * Sebelum 30 Januari 2023, jaringan ini bernama "Vungle (bidding)". | 4692500501762622185 |
Yieldmo (bidding) | 4193081836471107579 |
YieldOne (bidding) | 3154533971590234104 |
Zucks | 5506531810221735863 |
Memberikan reward kepada pengguna
Penting untuk menyeimbangkan pengalaman pengguna dan validasi reward saat memutuskan kapan harus 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, sekaligus melakukan validasi pada semua reward setelah menerima callback sisi server. Pendekatan ini memberikan pengalaman pengguna yang baik sekaligus memastikan validitas reward yang diberikan.
Namun, untuk aplikasi yang validitas reward-nya sangat penting (misalnya, reward memengaruhi ekonomi dalam game aplikasi Anda) dan penundaan dalam memberikan reward dapat diterima, menunggu callback sisi server terverifikasi mungkin merupakan pendekatan terbaik.
Data kustom
Aplikasi yang memerlukan data tambahan dalam callback verifikasi sisi server harus menggunakan
fitur data kustom iklan reward. Setiap nilai string yang ditetapkan pada objek iklan reward
diteruskan ke parameter kueri custom_data
dari callback SSV. Jika tidak ada nilai data kustom yang ditetapkan, nilai parameter kueri custom_data
tidak akan ada dalam callback SSV.
Contoh berikut menetapkan opsi SSV setelah iklan reward dimuat:
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 } })
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 SSV
berimbalan diuraikan di bawah. Meskipun cuplikan kode yang disertakan dalam Java dan
memanfaatkan library pihak ketiga Tink, langkah-langkah ini dapat diterapkan oleh Anda dalam
bahasa pilihan Anda, menggunakan library pihak ketiga apa pun 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 reward dapat diambil dari server kunci AdMob. 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
kunci. Kode berikut menyelesaikan tugas ini dan menyimpan representasi JSON kunci ke 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();
}
Perhatikan bahwa kunci publik dirotasi secara rutin. Anda akan menerima email untuk menginformasikan rotasi mendatang. Jika menyimpan kunci publik dalam cache, Anda harus memperbarui kunci setelah menerima email ini.
Setelah diambil, kunci publik harus diuraikan. Metode
parsePublicKeysJson
di bawah menggunakan 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;
}
Mendapatkan konten yang akan diverifikasi
Dua parameter kueri terakhir dari callback SSV reward selalu signature
dan key_id,
dalam urutan tersebut. Parameter kueri yang tersisa menentukan konten
yang akan diverifikasi. Anggaplah Anda mengonfigurasi AdMob untuk mengirim callback reward ke
https://www.myserver.com/mypath
. Cuplikan di bawah menunjukkan contoh callback SSV
imbalan 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 menunjukkan cara mengurai konten yang akan diverifikasi dari 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 tanda tangan dan key_id dari URL callback
Dengan menggunakan nilai queryString
dari langkah sebelumnya, mengurai parameter kueri signature
dan
key_id
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 ditampilkan dari
metode parsePublicKeysJson
dan gunakan parameter key_id
dari URL
callback untuk mendapatkan kunci publik dari pemetaan tersebut. Kemudian, verifikasi tanda tangan 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 telah berhasil diverifikasi.
FAQ
- Dapatkah saya menyimpan kunci publik yang disediakan oleh server kunci AdMob dalam cache?
- Sebaiknya simpan kunci publik yang disediakan oleh server kunci AdMob dalam cache untuk mengurangi jumlah operasi yang diperlukan guna memvalidasi callback SSV. Namun, perlu diperhatikan bahwa kunci publik dirotasi secara rutin dan tidak boleh 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 sesuai jadwal variabel. Untuk memastikan verifikasi callback SSV terus berfungsi seperti yang diinginkan, kunci publik tidak boleh di-cache selama lebih dari 24 jam.
- Apa yang terjadi jika server saya tidak dapat dijangkau?
- Google mengharapkan kode respons status keberhasilan
HTTP 200 OK
untuk callback SSV. Jika server Anda tidak dapat dijangkau atau tidak memberikan respons yang diharapkan, Google akan mencoba mengirim callback SSV lagi hingga lima kali dalam interval satu detik. - Bagaimana cara memverifikasi bahwa callback SSV berasal dari Google?
- Gunakan pencarian DNS terbalik untuk memverifikasi bahwa callback SSV berasal dari Google.