الدليل الشامل لدمج مدفوعات CIB وEdahabia عبر SATIM في الجزائر
دليل تقني خطوة بخطوة يغطي إعداد SATIM API، وتدفقات الدفع ببطاقات CIB الائتمانية وEdahabia المدينة، ومعالجة الأخطاء، والنشر في الإنتاج للتجارة الإلكترونية الجزائرية.
مشهد المدفوعات الجزائري
إذا كنت تبني منصة تجارة إلكترونية أو أي تطبيق يقبل مدفوعات عبر الإنترنت في الجزائر، ستواجه حتمًا SATIM. شركة أتمتة المعاملات البنكية والنقدية (SATIM) هي المحول الوطني للدفع الإلكتروني الذي يعالج عمليًا جميع المعاملات المبنية على البطاقات في الجزائر. يقع بين التجار والبنوك وشبكات البطاقات، ويعمل كطبقة التوجيه والتسوية المركزية.
نوعان من البطاقات يهيمنان على السوق الجزائري:
- CIB (البطاقة بين البنوك): البطاقة الائتمانية/المدينة القياسية بين البنوك التي تصدرها البنوك الجزائرية. بطاقات CIB تحمل شعار شبكة CIB ومقبولة في أجهزة نقاط البيع والتجار عبر الإنترنت في جميع أنحاء البلاد.
- Edahabia: بطاقة الخصم المباشر لبريد الجزائر، مرتبطة بحسابات CCP (الحساب الجاري البريدي). مع أكثر من 20 مليون حساب CCP في الجزائر، تمثل Edahabia أكبر قاعدة دفع محتملة في البلاد.
كلا نوعي البطاقات يُعالجان عبر بوابة الدفع SATIM (يُشار إليها أحيانًا بـ SATIM e-payment أو SATIM IPAY). من الناحية التقنية، تدفق التكامل متطابق تقريبًا لكليهما. تتعامل البوابة مع اكتشاف نوع البطاقة تلقائيًا بناءً على رقم البطاقة الذي يدخله العميل.
المتطلبات المسبقة
قبل كتابة أي كود، تحتاج ما يلي:
- حساب تاجر لدى بنك يدعم SATIM e-payment (معظم البنوك الجزائرية الكبرى تدعمه: BNA، CPA، BEA، BADR، Gulf Bank Algeria، من بين آخرين).
- معرف الجهاز (بصيغة:
E010XXXXXX)، يُقدم عبر بوابة SATIM لتعريف جهاز التاجر الخاص بك. - اسم المستخدم وكلمة المرور لواجهة SATIM API، يُقدمان عبر بوابة SATIM بعد الموافقة على حساب التاجر.
- بيانات اعتماد الاختبار لبيئة sandbox على
test2.satim.dz، تُقدم أيضًا عبر بوابة SATIM.
عملية التسجيل تتم بالكامل عبر بوابة SATIM على cibweb.dz. اتبع التعليمات هناك لتقديم مستنداتك واستلام بيانات الاعتماد. الجداول الزمنية تختلف، لذا ابدأ مبكرًا. إذا كنت بحاجة لمساعدة في عملية الاعتماد، نقدم استشارات اعتماد SATIM.
كيف يعمل تدفق الدفع
يستخدم SATIM تدفق دفع قائم على إعادة التوجيه، مشابه مفاهيميًا لـ 3D Secure أو Stripe Checkout. خادمك لا يتعامل أبدًا مع بيانات البطاقة الخام. إليك التسلسل الكامل:
- تسجيل الطلب: يستدعي الخادم الخلفي نقطة النهاية
register.doمن SATIM مع مبلغ الطلب ورقم طلب فريد وعناوين URL للاستدعاء الراجع. يرد SATIM بـorderIdوformUrl. - إعادة توجيه العميل: تعيد توجيه متصفح العميل إلى
formUrl. يستضيف SATIM صفحة الدفع حيث يدخل العميل تفاصيل بطاقته CIB أو Edahabia. - معالجة الدفع: يعالج SATIM المعاملة مع البنك المصدر. قد يرى العميل شاشة تحقق OTP/كلمة مرور (مشابهة لـ 3DS).
- إعادة التوجيه الراجع: بعد الدفع (نجاح أو فشل)، يعيد SATIM توجيه العميل إلى
returnUrlأوfailUrlمعorderIdكمعامل استعلام. - تأكيد من جانب الخادم: يستدعي الخادم الخلفي نقطة النهاية
acknowledgeTransaction.doللتحقق من حالة المعاملة. هذه هي الخطوة الحرجة؛ لا تثق أبدًا بإعادة التوجيه من جانب العميل وحدها.
Customer Your Server SATIM Gateway Bank
| | | |
| Place Order | | |
|----------------->| | |
| | register.do | |
| |------------------->| |
| | orderId + formUrl | |
| |<-------------------| |
| Redirect to | | |
| formUrl | | |
|<-----------------| | |
| Enter card info | | |
|---------------------------------------->| |
| | | Process payment |
| | |----------------->|
| | | Auth response |
| | |<-----------------|
| Redirect to | | |
| returnUrl | | |
|<-----------------------------------------| |
| Page loads | | |
|----------------->| | |
| | acknowledgeTransaction.do |
| |------------------->| |
| | Payment status | |
| |<-------------------| |
| Order confirmed | | |
|<-----------------| | |
الإعداد باستخدام satim-ts
حزمة @bakissation/satim هي SDK جاهزة للإنتاج بـ TypeScript تغلف SATIM API بأنماط صارمة وتحقق من المدخلات وتحويل تلقائي للمبالغ ومعالجة شاملة للأخطاء.
التثبيت
1npm install @bakissation/satim
تدعم SDK كلاً من ESM وCommonJS، وتتطلب Node.js 18 أو أحدث، وليس لها تبعيات خارجية (تستخدم fetch API الأصلية).
الإعداد
أبسط نهج هو متغيرات البيئة. أنشئ ملف .env:
1SATIM_USERNAME=your_merchant_username 2SATIM_PASSWORD=your_merchant_password 3SATIM_TERMINAL_ID=E010XXXXXX 4SATIM_API_URL=https://test2.satim.dz/payment/rest
ثم هيئ العميل:
1import { createSatimClient, fromEnv } from '@bakissation/satim'; 2 3const satim = createSatimClient(fromEnv());
للتحكم الأكثر صراحة، مرر الإعداد مباشرة:
1import { createSatimClient, API_BASE_URLS } from '@bakissation/satim'; 2 3const satim = createSatimClient({ 4 userName: process.env.SATIM_USERNAME!, 5 password: process.env.SATIM_PASSWORD!, 6 terminalId: process.env.SATIM_TERMINAL_ID!, 7 apiBaseUrl: API_BASE_URLS.TEST, // switch to API_BASE_URLS.PRODUCTION for live 8 language: 'fr', 9 currency: '012', // DZD (ISO 4217) 10});
تنفيذ تدفق الدفع
الخطوة 1: تسجيل الطلب
عندما يكون عميلك مستعدًا للدفع، أنشئ طلبًا على الخادم الخلفي:
1import { createSatimClient, fromEnv } from '@bakissation/satim'; 2import { randomUUID } from 'crypto'; 3 4const satim = createSatimClient(fromEnv()); 5 6async function createPayment(orderNumber: string, amountDzd: number) { 7 const response = await satim.register({ 8 orderNumber, // Max 10 chars, must be unique per transaction 9 amount: amountDzd, // In DZD (minimum 50 DZD), auto-converted to minor units 10 returnUrl: 'https://yoursite.com/payment/callback', 11 failUrl: 'https://yoursite.com/payment/failed', 12 description: 'Purchase from YourStore', 13 udf1: orderNumber, // Required: your internal reference 14 idempotencyKey: randomUUID(), // Prevents duplicate orders on retry 15 }); 16 17 if (response.isSuccessful()) { 18 // Save response.orderId to your database, linked to the order 19 return { 20 orderId: response.orderId, 21 redirectUrl: response.formUrl, 22 }; 23 } 24 25 throw new Error('Failed to register order with SATIM'); 26}
نقاط رئيسية حول استدعاء register:
- معالجة المبالغ: مرر المبالغ بالدينار كأرقام صحيحة أو عشرية (مثل
5000لـ 5,000 دينار، أو806.5لـ 806.50 دينار). SDK تحول تلقائيًا إلى الوحدات الصغرى (سنتيمات) كما يتطلب API. - رقم الطلب: بحد أقصى 10 أحرف. يجب أن يكون فريدًا لكل معاملة. إذا أعدت استخدام رقم طلب، سيرجع SATIM رمز خطأ 1.
- udf1: مطلوب من SATIM. استخدمه لمرجعك الداخلي (رقم فاتورة، معرف طلب، إلخ).
- العدم التكرار: مرر دائمًا
idempotencyKeyفي الإنتاج لمنع الرسوم المكررة إذا أُعيد طلبك بسبب مشاكل الشبكة.
الخطوة 2: إعادة توجيه العميل
على الواجهة الأمامية، أعد توجيه العميل إلى formUrl المرجع من استدعاء التسجيل:
1// In your API response handler (frontend) 2window.location.href = redirectUrl;
أو إذا كنت تبني خادمًا خلفيًا API-first، أرجع عنوان URL إلى واجهتك الأمامية/تطبيق الموبايل ودعه يتعامل مع إعادة التوجيه. صفحة الدفع SATIM تدعم الفرنسية والإنجليزية والعربية، قابلة للإعداد عبر معامل language.
الخطوة 3: معالجة الاستدعاء الراجع
بعد أن يكمل العميل (أو يتخلى عن) الدفع، يعيد SATIM توجيهه إلى returnUrl أو failUrl مع معامل استعلام orderId. على نقطة نهاية الاستدعاء الراجع، تحقق من الدفع من جانب الخادم:
1import { OrderStatus, interpretOrderStatus } from '@bakissation/satim'; 2 3async function handlePaymentCallback(orderId: string) { 4 const status = await satim.confirm(orderId); 5 6 if (status.isPaid()) { 7 // Payment successful, orderStatus === 2 (DEPOSITED) 8 console.log('Payment confirmed for order:', status.orderNumber); 9 console.log('Amount:', status.amount); 10 console.log('Card (masked):', status.pan); // e.g., 6280****7215 11 12 // Update your order status in the database 13 // Deliver the product/service 14 return { success: true, orderNumber: status.orderNumber }; 15 } 16 17 // Payment not successful 18 console.log('Payment status:', interpretOrderStatus(status.orderStatus)); 19 console.log('Details:', status.actionCodeDescription); 20 return { success: false, reason: status.actionCodeDescription }; 21}
طريقة confirm (المعروفة أيضًا بـ getOrderStatus) تستدعي نقطة النهاية acknowledgeTransaction.do من SATIM. هذه هي الطريقة الوحيدة الموثوقة لمعرفة ما إذا نجح الدفع. لا تعتمد أبدًا فقط على عنوان URL لإعادة التوجيه الذي وصل إليه العميل، لأنه يمكن تزويره أو التلاعب به.
الخطوة 4: الاسترجاع (عند الحاجة)
لاسترجاع معاملة مكتملة:
1async function refundPayment(orderId: string, amountDzd: number) { 2 const result = await satim.refund(orderId, amountDzd); 3 4 if (result.isSuccessful()) { 5 console.log('Refund processed successfully'); 6 return true; 7 } 8 9 console.error('Refund failed:', result.errorMessage); 10 return false; 11}
الاسترجاع يجب أن يشير إلى orderId الأصلي المرجع أثناء التسجيل. الاسترجاعات الجزئية مدعومة بتمرير مبلغ أقل من المعاملة الأصلية.
CIB مقابل Edahabia: ما يحتاج المطورون معرفته
من منظور تكامل API، مدفوعات CIB وEdahabia تُعالج بشكل متطابق من بوابة SATIM. لا تحتاج لتحديد نوع البطاقة عند تسجيل طلب. تحدد البوابة نوع البطاقة من رقم البطاقة الذي يدخله العميل في صفحة الدفع.
هناك بعض الاختلافات العملية يجب الانتباه لها:
- صيغة رقم البطاقة: بطاقات CIB تبدأ عادة بـ
6280(16 رقمًا). بطاقات Edahabia تتبع نظام ترقيم بريد الجزائر. كلاهما يُعالج عبر نفس نقاط النهايةregister.doوacknowledgeTransaction.do. - حدود المعاملات: بطاقات Edahabia قد يكون لها حدود معاملات يومية أقل مقارنة ببطاقات CIB، حسب إعداد حامل البطاقة مع بريد الجزائر. يجب أن يتعامل تطبيقك بلطف مع رفض "تجاوز الحد".
- جداول التسوية: قد تختلف التسوية لحسابك التجاري قليلاً بين معاملات CIB وEdahabia، حسب ترتيب البنك المكتسب. هذا يخص العمليات المصرفية وليس الكود.
- تحقق OTP: كلا نوعي البطاقات قد يتطلبان إدخال OTP أو كلمة مرور على صفحة الدفع SATIM. حقل
passwordفي بطاقات الاختبار يُستخدم لهذا الغرض في بيئة sandbox.
إذا كنت تحتاج تحديدًا للتعامل مع دفع فواتير أو نوع معاملة معين، يمكنك استخدام معامل fundingTypeIndicator (مثل 'CP' أو '698')، لكن لمشتريات التجارة الإلكترونية القياسية هذا غير مطلوب.
معالجة الأخطاء
توفر SDK فئات أخطاء مُنمطة لكل سيناريو فشل. معالجة الأخطاء القوية مهمة بشكل خاص لتكاملات الدفع حيث الفشل الصامت يمكن أن يؤدي لخسارة إيرادات أو رسوم مكررة.
1import { 2 SatimApiError, 3 ValidationError, 4 HttpError, 5 TimeoutError, 6 ConfigError, 7 mapSatimErrorCode, 8} from '@bakissation/satim'; 9 10async function safePayment(orderNumber: string, amount: number) { 11 try { 12 const response = await satim.register({ 13 orderNumber, 14 amount, 15 returnUrl: 'https://yoursite.com/payment/callback', 16 udf1: orderNumber, 17 idempotencyKey: crypto.randomUUID(), 18 }); 19 return response; 20 } catch (error) { 21 if (error instanceof ValidationError) { 22 // Bad input: invalid amount, missing fields, etc. 23 console.error(`Validation error [${error.code}]:`, error.message); 24 } else if (error instanceof SatimApiError) { 25 // SATIM rejected the request 26 console.error(`SATIM error [${error.satimErrorCode}]:`, error.message); 27 } else if (error instanceof TimeoutError) { 28 // Request timed out, safe to retry with same idempotency key 29 console.error(`Timeout after ${error.timeoutMs}ms`); 30 } else if (error instanceof HttpError) { 31 // Network or HTTP-level failure 32 console.error(`HTTP error [${error.httpStatus}]:`, error.message); 33 } else if (error instanceof ConfigError) { 34 // Missing or invalid configuration 35 console.error('Config error, missing keys:', error.missingKeys); 36 } 37 throw error; 38 } 39}
مرجع رموز أخطاء SATIM
هذه هي رموز الأخطاء التي يرجعها SATIM API عبر نقاط النهاية:
| الرمز | register.do | acknowledgeTransaction.do | refund.do |
|---|---|---|---|
| 0 | نجاح | نجاح | نجاح |
| 1 | طلب تمت معالجته مسبقًا | - | - |
| 2 | - | خطأ في بيانات الدفع | - |
| 3 | عملة غير معروفة | - | - |
| 4 | معامل مطلوب مفقود | - | - |
| 5 | معامل غير صالح | رفض الوصول | رفض الوصول / مبلغ غير صالح |
| 6 | - | طلب غير مسجل | طلب غير مسجل |
| 7 | خطأ في النظام | خطأ في النظام | خطأ في النظام |
| 14 | طريقة دفع غير صالحة | - | - |
رموز حالة الطلب
بعد استدعاء confirm() أو getOrderStatus()، تحقق من حقل orderStatus:
| الرمز | المعنى | الإجراء |
|---|---|---|
| 0 | مسجل ولم يُدفع | العميل تخلى أو لم يكمل الدفع بعد |
| -1 | رفض غير معروف | تواصل مع دعم SATIM إذا استمر |
| 1 | موافق عليه (تفويض مسبق محتجز) | بانتظار الإيداع |
| 2 | مُودع (الدفع مكتمل) | نفّذ الطلب |
| 3 | التفويض ملغى | الدفع ملغى قبل التسوية |
| 4 | مسترجع | تم استرجاع المعاملة |
| 6 | مرفوض | البطاقة رُفضت من البنك المصدر |
الاختبار مع بيئة SATIM sandbox
يوفر SATIM بيئة sandbox على https://test2.satim.dz/payment/rest لاختبار التكامل. استخدم بطاقات الاختبار التالية:
| رقم البطاقة | CVV2 | انتهاء الصلاحية | كلمة المرور | السيناريو |
|---|---|---|---|---|
| 6280581110007215 | 373 | 01/2027 | 123456 | دفع ناجح |
| 6280581110006712 | 897 | 01/2027 | 123456 | بطاقة محظورة مؤقتًا |
| 6280580610061110 | 260 | 01/2027 | 123456 | رصيد غير كافٍ |
| 6280580610061219 | 049 | 01/2027 | 123456 | تجاوز الحد |
| 6280581110006514 | 205 | 01/2027 | 123456 | CVV2 غير صحيح |
| 6280580610061011 | 992 | 01/2027 | 123456 | بطاقة ائتمان صالحة |
عند الاختبار:
- اضبط
SATIM_API_URLعلىhttps://test2.satim.dz/payment/rest. - استخدم بيانات اعتماد الاختبار المقدمة من بنكك (وليس بيانات الإنتاج).
- اختبر كل سيناريو: دفع ناجح، بطاقة مرفوضة، رصيد غير كافٍ، معالجة المهلة، ومنع تكرار الطلبات.
- تحقق أن استدعاء
confirm()يفسر كل حالة طلب بشكل صحيح. - اختبر رحلة المستخدم الكاملة، بما في ذلك ما يحدث عندما يغلق العميل المتصفح على صفحة الدفع SATIM دون إكمال المعاملة.
قائمة مراجعة نشر الإنتاج
قبل الانطلاق، راجع هذه القائمة:
الإعداد
- غيّر
SATIM_API_URLإلىhttps://satim.dz/payment/rest - استخدم بيانات اعتماد الإنتاج (اسم المستخدم، كلمة المرور، معرف الجهاز) من بنكك
- خزّن جميع بيانات الاعتماد في متغيرات البيئة، وليس في الكود المصدري
- تأكد أن ملفات
.envفي.gitignore - اضبط
NODE_ENV=productionلتعطيل التسجيل المفصل للتطوير
الأمان
- استخدم طريقة POST لجميع استدعاءات SATIM API (SDK تفعل ذلك افتراضيًا)
- تحقق دائمًا من حالة الدفع من جانب الخادم عبر
confirm()، ولا تثق أبدًا بإعادة التوجيه من جانب العميل - قدّم عناوين URL للاستدعاء الراجع عبر HTTPS فقط
- نفّذ مفاتيح عدم التكرار في كل استدعاء
register()لمنع الرسوم المكررة - لا تسجل أو تخزن أرقام البطاقات الكاملة أبدًا (SDK لا تكشفها أبدًا)
الموثوقية
- تعامل مع جميع أنواع الأخطاء (
SatimApiError،TimeoutError،HttpError،ValidationError) - نفّذ منطق إعادة المحاولة لسيناريوهات
TimeoutError(باستخدام نفس مفتاح عدم التكرار) - أعد مراقبة/تنبيهات للمدفوعات الفاشلة
- سجّل نتائج المعاملات (باستخدام مسجل SDK المدمج أو خاصتك عبر خيار
customLogger) - خزّن
orderIdمن SATIM في قاعدة بياناتك لكل معاملة، مما يتيح فحوصات الحالة المستقبلية والاسترجاعات
الاختبار
- شغّل اختبارات من طرف لطرف ضد بيئة sandbox قبل التحويل للإنتاج
- اختبر مع جميع سيناريوهات بطاقات sandbox (نجاح، رفض، أموال غير كافية، محظورة)
- تحقق أن تدفق الاسترجاع يعمل بشكل صحيح
- اختبر ما يحدث عندما يكون SATIM غير قابل للوصول (معالجة المهلة)
الامتثال
- تحقق مع بنكك أن تكاملك معتمد للاستخدام في الإنتاج
- تأكد أن شروط الخدمة وسياسة الخصوصية تغطي المدفوعات الإلكترونية
- احتفظ بسجلات جميع المعاملات كما تتطلب لوائح التجارة الجزائرية
متقدم: التكامل مع Fastify
إذا كنت تستخدم Fastify كإطار ويب، يوفر إضافة @bakissation/fastify-satim تكاملًا مبسطًا يسجل عميل SATIM كمزخرف Fastify:
1npm install @bakissation/fastify-satim
1import Fastify from 'fastify'; 2import satimPlugin from '@bakissation/fastify-satim'; 3 4const app = Fastify(); 5 6app.register(satimPlugin, { 7 userName: process.env.SATIM_USERNAME!, 8 password: process.env.SATIM_PASSWORD!, 9 terminalId: process.env.SATIM_TERMINAL_ID!, 10 apiBaseUrl: 'https://satim.dz/payment/rest', 11}); 12 13app.post('/pay', async (request, reply) => { 14 const { orderNumber, amount } = request.body as any; 15 16 const result = await app.satim.register({ 17 orderNumber, 18 amount, 19 returnUrl: 'https://yoursite.com/payment/callback', 20 udf1: orderNumber, 21 }); 22 23 if (result.isSuccessful()) { 24 return reply.redirect(result.formUrl!); 25 } 26 27 return reply.status(500).send({ error: 'Payment registration failed' }); 28});
هذا يتجنب إنشاء العميل يدويًا ويضمن إدارة دورة حياة عميل SATIM بواسطة Fastify.
هل تحتاج مساعدة في تكاملك؟
دمج المدفوعات عمل عالي المخاطر حيث الأخطاء تترجم مباشرة إلى خسارة إيرادات أو مشاكل امتثال. إذا كنت تحتاج مساعدة خبير في تكامل SATIM CIB/Edahabia، من الإعداد الأولي والتنسيق مع البنك إلى نشر الإنتاج والدعم المستمر، أقدم استشارات مخصصة لأنظمة الدفع الجزائرية.