كل المقالات
تحليل معمّقمدة القراءة 8 دقائق

تحقّق من إشعارات خادم App Store قبل أن تتصرّف بناءً على استرداد، لأن أي شخص يستطيع الإرسال إلى عنوان webhook الخاص بك

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

ختم شمع نحاسي ومغلّف مختوم بجوار حاسوب محمول على مكتب تحت ضوء مصباح دافئ، صورة تعبّر عن سبب التحقّق من إشعارات خادم App Store قبل الوثوق بها

أهم الخلاصات

  • ترسل Apple كل إشعار من إشعارات خادم App Store بالإصدار 2 على هيئة signedPayload، وهو JSON Web Signature من ثلاثة أجزاء بترميز Base64URL: ترويسة وحمولة وتوقيع.
  • تحمل ترويسة JWS في إشعار خادم App Store سلسلة شهادات x5c من ثلاث شهادات: شهادة توقيع طرفية، وشهادة وسيطة من Apple تتضمّن امتداد Apple Worldwide Developer Relations رقم 1.2.840.113635.100.6.2.1، وشهادة جذر من Apple.
  • تتحقّق App Store Server Library من Apple من الإشعارات عبر SignedDataVerifier الذي يفحص سلسلة الشهادات ومعرّف الحزمة والبيئة، وفي الإنتاج appAppleId، ويمكنه فحص إلغاء الشهادات عبر الإنترنت.
  • إشعارات Google Play Real-time developer notifications لا يوقّعها Google Play. في اشتراك الدفع (push)، يمكن لـ Pub/Sub إرفاق رمز OpenID Connect موقّع في ترويسة Authorization، ويتحقّق خادمك من توقيعه ومن مطالبتي الجمهور والبريد الإلكتروني.
  • تقول Google إن RTDN يخبرك فقط بأن حالة شراء قد تغيّرت، لذا فالاستجابة الآمنة لأي إشعار استرداد هي تأكيده عبر Google Play Developer API قبل سحب الوصول.
  • ترسل Apple إشعارات خادم App Store من الشبكة الفرعية 17.0.0.0/8، لكن قائمة السماح بعناوين IP لا تفعل سوى تضييق من يستطيع الوصول إلى نقطة النهاية لديك. التوقيع هو ما يثبت أن الرسالة جاءت من Apple.
  • المُتحقِّق الذي يرفض الرسائل الحقيقية خطأً مكلف بقدر المُتحقِّق الذي يقبل المزوّرة، لأن CONSUMPTION_REQUEST الذي تُسقطه له مهلة رد مدتها 12 ساعة، ومراجعة رد المبالغ (chargeback) في Google Play مهلتها 24 ساعة.

أي شخص يعرف عنوان webhook الخاص بك يستطيع أن يرسل إليه استرداداً. هذه هي الحقيقة المزعجة وراء كل إشعار من إشعارات خادم App Store وكل إشعار من Google Play Real-time developer notifications: إلى أن تتحقّق منها، فالرسالة مجرد طلب HTTPS POST يحتوي على بعض JSON. إذا كان خادمك يلغي اشتراكاً أو يسترجع أرصدة أو يحظر حساباً لأن طلباً ما قال REFUND، فقد سلّمت هذه السلطة لكل من يستطيع كتابة أمر curl.

كلا المتجرين يمنحك وسيلة للتحقّق. توقّع Apple الإشعار نفسه، لذا يمكنك التحقّق من إشعارات خادم App Store بشهادة الجذر من Apple وحدها. أما Google Play فيسلك طريقاً مختلفاً. الرسالة غير موقّعة، لكن تسليم Pub/Sub يمكن أن يكون موقّعاً، وتطلب منك Google على أي حال تأكيد كل تغيير عبر واجهتها البرمجية. إليك ما يثبته كل منهما، وما يكلّفك الخطأ في أي من الاتجاهين.

ما الذي يوقّعه كل متجر فعلاً

