الانتقال إلى المحتوى الرئيسي
كل المقالات
تقني10 دقائق قراءة

انتهاء صلاحية جلسة الدفع: دورة حياة الجلسة من إعادة التوجيه إلى الويب هوك

انتهاء صلاحية جلسة الدفع، وإغلاق الزبون للصفحة، وعناوين العودة: التسلسل الزمني للجلسة، ومدتها الافتراضية 72 ساعة، وإلغاؤها، ورموز 404 و409 و410.

انتهاء صلاحية جلسة الدفع يعني أنها لم تعد قابلة للأداء: فالجلسة تنتهي افتراضيًا بعد 72 ساعة من إنشائها، أو في الموعد الذي تضعونه في expiresAt، ويجب حينها إنشاء جلسة جديدة. ولا تُستهلك الجلسة إلا مرة واحدة، ويمكن إلغاؤها قبل الأداء، والزبون الذي يغلق الصفحة لا يغلق الجلسة: لا شيء يتأكد دون ويب هوك، وتبقى الجلسة صالحة إلى أن تنتهي صلاحيتها. يتتبّع هذا الدليل الجلسة من الإنشاء إلى الويب هوك، ثم يعالج انتهاء الصلاحية، والصفحة المغلقة، وصفحات العودة، والإلغاء، وقراءة الحالة، والرموز 404 و409 و410، والمطابقة بعد انقطاع.

التسلسل الزمني للجلسة، من الإنشاء إلى الويب هوك

ChariPay منصة دفع مغربية، مزوّد خدمات دفع (PSP) تشغّله Chari Money، مؤسسة أداء مرخَّصة من بنك المغرب. وفي موقع تجاري، تتحول الطلبية إلى جلسة: تنشئون الجلسة من جهة الخادم، وتعيدون توجيه المشتري إلى صفحة الدفع المستضافة — بالبطاقة مع ‎3-D Secure —، وتتلقون النتيجة عبر ويب هوك موقَّع. خطوة بخطوة:

  1. 1الإنشاء، من جهة الخادم. ينادي نظامكم الخلفي POST /v1/payment-sessions بالمبلغ وorderId الخاص بكم والزبون وعناوينكم، ثم يسجّل sessionId المُرجَع مقابل الطلبية، في حالة «في انتظار الأداء».
  2. 2إعادة التوجيه. ترسلون المشتري إلى checkoutUrl كما ورد في الرد، في صفحة كاملة، لا داخل iframe أبدًا.
  3. 3الأداء. يُدخل المشتري بطاقته في الصفحة المستضافة ويُصادق لدى بنكه عبر ‎3-D Secure.
  4. 4العودة. يعود المتصفح إلى عنوان accept لديكم بعد أداء ناجح، وإلى decline بعد فشل.
  5. 5الويب هوك. يتلقى خادمكم الحدث الموقَّع — payment.succeeded لأداء ناجح — وهو الذي يُعتدّ به: تؤكّدون الطلبية في تلك اللحظة.

نداء الإنشاء، مع عناوين العودة:

bash
curl -X POST 'https://api-psp.charipay.ma/v1/payment-sessions' \
  -H 'X-CHARI-PAY-API-KEY: chari_sk_test_...' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: order-2026-0421-session' \
  -d '{
    "amount": 250.00,
    "orderId": "ORD-2026-0421",
    "externalId": "order-2026-0421",
    "config": {
      "customer": {
        "email": "amine.bennani@example.com",
        "firstName": "Amine",
        "lastName": "Bennani",
        "phone": "+212600000000"
      },
      "urls": {
        "accept": "https://votre-boutique.ma/paiement/succes",
        "decline": "https://votre-boutique.ma/paiement/echec",
        "notification": "https://votre-boutique.ma/webhooks/charipay"
      }
    },
    "metadata": { "cartId": "c_987", "source": "web" }
  }'

يحتوي الرد — 201، أو 200 إن كان externalId نفسه قد استُعمل من قبل — على هذه الحقول الخمسة، ولا شيء غيرها:

json
{
  "sessionId": "ps_5Kd0Rn",
  "sessionToken": "st_…",
  "verifyKey": "vk_5Kd0Rn",
  "expiresAt": "2026-10-05T10:15:00Z",
  "checkoutUrl": "https://…/checkout/ps_5Kd0Rn"
}

