رموز أخطاء API الدفع: معنى كل رمز ومتى تصحّحون أو تعيدون المحاولة أو تنتظرون
رموز أخطاء API الدفع من 400 إلى 502: غلاف الخطأ، وcorrelationId، وسبب كل رمز وطريقة تصحيحه، ومتى تُعاد المحاولة بمفتاح Idempotency نفسه.
تتبع رموز أخطاء API الدفع لدى ChariPay قاعدة بسيطة: حالة HTTP تقول من عليه أن يتصرف، والحقل code يقول ماذا يفعل. فخطأ من فئة 4xx يُصحَّح من جهتكم قبل أي محاولة جديدة — إلا 429، حيث يكفي انتظار المدة التي تحدّدها Retry-After؛ وخطأ من فئة 5xx مصدره جهتنا ويُعاد بمفتاح Idempotency نفسه. يفصّل هذا الدليل غلاف الخطأ وcorrelationId الخاص به، وكل رمز منشور مع سببه وطريقة تصحيحه والسلوك الواجب، ثم قاعدة قرار، وطريقة تسجيل لا تُغرق تنبيهاتكم.
غلاف الخطأ وcorrelationId
ChariPay منصة دفع مغربية، مزوّد خدمات دفع (PSP) تشغّله Chari Money، مؤسسة أداء مرخَّصة من بنك المغرب. وكل أخطاء API التاجر لديها — المسارات /v1 — تشترك في الغلاف نفسه:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "amount: must be greater than 0"
},
"correlationId": "b0c1e2d3-4f56-7890-abcd-ef0123456789"
}ثلاثة حقول وثلاثة استعمالات. الحقل code ثابت: فهو جزء من العقد، وهو ما يختبره برنامجكم. والحقل message موجَّه إلى إنسان: يساعد على الفهم، لا على القرار. اختبروا code، لا message أبدًا. أما correlationId فيحدّد الطلب في سجلاتنا؛ ويعود أيضًا في الردود الناجحة، وإن أرسلتم ترويسة X-Request-Id خاصة بكم فإنها تُعاد في الرد وتصير هي correlationId. ولّدوا إذن معرّفًا لكل طلب من جهتكم، وأرسلوه في X-Request-Id، فيرتبط كل سطر في سجلاتكم بسطر في سجلاتنا دون عناء.
واستثناء ينبغي معرفته: نقاط التسجيل وبوابة التاجر لها صيغة أخطاء خاصة بها، برموز من نوع ERR-XXXX. أما الغلاف أعلاه فيخص مسارات /v1 التي يناديها مفتاحكم، كما يخص نداءات /checkout/* في الدفع المباشر، وهو موضوع بقية الدليل.
400 و401: الطلب أو المفتاح
| الرمز | الحالة | السبب | التصحيح |
|---|---|---|---|
VALIDATION_ERROR | 400 | حقل لا يجتاز التحقق | قراءة message وتصحيح الحقل |
MISSING_PARAMETER | 400 | معامل مطلوب غائب | إضافته |
INVALID_IDEMPOTENCY_KEY | 400 | Idempotency-Key غائب أو أطول من اللازم حيث يكون مطلوبًا، مثلًا في POST /checkout/submit لجلسة قابلة لإعادة الاستعمال | إرسال مفتاح صالح |
UNAUTHORIZED | 401 | مفتاح API غائب أو غير صالح | التحقق من الترويسة X-CHARI-PAY-API-KEY ومن المفتاح الذي تحمّله إعداداتكم |
INVALID_TOKEN | 401 | الدفع المباشر: مفتاح التحقق vk غير صالح أو مستهلَك، أو الجلسة لم يُتحقَّق منها أولًا | احترام ترتيب النداءات (verify قبل submit) وعدم إعادة استعمال vk أبدًا |
والرد 400 يعني أن API ترفض الطلب كما هو. وقبل مراجعة منطقكم، تحققوا من أن الطلب يحترم أعراف API:
- حقول بصيغة camelCase:
orderId، لاorder_id؛ - مبلغ بالدرهم، بالوحدات الرئيسية، بخانتين عشريتين على الأكثر:
149.90؛ - كل عنوان مصرَّح به — العودة، والإشعار، والشعار — بصيغة
https://عمومية، وإلا كان الرد400؛ expiresAtفي المستقبل، بصيغة ISO-8601 بتوقيت UTC:2026-07-31T10:15:00Z؛metadataمسطّحة — نصوص وأرقام وقيم منطقية —، دون كائن متداخل ولا مصفوفة، و4 كيلوبايت على الأكثر.
واحذفوا كذلك أي حقل اختياري بدل إرساله بقيمة null: فـAPI تتعامل مع الحقول الغائبة. ولا يُعاد طلب 400 كما هو أبدًا، أيًّا كان code الخاص به، لأن الطلب نفسه سينتج الخطأ نفسه. صحّحوا ثم أرسلوا الطلب المصحَّح. وإن ظهر خطأ 401 UNAUTHORIZED مباشرة بعد نشر جديد، فتحققوا أولًا من متغير البيئة الذي يحمل المفتاح: فقد يكون فارغًا أو خاطئ الاسم أو مبتورًا.
ولبناء معالجة الأخطاء لديكم، استحثّوا هذه الأخطاء بمفتاح اختبار — حقل محذوف، وعنوان بـhttp://، ومفتاح خاطئ عمدًا — ثم اقرؤوا الحالة وcode اللذين تحصلون عليهما. ابدؤوا في وضع الاختبار: ثلاثة حقول — الاسم، والبريد الإلكتروني المهني، والمؤسسة —، ثم رابط تفعيل يصلكم بالبريد لاختيار كلمة المرور، ثم بوابة التاجر حيث تنشئون بأنفسكم مفتاح الاختبار بعد إعادة مصادقة. وهو مجاني، بلا حد زمني، والتحقق من مؤسستكم لا يشترطه إلا الإنتاج.
403 PRODUCTION_ACCESS_NOT_ENABLED وFORBIDDEN
الرمز 403 PRODUCTION_ACCESS_NOT_ENABLED معناه هو ما يقوله تمامًا: تنادون API بمفتاح chari_sk_live_… بينما الإنتاج لم يُفعَّل بعد على حسابكم. ليس خللًا في الإدماج، بل خطوة إدارية. ولها تصحيحان: العودة إلى مفتاح chari_sk_test_… ما دمتم في التطوير، أو إتمام الانتقال إلى الإنتاج — التحقق من المؤسسة، ودراسة الملف، ورسوم التفعيل 6 000 درهم شاملة الضريبة تُؤدّى مرة واحدة —، وقائمة الانتقال في الانتقال من السندبوكس إلى الإنتاج. ويجب أن يختبر برنامجكم هذا الرمز بعينه، PRODUCTION_ACCESS_NOT_ENABLED، وأن يُظهره بوضوح لفريقكم بدل إغراقه وسط الأخطاء العامة.
أما 403 FORBIDDEN فمن طبيعة أخرى: المفتاح موثَّق، لكنه لا يملك الصلاحية المطلوبة لهذا النداء. فلكل مفتاح صلاحيات حسب الوحدة؛ والمثال المنشور هو تصدير CSV للمعاملات، الذي يشترط الصلاحية transaction:export. والتصحيح أن تتحققوا، في بوابة التاجر، من صلاحيات المفتاح الذي يستعمله إدماجكم، وأن تنادوا بمفتاح يحمل الصلاحية التي يشترطها النداء. ولا توسّعوا الصلاحيات تلقائيًا: امنحوا المفتاح ما يناديه إدماجكم فعلًا، ولا شيء غيره.
404 و409 و410 و422: المورد، والتعارض، وانتهاء الصلاحية، والحالة
| الحالة | الرمز | معناه | ماذا تفعلون |
|---|---|---|---|
| 404 | OPERATION_NOT_FOUND | لا عملية تطابق المعرّف المرسَل | التحقق من المعرّف ومن بيئة المفتاح |
| 404 | ORDER_NOT_FOUND | لا أداء يطابق المرجع | التحقق من المرجع المرسَل |
| 404 | SESSION_NOT_FOUND | جلسة غير معروفة | التحقق من sessionId المرسَل |
| 409 | IDEMPOTENCY_CONFLICT | Idempotency-Key نفسه بجسم مختلف | إصلاح طريقة توليد المفاتيح؛ وعدم الإعادة كما هي |
| 409 | SESSION_ALREADY_CONSUMED | الجلسة أُدّيت من قبل | عدم إرسالها مجددًا؛ ولأداء جديد إنشاء جلسة جديدة، ولإرجاع المال الاسترداد |
| 409 | SESSION_NOT_ACTIVE | الجلسة أُلغيت | إنشاء جلسة جديدة إذا كانت الطلبية ما تزال واجبة الأداء |
| 410 | SESSION_EXPIRED | الجلسة تجاوزت expiresAt | إنشاء جلسة جديدة |
| 422 | WALLET_NOT_ACTIVE | طلب صالح، لكن المحفظة المعنية غير نشطة | عدم الإعادة في حلقة؛ التواصل مع الفريق مع إرفاق correlationId |
وفي معاملة أو استرداد، قد يأتي OPERATION_NOT_FOUND أيضًا من operationId أُنشئ في البيئة الأخرى: فالمفتاح هو الذي يختار البيئة، والسندبوكس والإنتاج معزولان.
ويشير 409 IDEMPOTENCY_CONFLICT إلى خلل من جهتكم، لا إلى عطل: المفتاح نفسه استُعمل لجسمين مختلفين. فمفتاح Idempotency يُشتق من النية — الطلبية والعملية —، لا من سحب عشوائي أبدًا؛ والآلية كاملة في Idempotency في المدفوعات.
أما رموز الجلسة فليست أعطالًا: إنها تصف الحياة العادية لجلسة لا تُستهلك إلا مرة واحدة وتبقى صالحة إلى أن تنتهي صلاحيتها أو تُلغى. وهي ترد، من بين ما ترد عليه، على النداءات التي تفتح الجلسة وتؤدّيها — نداءات الصفحة المستضافة، أو نداءات خادمكم في الدفع المباشر. ودورة الحياة التي تنتجها مشروحة في انتهاء جلسة الدفع والعودة منها.
429 RATE_LIMITED: اقرؤوا Retry-After
تعلن النقاط المعروضة للعموم عن حصتها في ثلاث ترويسات للرد. وبعد تجاوزها، تجيب API بـ429 مع الرمز RATE_LIMITED وترويسة Retry-After.
| الترويسة | ما تخبركم به |
|---|---|
X-RateLimit-Limit | الحد الذي يسري على النداء |
X-RateLimit-Remaining | ما تبقّى لكم قبل بلوغه |
X-RateLimit-Reset | لحظة إعادة ضبط العدّاد |
Retry-After | في رد 429، المدة التي تنتظرونها قبل الإعادة |
والسلوك الواجب على مرحلتين: انتظار المدة التي تعلنها Retry-After، ثم إعادة الطلب نفسه بمفتاح Idempotency نفسه. وفي المعالجات المجمّعة — مزامنة كتالوج، أو إعادة قراءة حالات، أو تصدير —، وزّعوا النداءات وراقبوا X-RateLimit-Remaining بدل انتظار 429. فتتابع ردود 429 على نقطة قراءة قد يكشف استطلاعًا مفرطًا: نتيجة الأداء تُعرف بالويب هوك، والاستطلاع ليس إلا حلًا احتياطيًا. ولا يُنشر أي حد رقمي: فلا تكتبوا حدًا ثابتًا في الشفرة، واقرؤوا الترويسات.
5xx و502 BAAS_CHARI_ERROR: الإعادة بالمفتاح نفسه
أخطاء 5xx من جهتنا: أعيدوا المحاولة بمفتاح Idempotency الخاص بكم بدل إنشاء مورد جديد. وهذه بالضبط الحالة التي وُجدت لها أدوات Idempotency الثلاث:
- `Idempotency-Key` في عمليات الإنشاء: إعادة القيمة نفسها تُرجع النتيجة الأولى إن كانت المحاولة الأولى قد مرّت، دون تكرار.
- `externalId` في الروابط والجلسات والاشتراكات والمنتجات: الإنشاء بـ
externalIdموجود يُرجع المورد الموجود بالرد200بدل201. - `refundReference` في الاستردادات:
202عند الإنشاء، و200عند الإعادة، ولا استرداد ثانٍ أبدًا.
أعيدوا المحاولة بمهلة متزايدة بين المحاولات، وتوقفوا بعد بضع محاولات لتنبيه إنسان بدل الدوران في حلقة. فالإنشاء دون مفتاح Idempotency، إذا أُعيد بعد 5xx، قد يُنشئ نسخة مكررة: ولهذا يجب أن يحمل كل إنشاء ترسلونه مفتاحًا، منذ السندبوكس.
وحالة تشبه العطل وليست عطلًا: في السندبوكس، يرد 502 BAAS_CHARI_ERROR على بطاقة غير بطاقة الاختبار. لا فائدة من الإعادة: غيّروا البطاقة. فالبطاقة الوحيدة المقبولة في السندبوكس هي 4918 9141 0719 5005، مع CVV 123، وأي تاريخ انتهاء مستقبلي، ورمز 3-D Secure 555؛ وبقية مسار الاختبار في اختبار الدفع في وضع الاختبار.
رموز أخطاء API الدفع: قاعدة القرار
| الحالة | ماذا تفعلون | هل تعيدون؟ |
|---|---|---|
| 400 | تصحيح الحقل | بعد التصحيح |
| 401 | تصحيح الترويسة أو المفتاح | بعد التصحيح |
| 403 | استعمال المفتاح الصحيح أو الصلاحيات الصحيحة، أو تفعيل الإنتاج | بعد التصحيح |
| 404 | التحقق من المعرّف المرسَل، ومن بيئة المفتاح بالنسبة إلى عملية | بعد التصحيح |
| 409 | تعارض Idempotency: إصلاح توليد المفاتيح؛ جلسة أُدّيت أو أُلغيت: جلسة جديدة إن بقي أداء مستحق | ليس كما هو أبدًا |
| 410 | إنشاء جلسة جديدة | لا |
| 422 | رفع الحالة التي تمنع | ليس قبل ذلك |
| 429 | انتظار Retry-After | نعم، كما هو |
| 5xx | الانتظار بمهلة متزايدة | نعم، بمفتاح Idempotency نفسه |
502 BAAS_CHARI_ERROR في السندبوكس | تغيير البطاقة | لا |
وخطأ API ليس رفضًا من البنك. فالأداء الذي يرفضه بنك المشتري لا يظهر كخطأ في ندائكم: إنشاء الجلسة نجح، والأداء هو الذي لم يتمّ، ويُقرأ ذلك في حالته. وأسباب هذا الفشل — التخلي أثناء المصادقة، أو رفض البنك المُصدِر، أو معطيات خاطئة، أو اشتباه في الاحتيال — مشروحة في 3-D Secure ونسب النجاح.
التسجيل والتنبيه دون ضجيج
لكل نداء فاشل، سجّلوا الطريقة والمسار المنادى، وحالة HTTP، وcode، وcorrelationId — الذي يحمل X-Request-Id الخاص بكم إن أرسلتموه. ولا تسجّلوا مفتاح API أبدًا: فالمفتاح سرّ، والسجلات تتداولها الأيدي. ثم اضبطوا تنبيهاتكم حسب ما تعنيه كل إشارة:
| الإشارة | ما تعنيه | ردّ الفعل |
|---|---|---|
401 أو 403 في الإنتاج | مفتاح غائب أو غير صالح، أو صلاحية ناقصة، أو إنتاج غير مفعَّل | تنبيه فوري: نداءاتكم لم تعد تمرّ |
تتابع ردود 429 | استطلاع مفرط أو مجموعة نداءات سيئة التوزيع | تنبيه، ثم تنعيم النداءات |
5xx متكررة | عطل من جهتنا | تنبيه بعد عدة إخفاقات متتالية، وإعادة بمفاتيح Idempotency |
409 IDEMPOTENCY_CONFLICT | خلل في توليد المفاتيح | تذكرة ذات أولوية |
400 معزولة | معطيات طلبية بعينها | تذكرة، دون إيقاظ أحد |
وحين تراسلون الفريق — تواصلوا مع الفريق —، أرفقوا correlationId والتوقيت بـUTC والمسار المنادى: فهذا ما يسمح بإيجاد طلبكم في سجلاتنا. ويعيد دليل API الدفع في المغرب معالجة الأخطاء إلى سياق الإدماج الكامل، ويسرد توثيق API الردود الممكنة لكل نقطة.
أسئلة شائعة
ماذا يعني الخطأ 403 PRODUCTION_ACCESS_NOT_ENABLED؟
يعني أنكم تنادون API بمفتاح chari_sk_live_… بينما الإنتاج لم يُفعَّل بعد على حسابكم. ليس خللًا في الإدماج: واصلوا العمل بمفتاح الاختبار، أو أتمّوا الانتقال إلى الإنتاج — التحقق من المؤسسة، ودراسة الملف، ورسوم التفعيل 6 000 درهم شاملة الضريبة تُؤدّى مرة واحدة.
ما فائدة correlationId في رد الخطأ؟
يحدّد طلبكم في سجلاتنا. ويعود في كل الردود، بما فيها الناجحة، ويحمل ترويستكم X-Request-Id إن أرسلتموها. سجّلوه دائمًا وأرفقوه بكل طلب مساعدة، مع التوقيت بـUTC والمسار المنادى: فهذا ما يسمح بإيجاد طلبكم في سجلاتنا. وولّدوا X-Request-Id فريدًا لكل طلب لتربطوا سجلاتكم بسجلاتنا.
هل أعيد الطلب بعد خطأ 500؟
نعم. أخطاء 5xx من جهتنا: أعيدوا المحاولة بمفتاح Idempotency الخاص بكم بدل إنشاء مورد جديد. فـIdempotency-Key نفسه أو externalId نفسه أو refundReference نفسه يضمن ألا تُنشأ أي نسخة مكررة إن كانت المحاولة الأولى قد مرّت. باعدوا بين المحاولات بمهلة متزايدة ونبّهوا إنسانًا بعد بضعة إخفاقات.
ماذا أفعل بعد خطأ 429 RATE_LIMITED؟
انتظروا المدة التي تحدّدها الترويسة Retry-After، ثم أعيدوا الطلب نفسه بمفتاح Idempotency نفسه. ولتفادي الخطأ التالي، راقبوا X-RateLimit-Remaining، ووزّعوا معالجاتكم المجمّعة، واستبدلوا الاستطلاع بالويب هوك: فنتيجة الأداء تُعرف بحدث موقَّع، لا بمناداة API في حلقة. ولا يُنشر أي حد رقمي، فاقرؤوا الترويسات دائمًا.
لماذا يُرجع السندبوكس 502 BAAS_CHARI_ERROR؟
لأن البطاقة المُدخلة ليست بطاقة الاختبار. في السندبوكس، لا تُقبل إلا البطاقة 4918 9141 0719 5005، مع CVV 123، وأي تاريخ انتهاء مستقبلي، ورمز 3-D Secure 555. وأي بطاقة أخرى تتلقى 502 BAAS_CHARI_ERROR: الإعادة لا تغيّر شيئًا، فغيّروا البطاقة. والمسار الكامل للاختبار مشروح في دليل اختبار الدفع في وضع الاختبار.
ماذا يعني الخطأ 409 IDEMPOTENCY_CONFLICT؟
يعني أن Idempotency-Key نفسه أُرسل بجسم مختلف عن الطلب الأول. إنه خلل من جهتكم، لا عطل في الشبكة: فمفتاح Idempotency يُشتق من النية — الطلبية والعملية —، والطلب المختلف يستدعي مفتاحًا مختلفًا. أصلحوا طريقة توليد المفاتيح في شفرتكم بدل إعادة الطلب كما هو.
ما حدّ الطلبات في API؟
لا يُنشر أي حد رقمي. فالنقاط المعروضة للعموم تعلن حصتها في الترويسات X-RateLimit-Limit وX-RateLimit-Remaining وX-RateLimit-Reset، وتجيب بـ429 RATE_LIMITED مع Retry-After عند تجاوزها. اقرؤوا هذه الترويسات بدل كتابة حد ثابت في الشفرة: وسيتكيّف إدماجكم من تلقاء نفسه. وفي المعالجات المجمّعة، وزّعوا النداءات على الوقت.
هل يعني خطأ API أن البنك رفض الأداء؟
لا. خطأ API يعني أن طلبكم لم يمرّ. أما الأداء الذي يرفضه بنك المشتري فأمر آخر: الجلسة أُنشئت فعلًا، والأداء هو الذي لم يتمّ، ويُقرأ ذلك في حالته. وأسباب هذا الرفض تتعلق بالمصادقة وبالبنك المُصدِر، لا بشفرتكم. ويُكتشف هذا الرفض بقراءة حالة الجلسة، لا بانتظار رمز خطأ.
الخطوة التالية
استحثّوا هذه الأخطاء في السندبوكس — حقل غائب، وعنوان بـhttp://، ومفتاح خاطئ، وجلسة منتهية الصلاحية، ومفتاح Idempotency نفسه بجسم آخر، بما فيه externalId — وتحققوا من أن شفرتكم تتصرف كما تنص قاعدة القرار: ابدؤوا في وضع الاختبار. ثم اربطوا تسجيل correlationId، وحضّروا الانتقال مع الانتقال من السندبوكس إلى الإنتاج.
بقلم فريق ChariPay.