আপনার Flutter অ্যাপে অ্যাপ-মধ্যস্থ কেনাকাটা যোগ করা হচ্ছে

1. ভূমিকা

শেষ আপডেট: 2023-07-11

একটি Flutter অ্যাপে অ্যাপ-মধ্যস্থ কেনাকাটা যোগ করার জন্য সঠিকভাবে অ্যাপ এবং প্লে স্টোর সেট আপ করা, ক্রয় যাচাই করা এবং সাবস্ক্রিপশন সুবিধার মতো প্রয়োজনীয় অনুমতি প্রদান করা প্রয়োজন।

এই কোডল্যাবে আপনি একটি অ্যাপে (আপনার জন্য দেওয়া) তিন ধরনের অ্যাপ-মধ্যস্থ কেনাকাটা যোগ করবেন এবং Firebase-এর সাথে একটি ডার্ট ব্যাকএন্ড ব্যবহার করে এই কেনাকাটাগুলি যাচাই করবেন। প্রদত্ত অ্যাপ, ড্যাশ ক্লিকার, একটি গেম রয়েছে যা মুদ্রা হিসাবে ড্যাশ মাসকট ব্যবহার করে। আপনি নিম্নলিখিত ক্রয় বিকল্প যোগ করবেন:

  1. একবারে 2000 ড্যাশের জন্য একটি পুনরাবৃত্তিযোগ্য ক্রয়ের বিকল্প।
  2. পুরানো শৈলী ড্যাশকে আধুনিক শৈলীর ড্যাশে পরিণত করার জন্য একটি এককালীন আপগ্রেড ক্রয়৷
  3. একটি সদস্যতা যা স্বয়ংক্রিয়ভাবে উৎপন্ন ক্লিক দ্বিগুণ করে।

প্রথম ক্রয়ের বিকল্পটি ব্যবহারকারীকে 2000 ড্যাশের সরাসরি সুবিধা দেয়। এগুলি সরাসরি ব্যবহারকারীর কাছে উপলব্ধ এবং অনেকবার কেনা যায়৷ এটিকে একটি ভোগ্য বলা হয় কারণ এটি সরাসরি খাওয়া হয় এবং একাধিকবার খাওয়া যায়।

দ্বিতীয় বিকল্পটি ড্যাশকে আরও সুন্দর ড্যাশে আপগ্রেড করে। এটি শুধুমাত্র একবার কিনতে হবে এবং চিরকালের জন্য উপলব্ধ। এই ধরনের ক্রয়কে অ-ভোগযোগ্য বলা হয় কারণ এটি অ্যাপ দ্বারা ব্যবহার করা যায় না তবে এটি চিরতরে বৈধ।

তৃতীয় এবং শেষ ক্রয় বিকল্পটি একটি সাবস্ক্রিপশন। সাবস্ক্রিপশন সক্রিয় থাকাকালীন ব্যবহারকারী আরও দ্রুত ড্যাশ পাবেন, কিন্তু যখন তিনি সাবস্ক্রিপশনের জন্য অর্থ প্রদান বন্ধ করেন তখন সুবিধাগুলিও চলে যায়।

ব্যাকএন্ড পরিষেবা (আপনার জন্যও সরবরাহ করা হয়েছে) একটি ডার্ট অ্যাপ হিসাবে চলে, কেনাকাটা করা হয়েছে কিনা তা যাচাই করে এবং Firestore ব্যবহার করে সেগুলি সঞ্চয় করে। Firestore প্রক্রিয়াটিকে সহজ করার জন্য ব্যবহার করা হয়, কিন্তু আপনার প্রোডাকশন অ্যাপে, আপনি যেকোনো ধরনের ব্যাকএন্ড পরিষেবা ব্যবহার করতে পারেন।

300123416ebc8dc1.png7145d0fffe6ea741.png646317a79be08214.png

আপনি কি নির্মাণ করবেন

  • আপনি উপভোগযোগ্য ক্রয় এবং সদস্যতা সমর্থন করার জন্য একটি অ্যাপ প্রসারিত করবেন।
  • ক্রয়কৃত আইটেমগুলি যাচাই এবং সংরক্ষণ করতে আপনি একটি ডার্ট ব্যাকএন্ড অ্যাপও প্রসারিত করবেন।

আপনি কি শিখবেন

  • ক্রয়যোগ্য পণ্যগুলির সাথে অ্যাপ স্টোর এবং প্লে স্টোর কীভাবে কনফিগার করবেন।
  • কেনাকাটা যাচাই করতে এবং সেগুলি Firestore-এ সঞ্চয় করতে কীভাবে স্টোরগুলির সাথে যোগাযোগ করবেন।
  • কীভাবে আপনার অ্যাপে কেনাকাটা পরিচালনা করবেন।

আপনি কি প্রয়োজন হবে

  • অ্যান্ড্রয়েড স্টুডিও 4.1 বা তার পরে
  • Xcode 12 বা তার পরে (iOS বিকাশের জন্য)
  • ফ্লটার SDK

2. উন্নয়ন পরিবেশ সেট আপ করুন

এই কোডল্যাবটি শুরু করতে, কোডটি ডাউনলোড করুন এবং iOS-এর জন্য বান্ডেল শনাক্তকারী এবং অ্যান্ড্রয়েডের প্যাকেজের নাম পরিবর্তন করুন।

কোডটি ডাউনলোড করুন

কমান্ড লাইন থেকে GitHub সংগ্রহস্থল ক্লোন করতে, নিম্নলিখিত কমান্ডটি ব্যবহার করুন:

git clone https://github.com/flutter/codelabs.git flutter-codelabs

অথবা, যদি আপনার GitHub এর cli টুল ইনস্টল করা থাকে তবে নিম্নলিখিত কমান্ডটি ব্যবহার করুন:

gh repo clone flutter/codelabs flutter-codelabs

নমুনা কোডটি একটি flutter-codelabs ডিরেক্টরিতে ক্লোন করা হয় যাতে কোডল্যাবগুলির একটি সংগ্রহের জন্য কোড থাকে। এই কোডল্যাবের কোডটি flutter-codelabs/in_app_purchases এ রয়েছে।

flutter-codelabs/in_app_purchases অধীনে থাকা ডিরেক্টরি কাঠামোতে প্রতিটি নামের ধাপের শেষে আপনার কোথায় থাকা উচিত তার একটি সিরিজ স্ন্যাপশট রয়েছে। স্টার্টার কোডটি ধাপ 0-এ রয়েছে, তাই মিলে যাওয়া ফাইলগুলি সনাক্ত করা যতটা সহজ:

cd flutter-codelabs/in_app_purchases/step_00

আপনি যদি এড়িয়ে যেতে চান বা একটি ধাপের পরে কিছু দেখতে কেমন হওয়া উচিত তা দেখতে চাইলে, আপনি যে ধাপে আগ্রহী সেই ধাপের নাম অনুসারে নির্দেশিকাটি দেখুন৷ শেষ ধাপের কোডটি complete ফোল্ডারের নীচে রয়েছে৷

স্টার্টার প্রকল্প সেট আপ করুন

আপনার প্রিয় IDE-তে step_00 থেকে স্টার্টার প্রকল্পটি খুলুন। আমরা স্ক্রিনশটগুলির জন্য অ্যান্ড্রয়েড স্টুডিও ব্যবহার করেছি, তবে ভিজ্যুয়াল স্টুডিও কোডও একটি দুর্দান্ত বিকল্প। যেকোনো একটি এডিটরের সাথে, নিশ্চিত করুন যে লেটেস্ট ডার্ট এবং ফ্লাটার প্লাগইন ইনস্টল করা আছে।

আপনি যে অ্যাপগুলি তৈরি করতে যাচ্ছেন সেগুলিকে অ্যাপ স্টোর এবং প্লে স্টোরের সাথে যোগাযোগ করতে হবে কোন পণ্যগুলি উপলব্ধ এবং কী দামে তা জানার জন্য৷ প্রতিটি অ্যাপ একটি অনন্য আইডি দ্বারা চিহ্নিত করা হয়। iOS অ্যাপ স্টোরের জন্য এটিকে বান্ডেল শনাক্তকারী বলা হয় এবং অ্যান্ড্রয়েড প্লে স্টোরের জন্য এটি অ্যাপ্লিকেশন আইডি। এই শনাক্তকারীগুলি সাধারণত একটি বিপরীত ডোমেন নামের স্বরলিপি ব্যবহার করে তৈরি করা হয়। উদাহরণস্বরূপ flutter.dev-এর জন্য একটি ইন অ্যাপ ক্রয় অ্যাপ তৈরি করার সময় আমরা dev.flutter.inapppurchase ব্যবহার করব। আপনার অ্যাপের জন্য একটি শনাক্তকারীর কথা চিন্তা করুন, আপনি এখন প্রকল্প সেটিংসে সেটি সেট করতে যাচ্ছেন।

প্রথমে, iOS এর জন্য বান্ডেল শনাক্তকারী সেট আপ করুন।

অ্যান্ড্রয়েড স্টুডিওতে প্রজেক্ট খোলার সাথে, iOS ফোল্ডারে ডান-ক্লিক করুন, Flutter এ ক্লিক করুন এবং Xcode অ্যাপে মডিউলটি খুলুন।

942772eb9a73bfaa.png

