मुफ़्त APK बनाएँ

कमाई करना

ऐप के अंदर प्रोडक्ट बेचें (IAP)

अपनी वेबसाइट या ZIP ऐप से Google Play खरीदारी

1📥 गाइड + काम करता डेमो ऐप पाएँ#

इस गाइड की हर बात एक PDF में भी है जिसे आप ऑफ़लाइन पढ़ सकते हैं, साथ में एक पूरा डेमो ऐप जिसे आप आज ही बिल्ड करके चला सकते हैं।

नीचे दबाएँ और दोनों आपके फ़ोन में Downloads/AppMint/Guides के अंदर सेव हो जाते हैं:

  • AppMint-InApp-Purchases-Guide.pdf - पूरी गाइड, जिसमें Play Console के चरण और React / Next.js के उदाहरण शामिल हैं
  • iap-demo-index.html - एक छोटा गेम जो तीन चीज़ें बेचता है: विज्ञापन हटाना (एक बार), एक लेवल पैक (एक बार) और एक मासिक सीज़न पास

डेमो आज़माने के लिए: iap-demo-index.html को अकेले ज़िप करें, स्टेपर में अपलोड करें, और गाइड का पालन करें। अपने ऐप को छूने से पहले असली खरीदारी को शुरू से अंत तक चलता देखने का यह सबसे तेज़ तरीका है।

ऐप में, इस चरण का बटन इन्हें आपके फ़ोन में सेव करता है। यहाँ ये सीधे डाउनलोड होते हैं।

AppMint-InApp-Purchases-Guide.pdf PDF · 257 KB iap-demo-index.html HTML · 10 KB

2यह क्या है? (आम शब्दों में)#

आपका ऐप चीज़ें बेच सकता है - Pro अपग्रेड, अतिरिक्त लेवल, सब्सक्रिप्शन - और Google पैसे इकट्ठा करके हर महीने आपको दे देता है।

हम इस गाइड में शुरू से अंत तक एक उदाहरण साथ मिलकर बनाएँगे:

A game that sells two things
  • 'Levels 11-20'      ₹99  paid once, kept forever
  • 'Remove ads'        ₹49  every month

आखिर में आप अपने पेज में चार लाइनें जोड़ेंगे। बस इतना ही काम है - खरीदारी का कोई कोड लिखना नहीं है, और अगर आपने यह पहले कभी नहीं किया तब भी आप आराम से कर लेंगे।

कॉपी-पेस्ट से आगे की प्रोग्रामिंग जानना ज़रूरी नहीं है, और सर्वर, डेटाबेस या पेमेंट कंपनी के अकाउंट की भी ज़रूरत नहीं है। मुश्किल हिस्से Google Play संभालता है:

  • पेमेंट स्क्रीन दिखाता है (कार्ड / UPI / कैरियर बिलिंग)
  • आपकी कीमत को हर देश की मुद्रा में बदल देता है
  • याद रखता है किसने क्या खरीदा - रीइंस्टॉल के बाद भी
  • पैसा आपके बैंक में भेजता है

AppMint 0% लेता है - बिक्री सीधे आपके Google Play अकाउंट में जाती है।

Website → APK, ZIP → APK और AI से बने ऐप्स के लिए एक जैसा काम करता है।

3शुरू करने से पहले आपको क्या चाहिए#

बस चार चीज़ें:

  1. आपकी वेबसाइट या HTML/ZIP ऐप (जिसे आप APK में बदलते हैं)।
  2. बेचने के लिए कुछ - तय करें कि खरीदारों को क्या मिलेगा (जैसे ‘Pro version’: कोई सीमा नहीं, अतिरिक्त कंटेंट)।
  3. Google Play डेवलपर अकाउंट - play.google.com/console पर एक बार का $25 शुल्क। Play Store पर कोई भी ऐप प्रकाशित करने के लिए यह चाहिए, सिर्फ़ पेड ऐप्स के लिए नहीं।
  4. कुल मिलाकर करीब 1 घंटा, जो नीचे के चरणों में बँटा है।

पहले से जान लें: इन-ऐप खरीदारी तभी काम करती है जब आपका ऐप Play Store से इंस्टॉल हो। यह सबके लिए Google का नियम है, AppMint की सीमा नहीं।

4चरण 1 - अपना प्रोडक्ट ID सोचें#

प्रोडक्ट ID एक छोटा नाम है जो आप अपनी बेची जाने वाली चीज़ के लिए ख़ुद बनाते हैं। आप यही नाम बाद में दो जगह लिखेंगे (आपका पेज + Play Console), इसलिए इसे सरल रखें और लिख लें।

नियम: सिर्फ़ छोटे अक्षर (lowercase), संख्याएँ, अंडरस्कोर। अक्षर या संख्या से शुरू होना चाहिए।

इनमें से कोई एक कॉपी करें:

  • pro_unlock - ‘Pro version’ अपग्रेड (अच्छा डिफ़ॉल्ट - नीचे का कोड पहले से इसे इस्तेमाल करता है)
  • remove_ads - विज्ञापन-मुक्त अपग्रेड
  • level_pack_2 - अतिरिक्त कंटेंट पैक
  • coins_100 - गेम करेंसी पैक
  • premium_monthly - मासिक सब्सक्रिप्शन

कीमत ID का हिस्सा नहीं है - आप कीमत चरण 5 में तय करेंगे, और उसे कभी भी अपना कोड छुए बिना बदल सकते हैं।

5चरण 2 - अपना बटन मार्क करें (कोई कोड नहीं लिखना)#

आपको खरीदारी का कोई कोड नहीं लिखना है। आपके ऐप में खरीदारी का इंजन पहले से है - आप बस अपने HTML को लेबल करते हैं ताकि उसे पता चले कि क्या बेचना है।

नीचे का ब्लॉक अपने पेज में कॉपी करें और सिर्फ़ ये बदलें:

