كل المقالات
Deep diveمدة القراءة 9 دقائق

الشراء المعلّق يبدو كعملية بيع، لكن المال لم يصل بعد، ومنحه مبكرًا يعني إهداء المنتج مجانًا

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

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

أهم الخلاصات

  • الشراء المعلّق هو طلب حقيقي قبله المتجر لكنه لم يفرض عليه رسومًا بعد. يرى الكود عملية شراء جديدة، لكن المال لم يصل، وقد لا يصل أبدًا.
  • امنح الاستحقاق فقط عندما تكون الحالة PURCHASED على Google Play، أو عندما تكتمل المعاملة على Apple. لا تفتح أبدًا عند حالة PENDING أو عند نتيجة Apple المعلّقة.
  • على Google Play، يُسوّى الدفع نقدًا في متجر، والتحويلات المصرفية، وبعض فواتير مشغّل الاتصالات خارج النطاق، لذا يعود الشراء بحالة PENDING وليس PURCHASED حتى يدفع العميل فعليًا.
  • على Apple، عادةً ما يكون الشراء المعلّق هو Ask to Buy، حيث يتعيّن على منظّم العائلة الموافقة عليه. قد تستغرق الموافقة ساعات أو أيامًا، وتصل المعاملة المكتملة لاحقًا عبر Transaction.updates.
  • إذا فتحت عند طلب معلّق ثم أُلغي، فتكون قد أنفقت حوسبة، أو استدعاءات API، أو تخزينًا، أو صرفَ عنصر استهلاكي على رسم لم يكتمل قط. وعلى عكس الاسترداد، لا يوجد مال لاستعادته، لأنه لم يُحصَّل شيء أساسًا.
  • يخبرك المتجر عندما يسقط طلب معلّق. يرسل Google رسالة ONE_TIME_PRODUCT_CANCELED، من النوع 2، أو SUBSCRIPTION_PENDING_PURCHASE_CANCELED، من النوع 20. أما Apple فببساطة لا يسلّم معاملة مكتملة أبدًا.
  • يمكن أن يتحوّل الطلب المعلّق إلى عملية بيع حقيقية بينما يكون تطبيقك مغلقًا، لذا أعد التحقق عند العودة: استدعِ queryPurchasesAsync() داخل onResume() على Google Play، واستمر في الاستماع إلى Transaction.updates على Apple.

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

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

ما هو الشراء المعلّق فعليًا

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

Google Play: الدفع يجري في مكان آخر

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

Apple: الشراء ينتظر ضغطة شخص آخر

تعني حالة Apple المعلّقة أن المعاملة تحتاج إلى إجراء خارجي قبل أن تكتمل. أكثرها شيوعًا هو Ask to Buy، حيث يبدأ طفل عملية شراء ويتعيّن على منظّم العائلة الموافقة عليها. في StoreKit 2 يعيد استدعاء الشراء Product.PurchaseResult.pending. وفي StoreKit الأقدم يُبلَّغ عن المعاملة كمؤجّلة. في كلتا الحالتين، لم يفرض Apple رسومًا على أحد، والمعاملة المكتملة، إن أتت، تصل بشكل غير متزامن عبر Transaction.updates. اعرض للعميل حالة انتظار ولا تفتح أي شيء حتى تصل المعاملة المكتملة.

لماذا يكلّفك منح الشراء المعلّق مالًا حقيقيًا

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

أنت تسلّم، والمتجر لا يحصّل أبدًا

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

إشارة الإلغاء، وماذا تعني

المتجر يخبرك فعلًا عندما يموت طلب معلّق. على Google Play، المنتج لمرة واحدة الذي يسقط يرسل إشعار ONE_TIME_PRODUCT_CANCELED، من النوع 2، والاشتراك الذي كان معلّقًا يرسل SUBSCRIPTION_PENDING_PURCHASE_CANCELED، من النوع 20. وعندما تكتمل الطلبات نفسها بدلًا من ذلك، تحصل على ONE_TIME_PRODUCT_PURCHASED، من النوع 1، أو SUBSCRIPTION_PURCHASED، من النوع 4. على Apple لا يوجد حدث إلغاء لالتقاطه، لأن المعاملة المؤجّلة التي تُرفض ببساطة لا تصبح معاملة مكتملة أبدًا. إن كنت قد فتحت مبكرًا، فذلك الصمت هو الفاتورة.

