1. परिचय
किसी Flutter ऐप्लिकेशन में इन-ऐप्लिकेशन खरीदारी की सुविधा जोड़ने के लिए, App Store और Play Store को सही तरीके से सेट अप करना ज़रूरी है. साथ ही, खरीदारी की पुष्टि करना और ज़रूरी अनुमतियां देना भी ज़रूरी है. जैसे, सदस्यता के फ़ायदे.
इस कोडलैब में, आपको दिए गए ऐप्लिकेशन में तीन तरह की इन-ऐप्लिकेशन खरीदारी की सुविधाएं जोड़नी होंगी. साथ ही, Firebase के साथ Dart बैकएंड का इस्तेमाल करके, इन खरीदारी की पुष्टि करनी होगी. Dash Clicker ऐप्लिकेशन में एक ऐसा गेम शामिल है जिसमें डैश मैस्कॉट को मुद्रा के तौर पर इस्तेमाल किया जाता है. आपको खरीदारी के ये विकल्प जोड़ने होंगे:
- एक बार में 2,000 डैश खरीदने का विकल्प. इसे बार-बार इस्तेमाल किया जा सकता है.
- डैश की पुरानी स्टाइल को मॉडर्न स्टाइल में बदलने के लिए, एक बार अपग्रेड खरीदना होगा.
- यह एक ऐसी सदस्यता है जिसमें अपने-आप जनरेट होने वाले क्लिक की संख्या दोगुनी हो जाती है.
पहली बार खरीदारी करने पर, उपयोगकर्ता को सीधे तौर पर 2,000 डैश का फ़ायदा मिलता है. ये सीधे तौर पर उपयोगकर्ता के लिए उपलब्ध होते हैं और इन्हें कई बार खरीदा जा सकता है. इसे इस्तेमाल की जा सकने वाली चीज़ कहा जाता है, क्योंकि इसका इस्तेमाल सीधे तौर पर किया जाता है और इसे कई बार इस्तेमाल किया जा सकता है.
दूसरे विकल्प से, डैश को ज़्यादा बेहतर डैश में अपग्रेड किया जाता है. इसे सिर्फ़ एक बार खरीदना होता है और यह हमेशा के लिए उपलब्ध होता है. इस तरह की खरीदारी को नॉन-कंज्यूमेबल कहा जाता है, क्योंकि इसे ऐप्लिकेशन में इस्तेमाल नहीं किया जा सकता. हालांकि, यह हमेशा के लिए मान्य होती है.
तीसरा और आखिरी विकल्प, सदस्यता है. सदस्यता चालू रहने पर, उपयोगकर्ता को डैश तेज़ी से मिलेंगे. हालांकि, सदस्यता के लिए पेमेंट बंद करने पर, उसे मिलने वाले फ़ायदे भी खत्म हो जाएंगे.
बैकएंड सेवा (यह भी आपके लिए उपलब्ध कराई गई है) एक Dart ऐप्लिकेशन के तौर पर काम करती है. यह पुष्टि करती है कि खरीदारी की गई है और उन्हें Firestore का इस्तेमाल करके सेव करती है. Firestore का इस्तेमाल, प्रोसेस को आसान बनाने के लिए किया जाता है. हालांकि, प्रोडक्शन ऐप्लिकेशन में किसी भी तरह की बैकएंड सेवा का इस्तेमाल किया जा सकता है.

आपको क्या बनाना है
- आपको ऐप्लिकेशन को इस तरह से बढ़ाना होगा कि उसमें खपत की जा सकने वाली चीज़ों की खरीदारी और सदस्यताएं काम करें.
- खरीदे गए आइटम की पुष्टि करने और उन्हें सेव करने के लिए, आपको Dart बैकएंड ऐप्लिकेशन को भी बढ़ाना होगा.
आपको क्या सीखने को मिलेगा
- खरीदे जा सकने वाले प्रॉडक्ट के साथ App Store और Play Store को कॉन्फ़िगर करने का तरीका.
- खरीदारी की पुष्टि करने और उन्हें Firestore में सेव करने के लिए, स्टोर से कैसे संपर्क करें.
- अपने ऐप्लिकेशन में की गई खरीदारी को मैनेज करने का तरीका.
आपको किन चीज़ों की ज़रूरत होगी
- Android Studio
- Xcode (iOS डेवलपमेंट के लिए)
- Flutter SDK
2. डेवलपमेंट एनवायरमेंट सेट अप करना
इस कोडलैब को शुरू करने के लिए, कोड डाउनलोड करें. इसके बाद, iOS के लिए बंडल आईडी और Android के लिए पैकेज का नाम बदलें.
कोड डाउनलोड करना
कमांड लाइन से GitHub रिपॉज़िटरी को क्लोन करने के लिए, इस कमांड का इस्तेमाल करें:
git clone https://github.com/flutter/codelabs.git flutter-codelabs
इसके अलावा, अगर आपने GitHub का cli टूल इंस्टॉल किया है, तो इस कमांड का इस्तेमाल करें:
gh repo clone flutter/codelabs flutter-codelabs
सैंपल कोड को flutter-codelabs डायरेक्ट्री में क्लोन किया जाता है. इसमें कोडलैब के कलेक्शन का कोड होता है. इस कोडलैब का कोड flutter-codelabs/in_app_purchases में है.
flutter-codelabs/in_app_purchases में मौजूद डायरेक्ट्री स्ट्रक्चर में, हर चरण के आखिर में आपको कहां होना चाहिए, इसके स्नैपशॉट की सीरीज़ होती है. स्टार्टर कोड, चरण 0 में है. इसलिए, इस पर जाने के लिए यह तरीका अपनाएं:
cd flutter-codelabs/in_app_purchases/step_00
अगर आपको किसी चरण को छोड़ना है या यह देखना है कि किसी चरण के बाद कोई चीज़ कैसी दिखनी चाहिए, तो उस चरण के नाम वाली डायरेक्ट्री में देखें. आखिरी चरण का कोड, complete फ़ोल्डर में मौजूद है.
स्टार्टर प्रोजेक्ट सेट अप करना
अपने पसंदीदा IDE में, step_00/app से स्टार्टर प्रोजेक्ट खोलें. हमने स्क्रीनशॉट के लिए Android Studio का इस्तेमाल किया है. हालांकि, Visual Studio Code भी एक बेहतरीन विकल्प है. किसी भी एडिटर का इस्तेमाल करके, पक्का करें कि Dart और Flutter के नए प्लगिन इंस्टॉल हों.
आपको जिन ऐप्लिकेशन को बनाना है उन्हें App Store और Play Store से कम्यूनिकेट करना होगा. इससे उन्हें यह पता चलेगा कि कौनसे प्रॉडक्ट उपलब्ध हैं और उनकी कीमत क्या है. हर ऐप्लिकेशन की पहचान एक यूनीक आईडी से होती है. iOS App Store के लिए इसे बंडल आइडेंटिफ़ायर और Android Play Store के लिए इसे ऐप्लिकेशन आईडी कहा जाता है. इन आइडेंटिफ़ायर को आम तौर पर, रिवर्स डोमेन नेम नोटेशन का इस्तेमाल करके बनाया जाता है. उदाहरण के लिए, flutter.dev के लिए इन ऐप्लिकेशन खरीदारी की सुविधा वाला ऐप्लिकेशन बनाते समय, आपको dev.flutter.inapppurchase का इस्तेमाल करना होगा. अपने ऐप्लिकेशन के लिए कोई आइडेंटिफ़ायर सोचें. अब आपको उसे प्रोजेक्ट की सेटिंग में सेट करना है.
सबसे पहले, iOS के लिए बंडल आइडेंटिफ़ायर सेट अप करें. इसके लिए, Xcode ऐप्लिकेशन में Runner.xcworkspace फ़ाइल खोलें.

Xcode के फ़ोल्डर स्ट्रक्चर में, Runner project सबसे ऊपर होता है. साथ ही, Flutter, Runner, और Products टारगेट, Runner project के नीचे होते हैं. प्रोजेक्ट सेटिंग में बदलाव करने के लिए, Runner पर दो बार क्लिक करें. इसके बाद, Signing & Capabilities पर क्लिक करें. अपनी टीम सेट करने के लिए, टीम फ़ील्ड में वह बंडल आइडेंटिफ़ायर डालें जिसे आपने अभी चुना है.

अब Xcode को बंद करके, Android Studio पर वापस जाएं. इसके बाद, Android के लिए कॉन्फ़िगरेशन पूरा करें. इसके लिए, android/app, में जाकर build.gradle.kts फ़ाइल खोलें. इसके बाद, अपने applicationId को ऐप्लिकेशन आईडी में बदलें. यह आईडी, iOS बंडल आइडेंटिफ़ायर के जैसा ही होता है. यह आईडी, नीचे दिए गए स्क्रीनशॉट में लाइन 24 पर मौजूद है. ध्यान दें कि iOS और Android स्टोर के आईडी एक जैसे होने ज़रूरी नहीं हैं. हालांकि, इन्हें एक जैसा रखने से गड़बड़ी होने की संभावना कम हो जाती है. इसलिए, इस कोडलैब में हम एक जैसे आइडेंटिफ़ायर का इस्तेमाल करेंगे.

3. प्लगिन इंस्टॉल करना
इस कोडलैब के इस हिस्से में, आपको in_app_purchase प्लगिन इंस्टॉल करना होगा.
pubspec में डिपेंडेंसी जोड़ना
अपने प्रोजेक्ट की डिपेंडेंसी में in_app_purchase जोड़कर, pubspec में in_app_purchase जोड़ें:
$ cd app $ flutter pub add in_app_purchase dev:in_app_purchase_platform_interface Resolving dependencies... Downloading packages... characters 1.4.0 (1.4.1 available) flutter_lints 5.0.0 (6.0.0 available) + in_app_purchase 3.2.3 + in_app_purchase_android 0.4.0+3 + in_app_purchase_platform_interface 1.4.0 + in_app_purchase_storekit 0.4.4 + json_annotation 4.9.0 lints 5.1.1 (6.0.0 available) material_color_utilities 0.11.1 (0.13.0 available) meta 1.16.0 (1.17.0 available) provider 6.1.5 (6.1.5+1 available) test_api 0.7.6 (0.7.7 available) Changed 5 dependencies! 7 packages have newer versions incompatible with dependency constraints. Try `flutter pub outdated` for more information.
अपना pubspec.yaml खोलें. इसके बाद, पुष्टि करें कि अब आपको dependencies में in_app_purchase और dev_dependencies में in_app_purchase_platform_interface दिख रहा है.
pubspec.yaml
dependencies:
flutter:
sdk: flutter
cloud_firestore: ^6.0.0
cupertino_icons: ^1.0.8
firebase_auth: ^6.0.1
firebase_core: ^4.0.0
google_sign_in: ^7.1.1
http: ^1.5.0
intl: ^0.20.2
provider: ^6.1.5
logging: ^1.3.0
in_app_purchase: ^3.2.3
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^5.0.0
in_app_purchase_platform_interface: ^1.4.0
4. App Store सेट अप करना
iOS पर इन-ऐप्लिकेशन खरीदारी की सुविधा सेट अप करने और उसे आज़माने के लिए, आपको App Store में एक नया ऐप्लिकेशन बनाना होगा. साथ ही, वहां खरीदारी के लिए उपलब्ध प्रॉडक्ट बनाने होंगे. आपको कुछ भी पब्लिश करने या ऐप्लिकेशन को Apple को समीक्षा के लिए भेजने की ज़रूरत नहीं है. इसके लिए, आपके पास डेवलपर खाता होना चाहिए. अगर आपके पास Apple डेवलपर प्रोग्राम का खाता नहीं है, तो Apple डेवलपर प्रोग्राम में रजिस्टर करें.
पैसे चुकाकर डाउनलोड किए जाने वाले ऐप्लिकेशन के समझौते
इन-ऐप्लिकेशन खरीदारी की सुविधा का इस्तेमाल करने के लिए, आपके पास App Store Connect में पैसे चुकाकर डाउनलोड किए जाने वाले ऐप्लिकेशन के लिए, चालू कानूनी समझौता होना चाहिए. https://appstoreconnect.apple.com/ पर जाएं और Agreements, Tax, and Banking पर क्लिक करें.

आपको यहां मुफ़्त और पैसे चुकाकर इस्तेमाल किए जाने वाले ऐप्लिकेशन के लिए कानूनी समझौते दिखेंगे. मुफ़्त ऐप्लिकेशन का स्टेटस 'चालू' होना चाहिए. साथ ही, पैसे चुकाकर डाउनलोड किए जाने वाले ऐप्लिकेशन का स्टेटस 'नया' होना चाहिए. पक्का करें कि आपने शर्तें पढ़ ली हों, उन्हें स्वीकार कर लिया हो, और सभी ज़रूरी जानकारी डाल दी हो.

सब कुछ सही तरीके से सेट होने पर, पैसे चुकाकर डाउनलोड किए जाने वाले ऐप्लिकेशन का स्टेटस 'चालू है' के तौर पर दिखेगा. यह बेहद ज़रूरी है, क्योंकि चालू समझौते के बिना, इन-ऐप्लिकेशन खरीदारी की सुविधा को आज़माया नहीं जा सकता.

ऐप्लिकेशन आईडी रजिस्टर करें
Apple Developer Portal में नया आइडेंटिफ़ायर बनाएं. developer.apple.com/account/resources/identifiers/list पर जाएं. इसके बाद, पहचानकर्ता हेडर के बगल में मौजूद "प्लस" आइकॉन पर क्लिक करें.

ऐप्लिकेशन के आईडी चुनना

ऐप्लिकेशन चुनें

कुछ ब्यौरा दें और बंडल आईडी को XCode में पहले से सेट की गई वैल्यू से मैच करने के लिए सेट करें.

नया ऐप्लिकेशन आईडी बनाने के बारे में ज़्यादा जानकारी के लिए, डेवलपर खाते से जुड़ी सहायता देखें.
नया ऐप्लिकेशन बनाना
App Store Connect में, अपने यूनीक बंडल आइडेंटिफ़ायर का इस्तेमाल करके नया ऐप्लिकेशन बनाएं.


नया ऐप्लिकेशन बनाने और कानूनी समझौतों को मैनेज करने के बारे में ज़्यादा जानकारी के लिए, App Store Connect की सहायता टीम से संपर्क करें.
इन-ऐप्लिकेशन खरीदारी की सुविधा को टेस्ट करने के लिए, आपको सैंडबॉक्स टेस्ट उपयोगकर्ता की ज़रूरत होगी. यह टेस्ट उपयोगकर्ता, iTunes से कनेक्ट नहीं होना चाहिए. इसका इस्तेमाल सिर्फ़ इन-ऐप्लिकेशन खरीदारी की जांच करने के लिए किया जाता है. ऐसे ईमेल पते का इस्तेमाल नहीं किया जा सकता जिसका इस्तेमाल पहले से ही किसी Apple खाते के लिए किया जा रहा हो. नया सैंडबॉक्स खाता बनाने या मौजूदा सैंडबॉक्स Apple आईडी मैनेज करने के लिए, उपयोगकर्ता और ऐक्सेस में जाकर सैंडबॉक्स पर जाएं.

