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

أهم الخلاصات
- إن Voided Purchases API، أي طريقة purchases.voidedpurchases.list، تُعيد الطلبات التي ألغاها Google Play أو استرد مبلغها أو ارتدّت، حتى تتمكن من بناء نظام إلغاء يقطع الوصول عمّا لم يعد يملكه العميل.
- لا تظهر إلا الطلبات المُلغاة. إن الاسترداد الذي يُصدره المطوّر دون خيار الإلغاء غير مرئي لهذه الـ API، لذا إذا أردت سحب الوصول، فعليك أن تسترد المبلغ مع تفعيل الإلغاء.
- النافذة هي 30 يومًا. لا يمكن ضبط startTime على أقدم من 30 يومًا مضت، لذا فإن الخادم الذي يبقى معطلًا أكثر من شهر يفقد تلك الطلبات المُبطَلة إلى الأبد. استعلِم وفق جدول منتظم.
- يخبرك voidedSource بمن أبطل الطلب: 0 هو المستخدم، 1 هو المطوّر، 2 هو Google. ويخبرك voidedReason بالسبب، من 0 Other حتى 7 Chargeback و 8 Unacknowledged_purchase.
- ترسل Real-time developer notifications إشعار VoidedPurchaseNotification في اللحظة التي تُبطَل فيها عملية الشراء، لكن تعامل معه كإشارة. اتصل بـ Voided Purchases API للحصول على القائمة الموثوقة قبل أن تُلغي.
- حدّد تجديدات الاشتراك عبر orderId، لا عبر purchaseToken. إن purchaseToken واحدًا يغطي كل تجديد للاشتراك، لذا لا يمكن للرمز وحده أن يميّز بين تجديدَين.
- الحصص هي 6,000 استعلام في اليوم و 30 استعلامًا في أي نافذة مدتها 30 ثانية، لذا تصفّح النتائج صفحةً صفحة برمز المتابعة واستعلِم حسب النافذة الزمنية، ولا تُجرِ أبدًا اتصالًا واحدًا لكل طلب.
لا يطرق الاسترداد على Google Play بابك. ينتقل المال، ويُبقي العميل التطبيق مفتوحًا، وما لم تذهب لتبحث، لا يتغير شيء من جهتك. إن Voided Purchases API هي المكان الذي تذهب إليه لتبحث. تسلّمك قائمة بالطلبات التي أُلغيت أو استُرِدّت أو ارتدّت، حتى تتمكن من إلغاء الوصول عمّا لم يعد العميل يدفع مقابله. وجّه إليها مهمة مجدولة، واقرأ القائمة، واقطع حق الاستحقاق. هذه هي الحلقة كلها.
هناك عقبة واحدة تُعثِر معظم الفرق، وهي ليست في الشيفرة. لا تظهر هنا إلا الطلبات التي أُلغيت. إذا استردت مبلغ عملية شراء في Play Console دون تحديد خيار الإلغاء، فلن يصل ذلك الطلب أبدًا إلى هذه الـ API، وتعمل مهمتك بنظافة بينما يحتفظ عميل مسترَد له المبلغ بكل ما بعته له. تتناول هذه المقالة الـ API حقلًا بحقل، والأرقام التي تحدّها، وأين يتسرب المال حين تتجاوزها.
ماذا تُعيد Voided Purchases API فعلًا
تجيب الـ API عن سؤال واحد: أي طلبات لهذا التطبيق أُبطِلت مؤخرًا. يشمل الإبطال ثلاث نتائج تنتهي جميعها باسترجاع العميل لماله. إلغاء، أو استرداد، أو ردّ مبالغ. وينطبق على المنتجات داخل التطبيق ذات الشراء لمرة واحدة وعلى الاشتراكات، وتختار النطاق بمعامل واحد. اضبط type على 0 فتحصل على عمليات شراء المنتجات داخل التطبيق المُبطَلة فقط، وهو الوضع الافتراضي. اضبطه على 1 فتحصل على عمليات الشراء داخل التطبيق المُبطَلة وعمليات شراء الاشتراكات المُبطَلة معًا.
كل مُدخَل في القائمة هو كائن عملية شراء مُبطَلة. الحقول قليلة، وكل واحد منها مهم.
الحقول في عملية شراء مُبطَلة
| الحقل | ماذا يحمل |
|---|---|
| orderId | معرّف الطلب الذي يحدّد بشكل فريد عملية شراء لمرة واحدة، أو عملية شراء اشتراك، أو تجديد اشتراك واحد. هذا هو مفتاح الربط لديك |
| purchaseToken | الرمز الذي يحدّد عملية شراء لمرة واحدة أو اشتراكًا. لا يميّز بين التجديدات، لذا استخدم orderId لها |
| purchaseTimeMillis | وقت إجراء عملية الشراء، بالمللي ثانية منذ الحقبة |
| voidedTimeMillis | وقت إلغاء عملية الشراء أو استرداد مبلغها أو ارتدادها، بالمللي ثانية منذ الحقبة |
| voidedSource | من بدأ الإبطال: 0 المستخدم، 1 المطوّر، 2 Google |
| voidedReason | لماذا أُبطِلت عملية الشراء، عدد صحيح من 0 إلى 8 |
| voidedQuantity | الكمية المُبطَلة من استرداد جزئي قائم على الكمية، تُعاد فقط عندما يكون includeQuantityBasedPartialRefund مساويًا لـ true |
اقرأ voidedReason قبل أن تتصرف
إن voidedReason هو الحقل الذي يحوّل قائمة خامًا إلى قرار. يقع استرداد ندم المشتري وردّ المبالغ من البنك كلاهما في القائمة نفسها، لكنهما ليسا الحدث نفسه، وتجعل أسعار أغسطس أحدهما مكلفًا. إليك المجموعة الكاملة.
| voidedReason | التسمية | ماذا يعني لك |
|---|---|---|
| 0 | Other | لم تُسنَد أي فئة. ألغِ وامضِ قدمًا |
| 1 | Remorse | غيّر المشتري رأيه. استرداد عادي |
| 2 | Not_received | يقول العميل إنه لم يستلم المنتج قط. يستحق التحقق من تسليمك |
| 3 | Defective | لم يعمل المنتج. إشارة جودة، سجّلها |
| 4 | Accidental_purchase | عملية شراء غير مقصودة، غالبًا جهاز مشترك |
| 5 | Fraud | صنّف Google المعاملة على أنها احتيالية |
| 6 | Friendly_fraud | ردّ مبالغ يعترض فيه حامل البطاقة الشرعي على معاملة أجراها بنفسه |
| 7 | Chargeback | عكس بنك العميل الدفعة. نهائي لدى البنك، وتُحتسب عليك الآن |
| 8 | Unacknowledged_purchase | استرد Google تلقائيًا مبلغ عملية شراء لم يؤكّدها تطبيقك قط |
نافذة الثلاثين يومًا هي الفخ الذي يُفرغ قائمتك
لا تستطيع Voided Purchases API أن تُظهر إلا عمليات الشراء المُبطَلة خلال الثلاثين يومًا الماضية. يكون معامل startTime افتراضيًا الوقت الحالي ناقص 30 يومًا، ولا يمكن ضبطه على أقدم من ذلك. ويكون endTime افتراضيًا الآن. فالـ endpoint إذًا نافذة متحركة مدتها شهر، لا أرشيف.
النتيجة قاطعة. إذا تعطلت مهمة الاستعلام لديك ولم يلاحظ أحد لمدة خمسة أسابيع، فإن حالات الإبطال من الأسبوع الأول قد تقادمت وخرجت من الـ API. لا يوجد اتصال يعيدها. لن تُلغي تلك الطلبات، ولن تعرف حتى أنها وُجدت ما لم تلتقطها بطريقة أخرى ما. إن الـ API شبكة أمان بها ثقب بحجم أسوأ انقطاع خدمة لديك.
خيار الإلغاء يقرّر ما إذا كان الطلب سيظهر أصلًا
هذا هو السبب الأكثر شيوعًا الذي يجعل فريقًا يُبلّغ عن أن الـ API معطّلة. لا تُعاد إلا الطلبات المُلغاة. إن عمليات الاسترداد التي يبدأها المستخدم، والإلغاءات، وردود المبالغ، وعمليات الاسترداد التي يبدأها Google تُلغى دائمًا، لذا تظهر دائمًا. أما الاسترداد الذي يبدأه المطوّر فمختلف. عندما تسترد مبلغ طلب بنفسك، عبر Play Console أو Orders API، تختار ما إذا كنت ستلغيه أيضًا. استرد دون إلغاء، فيُسوّى الطلب مع العميل لكنه لا يظهر أبدًا في Voided Purchases API.
القاعدة التي تتبع ذلك بسيطة. إذا كان قصدك سحب الوصول، فاسترد المبلغ مع تفعيل خيار الإلغاء. وإلا فقد أعدت المال وتركت الباب مفتوحًا، ومهمة الإلغاء لديك، مهما أُحسِن كتابتها، ليس لديها ما تتصرف بشأنه.
كيف تستعلم عنها دون تجاوز الحصة
الـ endpoint محدود المعدل، والحدود منخفضة بما يكفي لأن تصطدم بها حلقة ساذجة. لديك 6,000 استعلام في اليوم، تُحتسب بتوقيت المحيط الهادئ، وما لا يزيد على 30 استعلامًا في أي فترة مدتها 30 ثانية. هذه الميزانية جيدة للاستعلام بالنوافذ ومعادية لتصاميم الطلب الواحد لكل طلب شراء.
نوافذ الاستعلام ورمز المتابعة
يكون maxResults افتراضيًا 1,000، وهو أيضًا الحد الأقصى. عندما تحوي نافذة أكثر من صفحة واحدة من حالات الإبطال، تحمل الاستجابة كائن tokenPagination مع nextPageToken. أعد تمرير ذلك الرمز في الاتصال التالي لتجتاز الصفحات. اضبط startTime و endTime لتحديد النافذة التي تهمّك، وتصفّح الصفحات حتى ينفد الرمز، ثم تقدّم بالنافذة. يُبقيك هذا النمط داخل حدّ الاندفاع لمدة 30 ثانية والحدّ اليومي معًا.
Real-time developer notifications تسدّ الفجوة
لا يزال الاستعلام يوميًا يترك ما يصل إلى يوم من العمى، وتعاقب نافذة الثلاثين يومًا الفجوات الطويلة. تزيل Real-time developer notifications هذا التأخر. ينشر Google إشعار VoidedPurchaseNotification إلى موضوع Cloud Pub/Sub تملكه أنت في اللحظة التي تُبطَل فيها عملية الشراء، ويستهلكه نظامك الخلفي خلال ثوانٍ. الرسالة صغيرة.
| حقل RTDN | ماذا يحمل |
|---|---|
| purchaseToken | الرمز من عملية الشراء الأصلية |
| orderId | معرّف الطلب للمعاملة المُبطَلة، جديد لكل تجديد اشتراك |
| productType | 1 للاشتراك، 2 لعملية شراء لمرة واحدة |
| refundType | 1 لاسترداد كامل، 2 لاسترداد جزئي قائم على الكمية |