السؤالGoogle PlayApple
ما الذي يثيرهنقدًا، تحويل مصرفي، بعض فواتير مشغّل الاتصالاتموافقة Ask to Buy، أو إجراء آخر مطلوب
الحالة التي تراهاPurchaseState PENDINGنتيجة معلّقة، أو معاملة مؤجّلة
امنح الوصول عندالحالة PURCHASEDاكتمال المعاملة
اكتمل الطلبONE_TIME_PRODUCT_PURCHASED (1)، SUBSCRIPTION_PURCHASED (4)معاملة مكتملة عبر Transaction.updates
سقط الطلبONE_TIME_PRODUCT_CANCELED (2)، SUBSCRIPTION_PENDING_PURCHASE_CANCELED (20)لا تصل أي معاملة مكتملة أبدًا
هل حُصِّل الماللا، ليس حتى PURCHASEDلا، ليس حتى تكتمل المعاملة

المهلة، ومن ينتظر من

الشراء المعلّق ليس ساعةً تتسابق معها. إنه ساعة لم تبدأ بعد، من جهتك.

Google Play يمنح العميل أيامًا، لا دقائق

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

موافقة Apple على هاتف منظّم العائلة

يصل طلب Ask to Buy إلى جهاز المنظّم كإشعار يوافق عليه أو يرفضه متى تسنّى له ذلك. قد يكون هذا بعد دقائق أو ساعات أو يوم، ولا يستطيع تطبيقك تعجيله. السلوك الصحيح الوحيد هو أن تعكس حالة الانتظار وتدع StoreKit يسلّمك المعاملة المكتملة إن أتت الموافقة ومتى أتت.

صندوق كرتوني مغلق بجانب ساعة رملية على مكتب، في تصوير لعملية شراء معلّق تكون فيها البضاعة جاهزة لكن الدفع لم يكتمل بعد

كيف تتعامل مع عمليات الشراء المعلّقة دون تسريب

المهمة كلها تتلخّص في أربع عادات. لا شيء منها صعب، وتجاوز أي منها هو المكان الذي يذهب فيه المال.

امنح عند الحالة المدفوعة، لا عند المعلّقة أبدًا

على Google Play، تحقّق من getPurchaseState() وامنح فقط عند PURCHASED، ولا تُقرّ باستلام الشراء وهو في حالة PENDING. على Apple، افتح فقط عند معاملة مكتملة ولا تفتح أبدًا عند النتيجة المعلّقة. هذه القاعدة الوحيدة تُغلق التسريب بالكامل. كل ما تبقّى يتعلّق بالتأكّد من أنك تلاحظ فعلًا وصول الحالة المدفوعة.

أعد التحقق عندما يعود التطبيق

غالبًا ما يحدث الانتقال من معلّق إلى مدفوع بينما تطبيقك غير قيد التشغيل. على Google Play، استدعِ queryPurchasesAsync() في معالج onResume() لالتقاط الطلبات التي أصبحت PURCHASED في الخلفية، وأبقِ مستمع Real-time Developer Notifications كمصدر الحقيقة على جانب الخادم. على Apple، استمع إلى Transaction.updates طوال عمر التطبيق كاملًا، لأن المعاملة المُوافَق عليها قد تصل بعد وقت طويل من عودة استدعاء الشراء الأصلي.

فعّل دعم المعلّق واختبر النهايتين

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

أخبر العميل أن الطلب لم يكتمل

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

نفس موجزات Real-time Developer Notifications و App Store Server Notifications التي يقرأها RefundHalt بالفعل للاستردادات وعمليات ردّ المبالغ تحمل هذه الإشارات أيضًا. إشعار الشراء الذي يقول إن طلبًا معلّقًا اكتمل أخيرًا، وإشعار الإلغاء الذي يقول إنه لم يكتمل، يصلان إلى لوحة التحكم بجانب بقية أحداث إيراداتك، بحيث يصبح الطلب المعلّق الذي سقط شيئًا يمكنك رؤيته بدلًا من شيء دفعت مقابله عن طريق الخطأ.

باختصار

