دليل تنفيذ الشراء داخل التطبيق

دليل تنفيذ الشراء داخل التطبيق

مرحباً. أنا zm soft، الذي سجّل في برنامج المطورين في العام الماضي (نهاية عام 2023) وبدأ في إصدار التطبيقات. كما نخطط لإصدار تطبيق مخصص للمطورين للمساعدة في تجاوز الاختبار المغلق بالتعاون بين المطورين، لذا يُرجى الاطلاع عليه إذا أردت.

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

ما هو الشراء داخل التطبيق أصلاً

كما تعلمون، ينقسم الشراء داخل التطبيق إلى نوعين:

  • العناصر الاستهلاكية
  • عناصر الاشتراك الدوري

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

إعدادات Play Store

تسجيل العناصر

أولاً، يرجى تسجيل العناصر لأنه لا يمكن عرضها بدون تسجيل. يمكن تغيير السعر والاسم وغيرهما لاحقاً بما في ذلك ما إذا كنت ستستخدم العنصر فعلياً، فلا بأس بإدخال بيانات مبدئية. ومع ذلك، لا يمكن تغيير المعرف (ID) وحده، لذا إذا كنت تفكر في استخدام عناصر الاختبار في الإنتاج بعد تعديلها، فمن الأفضل تسجيلها بمعرفات قابلة لإعادة الاستخدام مثل [product_1,2,3...] أو [sub_1,2,3...]. يمكن إجراء التسجيل من [Monetize]، حيث يمثل [In-app products] و[Subscriptions] العناصر الاستهلاكية والدورية على التوالي. يمكن تسجيل كل عنصر من خلال زر [Create xx].

Play Console の Monetize メニューにあるアプリ内商品・定期購入セクション

في الأساس، يمكنك المتابعة وفق شاشة الإدخال، لكن ما كان محيراً هو إعداد سعر عناصر الاشتراك الدوري. في البداية، لم أكن أعرف كيفية تعيين الأسعار لجميع المناطق دفعةً واحدة. اعتقدت أن كل منطقة تُدخَل يدوياً، لكن يمكن القيام بذلك بالترتيب التالي: [Set prices] - [Country / region] - [Set price].

Play Console の定期購入価格入力フォーム

عند الضغط على الزر، يظهر الحوار التالي الذي يتيح التعيين دفعةً واحدة.

全地域一括価格設定ダイアログ(Play Console)

تسجيل حساب الاختبار

بعد ذلك، سجّل حساب الاختبار. إذا أجريت اختباراً بالشراء دون تسجيل، فسيتم الدفع الفعلي، لذا يُرجى الانتباه. يتم تسجيل حساب الاختبار عبر تسجيل قائمة بريدية. يمكن التسجيل في الشاشة الأولى (شاشة المطورين) بعد تسجيل الدخول إلى Play Console من خلال [Settings] - [License testing].

Play Console のライセンステスト設定画面(テストアカウント登録用)

عند إجراء عملية شراء التطبيق بمستخدم مسجل في القائمة البريدية، ستظهر معلومات الدفع كـ"بطاقة اختبار"، مما يتيح إجراء الاختبار دون دفع فعلي.

أعمال التنفيذ

يستلزم التنفيذ على جانب التطبيق الأعمال التالية:

  • استيراد المكتبة
  • الاتصال بالمتجر وجلب معلومات العناصر
  • تنفيذ شاشة عرض العناصر
  • طلب الشراء
  • المعالجة عند اكتمال الشراء

سأشرح كل واحدة منها بالتفصيل أدناه.

استيراد المكتبة

يستلزم استيراد المكتبة ما يلي:

  • تعديل build.gradle
  • تعديل android.manifest

مثال على تعديل build.gradle:

dependencies {
    implementation "com.android.billingclient:6.0.0"
}

مثال على تعديل manifest:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="com.android.vending.BILLING" />

    <application

يُرجى مراجعة الوثائق الرسمية للإصدار وغيره.

الاتصال بالمتجر وجلب معلومات العناصر

يُتيح تهيئة BillingClient وتشغيل startConnection التواصلَ مع المتجر. يمكن الحصول على معلومات كل عنصر عبر queryProductDetailsAsync. للمزيد من التفاصيل، يُرجى مراجعة الوثائق الرسمية. في حالتي، لم يعمل الجلب الجماعي لسبب ما، لذا حصلت على قائمة العناصر أولاً ثم جلبت تفاصيل كل عنصر بشكل فردي عبر queryProductDetailAsync. يمكن التمييز بين العنصر المُحصَّل (SKU) من النوع الاستهلاكي أو الدوري من خلال النوع inapp أو subs.