كم يكلفك هذا من المال
الـ API سباكة، لكن سبب توصيلها فاتورة. كل إبطال في تلك القائمة يقابل رقمًا حقيقيًا، واثنان منها يزدادان تكلفة.
فاتورة ردّ المبالغ تقع عليك اعتبارًا من 3 أغسطس 2026
اعتبارًا من 3 أغسطس 2026، يحوّل Google تكلفة ردّ المبالغ إلى المطوّر. تخسر ثمن الشراء وتدفع رسوم ردّ المبالغ للبنك فوق ذلك. لم يعد voidedReason الذي قيمته 7 مجرد عملية بيع خاسرة، بل هو بند مرفق برسم. لا يمكنك عكس ردّ مبالغ، فهو نهائي لدى البنك، لكن يمكنك إيقاف النزيف بعده. يتيح لك التقاط الإبطال سريعًا إلغاء حق الاستحقاق، وبالنسبة لأي شيء ما زلت تسلّمه، إيقاف الإنفاق على عميل استُرِدّ له المبلغ ثم عكسه.
تستمر في الدفع لخدمة عميل استُرِدّ له المبلغ
يضيع ثمن الشراء في اللحظة التي يظهر فيها إبطال. ما تزال تتحكم فيه هو تكلفة الاستمرار في التسليم. كل ساعة يبقى فيها استحقاق مسترَد نشطًا، تستمر في الدفع مقابل الأشياء التي لم يعد العميل يموّلها: الحوسبة، واتصالات API النموذج، والتخزين، وأي مدفوعات للمبدعين أو الشركاء مرتبطة باستخدامه. إن نظام إلغاء يقوده هذا الـ API هو الطريقة التي توقف بها ذلك العدّاد. تجاوزه، وستموّل المنتج لأشخاص عوّضهم المتجر بالفعل.
الاحتيال الودّي نمط يستحق تتبع اتجاهه
إن voidedReason الذي قيمته 5 أو 6 ليس حدثًا فرديًا. يتجمّع الاحتيال والاحتيال الودّي حسب الحساب، وحسب الجهاز، وأحيانًا حسب العرض الترويجي. تمنحك الـ API الـ voidedSource والـ voidedReason في كل إبطال، وهذا يكفي لتتبع اتجاه إساءة الاستخدام حسب الحساب بدلًا من معاملة كل عكس على أنه تكلفة معزولة. إن عميلًا يردّ المبالغ مرتين يخبرك بشيء لم يخبرك به الاسترداد الأول.
توصيلها بطريقة RefundHalt
النموذج صغير بمجرد أن تُمسك بكل القطع. أنصِت إلى VoidedPurchaseNotification في الوقت الفعلي حتى لا ينتظر شيء يومًا كاملًا. اتصل بـ Voided Purchases API كمصدر للحقيقة، مفتاحه orderId حتى لا تختلط تجديدات الاشتراك أبدًا. اقرأ voidedSource و voidedReason حتى يُعالَج ردّ المبالغ بطريقة مختلفة عن استرداد الندم. استعلِم وفق جدول محكم بما يكفي لئلا تعضّك نافذة الثلاثين يومًا أبدًا، واسترد المبلغ مع تفعيل خيار الإلغاء كلما كان قصدك قطع الوصول.
هذا هو الجزء الذي يشغّله RefundHalt نيابةً عنك. يستهلك الإشعارات في الوقت الفعلي، ويطابق كل إبطال مع الـ API، ويلغي الطلب المحدد بدلًا من المنتج بأكمله، ويفصل ردّ المبالغ من البنك عن الاسترداد العادي حتى تُوسَم المكلفة منها لا أن تُدفَن. تحصل على الوصول مُلغى في ثوانٍ وعلى سجل بمن أبطل ماذا ولماذا، دون أن تُقيم بنفسك خط أنابيب Pub/Sub ومهمة استعلام.
الأسئلة الشائعة
- لماذا لا تظهر طلباتي المستردة في Voided Purchases API؟
- لأنه لا تُعاد إلا الطلبات المُلغاة. إن عمليات استرداد المستخدم، والإلغاءات، وردود المبالغ، وعمليات الاسترداد التي يبدأها Google تُلغى دائمًا وتظهر دائمًا. أما الاسترداد الذي يبدأه المطوّر فلا يظهر إلا إذا اخترت أيضًا خيار الإلغاء. إذا استردت مبلغ طلب دون إلغائه، فيُسوّى الطلب لكنه غير مرئي لهذه الـ API، لذا استرد المبلغ مع تفعيل الإلغاء كلما كنت تنوي سحب الوصول.
- إلى أي مدى في الماضي تعود Voided Purchases API؟
- ثلاثون يومًا. يكون معامل startTime افتراضيًا الوقت الحالي ناقص 30 يومًا ولا يمكن ضبطه على أقدم من ذلك، فالـ endpoint إذًا نافذة متحركة مدتها شهر لا أرشيف. إن طلبًا مُبطَلًا يتجاوز عمره 30 يومًا يختفي من الـ API دون أي طريقة لاستعادته، ولهذا تستعلم وفق جدول منتظم وتدعمه بإشعارات في الوقت الفعلي.
- هل ينبغي أن أستخدم Real-time developer notifications أم Voided Purchases API لإلغاء الوصول؟
- استخدم كليهما. يصل إشعار VoidedPurchaseNotification خلال ثوانٍ ويخبرك أن تنظر، لكن إرشاد Google نفسه هو التعامل معه كإشارة لا كمصدر للحقيقة. اتصل بـ Voided Purchases API لتأكيد الحالة الراهنة، ثم ألغِ. يزيل الإشعار التأخر، وتمنحك الـ API الـ voidedSource والـ voidedReason الموثوقين لتتصرف بناءً عليهما.
- كيف أميّز ردّ المبالغ عن الاسترداد العادي في الـ API؟
- اقرأ حقل voidedReason. القيمة 7 هي ردّ مبالغ، أي أن بنك العميل عكس الدفعة، والقيمة 6 هي احتيال ودّي. والقيمة 1 هي استرداد ندم. هذا مهم لأنه اعتبارًا من 3 أغسطس 2026 يمرّر Google ثمن شراء ردّ المبالغ ورسوم البنك إلى المطوّر، فيكلفك voidedReason الذي قيمته 7 أكثر مما يكلفك استرداد بسيط.
- هل تشمل Voided Purchases API الاشتراكات؟
- نعم. اضبط معامل type على 1 لتحصل على عمليات الشراء داخل التطبيق المُبطَلة وعمليات شراء الاشتراكات المُبطَلة معًا. الوضع الافتراضي، type 0، يعيد عمليات شراء المنتجات داخل التطبيق فقط. للاشتراكات، حدّد الفترة المُبطَلة بدقة عبر orderId، لأن purchaseToken واحدًا يغطي كل تجديد ويُنشأ orderId جديد لكل معاملة تجديد.
المصادر وقراءات إضافية
- Google Play Developer API: Voided Purchases API guide
- Google Play Developer API: purchases.voidedpurchases.list method
- Google Play Developer API: purchases.voidedpurchases resource (voidedSource and voidedReason)
- Android Developers: Real-time developer notifications reference (VoidedPurchaseNotification)
- Android Developers: Fight fraud and abuse with Play Billing
RefundHalt
الطيار الآلي للاستردادات في App Store وGoogle Play
تابع القراءة
تصل ثلاثة إشعارات استرداد من App Store بعد أن يقرر Apple، وإشعار REFUND_REVERSED يعيد البيع إليك
يرسل Apple أربع رسائل استرداد عبر App Store Server Notifications V2، ومعظم التطبيقات تتعامل مع اثنتين فقط. إشعار REFUND يخبرك بإلغاء الوصول، وإشعار REFUND_DECLINED يعني احتفظ بالبيع، وإشعار REFUND_REVERSED يعيد البيع إليك ويطلب منك استعادة ما سحبته. إليك ما يتطلبه كل واحد منها.
كل طلب استرداد من Apple يأتي الآن مع سبب، وconsumptionRequestReason هو الطريقة التي تقرأه بها
منذ WWDC24، يحمل كل CONSUMPTION_REQUEST من Apple حقل consumptionRequestReason، وهو السبب الذي ذكره العميل نفسه لرغبته في الاسترداد. هناك خمس قيم، من UNINTENDED_PURCHASE إلى LEGAL، وكل واحدة منها يجب أن تغيّر ما ترسله ردًا خلال نافذتك التي مدتها 12 ساعة. إليك كيف تقرأ كل واحدة منها.