Ücretsiz APK oluştur

JavaScript köprü API'si

Satın alımlar ve haklar

Kendi sayfanızdan satış yapma ve bu alıcının zaten neye sahip olduğunu sorma. Bunların etrafındaki kod için uygulama içi satın alma rehberine bakın.

AppMint.revenuecat yardımcı#

AppMint.revenuecat — RevenueCat subscriptions

Örnek

RevenueCat projeniz üzerinden abonelikler ve tek seferlik satın alımlar: teklifler, satın alımlar, geri yüklemeler ve haklar. Satın alımlar uygulamada Google Play üzerinden yürür; RevenueCat her birini uygulama tamamlamadan önce kaydeder.

Döndürür: her yöntem her zaman {ok: true, …} veya {ok: false, error, message?} ile tamamlanan bir Promise döndürür. configure({appUserId?}), getCustomerInfo(), restore(), logIn(id), logOut() → {ok, customerInfo}; getOfferings() → {ok, current, all}; purchase(pkgOrProductId, {offeringId?, offerId?, consumable?}) → {ok, productId, transaction, customerInfo}; getAppUserId() → {ok, appUserId}. Gerekenler: Entegrasyon adımı → GENEL Google Play anahtarınızla RevenueCat (goog_…).

if (window.AppMint && AppMint.revenuecat) {
  await AppMint.revenuecat.configure({ appUserId: currentUser && currentUser.id });   // omit for an anonymous user
  var off = await AppMint.revenuecat.getOfferings();
  if (off.ok && off.current) {
    off.current.availablePackages.forEach((p) => addPlanButton(p.identifier, p.title, p.price, p.period));
  }
}

async function buy(pkg) {                        // a package object from getOfferings, or its identifier
  var r = await AppMint.revenuecat.purchase(pkg);
  if (r.ok) unlock(r.customerInfo.entitlements.active);
  else if (r.error === 'pending') alert('Payment pending — it will unlock when Google confirms it.');
  else if (!r.cancelled) alert('Purchase failed: ' + (r.message || r.error));
}

var info = await AppMint.revenuecat.getCustomerInfo();
var isPro = info.ok && !!info.customerInfo.entitlements.active.pro;

window.addEventListener('appmint:revenuecat', (e) => render(e.detail.customerInfo));   // entitlements changed
document.getElementById('restore').onclick = () => AppMint.revenuecat.restore();

Satın alımları kendi hesabınıza bağlayın:

if (window.AppMint && AppMint.revenuecat) {
  var li = await AppMint.revenuecat.logIn(user.id);       // after sign-in
  if (li.ok) refreshPlan(li.customerInfo.entitlements.active);
  document.getElementById('signout').onclick = async function () {
    await AppMint.revenuecat.logOut();
    showAnonymous();
  };
}

Her başarısızlık için dürüst bir mesaj gösterin:

async function buyPackage(pkg) {
  var r = await AppMint.revenuecat.purchase(pkg);
  if (r.ok) return unlock(r.customerInfo.entitlements.active);
  if (r.cancelled || r.error === 'cancelled') return;
  var text = {
    not_configured: 'Purchases are not set up in this build',
    billing_unavailable: 'Google Play billing is not available on this phone',
    product_not_found: 'This plan is not available right now',
    network: 'No connection - try again online'
  }[r.error] || ('Purchase failed: ' + (r.message || r.error));
  alert(text);
}

Notlar: customerInfo = {appUserId, originalAppUserId, requestDate, managementUrl, entitlements: {active, all}, activeSubscriptions, allPurchasedProductIds, nonSubscriptionTransactions}; her hak {identifier, isActive, productIdentifier, expirationDate, latestPurchaseDate, willRenew, periodType, store, isSandbox}. Tekrar satın alınabilmeleri için jeton/kredi gibi ürünlerde {consumable: true} verin; abonelikler ve tek seferlik kilit açmalar onaylanır. Çevrimdışıyken getCustomerInfo, son bilinen bilgiyi stale: true ile yanıtlar; RevenueCat'e ulaşılamazken yapılan satın alma yine de tamamlanır ve makbuzu bir sonraki configure/restore sırasında gönderilir (receiptQueued: true). Hatalar: not_configured, network, revenuecat_error (status, code, message ile), billing_unavailable, billing_error, product_not_found, offer_not_found, cancelled, pending, already_anonymous, foreign_origin. RevenueCat'in Google Play hizmet hesabı bağlantısını RevenueCat'in istediği şekilde kurun; bir uygulamaya asla gizli sk_ anahtarı koymayın. Mevcut AppMintIAP satın alımları değişmeden çalışmaya devam eder.

AppMintIAP.available yardımcı#

window.AppMintIAP.available

Örnek

Bir özellik (işlev değil): bu sayfa Google Play üzerinden gerçekten satın alabildiğinde true.

Döndürür: bir mantıksal (boolean) özellik. Gerekenler: 💰 Uygulama İçi Ürün Sat. Bu olmadan window.AppMintIAP hiç bulunmaz.

Satışa bir şey sunmadan önce denetleyin ve burada satın almanın mümkün olmadığını açıkça söyleyin:

var inApp = !!(window.AppMintIAP && window.AppMintIAP.available);
var buyBtn = document.getElementById('buy-pro');

if (!inApp) {
  buyBtn.disabled = true;
  buyBtn.textContent = 'Available in the Android app';
}

Web siteniz ve uygulamanız için tek bir yükseltme düğmesi: uygulamada Google Play, webde kendi ödeme sayfanız:

document.getElementById('upgrade').addEventListener('click', function () {
  var iap = window.AppMintIAP;
  if (iap && iap.available) {
    iap.buy('pro_unlock');                                   // inside the app
  } else if (window.WebToApk) {
    showMessage('Upgrades are not available in this version of the app.');
  } else {
    location.href = '/checkout/pro';                         // your website's own checkout
  }
});

React veya Next.js'te bunu bir efekt içinde okuyun (sunucu oluşturmasında window yoktur); böylece uygulama dışında satın alınacak hiçbir şey oluşturulmaz:

import { useEffect, useState } from 'react';

export function useCanBuy() {
  const [canBuy, setCanBuy] = useState(false);     // false during the server render
  useEffect(() => {
    setCanBuy(!!(window.AppMintIAP && window.AppMintIAP.available));
  }, []);
  return canBuy;
}

// const canBuy = useCanBuy();
// return canBuy ? <UpgradeButton /> : null;

Notlar: window.AppMintIAP, sayfanızın ilk betiği çalışmadan önce kurulur; yalnızca yaklaşık 2021'den eski WebView'larda sayfa yüklenmeyi bitirdikten sonra gelir; bu yüzden orada da çalışması gereken kod onu beklemelidir (bkz. AppMintIAP.sell içindeki whenIap deseni). Aynı değer AppMintIAP.state().available içindedir. Teklifi data-iap-container içine almak, kod yazmadan uygulama dışında gizler.

AppMintIAP.buy yardımcı#

window.AppMintIAP.buy(id)

Örnek

Kendi düğmenizden bir ürün için Google Play'in satın alma sayfasını açar. Kendi başına hiçbir şey vermez.

Döndürür: hiçbir şey. Sonuç appmint:purchase {productId, owned:true} olarak (ve AppMintIAP.onChange aracılığıyla) veya appmint:purchase-failed {productId, reason} olarak gelir. Gerekenler: 💰 Uygulama İçi Ürün Sat ve Play Console'da etkin ürün.