السؤالApp Store Server Notifications V2Google Play RTDN
هل الرسالة نفسها موقّعة؟نعم، signedPayload هو JWSلا، حقل data هو JSON بترميز base64
ما الذي يثبت هوية المرسلسلسلة شهادات x5c حتى جذر Appleرمز Pub/Sub OIDC في الدفع، أو سحبك الموثَّق الخاص بك
كائنات موقّعة متداخلةsignedTransactionInfo وsignedRenewalInfo، ولكل منهما JWS خاص بهلا يوجد
حقول تطابقها مع تطبيقكbundleId، appAppleId، environmentpackageName
شبكة المصدر17.0.0.0/8، بيئة sandbox والإنتاجGoogle Cloud Pub/Sub
التأكيد قبل التصرّفاختياري، البيانات الموقّعة هي السجلإلزامي، تقول Google استدعِ Developer API
النقلHTTPS، TLS 1.2 أو أحدثHTTPS بالدفع، أو السحب عبر مكتبة العميل

كيف تتحقّق من إشعارات خادم App Store

تصف وثائق Apple الحقل signedPayload بأنه سلسلة من ثلاثة مكوّنات مرمّزة بـ Base64URL تفصل بينها نقاط: ترويسة وحمولة وتوقيع، موقّعة وفق مواصفة JSON Web Signature. فكّ ترميز الحمولة سهل. أما الوثوق بها فيتطلّب أربعة فحوص.

  • اقرأ مصفوفة x5c من الترويسة بعد فكّ ترميزها. توثّق Apple الترتيب: أولاً الشهادة التي تحمل مفتاح التوقيع، ثم شهادة وسيطة من Apple، ثم شهادة جذر من Apple.
  • تحقّق من صحة تلك السلسلة مقابل جذر من Apple نزّلته بنفسك من موقع PKI الخاص بـ Apple، وهو عملياً Apple Root CA - G3. لا تثق أبداً بالجذر الذي وصل داخل الرسالة، لأن المزوِّر يستطيع وضع أي شهادة هناك.
  • تأكّد من أن الشهادة الوسيطة تحمل امتداد Apple Worldwide Developer Relations رقم 1.2.840.113635.100.6.2.1، ثم تحقّق من توقيع JWS بالمفتاح العام للشهادة الطرفية.
  • طابِق البيانات المفكوكة مع تطبيقك: bundleId، وappAppleId في الإنتاج، وenvironment. توقيع Apple الصحيح على تطبيق مطوّر آخر، أو على عملية شراء من sandbox تصل إلى نقطة النهاية الإنتاجية لديك، لا يجعله استردادك.

ثم كرّر ذلك لما في الداخل. يحتوي كائن data على signedTransactionInfo وsignedRenewalInfo، وكل منهما JWS مستقل بسلسلة x5c خاصة به. معالج REFUND الذي يتحقّق من الغلاف لكنه يقرأ المعاملة دون التحقّق منها قد ترك ثغرة في المنتصف.

استخدم مكتبة Apple بدلاً من كتابتها بنفسك

تنشر Apple مكتبة App Store Server Library للغات Swift وJava وPython وNode. يأخذ SignedDataVerifier فيها شهادات الجذر الخاصة بك، وعلَماً للفحوص عبر الإنترنت، والبيئة، ومعرّف الحزمة الخاص بك، وفي الإنتاج appAppleId الخاص بك. تُظهر شيفرة Node المصدرية ما تفرضه: ثلاث شهادات بالضبط في السلسلة، وامتداد الشهادة الطرفية من Apple رقم 1.2.840.113635.100.6.11.1 وامتداد الشهادة الوسيطة، وتواريخ صلاحية الشهادات، وتطابق معرّف الحزمة ومعرّف التطبيق والبيئة. ومع تفعيل الفحوص عبر الإنترنت، تسأل أيضاً مستجيب OCSP عمّا إذا كانت الشهادة الطرفية والوسيطة قد أُلغيتا.

