Ücretsiz APK oluştur

JavaScript köprü API'si

Çökme raporları ve cihaz bütünlüğü

Sayfanın günlüğe yazdığı hatalar, son yerel çökme ve telefonun root'lu veya değiştirilmiş görünüp görünmediği; sunucunuz için isteğe bağlı bir Play Integrity belirteciyle.

AppMint.integrity yardımcı#

AppMint.integrity.check() / AppMint.integrity.token(nonce, cloudProjectNumber?)

Örnek

getDeviceIntegrity ve requestIntegrityToken işlevlerinin Promise sürümleri; derleme Cihaz bütünlüğü denetimlerini (Entegrasyon adımı, 3. Adım) açtığında sayfanızın ilk betiğinden önce bulunur.

Döndürür: check() sezgi nesnesiyle ({rooted, emulator, debuggerAttached, signals, installer, installedFromPlay, signatureSha256, method: 'heuristic'}) tamamlanır; token(nonce) Play Integrity belirteci dizesiyle tamamlanır veya iletisi hata adı (not_enabled, nonce_too_short, network_error, …) olan ve code Play hata kodunu taşıyan bir Error ile reddedilir. Sezgiler ipucudur; yalnızca sunucunuzun belirteci çözmesi bir karardır.

async function guard() {
  if (!(window.AppMint && AppMint.integrity)) return;          // browser, or option off
  const local = await AppMint.integrity.check();
  if (local.rooted) console.warn('rooted signals:', local.signals);

  const { nonce } = await fetch('/api/integrity/nonce').then(r => r.json());
  try {
    const token = await AppMint.integrity.token(nonce);          // project number from the build
    await fetch('/api/integrity/verify', { method: 'POST', body: token });
  } catch (e) {
    console.warn('Play Integrity unavailable:', e.message, e.code);
  }
}

Uygulama yeniden paketlenmiş bir kopya olduğunda uyarın:

if (window.AppMint && AppMint.integrity) {
  const d = await AppMint.integrity.check();
  if (!d.installedFromPlay) console.log('installed by', d.installer || 'side-loaded');
  if (d.signatureSha256 && d.signatureSha256 !== MY_CERT_SHA256) reportTamper(d.signatureSha256);
}

Bir belirteç hatasını adıyla ele alın:

async function getToken(nonce) {
  if (!(window.AppMint && AppMint.integrity)) return null;
  try {
    return await AppMint.integrity.token(nonce);
  } catch (e) {
    if (e.message === 'not_enabled') console.warn('Device integrity checks are off in this build');
    else if (e.message === 'nonce_too_short') console.warn('the server nonce must be longer');
    else console.warn('Play Integrity error', e.message, e.code);
    return null;
  }
}

getDeviceIntegrity köprü#

window.WebToApk.getDeviceIntegrity(): String

Yerel root / emülatör / hata ayıklayıcı SEZGİLERİ (bkz. DeviceIntegrity) JSON olarak: `{rooted, emulator, debuggerAttached, signals:[…], installer, installedFromPlay, signatureSha256, method:"heuristic", sdk}`. Temiz bir sonuç "işaret bulunamadı" demektir, kanıt değil - güvenebileceğiniz bir karar için sunucunuza bir Play Integrity belirteci gönderin (requestIntegrityToken). Derleme Cihaz bütünlüğünü açmadıysa `{"error":"not_enabled"}`. Birkaç milisaniye bekletir (dosyaları okur): bir kez çağırın.

Örnek

JSON dizesi olarak yerel root, emülatör ve hata ayıklayıcı SEZGİLERİ. Temiz bir sonuç "işaret bulunamadı" demektir, kanıt değil: root'u gizleyen bir modül kullanan kararlı bir kullanıcı geçer. Uyarmak veya bir denetim eklemek için kullanın; güvenebileceğiniz bir karar için sunucunuza bir Play Integrity belirteci gönderin (requestIntegrityToken).

Döndürür: '{"rooted":false,"emulator":false,"debuggerAttached":false,"signals":[],"installer":"com.android.vending","installedFromPlay":true,"signatureSha256":"AB:CD:…","method":"heuristic","sdk":34}' veya '{"error":"not_enabled"}'. Sinyaller neyin bulunduğunu adlandırır: test-keys, su-binary:/system/xbin/su, root-path:/data/adb/magisk, root-app:com.topjohnwu.magisk, system-writable, ro.debuggable=1, ro.secure=0, selinux-permissive, bootloader-unlocked, emulator:*, debugger-attached. Gerekenler: Entegrasyon adımında (3. Adım) Cihaz bütünlüğü denetimlerini açın. Birkaç milisaniye sürer (dosyaları okur): bir kez çağırın, döngüde değil.

