کتابخانه صورت‌حساب Google Play را در برنامه خود ادغام کنید

این مبحث نحوه ادغام کتابخانه صورت‌حساب Google Play را در برنامه خود برای شروع فروش محصولات توضیح می‌دهد.

عمر یک خرید

در اینجا یک جریان خرید معمولی برای خرید یک بار یا اشتراک وجود دارد.

  1. به کاربر نشان دهید چه چیزی می تواند بخرد.
  2. جریان خرید را راه اندازی کنید تا کاربر خرید را بپذیرد.
  3. خرید را در سرور خود تأیید کنید.
  4. به کاربر محتوا بدهید.
  5. تحویل محتوا را تایید کنید. برای محصولات مصرفی، خرید را مصرف کنید تا کاربر بتواند دوباره کالا را خریداری کند.

اشتراک ها به طور خودکار تمدید می شوند تا زمانی که لغو شوند. یک اشتراک می تواند در حالات زیر باشد:

  • فعال: کاربر در وضعیت خوبی قرار دارد و به اشتراک دسترسی دارد.
  • لغو شده: کاربر لغو کرده است اما هنوز تا انقضا دسترسی دارد.
  • در دوره مهلت: کاربر با مشکل پرداخت مواجه شد اما همچنان دسترسی دارد در حالی که Google روش پرداخت را دوباره امتحان می‌کند.
  • در انتظار: زمانی که Google روش پرداخت را دوباره امتحان می‌کند، کاربر با مشکل پرداخت مواجه شد و دیگر دسترسی ندارد.
  • Paused: کاربر دسترسی خود را متوقف کرده و تا زمانی که از سر گرفته نشود، دسترسی ندارد.
  • منقضی شده: کاربر اشتراک را لغو کرده و دسترسی خود را از دست داده است. کاربر در زمان انقضا خراشیده در نظر گرفته می شود.

اتصال به Google Play را راه اندازی کنید

اولین قدم برای ادغام با سیستم صورت‌حساب Google Play این است که کتابخانه صورت‌حساب Google Play را به برنامه خود اضافه کنید و اتصال را راه‌اندازی کنید.

وابستگی کتابخانه صورت‌حساب Google Play را اضافه کنید

مطابق شکل، وابستگی کتابخانه صورت‌حساب Google Play را به فایل build.gradle برنامه خود اضافه کنید:

شیار

dependencies {
    def billing_version = "7.0.0"

    implementation "com.android.billingclient:billing:$billing_version"
}

کاتلین

dependencies {
    val billing_version = "7.0.0"

    implementation("com.android.billingclient:billing:$billing_version")
}

اگر از Kotlin استفاده می‌کنید، ماژول Google Play Billing Library KTX حاوی برنامه‌های افزودنی و برنامه‌های مشترک Kotlin است که به شما امکان می‌دهد هنگام استفاده از کتابخانه صورت‌حساب Google Play، Kotlin اصطلاحی بنویسید. برای گنجاندن این پسوندها در پروژه خود، مطابق شکل، وابستگی زیر را به فایل build.gradle برنامه خود اضافه کنید:

شیار

dependencies {
    def billing_version = "7.0.0"

    implementation "com.android.billingclient:billing-ktx:$billing_version"
}

کاتلین

dependencies {
    val billing_version = "7.0.0"

    implementation("com.android.billingclient:billing-ktx:$billing_version")
}

یک BillingClient را راه اندازی کنید

هنگامی که یک وابستگی به کتابخانه صورت‌حساب Google Play اضافه کردید، باید یک نمونه BillingClient مقداردهی اولیه کنید. BillingClient رابط اصلی برای ارتباط بین Google Play Billing Library و بقیه برنامه شما است. BillingClient برای بسیاری از عملیات رایج صورت‌حساب، روش‌های راحت، هم همزمان و هم ناهمزمان را فراهم می‌کند. اکیداً توصیه می‌شود که یک اتصال BillingClient فعال در یک زمان باز داشته باشید تا از تماس‌های متعدد PurchasesUpdatedListener برای یک رویداد اجتناب کنید.

برای ایجاد BillingClient ، از newBuilder() استفاده کنید. شما می توانید هر زمینه ای را به newBuilder() منتقل کنید و BillingClient از آن برای دریافت یک زمینه برنامه استفاده می کند. این بدان معناست که لازم نیست نگران نشت حافظه باشید. برای دریافت به‌روزرسانی‌ها در مورد خریدها، باید setListener() نیز فراخوانی کنید و یک مرجع را به PurchasesUpdatedListener ارسال کنید. این شنونده برای همه خریدها در برنامه شما به‌روزرسانی‌ها را دریافت می‌کند.

