Page tree
Skip to end of metadata
Go to start of metadata

آشنایی با مدل‌های پیاده‌سازی خرید درون‌برنامه‌ای

در مایکت سه مدل کلی برای کسب درآمد از طریق خرید درون‌برنامه‌ای وجود دارد، محصولات مصرف‌شدنی، مصرف‌نشدنی و اشتراکی. محصولات مصرف‌شدنی محصولاتی است که کاربر آن‌ها را استفاده می‌کند و سپس، دوباره اقدام به خرید می‌کند. مثال اینگونه محصولات سکه، GEM و... است. محصولات مصرف‌نشدنی محصولاتی هستند که کاربران یک بار برای همیشه مبلغی بابت آن‌ها پرداخت می‌کند، مانند باز کردن مراحلی از بازی، حذف تبلیغات و اکانت پریمیوم. محصولات اشتراکی برای مدت زمانی محدود به کاربر تحویل داده می‌شود. مثلا اشتراک یک ماههٔ یک برنامهٔ موسیقی یا اشتراک یک سالهٔ یک کتابخوان دیجیتالی. در این مدل، مدت زمان مجاز برای استفاده از برنامه توسط مایکت کنترل می‌شود و نیازی به هیچ پیاده‌سازی جهت کنترل زمان ندارند. در ادامه، نحوهٔ پیاده‌سازی این سه مدل را بررسی می‌کنیم.

بارگذاری برنامه در پنل توسعه‌دهندگان

برای شروع باید یک حساب توسعه‌دهنده بسازید و برنامهٔ خود را در پنل توسعه‌دهندگان تعریف کنید. سپس،‌ کافیست کلید رمز عمومی برنامهٔ خود را دریافت و محصولات خود را تعریف نمایید. توجه کنید که برای اضافه کردن برنامه به پنل توسعه‌دهندگان مایکت نیازی نیست حتما apk برنامهٔ خود را بارگذاری کنید. تنها کافی است، در قسمت اضافه کردن برنامه یک شناسه (Packagename) برای برنامهٔ خود رزرو کنید.

دریافت فایل‌های کمکی

برای شروع باید فایل‌های کمکی را دانلود و به برنامهٔ خود اضافه کنید. برای این کار کافی است ابتدا برنامه نمونه TrivialDrive را از اینجا دانلود کنید (یا در Github مشاهده نمایید) و مراحل زیر را انجام دهید:

۱. ابتدا پوشهٔ util را به برنامهٔ خود منتقل کنید.

۲. فایل IInAppBillingService.aidl را به پوشهٔ src/main/aidl/com.android.vending.billing کپی کنید. بعد از انجام این دو مرحله ساختار پروژه شما باید اینچنین باشد:


توجه کنید، ساختار تمام پروژه‌های اندرویدی یکسان نیست. در پروژه‌هایی که با Android Studio ایجاد شده‌اند، باید فایل‌های AIDL را در پوشه‌ی java/aidl کپی داد. ولی در ساختار‌های قدیمی مانند پروژه‌هایی که با Eclipse ایجاد شده‌اند، باید فایل‌های AIDL را در آدرس خودش (پکیجی که در خود فایل آورده شده است) و در کنار دیگر فایل‌های Java کپی کرد:

src/com/android/vending/licensing


۳. دسترسی زیر را در قسمت دسترسی‌های فایل AndroidManifest.xml برنامهٔ خود اضافه کنید.


۴. پروژهٔ شما باید بدون خطا Build شود.

مراحل پیاده‌سازی

ساختن IabHelper

برای شروع کار باید یک Instance از IabHelper ایجاد کنیم. با توجه به اینکه در متد‌ها و Callbackهای مختلف نیاز دارید تا به این Object دسترسی داشته باشید، بهتر است این Instance را به صورت یک فیلد در Activity خود تعریف کنید. برای ساختن IabHelper نیاز است تا کلید رمز عمومی را (که از پنل توسعه‌دهندگان دریافت کرده‌اید) به همراه Context به Constructor کلاس IabHelper بدهید:

 

تنظیم Logger

کلاس IabHelper لاگ‌های خود را با تگ "IabHelper" در خروجی لاگ‌های اندروید (LogCat) می‌نویسد. شما می‌توانید این لاگ‌ها را فعال یا غیر فعال نمایید:

توجه کنید که لاگ‌ها را در حالت debug فعال و قبل از انتشار (release) غیر فعال کنید. برای این کار می‌توانید از تنظیمات Gradle استفاده نمایید.

