پرداخت درون‌برنامه‌ای بازار

پیاده‌سازی 


 

پیاده‌سازی سریع پرداخت درون‌برنامه‌ای با کلاس‌های کمکی

 

این راهنما در ۹ گام حداقل کارهای لازم برای پیاده‌سازی و راه‌اندازی پرداخت درون‌برنامه‌ای در برنامه‌تان را توضیح می‌دهد. 
پیاده‌سازی این بخش به کمک کلاس‌های کمکی انجام شده است که در پوشهٔ util مربوط به پروژهٔ مثال TrivialDrive قرار دارند. این کلاس‌ها کار شما را برای راه‌اندازی سریع پرداخت درون‌برنامه‌ای، مدیریت درخواست‌های پرداخت درون‌برنامه‌ای از thread اصلی برنامه‌ و…راحت‌تر می‌کنند. برای پیاده‌سازی کامل پرداخت درون‌برنامه‌ای با کمک کلاس‌های کمکی، کلاس آموزشی فروش محصولات درون‌برنامه‌ای را ببینید.
برای کارهای پیچیده‌تر لازم است مستندات دیگر را مطالعه کنید و از پیچ و خم اتفاقات از لحظهٔ پیشنهاد دادن کالاهای‌تان برای فروش تا زمان تحویل کالای دیجیتال‌تان به کاربر و ثبت این تحویل مطلع شوید.

۱: برنامه‌ای ساده برای پرداخت درون‌برنامه‌ای

خوب است که همیشه با مثالی شروع کنیم و با بازی با اجزای آن کار را جلو ببریم. این مثال که از سوی گوگل ارائه شده است TrivialDrive نام دارد و نمونهٔ مناسبی برای درک مفاهیم پایه‌ای پرداخت درون‌برنامه‌ای و دسترسی به فایل‌های لازم برای کپی کردن در محیط توسعه است (گام 2 را ببینید). ما سه خط از این مثال را تغییر داده‌ایم تا برای خرید به جای گوگل به سراغ بازار بیاید.
سورس کد کامل این برنامه را می‌توانید در قالب یک فایل زیپ شده از اینجا و یا در قالب یک git repository از اینجا دریافت کنید. سه خط تغییر ما نیز در خط‌های ۲۶۵ و ۲۶۶ کلاس IabHelper و خط ۲۷ از فایل AndroidManifest.xml قرار دارند.

۲: کتابخانه

فایل IInAppBillingService.aidl و پوشهٔ util از کد مثال را به پروژهٔ خود اضافه کنید. حواستان باشد که نام‌بستهٔ فایل‌های درون پوشهٔ util را با توجه به محل جدیدشان چنانچه لازم است ویرایش و به‌روز کنید. در انتهای این کار ساختار پوشه‌بندی پروژهٔ شما در Eclipse باید شبیه تصویر زیر باشد. در مورد جزئیات اضافه کردن این فایل، بخش افزودن کتابخانهٔ پرداخت درون‌برنامه‌ای در کلاس آموزشی فروش محصولات درون‌برنامه‌ای را ببینید.

۳: مجوز دسترسی

این کد را به فایل AndroidManifest.xml برنامهٔ خود اضافه کنید:

<uses-permission android:name="com.farsitel.bazaar.permission.PAY_THROUGH_BAZAAR"></uses-permission>

۴: محصول

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

۵: متغیرها

پیش از صدا زدن متد onCreate مربوط به activity ای که فروش محصول‌ درون‌برنامه‌ای را انجام می‌دهد، متغیرهای زیر را تعریف کنید و مقادیر پیش‌فرض مناسبی برای آن‌ها تعیین کنید:

// Debug tag, for logging
static final String TAG = "";

// SKUs for our products: the premium upgrade (non-consumable)
static final String SKU_PREMIUM = "";

// Does the user have the premium upgrade?
boolean mIsPremium = false;

// (arbitrary) request code for the purchase flow
static final int RC_REQUEST = ;

// The helper object
IabHelper mHelper;

۶: onCreate

به متد onCreate مربوط به activity ای که فروش محصول‌ درون‌برنامه‌ای را انجام می‌دهد، کدهای زیر را اضافه کنید:

String base64EncodedPublicKey = "";
// You can find it in your Bazaar console, in the Dealers section. 
// It is recommended to add more security than just pasting it in your source code;
mHelper = new IabHelper(this, base64EncodedPublicKey);

Log.d(TAG, "Starting setup.");
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.d(TAG, "Problem setting up In-app Billing: " + result);
        }
        // Hooray, IAB is fully set up!
        mHelper.queryInventoryAsync(mGotInventoryListener);
    }
});

۷: کدهای مثال مربوط به listenerها

کدهای داخل پوشهٔ util کارهای مشترک بین برنامه‌هایی که از طریق پرداخت درون‌برنامه‌ای کالا می‌فروشند را انجام می‌دهند. برای زمانی که این کدها چک‌های لازم را انجام داده‌اند و دیگر نوبت اقدامی توسط برنامه‌نویس است، لازم است که listenerهایی بنویسید تا به موقع وارد عمل شوند. در کد زیر listener اول زمانی استفاده می‌شود که بازار فهرست خریدهای مصرف نشدهٔ کاربر را باز می‌گرداند و listener دوم زمانی که یک خرید به اتمام می‌رسد فراخوانی خواهد شد.

IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.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.");
    }
};

IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.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
        }
    }
};

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    Log.d(TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + data);

    // Pass on the activity result to the helper for handling
    if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {
        super.onActivityResult(requestCode, resultCode, data);
    } else {
        Log.d(TAG, "onActivityResult handled by IABUtil.");
    }
}

۸: کد مثال فرستادن کاربر برای خرید یک کالا

این کد کاربر را به صفحهٔ خرید «ارتقا» در بازار می‌فرستد. وقتی کاربر از بازار برگردد، برنامهٔ شما به کمک کد mPurchaseFinishedListener خبردار می‌شود.

mHelper.launchPurchaseFlow(this, SKU_PREMIUM, RC_REQUEST, mPurchaseFinishedListener, "payload-string");

۹: onDestroy

در زمان اتمام عمر activity، اتصال خود را از سرویس قطع کنید:

@Override
public void onDestroy() {
    super.onDestroy();
    if (mHelper != null) mHelper.dispose();
    mHelper = null;
}

کار شما در این مرحله تمام است. فقط منتظر بمانید که بازار آخرین نسخهٔ apk شما را بر روی سِرورهایش قرار دهد.

 


 

کلاس آموزشی فروش محصولات درون‌برنامه‌ای

 

در این کلاس آموزشی یاد می‌گیرید که چگونه عملیات معمول پرداخت درون‌برنامه‌ای را در برنامه‌تان با استفاده از کلاس‌های کمکی موجود در پوشهٔ util از پروژهٔ مثال (TrivialDrive) پیاده‌سازی کنید. در صورت نیاز به کسب اطلاعات بیشتر در مورد جزئیات کلاس‌های پوشهٔ util، لطفاً به کدهای مربوطه‌شان مراجعه کنید.
این کلاس آموزشی مبتنی بر نسخهٔ ۳ از API پرداخت درون‌برنامه‌ای بازار است. بهتر است قبل از شروع این کلاس، بخش مقدماتی را برای آشنایی با مفاهیم پایه استفاده شده مطالعه کنید.

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

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

