Menggunakan verdict integritas

Play Integrity API menggunakan verdict integritas untuk menyampaikan informasi tentang validitas perangkat, aplikasi, dan pengguna. Server aplikasi Anda dapat menggunakan payload yang dihasilkan dalam verdict yang telah didekripsi dan diverifikasi guna menentukan cara terbaik untuk melanjutkan dengan tindakan atau permintaan tertentu di aplikasi Anda.

Membuat nonce

Saat melindungi tindakan di aplikasi dengan Play Integrity API, Anda dapat memanfaatkan kolom nonce untuk mengurangi jenis serangan tertentu, seperti serangan modifikasi tidak sah person-in-the-middle (PITM) dan serangan replay. Play Integrity API menampilkan nilai yang Anda tetapkan dalam kolom ini, di dalam respons integritas yang ditandatangani.

Nilai yang ditetapkan di kolom nonce harus diformat dengan benar:

  • String
  • URL-safe
  • Dienkode sebagai Base64 dan non-wrapping
  • Minimum 16 karakter
  • Maksimum 500 karakter

Berikut adalah beberapa cara umum untuk menggunakan kolom nonce di Play Integrity API. Untuk mendapatkan perlindungan terkuat dari nonce, Anda dapat menggabungkan metode di bawah ini.

Melindungi tindakan bernilai tinggi terhadap modifikasi tidak sah

Anda dapat menggunakan kolom nonce Play Integrity untuk melindungi konten tindakan bernilai tinggi tertentu dari modifikasi tidak sah. Misalnya, game mungkin ingin melaporkan skor pemain, dan Anda ingin memastikan skor ini tidak dirusak oleh server proxy. Implementasinya adalah sebagai berikut:

  1. Pengguna memulai tindakan bernilai tinggi.
  2. Aplikasi Anda menyiapkan pesan yang ingin dilindungi, misalnya, dalam format JSON.
  3. Aplikasi Anda menghitung hash kriptografi dari pesan yang ingin dilindungi. Misalnya, dengan SHA-256, atau algoritme hashing SHA-3-256.
  4. Aplikasi Anda memanggil Play Integrity API, dan memanggil setNonce() untuk menetapkan kolom nonce ke hash kriptografi yang dihitung di langkah sebelumnya.
  5. Aplikasi Anda mengirim pesan yang ingin dilindungi, serta hasil bertanda tangan Play Integrity API ke server.
  6. Server aplikasi Anda memverifikasi bahwa hash kriptografi dari pesan yang diterimanya cocok dengan nilai kolom nonce di hasil yang ditandatangani, dan menolak hasil yang tidak cocok.

Gambar 1 berisi diagram urutan yang menggambarkan langkah-langkah ini:

Gambar 1. Diagram urutan yang menunjukkan cara melindungi tindakan bernilai tinggi di aplikasi Anda terhadap modifikasi tidak sah.

Melindungi aplikasi Anda dari serangan replay

Untuk mencegah pengguna berbahaya menggunakan kembali respons sebelumnya dari Play Integrity API, Anda dapat menggunakan kolom nonce untuk mengidentifikasi setiap pesan secara unik. Implementasinya adalah sebagai berikut:

  1. Anda memerlukan nilai unik secara global dengan cara yang tidak dapat diprediksi oleh pengguna yang berbahaya. Misalnya, angka acak yang diamankan secara kriptografis yang dihasilkan pada sisi server dapat menjadi nilai tersebut. Sebaiknya buat nilai 128 bit atau yang lebih besar.
  2. Aplikasi Anda memanggil Play Integrity API, dan memanggil setNonce() untuk menetapkan kolom nonce ke nilai unik yang diterima oleh server aplikasi Anda.
  3. Aplikasi Anda mengirimkan hasil bertanda tangan Play Integrity API ke server.
  4. Server Anda memverifikasi bahwa kolom nonce dalam hasil bertanda tangan cocok dengan nilai unik yang dibuat sebelumnya, dan menolak hasil apa pun yang tidak cocok.

Gambar 2 berisi diagram urutan yang menggambarkan langkah-langkah ini:

Gambar 2. Diagram urutan yang menunjukkan cara melindungi aplikasi dari serangan replay.

Menggabungkan kedua perlindungan