اتصال به سرویس مایکت (startSetup)

برای اتصال به سرویس پرداخت درون‌برنامه‌ای مایکت کافی است از متد startSetup استفاده کنید. این متد را باید قبل از هر متد دیگری صدا بزنید و در صورت متصل شدن به سرویس می‌توانید از سرویس پرداخت درون‌برنامه‌ای مایکت استفاده کنید. حال یک Callback از جنس OnIabSetupFinishedListener در پارامتر‌ این متد پیاده‌سازی کنید:

پس از تلاش برای اتصال به سرویس، متد onIabSetupFinished همراه با یک IabResult فراخوانی می‌شود و نتیجه را اعلام می‌کند. در صورتی که خروجی متد isSuccess در IabResult برابر TRUE بود، اتصال با موفقیت انجام شده است. در غیر این صورت برنامهٔ شما نتوانسته به سرویس مایکت متصل شود که در این صورت می‌توانید از متد getMessage برای علت خطا استفاده کنید.

 

توجه کنید که در تمامی Callbackهای کلاس IabHelper باید به دلیل تاخیر در جواب حتما چک کنید که Instance این Object برابر با Null نشده باشد:

if (mHelper == null) return;

 

به‌روز کردن اطلاعات خرید(queryInventory)

به محض متصل شدن به سرویس مایکت (یعنی زمانی که متد isSuccess در IabResult برابر TRUE بود) باید خرید‌های کاربر را به‌روز کنید. به‌روز کردن خرید کاربر‌ها در مدل‌های محصولات مختلف مفهوم‌های مختلفی دارد:

برای محصولات مصرف‌شدنی این معنی را می‌دهد که به هر علت یک خرید مصرف نشده وجود دارد که باید مصرف شود و به کاربر تحویل داده شود. مثلا فرض کنید که در فرایند خرید قبلی، کاربر با مشکل مواجه می‌شود یا به هر دلیلی قبل از مصرف کردن محصول، برنامهٔ شما بسته می‌شود و یا ارتباط کاربر قطع می‌گردد. در این صورت باید در اجرای بعدی این محصول را مصرف کنید و به کاربر تحول دهید.

علت به‌روز کردن محصولات برای محصولات مصرف‌نشدنی این است که از این طریق متوجه می‌شوید که کاربر محصول شما را خریداری کرده است یا نه. در صورتی که خریداری کرده بود امکان مورد نظر (مثلا حذف تبلیغات یا مراحل کامل بازی یا ...) را برای او فعال می‌کنید.

در مورد محصولات اشتراکی هم از این طریق متوجه می‌شوید که کاربر هنوز مشترک برنامهٔ شما هست یا خیر. در صورتی که زمان اشتراک تمام نشده بود در queryInventory به شما خبر داده می‌شود.

در واقع می‌توان گفت در queryInventory، تمام محصولاتی که مصرف نکرده‌اید، همیشه برمی‌گردد. در صورتی که مصرف‌شدنی بود که باید همان‌جا مصرف شود و بعد تحویل کاربر شود و در صورتی که مصرف‌نشدنی یا اشتراکی بود، باید به کاربر تحویل داده شود. 

همچنین با استفاده از queryInventory می‌توانید اطلاعات مربوط به محصولات خود (نام محصول، توضیحات، قیمت و ...) که در پنل توسعه‌دهندگان وارد نموده‌اید را دریافت کنید. مثلا فرض کنید که بعد از انتشار خود قیمت یا نام یکی از محصولات خود را تغییر می‌دهید. با استفاده از این قابلیت می‌توانید در UI برنامهٔ خود همیشه مشخصات محصولی را نشان دهید که در پنل توسعه‌دهندگان مایکت قرار دارد و با تغییر آن برنامهٔ شما نیز تغییر می‌کند.

برای به‌روز کردن خرید‌ها باید نام محصولات را به همراه یک Callback از جنس QueryInventoryFinishedListener به متد queryInventoryAsync از کلاس IabHelper بدهید تا فرایند به‌روز کردن آغاز شود:

متد queryInventoryAsync دارای چهار پارامتر است که به ترتیب:

۱. boolean querySkuDetails: در صورتی که می‌خواهید اطلاعات محصولات به‌روز شود (نام، قیمت و ...) این Boolean را برابر TRUE قرار دهید. توجه کنید که در صورتی که نمی‌خواهید از این قابلیت استفاده کنید این مقدار را برابر FALSE قرار دهید تا سرویس اضافه‌ای صدا نشود.

