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

أهم الخلاصات
- تعيد Apple محاولة تسليم إشعار App Store Server Notifications V2 الفاشل خمس مرات، بعد 1 و12 و24 و48 و72 ساعة من المحاولة السابقة، لذا تصل المحاولة الثانية بعد نحو 13 ساعة من المحاولة الأولى.
- تطلب Apple من المطورين الرد على CONSUMPTION_REQUEST خلال 12 ساعة من استلامه، لذا فإن الخادم الذي يفوته التسليم الأول وإعادة المحاولة بعد ساعة قد لا يرى الطلب إلا بعد أن تكون تلك المهلة قد انقضت على الأرجح.
- تعيد نقطة النهاية Get Notification History من Apple إشعارات V2 الخاصة بآخر 180 يومًا في بيئة الإنتاج، بواقع 20 سجلًا في الصفحة، ويقصر خيار onlyFailures النتائج على الإشعارات التي لم تصل إلى خادمك قط، إضافة إلى ما لا يزال قيد إعادة المحاولة.
- لا يرسل Google Play مراجعات رد المبالغ المدفوعة إلا عبر PendingRefundReviewNotification، ويأتي في هذا الإشعار pendingRefundToken اللازم لاستدعاء orders.reviewrefund خلال 24 ساعة. ولا توثّق Google أي واجهة برمجة منفصلة تسرد المراجعات المعلقة.
- يحتفظ Google Cloud Pub/Sub بالرسائل غير المؤكَّد استلامها 7 أيام افتراضيًا، وبحد أقصى 31 يومًا، ويحذف الاشتراك بعد 31 يومًا من دون نشاط من المشترك ما لم تغيّر مدة انتهاء صلاحيته.
- تستعيد واجهة Voided Purchases API في Google Play عمليات الاسترداد والإلغاء ورد المبالغ المدفوعة لآخر 30 يومًا، بحصة قدرها 6,000 استعلام يوميًا و30 استعلامًا في أي فترة مدتها 30 ثانية، لكنها لا تستطيع استعادة مراجعة رد مبالغ لم تُجب عنها قط.
- ترسل بيئة sandbox من Apple كل إشعار مرة واحدة دون أي إعادة محاولة، لذا فإن روتين الاستعادة الذي لم يعمل إلا على sandbox لم يُختبر قط مع جدول إعادة المحاولة الحقيقي لدى Apple.
إشعارات الاسترداد الفائتة هي الطريقة الصامتة التي يخسر بها المطور القرارين الوحيدين في الاسترداد اللذين يكون له فيهما رأي. يبدأ كل من CONSUMPTION_REQUEST لدى Apple ومراجعة رد المبالغ المدفوعة لدى Google Play بإشعار، وكلاهما يُغلق خلال مهلة قصيرة: 12 ساعة لدى Apple و24 ساعة لدى Google. إذا كان خادمك متوقفًا أو بطيئًا أو مضبوطًا بشكل خاطئ عند وصول تلك الرسالة، فلن ينتظرك المتجر. سيقرر بما لديه من معلومات.
الخبر الجيد أن أيًّا من المتجرين لا يتخلص من الرسالة عند أول فشل. تعيد Apple المحاولة وتحتفظ بسجل لمدة 180 يومًا يمكنك الاستعلام عنه. وتنشر Google عبر Pub/Sub الذي يحتفظ بالرسائل غير المؤكَّد استلامها لعدة أيام. أما الخبر السيئ فهو أن توقيت إعادة المحاولة لا يتوافق مع مهل الرد، وأن أحد المسارين لا يملك أي واجهة برمجة للاستعادة على الإطلاق.
كيف يسلّم كل متجر إشعارات الاسترداد
ترسل Apple إشعارات App Store Server Notifications مباشرة إلى عنوان HTTPS تحدده في App Store Connect. أما Google فلا تستدعي خادمك مباشرة. بل تنشر Real-time developer notifications في topic على Google Cloud Pub/Sub، واشتراكك في ذلك الـ topic إما يدفعها إلى نقطة النهاية لديك أو ينتظر أن يسحبها خادمك.
| السؤال | App Store | Google Play |
|---|---|---|
| قناة التسليم | طلب HTTPS POST إلى عنوانك | topic على Cloud Pub/Sub، دفع أو سحب |
| ما يُعدّ نجاحًا | HTTP من 200 إلى 206 | تأكيد استلام رسالة Pub/Sub، أو رمز نجاح من نقطة نهاية الدفع |
| إعادة المحاولة | 5 مرات في V2، بعد 1 و12 و24 و48 و72 ساعة من المحاولة السابقة | يعيد Pub/Sub التسليم حتى تأكيد الاستلام أو انتهاء مدة الاحتفاظ |
| مدة بقاء الرسالة الفائتة | 180 يومًا في Get Notification History | 7 أيام افتراضيًا في الاشتراك، حتى 31 يومًا |
| الاسترداد الذي يطلب أدلتك | CONSUMPTION_REQUEST، الرد خلال 12 ساعة | PendingRefundReviewNotification، الرد خلال 24 ساعة |
| نسخة احتياطية بالسحب للاستردادات المقبولة | Get Notification History وGet Refund History | Voided Purchases API، آخر 30 يومًا |
| سلوك sandbox أو الاختبار | محاولة واحدة، دون إعادة محاولة | Send Test Message من Play Console |
ما تعدّه Apple فشلًا
وثائق Apple واضحة. أعد أي حالة من 200 إلى 206 ويكتمل التسليم. أعد 40x أو 50x وستعيد Apple المحاولة. وتعدّ Apple أي رمز آخر غير ناجح أيضًا. تُسجَّل كل محاولة مع قيمة sendAttemptResult، وتبدو قيمها كقائمة تحقق لانقطاعات حقيقية: TIMED_OUT وTLS_ISSUE وCIRCULAR_REDIRECT وNO_RESPONSE وPREMATURE_CLOSE وUNSUCCESSFUL_HTTP_RESPONSE_CODE وغيرها. وبالنسبة إلى TIMED_OUT، تنصح Apple بالتحقق من أن خادمك لا يعالج الرسائل قبل الرد عليها.
ما تعدّه Google فشلًا
في اشتراك الدفع، يكون رمز النجاح من نقطة النهاية لديك هو تأكيد الاستلام. وفي اشتراك السحب، يجب على خادمك تأكيد استلام كل رسالة يسحبها، وإلا سيسلّمها Pub/Sub مرة أخرى. المهلة الافتراضية لتأكيد الاستلام 10 ثوانٍ والحد الأقصى 600 ثانية. ويضيف دليل RTDN من Google قاعدة أخرى تستحق التكرار: الإشعار يقول فقط إن شيئًا ما تغيّر، لذا ما زلت بحاجة إلى استدعاء Google Play Developer API لمعرفة حالة الشراء كاملة.
لماذا لا يتوافق جدول إعادة المحاولة لدى Apple مع مهلة الـ 12 ساعة
تُحسب فترات إعادة المحاولة لدى Apple من المحاولة السابقة، لا من المحاولة الأولى. اجمعها وستحصل على هذا الجدول الزمني لإشعار V2 واحد يستمر خادمك في الفشل في استلامه.
| المحاولة | الانتظار بعد المحاولة السابقة | الساعات منذ المحاولة الأولى |
|---|---|---|
| التسليم الأول | لا شيء | 0 |
| إعادة المحاولة 1 | ساعة واحدة | 1 |
| إعادة المحاولة 2 | 12 ساعة | 13 |
| إعادة المحاولة 3 | 24 ساعة | 37 |
| إعادة المحاولة 4 | 48 ساعة | 85 |
| إعادة المحاولة 5 | 72 ساعة | 157 |
تطلب منك Apple الرد خلال 12 ساعة من استلام CONSUMPTION_REQUEST. إذا كان خادمك متوقفًا عند التسليم الأول وعند إعادة المحاولة بعد ساعة، فإن المحاولة التالية تصل بعد 13 ساعة من الأولى. ولا تقول Apple ما إذا كان الطلب الذي يُستلم لأول مرة في إعادة محاولة يحصل على 12 ساعة جديدة. والقراءة الحذرة هي اعتبار المحاولة الأولى بداية المهلة، ما يعني أن إعادة المحاولة وحدها لن تنقذ طلبًا فاتك لأكثر من ساعة.
هل تعيد Apple المحاولة بعد HTTP 400؟
نعم، وفقًا للوثائق. تذكر Apple أن الرموز 40x و50x هي التي تؤدي إلى إعادة المحاولة. في مارس 2026 أفاد مطور في منتديات Apple بأن إعادة المحاولة بعد ردود 400 بدت وكأنها توقفت، ثم قال إن التسليم استؤنف بعد يوم أو يومين، وطلبت Apple تقريرًا عبر Feedback Assistant. اعتبر السلوك الموثّق هو القاعدة، ومراقبتك الخاصة هي الدليل.
استعد إشعارات App Store الفائتة باستخدام Get Notification History
تعيد Get Notification History إشعارات الإصدار 2 التي حاولت Apple إرسالها إلى خادمك في فترة زمنية تختارها. يحمل كل سجل الحمولة الموقّعة كاملة، لذا يمكنك تمريرها إلى المعالج نفسه الذي يعالج الإشعارات الحية. ويتضمن كل سجل أيضًا مصفوفة sendAttempts تضم حتى ست إدخالات، واحد للمحاولة الأولى وواحد لكل من إعادات المحاولة الخمس.
- الحقلان startDate وendDate مطلوبان، ويجب أن يقع startDate ضمن آخر 180 يومًا في الإنتاج أو آخر 30 يومًا في sandbox.
- اضبط onlyFailures على true لتحصل فقط على الإشعارات التي لم تصل إلى خادمك، إضافة إلى ما لا تزال Apple تعيد محاولة إرساله.
- رشّح حسب notificationType واحد، مثل CONSUMPTION_REQUEST أو REFUND، أو حسب transactionId لعميل واحد، لكن ليس كليهما في الطلب نفسه.
- يعيد كل استدعاء حتى 20 سجلًا. واصل الاستدعاء باستخدام paginationToken ما دامت قيمة hasMore هي true، وأرسل جسم الطلب نفسه في كل مرة.
هناك تنبيه واحد مهم بالنسبة إلى الاستردادات. تقول Apple إن سجلات التاريخ تعكس حالة الشراء عند إرسال الإشعار أول مرة، لا حالته الآن. لذا فإن REFUND المستعاد حقيقة عن الماضي. قبل أن تسحب الوصول، تحقق من الحالة الحالية عبر نقاط نهاية حالة المعاملة أو الاشتراك.
لا يمكن استعادة إشعارات V1 بهذه الطريقة
لا تغطي Get Notification History إلا الإصدار 2. تعيد Apple محاولة إرسال إشعارات V1 ثلاث مرات، بعد 6 و24 و48 ساعة من المحاولة السابقة، وإذا فشلت جميعها فلا يوجد سجل يمكن إعادة تشغيله. وهذا سبب إضافي لنقل أي تطبيق لا يزال على V1 إلى V2.
استعد إشعارات الاسترداد الفائتة في Google Play
شبكة الأمان لدى Google هي Pub/Sub نفسه. ما دام اشتراكك موجودًا، تبقى الرسالة التي لم يؤكد أحد استلامها هناك طوال مدة الاحتفاظ، 7 أيام افتراضيًا وحتى 31 يومًا إذا رفعتها. وعندما تعود نقطة النهاية لديك، يُستأنف التسليم. لكن هناك إعدادين قد يُبطلان ذلك بصمت.
- ينقل dead-letter topic الرسالة خارج اشتراكك بعد عدد محدد من محاولات التسليم الفاشلة. العدد الافتراضي 5، ومع سياسة إعادة المحاولة الافتراضية القائمة على إعادة التسليم الفوري، قد تمر خمس محاولات بسرعة. إذا كنت تستخدمه، فراقب dead-letter topic كأنه صندوق بريد ثانٍ.
- الاشتراك الذي لا يشهد أي نشاط من المشترك، مثل الاتصالات المفتوحة أو عمليات السحب النشطة أو عمليات الدفع الناجحة، تنتهي صلاحيته بعد 31 يومًا افتراضيًا. ونقطة النهاية التي تبقى معطلة شهرًا كاملًا قد تفقد الاشتراك نفسه، ومعه كل ما ينتظر فيه.
للاستردادات الملغاة نسخة احتياطية لمدة 30 يومًا
بالنسبة إلى الاستردادات التي حدثت بالفعل، تمثل Voided Purchases API النسخة الاحتياطية القائمة على السحب. فهي تسرد عمليات الشراء الملغاة من آخر 30 يومًا، حتى 1,000 في الصفحة، بحصة قدرها 6,000 استعلام يوميًا و30 استعلامًا في أي فترة مدتها 30 ثانية لكل تطبيق. مرّر type 1 لتضمين الاشتراكات إلى جانب المنتجات لمرة واحدة. تشغيلها يوميًا يلتقط أي استرداد أسقطه مسار الإشعارات لديك، فتبقى الاستحقاقات وأرقام الإيرادات دقيقة.
مراجعات رد المبالغ المدفوعة لا نسخة احتياطية لها
أما PendingRefundReviewNotification فأمر مختلف. فهو يحمل pendingRefundToken الذي يجب أن تمرره إلى orders.reviewrefund، ولديك 24 ساعة من استلامه لإرسال تفضيلك بشأن الاسترداد وأدلة الاستخدام. تسجل Google استدعاءك الأول وتتجاهل ما بعده. ولا تصف وثائق Google أي نقطة نهاية تسرد المراجعات المعلقة، لذا إذا ضاعت رسالة Pub/Sub تلك، ضاعت معها أدلتك لذلك النزاع.
كم يكلفك إشعار الاسترداد الفائت ماليًا
الإشعار الفائت لاسترداد أصبح نهائيًا بالفعل يكلفك عملًا محاسبيًا فقط. تجده لاحقًا في Voided Purchases API أو Get Notification History، وتسحب الوصول إن كانت سياستك تقتضي ذلك، وتلحق تقاريرك بالواقع. أمر مزعج، لكنه رخيص.
أما الإشعار الفائت في أحد المسارين القابلين للاعتراض فيكلفك مالًا حقيقيًا، لأن المتجر يقرر دون أدلتك. لدى Apple، يعني ذلك أن قرار الاسترداد يُتخذ دون بيانات الاستهلاك الخاصة بك. ولدى Google Play، بالنسبة إلى الطلبات المقدمة بعد 3 أغسطس 2026، يكلفك النزاع الخاسر سعر الشراء مطروحًا منه رسوم خدمة Play، إضافة إلى رسوم رد المبالغ التي يفرضها بنك المشتري. ولا تتحمل Google إلا رسوم خدمتها.
والمال الذي أنفقته على تقديم الشراء كان قد ذهب قبل أن يبدأ النزاع.
- الحوسبة: وقت GPU والخوادم وراء كل عملية توليد أو تصيير أو استجابة نموذج استخدمها المشتري بالفعل.
- استدعاءات API: طلبات لأطراف ثالثة تُحتسب تكلفتها لحظة تنفيذها، وغالبًا لكل استدعاء.
- التخزين: الملفات والوسائط التي أنشأها المشتري، ولا تزال على فاتورة الاستضافة لديك.
- المدفوعات: حصص الإيرادات المدفوعة بالفعل للمبدعين أو المسوقين بالعمولة أو الشركاء عن تلك البيعة.
لا يعود شيء من ذلك عند تنفيذ الاسترداد. الأدلة التي ترسلها خلال تلك الـ 12 أو 24 ساعة هي الأداة الوحيدة التي تملكها، والإشعار الفائت يسلبك إياها.
روتين استعادة يصمد
- أكّد الاستلام أولًا. خزّن الحمولة الخام، وأعد رمز نجاح، ثم عالجها في طابور.
- استعلم دوريًا عن Get Notification History من Apple مع onlyFailures كل بضع دقائق بحثًا عن CONSUMPTION_REQUEST، حتى لا يكلفك الانقطاع إلا دقائق من مهلة الـ 12 ساعة.
- شغّل مسحًا يوميًا عبر Get Notification History مع onlyFailures على الأيام القليلة الماضية لكل الأنواع، وأعد معالجة ما تجده.
- شغّل استدعاءً يوميًا لـ Voided Purchases API مع type 1 لكل تطبيق على Google Play، وطابقه مع الاستردادات التي سجلتها بالفعل.
- في Pub/Sub، أبقِ مدة الاحتفاظ بالرسائل 7 أيام أو أكثر، وفعّل تنبيهًا على dead-letter topic إن وُجد، وتنبيهًا عند تراكم الرسائل غير المسلّمة.
- اختبر المسار كاملًا في الإنتاج أيضًا. ترسل بيئة sandbox من Apple كل إشعار مرة واحدة دون إعادة محاولة، ويُظهر Request a Test Notification مع Get Test Notification Status بدقة كيف رد خادمك.

