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

1. परिचय

पिछली बार अपडेट किए जाने की तारीख: 11-07-2023

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

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

  1. 2000 डैश के लिए एक साथ कई चीज़ें खरीदने का विकल्प.
  2. पुरानी शैली के Dash को आधुनिक शैली के Dash में बनाने के लिए, एक बार में अपग्रेड की गई खरीदारी.
  3. ऐसी सदस्यता जो अपने-आप जनरेट होने वाले क्लिक को दोगुना करती है.

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

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

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

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

आपको जो ऐप्लिकेशन बनाने हैं उन्हें App Store और Play Store से संपर्क करना होगा. ऐसा इसलिए, ताकि उन्हें यह पता चल सके कि कौनसे प्रॉडक्ट उपलब्ध हैं और उनकी कीमत कितनी है. हर ऐप्लिकेशन की पहचान एक यूनीक आईडी से की जाती है. iOS App Store के लिए इसे बंडल आइडेंटिफ़ायर कहा जाता है और Android Play Store के लिए यह ऐप्लिकेशन आईडी होता है. ये आइडेंटिफ़ायर आम तौर पर, रिवर्स डोमेन नेम नोटेशन का इस्तेमाल करके बनाए जाते हैं. उदाहरण के लिए, flutter.dev के लिए इन-ऐप्लिकेशन खरीदारी वाला ऐप्लिकेशन बनाते समय, हम dev.flutter.inapppurchase का इस्तेमाल करेंगे. अपने ऐप्लिकेशन के लिए आइडेंटिफ़ायर के बारे में सोचें. अब आपको इसे प्रोजेक्ट सेटिंग में सेट करना है.

सबसे पहले, iOS के लिए बंडल आइडेंटिफ़ायर सेट अप करें.

Android Studio में प्रोजेक्ट खोलने के बाद, iOS फ़ोल्डर पर राइट क्लिक करें. इसके बाद, Flutter पर क्लिक करें और Xcode ऐप्लिकेशन में मॉड्यूल खोलें.

942772eb9a73bfaa.png

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

812f919d965c649a.jpeg

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

5c4733ac560ae8c2.png

3. प्लग इन इंस्टॉल करें

कोडलैब के इस हिस्से में, आप in_app_purchase प्लग इन इंस्टॉल करेंगे.

pubspec में डिपेंडेंसी जोड़ना

अपने pubspec में in_app_purchase को डिपेंडेंसी में जोड़कर, in_app_purchase को pubspec में जोड़ें:

$ cd app
$ flutter pub add in_app_purchase

pubspec.yaml

dependencies:
  ..
  cloud_firestore: ^4.0.3
  firebase_auth: ^4.2.2
  firebase_core: ^2.5.0
  google_sign_in: ^6.0.1
  http: ^0.13.4
  in_app_purchase: ^3.0.1
  intl: ^0.18.0
  provider: ^6.0.2
  ..

पैकेज डाउनलोड करने या कमांड लाइन में flutter pub get चलाने के लिए, pub get पर क्लिक करें.

4. App Store सेट अप करें

इन-ऐप्लिकेशन खरीदारी को सेट अप करने और उसे iOS पर टेस्ट करने के लिए, आपको App Store में एक नया ऐप्लिकेशन बनाना होगा. इसके बाद, वहां से खरीदे जा सकने वाले प्रॉडक्ट बनाने होंगे. आपको कुछ भी पब्लिश करने या ऐप्लिकेशन को समीक्षा के लिए, Apple पर भेजने की ज़रूरत नहीं है. ऐसा करने के लिए, आपके पास एक डेवलपर खाता होना चाहिए. अगर आपके पास Google Ads खाता नहीं है, तो Apple Developer Program में रजिस्टर करें.

इन-ऐप्लिकेशन खरीदारी का इस्तेमाल करने के लिए, आपको App Store Connect में, पैसे चुकाकर डाउनलोड किए जाने वाले ऐप्लिकेशन के लिए भी एक चालू कानूनी समझौता करना होगा. https://appstoreconnect.apple.com/ पर जाएं और कानूनी समझौते, टैक्स, और बैंकिंग पर क्लिक करें.

6e373780e5e24a6f.png

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

74c73197472c9aec.png

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

4a100bbb8cafdbbf.jpeg

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

Apple डेवलपर पोर्टल में कोई नया आइडेंटिफ़ायर बनाएं.

55d7e592d9a3fc7b.png

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

13f125598b72ca77.png

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

41ac4c13404e2526.png

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

9d2c940ad80deeef.png

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

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

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

10509b17fbf031bd.png