Düğmeyi kendiniz çizdiğinizde kullanın; kilit açma dokunuştan değil, sahiplikten gelir:

var btn = document.getElementById('buy-pro');

btn.addEventListener('click', function () {
  if (!window.AppMintIAP || !window.AppMintIAP.available) { showMessage('Available in the Android app.'); return; }
  btn.disabled = true;
  window.AppMintIAP.buy('pro_unlock');
});

window.addEventListener('appmint:purchase', function (e) {
  if (e.detail.productId === 'pro_unlock') unlockPro();     // must be safe to run twice
  btn.disabled = false;
});
window.addEventListener('appmint:purchase-failed', function (e) {
  btn.disabled = false;
  showMessage(e.detail.reason === 'pending' ? 'Payment pending. It unlocks once Google confirms.' : 'Purchase failed: ' + e.detail.reason);
});
document.addEventListener('visibilitychange', function () { if (!document.hidden) btn.disabled = false; });   // cancel: no event

Seçili planı satın alan tek bir "Devam" düğmesi olan plan seçici:

document.getElementById('continue').addEventListener('click', function () {
  var choice = document.querySelector('input[name="plan"]:checked');   // value = product ID
  var iap = window.AppMintIAP;
  if (!choice || !iap || !iap.available) return;
  if (iap.isOwned(choice.value)) { showMessage('You already have this plan.'); return; }
  iap.buy(choice.value);
});

window.addEventListener('appmint:purchase', function () {
  document.getElementById('plan-picker').hidden = true;
  showMessage('Thanks! Your plan is active.');
});

Yalnızca Pro bir araca dokunmak, sahipse onu çalıştırır, değilse satın alma sayfasını açar:

document.querySelectorAll('[data-pro-tool]').forEach(function (btn) {
  btn.addEventListener('click', function () {
    var iap = window.AppMintIAP;
    if (iap && iap.isOwned('pro_unlock')) { runTool(btn.getAttribute('data-pro-tool')); return; }
    if (iap && iap.available) iap.buy('pro_unlock');
    else showMessage('Pro tools are available in the Android app.');
  });
});

window.addEventListener('appmint:purchase', function (e) {
  if (e.detail.productId === 'pro_unlock') document.body.classList.add('pro');
});

Notlar: buy(), çıplak WebToApk.purchase(id) çağrısıdır: meşgul etiketi yok, grup denetimi yok. sell(), start() veya data-iap-buy ile bağlanan düğmeler bunların hepsini sizin yerinize yapar, bu yüzden onları tercih edin.

AppMintIAP.isOwned yardımcı#

window.AppMintIAP.isOwned(id)

Örnek

Kullanıcının bir ürüne (tek seferlik ürün veya hâlâ etkin bir abonelik) sahip olup olmadığını anında ve çevrimdışı denetler.

Döndürür: eşzamanlı olarak true / false; uygulama dışında false. Gerekenler: 💰 Uygulama İçi Ürün Sat.

function paintPro() {
  var pro = !!(window.AppMintIAP && window.AppMintIAP.isOwned('pro_unlock'));
  document.getElementById('pro-tools').hidden = !pro;
}

// Re-check whenever ownership can change.
window.addEventListener('appmint:purchase', paintPro);
window.addEventListener('appmint:owned-changed', paintPro);

// AppMintIAP arrives after the page has loaded: paint once it is there.
var waitIap = setInterval(function () {
  if (window.AppMintIAP) { clearInterval(waitIap); paintPro(); }
}, 200);
setTimeout(function () { clearInterval(waitIap); }, 10000);   // not in the app, or selling is off

Birkaç plandan herhangi biri üzerinden Pro: aylık, yıllık ve ömür boyu ürünlerin hepsi sayılır:

var PRO_PLANS = ['pro_monthly', 'pro_yearly', 'pro_lifetime'];

function isPro() {
  var iap = window.AppMintIAP;
  return !!iap && PRO_PLANS.some(function (id) { return iap.isOwned(id); });
}

function paintPlan() { document.body.classList.toggle('pro', isPro()); }
paintPlan();
window.addEventListener('appmint:purchase', paintPlan);
window.addEventListener('appmint:owned-changed', paintPlan);   // an expired subscription or a refund

Bir rota koruması: yalnızca Pro ekranları, Pro'su olmayan kullanıcıyı yükseltme ekranına gönderir:

function route() {
  var path = location.hash.slice(1) || '/';
  var proOnly = path.indexOf('/reports') === 0;
  if (proOnly && !(window.AppMintIAP && window.AppMintIAP.isOwned('pro_unlock'))) {
    location.replace('#/upgrade');
    return;
  }
  render(path);
}

window.addEventListener('hashchange', route);
route();

Notlar: WebToApk.isOwned(id) ile aynı yanıt. Sahiplik her açılışta kullanıcının Google hesabından geri yüklenir ve telefona kaydedilir, bu yüzden yeniden yüklemelerden etkilenmez ve çevrimdışı çalışır. Hakkı asla yalnızca şurada tutmayın: localStorage.

AppMintIAP.onChange yardımcı#

window.AppMintIAP.onChange(fn)

Örnek

Fonksiyonunuzu satın alma durumuyla hemen ve her değiştiğinde yeniden çağırır (fiyatlar geldi, bir satın alma gerçekleşti, açılışta geri yükleme, bir iade, süresi dolan abonelik).

Döndürür: bir abonelikten çıkma işlevi. fn şunu alır: {available, adFree, owned, ownedIds, products}; products, yardımcının bildiği ürünler için {id: {price, currency, type, period, owned}} olur (sell, start veya data-iap-buy üzerinden). Gerekenler: 💰 Uygulama İçi Ürün Sat.

Bir React kancası. Abonelikten çıkma işlevini doğrudan efektten döndürür:

import { useEffect, useState } from 'react';

type IapState = { available: boolean; adFree: boolean; owned: boolean; ownedIds: string[];
  products: Record<string, { price: string | null; currency: string | null; type: string | null; period: string | null; owned: boolean }> };

export function useIap(ids: string[]) {
  const [iap, setIap] = useState<IapState>({ available: false, adFree: false, owned: false, ownedIds: [], products: {} });
  useEffect(() => {
    let stop: (() => void) | undefined;
    const t = setInterval(() => {                   // AppMintIAP arrives after page load
      const api = (window as any).AppMintIAP;
      if (!api) return;
      clearInterval(t);
      ids.forEach((id) => api.sell(id, null));      // track them, so their prices are fetched
      stop = api.onChange(setIap);
    }, 200);
    return () => { clearInterval(t); if (stop) stop(); };
  }, []);
  return iap;
}

// const iap = useIap(['pro_unlock']);
// const isPro = iap.ownedIds.includes('pro_unlock');
// const price = iap.products.pro_unlock?.price;

Düz JavaScript ile bir ödeme duvarı: iki planı düğmesiz izleyin, sonra fiyatlarını ve geçerli planı her değişiklikten çizin:

var iap = window.AppMintIAP;
var PLANS = ['pro_monthly', 'pro_yearly'];

if (iap) {
  iap.start({ plans: PLANS.map(function (id) { return { id: id }; }) });   // tracked, not drawn
  iap.onChange(function (s) {
    document.getElementById('paywall').hidden = !s.available;
    PLANS.forEach(function (id) {
      var el = document.querySelector('[data-plan="' + id + '"]');
      var p = s.products[id];
      el.hidden = !p;                                // Google has not answered for it (yet)
      if (p) el.querySelector('.price').textContent = p.owned ? 'Your plan' : p.price;
    });
  });
}