अब अपने iPhone पर सैंडबॉक्स उपयोगकर्ता को सेट अप किया जा सकता है. इसके लिए, सेटिंग > डेवलपर > सैंडबॉक्स Apple खाता पर जाएं.

इन-ऐप्लिकेशन खरीदारी की सुविधा कॉन्फ़िगर करना
अब खरीदने के लिए उपलब्ध तीन आइटम कॉन्फ़िगर करें:
dash_consumable_2k: यह एक ऐसी खरीदारी है जिसे कई बार किया जा सकता है. इससे उपयोगकर्ता को हर खरीदारी पर 2, 000 डैश (ऐप्लिकेशन में इस्तेमाल होने वाली मुद्रा) मिलते हैं.dash_upgrade_3d: यह एक बार की जाने वाली "अपग्रेड" खरीदारी है. इससे उपयोगकर्ता को क्लिक करने के लिए, कॉस्मेटिक तौर पर अलग डैश मिलता है.dash_subscription_doubler: यह एक ऐसी सदस्यता है जो उपयोगकर्ता को सदस्यता की अवधि के दौरान, हर क्लिक पर दो गुना डैश देती है.

इन-ऐप्लिकेशन खरीदारी पर जाएं.
यहां दिए गए आईडी का इस्तेमाल करके, इन-ऐप्लिकेशन खरीदारी की सुविधा बनाएं:
dash_consumable_2kको इस्तेमाल की जा सकने वाली चीज़ के तौर पर सेट अप करें. प्रॉडक्ट आईडी के तौर परdash_consumable_2kका इस्तेमाल करें. रेफ़रंस नाम का इस्तेमाल सिर्फ़ App Store Connect में किया जाता है. इसेdash consumable 2kपर सेट करें.
उपलब्धता सेट अप करें. प्रॉडक्ट, सैंडबॉक्स उपयोगकर्ता के देश में उपलब्ध होना चाहिए.
कीमत जोड़ें और उसे $1.99या अन्य मुद्रा में इसके बराबर पर सेट करें.
खरीदारी के लिए, अपनी स्थानीय भाषाएं जोड़ें. ब्यौरे के तौर पर 2000 dashes fly outका इस्तेमाल करके, खरीदारीSpring is in the airको कॉल करें.
समीक्षा का स्क्रीनशॉट जोड़ें. जब तक प्रॉडक्ट को समीक्षा के लिए नहीं भेजा जाता, तब तक कॉन्टेंट से कोई फ़र्क़ नहीं पड़ता. हालांकि, प्रॉडक्ट को "सबमिट करने के लिए तैयार" स्थिति में होना ज़रूरी है. यह तब ज़रूरी होता है, जब ऐप्लिकेशन App Store से प्रॉडक्ट फ़ेच करता है. 
dash_upgrade_3dको नॉन-कंज्यूमेबल के तौर पर सेट अप करें. प्रॉडक्ट आईडी के तौर परdash_upgrade_3dका इस्तेमाल करें. रेफ़रंस का नामdash upgrade 3dपर सेट करें. ब्यौरे के तौर परBrings your dash back to the futureका इस्तेमाल करके, खरीदारी3D Dashको कॉल करें. किराया$0.99पर सेट करें.dash_consumable_2kप्रॉडक्ट की तरह ही, उपलब्धता कॉन्फ़िगर करें और समीक्षा का स्क्रीनशॉट अपलोड करें.
dash_subscription_doublerको अपने-आप रिन्यू होने वाली सदस्यता के तौर पर सेट अप करें. सदस्यताओं के लिए, प्रोसेस थोड़ी अलग होती है. सबसे पहले, आपको सदस्यता ग्रुप बनाना होगा. जब एक ही ग्रुप में कई सदस्यताएं शामिल होती हैं, तो कोई उपयोगकर्ता एक समय में इनमें से सिर्फ़ एक सदस्यता ले सकता है. हालांकि, वह इन सदस्यताओं के बीच अपग्रेड या डाउनग्रेड कर सकता है. बस इस ग्रुपsubscriptionsको कॉल करो.
इसके बाद, सदस्यता ग्रुप के लिए स्थानीय भाषा जोड़ें.
इसके बाद, आपको सदस्यता बनानी होगी. रेफ़रंस नाम को dash subscription doublerऔर प्रॉडक्ट आईडी कोdash_subscription_doublerपर सेट करें.
इसके बाद, सदस्यता की अवधि के तौर पर एक हफ़्ता और स्थानीय भाषाएं चुनें. इस सदस्यता Jet Engineको नाम दें और इसके बारे में जानकारीDoubles your clicksदें. किराया$0.49पर सेट करें.dash_consumable_2kप्रॉडक्ट की तरह ही, उपलब्धता कॉन्फ़िगर करें और समीक्षा का स्क्रीनशॉट अपलोड करें.
अब आपको इन सूचियों में प्रॉडक्ट दिखने चाहिए:

5. Play Store सेट अप करना
App Store की तरह ही, आपको Play Store के लिए भी डेवलपर खाते की ज़रूरत होगी. अगर आपके पास अब तक कोई खाता नहीं है, तो खाता रजिस्टर करें.
नया ऐप्लिकेशन बनाना
Google Play Console में नया ऐप्लिकेशन बनाएं:
- Play Console खोलें.
- सभी ऐप्लिकेशन > ऐप्लिकेशन बनाएं चुनें.
- कोई डिफ़ॉल्ट भाषा चुनें और अपने ऐप्लिकेशन के लिए टाइटल जोड़ें. अपने ऐप्लिकेशन का वह नाम डालें जिसे Google Play पर दिखाना है. नाम को बाद में बदला जा सकता है.
- यह बताएं कि आपका ऐप्लिकेशन एक गेम है. हालांकि, इसे बाद में बदला जा सकता है.
- यह बताएं कि आपका ऐप्लिकेशन बिना शुल्क के उपलब्ध है या इसके लिए पैसे चुकाने होंगे.
- कॉन्टेंट से जुड़े दिशा-निर्देशों और अमेरिका के एक्सपोर्ट कानूनों की शर्तों को पूरा करें.
- ऐप्लिकेशन बनाएं को चुनें.
ऐप्लिकेशन बन जाने के बाद, डैशबोर्ड पर जाएं और ऐप्लिकेशन सेट अप करें सेक्शन में दिए गए सभी टास्क पूरे करें. यहां आपको अपने ऐप्लिकेशन के बारे में कुछ जानकारी देनी होती है. जैसे, कॉन्टेंट रेटिंग और स्क्रीनशॉट. 
ऐप्लिकेशन पर हस्ताक्षर करना
इन-ऐप्लिकेशन खरीदारी की सुविधा को टेस्ट करने के लिए, आपको Google Play पर कम से कम एक बिल्ड अपलोड करना होगा.
इसके लिए, आपको अपने रिलीज़ के लिए तैयार बिल्ड पर डीबग कुंजियों के अलावा किसी और कुंजी से साइन करना होगा.
कीस्टोर बनाना
अगर आपके पास पहले से कोई कीस्टोर है, तो अगले चरण पर जाएं. अगर ऐसा नहीं है, तो कमांड लाइन पर यहां दिया गया निर्देश चलाकर एक बनाएं.
Mac/Linux पर, इस कमांड का इस्तेमाल करें:
keytool -genkey -v -keystore ~/key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key
Windows पर, यह कमांड इस्तेमाल करें:
keytool -genkey -v -keystore c:\Users\USER_NAME\key.jks -storetype JKS -keyalg RSA -keysize 2048 -validity 10000 -alias key
इस कमांड से, key.jks फ़ाइल को आपकी होम डायरेक्ट्री में सेव किया जाता है. अगर आपको फ़ाइल को किसी दूसरी जगह पर सेव करना है, तो -keystore पैरामीटर में पास किए गए आर्ग्युमेंट को बदलें.
keystore
फ़ाइल निजी है; इसे सार्वजनिक सोर्स कंट्रोल में शामिल न करें!
ऐप्लिकेशन से कीस्टोर को रेफ़रंस करें
<your app dir>/android/key.properties नाम की एक फ़ाइल बनाएं. इसमें अपने कीस्टोर का रेफ़रंस शामिल करें:
storePassword=<password from previous step>
keyPassword=<password from previous step>
keyAlias=key
storeFile=<location of the key store file, such as /Users/<user name>/key.jks>
Gradle में साइन इन करने की सुविधा कॉन्फ़िगर करना
<your app dir>/android/app/build.gradle.kts फ़ाइल में बदलाव करके, अपने ऐप्लिकेशन के लिए साइनिंग की सुविधा कॉन्फ़िगर करें.
android ब्लॉक से पहले, अपनी प्रॉपर्टी फ़ाइल में कीस्टोर की जानकारी जोड़ें:
import java.util.Properties
import java.io.FileInputStream
plugins {
// omitted
}
val keystoreProperties = Properties()
val keystorePropertiesFile = rootProject.file("key.properties")
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
}
android {
// omitted
}
key.properties फ़ाइल को keystoreProperties ऑब्जेक्ट में लोड करें.
buildTypes ब्लॉक को इस पर अपडेट करें:
buildTypes {
release {
signingConfig = signingConfigs.getByName("release")
}
}
साइनिंग कॉन्फ़िगरेशन की जानकारी के साथ, अपने मॉड्यूल की build.gradle.kts फ़ाइल में signingConfigs ब्लॉक को कॉन्फ़िगर करें:
signingConfigs {
create("release") {
keyAlias = keystoreProperties["keyAlias"] as String
keyPassword = keystoreProperties["keyPassword"] as String
storeFile = keystoreProperties["storeFile"]?.let { file(it) }
storePassword = keystoreProperties["storePassword"] as String
}
}
buildTypes {
release {
signingConfig = signingConfigs.getByName("release")
}
}
अब आपके ऐप्लिकेशन की रिलीज़ बिल्ड पर अपने-आप हस्ताक्षर हो जाएंगे.
अपने ऐप्लिकेशन पर हस्ताक्षर करने के बारे में ज़्यादा जानने के लिए, developer.android.com पर अपने ऐप्लिकेशन पर हस्ताक्षर करना लेख पढ़ें.
अपना पहला बिल्ड अपलोड करना
साइनिंग के लिए ऐप्लिकेशन कॉन्फ़िगर होने के बाद, आपको यह कमांड चलाकर अपना ऐप्लिकेशन बनाना चाहिए:
flutter build appbundle
यह निर्देश डिफ़ॉल्ट रूप से रिलीज़ के लिए तैयार बिल्ड जनरेट करता है. इसका आउटपुट <your app dir>/build/app/outputs/bundle/release/ पर देखा जा सकता है
Google Play Console के डैशबोर्ड में, टेस्ट और रिलीज़ > टेस्टिंग > क्लोज़्ड टेस्टिंग पर जाएं. इसके बाद, क्लोज़्ड टेस्टिंग के लिए नई रिलीज़ बनाएं.
इसके बाद, app-release.aab ऐप्लिकेशन बंडल अपलोड करें. इसे बिल्ड कमांड से जनरेट किया गया था.
सेव करें पर क्लिक करें. इसके बाद, रिलीज़ की समीक्षा करें पर क्लिक करें.
आखिर में, क्लोज़्ड टेस्टिंग के लिए रिलीज़ को चालू करने के लिए, क्लोज़्ड टेस्टिंग के लिए रोलआउट शुरू करें पर क्लिक करें.
टेस्ट यूज़र सेट अप करना
इन-ऐप्लिकेशन खरीदारी की जांच करने के लिए, टेस्टर के Google खातों को Google Play Console में दो जगहों पर जोड़ना होगा:
- किसी खास टेस्ट ट्रैक (इंटरनल टेस्टिंग) के लिए
- लाइसेंस टेस्टर के तौर पर
सबसे पहले, टेस्टर को इंटरनल टेस्टिंग ट्रैक में जोड़ें. टेस्ट और रिलीज़ > टेस्टिंग > इंटरनल टेस्टिंग पर वापस जाएं और टेस्टर टैब पर क्लिक करें.

ईमेल सूची बनाएं पर क्लिक करके, नई ईमेल सूची बनाएं. सूची को कोई नाम दें. साथ ही, उन Google खातों के ईमेल पते जोड़ें जिन्हें ऐप्लिकेशन में खरीदारी की सुविधा की जांच करने का ऐक्सेस चाहिए.
इसके बाद, सूची के लिए चेकबॉक्स चुनें और बदलाव सेव करें पर क्लिक करें.
इसके बाद, लाइसेंस की जांच करने वाले लोगों के ईमेल पते जोड़ें:
- Google Play Console के सभी ऐप्लिकेशन व्यू पर वापस जाएं.
- सेटिंग > लाइसेंस टेस्टिंग पर जाएं.
- जांच करने वाले उन लोगों के ईमेल पते जोड़ें जिन्हें इन-ऐप्लिकेशन खरीदारी की जांच करनी है.
- लाइसेंस की स्थिति को
RESPOND_NORMALLYपर सेट करें. - बदलाव सेव करें पर क्लिक करें.

