1. परिचय
किसी Flutter ऐप्लिकेशन में इन-ऐप्लिकेशन खरीदारी की सुविधा जोड़ने के लिए, App Store और Play Store को सही तरीके से सेट अप करना, खरीदारी की पुष्टि करना, और ज़रूरी अनुमतियां देना ज़रूरी है. जैसे, सदस्यता के फ़ायदे.
इस कोडलैब में, आपको दिए गए ऐप्लिकेशन में तीन तरह की इन-ऐप्लिकेशन खरीदारी जोड़ने का तरीका बताया जाएगा. साथ ही, Firebase के साथ Dart बैकएंड का इस्तेमाल करके, इन खरीदारी की पुष्टि करने का तरीका बताया जाएगा. Dash Clicker ऐप्लिकेशन में एक ऐसा गेम शामिल है जिसमें Dash के मैस्कॉट को मुद्रा के तौर पर इस्तेमाल किया जाता है. आपको खरीदारी के ये विकल्प जोड़ने होंगे:
- एक बार में 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 प्रोजेक्ट सबसे ऊपर होता है. साथ ही, Flutter, Runner, और Products टारगेट, Runner प्रोजेक्ट के नीचे होते हैं. प्रोजेक्ट की सेटिंग में बदलाव करने के लिए, Runner पर दो बार क्लिक करें. इसके बाद, Signing & Capabilities पर क्लिक करें. अपनी टीम सेट करने के लिए, टीम फ़ील्ड में वह बंडल आइडेंटिफ़ायर डालें जिसे आपने अभी चुना है.
अब Xcode को बंद करें और Android Studio पर वापस जाएं. इसके बाद, Android के लिए कॉन्फ़िगरेशन पूरा करें. इसके लिए, android/app,
में जाकर build.gradle.kts
फ़ाइल खोलें. इसके बाद, अपने applicationId
(नीचे दिए गए स्क्रीनशॉट में 24वीं लाइन पर) को ऐप्लिकेशन आईडी में बदलें. यह आईडी, iOS बंडल आइडेंटिफ़ायर के जैसा ही होता है. ध्यान दें कि 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 डेवलपर प्रोग्राम में रजिस्टर करें.
पैसे चुकाकर डाउनलोड किए जाने वाले ऐप्लिकेशन के समझौते
इन-ऐप्लिकेशन खरीदारी की सुविधा का इस्तेमाल करने के लिए, आपके पास 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
या अन्य मुद्रा में इसके बराबर की कीमत पर सेट करें.खरीदारी के लिए, अपनी स्थानीय भाषाएं जोड़ें. खरीदारी को
Spring is in the air
के तौर पर कॉल करें. साथ ही,2000 dashes fly out
को ब्यौरे के तौर पर इस्तेमाल करें.समीक्षा का स्क्रीनशॉट जोड़ें. जब तक प्रॉडक्ट को समीक्षा के लिए नहीं भेजा जाता, तब तक कॉन्टेंट से कोई फ़र्क़ नहीं पड़ता. हालांकि, प्रॉडक्ट को "सबमिट करने के लिए तैयार" स्थिति में होना ज़रूरी है. यह तब ज़रूरी होता है, जब ऐप्लिकेशन App Store से प्रॉडक्ट फ़ेच करता है.
dash_upgrade_3d
को नॉन-कन्ज़्यूमेबल के तौर पर सेट अप करें. प्रॉडक्ट आईडी के तौर परdash_upgrade_3d
का इस्तेमाल करें. रेफ़रंस का नामdash upgrade 3d
पर सेट करें. खरीदारी को3D Dash
के तौर पर कॉल करें. साथ ही,Brings your dash back to the future
को ब्यौरे के तौर पर इस्तेमाल करें. किराया$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
Future<bool> buyConsumable({
required PurchaseParam purchaseParam,
bool autoConsume = true,
}) {
return Future.value(false);
}
@override
Future<bool> buyNonConsumable({required PurchaseParam purchaseParam}) {
return Future.value(false);
}
@override
Future<void> completePurchase(PurchaseDetails purchase) {
return Future.value();
}
@override
Future<bool> isAvailable() {
return Future.value(false);
}
@override
Future<ProductDetailsResponse> queryProductDetails(Set<String> identifiers) {
return Future.value(
ProductDetailsResponse(productDetails: [], notFoundIDs: []),
);
}
@override
T getPlatformAddition<T extends InAppPurchasePlatformAddition?>() {
// TODO: implement getPlatformAddition
throw UnimplementedError();
}
@override
Stream<List<PurchaseDetails>> get purchaseStream =>
Stream.value(<PurchaseDetails>[]);
@override
Future<void> restorePurchases({String? applicationUserName}) {
// TODO: implement restorePurchases
throw UnimplementedError();
}
@override
Future<String> 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;
StoreState storeState = StoreState.available;
late StreamSubscription<List<PurchaseDetails>> _subscription; // Add this line
List<PurchasableProduct> products = [
PurchasableProduct(
'Spring is in the air',
'Many dashes flying out from their nests',
'\$0.99',
),
PurchasableProduct(
'Jet engine',
'Doubles you clicks per second for a day',
'\$1.99',
),
];
bool get beautifiedDash => false;
final iapConnection = IAPConnection.instance; // And this line
DashPurchases(this.counter);
Future<void> buy(PurchasableProduct product) async {
product.status = ProductStatus.pending;
notifyListeners();
await Future<void>.delayed(const Duration(seconds: 5));
product.status = ProductStatus.purchased;
notifyListeners();
await Future<void>.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.notAvailable; // Modify this line
late StreamSubscription<List<PurchaseDetails>> _subscription;
List<PurchasableProduct> products = [
PurchasableProduct(
'Spring is in the air',
'Many dashes flying out from their nests',
'\$0.99',
),
PurchasableProduct(
'Jet engine',
'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.cancel();
super.dispose();
} // To here.
Future<void> buy(PurchasableProduct product) async {
product.status = ProductStatus.pending;
notifyListeners();
await Future<void>.delayed(const Duration(seconds: 5));
product.status = ProductStatus.purchased;
notifyListeners();
await Future<void>.delayed(const Duration(seconds: 5));
product.status = ProductStatus.purchasable;
notifyListeners();
}
// Add from here
void _onPurchaseUpdate(List<PurchaseDetails> 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 description => 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';
// ...
Future<void> loadPurchases() async {
final available = await iapConnection.isAvailable();
if (!available) {
storeState = StoreState.notAvailable;
notifyListeners();
return;
}
const ids = <String>{
storeKeyConsumable,
storeKeySubscription,
storeKeyUpgrade,
};
final response = await iapConnection.queryProductDetails(ids);
products = response.productDetails
.map((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
डार्ट बैकएंड, एपीआई एंडपॉइंट को दिखाने के लिए shelf
और shelf_router
का इस्तेमाल करता है. डिफ़ॉल्ट रूप से, सर्वर कोई भी रास्ता नहीं दिखाता है. बाद में, खरीदारी की पुष्टि करने की प्रोसेस को मैनेज करने के लिए, एक रूट बनाया जाएगा.
स्टार्टर कोड में पहले से शामिल एक हिस्सा, IapRepository
में lib/iap_repository.dart
है. 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 सब्सक्राइबर की भूमिका चुनें और हो गया पर क्लिक करें.
- खाता बन जाने के बाद, कुंजियां मैनेज करें पर जाएं.
- कुंजी जोड़ें > नई कुंजी बनाएं चुनें.
- 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.dashclicker
appStoreSharedSecret
: खरीदारी की पुष्टि करने के लिए, 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) { // Update from here
var firebaseNotifier = context.watch<FirebaseNotifier>();
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 storeState = StoreState.loading;
late StreamSubscription<List<PurchaseDetails>> _subscription;
List<PurchasableProduct> 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? get user => FirebaseAuth.instance.currentUser; // Add this line
Future<void> load() async {
// ...
DashPurchases
क्लास में _verifyPurchase
फ़ंक्शन जोड़ें. यह 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
Future<bool> handleNonSubscription({
required String userId,
required ProductData productData,
required String token,
});
/// Verify if subscription purchase (aka non-consumable) is valid
/// and update the database
Future<bool> 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.androidPublisher, this.iapRepository);
@override
Future<bool> handleNonSubscription({
required String? userId,
required ProductData productData,
required String token,
}) async {
return true;
}
@override
Future<bool> 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;
AppStorePurchaseHandler(this.iapRepository);
@override
Future<bool> handleNonSubscription({
required String userId,
required ProductData productData,
required String token,
}) async {
return true;
}
@override
Future<bool> 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:shelf_router/shelf_router.dart';
Future<void> 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
import 'package:shelf/shelf.dart';
import 'package:shelf_router/shelf_router.dart';
Future<Map<String, 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);
}
}
}
Future<void> _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,
productData: 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
Future<void> _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(
(element) =>
element.type == ProductType.subscription &&
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? appStoreToken;
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 == storeKeySubscription &&
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 StreamSubscription<List<PurchaseDetails>> _subscription;
List<PurchasableProduct> products = [];
IAPRepo iapRepo; // Add this line
bool get beautifiedDash => _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();
}
Future<void> 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();
}
}
अब आपने यह पक्का कर लिया है कि बैकएंड सेवा में सदस्यता और अपग्रेड का स्टेटस हमेशा अपडेट रहे और ऐप्लिकेशन के साथ सिंक हो. ऐप्लिकेशन इसके मुताबिक काम करता है और आपके डैश क्लिकर गेम पर सदस्यता और अपग्रेड की सुविधाएं लागू करता है.
12. सब हो गया!
बधाई हो!!! आपने यह कोडलैब पूरा कर लिया है. इस कोडलैब का पूरा कोड, complete फ़ोल्डर में देखा जा सकता है.
ज़्यादा जानने के लिए, अन्य Flutter कोडलैब आज़माएं.