الشراء المعلّق طلب بلا دفع، وكلا المتجرين صريحان في أنه ينبغي لك الانتظار. يعيد Google Play الطلبات النقدية والتحويلات المصرفية وبعض طلبات فواتير مشغّل الاتصالات بحالة PENDING ويخبرك بمنح الوصول فقط عند PURCHASED. ويعيد Apple معاملة معلّقة أو مؤجّلة لـ Ask to Buy وغيرها من الإجراءات المطلوبة، ويسلّم المعاملة المكتملة لاحقًا عبر Transaction.updates. افتح عند الحالة المدفوعة، وأعد التحقق عند استئناف تطبيقك، وفعّل دعم المعلّق واختبره، وسِمِ الطلب المنتظر للعميل. افعل ذلك فلن يكلّفك الشراء المعلّق شيئًا. تجاوزه فتسلّم منتجًا مدفوعًا مقابل رسم لم يأتِ قط، وهي الخسارة الوحيدة التي لا إيصال تشير إليه.

الأسئلة الشائعة

ما هو الشراء المعلّق؟
الشراء المعلّق طلب قبله المتجر لكنه لم يفرض عليه رسومًا بعد. على Google Play هو حالة الشراء PENDING، وتُستخدم لطرق الدفع التي تُسوّى لاحقًا مثل النقد والتحويل المصرفي وبعض فواتير مشغّل الاتصالات. على Apple هو معاملة معلّقة أو مؤجّلة، وغالبًا ما تكون عملية شراء Ask to Buy تنتظر موافقة منظّم العائلة. في كلتا الحالتين لم يُحصَّل أي مال بعد، لذا لا ينبغي أن تمنح الوصول.
هل ينبغي أن أمنح الوصول بينما الشراء معلّق؟
لا. امنح الاستحقاق فقط عندما تكون الحالة PURCHASED على Google Play، أو عندما تكتمل المعاملة على Apple. إذا فتحت ميزة بينما الطلب لا يزال معلّقًا ولم يكتمل الدفع أبدًا، فتكون قد سلّمت المنتج مجانًا، ولا يوجد رسم لعكسه لأنه لم يحدث أي رسم قط.
ما طرق الدفع التي تسبّب شراءً معلّقًا على Google Play؟
طرق الدفع التي تُسوّى خارج النطاق. الدفع نقدًا في متجر فعلي، والتحويلات المصرفية، وبعض خيارات فواتير مشغّل الاتصالات تتطلب خطوات إضافية بين الضغط والرسم، لذا يعيد Google الشراء بحالة PENDING بدلًا من PURCHASED. في حالة الدفع نقدًا يحصل العميل على رمز عبر الإشعار والبريد الإلكتروني، ثم يدفع في متجر مشارك.
ما هو Ask to Buy، وما علاقته بعمليات الشراء المعلّقة؟
Ask to Buy هو ميزة Family Sharing من Apple التي تتيح لطفل طلب عملية شراء يتعيّن على منظّم العائلة الموافقة عليها. بينما ينتظر الطلب، يكون الشراء في حالة Apple المعلّقة، ويعاد كـ Product.PurchaseResult.pending في StoreKit 2 أو كمعاملة مؤجّلة في StoreKit الأقدم. لا تصل المعاملة المكتملة، عبر Transaction.updates، إلا إن وافق المنظّم عليها ومتى وافق.
ماذا يحدث إذا لم يُدفع الشراء المعلّق أبدًا؟
يُلغى الطلب ولا ينتقل أي مال. على Google Play تتلقّى إشعار ONE_TIME_PRODUCT_CANCELED، من النوع 2، لمنتج لمرة واحدة، أو SUBSCRIPTION_PENDING_PURCHASE_CANCELED، من النوع 20، لاشتراك. على Apple، المعاملة المؤجّلة ببساطة لا تصبح معاملة مكتملة أبدًا. إن كنت قد منحت الوصول بالفعل، فتلك هي اللحظة التي تصبح فيها الخسارة حقيقية.
هل الشراء المعلّق مثل الاسترداد؟
لا. الاسترداد يعكس دفعًا حُصِّل فعلًا. أما الشراء المعلّق الذي يسقط فلم يُفرَض عليه رسم من الأساس، لذا لا شيء لعكسه ولا شيء يظهر في تقرير الاسترداد. إن كنت قد فتحته مبكرًا، فالتكلفة هي الحوسبة أو استدعاءات API أو التخزين أو العنصر الاستهلاكي الذي أنفقته في خدمة طلب لم يُنتج أي إيراد.

المصادر وقراءات إضافية

RefundHalt

الطيار الآلي للاستردادات في App Store وGoogle Play

تابع القراءة

طلب الاسترداد التالي في طريقه إليك بالفعل.

أعد RefundHalt في الوقت الذي تستغرقه لقراءة رسالة دعم أخرى عن استرداد لم تتمكن من الاعتراض عليه.