इन-ऐप्लिकेशन खरीदारी की सुविधा कॉन्फ़िगर करना
अब आपको उन आइटम को कॉन्फ़िगर करना होगा जिन्हें ऐप्लिकेशन में खरीदा जा सकता है.
App Store की तरह ही, आपको तीन अलग-अलग खरीदारी तय करनी होंगी:
dash_consumable_2k: यह एक ऐसी खरीदारी है जिसे कई बार किया जा सकता है. इससे उपयोगकर्ता को हर खरीदारी पर 2, 000 डैश (ऐप्लिकेशन में इस्तेमाल होने वाली मुद्रा) मिलते हैं.dash_upgrade_3d: यह एक बार खरीदा जा सकने वाला "अपग्रेड" है. इसे खरीदने के बाद, उपयोगकर्ता को क्लिक करने के लिए एक अलग तरह का डैश मिलता है.dash_subscription_doubler: यह एक ऐसी सदस्यता है जो उपयोगकर्ता को सदस्यता की अवधि के दौरान, हर क्लिक पर दो गुना डैश देती है.
सबसे पहले, इस्तेमाल किए जा सकने वाले और इस्तेमाल न किए जा सकने वाले प्रॉडक्ट जोड़ें.
- Google Play Console पर जाएं और अपना ऐप्लिकेशन चुनें.
- कमाई करें > प्रॉडक्ट > ऐप्लिकेशन में खरीदे जा सकने वाले प्रॉडक्ट पर जाएं.
- प्रॉडक्ट बनाएं
पर क्लिक करें - अपने प्रॉडक्ट की सभी ज़रूरी जानकारी डालें. पक्का करें कि प्रॉडक्ट आईडी, उस आईडी से मेल खाता हो जिसका आपको इस्तेमाल करना है.
- सेव करें पर क्लिक करें.
- चालू करें पर क्लिक करें.
- "अपग्रेड" के लिए की गई ऐसी खरीदारी के लिए भी यही प्रोसेस दोहराएं जिसे इस्तेमाल नहीं किया जा सकता.
इसके बाद, सदस्यता जोड़ें:
- Google Play Console पर जाएं और अपना ऐप्लिकेशन चुनें.
- कमाई करें > प्रॉडक्ट > सदस्यताएं पर जाएं.
- सदस्यता बनाएं
पर क्लिक करें - अपनी सदस्यता के लिए सभी ज़रूरी जानकारी डालें. पक्का करें कि प्रॉडक्ट आईडी, उस आईडी से मेल खाता हो जिसका आपको इस्तेमाल करना है.
- सेव करें पर क्लिक करें
अब आपकी खरीदारी, Play Console में सेट अप हो जानी चाहिए.
6. Firebase सेट अप करना
इस कोडलैब में, उपयोगकर्ताओं की खरीदारी की पुष्टि करने और उन्हें ट्रैक करने के लिए, बैकएंड सेवा का इस्तेमाल किया जाएगा.
बैकएंड सेवा का इस्तेमाल करने के कई फ़ायदे हैं:
- लेन-देन की पुष्टि सुरक्षित तरीके से की जा सकती है.
- ऐप्लिकेशन स्टोर से, बिलिंग इवेंट पर प्रतिक्रिया दी जा सकती है.
- डेटाबेस में खरीदारी को ट्रैक किया जा सकता है.
- उपयोगकर्ता, सिस्टम क्लॉक को पीछे करके आपके ऐप्लिकेशन को प्रीमियम सुविधाएं देने के लिए बेवकूफ़ नहीं बना पाएंगे.
बैकएंड सेवा को सेट अप करने के कई तरीके हैं. हालांकि, इस ट्यूटोरियल में आपको Google के Firebase का इस्तेमाल करके, क्लाउड फ़ंक्शन और Firestore का इस्तेमाल करना होगा.
इस कोडलैब में बैकएंड लिखने के बारे में नहीं बताया गया है. इसलिए, स्टार्टर कोड में पहले से ही एक Firebase प्रोजेक्ट शामिल है. यह प्रोजेक्ट, बुनियादी खरीदारी को मैनेज करता है, ताकि आप आसानी से शुरुआत कर सकें.
स्टार्टर ऐप्लिकेशन में Firebase प्लगिन भी शामिल होते हैं.
अब आपको अपना Firebase प्रोजेक्ट बनाना है. साथ ही, Firebase के लिए ऐप्लिकेशन और बैकएंड, दोनों को कॉन्फ़िगर करना है. इसके बाद, बैकएंड को डिप्लॉय करना है.
Firebase प्रोजेक्ट बनाना
Firebase कंसोल पर जाएं और एक नया Firebase प्रोजेक्ट बनाएं. इस उदाहरण के लिए, प्रोजेक्ट का नाम Dash Clicker रखें.
बैकएंड ऐप्लिकेशन में, खरीदारी को किसी खास उपयोगकर्ता से जोड़ा जाता है. इसलिए, आपको पुष्टि करने की ज़रूरत होती है. इसके लिए, Google से साइन इन करने की सुविधा के साथ Firebase के पुष्टि करने वाले मॉड्यूल का इस्तेमाल करें.
- Firebase डैशबोर्ड में, Authentication पर जाएं और अगर ज़रूरी हो, तो इसे चालू करें.
- साइन-इन का तरीका टैब पर जाएं और Google साइन-इन की सुविधा देने वाली कंपनी को चालू करें.

आपको Firebase के Firestore डेटाबेस का भी इस्तेमाल करना होगा. इसलिए, इसे भी चालू करें.

Cloud Firestore के नियमों को इस तरह सेट करें:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /purchases/{purchaseId} {
allow read: if request.auth &&!= null request.auth.uid == resource.data.userId
}
}
}
Flutter के लिए Firebase सेट अप करना
हमारा सुझाव है कि Flutter ऐप्लिकेशन पर Firebase इंस्टॉल करने के लिए, FlutterFire CLI का इस्तेमाल करें. सेटअप पेज पर दिए गए निर्देशों का पालन करें.
flutterfire configure कमांड चलाने के दौरान, वह प्रोजेक्ट चुनें जिसे आपने पिछले चरण में बनाया था.
$ flutterfire configure i Found 5 Firebase projects. ? Select a Firebase project to configure your Flutter application with › ❯ in-app-purchases-1234 (in-app-purchases-1234) other-flutter-codelab-1 (other-flutter-codelab-1) other-flutter-codelab-2 (other-flutter-codelab-2) other-flutter-codelab-3 (other-flutter-codelab-3) other-flutter-codelab-4 (other-flutter-codelab-4) <create a new project>
इसके बाद, दोनों प्लैटफ़ॉर्म चुनकर iOS और Android को चालू करें.
? Which platforms should your configuration support (use arrow keys & space to select)? › ✔ android ✔ ios macos web
जब आपसे firebase_options.dart को बदलने के बारे में पूछा जाए, तो हां चुनें.
? Generated FirebaseOptions file lib/firebase_options.dart already exists, do you want to override it? (y/n) › yes
Android के लिए Firebase सेट अप करना: अन्य चरण
Firebase डैशबोर्ड में, प्रोजेक्ट की खास जानकारी पर जाएं. इसके बाद,सेटिंग को चुनें और सामान्य टैब को चुनें.
नीचे की ओर स्क्रोल करके आपके ऐप्लिकेशन पर जाएं. इसके बाद, dashclicker (android) ऐप्लिकेशन को चुनें.