5b7c0bb684ef52c7.png

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

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

3ca2b26d4e391a4c.jpeg

अब आप Settings > में जाकर अपने iPhone पर सैंडबॉक्स उपयोगकर्ता सेट अप कर सकते हैं ऐप स्टोर > सैंडबॉक्स खाता.

c7dadad2c1d448fa.jpeg 5363f87efcddaa4.jpeg

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

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

  • dash_consumable_2k: इस्तेमाल की जा सकने वाली ऐसी खरीदारी जिसे कई बार खरीदा जा सकता है. इससे, लोगों को हर खरीदारी के लिए 2000 डैश (ऐप्लिकेशन के अंदर की मुद्रा) मिलते हैं.
  • 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

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

bd1b1d82eeee4cb3.png

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

d0bf39680ef0aa2e.png

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

99d5c4b446e8fecf.png

5. Play 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. Settings > पर जाएं लाइसेंस की जांच.
  3. टेस्टर के वही ईमेल पते जोड़ें जो इन-ऐप्लिकेशन खरीदारी की जांच कर सकते हैं.
  4. लाइसेंस का जवाब RESPOND_NORMALLY पर सेट करें.
  5. बदलाव सेव करें पर क्लिक करें.

a1a0f9d3e55ea8da.png

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

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

ऐप स्टोर की तरह ही, आपको तीन अलग-अलग खरीदारी को परिभाषित करना होगा:

  • dash_consumable_2k: इस्तेमाल की जा सकने वाली ऐसी खरीदारी जिसे कई बार खरीदा जा सकता है. इससे, लोगों को हर खरीदारी के लिए 2000 डैश (ऐप्लिकेशन के अंदर की मुद्रा) मिलते हैं.
  • 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 सेट अप करना

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

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

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

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

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

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

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

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

Firebase कंसोल पर जाएं और एक नया Firebase प्रोजेक्ट बनाएं. इस उदाहरण के लिए, प्रोजेक्ट डैश क्लिकर को कॉल करें.

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

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

7बाबा48832fbef29.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
    }
  }
}

Flutter के लिए Firebase सेट अप करना

हमारा सुझाव है कि Flutter ऐप्लिकेशन पर Firebase इंस्टॉल करने के लिए, FlutterFire सीएलआई का इस्तेमाल करें. सेटअप पेज में बताए गए निर्देशों का पालन करें.

फ़्लटरफ़ायर कॉन्फ़िगर करते समय, पिछले चरण में बनाया गया प्रोजेक्ट चुनें.

$ 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 डैशबोर्ड से, प्रोजेक्ट की खास जानकारी पर जाएं,सेटिंग चुनें और सामान्य टैब चुनें.

नीचे की ओर स्क्रोल करके, आपके ऐप्लिकेशन पर जाएं और डैशक्लिकर (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 खोलें. या अपनी पसंद के IDE के साथ.

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. अपनी ios/Runner/Info-Debug.plist और ios/Runner/Info-Release.plist, दोनों फ़ाइलों में वैल्यू को CFBundleURLTypes कुंजी के नीचे बदलें.
<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,
),

आपको 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!;
  }
}

अगर आपको जांच करनी है, तो आपको जांच को थोड़ा अपडेट करना होगा. TestIAPConnection का पूरा कोड देखने के लिए, GitHub पर widget_test.dart देखें.

test/widget_test.dart

void main() {
  testWidgets('App starts', (WidgetTester tester) async {
    IAPConnection.instance = TestIAPConnection();
    await tester.pumpWidget(const MyApp());
    expect(find.text('Tim Sneath'), findsOneWidget);
  });
}

lib/logic/dash_purchases.dart में, DashPurchases ChangeNotifier के कोड पर जाएं. फ़िलहाल, खरीदे गए डैश में सिर्फ़ DashCounter जोड़ा जा सकता है.

स्ट्रीम सदस्यता प्रॉपर्टी, _subscription (StreamSubscription<List<PurchaseDetails>> _subscription; टाइप की प्रॉपर्टी), IAPConnection.instance,, और इंपोर्ट जोड़ें. इससे बनने वाला कोड, यहां बताए गए तरीके से दिखना चाहिए:

lib/logic/dash_purchases.dart

import 'package:in_app_purchase/in_app_purchase.dart';

class DashPurchases extends ChangeNotifier {
  late StreamSubscription<List<PurchaseDetails>> _subscription;
  final iapConnection = IAPConnection.instance;

  DashPurchases(this.counter);
}

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

कंस्ट्रक्टर में, purchaseUpdatedStream पाएं और स्ट्रीम सुनना शुरू करें. dispose() तरीके में, स्ट्रीम की सदस्यता रद्द करें.