ثم تُقرأ الحالة والمبلغ عبر GET /v1/payment-sessions/{sessionId}. أما verifyKey فلا يعنيكم إلا في الدفع المباشر: مع الصفحة المستضافة، يكفي checkoutUrl. والنقاط الأربع للجلسة موصوفة في وحدة «جلسات الدفع» في توثيق API. واحفظوا أخيرًا القاعدة التي تحكم كل ما يلي: أكّدوا الطلبية عند استلام الويب هوك، لا عند إعادة التوجيه وحدها: فإعادة التوجيه تعني أن المشتري عاد، لا أن الأداء قد تمّ. أما المستقبِل نفسه فيُبنى مع إنجاح دمج الويب هوك.

وكل خطوة من هذه الخطوات ترونها بأعينكم في السندبوكس: أنشئوا جلسة وأدّوها، ثم اتركوا جلسة أخرى تنتهي صلاحيتها. ابدؤوا في وضع الاختبار: ثلاثة حقول — الاسم، والبريد الإلكتروني المهني، والمؤسسة —، ثم رابط تفعيل يصلكم بالبريد لاختيار كلمة المرور، ثم بوابة التاجر حيث تنشئون بأنفسكم مفتاح الاختبار بعد إعادة مصادقة، قبل الأداء ببطاقة الاختبار ‎4918 9141 0719 5005 ورمز ‎3-D Secure 555. مجانًا، بلا حد زمني، ودون انتظار التحقق من مؤسستكم.

انتهاء صلاحية جلسة الدفع: 72 ساعة افتراضيًا، وexpiresAt لتقصيرها

لكل جلسة مدة حياة محدودة — 72 ساعة افتراضيًا، وexpiresAt لتقصيرها — ولا تُستهلك إلا مرة واحدة.

المعاملالقيمة الافتراضيةما يضبطه
expiresAt72 ساعة بعد الإنشاءاللحظة التي لا يمكن بعدها أداء الجلسة؛ بصيغة ISO-8601 بتوقيت UTC، في المستقبل، وإلا كان الرد 400
singleUsetrueجلسة واحدة لأداء واحد

يُكتب expiresAt بتوقيت UTC؛ والمغرب على UTC+1، فحوّلوا الوقت المحلي قبل إرساله. ويكون لتقصير المدة معنى حين تحجز الطلبية شيئًا — منتجًا بمخزون محدود، أو مقعدًا، أو موعدًا: فتنتهي الجلسة مع انتهاء الحجز، ويحرّر نظامكم المورد. والتركيب التجاري لهذه الحالة، مع سياسة الإلغاء، مشروح في الأداء عند الحجز عبر الإنترنت. ويفترض هذا الدليل أنكم اخترتم الجلسة؛ وإن كنتم ما زلتم تقارنونها برابط الدفع، فالمقارنة في رابط دفع أم صفحة دفع.

والجلسة المنتهية الصلاحية لا تُؤدّى: ففي الدفع المباشر، يردّ POST /checkout/verify وPOST /checkout/submit بـ410 SESSION_EXPIRED. ولا يحمل أي من الأحداث العشرين الموثَّقة انتهاء صلاحية جلسة: فنظامكم هو الذي يقارن expiresAt بالوقت الحالي، أو يعيد قراءة الحالة، لينقل الطلبية إلى الحالة المقرَّرة — «منتهية»، أو «للتذكير»، أو محرَّرة.

الزبون يغلق صفحة الدفع: ماذا يحدث

الزبون الذي يغلق التبويب — قبل إدخال بطاقته، أو أثناء المصادقة، أو بعدها مباشرة — لا يُطلق أي شيء نهائي من جهتكم:

  • لا شيء يتأكد دون ويب هوك. ما دام الحدث الموقَّع لم يصل، تبقى الطلبية «في انتظار الأداء».
  • تبقى الجلسة صالحة إلى أن تنتهي صلاحيتها، أو إلى أن تلغوها.
  • إن كان الأداء قد تمّ قبل الإغلاق، يصل payment.succeeded رغم ذلك: لن يرى الزبون صفحة accept أبدًا، لكن المال موجود.
  • لمعرفة وضع الجلسة، اقرؤوا حالتها عبر GET /v1/payment-sessions/{sessionId}.
  • لا يصل `payment.failed` إلا إذا طلبتموه عند إنشاء الجلسة (notifyOnFailure): فلا تبنوا عليه أي مسار.

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