(1) pro_unlock → आपका प्रोडक्ट ID, data-iap-buy में
(2) बटन पर और पेड भाग के अंदर के शब्द

बस इतना ही। फिर बटन खरीदार की अपनी मुद्रा में Google की असली कीमत दिखाता है, दबाने पर Google की पेमेंट स्क्रीन खोलता है, भुगतान के बाद पेड भाग दिखाता है (और जो पहले खरीद चुके हैं उनके लिए हर लॉन्च पर), सब्सक्रिप्शन खत्म होने या भुगतान रिफ़ंड होने पर उसे वापस ले लेता है, और कुछ भी गलत हो तो साफ़ संदेश दिखाता है।

चार लेबल:

  • data-iap-buy="id" - इसे खरीदने का बटन बनाता है (ज़रूरी)
  • data-iap-label="…{price}…" - बटन का टेक्स्ट; {price} Google की असली कीमत बन जाता है
  • data-iap-owned="…" - खरीदने के बाद बटन का टेक्स्ट
  • data-iap-show - वह भाग जो सिर्फ़ खरीदने के बाद दिखता है (उसे 'hidden' से शुरू करें)

ये भी उपलब्ध हैं: data-iap-hide (खरीदने के बाद गायब हो जाता है), data-iap-container (एक रैपर जो सिर्फ़ ऐप के अंदर दिखता है), data-iap-text (अगर आपके बटन में आइकन भी हैं, तो इसे उस <span> पर लगाएँ जिसमें टेक्स्ट है)।

कई अलग चीज़ें बेच रहे हैं? हर एक के लिए बटन जोड़ें। वे स्वतंत्र हैं - एक ऐप विज्ञापन हटा सकता है और लेवल पैक बेच सकता है और सब्सक्रिप्शन भी चला सकता है, और एक का खरीदना बाकी पर कभी असर नहीं डालता। किसी भाग को data-iap-show="level_pack_2" से एक प्रोडक्ट से जोड़ें।

सिर्फ़ वे ID जो आप ‘Remove Ads (IAP) → Product ID’ में लिखते हैं, विज्ञापन बंद करते हैं। बाकी जो भी आप बेचते हैं वह बस वही अनलॉक करता है जो आपका पेज उसके साथ करता है, और विज्ञापन चलते रहते हैं।

एक ही चीज़ के दो प्लान (मासिक बनाम लाइफ़टाइम)? दोनों बटनों को एक जैसा data-iap-group="adfree" दें - तब किसी एक को खरीदने पर दूसरा छिप जाता है, ताकि कोई दो बार भुगतान न करे। ग्रुप के बिना कुछ नहीं छिपता, जो असंबंधित प्रोडक्ट के लिए सही है।

<!-- (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>

6वे 4 चीज़ें जो लोग आमतौर पर बेचते हैं#

जो आप पर लागू हो वह चुनें। इनमें फ़र्क सिर्फ़ इतना है कि आप Play Console में प्रोडक्ट कहाँ बनाते हैं - आपका पेज कोड हर बार एक ही रूप का रहता है।

  1. विज्ञापन हटाएँ, एक बार भुगतान (हमेशा के लिए)

इसे यहाँ बनाएँ: In-app products
id यहाँ भी लिखें: Remove Ads (IAP) → Product ID

  1. विज्ञापन हटाएँ, मासिक

इसे यहाँ बनाएँ: Subscriptions
id यहाँ भी लिखें: Remove Ads (IAP) → Product ID

  1. लेवल / कंटेंट अनलॉक करें, एक बार भुगतान

इसे यहाँ बनाएँ: In-app products
इसे Remove Ads बॉक्स में न लिखें - आपके विज्ञापन चलते रहेंगे।

  1. लेवल / कंटेंट अनलॉक करें, मासिक

इसे यहाँ बनाएँ: Subscriptions
इसे Remove Ads बॉक्स में न लिखें - आपके विज्ञापन चलते रहेंगे।

आप एक ही ऐप में ये चारों बेच सकते हैं, और जितने चाहें उतने प्रोडक्ट। एक का खरीदना बाकी पर कभी असर नहीं डालता।

<!-- 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>

7कौन-सी खरीदारियाँ विज्ञापन हटाती हैं?#

यही वह चीज़ है जिसमें लोग गलती करते हैं, इसलिए इसके लिए 30 सेकंड देना ठीक है।

आपके विज्ञापन आपके वेब पेज का हिस्सा नहीं हैं। वे असली Android विज्ञापन हैं जो उसके ऊपर बैठते हैं, इसलिए आपके पेज में कुछ भी उन्हें बंद नहीं कर सकता। सिर्फ़ एक चीज़ कर सकती है:

wizard Step 3 → Remove Ads (IAP) → Product ID

उस बॉक्स में आप जो भी id लिखेंगे, उसे कोई खरीदे तो विज्ञापन हट जाते हैं। आप जो बाकी id बेचते हैं वे बस वही अनलॉक करते हैं जो आपका पेज उनके साथ करता है, और विज्ञापन चलते रहते हैं।

विज्ञापन-मुक्त प्लान और लेवल पैक दोनों बेच रहे हैं? उस बॉक्स में सिर्फ़ विज्ञापन-मुक्त वाला id लिखें। हमारे उदाहरण में:

Product ID box:  remove_ads_monthly
NOT in the box:  level_pack_2

दो विज्ञापन-मुक्त प्लान दे रहे हैं (मासिक और लाइफ़टाइम)? दोनों को उसी एक बॉक्स में कॉमा से अलग करके डालें - इनमें से किसी एक का मालिक होने पर विज्ञापन हट जाते हैं:

remove_ads_monthly,remove_ads_lifetime

विज्ञापन हटाना बिल्कुल नहीं बेच रहे? वह टॉगल पूरी तरह बंद रखें और बस ‘💰 Sell In-App Products’ इस्तेमाल करें।

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)

8पूरा उदाहरण - सादा HTML पेज#