دانلود برنامهٔ نمونه

در این کلاس آموزشی از پیاده‌سازی API پرداخت درون‌برنامه‌ای بازار در برنامهٔ نمونه (که TrivialDrive نام دارد) استفاده می‌شود. برنامهٔ نمونه شامل کلاس‌های کمکی برای پیاده‌سازی سریع پرداخت درون‌برنامه‌ای است و نمونهٔ مناسبی برای درک مفاهیم پایه‌ای پرداخت درون‌برنامه‌ای است. ما سه خط از این مثال را تغییر داده‌ایم تا برای خرید به جای گوگل به سراغ بازار بیاید.
سورس کد کامل این برنامه را می‌توانید در قالب یک فایل زیپ شده از اینجا و یا در قالب یک git repository از اینجا دریافت کنید. سه خط تغییر ما نیز در خط‌های ۲۶۵ و ۲۶۶ کلاس IabHelper و خط ۲۷ از فایل AndroidManifest.xml قرار دارند.

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

پنل توسعه‌دهندگان بازار جایی است که برنامهٔ خود را در آن منتشر می‌کنید. زمانی که برنامهٔ جدیدی را در پنل توسعه‌دهندگان اضافه می‌کنید، بازار به صورت خودکار یک کلید عمومی برای برنامه‌‌ٔ شما تولید می‌کند. برای ایجاد ارتباطی امن بین برنامهٔ خود و سِرورهای بازار به این کلید نیاز دارید. این کلید تنها یک مرتبه برای برنامهٔ شما ساخته می‌شود و با به‌روزرسانی برنامهٔ خود و بارگذاری APK جدید تغییر نخواهد کرد. 
برای اضافه کردن برنامهٔ خود به پنل توسعه‌دهندگان بازار مراجعه کنید. در صورت نیاز به کسب اطلاعات بیشتر به مستندات پنل رجوع کنید.

افزودن کتابخانهٔ پرداخت درون‌برنامه‌ای

برای استفاده از قابلیت‌های پرداخت درون‌برنامه‌ای بازار،‌ باید فایل IInAppBillingService.aidl را به پروژهٔ خود اضافه کنید. این فایل رابط (interface) سرویس بازار را تعریف می‌کند.
می‌توانید فایل IInAppBillingService.aidl را در برنامهٔ نمونه TrivialDrive بیابید. بسته به این‌که برنامهٔ جدیدی ایجاد می‌کنید یا برنامهٔ قبلی خود را می‌خواهید تغییر دهید،‌ گام‌های زیر را برای اضافه کردن کتابخانهٔ پرداخت درون‌برنامه‌ای به پروژه‌تان دنبال کنید.

پروژهٔ جدید:

برای اضافه کردن کتابخانهٔ پرداخت درون‌برنامه‌ای به پروژهٔ جدید خود:

۱. فایل‌های برنامهٔ نمونهٔ TrivialDrive را در پروژهٔ اندروید خود کپی کنید.

۲. نام بستهٔ (package name) فایل‌هایی که کپی کرده‌اید را به نام بستهٔ پروژهٔ خود تغییر دهید. در Eclipse می‌توانید به این شکل عمل کنید: روی نام بسته راست کلیک کنید،‌ گزینهٔ Refactor و سپس گزینهٔ Rename را انتخاب کنید. 

۳. فایل AndroidManifest.xml را باز کنید و نام بسته را به نام بستهٔ پروژهٔ خود تغییر دهید. 

۴. عبارات import را تصحیح کنید تا پروژه‌تان به درستی کامپایل شود. در Eclipse می‌توانید از کلیدهای میانبر Ctrl+Shift+O در هر فایلی که خطا دارد استفاده کنید. 

۵. کدهای برنامهٔ نمونه را تغییر دهید تا برنامهٔ خود را بسازید. یادتان باشد کلید عمومی برنامهٔ خود را از پنل پرداخت در MainActivity.java کپی کنید.

پروژهٔ موجود:

۱. فایل IInAppBillingService.aidl را در پروژهٔ اندروید خود کپی کنید.

  • در Android Studio: پوشه‌ای به نام aidl در زیر پوشهٔ src/main ایجاد کنید. بستهٔ جدید com.android.vending.billing را در این پوشه اضافه کنید و فایل IInAppBillingService.aidl را در این بسته import کنید. 

  • در Eclipse: فایل IInAppBillingService.aidl را در پوشهٔ src/ پروژهٔ خود import کنید.

  • در دیگر محیط‌های توسعه: پوشهٔ /src/com/android/vending/billing را بسازید و فایل IInAppBillingService.aidl را در این پوشه کپی کنید.

۲. برنامهٔ خود را build کنید. پس از build موفقیت‌آمیز باید فایل IInAppBillingService.java تولید شده را در پوشهٔ gen/ پروژهٔ خود ببینید. 

۳. کلاس‌های کمکی درون پوشهٔ util/ برنامهٔ نمونهٔ TrivialDrive را به پروژه‌تان اضافه کنید. به یاد داشته باشید که نام بستهٔ این فایل‌ها را نیز متناسب با پروژهٔ خود تغییر دهید تا پروژه‌تان به درستی کامپایل شود. 

پروژهٔ شما هم‌اکنون شامل کتابخانهٔ پرداخت درون‌برنامه‌ای بازار است.

تنظیم مجوز پرداخت

برنامهٔ شما برای رد و بدل کردن پیغام‌های درخواست و پاسخ با سرویس پرداخت درون‌برنامه‌ای بازار نیاز به مجوز دارد. برای دادن مجوز لازم به برنامه‌تان خط زیر را به فایل AndroidManifest.xml خود اضافه کنید:

<uses-permission android:name="com.farsitel.bazaar.permission.PAY_THROUGH_BAZAAR"></uses-permission>
برقراری ارتباط با بازار

برای این‌که بتوانید درخواست‌های پرداخت درون‌برنامه‌ای را از برنامه‌تان به بازار بفرستید،‌ باید به سرویس پرداخت درون‌برنامه‌ای بازار متصل شوید. کلاس‌های کمکی موجود در برنامهٔ نمونه، اتصال به سرویس پرداخت درون‌برنامه‌ای را مدیریت می‌کنند. لذا نیازی نیست که خودتان مستقیماً ارتباط را مدیریت کنید. 
برای راه‌اندازی ارتباط با بازار، در متد onCreate مربوط به activity، نمونه‌ای از کلاس IabHelper بسازید. پارامترهای متد سازندهٔ (constructor) این کلاس عبارتند از: Context مربوط به activity و رشته‌‌ای که حاوی کلید عمومی برنامه‌‌ی شما است.

IabHelper mHelper;

@Override
public void onCreate(Bundle savedInstanceState) {
   // ...
   String base64EncodedPublicKey;

   // compute your public key and store it in base64EncodedPublicKey
   mHelper = new IabHelper(this, base64EncodedPublicKey);
}
توصیهٔ امنیتی: برای ایمن نگه داشتن کلید عمومی‌ از گزند کاربران مخرب یا هکرها، سعی کنید آن را به صورت رشته‌ای ثابت درون کد قرار ندهید. در عوض برای پنهان کردن کلید اصلی‌، رشته را به طریقی در زمان اجرا بسازید یا از دستکاری بیت‌ها (مانند XOR با چند رشتهٔ دیگر) استفاده کنید یا آن را از یک مخزن رمزشده بگیرید. کلید به خودی خود دادهٔ محرمانه‌ای نیست، امامطمئناً نمی‌خواهید کار را برای هکر‌ها جهت جایگزینی کلید عمومی‌ برنامهٔ شما با کلیدی دیگر آسان کنید.