Hesap ekranı açıkken abone olun, kapandığında abonelikten çıkın. adFree ve ownedIds, yardımcı çalışmaya başladıktan sonra doldurulur (sayfa sell(), start() veya data-iap-buy):

var stopAccount = null;

function openAccountScreen() {
  if (window.AppMintIAP) {
    stopAccount = window.AppMintIAP.onChange(function (s) {
      document.getElementById('ad-status').textContent = s.adFree ? 'Ad-free' : 'With ads';
      document.getElementById('owned-count').textContent = s.ownedIds.length + ' purchase(s)';
    });
  }
  showScreen('account');
}

function closeAccountScreen() {
  if (stopAccount) { stopAccount(); stopAccount = null; }   // no updates for a hidden screen
  showScreen('home');
}

Notlar: Sahipliği ürün başına okuyun (ownedIds veya products[id].owned). Üst düzey owned yalnızca "herhangi bir şeye sahip olunuyor" demektir. adFree, Reklamları Kaldır hakkıdır (WebToApk.isPremium()). Bir ürün products içine ancak Google onun için yanıt verdikten sonra girer.

AppMintIAP.owned yardımcı#

window.AppMintIAP.owned()

Örnek

Kullanıcının sahip olduğu her ürün kimliğini listeler.

Döndürür: eşzamanlı olarak ürün kimliklerinin bir dizisi, örneğin ['pro_unlock', 'level_pack_2']; uygulama dışında veya hiçbir şeye sahip olunmadığında []. Gerekenler: 💰 Uygulama İçi Ürün Sat.

function unlockOwnedLevels() {
  var ids = window.AppMintIAP ? window.AppMintIAP.owned() : [];
  document.querySelectorAll('[data-level-pack]').forEach(function (el) {
    el.classList.toggle('locked', ids.indexOf(el.getAttribute('data-level-pack')) === -1);
  });
}

window.addEventListener('appmint:owned-changed', unlockOwnedLevels);   // restore or refund changed the list
window.addEventListener('appmint:purchase', unlockOwnedLevels);

Her seviye paketi kendi ürünü olduğunda kaç seviyenin açık olduğunu sayın:

function unlockedLevelCount() {
  var ids = window.AppMintIAP ? window.AppMintIAP.owned() : [];
  var packs = ids.filter(function (id) { return /^level_pack_\d+$/.test(id); }).length;
  return 10 + packs * 10;                         // 10 free levels, 10 more per pack
}

function paintLevelCount() {
  document.getElementById('levels-label').textContent = unlockedLevelCount() + ' levels unlocked';
}
paintLevelCount();
window.addEventListener('appmint:owned-changed', paintLevelCount);

Ayarlar ekranınızda, her ürün için kendi adınızla bir "Satın alımlarım" listesi:

var LABELS = { pro_unlock: 'Pro features', remove_ads: 'No ads', level_pack_2: 'Levels 11-20' };

function renderPurchases() {
  var ids = window.AppMintIAP ? window.AppMintIAP.owned() : [];
  var list = document.getElementById('purchases');
  list.textContent = '';
  ids.forEach(function (id) {
    var li = document.createElement('li');
    li.textContent = LABELS[id] || id;
    list.appendChild(li);
  });
  document.getElementById('no-purchases').hidden = ids.length > 0;
}
renderPurchases();
window.addEventListener('appmint:owned-changed', renderPurchases);

Notlar: Sizin için WebToApk.getOwnedProducts() çıktısından ayrıştırılır. Etkin abonelikleri ve Reklamları Kaldır ürününü içerir. Çevrimdışı çalışır.

AppMintIAP.refresh yardımcı#

window.AppMintIAP.refresh()

Örnek

Son taramadan beri eklenen data-iap-buy düğmelerini yeniden arar ve bağlar (hiç bulamamışsa yardımcıyı başlatır).

Döndürür: AppMintIAP nesnesi. Gerekenler: 💰 Uygulama İçi Ürün Sat.

Yardımcı sayfadaki yeni data-iap-buy düğmelerini zaten izler, bu yüzden buna nadiren ihtiyaç duyarsınız. İzleyicinin kaçırabileceği şekilde düğme eklediğinizde, örneğin mevcut bir öğenin özniteliklerini değiştirerek çağırın:

function showShop() {
  var btn = document.getElementById('buy-coins');
  btn.setAttribute('data-iap-buy', 'coins_500');          // attribute added to an EXISTING node
  btn.setAttribute('data-iap-label', '500 coins — {price}');
  if (window.AppMintIAP) window.AppMintIAP.refresh();
}

Ürünleri kendi sunucunuzdan gelen mağaza kartları: düğmeler zaten sayfadadır, yalnızca öznitelikleri değişir; bu yüzden şununla bağlayın: refresh():

fetch('/shop.json').then(function (r) { return r.json(); }).then(function (shop) {
  shop.items.forEach(function (item) {           // [{slot, productId, name}]
    var btn = document.querySelector('.shop-card[data-slot="' + item.slot + '"] button');
    if (!btn) return;
    btn.setAttribute('data-iap-buy', item.productId);
    btn.setAttribute('data-iap-label', item.name + ' - {price}');
  });
  if (window.AppMintIAP) window.AppMintIAP.refresh();
});

Sayfada en baştan bulunan bir düğmede, belirli bir saatte indirime giren bir tatil paketi:

var saleStart = new Date('2026-12-01T00:00:00').getTime();

function openSale() {
  var btn = document.getElementById('sale-btn');
  btn.setAttribute('data-iap-buy', 'holiday_bundle');
  btn.setAttribute('data-iap-owned', 'Bundle unlocked');
  document.getElementById('sale-card').hidden = false;
  if (window.AppMintIAP) window.AppMintIAP.refresh();
}

var wait = saleStart - Date.now();
if (wait <= 0) openSale();
else if (wait < 86400000) setTimeout(openSale, wait);   // later than a day: the next launch opens it

Notlar: Her yeni düğme fiyatını Google'dan ve sahip olunma durumunu alır. Zaten bağlanmış bir düğme iki kez bağlanmaz. Öğesi sayfadan kaldırılan düğmeler bırakılır.

AppMintIAP.sell yardımcı#

window.AppMintIAP.sell(id, button, opts)

Örnek

Bir ürünü bir düğmeye tek satırda bağlar. Ürün başına bir kez çağırın.

Döndürür: AppMintIAP nesnesi (zincirleme için). Gerekenler: derleme sihirbazında 💰 Uygulama İçi Ürün Sat ve Play Console'da oluşturulmuş, etkin ürün. window.AppMintIAP, sayfanızın ilk betiği çalışmadan önce kurulur (yaklaşık 2021'den eski WebView'larda sayfa yüklenmeyi bitirdikten sonra - aşağıdaki whenIap deseni ikisini de kapsar).

function whenIap(fn) {
  if (window.AppMintIAP) { fn(window.AppMintIAP); return; }
  var tries = 0;
  var t = setInterval(function () {
    if (window.AppMintIAP) { clearInterval(t); fn(window.AppMintIAP); }
    else if (++tries > 50) clearInterval(t);          // not in the app, or selling is off
  }, 200);
}