کاتلین

private val purchasesUpdatedListener =
   PurchasesUpdatedListener { billingResult, purchases ->
       // To be implemented in a later section.
   }

private var billingClient = BillingClient.newBuilder(context)
   .setListener(purchasesUpdatedListener)
   // Configure other settings.
   .build()

جاوا

private PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() {
    @Override
    public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
        // To be implemented in a later section.
    }
};

private BillingClient billingClient = BillingClient.newBuilder(context)
    .setListener(purchasesUpdatedListener)
    // Configure other settings.
    .build();

به Google Play متصل شوید

پس از ایجاد BillingClient ، باید با Google Play ارتباط برقرار کنید.

برای اتصال به Google Play، با startConnection() تماس بگیرید. فرآیند اتصال ناهمزمان است، و شما باید یک BillingClientStateListener پیاده سازی کنید تا پس از تکمیل راه اندازی مشتری و آماده شدن برای درخواست های بیشتر، یک تماس پاسخ دریافت کنید.

همچنین باید منطق تلاش مجدد را برای مدیریت اتصالات از دست رفته به Google Play پیاده سازی کنید. برای پیاده‌سازی منطق امتحان مجدد، روش برگشت تماس onBillingServiceDisconnected() را لغو کنید و مطمئن شوید که BillingClient قبل از درخواست‌های بیشتر، متد startConnection() را برای اتصال مجدد به Google Play فراخوانی می‌کند.

مثال زیر نحوه شروع اتصال و آزمایش آماده استفاده از آن را نشان می دهد:

کاتلین

billingClient.startConnection(object : BillingClientStateListener {
    override fun onBillingSetupFinished(billingResult: BillingResult) {
        if (billingResult.responseCode ==  BillingResponseCode.OK) {
            // The BillingClient is ready. You can query purchases here.
        }
    }
    override fun onBillingServiceDisconnected() {
        // Try to restart the connection on the next request to
        // Google Play by calling the startConnection() method.
    }
})

جاوا

billingClient.startConnection(new BillingClientStateListener() {
    @Override
    public void onBillingSetupFinished(BillingResult billingResult) {
        if (billingResult.getResponseCode() ==  BillingResponseCode.OK) {
            // The BillingClient is ready. You can query purchases here.
        }
    }
    @Override
    public void onBillingServiceDisconnected() {
        // Try to restart the connection on the next request to
        // Google Play by calling the startConnection() method.
    }
});

نمایش محصولات موجود برای خرید

پس از برقراری ارتباط با Google Play، آماده پرس و جو برای محصولات موجود خود و نمایش آنها به کاربران خود هستید.

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

برای پرس و جو برای جزئیات محصول درون برنامه ای، با queryProductDetailsAsync() تماس بگیرید.

برای رسیدگی به نتیجه عملیات ناهمزمان، باید شنونده ای را نیز مشخص کنید که رابط ProductDetailsResponseListener را پیاده سازی کند. سپس می‌توانید onProductDetailsResponse() را نادیده بگیرید، که پس از اتمام کوئری به شنونده اطلاع می‌دهد، همانطور که در مثال زیر نشان داده شده است:

کاتلین

val queryProductDetailsParams =
    QueryProductDetailsParams.newBuilder()
        .setProductList(
            ImmutableList.of(
                Product.newBuilder()
                    .setProductId("product_id_example")
                    .setProductType(ProductType.SUBS)
                    .build()))
        .build()

billingClient.queryProductDetailsAsync(queryProductDetailsParams) {
    billingResult,
    productDetailsList ->
      // check billingResult
      // process returned productDetailsList
}

جاوا

QueryProductDetailsParams queryProductDetailsParams =
    QueryProductDetailsParams.newBuilder()
        .setProductList(
            ImmutableList.of(
                Product.newBuilder()
                    .setProductId("product_id_example")
                    .setProductType(ProductType.SUBS)
                    .build()))
        .build();

billingClient.queryProductDetailsAsync(
    queryProductDetailsParams,
    new ProductDetailsResponseListener() {
        public void onProductDetailsResponse(BillingResult billingResult,
                List<ProductDetails> productDetailsList) {
            // check billingResult
            // process returned productDetailsList
        }
    }
)

