1. परिचय
Flutter ऐप्लिकेशन में इन-ऐप्लिकेशन खरीदारी की सुविधा जोड़ने के लिए, ऐप्लिकेशन और Play Store को सही तरीके से सेट अप करना ज़रूरी है. साथ ही, खरीदारी की पुष्टि करनी होगी और सदस्यता के फ़ायदे जैसी ज़रूरी अनुमतियां देनी होंगी.
इस कोडलैब में, आपको एक ऐप्लिकेशन में तीन तरह की इन-ऐप्लिकेशन खरीदारी जोड़नी होंगी. साथ ही, Firebase के साथ Dart बैकएंड का इस्तेमाल करके, इन खरीदारी की पुष्टि करनी होगी. दिए गए ऐप्लिकेशन, Dash Clicker में एक गेम है, जिसमें Dash के मस्कॉट को मुद्रा के तौर पर इस्तेमाल किया जाता है. आपको खरीदारी के ये विकल्प जोड़ने होंगे:
- एक बार में 2,000 डैश खरीदने का विकल्प, जिसे बार-बार इस्तेमाल किया जा सकता है.
- पुराने स्टाइल के डैश को मॉडर्न स्टाइल के डैश में बदलने के लिए, एक बार खरीदा जाने वाला अपग्रेड.
- यह एक ऐसी सदस्यता है जिससे अपने-आप जनरेट होने वाले क्लिक की संख्या दोगुनी हो जाती है.
खरीदारी के पहले विकल्प से, उपयोगकर्ता को सीधे तौर पर 2,000 डैश मिलते हैं. ये सीधे उपयोगकर्ता के लिए उपलब्ध होते हैं और इन्हें कई बार खरीदा जा सकता है. इसे 'इस्तेमाल किया जा सकने वाला आइटम' कहा जाता है, क्योंकि इसे सीधे तौर पर इस्तेमाल किया जाता है और कई बार इस्तेमाल किया जा सकता है.
दूसरा विकल्प, डैश को ज़्यादा बेहतर डैश में अपग्रेड करता है. इसे सिर्फ़ एक बार खरीदना होता है और यह हमेशा के लिए उपलब्ध रहता है. इस तरह की खरीदारी को 'एक बार इस्तेमाल होने वाला' कहा जाता है, क्योंकि ऐप्लिकेशन इसका इस्तेमाल नहीं कर सकता. हालांकि, यह हमेशा के लिए मान्य होता है.
खरीदारी का तीसरा और आखिरी विकल्प सदस्यता है. सदस्यता चालू होने पर, उपयोगकर्ता को डैश ज़्यादा तेज़ी से मिलेंगे. हालांकि, सदस्यता के लिए पैसे चुकाने बंद करने पर, ये फ़ायदे भी बंद हो जाएंगे.
बैकएंड सेवा (आपके लिए भी उपलब्ध है), Dart ऐप्लिकेशन के तौर पर काम करती है. यह खरीदारी की पुष्टि करती है और उन्हें Firestore का इस्तेमाल करके सेव करती है. इस प्रोसेस को आसान बनाने के लिए, Firestore का इस्तेमाल किया जाता है. हालांकि, अपने प्रोडक्शन ऐप्लिकेशन में, किसी भी तरह की बैकएंड सेवा का इस्तेमाल किया जा सकता है.
आपको क्या बनाना है
- आपको ऐप्लिकेशन में, खर्च किए जा सकने वाले आइटम की खरीदारी और सदस्यताओं की सुविधा जोड़नी होगी.
- खरीदे गए आइटम की पुष्टि करने और उन्हें सेव करने के लिए, आपको Dart बैकएंड ऐप्लिकेशन भी एक्सटेंड़ करना होगा.
आपको क्या सीखने को मिलेगा
- खरीदे जा सकने वाले प्रॉडक्ट के साथ, App Store और Play Store को कॉन्फ़िगर करने का तरीका.
- खरीदारी की पुष्टि करने और उन्हें Firestore में सेव करने के लिए, स्टोर से संपर्क करने का तरीका.
- अपने ऐप्लिकेशन में खरीदारी को मैनेज करने का तरीका.
आपको किन चीज़ों की ज़रूरत होगी
- Android Studio 4.1 या इसके बाद का वर्शन
- Xcode 12 या इसके बाद का वर्शन (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
में मौजूद डायरेक्ट्री स्ट्रक्चर में, नाम वाले हर चरण के आखिर में आपको कहां होना चाहिए, इसकी झलक होती है. स्टार्टर कोड, पहले चरण में होता है. इसलिए, मिलती-जुलती फ़ाइलों को ढूंढना आसान है. इसके लिए:
cd flutter-codelabs/in_app_purchases/step_00
अगर आपको आगे जाना है या यह देखना है कि किसी चरण के बाद क्या दिखेगा, तो उस चरण के नाम पर क्लिक करके डायरेक्ट्री देखें. आखिरी चरण का कोड, फ़ोल्डर complete
में मौजूद है.
स्टार्टर प्रोजेक्ट सेट अप करना
अपने पसंदीदा IDE में, step_00
से स्टार्टर प्रोजेक्ट खोलें. हमने स्क्रीनशॉट के लिए Android Studio का इस्तेमाल किया है. हालांकि, Visual Studio Code भी एक अच्छा विकल्प है. किसी भी एडिटर का इस्तेमाल करते समय, पक्का करें कि Dart और Flutter के नए प्लग इन इंस्टॉल हों.
आपको जो ऐप्लिकेशन बनाने हैं उन्हें यह जानने के लिए, ऐप स्टोर और Play Store से संपर्क करना होगा कि कौनसे प्रॉडक्ट उपलब्ध हैं और उनकी कीमत क्या है. हर ऐप्लिकेशन की पहचान एक यूनीक आईडी से की जाती है. iOS App Store के लिए इसे बंडल आइडेंटिफ़ायर कहा जाता है और Android Play Store के लिए इसे ऐप्लिकेशन आईडी कहा जाता है. आम तौर पर, इन आइडेंटिफ़ायर को रिवर्स डोमेन नेम नोटेशन का इस्तेमाल करके बनाया जाता है. उदाहरण के लिए, flutter.dev के लिए इन-ऐप्लिकेशन खरीदारी वाला ऐप्लिकेशन बनाते समय, dev.flutter.inapppurchase
का इस्तेमाल किया जाएगा. अपने ऐप्लिकेशन के लिए कोई आइडेंटिफ़ायर सोचें. अब आपको उसे प्रोजेक्ट की सेटिंग में सेट करना है.
सबसे पहले, iOS के लिए बंडल आइडेंटिफ़ायर सेट अप करें.
Android Studio में प्रोजेक्ट खोलकर, iOS फ़ोल्डर पर दायां क्लिक करें. इसके बाद, Flutter पर क्लिक करें और Xcode ऐप्लिकेशन में मॉड्यूल खोलें.
Xcode के फ़ोल्डर स्ट्रक्चर में, Runner प्रोजेक्ट सबसे ऊपर होता है. इसके नीचे, Flutter, Runner, और Products टारगेट होते हैं. प्रोजेक्ट की सेटिंग में बदलाव करने के लिए, Runner पर दो बार क्लिक करें. इसके बाद, साइनिंग और सुविधाएं पर क्लिक करें. अपनी टीम सेट करने के लिए, टीम फ़ील्ड में वह बंडल आइडेंटिफ़ायर डालें जिसे आपने अभी चुना है.
अब Xcode को बंद करके, Android के लिए कॉन्फ़िगरेशन पूरा करने के लिए Android Studio पर वापस जाएं. इसके लिए, android/app,
में जाकर build.gradle
फ़ाइल खोलें और अपने applicationId
(नीचे दिए गए स्क्रीनशॉट में 37वीं लाइन पर) को ऐप्लिकेशन आईडी में बदलें. यह आईडी, iOS बंडल आइडेंटिफ़ायर जैसा ही होना चाहिए. ध्यान दें कि iOS और Android स्टोर के आईडी एक जैसे होने ज़रूरी नहीं हैं. हालांकि, उन्हें एक जैसा रखने से गड़बड़ियों की संभावना कम होती है. इसलिए, इस कोडलैब में हम एक जैसे आइडेंटिफ़ायर का इस्तेमाल करेंगे.
3. प्लग इन इंस्टॉल करना
कोडलैब के इस हिस्से में, आपको in_app_purchase प्लग इन इंस्टॉल करना होगा.
pubspec में डिपेंडेंसी जोड़ना
अपने pubspec में डिपेंडेंसी में in_app_purchase
जोड़कर, pubspec में in_app_purchase
जोड़ें:
$ cd app $ flutter pub add in_app_purchase dev:in_app_purchase_platform_interface
अपना pubspec.yaml
खोलें और पुष्टि करें कि अब आपके पास dependencies
में in_app_purchase
और dev_dependencies
में in_app_purchase_platform_interface
, एंट्री के तौर पर मौजूद है.
pubspec.yaml
dependencies:
flutter:
sdk: flutter
cloud_firestore: ^5.5.1
cupertino_icons: ^1.0.8
firebase_auth: ^5.3.4
firebase_core: ^3.8.1
google_sign_in: ^6.2.2
http: ^1.2.2
intl: ^0.20.1
provider: ^6.1.2
in_app_purchase: ^3.2.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^4.0.0
in_app_purchase_platform_interface: ^1.4.0
पैकेज डाउनलोड करने के लिए, pub get पर क्लिक करें या कमांड लाइन में flutter pub get
चलाएं.
4. App Store सेट अप करना
इन-ऐप्लिकेशन खरीदारी की सुविधा को सेट अप करने और iOS पर उसका टेस्ट करने के लिए, आपको App Store में नया ऐप्लिकेशन बनाना होगा. साथ ही, वहां खरीदे जा सकने वाले प्रॉडक्ट बनाने होंगे. आपको कुछ भी पब्लिश करने या ऐप्लिकेशन को समीक्षा के लिए Apple को भेजने की ज़रूरत नहीं है. ऐसा करने के लिए, आपके पास डेवलपर खाता होना चाहिए. अगर आपके पास कोई खाता नहीं है, तो Apple डेवलपर कार्यक्रम में रजिस्टर करें.
पैसे चुकाकर डाउनलोड किए जाने वाले ऐप्लिकेशन के समझौते
इन-ऐप्लिकेशन खरीदारी की सुविधा का इस्तेमाल करने के लिए, आपके पास App Store Connect में पैसे चुकाकर डाउनलोड किए जाने वाले ऐप्लिकेशन के लिए, एक चालू कानूनी समझौता भी होना चाहिए. https://appstoreconnect.apple.com/ पर जाएं और समझौते, टैक्स, और बैंकिंग पर क्लिक करें.
यहां आपको बिना शुल्क वाले और पैसे चुकाकर डाउनलोड किए जाने वाले ऐप्लिकेशन के लिए कानूनी समझौते दिखेंगे. मुफ़्त ऐप्लिकेशन का स्टेटस 'चालू है' और पैसे चुकाकर डाउनलोड किए जाने वाले ऐप्लिकेशन का स्टेटस 'नया' होना चाहिए. पक्का करें कि आपने शर्तें देख ली हों, उन्हें स्वीकार कर लिया हो, और सभी ज़रूरी जानकारी डाल ली हो.
सब कुछ सही तरीके से सेट होने पर, पैसे चुकाकर डाउनलोड किए जाने वाले ऐप्लिकेशन का स्टेटस 'चालू है' के तौर पर दिखेगा. यह बहुत ज़रूरी है, क्योंकि मान्य समझौते के बिना इन-ऐप्लिकेशन खरीदारी की सुविधा का इस्तेमाल नहीं किया जा सकता.
ऐप्लिकेशन आईडी रजिस्टर करना
Apple Developer Portal में नया आइडेंटिफ़ायर बनाएं.
ऐप्लिकेशन आईडी चुनना
ऐप्लिकेशन चुनें
कुछ जानकारी दें और बंडल आईडी को XCode में पहले से सेट की गई वैल्यू से मैच करने के लिए सेट करें.
नया ऐप्लिकेशन आईडी बनाने के बारे में ज़्यादा जानकारी के लिए, डेवलपर खाते से जुड़ी सहायता देखें .
नया ऐप्लिकेशन बनाना
अपने यूनीक बंडल आइडेंटिफ़ायर की मदद से, App Store Connect में नया ऐप्लिकेशन बनाएं.
नया ऐप्लिकेशन बनाने और कानूनी समझौतों को मैनेज करने के बारे में ज़्यादा जानकारी के लिए, App Store Connect सहायता देखें.
इन-ऐप्लिकेशन खरीदारी की जांच करने के लिए, आपके पास सैंडबॉक्स टेस्ट उपयोगकर्ता होना चाहिए. यह टेस्ट उपयोगकर्ता, iTunes से कनेक्ट नहीं होना चाहिए. इसका इस्तेमाल सिर्फ़ इन-ऐप्लिकेशन खरीदारी की जांच करने के लिए किया जाता है. किसी ऐसे ईमेल पते का इस्तेमाल नहीं किया जा सकता जिसका इस्तेमाल पहले से ही किसी Apple खाते के लिए किया जा रहा है. नया सैंडबॉक्स खाता बनाने या मौजूदा सैंडबॉक्स Apple आईडी मैनेज करने के लिए, उपयोगकर्ता और ऐक्सेस में जाकर, सैंडबॉक्स में टेस्टर पर जाएं.
अब अपने iPhone पर सैंडबॉक्स उपयोगकर्ता सेट अप करने के लिए, सेटिंग > App Store > सैंडबॉक्स-खाता पर जाएं.
इन-ऐप्लिकेशन खरीदारी की सुविधा कॉन्फ़िगर करना
अब आपको खरीदे जा सकने वाले तीन आइटम कॉन्फ़िगर करने होंगे:
dash_consumable_2k
: एक बार इस्तेमाल होने वाली ऐसी खरीदारी जिसे कई बार खरीदा जा सकता है. इससे उपयोगकर्ता को हर खरीदारी पर 2, 000 डैश (ऐप्लिकेशन में इस्तेमाल होने वाली मुद्रा) मिलते हैं.dash_upgrade_3d
: एक बार खरीदी जा सकने वाली "अपग्रेड" खरीदारी, जिसे सिर्फ़ एक बार खरीदा जा सकता है. साथ ही, यह उपयोगकर्ता को क्लिक करने के लिए, डैशबोर्ड के अलग-अलग वर्शन उपलब्ध कराता है.dash_subscription_doubler
: ऐसी सदस्यता जो सदस्यता की अवधि के दौरान, उपयोगकर्ता को हर क्लिक के लिए दोगुने डैश देती है.
इन-ऐप्लिकेशन खरीदारी > मैनेज करें पर जाएं.
दिए गए आईडी का इस्तेमाल करके, इन-ऐप्लिकेशन खरीदारी बनाएं:
dash_consumable_2k
को इस्तेमाल किए जा सकने वाले आइटम के तौर पर सेट अप करें.
प्रॉडक्ट आईडी के तौर पर dash_consumable_2k
का इस्तेमाल करें. रेफ़रंस का नाम सिर्फ़ ऐप स्टोर कनेक्ट में इस्तेमाल किया जाता है. इसे dash consumable 2k
पर सेट करें और खरीदारी के लिए स्थानीय भाषाओं में अपने प्रॉडक्ट के नाम जोड़ें. खरीदारी को Spring is in the air
के तौर पर कॉल करें और ब्यौरे के तौर पर 2000 dashes fly out
डालें.
dash_upgrade_3d
को इस्तेमाल न किए जा सकने वाले के तौर पर सेट अप करें.
प्रॉडक्ट आईडी के तौर पर dash_upgrade_3d
का इस्तेमाल करें. रेफ़रंस का नाम dash upgrade 3d
पर सेट करें और खरीदारी के लिए स्थानीय भाषाओं में जानकारी जोड़ें. खरीदारी को 3D Dash
के तौर पर कॉल करें और ब्यौरे के तौर पर Brings your dash back to the future
डालें.
dash_subscription_doubler
को अपने-आप रिन्यू होने वाली सदस्यता के तौर पर सेट अप करें.
सदस्यताओं के लिए फ़्लो थोड़ा अलग है. सबसे पहले, आपको रेफ़रंस का नाम और प्रॉडक्ट आईडी सेट करना होगा:
इसके बाद, आपको सदस्यता ग्रुप बनाना होगा. जब एक से ज़्यादा सदस्यताएं एक ही ग्रुप में होती हैं, तो कोई उपयोगकर्ता एक बार में सिर्फ़ एक सदस्यता ले सकता है. हालांकि, वह इन सदस्यताओं को आसानी से अपग्रेड या डाउनग्रेड कर सकता है. इस ग्रुप को subscriptions
नाम दें.
इसके बाद, सदस्यता की अवधि और स्थानीय भाषाओं में जानकारी डालें. इस सदस्यता को Jet Engine
नाम दें और ब्यौरा Doubles your clicks
डालें. सेव करें पर क्लिक करें.
सेव करें बटन पर क्लिक करने के बाद, सदस्यता की कीमत जोड़ें. अपनी पसंद के मुताबिक कोई भी कीमत चुनें.
अब आपको खरीदारी की सूची में तीन खरीदारी दिखेंगी:
5. Play Store सेट अप करना
App Store की तरह ही, Play Store के लिए भी आपको डेवलपर खाता बनाना होगा. अगर आपके पास अब तक कोई खाता नहीं है, तो खाता रजिस्टर करें.
नया ऐप्लिकेशन बनाना
Google Play Console में नया ऐप्लिकेशन बनाएं:
- Play Console खोलें.
- सभी ऐप्लिकेशन > ऐप्लिकेशन बनाएं चुनें.
- कोई डिफ़ॉल्ट भाषा चुनें और अपने ऐप्लिकेशन के लिए टाइटल जोड़ें. अपने ऐप्लिकेशन का वह नाम डालें जिसे Google Play पर दिखाना है. नाम को बाद में बदला जा सकता है.
- यह बताएं कि आपका ऐप्लिकेशन एक गेम है. हालांकि, इसे बाद में बदला जा सकता है.
- यह बताएं कि आपका ऐप्लिकेशन मुफ़्त है या इसके लिए पैसे चुकाने होंगे.
- वह ईमेल पता जोड़ें जिस पर Play Store के उपयोगकर्ता, इस ऐप्लिकेशन के बारे में आपसे संपर्क कर सकें.
- कॉन्टेंट से जुड़े दिशा-निर्देशों और अमेरिका के निर्यात कानूनों की शर्तें पूरी करें.
- ऐप्लिकेशन बनाएं को चुनें.
ऐप्लिकेशन बनाने के बाद, डैशबोर्ड पर जाएं और अपना ऐप्लिकेशन सेट अप करें सेक्शन में मौजूद सभी टास्क पूरे करें. यहां आपको अपने ऐप्लिकेशन के बारे में कुछ जानकारी देनी होगी. जैसे, कॉन्टेंट रेटिंग और स्क्रीनशॉट.
ऐप्लिकेशन पर हस्ताक्षर करना
इन-ऐप्लिकेशन खरीदारी की सुविधा को टेस्ट करने के लिए, आपके पास 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
फ़ाइल में बदलाव करके, अपने ऐप्लिकेशन के लिए साइनिंग की सुविधा कॉन्फ़िगर करें.
android
ब्लॉक से पहले, अपनी प्रॉपर्टी फ़ाइल से पासकोड की जानकारी जोड़ें:
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
android {
// omitted
}
key.properties
फ़ाइल को keystoreProperties
ऑब्जेक्ट में लोड करें.
buildTypes
ब्लॉक से पहले यह कोड जोड़ें:
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now,
// so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
अपने मॉड्यूल की build.gradle
फ़ाइल में, हस्ताक्षर करने के कॉन्फ़िगरेशन की जानकारी के साथ signingConfigs
ब्लॉक को कॉन्फ़िगर करें:
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
storePassword keystoreProperties['storePassword']
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
आपके ऐप्लिकेशन के रिलीज़ बिल्ड अब अपने-आप साइन हो जाएंगे.
अपने ऐप्लिकेशन पर हस्ताक्षर करने के बारे में ज़्यादा जानने के लिए, developer.android.com पर अपने ऐप्लिकेशन पर हस्ताक्षर करें लेख पढ़ें.
अपना पहला बिल्ड अपलोड करना
ऐप्लिकेशन को साइन करने के लिए कॉन्फ़िगर करने के बाद, इसे बनाने के लिए:
flutter build appbundle
यह कमांड, डिफ़ॉल्ट रूप से रिलीज़ बिल्ड जनरेट करता है. इसका आउटपुट <your app dir>/build/app/outputs/bundle/release/
पर देखा जा सकता है
Google Play Console के डैशबोर्ड में, रिलीज़ > टेस्टिंग > क्लोज़्ड टेस्टिंग पर जाएं और क्लोज़्ड टेस्टिंग के लिए नई रिलीज़ बनाएं.
इस कोडलैब में, आपको ऐप्लिकेशन को Google से साइन कराना होगा. इसलिए, ऑप्ट-इन करने के लिए Play ऐप्लिकेशन साइनिंग में जाकर, जारी रखें दबाएं.
इसके बाद, 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 डैशबोर्ड में, पुष्टि पर जाएं और ज़रूरत पड़ने पर इसे चालू करें.
- साइन इन करने का तरीका टैब पर जाएं और साइन इन करने के लिए 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
}
}
}
Firebase for Flutter सेट अप करना
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 हैश को कॉपी करें और ऐप्लिकेशन सबमिशन मोडल डायलॉग में आखिरी फ़ील्ड भरें.
iOS के लिए Firebase सेट अप करना: आगे के चरण
Xcode
की मदद से ios/Runnder.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-Debug.plist
और ios/Runner/Info-Release.plist
हैं.
की-वैल्यू पेयर पहले से जोड़ा गया था, लेकिन उनकी वैल्यू बदलनी होंगी:
GoogleService-Info.plist
फ़ाइल सेREVERSED_CLIENT_ID
की वैल्यू पाएं. इसके लिए,<string>..</string>
एलिमेंट को हटाएं.CFBundleURLTypes
पासकोड के तहत,ios/Runner/Info-Debug.plist
औरios/Runner/Info-Release.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
में, DashPurchases ChangeNotifier
के लिए कोड पर जाएं. फ़िलहाल, खरीदे गए डैश में सिर्फ़ एक 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
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
में मॉक प्रॉडक्ट दिखाया गया है. purchasable_product.dart
में मौजूद PurchasableProduct
क्लास को इस कोड से बदलकर, असल कॉन्टेंट दिखाने के लिए इसे अपडेट करें:
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;
}
}
स्टोर उपलब्ध होने पर, खरीदारी के लिए उपलब्ध प्रॉडक्ट लोड करें. Firebase के पिछले सेटअप को देखते हुए, आपको 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();
}
आखिर में, 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 Play Console पर जाएं और सभी ऐप्लिकेशन पेज से शुरू करें.
- सेटअप > एपीआई ऐक्सेस पर जाएं.
अगर Google Play Console आपसे कोई मौजूदा प्रोजेक्ट बनाने या उससे लिंक करने का अनुरोध करता है, तो पहले ऐसा करें और फिर इस पेज पर वापस आएं.
- वह सेक्शन ढूंढें जहां सेवा खाते तय किए जा सकते हैं. इसके बाद, नया सेवा खाता बनाएं पर क्लिक करें.
- पॉप-अप होने वाले डायलॉग में, Google Cloud Platform लिंक पर क्लिक करें.
- अपना प्रोजेक्ट चुनें. अगर आपको यह विकल्प नहीं दिखता है, तो पक्का करें कि आपने सबसे ऊपर दाईं ओर मौजूद खाता ड्रॉप-डाउन सूची में, सही Google खाते में साइन इन किया हो.
- अपना प्रोजेक्ट चुनने के बाद, सबसे ऊपर मौजूद मेन्यू बार में, + सेवा खाता बनाएं पर क्लिक करें.
- सेवा खाते के लिए कोई नाम डालें. इसके अलावा, आपके पास ब्यौरा देने का विकल्प भी है, ताकि आपको याद रहे कि यह खाता किस काम का है. इसके बाद, अगले चरण पर जाएं.
- सेवा खाते को एडिटर की भूमिका असाइन करें.
- विज़र्ड को पूरा करें. इसके बाद, डेवलपर कंसोल में एपीआई ऐक्सेस पेज पर वापस जाएं और सेवा खाते रीफ़्रेश करें पर क्लिक करें. आपको सूची में अपना नया खाता दिखेगा.
- अपने नए सेवा खाते के लिए, ऐक्सेस दें पर क्लिक करें.
- अगले पेज पर, नीचे की ओर स्क्रोल करके वित्तीय डेटा ब्लॉक पर जाएं. वित्तीय डेटा, ऑर्डर, और सदस्यताएं रद्द करने के बारे में हुए सर्वे में मिले जवाब देखना और ऑर्डर और सदस्यताएं मैनेज करना, दोनों को चुनें.
- उपयोगकर्ता को न्योता भेजें पर क्लिक करें.
- खाता सेट अप हो गया है. अब आपको कुछ क्रेडेंशियल जनरेट करने होंगे. Cloud Console में वापस जाकर, सेवा खातों की सूची में अपना सेवा खाता ढूंढें. इसके बाद, तीन वर्टिकल बिंदुओं पर क्लिक करें और कुंजियां मैनेज करें को चुनें.
- नई JSON कुंजी बनाएं और उसे डाउनलोड करें.
- डाउनलोड की गई फ़ाइल का नाम बदलकर
service-account-google-play.json,
करें और उसेassets/
डायरेक्ट्री में ले जाएं.
हमें एक और काम करना होगा. इसके लिए, 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
के बिल्ड तरीके की शुरुआत में, यह कोड जोड़ें:
lib/pages/purchase_page.dart
import '../logic/firebase_notifier.dart';
import '../model/firebase_state.dart';
import 'login_page.dart';
class PurchasePage extends StatelessWidget {
const PurchasePage({super.key});
@override
Widget build(BuildContext context) {
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();
}
// omitted
ऐप्लिकेशन से पुष्टि करने के लिए कॉल करने का एंडपॉइंट
ऐप्लिकेशन में, _verifyPurchase(PurchaseDetails purchaseDetails)
फ़ंक्शन बनाएं. यह फ़ंक्शन, एचटीटीपी पोस्ट कॉल का इस्तेमाल करके, आपके Dart बैकएंड पर /verifypurchase
एंडपॉइंट को कॉल करता है.
चुना गया स्टोर (Play Store के लिए google_play
या App Store के लिए app_store
), serverVerificationData
, और productID
भेजें. सर्वर, स्टेटस कोड दिखाता है. इससे पता चलता है कि खरीदारी की पुष्टि की गई है या नहीं.
ऐप्लिकेशन के कॉन्स्टेंट में, सर्वर आईपी को अपने लोकल मशीन के आईपी पते पर कॉन्फ़िगर करें.
lib/logic/dash_purchases.dart
FirebaseNotifier firebaseNotifier;
DashPurchases(this.counter, this.firebaseNotifier) {
// omitted
}
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
User? get user => FirebaseAuth.instance.currentUser;
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(1000);
}
}
}
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
क्लास में, खरीदे जा सकने वाले अलग-अलग प्रॉडक्ट की बुनियादी जानकारी होती है. इसमें प्रॉडक्ट आईडी (जिसे कभी-कभी SKU भी कहा जाता है) और 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,
}) {
return true;
}
@override
Future<bool> handleSubscription({
required String userId,
required ProductData productData,
required String token,
}) {
return true;
}
}
बढ़िया! अब आपके पास दो खरीदारी हैंडलर हैं. इसके बाद, खरीदारी की पुष्टि करने वाला एपीआई एंडपॉइंट बनाते हैं.
परचेज़ हैंडलर का इस्तेमाल करना
bin/server.dart
खोलें और shelf_route
का इस्तेमाल करके एपीआई एंडपॉइंट बनाएं:
bin/server.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
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
@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 do not 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 do not 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 {
//..
}
अब, 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) {
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 topic के ज़रिए बिलिंग इवेंट उपलब्ध कराता है. ये मैसेज की कतारें होती हैं. इन पर मैसेज पब्लिश किए जा सकते हैं और इनसे मैसेज इस्तेमाल किए जा सकते हैं.
यह सुविधा सिर्फ़ 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
को हर 10 सेकंड में _pullMessageFromSubSub
तरीके को कॉल करने के लिए कॉन्फ़िगर किया गया है. अपनी पसंद के हिसाब से, स्क्रीन पर दिखने का समय तय किया जा सकता है.
इसके बाद, _pullMessageFromSubSub
बनाएं
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/$googlePlayProjectName/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/$googlePlayProjectName/subscriptions/$googlePlayPubsubBillingTopic-sub';
await pubsubApi.projects.subscriptions.acknowledge(
request,
subscriptionName,
);
}
आपने जो कोड अभी जोड़ा है वह हर 10 सेकंड में Google Cloud के Pub/Sub विषय से संपर्क करता है और नए मैसेज के लिए अनुरोध करता है. इसके बाद, _processMessage
तरीके से हर मैसेज को प्रोसेस करता है.
यह तरीका, आने वाले मैसेज को डिकोड करता है और हर खरीदारी के बारे में अपडेट की गई जानकारी पाता है. इसमें सदस्यताओं और बिना सदस्यता वाली खरीदारी, दोनों की जानकारी शामिल होती है. इसके लिए, ज़रूरत पड़ने पर मौजूदा handleSubscription
या handleNonSubscription
को कॉल किया जाता है.
हर मैसेज को _askMessage
तरीके से स्वीकार किया जाना चाहिए.
इसके बाद, server.dart
फ़ाइल में ज़रूरी डिपेंडेंसी जोड़ें. क्रेडेंशियल कॉन्फ़िगरेशन में PubsubApi.cloudPlatformScope जोड़ें:
bin/server.dart
final clientGooglePlay =
await auth.clientViaServiceAccount(clientCredentialsGooglePlay, [
ap.AndroidPublisherApi.androidpublisherScope,
pubsub.PubsubApi.cloudPlatformScope, // new
]);
इसके बाद, PubsubApi इंस्टेंस बनाएं:
bin/server.dart
final pubsubApi = pubsub.PubsubApi(clientGooglePlay);
आखिर में, इसे GooglePlayPurchaseHandler
कन्स्ट्रक्टर को पास करें:
bin/server.dart
return {
'google_play': GooglePlayPurchaseHandler(
androidPublisher,
iapRepository,
pubsubApi, // new
),
'app_store': AppStorePurchaseHandler(
iapRepository,
),
};
Google Play का सेटअप
आपने Pub/Sub विषय से बिलिंग इवेंट का इस्तेमाल करने के लिए कोड लिखा है, लेकिन आपने Pub/Sub विषय नहीं बनाया है और न ही कोई बिलिंग इवेंट पब्लिश किया है. अब इसे सेट अप करने का समय आ गया है.
सबसे पहले, कोई Pub/Sub विषय बनाएं:
- Google Cloud Console पर, Cloud Pub/Sub पेज पर जाएं.
- पक्का करें कि आप अपने Firebase प्रोजेक्ट पर हों. इसके बाद, + विषय बनाएं पर क्लिक करें.
- नए विषय को ऐसा नाम दें जो
constants.ts
मेंGOOGLE_PLAY_PUBSUB_BILLING_TOPIC
के लिए सेट की गई वैल्यू से मेल खाता हो. इस मामले में, इसेplay_billing
नाम दें. अगर कोई दूसरा विकल्प चुना जाता है, तोconstants.ts
को अपडेट करना न भूलें. विषय बनाएं. - अपने 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;
AppStorePurchaseHandler(
this.iapRepository,
this.appStoreServerAPI, // new
)
कंस्ट्रक्टर में बदलाव करके, ऐसा टाइमर जोड़ें जो _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
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
// 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, // new
),
};
App Store का सेटअप
इसके बाद, App Store को सेट अप करें:
- App Store Connect में लॉग इन करें और उपयोगकर्ता और ऐक्सेस चुनें.
- की टाइप > इन-ऐप्लिकेशन खरीदारी पर जाएं.
- नया फ़ीड जोड़ने के लिए, "प्लस" आइकॉन पर टैप करें.
- इसे कोई नाम दें, जैसे कि "Codelab कुंजी".
- कुंजी वाली 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((DocumentSnapshot 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()
तरीके में लिसनर को हटाएं. शुरुआत में, listener सिर्फ़ एक खाली फ़ंक्शन हो सकता है. IAPRepo
एक ChangeNotifier
है और Firestore में खरीदारी में हर बार बदलाव होने पर notifyListeners()
को कॉल किया जाता है. इसलिए, खरीदे गए प्रॉडक्ट में बदलाव होने पर, purchasesUpdate()
का तरीका हमेशा कॉल किया जाता है.
lib/logic/dash_purchases.dart
IAPRepo iapRepo;
DashPurchases(this.counter, this.firebaseNotifier, this.iapRepo) {
final purchaseUpdated = iapConnection.purchaseStream;
_subscription = purchaseUpdated.listen(
_onPurchaseUpdate,
onDone: _updateStreamOnDone,
onError: _updateStreamOnError,
);
iapRepo.addListener(purchasesUpdate);
loadPurchases();
}
@override
void dispose() {
_subscription.cancel();
iapRepo.removeListener(purchasesUpdate);
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>(),
),
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 क्लिकर गेम में सदस्यता और अपग्रेड की सुविधाएं लागू करता है.
12. सब हो गया!
बधाई हो!!! आपने कोडलैब पूरा कर लिया है. इस कोडलैब का पूरा कोड, complete फ़ोल्डर में देखा जा सकता है.
ज़्यादा जानने के लिए, Flutter के अन्य कोडलैब आज़माएं.