در ادامه با فراخوانی متد startSetup از نمونهٔ IabHelper ای که پیشتر ساختید، عمل اتصال به سرویس را انجام دهید. به این متد نمونه‌ای از OnIabSetupFinishedListener بدهید. زمانی که IabHelper عملیات راه‌اندازی را تمام می‌کند OnIabSetupFinishedListener را فراخوانی می‌کند. همچنین در خلال فرآیند راه‌اندازی، IabHelper بررسی می‌کند که آیا بازار از نسخهٔ ۳ پرداخت درون‌برنامه‌ای پشتیبانی می‌کند یا خیر. در صورتی که نسخهٔ API پشتیبانی نشود یا خطای دیگری در هنگام برقراری اتصال به سرویس رخ دهد،‌ OnIabSetupFinishedListener مطلع می‌شود و یک شیء IabResult با پیغام خطای مربوطه به آن ارسال می‌شود. 

mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
   public void onIabSetupFinished(IabResult result) {
      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 برای رد و بدل کردن پیغام‌های درخواست و پاسخ بین برنامه‌تان و بازار استفاده کنید. وقتی کاربر، برنامه‌تان را باز می‌کند زمان خوبی است که فهرست محصولاتی را که صاحب آن‌ها است از بازار بپرسید. این موضوع بیشتر در بخش درخواست محصولات خریداری شده توضیح داده شده است.

مهم: به یاد داشته باشید که اتصال از سرویس پرداخت درون‌برنامه‌ای را زمانی‌ که activityتان را می‌بندید، قطع کنید. اگر اتصال را قطع نکنید، اتصال باز به سرویس باعث تنزل کارایی دستگاه کاربر می‌شود. برای قطع اتصال و آزاد کردن منابع سیستم، متد dispose از نمونهٔ کلاس IabHelper را زمانی که activityتان از بین می‌رود، فراخوانی کنید.
@Override
public void onDestroy() {
   super.onDestroy();
   if (mHelper != null) mHelper.dispose();
   mHelper = null;
}

محصولات درون‌برنامه‌ای

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

درخواست فهرست محصولات قابل فروش

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

توجه: وقتی کوئری می‌زنید،‌ بایستی شناسه‌‌ی محصولات را صریحاً مشخص کنید. شناسهٔ محصولی (که به آن SKU نیز گفته می‌شود) که برای هر محصول تعریف کرده‌اید را می‌توانید در پنل توسعه‌دهندگی‌تان، پس از انتخاب برنامه‌ای را که مدنظرتان است و در سربرگ «پرداخت درون‌برنامه‌ای»، در زیر ستون «شناسه کالا» مشاهده کنید.

برای دریافت جزئیات محصول، متد:

queryInventoryAsync(boolean, List, QueryInventoryFinishedListener)

را از نمونهٔ IabHelper ای که قبلاً ساخته‌اید، فراخوانی کنید: 

  • اولین پارامتر ورودی این متد نشان‌دهندهٔ این است که آیا جزئیات محصول هم باید برگردانده شود (که در این صورت باید مقدار آن را true بگذارید).
  • پارامتر دوم این متد، Listای است شامل یک یا چندین شناسهٔ محصولِ مربوط به محصولاتی که برای آن‌ها کوئری می‌زنید. 
  • پارامتر آخر این متد، QueryInventoryFinishedListener، یک listener را مشخص می‌کند که پس از پایان عملیات کوئری فراخوانی می‌شود و پاسخ کوئری را بررسی می‌کند. 

اگر از کلاس‌های کمکی برنامهٔ نمونهٔ TrivialDrive استفاده کنید، این کلاس‌ها مدیریت threadهای پس‌زمینه برای درخواست‌های پرداخت درون‌برنامه‌ای را انجام می‌دهند و شما می‌توانید به راحتی از thread اصلی برنامه‌تان کوئری بزنید. 
نمونه کد زیر نشان می‌دهد که چگونه می‌توانید جزئیات مربوط به دو محصول با شناسه‌های SKU_APPLE و SKU_BANANA را که قبلاً در پنل توسعه‌دهندگان تعریفشان کرده‌اید دریافت کنید.

List additionalSkuList = new List();
additionalSkuList.add(SKU_APPLE);
additionalSkuList.add(SKU_BANANA);
mHelper.queryInventoryAsync(true, additionalSkuList,
   mQueryFinishedListener);

اگر کوئری موفقیت‌آمیز باشد، نتایج کوئری در شیء Inventory ای ذخیره می‌شود که به listener برگردانده می‌شود. 
نمونه کد زیر نشان می‌دهد که چگونه می‌توانید قیمت‌ محصولات را از نتایج برگردانده شده بازیابی کنید.

IabHelper.QueryInventoryFinishedListener
   mQueryFinishedListener = new IabHelper.QueryInventoryFinishedListener() {
   public void onQueryInventoryFinished(IabResult result, Inventory inventory)  
   {
      if (result.isFailure()) {
         // handle error
         return;
       }

       String applePrice =
          inventory.getSkuDetails(SKU_APPLE).getPrice();
       String bananaPrice =
          inventory.getSkuDetails(SKU_BANANA).getPrice();

       // update the UI
   }
}
نکته: در صورتی که قصد دارید بدون استفاده از Inventory این اطلاعات را به دست بیاورید می‌توانید از متد querySkuDetails در IabHelper استفاده نمائید.

خرید محصولات درون‌برنامه‌ای

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

خرید محصول

برای شروع درخواست خرید از برنامه‌تان متد:

launchPurchaseFlow(Activity, String, int, OnIabPurchaseFinishedListener, String)

را از نمونهٔ IabHelperای که قبلاً ساخته‌اید فراخوانی کنید. این فراخوانی باید از thread اصلی برنامه انجام شود. در ادامه پارامترهای این متد توضیح داده شده‌اند:

  • اولین پارامتر،‌ activity است که فراخوانی را انجام داده.
  • پارمتر دوم، شناسهٔ محصول (یا SKU) آیتم مورد خریداری است. باید قبلاً محصول را در پنل پرداخت تعریف کرده باشید و وضعیت آن در حالت فعال باشد. دقت کنید که شناسهٔ محصول را بدهید نه نام آن را. در غیر این صورت قابل شناسایی نخواهد بود.
  • پارامتر سوم، مقدار کد درخواست است. این مقدار می‌تواند هر عدد صحیح مثبتی باشد. بازار این کد درخواست را به همراه پاسخ خرید به متد onActivityResult مربوط به activity درخواست دهنده، برمی‌گرداند. 
  • پارامتر چهارم، listenerای است که هنگام پایان یافتن عملیات خرید فراخوانی می‌شود و پاسخ خرید بازار را بررسی و مدیریت می‌کند.
  • پارامتر پنجم حاوی رشتهٔ 'developer payload' است. که شما می‌توانید در آن اطلاعات تکمیلی مربوط به سفارش خود را بفرستید (می‌تواند یک رشتهٔ خالی نیز باشد). معمولاً این رشته توکنی است که منحصراً این درخواست خرید را مشخص می‌کند. اگر این رشته را مقداردهی کنید، بازار آن را به همراه پاسخ خرید برمی‌گرداند. متعاقباً زمانی که در مورد این خرید کوئری می‌زنید، بازار این رشته را به همراه سایر جزئیات خرید برمی‌گرداند.
توصیهٔ امنیتی: بهتر است رشته‌ای بفرستید که به برنامهٔ شما کمک کند تا کاربری که خرید را انجام داده را شناسایی کنید؛ بدین ترتیب بعداً می‌توانید تشخیص دهید که خرید مورد نظر توسط آن کاربر معتبر است یا خیر. برای محصولات درون‌برنامه‌ای مصرفی می‌توانید از رشته‌ای که به صورت تصادفی تولید شده استفاده کنید، اما برای محصولات درون‌برنامه‌ای غیرمصرفی باید از رشته‌ای استفاده کنید که کاربر را به صورت منحصر به فرد شناسایی می‌کند. در این‌جا منظور کاربر برنامهٔ خودتان است، زیرا برنامهٔ شما به اطلاعات حساب کاربر در بازار دسترسی ندارد (البته در صورتی که در برنامه‌تان امکان ایجاد حساب کاربری وجود داشته باشد).

مثالی که در ادامه آمده است نشان می‌دهد که چگونه می‌توانید درخواست خرید برای محصولی با شناسهٔ SKU_GAS را بدهید. در این مثال مقدار دلخواه کد درخواست، 10001 است و رشتهٔ developer payload آن رمز شده است.

mHelper.launchPurchaseFlow(this, SKU_GAS, 10001,  
   mPurchaseFinishedListener, "bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ");

در صورتی که سفارش خرید موفقیت‌آمیز باشد، پاسخ بازار در شیء Purchase ای ذخیره می‌شود که به listener برگردانده می‌شود.
مثال‌ زیر نشان می‌دهد که چگونه می‌توانید پاسخ خرید را در listener بررسی و مدیریت کنید (برحسب این‌که سفارش خرید با موفقیت پایان یافته یا خیر، و این‌که کاربر بنزین خریداری کرده یا ارتقاء به نسخهٔ پولی را). در این مثال بنزین (gas) محصول درون‌برنامه‌ای است که می‌تواند چندین مرتبه خریداری شود، بنابراین باید پس از خرید، آن را مصرف کنید تا کاربر بتواند آن را دوباره خریداری کند. برای کسب اطلاعات بیشتر در مورد مصرف محصول، بخش مصرف کردن خرید را ببینید. ارتقاء به نسخهٔ پولی (premium upgrade) تنها یک مرتبه قابل خرید است، بنابراین نیازی نیست که آن را مصرف کنید. بهتر است پس از خرید موفقیت‌آمیزِ محصول توسط کاربر،‌ رابط کاربری (UI) برنامه‌تان را فوراً به نحوی تغییر دهید که کاربر بتواند محصول جدید خریداری شدهٔ خود را ببیند.

IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener
   = new IabHelper.OnIabPurchaseFinishedListener() {
   public void onIabPurchaseFinished(IabResult result, Purchase purchase)
   {
      if (result.isFailure()) {
         Log.d(TAG, "Error purchasing: " + result);
         return;
      }      
      else if (purchase.getSku().equals(SKU_GAS)) {
         // consume the gas and update the UI
      }
      else if (purchase.getSku().equals(SKU_PREMIUM)) {
         // give user access to premium content and update the UI
      }
   }
};
توصیهٔ امنیتی: زمانی که پاسخ خرید را از بازار دریافت می‌کنید، حتماً صحت امضای دادهٔ بازگشتی، orderId، و رشتهٔ developerPayload در شیء Purchase را بررسی کنید (جهت اطمینان از این‌که مقادیر مورد انتظار را دریافت می‌کنید). باید بررسی کنید که مقدار orderId مقدار یکتایی است و قبلاً‌ آن را پردازش نکرده‌اید. همچنین بررسی کنید که مقدار رشتهٔ developerPayload همان مقداری باشد که قبلاً با درخواست خرید ارسال کرده بودید. برای امنیت بیشتر باید این بررسی‌ها را سمت سِرور خودتان انجام دهید. 
کوئری محصولات خریداری شده

پس از یک خرید موفق، بازار اطلاعات خرید را ذخیره می‌کند. بهتر است هر از چند گاه، برای دریافت فهرست خریدهای کاربر به سرویس پرداخت درون‌برنامه‌ای کوئری بزنید (برای مثال هر موقع که کاربر برنامه را باز می‌کند). به این ترتیب می‌توانید به‌روزترین اطلاعات مالکیت کاربر بر محصولات درون‌برنامه‌ای را در برنامه‌تان منعکس کنید. 
برای دریافت فهرست خریدهای کاربر از برنامه‌تان متد:

queryInventoryAsync(QueryInventoryFinishedListener)

را از نمونهٔ IabHelperای که پیشتر ساخته‌اید فراخوانی کنید. پارامتر QueryInventoryFinishedListener، یک listener است که در پایان انجام عملیات کوئری، فراخوانی می‌شود و پاسخ کوئری را بررسی و مدیریت می‌کند. فراخوانی این متد از thread اصلی برنامه مشکلی ایجاد نمی‌کند.

mHelper.queryInventoryAsync(mGotInventoryListener);

در صورتی که کوئری موفقیت‌آمیز باشد،‌ نتایج کوئری در شی‌ء Inventoryای که به listener فرستاده می‌شود،‌ ذخیره می‌شود. سرویس پرداخت درون‌برنامه‌ای فقط خریدهای کاربری که هم‌اکنون در بازار لاگین است را برمی‌گرداند. 

IabHelper.QueryInventoryFinishedListener mGotInventoryListener
   = new IabHelper.QueryInventoryFinishedListener() {
   public void onQueryInventoryFinished(IabResult result,
      Inventory inventory) {

      if (result.isFailure()) {
        // handle error here
      }
      else {
        // does the user have the premium upgrade?
        mIsPremium = inventory.hasPurchase(SKU_PREMIUM);        
        // update UI accordingly
      }
   }
};
مصرف کردن خرید

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

یادآوری: از بین انواع محصولات درون‌برنامه‌ای،‌ محصولاتی که از نوع محصولات مصرفی در منطق برنامه‌تان تعریف کرده‌اید را مصرف کنید. زیرا این دسته از محصولات هستد که تأثیر موقتی دارند و کاربر می‌خواهد چندین مرتبه آن‌ها را خریداری کند (مانند سکهٔ درون بازی). مسلماً کاربر نمی‌خواهد محصولات درون‌برنامه‌ای غیرمصرفی که یک مرتبه آن‌ها را خریداری کرده اما تأثیرشان همیشگی است را دوباره خریداری کند (برای مثال ارتقاء برنامه به نسخهٔ پولی)؛ لذا درخواست مصرف را فقط برای محصولات مصرفی برنامه‌تان بفرستید. برای محصولات درون‌برنامه‌ای از نوع اشتراک نیز نباید درخواست مصرف بدهید. جهت کسب اطلاعات بیشتر بخش مفاهیم پایه را ببینید.

مسئولیت کنترل و پیگیریِ چگونگی ارائهٔ محصولات درون‌برنامه‌ای که کاربر در برنامه‌تان می‌خرد به عهدهٔ شما است. برای مثال اگر کاربر سکهٔ درون بازی را بخرد، شما باید دارایی کاربر را به مقدار سکه‌ای که خریداری کرده افزایش دهید.

توصیهٔ امنیتی: باید پیش از منعکس کردن اثر خرید کاربر در برنامه‌تان (در صورتی که محصول خریداری شده از نوع مصرفی باشد)، برای آن درخواست مصرف بفرستید. پیش از فراهم کردن محصول خریداری شده در برنامه‌تان،‌ مطمئن شوید که پاسخ مصرف موفق از بازار دریافت کرده‌اید.

برای مصرف یک محصول،‌ متد:

consumeAsync(Purchase, OnConsumeFinishedListener)

را ازنمونهٔ IabHelper ای که قبلاً ساخته‌اید فراخوانی کنید. اولین پارامتر این متد،‌ یک شیء Purchase است که بیانگر محصولی است که قرار است مصرف شود. پارامتر دوم این متد، OnConsumeFinishedListener، زمانی که عملیات مصرف پایان می‌یابد فراخوانی می‌شود که در ادامه پاسخ مصرف را بررسی و مدیریت خواهد کرد. فراخوانی این متد از thread اصلی مانعی ندارد. 
در این مثال،‌ می‌خواهید محصول بنزین (gas) که کاربر پیشتر در برنامه‌تان خریداری کرده است را مصرف کنید:

mHelper.consumeAsync(inventory.getPurchase(SKU_GAS),
   mConsumeFinishedListener);

مثال زیر نحوهٔ پیاده‌سازی OnConsumeFinishedListener را نشان می‌دهد:

IabHelper.OnConsumeFinishedListener mConsumeFinishedListener =
   new IabHelper.OnConsumeFinishedListener() {
   public void onConsumeFinished(Purchase purchase, IabResult result) {
      if (result.isSuccess()) {
         // provision the in-app purchase to the user
         // (for example, credit 50 gold coins to player's character)
      }
      else {
         // handle error
      }
   }
};
چک کردن آیتم‌های قابل مصرف در آغاز برنامه

چک کردن آیتم‌های قابل مصرف، زمانی که کاربر برنامهٔ شما را باز می‌کند،‌ کار مهمی است. معمولاً ابتدا محصولات خریداری شدهٔ کاربر را از سرویس خرید درون‌برنامه‌ای می‌پرسید (توسط queryInventoryAsync).سپس اشیاء Purchase قابل مصرف را از Inventory بگیرید. در صورتی که برنامهٔ شما تشخیص دهد که کاربر صاحب محصولاتی از نوع قابل مصرف است،‌ باید بلافاصله درخواست مصرف آن را به بازار بفرستد و در برنامه‌اش محصول را برای کاربر فراهم کند. برای کسب اطلاعات بیشتر در مورد پیاده‌سازی این بررسی در آغاز برنامه، برنامهٔ نمونهٔ TrivialDrive را ببینید.

 

 


 

پیاده‌سازی پرداخت درون‌برنامه‌ای توسط API پرداخت

برنامهٔ بازار واسط ساد‌ه و کارایی برای مدیریت تراکنش‌های پرداخت‌ درون‌برنامه‌ای ارائه می‌دهد. اطلاعات زیر نحوهٔ ارسال درخواست از برنامهٔ شما به سرویس پرداخت درون‌برنامه‌ای توسط API را نشان می‌دهد.

توجه: برای پیاده‌سازی کامل به کلاس آموزشی فروش محصولات درون‌برنامه‌ای و پروژهٔ TrivalDrive مراجعه کنید. این کلاس آموزشی،‌ مثال کاملی از پیاده‌سازی پرداخت درون‌برنامه‌ای ارائه می‌کند که شامل کلاس‌هایی برای انجام وظایف کلیدی مربوط به برقراری اتصالات، فرستادن درخواست پرداخت، پردازش پاسخ بازار و مدیریت موازی کارهای پس‌زمینه‌ای است تا شما بتوانید فراخوانی متدهای پرداخت درون‌برنامه‌ای را از activity اصلی خود انجام دهید.

قبل از شروع توصیه می‌شود بخش مقدماتی را به دقت بخوانید تا با مفاهیم کلی‌ پرداخت درون‌برنامه‌ای آشنا شوید و پیاده‌سازی آن برای‌تان آسان‌تر شود.


در ادامهٔ این بخش، پنج گام‌ اصلی زیر برای پیاده‌سازی پرداخت‌ درون‌برنامه‌ای شرح داده می‌شوند:

۱. کتابخانهٔ پرداخت درون‌برنامه‌ای (فایل AIDL) را به پروژهٔ خود اضافه کنید. 

۲. فایل AndroidManifest.xml را به‌روزرسانی کنید. 

۳. یک ServiceConnection ایجاد کنید و آن را به IInAppBillingService متصل کنید. 

۴. درخواست‌های پرداخت درون‌برنامه‌ای را از برنامهٔ خود به IInAppBillingService بفرستید.

۵. پاسخ‌های پرداخت درون‌برنامه‌ای بازار را مدیریت کنید.

اضافه کردن فایل AIDL به پروژه

فایل IInAppBillingService.aidl،‌ یک Android Interface Definition Language (AIDL)‎ است که رابطی برای سرویس پرداخت درون‌برنامه‌ای بازار است. از این رابط برای برقراری ارتباط با بازار و ساختن درخواست‌های پرداخت استفاده می‌کنید. 
دریافت فایل AIDL:

۱. پروژهٔ مثال TrivialDrive را از اینجا دانلود کنید. 

۲. فایل IInAppBillingService.aidl را می‌توانید در آدرس src/com/android/vending/billing/IInAppBillingService.aidl داخل پروژهٔ TrivialDrive پیدا کنید.

افزودن کتابخانهٔ پرداخت درون‌برنامه‌ای (فایل AIDL) به پروژه:

۱. فایل IInAppBillingService.aidl را در پروژهٔ اندروید خود کپی‌ کنید.

  • اگر از Eclipse استفاده می‌کنید:
    • اگر قبلاً پروژهٔ خود را ساخته‌اید آن را در Eclipse باز کنید، در غیر این صورت یک پروژهٔ اندروید جدید بسازید. 
    • در پوشهٔ src/ روی File>New>Package کلیک کنید و بستهٔ جدیدی با نام  com.android.vending.billing ایجاد کنید.
    • فایل IInAppBillingService.aidlای که در گام قبل گرفتید را در پوشهٔ  src/com.android.vending.billing/ کپی کنید.
  • در Android Studio: پوشه‌ای به نام aidl زیر پوشهٔ src/main ایجاد کنید. بستهٔ جدید 

    com.android.vending.billing را در این پوشه اضافه کنید و فایل IInAppBillingService.aidl را در این بسته import کنید.

  • در دیگر محیط‌های توسعه: پوشهٔ /src/com/android/vending/billing را بسازید و فایل IInAppBillingService.aidl را در این پوشه کپی کنید.

۲. برنامه خود را build کنید. بعد از انجام این کار بایستی فایلی به نام IInAppBillingService.java در پوشهٔ gen/ پروژهٔ خود داشته باشید. دقت کنید که شما این فایل را نمی‌سازید بلکه هنگام build  کردن پروژه‌‌تان این فایل به صورت خودکار ساخته خواهد شد. 

ویرایش فایل manifest برنامه

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

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

<uses-permission android:name="com.farsitel.bazaar.permission.PAY_THROUGH_BAZAAR"></uses-permission>

ساخت ServiceConnection

برنامهٔ شما باید از طریق یک ServiceConnection با بازار ارتباط برقرار کند. باید حداقل موارد زیر را در برنامه‌تان انجام دهید:

  • اتصال به IInAppBillingService.
  • فرستادن درخواست پرداخت به برنامه بازار.
  • مدیریت پیام‌های پاسخی که برای درخواست پرداخت برمی‌گردد.
اتصال به IInAppBillingService

برای برقراری ارتباط با سرویس پرداخت درون‌برنامه‌ای بازار، ServiceConnectionای پیاده‌سازی کنید که activity شما را به IInAppBillingService متصل می‌کند.

متدهای onServiceDisconnected و onServiceConnected را override کنید تا پس از برقراری اتصال نمونه‌ای از IInAppBillingService داشته باشید.

IInAppBillingService mService;

ServiceConnection mServiceConn = new ServiceConnection() {
   @Override
   public void onServiceDisconnected(ComponentName name) {
       mService = null;
   }

   @Override
   public void onServiceConnected(ComponentName name,
      IBinder service) {
       mService = IInAppBillingService.Stub.asInterface(service);
   }
};

در متد onCreate مربوط به activity خود، اتصال را با فراخوانی متد bindService برقرار کنید. به عنوان پارامتر ورودی به این متد، Intentای که به سرویس پرداخت درون‌برنامه‌ای اشاره دارد و نمونه‌ای از ServiceConnectionای که ایجاد کرده‌اید را بدهید. برای مقدار نام بستهٔ مقصد Intent، نام بستهٔ بازار (یعنیcom.farsitel.bazaar) را وارد کنید.

هشدار: همواره برای محافظت از تراکنش‌های پرداخت، مطمئن شوید که نام بستهٔ مقصد Intent را با استفاده از متد setPackage (آنچنان که در مثال زیر آمده است)، به نام بستهٔ بازار سِت کرده‌اید. این کار باعث می‌شود که تنها برنامهٔ بازار بتواند درخواست‌های پرداخت برنامهٔ شما را مدیریت کند و برنامه‌های دیگر نتوانند جلوی این درخواست‌ها را بگیرند. همچنین در صورتی که از API اندروید با نسخهٔ بالاتر از ۲۱ استفاده می‌کنید، حتما می‌بایست نام بسته را برای Intent ست کنید، در غیر این صورت در اندروید ۵ به بالا، سیستم عامل از وصل شدن به سرویس جلوگیری می‌کند.
@Override
public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  Intent serviceIntent = new Intent("ir.cafebazaar.pardakht.InAppBillingService.BIND");
  serviceIntent.setPackage("com.farsitel.bazaar");
  bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);