यह रहा हमारा पूरा उदाहरण ऐप: ‘Levels 11-20’ एक बार बेचता है, और ‘Remove ads’ मासिक। इसे कॉपी करें, दो id और शब्द बदलें, और बस हो गया।

पढ़ें कि हर लेबल क्या करता है:

  • data-iap-buy → यह बटन क्या बेचता है
  • data-iap-label → टेक्स्ट; {price} Google की असली कीमत बन जाता है
  • data-iap-owned → भुगतान के बाद का टेक्स्ट
  • data-iap-show="level_pack_2" → यह बॉक्स सिर्फ़ तब दिखता है जब वही प्रोडक्ट खरीदा जाए
  • data-iap-hide → कुछ भी खरीदे जाने पर यह बॉक्स गायब हो जाता है

ध्यान दें कि कोई <script> नहीं है और खरीदारी का कोई कोड नहीं है। आपके ऐप में वह पहले से मौजूद है।

<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>

9पूरा उदाहरण - React या Next.js#

आसान तरीका: यही लेबल React और Next.js में भी काम करते हैं, क्योंकि वे सिर्फ़ मार्कअप हैं। न useState, न useEffect, न 'use client'। React जो बटन बाद में जोड़ता है वे अपने आप पकड़े जाते हैं।

ज़्यादातर ऐप्स को बस इतना ही चाहिए - नीचे का पहला ब्लॉक इस्तेमाल करें और वहीं रुक जाएँ।

कंट्रोल्ड तरीका (दूसरा ब्लॉक): सिर्फ़ तब जब आप चाहते हैं कि React ख़ुद कीमत दिखाए और स्क्रीन बदले। तब:

  • Next.js में फ़ाइल की लाइन 1 पर 'use client' लगाएँ, वरना बिल्ड फ़ेल हो जाता है।
  • window.AppMintIAP को सिर्फ़ useEffect के अंदर छुएँ - कॉम्पोनेंट बॉडी में वह सर्वर पर चलता है, जहाँ window मौजूद नहीं है।
  • onChange(fn) आपको स्थिति तुरंत और हर बदलाव पर देता है, और वह unsubscribe फ़ंक्शन लौटाता है जो React को चाहिए।
  • Next.js में static export के रूप में बिल्ड करें: next.config.js में 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>;
}

10ब्राउज़र में तेज़ जाँच#

अपना पेज सामान्य ब्राउज़र (अपने PC या फ़ोन पर Chrome) में खोलें।

आपको खरीदने का बटन वहाँ बिना कुछ किए बैठा दिखना चाहिए, और आपका पेड भाग अब भी छिपा हुआ। दोनों सही हैं: ऐप के बाहर खरीदने को कुछ नहीं है, इसलिए गलती से कुछ खरीदा नहीं जा सकता।

ऐप के अंदर यह बिल्कुल अलग पेज है: बटन को असली कीमत मिलती है, Google की पेमेंट स्क्रीन खुलती है, और खरीदार के मालिक बनते ही बटन ख़ुद छिप जाता है। आपकी वेबसाइट सामान्य विज़िटर्स के लिए दोनों तरह सामान्य रूप से चलती रहती है।

चाहते हैं कि ऑफ़र वेब पर पूरी तरह गायब हो? उसे <div data-iap-container hidden> में लपेटें - वह रैपर सिर्फ़ ऐप के अंदर दिखाया जाता है।

11चरण 3 - STEPPER में खरीदारी चालू करें#

⚠️ इसे न छोड़ें। अगर आप इसे छोड़ते हैं तो आपके बटन फिर भी दिखते हैं, पर हर बार दबाने पर जवाब आता है ‘In-app purchases are not enabled in this app.’ और आपको कोई पैसा नहीं मिलता।

STEPPER वे चरण 1 → चरण 2 → चरण 3 वाली स्क्रीन हैं जिनसे आप अपना ऐप बनाते हैं। खरीदारी STEPPER चरण 3 में है।

यह क्रम से करें:

  1. स्टेपर खोलें और STEPPER चरण 3 पर जाएँ (वह स्क्रीन जिसमें Ads, Notifications और Permissions हैं)।
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)

12चरण 4 - अपना ऐप Google Play पर डालें#

आपके Play Console में (वह वेबसाइट जहाँ डेवलपर अपने ऐप्स संभालते हैं):

  1. ‘Create app’ → नाम और बुनियादी सवाल भरें।
  2. बाएँ मेन्यू में: ‘Testing’ → ‘Internal testing’ → ‘Create new release’ → अपनी AAB फ़ाइल अपलोड करें → ‘Save’ करें और रोल आउट करें।
  3. अब भी Internal testing में → ‘Testers’ टैब → अपना Gmail जोड़ें → बाद के लिए ‘join’ लिंक कॉपी करें।

Internal testing एक निजी क्षेत्र है - अभी कोई आपका ऐप नहीं देख सकता। यह इसलिए है कि सार्वजनिक होने से पहले आप ख़ुद खरीदारी आज़मा सकें।

13चरण 5 - अपना प्रोडक्ट बनाएँ और कीमत तय करें#

अब Google को बताएँ कि आप क्या बेच रहे हैं। कौन-सा मेन्यू इस्तेमाल होगा, यह इस पर निर्भर है कि लोग एक बार भुगतान करते हैं या हर महीने।

एक बार भुगतान (लेवल, Pro, सिक्के, हमेशा के लिए विज्ञापन हटाना)

  1. Play Console → आपका ऐप → ‘Monetize’ → ‘Products’ → ‘In-app products’।
  2. ‘Create product’।
  3. Product ID: अपने पेज से ठीक वही id लिखें - level_pack_2। वही स्पेलिंग, वही केस। Google आपको इसे बदलने या दोबारा इस्तेमाल करने नहीं देता।
  4. Name: खरीदार जो देखते हैं, जैसे ‘Levels 11-20’। Description: एक लाइन।
  5. अपनी कीमत अपनी मुद्रा में तय करें - Google उसे हर देश के लिए बदल देता है।
  6. ‘Activate’ दबाएँ।