Kolom nonce dapat digunakan untuk melindungi dari serangan replay dan gangguan secara bersamaan. Untuk melakukannya, Anda dapat menambahkan nilai unik secara global yang dihasilkan server ke hash pesan bernilai tinggi, dan menetapkan nilai ini sebagai kolom nonce di Play Integrity API. Implementasi yang menggabungkan kedua pendekatan tersebut adalah sebagai berikut:

  1. Pengguna memulai tindakan bernilai tinggi.
  2. Aplikasi Anda meminta nilai unik kepada server untuk mengidentifikasi permintaan
  3. Server aplikasi Anda menghasilkan nilai unik secara global dengan cara yang tidak dapat diprediksi oleh pengguna berbahaya. Misalnya, Anda dapat menggunakan generator angka acak yang aman secara kriptografis untuk membuat nilai tersebut. Sebaiknya buat nilai 128 bit atau lebih besar.
  4. Server aplikasi Anda mengirimkan nilai unik secara global ke aplikasi.
  5. Aplikasi Anda menyiapkan pesan yang ingin dilindungi, misalnya, dalam format JSON.
  6. Aplikasi Anda menghitung hash kriptografi dari pesan yang ingin dilindungi. Misalnya, dengan SHA-256, atau algoritme hashing SHA-3-256.
  7. Aplikasi Anda membuat string dengan menambahkan nilai unik yang diterima dari server aplikasi, dan hash pesan yang ingin dilindungi.
  8. Aplikasi akan memanggil Play Integrity API, dan memanggil setNonce() untuk menetapkan kolom nonce ke string yang dibuat pada langkah sebelumnya.
  9. Aplikasi Anda mengirim pesan yang ingin dilindungi, serta hasil bertanda tangan Play Integrity API ke server.
  10. Server aplikasi Anda membagi nilai kolom nonce, dan memastikan bahwa hash kriptografi pesan, serta nilai unik yang sebelumnya dihasilkan cocok dengan nilai yang diharapkan, dan menolak hasil apa pun yang tidak cocok.

Gambar 3 berisi diagram urutan yang menggambarkan langkah-langkah ini:

Gambar 3. Diagram urutan yang menunjukkan cara melindungi aplikasi dari serangan replay dan melindungi tindakan bernilai tinggi di aplikasi Anda terhadap modifikasi tidak sah.

Meminta verdict integritas

Setelah membuat nonce, Anda dapat meminta verdict integritas dari Google Play. Caranya, selesaikan langkah-langkah berikut:

  1. Buat IntegrityManager seperti yang ditunjukkan dalam contoh berikut.
  2. Buat IntegrityTokenRequest, yang menyediakan nonce melalui metode setNonce() di builder terkait. Aplikasi yang didistribusikan secara eksklusif di luar Google Play dan SDK juga harus menentukan nomor project Google Cloud melalui metode setCloudProjectNumber(). Aplikasi di Google Play ditautkan ke project Cloud di Konsol Play dan tidak perlu menetapkan nomor project Cloud dalam permintaan.
  3. Gunakan pengelola untuk memanggil requestIntegrityToken() yang menyediakan IntegrityTokenRequest.

Kotlin

// Receive the nonce from the secure server.
val nonce: String = ...

// Create an instance of a manager.
val integrityManager =
    IntegrityManagerFactory.create(applicationContext)

// Request the integrity token by providing a nonce.
val integrityTokenResponse: Task<IntegrityTokenResponse> =
    integrityManager.requestIntegrityToken(
        IntegrityTokenRequest.builder()
             .setNonce(nonce)
             .build())

Java

import com.google.android.gms.tasks.Task; ...

// Receive the nonce from the secure server.
String nonce = ...

// Create an instance of a manager.
IntegrityManager integrityManager =
    IntegrityManagerFactory.create(getApplicationContext());

// Request the integrity token by providing a nonce.
Task<IntegrityTokenResponse> integrityTokenResponse =
    integrityManager
        .requestIntegrityToken(
            IntegrityTokenRequest.builder().setNonce(nonce).build());

Unity

IEnumerator RequestIntegrityTokenCoroutine() {
    // Receive the nonce from the secure server.
    var nonce = ...

    // Create an instance of a manager.
    var integrityManager = new IntegrityManager();

    // Request the integrity token by providing a nonce.
    var tokenRequest = new IntegrityTokenRequest(nonce);
    var requestIntegrityTokenOperation =
        integrityManager.RequestIntegrityToken(tokenRequest);

    // Wait for PlayAsyncOperation to complete.
    yield return requestIntegrityTokenOperation;

    // Check the resulting error code.
    if (requestIntegrityTokenOperation.Error != IntegrityErrorCode.NoError)
    {
        AppendStatusLog("IntegrityAsyncOperation failed with error: " +
                requestIntegrityTokenOperation.Error);
        yield break;
    }

    // Get the response.
    var tokenResponse = requestIntegrityTokenOperation.GetResult();
}