هنگام درخواست جزئیات محصول، نمونه‌ای از QueryProductDetailsParams را ارسال کنید که فهرستی از رشته‌های شناسه محصول ایجاد شده در کنسول Google Play را به همراه یک ProductType مشخص می‌کند. ProductType می تواند ProductType.INAPP برای محصولات یک بار مصرف یا ProductType.SUBS برای اشتراک باشد.

پرس و جو با پسوندهای Kotlin

اگر از برنامه‌های افزودنی Kotlin استفاده می‌کنید، می‌توانید با فراخوانی تابع افزونه queryProductDetails() جزئیات محصول درون‌برنامه را جستجو کنید.

queryProductDetails() از کوروتین های Kotlin استفاده می کند تا نیازی به تعریف شنونده جداگانه نداشته باشید. در عوض، تابع تا زمانی که پرس و جو کامل شود، به حالت تعلیق در می آید، پس از آن می توانید نتیجه را پردازش کنید:

suspend fun processPurchases() {
    val productList = listOf(
        QueryProductDetailsParams.Product.newBuilder()
            .setProductId("product_id_example")
            .setProductType(BillingClient.ProductType.SUBS)
            .build()
    )
    val params = QueryProductDetailsParams.newBuilder()
    params.setProductList(productList)

    // leverage queryProductDetails Kotlin extension function
    val productDetailsResult = withContext(Dispatchers.IO) {
        billingClient.queryProductDetails(params.build())
    }

    // Process the result.
}

به ندرت، برخی از دستگاه‌ها نمی‌توانند از ProductDetails و queryProductDetailsAsync() پشتیبانی کنند، معمولاً به دلیل نسخه‌های قدیمی سرویس‌های Google Play . برای اطمینان از پشتیبانی مناسب از این سناریو، نحوه استفاده از ویژگی‌های سازگاری رو به عقب را در راهنمای انتقال Play Billing Library 5 بیاموزید.

نتیجه را پردازش کنید

کتابخانه صورت‌حساب Google Play نتایج جستجو را در List از اشیاء ProductDetails ذخیره می‌کند. سپس می‌توانید روش‌های مختلفی را روی هر شیء ProductDetails در فهرست فراخوانی کنید تا اطلاعات مربوط به محصول درون‌برنامه‌ای مانند قیمت یا توضیحات آن را مشاهده کنید. برای مشاهده اطلاعات جزئیات محصول موجود، به لیست روش ها در کلاس ProductDetails مراجعه کنید.

قبل از ارائه یک کالا برای فروش، بررسی کنید که کاربر قبلاً آن کالا را ندارد. اگر کاربر یک ماده مصرفی دارد که هنوز در کتابخانه اقلام او موجود است، قبل از اینکه بتواند دوباره آن را بخرد باید آن را مصرف کند.

قبل از ارائه اشتراک، بررسی کنید که کاربر قبلاً مشترک نشده باشد. همچنین به موارد زیر توجه کنید:

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

جریان خرید را راه اندازی کنید

برای شروع درخواست خرید از برنامه خود، متد launchBillingFlow() را از رشته اصلی برنامه خود فراخوانی کنید. این روش به یک شی BillingFlowParams اشاره می کند که حاوی شیء ProductDetails مربوطه است که از فراخوانی queryProductDetailsAsync() به دست آمده است. برای ایجاد یک شی BillingFlowParams ، از کلاس BillingFlowParams.Builder استفاده کنید.

کاتلین

// An activity reference from which the billing flow will be launched.
val activity : Activity = ...;

val productDetailsParamsList = listOf(
    BillingFlowParams.ProductDetailsParams.newBuilder()
        // retrieve a value for "productDetails" by calling queryProductDetailsAsync()
        .setProductDetails(productDetails)
        // For One-time product, "setOfferToken" method shouldn't be called.
        // For subscriptions, to get an offer token, call ProductDetails.subscriptionOfferDetails()
        // for a list of offers that are available to the user
        .setOfferToken(selectedOfferToken)
        .build()
)

val billingFlowParams = BillingFlowParams.newBuilder()
    .setProductDetailsParamsList(productDetailsParamsList)
    .build()

// Launch the billing flow
val billingResult = billingClient.launchBillingFlow(activity, billingFlowParams)

جاوا

// An activity reference from which the billing flow will be launched.
Activity activity = ...;

ImmutableList<ProductDetailsParams> productDetailsParamsList =
    ImmutableList.of(
        ProductDetailsParams.newBuilder()
             // retrieve a value for "productDetails" by calling queryProductDetailsAsync()
            .setProductDetails(productDetails)
            // For one-time products, "setOfferToken" method shouldn't be called.
            // For subscriptions, to get an offer token, call
            // ProductDetails.subscriptionOfferDetails() for a list of offers
            // that are available to the user.
            .setOfferToken(selectedOfferToken)
            .build()
    );

BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
    .setProductDetailsParamsList(productDetailsParamsList)
    .build();

// Launch the billing flow
BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams);

متد launchBillingFlow() یکی از چندین کد پاسخ فهرست شده در BillingClient.BillingResponseCode را برمی گرداند. حتماً این نتیجه را بررسی کنید تا مطمئن شوید هیچ خطایی در راه‌اندازی جریان خرید وجود ندارد. یک BillingResponseCode OK نشان دهنده راه اندازی موفقیت آمیز است.

در یک تماس موفقیت آمیز برای launchBillingFlow() ، سیستم صفحه خرید Google Play را نمایش می دهد. شکل 1 یک صفحه خرید برای اشتراک را نشان می دهد:

صفحه خرید google play اشتراکی را نشان می دهد که هست             موجود برای خرید
شکل 1. صفحه خرید Google Play اشتراکی را نشان می دهد که برای خرید در دسترس است.

Google Play onPurchasesUpdated() را فرا می خواند تا نتیجه عملیات خرید را به شنونده ای که رابط PurchasesUpdatedListener پیاده سازی می کند، ارائه دهد. شنونده با استفاده از متد setListener() زمانی که مشتری خود را مقداردهی اولیه می کنید مشخص می شود.

شما باید onPurchasesUpdated() را برای مدیریت کدهای پاسخ احتمالی پیاده سازی کنید. مثال زیر نحوه نادیده گرفتن onPurchasesUpdated() را نشان می دهد:

کاتلین

override fun onPurchasesUpdated(billingResult: BillingResult, purchases: List<Purchase>?) {
   if (billingResult.responseCode == BillingResponseCode.OK && purchases != null) {
       for (purchase in purchases) {
           handlePurchase(purchase)
       }
   } else if (billingResult.responseCode == BillingResponseCode.USER_CANCELED) {
       // Handle an error caused by a user cancelling the purchase flow.
   } else {
       // Handle any other error codes.
   }
}

جاوا

@Override
void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
    if (billingResult.getResponseCode() == BillingResponseCode.OK
        && purchases != null) {
        for (Purchase purchase : purchases) {
            handlePurchase(purchase);
        }
    } else if (billingResult.getResponseCode() == BillingResponseCode.USER_CANCELED) {
        // Handle an error caused by a user cancelling the purchase flow.
    } else {
        // Handle any other error codes.
    }
}

یک خرید موفق یک صفحه موفقیت خرید در Google Play مشابه شکل 2 ایجاد می کند.

صفحه موفقیت خرید گوگل پلی
شکل 2. صفحه موفقیت خرید Google Play.

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

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

قیمت شخصی را مشخص کنید

اگر برنامه شما می‌تواند بین کاربران اتحادیه اروپا توزیع شود، از روش setIsOfferPersonalized() استفاده کنید تا به کاربران نشان دهید که قیمت یک مورد با استفاده از تصمیم‌گیری خودکار شخصی‌سازی شده است.

صفحه خرید Google Play نشان می دهد که قیمت برای کاربر سفارشی شده است.
شکل 3. صفحه خرید Google Play که نشان می دهد قیمت برای کاربر سفارشی شده است.

باید با هنر مشورت کنید. 6 (1) (ea) CRD دستورالعمل حقوق مصرف کننده 2011/83/EU برای تعیین اینکه آیا قیمتی که به کاربران ارائه می دهید شخصی است یا خیر.

setIsOfferPersonalized() یک ورودی بولی می گیرد. وقتی true ، رابط کاربری Play شامل افشا می شود. وقتی false ، UI افشاء را حذف می کند. مقدار پیش فرض false است.

برای اطلاعات بیشتر به مرکز راهنمایی مصرف کننده مراجعه کنید.

پردازش خریدها

هنگامی که کاربر یک خرید را تکمیل کرد، برنامه شما باید آن خرید را پردازش کند. در بیشتر موارد، برنامه شما از خریدها از طریق PurchasesUpdatedListener شما مطلع می شود. با این حال، مواردی وجود دارد که برنامه شما با تماس با BillingClient.queryPurchasesAsync() همانطور که در واکشی خریدها توضیح داده شده است از خریدها مطلع می شود.