function deviceCheck() {
  if (!(window.WebToApk && window.WebToApk.getDeviceIntegrity)) return null;
  const r = JSON.parse(WebToApk.getDeviceIntegrity());
  if (r.error) return null;                       // 'not_enabled'
  return r;
}

const d = deviceCheck();
if (d && d.rooted) {
  showNotice('This phone looks rooted (' + d.signals.join(', ') + '). Some features are turned off.');
}
if (d && !d.installedFromPlay) console.log('installed by', d.installer || 'unknown (side-loaded)');

Kendi imzanızı denetleyin - uygulamanızın yeniden paketlenmiş bir kopyası başka bir anahtarla imzalanır:

const MY_CERT = 'AB:CD:EF:…';   // Play Console → App integrity → App signing key SHA-256
const d = deviceCheck();
if (d && d.signatureSha256 && d.signatureSha256 !== MY_CERT) reportTamper();

Güvenebileceğiniz bir karar için bir sunucuya sorun, bir Play Integrity belirteciyle (yanıt bir olay olarak gelir).

window.addEventListener('appmint:integrity', function (e) {
  if (e.detail.error) { showNotice('Check failed: ' + e.detail.error); return; }
  fetch('https://api.example.com/verify', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ token: e.detail.token })
  });
});
if (window.WebToApk && WebToApk.requestIntegrityToken) {
  const nonce = crypto.randomUUID().replace(/-/g, '') + '0000';   // your server issues and checks this
  WebToApk.requestIntegrityToken(nonce, '123456789012');          // your Google Cloud project number
}

getLastCrash köprü#

window.WebToApk.getLastCrash(): String

Bu kurulumun kaydettiği en yeni çökme (uygulamanın yakalanmayan bir hatayla kapanması veya sayfa oluşturucunun çökmesi), JSON olarak `{kind, message, stack, url, at, count, appVersion, user, fp}`, ya da hiç olmadıysa (veya Çökme raporları kapalıysa) "". Yüklemeden sonra da saklanır, böylece bir sayfa "üzgünüz, uygulama geçen sefer kapandı" diyebilir.

Örnek

Bu kurulumun kaydettiği en yeni çökme - uygulamanın yakalanmayan bir hatayla kapanması (kind: "native") veya sayfa oluşturucunun çökmesi (kind: "renderer") - JSON dizesi olarak, ya da hiç olmadıysa ''.

Döndürür: '{"kind","message","stack","url","at","count","appVersion","user","fp"}' veya ''. Derleme Çökme raporlarını açmadıysa ayrıca ''. Gerekenler: Entegrasyon adımında (3. Adım) Çökme raporları açık.

Bir çökmeden sonra bir kez özür dileyin:

(function () {
  if (!(window.WebToApk && window.WebToApk.getLastCrash)) return;
  const raw = WebToApk.getLastCrash();
  if (!raw) return;
  const crash = JSON.parse(raw);
  if (localStorage.getItem('seenCrash') === String(crash.at)) return;   // shown already
  localStorage.setItem('seenCrash', String(crash.at));
  showBanner('Sorry, the app closed unexpectedly last time. We have been told about it.');
})();

Saklanan çökmeyi kendi sunucunuza yükleyin, bir kez, gerçekleştiği uygulama sürümüyle.

(function () {
  if (!(window.WebToApk && WebToApk.getLastCrash)) return;
  const raw = WebToApk.getLastCrash();
  if (!raw) return;                         // no crash, or Crash reports are off
  const c = JSON.parse(raw);
  if (localStorage.getItem('sentCrash') === String(c.at)) return;
  fetch('https://api.example.com/crashes', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ kind: c.kind, message: c.message, version: c.appVersion, count: c.count })
  }).then(function () { localStorage.setItem('sentCrash', String(c.at)); });
})();

Uygun bir mesaj vermek için türüne göre dallanın.

const raw = window.WebToApk && WebToApk.getLastCrash && WebToApk.getLastCrash();
if (raw) {
  const c = JSON.parse(raw);
  showBanner(c.kind === 'renderer'
    ? 'The page ran out of memory last time. Try closing other apps.'
    : 'The app closed unexpectedly last time.');
}

Notlar: rapor yüklendikten sonra da kalır; bu yüzden mesajı bir kez göstermek için at değerini sakladığınızla karşılaştırın. Sayfa hataları ve logError çağrıları çökme değildir ve burada asla görünmez.

logError köprü#

window.WebToApk.logError(message: String, stack: String): Boolean

Çökme raporları: sayfanızın YAKALADIĞI bir hatayı (başarısız ödeme çağrısı, ayrıştırma hatası) kaydeder; böylece AppMint'in Çökme raporları ekranında çökmelerin yanında gruplanıp sayılarak görünür. [stack]: `err.stack` veya "". Derleme Çökme raporlarını açmadıysa, ikisi de boşsa veya aynı hata bu oturumda zaten çok kez günlüğe yazıldıysa false döndürür. Mesaja asla kişisel veri (e-posta, telefon, belirteç) koymayın.