عدسة صائغ مكبِّرة فوق شهادة ورقية بختم بارز على مكتب خشبي، وهو نوع الفحص الدقيق الذي تُخضع له رسالة الاسترداد قبل الوثوق بها

هل تكفي قائمة السماح بعناوين IP؟

لا. تقول Apple إنه إذا احتاج خادمك إلى قائمة سماح، فعليك إضافة الشبكة الفرعية 17.0.0.0/8، وهي تنطبق على sandbox والإنتاج معاً. هذا يقلّل الضجيج. لكنه لا يثبت أن الرسالة حقيقية، ويتعطّل لحظة تتغيّر شبكة Apple بطريقة لم تسمع بها. اجعل فحص التوقيع هو البوابة الحقيقية.

كيف تتحقّق من إشعارات الاسترداد في Google Play

ينشر Google Play رسائل RTDN في موضوع Pub/Sub تملكه أنت، ويجب ألا يحمل دور Pub/Sub Publisher عليه سوى حساب الخدمة google-play-developer-notifications@system.gserviceaccount.com. بعد ذلك يسلّم اشتراكك الرسائل بإحدى طريقتين، ولكل منهما إثباتها الخاص.

  • اشتراك السحب (pull): يفتح خادمك اتصالاً موثَّقاً بـ Pub/Sub ويجلب الرسائل. لا توجد نقطة نهاية عامة، فلا شيء يمكن لغريب أن يرسل إليه.
  • اشتراك الدفع (push) مع تفعيل المصادقة: يوقّع Pub/Sub رمز JSON Web Token ويرسله في ترويسة Authorization بوصفه رمز Bearer. يقول دليل Google أن تتحقّق من توقيع الرمز وأن تفحص تطابق مطالبتي البريد الإلكتروني والجمهور مع القيم التي ضبطتها، كما تشترط الشيفرة النموذجية أن تكون قيمة email_verified هي true. قد يصل عمر الرمز إلى ساعة.
  • اشتراك الدفع (push) دون مصادقة: أي جهة تستطيع الوصول إلى العنوان يمكنها إرسال رسالة بشكل RTDN.

لماذا تريد Google أن تؤكّد عبر الواجهة البرمجية

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

كم يكلّفك الاسترداد المزوّر أو المرفوض مالياً

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

الإخفاق المعاكس أهدأ وغالباً أكبر. المُتحقِّق الذي يرفض الرسائل الحقيقية، بسبب appAppleId خاطئ أو إعداد sandbox في الإنتاج أو شهادة جذر قديمة، يُسقط مسارَي الاسترداد اللذين يُحتسب فيهما دليلك. تطلب Apple بيانات الاستهلاك خلال 12 ساعة من CONSUMPTION_REQUEST. وتمنحك Google مهلة 24 ساعة للرد على مراجعة رد المبالغ عبر orders.reviewrefund، وبالنسبة إلى الطلبات المقدَّمة بعد August 3, 2026، فإن خسارة رد مبالغ في Google Play تكلّفك سعر الشراء مطروحاً منه رسوم خدمة Play، مضافاً إليه الرسم الذي يفرضه بنك المشتري.

الإخفاقما الذي يحدثما الذي يكلّفه
قبول REFUND مزوّريفقد عميل يدفع إمكانية الوصولوقت الدعم وطلب استرداد حقيقي جلبته بنفسك
قبول شراء مزوّروصول مجاني إلى الميزات المدفوعةحوسبة واستدعاءات واجهات برمجية وتخزين ومدفوعات دون إيرادات
رفض CONSUMPTION_REQUEST حقيقيتقرّر Apple دون بياناتكالبيع، وكل ما أُنفق مسبقاً على تقديمه
رفض مراجعة رد مبالغ حقيقيةتقرّر Google دون أدلّتكسعر الشراء ورسوم البنك على الطلبات بعد August 3, 2026