डीबग मोड में Google साइन-इन की सुविधा चालू करने के लिए, आपको अपने डीबग सर्टिफ़िकेट का SHA-1 हैश फ़िंगरप्रिंट देना होगा.
डीबग करने के लिए इस्तेमाल किए जाने वाले सर्टिफ़िकेट के हस्ताक्षर का हैश पाना
अपने Flutter ऐप्लिकेशन प्रोजेक्ट के रूट में, डायरेक्ट्री को android/ फ़ोल्डर में बदलें. इसके बाद, साइनिंग रिपोर्ट जनरेट करें.
cd android ./gradlew :app:signingReport
आपको साइनिंग की की एक बड़ी सूची दिखेगी. आपको डीबग सर्टिफ़िकेट का हैश चाहिए. इसलिए, ऐसे सर्टिफ़िकेट को ढूंढें जिसकी Variant और Config प्रॉपर्टी debug पर सेट हो. कीस्टोर, आपके होम फ़ोल्डर में .android/debug.keystore के नीचे मौजूद होता है.
> Task :app:signingReport
Variant: debug
Config: debug
Store: /<USER_HOME_FOLDER>/.android/debug.keystore
Alias: AndroidDebugKey
MD5: XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
SHA1: XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
SHA-256: XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Valid until: Tuesday, January 19, 2038
SHA-1 हैश को कॉपी करें और ऐप्लिकेशन सबमिट करने वाले मोडल डायलॉग में मौजूद आखिरी फ़ील्ड में इसे भरें.
आखिर में, ऐप्लिकेशन को अपडेट करने के लिए flutterfire configure कमांड को फिर से चलाएं, ताकि साइनिंग कॉन्फ़िगरेशन को शामिल किया जा सके.
$ flutterfire configure ? You have an existing `firebase.json` file and possibly already configured your project for Firebase. Would you prefer to reuse the valus in your existing `firebase.json` file to configure your project? (y/n) › yes ✔ You have an existing `firebase.json` file and possibly already configured your project for Firebase. Would you prefer to reuse the values in your existing `firebase.json` file to configure your project? · yes
iOS के लिए Firebase सेट अप करना: अन्य चरण
Xcode की मदद से ios/Runner.xcworkspace खोलें. इसके अलावा, अपनी पसंद के आईडीई का इस्तेमाल करके भी ऐसा किया जा सकता है.
VSCode में, ios/ फ़ोल्डर पर राइट क्लिक करें. इसके बाद, open in xcode पर क्लिक करें.
Android Studio में, ios/ फ़ोल्डर पर राइट क्लिक करें. इसके बाद, flutter पर क्लिक करें. इसके बाद, open iOS module in Xcode विकल्प पर क्लिक करें.
iOS पर Google साइन-इन की सुविधा चालू करने के लिए, अपनी बिल्ड plist फ़ाइलों में CFBundleURLTypes कॉन्फ़िगरेशन विकल्प जोड़ें. (ज़्यादा जानकारी के लिए, google_sign_in पैकेज के दस्तावेज़ देखें.) इस मामले में, फ़ाइल ios/Runner/Info.plist है.
की-वैल्यू पेयर पहले ही जोड़ा जा चुका है, लेकिन उनकी वैल्यू बदलनी होंगी:
GoogleService-Info.plistफ़ाइल सेREVERSED_CLIENT_IDकी वैल्यू पाएं. इसके लिए,<string>..</string>एलिमेंट का इस्तेमाल न करें.CFBundleURLTypesकुंजी के तहत, अपनीios/Runner/Info.plistफ़ाइल में मौजूद वैल्यू बदलें.
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<!-- TODO Replace this value: -->
<!-- Copied from GoogleService-Info.plist key REVERSED_CLIENT_ID -->
<string>com.googleusercontent.apps.REDACTED</string>
</array>
</dict>
</array>
अब Firebase सेटअप पूरा हो गया है.
7. खरीदारी के अपडेट सुनना
इस कोडलैब के इस हिस्से में, आपको प्रॉडक्ट खरीदने के लिए ऐप्लिकेशन तैयार करना होगा. इस प्रोसेस में, ऐप्लिकेशन शुरू होने के बाद खरीदारी से जुड़े अपडेट और गड़बड़ियों की जानकारी पाना शामिल है.
खरीदारी से जुड़े अपडेट सुनना
main.dart, में, MyHomePage विजेट ढूंढें. इसमें Scaffold है और इसमें दो पेज वाला BottomNavigationBar है. यह पेज, DashCounter, DashUpgrades,, और DashPurchases के लिए तीन Provider भी बनाता है. DashCounter, डैश की मौजूदा संख्या को ट्रैक करता है और उन्हें अपने-आप बढ़ाता है. DashUpgrades, डैश का इस्तेमाल करके खरीदे जा सकने वाले अपग्रेड मैनेज करता है. इस कोडलैब में, DashPurchases के बारे में बताया गया है.
डिफ़ॉल्ट रूप से, किसी प्रोवाइडर के ऑब्जेक्ट को तब तय किया जाता है, जब पहली बार उस ऑब्जेक्ट का अनुरोध किया जाता है. जब ऐप्लिकेशन शुरू होता है, तब यह ऑब्जेक्ट सीधे तौर पर खरीदारी से जुड़े अपडेट सुनता है. इसलिए, इस ऑब्जेक्ट पर लेज़ी लोडिंग की सुविधा बंद करें. इसके लिए, lazy: false का इस्तेमाल करें:
lib/main.dart
ChangeNotifierProvider<DashPurchases>(
create: (context) => DashPurchases(
context.read<DashCounter>(),
),
lazy: false, // Add this line
),
आपको InAppPurchaseConnection का एक इंस्टेंस भी चाहिए. हालांकि, ऐप्लिकेशन की जांच करने के लिए, आपको कनेक्शन को मॉक करने का कोई तरीका अपनाना होगा. इसके लिए, एक इंस्टेंस मेथड बनाएं, जिसे टेस्ट में बदला जा सकता है. साथ ही, इसे main.dart में जोड़ें.
lib/main.dart
// Gives the option to override in tests.
class IAPConnection {
static InAppPurchase? _instance;
static set instance(InAppPurchase value) {
_instance = value;
}
static InAppPurchase get instance {
_instance ??= InAppPurchase.instance;
return _instance!;
}
}
टेस्ट को इस तरह अपडेट करें:
test/widget_test.dart
import 'package:dashclicker/main.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:in_app_purchase/in_app_purchase.dart'; // Add this import
import 'package:in_app_purchase_platform_interface/src/in_app_purchase_platform_addition.dart'; // And this import
void main() {
testWidgets('App starts', (tester) async {
IAPConnection.instance = TestIAPConnection(); // Add this line
await tester.pumpWidget(const MyApp());
expect(find.text('Tim Sneath'), findsOneWidget);
});
}
class TestIAPConnection implements InAppPurchase< { > // Add from here
@override
Futurebool buyConsumable({
required PurchaseParam purchaseParam,
bool autoConsume = true,
}) {
retur<n Fu>ture.value(false);
}
@override
Futurebool buyNonConsumable({required PurchaseParam purchaseParam}) {
retur<n Fu>ture.value(false);
}
@override
Futurevoid completePurchase(PurchaseDetails purchase) {
<retu>rn Future.value();
}
@override
Futurebool isAvailable() {
retur<n Future.value(false);>
}
@override
Futu<reProd>uctDetailsResponse queryProductDetails(SetString identifiers) {
return Future.value(
ProductDetailsResponse(productDetails: [], notFoundIDs: []),<
);
}
@override
T getPlatform>AdditionT extends InAppPurchasePlatformAddition?() {
// TODO: implement getPlatformAddition
throw< Uni<mplementedError>>();
}
@override
> StreamListPurchase<Details get pur>chaseStream =
Stream<.val>ue(PurchaseDetails[]);
@override
Futurevoid restorePurchases({String? applicationUserName}) {
// TODO: implement restorePurchases
throw< Unimp>lementedError();
}
@override
FutureString countryCode() {
// TODO: implement countryCode
throw UnimplementedError();
}
} // To here.
lib/logic/dash_purchases.dart में, DashPurchasesChangeNotifier के कोड पर जाएं. फ़िलहाल, सिर्फ़ DashCounter को खरीदा जा सकता है और इसे खरीदे गए डैश में जोड़ा जा सकता है.
स्ट्रीम की सदस्यता वाली प्रॉपर्टी _subscription (टाइप StreamSubscription<List<PurchaseDetails>> _subscription;), IAPConnection.instance,, और इंपोर्ट जोड़ें. नतीजे वाला कोड कुछ ऐसा दिखेगा:
lib/logic/dash_purchases.dart
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:in_app_purchase/in_app_purchase.dart'; // Add this import
import '../main.dart'; // And this import
import '../model/purchasable_product.dart';
import '../model/store_state.dart';
import 'dash_counter.dart';
class DashPurchases extends ChangeNotifier {
DashCounter counter;
StoreSt<ate <storeState = St>>oreState.available;
late StreamSubscri<ptionListPurchaseD>etails _subscription; // Add this line
ListPurchasableProduct products = [
PurchasableProduct(
'Spring is in the air',
'Many dashes flying out from their nests',
'\$0.99',
),
PurchasableProduct(
'Jet engine',
>9;Doubles you clicks per second for a day',
'\$1.99',
),
];
bool get beautifiedDash = false;
final iap<Conn>ection = IAPConnection.instance; // And this line
DashPurchases(this.counter);
Futurevoid buy(Purchas<able>Product product) async {
product.status = ProductStatus.pending;
notifyListeners();
await Futurevoid.delayed(co<nst<>/span> Duration(seconds: 5));
product.status = ProductStatus.purchased;
notifyListeners();
await Futurevoid.delayed(const Duration(seconds: 5));
product.status = ProductStatus.purchasable;
notifyListeners();
}
}
late कीवर्ड को _subscription में जोड़ा गया है, क्योंकि _subscription को कंस्ट्रक्टर में शुरू किया गया है. इस प्रोजेक्ट को डिफ़ॉल्ट रूप से, नॉन-नलेबल बाय डिफ़ॉल्ट (एनएनबीडी) के तौर पर सेट अप किया गया है. इसका मतलब है कि जिन प्रॉपर्टी को नलेबल के तौर पर एलान नहीं किया गया है उनकी वैल्यू नॉन-नल होनी चाहिए. late क्वालिफ़ायर की मदद से, इस वैल्यू को बाद में तय किया जा सकता है.
कंस्ट्रक्टर में, purchaseUpdated स्ट्रीम पाएं और स्ट्रीम सुनना शुरू करें. dispose() तरीके में, स्ट्रीम की सदस्यता रद्द करें.
lib/logic/dash_purchases.dart
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:in_app_purchase/in_app_purchase.dart';
import '../main.dart';
import '../model/purchasable_product.dart';
import '../model/store_state.dart';
import 'dash_counter.dart';
class DashPurchases extends ChangeNotifier {
DashCounter counter;
StoreState storeState = StoreState.not<Avai<lable; >>// Modify this line
<late StreamSubscri>ptionListPurchaseDetails _subscription;
ListPurchasableProduct products = [
PurchasableProduct(
'Spring is in the air',
'Many dashes flying out from their nests',
'\$0.99',
),
PurchasableProduct(
'Jet engine',
>9;Doubles you clicks per second for a day',
'\$1.99',
),
];
bool get beautifiedDash = false;
final iapConnection = IAPConnection.instance;
DashPurchases(this.counter) { // Add from here
final purchaseUpdated = iapConnection.purchaseStream;
_subscription = purchaseUpdated.listen(
_onPurchaseUpdate,
onDone: _updateStreamOnDone,
onError: _updateStreamOnError,
);
}
@override
void dispose() {
_subscription<.can>cel();
super.dispose();
} // To here.
Futurevoid buy(Purchas<able>Product product) async {
product.status = ProductStatus.pending;
notifyListeners();
await Futurevoid.delayed(co<nst<>/span> Duration(seconds: 5));
product.status = ProductStatus.purchased;
notifyListeners();
await Futurevoid.delayed(const Duration(seconds: 5));
product.status = ProductStatus.purchasable;
notifyListeners(<);
}
> // Add from here
void _onPurchaseUpdate(ListPurchaseDetails purchaseDetailsList) {
// Handle purchases here
}
void _updateStreamOnDone() {
_subscription.cancel();
}
void _updateStreamOnError(dynamic error) {
//Handle error here
} // To here.
}
अब ऐप्लिकेशन को खरीदारी से जुड़े अपडेट मिल रहे हैं. इसलिए, अगले सेक्शन में आपको खरीदारी करनी होगी!
आगे बढ़ने से पहले, "flutter test" की मदद से टेस्ट चलाएं. इससे यह पुष्टि की जा सकेगी कि सब कुछ सही तरीके से सेट अप किया गया है.
$ flutter test 00:01 +1: All tests passed!
8. ख़रीदारी करना
इस कोडलैब के इस हिस्से में, आपको मौजूदा मॉक प्रॉडक्ट को खरीदने के लिए उपलब्ध असली प्रॉडक्ट से बदलना होगा. ये प्रॉडक्ट, स्टोर से लोड किए जाते हैं और इन्हें सूची में दिखाया जाता है. इन पर टैप करके इन्हें खरीदा जा सकता है.
Adapt PurchasableProduct
PurchasableProduct में नकली प्रॉडक्ट दिखाया गया है. PurchasableProduct क्लास में मौजूद purchasable_product.dart को इस कोड से बदलकर, इसे अपडेट करें, ताकि असल कॉन्टेंट दिखाया जा सके:
lib/model/purchasable_product.dart
import 'package:in_app_purchase/in_app_purchase.dart';
enum ProductStatus { purchasable, purchased, pending }
class PurchasableProduct {
String >get id = productDetails.id;
String get> title = productDetails.title;
String get descr>iption = productDetails.description;
String get> price = productDetails.price;
ProductStatus status;
ProductDetails productDetails;
PurchasableProduct(this.productDetails) : status = ProductStatus.purchasable;
}
dash_purchases.dart, में, डमी खरीदारी हटाएं और उन्हें खाली सूची से बदलें List<PurchasableProduct> products = [];.
उपलब्ध खरीदारी लोड करें
किसी उपयोगकर्ता को खरीदारी करने की सुविधा देने के लिए, स्टोर से खरीदारी की जानकारी लोड करें. सबसे पहले, देखें कि स्टोर उपलब्ध है या नहीं. स्टोर उपलब्ध न होने पर, storeState को notAvailable पर सेट करने से, उपयोगकर्ता को गड़बड़ी का मैसेज दिखता है.
lib/logic/dash_purchases.dart
Future<void> loadPurchases() async {
final available = await iapConnection.isAvailable();
if (!available) {
storeState = StoreState.notAvailable;
notifyListeners();
return;
}
}
स्टोर उपलब्ध होने पर, खरीदारी के लिए उपलब्ध प्रॉडक्ट लोड करें. Google Play और App Store के पिछले सेटअप के हिसाब से, आपको storeKeyConsumable, storeKeySubscription,, और storeKeyUpgrade दिखेंगे. जब उम्मीद के मुताबिक खरीदारी उपलब्ध न हो, तो इस जानकारी को कंसोल पर प्रिंट करें. ऐसा भी हो सकता है कि आपको यह जानकारी बैकएंड सेवा को भेजनी हो.
await iapConnection.queryProductDetails(ids) तरीके से, न मिलने वाले आईडी और खरीदारी के लिए उपलब्ध प्रॉडक्ट, दोनों की जानकारी मिलती है. जवाब से मिले productDetails का इस्तेमाल करके, यूज़र इंटरफ़ेस (यूआई) को अपडेट करें. साथ ही, StoreState को available पर सेट करें.
lib/logic/dash_purchases.dart
import '../constants.dart';
// ...
< Fu>turevoid loadPurchases() async {
final available = await iapConnection.isAvailable();
if (!available) {
storeState = StoreState.notAvailable;
notifyListeners();
return;
}
cons<t ids >= String{
storeKeyConsumable,
storeKeySubscription,
storeKeyUpgrade,
};
final response = await iapConnection.queryProductDetails(ids);
products = response.productDetails
.m>ap((e) = PurchasableProduct(e))
.toList();
storeState = StoreState.available;
notifyListeners();
}
कंस्ट्रक्टर में loadPurchases() फ़ंक्शन को कॉल करें:
lib/logic/dash_purchases.dart
DashPurchases(this.counter) {
final purchaseUpdated = iapConnection.purchaseStream;
_subscription = purchaseUpdated.listen(
_onPurchaseUpdate,
onDone: _updateStreamOnDone,
onError: _updateStreamOnError,
);
loadPurchases(); // Add this line
}
आखिर में, storeState फ़ील्ड की वैल्यू को StoreState.available से बदलकर StoreState.loading: करें
lib/logic/dash_purchases.dart
StoreState storeState = StoreState.loading;
खरीदे जा सकने वाले प्रॉडक्ट दिखाना
purchase_page.dart फ़ाइल को ध्यान में रखें. PurchasePage विजेट, StoreState के आधार पर _PurchasesLoading, _PurchaseList, या _PurchasesNotAvailable, दिखाता है. विजेट में, उपयोगकर्ता की पिछली खरीदारी की जानकारी भी दिखती है. इसका इस्तेमाल अगले चरण में किया जाता है.
_PurchaseList विजेट, खरीदने के लिए उपलब्ध प्रॉडक्ट की सूची दिखाता है. साथ ही, DashPurchases ऑब्जेक्ट को खरीदारी का अनुरोध भेजता है.
lib/pages/purchase_page.dart
class _PurchaseList extends StatelessWidget {
@override
Widget build(BuildContext context) {
var purchases = context.watch<DashPurchases>();
var products = purchases.products;
return Column(
children: products
.map(
(product) => _PurchaseWidget(
product: product,
onPressed: () {
purchases.buy(product);
},
),
)
.toList(),
);
}
}
अगर प्रॉडक्ट ठीक से कॉन्फ़िगर किए गए हैं, तो आपको Android और iOS स्टोर पर उपलब्ध प्रॉडक्ट दिखेंगे. ध्यान दें कि खरीदारी की जानकारी को संबंधित कंसोल में डालने के बाद, उसे उपलब्ध होने में कुछ समय लग सकता है.

dash_purchases.dart पर वापस जाएं और प्रॉडक्ट खरीदने के लिए फ़ंक्शन लागू करें. आपको सिर्फ़ इस्तेमाल की जाने वाली चीज़ों को, इस्तेमाल न की जाने वाली चीज़ों से अलग करना होगा. अपग्रेड और सदस्यता वाले प्रॉडक्ट, इस्तेमाल न किए जा सकने वाले प्रॉडक्ट हैं.
lib/logic/dash_purchases.dart
Future<void> buy(PurchasableProduct product) async {
final purchaseParam = PurchaseParam(productDetails: product.productDetails);
switch (product.id) {
case storeKeyConsumable:
await iapConnection.buyConsumable(purchaseParam: purchaseParam);
case storeKeySubscription:
case storeKeyUpgrade:
await iapConnection.buyNonConsumable(purchaseParam: purchaseParam);
default:
throw ArgumentError.value(
product.productDetails,
'${product.id} is not a known product',
);
}
}
आगे बढ़ने से पहले, _beautifiedDashUpgrade वैरिएबल बनाएं और इसे रेफ़रंस करने के लिए, beautifiedDash गेटर को अपडेट करें.
lib/logic/dash_purchases.dart
bool get beautifiedDash => _beautifiedDashUpgrade;
bool _beautifiedDashUpgrade = false;
_onPurchaseUpdate तरीके से खरीदारी के अपडेट मिलते हैं. साथ ही, खरीदारी वाले पेज पर दिखाए गए प्रॉडक्ट की स्थिति अपडेट होती है. इसके अलावा, खरीदारी को काउंटर लॉजिक पर लागू किया जाता है. खरीदारी को मैनेज करने के बाद, completePurchase को कॉल करना ज़रूरी है, ताकि स्टोर को पता चल सके कि खरीदारी को सही तरीके से मैनेज किया गया है.
lib/logic/dash_purchases.dart
Future<void> _onPurchaseUpdate(
List<PurchaseDetails> purchaseDetailsList,
) async {
for (var purchaseDetails in purchaseDetailsList) {
await _handlePurchase(purchaseDetails);
}
notifyListeners();
}
Future<void> _handlePurchase(PurchaseDetails purchaseDetails) async {
if (purchaseDetails.status == PurchaseStatus.purchased) {
switch (purchaseDetails.productID) {
case storeKeySubscription:
counter.applyPaidMultiplier();
case storeKeyConsumable:
counter.addBoughtDashes(2000);
case storeKeyUpgrade:
_beautifiedDashUpgrade = true;
}
}
if (purchaseDetails.pendingCompletePurchase) {
await iapConnection.completePurchase(purchaseDetails);
}
}
9. बैकएंड सेट अप करना
खरीदारी को ट्रैक और पुष्टि करने से पहले, ऐसा करने के लिए Dart बैकएंड सेट अप करें.
इस सेक्शन में, dart-backend/ फ़ोल्डर को रूट के तौर पर इस्तेमाल करें.
पक्का करें कि आपने ये टूल इंस्टॉल किए हों:
- Dart
- Firebase CLI
बुनियादी प्रोजेक्ट की खास जानकारी
इस प्रोजेक्ट के कुछ हिस्सों को इस कोडलैब के दायरे से बाहर माना जाता है. इसलिए, उन्हें स्टार्टर कोड में शामिल किया गया है. शुरू करने से पहले, स्टार्टर कोड में मौजूद जानकारी को देख लें. इससे आपको यह समझने में मदद मिलेगी कि आपको चीज़ों को कैसे व्यवस्थित करना है.
यह बैकएंड कोड, आपकी मशीन पर स्थानीय तौर पर चल सकता है. इसका इस्तेमाल करने के लिए, आपको इसे डिप्लॉय करने की ज़रूरत नहीं है. हालांकि, आपको अपने डेवलपमेंट डिवाइस (Android या iPhone) से उस मशीन से कनेक्ट करना होगा जिस पर सर्वर चलेगा. इसके लिए, दोनों डिवाइसों का एक ही नेटवर्क से कनेक्ट होना ज़रूरी है. साथ ही, आपको अपने डिवाइस का आईपी पता पता होना चाहिए.
इस कमांड का इस्तेमाल करके सर्वर चलाने की कोशिश करें:
$ dart ./bin/server.dart Serving at http://0.0.0.0:8080
Dart बैकएंड, एपीआई एंडपॉइंट को दिखाने के लिए shelf और shelf_router का इस्तेमाल करता है. डिफ़ॉल्ट रूप से, सर्वर कोई भी रास्ता नहीं दिखाता है. बाद में, खरीदारी की पुष्टि करने की प्रोसेस को मैनेज करने के लिए, एक रूट बनाया जाएगा.
स्टार्टर कोड में पहले से शामिल एक हिस्सा, lib/iap_repository.dart में मौजूद IapRepository है. Firestore या सामान्य तौर पर डेटाबेस के साथ इंटरैक्ट करने का तरीका सीखना, इस कोडलैब के लिए ज़रूरी नहीं है. इसलिए, स्टार्टर कोड में ऐसे फ़ंक्शन शामिल हैं जिनकी मदद से, Firestore में खरीदारी की जानकारी बनाई या अपडेट की जा सकती है. साथ ही, इसमें उन खरीदारी के लिए सभी क्लास भी शामिल हैं.
Firebase का ऐक्सेस सेट अप करना
Firebase Firestore को ऐक्सेस करने के लिए, आपको सेवा खाते की ऐक्सेस कुंजी की ज़रूरत होगी. इसे जनरेट करने के लिए, Firebase प्रोजेक्ट की सेटिंग खोलें और सेवा खाते सेक्शन पर जाएं. इसके बाद, नया निजी पासकोड जनरेट करें को चुनें.