lib/logic/dash_purchases.dart

class DashPurchases extends ChangeNotifier {
  DashCounter counter;
  late StreamSubscription<List<PurchaseDetails>> _subscription;
  final iapConnection = IAPConnection.instance;

  DashPurchases(this.counter) {
    final purchaseUpdated =
        iapConnection.purchaseStream;
    _subscription = purchaseUpdated.listen(
      _onPurchaseUpdate,
      onDone: _updateStreamOnDone,
      onError: _updateStreamOnError,
    );
  }

  @override
  void dispose() {
    _subscription.cancel();
    super.dispose();
  }

  Future<void> buy(PurchasableProduct product) async {
    // omitted
  }

  void _onPurchaseUpdate(List<PurchaseDetails> purchaseDetailsList) {
    // Handle purchases here
  }

  void _updateStreamOnDone() {
    _subscription.cancel();
  }

  void _updateStreamOnError(dynamic error) {
    //Handle error here
  }
}

अब ऐप्लिकेशन को खरीदारी के अपडेट मिलते हैं, ताकि अगले सेक्शन में आपको खरीदारी करने से जुड़ी जानकारी मिले!

आगे बढ़ने से पहले, जांच में "flutter test"" का इस्तेमाल करें. इससे यह पुष्टि हो पाएगी कि सब कुछ सही तरीके से सेट अप किया गया है.

$ flutter test

00:01 +1: All tests passed!                                                                                   

8. ख़रीदारी करना

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

PrechsableProduct को अपनाना

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);
    for (var element in response.notFoundIDs) {
      debugPrint('Purchase $element not found');
    }
    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);
        break;
      case storeKeySubscription:
      case storeKeyUpgrade:
        await iapConnection.buyNonConsumable(purchaseParam: purchaseParam);
        break;
      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();
          break;
        case storeKeyConsumable:
          counter.addBoughtDashes(2000);
          break;
        case storeKeyUpgrade:
          _beautifiedDashUpgrade = true;
          break;
      }
    }

    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({Key? key}) : super(key: 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 में उपयोगकर्ता के लिए गैटर जोड़ें, ताकि आप खरीदारी की पुष्टि करने वाले फ़ंक्शन में यूज़र आईडी पास कर सकें.

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) {
      print('Successfully verified purchase');
      return true;
    } else {
      print('failed request: ${response.statusCode} - ${response.body}');
      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();
            break;
          case storeKeyConsumable:
            counter.addBoughtDashes(1000);
            break;
        }
      }
    }

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

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

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

/// 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;
}

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

Google Play पर की गई खरीदारी की अब पुष्टि हो जानी चाहिए और डेटाबेस में सेव हो जानी चाहिए.

इसके बाद, iOS के लिए App Store से की गई खरीदारी पर जाएं.

iOS पर की गई खरीदारी की पुष्टि करना: खरीदारी हैंडलर लागू करना

App Store से खरीदारी की पुष्टि करने के लिए, app_store_server_sdk नाम का एक तीसरे पक्ष का Dart पैकेज मौजूद है. यह इस प्रोसेस को आसान बनाता है.

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) {
      print('Successfully verified purchase');
      final receipts = response.latestReceiptInfo ?? [];
      for (final receipt in receipts) {
        final product = productDataMap[receipt.productId];
        if (product == null) {
          print('Error: Unknown product: ${receipt.productId}');
          continue;
        }
        switch (product.type) {
          case ProductType.nonSubscription:
            await iapRepository.createOrUpdatePurchase(NonSubscriptionPurchase(
              userId: userId,
              productId: receipt.productId ?? '',
              iapSource: IAPSource.appstore,
              orderId: receipt.originalTransactionId ?? '',
              purchaseDate: DateTime.fromMillisecondsSinceEpoch(
                  int.parse(receipt.originalPurchaseDateMs ?? '0')),
              type: product.type,
              status: NonSubscriptionStatus.completed,
            ));
            break;
          case ProductType.subscription:
            await iapRepository.createOrUpdatePurchase(SubscriptionPurchase(
              userId: userId,
              productId: receipt.productId ?? '',
              iapSource: IAPSource.appstore,
              orderId: receipt.originalTransactionId ?? '',
              purchaseDate: DateTime.fromMillisecondsSinceEpoch(
                  int.parse(receipt.originalPurchaseDateMs ?? '0')),
              type: product.type,
              expiryDate: DateTime.fromMillisecondsSinceEpoch(
                  int.parse(receipt.expiresDateMs ?? '0')),
              status: SubscriptionStatus.active,
            ));
            break;
        }
      }
      return true;
    } else {
      print('Error: Status: ${response.status}');
      return false;
    }
  }