हर महीने (मासिक विज्ञापन हटाना, सभी-लेवल का सब्सक्रिप्शन)

  1. Play Console → ‘Monetize’ → ‘Products’ → ‘Subscriptions’।
  2. ‘Create subscription’। Product ID: remove_ads_monthly।
  3. Save करें, फिर एक BASE PLAN जोड़ें: ‘Auto-renewing’ टाइप करें, billing period ‘Monthly’, अपनी कीमत।
  4. बेस प्लान और सब्सक्रिप्शन दोनों पर ‘Activate’ दबाएँ। एक भी छूटा तो आपका ऐप उसे देख नहीं पाएगा।

हर उस id के लिए दोहराएँ जो आपने इस्तेमाल किया। आपका पेज कोड नहीं बदलता - ऐप Google से पूछता है कि हर id किस तरह का है।

मासिक *और* सालाना चाहिए? एक सब्सक्रिप्शन के अंदर दो बेस प्लान की जगह दो अलग सब्सक्रिप्शन बनाएँ (all_levels_monthly और all_levels_yearly)। कई बेस प्लान वाला एक सब्सक्रिप्शन हमेशा पहला ही दिखाता है, चुनने का कोई तरीका नहीं।

कीमत बाद में कभी भी Play Console में बदलें - आपका ऐप नई कीमत अपने आप दिखाता है, दोबारा बिल्ड की ज़रूरत नहीं।

14चरण 6 - इसे टेस्ट करें (आपसे शुल्क नहीं लिया जाएगा)#

  1. Play Console → बाएँ मेन्यू में ‘Setup’ → ‘License testing’ → टेस्टर सूची में अपना Gmail जोड़ें।
  2. अपने फ़ोन पर: चरण 4 का internal-testing join लिंक खोलें, स्वीकार करें, और अपना ऐप Play Store से ही इंस्टॉल करें।
  3. ऐप खोलें। आपका खरीदने का बटन असली कीमत के साथ दिखता है।
  4. इसे दबाएँ - Google की पेमेंट शीट खुलती है और ‘Test card, always approves’ दिखाती है। खरीदें। आपका प्रो कंटेंट दिख जाता है। आप कुछ भी नहीं चुकाते।
  5. बोनस जाँच: ऐप अनइंस्टॉल करके दोबारा इंस्टॉल करें - प्रो कंटेंट लॉन्च पर अपने आप अनलॉक हो जाता है। यह Google द्वारा खरीदारी बहाल करना है।

⚠️ अगर आप APK को Play Store से नहीं, सीधे इंस्टॉल (साइडलोड) करते हैं, तो खरीदने का बटन नहीं दिखेगा। यह सामान्य है - टेस्टिंग हमेशा Play Store लिंक से करें।

15चरण 7 - लाइव जाएँ और कमाई पाएँ#

  1. जब टेस्टिंग ठीक लगे: Play Console → ‘Production’ → उसी AAB से एक रिलीज़ बनाएँ → रोल आउट करें। (पहली बार वाले ऐप्स Google की समीक्षा से गुज़रते हैं - आमतौर पर कुछ दिन।)
  2. यूज़र आपके ऐप के अंदर खरीदते हैं → Google पैसे इकट्ठा करता है।
  3. Google आपके बैंक अकाउंट में हर महीने भुगतान करता है (Play Console → ‘Payments profile’)। Google अपनी मानक सेवा फ़ीस रखता है (ज़्यादातर डेवलपर्स के लिए 15%)। AppMint कुछ नहीं रखता।

बाद में और प्रोडक्ट जोड़ने के लिए: अपने पेज कोड में नया ID जोड़ें, एक बार फिर बिल्ड करें, और Play Console में वह ID बनाएँ। कीमत बदलने पर कभी दोबारा बिल्ड नहीं करना पड़ता।

16अगर कुछ काम न करे#

आपका ऐप चुपचाप फ़ेल होने की जगह बताता है कि क्या गलत हुआ। अपना दिखा संदेश ढूँढें:

‘In-app purchases are not enabled in this app.’
→ जब आपने बिल्ड किया तब STEPPER चरण 3 में ‘💰 Sell In-App Products’ बंद था। इसे चालू करें और फिर से बिल्ड करें।

‘This item is not available on Google Play yet.’
→ Google उस id को पहचानता नहीं। Play Console से अक्षर-अक्षर मिलाएँ, पक्का करें कि प्रोडक्ट ACTIVE है (सब्सक्रिप्शन के लिए बेस प्लान और सब्सक्रिप्शन दोनों चालू होने चाहिए), और यह भी कि आपने ऐप Play टेस्टर लिंक से इंस्टॉल किया है।

‘Google Play could not start the payment.’
→ ऐप Play Store से इंस्टॉल नहीं हुआ था, या उस कुंजी से साइन हुआ था जो आपने अपलोड की थी उससे अलग।

‘Payment pending - this unlocks automatically once Google confirms.’
→ कुछ करना नहीं है। धीमे भुगतान तरीकों (कैश वाउचर, कुछ UPI) में कुछ मिनट लगते हैं; अनलॉक अपने आप आ जाता है, ऐप दोबारा शुरू करने पर भी।

कोई संदेश नहीं, दबाने पर कुछ नहीं होता
→ लगभग हमेशा data-iap-buy में टाइपो होता है, या ऐप Play से इंस्टॉल नहीं है। बाकी हर वजह ऊपर के संदेशों में से कोई एक दिखाती है।

भुगतान हो गया पर विज्ञापन अब भी दिख रहे हैं
→ वह प्रोडक्ट id STEPPER चरण 3 → ‘Remove Ads (IAP)’ → ‘Play Store Product ID’ में नहीं है। इसे जोड़ें (कई हों तो कॉमा से अलग करें) और फिर से बिल्ड करें।