डाउनलोड की गई JSON फ़ाइल को assets/ फ़ोल्डर में कॉपी करें और उसका नाम बदलकर service-account-firebase.json करें.
Google Play का ऐक्सेस सेट अप करना
खरीदारी की पुष्टि करने के लिए Play Store को ऐक्सेस करने के लिए, आपको इन अनुमतियों के साथ एक सेवा खाता जनरेट करना होगा. साथ ही, इसके लिए JSON क्रेडेंशियल डाउनलोड करने होंगे.
- Google Cloud Console में, Google Play Android Developer API पेज पर जाएं.
अगर Google Play Console आपसे कोई नया प्रोजेक्ट बनाने या किसी मौजूदा प्रोजेक्ट से लिंक करने का अनुरोध करता है, तो पहले ऐसा करें. इसके बाद, इस पेज पर वापस आएं. - इसके बाद, सेवा खाते वाले पेज पर जाएं और + सेवा खाता बनाएं पर क्लिक करें.

- सेवा खाते का नाम डालें और बनाएं और जारी रखें पर क्लिक करें.

- Pub/Sub Subscriber भूमिका चुनें और हो गया पर क्लिक करें.

- खाता बन जाने के बाद, कुंजियां मैनेज करें पर जाएं.

- कुंजी जोड़ें > नई कुंजी बनाएं को चुनें.

- JSON कुंजी बनाएं और डाउनलोड करें.

- डाउनलोड की गई फ़ाइल का नाम बदलकर
service-account-google-play.json,रखें और उसेassets/डायरेक्ट्री में ले जाएं. - इसके बाद, Play Console में उपयोगकर्ता और अनुमतियां पेज पर जाएं

- नए उपयोगकर्ताओं को न्योता भेजें पर क्लिक करें. इसके बाद, पहले बनाए गए सेवा खाते का ईमेल पता डालें. आपको यह ईमेल, सेवा खाते वाले पेज पर मौजूद टेबल में मिल सकता है

- ऐप्लिकेशन के लिए, वित्तीय डेटा देखें और ऑर्डर और सदस्यताएं मैनेज करें अनुमतियां दें.

- उपयोगकर्ता को न्योता भेजें पर क्लिक करें.
हमें एक और काम करना है. lib/constants.dart, खोलें और androidPackageId की वैल्यू को उस पैकेज आईडी से बदलें जिसे आपने अपने Android ऐप्लिकेशन के लिए चुना है.
Apple App Store का ऐक्सेस सेट अप करना
खरीदारी की पुष्टि करने के लिए App Store को ऐक्सेस करने के लिए, आपको शेयर किया गया सीक्रेट सेट अप करना होगा:
- App Store Connect खोलें.
- मेरे ऐप्लिकेशन पर जाएं और अपना ऐप्लिकेशन चुनें.
- साइडबार नेविगेशन में, सामान्य > ऐप्लिकेशन की जानकारी पर जाएं.
- ऐप्लिकेशन के हिसाब से शेयर किया गया सीक्रेट हेडर में जाकर, मैनेज करें पर क्लिक करें.

- नया सीक्रेट जनरेट करें और उसे कॉपी करें.