App Store से की गई आपकी खरीदारी की पुष्टि हो चुकी है और इसे अब डेटाबेस में सेव कर दिया गया है!

बैकएंड चलाएं

ऐसे में, /verifypurchase एंडपॉइंट को सेवा देने के लिए dart bin/server.dart चलाया जा सकता है.

$ dart bin/server.dart 
Serving at http://0.0.0.0:8080

11. खरीदारी का ट्रैक रखें

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

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

बैकएंड पर स्टोर इवेंट प्रोसेस करना

स्टोर किसी भी बिलिंग इवेंट के बारे में, आपके बैकएंड को सूचना दे सकते हैं. जैसे, सदस्यताएं रिन्यू होने पर. अपने डेटाबेस में खरीदारी को अप-टू-डेट रखने के लिए, बैकएंड में इन इवेंट को प्रोसेस किया जा सकता है. इस सेक्शन में, इसे Google Play Store और Apple App Store, दोनों के लिए सेट अप करें.

Google Play Billing के इवेंट प्रोसेस करना

Google Play, क्लाउड pub/sub विषय के ज़रिए बिलिंग इवेंट उपलब्ध कराता है. ये ज़रूरी मैसेज लिस्ट हैं, जिन पर मैसेज पब्लिश किए जा सकते हैं और इनका इस्तेमाल भी किया जा सकता है.

यह सुविधा खास तौर पर Google Play के लिए है. इसलिए, आपने इसे GooglePlayPurchaseHandler में शामिल किया है.

lib/google_play_purchase_handler.dart को खोलकर और PubsubApi इंपोर्ट जोड़कर शुरुआत करें:

lib/google_play_purchase_handler.dart

import 'package:googleapis/pubsub/v1.dart' as pubsub;

इसके बाद, PubsubApi को GooglePlayPurchaseHandler को पास करें और इस तरह Timer बनाने के लिए, क्लास कंस्ट्रक्टर में बदलाव करें:

lib/google_play_purchase_handler.dart

class GooglePlayPurchaseHandler extends PurchaseHandler {
  final ap.AndroidPublisherApi androidPublisher;
  final IapRepository iapRepository;
  final pubsub.PubsubApi pubsubApi; // new

  GooglePlayPurchaseHandler(
    this.androidPublisher,
    this.iapRepository,
    this.pubsubApi, // new
  ) {
    // Poll messages from Pub/Sub every 10 seconds
    Timer.periodic(Duration(seconds: 10), (_) {
      _pullMessageFromPubSub();
    });
  }

Timer को हर 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 Billing के सभी इवेंट, अब इस विषय पर पब्लिश किए जाएंगे.

App Store के बिलिंग इवेंट प्रोसेस करना

इसके बाद, App Store के बिलिंग इवेंट के लिए भी ऐसा ही करें. App Store से खरीदारी के लिए, हैंडलिंग से जुड़े अपडेट लागू करने के दो असरदार तरीके हैं. एक तरीका यह है कि आप Apple को दिए गए वेबहुक को लागू करें. वह वेबहुक आपके सर्वर से संपर्क करने के लिए इस्तेमाल किया जाएगा. दूसरा तरीका, जो आपको इस कोडलैब में मिलेगा, वह है App Store के Server API से कनेक्ट करना और सदस्यता की जानकारी को मैन्युअल तौर पर हासिल करना.

यह कोडलैब दूसरे समाधान पर फ़ोकस क्यों कर रहा है, क्योंकि वेबहुक लागू करने के लिए आपको अपने सर्वर को इंटरनेट पर दिखाना होगा.

अगर आपको प्रोडक्शन के दौरान दोनों तरह के प्लैटफ़ॉर्म इस्तेमाल करने हैं, तो बेहतर होगा कि आप दोनों का इस्तेमाल करें. App Store से इवेंट की जानकारी पाने के लिए वेबहुक और Server API का इस्तेमाल करें. अगर आप किसी इवेंट से छूट गए हैं या आपको सदस्यता की स्थिति दोबारा देखनी है, तो इस वेबहुक का इस्तेमाल किया जा सकता है.

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. इसे एक नाम दें, जैसे "कोडलैब कुंजी".
  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() तरीके में लिसनर को हटाएं. शुरुआत में, लिसनर एक खाली फ़ंक्शन हो सकता है. 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() {
    iapRepo.removeListener(purchasesUpdate);
    _subscription.cancel();
    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_फ़ोल्डर.pngपूरा कोड देखें.

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