Xcode-এর ফোল্ডার স্ট্রাকচারে, রানার প্রোজেক্টটি শীর্ষে, এবং Flutter , Runner , এবং Products টার্গেট রানার প্রোজেক্টের নীচে। আপনার প্রকল্প সেটিংস সম্পাদনা করতে রানারে ডাবল-ক্লিক করুন, এবং সাইনিং এবং সক্ষমতা ক্লিক করুন। আপনার দল সেট করতে টিম ফিল্ডের অধীনে আপনি যে বান্ডিল শনাক্তকারীটি বেছে নিয়েছেন তা লিখুন।

812f919d965c649a.jpeg

আপনি এখন এক্সকোড বন্ধ করতে পারেন এবং অ্যান্ড্রয়েডের জন্য কনফিগারেশন শেষ করতে অ্যান্ড্রয়েড স্টুডিওতে ফিরে যেতে পারেন। এটি করতে android/app, অধীনে build.gradle ফাইলটি খুলুন এবং আপনার applicationId (নীচের স্ক্রিনশটে 37 লাইনে) অ্যাপ্লিকেশন আইডিতে পরিবর্তন করুন, iOS বান্ডেল শনাক্তকারীর মতো। মনে রাখবেন যে আইওএস এবং অ্যান্ড্রয়েড স্টোরগুলির জন্য আইডিগুলি অভিন্ন হতে হবে না, তবে তাদের অভিন্ন রাখা কম ত্রুটি প্রবণ এবং তাই এই কোডল্যাবে আমরা অভিন্ন শনাক্তকারীগুলিও ব্যবহার করব৷

5c4733ac560ae8c2.png

3. প্লাগইন ইনস্টল করুন

কোডল্যাবের এই অংশে আপনি in_app_purchase প্লাগইনটি ইনস্টল করবেন।

pubspec এ নির্ভরতা যোগ করুন

আপনার পাবস্পেকের নির্ভরতাগুলিতে in_app_purchase যোগ করে pubspec-এ in_app_purchase যোগ করুন:

$ 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
  ..

প্যাকেজ ডাউনলোড করতে pub get এ ক্লিক করুন বা কমান্ড লাইনে flutter pub get চালান।

4. অ্যাপ স্টোর সেট আপ করুন

অ্যাপ-মধ্যস্থ কেনাকাটা সেট আপ করতে এবং সেগুলি iOS-এ পরীক্ষা করতে, আপনাকে অ্যাপ স্টোরে একটি নতুন অ্যাপ তৈরি করতে হবে এবং সেখানে ক্রয়যোগ্য পণ্য তৈরি করতে হবে। আপনাকে কিছু প্রকাশ করতে হবে না বা অ্যাপটিকে পর্যালোচনার জন্য অ্যাপলে পাঠাতে হবে না। এটি করার জন্য আপনার একটি বিকাশকারী অ্যাকাউন্ট প্রয়োজন। আপনার যদি এটি না থাকে তবে Apple বিকাশকারী প্রোগ্রামে নথিভুক্ত করুন

অ্যাপ-মধ্যস্থ কেনাকাটা ব্যবহার করতে, অ্যাপ স্টোর কানেক্টে অর্থপ্রদানকারী অ্যাপগুলির জন্য আপনার একটি সক্রিয় চুক্তিও থাকতে হবে। https://appstoreconnect.apple.com/- এ যান এবং Agreements, Tax, and Banking-এ ক্লিক করুন।

6e373780e5e24a6f.png

আপনি এখানে বিনামূল্যে এবং অর্থপ্রদানের অ্যাপের জন্য চুক্তি দেখতে পাবেন। বিনামূল্যের অ্যাপগুলির স্থিতি সক্রিয় হওয়া উচিত এবং অর্থপ্রদানের অ্যাপগুলির স্থিতি নতুন। নিশ্চিত করুন যে আপনি শর্তাবলী দেখেছেন, তাদের গ্রহণ করেছেন এবং সমস্ত প্রয়োজনীয় তথ্য প্রবেশ করান।

74c73197472c9aec.png

যখন সবকিছু সঠিকভাবে সেট করা হয়, তখন অর্থপ্রদানের অ্যাপগুলির স্থিতি সক্রিয় হবে। এটি খুবই গুরুত্বপূর্ণ কারণ আপনি একটি সক্রিয় চুক্তি ছাড়া অ্যাপ-মধ্যস্থ কেনাকাটার চেষ্টা করতে পারবেন না।

4a100bbb8cafdbbf.jpeg

অ্যাপ আইডি নিবন্ধন করুন

অ্যাপল ডেভেলপার পোর্টালে একটি নতুন শনাক্তকারী তৈরি করুন।

55d7e592d9a3fc7b.png

অ্যাপ আইডি বেছে নিন

13f125598b72ca77.png

অ্যাপ নির্বাচন করুন

41ac4c13404e2526.png

কিছু বিবরণ প্রদান করুন এবং বান্ডেল আইডি সেট করুন যাতে পূর্বে XCode-এ সেট করা মানের সাথে বান্ডেল আইডির সাথে মিল থাকে।

9d2c940ad80deeef.png

কীভাবে একটি নতুন অ্যাপ আইডি তৈরি করতে হয় সে সম্পর্কে আরও নির্দেশনার জন্য, বিকাশকারী অ্যাকাউন্ট সহায়তা দেখুন।

একটি নতুন অ্যাপ তৈরি করা হচ্ছে

আপনার অনন্য বান্ডেল শনাক্তকারীর সাথে অ্যাপ স্টোর সংযোগে একটি নতুন অ্যাপ তৈরি করুন।

10509b17fbf031bd.png

5b7c0bb684ef52c7.png

কীভাবে একটি নতুন অ্যাপ তৈরি করবেন এবং চুক্তিগুলি পরিচালনা করবেন সে সম্পর্কে আরও নির্দেশনার জন্য, অ্যাপ স্টোর সংযোগ সহায়তা দেখুন।

অ্যাপ-মধ্যস্থ কেনাকাটা পরীক্ষা করার জন্য, আপনার একটি স্যান্ডবক্স পরীক্ষার ব্যবহারকারী প্রয়োজন। এই পরীক্ষা ব্যবহারকারীকে iTunes-এর সাথে সংযুক্ত করা উচিত নয়—এটি শুধুমাত্র অ্যাপ-মধ্যস্থ কেনাকাটা পরীক্ষা করার জন্য ব্যবহার করা হয়। আপনি এমন একটি ইমেল ঠিকানা ব্যবহার করতে পারবেন না যা ইতিমধ্যে একটি Apple অ্যাকাউন্টের জন্য ব্যবহৃত হয়েছে৷ ব্যবহারকারী এবং অ্যাক্সেসে , একটি নতুন স্যান্ডবক্স অ্যাকাউন্ট তৈরি করতে বা বিদ্যমান স্যান্ডবক্স অ্যাপল আইডিগুলি পরিচালনা করতে স্যান্ডবক্সের অধীনে পরীক্ষক- এ যান৷

3ca2b26d4e391a4c.jpeg

এখন আপনি সেটিংস > অ্যাপ স্টোর > স্যান্ডবক্স-অ্যাকাউন্টে গিয়ে আপনার আইফোনে আপনার স্যান্ডবক্স ব্যবহারকারী সেট আপ করতে পারেন।

c7dadad2c1d448fa.jpeg5363f87efcddaa4.jpeg

আপনার অ্যাপ-মধ্যস্থ কেনাকাটা কনফিগার করা হচ্ছে

এখন আপনি তিনটি ক্রয়যোগ্য আইটেম কনফিগার করবেন:

  • dash_consumable_2k : একটি ভোগ্য ক্রয় যা অনেকবার ক্রয় করা যায়, যা ব্যবহারকারীকে প্রতি ক্রয় 2000 ড্যাশ (অ্যাপ-এর মুদ্রা) প্রদান করে।
  • dash_upgrade_3d : একটি অ-ব্যবহারযোগ্য "আপগ্রেড" ক্রয় যা শুধুমাত্র একবার কেনা যায় এবং ব্যবহারকারীকে ক্লিক করার জন্য একটি প্রসাধনীভাবে ভিন্ন ড্যাশ দেয়।
  • dash_subscription_doubler : একটি সাবস্ক্রিপশন যা ব্যবহারকারীকে সাবস্ক্রিপশনের সময়কালের জন্য প্রতি ক্লিকে দ্বিগুণ ড্যাশ দেয়।

d156b2f5bac43ca8.png

অ্যাপ-মধ্যস্থ কেনাকাটা > পরিচালনায় যান।

নির্দিষ্ট আইডি দিয়ে আপনার অ্যাপ-মধ্যস্থ কেনাকাটা তৈরি করুন:

  1. dash_consumable_2k একটি Consumable হিসাবে সেট আপ করুন।

পণ্য আইডি হিসাবে 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 clicksSave এ ক্লিক করুন।

bd1b1d82eeee4cb3.png

আপনি সংরক্ষণ বোতামে ক্লিক করার পরে, একটি সাবস্ক্রিপশন মূল্য যোগ করুন। আপনি চান যে কোনো মূল্য চয়ন করুন.

d0bf39680ef0aa2e.png

আপনি এখন ক্রয়ের তালিকায় তিনটি ক্রয় দেখতে পাবেন:

99d5c4b446e8fecf.png

5. প্লে স্টোর সেট আপ করুন৷

