आपके Flutter ऐप्लिकेशन में इन-ऐप्लिकेशन खरीदारी जोड़ना

1. परिचय

Flutter ऐप्लिकेशन में इन-ऐप्लिकेशन खरीदारी की सुविधा जोड़ने के लिए, ऐप्लिकेशन और Play Store को सही तरीके से सेट अप करना ज़रूरी है. साथ ही, खरीदारी की पुष्टि करनी होगी और सदस्यता के फ़ायदे जैसी ज़रूरी अनुमतियां देनी होंगी.

इस कोडलैब में, आपको एक ऐप्लिकेशन में तीन तरह की इन-ऐप्लिकेशन खरीदारी जोड़नी होंगी. साथ ही, Firebase के साथ Dart बैकएंड का इस्तेमाल करके, इन खरीदारी की पुष्टि करनी होगी. दिए गए ऐप्लिकेशन, Dash Clicker में एक गेम है, जिसमें Dash के मस्कॉट को मुद्रा के तौर पर इस्तेमाल किया जाता है. आपको खरीदारी के ये विकल्प जोड़ने होंगे:

  1. एक बार में 2,000 डैश खरीदने का विकल्प, जिसे बार-बार इस्तेमाल किया जा सकता है.
  2. पुराने स्टाइल के डैश को मॉडर्न स्टाइल के डैश में बदलने के लिए, एक बार खरीदा जाने वाला अपग्रेड.
  3. यह एक ऐसी सदस्यता है जिससे अपने-आप जनरेट होने वाले क्लिक की संख्या दोगुनी हो जाती है.

खरीदारी के पहले विकल्प से, उपयोगकर्ता को सीधे तौर पर 2,000 डैश मिलते हैं. ये सीधे उपयोगकर्ता के लिए उपलब्ध होते हैं और इन्हें कई बार खरीदा जा सकता है. इसे 'इस्तेमाल किया जा सकने वाला आइटम' कहा जाता है, क्योंकि इसे सीधे तौर पर इस्तेमाल किया जाता है और कई बार इस्तेमाल किया जा सकता है.

दूसरा विकल्प, डैश को ज़्यादा बेहतर डैश में अपग्रेड करता है. इसे सिर्फ़ एक बार खरीदना होता है और यह हमेशा के लिए उपलब्ध रहता है. इस तरह की खरीदारी को 'एक बार इस्तेमाल होने वाला' कहा जाता है, क्योंकि ऐप्लिकेशन इसका इस्तेमाल नहीं कर सकता. हालांकि, यह हमेशा के लिए मान्य होता है.

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

बैकएंड सेवा (आपके लिए भी उपलब्ध है), Dart ऐप्लिकेशन के तौर पर काम करती है. यह खरीदारी की पुष्टि करती है और उन्हें Firestore का इस्तेमाल करके सेव करती है. इस प्रोसेस को आसान बनाने के लिए, Firestore का इस्तेमाल किया जाता है. हालांकि, अपने प्रोडक्शन ऐप्लिकेशन में, किसी भी तरह की बैकएंड सेवा का इस्तेमाल किया जा सकता है.

300123416ebc8dc1.png 7145d0fffe6ea741.png 646317a79be08214.png

आपको क्या बनाना है

  • आपको ऐप्लिकेशन में, खर्च किए जा सकने वाले आइटम की खरीदारी और सदस्यताओं की सुविधा जोड़नी होगी.
  • खरीदे गए आइटम की पुष्टि करने और उन्हें सेव करने के लिए, आपको 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 ऐप्लिकेशन में मॉड्यूल खोलें.

942772eb9a73bfaa.png

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

812f919d965c649a.jpeg

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

5c4733ac560ae8c2.png

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/ पर जाएं और समझौते, टैक्स, और बैंकिंग पर क्लिक करें.

11db9fca823e7608.png

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

74c73197472c9aec.png

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

4a100bbb8cafdbbf.jpeg

ऐप्लिकेशन आईडी रजिस्टर करना

Apple Developer Portal में नया आइडेंटिफ़ायर बनाएं.

55d7e592d9a3fc7b.png

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

13f125598b72ca77.png

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

41ac4c13404e2526.png

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

