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

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

مدة الشراء

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

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

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

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

إعداد الاتصال بـ Google Play

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

إضافة الموارد التابعة لـ 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 Billing Library دعم إضافات Kotlin والكوروتينات التي تمكّنك من كتابة نص اصطلاحي لغة Kotlin عند استخدام Google Play Billing Library. لتضمين هذه الإضافات في مشروعك، فأضِف التبعية التالية إلى ملف واحد (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"، ستحتاج إلى لإعداد مثيل BillingClient. BillingClient هو العنصر الرئيسي واجهة الاتصال بين Google Play Billing Library بقية التطبيق. توفّر ميزة "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 الخدمات: لضمان الدعم المناسب لهذا السيناريو، تعرف على كيفية استخدام ميزات التوافق مع الإصدارات القديمة في نقل البيانات إلى الإصدار 5 من Play Billing Library الدليل.

معالجة النتيجة

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

عند إجراء مكالمة ناجحة إلى launchBillingFlow()، يعرض النظام رمز تشغيل شاشة الشراء يعرض الشكل 1 شاشة شراء لأحد الاشتراكات:

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

يتصل Google Play بـ onPurchasesUpdated() لعرض نتيجة عملية الشراء العملية التي يتم إرسالها إلى مستمع ينفّذ PurchasesUpdatedListener من واجهة pyplot. ويتم تحديد المستمع باستخدام طريقة 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) (على سبيل المثال) تقرير حقوق الطبع والنشر (CRD) من توجيه حقوق المستهلك 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 تلبّي كلتا الطريقتان الإقرار. وتشير إلى أنّ تطبيقك منح المستخدم إذنًا بالوصول إلى البيانات. وتسمح هذه الطرق أيضًا لتطبيقك لجعل المنتج الذي يتم تحصيل سعره مرة واحدة مناسبًا رمز الشراء المُدخل المتاح لإعادة الشراء. معك consumeAsync() يجب أيضًا تمرير كائن ينفذ ConsumeResponseListener من واجهة pyplot. يعالج هذا العنصر نتيجة عملية الاستهلاك. يمكنك إلغاء طريقة onConsumeResponse()، التي يتم إرسال طلبات البيانات إلى Google Play Billing Library عند اكتمال العملية.

يوضّح المثال التالي استهلاك منتج مع 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:

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 Billing Library بعد التحقّق من 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

      }
    }
);

التعامل مع عمليات الشراء التي تتم خارج تطبيقك

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

  • عرض نافذة منبثقة داخل التطبيق
  • أوصل الرسالة إلى مربع رسائل داخل التطبيق، مع الإشارة بوضوح إلى وجود هي رسالة جديدة في مربّع الرسائل داخل التطبيق.
  • استخدام رسالة إشعار نظام التشغيل

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

يجب رصد عمليات الشراء بغض النظر عن الولاية التي يعمل فيها التطبيق. تم إجراء عملية الشراء. ومع ذلك، هناك بعض الاستثناءات التي قد تكون مقبولة. عدم إبلاغ المستخدم على الفور باستلام المنتج مثلاً:

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

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

التعامل مع المعاملات المعلّقة

يتيح 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 للتحقق من حالة عملية الشراء.

التعامل مع عمليات الشراء بكميات متعددة

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

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

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

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

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

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

إذا لم تتضمّن قيمة BillingResult المعروضة أي أخطاء، يمكنك بعد ذلك التحقّق من الحقل countryCode في العنصر BillingConfig للحصول على حساب المستخدم على 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
        }
      }
    });