آموزش پرداخت درون برنامه ای بازار ، صفر تا صد
:: مستندات بازار به روز شده است ! ، ممکن است بخش هایی از این آموزش با مستندات جدید تطابق نداشته باشد ! ، بازار همچنین REST API اش رو در مستندات اش اضافه کرده ، که برای برسی و تایید صحت پرداخت از سرور ، استفاده میشه :)
تغییرات آموزش جدید !
- آموزش بروز و کامل شد
- توضیحات بیشتری در مورد مصرف خرید اضافه شد
- سوالات دوستان ویرایش شد و پاسخ در خود سوال اضافه شد.
- تاپیک تمیز شد و پرسش و پاسخ های غیر مرتبط و تکراری حذف شد
توصیه میکنم با کلیک روی علامت زنگوله در این تاپیک مشترک بشید تا از تغییرات اش مطلع بشید
احتمالا شما هم قبول دارید که مستند سازی بازار در مورد پرداخت درون برنامه ای بسیار ضعیف هست و طبق مکاتبه ای که من با بازار داشتم خودشون هم این رو قبول دارند !
" بله حق با شماست، ما در حال حاضر مشغول آمادهسازی سند راهنمای پرداخت درونبرنامهای برای محیطهای غیربومی هستیم، با اتمام این پروژه، اسناد را بازبینی خواهیم کرد."
پس تصمیم گرفتم اطلاعات ام رو با شما به اشتراک بزارم :)
همون طور که اطلاع دارید ، بازار قابلیتی داره به نام پرداخت درون برنامه ای ، که به شما اجازه فروش محتوای دیجیتال رو میده ، حالا این محتوا میتونه به دو نوع باشه
- محتوای قابل مصرف (تمام شدنی – مثل خربد سوخت برای خودرو)
- محتوای غیر قابل مصرف (تمام نشدنی – مثل ارتقا به کاربر ویژه )
گام اول ، آماده سازی محیط توسعه !
ما در طول این پروژه از پکیج هایی که بازار در اختیارمون قرار داده استفاده میکنیم ، چون در سورس کد این پکیچ ها override وجود نداره و در محیط توسعه ی ما وجود override اجباری هست و نبودش خطایی رو ایجاد میکنه که باعث میشه برنامه اجرا نشه باید برای حل این مشکل به این سوال مراجعه کنید
بهترین کار ، استفاده تنظیم رویwarning هست ، چرا که ignore دیگه چیزی به ما نشون نمیده و این به دلایلی که استاد ذکر کردند خوب نیست !
بعد از انجام این کار ، دیگه مشکلی با فایل هایی که میخوایم وارد پروژه کنیم نداریم
این فایل رو دانلود کنید ، در پوشه ی Src پروژه تون از حالت فشرده خارج کنید ، ساختار پوشه ی Src شما باید به این شکل باشه
(ir پکیچ خودم هست ، ممکنه برای شما متفاوت باشه)
خوب حالا وارد محیط ایکلیپس بشید ، در قسمتی که پروژه ها رو میبینید ، ( workspace - package) راست کلیک و Refresh کنید ، ساختار پوشه ی Src باید به این شکل باشه (ir.negano.wallpapaer پکیچ کد های خودم هست ، مال شما اسمش فرق داره ، مهم نیست )
می بینید که دیگه اروری نیست (اگ باشه ، ارور های ساده و قابل حل هست ، یا هشدار که مهم نیست !)
گام بعد کدنویسی !
فعلا طبق مستندات ، غیر قابل استناد بازار میریم جلو !
مجوز پرداخت درون برنامه ای رو به Manifest اضافه کنید
<uses-permission android:name="com.farsitel.bazaar.permission.PAY_THROUGH_BAZAAR" />
پروژه را اجرا کنید ، اگه مشکلی نداشت ، وارد بازار بشید ، همین APK که تا اینجا بدست اومده رو آپلود کنید ، بعد آپلود متوجه میشید که این یکی با بقیه یه فرقایی داره !
البته نیازی به اضافه کردن پکیچ ها هم نبود ! ، یعنی وقتی مجوز درون پرداخت در Manifest باشه ، این قسمت فعال میشه پس اگه مشکل آپلود دارید میتونید اول یه برنامه ی جدید و فقط با مجوز اینترنت و بازار آپلود کنید (کم حجم تر میشه ! )
وارد می شید ، در قسمت محصولات ، محصول جدید ایجاد کنید ، خود بازار هم توضیح داده ، اما بازم میگم ، شناسهٔ کالا رو هرچی دوست دارید بزارید ، مثلا من میزارم "wallpaper2" - اگه مشکلی داشتید مستندات بازار برای مدیریت محصولات رو بخونید ( لینک )
حالا که محصول رو ساختید ، به 2 تا چیز نیاز دارید
- شناسه محصول
- کلید RSA
کلید RSA برنامه تون رو میتونید در قسمت ، برنامه های فروشنده > کلید RSA دریافت کنید ، یک رشته ی عجیب و طولانی
خوب به قول وردپرس "بزن بریم !"
اکتیویتی ای می خواین کاربر رو از اون به بازار منتقل کنید رو بسازید !
بازار میگیه این 5 فیلد رو به اکتیوتی اضافه کنید ، من میگم هرکدوم چیه !
// تگی که هر انقاقی بیفته با اون لاگ چاپ میکنه static final String TAG = ""; // شناسه ی محصول
// For me : "wallpaper2" as I told !
// بلـــــــــــــه فک کردی فقط خودت انگلیسی بلدی ! static final String SKU_PREMIUM = ""; // آیا کاربر ویژه هست ؟
// بیشتر هدفشون استفاده ازش در آخر کاره که اگه درست انجام شد
// true بشه boolean mIsPremium = false; // این مقدار تاثیری در خروجی نداره ، خود بازار هم نمی دونه چیه ! static final int RC_REQUEST =0 ; IabHelper mHelper;
برای استفاده از سرویس بازار ، ما باید اون رو Prepare کنیم ، برای انجام این آماده سازی ، بازار کد نا مشخصی نوشته بود که همین باعث کرش می شد !
چرا می گم کد نامشخص بود !
در این سیستم پرداخت امکان این وجود داره تا در شروع برنامه ، سریع سرور بازار چک بشه که آیا قبلا پرداخت کرده یا نه اما بازار به جای اینکه بگه این کد رو در شروع برنامه بزارید ، گفته در onCreate بزارید ! ، وقتی شما وارد صفحه پرداخت میشید ، سریع دکمه پرداخت رو میزنید ، چون هنوز نتیجه اون کوئری ( queryInventoryAsync ) نیومده ، در واقع سیستم آماده نیست ، پس کرش میکنه !
راهکار من چی بود ؟
ما 2 بار نیاز داریم درخواست آماده سازی بدیم ، یه بار موقع شروع برنامه که باید وضعیت prefrences ها رو بروز کنه ، یه بار هم تو صفحه پرداخت که باید سرویس رو برای پرداخت آماده کنه ، من تابع زیر رو تو کلاس G نوشتم
public static boolean prepareBazaarInAppBilling(final boolean requireUpdatePrefrences,
final QueryInventoryFinishedListener mGotInventoryListener) { try { mHelper = new IabHelper(G.context, base64EncodedPublicKey); Log.d(TAG, "Starting setup."); IabHelper.OnIabSetupFinishedListener listener =
new IabHelper.OnIabSetupFinishedListener() { @Override public void onIabSetupFinished(IabResult result) { Log.d(TAG, "Setup finished."); if ( !result.isSuccess()) { Log.d(TAG, "Problem setting up In-app Billing: " + result); } if (requireUpdatePrefrences && mGotInventoryListener != null) { mHelper.queryInventoryAsync(mGotInventoryListener); } } }; mHelper.startSetup(listener); return true; } catch (Exception e) { e.printStackTrace(); return false; } }
چون این تابع به همون کلید و RSA و اینها نیاز داره من اونها رو تو کلاس G تعریف کردم ، پس ما به بازار کاری نداریم که میگه تو oncreate اکتیویتی پرداخت تعریف کنید
حالا من این تابع رو یه بار تو صفحه ی splash برنامه فراخوانی میکنم ، که تا splash رو نشون بده ، بازار هم آمار رو به ما بده پس تو اکتیویتی اسپلش اینجوری کد میزنم
QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() { @Override public void onQueryInventoryFinished(IabResult result, Inventory inventory) { if ( !result.isFailure()) { boolean state = inventory.hasPurchase(G.SKU_PREMIUM); Log.i(G.TAG, "Bazaar say isPremiumUser is : " + state); G.setPremiumState(state); } goToNextActivity(); } }; G.prepareBazaarInAppBilling(true, mGotInventoryListener);
بهش میگم true , mGotInventoryListener یعنی بله نیاز دارم آماده سازی کنی و و و ...> وضعیت رو از بازار بگیری.
وقتی کارش تمو شد ، لیسنری که بهش دادم رو صدا میزنه ، منم گفتم اگه موفق به دریافت شده بودی ، تابع setPremiumState رو صدا بزن و بهش بگو مقدار رو بزار مقداری که بازار میگه (یعنی به بازار میگه آقا کاربر ویژه است یا نه ؟ میگه True یا False)
تابع setPremiumState هم میره تو Prefrence ها جواب بازار رو ثبت میکنه ، یعنی اگه کاربر مقدار ها رو دستی تغییر بده ، بازم موقع شروع اگه به نت وصل باشه ، وضعیت واقعی ایش رو از بازار میگیریم !
حالا در onCreate در اکتیویتی پرداخت کافی هست بگیم
G.prepareBazaarInAppBilling(false, null);
یعنی نرو سرور رو چک کن ، چون لزومی نداره ، یه بار اول برنامه چک کردیم ، اگه کاربر اینجا بلافاصله روی پرداخت کلیک کنه ، مشکلی پیش نمیاد و میره واسه پرداخت.
باید یک سری listener برای عملیات تعریف کنیم که از رویداد ها آگاه بشیم ، که بازار ( گوگل پلی دیگه ، بازار فقط کپی کرده ) به این شکل تعریف کرده بهتون میگم چی کار کرده ! (اما فعلا استفاده نکنید ، نمونه ی من هم ببینید )
QueryInventoryFinishedListener mGotInventoryListener = new QueryInventoryFinishedListener() { public void onQueryInventoryFinished(IabResult result, Inventory inventory) { Log.d(TAG, "Query inventory finished."); if (result.isFailure()) { Log.d(TAG, "Failed to query inventory: " + result); return; } else { Log.d(TAG, "Query inventory was successful."); // does the user have the premium upgrade? mIsPremium = inventory.hasPurchase(SKU_PREMIUM); // update UI accordingly Log.d(TAG, "User is " + (mIsPremium ? "PREMIUM" : "NOT PREMIUM")); } Log.d(TAG, "Initial inventory query finished; enabling main UI."); } }; OnIabPurchaseFinishedListener mPurchaseFinishedListener = new OnIabPurchaseFinishedListener() { public void onIabPurchaseFinished(IabResult result, Purchase purchase) { if (result.isFailure()) { Log.d(TAG, "Error purchasing: " + result); return; } else if (purchase.getSku().equals(SKU_PREMIUM)) { // give user access to premium content and update the UI } } };
ما مثل بازار نیومدیم تو صفحه ی پرداخت وضعیت سرور رو برسی کنیم و خیلی حرفه ای تر در شروع برنامه برسی میکنیم پس به mGotInventoryListener در اکتیویتی پرداخت نیازی نیست و در اسپلش ازش استفاده کردیم !
لیسنر onIabPurchaseFinished که واسه تموم شدن پرداخت هست ، یعنی کلا وقتی از بازار برگشت این لیستر فراخوانی میشه که من اون سوئیج بی روح (تو ورژن قبلی آموزش ) رو با دو تا توست ساده عوض کردم !
OnIabPurchaseFinishedListener mPurchaseFinishedListener = new OnIabPurchaseFinishedListener() { @Override public void onIabPurchaseFinished(IabResult result, Purchase purchase) { // نتیجه یک فییلور / خطا بوده ؟ if (result.isFailure()) { Log.d(G.TAG, "Error purchasing: " + result); Toast.makeText(G.context, "پرداخت انجام نشد", Toast.LENGTH_LONG).show(); return; } else if (purchase.getSku().equals(G.SKU_PREMIUM)) { G.setPremiumState(true); Toast.makeText(G.context,
"با تشکر ، شما با موفقیت به کاربر ویژه ارتقا پیدا کردید",
Toast.LENGTH_LONG).show(); finish(); // صفحه ی پرادخت رو میبندیم } } };
شما میتونید بر اساس این سند توست های کامل تری چاپ کنید و کاربر رو بیشتر در جریان بزارید !
حالا کافیه تو هر رویدادی که دوست دارید (مثل کلیک شدن یک دکمه ) این کد رو قرار بدید
btnGoToBazaar.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { try { G.mHelper.launchPurchaseFlow(ActivityPayment.this, G.SKU_PREMIUM, 0, mPurchaseFinishedListener); } catch (Exception e) { e.printStackTrace(); Toast.makeText(G.context, "Some Error Happend , Restart App please...", Toast.LENGTH_SHORT).show(); } } });
خوب دید که من تو کچ کردن خطا هم توست میدم ، یعنی اگه خطا داشت کاربر نگه کار نمیکنه ، پیام دادم دوباره تلاش کن یا برنامه را ری استارت کن !
در نهایت هم باید این کد رو عینا بزاریم آخر اکتیویتی پرداخت
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if ( !G.mHelper.handleActivityResult(requestCode, resultCode, data)) { super.onActivityResult(requestCode, resultCode, data); } } @Override public void onDestroy() { super.onDestroy(); if (G.mHelper != null) G.mHelper.dispose(); G.mHelper = null; }
خوب ، اونایی که فقط واسه دسترسی ویژه میخواستن ، دیگه وقتشه برن به سلامت ، موفق باشید D:
مصرف کردن یک خرید ( اولین بار هست اینقدر توضیح میدم ، خود باز اصلا کد نمونه واسش نداده ! )
کاربر شما وقتی یک بار محصولی مثل "wallpaper2" را بخره ، دیگه بلافاصله بعد از باز شدن بازار ، بازار پاسخ مثبت میده و میگه آره ، پرداخت انجام شد ، و تمام اطلاعت مربوط به پرداخت قبلیش هست ، اما خوب ما میخوایم کاربر 10 دفعه بتونه بنزین بزنه ! :))
برای مصرف کردن خرید ، شما باید یک جدول در بانک تون درست کنید و محصولاتی که واسش پرداخت انجام شده رو در جدول ذخیره کنید. جدول ها به این شکل هستن
Products Table
-----------------------------------------
ID | name | SKU (کدی که از بازار گرفتی)
-----------------------------------------
1 | A | 0001
2 | B | 0002
paid products
-----------------------------------------
ID | prductID | itemType | signature | json | isConsume
1 | 2 | Value | value | JSON Value | 1
2 | 4 | Value | value | JSON Value | 0
ما برای اینکه بتونیم هر وقت خواستیم فرایند مصرف رو انجام بدیم باید ورودی ها رو ذخیره کنیم یـــــــــــــــــا برای اینکه مصرف اش کنیم ، یه بار اول درخواست خرید اش رو بدیم که بازار همین مقدار ها رو در قالب یک purchase به ما بده !
برای اینکه بتونید بعدا این خرید رو مصرف کنید ، باید در زمان خرید ( همون جای که من توست موفقیت رو نشون میدم ) به این شکل عمل کنید و این مقدار ها رو در بانک بریزید :)
String itemType = purchase.getItemType(); String signature = purchase.getSignature(); String json = purchase.getOriginalJson();
برای مصرف کردن خرید ، بعد از فراخوانی درخواست آماده سازی ، درخواست مصرف رو به mHelper میدیم !
btn.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { G.prepareBazaarInAppBilling(false, null); purchases purchase = new Purchase(itemType, jsonPurchaseInfo, signature) G.mHelper.consumeAsync(purchases,lisetner); } });
این هم لیسنر اش :
IabHelper.OnConsumeFinishedListener listener = new IabHelper.OnConsumeFinishedListener() { @Override public void onConsumeFinished(Purchase purchase, IabResult result) { // TODO Auto-generated method stub if(result.isSuccess()){ // Consumed ! } } };
من وقتی میخام صفحه پرداختم رو اجرا کنم این خطا میاد و کرش میکنه : این خطا واسه چیه؟
06-15 11:30:38.733: E/AndroidRuntime(1579): java.lang.RuntimeException: Unable to start activity ComponentInfo{....pardakht/....Pardakht}: java.lang.IllegalStateException: IAB helper is not set up. Can't perform operation: launchPurchaseFlow
پاسخ A.L.U
علت خطا آماده نبودن Helper هست ، همون طور که در آموزش جدید توضیح دادم و با تابعی که نوشتم فقط در شروع برنامه بازار رو برسی میکنیم ، پس دیگه در صفحه پرداخت نیاز به لودینگ نداره و کاربر میتونه سریع پرداخت رو انجام بده.
بهتر هست قبل از شروع پرداخت از نصب بودن بازار مطمئن بشیم
public static boolean isPackageInstalled(String packagename) { PackageManager pm = context.getPackageManager(); try { pm.getPackageInfo(packagename, PackageManager.GET_ACTIVITIES); return true; } catch (NameNotFoundException e) { return false; } }
if (isPackageInstalled("com.farsitel.bazaar")) { // بازار نصب است }
توضیح کامل تر در : 4 امین محتوا (سومین پاسخ)
سلام، ببخشید من هی این تاپیک رو میارم بالا. پرداخت برنامه من دست شد و گذاشتمش بازار جهت بررسی اما یهو امروز دیدم باز کرش میکنه، این شد که سریع از حالت بررسی خارجش کردم، شانس آوردم تایی نشده بود.(این تاخیرهای بازار یجا مفید واقع شد!)
الان این پیغام رو میده :
06-22 16:47:26.292: E/AndroidRuntime(2246): java.lang.IllegalStateException:
Can't start async operation (launchPurchaseFlow)
because another async operation(refresh inventory) is in progress.
شماره خطی هم که خطا داره همون خطیه که بازار رو فراخوانی میکنم .
ممنون میشم راهنمایی کنید،
آموزش با توضیحات این پاسخ ام بازنویسی شد !
سلام ، بالاخره امتحان های من تموم شد و فرصت شد تا به عنوان شروع مجدد طبق قولی که به دوستان دادم ، یه سری توضیحات اضافه در مورد پرداخت درون برنامه ای بازار بدم ، خوب اینها فقط اظهار نظر شخصی من هست ، پس اگر اشتباه میکنم ، لطفا اساتید من رو به مسیر درست هدایت کنند :)
بازار یه سری کد رو از سورس کد های گوگل پلی کور کورانه کپی کرده و طبق اون این سیستم رو راه اندازی کرده و مشکلات زیادی هم داره که خودشون بهتر از ما به این موضوع واقف هستن ، اما ما خودمون هم باید طبق منطق خودمون سیستم رو برسی کنیم ، که من این کار رو انجام دادم و برداشت ام این بود !
1- چرا باید در هنگام باز شدن اکتیویتی پرداخت درخواستی به سرور ارسال بشه ؟
بازار اصرار داره در onCreate ما یک متد رو به شکل زیر فراخوانی کنیم ، در طول اجرا این متد هم یک دیالوگ لودینگ نشون بدیم :|
mHelper.queryInventoryAsync(mGotInventoryListener);
چرا من با این کار مخالف ام ؟
- ما معمولا قبل از باز شدن صفحه ی پرداخت به اطلاعات ویژه بودن یا نبودن کاربر درسترسی داریم ، پس نیازی نیست به بازار درخواست بدیم که یه برسی سریع داشته باشه !
- فرض رو بر این بگیریم ، کاربر برنامه رو حذف کرده و دوباره نصب کرده ، اگر این کار رو انجام ندیم چی میشه ؟ ، خوب هیچی ، برنامه به عنوان کاربر معمولی ، کاربر رو به صفحه پرداخت هدایت میکنه ، کاربر روی دکمه ی پرداخت کلیک میکنه اما بازار بلافاصله متوجه میشه کاربر قبلا پرداختی داشته ، بنابراین ، ResultCode 0 یعنی موفق و Token خرید قبلی رو در پاسخ بر میگردونه.
با حذف اون قسمت دیگه حتی باز شدن صفحه ی پرداخت در onCreate اکتیویتی هم باعث کرش نمیشه ، علت کرش ها این بود که هنوز بازار در حال انجام اون عملیات هست و ما به جای نشون دادن لودینگ میخوایم پرداخت رو انجام بدیدم !
با این کار دیگه به mGotInventoryListener هم نیازی نداریم و سورس کد کوتاه تر میشه !
پیش از شروع پرداخت ، برسی کنید که آیا بازار بر روی دستگاه نصب است یا خیر !
در صورتی که بازار نصب نباشه ، عملا کار ما بیهوده است یعنی پرداخت غیر قابل انجام هست ، با این تابع میتونیم از نصب بودن هر پکیجی مطمئن بشیم :)
public boolean isPackageInstalled(String PackageName) { PackageManager manager = getPackageManager(); boolean isAppInstalled = false; try { manager.getPackageInfo(PackageName, PackageManager.GET_ACTIVITIES); isAppInstalled = true; } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } return isAppInstalled; }
مثلا برای برسی نصب بودن بازار به این شکل عمل میکنیم :
if (isPackageInstalled("com.farsitel.bazaar")) { // Bazar is installed } else { // Bazar is not installed }
پس اگر بازار نصب بود باید عملیات پرداخت را شروع کنیم ! ، اما ممکنه هر خطایی اتفاق بی افته شایع ترین نوع خطا مربوط به همون Async نبودن هست (مورد اول ، queryInventoryAsync ) پس ما احتمالا کمتر کرش داریم (چون از اون استفاده نمی کنیم ) ، اما بازم نمیشه خطاهای احتمالی رو نادیده گرفت ، پس با یه Try , Catch از کرش کردن برنامه جلوگیری می کنیم
if (isPackageInstalled("com.farsitel.bazaar")) { // Bazar is installed try { mHelper.launchPurchaseFlow(this, SKU_PREMIUM, 0, mPurchaseFinishedListener); } catch (Exception e) { e.printStackTrace(); Toast.makeText(getApplicationContext(),
"Reopen App and Try Again", Toast.LENGTH_SHORT).show(); } } else { // Bazar is not installed //open Bazzar Home Page To Download It ! }
و دیگر هیچ !
سلام
یه سوال داشتم، من یک دیالوگ دارم که توش 4 تا محصول متفاوت از هم هست، کاربر میتونه با دکمه های رادیویی یک محصول رو انتخاب و پرداخت کنه، قیمت محصولات هم متفاوته. ازونجایی که مقدار دهی موقع ساختن اکتیویتی انجام میشه، من چطوری میتونم مقدار SKU_PREMIUM رو توی دیالوگ تغییر بدم؟
پاسخ A.L.U
بر اساس آموزش جدید ، ما SKU_Premium رو در کلاس G تعریف کردیم ، پس بر اساس شرایط می تونه تغییر کنه بعد فرایند پرداخت رو شروع کنید !
کلید RSA هم که در تمام برنامه یکسان هست و نیازی به تغییرش نیست !
سلام
چند عدد سوال دارم :-)
1 - من میخوام بدونم حساب کاربر چقدر پول توش هست ؟ یعنی قبل از اینکه تموم بشه متوجه بشم و اخطار بدم که مثلا 10 بار دیگه بیشتر نمیتونه استفاده کنه ، بره شارژ کنه
2 - مقدار شارژ را میخوام بتونم خودم مشخص کنم. مثلا یک بار بگم 1000 تومان یک بار 10000 تومان
3 - فرض کنیم برنامه ما هزینه ماهیانه برای شارژ میگیره و میخوایم بعد از 30 روز حساب طرف از حالت پریمیوم در بیاد چه باید کرد؟
4 - اگر بخوایم برنامه در هنگاه پرداخت به ما گزینه بده که میخوای با بازار پرداخت کنی یا گوگل یا سیستمهای بانکی چه باید کرد؟
5 - فرض کنید برنامه ای مثل فروش شارژ نوشته ایم ، شارژ 2000 تومانی را که بفروشیم باز هم 600 تومان را بازار بر میدارد ؟ هزینه تمام شده شارژ بیشتر از 1400 میشود. در حالت فروش چگونه میتوانیم از این سرویس استفاده کنیم ؟
پاسخ A.L.U
- تا جایی که من میدونم شدنی نیست یعنی نمیشه از بازار بپرسی این کاربر ما چقدر پول داره !
- میتونید محصولات متفاوت ایجاد کنید و بر اساس پاسخی که به سوال قبل دادم ، در زمان اجرا محصول رو عوض کنید اما طبق مکاتبه ای که با بازار داشتم ، فعلا پرداخت مبلغی که در زمان اجرا تعیین بشه ، ممکن نیست
- بازار نوع محصول اکانت ماهیانه - اکانت سالیانه رو اضافه کرده ، طبق آموزش جدید ، وقتی در لحظه ی شروع از بازار سوال کنیم آیا ویژه هست یا نه و برنامه رو براساس اون پیش ببریم ، دقیقا یک ماه یا یک سال بعد از پرداخت ، برنامه غیر فعال میشه !
- باید سیستم بانکی خودتون رو پیدا کنید !
- بله ، اما بازار گفته در این موارد باهاش مکاتبه کنید !
سلام و خسته نباشید خدمت همه دوستان
این پرداخت درون برنامه ای هم داستانی شده واسه ما، لامثب هر جاشو میچسبی از ی جای دیگش میزنه بیرون!
من به یک کرش جدیدی برخوردم که توو لیست کرش های بالا نبود؛ وقتی کاربر رو با یک دکمه به صفحه بازار میفرستم اگر طرف وارد حسابش نشده باشه و دکمه برگشت رو بزنه، برنامه کرش میکنه !(و اگر توو حسابش باشه هیچ مشکلی پیش نمیاد!) چطور میشه از این کرش جلوگیری کرد؟!
سلام دادش یگانه عزیز میخاستم ببینم از پرداخت درون برنامه ای این قسمت کالاهای مصرفی رو بیشتر توضیح میدید گفتید از این کد استفاده کنیم
mHelper.consumeAsync(info, consumeFinishedListener);
حالا یه مثال کوچیک اگه ازش بزنید ممنون میشم کجا از این کد استفاده کنیم
پاسخ A.L.U
در آموزش جدید ، این مورد رو بیشتر توضیح دادم ، اما باز هم بین علما در این مورد اختلاف زیاد هست :))
بازار هم مثل همیشه پاسخ اش اینه :
"ما پرداخت درون برنامه ای خود را بر اساس پرداخت درون برنامه ای گوگل پیاده کرده ایم ، می توانید از مستندات گوگل استفاده کنید"
سلام بر همه اساتید من طبق دستور العمل پیش رفتم . 5 تا فیلد بازار رو گزاشتم قبل از oncreate و اون کدها رو هم که گفته شد طبق مستندات بازاره گزاشتم بعد از oncreate . کلید RSA رو هم توی گیومه گزاشتم . چند جا ایراد import گرفت که برطرف شد . اما ایراد آخر اینه که به mGotInventoryListener ایراد می گیره و میگه یا باید local variable بکنیش یا فیلدش بکنی و چند راه دیگه که گفتم فیلدش کن مشکل حل شد حالا باید چه کار کنم ؟ برنامه هم بدون مشکل اجرا شد . تا اینجای کار رو اومدم و بقیه اش رو نرفتم . یعنی نمی دونم باید چه کار کنم . ببینید در کل من یک صفحه پرداخت دارم که یک کلید توشه به نام btnpurchase حالا از این به بعد چیه و لیسنری که باید برای دکمه نوشته بشه چیه و چه کاری قراره بکنه ؟ ممنون دوستان لطفا به زبون ساده راهنمایی کنید ... عکسم هر کاری کردم آپلود نشد ولی کد رو میزارم :
import util.IabHelper;
import util.IabHelper.QueryInventoryFinishedListener;
import util.IabResult;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Typeface;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class ActivityPurchase extends Activity {
static final String TAG = "";
static final String SKU_PREMIUM = "";
boolean mIsPremium = false;
static final int RC_REQUEST =0 ;
IabHelper mHelper;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.purchase);
String base64EncodedPublicKey = "RSA code here ";
mHelper = new IabHelper(this, base64EncodedPublicKey);
Log.d(TAG, "Starting setup.");
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
private QueryInventoryFinishedListener mGotInventoryListener;
public void onIabSetupFinished(IabResult result) {
Log.d(TAG, "Setup finished.");
if (!result.isSuccess()) {
// Oh noes, there was a problem.
Log.d(TAG, "Problem setting up In-app Billing: " + result);
}
// Hooray, IAB is fully set up!
mHelper.queryInventoryAsync(mGotInventoryListener);
}
});
Button btnpurchase = (Button) findViewById(R.id.btnpurchase);
// btnpurchase.setOnClickListener(new OnClickListener() {
//
// @Override
// public void onClick(View arg0) {
// // TODO Auto-generated method stub
// Intent intent = new Intent(ActivityPurchase.this, ActivityIndex.class);
// ActivityPurchase.this.startActivity(intent);
// ActivityPurchase.this.finish();
//
// }
// });
}
}
پاسخ A.L.U
شما باید کارهای گفته شده در 6 امین محتوا (5 امین پاسخ) را انجام بدید و یک OnIabPurchaseFinishedListener بنویسید !
سلام دوستان، طبق دستوراتی که توی پست اومده داخل اکتیوتی منو یک رابطی قرار دادم که با اون کاربر به صفحه خرید منتقل میشه اما مشکل NullPointer Exception دارم !
پاسخ A.L.U
در آموزش جدید به این خطا اشاره کردم :
- مطمئن بشید که تابع prepareBazaarInAppBilling یا همون آماده سازی درون پرداخت بازار در oncreate فراخوانی شده و mHelper در اون نقطه null نیست
- برسی کنید آیا بازار نصب هست (بالا تر با تابع isPackageInstalled توضیح دادم ) من خودم در این حالت اگه بازار نصب نباشه ، دکمه پرداخت رو مخفی میکنم و دکمه دانلود بازار رو نمایش میدم :)
- مجوز بازار را در برنامه قرار بدید
بچه ها بازار ایمیل من رو جواب داد:
حالا من از قسمت "طی پرداخت یا خرید به مشکلی برخورد کرده اید؟" با اکانت خودم وارد شدم، دیدم توکن خرید هام هست، اما ما چجوری از این توکن در برنامه برای تشخیص اینکه آیا برنامه ما قبلا توسط کاربر خریداری شده یا نه استفاده کنیم؟
پاسخ A.L.U
بعد از برسی ، مشخص شد قسمت ارسال SKU به سرور بازار مشکل داشت
مجتبی ممنونم بابت توضیحات و زحمتی که کشیدی
آقا من خیلی مشکل دارم... حدودا 80% مطالب رو نمی فهمم چون اصلا هیچ دیدی نصبت به پرداخت درون برنامه ای ندارم.
مثلا من یه برنامه دارم که 100نا تصویر آنلاین نمایش میده حالا میخوام کاری کنم که کسانی که پول ندادند فقط 20تا تصویر رو ببینند چیکار باید کنم الان؟
سوال بعدیم که splash چیه که تو آموزش گفتی splash...
سوال دیگم اینه که برای پرداخت درون برنامه ای باید یک اکتیویتی ایجاد کنم و از داخل اون اکتیویتی کاربر پرداخت کنه؟ حالا این اکتیویتی وقتی کاربر پول رو پرداخت می کنه دیگه نمایش داده نمیشه؟ چه جوری :))
سوال آخر هم اینکه من یه برنامه مثل قسمت اول آموزش (تا جایی که تو manifest جواز بازار رو می نویسیم) ساختم و برای بازار ارسال کردم حالا چه جوری پاکش کنم؟ چون برنامه الکی بود...
ممنون میشم اگه راهنماییم کنید
سوالات hosseinAmini
- Splash رو دوستان توضیح دادن ، منظور صفحه ی نمایش لوگوی برنامه بود ، که ما میتونیم از یک تایم 2 ثانیه ی استفاده کنیم و کار برسی وضعیت کاربر از بازار رو در اونجا شروع کنیم
- شما با کلیک کردن رو یه دکمه وارد اکتیویتی پرداخت میشین ! ، پس وقتی اون دکمه مخفی بشه ، مثل این هست که اون اکتیویتی دیگه وجود نداره ! ، View.setVisibility(View.GONE)z
- من که خیلی با بازار کار نمیکنم ، ولی از دوستان شنیدم باید بهشون ایمیل بزنید :)
سوالات mohammad
- goToNextActivity متدی هست که نوشته بودم تا یک intent بسازه و startActivity کنه تا بره به اکتیویتی بعدی ! ، شما هم بسازید :)
- setPremiumState متدی بود که نوشته بودم ، تا مقدار boolean وضعیت کاربر رو بهش بدم ، و اون رو به عنوان وضعیت کاربر در preference ها ثبت کنم ! ، تا بعدا هم قابل استفاده باشه و یک متد به نام getPremiumState هم دارم !
- یک متغییر premiumState هم در کلاس G دارم که وضعیت فعلی کاربر هست ، مثلا میخوام بگم اگه ویژه بود یه دکمه ی پرداخت مخفی بشه
if(G.premiumState){
btnPay.setVisibility(View.GONE);
}
آموزش ویدئویی پرداخت درون برنامه ای بازار :
البته این آموزش در محیط Basic4Android
لینک حجم حدود 49 مگابایت
خالی از لطف نیست.
سلام دوستان
من یه برنامه از قبل منتشر کردم و الان میخوام توی نسخه جدید پرداخت درون برنامه ای بزنم. تمام این روش ها که توضیح دادید قبل از انتشار برنامه اون را آپلود میکنیم و توی قسمت محصول جدید برنامه را انتخاب می کنیم و کلید مربوطه را میگیریم اما من الان برنامم منتشره و حتی نسخه جدید هم با دسترسی پرداخت آپلود کردم اما توی قسمت محصولات جدید توی بازار نمیتونم برنامه را انتخاب کنم و اصلا توی لیست نیست. به نظرتون باید چکار کنم؟
سلام خدمت دوستان.مخصوصا استاد تاپیک مجتبی یگانه عزیز.
من این پرداخت درون برنامه ای (بصورت یکبار مصرف : که با خرید کل قسمت های برنامه قابل دیدن باشه) رو طبق اموزش تاپیک درست کردم ، به نوعی لقمه آماده :)
هر کی استفاده کرد یه صلوات بفرسته .
کد صفحه پرداخت :
public class Payment extends Activity { @Override protected void onResume() { G.currentActivity = this; super.onResume(); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); overridePendingTransition(R.anim.zoom_enter, R.anim.zoom_exit); this.requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.payment); Button pay = (Button) findViewById(R.id.btn_pay); pay.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { if (isPackageInstalled("com.farsitel.bazaar")) { // Bazar is installed try { G.mHelper.launchPurchaseFlow(Payment.this, G.SKU_PREMIUM, 0, mPurchaseFinishedListener); } catch (Exception e) { e.printStackTrace(); Toast.makeText(G.context,"Some Error Happend , Restart App please...", Toast.LENGTH_SHORT).show(); } } else { // Bazar is not installed //open Bazzar Home Page To Download It ! Toast.makeText(G.context, "لطفا برنامه کافه بازار را بروی دستگاه گوشی نصب کنید", Toast.LENGTH_SHORT).show(); } } }); G.prepareBazaarInAppBilling(false, null); }
OnIabPurchaseFinishedListener mPurchaseFinishedListener =
new OnIabPurchaseFinishedListener() { @Override public void onIabPurchaseFinished(IabResult result, Purchase purchase) { if (result.isFailure()) { Log.d(G.TAG, "Error purchasing: " + result); Toast.makeText(G.context, "پرداخت انجام نشد", Toast.LENGTH_LONG).show(); return; } else if (purchase.getSku().equals(G.SKU_PREMIUM)) { G.setPremiumState(true); Toast.makeText(G.context, " پرداخت با موفقیت انجام شد.", Toast.LENGTH_LONG).show(); finish(); } } };
public static boolean prepareBazaarInAppBilling(final boolean requireUpdatePrefrences, final QueryInventoryFinishedListener mGotInventoryListener) { try { G.mHelper = new IabHelper(G.context, G.base64EncodedPublicKey); Log.d(G.TAG, "Starting setup."); IabHelper.OnIabSetupFinishedListener listener = new IabHelper.OnIabSetupFinishedListener() { @Override public void onIabSetupFinished(IabResult result) { Log.d(G.TAG, "Setup finished."); if ( !result.isSuccess()) { Log.d(G.TAG, "Problem setting up In-app Billing: " + result); } if (requireUpdatePrefrences && mGotInventoryListener != null) { G.mHelper.queryInventoryAsync(mGotInventoryListener); } } }; G.mHelper.startSetup(listener); return true; } catch (Exception e) { e.printStackTrace(); return false; } }
public boolean isPackageInstalled(String PackageName) { PackageManager manager = getPackageManager(); boolean isAppInstalled = false; try { manager.getPackageInfo(PackageName, PackageManager.GET_ACTIVITIES); isAppInstalled = true; } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } return isAppInstalled; }
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if ( !G.mHelper.handleActivityResult(requestCode, resultCode, data)) { super.onActivityResult(requestCode, resultCode, data); } }
@Override public void onDestroy() { super.onDestroy(); if (G.mHelper != null) G.mHelper.dispose(); G.mHelper = null; } }
کد صفحه اسپلش :
public class SplashScreen extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); setContentView(R.layout.main); G.HANDLER.postDelayed(new Runnable() { @Override public void run() { Intent intent = new Intent(SplashScreen.this, ActivityCategory.class); SplashScreen.this.startActivity(intent); SplashScreen.this.finish(); overridePendingTransition(R.drawable.fadein, R.drawable.fadeout); } }, 2000); QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() { @Override public void onQueryInventoryFinished(IabResult result, Inventory inventory) { if ( !result.isFailure()) { boolean state = inventory.hasPurchase(G.SKU_PREMIUM); Log.i(G.TAG, "Bazaar say isPremiumUser is : " + state); G.setPremiumState(state); } //goToNextActivity(); } }; G.prepareBazaarInAppBilling(true, mGotInventoryListener); } }
و کلاس G
public class G extends Application { public static Context context; public static LayoutInflater inflater; public static Activity currentActivity; public static final Handler HANDLER = new Handler(); //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv public static final String TAG = "";
public static final String SKU_PREMIUM = "eshgh_full";//شناسه محصول public static final int RC_REQUEST = 0; public static boolean mIsPremium = false; public static IabHelper mHelper; public static String base64EncodedPublicKey = "MIHNMA0GCSqGSIb..."; public static boolean setPremiumState = false; public static boolean premiumState = false; public static SharedPreferences preferences; //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@Override public void onCreate() { super.onCreate(); context = this.getApplicationContext(); inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
preferences = PreferenceManager.getDefaultSharedPreferences(context); premiumState = preferences.getBoolean("IS_PREMIUM", false); }
public static boolean prepareBazaarInAppBilling(final boolean requireUpdatePrefrences, final QueryInventoryFinishedListener mGotInventoryListener) { try { mHelper = new IabHelper(G.context, base64EncodedPublicKey); Log.d(TAG, "Starting setup."); IabHelper.OnIabSetupFinishedListener listener = new IabHelper.OnIabSetupFinishedListener() { @Override public void onIabSetupFinished(IabResult result) { Log.d(TAG, "Setup finished."); if ( !result.isSuccess()) { Log.d(TAG, "Problem setting up In-app Billing: " + result); } if (requireUpdatePrefrences && mGotInventoryListener != null) { mHelper.queryInventoryAsync(mGotInventoryListener); } } }; mHelper.startSetup(listener); return true; } catch (Exception e) { e.printStackTrace(); return false; } }
QueryInventoryFinishedListener mGotInventoryListener = new QueryInventoryFinishedListener(){ @Override public void onQueryInventoryFinished(IabResult result, Inventory inventory) { Log.d(TAG, "Query inventory finished."); if (result.isFailure()) { Log.d(TAG, "Failed to query inventory: " + result); return; } else{ Log.d(TAG, "Query inventory was successful."); // does the user have the premium upgrade? mIsPremium = inventory.hasPurchase(SKU_PREMIUM); // update UI accordingly Log.d(TAG, "User is " + (mIsPremium ? "PREMIUM" : "NOT PREMIUM")); } Log.d(TAG, "Initial inventory query finished; enabling main UI."); } };
OnIabPurchaseFinishedListener mPurchaseFinishedListener = new OnIabPurchaseFinishedListener(){ @Override public void onIabPurchaseFinished(IabResult result, Purchase purchase) { if (result.isFailure()) { Log.d(TAG, "Error purchasing: " + result); return; } else if (purchase.getSku().equals(SKU_PREMIUM)) { // give user access to premium content and update the UI } } };
public static void setPremiumState(boolean state) { SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("IS_PREMIUM", state); editor.commit(); premiumState = state;
} }
اکتیویتی هم که میخوایید محتوا (در صورت خرید) نشوون داده بشه :
if (G.premiumState){// اگر پرداخت شد
// نمایش همه محتوا
}else{// اگر پرداخت نشده بود
// نمایش محدود محتوا
}
بچه ها دعای خیر یادتون نره
قربونت برم آقا مجتبی ،بالاخره مفهمومی یاد گرفتم ،قبلا کپی پیست کرده بودم یک مشکلی داشت
با توجه به آموزش های آقا مجتبی عزیز ، کد کامل دورن پرداخت بازار این شکلی جمع بندی میشه البته برای غیر مصرفی ها
کد صفحه اسپلش :
QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
if ( !result.isFailure()) {
boolean state = inventory.hasPurchase(G.SKU_PREMIUM);
Log.i(G.TAG, "Bazaar say isPremiumUser is : " + state);
G.setPremiumState(state);
}
//goToNextActivity();
}
};
G.prepareBazaarInAppBilling(true, mGotInventoryListener);
کلاس G :
////پرداخت بازار
public static final Handler HANDLER = new Handler();
public static final String TAG = "TAG"; //bazaar
public static final String SKU_PREMIUM = "شناسه"; //bazaar
public static final int RC_REQUEST = 0; //bazaar
public static boolean mIsPremium = false; //bazaar
public static IabHelper mHelper; //bazaar
public static String base64EncodedPublicKey = "کد طویل !";
public static boolean setPremiumState ;
public static boolean premiumState ;
public static SharedPreferences preferences;
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
context = getApplicationContext();
preferences = PreferenceManager.getDefaultSharedPreferences(context);
premiumState = preferences.getBoolean("IS_PREMIUM", false);
}
////////////////////////////////////////////////پرداخت بازار/////////////////
/////////////////////////////////////////////////////////////////////////////
public static boolean prepareBazaarInAppBilling(final boolean requireUpdatePrefrences,
final QueryInventoryFinishedListener mGotInventoryListener) {
try {
mHelper = new IabHelper(G.context, base64EncodedPublicKey);
Log.d(TAG, "Starting setup.");
IabHelper.OnIabSetupFinishedListener listener =
new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
Log.d(TAG, "Setup finished.");
if ( !result.isSuccess()) {
Log.d(TAG, "Problem setting up In-app Billing: " + result);
}
if (requireUpdatePrefrences && mGotInventoryListener != null) {
mHelper.queryInventoryAsync(mGotInventoryListener);
}
}
};
mHelper.startSetup(listener);
return true;
}
catch (Exception e) {
e.printStackTrace();
return false;
}
}
///////
public static void setPremiumState(boolean state) {
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean("IS_PREMIUM", state);
editor.commit();
premiumState = state;
}
////////////////////////////////////////////////کار در کلاس جی تمام شد ///////
/////////////////////////////////////////////////////////////////////////////
}
کد صفحه پرداخت :
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_payment);
G.prepareBazaarInAppBilling(false, null);
ImageView pay = (ImageView) findViewById(R.id.imgPay);
pay.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
if (isPackageInstalled("com.farsitel.bazaar")) {
// Bazar is installed
try {
G.mHelper.launchPurchaseFlow(Payment.this,
G.SKU_PREMIUM, 0, mPurchaseFinishedListener);
}
catch (Exception e) {
e.printStackTrace();
Toast.makeText(G.context, "مشکلی در پرداخت کافه بازار بوجود آمد لطفا بعدا امتحان کنید ",
Toast.LENGTH_SHORT).show();
}
}
else {
// Bazar is not installed
//open Bazzar Home Page To Download It !
Toast.makeText(G.context,
"لطفا برنامه کافه بازار را بروی دستگاه گوشی نصب کنید ",
Toast.LENGTH_SHORT).show();
}
}
});
}
////////////////////////////////
OnIabPurchaseFinishedListener mPurchaseFinishedListener =
new OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
if (result.isFailure()) {
Log.d(G.TAG, "Error purchasing: " + result);
Toast.makeText(G.context, "پرداخت انجام نشد لطفا مجددا سعی کنید", Toast.LENGTH_LONG).show();
//G.setPremiumState(false);
return;
}
else if (purchase.getSku().equals(G.SKU_PREMIUM)) {
G.setPremiumState(true);
Toast.makeText(G.context, " با تشکر نسخه کامل فعال شد یکبار برنامه را بسته دوباره اجرا کنید", Toast.LENGTH_LONG).show();
finish();
}
}
};
///////////////آیا بازار نصب است ؟/////////////////////////////////////
public boolean isPackageInstalled(String PackageName) {
PackageManager manager = getPackageManager();
boolean isAppInstalled = false;
try {
manager.getPackageInfo(PackageName, PackageManager.GET_ACTIVITIES);
isAppInstalled = true;
}
catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return isAppInstalled;
}
///////////////////این هم کدی که باید آخراکتیویتی پرداخت باشه////////////////
/////////////////////////////////////////////////////////////////////////////
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if ( !G.mHelper.handleActivityResult(requestCode, resultCode, data)) {
super.onActivityResult(requestCode, resultCode, data);
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (G.mHelper != null)
G.mHelper.dispose();
G.mHelper = null;
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
}
خوب تمام شد حالا هر جایی خواستید می توانید چک کنید آیا پرداخت شده یا نه ، اگر پرداخت نشده بفرست به اکتیوتی پرداخت :
if (G.premiumState) {// اگر پرداخت شد
} else {// اگر پرداخت نشده بود
Toast.makeText(G.context, "لطفا نسخه کامل را تهیه فرمایید ", Toast.LENGTH_LONG).show();
Intent intent2 = new Intent(G.Curreantactivity, Payment.class);
G.Curreantactivity.startActivity(intent2);
}
فعلا که کار می کنه و مشکلی که در پست بالا گفتم را نداره ، دوباره از آموزشی که دادی تشکر می کنم ،و از دوست عزیمان هم که با جمع بندی یک دید کلی دادند تشکر می کنم ،
ببخشید mHelper.queryInventoryAsync(mGotInventoryListener) کارش چیه؟ اگه اول برنامه اجراش نکنیم و وضعیت کاربر رو چک نکنیم، حتما باید موقع پرداخت اجرا بشه؟
کد بنده به صورت زیر میباشد:
public static final String SKU_PREMIUM = "***"; public boolean mIsPremium = false; public static final int RC_REQUEST = 0; public IabHelper mHelper;
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ((AnalyticHelper) getApplication()) .getTracker(AnalyticHelper.TrackerName.APP_TRACKER); String base64EncodedPublicKey = "****"; mHelper = new IabHelper(this, base64EncodedPublicKey); mHelper.enableDebugLogging(true); Log.d(TAG, "Starting setup In app Purchase."); mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() { public void onIabSetupFinished(IabResult result) { Log.d(TAG, "Setup finished."); if (!result.isSuccess()) { // Oh noes, there was a problem. Log.e(TAG, "Problem setting up in-app billing: " + result); return; } // Have we been disposed of in the meantime? If so, quit. if (mHelper == null) return; // IAB is fully set up. Now, let's get an inventory of stuff we own. Log.d(TAG, "Setup successful. Querying inventory."); mHelper.queryInventoryAsync(mGotInventoryListener); } }); }
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() { @Override public void onQueryInventoryFinished(IabResult result, Inventory inventory) { Log.d(TAG, "Query inventory finished."); if (result.isFailure()) { Log.d(TAG, "Failed to query inventory: " + result); return; } else { Log.d(TAG, "Query inventory was successful."); // does the user have the premium upgrade? mIsPremium = inventory.hasPurchase(SKU_PREMIUM); // update UI accordingly Log.d(TAG, "User is " + (mIsPremium ? "PREMIUM" : "NOT PREMIUM")); } Log.d(TAG, "Initial inventory query finished; enabling main UI."); } };
کد بالا در اولین صفحه ی برنامه ام اجرا میشه ( با توجه به شرایط و معماری خیلی خیلی خاص که پروژه من داره واسم سخته که بخوام دقیقا طبق روشی که دوست عزیزمون زحمت کشیدن و محبت کردن برم) و در صفحه دیگری خرید انجام میشه که با موفقیت انجام دادم. اما نمیدونم که چرا برنامه را که دوباره اجرا میکنم باز هم mIsPermium من برابر با false هستش با توجه به اینکه در همان لحظه ظاهرا از بازار سوال میپرسه و جوابشو میگیره. زمانی هم که خرید رو انجام میدم همه چی نرمال انجام میشه و فقط پولی از حسابم کم نمیشه و قابلیت های پرمیومی که تعریف کردم تازه اون موقع میاد.
خیلی ممنون میشم یکی منو راهنمایی کند.
مث خیلی از دوستان واسم سوال شده بود که چرا باید تو صفحه splash کد بزنیم اما با مشکلی که تو این کامنتای بالا بهش برخوردم دلیل این کار آقای یگانه رو فهمیدم.کد زدن تو splash معجزه میکنه.
مشکل کامنتای بالا چیه؟؟؟
همونطور که خودم و چندتا از دوستان به این مشکل برخوردیم وقتی پرداخت رو از طریق بازار انجام میدیم تا زمانی که داخل برناممون هستیم هیچ مشکلی پیش نمیاد اما به محض اینکه از برنامه خارج میشیم و دوباره وارد میشیم درخواست پرداخت میکنه.
چرا این مشکل (درواقع یه حسن)بوجود میاد؟
ما تو کدامون گفتیم که وقتی پرداخت انجام شد premiumstate رو از false به true تغییر بده.به همین دلیل تا وقتی تو برنامه هستیم قسمت های قفل شده premiumstateرو چک میکنن و چون true هست باز میشن اما وقتی برنامه رو دوباره اجرا میکنیم معجزه صفحه splash رخ میده.اگر به اینترنت وصل نباشه که از حالت ذخیره شده استفاده میکنه وبازم اون قسمتا باز میشن اما اگه به اینترنت وصل یاشه از بازار درخواست میکنه.بازارم در کمال تعجب میگه ((نه)).واون true ذخیره شده دوباره تبدیل به false میشه.درسته که ما قبلا پرداخت رو انجام دادیم اما توجه داشته باشین که ما فقط داریم برناممون رو تست میکنیم و هنوز کامل برای فروش نزاشتیمش.بازار چیزی رو به عنوان خرید قبول میکنه که واقعا خرید شده باشه.
از کجا معلوم که حرفای من درست باشه؟؟؟
به دو دلیل:1.همونطور که گفتم اگه به اینترنت وصل نباشه بازم قسمتای قفل شده بازن یعنی حالت ذخیره شده همون true مونده.پس کدای پرداخت کاملا درسته.2.اگه قیمت رو صفر هم نزاریم و مثلا 100 تومن بزاریم بعد از پرداخت ایمیل بازار میاد اما هیچ پولی کم نمیشه پس بازار این فرایند رو به عنوان یه خرید قبول نداره تا زمانی که برنامه واقعا منتشر بشه.
البته این چیزی بود که من فهمیدم.لطفا اساتید بگن این نظر من درسته یا نه؟
با تشکر.
یک سوال ، این ریسپانسایی که بازار بعد از خرید میفرسته، برای من به شکل رندم تغییر میکنه! (به جز حالت خرید که همه چیش درست هست) مثلا اگه خرید تکراری باشه دوباره مقدار صفر که یعنی موفقیت آمیز بوده رو برمیگردونه یا گاهی اعداد دیگه از صفر تا 9 ؛ دلیلش چیه؟
یک نکته تکمیلی
خواستم خیلی تشکر کنم، چون من این چند روز درگیر اضافه کردن پرداخت درون برنامه ای به پروژه جدیدم بودم و این آموزش، آموزش آقای کاشی زاده و hamedjj خیلی به من کمک کرد، فقط خواستم یک نکته تکمیلی اضافه کنم که برای من فقط روی امولاتور وقتی که بازار نصب نبود و در مرحله برگشت به activity اصلی اتفاق می افتاد و شاید مشکل بعضی ها باشه
به قول کسی که این سئوال را در stack overflow مطرح کرده بود، شاید کمتر از 1% مواقع، ولی این خطا پیش می آید که خیلی جالب نیست:
Unable to destroy activity:: java.lang.IllegalArgumentException:Service not registered
و همین طور که در لینک بالا اشاره شده، مشکل با تغییر این خط در IabHelper حل خواهد شد (ظاهراً از باگ های پروژه تستی موجود در google play هست که بازار هم همون را استفاده کرده):
if (mContext != null) mContext.unbindService(mServiceConn);
به این صورت:
if (mContext != null && mService != null) mContext.unbindService(mServiceConn);
سلام
در target 21 به بالا با ارور مواجه می شویم
از کلاس ٖIabHelper.java هم ایراد می گیره
کسی راه کاری پیدا نکرده ؟ واقعا کلافه شدم
پیشا پیش تشکر
اگر در هنگام خرید درون برنامه ای و در کد زیر وارد کچ می شه و پیغام مشکل در . . . توست می شه ، ایراد از کجا می تونه باشه ؟ ممنون
if (isPackageInstalled("com.farsitel.bazaar")) {
// Bazar is installed
try {
G.mHelper.launchPurchaseFlow(ActivityPayment.this, G.SKU_PREMIUM, RC_REQUEST, mPurchaseFinishedListener);
}
catch (Exception e) {
e.printStackTrace();
Toast.makeText(G.context, "مشکلی در پرداخت کافه بازار بوجود آمد لطفا بعدا امتحان کنید ",
Toast.LENGTH_SHORT).show();
}
}
else {
// Bazar is not installed
//open Bazzar Home Page To Download It !
Toast.makeText(G.context,
"لطفا برنامه کافه بازار را بروی دستگاه گوشی نصب کنید ",
Toast.LENGTH_SHORT).show();
}
با سلام به همه عزیزان
فایده کلید RSA چیه؟
آخه بدون اون هم می شه پرداخت کرد و ثبت خرید در تراکنش های ما قرار می گیره.
اگر کسی اونو هک کنه چه اتفاقی می افته؟!
سلم خدمت اساتید عزیز چنتا مشکل دارم :
1 - چرا پرداخت درون برنامه توی اندروید 5 به بالا کرش میکنه و خطا میده که مشکل از IabHelper.java هست
2 - من طبق آموزش های A.L.U پیش رفتم و مشکلی ندارم ولی موقعی که روی دکمه کلیک میکنم Lucky Patcher بالا میاد که دورش بزنه جلو چشم خودم :|
کد لیستنر ها رو به همراه mHelper رو داخل یه تابع گذاشتم به نظرتون کاره درستیه ؟
public static void Inventory(final String SKU) {
mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
if (result.isFailure()) {
Toast.makeText(G.context, "دریافت اطلاعات از بازار ناموفق بود: " + result, Toast.LENGTH_SHORT).show();
return;
}
else {
Toast.makeText(G.context,"دریافت اطلاعات از بازار با موفقیت انجام شد",Toast.LENGTH_SHORT).show();
mIsPremium = inventory.hasPurchase(SKU);
Toast.makeText(G.context,"کاربر : " + (mIsPremium ? "ویژه" : "ویژه نیست"),Toast.LENGTH_SHORT).show();
}
}
};
mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() {
@Override
public void onConsumeFinished(Purchase purchase, IabResult result) {
// TODO Auto-generated method stub
if(result.isSuccess()){
// Consumed !
}
}
};
mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
if (result.isFailure()) {
Toast.makeText(G.context,"پرداخت با موفقیت انجام نشد" + result,Toast.LENGTH_SHORT).show();
return;
}
else if (purchase.getSku().equals(SKU)) {
Toast.makeText(G.context,"پرداخت انجام شد ... !!" + result,Toast.LENGTH_SHORT).show();
mHelper.consumeAsync(purchase, mConsumeFinishedListener);
}
}
};
mHelper = new IabHelper(G.context, base64EncodedPublicKey);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
Toast.makeText(G.context, "پرداخت انجام نشد", Toast.LENGTH_LONG).show();
}
mHelper.queryInventoryAsync(mGotInventoryListener);
}
});
}
اینم بخش اتصال به بازار هست :
G.Inventory(SKU_SEKE2); //این همون تابع بالاست
if (isPackageInstalled("com.farsitel.bazaar")) {
try {
G.mHelper.launchPurchaseFlow(this, SKU_SEKE3, G.RC_REQUEST, G.mPurchaseFinishedListener, "payload-string");
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(G.context, "یک اتفاقی رخ داده است لطفا بعدا سعی کنید", Toast.LENGTH_SHORT).show();
}
}else {
Toast.makeText(G.context,"جهت افزایش سکه نیازمند برنامه بازار می باشید",Toast.LENGTH_SHORT).show();
}
ممنون میشم راهنمایی کنید
سلام
من قبلا پرداخت درون برنامه ای رو پیاده کرده بودم. این چنده روزه خواستم دوباره بنویسم ولی متوجه شدم بازار نیاز به سرور رو هم در نظر گرفته. ایا داشتن سرور ضروریه؟
سلام ممنون از تاپیک خوبتون
جناب ALU درمورد کلاس G بیشتر توضیح میدید ؟
کدی که قرار دادین رو کجای کلاس باید بزارم؟ کلاس باید extend به چی باشه ؟
ممنون...
سلام آقایALU ، در سورسهای فعلی بازار برای پرداخت درون برنامه ای، تمام خطاها رو برطرف کردم، امّا این خطای پایین رو هرکاری کردم حل نشد، اشکال از چیه، یک هفته ست درگیر این مشکلم.
برای خطای R راه حلی پیدا نکردم، نه در stackOverFlow نه قسمت پرسش های انجمنuncox، اما خطای getLongVersioncode رو برطرف کردم.
خواهشا اگر کسی این پرداخت درون برنامه ای رو کامل انجام داده، برای این مشکل یه راه حل بده. تشکر از همگی دوستان
پاسخگویی و مشاهده پاسخ های این سوال تنها برای اعضای ویژه سایت امکان پذیر است .
چنانچه تمایل دارید به همه بخش ها دسترسی داشته باشید میتوانید از این بخش لایسنس این آموزش را خریداری نمایید .