9d2c940ad80deeef.png

नया ऐप्लिकेशन आईडी बनाने के बारे में ज़्यादा जानकारी के लिए, डेवलपर खाते से जुड़ी सहायता देखें .

नया ऐप्लिकेशन बनाना

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

10509b17fbf031bd.png

5b7c0bb684ef52c7.png

नया ऐप्लिकेशन बनाने और कानूनी समझौतों को मैनेज करने के बारे में ज़्यादा जानकारी के लिए, App Store Connect सहायता देखें.

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

3ca2b26d4e391a4c.jpeg

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

d99e0b89673867cd.jpeg e1621bcaeb33d3c5.jpeg

इन-ऐप्लिकेशन खरीदारी की सुविधा कॉन्फ़िगर करना

अब आपको खरीदे जा सकने वाले तीन आइटम कॉन्फ़िगर करने होंगे:

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

d156b2f5bac43ca8.png

इन-ऐप्लिकेशन खरीदारी > मैनेज करें पर जाएं.

दिए गए आईडी का इस्तेमाल करके, इन-ऐप्लिकेशन खरीदारी बनाएं:

  1. dash_consumable_2k को इस्तेमाल किए जा सकने वाले आइटम के तौर पर सेट अप करें.

प्रॉडक्ट आईडी के तौर पर dash_consumable_2k का इस्तेमाल करें. रेफ़रंस का नाम सिर्फ़ ऐप स्टोर कनेक्ट में इस्तेमाल किया जाता है. इसे dash consumable 2k पर सेट करें और खरीदारी के लिए स्थानीय भाषाओं में अपने प्रॉडक्ट के नाम जोड़ें. खरीदारी को Spring is in the air के तौर पर कॉल करें और ब्यौरे के तौर पर 2000 dashes fly out डालें.

ec1701834fd8527.png

  1. dash_upgrade_3d को इस्तेमाल न किए जा सकने वाले के तौर पर सेट अप करें.

प्रॉडक्ट आईडी के तौर पर dash_upgrade_3d का इस्तेमाल करें. रेफ़रंस का नाम dash upgrade 3d पर सेट करें और खरीदारी के लिए स्थानीय भाषाओं में जानकारी जोड़ें. खरीदारी को 3D Dash के तौर पर कॉल करें और ब्यौरे के तौर पर Brings your dash back to the future डालें.

6765d4b711764c30.png

  1. dash_subscription_doubler को अपने-आप रिन्यू होने वाली सदस्यता के तौर पर सेट अप करें.

सदस्यताओं के लिए फ़्लो थोड़ा अलग है. सबसे पहले, आपको रेफ़रंस का नाम और प्रॉडक्ट आईडी सेट करना होगा:

6d29e08dae26a0c4.png

इसके बाद, आपको सदस्यता ग्रुप बनाना होगा. जब एक से ज़्यादा सदस्यताएं एक ही ग्रुप में होती हैं, तो कोई उपयोगकर्ता एक बार में सिर्फ़ एक सदस्यता ले सकता है. हालांकि, वह इन सदस्यताओं को आसानी से अपग्रेड या डाउनग्रेड कर सकता है. इस ग्रुप को subscriptions नाम दें.

5bd0da17a85ac076.png

इसके बाद, सदस्यता की अवधि और स्थानीय भाषाओं में जानकारी डालें. इस सदस्यता को Jet Engine नाम दें और ब्यौरा Doubles your clicks डालें. सेव करें पर क्लिक करें.

bd1b1d82eeee4cb3.png

सेव करें बटन पर क्लिक करने के बाद, सदस्यता की कीमत जोड़ें. अपनी पसंद के मुताबिक कोई भी कीमत चुनें.

d0bf39680ef0aa2e.png

अब आपको खरीदारी की सूची में तीन खरीदारी दिखेंगी:

99d5c4b446e8fecf.png

5. Play Store सेट अप करना

App Store की तरह ही, Play Store के लिए भी आपको डेवलपर खाता बनाना होगा. अगर आपके पास अब तक कोई खाता नहीं है, तो खाता रजिस्टर करें.

नया ऐप्लिकेशन बनाना