ऐप में खरीदने का बटन कभी नहीं दिखता
→ बिल्ड के समय ‘💰 Sell In-App Products’ बंद था। सामान्य वेब ब्राउज़र में बटन जानबूझकर छिपे रहते हैं - ऐप के बाहर खरीदने को कुछ नहीं है।

Google की पेमेंट शीट बंद करने के बाद कुछ नहीं हुआ
→ यह सामान्य कैंसल है। फिर से दबाएँ और भुगतान पूरा करें।

कीमत पुरानी दिख रही है
→ Play डिवाइस पर कीमतें कुछ घंटों तक कैश रखता है। नया इंस्टॉल हमेशा नई कीमत दिखाता है।

17React, Next.js, या सादा JavaScript#

React और Next.js को कुछ ख़ास नहीं चाहिए। ऊपर के लेबल सिर्फ़ मार्कअप हैं, इसलिए वही एक लाइन वहाँ भी काम करती है - न useState, न useEffect, न 'use client':

<button data-iap-buy="pro_unlock"

      data-iap-label="Unlock Pro — {price}">Unlock Pro</button>

React जो बटन बाद में माउंट करता है वे अपने आप मिल जाते हैं, और दोबारा माउंट करना सुरक्षित है। Next.js में static export के रूप में बिल्ड करें (output: 'export')।

लेबल की जगह कोड पसंद है? एक लाइन वही काम करती है:

AppMintIAP.sell('pro_unlock', '#buy-btn');

वैकल्पिक तीसरा आर्गुमेंट: {label:'Unlock Pro - {price}', owned:'✓ Unlocked', show:'#thanks', hide:'.upsell'}। हर प्रोडक्ट के लिए एक बार कॉल करें।

सिर्फ़ तब जब आप चाहते हैं कि React ख़ुद कीमत दिखाए - वरना इसे छोड़ दें:

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; // always optional-chain

if (!iap.available) return null; // nothing to buy outside the app
if (pro?.owned) return <p>Thanks!</p>;
return <button onClick={() => window.AppMintIAP.buy('pro_unlock')}>

       Unlock Pro {pro?.price}
     </button>;

दो आदतें इसे टूटने से बचाती हैं:

  1. ओनरशिप प्रोडक्ट के हिसाब से पढ़ें - iap.products?.<id>?.owned। ऊपरी स्तर का iap.owned मतलब ‘कुछ भी खरीदा है’ और दूसरा प्रोडक्ट बेचते ही उसका मतलब बदल जाता है।
  2. पूरे पाथ पर optional-chain लगाएँ, ताकि React के रेंडर करते समय कोई अनपेक्षित फ़ील्ड कभी त्रुटि न फेंके।

onChange आपको तुरंत और हर बदलाव पर {available, adFree, owned, ownedIds, products} देता है, और एक unsubscribe फ़ंक्शन लौटाता है - इसलिए लौटा हुआ खरीदार पहले रेंडर पर ही अनलॉक होता है। Next.js में लाइन 1 पर 'use client' जोड़ें और output: 'export' के साथ बिल्ड करें।

और भी: AppMintIAP.available, .isOwned(id), .owned(), .buy(id), .state(), .refresh()। data-iap-buy लेबल JSX में भी काम करते हैं, और React जो बटन बाद में माउंट करता है वे अपने आप पकड़े जाते हैं।

कच्चा ब्रिज ख़ुद चलाना पसंद है? ऊपर सब कुछ इन पर बना है:

  • iap.isOwned(id) - तुरंत true/false, ऑफ़लाइन भी काम करता है
  • iap.getOwnedProducts() - सभी खरीदे गए IDs का JSON ऐरे
  • iap.getProducts(reqId, idsJson) - लाइव कीमतें; नतीजा 'appmint:products' इवेंट के रूप में आता है {requestId, products:[{productId, title, price, currency, type, owned}]}
  • iap.purchase(id) - एक बार वाले प्रोडक्ट और सब्सक्रिप्शन दोनों (अपने आप पहचाने जाते हैं)
  • इवेंट: 'appmint:purchase' {productId} (अनलॉक सिर्फ़ यहीं करें - पहले से खरीदी चीज़ पर दबाने से यह फिर चलता है), 'appmint:owned-changed' {products} (लॉन्च पर रीस्टोर पूरा हुआ, या रिफ़ंड से कुछ चला गया - ओनरशिप फिर पढ़ें),'appmint:purchase-failed' {reason: disabled | not_found | billing_error | pending}

सिर्फ़ एक ‘remove ads’ अपग्रेड बेच रहे हैं? इसके बजाय सरल बिल्ट-इन जोड़ी इस्तेमाल करें - विज़ार्ड चरण 3 में ‘Remove Ads (IAP)’ चालू करें:

var iap = window.WebToApk;

if (iap && !iap.isPremium()) {
  showRemoveAdsButton(function onTap() {
    iap.startRemoveAdsPurchase();
  });
}

window.addEventListener('appmint:premium', function (e) {
  if (e.detail.premium) hideRemoveAdsButton();
});

Remove Ads (IAP)

Google Play Billing सेटअप

1Managed Product बनाएँ#

आपके Google Play Console डैशबोर्ड में:

  • ‘Monetization’ > ‘In-app products’ पर जाएँ
  • ‘Create product’ दबाएँ
  • एक Product ID डालें (जैसे ‘remove_ads_premium’)
  • एक Name (जैसे ‘Remove All Ads’) और Description डालें
  • Price सेट करें और ‘Save’ दबाएँ
  • टेस्टिंग के लिए उपलब्ध कराने हेतु ‘Activate’ दबाएँ