صفحتا العودة accept وdecline

العنوانالدورإن لم تحدّدوه
config.urls.acceptالصفحة التي يعود إليها المشتري بعد أداء ناجحالقيمة الافتراضية لحسابكم، ثم قيمة المنصة
config.urls.declineالصفحة التي يعود إليها المشتري بعد فشلالقيمة الافتراضية لحسابكم، ثم قيمة المنصة
config.urls.notificationالعنوان الذي يتلقى الويب هوك الموقَّع لهذه الجلسةالقيمة الافتراضية لحسابكم فقط، دون رجوع إلى المنصة

ويجب أن تكون كلها عناوين https:// عمومية: فالعنوان بـhttp:// أو العنوان الخاص يُرجع 400 منذ الإنشاء.

صفحة accept ليست صفحة نجاح. إنها تعرض «نتحقق من أدائكم» إلى أن يصل الويب هوك، ثم تنتقل إلى التأكيد. وعمليًا، تسأل خادمكم أنتم — الذي يعرف هل وصل الحدث — ولا تسأل API الدفع مباشرة أبدًا: فمفتاح API لا يغادر خادمكم. وإن تأخر الحدث، يمكن لخادمكم أن يعيد قراءة حالة الجلسة؛ أما الزبون فيرى رسالة صادقة بدل تأكيد سابق لأوانه.

وتشرح صفحة decline، دون مصطلحات تقنية، أن الأداء لم يتمّ، وتقترح إعادة الطلبية — بجلسة جديدة. ولا تَعِدوا فيها بسبب محدّد: فالفشل قد يأتي من مصادقة متروكة، أو من رفض البنك المُصدِر، أو من معطيات خاطئة، وكثير من البطاقات المغربية مغلقة افتراضيًا أمام الأداء عبر الإنترنت. وجملة تدعو الزبون إلى التحقق من بطاقته لدى بنكه أنفع له من رمز تقني.

إلغاء الجلسة قبل الأداء

يُلغي POST /v1/payment-sessions/{sessionId}/cancel الجلسة: فلا يمكن أداؤها بعد ذلك، ويُرجع الرد حالتها المحدَّثة. وثلاث حالات تستدعيه:

  • تغيّرت السلة — حُذف منتج أو عُدّلت كمية — ولم يعد المبلغ صحيحًا؛
  • نفد المخزون قبل الأداء؛
  • سحب البائع الطلبية، أو استبدلها بأخرى.

ألغوا دائمًا الجلسة القديمة قبل إنشاء جلسة جديدة للطلبية نفسها. وأنشئوا الجلسة الجديدة بـexternalId جديد: فالإنشاء بـexternalId سبق استعماله يُرجع الجلسة الموجودة بالرد 200، لا جلسة جديدة. وغيّروا أيضًا قيمة الترويسة Idempotency-Key إن كنتم ترسلونها: فالمفتاح نفسه بجسم مختلف يُرجع 409 IDEMPOTENCY_CONFLICT. أما orderId فيمكن أن يبقى كما هو، لأنه لا يخضع لقيد التفرّد. وتفاصيل هذه الآليات في Idempotency في المدفوعات. وبعد نجاح الأداء، لا يتعلق الأمر بإلغاء. إلغاء جلسة أُدّيت من قبل يردّ بـ409 SESSION_ALREADY_CONSUMED: استعملوا الاسترداد.

قراءة حالة الجلسة

يُرجع GET /v1/payment-sessions/{sessionId} الوضع الحالي للجلسة: حالتها ومبلغها وانتهاء صلاحيتها. ويسرد GET /v1/payment-sessions جلسات بيئة مفتاحكم، من الأحدث إلى الأقدم، بالترقيم المشترك بين القوائم — page بدءًا من الصفر وsize، تُتصفَّح حتى totalPages — ومع مرشّح للمصدر، origin=API أو origin=DASHBOARD.

الحالة تُقرأ ولا تُخمَّن. لا تكتبوا قائمة قيمها ثابتة في الشفرة، وتقبّلوا القيم الجديدة: فهكذا تتطور API دون أن تكسر إدماجكم. والأهم أن الاستطلاع حل احتياطي، لا مصدر الحقيقة. اسألوا عن الحالة في ثلاث لحظات مفيدة — حين يعود الزبون إلى accept قبل وصول الحدث، وعند اقتراب expiresAt لطلبية ما زالت في الانتظار، وأثناء المطابقة —، لا كل ثانية في انتظار الزبون: فالنداءات المتقاربة جدًا تنتهي بـ429 RATE_LIMITED.