অ্যাপ স্টোরের মতো, আপনার প্লে স্টোরের জন্য একটি বিকাশকারী অ্যাকাউন্টও প্রয়োজন। আপনার যদি এখনও একটি না থাকে তবে একটি অ্যাকাউন্ট নিবন্ধন করুন

একটি নতুন অ্যাপ তৈরি করুন

গুগল প্লে কনসোলে একটি নতুন অ্যাপ তৈরি করুন:

  1. প্লে কনসোল খুলুন।
  2. সমস্ত অ্যাপ নির্বাচন করুন > অ্যাপ তৈরি করুন।
  3. একটি ডিফল্ট ভাষা নির্বাচন করুন এবং আপনার অ্যাপের জন্য একটি শিরোনাম যোগ করুন। আপনার অ্যাপের নামটি টাইপ করুন যেভাবে আপনি এটিকে Google Play-তে দেখাতে চান। আপনি পরে নাম পরিবর্তন করতে পারেন.
  4. আপনার অ্যাপ্লিকেশন একটি খেলা উল্লেখ করুন. আপনি পরে এটি পরিবর্তন করতে পারেন।
  5. আপনার আবেদন বিনামূল্যে বা অর্থপ্রদান কিনা তা উল্লেখ করুন।
  6. একটি ইমেল ঠিকানা যোগ করুন যা প্লে স্টোর ব্যবহারকারীরা এই অ্যাপ্লিকেশন সম্পর্কে আপনার সাথে যোগাযোগ করতে ব্যবহার করতে পারে৷
  7. বিষয়বস্তু নির্দেশিকা এবং মার্কিন রপ্তানি আইন ঘোষণা সম্পূর্ণ করুন।
  8. অ্যাপ তৈরি করুন নির্বাচন করুন।

আপনার অ্যাপ তৈরি হওয়ার পরে, ড্যাশবোর্ডে যান এবং আপনার অ্যাপ সেট আপ বিভাগে সমস্ত কাজ সম্পূর্ণ করুন। এখানে, আপনি আপনার অ্যাপ সম্পর্কে কিছু তথ্য প্রদান করেন, যেমন কন্টেন্ট রেটিং এবং স্ক্রিনশট। 13845badcf9bc1db.png

আবেদনে স্বাক্ষর করুন

অ্যাপ-মধ্যস্থ কেনাকাটা পরীক্ষা করতে সক্ষম হওয়ার জন্য, আপনাকে Google Play-এ অন্তত একটি বিল্ড আপলোড করতে হবে।

এর জন্য, আপনার রিলিজ বিল্ডটি ডিবাগ কী ব্যতীত অন্য কিছুর সাথে স্বাক্ষর করতে হবে।

একটি কীস্টোর তৈরি করুন

আপনার যদি একটি বিদ্যমান কীস্টোর থাকে তবে পরবর্তী ধাপে যান। যদি তা না হয়, কমান্ড লাইনে নিম্নলিখিতটি চালিয়ে একটি তৈরি করুন।

ম্যাক/লিনাক্সে, নিম্নলিখিত কমান্ডটি ব্যবহার করুন:

keytool -genkey -v -keystore ~/key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key

উইন্ডোজে, নিম্নলিখিত কমান্ডটি ব্যবহার করুন:

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
   }

keystoreProperties অবজেক্টে key.properties ফাইলটি লোড করুন।

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-এর ড্যাশবোর্ড থেকে, Release > Testing > Closed testing- এ যান এবং একটি নতুন, ক্লোজড টেস্টিং রিলিজ তৈরি করুন।

এই কোডল্যাবের জন্য, আপনি অ্যাপটিতে সাইন করার জন্য Google-এ লেগে থাকবেন, তাই অপ্ট-ইন করতে প্লে অ্যাপ সাইনিং-এর অধীনে চালিয়ে যান টিপুন।

ba98446d9c5c40e0.png

এরপর, app-release.aab অ্যাপ বান্ডেল আপলোড করুন যা বিল্ড কমান্ড দ্বারা তৈরি করা হয়েছিল।

সংরক্ষণ ক্লিক করুন এবং তারপর রিভিউ রিলিজ ক্লিক করুন.

অবশেষে, অভ্যন্তরীণ পরীক্ষার রিলিজ সক্রিয় করতে অভ্যন্তরীণ পরীক্ষায় রোলআউট শুরু করুন ক্লিক করুন।

পরীক্ষা ব্যবহারকারীদের সেট আপ করুন

অ্যাপ-মধ্যস্থ কেনাকাটা পরীক্ষা করতে সক্ষম হওয়ার জন্য, আপনার পরীক্ষকদের Google অ্যাকাউন্ট দুটি স্থানে Google Play কনসোলে যোগ করতে হবে:

  1. নির্দিষ্ট টেস্ট ট্র্যাকের কাছে (অভ্যন্তরীণ পরীক্ষা)
  2. লাইসেন্স পরীক্ষক হিসেবে

প্রথমে, অভ্যন্তরীণ টেস্টিং ট্র্যাকে পরীক্ষক যোগ করে শুরু করুন। রিলিজ > টেস্টিং > ইন্টারনাল টেস্টিং- এ ফিরে যান এবং পরীক্ষক ট্যাবে ক্লিক করুন।

a0d0394e85128f84.png

ইমেল তালিকা তৈরি করুন ক্লিক করে একটি নতুন ইমেল তালিকা তৈরি করুন। তালিকার একটি নাম দিন, এবং অ্যাপ-মধ্যস্থ কেনাকাটার পরীক্ষা করার জন্য অ্যাক্সেস প্রয়োজন এমন Google অ্যাকাউন্টগুলির ইমেল ঠিকানাগুলি যোগ করুন৷

এরপরে, তালিকার জন্য চেকবক্স নির্বাচন করুন এবং পরিবর্তনগুলি সংরক্ষণ করুন ক্লিক করুন।

তারপর, লাইসেন্স পরীক্ষক যোগ করুন:

  1. Google Play Console-এর সমস্ত অ্যাপ ভিউতে ফিরে যান।
  2. সেটিংস > লাইসেন্স পরীক্ষায় যান।
  3. অ্যাপ-মধ্যস্থ কেনাকাটা পরীক্ষা করতে সক্ষম হওয়া পরীক্ষকদের একই ইমেল ঠিকানা যোগ করুন।
  4. RESPOND_NORMALLYলাইসেন্স প্রতিক্রিয়া সেট করুন।
  5. পরিবর্তনগুলি সংরক্ষণ করুন ক্লিক করুন।

a1a0f9d3e55ea8da.png

আপনার অ্যাপ-মধ্যস্থ কেনাকাটা কনফিগার করা হচ্ছে

এখন আপনি অ্যাপের মধ্যে ক্রয়যোগ্য আইটেমগুলি কনফিগার করবেন।

অ্যাপ স্টোরের মতো, আপনাকে তিনটি ভিন্ন ক্রয় সংজ্ঞায়িত করতে হবে:

  • dash_consumable_2k : একটি ভোগ্য ক্রয় যা অনেকবার ক্রয় করা যায়, যা ব্যবহারকারীকে প্রতি ক্রয় 2000 ড্যাশ (অ্যাপ-এর মুদ্রা) প্রদান করে।
  • dash_upgrade_3d : একটি অ-ব্যবহারযোগ্য "আপগ্রেড" ক্রয় যা শুধুমাত্র একবার কেনা যায়, যা ব্যবহারকারীকে ক্লিক করার জন্য একটি প্রসাধনীভাবে ভিন্ন ড্যাশ দেয়।
  • dash_subscription_doubler : একটি সাবস্ক্রিপশন যা ব্যবহারকারীকে সাবস্ক্রিপশনের সময়কালের জন্য প্রতি ক্লিকে দ্বিগুণ ড্যাশ দেয়।

প্রথমে, ব্যবহারযোগ্য এবং অ-ব্যবহারযোগ্য যোগ করুন।

  1. গুগল প্লে কনসোলে যান এবং আপনার অ্যাপ্লিকেশন নির্বাচন করুন।
  2. মনিটাইজ > পণ্য > অ্যাপ-মধ্যস্থ পণ্যগুলিতে যান।
  3. পণ্য তৈরি করুন ক্লিক করুন c8d66e32f57dee21.png
  4. আপনার পণ্যের জন্য প্রয়োজনীয় সমস্ত তথ্য লিখুন। আপনি যে আইডিটি ব্যবহার করতে চান তার সাথে পণ্যের আইডি মেলে তা নিশ্চিত করুন।
  5. Save এ ক্লিক করুন।
  6. সক্রিয় ক্লিক করুন.
  7. অ-ভোগযোগ্য "আপগ্রেড" ক্রয়ের জন্য প্রক্রিয়াটি পুনরাবৃত্তি করুন।

পরবর্তী, সদস্যতা যোগ করুন:

  1. গুগল প্লে কনসোলে যান এবং আপনার অ্যাপ্লিকেশন নির্বাচন করুন।
  2. মনিটাইজ > পণ্য > সাবস্ক্রিপশন- এ যান।
  3. সাবস্ক্রিপশন তৈরি করুন ক্লিক করুন 32a6a9eefdb71dd0.png
  4. আপনার সদস্যতার জন্য প্রয়োজনীয় সমস্ত তথ্য লিখুন। নিশ্চিত করুন যে পণ্যের আইডিটি আপনি যে আইডিটি ব্যবহার করতে চান তার সাথে মেলে।
  5. Save এ ক্লিক করুন