شما اکنون می‌توانید از mService برای ارتباط با سرویس بازار استفاده کنید.

مهم: به یاد داشته باشید زمانی‌ که activityتان از بین می‌رود اتصال از سرویس پرداخت درون‌برنامه‌ای را قطع کنید. اگر اتصال را قطع نکنید، اتصال باز به سرویس باعث تنزل کارایی دستگاه کاربر می‌شود. مثال زیر طریقهٔ قطع کردن اتصال در سرویس پرداخت درون‌برنامه‌ای با نام mServiceConn را نشان می‌دهد که با override کردن متد onDestroy در activity انجام می‌شود.
@Override
public void onDestroy() {
    super.onDestroy();
    if (mServiceConn != null) {
        unbindService(mServiceConn);
    }   
}

برای دیدن پیاده‌سازی کامل ServiceConnectionای که به IInAppBillingService متصل می‌شود به کلاس آموزشی فروش محصولات درون‌برنامه‌ای و پروژهٔ مثال TrivialDrive مراجعه کنید.

ساخت درخواست پرداخت درون‌برنامه‌ای

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

کوئری برای محصولات قابل خرید

می‌توانید در برنامه‌تان جزئیات محصول را از بازار بپرسید. برای این کار نخست Bundleای بسازید که شامل یک ArrayList از شناسهٔ محصولات با کلید "ITEM_ID_LIST" است.