علاوه بر این، اگر یک سرویس گیرنده Real Time Developer Notifications در پشتیبان امن خود دارید، می‌توانید با دریافت subscriptionNotification یا oneTimeProductNotification خریدهای جدید را ثبت کنید. پس از دریافت این اعلان‌ها، با Google Play Developer API تماس بگیرید تا وضعیت کامل را دریافت کنید و وضعیت backend خود را به‌روزرسانی کنید.

برنامه شما باید خرید را به روش زیر پردازش کند:

  1. خرید را تایید کنید
  2. به کاربر محتوا بدهید و تحویل محتوا را تایید کنید. در صورت تمایل، کالا را به عنوان مصرف شده علامت گذاری کنید تا کاربر بتواند دوباره کالا را خریداری کند.

برای تأیید خرید، ابتدا بررسی کنید که وضعیت خرید PURCHASED باشد. اگر خرید PENDING است، باید خرید را همانطور که در مدیریت معاملات معلق توضیح داده شده است پردازش کنید. برای خریدهایی که از onPurchasesUpdated() یا queryPurchasesAsync() دریافت می کنید، باید خرید را بیشتر تأیید کنید تا قبل از اعطای حق به برنامه، از قانونی بودن آن اطمینان حاصل کنید. برای آشنایی با نحوه صحیح تأیید خرید، به تأیید خریدها قبل از اعطای حق مراجعه کنید.

هنگامی که خرید را تأیید کردید، برنامه شما آماده است تا به کاربر حق اعطا کند. حساب کاربری مرتبط با خرید را می توان با ProductPurchase.obfuscatedExternalAccountId بازگردانده شده توسط Purchases.products:get خرید obfuscatedAccountId در برنامه و SubscriptionPurchase.obfuscatedExternalAccountId که توسط Purchases.subscriptions:get Purchase.getAccountIdentifiers() در سمت مشتری، اگر هنگام خرید با setObfuscatedAccountId تنظیم شده بود.

پس از اعطای حق، برنامه شما باید خرید را تأیید کند. این تأییدیه به Google Play اعلام می‌کند که شما حق خرید را اعطا کرده‌اید.

فرآیند اعطای حق و تأیید خرید بستگی به این دارد که آیا خرید یک خرید مصرفی، غیر مصرفی یا اشتراک است.

محصولات مصرفی

در مورد مواد مصرفی، اگر برنامه شما دارای پشتوانه امنی است، توصیه می‌کنیم از Purchases.products:consume برای مصرف مطمئن خریدها استفاده کنید. اطمینان حاصل کنید که خرید قبلاً مصرف نشده است، با بررسی consumptionState از نتیجه تماس Purchases.products:get . اگر برنامه شما فقط برای کلاینت و بدون پشتیبان است، از consumeAsync() از کتابخانه صورت‌حساب Google Play استفاده کنید. هر دو روش الزامات تأیید را برآورده می‌کنند و نشان می‌دهند که برنامه شما به کاربر این حق را داده است. این روش‌ها همچنین به برنامه شما امکان می‌دهند محصول یک‌بار مصرف مربوط به رمز خرید ورودی را برای خرید مجدد در دسترس قرار دهد. با consumeAsync() باید یک شی را نیز ارسال کنید که رابط ConsumeResponseListener را پیاده سازی کند. این شیء نتیجه عملیات مصرف را کنترل می کند. می‌توانید روش onConsumeResponse() را که کتابخانه صورت‌حساب Google Play پس از اتمام عملیات فراخوانی می‌کند، لغو کنید.

مثال زیر مصرف یک محصول با کتابخانه صورت‌حساب Google Play را با استفاده از رمز خرید مرتبط نشان می‌دهد:

کاتلین

suspend fun handlePurchase(purchase: Purchase) {
    // Purchase retrieved from BillingClient#queryPurchasesAsync or your PurchasesUpdatedListener.
    val purchase : Purchase = ...;

    // Verify the purchase.
    // Ensure entitlement was not already granted for this purchaseToken.
    // Grant entitlement to the user.

    val consumeParams =
        ConsumeParams.newBuilder()
            .setPurchaseToken(purchase.getPurchaseToken())
            .build()
    val consumeResult = withContext(Dispatchers.IO) {
        client.consumePurchase(consumeParams)
    }
}

جاوا

