Para kazanma
Uygulama İçi Ürün Satışı (IAP)
Kendi web sitenizden veya ZIP uygulamanizdan Google Play satin almalari
1📥 Rehberi + calisan demo uygulamayi alin#
Bu rehberdeki her sey ayni zamanda cevrimdisi okuyabileceginiz bir PDF ve bugun derleyip calistirabileceginiz eksiksiz bir demo uygulamadir.
Asagiya dokunun; ikisi de telefonunuza Downloads/AppMint/Guides altina kaydedilir:
- AppMint-InApp-Purchases-Guide.pdf - Play Console adimlari ve React / Next.js ornekleri dahil tam rehber
- iap-demo-index.html - uc sey satan kucuk bir oyun: reklamlari kaldirma (tek seferlik), bir seviye paketi (tek seferlik) ve aylik bir sezon gecisi
Demoyu denemek icin: iap-demo-index.html dosyasini tek basina zip'leyin, adimlayicida yukleyin ve rehberi izleyin. Kendi uygulamaniza dokunmadan once gercek bir satin almanin bastan sona calistigini gormenin en hizli yoludur.
Uygulamada, bu adımın düğmesi bunları telefonunuza kaydeder. Burada doğrudan indirilebilir.
AppMint-InApp-Purchases-Guide.pdf iap-demo-index.html2Bu nedir? (sade anlatimla)#
Uygulamaniz bir seyler satabilir: Pro yukseltmesi, ek seviyeler, abonelik. Google parayi toplar ve her ay size oder.
BU REHBER BOYUNCA BIRLIKTE BIR ORNEK OLUSTURACAGIZ:
A game that sells two things
• 'Levels 11-20' ₹99 paid once, kept forever
• 'Remove ads' ₹49 every month
Sonunda sayfaniza dort satir ekleyeceksiniz. Is bundan ibaret; yazilacak satin alma kodu yok ve bunu daha once hic yapmadiysaniz bile sorun yasamazsiniz.
Kopyala-yapistirmanin otesinde programlama bilmeniz GEREKMEZ; sunucu, veritabani veya odeme sirketi hesabi da GEREKMEZ. Zor kisimlari Google Play yapar:
- Odeme ekranini gosterir (kart / UPI / operator faturalandirmasi)
- Fiyatinizi her ulkenin para birimine cevirir
- Kimin ne satin aldigini hatirlar, yeniden kurulumdan sonra bile
- Parayi banka hesabiniza gonderir
AppMint %0 alir; satis dogrudan SIZIN Google Play hesabiniza gider.
Web Sitesi → APK, ZIP → APK ve yapay zeka ile olusturulan uygulamalar icin ayni sekilde calisir.
3Baslamadan once neye ihtiyaciniz var#
Yalnizca dort sey:
- Web siteniz ya da HTML/ZIP uygulamaniz (APK'ya donusturdugunuz sey).
- Satacak bir sey: alicilarin ne alacagina karar verin (ornegin 'Pro surum': limitsiz, ek icerik).
- Bir Google Play gelistirici hesabi: play.google.com/console adresinde tek seferlik 25 $ ucret. Play Store'da HERHANGI bir uygulamayi yayinlamak icin buna ihtiyaciniz var, yalnizca ucretlileri icin degil.
- Asagidaki adimlara yayilmis, toplam yaklasik 1 saat.
Bastan bilmeniz gereken onemli nokta: uygulama ici satin almalar yalnizca uygulamaniz Play Store'DAN kurulduğunda calisir. Bu herkes icin gecerli bir Google kuralidir, AppMint sinirlamasi degil.
41. Adim - Urun KIMLIGINIZI bulun#
Urun kimligi, sattiginiz sey icin uydurdugunuz kisa bir addir. Bu adi daha sonra iki yerde (sayfaniz + Play Console) yazacaksiniz, bu yuzden basit tutun ve bir yere not edin.
Kurallar: yalnizca kucuk harfler, rakamlar, alt cizgiler. Bir harf veya rakamla baslamali.
Bunlardan birini kopyalayin:
- pro_unlock - 'Pro surum' yukseltmesi (iyi bir varsayilan; asagidaki kod bunu zaten kullanir)
- remove_ads - reklamsiz surume yukseltme
- level_pack_2 - ek icerik paketi
- coins_100 - oyun para birimi paketi
- premium_monthly - aylik abonelik
Fiyat KIMLIGIN parcasi DEGILDIR; fiyati 5. Adim'da belirlersiniz ve kodunuza dokunmadan istediginiz zaman degistirebilirsiniz.
52. Adim - Dugmenizi isaretleyin (yazilacak kod yok)#
Hicbir satin alma kodu YAZMAZSINIZ. Uygulamaniz satin alma motorunu zaten tasir; siz yalnizca kendi HTML'inizi etiketleyerek neyin satilacagini bildirirsiniz.
Asagidaki blogu sayfaniza kopyalayin ve yalnizca sunlari degistirin:
(1) pro_unlock → urun kimliginiz, data-iap-buy icinde
(2) dugmedeki ve ucretli bolumun icindeki sozcukler
Hepsi bu. Dugme o zaman alicinin kendi para biriminde Google'in gercek fiyatini gosterir, dokununca Google'in odeme ekranini acar, odemeden sonra ucretli bolumu gosterir (ve zaten satin almis kisiler icin her acilista yeniden), abonelik sona erirse ya da odeme iade edilirse geri alir ve bir sey ters giderse net bir mesaj gosterir.
DORT ETIKET:
- data-iap-buy="id" - bunu satin alma dugmesi yapar (gerekli)
- data-iap-label="…{price}…" - dugme metni; {price}, Google'in gercek fiyati olur
- data-iap-owned="…" - satin alindiktan sonraki dugme metni
- data-iap-show - yalnizca satin almadan sonra gorunen bir bolum ('hidden' olarak baslatin)
Ayrica mevcut: data-iap-hide (satin alindiktan sonra kaybolur), data-iap-container (yalnizca uygulamanin icinde gorunen bir sarmalayici), data-iap-text (dugmenizde simgeler de varsa, metni tutan <span> ogesine koyun).
BIRKAC FARKLI SEY MI SATIYORSUNUZ? Her biri icin bir dugme ekleyin. Birbirinden bagimsizdirlar: bir uygulama reklamlari kaldirabilir, seviye paketleri satabilir VE abonelik calistirabilir; birini almak digerlerini asla etkilemez. Bir bolumu data-iap-show="level_pack_2" ile tek bir urune baglayin.
Reklamlari yalnizca 'Reklamlari Kaldir (IAP) → Urun Kimligi' alanina yazdiginiz kimlikler kapatir. Sattiginiz diger her sey sayfanizin onunla yaptigi seyin kilidini acar ve reklamlar calismaya devam eder.
AYNI ISI ICIN IKI PLAN MI VAR (aylik ve omur boyu)? Iki dugmeye de ayni data-iap-group="adfree" verin; boylece birini almak digerini gizler ve kimse iki kez odemez. Grup olmadan hicbir sey gizlenmez; ilgisiz urunlerin ihtiyaci budur.
<!-- (1) CHANGE pro_unlock to your product ID -->
<button data-iap-buy="pro_unlock"
data-iap-label="Unlock Pro — {price}"
data-iap-owned="✓ Unlocked">
Unlock Pro
</button>
<!-- (2) CHANGE the words. Appears only after buying. -->
<div data-iap-show hidden>
🎉 Pro content here — visible after buying.
</div>
<!-- Optional: disappears after buying -->
<div data-iap-hide>
Upgrade to remove the ads.
</div>
<!-- DIFFERENT THINGS: independent — buying one never affects the others -->
<button data-iap-buy="remove_ads" data-iap-label="Remove ads — {price}">Remove ads</button>
<button data-iap-buy="level_pack_2" data-iap-label="Levels 11-20 — {price}">Get levels</button>
<div data-iap-show="level_pack_2" hidden>Levels 11-20 unlocked!</div>
<!-- SAME THING, TWO PLANS: one group, so buying either hides the other -->
<button data-iap-buy="remove_ads_monthly" data-iap-group="adfree"
data-iap-label="Remove ads — {price}/month"
data-iap-owned="✓ Ad-free (monthly)">Remove ads monthly</button>
<button data-iap-buy="remove_ads_lifetime" data-iap-group="adfree"
data-iap-label="Remove ads forever — {price}"
data-iap-owned="✓ Ads removed forever">Remove ads forever</button>6Insanlarin genellikle sattigi 4 sey#
Size uyani secin. Aralarindaki tek fark urunu Play Console'da nerede olusturdugunuzdur; sayfa kodunuz her seferinde ayni bicimdedir.
- REKLAMLARI KALDIR, BIR KEZ ODEME (sonsuza dek)
Sunun altinda olusturun: Uygulama ici urunler
Kimligi ayrica su alana da yazin: Reklamlari Kaldir (IAP) → Urun Kimligi
- REKLAMLARI KALDIR, AYLIK
Sunun altinda olusturun: Abonelikler
Kimligi ayrica su alana da yazin: Reklamlari Kaldir (IAP) → Urun Kimligi
- SEVIYELERIN / ICERIGIN KILIDINI ACIN, BIR KEZ ODEMEYLE
Sunun altinda olusturun: Uygulama ici urunler
Bunu Reklamlari Kaldir kutusuna YAZMAYIN; reklamlariniz calismaya devam eder.
- SEVIYELERIN / ICERIGIN KILIDINI ACIN, AYLIK
Sunun altinda olusturun: Abonelikler
Bunu Reklamlari Kaldir kutusuna YAZMAYIN; reklamlariniz calismaya devam eder.
Ayni uygulamada DORDUNU de ve istediginiz kadar urun satabilirsiniz. Birini satin almak digerlerini asla etkilemez.
<!-- 1. remove ads, paid once -->
<button data-iap-buy="remove_ads_lifetime"
data-iap-label="Remove ads forever — {price}"
data-iap-owned="✓ Ads removed">Remove ads forever</button>
<!-- 2. remove ads, monthly -->
<button data-iap-buy="remove_ads_monthly"
data-iap-label="Remove ads — {price}/month"
data-iap-owned="✓ Ad-free">Remove ads monthly</button>
<!-- 3. levels, paid once -->
<button data-iap-buy="level_pack_2"
data-iap-label="Levels 11-20 — {price}"
data-iap-owned="✓ Unlocked">Get levels 11-20</button>
<!-- 4. all levels, monthly -->
<button data-iap-buy="all_levels_monthly"
data-iap-label="All levels — {price}/month"
data-iap-owned="✓ Subscribed">Subscribe</button>7Hangi satin almalar reklamlari kaldirir?#
Insanlarin en cok yanlis yaptigi sey bu, bu yuzden 30 saniyeyi hak ediyor.
Reklamlariniz web sayfanizin PARCASI DEGILDIR. Sayfanin ustunde duran gercek Android reklamlaridir, bu yuzden sayfanizdaki hicbir sey onlari kapatamaz. Yalnizca BIR sey kapatabilir:
wizard Step 3 → Remove Ads (IAP) → Product ID
O kutuya yazdiginiz kimlik, biri satin aldiginda reklamlari kaldirir. Sattiginiz diger her kimlik yalnizca sayfanizin onunla yaptigi seyin kilidini acar ve reklamlar devam eder.
Hem reklamsiz plan HAM de seviye paketleri mi satiyorsunuz? O kutuya yalnizca reklamsiz plan kimligini yazin. Ornegimizde:
Product ID box: remove_ads_monthly
NOT in the box: level_pack_2
Iki reklamsiz plan mi sunuyorsunuz (aylik VE omur boyu)? Ikisini de virgulle ayirarak o tek kutuya koyun; birine sahip olmak reklamlari kaldirir:
remove_ads_monthly,remove_ads_lifetime
Reklam kaldirmayi hic satmiyor musunuz? O anahtari tamamen kapali birakin ve yalnizca '💰 Uygulama Ici Urun Sat'i kullanin.
Selling levels only:
Remove Ads (IAP) OFF
Sell In-App Products ON
Selling ad removal only:
Remove Ads (IAP) ON → Product ID: remove_ads_monthly
Sell In-App Products ON
Selling both, with two ad-free plans:
Remove Ads (IAP) ON → Product ID: remove_ads_monthly,remove_ads_lifetime
Sell In-App Products ON
(level_pack_2 stays out of the box)8Tam ornek - duz HTML sayfasi#
Iste tum ornek uygulamamiz: 'Seviyeler 11-20'yi bir kez, 'Reklamlari kaldir'i aylik satar. Kopyalayin, iki kimligi ve sozcukleri degistirin, isiniz biter.
Her etiketin ne yaptigini okuyun:
- data-iap-buy → bu dugmenin sattigi sey
- data-iap-label → metin; {price}, Google'in gercek fiyati olur
- data-iap-owned → odeme yaptiktan sonraki metin
- data-iap-show="level_pack_2" → bu kutu yalnizca O urun satin alindiktan sonra gorunur
- data-iap-hide → bu kutu herhangi bir sey satin alindiginda kaybolur
Dikkat edin, hicbir <script> ve satin alma kodu yok. Uygulamaniz bunu zaten icerir.
<h2>Get more levels</h2>
<button data-iap-buy="level_pack_2"
data-iap-label="Levels 11-20 — {price}"
data-iap-owned="✓ Levels 11-20 unlocked">
Levels 11-20
</button>
<div data-iap-show="level_pack_2" hidden>
🎉 Levels 11 to 20 are yours. Have fun!
</div>
<h2>Tired of ads?</h2>
<button data-iap-buy="remove_ads_monthly"
data-iap-label="Remove ads — {price}/month"
data-iap-owned="✓ No more ads">
Remove ads
</button>
<div data-iap-hide>
Ads help keep this game free.
</div>9Tam ornek - React veya Next.js#
KOLAY YOL: ayni etiketler React ve Next.js'te de calisir, cunku yalnizca isaretlemedir. useState yok, useEffect yok, 'use client' yok. React'in sonradan ekledigi dugmeler otomatik alinir.
Cogu uygulamanin ihtiyaci olan bu kadar; asagidaki ilk blogu kullanin ve orada durun.
KONTROLLU YOL (ikinci blok): yalnizca fiyati React'in cizmesini ve ekrani onun degistirmesini istiyorsaniz. O zaman:
- Next.js'te dosyanin 1. satirina 'use client' koyun, yoksa derleme basarisiz olur.
- window.AppMintIAP'a yalnizca useEffect icinde dokunun; bilesen govdesinde sunucuda calisir ve orada window yoktur.
- onChange(fn) durumu hemen ve her degisiklikte verir ve React'in istedigi abonelikten cikma fonksiyonunu dondurur.
- Next.js'te statik disa aktarim olarak derleyin: next.config.js icinde output: 'export'.
// ── EASY WAY — just JSX, nothing else ──────────────────────────
export default function Shop() {
return (
<>
<button data-iap-buy="level_pack_2"
data-iap-label="Levels 11-20 — {price}"
data-iap-owned="✓ Unlocked">Levels 11-20</button>
<div data-iap-show="level_pack_2" hidden>🎉 Levels 11-20 are yours!</div>
</>
);
}
// ── CONTROLLED WAY — React draws everything ───────────────────
'use client'; // Next.js: must be line 1
import { useEffect, useState } from 'react';
export default function Shop() {
const [iap, setIap] = useState({ available: false, products: {} });
useEffect(() => {
if (typeof window === 'undefined' || !window.AppMintIAP) return;
window.AppMintIAP.start({
plans: [{ id: 'level_pack_2' }, { id: 'remove_ads_monthly' }]
});
return window.AppMintIAP.onChange(setIap);
}, []);
if (!iap.available) return null; // outside the app there is nothing to buy
const levels = iap.products?.level_pack_2; // always optional-chain
return levels?.owned
? <p>🎉 Levels 11-20 are yours!</p>
: <button onClick={() => window.AppMintIAP.buy('level_pack_2')}>
Levels 11-20 {levels?.price}
</button>;
}10Tarayicinizda hizli kontrol#
Sayfanizi normal bir tarayicida acin (PC'nizde veya telefonunuzda Chrome).
Satin alma dugmesinin orada hicbir sey yapmadan durdugunu ve ucretli bolumunuzun hala gizli oldugunu gormelisiniz. Ikisi de dogru: uygulamanin disinda satin alinacak bir sey yoktur, bu yuzden kazara hicbir sey alinamaz.
Uygulamanin icinde bu tamamen farkli bir sayfadir: dugme gercek fiyati alir, Google'in odeme ekranini acar ve alici sahip olunca kendini gizler. Web siteniz her iki durumda da siradan ziyaretciler icin normal calismaya devam eder.
Teklifin webde tamamen kaybolmasini mi istiyorsunuz? Onu bir <div data-iap-container hidden> icine sarin; o sarmalayici yalnizca uygulamanin icinde gorunur.
113. Adim - ADIMLAYICI'da satin almalari ACIN#
⚠️ BUNU ATLAMAYIN. Atlarsaniz dugmeleriniz yine gorunur ama her dokunus 'Bu uygulamada uygulama ici satin almalar etkin degil.' yanitini verir ve para almazsiniz.
ADIMLAYICI (STEPPER), uygulamanizi olusturmak icin kullandiginiz 1. Adim → 2. Adim → 3. Adim ekranlaridir. Satin almalar ADIMLAYICI 3. ADIM'dadir.
BUNLARI SIRAYLA YAPIN:
- Adimlayiciyi acin ve ADIMLAYICI 3. ADIM'a gidin (Reklamlar, Bildirimler ve Izinler olan ekran).
STEPPER STEP 3 — what to set
Ads ......................... ON (needed to see the card below)
💰 Sell In-App Products ..... ON ← REQUIRED for selling anything
Selling ad removal too?
Remove Ads (IAP) ............ ON
Play Store Product ID ....... remove_ads_monthly
(two plans) .......... remove_ads_monthly,remove_ads_lifetime
License Key ................. paste from Play Console
Export ...................... AAB (not APK)124. Adim - Uygulamanizi Google Play'e koyun#
Play Console'unuzda (gelistiricilerin uygulamalarini yonettigi web sitesi):
- 'Uygulama olustur' → adi ve temel sorulari doldurun.
- Sol menude: 'Test' → 'Dahili test' → 'Yeni surum olustur' → AAB dosyanizi yukleyin → 'Kaydet' ve yayina alin.
- Hala Dahili test icindeyken → 'Test kullanicilari' sekmesi → kendi Gmail adresinizi ekleyin → daha sonrasi icin 'katil' baglantisini kopyalayin.
Dahili test ozel bir alandir; henuz kimse uygulamanizi goremez. Halka acilmadan once satin almalari SIZIN denemeniz icin vardir.
135. Adim - Urununuzu olusturun ve fiyati belirleyin#
Simdi Google'a ne sattiginizi soyleyin. Hangi menuyu kullanacaginiz, kullanicilarin bir kez mi yoksa her ay mi odedigine baglidir.
BIR KEZ ODENIR (seviyeler, Pro, jetonlar, reklamlari sonsuza dek kaldirma)
- Play Console → uygulamaniz → 'Gelir elde et' → 'Urunler' → 'Uygulama ici urunler'.
- 'Urun olustur'.
- Urun kimligi: sayfanizdaki kimligi TAM OLARAK yazin: level_pack_2. Ayni yazim, ayni buyuk/kucuk harf. Google bunu asla degistirmenize veya yeniden kullanmaniza izin vermez.
- Ad: alicilarin gordugu, ornegin 'Seviyeler 11-20'. Aciklama: tek satir.
- Fiyatinizi kendi para biriminizde belirleyin; Google onu her ulke icin donusturur.
- 'Etkinlestir'e basin.
HER AY (reklamlari aylik kaldir, tum seviyeler aboneligi)
- Play Console → 'Gelir elde et' → 'Urunler' → 'Abonelikler'.
- 'Abonelik olustur'. Urun kimligi: remove_ads_monthly.
- Kaydedin, sonra bir TEMEL PLAN ekleyin: tur 'Otomatik yenilenen', faturalandirma donemi 'Aylik', fiyatiniz.
- Hem temel planda HEM DE aboneligin kendisinde 'Etkinlestir'e basin. Birini atlarsaniz uygulamaniz onu goremez.
Kullandiginiz her kimlik icin tekrarlayin. Sayfa kodunuz degismez; uygulama Google'a her kimligin hangi tur oldugunu sorar.
AYLIK *VE* YILLIK MI ISTIYORSUNUZ? Tek bir aboneligin icinde iki temel plan yerine IKI ayri abonelik olusturun (all_levels_monthly ve all_levels_yearly). Birden fazla temel plani olan tek bir abonelik her zaman ilkini sunar, secme yolu yoktur.
Fiyati daha sonra Play Console'da istediginiz zaman degistirin; uygulamaniz yeni fiyati kendiliginden gosterir, yeniden derleme gerekmez.
146. Adim - Test edin (ucret alinmaz)#
- Play Console → sol menu 'Kurulum' → 'Lisans testi' → Gmail adresinizi test kullanicilari listesine ekleyin.
- Telefonunuzda: 4. Adim'daki dahili test katilma baglantisini acin, kabul edin ve uygulamanizi Play Store'DAN kurun.
- Uygulamayi acin. Satin alma dugmeniz gercek fiyatla gorunur.
- Dokunun; Google'in odeme sayfasi acilir ve 'Test karti, her zaman onaylar' yazar. Satin alin. Pro iceriginiz gorunur. Hicbir sey odemezsiniz.
- Bonus kontrol: uygulamayi kaldirip yeniden kurun; pro icerik acilista kendiliginden acilir. Bu, Google'in satin almayi geri yuklemesidir.
⚠️ APK'yi Play Store yerine dogrudan (sideload) kurarsaniz satin alma dugmesi GORUNMEZ. Bu normaldir; her zaman Play Store baglantisi uzerinden test edin.
157. Adim - Yayina alin ve odeme alin#
- Test iyi gorunuyorsa: Play Console → 'Uretim' → ayni AAB ile bir surum olusturun → yayina alin. (Ilk kez yayinlanan uygulamalar Google'in incelemesinden gecer; genellikle birkac gun surer.)
- Kullanicilar uygulamanizin icinde satin alir → parayi Google toplar.
- Google banka hesabiniza aylik odeme yapar (Play Console → 'Odeme profili'). Google standart hizmet bedelini alir (cogu gelistirici icin %15). AppMint hicbir sey almaz.
Daha sonra daha fazla urun eklemek icin: yeni kimligi sayfa kodunuza ekleyin, bir kez yeniden derleyin ve kimligi Play Console'da olusturun. Fiyat degisiklikleri asla yeniden derleme gerektirmez.
16Bir şey çalışmazsa#
Uygulamaniz sessizce basarisiz olmak yerine neyin ters gittigini soyler. Gordugunuz mesaji bulun:
'Bu uygulamada uygulama ici satin almalar etkin degil.'
→ Derleme sirasinda ADIMLAYICI 3. ADIM'da '💰 Uygulama Ici Urun Sat' KAPALIYDI. Acin ve yeniden derleyin.
'Bu oge henuz Google Play'de mevcut degil.'
→ Google bu kimligi tanimiyor. Play Console ile harf harf karsilastirin, urunun ETKIN oldugunu dogrulayin (abonelikte temel plan VE abonelik ikisi de etkinlestirilmeli) ve uygulamayi Play test baglantisindan kurdugunuzdan emin olun.
'Google Play odemeyi baslatamadi.'
→ Uygulama Play Store'dan kurulmamis ya da yukledginizden farkli bir anahtarla imzalanmis.
'Odeme beklemede - Google onayladiginda kendiliginden acilir.'
→ Yapilacak bir sey yok. Yavas odeme yontemleri (nakit kuponlari, bazi UPI'ler) birkac dakika alir; kilit kendiliginden acilir, uygulamayi yeniden baslatsaniz bile.
HICBIR MESAJ YOK, dokununca hicbir sey olmuyor
→ Neredeyse her zaman data-iap-buy icinde bir yazim hatasi ya da uygulamanin Play'den kurulmamis olmasidir. Diger tum nedenler yukaridaki mesajlardan birini gosterir.
ODEME CALISTI AMA REKLAMLAR HALA ORADA
→ O urun kimligi ADIMLAYICI 3. ADIM → 'Reklamlari Kaldir (IAP)' → 'Play Store Urun Kimligi' icinde degil. Ekleyin (birkacini virgulle ayirin) ve yeniden derleyin.
SATIN ALMA DUGMESI UYGULAMADA HIC GORUNMUYOR
→ Derleme sirasinda '💰 Uygulama Ici Urun Sat' kapaliydi. Normal bir web tarayicisinda dugmeler BILEREK gizlidir; uygulamanin disinda satin alinacak bir sey yoktur.
GOOGLE'IN ODEME SAYFASINI KAPATTIKTAN SONRA HICBIR SEY OLMADI
→ Bu normal bir iptaldir. Yeniden dokunun ve odemeyi tamamlayin.
FIYAT ESKI GORUNUYOR
→ Play, fiyatlari bir cihazda birkac saat onbellekte tutar. Yeni bir kurulum her zaman yeni fiyati gosterir.
17React, Next.js veya duz JavaScript#
REACT VE NEXT.JS OZEL BIR SEY GEREKTIRMEZ. Yukaridaki etiketler yalnizca isaretlemedir, bu yuzden ayni tek satir orada da calisir: useState yok, useEffect yok, 'use client' yok:
<button data-iap-buy="pro_unlock"
data-iap-label="Unlock Pro — {price}">Unlock Pro</button>
React'in sonradan olusturdugu dugmeler otomatik bulunur ve yeniden baglamak guvenlidir. Next.js'te statik disa aktarim olarak derleyin (output: 'export').
Etiket yerine kod mu tercih edersiniz? Tek satir ayni isi yapar:
AppMintIAP.sell('pro_unlock', '#buy-btn');
Istege bagli ucuncu bagimsiz degisken: {label:'Pro Kilidini Ac - {price}', owned:'✓ Kilit Acildi', show:'#thanks', hide:'.upsell'}. Her urun icin bir kez cagirin.
YALNIZCA FIYATI REACT'IN CIZMESINI ISTIYORSANIZ; aksi halde bunu atlayin:
const [iap, setIap] = useState({ available: false, products: {} });
useEffect(() => {
if (typeof window === 'undefined' || !window.AppMintIAP) return;
window.AppMintIAP.start({ plans: [{ id: 'pro_unlock' }] });
return window.AppMintIAP.onChange(setIap);
}, []);
const pro = iap.products?.pro_unlock; // her zaman istege bagli zincirleyin
if (!iap.available) return null; // uygulamanin disinda satin alinacak bir sey yok
if (pro?.owned) return <p>Tesekkurler!</p>;
return <button onClick={() => window.AppMintIAP.buy('pro_unlock')}>
Unlock Pro {pro?.price}
</button>;
IKI ALISKANLIK BUNUN BOZULMASINI ENGELLER:
- Sahipligi URUN BAZINDA okuyun: iap.products?.<id>?.owned. Ust duzey iap.owned 'herhangi bir sey sahipli' demektir ve ikinci bir urun sattiginiz gun anlami degisir.
- Tum yolu istege bagli zincirleyin; boylece beklemediginiz bir alan React cizim yaparken asla hata firlatmaz.
onChange size {available, adFree, owned, ownedIds, products} degerlerini hemen ve her degisiklikte verir, ayrica bir abonelikten cikma fonksiyonu dondurur; boylece geri donen bir alicinin kilidi ilk cizimde zaten acik olur. Next.js'te 1. satira 'use client' ekleyin ve output: 'export' ile derleyin.
Ayrica: AppMintIAP.available, .isOwned(id), .owned(), .buy(id), .state(), .refresh(). data-iap-buy etiketleri JSX'te de calisir ve React'in sonradan olusturdugu dugmeler otomatik alinir.
Ham koprusunu kendiniz kullanmak mi istiyorsunuz? Yukaridakilerin hepsi bunlar uzerine kuruludur:
- iap.isOwned(id) - aninda dogru/yanlis, cevrimdisi calisir
- iap.getOwnedProducts() - sahip olunan tum kimliklerin JSON dizisi
- iap.getProducts(reqId, idsJson) - canli fiyatlar; sonuc
'appmint:products'olayi {requestId, products:[{productId, title, price, currency, type, owned}]} olarak gelir - iap.purchase(id) - tek seferlik urunler VE abonelikler (otomatik algilanir)
- Olaylar:
'appmint:purchase'{productId} (kilidi YALNIZCA burada acin; zaten sahip olunan bir seye dokunmak bunu yeniden tetikler),'appmint:owned-changed'{products} (acilistaki geri yukleme bitti ya da bir iade bir seyi aldi; sahipligi yeniden okuyun),'appmint:purchase-failed'{reason: disabled | not_found | billing_error | pending}
Yalnizca tek bir 'reklamlari kaldir' yukseltmesi mi satiyorsunuz? Bunun yerine daha basit yerlesik cifti kullanin: sihirbazin 3. Adiminda 'Reklamlari Kaldir (IAP)' secenegini etkinlestirin:
var iap = window.WebToApk;
if (iap && !iap.isPremium()) {
showRemoveAdsButton(function onTap() {
iap.startRemoveAdsPurchase();
});
}
window.addEventListener('appmint:premium', function (e) {
if (e.detail.premium) hideRemoveAdsButton();
});Reklamlari Kaldir (IAP)
Google Play Faturalandirma Kurulumu
1Yonetilen Urun Olustur#
Google Play Console kontrol panelinizde:
- 'Gelir elde etme' > 'Uygulama ici urunler'e gidin
- 'Urun olustur'a tiklayin
- Bir Urun Kimligi girin (ornegin 'remove_ads_premium')
- Bir Ad (ornegin 'Tum Reklamlari Kaldir') ve Aciklama girin
- Fiyati ayarlayin ve 'Kaydet'e tiklayin
- Test icin kullanilabilir hale getirmek icin 'Etkinlestir'e tiklayin
Reklam kaldirmayi ABONELIK olarak mi satiyorsunuz? 'Gelir elde etme' > 'Abonelikler' altinda otomatik yenilenen bir temel planla olusturun ve temel plani VE aboneligi ETKINLESTIRIN. Reklamsiz durum, abonelik etkin oldugu surece surer ve abonelik sona erince reklamlar otomatik geri gelir.
2Lisans Anahtarinizi alin#
Google Play, satin almalari guvenle dogrulamak icin bir RSA genel anahtari ister.
Play Console'da:
- 'Gelir elde etme' > 'Gelir elde etme kurulumu'na gidin
- 'Lisanslama' altinda 'Base64 kodlu RSA genel anahtari'ni kopyalayin
- ⚠️ ONEMLI: Bunu daha sonra Olusturucu'ya yapistirmaniz gerekecek.
Example Key Format:
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...3WebToApk Olusturucu'da kurulum#
Olusturucu uygulamasinda:
- 3. Adim'da 'Reklamlari Kaldir (IAP)' anahtarini etkinlestirin
- 1. Adim'da olusturdugunuz 'Urun Kimligi'ni TAM OLARAK ayni sekilde girin
- 2. Adim'daki 'Base64 Lisans Anahtari'ni yapistirin
- APK/AAB'nizi olusturun
Birden fazla reklamsiz plan mi sunuyorsunuz (ornegin aylik abonelik VE omur boyu tek seferlik kilit acma)? Her Urun Kimligini bu tek alana, virgulle ayirarak yazin; HERHANGI birine sahip olmak reklamlari kaldirir. Listelenen ilk kimlik, yerlesik 'Reklamlari Kaldir' menu ogesinin satin aldigi kimliktir, bu yuzden varsayilan planinizi basa koyun.
Product ID: remove_ads_premium
Two plans:
remove_ads_monthly,remove_ads_lifetime4Lisans Test Kullanicilarini Ayarlayin#
Gercek para harcamadan satin almalari test etmek icin:
- Play Console'da 'Kurulum' > 'Lisans testi'ne gidin
- Gmail adresinizi 'Lisans test kullanicilari' listesine ekleyin
- 'Lisans yaniti'nin 'RESPOND_NORMALLY' olarak ayarli oldugundan emin olun
5Dahili veya Kapali Kanalda test edin#
Uygulama ici satin almalar YALNIZCA uygulama Play Store uzerinden kurulduğunda calisir (Test kanallari):
- Olusturdugunuz AAB dosyasini 'Dahili Test' veya 'Kapali Test' kanalina yukleyin
- Test kullanicilarinin katilma baglantisini test hesaplarinizla paylasin
- Uygulamayi Play Store'dan indirin
- Akisi test etmek icin uygulamanizdaki 'Reklamlari Kaldir' dugmesine dokunun!
Note: Purchases will show a 'Test instrument, always approves' message for license testers.Kod tarifleri
Yukaridaki adimlar temelleri kapsar: dort etiket, tam bir duz sayfa, React tek satiri. Bu tarifler onlarin kapsamadiklarini ele alir: birbirinin yerini alan birkac plan, sona eren abonelikler, framework kancalari, ham kopruyu tam olarak ve referans tablolari.
Tek kural#
Bir satin alma yalnizca Google onayladiginda verilir. Dugmeye tiklandiginda degil, sayfa acildiginda degil, kullanici sayfaniza geri dondugunde degil. Asagidaki her sey bu tek kuralin farkli bir ifade bicimidir.
Calisma zamani bunu zaten uygular: AppMintIAP, sahipligi acilista, her satin almada, her iadede ve uygulama her yeniden acildiginda koprudan yeniden okur. Sahiplik yeniden kurulumlarda, yeni telefonda ve cevrimdisiyken de korunur, cunku sayfanizda degil alicinin Google hesabinda yasar.
Kilidi asla tiklama islemcisinde acmayin ve hak sahipligini asla yalnizca localStorage icinde tutmayin. Temizlenmis bir tarayici deposu, odeme yapan bir musteriye satin aldigini kaybettirmemeli; kopyalanmis bir localStorage degeri de onu bedavaya vermemeli.
Tarif 1 - canli fiyatli bir fiyat tablosu#
Herhangi birini almanin digerlerinin yerini aldigi uc plan. group bunlari alternatif yapan seydir: o olmadan ilgisiz urunler olurlar ve omur boyu plani almak aylik dugmeyi orada birakir.
<div class="plans" data-iap-container>
<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_yearly" data-iap-group="pass"
data-iap-label="Yearly — {price}" data-iap-owned="Active">Yearly</button>
<button data-iap-buy="pass_lifetime" data-iap-group="pass"
data-iap-label="Lifetime — {price}" data-iap-owned="Owned">Lifetime</button>
</div>
<p data-iap-show="pass">You are a member. Thank you.</p>
<p data-iap-hide="pass">Members get every level and no ads.</p>Google'in bilmedigi bir urun kimligi (henuz olusturulmamis, etkinlestirilmemis veya uygulama Play Store'dan kurulmamis) fiyatsiz birakilir. Dugmesi bilerek dokunulabilir kalir: dokununca hicbir sey yapmak yerine gercek nedeni bildirir.
Tarif 2 - React#
onChange gecerli durumla hemen tetiklenir ve her degisiklikte yeniden tetiklenir; ayrica kendi abonelikten cikma fonksiyonunu dondurur, yani tam olarak bir useEffect ogesinin istedigi bicimdedir.
import { useEffect, useState } from 'react';
/** Purchase state for the whole app. Safe in a browser too: available is false there. */
export function useIap() {
const [iap, setIap] = useState({ available: false, ownedIds: [], products: {}, adFree: false });
useEffect(() => {
if (!window.AppMintIAP) return; // running in a browser, or IAP not enabled
return window.AppMintIAP.onChange(setIap); // returns the unsubscribe fn
}, []);
return {
...iap,
owns: (id) => iap.ownedIds.includes(id),
priceOf: (id) => iap.products[id]?.price ?? null,
buy: (id) => window.AppMintIAP?.buy(id)
};
}import { useIap } from './hooks/useIap';
export function ProGate({ children }) {
const { available, owns, priceOf, buy } = useIap();
if (owns('pro_unlock')) return children;
return (
<div className="upsell">
<h2>Pro</h2>
<p>Unlimited projects, no watermark.</p>
<button onClick={() => buy('pro_unlock')} disabled={!available}>
{available ? `Unlock Pro${priceOf('pro_unlock') ? ` — ${priceOf('pro_unlock')}` : ''}` : 'Not available here'}
</button>
</div>
);
}Dugme buy() cagirir; hicbir seyin kilidini acmaz. Kilit, Google onayladigi, onChange tetiklendigi, owns('pro_unlock') true oldugu ve bilesen yeniden cizildigi icin acilir. Bu ayni kuraldir, React'te ifade edilmis hali.
Tarif 3 - Vue#
<script setup>
import { ref, onMounted, onUnmounted, computed } from 'vue';
const iap = ref({ available: false, ownedIds: [], products: {} });
let stop;
onMounted(() => { stop = window.AppMintIAP?.onChange(s => (iap.value = s)); });
onUnmounted(() => stop && stop());
const isPro = computed(() => iap.value.ownedIds.includes('pro_unlock'));
const price = computed(() => iap.value.products.pro_unlock?.price ?? '');
</script>
<template>
<slot v-if="isPro" />
<button v-else :disabled="!iap.available" @click="AppMintIAP.buy('pro_unlock')">
Unlock Pro {{ price }}
</button>
</template>Tarif 4 - abonelikler#
Abonelik, tek seferlik urunle tamamen ayni sekilde satin alinir; fark, sona erebilmesidir. type bir urunun hangi turde oldugunu soyler, period ise faturalandirma araligini ISO-8601 suresi olarak verir: P1M aylik, P1Y yillik, P1W haftalik.
AppMintIAP.onChange(state => {
const pass = state.products.season_pass; // undefined until Google answers
const active = state.ownedIds.includes('season_pass');
document.body.classList.toggle('has-pass', active);
if (pass && !active) {
const every = { P1W: 'week', P1M: 'month', P1Y: 'year' }[pass.period] || 'period';
label.textContent = `Season pass — ${pass.price} / ${every}`;
}
});
// The pass lapsed (not renewed, or refunded): the same handler runs with it gone.
// Nothing to write — the class comes off and the paid content hides itself.Yoklama yapmazsiniz ve bitis tarihine ihtiyaciniz yoktur. Play, sahip olunan seti her acilista geri yukler, bu yuzden suresi dolmus bir abonelik artik sunda gorunmez ownedIds.
Play Console'da hem aboneligi hem de temel planini etkinlestirin. Temel plani etkin olmayan bir abonelik hic ayrinti dondurmez ve dugme "henuz Google Play'de mevcut degil" diye bildirir.
Tarif 5 - ham kopru, yardimci yok#
Yukaridakilerin hepsi alti yontem ve bes olay uzerine kuruludur. Arayuzu tamamen kendiniz yonetmek istiyorsaniz bunlari dogrudan kullanin.
const bridge = window.WebToApk;
const enabled = !!(bridge && bridge.purchase); // false in a browser
// --- what does Google charge for these, in this buyer's currency? ---
const reqId = 'prices-' + Date.now();
window.addEventListener('appmint:products', function onProducts(e) {
if (e.detail.requestId !== reqId) return; // not our answer
window.removeEventListener('appmint:products', onProducts);
for (const p of e.detail.products) {
// {productId, type:'inapp'|'subs', title, description, price, currency, period?, owned}
render(p);
}
});
bridge.getProducts(reqId, JSON.stringify(['pro_unlock', 'season_pass']));
// --- what does this buyer already own? instant, and works offline ---
const owned = JSON.parse(bridge.getOwnedProducts() || '[]');
const isPro = bridge.isOwned('pro_unlock');
// --- buy ---
buyButton.onclick = () => bridge.purchase('pro_unlock');
// --- the ONLY place a purchase is ever granted ---
window.addEventListener('appmint:purchase', e => {
if (e.detail.productId === 'pro_unlock' && e.detail.owned) unlockPro();
});
// --- and the only place failures show up ---
window.addEventListener('appmint:purchase-failed', e => {
toast({
not_found: 'This item is not available on Google Play yet.',
billing_error: 'Google Play could not start the payment. Please try again.',
pending: 'Payment pending — this unlocks automatically once Google confirms.',
disabled: 'In-app purchases are not enabled in this app.'
}[e.detail.reason] || 'Purchase could not be completed. Please try again.');
});
// --- the launch-time restore finished, or a refund took something away ---
window.addEventListener('appmint:owned-changed', () => refreshEverything());appmint:purchase zaten sahip olunan bir sey icin bilerek yeniden tetiklenir. unlockPro() fonksiyonunu iki kez calismasi zararsiz olacak sekilde yazin; boylece zaten calistirip calistirmadiginizi izlemeniz gerekmez.
Tarif 6 - uygulamanin disinda durustce geri cekilin#
Ayni sayfanin genellikle normal bir tarayicida da acilmasi gerekir; gelistirme sirasinda ya da webde de yayinladiginiz icin. Orada window.WebToApk yoktur ve hicbir satin alma olamaz. Hicbir sey yapmayan bir dugme gostermek yerine bunu soyleyin.
const inApp = !!(window.AppMintIAP && window.AppMintIAP.available);
if (!inApp) {
buyButton.disabled = true;
buyButton.textContent = 'Available in the Android app';
// and let people get it:
storeLink.hidden = false;
}Başvuru#
Sayfanin cagirabilecegi, dinleyebilecegi veya isaretleyebilecegi her sey.
| Cagri | Ne yapar |
|---|---|
AppMintIAP.available | Tarayicida ya da bu derleme icin uygulama ici satin almalar acilmadiysa false. Bir sey satisa sunmadan once kontrol edin. |
AppMintIAP.sell(id, button, opts) | Bir urunu bir dugmeye baglar. opts: label, owned, group, show, hide, labelEl. Her urun icin bir kez cagirin. |
AppMintIAP.start(config) | Birkacini birden baglayin: {plans, show, hide, container, toast, hideOtherPlans}. Kendi toast fonksiyonunuzu istediginizde ya da biri satin alindiktan sonra bir grubun diger planlarini gorunur tutmak istediginizde kullanin (hideOtherPlans: false). |
AppMintIAP.onChange(fn) | Gecerli durumla hemen tetiklenir, sonra her degisiklikte. Bir abonelikten cikma fonksiyonu dondurur. |
AppMintIAP.state() | Ayni nesne, bir kez: {available, adFree, owned, ownedIds, products}. |
AppMintIAP.isOwned(id) | Dogru/yanlis, aninda, cevrimdisi. |
AppMintIAP.owned() | Sahip olunan her urun kimliginin dizisi. |
AppMintIAP.buy(id) | Google'in satin alma sayfasini acar. Kendi basina hicbir sey vermez. |
AppMintIAP.refresh() | Son taramadan beri eklenen data-iap-buy dugmelerini yeniden tarar. Nadiren gerekir; calisma zamani DOM'u izler. |
| Cagri | Dondurur |
|---|---|
purchase(productId) | Hicbir sey. Sayfayi acar; sonuc bir olay olarak gelir. |
getProducts(requestId, idsJson) | Hicbir sey. Yanit, ayni degeri tasiyan appmint:products olarak gelir requestId. |
getOwnedProducts() | Sahip olunan urun kimliklerinin JSON dizisi, metin olarak. |
isOwned(productId) | Boolean - sahip olunan tek seferlik bir urun ya da su an etkin bir abonelik. |
isPremium() | Boolean - ozellikle "reklamlari kaldir" hak sahipligi. |
startRemoveAdsPurchase() | Hicbir sey. Sihirbazin "Reklamlari Kaldir" alanindaki ILK kimlik icin sayfayi acar; orada listelenen herhangi bir kimlige sahip olmak reklamlari kaldirir. Sonuc su olarak gelir appmint:premium. |
| Olay | ayrinti | Ne zaman |
|---|---|---|
appmint:purchase | {productId, owned} | Google onayladi. Kilidi acmanin tek yeri. Zaten sahip olunan bir sey icin yeniden tetiklenir. |
appmint:purchase-failed | {productId, reason} | Sayfa hic acilmadi, akis hata verdi veya odeme beklemede. Duz bir iptalde asla tetiklenmez. |
appmint:products | {requestId, products[]} | Bir getProducts() cagrisina yanit. Play'in tanimadigi kimlikler yoktur. |
appmint:premium | {premium} | Reklamsiz hak sahipligi degisti: satin alma, acilista geri yukleme, iade veya sona erme. |
appmint:owned-changed | {products[]} | Acilistaki geri yukleme bitti ya da bir iade bir seyi aldi. Sahip olunan her kimligi tasir. |
| Ozellik | Acik | Ne yapar |
|---|---|---|
data-iap-buy="id" | dugme | O urunu satin aldirir. |
data-iap-label="… {price}" | dugme | Etiket sablonu. {price}, Google'in canli, yerellestirilmis fiyati olur. |
data-iap-owned="…" | dugme | Urun satin alindiktan sonraki etiket. |
data-iap-group="name" | dugme | Planlari birbirinin alternatifi olarak isaretler. |
data-iap-show="id|group" | herhangi bir sey | Yalnizca buna sahip olunduğunda gosterilir. |
data-iap-hide="id|group" | herhangi bir sey | Buna sahip olunduktan sonra gizlenir. |
data-iap-text | bir dugmenin icinde | Dugmenin icinde metinden fazlasi varsa, etiketin metnini alan oge. |
data-iap-container | sarmalayici | Yalnizca uygulamanin icinde gosterilir; satin alinacak bir sey olmayan tarayicida gizlidir. Tum teklifi bununla sarmalayin. |
data-iap-state="owned" | calisma zamani tarafindan ayarlanir | Sahip olunan bir urunun dugmesinde, boylece onu bicimlendirebilirsiniz. |
| neden | Gercekte ne oldu |
|---|---|
not_found | Google bu urun kimligini tanimiyor: olusturulmamis, etkinlestirilmemis ya da uygulama Play Store'dan kurulmamis. |
billing_error | Play odemeyi baslatamadi. Genellikle gecici; alicinin yeniden denemesi gerekir. |
pending | Yavas bir odeme yontemi (nakit, banka havalesi). Google onayladiginda kendiliginden acilir; bunu basarisizlik olarak gormeyin. |
disabled | Bu derleme icin uygulama ici satin almalar acilmadi. |
unknown | Baska herhangi bir sey. |
Yayinlamadan once#
- Kodunuzdaki her urun kimligi Play Console'da var ve etkinlestirilmis. Abonelik icin temel plan da.
- Derleme ayni kimlikleri ve Gelir elde etme kurulumundaki Base64 RSA lisans anahtarini tasir.
- Gmail adresiniz Kurulum → Lisans testi bolumunde, yanit RESPOND_NORMALLY olarak ayarli sekilde.
- AAB'yi Dahili veya Kapali test kanalina yuklediniz ve Play Store baglantisindan kurdunuz; sideload edilmis bir APK hicbir sey satin alamaz.
- Her urunu lisans test kullanicisi olarak satin aldiniz ve "Test araci, her zaman onaylar" mesajini gordunuz.
- Uygulamayi kaldirip yeniden kurdunuz ve satin alma kendiliginden geri geldi.
- Agi kapattiniz ve ucretli icerigin kilidi hala acik.
- Play Console'da bir test satin almasini iade ettiniz ve uygulamanin yeniden kilitlendigini gordunuz.
Paraya mal olan hatalar#
- Kilidi tiklama islemcisinde acmak. Sayfayi acip geri donen herkes urunu bedavaya alir.
- Hak sahipligini yalnizca
localStorageicinde saklamak. Site verilerini temizlemek bir musterinin odedigi seyi alir; bir degeri kopyalamak onu bedavaya verir. - Fiyati HTML'e sabit yazmak. Diger her ulkede yanlis olur ve degistirdiginiz gun eskir.
{price}veya kullaninproducts[id].price. - Play faturalandirmasi uzerinden fiziksel mal veya gercek dunya hizmeti satmak. Google tam tersini ister: bunlar normal bir odeme islemcisi kullanmalidir. Stripe rehberi bunun icindir.
- Sideload edilmis bir APK'da test etmek. Uygulama ici satin almalar yalnizca uygulama Play Store'dan kurulduğunda calisir. Yazdiginiz hicbir sey bunu degistiremez.
pendingdurumunu basarisizlik olarak gormek. Alici yavas bir yontemle odedi; Google onayladiginda kendiliginden acilir.