আপনার কেনাকাটা এখন প্লে কনসোলে সেট আপ করা উচিত।

6. Firebase সেট আপ করুন৷

এই কোডল্যাবে, আপনি ব্যবহারকারীদের ক্রয় যাচাই এবং ট্র্যাক করতে একটি ব্যাকএন্ড পরিষেবা ব্যবহার করবেন৷

একটি ব্যাকএন্ড পরিষেবা ব্যবহার করার বিভিন্ন সুবিধা রয়েছে:

  • আপনি নিরাপদে লেনদেন যাচাই করতে পারেন.
  • আপনি অ্যাপ স্টোর থেকে বিলিং ইভেন্টগুলিতে প্রতিক্রিয়া জানাতে পারেন।
  • আপনি একটি ডাটাবেসে ক্রয়ের ট্র্যাক রাখতে পারেন।
  • ব্যবহারকারীরা তাদের সিস্টেম ঘড়ি রিওয়াইন্ড করে প্রিমিয়াম বৈশিষ্ট্য প্রদান করে আপনার অ্যাপকে বোকা বানাতে পারবে না।

যদিও একটি ব্যাকএন্ড পরিষেবা সেট আপ করার অনেক উপায় আছে, আপনি Google এর নিজস্ব ফায়ারবেস ব্যবহার করে ক্লাউড ফাংশন এবং ফায়ারস্টোর ব্যবহার করে এটি করবেন।

ব্যাকএন্ড লেখাকে এই কোডল্যাবের সুযোগের বাইরে বিবেচনা করা হয়, তাই স্টার্টার কোডে ইতিমধ্যেই একটি ফায়ারবেস প্রকল্প অন্তর্ভুক্ত রয়েছে যা আপনাকে শুরু করতে প্রাথমিক কেনাকাটা পরিচালনা করে।

ফায়ারবেস প্লাগইনগুলিও স্টার্টার অ্যাপের সাথে অন্তর্ভুক্ত করা হয়েছে।

আপনার নিজের ফায়ারবেস প্রজেক্ট তৈরি করা, ফায়ারবেসের জন্য অ্যাপ এবং ব্যাকএন্ড উভয় কনফিগার করা এবং অবশেষে ব্যাকএন্ড স্থাপন করা।

একটি ফায়ারবেস প্রকল্প তৈরি করুন

Firebase কনসোলে যান, এবং একটি নতুন Firebase প্রকল্প তৈরি করুন৷ এই উদাহরণের জন্য, প্রকল্পটিকে কল করুন ড্যাশ ক্লিকার।

ব্যাকএন্ড অ্যাপে, আপনি একটি নির্দিষ্ট ব্যবহারকারীর সাথে কেনাকাটা টাই করেন, তাই আপনার প্রমাণীকরণ প্রয়োজন। এর জন্য, Google সাইন-ইন দিয়ে Firebase-এর প্রমাণীকরণ মডিউল ব্যবহার করুন।

  1. ফায়ারবেস ড্যাশবোর্ড থেকে, প্রমাণীকরণে যান এবং প্রয়োজনে এটি সক্ষম করুন।
  2. সাইন-ইন পদ্ধতি ট্যাবে যান এবং Google সাইন-ইন প্রদানকারী সক্ষম করুন।

7babb48832fbef29.png

যেহেতু আপনি ফায়ারবেসের ফায়ারস্টোর ডাটাবেসও ব্যবহার করবেন, এটিও সক্ষম করুন।

e20553e0de5ac331.png

এই মত ক্লাউড ফায়ারস্টোর নিয়ম সেট করুন:

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 সেট আপ করুন

Flutter অ্যাপে Firebase ইনস্টল করার প্রস্তাবিত উপায় হল FlutterFire CLI ব্যবহার করা। সেটআপ পৃষ্ঠায় বর্ণিত নির্দেশাবলী অনুসরণ করুন।

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 সেট আপ করুন: আরও ধাপ

ফায়ারবেস ড্যাশবোর্ড থেকে, প্রজেক্ট ওভারভিউতে যান, সেটিংস নির্বাচন করুন এবং সাধারণ ট্যাব নির্বাচন করুন।

আপনার অ্যাপে নিচে স্ক্রোল করুন এবং ড্যাশক্লিকার (অ্যান্ড্রয়েড) অ্যাপটি নির্বাচন করুন।

b22d46a759c0c834.png

ডিবাগ মোডে Google সাইন-ইন করার অনুমতি দিতে, আপনাকে অবশ্যই আপনার ডিবাগ শংসাপত্রের SHA-1 হ্যাশ ফিঙ্গারপ্রিন্ট প্রদান করতে হবে।

আপনার ডিবাগ স্বাক্ষর শংসাপত্র হ্যাশ পান

আপনার Flutter অ্যাপ প্রকল্পের মূলে, android/ ফোল্ডারে ডিরেক্টরি পরিবর্তন করুন তারপর একটি স্বাক্ষর প্রতিবেদন তৈরি করুন।

cd android
./gradlew :app:signingReport

আপনাকে সাইনিং কীগুলির একটি বড় তালিকা উপস্থাপন করা হবে। যেহেতু আপনি ডিবাগ শংসাপত্রের জন্য হ্যাশ খুঁজছেন, debug সেট করা Variant এবং Config বৈশিষ্ট্য সহ শংসাপত্রটি সন্ধান করুন। .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

অ্যান্ড্রয়েড স্টুডিওতে ios/ ফোল্ডারে রাইট ক্লিক করুন তারপর open iOS module in Xcode দ্বারা flutter ক্লিক করুন।

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 আপগ্রেডগুলি পরিচালনা করে যা আপনি Dashes দিয়ে কিনতে পারেন। এই কোডল্যাবটি 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);
}

_subscriptionlate কীওয়ার্ড যোগ করা হয়েছে কারণ _subscription কনস্ট্রাক্টরে আরম্ভ করা হয়েছে। এই প্রজেক্টটি ডিফল্টরূপে নন-নালবল হতে সেট আপ করা হয়েছে (NNBD), যার অর্থ হল যে বৈশিষ্ট্যগুলি বাতিলযোগ্য বলে ঘোষণা করা হয় না তাদের অবশ্যই একটি নন-নাল মান থাকতে হবে। 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. কেনাকাটা করুন

কোডল্যাবের এই অংশে, আপনি বর্তমানে বিদ্যমান মক পণ্যগুলিকে প্রকৃত ক্রয়যোগ্য পণ্যগুলির সাথে প্রতিস্থাপন করবেন। এই পণ্যগুলি স্টোর থেকে লোড করা হয়, একটি তালিকায় দেখানো হয় এবং পণ্যটি ট্যাপ করার সময় কেনা হয়।

ক্রয়যোগ্য পণ্য মানিয়ে নিন

PurchasableProduct একটি উপহাস পণ্য প্রদর্শন করে। নিম্নলিখিত কোড দিয়ে purchasable_product.dartPurchasableProduct ক্লাস প্রতিস্থাপন করে প্রকৃত সামগ্রী দেখানোর জন্য এটি আপডেট করুন:

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

দোকান উপলব্ধ হলে, উপলব্ধ কেনাকাটা লোড করুন. পূর্ববর্তী ফায়ারবেস সেটআপ দেওয়া, storeKeyConsumable , storeKeySubscription, এবং storeKeyUpgrade দেখার প্রত্যাশা করুন। একটি প্রত্যাশিত ক্রয় উপলব্ধ না হলে, কনসোলে এই তথ্য মুদ্রণ করুন; আপনি ব্যাকএন্ড পরিষেবাতে এই তথ্য পাঠাতে চাইতে পারেন।

await iapConnection.queryProductDetails(ids) পদ্ধতিটি খুঁজে পাওয়া যায়নি এমন আইডি এবং পাওয়া যায় এমন ক্রয়যোগ্য পণ্য উভয়ই ফেরত দেয়। UI আপডেট করতে প্রতিক্রিয়া থেকে 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-backend/ ফোল্ডার থেকে রুট হিসাবে কাজ করুন।

নিশ্চিত করুন যে আপনার নিম্নলিখিত সরঞ্জামগুলি ইনস্টল করা আছে:

বেস প্রকল্প ওভারভিউ

কারণ এই প্রকল্পের কিছু অংশ এই কোডল্যাবের সুযোগের বাইরে বিবেচিত হয়, সেগুলি স্টার্টার কোডে অন্তর্ভুক্ত করা হয়। আপনি শুরু করার আগে স্টার্টার কোডে যা আছে তা দেখে নেওয়া একটি ভাল ধারণা, আপনি কীভাবে জিনিসগুলি গঠন করতে যাচ্ছেন সে সম্পর্কে ধারণা পেতে।

এই ব্যাকএন্ড কোডটি আপনার মেশিনে স্থানীয়ভাবে চলতে পারে, এটি ব্যবহার করার জন্য আপনাকে এটি স্থাপন করার প্রয়োজন নেই। যাইহোক, আপনাকে আপনার ডেভেলপমেন্ট ডিভাইস (Android বা iPhone) থেকে সেই মেশিনে সংযোগ করতে সক্ষম হতে হবে যেখানে সার্ভারটি চলবে। এর জন্য, তাদের একই নেটওয়ার্কে থাকতে হবে এবং আপনাকে আপনার মেশিনের আইপি ঠিকানা জানতে হবে।