void handlePurchase(Purchase purchase) {
    // Purchase retrieved from BillingClient#queryPurchasesAsync or your PurchasesUpdatedListener.
    Purchase purchase = ...;

    // Verify the purchase.
    // Ensure entitlement was not already granted for this purchaseToken.
    // Grant entitlement to the user.

    ConsumeParams consumeParams =
        ConsumeParams.newBuilder()
            .setPurchaseToken(purchase.getPurchaseToken())
            .build();

    ConsumeResponseListener listener = new ConsumeResponseListener() {
        @Override
        public void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
            if (billingResult.getResponseCode() == BillingResponseCode.OK) {
                // Handle the success of the consume operation.
            }
        }
    };

    billingClient.consumeAsync(consumeParams, listener);
}

محصولات غیر مصرفی

برای تأیید خریدهای غیرقابل مصرف، اگر برنامه شما دارای پشتوانه ایمن است، توصیه می کنیم از Purchases.products:acknowledge برای تأیید مطمئن خریدها استفاده کنید. اطمینان حاصل کنید که خرید قبلا تأیید نشده است، با بررسی acknowledgementState از نتیجه تماس Purchases.products:get .

اگر برنامه شما فقط برای مشتری است، از BillingClient.acknowledgePurchase() از کتابخانه صورت‌حساب Google Play در برنامه خود استفاده کنید. قبل از تأیید خرید، برنامه شما باید با استفاده از روش isAcknowledged() در کتابخانه صورت‌حساب Google Play بررسی کند که آیا قبلاً تأیید شده است یا خیر.

مثال زیر نحوه تأیید خرید با استفاده از کتابخانه صورت‌حساب Google Play را نشان می‌دهد:

کاتلین

val client: BillingClient = ...
val acknowledgePurchaseResponseListener: AcknowledgePurchaseResponseListener = ...

suspend fun handlePurchase() {
    if (purchase.purchaseState === PurchaseState.PURCHASED) {
        if (!purchase.isAcknowledged) {
            val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
                    .setPurchaseToken(purchase.purchaseToken)
            val ackPurchaseResult = withContext(Dispatchers.IO) {
               client.acknowledgePurchase(acknowledgePurchaseParams.build())
            }
        }
     }
}

جاوا

BillingClient client = ...
AcknowledgePurchaseResponseListener acknowledgePurchaseResponseListener = ...

void handlePurchase(Purchase purchase) {
    if (purchase.getPurchaseState() == PurchaseState.PURCHASED) {
        if (!purchase.isAcknowledged()) {
            AcknowledgePurchaseParams acknowledgePurchaseParams =
                AcknowledgePurchaseParams.newBuilder()
                    .setPurchaseToken(purchase.getPurchaseToken())
                    .build();
            client.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener);
        }
    }
}

اشتراک ها

اشتراک ها به طور مشابه با موارد غیر مصرفی اداره می شوند. در صورت امکان، از Purchases.subscriptions.acknowledge از Google Play Developer API استفاده کنید تا به طور قابل اعتماد خرید را از قسمت پشتیبان امن خود تأیید کنید. با بررسی وضعیت acknowledgementState در منبع خرید از Purchases.subscriptions:get ، اطمینان حاصل کنید که خرید قبلاً تأیید نشده است. در غیر این صورت، می‌توانید با استفاده از BillingClient.acknowledgePurchase() از کتابخانه صورت‌حساب Google Play پس از بررسی isAcknowledged() اشتراک را تأیید کنید. همه خریدهای اشتراک اولیه باید تایید شوند. تمدید اشتراک نیازی به تایید ندارد. برای کسب اطلاعات بیشتر در مورد اینکه چه زمانی باید اشتراک ها تایید شوند، به مبحث فروش اشتراک ها مراجعه کنید.

واکشی خریدها

گوش دادن به خرید به‌روزرسانی‌ها با استفاده از PurchasesUpdatedListener برای اطمینان از اینکه برنامه شما همه خریدها را پردازش می‌کند، کافی نیست. ممکن است برنامه شما از همه خریدهایی که کاربر انجام داده است آگاه نباشد. در اینجا چند سناریو وجود دارد که ممکن است برنامه شما مسیر خود را از دست بدهد یا از خریدها بی اطلاع باشد:

  • مشکلات شبکه در حین خرید : کاربر خرید موفقی انجام می دهد و تأییدیه را از Google دریافت می کند، اما قبل از اینکه دستگاهش از طریق PurchasesUpdatedListener اعلان خرید را دریافت کند، دستگاه او اتصال شبکه را از دست می دهد.
  • چند دستگاه : کاربر یک مورد را در یک دستگاه خریداری می‌کند و پس از تعویض دستگاه انتظار دارد آن مورد را ببیند.
  • رسیدگی به خریدهای خارج از برنامه شما : برخی از خریدها، مانند بازخریدهای تبلیغاتی، می توانند خارج از برنامه شما انجام شوند.

