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

أرفِق appAccountToken بكل عملية شراء من App Store، وإلا فلن تستطيع الدفاع عن استرداد الأموال

تُرسل Apple إلى خادمك طلب CONSUMPTION_REQUEST عندما يطلب العميل استرداد أمواله، لكن المعاملة لا تذكر أبدًا هوية صاحبها. appAccountToken هو الـ UUID الذي يربط عملية الشراء بمستخدمك. اضبطه وستتمكن من الرد على Apple ببيانات حقيقية. تجاهله وستظل تخمّن.

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

أهم الخلاصات

  • appAccountToken هو UUID تُرفِقه بعملية شراء من App Store لكي تشير المعاملة الناتجة إلى المستخدم المحدد في نظامك الخاص. تخزّنه Apple على المعاملة وتُعيده في كل مكان تظهر فيه تلك المعاملة.
  • القاعدة الوحيدة التي تفرضها Apple على التنسيق هي أن تكون القيمة UUID صالحًا. مرّر أي شيء آخر، معرّفًا أو بريدًا إلكترونيًا أو سلسلة نصية مدمجة، وسيتجاهله StoreKit بصمت ويُعيد appAccountToken بقيمة nil.
  • في StoreKit 2 تضبطه بخيار شراء واحد، Product.PurchaseOption.appAccountToken(_:)، باستخدام UUID ثابت أنشأته وخزّنته لذلك الحساب.
  • اضبطه مرة واحدة على عملية الشراء الأصلية وستحمل Apple الرمز نفسه عبر كل تجديد ومحاولة فوترة وترقية في سلسلة الاشتراك.
  • منذ 2025، تتيح نقطة النهاية Set App Account Token لخادمك إرفاق رمز بعمليات الشراء التي تتم خارج تطبيقك، مثل استبدال أكواد العروض والمشتريات المروّجة، التي لم يكن بمقدور التدفق داخل التطبيق الوصول إليها.
  • appAccountToken هو ما يجعل طلب CONSUMPTION_REQUEST من Apple قابلًا للإجابة. بدونه لا يمكنك ربط استرداد الأموال بالعميل الذي يُفترض أن تصف استخدامه خلال نافذة الاثنتي عشرة ساعة.
  • استرداد الأموال الذي لا تستطيع تحديد هويته هو استرداد لا تستطيع الدفاع عنه. تُعيد المال على مشتريات كان لديك دليل للاحتفاظ به، إضافةً إلى الحوسبة واستدعاءات الـ API والمدفوعات التي أنفقتها بالفعل لتقديمها.

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

appAccountToken هو UUID تُرفِقه بعملية شراء لكي تحمل معاملة App Store الناتجة مؤشرًا يعود إلى المستخدم المحدد في نظامك. اضبطه، وسيصل كل سؤال عن استرداد الأموال تطرحه Apple عن ذلك العميل ومعه هويته. تجاهله، وستظل تخمّن. إليك ما هو هذا الحقل، وكيفية ضبطه، ونقطة النهاية الجديدة التي تنقذ المشتريات التي تتم خارج تطبيقك، وما تكلّفه الحلقة المفقودة فعليًا عند وصول طلب استرداد.

ما هو appAccountToken فعليًا

appAccountToken هو UUID غير شفاف تنشئه وتمرّره إلى StoreKit في لحظة الشراء. تخزّنه Apple على المعاملة وتُعيده في معلومات المعاملة لتلك العملية، ويبقى هناك. على حد تعبير Apple، هو "الـ UUID الذي يربط المعاملة بحساب المستخدم في خدمتك الخاصة." القاعدة الوحيدة للتنسيق هي أن يكون UUID. لا تقرأه Apple، ولا تتحقق مما يشير إليه، ولا يهمها ما يعنيه في جانبك. إنه رابط تتحكم فيه أنت.

ولأنه يعيش على المعاملة، فإنه يعود في كل مكان تعود إليه المعاملة. المعاملة الموقّعة في إشعار الخادم، واستجابة Get Transaction Info من App Store Server API، وكل تجديد في سلسلة اشتراك، جميعها تحمل الرمز نفسه إذا ضبطته على عملية الشراء الأصلية. UUID واحد، مُرفَق مرة واحدة، يتبع فوترة العميل طوال عمر العلاقة.

يجب أن يكون UUID حقيقيًا، وإلا فسيختفي بصمت

