استرداد مبلغ لزبون: كليًا أو جزئيًا، من بوابة التاجر أو عبر API
استرداد مبلغ لزبون كليًا أو جزئيًا، من بوابة التاجر أو عبر API: مرجع refundReference، والحالات، والأثر على الرصيد، وسطر التصدير، والرسالة إلى الزبون.
استرداد مبلغ لزبون يتمّ من بوابة التاجر في ChariPay أو عبر API، كليًا أو جزئيًا: تحدّدون الأداء الأصلي والمبلغ المراد ردّه، فيُخصم هذا المبلغ من رصيدكم المتاح. والاسترداد مجاني. وعبر API، يضمن مرجع refundReference المشتقّ من الطلبية ألّا يخرج الاسترداد نفسه مرتين أبدًا، حتى لو أعادت شفرتكم النداء. أما إذا لم يؤدِّ الزبون بعد، فلا شيء يُستردّ: يكفي إلغاء الرابط أو الجلسة. يغطي هذا الدليل الحالتين، والتتبّع عبر الويب هوك، والأثر على الرصيد وعلى الدفعة التالية نحو البنك، وسطر الاسترداد في التصدير، والرسالة التي تُرسل إلى الزبون.
الإلغاء قبل الأداء، والاسترداد بعده
ChariPay منصة دفع مغربية، مزوّد خدمات دفع (PSP) تشغّله Chari Money، مؤسسة أداء مرخَّصة من بنك المغرب. قبل البحث عن طريقة الاسترداد، اطرحوا سؤالًا واحدًا: هل تحرّك المال فعلًا؟ ما دام الزبون لم يؤدِّ، فلا شيء يُردّ — يكفي منعه من الأداء. أما بعد نجاح أداء بالبطاقة، فالاسترداد هو الإجراء الوحيد الممكن.
| الحالة | الإجراء الصحيح | ما يحدث |
|---|---|---|
| رابط دفع أُرسل ولم يُؤدَّ بعد | إلغاء الرابط — عبر API: POST /v1/payment-links/{reference}/cancel | يصبح الرابط «ملغى»؛ والرابط الملغى لا يمكن أداؤه بعد ذلك |
| جلسة دفع أُنشئت ولم تُؤدَّ بعد | إلغاء الجلسة — POST /v1/payment-sessions/{sessionId}/cancel | تُلغى الجلسة؛ ودون هذا النداء تنتهي صلاحيتها من تلقاء نفسها، بعد 72 ساعة افتراضيًا أو عند expiresAt |
| أداء ناجح بالبطاقة | الاسترداد، كليًا أو جزئيًا | يُخصم المبلغ من رصيدكم المتاح؛ والعودة من جهة البنك ليست فورية |
الإلغاء هو الإجراء الأنظف: لا حركة مالية ولا سطر للمطابقة. ألغوا الرابط بمجرد ألّا يعود مطابقًا للطلبية — نفاد المخزون، أو زبون تراجع، أو خطأ في المبلغ — بدل تركه قائمًا إلى أن تنتهي صلاحيته: فقد يؤدّيه زبون مستعجل.
وإن كنتم تبيعون عبر Shopify أو WooCommerce، فطريقة إظهار الاسترداد داخل المنصة نفسها مشروحة في Shopify في المغرب وWooCommerce في المغرب. أما هذا الدليل فيتناول الاسترداد من جهة ChariPay، أيًّا كانت أداة البيع.
استرداد مبلغ لزبون من بوابة التاجر
تتيح بوابة التاجر استرداد مبلغ لزبون كليًا أو جزئيًا، دون سطر شفرة واحد. ولا يستطيع كل أعضاء الفريق القيام بذلك، وهذا مقصود: تفصل أدوار بوابة التاجر بين الاطّلاع والفعل. يُطلق المالك أو مسيّر مخوَّل العمليات الصادرة، ومنها الاستردادات؛ ويستخلص أمين الصندوق دون أن يرى الرصيد أو التحويلات، ولا يستردّ؛ ويقرأ المحاسب كل شيء دون أن يُطلق أي عملية. وهكذا يُرفع طلب الاسترداد الوارد إلى الصندوق إلى المسيّر.
قبل التأكيد، أربعة قرارات تعود إليكم:
- 1الأداء المعني. حدّدوه بين معاملاتكم، وتحقّقوا من المبلغ المؤدّى ومن الزبون.
- 2كليًا أم جزئيًا. هل تردّون المبلغ كله أم جزءًا منه فقط؟ يتوقف الجواب على سياسة الإرجاع لديكم وعلى ما سُلِّم فعلًا.
- 3المبلغ. في الاسترداد الجزئي، احسبوه قبل البدء: منتج ناقص، أو خدمة لم تُنجز، أو بادرة تجارية.
- 4السبب. دوّنوه في متابعتكم الخاصة: سيسألكم محاسبكم عنه لاحقًا، وأحيانًا زبونكم أيضًا.
بعد ذلك يُخصم المبلغ من رصيدكم المتاح. الأدوار تفرز من البداية؛ وفي النهاية، سجل التدقيق مسلسل تشفيريًا: لا يمكن إعادة كتابة التاريخ.
الاسترداد عبر API بمرجع refundReference ثابت
عبر API، يكفي نداء واحد للاسترداد: POST /v1/refunds، مع معرّف الأداء الأصلي ومرجع الاسترداد الخاص بكم وسبب.
curl -X POST 'https://api-psp.charipay.ma/v1/refunds' \
-H 'X-CHARI-PAY-API-KEY: chari_sk_test_...' \
-H 'Content-Type: application/json' \
-d '{
"operationId": 8123,
"refundReference": "refund-ORD-2026-0421-1",
"refundAmount": 100.00,
"reason": "CUSTOMER_CANCELLATION",
"note": "Article retourne"
}'حقلًا حقلًا:
- `operationId` يحدّد الأداء المراد استرداده. اقرؤوه في
GET /v1/transactionsأوGET /v1/transactions/{operationId}. وبدلًا منه، يقبلexternalIdقيمةorderIdالخاصة بكم لأداء تمّ عبر جلسة — انتبهوا: ليس هوexternalIdالذي أرسلتموه عند إنشاء الجلسة. ويبيّن دليل ربط الأداء بالطلبية أين يعود كل معرّف. - `refundReference` (64 حرفًا على الأكثر) مرجعكم أنتم، ثابت ومبنيّ على رقم الطلبية، مثل
refund-ORD-2026-0421-1. وهو مفتاح الـIdempotency: يجيب الإنشاء بـ202 Accepted، وإعادة المرجع نفسه تُرجع الاسترداد الموجود بـ200دون خصم ثانٍ — ولا201أبدًا. والمبدأ مشروح في Idempotency في المدفوعات. - `refundAmount` يحدّد مبلغ الاسترداد الجزئي؛ وفي غيابه يكون الاسترداد كليًا.
- `reason` يأخذ قيمة من قائمة محدّدة:
MERCHANT_CANCELLATIONأوCUSTOMER_CANCELLATIONأوPLATFORM_ORCHESTRATIONأوOTHER. والنص الحرّ مرفوض فيه: مكانه `note`، في حدود 128 حرفًا. ويقبلmetadataأيضًا مفاتيحكم الخاصة.
والمرجع الكامل لنقاط النهاية الثلاث — الإنشاء والقراءة والقائمة — في وحدة «الاستردادات» في توثيق API.
يمكنكم تكرار هذه الدورة دون أي مخاطرة في السندبوكس: ابدؤوا في وضع الاختبار. ثلاثة حقول (الاسم، والبريد الإلكتروني المهني، والمؤسسة)، ورابط تفعيل يصلكم بالبريد لاختيار كلمة المرور، ثم بوابة التاجر حيث تنشئون بأنفسكم مفتاح الاختبار. أدّوا طلبية ببطاقة الاختبار، واستردّوها عبر API، وأعيدوا النداء لتروا الرد 200: لا يتحرك أي مال حقيقي، والتحقق من مؤسستكم (KYB) لا يشترطه إلا الانتقال إلى الإنتاج.
استرداد جزئي أم كلي: refundAmount هو الفيصل
الاسترداد الجزئي ليس عملية مستقلة: إنه النداء نفسه بحقل إضافي. دون refundAmount تردّون الأداء كاملًا؛ ومع refundAmount تردّون ذلك المبلغ بالدرهم وبالوحدات الرئيسية (100.00 = 100,00 درهم).
مثال: أُدّيت الطلبية ORD-2026-0421 بمبلغ 250,00 درهمًا، وأرجع الزبون منتجًا ثمنه 100,00 درهم. وهذا هو النداء أعلاه: "refundAmount": 100.00، والسبب CUSTOMER_CANCELLATION، والملاحظة "Article retourne" (منتج مُرجَع). فيحتفظ الزبون ببقية طلبيته، ويعرف نظامكم بدقة ما رُدّ ولماذا.
| الحالة | refundAmount | reason المقترح | note |
|---|---|---|---|
| تلغون قبل الشحن (نفاد المخزون) | غائب: كلي | MERCHANT_CANCELLATION | «نفاد المخزون» |
| الزبون يتراجع قبل التسليم | غائب: كلي | CUSTOMER_CANCELLATION | «إلغاء من الزبون» |
| الزبون يُرجع منتجًا | ثمن المنتج | CUSTOMER_CANCELLATION | «منتج مُرجَع» |
| منتج ناقص في الطرد | ثمن المنتج | MERCHANT_CANCELLATION | «منتج ناقص» |
| خدمة أُنجزت جزئيًا | الجزء غير المنجز | حسب من يتراجع | وصف قصير |
| بادرة تجارية بعد تأخير | مبلغ البادرة | OTHER | «بادرة تجارية» |
القاعدة الذهبية: حدّدوا المبلغ الدقيق قبل النداء، مع الشخص الذي يعرف الطلبية. فمرجع refundReference وملاحظة note ومبلغ refundAmount يجب أن تروي القصة نفسها.
تتبّع الاسترداد: الحالة والأحداث
الردّ 202 Accepted يعني أن الطلب سُجّل، لا أن المال عاد إلى الزبون: فالاسترداد ليس فوريًا من جهة البنك. ولمعرفة النتيجة، طريقان متكاملان:
- أحداث الويب هوك.
refund.succeededحين ينجح الاسترداد، وrefund.failedحين يفشل. إن كانت نقطة الاستقبال لديكم تصرّح بقائمةenabledEventsصريحة، فأضيفوا إليها هذين الحدثين — والقائمة الفارغة تستقبل كل الأحداث. وجّهوا المعالجة حسب الترويسةChari-Event-Type، وأزيلوا التكرار علىChari-Event-Id. - القراءة المباشرة. يعيد
GET /v1/refunds/{reference}الاسترداد انطلاقًا من مرجعrefundReferenceالخاص بكم (أو من المعرّف الذي تمنحه المنصة)، ويعرضGET /v1/refundsقائمة استرداداتكم من الأحدث إلى الأقدم.
في نظامكم، تصبح الطلبية «مستردّة» عند وصول refund.succeeded، لا لحظة النداء. وبين الاثنين، اعرضوا «استرداد قيد المعالجة»: فهذه هي الحالة الفعلية. وإن لم ينجح استرداد، فتحقّقوا أولًا من رصيدكم المتاح — فإن لم يكفِ تُوقَف العملية — ثم تواصلوا مع الفريق إن لم يكن السبب واضحًا.
وبلا مطوّر، تكفي بوابة التاجر: فكل حركة في الحساب، ومنها الاستردادات، تحمل فيها مرجع البيع أو الاسترداد أو الدفعة التي تفسّرها.
الأثر على رصيدكم وعلى الدفعة التالية
استرداد مبلغ لزبون مجاني: لا تُضاف أي رسوم استرداد، كما تبيّن صفحة الأسعار. ويُخصم المبلغ المستردّ من رصيدكم المتاح.
ChariPay ليس بنكًا: حساب الأداء الخاص بكم تمسكه Chari Money. إنه يكمّل بنككم ولا يعوّضه. ولا سحب على المكشوف فيه ولا ائتمان: فإذا لم يكفِ الرصيد المتاح لتغطية الاسترداد، تُوقَف العملية بدل إحداث رصيد مدين. ومن ذلك عادتان:
- قبل استرداد مبلغ كبير، انظروا إلى رصيدكم المتاح — في بوابة التاجر أو عبر
GET /v1/wallet. - وإن لم يغطِّ المبلغ، فانتظروا مستخلصات جديدة أو غذّوا الحساب عبر
POST /v1/wallet/cash-ins، مع ترويسةIdempotency-Keyالإلزامية.
والدفعة نحو بنككم عملية مستقلة: تلقائية كل ليلة متى ضُبط حساب التسوية في بوابة التاجر، أو بتحويل عند الطلب، وتحمل كل دفعة مرجعًا يظهر في كشف حسابكم لدى البنك. وقد يُنقص الاسترداد الدفعةَ التالية، دون سطر مستقل في كشف الحساب البنكي: سترون دفعة أصغر، لا سطرًا بعنوان «استرداد». والتفسير موجود في تصدير الحساب، لا في الكشف. والجدول الزمني الكامل، من الأداء الناجح إلى بنككم، مفصّل في متى تصل أموال الدفع الإلكتروني.
سطر الاسترداد في التصدير
الاسترداد لا يمحو البيع: بل يليه. في التصدير، كل استرداد حركة مستقلة، مرتبطة بالمعاملة الأصلية وحاملة لمرجع الاسترداد. وتميّز بوابة التاجر بين المبلغ المدفوع والرسوم والصافي، ويحتفظ تصدير CSV بهذه المراجع — من بوابة التاجر أو عبر GET /v1/transactions/export.csv، حتى 10 000 سطر؛ وفوق ذلك قسّموا حسب الفترة بـfrom وto.
وبالنسبة إلى محاسبكم، يعني ذلك قيدين مؤرَّخين: البيع، ثم الاسترداد. ثلاث عادات تُبسّط هذا التتبّع:
- 1احتفظوا بمرجع `refundReference` في نظام الطلبيات لديكم: فهو الذي يربط سطر التصدير بملف الزبون.
- 2طابقوا الدفعة الأصغر مع تصدير الفترة نفسها، لا مع البيع المستردّ.
- 3حوّلوا الطوابع الزمنية: فهي بتوقيت UTC، والمغرب على UTC+1. والاسترداد الذي يتمّ بُعيد منتصف الليل بتوقيت المغرب يحمل في التصدير تاريخ اليوم السابق.
والطريقة الكاملة — الفوارق بين المستخلَص وما دُفع نحو البنك، والاستردادات، والمنطقة الزمنية — في مطابقة المستخلصات.
إخبار الزبون: الرسالة التي تُرسل
استرداد لا يفهمه الزبون يتحوّل إلى مكالمة لخدمة الزبناء. أرسلوا رسالة قصيرة لحظة إطلاق العملية، فيها ما يحتاج إلى معرفته ولا شيء أكثر:
مرحبًا أمين، أطلقنا استرداد 100,00 درهم على طلبيتكم ORD-2026-0421 (استرداد جزئي: منتج مُرجَع). والاسترداد ليس فوريًا من جهة البنك: سيعود إليكم المبلغ حسب معالجة بنككم. مرجعنا: refund-ORD-2026-0421-1. ولن نطلب منكم أبدًا بيانات بطاقتكم.
ما يجب أن تتضمّنه الرسالة: المبلغ، ومرجع الطلبية، ونوع الاسترداد (كلي أو جزئي)، والجملة الخاصة بمعالجة البنك دون أي رقم — فأنتم لا تتحكّمون في مدتها — ومرجع الاسترداد الخاص بكم، الذي يستطيع الزبون ذكره. وما لا يجوز أن تتضمّنه أبدًا: طلب رقم بطاقة أو رمز أو RIB «للتسريع». فالاسترداد مرتبط بالأداء الأصلي ولا يحتاج إلى شيء من ذلك؛ والزبون الذي اعتاد ألّا تطلبوا منه شيئًا يكتشف أسرع أي احتيال باسمكم.
وإن سمحت أداتكم، فرسالة ثانية أقصر عند وصول refund.succeeded تُغلق الدائرة. وللعلاقة مع الزبون عبر الإنترنت بمعناها الواسع — التأكيدات والإيصالات وسياسة الإرجاع — راجعوا دليل الدفع الإلكتروني للتجارة الإلكترونية في المغرب.
أسئلة شائعة
كم يكلّف الاسترداد؟
لا شيء: استرداد مبلغ لزبون مجاني، كليًا أو جزئيًا، من بوابة التاجر كما عبر API. لا تُضاف أي رسوم استرداد إلى المبلغ المردود؛ بل يُخصم هذا المبلغ ببساطة من رصيدكم المتاح. ووضع الاختبار، المجاني بدوره وبلا حد زمني، يتيح لكم تجربة استرداد كامل، من الأداء إلى التصدير، قبل خدمة زبناء حقيقيين.
هل يمكن القيام باسترداد جزئي؟
نعم. عبر API، أضيفوا refundAmount بالمبلغ المراد ردّه، بالدرهم وبالوحدات الرئيسية؛ ودون هذا الحقل يكون الاسترداد كليًا. ومن بوابة التاجر، تختارون بالطريقة نفسها بين المبلغ الكامل ومبلغ جزئي. حالات نموذجية: منتج ناقص، أو خدمة أُنجزت جزئيًا، أو بادرة تجارية. احسبوا المبلغ الدقيق قبل إطلاق العملية.
ماذا يحدث إذا لم يكفِ الرصيد للاسترداد؟
تُوقَف العملية بدل إحداث رصيد مدين: فلا سحب على المكشوف في حساب الأداء ولا ائتمان. تحقّقوا من رصيدكم المتاح في بوابة التاجر أو عبر GET /v1/wallet، ثم انتظروا مستخلصات جديدة أو غذّوا الحساب عبر POST /v1/wallet/cash-ins. وفي الأثناء، أخبروا الزبون بأن استرداده قيد التحضير بدل تركه دون أخبار.
متى يرى الزبون المال يعود إلى بطاقته؟
الاسترداد ليس فوريًا من جهة البنك، ولا ننشر أي أجل: فالعودة تتوقف على معالجة بنك الزبون. ومن جهتكم، يخبركم الحدث refund.succeeded، أو القراءة عبر GET /v1/refunds/{reference}، بأن الاسترداد نجح. أخبروا الزبون بأن العملية انطلقت، وأعطوه مرجع الاسترداد الخاص بكم، ولا تعِدوه بتاريخ محدّد.
من يستطيع استرداد مبلغ لزبون في بوابة التاجر؟
المالك أو مسيّر مخوَّل: فهما من يُطلقان العمليات الصادرة. يستخلص أمين الصندوق دون أن يرى الرصيد أو التحويلات، ولا يستردّ؛ ويقرأ المحاسب كل شيء دون أن يُطلق أي عملية. لذا يُرفع الطلب الوارد إلى الصندوق إلى المسيّر. وعبر API، ينطلق النداء بمفتاح API الخاص بكم، وهو سرّ لا يغادر خادمكم أبدًا.
هل نستردّ أم نلغي رابط دفع لم يُؤدَّ؟
نلغيه. ما دام الرابط غير مؤدّى، فلا شيء يُستردّ: ألغوه، والرابط الملغى لا يمكن أداؤه بعد ذلك. وعبر API، النداء هو POST /v1/payment-links/{reference}/cancel. والمنطق نفسه لجلسة دفع غير مؤدّاة، عبر POST /v1/payment-sessions/{sessionId}/cancel. فالاسترداد لا يخص إلا الأداءات الناجحة؛ ومن بين الإجراءين، هو وحده ما يحرّك رصيدكم.
هل يظهر الاسترداد في كشف حسابي البنكي؟
ليس في سطر مستقل. الدفعة نحو بنككم عملية مستقلة، بمرجعها الخاص في كشفكم؛ وقد يُنقص الاسترداد الدفعة التالية، دون سطر مستقل في كشف الحساب البنكي. والتفصيل يُقرأ في تصدير حساب الأداء الخاص بكم، حيث كل استرداد حركة مستقلة، مرتبطة بالمعاملة الأصلية وحاملة لمرجع الاسترداد.
أي سبب نذكر في استرداد عبر API؟
لا يقبل الحقل reason إلا قيمة من قائمة محدّدة: MERCHANT_CANCELLATION حين تلغون أنتم، وCUSTOMER_CANCELLATION حين يتراجع الزبون، وOTHER لأي سبب آخر؛ وتضمّ القائمة أيضًا PLATFORM_ORCHESTRATION. والنص الحرّ مرفوض فيه. ضعوا شروحكم في note، في حدود 128 حرفًا — «منتج ناقص»، «بادرة تجارية» — واحتفظوا بالصياغة نفسها في نظامكم.
الخطوة التالية
أنشئوا حساب الاختبار — ابدؤوا في وضع الاختبار — ثم أدّوا طلبية ببطاقة الاختبار، واستردّوها جزئيًا عبر API، وانتظروا refund.succeeded، وابحثوا عن السطر في التصدير. تفصّل وحدة «الاستردادات» في توثيق API نقاط النهاية الثلاث، ويساعدكم ربط الأداء بالطلبية على إيجاد operationId لكل أداء.
بقلم فريق ChariPay.