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

دمج API للدفع في المغرب: مفاتيح الاختبار والإنتاج، الروابط، الجلسات، الويب هوك الموقّع، والانتقال إلى الإنتاج

دمج API للدفع في المغرب: مفاتيح الاختبار والإنتاج على عنوان واحد، روابط وجلسات الدفع، ويب هوك موقّع بـ HMAC، Idempotency، بطاقة الاختبار، والانتقال إلى الإنتاج.

دمج API الدفع في المغرب يقوم على أربع لبنات: مفتاح API يحدّد البيئة، وعملية إنشاء من جهة الخادم — رابط دفع أو جلسة دفع —، وويب هوك موقَّع هو وحده المرجع، ومفتاح Idempotency حتى لا تستخلصوا المبلغ نفسه مرتين أبدًا. يعرض هذا الدليل هذه اللبنات بترتيب إدماج حقيقي، بشيفرة تعمل كما هي على https://api-psp.charipay.ma: أمثلة curl وNode وPython لرابط الدفع، ثم الجلسة واستجابتها، والتحقق من الويب هوك، وغلاف الأخطاء. ويُختَم بالسندبوكس — المفتوحة منذ التسجيل، مجانًا ودون حدّ زمني — وبالقائمة الدقيقة لما يتغيّر يوم الانتقال إلى الإنتاج: المفتاح، وسرّ الويب هوك، ولا شيء سواهما.

واجهات API للدفع في المغرب: ما الذي يجب أن تقدّمه واجهة تقف خلفها مؤسسة مرخَّصة

في المغرب، الاستخلاص لحساب الغير نشاط خاضع للتنظيم. فالجهة التي تتسلّم مال المشتري، وتحتفظ به طوال المعالجة، ثم تعيده إلى التاجر، يجب أن تكون مرخَّصة من بنك المغرب. لذلك لا توجد واجهة برمجية للدفع في المغرب «قائمة بذاتها»: خلف نقاط النهاية (endpoints) مؤسسةٌ، وحسابٌ يصل إليه المال، والتزاماتُ امتثال لا يحلّها سطر من الشيفرة. تشغّل ChariPay شركةُ Chari Money، وهي مؤسسة أداء مرخَّصة من بنك المغرب؛ والمنصة حاصلة على شهادة PCI DSS من المستوى الأول، تُجدَّد كل سنة، وتُعالَج المعطيات الشخصية في إطار القانون ‎09-08. تشرح صفحة بوابة الدفع في المغرب ما يغطيه هذا الإطار وكيف تتحققون من ترخيص ما؛ أما هنا فنقف عند ما يترتب عليه تقنيًا.

وقبل أي سطر من شيفرة دمج الدفع الإلكتروني في المغرب، خمس نقاط تفصل بين واجهة «تجيب» وواجهة يمكن أن تُطلقوا عليها منتجًا:

  • مُشغِّل مرخَّص ومُسمّى. يجب أن تستطيعوا الإجابة عن سؤال: «أي جهة هي المرخَّصة، وهل تمرّ أموالي عبرها؟». وفي ChariPay، الجواب اسم واحد: Chari Money.
  • تسوية بالدرهم، في حساب باسم الشركة. ليس في الواجهة حقل للعملة: كل شيء بالدرهم (MAD)، بالوحدات الكبرى وبرقمين عشريين (149.90). وكل أداء ناجح يصبح متاحًا في اللحظة على حساب الأداء الخاص بالتاجر — حساب أداء تديره Chari Money، بـRIB باسم الشركة، يمكن الاطلاع عليه عبر الواجهة.
  • ‎3-D Secure على البطاقة، دون أي بيانات بطاقة عندكم. تُدخَل بطاقات Visa وMastercard وMaroc Pay في صفحة الدفع المستضافة، داخل نطاق حاصل على شهادة PCI DSS من المستوى الأول. ولا يرى خادمكم رقم بطاقة أبدًا.
  • النقد في الوكالة، عبر الويب هوك نفسه. ChariPay هي بوابة الدفع الوحيدة في المغرب التي تستخلص النقد أيضًا في الوكالة: يتلقى المشتري مرجعًا — هو مرجع رابط دفع أحادي الاستعمال —، ويؤدّيه في وكالة من شبكة Chari، فيصلكم payment.succeeded تمامًا كما في أداء بالبطاقة. أما معالجة الويب هوك فلا يتغيّر فيها سطر واحد.
  • ويب هوك موقّع، وIdempotency موثّقة، وأخطاء برموز ثابتة — وتوثيق عمومي. يُولَّد توثيق الواجهة البرمجية من مواصفة OpenAPI التي تنشرها الواجهة نفسها، ويُنزَّل مع مجموعة Postman: يمكنكم التحقق من كل ما يقوله هذا الدليل مقابل العقد ذاته.

ثلاثة أنماط للإدماج: رابط الدفع، صفحة الدفع المستضافة، الواجهة المباشرة

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