Google Play Console में नया ऐप्लिकेशन बनाएं:

  1. Play Console खोलें.
  2. सभी ऐप्लिकेशन > ऐप्लिकेशन बनाएं चुनें.
  3. कोई डिफ़ॉल्ट भाषा चुनें और अपने ऐप्लिकेशन के लिए टाइटल जोड़ें. अपने ऐप्लिकेशन का वह नाम डालें जिसे Google Play पर दिखाना है. नाम को बाद में बदला जा सकता है.
  4. यह बताएं कि आपका ऐप्लिकेशन एक गेम है. हालांकि, इसे बाद में बदला जा सकता है.
  5. यह बताएं कि आपका ऐप्लिकेशन मुफ़्त है या इसके लिए पैसे चुकाने होंगे.
  6. वह ईमेल पता जोड़ें जिस पर Play Store के उपयोगकर्ता, इस ऐप्लिकेशन के बारे में आपसे संपर्क कर सकें.
  7. कॉन्टेंट से जुड़े दिशा-निर्देशों और अमेरिका के निर्यात कानूनों की शर्तें पूरी करें.
  8. ऐप्लिकेशन बनाएं को चुनें.

ऐप्लिकेशन बनाने के बाद, डैशबोर्ड पर जाएं और अपना ऐप्लिकेशन सेट अप करें सेक्शन में मौजूद सभी टास्क पूरे करें. यहां आपको अपने ऐप्लिकेशन के बारे में कुछ जानकारी देनी होगी. जैसे, कॉन्टेंट रेटिंग और स्क्रीनशॉट. 13845badcf9bc1db.png

ऐप्लिकेशन पर हस्ताक्षर करना

इन-ऐप्लिकेशन खरीदारी की सुविधा को टेस्ट करने के लिए, आपके पास 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 ऐप्लिकेशन साइनिंग में जाकर, जारी रखें दबाएं.

ba98446d9c5c40e0.png

इसके बाद, app-release.aab ऐप्लिकेशन बंडल अपलोड करें. यह बंडल, 'बिल्ड करें' निर्देश से जनरेट होता है.

सेव करें पर क्लिक करें. इसके बाद, रिलीज़ की समीक्षा करें पर क्लिक करें.

आखिर में, संगठन में काम करने वाले लोगों के लिए उपलब्ध टेस्टिंग रिलीज़ को चालू करने के लिए, इंटरनल टेस्टिंग के लिए रोल आउट शुरू करें पर क्लिक करें.

टेस्ट उपयोगकर्ता सेट अप करना

इन-ऐप्लिकेशन खरीदारी की जांच करने के लिए, आपके टेस्टर के Google खातों को Google Play Console में दो जगहों पर जोड़ना होगा:

  1. किसी खास टेस्ट ट्रैक पर (इंटरनल टेस्टिंग)
  2. लाइसेंस टेस्टर के तौर पर

सबसे पहले, टेस्टर को इंटरनल टेस्टिंग ट्रैक में जोड़ें. रिलीज़ > टेस्टिंग > इंटरनल टेस्टिंग पर वापस जाएं और टेस्टर टैब पर क्लिक करें.

a0d0394e85128f84.png

ईमेल सूची बनाएं पर क्लिक करके, नई ईमेल सूची बनाएं. सूची को कोई नाम दें और उन Google खातों के ईमेल पते जोड़ें जिन्हें इन-ऐप्लिकेशन खरीदारी की जांच करने का ऐक्सेस चाहिए.

इसके बाद, सूची के लिए चेकबॉक्स चुनें और बदलाव सेव करें पर क्लिक करें.

इसके बाद, लाइसेंस की जांच करने वाले लोगों के ईमेल पते जोड़ें:

  1. Google Play Console के सभी ऐप्लिकेशन व्यू पर वापस जाएं.
  2. सेटिंग > लाइसेंस टेस्टिंग पर जाएं.
  3. जांच करने वाले उन लोगों के ईमेल पते जोड़ें जिन्हें इन-ऐप्लिकेशन खरीदारी की जांच करनी है.
  4. लाइसेंस का जवाब को RESPOND_NORMALLY पर सेट करें.
  5. बदलाव सेव करें पर क्लिक करें.

a1a0f9d3e55ea8da.png