विज्ञापन हटाना सब्सक्रिप्शन के रूप में बेचना चाहते हैं? इसे ‘Monetization’ > ‘Subscriptions’ में auto-renewing बेस प्लान के साथ बनाएँ, और बेस प्लान और सब्सक्रिप्शन दोनों चालू करें। तब विज्ञापन-मुक्त स्थिति सब्सक्रिप्शन चालू रहने तक चलती है, और खत्म होने पर विज्ञापन अपने आप लौट आते हैं।

2अपनी License Key पाएँ#

Google Play को खरीदारियों की सुरक्षित पुष्टि के लिए RSA public key चाहिए।

Play Console में:

  • ‘Monetization’ > ‘Monetization setup’ पर जाएँ
  • ‘Licensing’ के नीचे ‘Base64-encoded RSA public key’ कॉपी करें
  • ⚠️ ज़रूरी: इसे आपको बाद में Generator में पेस्ट करना होगा।
Example Key Format:
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...

3WebToApk Generator में सेटअप#

जनरेटर ऐप में:

  1. चरण 3 में ‘Remove Ads (IAP)’ टॉगल चालू करें
  2. ठीक वही ‘Product ID’ डालें जो आपने चरण 1 में बनाया था
  3. चरण 2 से अपनी ‘Base64 License Key’ पेस्ट करें
  4. अपना APK/AAB बनाएँ

एक से ज़्यादा विज्ञापन-मुक्त प्लान दे रहे हैं (जैसे मासिक सब्सक्रिप्शन और लाइफ़टाइम एक बार का अनलॉक)? हर Product ID को इसी एक फ़ील्ड में कॉमा से अलग करके डालें - इनमें से किसी का भी मालिक होने पर विज्ञापन हट जाते हैं। सूची का पहला ID वही है जिसे बिल्ट-इन ‘Remove Ads’ मेन्यू आइटम खरीदता है, इसलिए अपना डिफ़ॉल्ट प्लान पहले रखें।

Product ID: remove_ads_premium

Two plans:
remove_ads_monthly,remove_ads_lifetime

4लाइसेंस टेस्टर सेट करें#

असली पैसे खर्च किए बिना खरीदारी टेस्ट करने के लिए:

  • Play Console में ‘Setup’ > ‘License testing’ पर जाएँ
  • अपना Gmail पता ‘License testers’ सूची में जोड़ें
  • पक्का करें कि ‘License response’ ‘RESPOND_NORMALLY’ पर सेट है

5Internal या Closed Track में टेस्ट करें#

इन-ऐप खरीदारी सिर्फ़ तब काम करती है जब ऐप Play Store (Testing tracks) से इंस्टॉल हो:

  1. बनाया हुआ AAB ‘Internal Testing’ या ‘Closed Testing’ ट्रैक पर अपलोड करें
  2. टेस्टर join लिंक अपने टेस्ट अकाउंट्स के साथ साझा करें
  3. ऐप Play Store से डाउनलोड करें
  4. फ़्लो टेस्ट करने के लिए अपने ऐप में ‘Remove Ads’ बटन दबाएँ!
Note: Purchases will show a 'Test instrument, always approves' message for license testers.

कोड रेसिपी

ऊपर के चरण बुनियादी बातें बताते हैं - चार लेबल, पूरा सादा पेज, React की एक लाइन। ये रेसिपी वह बताती हैं जो वे नहीं बतातीं: कई प्लान जो एक-दूसरे की जगह लेते हैं, खत्म होने वाले सब्सक्रिप्शन, फ़्रेमवर्क हुक, पूरा कच्चा ब्रिज, और रेफ़रेंस टेबल।

एक नियम#

खरीदारी सिर्फ़ तब दी जाती है जब Google पुष्टि करे। बटन दबाने पर नहीं, शीट खुलने पर नहीं, यूज़र के आपके पेज पर लौटने पर नहीं। नीचे सब कुछ उसी एक नियम को कहने का अलग तरीका है।

रनटाइम इसे पहले से लागू करता है: AppMintIAP लॉन्च पर, हर खरीदारी पर, हर रिफ़ंड पर और ऐप दोबारा खुलने पर हर बार ब्रिज से ओनरशिप फिर पढ़ता है। ओनरशिप रीइंस्टॉल, नए फ़ोन और ऑफ़लाइन होने पर भी बनी रहती है, क्योंकि वह आपके पेज में नहीं, खरीदार के Google अकाउंट में रहती है।

क्लिक हैंडलर में कभी अनलॉक न करें, और हक़ को कभी सिर्फ़ localStorage में न रखें। ब्राउज़र स्टोर साफ़ होने से पैसे देने वाले ग्राहक की ख़रीदी चीज़ नहीं जानी चाहिए - और कॉपी की गई localStorage वैल्यू से वह मुफ़्त में नहीं मिलनी चाहिए।

रेसिपी 1 - लाइव कीमतों वाली प्राइसिंग टेबल#

तीन प्लान जहाँ किसी एक को खरीदने से बाकी बदल जाते हैं। group वही है जो उन्हें विकल्प बनाता है: इसके बिना वे असंबंधित प्रोडक्ट हैं और लाइफ़टाइम प्लान खरीदने के बाद भी मासिक बटन वहाँ बैठा रहेगा।

तीन विकल्प, एक ग्रुप
<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>

जो प्रोडक्ट id Google नहीं जानता - अभी बना नहीं, चालू नहीं, या ऐप Play Store से इंस्टॉल नहीं हुआ - उसे बस बिना कीमत के छोड़ दिया जाता है। उसका बटन जानबूझकर दबने लायक रहता है: दबाने पर कुछ न करने की जगह असली वजह बताता है।

रेसिपी 2 - React#

onChange तुरंत मौजूदा स्थिति के साथ चलता है और हर बदलाव पर फिर चलता है, और अपना unsubscribe फ़ंक्शन लौटाता है - इसलिए यह ठीक वही रूप है जो useEffect को चाहिए।

hooks/useIap.js
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>
  );
}

