دمج "مكتبة الفوترة في Google Play" في تطبيقك

يوضّح هذا الموضوع كيفية دمج Google Play Billing Library في تطبيقك لبدء بيع المنتجات.

مدة عملية الشراء

في ما يلي مسار الشراء المعتاد لعملية شراء لمرة واحدة أو اشتراك.

  1. اعرض للمستخدم ما يمكنه شراؤه.
  2. ابدأ مسار الشراء ليقبل المستخدم عملية الشراء.
  3. تأكَّد من عملية الشراء على خادمك.
  4. قدِّم المحتوى للمستخدم.
  5. الإقرار بتسليم المحتوى بالنسبة إلى المنتجات الاستهلاكية، يمكنك إكمال عملية الشراء لكي يتمكّن المستخدم من شراء المنتج مرة أخرى.

يتم تجديد الاشتراكات تلقائيًا إلى أن يتم إلغاؤها. يمكن أن يمرّ الاشتراك بالحالات التالية:

  • نشط: يشير ذلك إلى أنّ المستخدم في وضع جيد ويمكنه الوصول إلى الاشتراك.
  • ملغى: يشير هذا الرمز إلى أنّ المستخدم ألغى الاشتراك ولكن لا يزال بإمكانه الوصول إلى التطبيق حتى انتهاء صلاحيته.
  • في فترة السماح: واجه المستخدم مشكلة في الدفع، ولكن لا يزال بإمكانه الوصول أثناء إعادة محاولة استخدام طريقة الدفع من قِبل Google.
  • في انتظار المراجعة: واجه المستخدم مشكلة في الدفع ولم يعُد بإمكانه الوصول إلى الاشتراك بينما تتم محاولة Google إعادة استخدام طريقة الدفع.
  • متوقف مؤقتًا: أوقف المستخدم إذن الوصول مؤقتًا ولا يمكنه الوصول إلى المحتوى إلى أن تتم إعادة تفعيله.
  • منتهي الصلاحية: ألغى المستخدم الاشتراك ولم يعُد بإمكانه الوصول إليه. يُعتبر العميل متوقفًا عن استخدام الخدمة عند انتهاء صلاحية الاشتراك.

بدء عملية الربط بخدمة Google Play

الخطوة الأولى للدمج مع نظام الفوترة في Google Play هي إضافة مكتبة Google Play Billing Library إلى تطبيقك وبدء عملية الربط.

إضافة التبعية لـ Google Play Billing Library

أضِف الاعتمادية التي تستخدمها Google Play Billing Library إلى ملف build.gradle في تطبيقك على النحو الموضّح:

Groovy

dependencies {
    def billing_version = "7.0.0"

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

Kotlin

dependencies {
    val billing_version = "7.0.0"

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

إذا كنت تستخدم Kotlin، تحتوي وحدة KTX في "مكتبة الفوترة في Google Play" على ميزات برمجة Kotlin ودعم سلاسل المهام المتزامنة التي تتيح لك كتابة برمجة Kotlin مألوفة عند استخدام "مكتبة الفوترة في Google Play". لتضمين هذين الامتدادَين في مشروعك، أضِف التبعية التالية إلى ملف build.gradle في تطبيقك كما هو موضّح:

Groovy

dependencies {
    def billing_version = "7.0.0"

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

Kotlin

dependencies {
    val billing_version = "7.0.0"

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

إعداد عميل BillingClient

بعد إضافة تبعية لمجموعة Google Play Billing Library، عليك تهيئة مثيل BillingClient. BillingClient هي الواجهة الرئيسية للتواصل بين "مكتبة الفوترة في Google Play" و بقية أجزاء تطبيقك. توفّر BillingClient طرقًا سهلة الاستخدام، سواء كانت تزامنية أو غير تزامنية، لتنفيذ العديد من عمليات الفوترة الشائعة. ننصحك بشدة بفتح اتصال BillingClient نشط واحد في المرة الواحدة لتجنُّب عمليات استدعاء PurchasesUpdatedListener متعددة لحدث واحد.

لإنشاء BillingClient، استخدِم newBuilder(). يمكنك تمرير أي سياق إلى newBuilder()، ويستخدمه BillingClient للحصول على سياق التطبيق. وهذا يعني أنّه لا داعي للقلق بشأن تسرُّب الذاكرة. لتلقّي آخر المعلومات عن عمليات الشراء، عليك أيضًا الاتصال بـ setListener() وإرسال مرجع إلى PurchasesUpdatedListener. يتلقّى هذا المستمع آخر المعلومات عن كل عمليات الشراء في تطبيقك.

Kotlin

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()

Java

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 قبل إرسال طلبات أخرى.

يوضح المثال التالي كيفية بدء اتصال واختبار مدى جاهزيته للاستخدام:

Kotlin

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.
    }
})

Java

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()، التي تُعلِم المستمع عند انتهاء طلب البحث، كما هو موضّح في المثال التالي:

Kotlin

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
}