Örnek

Sayfanızın YAKALADIĞI bir hatayı (başarısız ödeme çağrısı, JSON ayrıştırma hatası) kaydeder; böylece AppMint'in Çökme raporları ekranında (Uygulamayı Güncelle → uygulamanız → Çökme raporları) çökmelerle gruplanıp sayılarak görünür. Yakalanmayan hatalar ve işlenmeyen promise reddedilmeleri sizin için kaydedilir; bu, sizin ele aldıklarınız içindir.

Döndürür: kaydedildiğinde true; derleme Çökme raporlarını açmadıysa, iki argüman da boşsa veya aynı hata bu oturumda zaten çok kez günlüğe yazıldıysa false. Raporlar uygulama bir sonraki açıldığında yüklenir. Gerekenler: derleme sırasında Entegrasyon adımında (3. Adım) Çökme raporları seçeneğini açın. Mesaja asla kişisel veri (e-posta adresi, telefon numarası, belirteç) koymayın.

Ele alınmış bir hatayı günlüğe yazın:

function logError(err, context) {
  if (!(window.WebToApk && window.WebToApk.logError)) return false;   // browser, or an older build
  var msg = (context ? context + ': ' : '') + (err && err.message ? err.message : String(err));
  return WebToApk.logError(msg, (err && err.stack) || '');
}

async function checkout(cart) {
  try {
    await payments.charge(cart.total);
  } catch (err) {
    logError(err, 'checkout');          // shows up as "Logged by your page"
    showToast('Payment failed, please try again');
  }
}

Başarısız API çağrılarını adresin sorgu dizesi olmadan bildirin (belirteç taşıyabilir):

async function api(path) {
  const res = await fetch('/api' + path);
  if (!res.ok && window.WebToApk && WebToApk.logError) {
    WebToApk.logError('API ' + res.status + ' ' + path.split('?')[0], '');
  }
  return res;
}

Yakalanan bir hatayı kullanıcı kimliğiyle günlüğe yazın; böylece bir kullanıcının sorunları sonradan bulunabilir.

if (window.WebToApk && WebToApk.setCrashUserId) WebToApk.setCrashUserId('user_' + currentUser.id);

try {
  JSON.parse(savedState);
} catch (err) {
  if (window.WebToApk && WebToApk.logError) {
    const logged = WebToApk.logError('state parse: ' + err.message, err.stack || '');
    if (!logged) console.warn('not recorded (Crash reports off, or repeated too often)');
  }
}

Notlar: mesajdaki sayılar farklı olsa bile aynı nedene sahip raporlar gruplanır (item 42 ve item 7). Sayfa adresi sorgu ve parça kısmı olmadan saklanır.

requestIntegrityToken köprü#

window.WebToApk.requestIntegrityToken(nonce: String, cloudProjectNumber: String): String

Google Play'den bir Play Integrity belirteci ister (klasik istek). [nonce]: URL-güvenli Base64, 16-500 karakter, bu tek istek için SİZİN sunucunuz tarafından üretilir. [cloudProjectNumber]: Google Cloud proje numaranız veya derlemede ayarlananı kullanmak için "" (Play'den yüklenen bir uygulama ikisini de boş bırakabilir). Hemen bir istek kimliği döndürür (derleme Cihaz bütünlüğünü açmadıysa ""); yanıt `appmint:integrity` / `window.onAppMintIntegrity({requestId, token})` veya `{requestId, error, code}` olarak gelir. Belirteci sunucunuza gönderin; sunucu onu Google ile çözer (playintegrity.googleapis.com … :decodeIntegrityToken) ve nonce değerini denetler.

Örnek

Google Play'den bir Play Integrity belirteci ister (klasik istek). Belirteç telefonda opaktır: SUNUCUNUZ onu Google'a gönderir, kararı okur (gerçek uygulama mı? gerçek cihaz mı? lisanslı mı?) ve nonce değerinin kendi verdiği olduğunu denetler. Yanıtı güvenilir yapan şey bu sunucu denetimidir.

Döndürür: hemen bir istek kimliği (derleme Cihaz bütünlüğünü açmadıysa ''). Yanıt appmint:integrity olayı olarak (ve window.onAppMintIntegrity) {requestId, token} veya {requestId, error, code} ile gelir - nonce_too_short, nonce_is_not_base64, network_error, play_store_not_found, cloud_project_number_is_invalid, too_many_requests gibi hatalar. Gerekenler: Entegrasyon adımında (3. Adım) Cihaz bütünlüğü denetimleri açık, uygulama için Play Integrity API etkin (Play Console → Uygulama bütünlüğü) ve - Google Play'den YÜKLENMEMİŞ bir uygulama için - sihirbazda veya ikinci argüman olarak Google Cloud proje numaranız ('' derlemedekini kullanır).