इन-ऐप्लिकेशन खरीदारी की सुविधा कॉन्फ़िगर करना

अब आपको उन आइटम को कॉन्फ़िगर करना होगा जिन्हें ऐप्लिकेशन में खरीदा जा सकता है.

App Store की तरह ही, आपको तीन अलग-अलग खरीदारी तय करनी होंगी:

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

सबसे पहले, इस्तेमाल किए जा सकने वाले और इस्तेमाल न किए जा सकने वाले आइटम जोड़ें.

  1. Google Play Console पर जाएं और अपना ऐप्लिकेशन चुनें.
  2. कमाई करना > प्रॉडक्ट > इन-ऐप्लिकेशन प्रॉडक्ट पर जाएं.
  3. प्रॉडक्ट बनाएं पर क्लिक करेंc8d66e32f57dee21.png
  4. अपने प्रॉडक्ट की सभी ज़रूरी जानकारी डालें. पक्का करें कि प्रॉडक्ट आईडी, उस आईडी से मेल खाता हो जिसका इस्तेमाल करना है.
  5. सेव करें पर क्लिक करें.
  6. चालू करें पर क्लिक करें.
  7. एक बार इस्तेमाल होने वाले "अपग्रेड" की खरीदारी के लिए, यह प्रोसेस दोहराएं.

इसके बाद, सदस्यता जोड़ें:

  1. Google Play Console पर जाएं और अपना ऐप्लिकेशन चुनें.
  2. कमाई करना > प्रॉडक्ट > सदस्यताएं पर जाएं.
  3. सदस्यता बनाएं पर क्लिक करें32a6a9eefdb71dd0.png
  4. अपनी सदस्यता के लिए सभी ज़रूरी जानकारी डालें. पक्का करें कि प्रॉडक्ट आईडी, उस आईडी से पूरी तरह मेल खाता हो जिसका इस्तेमाल करना है.
  5. सेव करें पर क्लिक करें

अब आपकी खरीदारी, Play Console में सेट अप हो गई होंगी.

6. Firebase सेट अप करना

इस कोडलैब में, आपको उपयोगकर्ताओं की खरीदारी की पुष्टि करने और उन्हें ट्रैक करने के लिए, बैकएंड सेवा का इस्तेमाल करना होगा.

बैकएंड सेवा का इस्तेमाल करने के कई फ़ायदे हैं:

  • लेन-देन की सुरक्षित तरीके से पुष्टि की जा सकती है.
  • ऐप स्टोर से बिलिंग इवेंट पर प्रतिक्रिया दी जा सकती है.
  • डेटाबेस में खरीदारी का ट्रैक रखा जा सकता है.
  • उपयोगकर्ता, अपने सिस्टम क्लॉक को पीछे ले जाकर, आपके ऐप्लिकेशन को धोखा नहीं दे पाएंगे.

बैकएंड सेवा सेट अप करने के कई तरीके हैं. हालांकि, आपको Google के Firebase का इस्तेमाल करके, क्लाउड फ़ंक्शन और Firestore का इस्तेमाल करना होगा.

इस कोडलैब में बैकएंड कोड नहीं लिखा गया है. इसलिए, स्टार्टर कोड में पहले से ही एक Firebase प्रोजेक्ट शामिल है, जो आपको शुरुआत करने के लिए बुनियादी खरीदारी को मैनेज करता है.

स्टार्टर ऐप्लिकेशन में Firebase प्लग इन भी शामिल हैं.

अब आपको अपना Firebase प्रोजेक्ट बनाना है. इसके बाद, Firebase के लिए ऐप्लिकेशन और बैकएंड, दोनों को कॉन्फ़िगर करना है. आखिर में, बैकएंड को डिप्लॉय करना है.

Firebase प्रोजेक्ट बनाना

Firebase कंसोल पर जाएं और नया Firebase प्रोजेक्ट बनाएं. इस उदाहरण के लिए, प्रोजेक्ट को Dash Clicker नाम दें.