lib/constants.dart,खोलें औरappStoreSharedSecretकी वैल्यू को, अभी जनरेट किए गए शेयर किए गए सीक्रेट से बदलें.
कॉन्स्टैंट कॉन्फ़िगरेशन फ़ाइल
आगे बढ़ने से पहले, पक्का करें कि lib/constants.dart फ़ाइल में ये कॉन्स्टेंट कॉन्फ़िगर किए गए हों:
androidPackageId: Android पर इस्तेमाल किया गया पैकेज आईडी, जैसे किcom.example.dashclickerappStoreSharedSecret: खरीदारी की पुष्टि करने के लिए, App Store Connect को ऐक्सेस करने के लिए शेयर किया गया सीक्रेट.bundleId: iOS पर इस्तेमाल किया गया बंडल आईडी, जैसे किcom.example.dashclicker
फ़िलहाल, अन्य कॉन्स्टेंट को अनदेखा किया जा सकता है.
10. खरीदारियों की पुष्टि करना
खरीदारी की पुष्टि करने का सामान्य फ़्लो, iOS और Android के लिए एक जैसा होता है.
दोनों स्टोर के लिए, खरीदारी होने पर आपके ऐप्लिकेशन को एक टोकन मिलता है.
यह टोकन, ऐप्लिकेशन से आपकी बैकएंड सेवा को भेजा जाता है. इसके बाद, यह सेवा दिए गए टोकन का इस्तेमाल करके, संबंधित स्टोर के सर्वर से खरीदारी की पुष्टि करती है.
इसके बाद, बैकएंड सेवा खरीदारी को सेव कर सकती है. साथ ही, ऐप्लिकेशन को यह बता सकती है कि खरीदारी मान्य है या नहीं.
बैकएंड सेवा को, उपयोगकर्ता के डिवाइस पर चल रहे ऐप्लिकेशन के बजाय स्टोर से पुष्टि करने की अनुमति दें. इससे, उपयोगकर्ता को प्रीमियम सुविधाओं का ऐक्सेस पाने से रोका जा सकता है. उदाहरण के लिए, सिस्टम क्लॉक को रिवाइंड करके.
Flutter साइड को सेट अप करना
पुष्टि करने की सुविधा सेट अप करना
आपको खरीदारी की जानकारी अपनी बैकएंड सेवा को भेजनी है. इसलिए, आपको यह पक्का करना होगा कि खरीदारी करते समय उपयोगकर्ता की पुष्टि हो गई हो. स्टार्टर प्रोजेक्ट में, पुष्टि करने से जुड़ा ज़्यादातर लॉजिक पहले से ही जोड़ दिया गया है. आपको बस यह पक्का करना है कि जब उपयोगकर्ता ने लॉग इन न किया हो, तब PurchasePage में लॉगिन बटन दिखे. PurchasePage की build मेथड की शुरुआत में यह कोड जोड़ें:
lib/pages/purchase_page.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../logic/dash_purchases.dart';
import '../logic/firebase_notifier.dart'; // Add this import
import '../model/firebase_state.dart'; // And this import
import '../model/purchasable_product.dart';
import '../model/store_state.dart';
import '../repo/iap_repo.dart';
import 'login_page.dart'; // And this one as well
class PurchasePage extends StatelessWidget {
const PurchasePage({super.key});
@override
Widget build(BuildContext context) { < // U>pdate from here
var firebaseNotifier = context.watchFirebaseNotifier();
if (firebaseNotifier.state == FirebaseState.loading) {
return _PurchasesLoading();
} else if (firebaseNotifier.state == FirebaseState.notAvailable) {
return _PurchasesNotAvailable();
}
if (!firebaseNotifier.loggedIn) {
return const LoginPage();
} // To here.
// ...
ऐप्लिकेशन से कॉल की पुष्टि करने वाले एंडपॉइंट को कॉल करना
ऐप्लिकेशन में, _verifyPurchase(PurchaseDetails purchaseDetails) फ़ंक्शन बनाएं. यह http पोस्ट कॉल का इस्तेमाल करके, आपके Dart बैकएंड पर मौजूद /verifypurchase एंडपॉइंट को कॉल करता है.
चुने गए स्टोर (Play Store के लिए google_play या App Store के लिए app_store), serverVerificationData, और productID भेजें. सर्वर, स्टेटस कोड दिखाता है. इससे पता चलता है कि खरीदारी की पुष्टि हुई है या नहीं.
ऐप्लिकेशन के कॉन्स्टेंट में, सर्वर आईपी को अपने कंप्यूटर के आईपी पते पर कॉन्फ़िगर करें.
lib/logic/dash_purchases.dart
import 'dart:async';
import 'dart:convert'; // Add this import
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http; // And this import
import 'package:in_app_purchase/in_app_purchase.dart';
import '../constants.dart';
import '../main.dart';
import '../model/purchasable_product.dart';
import '../model/store_state.dart';
import 'dash_counter.dart';
import 'firebase_notifier.dart'; // And this one
class DashPurchases extends ChangeNotifier {
DashCounter counter;
FirebaseNotifier firebaseNotifier; < < // Add this>> line
StoreState sto<reState = StoreSta>te.loading;
late StreamSubscriptionListPur>chaseDetails _subscription;
ListPurchasableProduct products = [];
bool get beautifiedDash = _beautifiedDashUpgrade;
bool _beautifiedDashUpgrade = false;
final iapConnection = IAPConnection.instance;
DashPurchases(this.counter, this.firebaseNotifier) { // Update this line
final purchaseUpdated = iapConnection.purchaseStream;
_subscription = purchaseUpdated.listen(
_onPurchaseUpdate,
onDone: _updateStreamOnDone,
onError: _updateStreamOnError,
);
loadPurchases();
}
main.dart: में DashPurchases बनाते समय, firebaseNotifier जोड़ें
lib/main.dart
ChangeNotifierProvider<DashPurchases>(
create: (context) => DashPurchases(
context.read<DashCounter>(),
context.read<FirebaseNotifier>(),
),
lazy: false,
),
FirebaseNotifier में उपयोगकर्ता के लिए एक गेटर जोड़ें, ताकि खरीदारी की पुष्टि करने वाले फ़ंक्शन को User-ID पास किया जा सके.
lib/logic/firebase_notifier.dart
Future<FirebaseFirestore> get firestore async {
var isInitialized = await _isInitialized.future;
if (!isInitialized) {
throw Exception('Firebase is not initialized');
}
return FirebaseFirestore.instance;
}
User? ge>t user = FirebaseAuth.instance.currentUser; // Add this line
< Fu>turevoid load() async {
// ...
_verifyPurchase फ़ंक्शन को DashPurchases क्लास में जोड़ें. यह async फ़ंक्शन, बूलियन वैल्यू दिखाता है. इससे पता चलता है कि खरीदारी की पुष्टि हुई है या नहीं.
lib/logic/dash_purchases.dart
Future<bool> _verifyPurchase(PurchaseDetails purchaseDetails) async {
final url = Uri.parse('http://$serverIp:8080/verifypurchase');
const headers = {
'Content-type': 'application/json',
'Accept': 'application/json',
};
final response = await http.post(
url,
body: jsonEncode({
'source': purchaseDetails.verificationData.source,
'productId': purchaseDetails.productID,
'verificationData':
purchaseDetails.verificationData.serverVerificationData,
'userId': firebaseNotifier.user?.uid,
}),
headers: headers,
);
if (response.statusCode == 200) {
return true;
} else {
return false;
}
}
खरीदारी लागू करने से ठीक पहले, _handlePurchase में _verifyPurchase फ़ंक्शन को कॉल करें. खरीदारी की पुष्टि हो जाने के बाद ही, उसे लागू करें. प्रोडक्शन ऐप्लिकेशन में, इसे और बेहतर तरीके से तय किया जा सकता है. उदाहरण के लिए, जब स्टोर कुछ समय के लिए उपलब्ध न हो, तब बिना किसी शुल्क के आज़माने की सदस्यता लागू की जा सकती है. हालांकि, इस उदाहरण के लिए, खरीदारी की पुष्टि होने के बाद ही खरीदारी लागू करें.
lib/logic/dash_purchases.dart
Future<void> _onPurchaseUpdate(
List<PurchaseDetails> purchaseDetailsList,
) async {
for (var purchaseDetails in purchaseDetailsList) {
await _handlePurchase(purchaseDetails);
}
notifyListeners();
}
Future<void> _handlePurchase(PurchaseDetails purchaseDetails) async {
if (purchaseDetails.status == PurchaseStatus.purchased) {
// Send to server
var validPurchase = await _verifyPurchase(purchaseDetails);
if (validPurchase) {
// Apply changes locally
switch (purchaseDetails.productID) {
case storeKeySubscription:
counter.applyPaidMultiplier();
case storeKeyConsumable:
counter.addBoughtDashes(2000);
case storeKeyUpgrade:
_beautifiedDashUpgrade = true;
}
}
}
if (purchaseDetails.pendingCompletePurchase) {
await iapConnection.completePurchase(purchaseDetails);
}
}
ऐप्लिकेशन में, अब खरीदारी की पुष्टि करने के लिए सभी ज़रूरी चीज़ें उपलब्ध हैं.
बैकएंड सेवा सेट अप करना
इसके बाद, बैकएंड पर खरीदारी की पुष्टि करने के लिए, बैकएंड सेट अप करें.
खरीदारी के हैंडलर बनाना
दोनों स्टोर के लिए पुष्टि करने का तरीका लगभग एक जैसा है. इसलिए, एक ऐब्स्ट्रैक्ट PurchaseHandler क्लास सेट अप करें. इसमें हर स्टोर के लिए अलग-अलग तरीके से लागू करने की सुविधा हो.

शुरुआत में, lib/ फ़ोल्डर में एक purchase_handler.dart फ़ाइल जोड़ें. इसमें, आपको एक ऐब्स्ट्रैक्ट PurchaseHandler क्लास को दो ऐब्स्ट्रैक्ट तरीकों से तय करना होगा. इन तरीकों का इस्तेमाल, दो तरह की खरीदारी की पुष्टि करने के लिए किया जाएगा: सदस्यताएं और बिना सदस्यता वाली खरीदारी.
lib/purchase_handler.dart
import 'products.dart';
/// Generic purchase handler,
/// must be implemented for Google Play and Apple Store
abstract class PurchaseHandler {
/// Verify if non-subscription purchase (aka consumable) is valid
/// and update the database
< Fu>turebool handleNonSubscription({
required String userId,
required ProductData productData,
required String token,
});
/// Verify if subscription purchase (aka non-consumable) is valid
/// and update the database
< Fu>turebool handleSubscription({
required String userId,
required ProductData productData,
required String token,
});
}
जैसा कि आप देख सकते हैं, हर तरीके के लिए तीन पैरामीटर ज़रूरी हैं:
userId:लॉग इन किए हुए उपयोगकर्ता का आईडी, ताकि खरीदारी को उपयोगकर्ता से जोड़ा जा सके.productData:प्रॉडक्ट के बारे में डेटा. आपको इसे एक मिनट में तय करना है.token:यह टोकन, स्टोर ने उपयोगकर्ता को दिया है.
इसके अलावा, खरीदारी के इन हैंडलर को इस्तेमाल करने में आसानी हो, इसके लिए एक verifyPurchase() तरीका जोड़ें. इसका इस्तेमाल सदस्यता और बिना सदस्यता वाली खरीदारी, दोनों के लिए किया जा सकता है:
lib/purchase_handler.dart
/// Verify if purchase is valid and update the database
Future<bool> verifyPurchase({
required String userId,
required ProductData productData,
required String token,
}) async {
switch (productData.type) {
case ProductType.subscription:
return handleSubscription(
userId: userId,
productData: productData,
token: token,
);
case ProductType.nonSubscription:
return handleNonSubscription(
userId: userId,
productData: productData,
token: token,
);
}
}
अब दोनों मामलों के लिए, सिर्फ़ verifyPurchase को कॉल किया जा सकता है. हालांकि, अब भी अलग-अलग तरीके से लागू किया जा सकता है!
ProductData क्लास में, खरीदने के लिए उपलब्ध अलग-अलग प्रॉडक्ट के बारे में बुनियादी जानकारी होती है. इसमें प्रॉडक्ट आईडी (कभी-कभी इसे एसकेयू भी कहा जाता है) और ProductType शामिल होता है.
lib/products.dart
class ProductData {
final String productId;
final ProductType type;
const ProductData(this.productId, this.type);
}
ProductType सदस्यता या बिना सदस्यता के उपलब्ध हो सकता है.
lib/products.dart
enum ProductType { subscription, nonSubscription }
आखिर में, प्रॉडक्ट की सूची को उसी फ़ाइल में मैप के तौर पर तय किया जाता है.
lib/products.dart
const productDataMap = {
'dash_consumable_2k': ProductData(
'dash_consumable_2k',
ProductType.nonSubscription,
),
'dash_upgrade_3d': ProductData(
'dash_upgrade_3d',
ProductType.nonSubscription,
),
'dash_subscription_doubler': ProductData(
'dash_subscription_doubler',
ProductType.subscription,
),
};
इसके बाद, Google Play Store और Apple App Store के लिए कुछ प्लेसहोल्डर लागू करें. Google Play का इस्तेमाल शुरू करना:
lib/google_play_purchase_handler.dart बनाएं. इसके बाद, ऐसी क्लास जोड़ें जो अभी-अभी लिखे गए PurchaseHandler को बढ़ाती है:
lib/google_play_purchase_handler.dart
import 'dart:async';
import 'package:googleapis/androidpublisher/v3.dart' as ap;
import 'constants.dart';
import 'iap_repository.dart';
import 'products.dart';
import 'purchase_handler.dart';
class GooglePlayPurchaseHandler extends PurchaseHandler {
final ap.AndroidPublisherApi androidPublisher;
final IapRepository iapRepository;
GooglePlayPurchaseHandler(this.androidPubli<sher>, this.iapRepository);
@override
Futurebool handleNonSubscription({
required String? userId,
required ProductData productData,
required String token,
}) as<ync<>/span> {
return true;
}
@override
Futurebool handleSubscription({
required String? userId,
required ProductData productData,
required String token,
}) async {
return true;
}
}
फ़िलहाल, यह हैंडलर के तरीकों के लिए true दिखाता है. आपको इनके बारे में बाद में पता चलेगा.
जैसा कि आपने देखा होगा कि कंस्ट्रक्टर, IapRepository का इंस्टेंस लेता है. खरीदारी हैंडलर इस इंस्टेंस का इस्तेमाल, बाद में Firestore में खरीदारी के बारे में जानकारी सेव करने के लिए करता है. Google Play से कम्यूनिकेट करने के लिए, आपको AndroidPublisherApi का इस्तेमाल करना होगा.
इसके बाद, ऐप्लिकेशन स्टोर हैंडलर के लिए भी ऐसा ही करें. lib/app_store_purchase_handler.dart बनाएं और PurchaseHandler को फिर से बढ़ाने वाली क्लास जोड़ें:
lib/app_store_purchase_handler.dart
import 'dart:async';
import 'package:app_store_server_sdk/app_store_server_sdk.dart';
import 'constants.dart';
import 'iap_repository.dart';
import 'products.dart';
import 'purchase_handler.dart';
class AppStorePurchaseHandler extends PurchaseHandler {
final IapRepository iapRepository;
AppStorePurchaseHa<ndle>r(this.iapRepository);
@override
Futurebool handleNonSubscription({
required String userId,
required ProductData productData,
required String token,
}) as<ync<>/span> {
return true;
}
@override
Futurebool handleSubscription({
required String userId,
required ProductData productData,
required String token,
}) async {
return true;
}
}
बढ़िया! अब आपके पास खरीदारी के दो हैंडलर हैं. इसके बाद, खरीदारी की पुष्टि की सुविधा वाले एपीआई एंडपॉइंट को बनाएं.
खरीदारी के हैंडलर इस्तेमाल करना
bin/server.dart खोलें और shelf_route का इस्तेमाल करके एपीआई एंडपॉइंट बनाएं:
bin/server.dart
import 'dart:convert';
import 'package:firebase_backend_dart/helpers.dart';
import 'package:firebase_backend_dart/products.dart';
import 'package:shelf/shelf.dart';
import 'package:shel<f_ro>uter/shelf_router.dart';
Futurevoid main() async {
final router = Router();
final purchaseHandlers = await _createPurchaseHandlers();
router.post('/verifypurchase', (Request request) async {
final dynamic payload = json.decode(await request.readAsString());
final (:userId, :source, :productData, :token) = getPurchaseData(payload);
final result = await purchaseHandlers[source]!.verifyPurchase(
userId: userId,
productData: productData,
token: token,
);
if (result) {
return Response.ok('all good!');
} else {
return Response.internalServerError();
}
});
await serveHandler(router.call);
}
({String userId, String source, ProductData productData, String token})
getPurchaseData(dynamic payload) {
if (payload case {
'userId': String userId,
'source': String source,
'productId': String productId,
'verificationData': String token,
}) {
return (
userId: userId,
source: source,
productData: productDataMap[productId]!,
token: token,
);
} else {
throw const FormatException('Unexpected JSON');
}
}
यह कोड ये काम कर रहा है:
- एक POST एंडपॉइंट तय करें, जिसे पहले बनाए गए ऐप्लिकेशन से कॉल किया जाएगा.
- JSON पेलोड को डिकोड करें और यह जानकारी निकालें:
userId: लॉग इन किए गए उपयोगकर्ता का आईडीsource: इस्तेमाल किया गया स्टोर,app_storeयाgoogle_play.productData: यह जानकारी, आपके पहले बनाए गएproductDataMapसे ली गई है.token: इसमें पहचान की पुष्टि के लिए सबमिट किया गया डेटा होता है, जिसे स्टोर को भेजा जाता है.
- सोर्स के आधार पर,
GooglePlayPurchaseHandlerयाAppStorePurchaseHandlerके लिए,verifyPurchaseवाले तरीके को कॉल करें. - अगर पुष्टि हो जाती है, तो यह तरीका क्लाइंट को
Response.okदिखाता है. - अगर पुष्टि नहीं हो पाती है, तो यह तरीका क्लाइंट को
Response.internalServerErrorदिखाता है.
एपीआई एंडपॉइंट बनाने के बाद, आपको खरीदारी के दो हैंडलर कॉन्फ़िगर करने होंगे. इसके लिए, आपको पिछले चरण में मिली सेवा खाते की कुंजियां लोड करनी होंगी. साथ ही, Android Publisher API और Firebase Firestore API जैसी अलग-अलग सेवाओं के लिए ऐक्सेस कॉन्फ़िगर करना होगा. इसके बाद, अलग-अलग डिपेंडेंसी के साथ दो परचेज़ हैंडलर बनाएं:
bin/server.dart
import 'dart:convert';
import 'dart:io'; // new
import 'package:firebase_backend_dart/app_store_purchase_handler.dart'; // new
import 'package:firebase_backend_dart/google_play_purchase_handler.dart'; // new
import 'package:firebase_backend_dart/helpers.dart';
import 'package:firebase_backend_dart/iap_repository.dart'; // new
import 'package:firebase_backend_dart/products.dart';
import 'package:firebase_backend_dart/purchase_handler.dart'; // new
import 'package:googleapis/androidpublisher/v3.dart' as ap; // new
import 'package:googleapis/firestore/v1.dart' as fs; // new
import 'package:googleapis_auth/auth_io.dart' as auth; // new
imp<ort< 'package:shelf/she>>lf.dart';
import 'package:shelf_router/shelf_router.dart';
FutureMapString, PurchaseHandler _createPurchaseHandlers() async {
// Configure Android Publisher API access
final serviceAccountGooglePlay =
File('assets/service-account-google-play.json').readAsStringSync();
final clientCredentialsGooglePlay =
auth.ServiceAccountCredentials.fromJson(serviceAccountGooglePlay);
final clientGooglePlay =
await auth.clientViaServiceAccount(clientCredentialsGooglePlay, [
ap.AndroidPublisherApi.androidpublisherScope,
]);
final androidPublisher = ap.AndroidPublisherApi(clientGooglePlay);
// Configure Firestore API access
final serviceAccountFirebase =
File('assets/service-account-firebase.json').readAsStringSync();
final clientCredentialsFirebase =
auth.ServiceAccountCredentials.fromJson(serviceAccountFirebase);
final clientFirebase =
await auth.clientViaServiceAccount(clientCredentialsFirebase, [
fs.FirestoreApi.cloudPlatformScope,
]);
final firestoreApi = fs.FirestoreApi(clientFirebase);
final dynamic json = jsonDecode(serviceAccountFirebase);
final projectId = json['project_id'] as String;
final iapRepository = IapRepository(firestoreApi, projectId);
return {
'google_play': GooglePlayPurchaseHandler(
androidPublisher,
iapRepository,
),
'app_store': AppStorePurchaseHandler(
iapRepository,
),
};
}
Android डिवाइस पर की गई खरीदारी की पुष्टि करना: खरीदारी के हैंडलर को लागू करना
इसके बाद, Google Play से की गई खरीदारी के हैंडलर को लागू करना जारी रखें.
Google पहले से ही Dart पैकेज उपलब्ध कराता है. इनकी मदद से, उन एपीआई के साथ इंटरैक्ट किया जा सकता है जिनकी मदद से आपको खरीदारी की पुष्टि करनी है. आपने उन्हें server.dart फ़ाइल में शुरू किया है और अब उनका इस्तेमाल GooglePlayPurchaseHandler क्लास में किया जा रहा है.
बिना सदस्यता वाली खरीदारी के लिए हैंडलर लागू करें:
lib/google_play_purchase_handler.dart
/// Handle non-subscription purchases (one time purchases).
///
/// Retrieves the purchase status from Google Play and updates
/// the Firestore Database accordingly.
@override
Future<bool> handleNonSubscription({
required String? userId,
required ProductData productData,
required String token,
}) async {
print(
'GooglePlayPurchaseHandler.handleNonSubscription'
'($userId, ${productData.productId}, ${token.substring(0, 5)}...)',
);
try {
// Verify purchase with Google
final response = await androidPublisher.purchases.products.get(
androidPackageId,
productData.productId,
token,
);
print('Purchases response: ${response.toJson()}');
// Make sure an order ID exists
if (response.orderId == null) {
print('Could not handle purchase without order id');
return false;
}
final orderId = response.orderId!;
final purchaseData = NonSubscriptionPurchase(
purchaseDate: DateTime.fromMillisecondsSinceEpoch(
int.parse(response.purchaseTimeMillis ?? '0'),
),
orderId: orderId,
productId: productData.productId,
status: _nonSubscriptionStatusFrom(response.purchaseState),
userId: userId,
iapSource: IAPSource.googleplay,
);
// Update the database
if (userId != null) {
// If we know the userId,
// update the existing purchase or create it if it does not exist.
await iapRepository.createOrUpdatePurchase(purchaseData);
} else {
// If we don't know the user ID, a previous entry must already
// exist, and thus we'll only update it.
await iapRepository.updatePurchase(purchaseData);
}
return true;
} on ap.DetailedApiRequestError catch (e) {
print(
'Error on handle NonSubscription: $e\n'
'JSON: ${e.jsonResponse}',
);
} catch (e) {
print('Error on handle NonSubscription: $e\n');
}
return false;
}
सदस्यता खरीदने के हैंडलर को भी इसी तरह अपडेट किया जा सकता है:
lib/google_play_purchase_handler.dart
/// Handle subscription purchases.
///
/// Retrieves the purchase status from Google Play and updates
/// the Firestore Database accordingly.
@override
Future<bool> handleSubscription({
required String? userId,
required ProductData productData,
required String token,
}) async {
print(
'GooglePlayPurchaseHandler.handleSubscription'
'($userId, ${productData.productId}, ${token.substring(0, 5)}...)',
);
try {
// Verify purchase with Google
final response = await androidPublisher.purchases.subscriptions.get(
androidPackageId,
productData.productId,
token,
);
print('Subscription response: ${response.toJson()}');
// Make sure an order ID exists
if (response.orderId == null) {
print('Could not handle purchase without order id');
return false;
}
final orderId = extractOrderId(response.orderId!);
final purchaseData = SubscriptionPurchase(
purchaseDate: DateTime.fromMillisecondsSinceEpoch(
int.parse(response.startTimeMillis ?? '0'),
),
orderId: orderId,
productId: productData.productId,
status: _subscriptionStatusFrom(response.paymentState),
userId: userId,
iapSource: IAPSource.googleplay,
expiryDate: DateTime.fromMillisecondsSinceEpoch(
int.parse(response.expiryTimeMillis ?? '0'),
),
);
// Update the database
if (userId != null) {
// If we know the userId,
// update the existing purchase or create it if it does not exist.
await iapRepository.createOrUpdatePurchase(purchaseData);
} else {
// If we don't know the user ID, a previous entry must already
// exist, and thus we'll only update it.
await iapRepository.updatePurchase(purchaseData);
}
return true;
} on ap.DetailedApiRequestError catch (e) {
print(
'Error on handle Subscription: $e\n'
'JSON: ${e.jsonResponse}',
);
} catch (e) {
print('Error on handle Subscription: $e\n');
}
return false;
}
}
ऑर्डर आईडी को पार्स करने के लिए, यहां दिया गया तरीका जोड़ें. साथ ही, खरीदारी की स्थिति को पार्स करने के लिए दो तरीके जोड़ें.
lib/google_play_purchase_handler.dart
NonSubscriptionStatus _nonSubscriptionStatusFrom(int? state) {
return switch (state) {
0 => NonSubscriptionStatus.completed,
2 => NonSubscriptionStatus.pending,
_ => NonSubscriptionStatus.cancelled,
};
}
SubscriptionStatus _subscriptionStatusFrom(int? state) {
return switch (state) {
// Payment pending
0 => SubscriptionStatus.pending,
// Payment received
1 => SubscriptionStatus.active,
// Free trial
2 => SubscriptionStatus.active,
// Pending deferred upgrade/downgrade
3 => SubscriptionStatus.pending,
// Expired or cancelled
_ => SubscriptionStatus.expired,
};
}
/// If a subscription suffix is present (..#) extract the orderId.
String extractOrderId(String orderId) {
final orderIdSplit = orderId.split('..');
if (orderIdSplit.isNotEmpty) {
orderId = orderIdSplit[0];
}
return orderId;
}
अब Google Play पर की गई आपकी खरीदारी की पुष्टि हो जाएगी और उसे डेटाबेस में सेव कर दिया जाएगा.
इसके बाद, iOS के लिए App Store से की गई खरीदारी पर जाएं.
iOS पर की गई खरीदारी की पुष्टि करना: खरीदारी के हैंडलर को लागू करना
App Store से की गई खरीदारी की पुष्टि करने के लिए, तीसरे पक्ष का एक Dart पैकेज उपलब्ध है. इसका नाम app_store_server_sdk है. इससे पुष्टि करने की प्रोसेस आसान हो जाती है.
सबसे पहले, ITunesApi इंस्टेंस बनाएं. सैंडबॉक्स कॉन्फ़िगरेशन का इस्तेमाल करें. साथ ही, गड़बड़ी को डीबग करने के लिए लॉगिंग की सुविधा चालू करें.
lib/app_store_purchase_handler.dart
final _iTunesAPI = ITunesApi(
ITunesHttpClient(ITunesEnvironment.sandbox(), loggingEnabled: true),
);
अब Google Play API के उलट, App Store सदस्यता और बिना सदस्यता वाले ऐप्लिकेशन, दोनों के लिए एक ही एपीआई एंडपॉइंट का इस्तेमाल करता है. इसका मतलब है कि दोनों हैंडलर के लिए एक ही लॉजिक का इस्तेमाल किया जा सकता है. इन्हें एक साथ मर्ज करें, ताकि ये एक ही तरीके से लागू हों:
lib/app_store_purchase_handler.dart
@override
Future<bool> handleNonSubscription({
required String userId,
required ProductData productData,
required String token,
}) {
return handleValidation(userId: userId, token: token);
}
@override
Future<bool> handleSubscription({
required String userId,
required ProductData productData,
required String token,
}) {
return handleValidation(userId: userId, token: token);
}
/// Handle purchase validation.
Future<bool> handleValidation({
required String userId,
required String token,
}) async {
// See next step
}
अब, handleValidation लागू करें:
lib/app_store_purchase_handler.dart
/// Handle purchase validation.
Future<bool> handleValidation({
required String userId,
required String token,
}) async {
print('AppStorePurchaseHandler.handleValidation');
final response = await _iTunesAPI.verifyReceipt(
password: appStoreSharedSecret,
receiptData: token,
);
print('response: $response');
if (response.status == 0) {
print('Successfully verified purchase');
final receipts = response.latestReceiptInfo ?? [];
for (final receipt in receipts) {
final product = productDataMap[receipt.productId];
if (product == null) {
print('Error: Unknown product: ${receipt.productId}');
continue;
}
switch (product.type) {
case ProductType.nonSubscription:
await iapRepository.createOrUpdatePurchase(
NonSubscriptionPurchase(
userId: userId,
productId: receipt.productId ?? '',
iapSource: IAPSource.appstore,
orderId: receipt.originalTransactionId ?? '',
purchaseDate: DateTime.fromMillisecondsSinceEpoch(
int.parse(receipt.originalPurchaseDateMs ?? '0'),
),
type: product.type,
status: NonSubscriptionStatus.completed,
),
);
break;
case ProductType.subscription:
await iapRepository.createOrUpdatePurchase(
SubscriptionPurchase(
userId: userId,
productId: receipt.productId ?? '',
iapSource: IAPSource.appstore,
orderId: receipt.originalTransactionId ?? '',
purchaseDate: DateTime.fromMillisecondsSinceEpoch(
int.parse(receipt.originalPurchaseDateMs ?? '0'),
),
type: product.type,
expiryDate: DateTime.fromMillisecondsSinceEpoch(
int.parse(receipt.expiresDateMs ?? '0'),
),
status: SubscriptionStatus.active,
),
);
break;
}
}
return true;
} else {
print('Error: Status: ${response.status}');
return false;
}
}
अब App Store से की गई आपकी खरीदारी की पुष्टि हो जाएगी और उसे डेटाबेस में सेव कर लिया जाएगा!
बैकएंड चलाना
इस चरण में, /verifypurchase एंडपॉइंट को दिखाने के लिए, dart bin/server.dart को चलाया जा सकता है.
$ dart bin/server.dart Serving at http://0.0.0.0:8080
11. खरीदारी का हिसाब रखना
हमारा सुझाव है कि आप बैकएंड सेवा में, उपयोगकर्ताओं की खरीदारी को ट्रैक करें. ऐसा इसलिए है, क्योंकि आपका बैकएंड स्टोर से मिले इवेंट का जवाब दे सकता है. इसलिए, कैश मेमोरी की वजह से पुरानी जानकारी मिलने की संभावना कम होती है. साथ ही, इसमें छेड़छाड़ होने की संभावना भी कम होती है.
सबसे पहले, आपने जो Dart बैकएंड बनाया है उसकी मदद से, स्टोर इवेंट को बैकएंड पर प्रोसेस करने की सुविधा सेट अप करें.
बैकएंड पर स्टोर इवेंट प्रोसेस करना
स्टोर, आपके बैकएंड को बिलिंग से जुड़े किसी भी इवेंट के बारे में सूचना दे सकते हैं. जैसे, सदस्यता के रिन्यू होने पर. इन इवेंट को अपने बैकएंड में प्रोसेस किया जा सकता है, ताकि आपके डेटाबेस में खरीदारी की जानकारी अप-टू-डेट रहे. इस सेक्शन में, Google Play Store और Apple App Store, दोनों के लिए इसे सेट अप करें.
Google Play के बिलिंग इवेंट प्रोसेस करना
Google Play, बिलिंग इवेंट की जानकारी देता है. इसके लिए, वह Cloud Pub/Sub विषय का इस्तेमाल करता है. ये मैसेज कतारें होती हैं. इन पर मैसेज पब्लिश किए जा सकते हैं. साथ ही, इनसे मैसेज इस्तेमाल किए जा सकते हैं.
यह सुविधा सिर्फ़ Google Play पर उपलब्ध है. इसलिए, आपको इसे GooglePlayPurchaseHandler में शामिल करना होगा.
lib/google_play_purchase_handler.dart खोलें और PubsubApi इंपोर्ट करें:
lib/google_play_purchase_handler.dart
import 'package:googleapis/pubsub/v1.dart' as pubsub;
इसके बाद, PubsubApi को GooglePlayPurchaseHandler में पास करें. साथ ही, क्लास कंस्ट्रक्टर में बदलाव करके Timer बनाएं. इसके लिए, यहां दिया गया तरीका अपनाएं:
lib/google_play_purchase_handler.dart
class GooglePlayPurchaseHandler extends PurchaseHandler {
final ap.AndroidPublisherApi androidPublisher;
final IapRepository iapRepository;
final pubsub.PubsubApi pubsubApi; // new
GooglePlayPurchaseHandler(
this.androidPublisher,
this.iapRepository,
this.pubsubApi, // new
) {
// Poll messages from Pub/Sub every 10 seconds
Timer.periodic(Duration(seconds: 10), (_) {
_pullMessageFromPubSub();
});
}
Timer को हर दस सेकंड में _pullMessageFromPubSub तरीके को कॉल करने के लिए कॉन्फ़िगर किया गया है. अपनी पसंद के मुताबिक, अवधि में बदलाव किया जा सकता है.
इसके बाद, _pullMessageFromPubSub बनाएं
lib/google_play_purchase_handler.dart
/// Process messages from Google Play
/// Called every 10 seconds
Future<void> _pullMessageFromPubSub() async {
print('Polling Google Play messages');
final request = pubsub.PullRequest(maxMessages: 1000);
final topicName =
'projects/$googleCloudProjectId/subscriptions/$googlePlayPubsubBillingTopic-sub';
final pullResponse = await pubsubApi.projects.subscriptions.pull(
request,
topicName,
);
final messages = pullResponse.receivedMessages ?? [];
for (final message in messages) {
final data64 = message.message?.data;
if (data64 != null) {
await _processMessage(data64, message.ackId);
}
< }
> }
Futurevoid _processMessage(String data64, String? ackId) async {
final dataRaw = utf8.decode(base64Decode(data64));
print('Received data: $dataRaw');
final dynamic data = jsonDecode(dataRaw);
if (data['testNotification'] != null) {
print('Skip test messages');
if (ackId != null) {
await _ackMessage(ackId);
}
return;
}
final dynamic subscriptionNotification = data['subscriptionNotification'];
final dynamic oneTimeProductNotification =
data['oneTimeProductNotification'];
if (subscriptionNotification != null) {
print('Processing Subscription');
final subscriptionId =
subscriptionNotification['subscriptionId'] as String;
final purchaseToken = subscriptionNotification['purchaseToken'] as String;
final productData = productDataMap[subscriptionId]!;
final result = await handleSubscription(
userId: null,
pr&&oductData: productData,
token: purchaseToken,
);
if (result ackId != null) {
await _ackMessage(ackId);
}
} else if (oneTimeProductNotification != null) {
print('Processing NonSubscription');
final sku = oneTimeProductNotification['sku'] as String;
final purchaseToken =
oneTimeProductNotification['purchaseToken'] as String;
final productData = productDataMap[sku]!;
final result = await handleNonSubscription(
&&userId: null,
productData: productData,
token: purchaseToken,
);
if (result ackId != null) {
await _ackMessage(ackId<);
> }
} else {
print('invalid data');
}
}
/// ACK Messages from Pub/Sub
Futurevoid _ackMessage(String id) async {
print('ACK Message');
final request = pubsub.AcknowledgeRequest(ackIds: [id]);
final subscriptionName =
'projects/$googleCloudProjectId/subscriptions/$googlePlayPubsubBillingTopic-sub';
await pubsubApi.projects.subscriptions.acknowledge(
request,
subscriptionName,
);
}
आपने अभी जो कोड जोड़ा है वह हर दस सेकंड में Google Cloud के Pub/Sub विषय से कम्यूनिकेट करता है और नए मैसेज के लिए पूछता है. इसके बाद, हर मैसेज को _processMessage तरीके से प्रोसेस करता है.
यह तरीका, आने वाले मैसेज को डिकोड करता है. साथ ही, हर खरीदारी के बारे में अपडेट की गई जानकारी हासिल करता है. इसमें सदस्यताएं और बिना सदस्यता वाली खरीदारी, दोनों शामिल हैं. अगर ज़रूरी हो, तो यह मौजूदा handleSubscription या handleNonSubscription को कॉल करता है.
हर मैसेज की पुष्टि _askMessage तरीके से की जानी चाहिए.
इसके बाद, server.dart फ़ाइल में ज़रूरी डिपेंडेंसी जोड़ें. क्रेडेंशियल कॉन्फ़िगरेशन में PubsubApi.cloudPlatformScope जोड़ें:
bin/server.dart
import 'package:googleapis/pubsub/v1.dart' as pubsub; // Add this import
final clientGooglePlay = await auth
.clientViaServiceAccount(clientCredentialsGooglePlay, [
ap.AndroidPublisherApi.androidpublisherScope,
pubsub.PubsubApi.cloudPlatformScope, // Add this line
]);
इसके बाद, PubsubApi इंस्टेंस बनाएं:
bin/server.dart
final pubsubApi = pubsub.PubsubApi(clientGooglePlay);
आखिर में, इसे GooglePlayPurchaseHandler कंस्ट्रक्टर को पास करें:
bin/server.dart
return {
'google_play': GooglePlayPurchaseHandler(
androidPublisher,
iapRepository,
pubsubApi, // Add this line
),
'app_store': AppStorePurchaseHandler(
iapRepository,
),
};
Google Play का सेटअप
आपने pub/sub विषय से बिलिंग इवेंट इस्तेमाल करने के लिए कोड लिखा है, लेकिन आपने pub/sub विषय नहीं बनाया है. साथ ही, न ही कोई बिलिंग इवेंट पब्लिश किया जा रहा है. इसे सेट अप करने का समय आ गया है.
सबसे पहले, Pub/Sub विषय बनाएं:
constants.dartमें मौजूदgoogleCloudProjectIdकी वैल्यू को अपने Google Cloud प्रोजेक्ट के आईडी पर सेट करें.- Google Cloud Console पर Cloud Pub/Sub पेज पर जाएं.
- पक्का करें कि आप अपने Firebase प्रोजेक्ट पर हों. इसके बाद, + विषय बनाएं पर क्लिक करें.

- नए विषय को वही नाम दें जो
constants.dartमेंgooglePlayPubsubBillingTopicके लिए सेट किया गया है. इस मामले में, इसेplay_billingनाम दें. अगर आपने कोई दूसरा विकल्प चुना है, तोconstants.dartको अपडेट करना न भूलें. विषय बनाएं.
- Pub/Sub विषयों की सूची में, अभी बनाए गए विषय के लिए तीन वर्टिकल बिंदुओं पर क्लिक करें. इसके बाद, अनुमतियां देखें पर क्लिक करें.

- दाईं ओर मौजूद साइडबार में, प्रिंसिपल जोड़ें को चुनें.
- यहां
google-play-developer-notifications@system.gserviceaccount.comको जोड़ें और उसे Pub/Sub पब्लिशर की भूमिका दें.
- अनुमति में किए गए बदलावों को सेव करें.
- आपने अभी-अभी जो विषय बनाया है उसका विषय का नाम कॉपी करें.
- Play Console को फिर से खोलें और सभी ऐप्लिकेशन सूची से अपना ऐप्लिकेशन चुनें.
- नीचे की ओर स्क्रोल करें और कमाई करें > कमाई करने के लिए सेटअप पर जाएं.
- पूरा विषय भरें और अपने बदलाव सेव करें.

Google Play की बिलिंग से जुड़े सभी इवेंट अब इस विषय पर पब्लिश किए जाएंगे.
App Store के बिलिंग इवेंट प्रोसेस करना
इसके बाद, App Store के बिलिंग इवेंट के लिए भी ऐसा ही करें. App Store पर खरीदारी के अपडेट को लागू करने के दो असरदार तरीके हैं. पहला तरीका यह है कि Apple को एक वेबहुक उपलब्ध कराया जाए. Apple इसका इस्तेमाल आपके सर्वर से कम्यूनिकेट करने के लिए करता है. दूसरा तरीका यह है कि App Store Server API से कनेक्ट करके, सदस्यता की जानकारी मैन्युअल तरीके से हासिल की जाए. इस कोडलैब में आपको यही तरीका मिलेगा.
इस कोडलैब में दूसरे समाधान पर इसलिए फ़ोकस किया गया है, क्योंकि वेबुक को लागू करने के लिए आपको अपने सर्वर को इंटरनेट पर उपलब्ध कराना होगा.
प्रोडक्शन एनवायरमेंट में, आपको इन दोनों को इस्तेमाल करना चाहिए. App Store से इवेंट पाने के लिए वेबहुक और अगर आपसे कोई इवेंट छूट गया है या आपको सदस्यता की स्थिति की दोबारा जांच करनी है, तो सर्वर एपीआई का इस्तेमाल करें.
lib/app_store_purchase_handler.dart खोलें और AppStoreServerAPI डिपेंडेंसी जोड़ें:
lib/app_store_purchase_handler.dart
final AppStoreServerAPI appStoreServerAPI; // Add this member
AppStorePurchaseHandler(
this.iapRepository,
this.appStoreServerAPI, // And this parameter
);
कंस्ट्रक्टर में बदलाव करके, एक टाइमर जोड़ें. यह टाइमर, _pullStatus तरीके को कॉल करेगा. यह टाइमर, हर 10 सेकंड में _pullStatus तरीके को कॉल करेगा. इस टाइमर की अवधि को अपनी ज़रूरत के हिसाब से बदला जा सकता है.
lib/app_store_purchase_handler.dart
AppStorePurchaseHandler(this.iapRepository, this.appStoreServerAPI) {
// Poll Subscription status every 10 seconds.
Timer.periodic(Duration(seconds: 10), (_) {
_pullStatus();
});
}
इसके बाद, _pullStatus तरीके को इस तरह बनाएं:
lib/app_store_purchase_handler.dart
/// Request the App Store for the latest subscription status.
/// Updates all App Store subscriptions in the database.
/// NOTE: This code only handles when a subscription expires as example.
Future<void> _pullStatus() async {
print('Polling App Store');
final purchases = await iapRepository.getPurchases();
// filter for App Store subscriptions
final appStoreSubscriptions = purchases.where(
(el>ement) =
element.type == ProductType.subsc&&ription
element.iapSource == IAPSource.appstore,
);
for (final purchase in appStoreSubscriptions) {
final status = await appStoreServerAPI.getAllSubscriptionStatuses(
purchase.orderId,
);
// Obtain all subscriptions for the order ID.
for (final subscription in status.data) {
// Last transaction contains the subscription status.
for (final transaction in subscription.lastTransactions) {
final expirationDate = DateTime.fromMillisecondsSinceEpoch(
transaction.transactionInfo.expiresDate ?? 0,
);
// Check if subscription has expired.
final isExpired = expirationDate.isBefore(DateTime.now());
print('Expiration Date: $expirationDate - isExpired: $isExpired');
// Update the subscription status with the new expiration date and status.
await iapRepository.updatePurchase(
SubscriptionPurchase(
userId: null,
productId: transaction.transactionInfo.productId,
iapSource: IAPSource.appstore,
orderId: transaction.originalTransactionId,
purchaseDate: DateTime.fromMillisecondsSinceEpoch(
transaction.transactionInfo.originalPurchaseDate,
),
type: ProductType.subscription,
expiryDate: expirationDate,
status: isExpired
? SubscriptionStatus.expired
: SubscriptionStatus.active,
),
);
}
}
}
}
यह तरीका इस तरह काम करता है:
- यह IapRepository का इस्तेमाल करके, Firestore से चालू सदस्यताओं की सूची हासिल करता है.
- यह हर ऑर्डर के लिए, App Store Server API से सदस्यता की स्थिति का अनुरोध करता है.
- यह कुकी, सदस्यता खरीदने के लिए किए गए आखिरी लेन-देन की जानकारी इकट्ठा करती है.
- यह कुकी, समयसीमा खत्म होने की तारीख की जांच करती है.
- यह कुकी, Firestore पर सदस्यता की स्थिति को अपडेट करती है. अगर सदस्यता खत्म हो गई है, तो उसे 'खत्म हो गई' के तौर पर मार्क कर दिया जाएगा.
आखिर में, App Store Server API के ऐक्सेस को कॉन्फ़िगर करने के लिए, सभी ज़रूरी कोड जोड़ें:
bin/server.dart
import 'package:app_store_server_sdk/app_store_server_sdk.dart'; // Add this import
import 'package:firebase_backend_dart/constants.dart'; // And this one.
// add from here
final subscriptionKeyAppStore = File(
'assets/SubscriptionKey.p8',
).readAsStringSync();
// Configure Apple Store API access
var appStoreEnvironment = AppStoreEnvironment.sandbox(
bundleId: bundleId,
issuerId: appStoreIssuerId,
keyId: appStoreKeyId,
privateKey: subscriptionKeyAppStore,
);
// Stored token for Apple Store API access, if available
final file = File('assets/appstore.token');
String? appStor&&eToken;
if (file.>existsSync() file.lengthSync() 0) {
appStoreToken = file.readAsStringSync();
}
final appStoreServerAPI = AppStoreServerAPI(
AppStoreServerHttpClient(
appStoreEnvironment,
jwt: appStoreToken,
jwtTokenUpdatedCallback: (token) {
file.writeAsStringSync(token);
},
),
);
// to here
return {
'google_play': GooglePlayPurchaseHandler(
androidPublisher,
iapRepository,
pubsubApi,
),
'app_store': AppStorePurchaseHandler(
iapRepository,
appStoreServerAPI, // Add this argument
),
};
App Store का सेटअप
इसके बाद, App Store सेट अप करें:
- App Store Connect में लॉगिन करें. इसके बाद, उपयोगकर्ता और ऐक्सेस को चुनें.
- इंटिग्रेशन > कुंजियां > इन-ऐप्लिकेशन खरीदारी पर जाएं.
- नया फ़ीड जोड़ने के लिए, "प्लस" आइकॉन पर टैप करें.
- इसे कोई नाम दें, जैसे कि "कोड लैब की कुंजी".
- कुंजी वाली p8 फ़ाइल डाउनलोड करें.
- इसे ऐसेट फ़ोल्डर में
SubscriptionKey.p8नाम से कॉपी करें. - नई बनाई गई कुंजी से कुंजी आईडी कॉपी करें और उसे
lib/constants.dartफ़ाइल मेंappStoreKeyIdकॉन्स्टेंट पर सेट करें. - कुंजियों की सूची में सबसे ऊपर मौजूद, जारी करने वाले का आईडी कॉपी करें. इसके बाद, इसे
lib/constants.dartफ़ाइल मेंappStoreIssuerIdपर सेट करें.

डिवाइस पर की गई खरीदारी को ट्रैक करना
सर्वर साइड पर अपनी खरीदारी को ट्रैक करना सबसे सुरक्षित तरीका है, क्योंकि क्लाइंट को सुरक्षित करना मुश्किल होता है. हालांकि, आपको क्लाइंट को जानकारी वापस भेजने का कोई तरीका अपनाना होगा, ताकि ऐप्लिकेशन सदस्यता की स्थिति की जानकारी के आधार पर कार्रवाई कर सके. Firestore में खरीदारी की जानकारी सेव करके, डेटा को क्लाइंट के साथ सिंक किया जा सकता है. साथ ही, इसे अपने-आप अपडेट किया जा सकता है.
आपने ऐप्लिकेशन में पहले से ही IAPRepo को शामिल किया है. यह Firestore रिपॉज़िटरी है. इसमें List<PastPurchase> purchases में उपयोगकर्ता की खरीदारी का पूरा डेटा शामिल होता है. इस रिपॉज़िटरी में hasActiveSubscription, भी शामिल है. यह तब सही होता है, जब productId storeKeySubscription का इस्तेमाल करके खरीदारी की गई हो और उसकी स्थिति 'समयसीमा खत्म नहीं हुई' हो. जब उपयोगकर्ता लॉग इन नहीं होता है, तो सूची खाली होती है.
lib/repo/iap_repo.dart
void updatePurchases() {
_purchaseSubscription?.cancel();
var user = _user;
if (user == null) {
purchases = [];
hasActiveSubscription = false;
hasUpgrade = false;
return;
}
var purchaseStream = _firestore
.collection('purchases')
.where('userId', isEqualTo: user.uid)
.snapshots();
_purchaseSubscription = purchaseStream.listen((snapshot) {
purchases = snapshot.docs.map((document) {
var data = document.data();
return PastPurchase.fromJson(data);
}).toList();
hasActiveSubscription = purchases.any(
> (element) =
element.productId == store&&KeySubscription
element.status != Status.expired,
);
hasUpgrade = purchases.any(
> (element) = element.productId == storeKeyUpgrade,
);
notifyListeners();
});
}
खरीदारी से जुड़ा सारा लॉजिक DashPurchases क्लास में होता है. यहीं से सदस्यताएं लागू या हटाई जानी चाहिए. इसलिए, क्लास में iapRepo को प्रॉपर्टी के तौर पर जोड़ें और कंस्ट्रक्टर में iapRepo असाइन करें. इसके बाद, कंस्ट्रक्टर में सीधे तौर पर लिसनर जोड़ें और dispose() तरीके से लिसनर हटाएं. शुरुआत में, लिसनर सिर्फ़ एक खाली फ़ंक्शन हो सकता है. IAPRepo एक ChangeNotifier है. साथ ही, Firestore में खरीदारी से जुड़े डेटा में बदलाव होने पर, notifyListeners() को कॉल किया जाता है. इसलिए, खरीदे गए प्रॉडक्ट में बदलाव होने पर, purchasesUpdate() तरीके को हमेशा कॉल किया जाता है.
lib/logic/dash_purchases.dart
import '../repo/iap_repo.dart'; // Add this import
class DashPurchases extends ChangeNotifier {
DashCounter counter;
FirebaseNotifier firebaseNotifier;
StoreState storeState = StoreState.loading;
late StreamSubs<crip<tionListPurchas>>eDetails _subscription<;
ListPurchasabl>eProduct products = [];
IAPRepo iapRepo; // Add this line
bool get beautifi>edDash = _beautifiedDashUpgrade;
bool _beautifiedDashUpgrade = false;
final iapConnection = IAPConnection.instance;
// Add this.iapRepo as a parameter
DashPurchases(this.counter, this.firebaseNotifier, this.iapRepo) {
final purchaseUpdated = iapConnection.purchaseStream;
_subscription = purchaseUpdated.listen(
_onPurchaseUpdate,
onDone: _updateStreamOnDone,
onError: _updateStreamOnError,
);
iapRepo.addListener(purchasesUpdate);
loadPurchases();
}
< Fu>turevoid loadPurchases() async {
// Elided.
}
@override
void dispose() {
_subscription.cancel();
iapRepo.removeListener(purchasesUpdate); // Add this line
super.dispose();
}
void purchasesUpdate() {
//TODO manage updates
}
इसके बाद, main.dart. में कंस्ट्रक्टर को IAPRepo दें. context.read का इस्तेमाल करके रिपॉज़िटरी पाई जा सकती है, क्योंकि यह पहले से ही Provider में बनाई गई है.
lib/main.dart
ChangeNotifierProvider<DashPurchases>(
create: (context) => DashPurchases(
context.read<DashCounter>(),
context.read<FirebaseNotifier>(),
context.read<IAPRepo>(), // Add this line
),
lazy: false,
),
इसके बाद, purchaseUpdate() फ़ंक्शन के लिए कोड लिखें. dash_counter.dart, में, applyPaidMultiplier और removePaidMultiplier तरीके, मल्टीप्लायर को क्रमशः 10 या 1 पर सेट करते हैं. इसलिए, आपको यह देखने की ज़रूरत नहीं है कि सदस्यता पहले से लागू है या नहीं. सदस्यता की स्थिति बदलने पर, खरीदारी के लिए उपलब्ध प्रॉडक्ट की स्थिति को भी अपडेट करें. इससे खरीदारी वाले पेज पर यह दिखाया जा सकेगा कि सदस्यता पहले से ही चालू है. अपग्रेड खरीदा गया है या नहीं, इसके आधार पर _beautifiedDashUpgrade प्रॉपर्टी सेट करें.
lib/logic/dash_purchases.dart
void purchasesUpdate() {
var subscriptions = <PurchasableProduct>[];
var upgrades = <PurchasableProduct>[];
// Get a list of purchasable products for the subscription and upgrade.
// This should be 1 per type.
if (products.isNotEmpty) {
subscriptions = products
.where((element) => element.productDetails.id == storeKeySubscription)
.toList();
upgrades = products
.where((element) => element.productDetails.id == storeKeyUpgrade)
.toList();
}
// Set the subscription in the counter logic and show/hide purchased on the
// purchases page.
if (iapRepo.hasActiveSubscription) {
counter.applyPaidMultiplier();
for (var element in subscriptions) {
_updateStatus(element, ProductStatus.purchased);
}
} else {
counter.removePaidMultiplier();
for (var element in subscriptions) {
_updateStatus(element, ProductStatus.purchasable);
}
}
// Set the Dash beautifier and show/hide purchased on
// the purchases page.
if (iapRepo.hasUpgrade != _beautifiedDashUpgrade) {
_beautifiedDashUpgrade = iapRepo.hasUpgrade;
for (var element in upgrades) {
_updateStatus(
element,
_beautifiedDashUpgrade
? ProductStatus.purchased
: ProductStatus.purchasable,
);
}
notifyListeners();
}
}
void _updateStatus(PurchasableProduct product, ProductStatus status) {
if (product.status != ProductStatus.purchased) {
product.status = ProductStatus.purchased;
notifyListeners();
}
}
अब आपने यह पक्का कर लिया है कि बैकएंड सेवा में सदस्यता और अपग्रेड का स्टेटस हमेशा अप-टू-डेट रहे. साथ ही, यह ऐप्लिकेशन के साथ सिंक हो. ऐप्लिकेशन, सदस्यता और अपग्रेड की सुविधाओं को आपके Dash clicker गेम पर लागू करता है.
12. सब हो गया!
बधाई हो!!! आपने कोडलैब पूरा कर लिया है. इस कोडलैब का पूरा कोड,
complete फ़ोल्डर में देखा जा सकता है.
ज़्यादा जानने के लिए, अन्य Flutter कोडलैब आज़माएं.