whenIap(function (iap) {
  iap.sell('pro_unlock', '#buy-pro', {
    label: 'Unlock Pro — {price}',     // {price} = Google's live, localized price
    owned: '✓ Unlocked',               // the button text once owned
    show: '#thanks',                   // shown once owned
    hide: '.upsell'                    // hidden once owned
  });
  iap.sell('coins_500', '#buy-coins', { label: '500 coins — {price}' });
});

Birini almanın diğerlerinin yerine geçtiği birkaç plan ortak bir group:

whenIap(function (iap) {
  iap.sell('pass_monthly', '#monthly', { label: 'Monthly — {price}', group: 'pass' })
     .sell('pass_yearly', '#yearly', { label: 'Yearly — {price}', group: 'pass' });
});

Düğmesiz bir ürünü izleyin ve teklifi kendiniz çizin (null hiçbir şey bağlamaz; düğmeniz şunu çağırır: buy):

whenIap(function (iap) {
  iap.sell('remove_ads', null);                      // tracked, never drawn by the helper
  iap.onChange(function (s) {
    var p = s.products.remove_ads;                   // undefined until Google answers
    var btn = document.getElementById('remove-ads');
    if (!s.available || (p && p.owned)) { btn.hidden = true; return; }
    btn.hidden = false;
    btn.textContent = p && p.price ? 'Remove ads for ' + p.price : 'Remove ads';
  });
  document.getElementById('remove-ads').addEventListener('click', function () {
    iap.buy('remove_ads');
  });
});

Yalnızca mağaza iletişim kutunuz açılınca var olan bir düğmeyi bağlayın (geç gelen çağrı çalışan yardımcıya katılır):

document.getElementById('open-shop').addEventListener('click', function () {
  openShopDialog();                                  // your code: adds <button id="buy-levels">
  if (!window.AppMintIAP) return;                    // not in the app
  window.AppMintIAP.sell('level_pack_2', '#buy-levels', {
    label: 'Levels 11-20 — {price}',
    owned: '✓ Levels 11-20 unlocked',
    show: '#levels-2-link'                           // your menu entry, shown once owned
  });
});

Notlar: button bir öğe veya CSS seçicisidir; düğmesiz bir ürünü izlemek için null verin (kendi arayüzünüz AppMintIAP.state() değerini okur). Diğer opts: labelEl (düğmenin içinde metni değişen öğe). Yardımcı başlamadan önce veya sonra, bir React efektinden de çalışır. data-iap-buy öznitelikleriyle aynıdır (bkz. AppMintIAP.start). Ücretli içeriğin kilidini yalnızca sahiplikten (isOwned, onChange) açın, asla kendi tıklama işleyicinizde değil.

AppMintIAP.start yardımcı#

window.AppMintIAP.start(config) and the data-iap-* attributes

Örnek

Satın alma düğmelerinizi tek adımda Google Play'e bağlar: düğmelerde güncel fiyatlar, dokunmada satın alma sayfası, "sahip olundu" etiketleri, teşekkür blokları, açılışta geri yükleme ve her başarısızlık için görünür bir mesaj.

Döndürür: AppMintIAP nesnesi (zincirleme için). Gerekenler: derleme sihirbazında 💰 Uygulama İçi Ürün Sat ve Play Console'da oluşturulmuş, etkin ürün kimlikleri. Uygulama, sayfanızın ilk betiği çalışmadan önce window.AppMintIAP öğesini kurar (yaklaşık 2021'den eski WebView'larda yalnızca sayfa yüklenmeyi bitirdikten sonra).

Hiç kod yok: HTML'yi işaretleyin. Yardımcı bu öznitelikleri eklenir eklenmez kendiliğinden okur ve React veya Vue'nun sonradan oluşturduğu düğmeleri izler:

<div data-iap-container>                              <!-- shown only inside the app -->
  <button data-iap-buy="pass_monthly" data-iap-group="pass"
          data-iap-label="Monthly — {price}" data-iap-owned="Active">Monthly</button>
  <button data-iap-buy="pass_lifetime" data-iap-group="pass"
          data-iap-label="Lifetime — {price}" data-iap-owned="Owned">
    <img src="star.svg" alt=""> <span data-iap-text>Lifetime</span>   <!-- only this text changes -->
  </button>
  <button data-iap-buy="coins_500" data-iap-label="500 coins — {price}">500 coins</button>
</div>

<p data-iap-show="pass" hidden>You are a member. Thank you!</p>   <!-- shown when any "pass" plan is owned -->
<p data-iap-hide="pass">Members get every level and no ads.</p>    <!-- hidden once it is owned -->

Kodla: kendi mesaj işlevinizi veya kilit açma kancalarınızı istediğinizde start() işlevini kendiniz çağırın. HTML'de data-iap-buy düğmesi OLMAYAN sayfalarda kullanın (onlarla yardımcı zaten başlamıştır ve ikinci bir başlatma hiçbir şey yapmaz):

function whenIap(fn) {                               // AppMintIAP arrives after page load
  if (window.AppMintIAP) { fn(window.AppMintIAP); return; }
  var tries = 0;
  var t = setInterval(function () {
    if (window.AppMintIAP) { clearInterval(t); fn(window.AppMintIAP); }
    else if (++tries > 50) clearInterval(t);         // not in the app, or selling is off
  }, 200);
}

whenIap(function (iap) {
  iap.start({
    plans: [
      { id: 'pro_unlock', button: '#buy-pro', label: 'Unlock Pro — {price}', owned: '✓ Pro' },
      { id: 'coins_500', button: '#buy-coins', label: '500 coins — {price}' }
    ],
    show: '#pro-area',                 // shown once anything is owned
    hide: '.upsell',                   // hidden once anything is owned
    busyLabel: 'Opening Google Play…',
    onUnlock: function (id) { console.log('now owned:', id); },
    onLock: function (id) { console.log('no longer owned (refund or expiry):', id); },
    toast: function (msg) { showMessage(msg); }     // failure messages; default: window.showToast, else alert
  });
});

React'te: button olmayan planlarla başlayın (hiçbir şey çizilmez) ve React'in efekt için abonelikten çıkma işlevini döndüren onChange üzerinden çizmesine izin verin:

function ProButton() {
  const [iap, setIap] = React.useState({ available: false, products: {} });

  React.useEffect(() => {
    if (typeof window === 'undefined' || !window.AppMintIAP) return;   // server render / browser
    window.AppMintIAP.start({ plans: [{ id: 'pro_unlock' }] });       // safe to run twice
    return window.AppMintIAP.onChange(setIap);
  }, []);

  const pro = iap.products?.pro_unlock;                                // read ownership per product
  if (!iap.available) return null;                                     // nothing to buy outside the app
  return pro?.owned
    ? <p>Thanks! Pro is unlocked.</p>
    : <button onClick={() => window.AppMintIAP.buy('pro_unlock')}>Unlock Pro {pro?.price}</button>;
}