القاعدة الوحيدة التي تفرضها Apple هي التنسيق. يتطلب StoreKit 2 معرّف UUID وفق RFC 4122. إذا مرّرت سلسلة نصية مدمجة أو معرّفًا رقميًا أو عنوان بريد إلكتروني، فلن يُطلق StoreKit خطأ. بل يتجاهل القيمة وتعود المعاملة وقد ضُبط appAccountToken على nil. يصطدم المطورون بهذا باستمرار، والعَرَض دائمًا هو نفسه، صيغة ما من "appAccountToken مفقود في حمولة المعاملة" على منتديات Apple نفسها، وغالبًا لأن القيمة الممرَّرة لم تكن UUID صالحًا. أنشئ UUID حقيقيًا من جانب الخادم، وخزّنه مقابل الحساب، ولا تسلّم StoreKit أي شيء آخر أبدًا.

كيفية ضبطه وقت الشراء

في StoreKit 2 هو خيار شراء واحد. أنشئ الـ UUID على خادمك عندما يسجّل المستخدم أو يصل إلى الدفع لأول مرة، وخزّنه في سجل حسابه، ومرّر القيمة نفسها إلى استدعاء الشراء.

التوقيع هو Product.PurchaseOption.appAccountToken(_ token: UUID)، وتبدو عملية الشراء هكذا try await product.purchase(options: [.appAccountToken(token)]). عندما تعود المعاملة، سواء تم التحقق منها عبر App Store Server API أو تم تسليمها بإشعار خادم، فإنها تحمل ذلك الـ UUID، ويبحث خادمك عن العميل باستعلام واحد.

استخدم رمزًا ثابتًا واحدًا لكل حساب

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

نقطة النهاية التي تنقذ المشتريات خارج التطبيق

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

أغلقت WWDC 2025 الفجوة بنقطة النهاية Set App Account Token. يستدعي خادمك PUT /inApps/v1/transactions/{originalTransactionId}/appAccountToken على App Store Server API مع الـ UUID في المتن، وتضبط Apple الرمز على تلك المعاملة. يعمل ذلك مع كل أنواع المنتجات، ويغطي استبدال أكواد العروض والمشتريات المروّجة، والقيمة التي ترسلها تتجاوز أي رمز موجود بالفعل على المعاملة. يمكنك الآن ربط عملية شراء لاحقًا، من خادمك، دون أن تمرّ أبدًا عبر تطبيقك.

مجموعة من إيصالات المبيعات الورقية على مكتب داكن مع سطر اسم العميل متروكًا فارغًا، واحد منها مضاء ببقعة ضوء دافئة، لتوضيح عملية شراء من App Store تصل دون appAccountToken لتحديد هوية المشتري
قناة الشراءأين تضبط appAccountTokenملاحظات
عملية شراء داخل التطبيقخيار شراء StoreKit وقت الشراءProduct.PurchaseOption.appAccountToken(UUID)
استبدال كود عرضنقطة النهاية Set App Account Token، من جانب الخادملا يوجد تدفق داخل التطبيق للربط به، لذا اضبطه لاحقًا
عملية شراء مروّجة داخل التطبيق من App Storeنقطة النهاية Set App Account Token، من جانب الخادميحدث الشراء خارج تطبيقك
تجديد اشتراكلا شيء تفعلهيُنقل تلقائيًا من عملية الشراء الأصلية

أين تكلّفك الحلقة المفقودة مالًا

الغرض من الرمز ليس سجلات مرتبة. الغرض هو أن سؤال Apple الوحيد عن استرداد الأموال للمطورين، وهو CONSUMPTION_REQUEST، لا يكون قابلًا للإجابة إلا إذا استطعت العثور على العميل المعني به.

عندما يطلب مشترٍ استرداد أموال على منتج استهلاكي أو اشتراك غير متجدد، تُرسل Apple إلى خادمك إشعار CONSUMPTION_REQUEST وتمنحك اثنتي عشرة ساعة للرد عبر Send Consumption Information. إجابتك هي بيانات عن ذلك العميل تحديدًا: مقدار ما استهلكه من المنتج، ومدة بقاء حسابه، وإجمالي إنفاقه، وحالة التسليم. appAccountToken هو نفسه أحد الحقول في ذلك الطلب، والأهم أنه الطريقة التي تربط بها معاملة الإشعار بالحساب الذي أنت على وشك وصف استخدامه. لا رمز، لا بحث، لا إجابة دقيقة.

ما تكلّفه الإجابة الفارغة فعليًا

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

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

الفكرة نفسها موجودة على Android، باسم آخر

تحل Google Play المشكلة نفسها عبر setObfuscatedAccountId، الذي يُرفِق معرّف حساب بعملية شراء لكي تُصبح مراجعة ردّ المبالغ المدفوعة من Google عبر orders.reviewrefund قابلة للإجابة مقابل مستخدم حقيقي. متجر مختلف، آليات مختلفة، الدرس نفسه: أرفِق الهوية وقت الشراء وإلا فلن تستطيع الدفاع عن النزاع لاحقًا. على App Store، تلك الأداة هي appAccountToken، ويجب أن تكون UUID.