تنفيذ شاشة عرض العناصر

بعد الحصول على معلومات العناصر، اعرضها في قائمة لتمكين اختيار الشراء. عند التعامل مع عناصر الاشتراك الدوري، يلزم الامتثال لـSubscription Policy، وفي حالتي تم رفض تحديثات التطبيق بشكل متتالٍ لسببين:

  • الترجمة غير الكاملة للسعر والشروط
  • عدم اكتمال بنود عرض الاشتراك

أولاً، بخصوص الترجمة غير الكاملة: كان السبب عرض الفترة (مثال: $10/month)؛ كلمة "month" لم تُترجم عند تشغيل التطبيق بإعدادات منطقة لغة غير مدعومة. عند الحصول على المعلومات عبر BillingClient، يُسترد مبلغ السعر كسلسلة formattedPrice تشمل وحدة العملة، فلا حاجة لترجمتها. في المقابل، تُرسَل معلومات الفترة بصيغة ISO 8601 مثل P1M، وتستلزم الترجمة داخل التطبيق. تمت المعالجة عبر إضافة تحويلات لكل لغة كما يلي:

fun formatBillingPeriod(billingPeriod: String, languageCode: String): String {
    return when(languageCode) {
        "en" -> {
            when (billingPeriod) {
                "P1W" -> "weekly"
                "P1M" -> "monthly"
                "P3M" -> "every 3 months"
                "P6M" -> "every 6 months"
                "P1Y" -> "annually"
                else -> "unknown"
            }
        }
        "ja" -> {
            when (billingPeriod) {
                "P1W" -> "週間"
                "P1M" -> "月額"
                "P3M" -> "3ヶ月ごと"
                "P6M" -> "6ヶ月ごと"
                "P1Y" -> "年額"
                else -> "不明"
            }
        }
        "fr" -> {
            when (billingPeriod) {
                "P1W" -> "hebdomadaire"
                "P1M" -> "mensuel"
                "P3M" -> "tous les 3 mois"
                "P6M" -> "tous les 6 mois"
                "P1Y" -> "annuel"
                else -> "inconnu"
            }
        }
        "es" -> {
            when (billingPeriod) {
                "P1W" -> "semanal"
                "P1M" -> "mensual"
                "P3M" -> "cada 3 meses"
                "P6M" -> "cada 6 meses"
                "P1Y" -> "anual"
                else -> "desconocido"
            }
        }
        "de" -> {
            when (billingPeriod) {
                "P1W" -> "wöchentlich"
                "P1M" -> "monatlich"
                "P3M" -> "alle 3 Monate"
                "P6M" -> "alle 6 Monate"
                "P1Y" -> "jährlich"
                else -> "unbekannt"
            }
        }
        else -> "unknown"
    }
}

بعد ذلك، بخصوص عدم اكتمال بنود عرض الاشتراك:

オファー条件違反のサブスクリプション拒否通知 サブスクリプションポリシー違反の拒否メール抜粋

فهمت الإشكالية، لكن لم أعرف بالتحديد ما هي الصياغة المطلوبة. فيما يلي مقتطف من البريد الإلكتروني الأصلي الذي تلقيت فيه إشعار الرفض:

Issue found: Violation of Subscriptions policy

Your app does not comply with the Subscriptions policy.

  • Your offer does not clearly and accurately describe the terms of your trial offer or introductory pricing, including when a free trial will convert to a paid subscription, how much the paid subscription will cost, and that a user can cancel if they do not want to convert to a paid subscription.

كحل، استعنت بصياغة من تطبيقات أخرى. بما أنها تبدو كصياغات معتادة، رأيت أن اتباع السوابق الموثوقة أفضل من التفكير من الصفر.

طلب الشراء

بعد أن يختار المستخدم عنصراً، يأتي معالجة الشراء. بتنفيذ تدفق الشراء للعنصر المُحصَّل، يمكن استدعاء شاشة الشراء الفعلية.

الاختبار

يمكن إجراء الاختبار بدون رسوم فعلية عبر تسجيل الدخول بحساب الاختبار المُعدّ في البداية. نقطتا التنبه هنا هما:

  • تنفيذ تدفق الشراء يتم مع التطبيق المسجل في المتجر
  • يتم تعيين فترة مخصصة لعناصر الاشتراك الدوري

أولاً، بالنسبة لتدفق الشراء، إذا كان التطبيق بإصدار debug، ستظهر رسالة خطأ في شاشة الشراء ولن يمكن تشغيل التدفق.

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

خلاصة

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