رموز 404 و409 و410 الخاصة بالجلسة

أربعة رموز ترافق حياة الجلسة. وهي ترد، من بين ما ترد عليه، على النداءات التي تفتح الجلسة وتؤدّيها — نداءات الصفحة المستضافة، أو نداءات خادمكم في الدفع المباشر:

الرمزاللحظة في دورة الحياةماذا تفعلون
404 SESSION_NOT_FOUNDالجلسة غير معروفةالتحقق من sessionId المرسَل
409 SESSION_ALREADY_CONSUMEDالجلسة أُدّيت من قبلعدم إرسالها مجددًا؛ ولأداء جديد إنشاء جلسة جديدة، ولإرجاع المال الاسترداد
409 SESSION_NOT_ACTIVEالجلسة أُلغيتإنشاء جلسة جديدة إذا كانت الطلبية ما تزال واجبة الأداء
410 SESSION_EXPIREDالجلسة تجاوزت expiresAtإنشاء جلسة جديدة، بـexternalId جديد وIdempotency-Key جديد

أما مفتاح التحقق vk غير الصالح أو المستهلَك فيردّ بـ401 INVALID_TOKEN، لا بـ409. ولا يبرّر أي من هذه الرموز إعادة المحاولة كما هي. والقائمة الكاملة، بما فيها الرموز التي لا تخص الجلسات، في رموز أخطاء API الدفع.

بعد انقطاع: المطابقة عبر GET /v1/transactions

تبلغ محاولات تسليم الويب هوك 16 محاولة على الأكثر، على مدى نحو 72 ساعة. وإن ظل خادمكم معطّلًا مدة أطول، فلا تنتظروا الحدث: طابقوا عبر GET /v1/transactions.

  1. 1حدّدوا فترة الانقطاع بـfrom وto.
  2. 2تصفّحوا المعاملات بالترقيم بالمؤشّر: limit، ثم cursor = nextCursor ما دام hasMore صحيحًا. فهو لا يقفز سطرًا ولا يكرّره حين تصل معاملات جديدة أثناء التصفح.
  3. 3أعيدوا قراءة كل جلسة بقيت طلبيتها «في انتظار الأداء» خلال الفترة، عبر GET /v1/payment-sessions/{sessionId} — ومن هنا أهمية تسجيل sessionId مقابل الطلبية منذ الإنشاء.
  4. 4أكّدوا الطلبيات المدفوعة، وحرّروا تلك التي انتهت صلاحية جلستها.
  5. 5إن أعدتم بعد ذلك تسليمات فاشلة من بوابة التاجر، فتأكدوا من أن معالجتكم تتعرف على الطلبية المؤكَّدة من قبل ولا تؤكّدها مرتين.

وربط الأداء بطلبيته — metadata وorderId وsessionId وoperationId — مفصّل في ربط الأداء بالطلبية.

أسئلة شائعة

كم تدوم جلسة الدفع؟

72 ساعة بعد إنشائها افتراضيًا. ويمكنكم تقصيرها بـexpiresAt، وهو تاريخ بصيغة ISO-8601 بتوقيت UTC يقع في المستقبل. ولا تُستهلك الجلسة إلا مرة واحدة، والجلسة المنتهية الصلاحية لا تُؤدّى: لاستخلاص الطلبية نفسها بعد انتهاء الصلاحية، أنشئوا جلسة جديدة بـexternalId جديد وIdempotency-Key جديد. ويمكن أن يبقى orderId كما هو.

ماذا يحدث إذا أغلق الزبون صفحة الدفع؟

لا شيء نهائي. ما دام الحدث الموقَّع لم يصل، تبقى الطلبية في الانتظار، وتبقى الجلسة صالحة إلى أن تنتهي صلاحيتها أو تُلغى. وإن كان الأداء قد تمّ قبل الإغلاق، يصل payment.succeeded رغم ذلك. ولمعرفة وضع الجلسة، اقرؤوا حالتها عبر GET /v1/payment-sessions/{sessionId}.

ماذا تعرض صفحة العودة بعد الأداء؟