لا تدع خطأً في التحقّق يلتهم مهلة

  • خزّن الطلب الخام قبل التحقّق منه، حتى لا تضيع الرسالة المرفوضة فعلياً أبداً.
  • أطلق تنبيهاً عند كل إخفاق في التحقّق. رسالة مزوّرة واحدة مجرد ضجيج. أما إخفاق كل الرسائل دفعة واحدة فهو خطأ في نظامك أنت.
  • في Apple، يمكنك إعادة تشغيل ما رفضته. تُعيد Get Notification History الحقل signedPayload الأصلي لآخر 180 يوماً، فأصلِح المُتحقِّق ثم مرّر الحمولات المخزّنة أو المستعادة عبره من جديد.
  • في Google، لا توجد مراجعة رد المبالغ المعلّقة إلا في إشعارها، لذا فإن جمهور دفع مضبوطاً خطأً يعني مراجعة فائتة. اختبره باستخدام Send Test Message في Play Console بعد كل تغيير.

تتحقّق RefundHalt من إشعارات المتجرين كليهما، وتؤكّد عمليات الاسترداد عبر الواجهات البرمجية للمتاجر قبل تغيير أي شيء، وتردّ على CONSUMPTION_REQUEST ومراجعات رد المبالغ ضمن مهلها، حتى لا تكلّفك رسالة مزيّفة عميلاً ولا تفوتك رسالة حقيقية.

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

كيف أتحقّق من إشعار خادم App Store؟
تحقّق من signedPayload بوصفه JSON Web Signature. اقرأ سلسلة شهادات x5c من ترويسته، وتحقّق من صحتها مقابل شهادة جذر من Apple نزّلتها من موقع PKI الخاص بـ Apple، وتحقّق من التوقيع بمفتاح الشهادة الطرفية، ثم افحص تطابق bundleId وappAppleId وenvironment مع تطبيقك. تنجز App Store Server Library من Apple كل ذلك في SignedDataVerifier.
أي مفتاح أستخدم للتحقّق من إشعارات خادم App Store؟
لا تحتاج إلى مفتاح من App Store Connect. المفتاح العام موجود في الشهادة الأولى من سلسلة x5c داخل ترويسة JWS، ولا تثق به إلا بعد التحقّق من السلسلة حتى شهادة جذر من Apple حصلت عليها بنفسك. مفتاح In-App Purchase الخاص بك مخصّص لاستدعاء الواجهات البرمجية لـ Apple، لا لفحص الإشعارات.
هل إشعارات Google Play Real-time developer notifications موقّعة؟
لا، لا يوقّع Google Play رسالة RTDN نفسها. في اشتراك الدفع الموثَّق، يرسل Pub/Sub رمز OpenID Connect موقّعاً في ترويسة Authorization تتحقّق أنت منه، وتقول Google أن تستدعي Google Play Developer API بعد كل إشعار للحصول على الحالة الكاملة لعملية الشراء.
هل يجب أن يُعيد خادمي خطأً عندما يفشل إشعار في التحقّق؟
خزّن الطلب الخام أولاً، ثم قرّر. تعيد Apple إرسال إشعار الإصدار 2 بعد استجابة 40x أو 50x، بعد 1 و12 و24 و48 و72 ساعة من المحاولة السابقة. وإذا كان الإخفاق ناتجاً عن إعداداتك أنت، فإن Get Notification History تتيح لك استعادة signedPayload الأصلي لمدة 180 يوماً والتحقّق منه مجدداً بعد الإصلاح.
هل ما زلت بحاجة إلى التحقّق من الإشعارات إذا سمحت بنطاق عناوين IP الخاص بـ Apple فقط؟
نعم. تطلب Apple من الخوادم التي لديها قائمة سماح أن تقبل الشبكة الفرعية 17.0.0.0/8، وهذا يحدّ من الجهات التي يمكنها الاتصال لكنه لا يثبت أن الرسالة حقيقية. توقيع JWS هو الفحص الذي يربط الإشعار بـ Apple وبتطبيقك.

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

RefundHalt

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

تابع القراءة

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

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