دمج "مكتبة الفوترة في 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.1.1"

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

Kotlin

dependencies {
    val billing_version = "7.1.1"

    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، استخدِم 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 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()
)

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()، يعرض النظام شاشة الشراء في 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) {
           // Process the purchase as described in the next section.
       }
   } else if (billingResult.responseCode == BillingResponseCode.USER_CANCELED) {
       // Handle an error caused by a user canceling 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) {
            // Process the purchase as described in the next section.
        }
    } else if (billingResult.getResponseCode() == BillingResponseCode.USER_CANCELED) {
        // Handle an error caused by a user canceling the purchase flow.
    } else {
        // Handle any other error codes.
    }
}

تؤدي عملية الشراء الناجحة إلى ظهور شاشة نجاح الشراء على Google Play مشابهة لتلك الموضّحة في الشكل 2.

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

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

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

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

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

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

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

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

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

إرفاق معرّفات المستخدمين

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

رصد عمليات الشراء ومعالجتها

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

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

  1. عند استدعاء onPurchasesUpdated نتيجةً لاستدعاء تطبيقك لصيغة launchBillingFlow (كما هو موضّح في القسم السابق) أو إذا كان تطبيقك قيد التشغيل مع اتصال نشط بـ Billing Library عند إجراء عملية شراء خارج تطبيقك أو اكتمال عملية شراء في انتظار المراجعة. على سبيل المثال، يوافق أحد أفراد العائلة على عملية شراء معلّقة على جهاز آخر.
  2. عندما يستدعي تطبيقك queryPurchasesAsync لطلب معلومات عن عمليات شراء المستخدم

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

بالنسبة إلى الخطأ 2، يجب استدعاء BillingClient.queryPurchasesAsync()‎ لضمان معالجة تطبيقك لجميع عمليات الشراء. ننصحك بتنفيذ ذلك عندما يُنشئ تطبيقك اتصالاً بنجاح مع "مكتبة الفوترة في Google Play" (ننصح بتنفيذ ذلك عند تشغيل تطبيقك أو إظهاره في المقدّمة كما هو موضح في تهيئة BillingClient). يمكن تنفيذ ذلك من خلال استدعاء queryPurchasesAsync عند تلقّي نتيجة ناجحة لمحاولة onServiceConnected. من المهم اتّباع هذا الاقتراح للتعامل مع الأحداث والمواقف، مثل:

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

بعد أن يرصد تطبيقك عملية شراء جديدة أو مكتملة، من المفترض أن ينفِّذ ما يلي:

  • أكِّد عملية الشراء.
  • منح المستخدم المحتوى مقابل عمليات الشراء المكتملة
  • أرسِل إشعارًا إلى المستخدم.
  • إبلاغ Google بأنّ تطبيقك عالج عمليات الشراء المكتملة

تتم مناقشة هذه الخطوات بالتفصيل في الأقسام التالية، متبوعة بأحد الأقسام التي تلخّص جميع الخطوات.

إثبات عملية الشراء

يجب أن يتحقّق تطبيقك دائمًا من عمليات الشراء للتأكّد من شرعيتها قبل منح أي مزايا للمستخدمين. ويمكن إجراء ذلك باتّباع الإرشادات описанة في مقالة التحقّق من عمليات الشراء قبل منح الأذونات. بعد إتمام عملية verifying the purchase (إثبات صحة عملية الشراء)، يجب أن يواصل تطبيقك معالجة عملية الشراء ومنح الأذونات للمستخدم، كما هو موضّح في القسم التالي.

منح الإذن للمستخدم

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

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

إشعار المستخدم

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

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

عند إرسال إشعار إلى المستخدم بشأن عملية شراء، ينصح Google Play باستخدام الآليات التالية:

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

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

إبلاغ Google بأنّه تمت معالجة عملية الشراء

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

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

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

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

Kotlin

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

Java

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

val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
    .setPurchaseToken(purchase.purchaseToken)
val ackPurchaseResult = withContext(Dispatchers.IO) {
     client.acknowledgePurchase(acknowledgePurchaseParams.build())
}

Java

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

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

ملخّص

يعرض مقتطف الرمز التالي ملخّصًا لهذه الخطوات.

Kotlin

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

    // Step 1: Send the purchase to your secure backend to verify the purchase
    // following
    // https://developer.android.com/google/play/billing/security#verify
.
    // Step 2: Update your entitlement storage with the purchase. If purchase is
    // in PENDING state then ensure the entitlement is marked as pending and the
    // user does not receive benefits yet. It is recommended that this step is
    // done on your secure backend and can combine in the API call to your
    // backend in step 1.

    // Step 3: Notify the user using appropriate messaging (delaying
    // notification if needed as discussed above).

    // Step 4: Notify Google the purchase was processed using the steps
    // discussed in the processing purchases section.
}

Java

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

    // Step 1: Send the purchase to your secure backend to verify the purchase
    // following
    // https://developer.android.com/google/play/billing/security#verify

    // Step 2: Update your entitlement storage with the purchase. If purchase is
    // in PENDING state then ensure the entitlement is marked as pending and the
    // user does not receive benefits yet. It is recommended that this step is
    // done on your secure backend and can combine in the API call to your
    // backend in step 1.

    // Step 3: Notify the user using appropriate messaging (delaying
    // notification if needed as discussed above).

    // Step 4: Notify Google the purchase was processed using the steps
    // discussed in the processing purchases section.
}

للتأكّد من أنّ تطبيقك نفَّذ هذه الخطوات بشكلٍ صحيح، يمكنك اتّباع دليل الاختبار.

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

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

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

اتصل بـ enablePendingPurchases() كجزء من بدء استخدام BillingClient لتفعيل المعاملات المعلّقة في تطبيقك. يجب أن يسمح تطبيقك بالمعاملات المعلّقة للمنتجات التي يتم تحصيل سعرها مرة واحدة وأن يتيح استخدامها. قبل إضافة ميزة المساعدة، احرص على فهم رحلة الشراء للمعاملات التي لم تكتمل بعد.

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

إذا كان تطبيقك قيد التشغيل وكان لديك اتصال نشط بـ Play Billing Library عندما يُكمل المستخدم عملية الشراء، تتم دعوة PurchasesUpdatedListener مجددًا، ويصبح PurchaseState هو PURCHASED. في هذه المرحلة، يمكن لتطبيقك معالجة عملية الشراء باستخدام الطريقة العادية لرصد عمليات الشراء ومعالجتها. يجب أن يستدعي تطبيقك أيضًا queryPurchasesAsync() في onResume() لمعالجة عمليات الشراء التي تم نقلها إلى الحالة PURCHASED عندما لم يكن تطبيقك قيد التشغيل.

عند انتقال عملية الشراء من PENDING إلى PURCHASED، يتلقّى العميل real_time_developer_notifications إشعارًا 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. يتلقّى هذا المستمع آخر المعلومات عن جميع طلبات البحث عن billing config التي يتمّ بدؤها من تطبيقك.

إذا لم يتضمّن العنصر 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
        }
      }
    });

تذكيرات بشأن التخلّي عن سلة التسوّق في الصفحة الرئيسية لتطبيق "ألعاب 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" طلبًا لإجراء عملية شراء لطلب تم التخلي عنه سابقًا.