ArrayList skuList = new ArrayList();
skuList.add("premiumUpgrade");
skuList.add("gas");
Bundle querySkus = new Bundle();
querySkus.putStringArrayList(“ITEM_ID_LIST”, skuList);

برای بازیابی این اطلاعات از بازار، متد getSkuDetails را فراخوانی کنید. ورودی‌های این متد عبارتند از: نسخهٔ API پرداخت درون‌برنامه‌ای ("3")، نام بستهٔ برنامهٔ خود، نوع خرید ("inapp") و Bundleای که پیشتر ساختید.

Bundle skuDetails = mService.getSkuDetails(3, 
   getPackageName(), "inapp", querySkus);

اگر درخواست موفقیت‌آمیز بود، Bundleای که برگردانده می‌شود کد پاسخ 0 یا BILLING_RESPONSE_RESULT_OK دارد.

 

هشدار: متد getSkuDetails را در thread اصلی (thread رابط کاربری) برنامه‌تان فراخوانی نکنید. فراخوانی این متد باعث ایجاد درخواست شبکه‌ای می‌شود که نباید در thread اصلی (thread رابط کاربری) برنامهٔ شما انجام شود و آن را بلوکه کند. در عوض یک thread جداگانه بسازید و متد getSkuDetail را از درون آن فراخوانی کنید.