async function integrityToken() {
  // 1. Your server makes a one-time nonce: URL-safe Base64, 16-500 characters.
  const { nonce } = await fetch('/api/integrity/nonce').then(r => r.json());
  return new Promise((resolve, reject) => {
    const id = WebToApk.requestIntegrityToken(nonce, '');
    if (!id) return reject(new Error('not_enabled'));
    window.addEventListener('appmint:integrity', function on(e) {
      if (e.detail.requestId !== id) return;
      window.removeEventListener('appmint:integrity', on);
      e.detail.token ? resolve(e.detail.token) : reject(new Error(e.detail.error));
    });
  });
}

// 2. Your server decodes it with Google and decides.
const token = await integrityToken();
const verdict = await fetch('/api/integrity/verify', { method: 'POST', body: token }).then(r => r.json());

Sunucu adımı - bağlı Cloud projesinin bir hizmet hesabıyla {"integrity_token": token} ile POST https://playintegrity.googleapis.com/v1/<your.package>:decodeIntegrityToken çağrısı yapın, sonra denetleyin:

const p = decoded.tokenPayloadExternal;
const ok = p.requestDetails.nonce === issuedNonce &&
  p.appIntegrity.appRecognitionVerdict === 'PLAY_RECOGNIZED' &&
  p.deviceIntegrity.deviceRecognitionVerdict.includes('MEETS_DEVICE_INTEGRITY');

Geri çağrı biçimi, olayları dinleyemeyen kod için:

window.onAppMintIntegrity = function (d) {
  if (d.error) console.warn('integrity failed', d.error, d.code);
  else sendToServer(d.token);
};
WebToApk.requestIntegrityToken(nonceFromServer, '123456789012');

setCrashUserId köprü#

window.WebToApk.setCrashUserId(id: String): Boolean

Çökme raporları: oturum açmış kullanıcı için SİZİN kimliğiniz (harfler, rakamlar ve _ @ . : + -, en fazla 128), bundan sonraki raporlara eklenir; böylece etkilenen kullanıcıları sayabilir ve bir kullanıcının çökmelerini bulabilirsiniz. "" temizler. Asla e-posta adresi veya telefon numarası değil. Başka karakterler içeren bir kimlik için veya Çökme raporları kapalıysa false döndürür.

Örnek

Oturum açmış kullanıcı için SİZİN kimliğinizi bundan sonraki çökme raporlarına ekler; böylece Çökme raporları ekranı bir sorunun kaç kullanıcıyı etkilediğini sayabilir. Oturum açtıktan sonra çağırın; oturumu kapatırken '' ile çağırın.

Döndürür: kaydedildiğinde true; harf, rakam ve _ @ . : + - dışında karakterler içeren (veya 128'den uzun) bir kimlik için ya da derleme Çökme raporlarını açmadıysa false. Gerekenler: Entegrasyon adımında (3. Adım) Çökme raporları açık. Veritabanı kimliğinizi kullanın, asla e-posta adresi veya telefon numarası değil.

function crashUser(id) {
  if (!(window.WebToApk && window.WebToApk.setCrashUserId)) return false;
  return WebToApk.setCrashUserId(id);
}

async function signIn(email, password) {
  const user = await myBackend.signIn(email, password);
  crashUser(user.id);            // e.g. 'u_42'
}

function signOut() {
  crashUser('');                 // later reports carry no user
}

Reddedilen bir kimlik veya Çökme raporsuz bir derleme false döndürür:

function setCrashUser(id) {
  if (!(window.WebToApk && WebToApk.setCrashUserId)) return;
  if (!WebToApk.setCrashUserId(id)) {
    console.warn('Crash user id not saved: bad characters, over 128 long, or Crash reports are off');
  }
}
setCrashUser('u_42');       // your database id, never an e-mail address

Yeniden başlatmadan sonra kullanıcıya son çökmeyi bildirin:

if (window.WebToApk && WebToApk.getLastCrash) {
  const raw = WebToApk.getLastCrash();          // '' when there has been none
  if (raw) {
    const c = JSON.parse(raw);
    showMessage('Sorry, the app closed last time (' + c.kind + ').');
  }
}

Notlar: kimlik, siz değiştirene kadar telefonda kalır ve yalnızca çökme raporlarının içinde gönderilir.

Her belge derlemesinde uygulama çalışma zamanından ve örnek dosyalarından oluşturulur. Markdown olarak okuyun · Tüm aileler.

22.10.2026 tarihinde yayınlanan köprüyle karşılaştırılarak kontrol edildi.