बैकएंड ऐप्लिकेशन में, खरीदारी को किसी खास उपयोगकर्ता से जोड़ा जाता है. इसलिए, आपको पुष्टि करनी होगी. इसके लिए, Google साइन इन की मदद से Firebase के पुष्टि करने वाले मॉड्यूल का इस्तेमाल करें.

  1. Firebase डैशबोर्ड में, पुष्टि पर जाएं और ज़रूरत पड़ने पर इसे चालू करें.
  2. साइन इन करने का तरीका टैब पर जाएं और साइन इन करने के लिए Google को चालू करें.

7babb48832fbef29.png

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

e20553e0de5ac331.png

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) ऐप्लिकेशन चुनें.

b22d46a759c0c834.png

डीबग मोड में 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 हैं.

की-वैल्यू पेयर पहले से जोड़ा गया था, लेकिन उनकी वैल्यू बदलनी होंगी:

  1. GoogleService-Info.plist फ़ाइल से REVERSED_CLIENT_ID की वैल्यू पाएं. इसके लिए, <string>..</string> एलिमेंट को हटाएं.
  2. 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 स्टोर पर उपलब्ध प्रॉडक्ट दिखेंगे. ध्यान दें कि खरीदे गए कॉन्टेंट को संबंधित कंसोल में डालने के बाद, उसे उपलब्ध होने में कुछ समय लग सकता है.

ca1a9f97c21e552d.png

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/ फ़ोल्डर को रूट के तौर पर इस्तेमाल करें.

पक्का करें कि आपके पास ये टूल इंस्टॉल हों:

बेस प्रोजेक्ट की खास जानकारी

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