برای رسیدگی به این شرایط، مطمئن شوید که برنامه شما BillingClient.queryPurchasesAsync() را در روش onResume() خود فراخوانی می‌کند تا اطمینان حاصل شود که همه خریدها با موفقیت پردازش می‌شوند، همانطور که در پردازش خریدها توضیح داده شد.

مثال زیر نحوه واکشی برای خریدهای اشتراک کاربر را نشان می دهد. توجه داشته باشید که queryPurchasesAsync() فقط اشتراک های فعال و خریدهای یک بار مصرف نشده را برمی گرداند.

کاتلین

val params = QueryPurchasesParams.newBuilder()
               .setProductType(ProductType.SUBS)

// uses queryPurchasesAsync Kotlin extension function
val purchasesResult = billingClient.queryPurchasesAsync(params.build())

// check purchasesResult.billingResult
// process returned purchasesResult.purchasesList, e.g. display the plans user owns

جاوا

billingClient.queryPurchasesAsync(
    QueryPurchasesParams.newBuilder()
      .setProductType(ProductType.SUBS)
      .build(),
    new PurchasesResponseListener() {
      public void onQueryPurchasesResponse(BillingResult billingResult, List<Purchase> purchases) {
        // check billingResult
        // process returned purchase list, e.g. display the plans user owns

      }
    }
);

خریدهای خارج از برنامه خود را مدیریت کنید

برخی از خریدها ممکن است خارج از برنامه شما اتفاق بیفتد، مانند بازخرید تبلیغات یا یادآوری‌های رها کردن سبد خرید برای خریدهای درون‌برنامه بازی‌های Google Play (IAP). وقتی کاربری خارج از برنامه شما خریدی انجام می دهد، انتظار دارد برنامه شما یک پیام درون برنامه ای را نشان دهد یا از نوعی مکانیسم اعلان استفاده می کند تا به کاربر اطلاع دهد که برنامه خرید را به درستی دریافت و پردازش کرده است. برخی از مکانیسم های قابل قبول عبارتند از:

  • نمایش یک پنجره بازشو درون برنامه
  • پیام را به یک جعبه پیام درون برنامه تحویل دهید، و به وضوح بیان کنید که یک پیام جدید در جعبه پیام درون برنامه وجود دارد.
  • از پیام اعلان سیستم عامل استفاده کنید.

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

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

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

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

یادآوری‌های ترک سبد خرید در صفحه اصلی بازی‌های Google Play (به طور پیش‌فرض فعال است)

برای توسعه‌دهندگان بازی‌هایی که از طریق IAP کسب درآمد می‌کنند، یکی از راه‌هایی که می‌توان واحدهای ذخیره‌سازی سهام (SKU) فعال در کنسول Google Play را خارج از برنامه‌تان فروخت، ویژگی Cart Abandonment Reminder است، که کاربران را وادار می‌کند تا خریدهای قبلی خود را انجام دهند. در حال مرور فروشگاه Google Play این خریدها خارج از برنامه شما، از خانه بازی‌های Google Play در فروشگاه Google Play انجام می‌شود.

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

تصاویر زیر یادآور ترک سبد خرید را نشان می دهد که در فروشگاه Google Play ظاهر می شود:

صفحه فروشگاه Google Play یک را نشان می دهد     درخواست خرید برای خریدی که قبلاً رها شده است
شکل 2. صفحه فروشگاه Google Play یک درخواست خرید برای خریدی که قبلاً رها شده را نشان می دهد.

صفحه فروشگاه Google Play یک را نشان می دهد     درخواست خرید برای خریدی که قبلاً رها شده است
شکل 3. صفحه فروشگاه Google Play یک درخواست خرید برای خریدی که قبلاً رها شده است را نشان می دهد.

رسیدگی به معاملات معلق

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

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

enablePendingPurchases() به عنوان بخشی از مقداردهی اولیه BillingClient برای فعال کردن تراکنش های معلق برای برنامه خود فراخوانی کنید. برنامه شما باید تراکنش های معلق را برای محصولات یکبار مصرف فعال و پشتیبانی کند. قبل از افزودن پشتیبانی، مطمئن شوید که چرخه عمر خرید تراکنش های معلق را درک کرده اید.

وقتی برنامه شما خرید جدیدی دریافت می‌کند، یا از طریق PurchasesUpdatedListener شما یا در نتیجه فراخوانی queryPurchasesAsync() ، از روش getPurchaseState() برای تعیین اینکه آیا وضعیت خرید PURCHASED یا PENDING است استفاده کنید. فقط زمانی باید این حق را بدهید که ایالت PURCHASED باشد.