۲. List<String> moreItemSkus: لیستی از ID محصولات فروشی (مصرف‌شدنی و مصرف‌نشدنی). در صورتی که برنامهٔ شما محصولات فروشی ندارد این لیست را برابر NULL قرار دهید.

۳. List<String> moreSubsSkus: لیستی از ID محصولات اشتراکی. در صورتی که برنامهٔ شما محصولات اشتراکی ندارد این لیست را برابر NULL قرار دهید.

۴. QueryInventoryFinishedListener listener: برای جواب این سرویس Callbackی از جنس QueryInventoryFinishedListener ست شود.

توجه کنید که در یک لحظه می‌تواند فقط یک درخواست به سرویس مایکت زد. در صورتی که درخواستی در حال اجرا باشد و شما درخواست دیگری را صدا بزنید، خطای IabAsyncInProgressException رخ می‌دهد.


نتیجهٔ این درخواست با دو Object با نام‌های IabResult و Inventory در متد onQueryInventoryFinished بازمی‌گردد. از IabResult موفق بودن/نبودن درخواست مشخص می‌شود و خرید‌های به‌روز شده و اطلاعات محصولات در Inventory قرار می‌گیرد. Inventory شامل دو Map با نام‌های mSkuMap و mPurchaseMap هست. خرید‌های به‌روز‌ شده در mPurchaseMap قرار می‌گیرد که با استفاده از متد getPurchase در کلاس Inventory می‌توانید متوجه شوید برای محصول مورد نظرتان خرید به‌روز شده‌ای وجود دارد یا خیر. همچنین می‌توانید با استفاده از متد getAllPurchases به تمامی لیستی (از جنس Purchase) درسترسی پیدا کنید.

همچنین می‌توانید اطلاعات خرید که در پنل توسعه‌دهندگان تنظیم کرده‌اید را در mSkuMap بیابید. توجه کنید در صورتی که مقدار querySkuDetails را برابر FALSE قرار دهید این Map برابر با NULL خواهد بود. با استفاده از متد getSkuDetails در کلاس Inventory می‌توانید به اطلاعات خرید محصول مورد نظر دسترسی پیدا کنید و UI برنامهٔ خود را به‌روز نمایید.

مفهوم queryInventory، تجمیع دو مفهوم getPurchases و getSkuDetail است. در واقع با هر queryInventory، یک getPurchases زده می‌شود و در صورتی که فیلد querySkuDetails را TRUE قرار دهید، getSkuDetail هم صدا زده می‌شود. برای آشنایی بیشتر با مفاهیم این APIها ویدئو آموزشی مقدماتی را ببینید یا مستند آشنایی با خرید درون‌برنامه‌ای مایکت را مطالعه نمایید.


نمونه‌ای از QueryInventoryFinishedListener را می‌توانید در اینجا ببینید:

همانطور که در مثال بالا می‌بینید در قسمت (1) محصول با شناسهٔ SKU_PREMIUM را از خرید‌های به‌روز شده جست‌وجو می‌کند و در صورتی که premiumPurchase برابر با NULL نباشد این محصول مصرف‌نشدنی به کاربر تحویل می‌شود.

در قسمت (2) برای محصولات اشتراکی جست‌وجو می‌شود در صورتی که اشتراک ماهیانه یا سالیانه کاربر فعال باشد، مقدار gasMonthly یا gasYearly مخالف NULL خواهد بود.

در قسمت (3) برای محصول مصرف‌شدنی SKU_GAS جست‌وجو انجام می‌شود و در صورت NULL نبودن gasPurchase این محصول مصرف می‌شود (Consume) و سپس به کاربر تحویل داده خواهد شد. در خصوص Consume در ادامه توضیح خواهیم داد.


فیلد‌های آبجکت Purchase به صورت زیر است:

 

توضیحاتTypeنام
نوع محصول (inapp/subs)StringmItemType
شمارهٔ فاکتور خریدStringmOrderId
پکیج‌نیم برنامهٔ شماStringmPackageName
ID محصولStringmSku
Timestamp زمان خریدlongmPurchaseTime
وضعیت خرید (مصرف‌شده / مصرف نشده)intmPurchaseState
Developer payloadStringmDeveloperPayload
توکن خرید این محصولStringmToken
رشتهٔ JSON که از سرور مایکت برمی‌گرددString
mOriginalJson
Signiture رشتهٔ mOriginalJsonStringmSignature
فعال/غیر‌فعال بودن Auto RenewbooleanmIsAutoRenewing