برای دیدن تمامی‌ کدهای پاسخ بازار مرجع API را مشاهده کنید.
نتایج پرس‌و‌جو در یک ArrayList رشته‌ای با کلید DETAILS_LIST و اطلاعات خرید نیز در یک رشته با فرمت JSON ذخیره شده‌اند. برای دیدن انواع اطلاعات برگردانده شده مربوط به جزئیات محصول بخش مرجع API را مشاهده کنید.
در این مثال، قیمت محصولات درون‌برنامه‌ای خود را از skuDetails (همان Bundleای که در کد قبلی برگردانده شده است) بازیابی می‌کنید.

int response = skuDetails.getInt("RESPONSE_CODE");
if (response == 0) {
   ArrayList responseList
      = skuDetails.getStringArrayList("DETAILS_LIST");
   
   for (String thisResponse : responseList) {
      JSONObject object = new JSONObject(thisResponse);
      String sku = object.getString("productId");
      String price = object.getString("price");
      if (sku.equals("premiumUpgrade")) mPremiumUpgradePrice = price;
      else if (sku.equals("gas")) mGasPrice = price;
   }
}
خرید محصول

برای شروع درخواست خرید، متد getBuyIntent از سرویس پرداخت درون‌برنامه‌ای را فراخوانی کنید. ورودی‌های این متد عبارتند از: نسخه‌‌ٔ API پرداخت درون‌برنامه‌ای ("3")، نام بستهٔ برنامه‌‌تان، شناسهٔ محصول، نوع خرید ("inapp" یا "subs") و رشتهٔ developerPayload. از رشتهٔ developerPayload برای مشخص کردن هرگونه اطلاعات تکمیلی که می‌خواهید بازار به همراه اطلاعات خرید برای شما برگرداند، استفاده می‌شود.

Bundle buyIntentBundle = mService.getBuyIntent(3, getPackageName(),
   sku, "inapp", "bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ");