في صفحة accept، رسالة من نوع «نتحقق من أدائكم»، إلى أن يتلقى خادمكم الويب هوك؛ ثم يأتي التأكيد. وتسأل الصفحة خادمكم، لا API مباشرة أبدًا. وفي صفحة decline، شرح خالٍ من المصطلحات التقنية مع إمكانية إعادة الطلبية بجلسة جديدة، ودعوة إلى التحقق من البطاقة لدى البنك.

كيف ألغي جلسة دفع؟

عبر POST /v1/payment-sessions/{sessionId}/cancel، قبل الأداء: تُلغى الجلسة ولا يمكن أداؤها بعد ذلك، ويُرجع الرد حالتها المحدَّثة. وألغوا دائمًا الجلسة القديمة قبل إنشاء جلسة جديدة للطلبية نفسها، وامنحوا الجديدة externalId جديدًا — كي لا تُرجع API الجلسة القديمة — وIdempotency-Key جديدًا. وبعد الأداء، يصبح الأمر استردادًا لا إلغاءً.

ماذا يعني الخطأ 410 SESSION_EXPIRED؟

يعني أن الجلسة تجاوزت expiresAt — 72 ساعة بعد الإنشاء افتراضيًا — ولم يعد ممكنًا أداؤها. لا تعيدوا النداء: أنشئوا جلسة جديدة، بـexternalId جديد وIdempotency-Key جديد، وأعيدوا توجيه المشتري إلى checkoutUrl الخاص بها. ويمكن أن يبقى orderId كما هو، لأنه لا يخضع لقيد التفرّد.

هل يمكن إعادة استعمال جلسة دفع أُدّيت من قبل؟

لا. لا تُستهلك الجلسة إلا مرة واحدة — singleUse قيمته true افتراضيًا —، ومحاولة أداء جديدة على جلسة أُدّيت من قبل تتلقى 409 SESSION_ALREADY_CONSUMED. لأداء جديد، أنشئوا جلسة جديدة. أما إرجاع المال على أداء ناجح فهو استرداد، لا جلسة، وله نداؤه الخاص المستقل.

ماذا يحدث إن لم أحدّد عنوان عودة؟

بالنسبة إلى accept وdecline، تسري القيمة الافتراضية المضبوطة على حسابكم، ثم قيمة المنصة. وبالنسبة إلى notification، لا تسري إلا القيمة الافتراضية لحسابكم، دون رجوع إلى المنصة؛ أما نقاط الاستقبال المسجَّلة فتتلقى الأحداث المشترَكة فيها. ويجب أن يكون كل عنوان تحدّدونه https:// عموميًا، وإلا كان الرد 400.

كيف أعرف أن جلسة أُدّيت حين لا يصل الويب هوك؟

اقرؤوا حالتها عبر GET /v1/payment-sessions/{sessionId}، واطّلعوا على سجل التسليمات: فهو يخبركم هل انطلق الحدث وبماذا أجاب خادمكم. وتبلغ محاولات التسليم 16 محاولة على الأكثر، على مدى نحو 72 ساعة؛ وبعد انقطاع أطول، طابقوا عبر GET /v1/transactions. فالاستطلاع حل احتياطي، والويب هوك هو مصدر الحقيقة.

الخطوة التالية

أعيدوا في السندبوكس النتائج الثلاث للجلسة — أداء ناجح، وصفحة مغلقة، وانتهاء صلاحية قصير — قبل ربط صفحات العودة: ابدؤوا في وضع الاختبار. ثم ابنوا المستقبِل مع إنجاح دمج الويب هوك، واربطوا كل أداء بطلبيته مع ربط الأداء بالطلبية.

بقلم فريق ChariPay.

اقرؤوا بعده

دليل4 دقائق قراءة

رابط دفع أم صفحة دفع مدمجة — أيّهما تختارون

رابط الدفع أم صفحة الدفع المستضافة: طريقتان للتحصيل عبر الإنترنت وعبئا عمل مختلفان. المعايير التي تحسم فعلًا — الحجم، والمبلغ، والفريق، والتخصيص.

تقني4 دقائق قراءة

إنجاح دمج الويب هوك

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

مستعدون للتجربة بأنفسكم؟

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

  • مجاني ودون التزام
  • وضع الاختبار منذ التسجيل
  • لا موافقة تنتظرونها