با توجه به اینکه اولین درخواستی که باید بعد از اتصال به سرویس مایکت صدا زده شود در خواست queryInventoryAsync است، بدنهٔ کلی متد startSetup به صورت زیر خواهد بود (با فرض اینکه لیست‌های subSkus و itemSkus با مقادیر IDهای محصولات ساخته شده است و mGotInventoryListener نیز پیاده‌سازی شده است):

«بسیار مهم»

در صورتی که queryInventory را در لحظهٔ شروع برنامه صدا نزنید ممکن است از کاربران برنامهٔ‌ شما مبلغی کاسته شود و هیچگاه، محصول تحویل نگردد. برخی توسعه‌دهندگان برای به‌روز کردن خرید‌ها از دکمه‌ای با نام «به‌روزرسانی خرید‌ها» در برنامهٔ خود استفاده می‌کنند که در آن queryInventory را فراخوانی می‌کنند. توجه کنید که حتی با پیاده‌سازی این دکمه باید در لحظهٔ شروع برنامه خرید‌های کاربر را به‌روز کنید. این مشکل در بین توسعه‌دهندگان سرویس پرداخت درون‌برنامه‌ای بسیار متداول است.

شروع فرایند خرید (launchPurchaseFlow)

فرض کنید که برنامهٔ شما باز شده و با موفقیت به سرویس مایکت متصل گردیده است. کاربر وارد صفحهٔ فروشگاه برنامه می‌شود و لیستی از محصولات شما را به همراه قیمت‌های آن‌ها مشاهده می‌کند (لیستی که با استفاده از mSkuMap در کلاس Inventory ساخته‌اید). در کنار هر آیتم لیست یک دکمهٔ خرید وجود دارد. کاربر با زدن دکمهٔ خرید وارد فرایند خرید آن محصول می‌شود. شروع فرایند خرید تمامی محصولات درون‌برنامه‌ای با استفاده از متد launchPurchaseFlow در کلاس IabHelper، به صورت زیر است:

متد launchPurchaseFlow پارامتر‌های زیر را به ترتیب می‌پذیرد:

۱. Activity act: اکتیویتی برنامهٔ شما که فرایند خرید در آن انجام می‌شود.

۲. String sku: شناسهٔ محصول درون‌برنامه‌ای که قصد خرید آن را دارید.

۳. int requestCode: یک Integer به عنوان requestCode جهت چک کردن در onActivityResult

۴. OnIabPurchaseFinishedListener listener: یک Callback از جنس OnIabPurchaseFinishedListener که پایان فرایند خرید را خبر می‌دهد.

۵. String developerPayload: مقدار Developer payload این خرید.

 

زمانی که با استفاده از launchPurchaseFlow فرایند خرید را آغاز می‌کنید، یک Activity از مایکت روی برنامهٔ شما باز می‌شود و مشخصات محصول به همراه درگاه‌های خرید را به کاربر نمایش می‌دهد تا کاربر خرید را انجام دهد. زمانی که خرید به صورت موفق یا ناموفق به پایان رسید، مایکت با requestCodeی که در launchPurchaseFlow تعیین نموده‌اید، متد onActivityResult اکتیویتی برنامهٔ شما را صدا می‌زند و شما باید به صورت زیر این رویداد را به IabHelper خود اطلاع دهید:

سپس IabHelper پس از بررسی Signature خرید با کلید رمز عمومی شما، OnIabPurchaseFinishedListener را فراخوانی می‌کند. در متد onIabPurchaseFinished دو Object با نام‌های IabResult و Purchase باز می‌گردد. IabResult نشان می‌دهد که خرید موفق انجام شده است یا خیر و در صورت موفقیت، مشخصات محصول خریداری شده در Purchase قرار داده می‌شود. یک نمونه از پیاده‌سازی این Callback به صورت زیر است:

پس از اینکه خرید با موفقیت انجام شد، با توجه به مدل محصول، آن را به کاربر تحویل دهید. اگر محصول برنامهٔ شما مصرف‌نشدنی یا اشتراکی است باید آن را پس از onIabPurchaseFinished موفقیت آمیز به کاربر تحویل دهید. در صورتی که محصول برنامهٔ شما مصرف‌شدنی است ابتدا باید آن را Consume کنید و سپس به کاربر تحویل دهید. Consume کردن یک محصول باعث می‌شود که آن محصول، در لیست queryInventory (محصولات به‌روز شده) بازنگردد، بنابراین محصولات مصرف‌نشدنی و اشتراکی را Consume نکنید.