Java

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 Console مع 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 Billing Library نتائج طلب البحث في List من عناصر ProductDetails. ويمكنك بعد ذلك استدعاء طرق متنوعة لكل كائن ProductDetails في القائمة لعرض المعلومات ذات الصلة عن منتج داخل التطبيق، مثل سعره أو وصفه. ولعرض معلومات تفاصيل المنتج المتوفرة، اطّلِع على قائمة الطرق في الفئة ProductDetails.

قبل عرض سلعة للبيع، تأكَّد من أنّ المستخدم لا يملك السلعة. إذا كان لدى المستخدم عنصر مستهلك لا يزال في مكتبة العناصر، عليه استخدام العنصر قبل أن يتمكّن من شرائه مرة أخرى.

قبل تقديم اشتراك، تأكَّد من أنّ المستخدم ليس مشتركًا. يُرجى أيضًا مراعاة ما يلي:

  • queryProductDetailsAsync() تعرِض تفاصيل المنتجات المتوفّرة عند الاشتراك و 50 عرضًا كحدّ أقصى لكل اشتراك.
  • لا تعرض queryProductDetailsAsync() سوى العروض التي يكون المستخدم مؤهلاً للاستفادة منها. إذا حاول المستخدم شراء عرض ليس مؤهلاً للاستفادة منه، (على سبيل المثال، إذا كان التطبيق يعرض قائمة قديمة بالعروض المؤهَّلة)، يُعلم Play المستخدم بأنّه غير مؤهَّل، ويمكنه شراء الخطة الأساسية بدلاً من ذلك.

بدء مسار الشراء

لبدء طلب شراء من تطبيقك، استخدِم الطريقة launchBillingFlow() من سلسلة المهام الرئيسية في تطبيقك. تأخذ هذه الطريقة مرجعًا إلى عنصر BillingFlowParams يحتوي على عنصر ProductDetails ذي الصلة الذي تم الحصول عليه من استدعاء queryProductDetailsAsync(). لإنشاء عنصر BillingFlowParams، استخدِم فئة BillingFlowParams.Builder.

Kotlin

// 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)

Java

// 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. احرص على التحقّق من هذه النتيجة للتأكّد من عدم حدوث أي أخطاء عند بدء عملية الشراء. ويشير BillingResponseCodeOK إلى نجاح عملية الإطلاق.

عند إتمام مكالمة ناجحة إلى launchBillingFlow()، يعرض النظام شاشة الشراء في Google Play. يعرض الشكل 1 شاشة شراء اشتراك:

شاشة الشراء في Google Play تعرض اشتراكًا
            متاحًا للشراء
الشكل 1. تعرض شاشة "الشراء على Google Play" اشتراكًا متاحًا للشراء.

يطلب Google Play من onPurchasesUpdated() عرض نتيجة عملية الشراء إلى مستمع ينفّذ واجهة PurchasesUpdatedListener. يتم تحديد المستمع باستخدام الطريقة setListener() عند تهيئة العميل.