يستقبل RefundHalt إشعارات المتجرين نيابة عنك، ويرد تلقائيًا على CONSUMPTION_REQUEST ومراجعات رد المبالغ المدفوعة ضمن مهلها، فلا تكون أنت من يراقب الساعة.
الأسئلة الشائعة
- إلى متى تواصل Apple إعادة محاولة إرسال إشعار خادم App Store الفاشل؟
- نحو ستة أيام ونصف في الإصدار 2. تعيد Apple المحاولة خمس مرات، بعد 1 و12 و24 و48 و72 ساعة من المحاولة السابقة، لذا تأتي آخر محاولة بعد 157 ساعة من التسليم الأول. وتحصل إشعارات الإصدار 1 على ثلاث إعادات محاولة، بعد 6 و24 و48 ساعة من المحاولة السابقة.
- إلى أي مدى زمني يمكنني استعادة إشعارات خادم App Store الفائتة؟
- 180 يومًا في الإنتاج. تعيد نقطة النهاية Get Notification History من Apple إشعارات الإصدار 2 من آخر 180 يومًا، أو آخر 30 يومًا في sandbox، بواقع 20 سجلًا لكل استدعاء، ويقصر خيار onlyFailures النتائج على الإشعارات التي لم تصل إلى خادمك قط.
- هل تعيد Apple محاولة إرسال الإشعار إذا أعاد خادمي HTTP 400؟
- نعم، وفقًا لوثائق Apple. تعيد Apple المحاولة عندما يعيد خادمك حالة 40x أو 50x، وتعدّ الحالات من 200 إلى 206 نجاحًا. وأي حالة أخرى تُحتسب أيضًا غير ناجحة.
- هل يمكنني استعادة مراجعة رد مبالغ فائتة في Google Play؟
- فقط ما دامت رسالة Pub/Sub موجودة. يصل pendingRefundToken الذي تحتاجه لـ orders.reviewrefund في PendingRefundReviewNotification، ولا توثّق Google أي واجهة برمجة تسرد المراجعات المعلقة، لذا فإن رسائل الاشتراك غير المؤكَّد استلامها، المحفوظة 7 أيام افتراضيًا، هي نسختك الوحيدة.
- كيف أجد استردادات Google Play التي فاتت خادمي؟
- استدعِ Voided Purchases API. فهي تعيد عمليات الشراء الملغاة من آخر 30 يومًا، بما فيها الاستردادات والإلغاءات ورد المبالغ المدفوعة، بحد أقصى 1,000 نتيجة في الصفحة وحصة قدرها 6,000 استعلام يوميًا لكل تطبيق. مرّر type 1 لتضمين الاشتراكات.
المصادر وقراءات إضافية
- Apple Developer: Responding to App Store Server Notifications
- Apple Developer: Get Notification History
- Apple Developer: NotificationHistoryRequest
- Apple Developer: Send Consumption Information
- Android Developers: Real-time developer notifications reference
- Android Developers: Help Google dispute chargebacks
- Google Play Developer API: Voided Purchases API
- Google Cloud: Pub/Sub subscription properties
- Play Console Help: Updates to refund protection and chargeback cost responsibility
RefundHalt
الطيار الآلي للاستردادات في App Store وGoogle Play
تابع القراءة
طلبات الاسترداد المفرطة قد تكلفك أكثر من المبالغ المستردة نفسها، لأن متجري التطبيقات يعدّانها مؤشرًا على الجودة
تذكر الإرشادات 5.6.4 من Apple طلبات الاسترداد المفرطة دليلًا على أن التطبيق لا يفي بمعايير الجودة، واتفاقية التوزيع من Google تسمح لها بإزالة تطبيقات المطورين الجدد بناءً على معدلات الاسترداد. إليك ما يقوله كل متجر، وكم تكلفك الإزالة، وكيف تبقى بعيدًا عن الخطر.
تحقّق من إشعارات خادم App Store قبل أن تتصرّف بناءً على استرداد، لأن أي شخص يستطيع الإرسال إلى عنوان webhook الخاص بك
إشعار الاسترداد ليس سوى طلب HTTPS إلى أن تتحقّق من توقيعه. إليك كيف تتحقّق من إشعارات خادم App Store ورسائل Google Play RTDN، وما الذي يوقّعه كل متجر، وما الذي يكلّفك إياه استرداد مزوّر أو مرفوض خطأً.