برای Consume کردن یک محصول، از متد consumeAsync به همراه یک Purchase و یک Callback از جنس OnConsumeFinishedListener استفاده کنید:

یک نمونه از پیاده‌سازی OnConsumeFinishedListener به صورت زیر است:

در متد onConsumeFinished دقیقا مانند onIabPurchaseFinished، یک Purchase برای مشخصات خرید و یک IabResult برای نتیجهٔ Consume برمی‌گردد. در صورتی که isSuccess برابر TRUE بود، می‌توانید محصول را به کاربر تحویل دهید (مثلا سکهٔ او را افزایش دهید).

ارسال اطلاعات خرید به سرور

در صورتی که تحویل محصولات در منطق برنامهٔ شما سمت سرور انجام می‌شود، لازم است که اطلاعات خرید و کاربر خود را به سرور ارسال کنید. پیشنهاد می‌شود که این کار را پس از انجام موفق Consume در OnConsumeFinishedListener انجام دهید. برای این کار کافی است که اطلاعات خرید را با استفاده از متد getOriginalJson در کلاس Purchase به سرور ارسال کنید. توجه کنید که فیلد token در این Json برای تمام خرید‌های برنامهٔ شما یکتا است و می‌توانید به عنوان کلید از آن استفاده کنید. توجه کنید که لازم است حتما در کنار این Json مقدار Signature را نیز (با استفاده از متد getSignature در کلاس Purchase) برای سرور ارسال کنید و با استفاده از کلید رمز عمومی که از پنل توسعه‌دهندگان مایکت دریافت نموده‌اید، با هم تطبیق دهید.

آماده‌سازی جهت بستن برنامه (onDestroy)

در onDestroy اکتیویتی برنامهٔ خود که یک Instance از IabHelper را در آن نگه داشته‌اید، کد‌ زیر را بنویسید:

 

برنامهٔ آموزشی TrivialDrive

برنامهٔ آموزشی TrivialDrive توسط گوگل طراحی شده است که ما با تغییرات آن را به سرویس پرداخت درون‌برنامه‌ای مایکت متصل کرده‌ایم. سورس این برنامه می‌توانید از اینجا دانلود کنید و یا در Github مشاهده نمایید.


در این برنامه چهار دکمه وجود دارد. با استفاده از دکمهٔ سبز رنگ (Drive) می‌توانید بازی کنید و مشاهده نمایید که شاخص بنزین شما کم می‌شود. در صورتی که بنزین شما تمام شود دیگر نمی‌توانید بازی کنید و مجبور به خرید بنزین می‌شوید. با دکمهٔ زرد رنگ (Buy GAS) می‌توانید بنزین بخرید. توجه کنید بنزین در این مثال یک محصول مصرف‌شدنی است. با استفاده از دکمهٔ آبی رنگ (Upgrade my Car) می‌توانید محصول مصرف‌نشدنی بخرید و برای همیشه بنزین داشته باشید و با استفاده از دکمهٔ قرمز رنگ (Get Infinite GAS) می‌توانید اشتراک یک ماهه یا یک ساله بخرید.

ملاحظات امنیتی

برای جلوگیری از دسترسی غیر مجاز به امکانات غیر رایگان برنامهٔ شما لازم است که موارد امنیتی زیر را در نظر بگیرید.

نگه‌داری از محتوا

سعی کنید از سروری برای نگه‌داری محتوای غیر رایگان برنامهٔ خود استفاده کنید و پس از خرید کاربر با استفاده از وب‌سرویس آن را به کلاینت خود منتقل کنید.  همچنین پس از دریافت محتوا از سرور، آن را به صورت رمز شده در حافظه دستگاه نگه دارید و از کلیدی منحصر به همان دستگاه برای دسترسی به اطلاعات رمز شده استفاده کنید. در نظر داشته باشید که در صورتی که محتوای برنامهٔ شما درون فایل apk باشد، امن نیست و می‌توان به آن دسترسی داشت.

تایید امضای دیجیتالی روی سِرور