Native

/// Create an IntegrityTokenRequest opaque object.
const char* nonce = RequestNonceFromServer();
IntegrityTokenRequest* request;
IntegrityTokenRequest_create(&request);
IntegrityTokenRequest_setNonce(request, nonce);

/// Prepare an IntegrityTokenResponse opaque type pointer and call
/// IntegerityManager_requestIntegrityToken().
IntegrityTokenResponse* response;
IntegrityErrorCode error_code =
        IntegrityManager_requestIntegrityToken(request, &response);

/// ...
/// Proceed to polling iff error_code == INTEGRITY_NO_ERROR
if (error_code != INTEGRITY_NO_ERROR)
{
    /// Remember to call the *_destroy() functions.
    return;
}
/// ...
/// Use polling to wait for the async operation to complete.
/// Note, the polling shouldn't block the thread where the IntegrityManager
/// is running.

IntegrityResponseStatus response_status;

/// Check for error codes.
IntegrityErrorCode error_code =
        IntegrityTokenResponse_getStatus(response, &response_status);
if (error_code == INTEGRITY_NO_ERROR
    && response_status == INTEGRITY_RESPONSE_COMPLETED)
{
    const char* integrity_token = IntegrityTokenResponse_getToken(response);
    SendTokenToServer(integrity_token);
}
/// ...
/// Remember to free up resources.
IntegrityTokenRequest_destroy(request);
IntegrityTokenResponse_destroy(response);
IntegrityManager_destroy();

Mendekripsi dan memverifikasi verdict integritas

Saat Anda meminta verdict integritas, Play Integrity API akan menyediakan token respons yang ditandatangani. Nonce yang Anda sertakan dalam permintaan Anda akan menjadi bagian dari token respons.

Format token

Token berupa JSON Web Token (JWT) bertingkat, yaitu JSON Web Encryption (JWE) dari JSON Web Signature (JWS). Komponen JWE dan JWS direpresentasikan menggunakan serialisasi ringkas.

Algoritma enkripsi dan penandatanganan didukung dengan baik di berbagai implementasi JWT:

  • JWE menggunakan A256KW untuk alg dan A256GCM untuk enc.
  • JWS menggunakan ES256.

Mendekripsi dan memverifikasi di server Google (direkomendasikan)

Play Integrity API memungkinkan Anda mendekripsi dan memverifikasi verdict integritas di server Google, yang meningkatkan keamanan aplikasi. Untuk melakukannya, selesaikan langkah-langkah berikut:

  1. Buat akun layanan dalam project Google Cloud yang ditautkan ke aplikasi Anda. Selama proses pembuatan akun ini, Anda perlu memberikan peran Service Account User dan Service Usage Consumer kepada akun layanan Anda.
  2. Di server aplikasi Anda, ambil token akses dari kredensial akun layanan menggunakan cakupan playintegrity, dan buat permintaan berikut:

    playintegrity.googleapis.com/v1/PACKAGE_NAME:decodeIntegrityToken -d \
      '{ "integrity_token": "INTEGRITY_TOKEN" }'
  3. Baca respons JSON.

Mendekripsi dan memverifikasi secara lokal

Jika memilih untuk mengelola dan mendownload kunci enkripsi respons, Anda dapat mendekripsi dan memverifikasi token yang ditampilkan dalam lingkungan server Anda yang aman. Anda bisa mendapatkan token yang ditampilkan menggunakan metode IntegrityTokenResponse#token().

Contoh berikut menunjukkan cara mendekode kunci AES dan kunci EC publik yang dienkode DER untuk verifikasi tanda tangan dari Konsol Play ke kunci bahasa tertentu (dalam hal ini, bahasa pemograman Java) di backend aplikasi. Perhatikan bahwa kunci dienkode dengan base64 menggunakan flag default.

Kotlin

// base64OfEncodedDecryptionKey is provided through Play Console.
var decryptionKeyBytes: ByteArray =
    Base64.decode(base64OfEncodedDecryptionKey, Base64.DEFAULT)

// Deserialized encryption (symmetric) key.
var decryptionKey: SecretKey = SecretKeySpec(
    decryptionKeyBytes,
    /* offset= */ 0,
    AES_KEY_SIZE_BYTES,
    AES_KEY_TYPE
)