यह बैकएंड कोड, आपकी मशीन पर लोकल तौर पर चल सकता है. इसका इस्तेमाल करने के लिए, आपको इसे डिप्लॉय करने की ज़रूरत नहीं है. हालांकि, आपको अपने डेवलपमेंट डिवाइस (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 प्रोजेक्ट की सेटिंग खोलकर, सेवा खाते सेक्शन पर जाएं. इसके बाद, नई निजी कुंजी जनरेट करें को चुनें.

27590fc77ae94ad4.png

डाउनलोड की गई JSON फ़ाइल को assets/ फ़ोल्डर में कॉपी करें और उसका नाम बदलकर service-account-firebase.json करें.

Google Play का ऐक्सेस सेट अप करना

खरीदारी की पुष्टि करने के लिए Play Store को ऐक्सेस करने के लिए, आपको इन अनुमतियों के साथ एक सेवा खाता जनरेट करना होगा. साथ ही, इसके लिए JSON क्रेडेंशियल डाउनलोड करने होंगे.

  1. Google Play Console पर जाएं और सभी ऐप्लिकेशन पेज से शुरू करें.
  2. सेटअप > एपीआई ऐक्सेस पर जाएं. 317fdfb54921f50e.png अगर Google Play Console आपसे कोई मौजूदा प्रोजेक्ट बनाने या उससे लिंक करने का अनुरोध करता है, तो पहले ऐसा करें और फिर इस पेज पर वापस आएं.
  3. वह सेक्शन ढूंढें जहां सेवा खाते तय किए जा सकते हैं. इसके बाद, नया सेवा खाता बनाएं पर क्लिक करें.1e70d3f8d794bebb.png
  4. पॉप-अप होने वाले डायलॉग में, Google Cloud Platform लिंक पर क्लिक करें. 7c9536336dd9e9b4.png
  5. अपना प्रोजेक्ट चुनें. अगर आपको यह विकल्प नहीं दिखता है, तो पक्का करें कि आपने सबसे ऊपर दाईं ओर मौजूद खाता ड्रॉप-डाउन सूची में, सही Google खाते में साइन इन किया हो. 3fb3a25bad803063.png
  6. अपना प्रोजेक्ट चुनने के बाद, सबसे ऊपर मौजूद मेन्यू बार में, + सेवा खाता बनाएं पर क्लिक करें. 62fe4c3f8644acd8.png
  7. सेवा खाते के लिए कोई नाम डालें. इसके अलावा, आपके पास ब्यौरा देने का विकल्प भी है, ताकि आपको याद रहे कि यह खाता किस काम का है. इसके बाद, अगले चरण पर जाएं. 8a92d5d6a3dff48c.png
  8. सेवा खाते को एडिटर की भूमिका असाइन करें. 6052b7753667ed1a.png
  9. विज़र्ड को पूरा करें. इसके बाद, डेवलपर कंसोल में एपीआई ऐक्सेस पेज पर वापस जाएं और सेवा खाते रीफ़्रेश करें पर क्लिक करें. आपको सूची में अपना नया खाता दिखेगा. 5895a7db8b4c7659.png
  10. अपने नए सेवा खाते के लिए, ऐक्सेस दें पर क्लिक करें.
  11. अगले पेज पर, नीचे की ओर स्क्रोल करके वित्तीय डेटा ब्लॉक पर जाएं. वित्तीय डेटा, ऑर्डर, और सदस्यताएं रद्द करने के बारे में हुए सर्वे में मिले जवाब देखना और ऑर्डर और सदस्यताएं मैनेज करना, दोनों को चुनें. 75b22d0201cf67e.png
  12. उपयोगकर्ता को न्योता भेजें पर क्लिक करें. 70ea0b1288c62a59.png
  13. खाता सेट अप हो गया है. अब आपको कुछ क्रेडेंशियल जनरेट करने होंगे. Cloud Console में वापस जाकर, सेवा खातों की सूची में अपना सेवा खाता ढूंढें. इसके बाद, तीन वर्टिकल बिंदुओं पर क्लिक करें और कुंजियां मैनेज करें को चुनें. 853ee186b0e9954e.png
  14. नई JSON कुंजी बनाएं और उसे डाउनलोड करें. 2a33a55803f5299c.png cb4bf48ebac0364e.png
  15. डाउनलोड की गई फ़ाइल का नाम बदलकर service-account-google-play.json, करें और उसे assets/ डायरेक्ट्री में ले जाएं.

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

Apple App Store का ऐक्सेस सेट अप करना

खरीदारी की पुष्टि करने के लिए, App Store को ऐक्सेस करने के लिए, आपको शेयर किया गया पासवर्ड सेट अप करना होगा:

  1. App Store Connect खोलें.
  2. मेरे ऐप्लिकेशन पर जाएं और अपना ऐप्लिकेशन चुनें.
  3. साइडबार नेविगेशन में, इन-ऐप्लिकेशन खरीदारी > मैनेज करें पर जाएं.
  4. सूची में सबसे ऊपर दाईं ओर, ऐप्लिकेशन के हिसाब से शेयर किया गया पासवर्ड पर क्लिक करें.
  5. नया सीक्रेट जनरेट करें और उसे कॉपी करें.
  6. lib/constants.dart, खोलें और appStoreSharedSecret की वैल्यू को, शेयर किए गए उस सीक्रेट कोड से बदलें जिसे आपने अभी जनरेट किया है.

d8b8042470aaeff.png

b72f4565750e2f40.png

कॉन्स्टेंट कॉन्फ़िगरेशन फ़ाइल

आगे बढ़ने से पहले, पक्का करें कि 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 क्लास सेट अप करें.

be50c207c5a2a519.png

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');
  }
}