برای فرستادن اطلاعات به سِرور برنامهٔ خود، حتما رشتهٔ Json را به همراه Signature دریافتی از مایکت (با استفاده از متد getSignature و getOriginalJson در کلاس Purchase) ارسال کنید و فرایند تایید امضای دیجیتال را با کلید رمز عمومی (PublicKey) را در سِرور نیز انجام دهید. توجه کنید فرایند تایید امضای دیجیتال در متد verifyPurchase در کلاس Security انجام می‌شود و دقیقا مانند همین کار را باید در سِرور خود نیز انجام دهید:

به هم ریختگی کد‌ها

برای جلوگیری از عملکرد برنامه‌های مخرب، حتما از سورس کد برنامهٔ خود محافظت کنید. ابتدا باید در کد خود بهم‌ریختگی (obfuscation) ایجاد کنید. برای این کار می‌توانید از ابزار‌هایی نظیر ProGuard استفاده کنید. همچنین مقادیر حساس نظیر کلید رمز عمومی (Public key) را به صورت رشته‌ای ثابت در کد خود قرار ندهید و سعی کنید این رشته‌ها را در زمان اجرای برنامه بسازید (مانند XOR با چند رشتهٔ دیگر). توجه کنید سورس کد‌ کمکی که در اختیار شما قرار دارد می‌تواند در اختیار هکر‌ها نیز قرار گیرد بنابراین بهتر است ساختار پروژه و ترتیب متد‌ها را تغییر دهید تا برنامهٔ شما الگوی متفاوتی داشته باشد و پیدا کردن آن زمان‌بَر شود.

استفاده از Developer payload

در سرویس پرداخت درون‌برنامه‌ای مایکت می‌توانید همراه درخواست خرید یک رشته‌‌، موسوم به developer payload هم ارسال کنید. این رشته می‌تواند به عنوان یک شناسهٔ منحصر به فرد از سمت شما برای این خرید در نظر گرفته شود. بعد از اتمام مراحل خرید این رشته را در کلاس Purchase به برنامهٔ شما بازمی‌گرداند. همچنین زمانی که خرید‌های خود را به‌روز می‌کنید، مایکت این رشته را نیز همراه دیگر جزئیات خرید برمی‌گرداند.
شما باید توکن رشته‌ای استفاده کنید که به برنامه‌تان در تشخیص کاربری که خرید را انجام داده کمک کند. به این ترتیب بعداً می‌توانید بفهمید که خرید مورد نظر برای کاربر معتبر است یا خیر. برای محصولات مصرف‌شدنی این رشته می‌تواند کاملاً تصادفی باشد. اما برای محصولات مصرف‌نشدنی یا اشتراکی، برای اطمینان از صحت خریده شدن محصول توسط کاربر باید از رشته‌ای استفاده کنید که منحصراً آن فرد را شناسایی می‌کند.

وقتی‌ که پاسخ را از مایکت دریافت کردید، مطمئن شوید رشتهٔ developer payload که همراه با جزئیات خرید به شما بازگردانده است، همانی است که شما برای شروع عملیات پرداخت ارسال کرده بودید. برای اطمینان از امنیت بیشتر پیشنهاد می‌شود این عملیات اعتبارسنجی را بر روی سِرور خود انجام دهید.

بررسی همیشگی مجوز دسترسی به محتوای برنامه

برای امنیت بیش‌تر پیشنهاد می‌شود که برای محصولات مصرف‌نشدنی و اشتراکی، همیشه با باز شدن برنامه مجوز دسترسی را از مایکت بپرسید و در هیچ‌جا ذخیره نکنید.

تنظیمات ProGuard

در صورتی که از ProGuard برای بهم‌ریختگی و مبهم کردن کد‌های خود استفاده می‌کنید، حتما خط زیر را به تنظیمات ProGuard برنامهٔ خود اضافه کنید:


تست پیش از انتشار

برای تست سناریو‌های خرید درون‌برنامه‌ای مایکت تنها کافی است که در پنل توسعه‌دهندگان، Packagename برنامهٔ خود را رزرو کنید یا یک نسخه از apk برنامه را بارگذاری نمایید که به کلید رمز عمومی برنامهٔ خود دسترسی داشته باشید. سپس یک یا چند محصول درون‌برنامه‌ای جهت تست با قیمت بسیار کم (مثلا ۱۰۰ ریال) ایجاد کنید و سناریوهای مختلف را تست نمایید. در نظر داشته باشید برای تست خرید درون‌برنامه‌ای نیاز به انتشار برنامه یا تایید مدیر ندارید.

  • No labels