يجب تنفيذ onPurchasesUpdated() لمعالجة رموز الاستجابة المحتمَلة. يوضّح المثال التالي كيفية إلغاء onPurchasesUpdated():

Kotlin

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.
   }
}

Java

@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.

شاشة نجاح عملية الشراء في Google Play
الشكل 2. شاشة إتمام عملية الشراء في Google Play

تؤدي عملية الشراء الناجحة أيضًا إلى إنشاء رمز مميّز للشراء، وهو معرّف فريد يمثّل المستخدم ومعرّف المنتج داخل التطبيق الذي اشتراه. يمكن لتطبيقاتك تخزين الرمز المميّز للشراء على الجهاز، ولكننا ننصح بإرسال الرمز المميّز إلى خادم الخلفية الآمن حيث يمكنك بعد ذلك إثبات صحة عملية الشراء والحماية من الاحتيال. يُرجى الاطّلاع على القسم التالي للتعرّف على مزيد من التفاصيل حول هذه العملية.

يتلقّى المستخدم أيضًا إيصالاً للمعاملة عبر البريد الإلكتروني يحتوي على معرّف الطلب أو معرّف فريد للمعاملة. يتلقّى المستخدمون رسالة إلكترونية تتضمّن معرّف طلب فريدًا لكل عملية شراء لمنتج يتم تحصيل سعره مرة واحدة، وكذلك لعملية شراء الاشتراك الأولي والتجديدات التلقائية المتكرّرة اللاحقة. يمكنك استخدام مُعرِّف الطلب لإدارة عمليات ردّ الأموال في Google Play Console.

الإشارة إلى سعر مخصّص

إذا كان بإمكانك توزيع تطبيقك على المستخدمين في الاتحاد الأوروبي، استخدِم setIsOfferPersonalized() لإعلام المستخدمين بأنّه تم تخصيص سعر السلعة باستخدام عملية صنع القرار الآلي.

شاشة الشراء في Google Play تشير إلى أنّه تم تخصيص السعر للمستخدم
الشكل 3. شاشة "الشراء على Google Play" التي تشير إلى أنّ السعر مخصّص للمستخدم

عليك الرجوع إلى Art. 6 (1) (ea) من "توجيه حقوق المستهلك" 2011/83/EU لتحديد ما إذا كان السعر الذي تقدّمه للمستخدمين مخصّصًا أو لا

يقبل setIsOfferPersonalized() إدخالًا منطقيًا. عندما يكون true، يتضمّن واجهة مستخدم Play بيان الإفصاح. عندما يكون false، تحذف واجهة المستخدم بيان الإفصاح. القيمة التلقائية هي false.

يمكنك الانتقال إلى مركز مساعدة المستهلكين للحصول على مزيد من المعلومات.

جارٍ معالجة عمليات الشراء

بعد أن يُكمل المستخدم عملية شراء، يجب أن يعالج تطبيقك هذه العملية. في معظم الحالات، يتم إعلام تطبيقك بعمليات الشراء من خلال PurchasesUpdatedListener. ومع ذلك، في بعض الحالات، سيتم إعلام تطبيقك بعمليات الشراء من خلال استدعاء BillingClient.queryPurchasesAsync() كما هو موضّح في جلب عمليات الشراء.

بالإضافة إلى ذلك، إذا كان لديك عميل إشعارات في الوقت الفعلي للمطوّرين في الخلفية الآمنة، يمكنك تسجيل عمليات الشراء الجديدة من خلال تلقّي subscriptionNotification أو oneTimeProductNotification لإعلامك بحالة عملية شراء جديدة. بعد تلقّي هذه الإشعارات، يمكنك الاتصال بواجهة برمجة تطبيقات Google Play Developer API للحصول على الحالة الكاملة وتعديل حالة الخلفية.