নিম্নলিখিত কমান্ড ব্যবহার করে সার্ভার চালানোর চেষ্টা করুন:

$ dart ./bin/server.dart

Serving at http://0.0.0.0:8080

ডার্ট ব্যাকএন্ড API এন্ডপয়েন্ট পরিবেশন করতে shelf এবং shelf_router ব্যবহার করে। ডিফল্টরূপে, সার্ভার কোনো রুট প্রদান করে না। পরে আপনি ক্রয় যাচাইকরণ প্রক্রিয়া পরিচালনা করার জন্য একটি রুট তৈরি করবেন।

একটি অংশ যা ইতিমধ্যেই স্টার্টার কোডে অন্তর্ভুক্ত করা হয়েছে তা হল lib/iap_repository.dartIapRepository । যেহেতু ফায়ারস্টোর বা সাধারণভাবে ডেটাবেসের সাথে কীভাবে ইন্টারঅ্যাক্ট করতে হয় তা শেখা এই কোডল্যাবের সাথে প্রাসঙ্গিক বলে বিবেচিত হয় না, স্টার্টার কোডটিতে আপনার জন্য Firestore-এ কেনাকাটা তৈরি বা আপডেট করার ফাংশন রয়েছে, সেইসাথে সেই কেনাকাটার জন্য সমস্ত ক্লাস রয়েছে৷

Firebase অ্যাক্সেস সেট আপ করুন

Firebase Firestore অ্যাক্সেস করতে, আপনার একটি পরিষেবা অ্যাকাউন্ট অ্যাক্সেস কী প্রয়োজন৷ Firebase প্রোজেক্ট সেটিংস খোলার জন্য একটি তৈরি করুন এবং পরিষেবা অ্যাকাউন্ট বিভাগে নেভিগেট করুন, তারপর নতুন ব্যক্তিগত কী তৈরি করুন নির্বাচন করুন।

27590fc77ae94ad4.png

ডাউনলোড করা JSON ফাইলটিকে assets/ ফোল্ডারে কপি করুন এবং service-account-firebase.json এ নাম পরিবর্তন করুন।

Google Play অ্যাক্সেস সেট আপ করুন

কেনাকাটা যাচাই করার জন্য প্লে স্টোর অ্যাক্সেস করতে, আপনাকে অবশ্যই এই অনুমতিগুলির সাথে একটি পরিষেবা অ্যাকাউন্ট তৈরি করতে হবে এবং এর জন্য JSON শংসাপত্রগুলি ডাউনলোড করতে হবে৷

  1. Google Play Console-এ যান এবং সমস্ত অ্যাপ পৃষ্ঠা থেকে শুরু করুন।
  2. সেটআপ > API অ্যাক্সেসে যান। 317fdfb54921f50e.png যদি Google Play Console অনুরোধ করে যে আপনি একটি বিদ্যমান প্রোজেক্ট তৈরি বা লিঙ্ক করুন, প্রথমে তা করুন এবং তারপরে এই পৃষ্ঠায় ফিরে আসুন।
  3. বিভাগটি খুঁজুন যেখানে আপনি পরিষেবা অ্যাকাউন্টগুলি সংজ্ঞায়িত করতে পারেন এবং নতুন পরিষেবা অ্যাকাউন্ট তৈরি করুন ক্লিক করুন৷ 1e70d3f8d794bebb.png
  4. পপ আপ হওয়া ডায়ালগে Google ক্লাউড প্ল্যাটফর্ম লিঙ্কে ক্লিক করুন। 7c9536336dd9e9b4.png
  5. আপনার প্রকল্প নির্বাচন করুন. আপনি যদি এটি দেখতে না পান তবে নিশ্চিত করুন যে আপনি উপরের ডানদিকে অ্যাকাউন্ট ড্রপ-ডাউন তালিকার অধীনে সঠিক Google অ্যাকাউন্টে সাইন ইন করেছেন৷ 3fb3a25bad803063.png
  6. আপনার প্রকল্প নির্বাচন করার পরে, উপরের মেনু বারে + পরিষেবা অ্যাকাউন্ট তৈরি করুন ক্লিক করুন। 62fe4c3f8644acd8.png
  7. পরিষেবা অ্যাকাউন্টের জন্য একটি নাম প্রদান করুন, ঐচ্ছিকভাবে একটি বিবরণ প্রদান করুন যাতে আপনি এটি কিসের জন্য তা মনে রাখতে পারেন এবং পরবর্তী ধাপে যান৷ 8a92d5d6a3dff48c.png
  8. পরিষেবা অ্যাকাউন্টটিকে সম্পাদকের ভূমিকা বরাদ্দ করুন। 6052b7753667ed1a.png
  9. উইজার্ডটি শেষ করুন, বিকাশকারী কনসোলের মধ্যে API অ্যাক্সেস পৃষ্ঠায় ফিরে যান এবং পরিষেবা অ্যাকাউন্টগুলি রিফ্রেশ করুন ক্লিক করুন৷ আপনি তালিকায় আপনার নতুন তৈরি অ্যাকাউন্ট দেখতে হবে. 5895a7db8b4c7659.png
  10. আপনার নতুন পরিষেবা অ্যাকাউন্টের জন্য অ্যাক্সেস মঞ্জুর করুন ক্লিক করুন৷
  11. পরবর্তী পৃষ্ঠায় স্ক্রোল করুন, আর্থিক ডেটা ব্লকে। আর্থিক ডেটা, অর্ডার এবং বাতিলকরণ সমীক্ষার প্রতিক্রিয়া দেখুন এবং অর্ডার এবং সদস্যতা পরিচালনা করুন উভয়ই নির্বাচন করুন। 75b22d0201cf67e.png
  12. ব্যবহারকারীকে আমন্ত্রণ করুন ক্লিক করুন। 70ea0b1288c62a59.png
  13. এখন যেহেতু অ্যাকাউন্ট সেট আপ করা হয়েছে, আপনাকে শুধু কিছু শংসাপত্র তৈরি করতে হবে। ক্লাউড কনসোলে ফিরে, পরিষেবা অ্যাকাউন্টের তালিকায় আপনার পরিষেবা অ্যাকাউন্ট খুঁজুন, তিনটি উল্লম্ব বিন্দুতে ক্লিক করুন এবং কীগুলি পরিচালনা করুন বেছে নিন। 853ee186b0e9954e.png
  14. একটি নতুন JSON কী তৈরি করুন এবং এটি ডাউনলোড করুন। 2a33a55803f5299c.pngcb4bf48ebac0364e.png
  15. ডাউনলোড করা ফাইলটিকে service-account-google-play.json, এ পুনঃনামকরণ করুন এবং এটিকে assets/ ডিরেক্টরিতে স্থানান্তর করুন৷

আমাদের আরও একটি জিনিস করতে হবে তা হল lib/constants.dart, এবং আপনার Android অ্যাপের জন্য বেছে নেওয়া প্যাকেজ আইডি দিয়ে androidPackageId এর মান প্রতিস্থাপন করুন।

অ্যাপল অ্যাপ স্টোর অ্যাক্সেস সেট আপ করুন

কেনাকাটা যাচাই করার জন্য অ্যাপ স্টোর অ্যাক্সেস করতে, আপনাকে একটি ভাগ করা গোপন সেট আপ করতে হবে:

  1. অ্যাপ স্টোর কানেক্ট খুলুন।
  2. My Apps এ যান এবং আপনার অ্যাপ নির্বাচন করুন।
  3. সাইডবার নেভিগেশনে, অ্যাপ-মধ্যস্থ কেনাকাটা > পরিচালনায় যান।
  4. তালিকার উপরের ডানদিকে, অ্যাপ-স্পেসিফিক শেয়ারড সিক্রেট ক্লিক করুন।
  5. একটি নতুন গোপন তৈরি করুন এবং এটি অনুলিপি করুন।
  6. lib/constants.dart, খুলুন এবং appStoreSharedSecret এর মানটি আপনার সদ্য জেনারেট করা শেয়ার্ড সিক্রেট দিয়ে প্রতিস্থাপন করুন।

d8b8042470aaeff.png

b72f4565750e2f40.png

ধ্রুবক কনফিগারেশন ফাইল

অগ্রসর হওয়ার আগে, নিশ্চিত হয়ে নিন যে নিম্নলিখিত ধ্রুবকগুলি lib/constants.dart কনফিগার করা আছে D ডার্ট ফাইল:

  • androidPackageId : অ্যান্ড্রয়েডে ব্যবহৃত প্যাকেজ আইডি। যেমন com.example.dashclicker
  • appStoreSharedSecret : ক্রয় যাচাইকরণ সম্পাদন করতে অ্যাপ স্টোর কানেক্ট অ্যাক্সেস করার জন্য ভাগ করা গোপনীয়তা।
  • bundleId : আইওএসে ব্যবহৃত বান্ডিল আইডি। যেমন com.example.dashclicker

আপনি আপাতত বাকী ধ্রুবকগুলি উপেক্ষা করতে পারেন।

10. ক্রয় যাচাই করুন

ক্রয়ের যাচাইয়ের জন্য সাধারণ প্রবাহ আইওএস এবং অ্যান্ড্রয়েডের জন্য অনুরূপ।