اگر وقتی کاربر خرید را کامل کرد، برنامه شما اجرا می‌شود، PurchasesUpdatedListener شما دوباره فراخوانی می‌شود و PurchaseState اکنون PURCHASED است. در این مرحله، برنامه شما می‌تواند خرید را با استفاده از روش استاندارد برای پردازش خریدها پردازش کند. برنامه شما همچنین باید queryPurchasesAsync() را در روش onResume() برنامه شما فراخوانی کند تا خریدهایی را انجام دهد که در حالی که برنامه شما در حال اجرا نبود به حالت PURCHASED منتقل شده اند.

وقتی خرید از PENDING به PURCHASED تغییر می‌کند، مشتری اعلان‌های برنامه‌نویس بلادرنگ شما یک اعلان ONE_TIME_PRODUCT_PURCHASED یا SUBSCRIPTION_PURCHASED دریافت می‌کند. اگر خرید لغو شود، یک اخطار ONE_TIME_PRODUCT_CANCELED یا SUBSCRIPTION_PENDING_PURCHASE_CANCELED دریافت می‌کنید. این می تواند اتفاق بیفتد اگر مشتری شما پرداخت را در بازه زمانی لازم انجام ندهد. توجه داشته باشید که همیشه می‌توانید از Google Play Developer API برای بررسی وضعیت فعلی خرید استفاده کنید.

رسیدگی به خریدهای چندتایی

Google Play که در نسخه‌های 4.0 و بالاتر از کتابخانه صورت‌حساب Google Play پشتیبانی می‌شود، به مشتریان امکان می‌دهد با تعیین مقداری از سبد خرید، بیش از یک محصول درون‌برنامه مشابه را در یک تراکنش خریداری کنند. از برنامه شما انتظار می‌رود که خریدهای چندتایی را انجام دهد و بر اساس مقدار خرید مشخص‌شده، حق اعطا کند.

برای احترام به خریدهای چندتایی، منطق تهیه برنامه شما باید مقدار مورد را بررسی کند. می توانید از یکی از API های زیر به فیلد quantity دسترسی داشته باشید:

هنگامی که منطق را برای انجام خریدهای چندتایی اضافه کردید، سپس باید ویژگی چند مقداری را برای محصول مربوطه در صفحه مدیریت محصول درون برنامه ای در کنسول برنامه نویس Google Play فعال کنید.

پیکربندی صورت‌حساب کاربر را جویا شوید

getBillingConfigAsync() کشوری را که کاربر برای Google Play استفاده می کند ارائه می کند.

می‌توانید پیکربندی صورت‌حساب کاربر را پس از ایجاد BillingClient جستجو کنید. قطعه کد زیر نحوه برقراری تماس با getBillingConfigAsync() را توضیح می دهد. پاسخ را با اجرای BillingConfigResponseListener مدیریت کنید. این شنونده به‌روزرسانی‌هایی را برای تمام درخواست‌های پیکربندی صورت‌حساب که از برنامه شما آغاز شده است، دریافت می‌کند.

اگر BillingResult برگشتی حاوی هیچ خطایی نباشد، می‌توانید قسمت countryCode را در شی BillingConfig بررسی کنید تا Play Country کاربر را بدست آورید.

کاتلین

// Use the default GetBillingConfigParams.
val getBillingConfigParams = GetBillingConfigParams.newBuilder().build()
billingClient.getBillingConfigAsync(getBillingConfigParams,
    object : BillingConfigResponseListener {
        override fun onBillingConfigResponse(
            billingResult: BillingResult,
            billingConfig: BillingConfig?
        ) {
            if (billingResult.responseCode == BillingResponseCode.OK
                && billingConfig != null) {
                val countryCode = billingConfig.countryCode
                ...
            } else {
                // TODO: Handle errors
            }
        }
    })

جاوا

// Use the default GetBillingConfigParams.
GetBillingConfigParams getBillingConfigParams = GetBillingConfigParams.newBuilder().build();
billingClient.getBillingConfigAsync(getBillingConfigParams,
    new BillingConfigResponseListener() {
      public void onBillingConfigResponse(
          BillingResult billingResult, BillingConfig billingConfig) {
        if (billingResult.getResponseCode() == BillingResponseCode.OK
            && billingConfig != null) {
            String countryCode = billingConfig.getCountryCode();
            ...
         } else {
            // TODO: Handle errors
        }
      }
    });