النمطلمنما تكتبونهبيانات البطاقةنقطة الدخول
رابط الدفعبيع بلا موقع: عرض سعر، فاتورة، واتساب، شبّاكنداء واحد من الخادم، أو لا شيء (بوابة التاجر تكفي)لا تمرّ عندكم أبدًاPOST /v1/payment-links
جلسة دفع + صفحة الدفع المستضافةمتجر إلكتروني أو تطبيق بمسار طلبإنشاء من الخادم، وإعادة توجيه، وويب هوكلا تمرّ عندكم أبدًاPOST /v1/payment-sessions
الدفع المباشر (Checkout Direct)استمارة بطاقة خاصة بكم، حقلًا بحقلverify → submit → ‎3-D Secure → returnداخل نطاق امتثالكم/checkout/* بمفتاح vk

رابط الدفع أقصر الأنماط: مبلغ ووصف، فتعيد الواجهة صفحة دفع مستضافة جاهزة للمشاركة — مع رمز QR وملصق PDF قابل للطباعة. إنه الجواب المناسب لعرض سعر قُبل على واتساب، أو اشتراك في جمعية، أو عربون، أو بيع على الشبّاك. تُظهر صفحة روابط الدفع ما يراه المشتري؛ وتسرد وحدة روابط الدفع في التوثيق نقاط النهاية السبع.

جلسة الدفع مصمَّمة لموقع تاجر: تتحوّل الطلبية إلى جلسة، وتوجّهون المشتري إلى صفحة الدفع المستضافة المحمية بـ‎3-D Secure، وتؤكّدون الطلبية عند وصول الويب هوك. تصف صفحة الأداء عبر الإنترنت المسار؛ وتفصّل وحدة جلسات الدفع الحقول.

الدفع المباشر لا معنى له إلا إن كان لديكم سبب محدّد لرسم شاشة البطاقة بأنفسكم: فهو يُدخلكم نطاق امتثال بيانات البطاقات، ونقاطه لا تُصادَق بمفتاح API الخاص بكم، بل بالجلسة ومفتاح تحقّق أحادي الاستعمال. ولأغلب المشاريع يكفي النمطان الأولان — بما في ذلك متجر Shopify أو WooCommerce، الذي يُربط برابط دفع أو بجلسة تُنشأ من جهة الخادم، دون تثبيت أي إضافة.

المصادقة والبيئات

يحمل كل طلب مفتاح API الخاص بكم في الترويسة X-CHARI-PAY-API-KEY. لا مسار OAuth ولا رمز يُجدَّد: المفتاح يكفي، وهو الذي يحدّد البيئة. يبدأ مفتاح السندبوكس بـchari_sk_test_، ومفتاح الإنتاج بـchari_sk_live_. وللواجهة عنوان أساسي واحد، https://api-psp.charipay.ma، في الاختبار كما في الإنتاج: لا تختارون البيئة في العنوان ولا في ترويسة، بل تختارونها باختيار المفتاح. وهذا مقصود — فيستحيل إرسال طلب اختباري إلى الإنتاج خطأً، ولا يتطلّب الانتقال إلى الإنتاج أي تغيير في العنوان.

ومفتاح الاختبار في متناولكم منذ الآن: ابدؤوا في وضع الاختبار؛ ستُطلب منكم ثلاثة حقول — الاسم، والبريد الإلكتروني المهني، والمؤسسة —، ثم يصلكم بالبريد رابط تفعيل تختارون عبره كلمة المرور، فتُنشئون مفتاح chari_sk_test_… من بوابة التاجر، ثم تُجرون أول أداء تجريبي ببطاقة الاختبار. وضع الاختبار مجاني، بلا حدّ زمني، ولا موافقة تنتظرونها؛ أما التحقق من مؤسستكم (KYB) فلا يشترطه إلا الانتقال إلى الإنتاج.

ثلاث قواعد للمفاتيح، حتى قبل النداء الأول:

  1. 1المفتاح سرّ. يعيش على خادمكم، في متغيّر بيئة أو خزنة أسرار. لا في شيفرة ينفّذها المتصفح أبدًا، ولا في تطبيق جوال، ولا في مستودع git.
  2. 2للمفتاح نطاق. تُصدَر المفاتيح لكل مؤسسة ولكل بيئة، مع صلاحيات لكل وحدة: لا تمنحوا إلا ما يستدعيه إدماجكم فعلًا. ولا يُعرض المفتاح الكامل إلا مرة واحدة، عند إنشائه — انسخوه في تلك اللحظة.
  3. 3**نقاط /checkout/* هي الاستثناء.** ينفّذها متصفّح المشتري، وتحملها الجلسة نفسها بمفتاح التحقق vk الأحادي الاستعمال. لا ترسلوا إليها مفتاح API الخاص بكم أبدًا.

العادات الصحيحة — التدوير، والتسريب، والمفاتيح المشتركة داخل فريق — مفصّلة في حماية مفاتيح الواجهة.

إنشاء رابط دفع

أول نداء مفيد تكفيه ثلاثة حقول: مبلغ بالدرهم، ووصف يُعرض على المشتري، وexternalId مشتقّ من مرجع طلبيتكم يجعل النداء قابلًا للإعادة دون أثر جانبي. أما ترويسة Idempotency-Key فتحمي من إعادة المحاولة الشبكية — ونعود إليها أدناه.

bash
curl -X POST 'https://api-psp.charipay.ma/v1/payment-links' \
  -H 'X-CHARI-PAY-API-KEY: chari_sk_test_...' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: order-1042-link' \
  -d '{
    "amount": 149.90,
    "description": "Order #1042",
    "singleUse": true,
    "externalId": "order-1042",
    "customerEmail": "amine.bennani@example.com",
    "customerPhone": "+212600000000",
    "metadata": { "cartId": "c_987" }
  }'

النداء نفسه بـNode، عبر fetch ومفتاح يُقرأ من البيئة:

javascript
const response = await fetch('https://api-psp.charipay.ma/v1/payment-links', {
  method: 'POST',
  headers: {
    'X-CHARI-PAY-API-KEY': process.env.CHARI_PAY_API_KEY,
    'Content-Type': 'application/json',
    'Idempotency-Key': 'order-1042-link',
  },
  body: JSON.stringify({
    amount: 149.9,
    description: 'Order #1042',
    singleUse: true,
    externalId: 'order-1042',
    customerEmail: 'amine.bennani@example.com',
    metadata: { cartId: 'c_987' },
  }),
});

if (!response.ok) throw new Error(await response.text());
const link = await response.json();
// link.reference — e.g. "pl_3ND8xk"; link.payUrl — the payment page to share

وبـPython، عبر requests:

python
import os, requests

response = requests.post(
    'https://api-psp.charipay.ma/v1/payment-links',
    headers={
        'X-CHARI-PAY-API-KEY': os.environ['CHARI_PAY_API_KEY'],
        'Content-Type': 'application/json',
        'Idempotency-Key': 'order-1042-link',
    },
    json={
        'amount': 149.90,
        'description': 'Order #1042',
        'singleUse': True,
        'externalId': 'order-1042',
        'customerEmail': 'amine.bennani@example.com',
        'metadata': {'cartId': 'c_987'},
    },
)
response.raise_for_status()
link = response.json()
print(link['reference'], link['payUrl'])

تصل الاستجابة بالحالة 201 حاملةً مرجع الرابط reference (على شكل pl_…)، وحالته status (ACTIVE)، والعملة currency (MAD دائمًا)، وpayUrl — الصفحة المستضافة التي ترسلون إليها المشتري. وإن أعدتم النداء بـexternalId نفسه، أعادت الواجهة الرابط القائم بالحالة 200 بدل إنشاء رابط ثانٍ: اختبروا رمز الحالة، فكلاهما نجاح.

أربعة خيارات تستحق المعرفة منذ اليوم الأول. يجعل singleUse: false الرابط قابلًا لإعادة الاستعمال — وهو مفيد لملصق في واجهة المحل أو لاشتراك دوري في جمعية. ويحدّد expiresAt تاريخ انتهاء (بصيغة ISO-8601 وتوقيت UTC، في المستقبل). وينشئ paymentMethod: "CASH" رابطًا يُؤدّى نقدًا: يتلقى المشتري cashinCode يقدّمه في الوكالة، ويؤكّد لكم ويب هوك payment.succeeded الإيداع. وأخيرًا تخصّص acceptUrl وdeclineUrl وnotificationUrl — وكلها بـhttps:// — العودة والإشعار لهذا الرابط تحديدًا. أما رمز QR (GET /v1/payment-links/{reference}/qr) والملصق PDF (/poster) والإرسال بالبريد الإلكتروني (POST …/send) فنقاط نهاية من الوحدة نفسها؛ ويجعل POST …/cancel الرابط غير قابل للأداء إن لم تتمّ البيعة.

إنشاء جلسة دفع وتدبير العودة

في موقع تاجر، تحلّ جلسة الدفع محلّ الرابط: تحمل orderId الخاص بكم، والمشتري، وعناوين العودة. تُنشئونها من جهة الخادم، وتوجّهون المشتري إلى checkoutUrl الذي تعيده الواجهة، ثم تنتظرون الويب هوك.

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://your-store.ma/payment/success",
        "decline": "https://your-store.ma/payment/failure",
        "notification": "https://your-store.ma/webhooks/charipay"
      }
    },
    "metadata": { "cartId": "c_987", "source": "web" }
  }'

تحمل الاستجابة، بالحالة 201، الحقول الخمسة التي يحدّدها العقد: معرّف الجلسة sessionId، ورمزها من جهة الخادم sessionToken، ومفتاح التحقق verifyKey، وتاريخ انتهائها expiresAt، وcheckoutUrl الذي توجّهون إليه المشتري:

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

بعض التوضيحات حول الحقول. verifyKey هو مفتاح التحقق الأحادي الاستعمال الذي يمرّ في المعامل vk، ولا يعنيكم إلا في الدفع المباشر؛ مع صفحة الدفع المستضافة يكفيكم checkoutUrl، أما حالة الجلسة ومبلغها وتاريخ انتهائها فتقرؤونها عبر GET /v1/payment-sessions/{sessionId}. وorderId مرجعكم التجاري: يُعرض، ويعود في الويب هوكس، دون قيد تفرّد. أما externalId فشيء آخر: فريد لكل تاجر ولكل بيئة، ويجعل الإنشاء قابلًا للإعادة. والحقول الأربعة في config.customer إلزامية؛ وعناوين config.urls كلها اختيارية ويجب أن تكون بـhttps:// — فإن غاب accept أو decline سرت القيمة المضبوطة لحسابكم ثم القيمة الافتراضية للمنصة، وإن غاب notification سرت قيمة حسابكم. والجلسة أحادية الاستعمال، وتنتهي صلاحيتها افتراضيًا بعد 72 ساعة من إنشائها؛ ويقصّرها expiresAt. ويسمح config.keepAlive: true للمشتري بإعادة المحاولة بعد فشل؛ ويرسل لكم notifyOnFailure: true الحدث payment.failed أيضًا. أما كائن metadata (4 كيلوبايت على الأكثر) فيعود كما هو في ويب هوك الأداء.

عودة المشتري إلى موقعكم هي الموضع الذي تخطئ فيه معظم عمليات الإدماج. والقاعدة جملة واحدة: صدّقوا الويب هوك، لا إعادة التوجيه. إليكم التسلسل الصحيح:

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

الويب هوك: التحقق من التوقيع

لا يكتمل الأداء لحظة ندائكم: يمرّ المشتري عبر بنكه، ويجتاز ‎3-D Secure، ثم يعود — أو لا يعود. لذلك تصلكم النتيجة عبر ويب هوك. تصرّحون بعناوين الاستقبال من بوابة التاجر أو عبر POST /api/v1/partner/webhooks/endpoints، مع لائحة صريحة بالأحداث (enabledEvents): لا تشتركوا إلا فيما تعالجونه. ويجب أن يكون العنوان HTTPS عموميًا على المنفذ 443؛ ويُرفض localhost والعناوين الخاصة منذ التسجيل — وفي التطوير، استعملوا نفقًا (tunnel). ويتلقى كل عنوان سرّ توقيع خاصًا به، يُحفظ كما يُحفظ مفتاح API، والأسرار متمايزة بين السندبوكس والإنتاج.

تحمل كل عملية تسليم ترويستين: X-CHARI-SIGNATURE، وهو HMAC-SHA256 بالنظام الست عشري وبحروف صغيرة، محسوب على السلسلة timestamp + "." + rawBody — أي الطابع الزمني، ثم نقطة، ثم الجسم الخام —؛ وX-CHARI-TIMESTAMP، وهو الطابع الزمني بالميلي ثانية منذ epoch. وهذا التحقق، منقولًا عن توثيق الواجهة:

javascript
const crypto = require('crypto');

function verify(rawBody, signature, timestamp, secret) {
  // ±5-minute anti-replay window — the timestamp is in milliseconds.
  if (Math.abs(Date.now() - Number(timestamp)) > 5 * 60 * 1000) return false;

  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.${rawBody}`)
    .digest('hex');

  // A malformed signature must return false — never throw (500 → retries).
  if (!/^[0-9a-f]{64}$/i.test(signature)) return false;

  // Constant-time comparison: never `===`.
  return crypto.timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(signature, 'hex'));
}

أربع قواعد تحمل أمن هذا التحقق كله. احسبوا الـHMAC على البايتات الخام، قبل أي تحليل: فإطار العمل الذي يفكّ تسلسل JSON ثم يعيد تسلسله لا ينتج البايتات التي وُقّعت — وهذا أكثر الأخطاء شيوعًا. وارفضوا طابعًا زمنيًا يزيد انحرافه عن ±5 دقائق. وقارنوا في زمن ثابت، لا بـ=== أبدًا. وأثناء تدوير السرّ، اقبلوا أحد التوقيعين: فما دام السرّ القديم في مهلة السماح، تحمل كل عملية تسليم X-CHARI-SIGNATURE بالسرّ القديم وX-CHARI-SIGNATURE-NEXT بالجديد.

ثم تأتي إزالة التكرار، وهي الفرق الذي يكلّف أكثر من غيره حين يُخطأ فيه: أزيلوا التكرار اعتمادًا على `Chari-Event-Id`، لا على `Chari-Webhook-Id`. فالحدث المنطقي الواحد قد يُسلَّم عدة مرات؛ وتنال كل محاولة Chari-Webhook-Id خاصًا بها، يتغيّر إذن في كل محاولة، لكنها جميعًا تحمل Chari-Event-Id نفسه. والتسليم يتمّ مرة واحدة على الأقل، وقد يصل خارج الترتيب — وهذا بالتصميم. فلا تجيبوا بـ2xx إلا بعد أن يثبت التحديث التجاري وقيد إزالة التكرار معًا، وأنجزوا العمل الثقيل في الخلفية: فمعالجة تستغرق عشر ثوانٍ تنتهي بإطلاق إعادات تسليم، ثم بتعليق عنوانكم.

إن لم يُجب خادمكم بـ2xx، نعيد المحاولة: تنطلق المحاولة الأولى فورًا، ثم بعد دقيقة، فخمس دقائق، فثلاثين دقيقة، فساعة، ثم كل ست ساعات، حتى 16 محاولة على مدى نحو 72 ساعة؛ وبعدها يُعلَّم التسليم فاشلًا. ويعرض سجل التسليمات في بوابة التاجر كل محاولة، وجواب خادمكم، والجسم الدقيق للحدث، ويتيح إعادة الإرسال بنقرة واحدة. وبعد انقطاع أطول، طابقوا عبر GET /v1/transactions بدل انتظار ويب هوك لن يعود.

الأحداث التي ستنتظرونها أكثر من غيرها: payment.succeeded، وpayment.failed (إن طُلب عند الإنشاء)، وorder.paid، وrefund.succeeded، وrefund.failed، وللاشتراكات subscription.payment_succeeded وsubscription.payment_failed وsubscription.canceled. ويعود حقلا metadata وexternalId في كل حدث يخصّ المورد الذي حملهما: وهكذا تطابقون دون تخزين مراجعنا. ويتوفّر حدث اختباري على كل عنوان مصرَّح به — أرسلوه قبل أول أداء حقيقي. تسرد وحدة الويب هوكس الأحداث العشرين التي تُرسَل فعلًا؛ ويفصّل مقال إنجاح دمج الويب هوك الأخطاء الخمسة المعتادة والمعالجة الكاملة في خمسة عشر سطرًا.

آلية Idempotency: لا استخلاص مرتين

تنقطع الشبكة بين خادمكم وخادمنا، فيعيد عميل HTTP المحاولة، ولا تدرون هل مرّت المحاولة الأولى. وفي الأداء، إعادة المحاولة على غير هدى أقصر طريق إلى خصم المبلغ مرتين من الزبون نفسه. آليتان مستقلتان تجيبان عن عطبين مختلفين، وتتراكمان.

ترويسة `Idempotency-Key` تحمي من إعادة المحاولة الشبكية. ترسلونها مع عمليات الإنشاء؛ وإعادة نداء بالقيمة نفسها تعيد النتيجة الأولى بدل إنشاء نسخة مكرّرة. إنها الحماية من انتهاء المهلة، أو انقطاع الاتصال، أو طابور رسائل سلّم الرسالة مرتين. وحاجزها محصور في الحساب — فاحتفظوا بمفاتيح متمايزة بين اختباراتكم وإنتاجكم.

حقل `externalId` يحمي من إعادة الإصدار من جانبكم. وهو فريد لكل حساب ولكل بيئة، ويجعل الإنشاء عملية يمكنكم إعادة إطلاقها دون الاحتفاظ بأي حالة وسيطة: إنشاء مورد بـexternalId موجود سلفًا يعيد المورد القائم بالحالة 200 OK، بينما يجيب الإنشاء الحقيقي بـ201 Created. إنها الحماية من نظامكم نفسه حين يعيد إصدار النية ذاتها — مهمة أُعيد تشغيلها، أو طابور أُعيد تمريره، أو نقرة مزدوجة في المكتب الخلفي. وقد يوجد externalId نفسه مرة في السندبوكس ومرة في الإنتاج دون تعارض.

والاستردادات تستعمل `refundReference` للسبب نفسه: الاسترداد الجديد يجيب بـ202 Accepted — فهو يُنفَّذ لاتزامنيًا — وإعادة النداء بالمرجع نفسه تعيد الاسترداد القائم بالحالة 200، دون خصم ثانٍ. ولا 201 أبدًا. وفي الحالات الثلاث، اشتقّوا المفتاح من مرجع طلبيتكم — لا من قيمة عشوائية أبدًا، فهي تُفرغ الآلية من معناها. أنماط المفاتيح الجيدة، وما لا تفعله Idempotency، تجدونها في لا تحصّلوا مرتين أبدًا — شرح Idempotency.

الأخطاء ومعرّف correlationId

تتقاسم كل أخطاء واجهة التاجر (/v1) الغلاف نفسه: code ثابت يستطيع برنامجكم اختباره، وmessage مقروء، وcorrelationId تقدّمونه للدعم.

json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "amount: must be greater than 0"
  },
  "correlationId": "b0c1e2d3-4f56-7890-abcd-ef0123456789"
}

اختبروا code، لا message أبدًا: فقد يُعاد صوغ الرسالة أو تُترجَم أو تُدقَّق، أما الرمز فجزء من العقد. ويُعاد correlationId في كل الاستجابات، بما فيها الناجحة — سجّلوه بانتظام، فهو ما يتيح العثور على طلب بعينه في سجلاتنا. وإن أرسلتم X-Request-Id الخاص بكم، أُعيد في الاستجابة وصار هو correlationId.

الحالةالرموزمعناها، وما العمل
400VALIDATION_ERROR، MISSING_PARAMETER، INVALID_IDEMPOTENCY_KEYحقل لا يمرّ (مبلغ منعدم، عنوان ليس بـhttps://، مفتاح Idempotency غائب أو مفرط الطول في جلسة قابلة لإعادة الاستعمال). صحّحوا، ولا تعيدوا النداء كما هو.
401UNAUTHORIZED، INVALID_TOKENمفتاح API غائب أو غير صالح: تحقّقوا من الترويسة X-CHARI-PAY-API-KEY ومن بيئة المفتاح. وعلى /checkout/*، يدلّ INVALID_TOKEN على مفتاح vk غير صالح أو سبق استعماله.
403FORBIDDEN، PRODUCTION_ACCESS_NOT_ENABLEDصلاحية ناقصة في المفتاح، أو مفتاح إنتاج على حساب لم يُفعَّل إنتاجه بعد.
404OPERATION_NOT_FOUND، ORDER_NOT_FOUND، SESSION_NOT_FOUNDالمورد غير موجود في هذه البيئة — فمعرّف السندبوكس لا قيمة له في الإنتاج.
409IDEMPOTENCY_CONFLICT، SESSION_ALREADY_CONSUMED، SESSION_NOT_ACTIVEمفتاح Idempotency نفسه بجسم مختلف، أو جلسة أُدّيت أو أُلغيت: أنشئوا جلسة جديدة.
410SESSION_EXPIREDتجاوزت الجلسة تاريخ expiresAt. أنشئوا جلسة جديدة.
422WALLET_NOT_ACTIVE، PAYMENT_METHOD_CONSENT_REQUIREDطلب صحيح، لكن قاعدة عملية تمنعه.
429RATE_LIMITEDطلبات كثيرة: خفّفوا الوتيرة واقرؤوا الترويسة Retry-After. وتُعلن النقاط المكشوفة للعموم X-RateLimit-Limit وX-RateLimit-Remaining وX-RateLimit-Reset.
502BAAS_CHARI_ERRORفي السندبوكس، السبب في الغالب بطاقة غير بطاقة الاختبار. تحقّقوا من الـPAN المُدخَل.
5xx—من جانبنا. أعيدوا النداء بمفتاح Idempotency الخاص بكم بدل إنشاء مورد جديد.

قاعدتان تكمّلان الجدول. كل عنوان تصرّحون به — عودةً كان أو إشعارًا — يجب أن يكون بـhttps://، وإلا فالخطأ 400. وتسامحوا مع قيم enum الجديدة ومع غياب الحقول الاختيارية — فهي تغيب ولا تأتي بقيمة null أبدًا: هكذا تتطوّر الواجهة دون أن تكسر إدماجكم. ولنقاط التسجيل والبوابة صيغة أخطاء خاصة بها، برموز ERR-XXXX؛ أما الغلاف أعلاه فيسري على واجهة التاجر، تلك التي يناديها مفتاحكم.

الاسترداد والاشتراكات وحساب الأداء عبر الواجهة البرمجية

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

الاسترداد. POST /v1/refunds بمعرّف الأداء — operationId أو externalId —، وسبب (reason)، وrefundReference من اختياركم؛ كاملًا افتراضيًا، أو بمبلغ جزئي عبر refundAmount. تجيب الواجهة بـ202 ريثما تتمّ التسوية، ثم يصل refund.succeeded عبر ويب هوك — فالاسترداد ليس فوريًا من جهة البنك. واسترجاع المال لزبون مجاني، ويُخصم المبلغ من الرصيد المتاح في حساب الأداء الخاص بكم.

bash
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 '{
    "externalId": "order-2026-0421",
    "refundReference": "refund-order-2026-0421",
    "refundAmount": 100.00,
    "reason": "Item returned"
  }'

تصف وحدة الاستردادات نقاط النهاية الثلاث والحالات.

الاشتراكات. أنشئوا الزبون (POST /v1/clients) ثم الاشتراك (POST /v1/subscriptions) بدوريته ومبلغه؛ ويتمّ الأداء الأول بحضور الزبون، على صفحة الدفع المستضافة، بموافقته الصريحة على الاحتفاظ بوسيلة أدائه. ويُرسَل إشعار ما قبل الاقتطاع بالبريد الإلكتروني قبل كل استحقاق. والاقتطاع الذي يفشل يُعاد يوم الاستحقاق، ثم بعد يوم، فثلاثة أيام، فسبعة أيام، مع رابط دفع احتياطي يُرسَل إلى الزبون، ثم يُلغى الاشتراك — تتابعون ذلك عبر subscription.payment_failed وsubscription.canceled. وفي السندبوكس، يفرض POST /v1/subscriptions/{reference}/test-auto-pay الاستحقاق التالي: تجتازون سنة اشتراك في دقيقة، وتتحقّقون من فوترتكم قبل أن يتحمّلها زبون حقيقي. تفصّل وحدة الاشتراكات نقاط النهاية التسع — الإيقاف المؤقت، والاستئناف، والإلغاء، والاستحقاقات.

حساب الأداء. مال كل أداء ناجح متاح في اللحظة على حساب الأداء ChariPay الخاص بكم، الذي تديره Chari Money، والواجهة تُريكم إياه: يعيد GET /v1/wallet الرصيد، وGET /v1/wallet/account الـRIB باسم شركتكم، وGET /v1/wallet/account/rib-document الشهادة بصيغة PDF، بينما يغذّي POST /v1/wallet/cash-ins الحساب قبل عملية صادرة. ومن هذا الحساب ترسلون تحويلات إلى حسابات لدى البنوك المغربية وتؤدّون الفواتير من بوابة التاجر، وتعبّئون خطوط الاتصالات عبر الواجهة البرمجية؛ وتُخطَرون بالحركات الصادرة عبر ويب هوك (merchant_transfer.completed، وbill_payment.succeeded، وtopup.succeeded، وأحداث فشلها). تسرد وحدة المحفظة نقاط النهاية الأربع؛ وتصف صفحة المحفظة والدفعات ما يمكنكم فعله من الحساب، بما في ذلك الدفعة التلقائية كل ليلة متى ضُبط حساب التسوية.

الاختبار في السندبوكس

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

  1. 1سجّلوا: أنشئوا حساب الاختبار بثلاثة حقول — الاسم، والبريد الإلكتروني المهني، والمؤسسة.
  2. 2فعّلوا الحساب بالرابط الذي يصلكم بالبريد، واختاروا كلمة المرور؛ ورمز التفعيل لا يوجد إلا في ذلك البريد.
  3. 3سجّلوا الدخول إلى بوابة التاجر؛ وبحسب صلاحيات حسابكم، قد يُطلب منكم تطبيق TOTP — احتفظوا برموز الاستعادة.
  4. 4اختاروا مؤسستكم: هذه الاستجابة، لا الدخول، هي التي تحمل رمز جلستكم.
  5. 5اجتازوا المصادقة المعزَّزة (step-up): إنشاء مفتاح فعل حسّاس، ويُشترط له رمز تأكيد قصير العمر.
  6. 6اطّلعوا على الصلاحيات المتاحة، ولا تمنحوا إلا ما يستدعيه إدماجكم.
  7. 7أنشئوا مفتاحكم chari_sk_test_… — لا يُعرض إلا مرة واحدة: ضعوه فورًا في خزنة أسرار أو متغيّر بيئة.
  8. 8تحقّقوا عبر GET /v1/wallet: إن أجابكم الرصيد، فنداؤكم مُصادَق عليه.

في السندبوكس، بطاقة اختبار واحدة مقبولة: 4918 9141 0719 5005 (تُدخَل دون مسافات)، وCVV 123، وأي تاريخ انتهاء في المستقبل، و555 رمزًا لـ‎3-D Secure. تُطلق مسارًا حقيقيًا — صفحة الدفع، والمصادقة، والويب هوك — دون أي حركة مال. وأي PAN آخر، بما في ذلك بطاقات 4242… الخاصة بمنصات أخرى، يُرفض عند المنبع بالحالة 502 والرمز BAAS_CHARI_ERROR: إن صادفتم هذا الخطأ أثناء الاختبار، فتحقّقوا من البطاقة المُدخَلة أولًا.

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

الانتقال إلى الإنتاج

تُفتح السندبوكس فورًا؛ أما الإنتاج فيُفعَّل على حسابكم بعد التحقق من مؤسستكم. وإلى ذلك الحين، يتلقى مفتاح chari_sk_live_… خطأ 403 صريحًا بالرمز PRODUCTION_ACCESS_NOT_ENABLED — وليس هذا خللًا في الإدماج، بل خطوة إدارية. والخطوات بالترتيب: التحقق KYB (وثائق الهوية والسجل التجاري، بموافقة بشرية مزدوجة)، ثم دراسة ملفكم والعرض المسعَّر — عمولة بنسبة مئوية عن كل أداء ناجح، وكفالة، تُحدَّدان بحسب منتجاتكم ووسائل الدفع وأحجامكم —، ثم أداء رسوم التفعيل البالغة 6 000 درهم شاملة الضريبة مرة واحدة، ثم التفعيل. وجدول الأسعار الكامل في صفحة التسعير. ولا نَعِد بأي أجل: كل ملف يُدرَس.

يوم التبديل تغيّرون شيئين، ولا شيء غيرهما: مفتاح API — يحلّ chari_sk_live_… محلّ chari_sk_test_… — وسرّ الويب هوك، المتمايز في الإنتاج. أما العناوين والحمولات ورموز الأخطاء والقاعدة https://api-psp.charipay.ma فهي نفسها. وقبل التبديل، خمس تحقّقات تستحق وقتها: معالِج الويب هوك لديكم يتحقّق من التوقيع على الجسم الخام ويتحمّل التكرار؛ وعمليات الإنشاء ترسل مفتاح Idempotency مشتقًّا من مرجعكم؛ وتسجّلون correlationId لكل نداء؛ ومفاتيحكم في خزنة لا في المستودع؛ وقد اختبرتم استردادًا وأداءً فاشلًا. القائمة الكاملة، مع الإغفالات الثلاثة الأكثر تكرارًا، في من الاختبار إلى الإنتاج: قائمة التبديل.

التنزيلات

كل ما يؤكّده هذا الدليل يمكن التحقق منه مقابل العقد. ثلاثة موارد متاحة بحرية، دون حساب:

  • مواصفة OpenAPI — العقد نفسه، الذي يُولَّد منه المرجع المنشور على الموقع، دون إعادة صياغة؛ حمّلوه في مولّد العميل، أو أداة الاختبار، أو المحرّر.
  • مجموعة Postman — نقاط النهاية الـ62 جاهزة للتنفيذ، وفي مقدّمتها طلبات التسجيل الثمانية: من الصفر إلى أول مفتاح دون مغادرة Postman.
  • حزمة LLM — ملف Markdown لكل وحدة، وأدلة المصادقة والويب هوكس والأخطاء، وبطاقة الاختبار، والمواصفة: ما يحتاج مساعد برمجي إلى قراءته لإدماج الواجهة دون اتصال.

يغطي توثيق الواجهة البرمجية الوحدات الـ12 ونقاط النهاية الـ62، بأمثلة curl وJavaScript وPython وPHP؛ وتلخّص صفحة المطوّرين الأعراف المعتمدة والمسار من أول نداء إلى الإنتاج.

أسئلة شائعة

هل توجد API دفع مجانية في المغرب للاختبار؟

نعم. سندبوكس ChariPay مجانية، دون حدّ زمني، وذاتية الخدمة: تسجّلون عبر الإنترنت، وتفعّلون حسابكم بالبريد الإلكتروني، وتنشئون بأنفسكم مفتاح chari_sk_test_…، دون موعد ولا مراسلة للدعم. وهي تعمل على نقاط النهاية نفسها التي يعمل عليها الإنتاج، وترسل ويب هوكس موقّعة حقيقية، وتقبل بطاقة الاختبار 4918 9141 0719 5005. وحده الانتقال إلى الإنتاج مؤدّى عنه.

ما العنوان الأساسي، وكيف أختار البيئة؟

للواجهة عنوان أساسي واحد، https://api-psp.charipay.ma، في السندبوكس كما في الإنتاج. والمفتاح هو الذي يختار البيئة: chari_sk_test_… يستهدف السندبوكس، وchari_sk_live_… يستهدف الإنتاج، فلا يستطيع مفتاح اختبار أن يمسّ المال الحقيقي. والعناوين والحمولات ورموز الأخطاء متطابقة في الحالتين؛ ويوم الانتقال إلى الإنتاج تغيّرون المفتاح وسرّ الويب هوك، لا غير.

كيف أتحقق من ويب هوك؟

أعيدوا حساب HMAC-SHA256 بسرّ العنوان على قيمة X-CHARI-TIMESTAMP تليها نقطة ثم الجسم الخام، قبل أي تحليل، وقارنوا النتيجة في زمن ثابت بـX-CHARI-SIGNATURE. ارفضوا الطوابع الزمنية التي يزيد انحرافها عن ±5 دقائق، وأزيلوا التكرار اعتمادًا على Chari-Event-Id، ولا تجيبوا بـ2xx إلا بعد تسجيل المعالجة. وشيفرة التحقق الكاملة بـNode واردة أعلاه، وفي دليل الويب هوك ضمن توثيق الواجهة البرمجية.

ماذا يحدث إن تعذّر الوصول إلى خادمي؟

نعيد محاولة التسليم: فورًا، ثم بعد دقيقة، فخمس دقائق، فثلاثين دقيقة، فساعة، ثم كل ست ساعات، حتى 16 محاولة على مدى نحو 72 ساعة. وتحمل كل محاولة Chari-Webhook-Id جديدًا، لكن Chari-Event-Id نفسه. وبعد ذلك يُعلَّم التسليم فاشلًا؛ ويتيح سجل بوابة التاجر إعادة إرساله بنقرة واحدة، ويُستعمل GET /v1/transactions للمطابقة بعد انقطاع أطول.

كيف أتجنّب خصم المبلغ مرتين؟

أرسلوا ترويسة Idempotency-Key مع كل عملية إنشاء: إعادة المحاولة الشبكية بالقيمة نفسها تعيد النتيجة الأولى. وأضيفوا externalId مشتقًّا من مرجع طلبيتكم: إعادة الإصدار تعيد المورد القائم بالحالة 200 بدل 201. وفي الاسترداد، يؤدّي refundReference الدور نفسه — 202 عند الإنشاء، و200 عند الإعادة. وأخيرًا، أزيلوا تكرار الويب هوكس اعتمادًا على Chari-Event-Id.

هل يمكنني إدماج Shopify أو WooCommerce عبر الواجهة البرمجية؟

نعم، دون تثبيت أي إضافة: لا توجد إضافة، ولا حاجة إليها. للمتجر الذي لا مطوّر له، يُنشأ رابط دفع من بوابة التاجر ويُرسَل عند الطلب. ومع مطوّر، ينشئ خادمكم جلسة دفع عند تأكيد السلة، ويوجّه المشتري إلى صفحة الدفع المستضافة، ثم يؤكّد الطلبية عند وصول payment.succeeded. ويفصّل دليلا Shopify وWooCommerce في المدوّنة المسارين معًا.

كيف أستخلص النقد عبر الواجهة البرمجية؟

أنشئوا رابط دفع أحادي الاستعمال بـpaymentMethod: "CASH"، أو اتركوا المشتري يختار النقد على صفحة الرابط؛ أما الجلسة فلا تستخلص إلا البطاقة. يتلقى مرجعًا — هو cashinCode الخاص بالرابط — يقدّمه في وكالة من شبكة Chari، فيودع المبلغ، ويصلكم payment.succeeded: الويب هوك نفسه، وإزالة التكرار نفسها، والمطابقة نفسها كما في البطاقة. فـChariPay هي بوابة الدفع الوحيدة في المغرب التي تستخلص النقد أيضًا في الوكالة.

كم يستغرق الانتقال إلى الإنتاج؟

لا ننشر أي أجل، لأن كل ملف يُدرَس على حدة. أما الخطوات فمعروفة: التحقق KYB بوثائق هويتكم وسجلّكم التجاري، ثم دراسة الملف والعرض المسعَّر، ثم أداء رسوم التفعيل البالغة 6 000 درهم شاملة الضريبة، ثم تفعيل الإنتاج على حسابكم. ولا ينتظر إدماجكم شيئًا: يُبنى في السندبوكس خلال ذلك، وينتقل إلى الإنتاج بتغيير المفتاح.

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

افتحوا حساب الاختبار — ابدؤوا في وضع الاختبار — ثم نفّذوا أول POST /v1/payment-links وأدّوا الرابط ببطاقة الاختبار. وبعد ذلك افتحوا صفحة المطوّرين للأعراف المعتمدة، وتوثيق الواجهة البرمجية لكل نقطة نهاية. وحين يصل payment.succeeded إلى خادمكم بتوقيع صالح، تكونون قد أنجزتم الجوهر: فالانتقال إلى الإنتاج تغيير مفتاح لا أكثر.

بقلم فريق ChariPay.

اقرؤوا بعده

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

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

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

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

لا تحصّلوا مرتين أبدًا — شرح Idempotency

شرح Idempotency في المدفوعات: كيف تمنع ترويسة Idempotency-Key وحقل externalId خصم المبلغ من الزبون مرتين حين تنقطع الشبكة في منتصف النداء.

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

من الاختبار إلى الإنتاج: قائمة التبديل

الانتقال من السندبوكس إلى الإنتاج: قائمة التحقق قبل تبديل المفتاح — ويب هوك موقَّع، وIdempotency، والأسرار، والإغفالات الكلاسيكية.

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

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

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