উভয় স্টোরের জন্য, আপনার অ্যাপ্লিকেশনটি যখন কোনও ক্রয় করা হয় তখন একটি টোকেন পায়।

এই টোকেনটি অ্যাপ্লিকেশন দ্বারা আপনার ব্যাকএন্ড পরিষেবাতে প্রেরণ করা হয়, যা তারপরে, সরবরাহিত টোকেন ব্যবহার করে সংশ্লিষ্ট স্টোরের সার্ভারগুলির সাথে ক্রয়টি যাচাই করে।

এরপরে ব্যাকএন্ড পরিষেবাটি ক্রয়টি সঞ্চয় করতে বেছে নিতে পারে এবং ক্রয়টি বৈধ ছিল কি না তা অ্যাপ্লিকেশনটির জবাব দিতে পারে।

ব্যাকএন্ড পরিষেবাটি আপনার ব্যবহারকারীর ডিভাইসে চলমান অ্যাপ্লিকেশনটির পরিবর্তে স্টোরগুলির সাথে বৈধতাটি করে, আপনি ব্যবহারকারীকে প্রিমিয়াম বৈশিষ্ট্যগুলিতে অ্যাক্সেস অর্জন করতে বাধা দিতে পারেন, উদাহরণস্বরূপ, তাদের সিস্টেম ঘড়িটি রিওয়াইন্ড করে।

ঝাঁকুনির দিকটি সেট আপ করুন

প্রমাণীকরণ সেট আপ করুন

আপনি যখন আপনার ব্যাকএন্ড পরিষেবাতে ক্রয়গুলি প্রেরণ করতে যাচ্ছেন, আপনি নিশ্চিত করতে চান যে কোনও ক্রয় করার সময় ব্যবহারকারী অনুমোদিত হয়েছে। স্টার্টার প্রকল্পে আপনার জন্য ইতিমধ্যে বেশিরভাগ প্রমাণীকরণের যুক্তি যুক্ত করা হয়েছে, আপনাকে কেবল নিশ্চিত করতে হবে যে PurchasePage লগইন বোতামটি দেখায় যখন ব্যবহারকারী এখনও লগ ইন না করা হয়। PurchasePage বিল্ড পদ্ধতির শুরুতে নিম্নলিখিত কোডটি যুক্ত করুন:

lib/পৃষ্ঠা/ক্রয়_পেজ.ডার্ট

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) ফাংশন তৈরি করুন যা এইচটিটিপি পোস্ট কল ব্যবহার করে আপনার ডার্ট ব্যাকএন্ডে /verifypurchase এন্ডপয়েন্টকে কল করে।

নির্বাচিত স্টোর (প্লে স্টোরের জন্য google_play বা অ্যাপ স্টোরের জন্য app_store ), serverVerificationData এবং productID প্রেরণ করুন। ক্রয়টি যাচাই করা হয়েছে কিনা তা নির্দেশ করে সার্ভার স্ট্যাটাস কোডটি ফেরত দেয়।

অ্যাপ কনস্ট্যান্টগুলিতে, আপনার স্থানীয় মেশিন আইপি ঠিকানায় সার্ভার আইপি কনফিগার করুন।

lib/লজিক/ড্যাশ_প্রেসেস.ডার্ট

  FirebaseNotifier firebaseNotifier;

  DashPurchases(this.counter, this.firebaseNotifier) {
    // omitted
  }

মেইন.ডার্টে DashPurchases তৈরির সাথে firebaseNotifier যুক্ত করুন main.dart:

lib/main.dart

        ChangeNotifierProvider<DashPurchases>(
          create: (context) => DashPurchases(
            context.read<DashCounter>(),
            context.read<FirebaseNotifier>(),
          ),
          lazy: false,
        ),

ফায়ারবেসেনোটিফায়ারে ব্যবহারকারীর জন্য একটি গেটার যুক্ত করুন, যাতে আপনি ব্যবহারকারী আইডিটি যাচাই ক্রয়ের ফাংশনে পাস করতে পারেন।

lib/লজিক/ফায়ারবেস_নোটিফায়ার.ডার্ট

  User? get user => FirebaseAuth.instance.currentUser;

DashPurchases ক্লাসে ফাংশন _verifyPurchase যুক্ত করুন। এই async ফাংশনটি একটি বুলিয়ান ফেরত দেয় যা ক্রয়টি বৈধ হয়েছে কিনা তা নির্দেশ করে।

lib/লজিক/ড্যাশ_প্রেসেস.ডার্ট

  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/লজিক/ড্যাশ_প্রেসেস.ডার্ট

  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/ক্রয়_হ্যান্ডলার.ডার্ট

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/ক্রয়_হ্যান্ডলার.ডার্ট

  /// Verify if purchase is valid and update the database
  Future<bool> verifyPurchase({
    required String userId,
    required ProductData productData,
    required String token,
  }) async {
    switch (productData.type) {
      case ProductType.subscription:
        return handleSubscription(
          userId: userId,
          productData: productData,
          token: token,
        );
      case ProductType.nonSubscription:
        return handleNonSubscription(
          userId: userId,
          productData: productData,
          token: token,
        );
    }
  }

এখন, আপনি উভয় ক্ষেত্রেই কেবল verifyPurchase কল করতে পারেন, তবে এখনও পৃথক বাস্তবায়ন রয়েছে!

ProductData ক্লাসে বিভিন্ন ক্রয়যোগ্য পণ্য সম্পর্কে প্রাথমিক তথ্য রয়েছে, যার মধ্যে পণ্য আইডি (কখনও কখনও এসকেইউ হিসাবেও পরিচিত) এবং ProductType অন্তর্ভুক্ত রয়েছে।

lib/product.dart

class ProductData {
  final String productId;
  final ProductType type;

  const ProductData(this.productId, this.type);
}

ProductType হয় সাবস্ক্রিপশন বা নন-সাবস্ক্রিপশন হতে পারে।

lib/product.dart

enum ProductType {
  subscription,
  nonSubscription,
}

অবশেষে, পণ্যগুলির তালিকা একই ফাইলের মানচিত্র হিসাবে সংজ্ঞায়িত করা হয়।

lib/product.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,
  ),
};

এরপরে, গুগল প্লে স্টোর এবং অ্যাপল অ্যাপ স্টোরের জন্য কিছু স্থানধারক বাস্তবায়ন সংজ্ঞায়িত করুন। গুগল প্লে দিয়ে শুরু করুন:

lib/google_play_purchase_handler.dart তৈরি করুন এবং এমন একটি ক্লাস যুক্ত করুন যা আপনি কেবল লিখেছেন এমন PurchaseHandler প্রসারিত করে:

lib/google_ple_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 উদাহরণ নেন। ক্রয় হ্যান্ডলার পরে ফায়ারস্টোরে ক্রয় সম্পর্কিত তথ্য সঞ্চয় করতে এই উদাহরণটি ব্যবহার করে। গুগল প্লে এর সাথে যোগাযোগ করতে, আপনি সরবরাহিত 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 ব্যবহার করে একটি এপিআই এন্ডপয়েন্ট তৈরি করুন:

বিন/সার্ভার.ডার্ট

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. উত্সের উপর নির্ভর করে GooglePlayPurchaseHandler বা AppStorePurchaseHandler জন্য, verifyPurchase পদ্ধতিতে কল করুন।
  8. যদি যাচাইকরণ সফল হয় তবে পদ্ধতিটি একটি Response.ok দেয় ok ক্লায়েন্টকে।
  9. যদি যাচাইকরণ ব্যর্থ হয় তবে পদ্ধতিটি একটি Response.internalServerError দেয় Clien

এপিআই এন্ডপয়েন্ট তৈরি করার পরে, আপনাকে দুটি ক্রয় হ্যান্ডলারগুলি কনফিগার করতে হবে। এর জন্য আপনার পূর্ববর্তী পদক্ষেপে প্রাপ্ত পরিষেবা অ্যাকাউন্ট কীগুলি লোড করা এবং অ্যান্ড্রয়েড প্রকাশক এপিআই এবং ফায়ারবেস ফায়ারস্টোর এপিআই সহ বিভিন্ন পরিষেবাগুলিতে অ্যাক্সেস কনফিগার করতে হবে। তারপরে, বিভিন্ন নির্ভরতা সহ দুটি ক্রয় হ্যান্ডলার তৈরি করুন:

বিন/সার্ভার.ডার্ট

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

অ্যান্ড্রয়েড ক্রয়গুলি যাচাই করুন: ক্রয়ের হ্যান্ডারটি প্রয়োগ করুন

এরপরে, গুগল প্লে ক্রয় হ্যান্ডলারটি বাস্তবায়ন চালিয়ে যান।

গুগল ইতিমধ্যে আপনার ক্রয়গুলি যাচাই করতে প্রয়োজনীয় এপিআইগুলির সাথে ইন্টারঅ্যাক্ট করার জন্য ডার্ট প্যাকেজ সরবরাহ করে। আপনি এগুলি server.dart ফাইলে আরম্ভ করেছেন এবং এখন এগুলি GooglePlayPurchaseHandler ক্লাসে ব্যবহার করুন।

নন-সাবস্ক্রিপশন-টাইপ ক্রয়ের জন্য হ্যান্ডলারটি প্রয়োগ করুন:

lib/google_ple_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_ple_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_ple_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,
  };
}