Notlar: Öznitelikler: data-iap-buy="id", data-iap-label ({price} Play'in güncel fiyatı olur), data-iap-owned (sahip olunduktan sonraki etiket; varsayılan "✓ Satın alındı"), data-iap-group (birbirinin yerine geçen planlar; biri alındıktan sonra diğerleri hideOtherPlans: false değilse gizlenir), data-iap-show / data-iap-hide (değer = ürün kimliği veya grup; boş = herhangi bir şey sahipliği), data-iap-text (metni değişen düğme bölümü), data-iap-container. Sahip olunan düğme data-iap-state="owned" ve iap-unlocked sınıfını alır. Google'ın sayfasında iptal olay göndermez; düğme sayfa yeniden görünür olunca (veya 30 sn sonra) serbest bırakılır. Yardımcı data-iap-buy öznitelikleriyle zaten kendiliğinden başladıktan sonra start(options) çağırmak seçenekleri (planlar, göster/gizle, kapsayıcı, onUnlock/onLock, toast, busyLabel, hideOtherPlans) çalışan kümeye BİRLEŞTİRİR; iki kez çağırmak zararsızdır. Kendi appmint-iap.js dosyasını getiren bir sayfa onu korur - yerleşik kopya geri çekilir.

AppMintIAP.started yardımcı#

window.AppMintIAP.started

Örnek

Bir özellik (işlev değil): yardımcı çalışmaya başladığında yani start(), sell() veya data-iap-buy düğmelerinden en az bir ürünü bağladığında true.

Döndürür: bir mantıksal (boolean) özellik. Gerekenler: 💰 Uygulama İçi Ürün Sat.

start() (kendi seçeneklerinizle) ve sell() (çalışan bir yardımcıda da çalışır) arasında karar vermek için kullanın:

function setUpShop(iap) {
  if (!iap.started) {
    iap.start({
      plans: [{ id: 'pro_unlock', button: '#buy-pro', label: 'Unlock Pro — {price}' }],
      toast: function (msg) { showMessage(msg); }
    });
  } else {
    // Already running (the page had data-iap-buy buttons): start() would do nothing now.
    iap.sell('pro_unlock', '#buy-pro', { label: 'Unlock Pro — {price}' });
  }
}
if (window.AppMintIAP) setUpShop(window.AppMintIAP);

Geliştirme sırasında sayfada hiçbir şey bağlanmadığında kendinizi uyarın (data-iap-buy içinde bir yazım hatası veya düğmeler hiç oluşturulmamış):

window.addEventListener('load', function () {
  setTimeout(function () {
    var iap = window.AppMintIAP;
    if (iap && !iap.started) {
      console.warn('AppMintIAP is still waiting: no data-iap-buy button, sell() or start() on this page');
    }
  }, 3000);
});

Bir mağaza ekranını "hâlâ yükleniyor" ve "satın almalar kullanılamıyor" durumlarından ayırt edin:

function storeStatus() {
  var iap = window.AppMintIAP;
  if (!iap) return 'web';                            // opened in a normal browser
  if (!iap.started) return 'waiting';                // no product wired yet
  return iap.state().available ? 'ready' : 'unavailable';
}
document.getElementById('store').setAttribute('data-status', storeStatus());

Notlar: İkinci bir start() onu asla iki kez başlatmaz (React StrictMode efektleri iki kez çalıştırır); seçenekleri çalışan kümeye eklenir. Henüz hiçbir ürün bağlanmamışken false kalır; yardımcı bu sırada sayfada data-iap-buy düğmelerini izlemeye devam eder.

AppMintIAP.state yardımcı#

window.AppMintIAP.state()

Örnek

Abone olmadan, geçerli satın alma durumunu bir kez döndürür.

Döndürür: eşzamanlı olarak {available, adFree, owned, ownedIds, products}. ownedIds ürün kimliklerinin dizisidir; products, yardımcının Google'a sorduğu ürünler için {id: {price, currency, type: 'inapp'|'subs', period, owned}} olur. Gerekenler: 💰 Uygulama İçi Ürün Sat.

function describePass() {
  if (!window.AppMintIAP) return 'Available in the Android app';
  var s = window.AppMintIAP.state();
  if (s.ownedIds.indexOf('season_pass') !== -1) return 'Your season pass is active';
  var p = s.products.season_pass;                       // undefined until Google answers
  if (!p || !p.price) return 'Season pass';
  var every = { P1W: 'week', P1M: 'month', P1Y: 'year' }[p.period] || 'period';
  return 'Season pass — ' + p.price + ' / ' + every;
}
document.getElementById('pass-label').textContent = describePass();

Oyuncu kilitli bir seviyeye dokunduğu anda sahipliği denetleyin, değilse onu mağazanıza yönlendirin:

function openLevel(n) {
  var s = window.AppMintIAP ? window.AppMintIAP.state() : null;
  var needsPack = n > 10;
  if (needsPack && !(s && s.ownedIds.indexOf('level_pack_2') !== -1)) {
    showShop('level_pack_2');                        // your own screen
    return;
  }
  startLevel(n);
}

Derlemenin reklamsız hakkı etkinleştiğinde kendi "reklamsız ol" istemlerinizi gizleyin ve Google'ın şimdiye kadar gönderdiği fiyatları listeleyin:

if (window.AppMintIAP) {
  var s = window.AppMintIAP.state();
  document.querySelectorAll('.go-ad-free').forEach(function (el) { el.hidden = s.adFree; });

  var list = document.getElementById('price-list');
  Object.keys(s.products).forEach(function (id) {
    var p = s.products[id];
    var li = document.createElement('li');
    li.textContent = id + ': ' + (p.owned ? 'owned' : (p.price || '…')) +
                     (p.type === 'subs' ? ' (subscription)' : '');
    list.appendChild(li);
  });
}

Notlar: Bu bir anlık görüntüdür. Değişiklikleri izlemek için her şey değiştiğinde aynı nesneyi veren AppMintIAP.onChange(fn) kullanın. owned, herhangi bir şeye sahip olunduğunda true olur; bir ürünü ownedIds ile veya şununla denetleyin: AppMintIAP.isOwned(id).

getOwnedProducts köprü#

window.WebToApk.getOwnedProducts(): String

Sahip olunan her ürün kimliğinin JSON dizisi - başlangıçta tüm ekranları kapılamak için.

Örnek

Kullanıcının sahip olduğu her Play ürününü listeler; başlangıçta tüm ekranların kilidini açmak için.

Döndürür: eşzamanlı olarak bir JSON dizesi (ürün kimlikleri dizisi), örneğin '["pro_unlock","level_pack_2"]'. JSON.parse ile ayrıştırın. Hiçbir şeye sahip olunmadığında '[]'. Gerekenler: derleme sihirbazında 💰 Uygulama İçi Ürün Sat.

function ownedIds() {
  var b = window.WebToApk;
  if (!b || typeof b.getOwnedProducts !== 'function') return [];
  try { return JSON.parse(b.getOwnedProducts()); } catch (e) { return []; }
}

function unlockAll(ids) {
  ids.forEach(function (id) {
    var el = document.querySelector('[data-product="' + id + '"]');
    if (el) el.classList.add('unlocked');
  });
}
unlockAll(ownedIds());

// The owned list changed (launch restore, a new purchase, a refund): the full new list.
window.addEventListener('appmint:owned-changed', function (e) {
  document.querySelectorAll('.unlocked').forEach(function (el) { el.classList.remove('unlocked'); });
  unlockAll(e.detail.products);              // e.detail = {products: ['pro_unlock', ...]}
});

Aynı değişiklik, tanımlarsanız window.onAppMintOwnedChanged(detail) işlevini de çağırır.

Ayarlar ekranınızda, her ürün için kendi adınızla bir "Satın alımlarım" listesi:

var NAMES = { pro_unlock: 'Pro features', level_pack_2: 'Levels 11-20', remove_ads: 'No ads' };

function renderMyPurchases() {
  var b = window.WebToApk, ids = [];
  if (b && typeof b.getOwnedProducts === 'function') {
    try { ids = JSON.parse(b.getOwnedProducts()); } catch (e) {}
  }
  var list = document.getElementById('my-purchases');
  list.textContent = '';
  ids.forEach(function (id) {
    var li = document.createElement('li');
    li.textContent = NAMES[id] || id;
    list.appendChild(li);
  });
  document.getElementById('nothing-bought').hidden = ids.length > 0;
}
renderMyPurchases();
window.addEventListener('appmint:owned-changed', renderMyPurchases);

Her katman kendi ürünü olduğunda kullanıcının sahip olduğu en iyi katmanı bulun:

var TIERS = ['premium_plus', 'premium', 'basic'];     // best first

function currentTier() {
  var b = window.WebToApk, owned = [];
  if (b && typeof b.getOwnedProducts === 'function') {
    try { owned = JSON.parse(b.getOwnedProducts()); } catch (e) {}
  }
  for (var i = 0; i < TIERS.length; i++) if (owned.indexOf(TIERS[i]) !== -1) return TIERS[i];
  return 'free';
}

applyTier(currentTier());
window.addEventListener('appmint:purchase', function () { applyTier(currentTier()); });
window.addEventListener('appmint:owned-changed', function () { applyTier(currentTier()); });

Notlar: Tek seferlik ürünleri ve etkin abonelikleri, ayrıca kullanıcı satın aldıysa Reklamları Kaldır ürününü içerir. Çevrimdışı çalışır; liste her açılışta geri yükleme ve satın alımda telefona kaydedilir. appmint:owned-changed yalnızca liste gerçekten değiştiğinde tetiklenir, bu yüzden normal bir açılışta hiç tetiklenmeyebilir.

getProducts köprü#

window.WebToApk.getProducts(requestId: String, productIdsJson: String)

Play'den verilen ürün kimlikleri (dize dizisi içeren JSON) için güncel ayrıntıları ister. Sonuç 'appmint:products' {requestId, products:[{productId, type, title, description, price, currency, period?, owned}]} olarak gelir. Play'in tanımadığı kimlikler sonuçta yoktur - sayfa bu öğeleri gizler.

Örnek

Google Play'den ürünlerinizin güncel ayrıntılarını ister: başlık, açıklama ve yerelleştirilmiş fiyat.

Döndürür: şimdilik hiçbir şey. Yanıt appmint:products olayı olarak gelir: {requestId, products: [{productId, type, title, description, price, currency, period?, owned}]}. type, 'inapp' veya 'subs' olur; period, 'P1M' gibi bir ISO-8601 süresidir (yalnızca abonelikler). Gerekenler: derleme sihirbazında 💰 Uygulama İçi Ürün Sat. İkinci argüman bir JSON dizesidir: şunu kullanın: JSON.stringify.

function loadPrices(ids) {
  return new Promise(function (resolve) {
    var b = window.WebToApk;
    if (!b || typeof b.getProducts !== 'function') { resolve([]); return; }   // browser
    var requestId = 'prices-' + Date.now();
    window.addEventListener('appmint:products', function handler(e) {
      if (String(e.detail.requestId) !== requestId) return;   // another call's answer
      window.removeEventListener('appmint:products', handler);
      resolve(e.detail.products || []);
    });
    b.getProducts(requestId, JSON.stringify(ids));
  });
}

loadPrices(['pro_unlock', 'coins_500', 'pro_monthly']).then(function (products) {
  products.forEach(function (p) {
    var btn = document.querySelector('[data-product="' + p.productId + '"]');
    if (!btn) return;
    btn.hidden = false;
    btn.textContent = p.owned ? 'Owned' : p.title + ' · ' + p.price + (p.period === 'P1M' ? ' / month' : '');
  });
});

Yanıt, tanımlarsanız window.onAppMintProducts(detail) işlevine de iletilir.

Her kartı Play'in fiyatı ve faturalama dönemiyle etiketlenmiş bir abonelik planı seçici:

var PERIODS = { P1W: 'week', P1M: 'month', P3M: '3 months', P6M: '6 months', P1Y: 'year' };

window.addEventListener('appmint:products', function (e) {
  if (e.detail.requestId !== 'plans') return;
  e.detail.products.forEach(function (p) {
    var card = document.querySelector('.plan[data-product="' + p.productId + '"]');
    if (!card || p.type !== 'subs') return;
    card.hidden = false;
    card.querySelector('.price').textContent = p.price + ' / ' + (PERIODS[p.period] || p.period);
    card.classList.toggle('current', p.owned);
  });
});

var b = window.WebToApk;
if (b && typeof b.getProducts === 'function') {
  b.getProducts('plans', JSON.stringify(['pro_weekly', 'pro_monthly', 'pro_yearly']));
}

Test ederken Google Play'in döndürmediği kimlikleri sayfa kancası üzerinden günlüğe yazın:

var WANTED = ['pro_unlock', 'coins_500', 'pro_monthly'];

window.onAppMintProducts = function (detail) {       // same payload as appmint:products
  if (detail.requestId !== 'store-check') return;
  var found = detail.products.map(function (p) { return p.productId; });
  var missing = WANTED.filter(function (id) { return found.indexOf(id) === -1; });
  if (missing.length) console.warn('Not returned by Google Play: ' + missing.join(', '));
  document.getElementById('store').hidden = found.length === 0;
};

if (window.WebToApk && typeof window.WebToApk.getProducts === 'function') {
  window.WebToApk.getProducts('store-check', JSON.stringify(WANTED));
}

Notlar: price değerini Play'in verdiği haliyle gösterin ve asla saklamayın. Play'in tanımadığı bir kimlik (oluşturulmamış, etkin değil veya uygulama Play'den yüklenmemiş) products içinde yoktur; o öğeyi gizli tutun. Sayıya benzeyen bir requestId sayı olarak döner, bu yüzden metin kimliği kullanın veya şununla karşılaştırın: String().

isOwned köprü#

window.WebToApk.isOwned(productId: String): Boolean

Anında cihaz üzerinde sahiplik denetimi (tek seferlik ürün sahipliği VEYA abonelik etkin).

Örnek

Kullanıcının Play ürünlerinizden birine (tek seferlik ürün veya hâlâ etkin bir abonelik) sahip olup olmadığını anında ve çevrimdışı denetler.

Döndürür: eşzamanlı olarak true / false. Gerekenler: derleme sihirbazında 💰 Uygulama İçi Ürün Sat. Bu anahtar açıkken Reklamları Kaldır (IAP) ürünü için de yanıt verir; yalnızca reklam kaldırma satan bir uygulamada (anahtar kapalı) her ürün için false yanıtı verir, bu yüzden orada reklam kaldırmayı isPremium() ile denetleyin.

function hasPro() {
  var b = window.WebToApk;
  return !!(b && typeof b.isOwned === 'function' && b.isOwned('pro_unlock'));
}

function paint() {
  document.getElementById('pro-features').hidden = !hasPro();
  document.getElementById('buy-pro').hidden = hasPro();
}
paint();

// Ownership changes: a purchase landed, or the launch restore / a refund changed the list.
window.addEventListener('appmint:purchase', paint);
window.addEventListener('appmint:owned-changed', paint);

Kullanım anında denetleyin: yalnızca Pro bir dışa aktarma, sahipse çalışır, değilse satın alma sayfasını açar ve satın alma gerçekleşince tamamlanır:

var exportWaiting = false;

document.getElementById('export-hd').addEventListener('click', function () {
  var b = window.WebToApk;
  if (!b || typeof b.isOwned !== 'function') { exportStandard(); return; }   // browser
  if (b.isOwned('pro_unlock')) { exportHd(); return; }
  exportWaiting = true;
  b.purchase('pro_unlock');
});

window.addEventListener('appmint:purchase', function (e) {
  if (e.detail.productId === 'pro_unlock' && exportWaiting) {
    exportWaiting = false;
    exportHd();                                  // finish what the user asked for
  }
});

Birkaç plandan herhangi biri üzerinden Pro: aylık, yıllık ve ömür boyu ürünlerin hepsi sayılır:

var PRO_PLANS = ['pro_monthly', 'pro_yearly', 'pro_lifetime'];

function isPro() {
  var b = window.WebToApk;
  if (!b || typeof b.isOwned !== 'function') return false;
  return PRO_PLANS.some(function (id) { return b.isOwned(id); });
}

function paintPlan() { document.body.classList.toggle('pro', isPro()); }
paintPlan();
window.addEventListener('appmint:purchase', paintPlan);
window.addEventListener('appmint:owned-changed', paintPlan);   // an expired subscription or a refund

Notlar: Sahip olunanlar listesi her açılışta kullanıcının Google hesabından geri yüklenir ve telefona kaydedilir; bu yüzden sunucu olmadan çevrimdışı ve yeniden yüklemeden sonra da çalışır. Tarayıcıda her zaman false. Play artık etkin olarak bildirmediğinde bir abonelik sayılmaz.

isPremium köprü#

window.WebToApk.isPremium(): Boolean

Kullanıcı "Reklamları Kaldır"a sahipse doğrudur (Play Billing hakkı, her açılışta otomatik geri yüklenir). Şuradaki 'ad_free' nedeniyle eşleşir: 'appmint:ad-unavailable'.

Örnek

Kullanıcının "Reklamları Kaldır" ürününüze sahip olup olmadığını söyler; böylece sayfa "Reklamları kaldır" düğmesini ve reklamla ilgili arayüzü gizleyebilir.

Döndürür: eşzamanlı olarak true / false. Gerekenler: Reklamlar açık ve Play Store Ürün Kimliği ile Reklamları Kaldır (IAP). Bu olmadan her zaman false.

var removeAdsBtn = document.getElementById('remove-ads');

function paintPremium(premium) {
  removeAdsBtn.hidden = premium;
  document.body.classList.toggle('premium', premium);
}

var b = window.WebToApk;
paintPremium(!!(b && typeof b.isPremium === 'function' && b.isPremium()));

// Changes later: purchase finished, restore at launch, refund or expired subscription.
window.addEventListener('appmint:premium', function (e) {
  paintPremium(e.detail.premium);            // e.detail = {premium: true | false}
});

Aynı değişiklik, tanımlarsanız bir sayfa kancasını da çağırır:

window.onAppMintPremiumChanged = function (info) { paintPremium(info.premium); };   // {premium}

Kendi "Reklamları kaldır" teklifinizi oyun bitti ekranında yalnızca hâlâ reklam gören oyunculara gösterin:

function showGameOver(score) {
  document.getElementById('final-score').textContent = score;
  var b = window.WebToApk;
  var premium = !!(b && typeof b.isPremium === 'function' && b.isPremium());
  // Ad-free already, or a plain browser (no ads there): no offer.
  document.getElementById('remove-ads-offer').hidden = premium || !b;
  document.getElementById('game-over').hidden = false;
}

Notlar: Sahiplik her açılışta kullanıcının Google hesabından geri yüklenir; bu yüzden başlangıçta bir kez okuyun, sonra appmint:premium olayını izleyin. Ürün Kimliği alanı virgülle ayrılmış birkaç kimlik kabul eder (örneğin aylık bir plan ve ömür boyu bir plan); herhangi birine sahip olmak sayılır. Premium iken reklamlar kaldırılır ve reklam çağrıları şunu yanıtlar: ad_free.

purchase köprü#

window.WebToApk.purchase(productId: String)

[productId] için Google'ın satın alma sayfasını açar (tek seferlik ürün veya abonelik - otomatik çözülür). YALNIZCA 'appmint:purchase' {productId, owned:true} olayında verin; başarısızlıklar 'appmint:purchase-failed' {reason} tetikler. Zaten sahip olunan, satın alma olayını yeniden tetikler; böylece sayfa kilidi tekrarlanabilir biçimde açabilir.

Örnek

Ürünlerinizden biri için Google Play'in satın alma sayfasını açar (tek seferlik ürün veya abonelik; uygulama hangisi olduğunu bulur).

Döndürür: şimdilik hiçbir şey. Başarı appmint:purchase {productId, owned: true} tetikler. Başarısızlık appmint:purchase-failed {productId, reason} tetikler. Gerekenler: derleme sihirbazında 💰 Uygulama İçi Ürün Sat ve Play Console'da oluşturulmuş, etkin ürün.

Kilidi YALNIZCA appmint:purchase işleyicisinde açın, asla dokunuşta değil:

var b = window.WebToApk;
var buyBtn = document.getElementById('buy-pro');

if (!b || typeof b.purchase !== 'function') {
  buyBtn.hidden = true;                              // browser: nothing to buy here
} else {
  buyBtn.addEventListener('click', function () {
    buyBtn.disabled = true;
    b.purchase('pro_unlock');
  });
}

window.addEventListener('appmint:purchase', function (e) {
  if (e.detail.productId === 'pro_unlock') unlockPro();   // safe to run twice
  buyBtn.disabled = false;
});

window.addEventListener('appmint:purchase-failed', function (e) {
  buyBtn.disabled = false;
  if (e.detail.reason === 'pending') showMessage('Payment pending. It unlocks by itself once Google confirms.');
  else showMessage('Purchase is not available right now (' + e.detail.reason + ').');
});

// A cancel on Google's sheet sends no event: release the button when the page is visible again.
document.addEventListener('visibilitychange', function () { if (!document.hidden) buyBtn.disabled = false; });

Aynı sonuçlar, tanımlarsanız window.onAppMintPurchase(detail) ve window.onAppMintPurchaseFailed(detail) işlevlerini de çağırır.

Abonelik seçicide kullanıcının seçtiği planı satın alın ve kimliğini Play'in tanımadığı planı gizleyin:

var picked = 'pro_yearly';
document.querySelectorAll('.plan').forEach(function (card) {
  card.addEventListener('click', function () { picked = card.getAttribute('data-product'); });
});

document.getElementById('subscribe').addEventListener('click', function () {
  var b = window.WebToApk;
  if (b && typeof b.purchase === 'function') b.purchase(picked);
});

window.addEventListener('appmint:purchase', function (e) {
  if (/^pro_/.test(e.detail.productId)) unlockPro();        // any plan unlocks Pro
});
window.addEventListener('appmint:purchase-failed', function (e) {
  if (e.detail.reason !== 'not_found') return;
  var card = document.querySelector('.plan[data-product="' + e.detail.productId + '"]');
  if (card) card.hidden = true;
});

Oyuncunun dokunduğu kilitli seviyeden bir seviye paketinin kilidini açın, sonra o seviyeyi açın:

var wantedLevel = null;

function openLevel(n) {
  var b = window.WebToApk;
  var owned = !!(b && typeof b.isOwned === 'function' && b.isOwned('level_pack_2'));
  if (n <= 10 || owned) { startLevel(n); return; }
  if (!b || typeof b.purchase !== 'function') { showMessage('Levels 11-20 are in the Android app.'); return; }
  wantedLevel = n;
  b.purchase('level_pack_2');
}

window.onAppMintPurchase = function (detail) {       // {productId, owned: true}
  if (detail.productId !== 'level_pack_2' || !wantedLevel) return;
  var n = wantedLevel;
  wantedLevel = null;
  startLevel(n);
};

Notlar: Nedenler: disabled (Uygulama İçi Ürün Sat kapalı), not_found (kimlik Play Console'da yok), billing_error, pending (kullanıcı yavaş bir yöntemle ödedi; kilit açma sonradan açılışta geri yükleme ve appmint:owned-changed ile gelir). Ürüne zaten sahipse appmint:purchase hemen yeniden tetiklenir, bu yüzden kilit açma işleminiz tekrarlanmaya karşı güvenli olmalı. Uygulama hatalar için kendi kısa mesajını da gösterir.

revenuecat köprü#

window.WebToApk.revenuecat(action: String, argsJson: String, callbackId: String)

[argsJson] ile bir RevenueCat [action] çağrısı; [callbackId] değerini {ok, …} veya {ok:false, error} ile tamamlar.

Örnek

AppMint.revenuecat arkasındaki ham yöntem - sarmalayıcıyı kullanın. Tek giriş noktası: action şunlardan biridir: configure, getOfferings, purchase, restore, getCustomerInfo, logIn, logOut veya getAppUserId.

Döndürür: doğrudan hiçbir şey; sarmalayıcının verdiği aynı yanıtla window.__appmintReplies[callbackId] tamamlanır. Gerekenler: Entegrasyon adımı → genel goog_ anahtarınızla RevenueCat.

window.__appmintReplies = window.__appmintReplies || {};
window.__appmintReplies['rc1'] = (r) => console.log(r.ok ? r.customerInfo : r.error);
WebToApk.revenuecat('getCustomerInfo', '{}', 'rc1');

Bir kullanıcı kimliğiyle yapılandırın ve teklifleri okuyun:

window.__appmintReplies = window.__appmintReplies || {};
window.__appmintReplies['rc-cfg'] = (r) => {
  if (!r.ok) return console.warn('RevenueCat:', r.error);
  window.__appmintReplies['rc-off'] = (o) => console.log(o.ok ? o.current : o.error);
  WebToApk.revenuecat('getOfferings', '{}', 'rc-off');
};
if (window.WebToApk && WebToApk.revenuecat) {
  WebToApk.revenuecat('configure', JSON.stringify({ appUserId: 'u_42' }), 'rc-cfg');
}

Satın alın ve reddedilmeleri ele alın:

window.__appmintReplies = window.__appmintReplies || {};
window.__appmintReplies['rc-buy'] = (r) => {
  if (r.ok) return unlock(r.customerInfo.entitlements.active);
  if (r.error === 'foreign_origin') return showMessage('Purchases only work inside the app.');
  if (r.cancelled) return;
  showMessage('Purchase failed: ' + (r.message || r.error));
};
WebToApk.revenuecat('purchase', JSON.stringify({ package: '$rc_monthly' }), 'rc-buy');

Notlar: her eylemin argümanları ve yanıtları için AppMint.revenuecat bölümüne bakın.

startRemoveAdsPurchase köprü#

window.WebToApk.startRemoveAdsPurchase()

Sayfadan Google Play "Reklamları Kaldır" satın alma akışını başlatın (kullanıcı tarafından başlatılan, örn. uygulamanın kendi "Reklamları kaldır" düğmesi). Bu olmadan TEK giriş noktası yerel yan menü öğesiydi - yan menüsüz derlenen uygulamalarda ulaşılamazdı. Sonuç sayfaya 'appmint:premium' {premium:true} ile ulaşır; zaten sahip olunmuşsa hemen yeniden bildirir; akış hataları yerel bir toast olarak görünür.

Örnek

Sayfanızın kendi "Reklamları kaldır" düğmesinden "Reklamları Kaldır" ürününüz için Google Play'in satın alma sayfasını açar.

Döndürür: hiçbir şey. Sonuç appmint:premium {premium: true} olarak gelir (ve window.onAppMintPremiumChanged). Gerekenler: Reklamlar açık ve Play Store Ürün Kimliği ile Reklamları Kaldır (IAP) (listedeki ilk kimlik satılandır). Yan Menü açıkken uygulamanın orada kendi Reklamları Kaldır öğesi de vardır (kullanıcı sahip olunca gizlenir); yan menü kapalıyken sayfanızdaki bu düğme satın almanın tek yoludur.

var b = window.WebToApk;
var btn = document.getElementById('remove-ads');

if (!b || typeof b.startRemoveAdsPurchase !== 'function' || b.isPremium()) {
  btn.hidden = true;                          // browser, or already ad-free
} else {
  btn.addEventListener('click', function () {
    btn.disabled = true;
    b.startRemoveAdsPurchase();
  });
}

window.addEventListener('appmint:premium', function (e) {
  if (e.detail.premium) { btn.hidden = true; showMessage('Thank you! Ads are gone.'); }
});

// A cancel on Google's sheet sends no event: release the button when the page is visible again.
document.addEventListener('visibilitychange', function () {
  if (!document.hidden) btn.disabled = false;
});

Reklamların kaldırılıp kaldırılmadığını gösteren ve satın alan düğmeli bir ayarlar satırı:

var row = document.getElementById('ads-setting');
var btn = row.querySelector('button');
var b = window.WebToApk;

function paintAdsRow(premium) {
  row.querySelector('.status').textContent = premium ? 'Ads removed. Thank you!' : 'Ads are on';
  btn.hidden = premium;
}

if (b && typeof b.startRemoveAdsPurchase === 'function') {
  paintAdsRow(b.isPremium());
  btn.addEventListener('click', function () { b.startRemoveAdsPurchase(); });
  window.onAppMintPremiumChanged = function (info) { paintAdsRow(info.premium); };   // {premium}
} else {
  row.hidden = true;                                  // browser: nothing to sell
}

Her üçüncü biten turdan sonra, oyuncunun dokunarak yanıtladığı bir iletişim kutusunda sunun:

var rounds = 0;
var offer = document.getElementById('remove-ads-offer');   // a <dialog>

function onRoundFinished() {
  var b = window.WebToApk;
  if (!b || typeof b.startRemoveAdsPurchase !== 'function' || b.isPremium()) return;
  rounds += 1;
  if (rounds % 3 === 0) offer.showModal();
}

offer.querySelector('.buy').addEventListener('click', function () {
  offer.close();
  window.WebToApk.startRemoveAdsPurchase();   // opened from the player's own tap
});
offer.querySelector('.later').addEventListener('click', function () { offer.close(); });

Notlar: Kullanıcı ona zaten sahipse appmint:premium {premium:true} hemen tetiklenir. Hatalar (Play'e ulaşılamıyor, ürün Play Console'da bulunamadı) uygulama tarafından kısa bir yerel mesaj olarak gösterilir; sayfa bunlar için olay almaz. Bir Play test kanalından yüklenen uygulamayla test edin.

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.