يجب أن يعالج تطبيقك عملية الشراء على النحو التالي:

  1. أكِّد عملية الشراء.
  2. تقديم المحتوى للمستخدم والإقرار بتسليمه يمكنك اختياريًا وضع علامة على العنصر على أنّه مستهلك كي يتمكّن المستخدم من شراء العنصر مجددًا.

للتحقّق من عملية شراء، تأكَّد أولاً من أنّ حالة الشراء هي PURCHASED. إذا كانت عملية الشراء PENDING، عليك معالجة الشراء كما هو موضّح في معالجة المعاملات المعلّقة. بالنسبة إلى عمليات الشراء التي يتم إجراؤها من خلال onPurchasesUpdated() أو queryPurchasesAsync()، يجب التحقّق من عملية الشراء بشكل إضافي لضمان شرعيتها قبل أن يمنح تطبيقك الإذن بالوصول إلى المحتوى. للتعرّف على كيفية التحقّق من عملية الشراء بشكل صحيح، اطّلِع على مقالة التحقّق من عمليات الشراء قبل منح الأذونات.

بعد إثبات عملية الشراء، يصبح تطبيقك جاهزًا لمنح الإذن للمستخدم. يمكن تحديد حساب المستخدم المرتبط بعملية الشراء بقيمة ProductPurchase.obfuscatedExternalAccountId التي أرجعها Purchases.products:get لعمليات شراء المنتجات داخل التطبيق و SubscriptionPurchase.obfuscatedExternalAccountId التي تم إرجاعها بواسطة Purchases.subscriptions:get للاشتراكات من جهة الخادم أو obfuscatedAccountId من Purchase.getAccountIdentifiers() من جانب العميل، في حال ضبط القيمة على setObfuscatedAccountId.

بعد منح الإذن، يجب أن يُبلغك التطبيق بعملية الشراء. تُرسل هذه الرسالة إلى Google Play لإعلامه بأنّك منحت الإذن بإتمام عملية الشراء.

تعتمد عملية منح الإذن وتأكيد عملية الشراء على ما إذا كانت عملية الشراء متعلّقة بمنتج قابل للاستهلاك أو غير قابل للاستهلاك أو اشتراكًا.

المنتجات الاستهلاكية

بالنسبة إلى المستهلكين، إذا كان لتطبيقك خلفية آمنة، ننصحك باستخدام السمة Purchases.products:consume لاستهلاك عمليات الشراء بشكل موثوق. تأكَّد من أنّه لم يتم استخدام عملية الشراء من خلال التحقّق من consumptionState من نتيجة الاتصال برقم Purchases.products:get. إذا كان تطبيقك مخصّصًا للعملاء فقط بدون نظام خلفية، استخدِم consumeAsync() من Google Play Billing Library. تفي كلتا الطريقتين بمتطلبات الإقرار وتشيران إلى أن تطبيقك منح الإذن للمستخدم. وتتيح هذه الطرق أيضًا لتطبيقك إمكانية إتاحة المنتج الذي يتم تحصيل سعره مرة واحدة والمطابق للرمز المميّز للشراء الذي تم إدخاله لإعادة الشراء. باستخدام consumeAsync()، يجب أيضًا تمرير عنصر ينفِّذ واجهة ConsumeResponseListener . يعالج هذا العنصر نتيجة عملية الاستهلاك. يمكنك تجاوز طريقة onConsumeResponse() التي تُطلِقها "مكتبة الفوترة في Play" عند اكتمال العملية.

يوضّح المثال التالي استخدام منتج من خلال مكتبة Google Play Billing Library باستخدام رمز الشراء ذي الصلة:

Kotlin

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)
    }
}

Java

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 Billing Library في تطبيقك. قبل تأكيد عملية شراء، يجب أن يتحقّق تطبيقك ممّا إذا سبق أن تم تأكيدها باستخدام الأسلوب isAcknowledged() في Google Play Billing Library.