ثلاث عادات تُبقي الرمز في مكانه

  • أنشئ UUID لكل حساب وخزّنه. رمز ثابت واحد لكل عميل، يُنشأ عند تسجيله أو عند أول عملية دفع، ويُحفظ في سجله ويُعاد استخدامه لكل عملية شراء.
  • تحقق قبل أن تمرّره. تأكد من أن القيمة UUID حقيقي في كود الشراء، حتى لا يتحول معرّف مشوّه بصمت إلى رمز nil على المعاملة.
  • عوّض المشتريات خارج التطبيق. عندما يصل إشعار خادم لاستبدال كود عرض أو لعملية شراء مروّجة بلا رمز، استدعِ نقطة النهاية Set App Account Token لإرفاق الرمز الصحيح.

افعل هذه الثلاثة وستصل كل معاملة ترسلها Apple إليك، بما في ذلك طلبات استرداد الأموال، مربوطةً بالفعل بالعميل الذي تخصّه.

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

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

ما هو appAccountToken في App Store؟
appAccountToken هو UUID تنشئه وتُرفِقه بعملية شراء عبر StoreKit لكي تشير معاملة App Store الناتجة إلى حساب مستخدم محدد في نظامك الخاص. تخزّنه Apple على المعاملة وتُعيده في معلومات المعاملة وإشعارات الخادم وكل تجديد في السلسلة نفسها، مما يتيح لك ربط أي حدث مستقبلي، بما في ذلك طلب استرداد الأموال، بالعميل الصحيح.
لماذا يكون appAccountToken بقيمة nil أو مفقودًا؟
دائمًا تقريبًا لأن القيمة التي مرّرتها لم تكن UUID صالحًا. يتطلب StoreKit 2 معرّف UUID وفق RFC 4122 ويتجاهل بصمت أي شيء آخر، لذا تعود السلسلة النصية المدمجة أو المعرّف الرقمي أو البريد الإلكتروني بقيمة appAccountToken تساوي nil بينما تنجح عملية الشراء. السبب الشائع الآخر هو عملية شراء تمت خارج تطبيقك، مثل استبدال كود عرض، حيث لم يعمل أي تدفق داخل التطبيق لضبط الرمز.
هل يمكنني ضبط appAccountToken بعد الشراء، لأكواد العروض؟
نعم، منذ 2025. تتيح نقطة النهاية Set App Account Token على App Store Server API لخادمك إرفاق الرمز أو الكتابة فوقه على معاملة قائمة عبر استدعاء PUT /inApps/v1/transactions/{originalTransactionId}/appAccountToken مع الـ UUID في المتن. يعمل ذلك مع كل أنواع المنتجات وهو مبني للمشتريات التي تتم خارج تطبيقك، مثل استبدال أكواد العروض والمشتريات المروّجة داخل التطبيق.
هل يجب أن يكون appAccountToken من نوع UUID؟
نعم. قاعدة التنسيق الوحيدة التي تفرضها Apple هي أن تكون القيمة UUID صالحًا. وهو بخلاف ذلك غير شفاف، لذا يمكنه الإشارة إلى أي مفتاح حساب تريده في جانبك، لكن إذا لم يكن UUID فلن يخزّنه StoreKit وتعود المعاملة وقد ضُبط appAccountToken على nil.
كيف يساعد appAccountToken في استرداد الأموال؟
عندما يطلب عميل استرداد أمواله، تُرسل Apple طلب CONSUMPTION_REQUEST وتمنحك اثنتي عشرة ساعة للرد ببيانات عن ذلك المشتري تحديدًا. appAccountToken هو الطريقة التي تطابق بها معاملة الإشعار بالحساب الذي تحتاج إلى الإبلاغ عن استخدامه، وهو أحد الحقول في طلب الاستهلاك نفسه. بدونه لا يمكنك الرد بالاستخدام الحقيقي، لذا تميل Apple نحو منح استردادات كان لديك دليل للطعن فيها.
هل يجب أن يكون appAccountToken مختلفًا لكل عملية شراء؟
لا. استخدم UUID ثابتًا واحدًا لكل حساب وأعِد استخدامه لكل عملية شراء يقوم بها ذلك المستخدم. تحمل Apple الرمز عبر التجديدات والترقيات في سلسلة اشتراك، لذا يمنحك رمز ثابت رابطًا نظيفًا عبر الزمن. الرمز الذي يتغير مع كل عملية شراء يقطع ذلك الرابط ويجعل ربط المعاملات بالعميل نفسه أصعب.

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

RefundHalt

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

تابع القراءة

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

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