اگر درخواست موفقیت آمیز بود، Bundle برگشتی‌ کد پاسخ BILLING_RESPONSE_RESULT_OK (یا 0) و PendingIntentای را که برای شروع عملیات خرید می‌توانید از آن استفاده کنید به همراه دارد. برای دیدن تمامی‌ کدهای پاسخ بازار، بخش مرجع API را مشاهده کنید. در ادامه یک PendingIntent از Bundle برگردانده شده با کلید BUY_INTENT استخراج کنید.

 

PendingIntent pendingIntent = buyIntentBundle.getParcelable("BUY_INTENT");

برای تکمیل تراکنش خرید، متد startIntentSenderForResult را فراخوانی کنید و از PendingIntentای که خودتان ساخته‌اید، استفاده کنید. در این مثال شما برای کد درخواست از مقدار دلخواه 1001 استفاده می‌کنید.

startIntentSenderForResult(pendingIntent.getIntentSender(),
   1001, new Intent(), Integer.valueOf(0), Integer.valueOf(0),
   Integer.valueOf(0));

بازار پاسخ PendingIntent را به متد onActivityResult برنامهٔ شما می‌فرستد. متد onActivityResult کد نتیجهٔ (1) Activity.RESULT_OK یا (0) Activity.RESULT_CANCELED را خواهد داشت. برای مشاهدهٔ اطلاعات دیگر سفارش که در Intent برگردانده شده وجود دارد،‌ بخش مرجع API را ببینید. 
به همراه این کد، Intentی خواهد بود که نقش حوالهٔ دیجیتالی خرید شما را خواهد داشت. این حواله در فرمت JSON است و با کلید INAPP_PURCHASE_DATA در Intent پاسخ قرار دارد. به عنوان مثال:

'{
   "orderId":"12999763169054705758.1371079406387615",
   "packageName":"com.example.app",
   "productId":"exampleSku",
   "purchaseTime":1345678900000,
   "purchaseState":0,
   "developerPayload":"bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ",
   "purchaseToken":"opaque-token-up-to-1000-characters"
 }'
توجه: بازار یک توکن برای خرید تولید می‌کند. این توکن دنباله‌ای از کاراکترهای مبهم است و حداکثر طول آن ۱۰۰۰ کاراکتر است. این توکن را به متدهای دیگر پاس کنید، برای مثال زمانی که خرید را مصرف می‌کنید (توضیحات مصرف کردن خرید را ببینید). این توکن را خلاصه و کوتاه نکنید؛‌ کل توکن را باید ذخیره کنید و برگردانید.

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

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
   if (requestCode == 1001) {          
      int responseCode = data.getIntExtra("RESPONSE_CODE", 0);
      String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
      String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");
       
      if (resultCode == RESULT_OK) {
         try {
            JSONObject jo = new JSONObject(purchaseData);
            String sku = jo.getString("productId");
            alert("You have bought the " + sku + ". Excellent choice,
               adventurer!");
          }
          catch (JSONException e) {
             alert("Failed to parse purchase data.");
             e.printStackTrace();
          }
      }
   }
}
پیشنهاد امنیتی: وقتی‌ درخواست خرید را می‌فرستید، یک token رشته‌ای بسازید که به طور مشخص این درخواست خرید را مشخص می‌کند و آن را در developerPayload قرار دهید. می‌توانید از رشته‌هایی که به طور تصادفی ساخته شده‌اند به عنوان token استفاده کنید. وقتی‌ پاسخ درخواست خرید را از بازار دریافت کردید، حتماً امضای داده‌های برگشتی‌، orderId و رشته developerPayload را بررسی کنید. برای امنیت بیشتر، باید این کار را در سِرور امن خود انجام دهید. مطمئن شوید که مقدارorderId منحصر به فرد است و قبلاً آن را پردازش نکرده‌اید و رشتهٔ developerPayload با token ای که قبلًا با درخواست خرید فرستاده بودید، مطابقت دارد.
مهم: برخی اوقات، ممکن است بعد از اینکه کاربر را به سیستم پرداخت درون‌برنامه‌ای منتقل نمودید، Activity برنامهٔ شما به صورت خودکار توسط اندروید بسته شود. با این‌که پس از اتمام کار کاربر با سیستم پرداخت، اندروید به صورت خودکار دوباره Activity برنامهٔ شما را می‌سازد، ولی تغییراتی که شما در Activity مورد نظر داده‌اید از بین خواهد رفت. لذا این اتفاق باعث می‌شود که کاربر خرید را انجام دهد ولی برنامهٔ شما نتواند آن‌ را ثبت کند. این اتفاق روی دستگاه‌های ضعیف اندرویدی به بسیار متداول است و در گوشی‌های قوی هم گاهی اتفاق می‌افتد. برای جلوگیری از این کار، متد onSaveInstanceState را برای Activityای که کاربر را به سیستم پرداخت می‌فرستد پیاده کنید و هر چیزی که می‌خواهید پس از بسته‌ شدن Activity بازیابی کنید، در Bundleای که به عنوان آرگومان به متد گفته شده داده شده است، قرار دهید. پس از بسته شدن Activity و باز شدن مجدد آن توسط اندروید، همین Bundleای که در متد onSaveInstanceState تغییرش دادید، به عنوان آرگومان متد onCreate در Activity گفته شده داده خواهد شد، که می‌توانید اطلاعات ذخیره‌شده‌تان را بازیابی کنید. برای اطلاعات بیشتر به اینجا سر بزنید.
کوئری محصولات خریداری شده

برای بازیابی اطلاعات مربوط به خرید‌های کاربر از برنامه‌‌‌ی شما، متد getPurchases را فراخوانی کنید. پارامترهای ورودی این متد عبارتند از: شمارهٔ نسخهٔ API پرداخت درون برنامه‌ای ("3")، نام بستهٔ برنامه‌تان و نوع خرید ("inapp" یا "subs").

Bundle ownedItems = mService.getPurchases(3, getPackageName(), "inapp", null);

بازار صرفاً خریدهای مربوط به حساب کاربری که در حال حاضر در بازار لاگین است را باز می‌گرداند. اگر درخواست موفقیت‌آمیز باشد، Bundle برگشتی‌ کد پاسخ 0 دارد. همچنین Bundle پاسخ شامل لیستی از شناسهٔ محصولات، لیستی از جزئیات سفارش هر خرید و امضای هر خرید است. 
سرویس پرداخت درون‌ برنامه‌ای برای افزایش کارایی، زمانی که برای اولین بار getPurchases فراخوانی می‎شود حداکثر تا ۱۰۰ محصول را که متعلق به کاربر است باز می‌گرداند. اگر کاربر تعداد زیادی محصول خریداری شده داشته باشد، بازار برای نشان دادن این‌که محصولات بیشتری می‌توانند بازیابی شوند یک token رشته‌ای که به کلید INAPP_CONTINUATION_TOKEN نگاشته شده است را در Bundle پاسخ قرار می‌دهد. سپس برنامهٔ شما می‌تواند مجدداً getPurchases را فراخوانی کند و این token را به عنوان آرگومان به آن بدهد. بازار تا زمانی که تمام محصولاتی که متعلق به کاربر است به برنامهٔ شما فرستاده شود به برگرداندن ‌token‌‌های متوالی در Bundle پاسخ ادامه خواهد داد.
برای کسب اطلاعات بیشتر در مورد داده‌هایی که توسط getPurchases بازگردانده می‌شود، بخش مرجع API را مشاهده کنید. مثال زیر نشان می‌دهد که چگونه شما می‌توانید این داده‌ها را دریافت کنید.

