دمج "مكتبة الفوترة في 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 في تطبيقك كما هو موضّح:

رائع

dependencies {
    def billing_version = "8.0.0"

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

Kotlin

dependencies {
    val billing_version = "8.0.0"

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

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

رائع

dependencies {
    def billing_version = "8.0.0"

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

Kotlin

dependencies {
    val billing_version = "8.0.0"

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

إعداد BillingClient

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

  • ننصحك بإبقاء عملية ربط BillingClient واحدة نشطة في كل مرة لتجنُّب تلقّي إشعارات PurchasesUpdatedListener متعددة لحدث واحد.
  • ننصحك ببدء عملية ربط لـ BillingClient عند تشغيل تطبيقك أو ظهوره في المقدّمة لضمان معالجة عمليات الشراء في تطبيقك في الوقت المناسب. ويمكن تحقيق ذلك باستخدام ActivityLifecycleCallbacks المسجَّل بواسطة registerActivityLifecycleCallbacks والاستماع إلى onActivityResumed لبدء اتصال عند رصد نشاط يتم استئنافه لأول مرة. يُرجى الرجوع إلى قسم معالجة عمليات الشراء للحصول على مزيد من التفاصيل حول أهمية اتّباع أفضل الممارسات هذه. يُرجى أيضًا تذكُّر إنهاء الاتصال عند إغلاق تطبيقك.

لإنشاء 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.
    }
});

إعادة إنشاء اتصال تلقائيًا

مع طرح طريقة enableAutoServiceReconnection() في BillingClient.Builder في الإصدار 8.0.0، يمكن لمكتبة Play Billing Library الآن إعادة إنشاء اتصال الخدمة تلقائيًا في حال إجراء طلب من واجهة برمجة التطبيقات أثناء انقطاع الاتصال بالخدمة. ويمكن أن يؤدي ذلك إلى انخفاض في عدد الردود SERVICE_DISCONNECTED لأنّ إعادة الاتصال تتم داخليًا قبل إجراء طلب البيانات من واجهة برمجة التطبيقات.

كيفية تفعيل ميزة إعادة الاتصال التلقائي

عند إنشاء مثيل BillingClient، استخدِم طريقة enableAutoServiceReconnection() في BillingClient.Builder لتفعيل إعادة الاتصال التلقائي.

Kotlin

val billingClient = BillingClient.newBuilder(context)
    .setListener(listener)
    .enablePendingPurchases()
    .enableAutoServiceReconnection() // Add this line to enable reconnection
    .build()

Java

BillingClient billingClient = BillingClient.newBuilder(context)
    .setListener(listener)
    .enablePendingPurchases()
    .enableAutoServiceReconnection() // Add this line to enable reconnection
    .build();

عرض المنتجات المتاحة للشراء

بعد إنشاء اتصال بـ 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,
    queryProductDetailsResult ->
      if (billingResult.getResponseCode() == BillingResponseCode.OK) {
               for (ProductDetails productDetails : queryProductDetailsResult.getProductDetailsList()) {
                 // Process successfully retrieved product details here.
               }

               for (UnfetchedProduct unfetchedProduct : queryproductDetailsResult.getUnfetchedProductList()) {
                 // Handle any unfetched products as appropriate.
               }
            }
}

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,
                QueryProductDetailsResult queryProductDetailsResult) {
            if (billingResult.getResponseCode() == BillingResponseCode.OK) {
               for (ProductDetails productDetails : queryProductDetailsResult().getProductDetailsList()) {
                 // Process success retrieved product details here.
               }

               for (UnfetchedProduct unfetchedProduct : queryproductDetailsResult.getUnfetchedProductList()) {
                 // Handle any unfetched products as appropriate.
               }
            }
        }
    }
)

عند طلب تفاصيل المنتج، مرِّر مثيلاً من 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. لتقديم الدعم المناسب لهذا السيناريو، تعرَّف على كيفية استخدام ميزات التوافق مع الإصدارات القديمة في دليل نقل البيانات إلى الإصدار 7 من Play Billing Library.

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

تخزِّن Google Play Billing Library نتائج طلب البحث في عنصر QueryProductDetailsResult. يحتوي QueryProductDetailsResult على List من عناصر ProductDetails. يمكنك بعد ذلك استدعاء مجموعة متنوعة من الطرق على كل عنصر ProductDetails في القائمة لعرض المعلومات ذات الصلة حول منتج تم جلبه بنجاح لمرة واحدة، مثل سعره أو وصفه. للاطّلاع على معلومات تفاصيل المنتج المتاحة، راجِع قائمة الطرق في فئة ProductDetails.

يحتوي QueryProductDetailsResult أيضًا على List من عناصر UnfetchedProduct. يمكنك بعد ذلك طلب كل UnfetchedProduct للحصول على رمز حالة يتوافق مع سبب تعذُّر الجلب. للاطّلاع على معلومات المنتجات غير التي تم جلبها، راجِع قائمة الطرق في فئة UnfetchedProduct.

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

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

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

  • بالنسبة إلى المنتجات التي يتم تحصيل سعرها مرة واحدة، لا تعرض طريقة 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)
        // Get the offer token:
        // a. For one-time products, call ProductDetails.getOneTimePurchaseOfferDetailsList()
        // for a list of offers that are available to the user.
        // b. For subscriptions, 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)
            // Get the offer token:
            // a. For one-time products, call ProductDetails.getOneTimePurchaseOfferDetailsList()
            // for a list of offers that are available to the user.
            // b. For subscriptions, 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) CRD من "توجيه حقوق المستهلك" ‎2011/83/EU لمعرفة ما إذا كان السعر الذي يُعرض للمستخدمين مُخصّصًا أو لا.

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

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

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

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

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

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

يرصد تطبيقك عمليات الشراء الجديدة وعمليات الشراء المعلّقة المكتملة بإحدى الطرق التالية:

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

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

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

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

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

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

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

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

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

منح المستخدم إذن الوصول إلى المحتوى

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

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

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

لإتاحة عمليات الشراء بكميات متعددة، يجب أن تتحقّق منطق توفير تطبيقك من كمية المنتج. يمكنك الوصول إلى حقل 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
        }
      }
    });

تذكيرات بشأن التخلّي عن سلة التسوّق في الصفحة الرئيسية لبرنامج "ألعاب Google Play" (مفعّلة تلقائيًا)

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

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

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

تعرض شاشة &quot;متجر Google Play&quot; طلبًا بإكمال عملية شراء تم إلغاؤها سابقًا
الشكل 2. تعرض شاشة &quot;متجر Google Play&quot; طلبًا بإكمال عملية شراء تم إيقافها مؤقتًا.

تعرض شاشة &quot;متجر Google Play&quot; طلبًا بإكمال عملية شراء تم إلغاؤها سابقًا
الشكل 3. تعرض شاشة &quot;متجر Google Play&quot; طلبًا بإكمال عملية شراء تم إيقافها مؤقتًا.