ऊपर दिया गया कोड ये काम कर रहा है:

  1. कोई POST एंडपॉइंट तय करें, जिसे आपके बनाए गए ऐप्लिकेशन से कॉल किया जाएगा.
  2. JSON पेलोड को डिकोड करें और यह जानकारी निकालें:
  3. userId: फ़िलहाल लॉग इन किया हुआ यूज़र आईडी
  4. source: इस्तेमाल किया गया स्टोर, app_store या google_play.
  5. productData: यह उस productDataMap से मिला है जिसे आपने पहले बनाया था.
  6. token: इसमें स्टोर को भेजने के लिए, पुष्टि करने वाला डेटा शामिल होता है.
  7. सोर्स के आधार पर, GooglePlayPurchaseHandler या AppStorePurchaseHandler के लिए verifyPurchase वाले तरीके को कॉल करें.
  8. अगर पुष्टि हो जाती है, तो यह मेथड क्लाइंट को Response.ok दिखाता है.
  9. अगर पुष्टि नहीं हो पाती है, तो यह तरीका क्लाइंट को 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 विषय बनाएं:

  1. Google Cloud Console पर, Cloud Pub/Sub पेज पर जाएं.
  2. पक्का करें कि आप अपने Firebase प्रोजेक्ट पर हों. इसके बाद, + विषय बनाएं पर क्लिक करें. d5ebf6897a0a8bf5.png
  3. नए विषय को ऐसा नाम दें जो constants.ts में GOOGLE_PLAY_PUBSUB_BILLING_TOPIC के लिए सेट की गई वैल्यू से मेल खाता हो. इस मामले में, इसे play_billing नाम दें. अगर कोई दूसरा विकल्प चुना जाता है, तो constants.ts को अपडेट करना न भूलें. विषय बनाएं. 20d690fc543c4212.png
  4. अपने Pub/Sub विषयों की सूची में, अभी बनाए गए विषय के लिए तीन वर्टिकल बिंदुओं पर क्लिक करें. इसके बाद, अनुमतियां देखें पर क्लिक करें. ea03308190609fb.png
  5. दाईं ओर मौजूद साइडबार में, प्रिंसिपल जोड़ें को चुनें.
  6. यहां google-play-developer-notifications@system.gserviceaccount.com जोड़ें और उसे Pub/Sub पब्लिशर की भूमिका दें. 55631ec0549215bc.png
  7. अनुमति में किए गए बदलावों को सेव करें.
  8. आपने अभी-अभी जो विषय बनाया है उसका विषय का नाम कॉपी करें.
  9. Play Console को फिर से खोलें और सभी ऐप्लिकेशन सूची से अपना ऐप्लिकेशन चुनें.
  10. नीचे की ओर स्क्रोल करके, कमाई करें > कमाई करने के लिए सेटअप पर जाएं.
  11. पूरा विषय भरें और अपने बदलावों को सेव करें. 7e5e875dc6ce5d54.png

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,
          ));
        }
      }
    }
  }

यह तरीका इस तरह से काम करता है:

  1. IapRepository का इस्तेमाल करके, Firestore से चालू सदस्यताओं की सूची पाता है.
  2. हर ऑर्डर के लिए, यह App Store Server API से सदस्यता की स्थिति का अनुरोध करता है.
  3. उस सदस्यता की खरीदारी के लिए, आखिरी लेन-देन की जानकारी पाता है.
  4. यह देखता है कि कूपन की समयसीमा खत्म हो चुकी है या नहीं.
  5. 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 को सेट अप करें:

  1. App Store Connect में लॉग इन करें और उपयोगकर्ता और ऐक्सेस चुनें.
  2. की टाइप > इन-ऐप्लिकेशन खरीदारी पर जाएं.
  3. नया फ़ीड जोड़ने के लिए, "प्लस" आइकॉन पर टैप करें.
  4. इसे कोई नाम दें, जैसे कि "Codelab कुंजी".
  5. कुंजी वाली p8 फ़ाइल डाउनलोड करें.
  6. इसे ऐसेट फ़ोल्डर में SubscriptionKey.p8 नाम से कॉपी करें.
  7. नई बनाई गई कुंजी से कुंजी आईडी कॉपी करें और उसे lib/constants.dart फ़ाइल में appStoreKeyId कॉन्स्टेंट पर सेट करें.
  8. पासकोड की सूची में सबसे ऊपर मौजूद, जारीकर्ता आईडी को कॉपी करें और इसे lib/constants.dart फ़ाइल में appStoreIssuerId कॉन्स्टेंट पर सेट करें.

9540ea9ada3da151.png

डिवाइस पर की गई खरीदारी को ट्रैक करना

अपनी खरीदारी को ट्रैक करने का सबसे सुरक्षित तरीका, सर्वर साइड पर है. इसकी वजह यह है कि क्लाइंट को सुरक्षित रखना मुश्किल होता है. हालांकि, आपके पास क्लाइंट को जानकारी वापस पाने का कोई तरीका होना चाहिए, ताकि ऐप्लिकेशन सदस्यता की स्थिति की जानकारी के हिसाब से काम कर सके. खरीदारी को 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. सब हो गया!

बधाई हो!!! आपने कोडलैब पूरा कर लिया है. इस कोडलैब का पूरा कोड, android_studio_folder.pngcomplete फ़ोल्डर में देखा जा सकता है.

ज़्यादा जानने के लिए, Flutter के अन्य कोडलैब आज़माएं.