int response = ownedItems.getInt("RESPONSE_CODE");
if (response == 0) {
   ArrayList ownedSkus = 
      ownedItems.getStringArrayList("INAPP_PURCHASE_ITEM_LIST");
   ArrayList purchaseDataList = 
      ownedItems.getStringArrayList("INAPP_PURCHASE_DATA_LIST");
   ArrayList signatureList = 
      ownedItems.getStringArrayList("INAPP_DATA_SIGNATURE");
   String continuationToken = 
      ownedItems.getString("INAPP_CONTINUATION_TOKEN");
   
   for (int i = 0; i < purchaseDataList.size(); ++i) {
      String purchaseData = purchaseDataList.get(i);
      String signature = signatureList.get(i);
      String sku = ownedSkus.get(i);
  
      // do something with this purchase information
      // e.g. display the updated list of products owned by user
   } 

   // if continuationToken != null, call getPurchases again 
   // and pass in the token to retrieve more items
}
مصرف کردن یک خرید

شما می‌توانید از API پرداخت درون‌برنامه‌ای بازار برای پیگیری مالکیت کاربر بر محصولات درون‌برنامه‌ای خریداری شده توسط وی استفاده کنید. وقتی‌ محصول درون‌برنامه‌ای خریداری می‌شود، به عنوان داراییِ «تحت تملک» آن کاربر تلقی‌ شده و نمی‌تواند دوباره توسط آن کاربر خریداری شود. برای اینکه امکان خرید مجدد آن محصول فراهم شود،‌ باید درخواست مصرف آن را به بازار بفرستید.

مهم: تنها خرید مربوط به محصولات مصرفی را مصرف کنید. جهت کسب اطلاعات بیشتر،‌ بخش مفاهیم پایه را ببینید.

چگونگی اجرای مکانیزم مصرف در برنامه‌تان بستگی به خودتان دارد. معمولاً محصولات درون‌برنامه‌ای را مصرف می‌کنید که اثرات موقت دارند و کاربر ممکن است چندین مرتبه آن‌ها را خریداری کند (برای مثال خرید سکه یا ابزار درون بازی). اما محصولاتی که تنها یک‌بار فروخته می‌شوند و اثری دائمی دارند (مانند ارتقا دادن به نسخه کامل برنامه) را مصرف نکنید.
برای ثبت مصرف یک خرید، متد consumePurchase را فراخوانی کنید و مقدار رشتهٔ purchaseToken (که خریدی که قرار است مصرف کنید را مشخص می‌کند) را به عنوان ورودی به آن بدهید. purchaseToken قسمتی از داده‌ای است که در رشتهٔ INAPP_PURCHASE_DATA توسط بازار در جواب یک خرید موفق برگردانده می‌شود. در مثال زیر مصرف محصولی که purchaseToken آن در متغیر token قراد دارد را ثبت می‌کنید.

int response = mService.consumePurchase(3, getPackageName(), token);
هشدار: متد consumePurchase را در thread اصلی (thread رابط کاربری) فراخوانی نکنید. فراخوانی این متد باعث ایجاد درخواستی شبکه‌ای می‌شود که thread اصلی شما (thread رابط کاربری) را بلوکه می‌کند. به جای آن یک thread جداگانه بسازید و متد consumePurchase را از درون آن فراخوانی کنید.

شما تصمیم‌ می‌گیرید که محصولات درون‌برنامه‌ای خریداری شده را به چه شکلی در برنامه‌تان برای کاربر فراهم کنید (به این امر به اصطلاح «تأمین کردن محصول» می‌گوییم) و خود مسئول کنترل و پیگیری آن هستید. برای مثال، در صورتی که کاربر سکه‌‌ی داخل بازی را خریداری کرده، شما باید فهرست اموال بازیکن را با مقدار سکه‌ای که خریده است به‌روز کنید.

پیشنهاد امنیتی: قبل از تأمین محصولات مصرفی در برنامه‌تان، شما باید درخواست مصرف را به بازار فرستاده و پاسخی موفق از این‌که مصرف ثبت شده است، دریافت کرده باشید.

برای فهم بهتر موضوع اینفوگرافیک تهیه شده (شکل ۱) را ببینید.

پیاده‌سازی اشتراک

روند پیاده‌سازی اشتراک مانند روند پیاده‌سازی خرید محصول است با این تفاوت که نوع محصول باید برابر با"subs" باشد. پاسخ و نتیجهٔ خرید دقیقا همانند خرید محصولات درون‌برنامه‌ای، به متد onActivityResult شما ارسال می‌شود.

Bundle bundle = mService.getBuyIntent(3, "com.example.myapp",
   MY_SKU, "subs", developerPayload);

PendingIntent pendingIntent = bundle.getParcelable(RESPONSE_BUY_INTENT);
if (bundle.getInt(RESPONSE_CODE) == BILLING_RESPONSE_RESULT_OK) {
   // Start purchase flow (this brings up the Google Play UI).
   // Result will be delivered through onActivityResult().
   startIntentSenderForResult(pendingIntent, RC_BUY, new Intent(),
       Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0));
}

برای بازیابی اشتراک‌های فعال،‌ بار دیگر از متد getPurchases استفاده کنید با این تفاوت که پارامتر مربوط به نوع محصول را برابر با "subs" قرار دهید.

Bundle activeSubs = mService.getPurchases(3, "com.example.myapp",
                   "subs", continueToken);

این فراخوانی یک Bundle به شما باز می‌گرداند که شامل تمامی اشتراک‌های فعال کاربر است. به محض منقضی شدن اشتراک (در صورت انصراف و یا عدم موجودی کاربر) دیگر در این Bundle برگردانده شده نخواهد آمد.

امن کردن برنامه‌تان

برای کسب اطمینان از بابت عدم دستکاری حوالهٔ خرید داده شده به برنامه‌تان، بازار رشتهٔ JSON ارسالی حاوی جزئیات خرید را امضاء می‌کند. بازار از رمزنگاری نامتقارن برای ساختن این امضاء استفاده می‌کند. به این ترتیب که با استفاده از کلید خصوصی تولید شده در سِرور پرداخت، رشتهٔ مربوطه امضاء می‌شود و این امضاء توسط کلید عمومی‌ای که در بخش «برنامه‌های فروشنده» در پنل پرداخت در اختیار شما قرار گرفته، قابل بررسی است.

توجه: کلید عمومی برای هر برنامه منحصر به فرد است. برای دریافت این کلید در پنل توسعه‌دهندگی‌تان برنامه‌ای را که مدنظرتان است انتخاب کنید. سپس در سربرگ «پرداخت درون‌برنامه‌ای» بر روی کلید RSA کلیک کرده و آن را دریافت کنید.

شما می‌توانید بررسی امضای گفته شده را بر روی برنامه خود انجام دهید، آن‌طور که در پروژهٔ TrivalDrive انجام شده است. ولی چنانچه برنامه‌تان برای ارائهٔ امکاناتش به سِروری امن اتصال پیدا می‌کند، پیشنهاد ما انجام این بررسی بر روی آن سِرور امن است.
برای کسب اطلاعات بیشتر درباره طراحی‌های امنیتی به بخش ملاحظات امنیتی مراجعه کنید.