يوضّح هذا الموضوع كيفية دمج Google Play Billing Library في تطبيقك لبدء بيع المنتجات.
مدة الشراء
في ما يلي خطوات الشراء المعتادة لعملية شراء لمرة واحدة أو اشتراك.
- أظهر للمستخدم ما يمكنه شراؤه.
- ابدأ مسار الشراء للمستخدم لقبول عملية الشراء.
- أثبِت عملية الشراء على خادمك.
- تقديم المحتوى للمستخدم
- الإقرار بإرسال المحتوى بالنسبة للمنتجات الاستهلاكية، استهلك عملية الشراء حتى يتمكن المستخدم من شراء العنصر مرة أخرى.
يتم تجديد الاشتراكات تلقائيًا إلى أن يتم إلغاؤها. يمكن أن يمر الاشتراك في الحالات التالية:
- نشِط: أنّ المستخدم في وضع جيد ويمكنه الوصول إلى الاشتراك.
- تم الإلغاء: ألغى المستخدم الاشتراك، ولكنه لا يزال بإمكانه الوصول إلى أن تنتهي صلاحيته.
- في فترة السماح: واجه المستخدم مشكلة في الدفع، ولكن لا يزال بإمكانه الوصول أثناء إعادة محاولة استخدام طريقة الدفع من خلال Google.
- معلّق: واجه المستخدم مشكلة في الدفع ولم يعُد بإمكانه الوصول إلى الحساب بينما تعيد Google محاولة استخدام طريقة الدفع.
- متوقف مؤقتًا: أوقف المستخدم مؤقتًا إمكانية وصوله ولا يملك إذن الوصول إلى أن يستأنف.
- منتهي الصلاحية: ألغى المستخدم الاشتراك وفقد إمكانية الوصول إليه. يُعتبَر المستخدم متوقفًا عن الاستخدام عند انتهاء الصلاحية.
تهيئة الاتصال بـ Google Play
الخطوة الأولى لدمج خدمة الفوترة مع نظام الفوترة في Google Play هي إضافة Google Play Billing Library إلى تطبيقك وإعداد عملية الربط.
إضافة تبعية Google Play Billing Library
أضِف تبعية Google Play Billing Library إلى ملف build.gradle
لتطبيقك على النحو الموضّح:
رائع
dependencies { def billing_version = "7.0.0" implementation "com.android.billingclient:billing:$billing_version" }
Kotlin
dependencies { val billing_version = "7.0.0" implementation("com.android.billingclient:billing:$billing_version") }
وإذا كنت تستخدم لغة البرمجة Kotlin، فإنّ وحدة KTX لمكتبة الفوترة في Google Play تحتوي على
إضافات وأدوات كورروتينية في لغة Kotlin تتيح لك كتابة لغة اصطلاحية
عند استخدام Google Play Billing Library. لتضمين هذه الإضافات في مشروعك، أضِف التبعية التالية إلى ملف build.gradle
لتطبيقك كما هو موضّح:
رائع
dependencies { def billing_version = "7.0.0" implementation "com.android.billingclient:billing-ktx:$billing_version" }
Kotlin
dependencies { val billing_version = "7.0.0" implementation("com.android.billingclient:billing-ktx:$billing_version") }
إعداد BillingClient
بعد إضافة تبعية إلى Google Play Billing Library، عليك إعداد مثيل BillingClient
. إنّ BillingClient
هي الواجهة الرئيسية للتواصل بين "مكتبة الفوترة في Google Play" وبقية تطبيقك. يوفّر BillingClient
طرقًا ملائمة، متزامنة وغير متزامنة، للعديد من عمليات الفوترة الشائعة. ننصحك بشدة
بفتح اتصال BillingClient
واحد نشط في الوقت نفسه
لتجنُّب تكرار عمليات استدعاء PurchasesUpdatedListener
لحدث واحد.
لإنشاء BillingClient
، استخدِم newBuilder()
. يمكنك إضافة أي سياق إلى newBuilder()
، ويستخدمه
BillingClient
للحصول على سياق التطبيق.
يعني ذلك أنّه لا داعي للقلق بشأن تسرُّب الذاكرة. لتلقّي آخر الأخبار حول
عمليات الشراء، يجب أيضًا الاتصال على الرقم setListener()
، مع إدراج إشارة مرجعية إلى PurchasesUpdatedListener
. يتلقّى هذا المستمع تحديثات لجميع
عمليات الشراء في تطبيقك.
Kotlin
private val purchasesUpdatedListener = PurchasesUpdatedListener { billingResult, purchases -> // To be implemented in a later section. } private var billingClient = BillingClient.newBuilder(context) .setListener(purchasesUpdatedListener) // Configure other settings. .build()
Java
private PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() { @Override public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) { // To be implemented in a later section. } }; private BillingClient billingClient = BillingClient.newBuilder(context) .setListener(purchasesUpdatedListener) // Configure other settings. .build();
الربط بمتجر Google Play
بعد إنشاء BillingClient
، يجب الربط
بGoogle Play.
للربط بـ Google Play، يمكنك الاتصال بالرقم startConnection()
. إنّ عملية الربط غير متزامنة، وعليك تنفيذ BillingClientStateListener
لتلقّي معاودة الاتصال بعد اكتمال إعداد العميل وعندما يصبح جاهزًا لتقديم المزيد من الطلبات.
يجب أيضًا تنفيذ منطق إعادة المحاولة لمعالجة عمليات الاتصال المفقودة إلى Google Play.
لتنفيذ منطق إعادة المحاولة، عليك إلغاء طريقة معاودة الاتصال في onBillingServiceDisconnected()
والتأكّد من أنّ BillingClient
يستدعي طريقة
startConnection()
لإعادة الاتصال بمتجر Google Play قبل إرسال
طلبات أخرى.
يوضح المثال التالي كيفية بدء اتصال واختبار أنه جاهز للاستخدام:
Kotlin
billingClient.startConnection(object : BillingClientStateListener { override fun onBillingSetupFinished(billingResult: BillingResult) { if (billingResult.responseCode == BillingResponseCode.OK) { // The BillingClient is ready. You can query purchases here. } } override fun onBillingServiceDisconnected() { // Try to restart the connection on the next request to // Google Play by calling the startConnection() method. } })
Java
billingClient.startConnection(new BillingClientStateListener() { @Override public void onBillingSetupFinished(BillingResult billingResult) { if (billingResult.getResponseCode() == BillingResponseCode.OK) { // The BillingClient is ready. You can query purchases here. } } @Override public void onBillingServiceDisconnected() { // Try to restart the connection on the next request to // Google Play by calling the startConnection() method. } });
عرض المنتجات المتاحة للشراء
بعد الربط بـ Google Play، تكون على استعداد للاستعلام عن المنتجات المتاحة وعرضها للمستخدمين.
يُعدّ طلب البحث عن تفاصيل المنتج خطوة مهمة قبل عرض منتجاتك للمستخدمين، إذ إنّه يعرض معلومات مترجَمة عن المنتجات. بالنسبة إلى الاشتراكات، تأكَّد من أنّ عرض منتجك يتوافق مع جميع سياسات Play.
لطلب تفاصيل عن منتج داخل التطبيق، يمكنك الاتصال بالرقم queryProductDetailsAsync()
.
لمعالجة نتيجة العملية غير المتزامنة، يجب أيضًا تحديد
مستمع ينفذ واجهة ProductDetailsResponseListener
.
يمكنك بعد ذلك إلغاء السمة onProductDetailsResponse()
التي ترسل إشعارًا إلى المستمع
عند انتهاء طلب البحث، كما هو موضّح في المثال التالي:
Kotlin
val queryProductDetailsParams = QueryProductDetailsParams.newBuilder() .setProductList( ImmutableList.of( Product.newBuilder() .setProductId("product_id_example") .setProductType(ProductType.SUBS) .build())) .build() billingClient.queryProductDetailsAsync(queryProductDetailsParams) { billingResult, productDetailsList -> // check billingResult // process returned productDetailsList }
Java
QueryProductDetailsParams queryProductDetailsParams = QueryProductDetailsParams.newBuilder() .setProductList( ImmutableList.of( Product.newBuilder() .setProductId("product_id_example") .setProductType(ProductType.SUBS) .build())) .build(); billingClient.queryProductDetailsAsync( queryProductDetailsParams, new ProductDetailsResponseListener() { public void onProductDetailsResponse(BillingResult billingResult, List<ProductDetails> productDetailsList) { // check billingResult // process returned productDetailsList } } )
عند إجراء طلب بحث عن تفاصيل منتج، مرِّر مثيل QueryProductDetailsParams
الذي يحدد قائمة بسلاسل معرّفات المنتج
التي تم إنشاؤها في Google Play Console مع ProductType
. يمكن أن تكون القيمة
ProductType
إما ProductType.INAPP
للمنتجات التي يتم تحصيل سعرها مرة واحدة أو
ProductType.SUBS
للاشتراكات.
الاستعلام باستخدام إضافات Kotlin
إذا كنت تستخدم إضافات Kotlin، يمكنك طلب البحث عن تفاصيل المنتج داخل التطبيق
من خلال استدعاء وظيفة الإضافة queryProductDetails()
.
يستفيد queryProductDetails()
من الكوروتينات في لغة Kotlin بحيث لا تحتاج إلى تحديد مستمع منفصل. بدلاً من ذلك، يتم تعليق الدالة حتى يكتمل الاستعلام،
وبعد ذلك يمكنك معالجة النتيجة:
suspend fun processPurchases() {
val productList = listOf(
QueryProductDetailsParams.Product.newBuilder()
.setProductId("product_id_example")
.setProductType(BillingClient.ProductType.SUBS)
.build()
)
val params = QueryProductDetailsParams.newBuilder()
params.setProductList(productList)
// leverage queryProductDetails Kotlin extension function
val productDetailsResult = withContext(Dispatchers.IO) {
billingClient.queryProductDetails(params.build())
}
// Process the result.
}
نادرًا ما تكون بعض الأجهزة غير متوافقة مع ProductDetails
وqueryProductDetailsAsync()
، ويرجع ذلك عادةً إلى إصدارات قديمة من خدمات Google Play. لضمان تقديم الدعم المناسب لهذا السيناريو، تعرَّف على كيفية استخدام
ميزات التوافق مع الأنظمة القديمة في دليل نقل البيانات للإصدار 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()
، يعرض النظام شاشة الشراء في Google Play. يوضح الشكل 1 شاشة شراء لأحد الاشتراكات:
يستدعي Google Play onPurchasesUpdated()
لعرض نتيجة عملية الشراء على مستمع يستخدم واجهة PurchasesUpdatedListener
. يتم تحديد أداة المستمع باستخدام طريقة setListener()
عند
إعداد برنامجك.
يجب تنفيذ onPurchasesUpdated()
لمعالجة رموز الاستجابة المحتملة. يعرض
المثال التالي كيفية إلغاء السمة onPurchasesUpdated()
:
Kotlin
override fun onPurchasesUpdated(billingResult: BillingResult, purchases: List<Purchase>?) { if (billingResult.responseCode == BillingResponseCode.OK && purchases != null) { for (purchase in purchases) { handlePurchase(purchase) } } else if (billingResult.responseCode == BillingResponseCode.USER_CANCELED) { // Handle an error caused by a user cancelling the purchase flow. } else { // Handle any other error codes. } }
Java
@Override void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) { if (billingResult.getResponseCode() == BillingResponseCode.OK && purchases != null) { for (Purchase purchase : purchases) { handlePurchase(purchase); } } else if (billingResult.getResponseCode() == BillingResponseCode.USER_CANCELED) { // Handle an error caused by a user cancelling the purchase flow. } else { // Handle any other error codes. } }
تؤدي عملية الشراء الناجحة إلى إنشاء شاشة نجاح عملية شراء على Google Play على غرار الشكل 2.
تؤدي عملية الشراء الناجحة أيضًا إلى إنشاء رمز مميّز للشراء، وهو معرّف فريد يمثّل المستخدم ومعرّف المنتج للمنتج داخل التطبيق الذي اشتراه. يمكن لتطبيقاتك تخزين الرمز المميّز للشراء محليًا، ولكن ننصحك بتمرير هذا الرمز إلى خادم الخلفية الآمنة حيث يمكنك التحقّق من عملية الشراء والحماية من الاحتيال. يتم توضيح هذه العملية بمزيد من الشرح في القسم التالي.
يتم أيضًا إرسال إيصال بالمعاملة عبر البريد الإلكتروني إلى المستخدم يحتوي على مُعرّف الطلب أو معرِّف فريد للمعاملة. يتلقى المستخدمون رسالة بريد إلكتروني بمعرف طلب فريد لكل عملية شراء منتج لمرة واحدة، وأيضًا لشراء الاشتراك الأولي وعمليات التجديد التلقائي المتكررة اللاحقة. يمكنك استخدام مُعرّف الطلب لإدارة عمليات ردّ الأموال في Google Play Console.
تحديد سعر مخصّص
إذا كان من الممكن توزيع تطبيقك على المستخدمين في الاتحاد الأوروبي، يُرجى استخدام
طريقة setIsOfferPersonalized()
لإعلام المستخدمين بأنّه تم تخصيص سعر السلعة
من خلال عمليات اتّخاذ القرارات المبرمَجة.
يجب الرجوع إلى Art. 6 (1) (أ) الوصول إلى الكمبيوتر المكتبي عن بُعد (CRD) في توجيه حقوق المستهلك 2011/83/EU لتحديد ما إذا كان السعر الذي تقدّمه للمستخدمين مخصّصًا
تستخدم setIsOfferPersonalized()
إدخالاً منطقيًا. عند true
، تتضمّن واجهة مستخدم
Play بيان الإفصاح. عند false
، تحذِف واجهة المستخدم بيان الإفصاح. القيمة التلقائية هي false
.
اطّلِع على مركز مساعدة المستهلكين للحصول على مزيد من المعلومات.
جارٍ معالجة عمليات الشراء
وبعد أن يُكمل المستخدم عملية شراء، يحتاج تطبيقك إلى معالجة عملية الشراء هذه.
في معظم الحالات، يتم إرسال إشعار إلى تطبيقك بعمليات الشراء من خلال
PurchasesUpdatedListener
. ومع ذلك، هناك حالات يتم فيها تعريف تطبيقك على
عمليات الشراء من خلال استدعاء BillingClient.queryPurchasesAsync()
كما هو موضح في جلب عمليات الشراء.
بالإضافة إلى ذلك، إذا كان لديك برنامج إشعارات في الوقت الفعلي خاصة بالمطوّرين في الخلفية الآمنة، يمكنك تسجيل عمليات الشراء الجديدة من خلال تلقّي
subscriptionNotification
أو oneTimeProductNotification
لتنبيهك
بعملية شراء جديدة. بعد تلقّي هذه الإشعارات، يمكنك طلب بيانات من واجهة برمجة التطبيقات Google Play
Developer API للحصول على الحالة الكاملة وتعديل حالة الخلفية.
يجب أن يعالج تطبيقك عملية الشراء على النحو التالي:
- أثبِت عملية الشراء.
- تقديم المحتوى للمستخدم، والإقرار بتسليمه اختياريًا، ضَع علامة على السلعة للإشارة إلى أنّها مستهلِكة حتى يتمكّن المستخدم من شرائها مرة أخرى.
لتأكيد عملية شراء، تأكَّد أولاً من أنّ حالة الشراء هي
PURCHASED
. إذا كانت عملية الشراء PENDING
، عليك معالجة عملية الشراء
على النحو الموضّح في المقالة معالجة المعاملات المعلّقة. بالنسبة إلى عمليات الشراء
التي تتلقّاها من onPurchasesUpdated()
أو queryPurchasesAsync()
، عليك
إثبات صحة عملية الشراء قبل أن يمنحها التطبيق الأذونات. لمعرفة كيفية التحقّق من عملية شراء بشكل صحيح، يُرجى الاطّلاع على التحقّق من عمليات الشراء
قبل منح الاستحقاقات.
بعد التحقُّق من عملية الشراء، يصبح تطبيقك جاهزًا لمنح الاستحقاق
للمستخدم. يمكن التعرّف على حساب المستخدم المرتبط بعملية الشراء بقيمة
ProductPurchase.obfuscatedExternalAccountId
التي تم إرجاعها من خلال
Purchases.products:get
لعمليات شراء المنتجات داخل التطبيق و
SubscriptionPurchase.obfuscatedExternalAccountId
التي تم إرجاعها من خلال
Purchases.subscriptions:get
للاشتراكات من جهة الخادم، أو
obfuscatedAccountId
من Purchase.getAccountIdentifiers()
من جهة
العميل، في حال ضبط الحساب باستخدام setObfuscatedAccountId
Purchases.subscriptions:get
للاشتراكات من جهة الخادم.
بعد منح الإذن، يجب أن يوافق تطبيقك على عملية الشراء. يشير هذا الإقرار إلى Google Play بأنّك قد منحت الإذن لعملية الشراء.
عملية منح الإذن والإقرار بعملية الشراء تعتمد على ما إذا كانت عملية الشراء قابلة للاستهلاك أو غير قابلة للاستهلاك أو اشتراكًا.
المنتجات الاستهلاكية
بالنسبة إلى الأجهزة الاستهلاكية، إذا كان تطبيقك يحتوي على خلفية آمنة، ننصحك باستخدام Purchases.products:consume
لتلقّي عمليات الشراء بشكل موثوق. تأكَّد من أنّ عملية الشراء لم يتم استهلاكها من قبل، وذلك من خلال الاطّلاع على consumptionState
من نتيجة طلب الرقم Purchases.products:get
. إذا كان تطبيقك مخصّصًا للبرنامج فقط بدون خلفية، استخدِم consumeAsync()
من
Google Play Billing Library. وتستوفي كلتا الطريقتين شرط الإقرار
وتشيران إلى أنّ تطبيقك قد منح المستخدم الإذن بالوصول إليه.
تؤدّي هذه الطرق أيضًا إلى تمكين تطبيقك من إتاحة المنتج الذي يتم استخدامه مرة واحدة فقط والمطابق للرمز المميّز لشراء الإدخال، لإعادة الشراء. باستخدام consumeAsync()
، يجب أيضًا تمرير كائن ينفّذ الواجهة ConsumeResponseListener
. يعالج هذا الكائن نتيجة عملية الاستهلاك. يمكنك إلغاء طريقة onConsumeResponse()
التي تستدعيها "مكتبة الفوترة في Google Play" عند اكتمال العملية.
يوضّح المثال التالي استهلاك منتج في Google Play Billing Library باستخدام رمز الشراء المميّز المرتبط:
Kotlin
suspend fun handlePurchase(purchase: Purchase) { // Purchase retrieved from BillingClient#queryPurchasesAsync or your PurchasesUpdatedListener. val purchase : Purchase = ...; // Verify the purchase. // Ensure entitlement was not already granted for this purchaseToken. // Grant entitlement to the user. val consumeParams = ConsumeParams.newBuilder() .setPurchaseToken(purchase.getPurchaseToken()) .build() val consumeResult = withContext(Dispatchers.IO) { client.consumePurchase(consumeParams) } }
Java
void handlePurchase(Purchase purchase) { // Purchase retrieved from BillingClient#queryPurchasesAsync or your PurchasesUpdatedListener. Purchase purchase = ...; // Verify the purchase. // Ensure entitlement was not already granted for this purchaseToken. // Grant entitlement to the user. ConsumeParams consumeParams = ConsumeParams.newBuilder() .setPurchaseToken(purchase.getPurchaseToken()) .build(); ConsumeResponseListener listener = new ConsumeResponseListener() { @Override public void onConsumeResponse(BillingResult billingResult, String purchaseToken) { if (billingResult.getResponseCode() == BillingResponseCode.OK) { // Handle the success of the consume operation. } } }; billingClient.consumeAsync(consumeParams, listener); }
المنتجات غير الاستهلاكية
للإقرار بعمليات الشراء غير الاستهلاكية، إذا كان تطبيقك يحتوي على خلفية آمنة، ننصحك باستخدام Purchases.products:acknowledge
للإقرار بعمليات الشراء بشكل موثوق. تأكَّد من أنّه لم يتم الإقرار سابقًا بعملية الشراء من خلال الاطّلاع على acknowledgementState
في نتيجة الاتصال بفريق الدعم على
Purchases.products:get
.
إذا كان تطبيقك مخصّصًا للعملاء فقط، يُرجى استخدام BillingClient.acknowledgePurchase()
من
"مكتبة الفوترة في Google Play" في تطبيقك. وقبل الإقرار بعملية الشراء، يجب أن يتحقّق التطبيق مما إذا كان قد تم قبولها أم لا باستخدام طريقة
isAcknowledged()
في Google Play Billing Library.
يوضّح المثال التالي كيفية تأكيد عملية الشراء باستخدام Google Play Billing Library:
Kotlin
val client: BillingClient = ... val acknowledgePurchaseResponseListener: AcknowledgePurchaseResponseListener = ... suspend fun handlePurchase() { if (purchase.purchaseState === PurchaseState.PURCHASED) { if (!purchase.isAcknowledged) { val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder() .setPurchaseToken(purchase.purchaseToken) val ackPurchaseResult = withContext(Dispatchers.IO) { client.acknowledgePurchase(acknowledgePurchaseParams.build()) } } } }
Java
BillingClient client = ... AcknowledgePurchaseResponseListener acknowledgePurchaseResponseListener = ... void handlePurchase(Purchase purchase) { if (purchase.getPurchaseState() == PurchaseState.PURCHASED) { if (!purchase.isAcknowledged()) { AcknowledgePurchaseParams acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder() .setPurchaseToken(purchase.getPurchaseToken()) .build(); client.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener); } } }
الاشتراكات
يتم التعامل مع الاشتراكات بالطريقة نفسها التي يتم التعامل بها مع المنتجات غير الاستهلاكية. إن أمكن، استخدِم Purchases.subscriptions.acknowledge
من واجهة برمجة التطبيقات Google Play Developer API للإقرار بشكل موثوق بعملية الشراء من خلال الخلفية الآمنة. تأكَّد من أنّه لم يتم الإقرار سابقًا بعملية الشراء من خلال
الاطّلاع على acknowledgementState
في مرجع الشراء من
Purchases.subscriptions:get
. بخلاف ذلك، يمكنك تأكيد الاشتراك باستخدام BillingClient.acknowledgePurchase()
من
Google Play 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 للتحقّق من حالة عملية الشراء الحالية.
التعامل مع عمليات الشراء بكميات متعدّدة
يتوافق Google Play مع الإصدار 4.0 والإصدارات الأحدث من Google Play Billing Library، ويتيح للعملاء شراء أكثر من منتج واحد داخل التطبيق في معاملة واحدة من خلال تحديد كمية المنتج من سلّة الشراء. من المتوقّع أن يتعامل تطبيقك مع عمليات الشراء التي تُجري عمليات شراء متعددة الكميات وأن يمنح الأذونات استنادًا إلى كمية عمليات الشراء المحدّدة.
لتلبية عمليات شراء كميات متعددة، يحتاج منطق توفير المتطلبات اللازمة في تطبيقك إلى التحقق من كمية السلعة. يمكنك الوصول إلى الحقل quantity
من إحدى واجهات برمجة التطبيقات التالية:
getQuantity()
من Google Play Billing Library.Purchases.products.quantity
من Google Play Developer API.
بعد إضافة منطق للتعامل مع عمليات الشراء بكميات متعددة، ستحتاج بعد ذلك إلى تفعيل ميزة "تعدّد الكميات" للمنتج المعني في صفحة إدارة المنتج داخل التطبيق في 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
}
}
});