// base64OfEncodedVerificationKey is provided through Play Console.
var encodedVerificationKey: ByteArray =
    Base64.decode(base64OfEncodedVerificationKey, Base64.DEFAULT)

// Deserialized verification (public) key.
var verificationKey: PublicKey = KeyFactory.getInstance(EC_KEY_TYPE)
    .generatePublic(X509EncodedKeySpec(encodedVerificationKey))

Java


// base64OfEncodedDecryptionKey is provided through Play Console.
byte[] decryptionKeyBytes =
    Base64.decode(base64OfEncodedDecryptionKey, Base64.DEFAULT);

// Deserialized encryption (symmetric) key.
SecretKey decryptionKey =
    new SecretKeySpec(
        decryptionKeyBytes,
        /* offset= */ 0,
        AES_KEY_SIZE_BYTES,
        AES_KEY_TYPE);

// base64OfEncodedVerificationKey is provided through Play Console.
byte[] encodedVerificationKey =
    Base64.decode(base64OfEncodedVerificationKey, Base64.DEFAULT);
// Deserialized verification (public) key.
PublicKey verificationKey =
    KeyFactory.getInstance(EC_KEY_TYPE)
        .generatePublic(new X509EncodedKeySpec(encodedVerificationKey));

Berikutnya, gunakan kunci ini untuk mendekripsi token integritas (bagian JWE) terlebih dahulu, lalu verifikasi dan ekstrak bagian JWS bertingkat.

Kotlin

val jwe: JsonWebEncryption =
    JsonWebStructure.fromCompactSerialization(integrityToken) as JsonWebEncryption
jwe.setKey(decryptionKey)

// This also decrypts the JWE token.
val compactJws: String = jwe.getPayload()

val jws: JsonWebSignature =
    JsonWebStructure.fromCompactSerialization(compactJws) as JsonWebSignature
jws.setKey(verificationKey)

// This also verifies the signature.
val payload: String = jws.getPayload()

Java

JsonWebEncryption jwe =
    (JsonWebEncryption)JsonWebStructure
        .fromCompactSerialization(integrityToken);
jwe.setKey(decryptionKey);

// This also decrypts the JWE token.
String compactJws = jwe.getPayload();

JsonWebSignature jws =
    (JsonWebSignature) JsonWebStructure.fromCompactSerialization(compactJws);
jws.setKey(verificationKey);

// This also verifies the signature.
String payload = jws.getPayload();

Payload yang dihasilkan adalah token teks biasa yang berisi sinyal integritas.

Format payload yang ditampilkan

Payload berupa JSON teks biasa dan berisi sinyal integritas bersama informasi yang disediakan developer.

Berikut adalah struktur payload umum:

{
  requestDetails: { ... }
  appIntegrity: { ... }
  deviceIntegrity: { ... }
  accountDetails: { ... }
}

Anda harus memastikan terlebih dahulu bahwa nilai di kolom requestDetails cocok dengan nilai dari permintaan asli sebelum memeriksa setiap verdict integritas.

Bagian berikut menjelaskan setiap kolom secara lebih mendetail.

Kolom detail permintaan

Kolom requestDetails berisi informasi yang disediakan dalam permintaan, termasuk nonce.

requestDetails: {
  // Application package name this attestation was requested for.
  // Note that this field might be spoofed in the middle of the
  // request.
  requestPackageName: "com.package.name"
  // base64-encoded URL-safe no-wrap nonce provided by the developer.
  nonce: "aGVsbG8gd29scmQgdGhlcmU"
  // The timestamp in milliseconds when the request was made
  // (computed on the server).
  timestampMillis: "1617893780"
}

Nilai ini harus cocok dengan permintaan asal. Oleh karena itu, verifikasi bagian requestDetails payload JSON dengan memastikan bahwa requestPackageName dan nonce cocok dengan yang dikirim dalam permintaan asli, seperti yang ditunjukkan dalam cuplikan kode:

Kotlin

val requestDetails = JSONObject(payload).getJSONObject("requestDetails")
val requestPackageName = requestDetails.getString("requestPackageName")
val nonce = requestDetails.getString("nonce")
val timestampMillis = requestDetails.getLong("timestampMillis")
val currentTimestampMillis = ...

// Ensure the token is from your app.
if (!requestPackageName.equals(expectedPackageName)
        // Ensure the token is for this specific request. See “Generate nonce”
        // section of the doc on how to store/compute the expected nonce.
    || !nonce.equals(expectedNonce)
        // Ensure the freshness of the token.
    || currentTimestampMillis - timestampMillis > ALLOWED_WINDOW_MILLIS) {
        // The token is invalid! See below for further checks.
        ...
}