يوضّح المثال التالي كيفية الموافقة على عملية شراء باستخدام مكتبة Google Play Billing Library:

Kotlin

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())
            }
        }
     }
}

Java

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() تعرض الاشتراكات النشطة فقط وعمليات الشراء التي تتم لمرة واحدة بدون استهلاكها.

Kotlin

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

Java

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" (مفعَّلة تلقائيًا)

بالنسبة إلى مطوّري الألعاب الذين يحقّقون الربح من خلال عمليات الشراء داخل التطبيق، تُعدّ ميزة "التذكير بترك سلة التسوّق" إحدى الطرق التي يمكن من خلالها بيع رموز التخزين التعريفية النشطة في Google Play Console خارج تطبيقك، إذ تحثّ هذه الميزة المستخدمين على إكمال عمليات الشراء التي لم تكتمل سابقًا أثناء تصفّح "متجر Google Play". تتم عمليات الشراء هذه خارج تطبيقك، من صفحة Google Play Home في "متجر Google Play".

تكون هذه الميزة مفعّلة تلقائيًا لمساعدة المستخدمين في مواصلة القراءة من حيث توقفوا ومساعدتهم على زيادة مبيعاتهم إلى أقصى حدّ. ومع ذلك، يمكنك إيقاف هذه الميزة في تطبيقك من خلال إرسال نموذج إيقاف ميزة تذكير التخلّي عن سلة التسوق. للاطّلاع على أفضل الممارسات المتعلقة بإدارة رموز التخزين التعريفية (SKU) في Google Play Console، يُرجى الاطّلاع على مقالة إنشاء منتج داخل التطبيق.

تعرض الصور التالية تذكيرًا بعدم إكمال عملية الشراء في سلة التسوّق يظهر على متجر Google Play:

تظهر على شاشة &quot;متجر Google Play&quot; علامة
    تطلب إكمال عملية شراء تم التخلي عنها سابقًا
الشكل 2. تعرض شاشة "متجر Google Play" طلبًا لإجراء عملية شراء لطلب تم التخلي عنه سابقًا.

تعرض شاشة &quot;متجر Google Play&quot;
    طلب شراء لعملية شراء تم التخلي عنها
الشكل 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" والإصدارات الأحدث. من المفترض أن يعالج تطبيقك عمليات الشراء المتعددة الكميات ويمنح الإذن بالاستفادة من المحتوى استنادًا إلى كمية الشراء المحدّدة.

لتنفيذ عمليات الشراء متعددة الكميات، يحتاج منطق توفير المتطلبات في تطبيقك إلى التحقق من كمية العناصر. يمكنك الوصول إلى حقل quantity من إحدى واجهات برمجة التطبيقات التالية:

بعد إضافة منطق للتعامل مع عمليات الشراء متعددة الكميات، ستحتاج بعد ذلك إلى تفعيل ميزة متعدد الكميات للمنتج المناسب في صفحة إدارة المنتج داخل التطبيق على Google Play Console.

إرسال طلب بحث إلى إعدادات الفوترة للمستخدم

getBillingConfigAsync() يوفّر البلد الذي يستخدمه المستخدم في Google Play.

يمكنك الاستعلام عن إعدادات الفوترة للمستخدم بعد إنشاء BillingClient. يصف مقتطف الرمز التالي كيفية إجراء اتصال برقم خدمة getBillingConfigAsync(). يمكنك معالجة الردّ من خلال تنفيذ BillingConfigResponseListener. يتلقّى هذا المستمع تعديلات لجميع طلبات البحث عن إعدادات الفوترة التي يتم بدؤها من تطبيقك.

إذا لم يتضمّن العنصر BillingResult الذي تم إرجاعه أي أخطاء، يمكنك بعد ذلك التحقّق من الحقل countryCode في العنصر BillingConfig للحصول على قيمة "بُلدان Google Play" الخاصة بالمستخدم.

Kotlin

// 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
            }
        }
    })

Java

// 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
        }
      }
    });