আপনার গুগল প্লে ক্রয়গুলি এখন যাচাই করা উচিত এবং ডাটাবেসে সংরক্ষণ করা উচিত।

এরপরে, আইওএসের জন্য অ্যাপ স্টোর ক্রয়ের দিকে এগিয়ে যান।

আইওএস ক্রয়গুলি যাচাই করুন: ক্রয় হ্যান্ডলারটি প্রয়োগ করুন

অ্যাপ স্টোর দিয়ে ক্রয় যাচাই করার জন্য, একটি তৃতীয় পক্ষের ডার্ট প্যাকেজটি app_store_server_sdk নামযুক্ত যা প্রক্রিয়াটিকে আরও সহজ করে তোলে।

ITunesApi উদাহরণ তৈরি করে শুরু করুন। স্যান্ডবক্স কনফিগারেশনটি ব্যবহার করুন, পাশাপাশি ত্রুটি ডিবাগিংয়ের সুবিধার্থে লগিং সক্ষম করুন।

lib/app_store_purchase_handler.dart

  final _iTunesAPI = ITunesApi(
    ITunesHttpClient(
      ITunesEnvironment.sandbox(),
      loggingEnabled: true,
    ),
  );

এখন, গুগল প্লে এপিআইগুলির বিপরীতে, অ্যাপ স্টোরটি সাবস্ক্রিপশন এবং নন-সাবস্ক্রিপশন উভয়ের জন্য একই এপিআই এন্ডপয়েন্টগুলি ব্যবহার করে। এর অর্থ হ'ল আপনি উভয় হ্যান্ডলারের জন্য একই যুক্তি ব্যবহার করতে পারেন। তাদের একত্রে মার্জ করুন যাতে তারা একই বাস্তবায়ন বলে:

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

আপনার অ্যাপ স্টোর ক্রয়গুলি এখন যাচাই করা উচিত এবং ডাটাবেসে সংরক্ষণ করা উচিত!

ব্যাকএন্ড চালান

এই মুহুর্তে, আপনি /verifypurchase এন্ডপয়েন্টটি পরিবেশন করতে dart bin/server.dart চালাতে পারেন।

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

১১. ক্রয়ের উপর নজর রাখুন

আপনার ব্যবহারকারীদের ক্রয়গুলি ট্র্যাক করার প্রস্তাবিত উপায়টি ব্যাকএন্ড পরিষেবাতে। এটি কারণ আপনার ব্যাকএন্ড স্টোর থেকে ইভেন্টগুলিতে সাড়া দিতে পারে এবং এভাবে ক্যাশিংয়ের কারণে পুরানো তথ্যগুলিতে দৌড়াতে কম ঝুঁকির পাশাপাশি টেম্পার হওয়ার জন্য কম সংবেদনশীল হতে পারে।

প্রথমে, আপনি যে ডার্ট ব্যাকএন্ডটি তৈরি করছেন তার সাথে ব্যাকএন্ডে স্টোর ইভেন্টগুলির প্রক্রিয়াকরণ সেট আপ করুন।

ব্যাকএন্ডে স্টোর ইভেন্টগুলি প্রক্রিয়া করুন

স্টোরগুলিতে আপনার যে কোনও বিলিং ইভেন্টগুলি ঘটে তার ব্যাকএন্ডকে অবহিত করার ক্ষমতা রয়েছে যেমন সাবস্ক্রিপশন পুনর্নবীকরণ। আপনার ডাটাবেস কারেন্টে ক্রয়গুলি রাখতে আপনি আপনার ব্যাকএন্ডে এই ইভেন্টগুলি প্রক্রিয়া করতে পারেন। এই বিভাগে, এটি গুগল প্লে স্টোর এবং অ্যাপল অ্যাপ স্টোর উভয়ের জন্য সেট আপ করুন।

প্রক্রিয়া গুগল প্লে বিলিং ইভেন্ট

গুগল প্লে তারা ক্লাউড পাব/সাব বিষয়কে যা বলে তার মাধ্যমে বিলিং ইভেন্টগুলি সরবরাহ করে। এগুলি মূলত বার্তা সারি যা বার্তাগুলি প্রকাশ করা যেতে পারে, পাশাপাশি গ্রাস করা যায়।

যেহেতু এটি গুগল প্লে সম্পর্কিত কার্যকারিতা নির্দিষ্ট, আপনি এই কার্যকারিতাটি GooglePlayPurchaseHandler অন্তর্ভুক্ত করেছেন।

lib/google_play_purchase_handler.dart খোলার মাধ্যমে এবং পাবসুবাপি আমদানি যুক্ত করে শুরু করুন:

lib/google_ple_purchase_handler.dart

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

তারপরে, PubsubApi GooglePlayPurchaseHandler কাছে পাস করুন এবং নিম্নলিখিত হিসাবে একটি Timer তৈরি করতে ক্লাস কনস্ট্রাক্টরকে সংশোধন করুন:

lib/google_ple_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 প্রতি দশ সেকেন্ডে _ _pullMessageFromSubSub পদ্ধতিতে কল করার জন্য কনফিগার করা হয়। আপনি সময়কালটি আপনার নিজের পছন্দকে সামঞ্জস্য করতে পারেন।

তারপরে, _pullMessageFromSubSub তৈরি করুন

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

আপনি সবেমাত্র যুক্ত হওয়া কোডটি প্রতি দশ সেকেন্ডে গুগল ক্লাউড থেকে পাব/সাব বিষয়ের সাথে যোগাযোগ করে এবং নতুন বার্তাগুলির জন্য জিজ্ঞাসা করে। তারপরে, _processMessage পদ্ধতিতে প্রতিটি বার্তা প্রক্রিয়া করে।

এই পদ্ধতিটি আগত বার্তাগুলি ডিকোড করে এবং প্রতিটি ক্রয় সম্পর্কে আপডেট হওয়া তথ্য, উভয় সাবস্ক্রিপশন এবং নন-সাবস্ক্রিপশন সম্পর্কে বিদ্যমান handleSubscription বা handleNonSubscription প্রয়োজনে কল করে।

প্রতিটি বার্তা _askMessage পদ্ধতিতে স্বীকৃত হওয়া দরকার।

এরপরে, server.dart ফাইলে প্রয়োজনীয় নির্ভরতা যুক্ত করুন। শংসাপত্রগুলি কনফিগারেশনে পাবসুবাপি.ক্লাউডপ্ল্যাটফর্মস্কোপ যুক্ত করুন:

বিন/সার্ভার.ডার্ট

 final clientGooglePlay =
      await auth.clientViaServiceAccount(clientCredentialsGooglePlay, [
    ap.AndroidPublisherApi.androidpublisherScope,
    pubsub.PubsubApi.cloudPlatformScope, // new
  ]);

তারপরে, পাবসুবাপি উদাহরণ তৈরি করুন:

বিন/সার্ভার.ডার্ট

  final pubsubApi = pubsub.PubsubApi(clientGooglePlay);

এবং শেষ অবধি, এটি GooglePlayPurchaseHandler কনস্ট্রাক্টরকে পাস করুন:

বিন/সার্ভার.ডার্ট

  return {
    'google_play': GooglePlayPurchaseHandler(
      androidPublisher,
      iapRepository,
      pubsubApi, // new
    ),
    'app_store': AppStorePurchaseHandler(
      iapRepository,
    ),
  };

গুগল প্লে সেটআপ

আপনি পাব/সাব বিষয় থেকে বিলিং ইভেন্টগুলি গ্রাস করার জন্য কোডটি লিখেছেন, তবে আপনি পাব/সাব বিষয় তৈরি করেন নি, বা আপনি কোনও বিলিং ইভেন্ট প্রকাশ করছেন না। এটি সেট আপ করার সময়।

প্রথমত, একটি পাব/সাব বিষয় তৈরি করুন:

  1. গুগল ক্লাউড কনসোলে ক্লাউড পাব/সাব পৃষ্ঠাটি দেখুন।
  2. আপনি আপনার ফায়ারবেস প্রকল্পে রয়েছেন তা নিশ্চিত করুন এবং + ক্লিক করুন বিষয় ক্লিক করুন। d5ebf6897a0a8bf5.png
  3. নতুন বিষয়টিকে একটি নাম দিন, GOOGLE_PLAY_PUBSUB_BILLING_TOPIC জন্য constants.ts সেটটির মান সেট করুন। এই ক্ষেত্রে, এটি play_billing নাম দিন। আপনি যদি অন্য কিছু চয়ন করেন তবে constants.ts আপডেট করার বিষয়টি নিশ্চিত করুন। বিষয় তৈরি করুন। 20d690fc543c4212.png
  4. আপনার পাব/উপ -বিষয়গুলির তালিকায়, আপনি সবেমাত্র তৈরি করা বিষয়টির জন্য তিনটি উল্লম্ব বিন্দুতে ক্লিক করুন এবং অনুমতিগুলি দেখুন ক্লিক করুন। EA03308190609fb.png
  5. ডানদিকে সাইডবারে, অধ্যক্ষ যুক্ত করুন চয়ন করুন।
  6. এখানে, google-play-developer-notifications@system.gserviceaccount.com যুক্ত করুন এবং এটিকে পাব/সাব প্রকাশকের ভূমিকা প্রদান করুন। 55631EC0549215bc.png
  7. অনুমতি পরিবর্তন সংরক্ষণ করুন।
  8. আপনি সবেমাত্র তৈরি করা বিষয়টির বিষয়টির নামটি অনুলিপি করুন।
  9. আবার প্লে কনসোলটি খুলুন এবং সমস্ত অ্যাপ্লিকেশন তালিকা থেকে আপনার অ্যাপ্লিকেশনটি চয়ন করুন।
  10. নীচে স্ক্রোল করুন এবং নগদীকরণ> নগদীকরণ সেটআপে যান।
  11. সম্পূর্ণ বিষয় পূরণ করুন এবং আপনার পরিবর্তনগুলি সংরক্ষণ করুন। 7e5e875dc6ce5d54.png

