سلام؛
تو این مقاله میخوام روشی رو برای پیاده سازی همزمان پرداخت درون برنامه ای بازار و مایکت معرفی کنم که به کمک flavor بتونیم تنها با انتخاب نوع flavor، تمام فرآیند های انتخاب نوع پرداخت هم تغییر کنه.
نکته مهمی که این مقاله دربرگیرنده اون هستش نحوه استفاده از flavor ها هستش و پیاده سازی پرداخت درون برنامه صرفا مثالی برای این پیاده سازی هستش. یکی دیگه از بهترین مثال های پیاده سازی این روش، داشتن همزمان کدهای مربوط به GMS و HMS ( سرویس های موبایلی گوگل و هواوی) هستش که درصورت بارگذاری اپ داخل استور هواوی، میبایست کتابخونه های گوگل حذف شوند.
نکته: برای آشنایی به مبحث flavor در اندروید میتونید به مقالات مرتبط مراجعه کنید.
نکته: اگه حال خوندن مطلب رو ندارید من سورس نمونه این برنامه رو تو گیت خودم گذاشتم که از لینک زیر در دسترس هستش و میتونید کانفیگ هاش رو ببینید.
https://github.com/majidsa70/inAppPurchase
در واقع شما اگه یه برنامه یا بازی داشته باشی که داخلش محصولی برای فروش داشته باشی و شامل قوانین پرداخت درون برنامه ای بشه، باید برای هر استور، SDK پرداخت اون استور رو هم داشته باشی. (واقعا رو مخه !)
این مقاله از اون جایی شکل میگیره که خودم با این مساله مواجه بودم و مجبور شدم هر دو رو پیاده کنم. نکته ای که وجود داره اینه که وقتی بیلد بازار رو میخواد بذارید داخل بازار، نباید اثری از مایکت و یا سایر استور ها داخل بیلد کافه بازار باشه، و خب البته برعکس این هم صادقه، یعنی مایکت هم همین ایراد رو میگیره و البته بقیه استورها؛ پس باید یه راهی پیدا کنیم برای این موضوع که خب ممکنه راههای زیادی هم داشته باشه و هر کسی راه خودش رو بره و خب منم اینجا راه خودم رو معرفی میکنم. پس سعی میکنم گام به گام این پیاده سازی رو جلو ببریم.
1 - تعریف flavor های لازم در فایل build.gradle در ماژول اصلی
به فایل نمونه توجه کنید :
در این بخش من flavor های لازم رو اضافه کردم که میتونید تو عکس ببینید. بعد از اضافه کردن این بخش ها و rebuild کردن پروژه، در بخش Build Variants میتویند این flavor ها رو ببینید. مطابق تصویر زیر :
2 - اضافه کردن dependency های پرداخت براساس نوع flavor
در بخش dependency در فایل build.gradle ماژول، ما باید کتابخونه SDK رو براساس نوع flavor اضافه کنیم. با این روش وقتی برای یک استور مشخص بیلد بگیریم، dependency استور دیگه داخل بیلد و manifest قرار نمیگیره و این عالیه !
همونطور که در عکس بالا میبینید، من dependency رو براساس نوع flavor انتخاب کردم. به طور مثال برای flavor از نوع myket که در مرحله یک توضیح دادم، از دستور myKetImplementation استفاده کردم.
3 - حالا بیاید یکم کار جدی و تمیز انجام بدیم. از اونجایی که ما برای پیاده سازی پرداخت درون برنامه ای به طور یکسان با هر استور رفتار میکنیم (البته تقریبا)، یعنی فرقی نداره تو چه استوری هستیم، پس میتونیم به روش abstract یک دستورالعمل یکسان برای همه ی مراحل پرداخت بنویسیم و بعدش به کمک strategy pattern ، برای هر استور پیاده سازی (implementation) متفاوتی داشته باشیم. پس بیاید شروع کنیم :
سلکتور بخش پکیج بندی کد ها رو از حالت Android به Project تغییر بدید تا مشابه تصویر زیر بشه
حالا روی src کلیک راست کنید و بعد New -> Directory . حالا یک اسم کاملا مشابه اسم flavor براش انتخاب کنید. مثلا myket . سپس روی پوشه تازه ساخته شده دوباره مراحل قبل رو تکرار کنید و اینبار بجای تایپ کردن اسم، گزینه java رو از منوی نمایش داده شده انتخاب کنید:
حالا شما یک پوشه به اسم myket دارید که مشابه پوشه main هستش. حالا باید پوشه های داخلی myket رو هم مشابه main ایجاد کنید. (در واقع پوشه ها همون package ها هستن که موقع ساخت پروژه جدید، براساس اسمی که از شما میپرسه خودش ایجادشون میکنه ). در نهایت میشه مثل عکس زیر :
حالا مراحل بالا رو باید برای همه ی flavor هایی که قبلا ساختید هم انجام بدید.
4 - تو این مرحله من یه package جدید برای همه ی flavor ها به نام payment ایجاد میکنم. توجه داشته باشید که چون من میخوام فارغ از نوع flavor دستورات abstract رو بنویسم، پس payment رو تو همه ایجاد میکنم و بعدش کلاس abstract رو که از این به بعد بهش PaymentRepository میگم رو توی پوشه main میذارم که در واقع directory اصلی هستش. و بعد از اون سایر پوشه ها (flavor) کلاس پیاده سازی (implementation) خودشون رو خواهند داشت.
PaymentRepository :
interface PaymentRepository { fun initService( result: (Result<String>) -> Unit) fun stopConnection() fun launchPurchase( SKU: String,payload:String, activityResultRegistry: ActivityResultRegistry, result: (Result<MyPurchaseInfo>) -> Unit ) fun getPurchasedList(result: (Result<List<MyPurchaseInfo>>) -> Unit) fun consumePurchase(purchaseInfo: MyPurchaseInfo, result: (Result<MyPurchaseInfo>) -> Unit) }
همونطور که قبلا گفتم این فقط یک نمونه اس و شما میتونید مدل کاملتری از این پرداخت رو داشته باشید.
کلاس های نمونه پیاده سازی این interface رو میتونید توی سورس کامل github که ابتدای این مقاله گذاشتم ببینید.
موفق باشید.