बटन buy() कॉल करता है; वह ख़ुद कुछ अनलॉक नहीं करता। अनलॉक इसलिए होता है कि Google पुष्टि करता है, onChange चलता है, owns('pro_unlock') true हो जाता है और कॉम्पोनेंट फिर से रेंडर होता है। यह वही नियम है, बस React में कहा गया।

रेसिपी 3 - Vue#

यही गेट Vue 3 में
<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>

रेसिपी 4 - सब्सक्रिप्शन#

सब्सक्रिप्शन ठीक एक बार वाले प्रोडक्ट की तरह खरीदा जाता है; फ़र्क यह है कि वह खत्म हो सकता है। type बताता है कि प्रोडक्ट किस तरह का है और period बिलिंग अंतराल को ISO-8601 अवधि के रूप में देता है - P1M मासिक, P1Y सालाना, P1W साप्ताहिक।

मासिक पास जो अपने आप खत्म हो जाता है
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.

आपको पोल नहीं करना है और समाप्ति तिथि की भी ज़रूरत नहीं है। Play हर लॉन्च पर खरीदे हुए की सूची बहाल करता है, इसलिए खत्म हुआ सब्सक्रिप्शन अपने आप दिखना बंद हो जाता है ownedIds.

Play Console में सब्सक्रिप्शन और उसके बेस प्लान, दोनों को चालू करें। जिस सब्सक्रिप्शन का बेस प्लान चालू नहीं है, वह कोई जानकारी नहीं लौटाता, और बटन “not available on Google Play yet” बताएगा।

रेसिपी 5 - कच्चा ब्रिज, कोई हेल्पर नहीं#

ऊपर की हर चीज़ छह मेथड और पाँच इवेंट पर बनी है। अगर आप UI पूरी तरह ख़ुद संभालना चाहते हैं, तो इन्हें सीधे इस्तेमाल करें।

पूरी सतह, एक फ़ाइल में
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 जानबूझकर पहले से खरीदी चीज़ के लिए दोबारा चलता है। unlockPro() ऐसे लिखें कि उसे दो बार चलाना नुकसान न करे और आपको यह ट्रैक न करना पड़े कि वह पहले चल चुका है या नहीं।

रेसिपी 6 - ऐप के बाहर ईमानदारी से कम फ़ीचर दिखाएँ#

आमतौर पर वही पेज सामान्य ब्राउज़र में भी खुलना ज़रूरी होता है - डेवलपमेंट के दौरान, या इसलिए कि आप उसे वेब पर भी प्रकाशित करते हैं। वहाँ window.WebToApk मौजूद नहीं होता और कोई खरीदारी नहीं हो सकती। ऐसे बटन की जगह जो कुछ नहीं करता, यह बात साफ़ बता दें।

एक जाँच, सबसे ऊपर
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;
}

रेफ़रेंस#

वह सब कुछ जो पेज कॉल कर सकता है, सुन सकता है, या मार्क कर सकता है।

window.AppMintIAP - रनटाइम द्वारा डाला गया हेल्पर
कॉलक्या करता है
AppMintIAP.availableब्राउज़र में False, या जब इस बिल्ड के लिए इन-ऐप खरीदारी चालू नहीं की गई थी। कुछ भी बिक्री के लिए दिखाने से पहले इसे जाँचें।
AppMintIAP.sell(id, button, opts)एक प्रोडक्ट को एक बटन से जोड़ें। opts: label, owned, group, show, hide, labelEl। हर प्रोडक्ट के लिए एक बार कॉल करें।
AppMintIAP.start(config)कई एक साथ जोड़ें: {plans, show, hide, container, toast, hideOtherPlans}। इसका इस्तेमाल तब करें जब आप अपना ख़ुद का टोस्ट फ़ंक्शन चाहते हों, या एक के खरीदे जाने के बाद ग्रुप के बाकी प्लान दिखाते रखना चाहते हों (hideOtherPlans: false).
AppMintIAP.onChange(fn)तुरंत मौजूदा स्थिति के साथ चलता है, फिर हर बदलाव पर। एक unsubscribe फ़ंक्शन लौटाता है।
AppMintIAP.state()वही ऑब्जेक्ट, एक बार: {available, adFree, owned, ownedIds, products}.
AppMintIAP.isOwned(id)True/false, तुरंत, ऑफ़लाइन।
AppMintIAP.owned()हर खरीदे गए प्रोडक्ट id का ऐरे।
AppMintIAP.buy(id)Google की खरीदारी शीट खोलता है। ख़ुद कुछ अनलॉक नहीं करता।
AppMintIAP.refresh()पिछले पास के बाद जुड़े data-iap-buy बटनों को फिर से स्कैन करें। इसकी ज़रूरत कम पड़ती है - रनटाइम DOM पर नज़र रखता है।
window.WebToApk - नीचे का ब्रिज
कॉललौटाता है
purchase(productId)कुछ नहीं। शीट खोलता है; नतीजा इवेंट के रूप में आता है।
getProducts(requestId, idsJson)कुछ नहीं। जवाब appmint:products के रूप में आता है, जिसमें यही होता है requestId.
getOwnedProducts()खरीदे हुए प्रोडक्ट ids का JSON ऐरे, स्ट्रिंग के रूप में।
isOwned(productId)Boolean - एक बार वाला प्रोडक्ट खरीदा है, या सब्सक्रिप्शन अभी चालू है।
isPremium()Boolean - ख़ास तौर पर “remove ads” का हक़।
startRemoveAdsPurchase()कुछ नहीं। विज़ार्ड के “Remove Ads” फ़ील्ड के पहले id के लिए शीट खोलता है; वहाँ लिखे किसी भी id का मालिक होने पर विज्ञापन हट जाते हैं। नतीजा आता है appmint:premium.
इवेंट
इवेंटdetailकब
appmint:purchase{productId, owned}Google ने पुष्टि की। अनलॉक करने की अकेली जगह। पहले से खरीदी चीज़ के लिए फिर चलता है।
appmint:purchase-failed{productId, reason}शीट कभी खुली नहीं, प्रक्रिया में त्रुटि आई, या भुगतान लंबित है। सामान्य कैंसल पर कभी नहीं चलता।
appmint:products{requestId, products[]}getProducts() कॉल का जवाब। जिन id को Play नहीं जानता, वे बस गायब रहते हैं।
appmint:premium{premium}विज्ञापन-मुक्त हक़ बदल गया - खरीदारी, लॉन्च पर रीस्टोर, रिफ़ंड या समाप्ति।
appmint:owned-changed{products[]}लॉन्च पर रीस्टोर पूरा हुआ, या रिफ़ंड से कुछ चला गया। हर खरीदा हुआ id साथ लाता है।
HTML एट्रिब्यूट
एट्रिब्यूटकिस परक्या करता है
data-iap-buy="id"बटनउसे वह प्रोडक्ट खरीदने वाला बनाता है।
data-iap-label="… {price}"बटनलेबल टेम्पलेट। {price} Google की लाइव स्थानीय कीमत बन जाता है।
data-iap-owned="…"बटनप्रोडक्ट खरीदे जाने के बाद का लेबल।
data-iap-group="name"बटनप्लान्स को एक-दूसरे का विकल्प के रूप में मार्क करता है।
data-iap-show="id|group"कुछ भीसिर्फ़ तब दिखता है जब वह खरीदा जा चुका हो।
data-iap-hide="id|group"कुछ भीवह खरीदा जाते ही छिप जाता है।
data-iap-textबटन के अंदरवह एलिमेंट जिसके टेक्स्ट को लेबल मिलता है, जब बटन के अंदर टेक्स्ट से ज़्यादा कुछ हो।
data-iap-containerरैपरसिर्फ़ ऐप के अंदर दिखता है; ब्राउज़र में छिपा रहता है, जहाँ खरीदने को कुछ नहीं है। पूरा ऑफ़र इसमें लपेटें।
data-iap-state="owned"रनटाइम द्वारा सेटखरीदे हुए प्रोडक्ट के बटन पर, ताकि आप उसे स्टाइल कर सकें।
appmint:purchase-failed पर असफलता की वजहें
reasonअसल में क्या हुआ
not_foundGoogle इस प्रोडक्ट id को नहीं जानता - यह बनाया नहीं गया, चालू नहीं किया गया, या ऐप Play Store से इंस्टॉल नहीं हुआ था।
billing_errorPlay भुगतान शुरू नहीं कर सका। आमतौर पर अस्थायी; खरीदार को फिर कोशिश करनी चाहिए।
pendingधीमा भुगतान तरीका (कैश, बैंक ट्रांसफ़र)। Google के पुष्टि करते ही यह अपने आप अनलॉक हो जाता है - इसे असफलता न मानें।
disabledइस बिल्ड के लिए इन-ऐप खरीदारी चालू नहीं की गई थी।
unknownबाकी सब कुछ।