Java

JSONObject requestDetails =
    new JSONObject(payload).getJSONObject("requestDetails");
String requestPackageName = requestDetails.getString("requestPackageName");
String nonce = requestDetails.getString("nonce");
long timestampMillis = requestDetails.getLong("timestampMillis");
long currentTimestampMillis = ...;

// Ensure the token is from your app.
if (!requestPackageName.equals(expectedPackageName)
        // Ensure the token is for this specific request. See “Generate nonce”
        // section of the doc on how to store/compute the expected nonce.
    || !nonce.equals(expectedNonce)
        // Ensure the freshness of the token.
    || currentTimestampMillis - timestampMillis > ALLOWED_WINDOW_MILLIS) {
        // The token is invalid! See below for further checks.
        ...
}

Kolom integritas aplikasi

Kolom appIntegrity berisi informasi terkait paket.

appIntegrity: {
  // PLAY_RECOGNIZED, UNRECOGNIZED_VERSION, or UNEVALUATED.
  appRecognitionVerdict: "PLAY_RECOGNIZED"
  // The package name of the app.
  // This field is populated iff appRecognitionVerdict != UNEVALUATED.
  packageName: "com.package.name"
  // The sha256 digest of app certificates.
  // This field is populated iff appRecognitionVerdict != UNEVALUATED.
  certificateSha256Digest: ["6a6a1474b5cbbb2b1aa57e0bc3"]
  // The version of the app.
  // This field is populated iff appRecognitionVerdict != UNEVALUATED.
  versionCode: "42"
}

appRecognitionVerdict dapat memiliki nilai berikut:

PLAY_RECOGNIZED
Aplikasi dan sertifikat cocok dengan versi yang didistribusikan oleh Google Play.
UNRECOGNIZED_VERSION
Sertifikat atau nama paket tidak cocok dengan data Google Play.
UNEVALUATED
Integritas aplikasi tidak dievaluasi. Persyaratan yang diperlukan tidak terpenuhi, seperti perangkat tidak cukup tepercaya.

Untuk memastikan bahwa token dibuat oleh aplikasi yang dibuat oleh Anda, pastikan integritas aplikasi seperti yang diharapkan, seperti yang ditampilkan dalam cuplikan kode berikut:

Kotlin

val requestDetails = JSONObject(payload).getJSONObject("appIntegrity")
val appRecognitionVerdict = requestDetails.getString("appRecognitionVerdict")

if (appRecognitionVerdict == "PLAY_RECOGNIZED") {
    // Looks good!
}

Java

JSONObject requestDetails =
    new JSONObject(payload).getJSONObject("appIntegrity");
String appRecognitionVerdict =
    requestDetails.getString("appRecognitionVerdict");

if (appRecognitionVerdict.equals("PLAY_RECOGNIZED")) {
    // Looks good!
}

Anda juga dapat memeriksa nama paket aplikasi, versi aplikasi, dan sertifikat aplikasi secara manual.

Kolom integritas perangkat

Kolom deviceIntegrity dapat berisi satu nilai, deviceRecognitionVerdict, yang memiliki satu atau beberapa label yang merepresentasikan seberapa baik perangkat dapat menerapkan integritas aplikasi. Jika perangkat tidak memenuhi kriteria label apa pun, kolom deviceIntegrity akan kosong.

deviceIntegrity: {
  // "MEETS_DEVICE_INTEGRITY" is one of several possible values.
  deviceRecognitionVerdict: ["MEETS_DEVICE_INTEGRITY"]
}

Secara default, deviceRecognitionVerdict dapat memiliki salah satu label berikut:

MEETS_DEVICE_INTEGRITY
Aplikasi ini berjalan pada perangkat Android yang didukung oleh layanan Google Play. Perangkat ini lulus pemeriksaan integritas sistem dan memenuhi persyaratan kompatibilitas Android.
Tanpa label (nilai kosong)
Aplikasi berjalan pada perangkat yang memiliki tanda serangan (seperti hooking API) atau penyusupan sistem (seperti di-root), atau aplikasi tidak berjalan pada perangkat fisik (seperti emulator yang tidak lulus pemeriksaan integritas Google Play).

Untuk memastikan bahwa token berasal dari perangkat tepercaya, pastikan deviceRecognitionVerdict sesuai dengan yang diharapkan, seperti ditunjukkan dalam cuplikan kode berikut:

Kotlin

val deviceIntegrity =
                JSONObject(payload).getJSONObject("deviceIntegrity")
val deviceRecognitionVerdict =
                if (deviceIntegrity.has("deviceRecognitionVerdict")) {
                    deviceIntegrity.getJSONArray("deviceRecognitionVerdict").toString()
                } else {
                    ""
                }

if (deviceRecognitionVerdict.contains("MEETS_DEVICE_INTEGRITY")) {
    // Looks good!
}

Java

JSONObject deviceIntegrity =
                new JSONObject(payload).getJSONObject("deviceIntegrity");
String deviceRecognitionVerdict =
                deviceIntegrity.has("deviceRecognitionVerdict")
                ? deviceIntegrity.getJSONArray("deviceRecognitionVerdict").toString()
                : "";

if (deviceRecognitionVerdict.contains("MEETS_DEVICE_INTEGRITY")) {
    // Looks good!
}

Jika Anda mengalami masalah dengan pengujian perangkat memenuhi integritas perangkat, pastikan ROM pabrik sudah diinstal (misalnya, dengan mereset perangkat) dan bootloader terkunci. Anda juga dapat membuat pengujian Integritas Play di Konsol Play.

Jika Anda memilih untuk menerima label tambahan dalam verdict integritas, deviceRecognitionVerdict dapat memiliki label tambahan berikut:

MEETS_BASIC_INTEGRITY
Aplikasi berjalan di perangkat yang telah lulus pemeriksaan integritas sistem dasar. Perangkat mungkin tidak memenuhi persyaratan kompatibilitas Android dan mungkin tidak disetujui untuk menjalankan layanan Google Play. Misalnya, perangkat mungkin menjalankan versi Android yang tidak dikenal, mungkin memiliki bootloader yang tidak terkunci, atau mungkin belum disertifikasi oleh produsen.
MEETS_STRONG_INTEGRITY
Aplikasi tersebut berjalan di perangkat Android yang didukung oleh layanan Google Play dan memiliki jaminan kuat atas integritas sistem seperti bukti integritas booting yang didukung hardware. Perangkat lulus pemeriksaan integritas sistem dan memenuhi persyaratan kompatibilitas Android.

Selain itu, jika aplikasi Anda dirilis ke emulator yang disetujui, deviceRecognitionVerdict juga dapat menggunakan label berikut:

MEETS_VIRTUAL_INTEGRITY
Aplikasi berjalan di Android emulator yang didukung oleh layanan Google Play. Emulator lulus pemeriksaan integritas sistem dan memenuhi persyaratan kompatibilitas inti Android.

Kolom detail akun

Kolom accountDetails berisi satu nilai, appLicensingVerdict, yang menunjukkan status pemberian lisensi/hak aplikasi.

accountDetails: {
  // This field can be LICENSED, UNLICENSED, or UNEVALUATED.
  appLicensingVerdict: "LICENSED"
}

appLicensingVerdict dapat memiliki nilai berikut:

LICENSED
Pengguna memiliki hak aplikasi. Dengan kata lain, pengguna menginstal atau membeli aplikasi Anda di Google Play.
UNLICENSED
Pengguna tidak memiliki hak aplikasi. Hal ini terjadi jika, misalnya, pengguna melakukan sideload aplikasi Anda atau tidak mendapatkan aplikasi dari Google Play.
UNEVALUATED

Detail pemberian lisensi tidak dievaluasi karena persyaratan yang diperlukan tidak terjawab.

Hal ini dapat terjadi karena beberapa alasan, termasuk:

  • Perangkat tidak cukup tepercaya.
  • Versi aplikasi yang diinstal di perangkat tidak dikenal oleh Google Play.
  • Pengguna tidak login ke Google Play.

Untuk memeriksa apakah pengguna memiliki hak aplikasi untuk aplikasi Anda, verifikasi bahwa appLicensingVerdict sudah sesuai harapan, seperti yang ditunjukkan dalam cuplikan kode berikut:

Kotlin

val requestDetails = JSONObject(payload).getJSONObject("accountDetails")
val appLicensingVerdict = requestDetails.getString("appLicensingVerdict")

if (appLicensingVerdict == "LICENSED") {
    // Looks good!
}

Java

JSONObject requestDetails =
    new JSONObject(payload).getJSONObject("accountDetails");
String appLicensingVerdict = requestDetails.getString("appLicensingVerdict");

if (appLicensingVerdict.equals("LICENSED")) {
    // Looks good!
}