সমস্ত গুগল প্লে বিলিং ইভেন্টগুলি এখন বিষয়টিতে প্রকাশিত হবে।

প্রক্রিয়া অ্যাপ স্টোর বিলিং ইভেন্টগুলি

এরপরে, অ্যাপ স্টোর বিলিং ইভেন্টগুলির জন্য একই করুন। অ্যাপ স্টোরের জন্য ক্রয়গুলিতে হ্যান্ডলিং আপডেটগুলি বাস্তবায়নের দুটি কার্যকর উপায় রয়েছে। একটি হ'ল আপনি অ্যাপলকে সরবরাহ করেন এমন একটি ওয়েবহুক প্রয়োগ করে এবং তারা আপনার সার্ভারের সাথে যোগাযোগের জন্য ব্যবহার করে। দ্বিতীয় উপায়, যা আপনি এই কোডেল্যাবটিতে পাবেন তা হ'ল অ্যাপ স্টোর সার্ভার এপিআইয়ের সাথে সংযোগ স্থাপন এবং ম্যানুয়ালি সাবস্ক্রিপশন তথ্য প্রাপ্তি।

এই কোডল্যাবটি দ্বিতীয় সমাধানের দিকে মনোনিবেশ করার কারণ হ'ল ওয়েবহুকটি বাস্তবায়নের জন্য আপনাকে আপনার সার্ভারটি ইন্টারনেটে প্রকাশ করতে হবে।

একটি উত্পাদন পরিবেশে, আদর্শভাবে আপনি উভয়ই থাকতে চান। অ্যাপ স্টোর থেকে ইভেন্টগুলি পেতে ওয়েবহুক এবং আপনি যদি কোনও ইভেন্ট মিস করেন বা সাবস্ক্রিপশনের স্থিতি ডাবল চেক করতে হবে তবে সার্ভার এপিআই।

lib/app_store_purchase_handler.dart খোলার মাধ্যমে শুরু করুন এবং অ্যাপস্টোরস সার্ভারাপি নির্ভরতা যুক্ত করে:

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. আইএপ্রেসিটরি ব্যবহার করে ফায়ারস্টোর থেকে সক্রিয় সাবস্ক্রিপশনগুলির তালিকা অর্জন করে।
  2. প্রতিটি ক্রমের জন্য, এটি অ্যাপ স্টোর সার্ভার এপিআইতে সাবস্ক্রিপশন স্থিতি অনুরোধ করে।
  3. সেই সাবস্ক্রিপশন ক্রয়ের জন্য শেষ লেনদেনটি গ্রহণ করে।
  4. মেয়াদ শেষ হওয়ার তারিখ পরীক্ষা করে।
  5. ফায়ারস্টোরে সাবস্ক্রিপশন স্থিতি আপডেট করে, যদি এটি মেয়াদোত্তীর্ণ হয় তবে এটি চিহ্নিত করা হবে।

অবশেষে, অ্যাপ স্টোর সার্ভার এপিআই অ্যাক্সেস কনফিগার করতে সমস্ত প্রয়োজনীয় কোড যুক্ত করুন:

বিন/সার্ভার.ডার্ট

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

অ্যাপ স্টোর সেটআপ

এরপরে, অ্যাপ স্টোর সেট আপ করুন:

  1. অ্যাপ স্টোর সংযোগে লগ ইন করুন এবং ব্যবহারকারীদের নির্বাচন করুন এবং অ্যাক্সেস করুন
  2. কী টাইপ> ইন-অ্যাপ্লিকেশন ক্রয়ে যান।
  3. একটি নতুন যুক্ত করতে "প্লাস" আইকনে আলতো চাপুন।
  4. এটি একটি নাম দিন, যেমন "কোডেল্যাব কী"।
  5. কীযুক্ত পি 8 ফাইলটি ডাউনলোড করুন।
  6. SubscriptionKey.p8 নাম সহ এটি সম্পদ ফোল্ডারে অনুলিপি করুন।
  7. সদ্য নির্মিত কী থেকে কী আইডিটি অনুলিপি করুন এবং এটি lib/constants.dart ফাইলে appStoreKeyId ধ্রুবকটিতে সেট করুন।
  8. কী তালিকার শীর্ষে ইস্যুকারী আইডিটি অনুলিপি করুন এবং এটি lib/constants.dart ফাইলটিতে appStoreIssuerId ধ্রুবকটিতে সেট করুন।

9540EA9ADA3DA151.png

ডিভাইসে ক্রয় ট্র্যাক করুন

আপনার ক্রয়গুলি ট্র্যাক করার সর্বাধিক সুরক্ষিত উপায়টি সার্ভার সাইডে রয়েছে কারণ ক্লায়েন্টটি সুরক্ষিত করা শক্ত, তবে আপনার কাছে ক্লায়েন্টের কাছে তথ্য ফিরে পাওয়ার জন্য কিছু উপায় থাকতে হবে যাতে অ্যাপ্লিকেশনটি সাবস্ক্রিপশন স্থিতির তথ্যে কাজ করতে পারে। ফায়ারস্টোরে ক্রয়গুলি সংরক্ষণ করে আপনি সহজেই ক্লায়েন্টের সাথে ডেটা সিঙ্ক করতে পারেন এবং এটি স্বয়ংক্রিয়ভাবে আপডেট রাখতে পারেন।

আপনি ইতিমধ্যে অ্যাপটিতে আইপ্রেপো অন্তর্ভুক্ত করেছেন, এটি ফায়ারস্টোর রিপোজিটরি যা ব্যবহারকারীর সমস্ত ক্রয়ের ডেটা 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 এবং আপনি যখন ফায়ারস্টোর পরিবর্তনের ক্ষেত্রে ক্রয়গুলি প্রতিবার notifyListeners() কল করেন, ক্রয়কৃত পণ্যগুলি পরিবর্তিত হলে purchasesUpdate() পদ্ধতিটি সর্বদা কল করা হয়।

lib/লজিক/ড্যাশ_প্রেসেস.ডার্ট

  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 ব্যবহার করে সংগ্রহস্থলটি পেতে পারেন 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/লজিক/ড্যাশ_প্রেসেস.ডার্ট

void purchasesUpdate() {
    var subscriptions = <PurchasableProduct>[];
    var upgrades = <PurchasableProduct>[];
    // Get a list of purchasable products for the subscription and upgrade.
    // This should be 1 per type.
    if (products.isNotEmpty) {
      subscriptions = products
          .where((element) => element.productDetails.id == storeKeySubscription)
          .toList();
      upgrades = products
          .where((element) => element.productDetails.id == storeKeyUpgrade)
          .toList();
    }

    // Set the subscription in the counter logic and show/hide purchased on the
    // purchases page.
    if (iapRepo.hasActiveSubscription) {
      counter.applyPaidMultiplier();
      for (var element in subscriptions) {
        _updateStatus(element, ProductStatus.purchased);
      }
    } else {
      counter.removePaidMultiplier();
      for (var element in subscriptions) {
        _updateStatus(element, ProductStatus.purchasable);
      }
    }

    // Set the Dash beautifier and show/hide purchased on
    // the purchases page.
    if (iapRepo.hasUpgrade != _beautifiedDashUpgrade) {
      _beautifiedDashUpgrade = iapRepo.hasUpgrade;
      for (var element in upgrades) {
        _updateStatus(
          element,
          _beautifiedDashUpgrade
              ? ProductStatus.purchased
              : ProductStatus.purchasable);
      }
      notifyListeners();
    }
  }

  void _updateStatus(PurchasableProduct product, ProductStatus status) {
    if (product.status != ProductStatus.purchased) {
      product.status = ProductStatus.purchased;
      notifyListeners();
    }
  }

আপনি এখন নিশ্চিত করেছেন যে সাবস্ক্রিপশন এবং আপগ্রেডের স্থিতি সর্বদা ব্যাকএন্ড পরিষেবাতে বর্তমান এবং অ্যাপ্লিকেশনটির সাথে সিঙ্ক্রোনাইজড। অ্যাপটি সেই অনুযায়ী কাজ করে এবং আপনার ড্যাশ ক্লিকার গেমটিতে সাবস্ক্রিপশন এবং আপগ্রেড বৈশিষ্ট্যগুলি প্রয়োগ করে।

12. সব শেষ!

অভিনন্দন!!! আপনি কোডেল্যাব শেষ করেছেন। আপনি এই কোডেল্যাবের জন্য সম্পূর্ণ কোডটি খুঁজে পেতে পারেন android_studio_folder.png সম্পূর্ণ ফোল্ডার।

আরও জানতে, অন্যান্য ফ্লাটার কোডল্যাবগুলি চেষ্টা করে দেখুন।