शिप करने से पहले#

  1. आपके कोड का हर प्रोडक्ट id Play Console में मौजूद है और चालू है - सब्सक्रिप्शन के लिए बेस प्लान भी।
  2. बिल्ड में वही id होते हैं, और Monetization setup से लिया Base64 RSA लाइसेंस कुंजी।
  3. आपका Gmail पता Setup → License testing में है, और रिस्पॉन्स RESPOND_NORMALLY पर सेट है।
  4. आपने AAB को Internal या Closed testing पर अपलोड किया और उसे Play Store लिंक से इंस्टॉल किया - साइडलोड किया APK कभी कुछ खरीद नहीं सकता।
  5. आपने हर प्रोडक्ट लाइसेंस टेस्टर के रूप में खरीदा और “Test instrument, always approves” देखा।
  6. आपने अनइंस्टॉल करके दोबारा इंस्टॉल किया, और खरीदारी अपने आप वापस आ गई।
  7. आपने नेटवर्क बंद किया और पेड कंटेंट अब भी अनलॉक है।
  8. आपने Play Console में एक टेस्ट खरीदारी रिफ़ंड की और ऐप को फिर से लॉक होते देखा।

गलतियाँ जो पैसे का नुकसान कराती हैं#

  • क्लिक हैंडलर में अनलॉक करना। जो भी शीट खोलकर पीछे हट जाता है, उसे प्रोडक्ट मुफ़्त मिल जाता है।
  • हक़ को सिर्फ़ localStorage में रखना। साइट डेटा साफ़ करने से ग्राहक की चुकाई हुई चीज़ चली जाती है; वैल्यू कॉपी करने से वह मुफ़्त में मिल जाती है।
  • कीमत अपने HTML में हार्ड-कोड करना। वह हर दूसरे देश में गलत होगी और जिस दिन आप बदलेंगे उसी दिन पुरानी हो जाएगी। {price} या इस्तेमाल करें products[id].price.
  • Play बिलिंग से भौतिक सामान या असली दुनिया की सेवा बेचना। Google ठीक उल्टा माँगता है: इनके लिए सामान्य पेमेंट प्रोसेसर इस्तेमाल करना होगा। Stripe गाइड इसी के लिए है।
  • साइडलोड किए APK पर टेस्ट करना। इन-ऐप खरीदारी तभी काम करती है जब ऐप Play Store से इंस्टॉल हुआ हो। आप जो भी लिखें, इसे नहीं बदल सकता।
  • pending को असफलता मानना। खरीदार ने धीमे तरीके से भुगतान किया; Google के पुष्टि करते ही यह अपने आप अनलॉक हो जाता है।

यह पेज ऐप के अंदर की इंटीग्रेशन गाइड से बनाया गया है, इसलिए इसमें वही लिखा है जो मौजूदा बिल्ड में है। इसे Markdown में पढ़ें.

2026-10-03 को शिप की गई गाइड से मिलान किया गया।