لنفرض أن شركة تقنية مالية صغيرة تشغل منصة معالجة لبرنامجي بطاقات مسبقة الدفع، كل برنامج منهما مع بنك مصدر مختلف، وتستقبل طلبات التفويض من المستحوذين، وتوجه كل طلب إلى البنك الصحيح. وفي ليلة نهاية الشهر وصلت إلى فريق الدعم شكوى غريبة من عميل: «رفض الجهاز عمليتي برسالة "البنك لا يرد"، لكن المبلغ ظهر محجوزاً في تطبيق بطاقتي، ثم اختفى الحجز بعد ثوان». والعميل ليس مخطئاً في أي جزء من كلامه، فالرفض حدث، والحجز حدث، والإلغاء حدث، وكلها في أقل من خمس ثوان، والجهة التي رأت الصورة كاملة هي المحول الذي يقف في الوسط.
والمحول المالي Payment Switch هو البرنامج الذي يستقبل رسائل البطاقات من جهة، ويقرر إلى أين يرسل كل رسالة، وينتظر ردها، ويعيده إلى صاحبه، ويتصرف حين لا يأتي الرد. وقد رأينا في مقال رسائل البنوك شكل رسالة ISO 8583، وفي مقال الصراف الآلي كيف يتعامل طرف واحد مع المهلة والعكس، وفي مقال وحدات أمن العتاد HSM كيف تترجم كتلة الرقم السري بين المفاتيح في الطريق. وهذا المقال يجمعها في المكان الذي تلتقي فيه كلها.
وسوف نبني محولاً حقيقياً بمكتبة jPOS، أشهر إطار مفتوح المصدر لهذا الغرض بلغة Java، يوجه رسائل مستحوذ تجريبي إلى مصدرين تجريبيين، ثم نعيد قصة العميل كما هي، ونجرب مصدراً متوقفاً ورقمي تتبع متطابقين من مستحوذين مختلفين. وسوف نناقش في هذا المقال ما يلي:
- ما المحول ولماذا يوجد، وأين يقف في السعودية بحسب صفحة مدى لدى البنك المركزي.
- التوجيه بجدول BIN، ولماذا يعطي المحول كل رسالة رقم تتبع خاصاً به.
- jPOS وQ2: المحول كملفات إعداد، والقناة ChannelAdaptor، والمطابق QMUX، والخادم QServer.
- تجربة عملية كاملة في Containers، وثلاث مشكلات حقيقية واجهناها أثناء بنائها.
- المهلة والعكس والرد المتأخر، والمصدر المتوقف، وتعارض أرقام التتبع.
- رسائل إدارة الشبكة والإغلاق اليومي، والنيابة عن المصدر، والتسجيل الآمن.
- حالة FASTCash الموثقة، وكيف يبدو المحول في مؤسسة حقيقية، والأخطاء الشائعة، وقائمة تحقق.
لماذا يوجد محول أصلاً؟
تخيل أن كل مستحوذ يتصل مباشرة بكل بنك مصدر. مع عشرة مستحوذين وعشرين مصدراً تحتاج مئتي اتصال، ولكل اتصال مفاتيحه واختبارات توافقه ومراقبته، وكل مصدر جديد يحتاج عشرة اتصالات جديدة. وهذه بالضبط المشكلة التي ذكرناها في أول مقال رسائل البنوك عن عدد «المترجمين» بين الأطراف، والحل هنا مشابه: نقطة في الوسط يتصل بها الجميع، فيكون لكل طرف اتصال واحد، والنقطة في الوسط تعرف إلى أين يذهب كل شيء.
وهذا ما تفعله الشبكات الوطنية وشبكات البطاقات الدولية على مستوى البلد والعالم، وما تفعله المحولات الداخلية في البنوك والمعالجات على مستوى أصغر. وفي السعودية تذكر صفحة مدى لدى البنك المركزي أن قنوات الشبكة، من نقاط البيع والصرافات ومواقع التجارة الإلكترونية، متصلة كلها بنظام مدفوعات مركزي يوجه العمليات المالية التي تتم عليها إلى البنك المصدر للبطاقة. أي أن العملية التي تتم ببطاقة بنك على جهاز بنك آخر تمر بمحول في الوسط يعرف البنكين معاً. وقد شرحنا مدى وقنواته في مقال منظومة المدفوعات في السعودية، أما هنا فننزل إلى ما يحدث داخل المحول نفسه.
والمحول في الشركة الناشئة في قصتنا أصغر من ذلك بكثير، لكنه يحمل المسؤوليات نفسها: أن يعرف المصدر من رقم البطاقة، وأن يربط كل رد بطلبه بين آلاف الرسائل، وأن يرد على المستحوذ في وقت معقول حتى لو صمت المصدر، وأن يصحح ما يجب تصحيحه حين لا يعرف النتيجة، وأن يسجل كل ذلك دون أن يسجل ما لا يجب.
التوجيه: جدول BIN
أول ستة أرقام من رقم البطاقة، وفي الأنظمة الحديثة أول ثمانية، تسمى رقم تعريف المصدر BIN أو IIN، وهي ما يعرف المحول منه إلى أين يرسل الطلب. والمحول يحمل جدولاً يقابل بين نطاقات هذه الأرقام والوجهات، فإذا بدأ رقم البطاقة بـ 424242 ذهب الطلب إلى المصدر A، وإذا بدأ بـ 400005 ذهب إلى المصدر B، وإذا لم يطابق شيئاً رد المحول بنفسه برمز 15 «لا يوجد مصدر بهذا الرقم» No such issuer، بحسب جدول رموز الرد في ملخص المعيار.
وفي الواقع الجدول أعقد من سطرين. فالنطاقات تتداخل، فقد يكون لبنك واحد نطاق ستة أرقام ولبرنامج داخله نطاق ثمانية أرقام يذهب إلى معالج آخر، ولهذا يطابق المحول أطول بادئة أولاً. والجدول يتغير كل أسبوع تقريباً، ببرامج بطاقات جديدة وبطاقات تنقل بين معالجين، فيجب أن يحدث دون إعادة تشغيل، وأن يكون لكل تغيير فيه سجل ومراجعة، لأن سطراً خاطئاً واحداً يرسل عمليات بنك كامل إلى بنك آخر. وفي مختبرنا جعلنا الجدول مصفوفة في أول السكربت حتى يرى القارئ المنطق كله في مكان واحد:
String[][] ROUTES = { {"424242", "issuer-a"}, {"400005", "issuer-b"} }; // BIN table, longest prefix first in real lifeلماذا يعطي المحول كل رسالة رقمه الخاص؟
رأينا في مقال الصراف أن رقم التتبع STAN في الحقل 11 يربط الرد بطلبه. لكن هذا الرقم يولده المستحوذ، وكل مستحوذ يولده مستقلاً عن غيره. فإذا وصل إلى المحول في اللحظة نفسها طلبان من مستحوذين مختلفين برقم التتبع نفسه، ورقم الجهاز نفسه في الحقل 41، وذهبا إلى المصدر نفسه، فبماذا يعرف المحول أي رد لأي طلب؟ لا يمكن، إذا مررهما كما هما. ولهذا يعطي المحول كل رسالة يرسلها رقم تتبع من تسلسله هو، ويحفظ المقابلة بين رقمه ورقم المستحوذ، ويعيد رقم المستحوذ في الرد. وهذا ليس افتراضاً نظرياً، فقد جربناه في المختبر في الاتجاهين كما سنرى، ورأينا بالضبط ما يحدث حين لا يفعل المحول ذلك.
ويضيف المحول أيضاً معرف المؤسسة المستحوذة في الحقل 32، حتى يعرف المصدر من أين جاء الطلب، ويحتاجه في التسوية والاعتراضات. ورأينا في سجل المختبر الرسالة نفسها قبل المحول وبعده: دخلت برقم 000101 ودون الحقل 32، وخرجت إلى المصدر برقم 000001 ومعها الحقل 32 بقيمة 100001.
jPOS وQ2: المحول كملفات إعداد
يمكن كتابة محول من الصفر، لكن أغلب المشكلات التي سنراه يتعامل معها، من إعادة الاتصال إلى مطابقة الردود إلى المهلة، حلتها أطر جاهزة منذ سنوات. وأشهرها في العالم المفتوح المصدر jPOS، وهو إطار Java لرسائل ISO 8583 ومعالجتها، ومستودعه على GitHub بترخيص AGPL الإصدار 3. وهذا الترخيص يستحق انتباه أي شركة قبل أن تبني عليه منتجاً: فبحسب نصه المنشور في موقع المشروع، من يعدل البرنامج ويتيحه لمستخدمين عبر الشبكة عليه أن يعرض عليهم الكود المصدري للنسخة المعدلة. فاعرض الأمر على مستشارك القانوني، واسأل أصحاب المشروع عن خيارات الترخيص الأخرى إن احتجتها.
وقلب jPOS حاوية خدمات اسمها Q2: تقرأ مجلداً اسمه deploy، وكل ملف XML فيه يصف خدمة، فتنشئها بترتيب أسماء الملفات، وإذا أضفت ملفاً جديداً أثناء التشغيل أنشأت الخدمة دون إعادة تشغيل. والخدمات التي نحتاجها أربع:
- القناة ChannelAdaptor: تحافظ على اتصال TCP مع المصدر، وتعيد الاتصال إذا انقطع، وتحول الرسائل بين الشبكة وطابور في الذاكرة.
- المطابق QMUX: يرسل الطلب إلى طابور القناة، وينتظر رده، ويطابقه بمفتاح من حقول الرسالة، فيستطيع مئات الطلبات أن تشترك في اتصال واحد مع المصدر.
- الخادم QServer: يستمع على منفذ للمستحوذين، ويعطي كل رسالة تصل إلى «مستمع طلبات» Request Listener يقرر ما يفعل بها.
- السجل Logger: يكتب كل رسالة وكل حدث، ومعه مستمعات تخفي البيانات الحساسة قبل الكتابة.
والصورة التالية تبين ملفات المحول في المختبر وكيف ترتبط:

والملفات قصيرة، فهذا ملف القناة إلى المصدر A:
<channel-adaptor name="issuer-a-channel" class="org.jpos.q2.iso.ChannelAdaptor" logger="Q2">
<channel class="org.jpos.iso.channel.ASCIIChannel" packager="org.jpos.iso.packager.ISO87APackager" logger="Q2">
<property name="host" value="sfa-issuer-a" />
<property name="port" value="9001" />
</channel>
<in>issuer-a-send</in>
<out>issuer-a-receive</out>
<reconnect-delay>2000</reconnect-delay>
</channel-adaptor>وهذا ملف المطابق فوقها:
<mux class="org.jpos.q2.iso.QMUX" logger="Q2" name="issuer-a">
<in>issuer-a-receive</in>
<out>issuer-a-send</out>
<ready>issuer-a-channel.ready</ready>
</mux>في الإعداد أعلاه لاحظ التالي:
- القناة
ASCIIChannelتسبق كل رسالة بطولها في أربعة أرقام نصية، وهي طريقة تأطير أخرى غير البايتين الثنائيين في مختبر الصراف. والمواصفةISO87APackagerتكتب الحقول والخريطة نصاً كما في مختبر رسائل البنوك. وكل شريك يحدد القناة والمواصفة التي يتكلمها، فيكون لكل مصدر ملف قناة بإعداده. inوoutأسماء طوابير في ذاكرة مشتركة يسميها jPOS المساحة Space، والمطابق يكتب في طابور القناة ويقرأ من طابورها، فلا يعرف أحدهما الآخر إلا بالأسماء. وهذا يجعل القناة قابلة للاستبدال دون لمس المطابق.readyيقول للمطابق متى تكون القناة متصلة، وسنرى قيمته حين يتوقف المصدر B.reconnect-delayثانيتان بين محاولات الاتصال، فالقناة لا تستسلم إذا توقف المصدر، بل تعيد المحاولة حتى يعود.
أما قرار التوجيه فمكتوب في سكربت BeanShell اسمه switch.bsh، يشغله الخادم لكل رسالة عبر المستمع BSHRequestListener الذي يأتي مع jPOS، فيعطيه الرسالة في المتغير message والاتصال الذي جاءت منه في source. والسكربت مناسب للمختبر لأن القارئ يرى المنطق كله في ملف واحد، وفي الإنتاج يكتب هذا المنطق بـ Java كمستمع أو كمشاركين في مدير عمليات jPOS.
تجربة عملية: محول ومصدران ومستحوذ
بنينا المختبر من أربعة أجزاء، كل منها في Container على شبكة Docker خاصة اسمها sfa-switch-net دون أي منفذ منشور على الجهاز:
- مصدران
sfa-issuer-aوsfa-issuer-b: كل منهما Q2 فيه خادم واحد على المنفذ 9001 وسكربتissuer.bshيوافق إذا كفى الرصيد، ويرفض برمز 51 إذا لم يكف، ويحفظ كل موافقة ليلغيها إذا جاء عكس، ويتأخر عمداً أربع ثوان إذا كان المبلغ 777 ريالاً. - المحول
sfa-switch: Q2 بالملفات الستة أعلاه، يستمع للمستحوذين على المنفذ 8000. - المستحوذ
sfa-acquirer: سكربتacquirer.bshيرسل طلبات تفويض 0100 ويطبع ردودها، ثم يخرج.
والبطاقات أرقام الاختبار المنشورة في توثيق Stripe للاختبار، والعملة 682 أي الريال. وشغلنا jPOS بالإصدار 2.1.10، كما نشره المشروع في مستودع Maven المركزي في مارس 2025، على Java 21 بإصدار Temurin 21.0.11 الموجود في Docker Image الرسمي apache/kafka:4.3.1، فلا يحتاج jPOS إلا بيئة تشغيل Java، وأي Image فيه Java 21 يكفي. وهذا سكربت التشغيل كاملاً:
#!/bin/sh
# Start two mock issuers and the switch (jPOS Q2), then run the acquirer scenarios. Git Bash on Windows.
J=$(cd ../../../../../sfa-jpos-lib && pwd -W) # jPOS 2.1.10 and its jars from Maven Central
L=$(pwd -W)
IMG=apache/kafka:4.3.1 # any image with a Java 21 runtime
run() { MSYS_NO_PATHCONV=1 docker run "$@"; }
docker network create sfa-switch-net >/dev/null
for X in a b; do
run -d --name sfa-issuer-$X --network sfa-switch-net -e ISSUER=$(echo $X | tr a-z A-Z) \
-v "$J":/jpos:ro -v "$L":/lab -w /lab --entrypoint java $IMG -cp '/jpos/*' org.jpos.q2.Q2 -d /lab/issuer/deploy >/dev/null
done
run -d --name sfa-switch --network sfa-switch-net -e SWITCH_REMAP=${SWITCH_REMAP:-true} \
-v "$J":/jpos:ro -v "$L":/lab -w /lab --entrypoint java $IMG -cp '/jpos/*' org.jpos.q2.Q2 -d /lab/switch/deploy >/dev/null
sleep 8
acq() { run --rm --name sfa-acquirer --network sfa-switch-net -v "$J":/jpos:ro -v "$L":/lab -w /lab \
--entrypoint java $IMG -cp '/jpos/*' bsh.Interpreter acquirer.bsh "$@"; }
echo "== basic"; acq basic
echo "== same STAN"; acq same-stan
docker stop sfa-issuer-b >/dev/null
sleep 3
echo "== issuer B down"; acq downفي السكربت أعلاه لاحظ التالي:
- الملفات تركب من الجهاز بخيار
-v، والمكتبات للقراءة فقط:ro، فلا يكتب أي Container فيها. وتغيير سطر فيswitch.bshيظهر في الرسالة التالية دون أي بناء، لأن المستمع يقرأ السكربت في كل رسالة. MSYS_NO_PATHCONV=1وpwd -Wلأننا شغلناه من Git Bash على Windows، فبدونهما يحول Git Bash المسارات التي تبدأ بـ/إلى مسارات Windows فيتعطل التركيب. وعلى Linux لا تحتاجهما.- المتغير
SWITCH_REMAPيجعلنا نشغل المحول نفسه مرة بإعادة ترقيم أرقام التتبع ومرة دونها، لنرى الفرق بالتجربة.
ثلاث مشكلات حقيقية قبل أول رسالة
لم يعمل المختبر من المحاولة الأولى، وكل مشكلة منها تستحق أن يعرفها من يبدأ مع jPOS:
- الإصدار وJava: بدأنا بآخر إصدار في المستودع، 3.0.0، وقبل التشغيل قرأنا رأس أحد ملفاته المترجمة، فكانت البايتات
ca fe ba be 00 00 00 42، أي إصدار ملف الصنف 66، وهو Java 22. وبيئة Java 21 لا تشغل ملفات مترجمة لإصدار أحدث منها. أما 2.1.10 فرأسه00 00 00 34، أي الإصدار 52، وهو Java 8، فيعمل على Java 21 دون مشكلة. فاختر إصدار jPOS بحسب Java التي تملكها في الإنتاج، لا بحسب الأحدث. - الاعتماديات واحدة بعد الأخرى: نزلنا ملفات المكتبة يدوياً من Maven المركزي دون أداة بناء، فتوقف Q2 عند كل اعتمادية ناقصة برسالة
NoClassDefFoundError: أولاًorg/osgi/framework/BundleException، ثم صنف من commons-cli، ثم من BouncyCastle، ثمorg/yaml/snakeyaml/scanner/ScannerException. وكلها مذكورة في ملف المشروعpom، فقرأناه وأضفنا ما ينقص حتى وصلنا إلى ثلاثة عشر ملفاً بحجم 8.2 ميغابايت. وفي مشروع حقيقي تحل أداة البناء Maven أو Gradle هذا، لكن من المفيد أن تعرف أن Q2 يحتاج أكثر من ملف jPOS نفسه. - BeanShell قديم: سطر الطباعة في سكربت المستحوذ
String.format("...", a, b, c)فشل بـStatic method format(...) not found، لأن BeanShell 2.0b6 الذي يأتي مع jPOS أقدم من دعم المعاملات المتغيرة Varargs في Java، فلا يعرف كيف يجمعها. والحل أن نمرر المعاملات مصفوفة صريحةnew Object[] { ... }. وهذا تذكير بأن السكربت مناسب للتجربة، والمنطق الإنتاجي مكانه Java.
السيناريو الأول: خمسة طلبات
بعد التشغيل طبع المستحوذ هذه السطور:
== basic
issuer A card, 150.00 STAN 000101 -> 0110 rc 00 auth A33273 (426 ms)
issuer B card, 150.00 STAN 000102 -> 0110 rc 00 auth B70832 (129 ms)
issuer B card, 500.00 STAN 000103 -> 0110 rc 51 auth - (31 ms)
unknown BIN 5555 STAN 000104 -> 0110 rc 15 auth - (18 ms)
issuer A, slow answer (777.00) STAN 000105 -> 0110 rc 91 auth - (3020 ms)وطبع المحول سطراً لكل طلب يشرح ما فعله:
SWITCH 0100 acq STAN 000101 term POS00001 PAN 424242...4242 | issuer-a switch STAN 000001 -> 00 | rc 00 | 244 ms
SWITCH 0100 acq STAN 000102 term POS00001 PAN 400005...5556 | issuer-b switch STAN 000002 -> 00 | rc 00 | 118 ms
SWITCH 0100 acq STAN 000103 term POS00001 PAN 400005...5556 | issuer-b switch STAN 000003 -> 51 | rc 51 | 22 ms
SWITCH 0100 acq STAN 000104 term POS00001 PAN 555555...4444 | no route | rc 15 | 4 ms
SWITCH 0100 acq STAN 000105 term POS00001 PAN 424242...4242 | issuer-a timed out, reversal sent | rc 91 | 3008 msفي المخرج أعلاه لاحظ التالي:
- بطاقتا A وB ذهبتا إلى مصدرين مختلفين بحسب أول ستة أرقام، ورمز الموافقة يبدأ بحرف المصدر، A33273 وB70832، فنعرف من الرد نفسه من أجاب.
- رقم المستحوذ عاد كما هو في كل رد، 000101 و000102، مع أن المصدر رأى 000001 و000002. فالمستحوذ لا يعرف أن المحول غير الرقم، وهذا هو المطلوب.
- أول طلب أبطأ بكثير، 426 ms عند المستحوذ مقابل 129 ms للثاني، لأن Java تحمل الأصناف وتترجم السكربت لأول مرة. وهذا سبب أن المحولات الحقيقية ترسل رسائل تجريبية بعد كل تشغيل قبل أن تفتح للعمليات، حتى لا يدفع العميل الأول ثمن البداية الباردة.
- رصيد بطاقة B لا يكفي 500، فجاء الرفض 51 من المصدر نفسه، والمحول مرره كما هو.
- البطاقة 5555 لا تطابق الجدول، فرد المحول برمز 15 في 4 ms دون أن يسأل أحداً.
- الطلب البطيء هو قصة العميل، ونفصلها في الفقرة التالية.

حين يتأخر المصدر: قصة العميل
أرسل المستحوذ طلب 777 ريالاً، فوجهه المحول إلى المصدر A برقم 000004، وانتظر ثلاث ثوان، وهي المهلة في السطر mux.request(fwd, 3000). والمصدر في المختبر يتأخر أربع ثوان في هذا المبلغ، فانتهت المهلة، فرد المحول على المستحوذ برمز 91 «المصدر أو المحول لا يعمل» Issuer or switch inoperative، بعد 3,008 ms. وهذا ما رآه العميل على الجهاز: رفض. لكن المحول لا يعرف هل وافق المصدر أم لا، فالطلب وصل، وربما يعالج الآن. فأرسل إليه فوراً رسالة عكس 0420 برقم جديد 000005، وفي حقلها 90 رقم الطلب الأصلي:
<field id="0" value="0420"/>
<field id="2" value="424242______4242"/>
<field id="4" value="000000077700"/>
<field id="11" value="000005"/>
<field id="90" value="010000000400000000000000000000000000000000"/>ثم حدث ما يفسر الشكوى كلها. فبعد 4,276 ms من الإرسال وصل من المصدر رد بالموافقة 0110 برمز 00 ورقم موافقة A86012 للطلب 000004، أي أن المصدر وافق وحجز المبلغ، والعميل يرى الحجز في تطبيقه. لكن لا أحد في المحول ينتظر هذا الرد، فالطلب انتهى بالمهلة، فأهمله المطابق QMUX لأنه لا يطابق أي طلب قائم. ثم عالج المصدر رسالة العكس، فطبع:
A 0100 STAN 000004 amount 77700 -> 00
A reversal for switch STAN 000004: hold releasedفاختفى الحجز. والصورة التالية تجمع التسلسل كما ظهر في السجلين:

وفي هذا التسلسل تفصيل لم نصممه وظهر في السجل: رسالة العكس أرسلت بعد ثلاث ثوان، والموافقة خرجت من المصدر بعد أربع، ومع ذلك عالج المصدر العكس بعد الموافقة لا قبلها. والسبب أن الرسالتين وصلتا على الاتصال نفسه، والخادم في مصدر المختبر يعالج رسائل الاتصال الواحد بالترتيب، فانتظر العكس خلف الطلب البطيء حتى انتهى. وفي هذه الحالة كان ذلك لمصلحتنا، فوجد العكس الحجز وألغاه. لكن تخيل العكس يصل عبر اتصال آخر قبل أن ينتهي الطلب الأصلي: سيبحث المصدر عن حجز غير موجود بعد، فيقول «لا شيء لإلغائه»، ثم يحجز الطلب الأصلي بعد ذلك، ويبقى الحجز معلقاً. ولهذا يجب على المصدر أن يتذكر العكوس التي وصلت لطلبات لم يرها بعد، ويرفض الطلب الأصلي أو يلغيه فوراً حين يصل متأخراً، ولا يعتمد على أن الترتيب سيحفظ دائماً.
وفي المحول نفسه درس آخر: نحن أرسلنا العكس مرة واحدة دون انتظار رده، كما يقول التعليق في السكربت. والمحول الحقيقي لا يفعل ذلك، بل يحفظ العكس في طابور دائم يسمى التخزين ثم الإرسال Store and Forward، ويعيد إرساله برسالة التكرار 0421 حتى يأتي رد، تماماً كما رأينا في مقال الصراف، لأن العكس الذي يضيع يعني حجزاً يبقى على حساب العميل أياماً حتى تنتهي صلاحية التفويض.
كم تكون المهلة؟
المهلة في المختبر ثلاث ثوان، والمصدر أبطأ منها بثانية، فرفض المحول عملية كان المصدر سيوافق عليها. فلماذا لا نطيلها؟ لأن المحول نفسه في وسط سلسلة مهل: الجهاز ينتظر المستحوذ، والمستحوذ ينتظر المحول، والمحول ينتظر المصدر، وكل طرف عنده مهلة. فإذا كانت مهلة المحول أطول من مهلة المستحوذ، فقد يعكس المستحوذ العملية عنده ويعطي العميل رفضاً، بينما المحول ما زال ينتظر، ثم يصل رد بالموافقة إلى محول لا يعرف أن المستحوذ استسلم. والقاعدة أن تكون كل مهلة أقصر من مهلة الطرف الذي قبلها، حتى يعرف كل طرف النتيجة قبل أن يستسلم من ينتظره، ومهل الشبكات الحقيقية تحددها قواعد الشبكة لا تقدير الفريق.
ماذا لو أرسل مستحوذان رقم التتبع نفسه؟
في السيناريو الثاني أرسل مستحوذان في اللحظة نفسها طلبين إلى المصدر A، كلاهما برقم تتبع 000777 ومن جهاز رقمه POS00001، لأن كل مستحوذ يرقم أجهزته بنفسه. والمحول في الوضع العادي يعيد الترقيم، فكانت النتيجة:
== same STAN
acquirer 1 amount 000000011100 STAN 000777 -> 0110 rc 00 auth A27643 (87 ms)
acquirer 2 amount 000000022200 STAN 000777 -> 0110 rc 00 auth A42390 (77 ms)كلاهما وافق، وفي سجل المحول ذهب الأول برقم 000006 والثاني برقم 000007. ثم شغلنا المختبر كله مرة ثانية بالمتغير SWITCH_REMAP=false، أي أن المحول يمرر رقم المستحوذ كما هو:
== same STAN
acquirer 2 amount 000000022200 STAN 000777 -> 0110 rc 00 auth A64951 (65 ms)
acquirer 1 amount 000000011100 STAN 000777 -> 0110 rc 96 auth - (43 ms)وفي سجل المحول السبب بالضبط:
SWITCH 0100 acq STAN 000777 term POS00001 PAN 424242...4242 | issuer-a Duplicate key 'issuer-a-send.01000000000POS00001000777.req' detected | rc 96 | 10 msفالمطابق QMUX يبني مفتاح كل طلب من نوع الرسالة ورقم الجهاز ورقم التتبع، كما يظهر في نص المفتاح نفسه 01000000000POS00001000777، وحين وصل الطلب الثاني بالمفتاح نفسه والأول ما زال ينتظر، رفض المطابق تسجيله باستثناء Duplicate key، لأنه لو قبله لما عرف لمن يعطي الرد. فرد المحول على أحد المستحوذين برمز 96 «عطل في النظام» System malfunction.

والأخطر كان قبل ذلك. ففي النسخة الأولى من switch.bsh لم نلتقط الاستثناء، فرمى المطابق خطأه، وانتهى السكربت دون أن يرسل أي رد، فبقي المستحوذ ينتظر، وسكربت المستحوذ في تلك النسخة لم يكن فيه حد للانتظار، فتوقف المختبر كله حتى أنهيناه يدوياً بعد خمس دقائق. فأصلحنا الطرفين: المحول يلتقط أي استثناء من المطابق ويرد برمز 96 بدل الصمت، والمستحوذ يضع مهلة عشر ثوان على الاستقبال بـ c.setTimeout(10000). والقاعدة التي خرجنا بها أوضح من أي شرح: المحول يجب أن يرد على كل رسالة يستقبلها، برد حقيقي أو برفض، ولا يسكت أبداً، لأن الصمت عند المحول يتحول إلى مهلة عند المستحوذ، ثم إلى عكس، ثم إلى عميل لا يعرف ما حدث.
مصدر متوقف لا يوقف المحول
في السيناريو الثالث أوقفنا المصدر B بالأمر docker stop، ثم أرسلنا طلبين، الأول لبطاقة B والثاني لبطاقة A:
== issuer B down
issuer B card while B is down STAN 000201 -> 0110 rc 91 auth - (42 ms)
issuer A card at the same time STAN 000202 -> 0110 rc 00 auth A96698 (52 ms)وفي سجل Q2 في المحول ظهر ما فعلته القناة فور توقف المصدر:
<log realm="org.jpos.q2.iso.ChannelAdaptor" at="2026-10-27T18:16:37.779996421">
<warn>
channel-receiver-issuer-b-receive
Read timeout / EOF - reconnecting
</warn>
</log>
<log realm="channel/192.168.80.3:9001" at="2026-10-27T18:16:42.903074962" lifespan="3122ms">
<connect>
Try 0 sfa-issuer-b:9001
Connection refused
Unable to connect
</connect>
</log>في المخرج أعلاه لاحظ التالي:
- رفض B جاء خلال 5 ms في المحول، لا بعد مهلة ثلاث ثوان، لأن السكربت يسأل المطابق أولاً
mux.isConnected()، والمطابق يعرف من إشارةreadyأن القناة غير متصلة. والفرق كبير في ساعة الذروة: مصدر متوقف مع مهلة كاملة لكل طلب يعني أن خيوط المحول تمتلئ بطلبات تنتظر شيئاً لن يأتي، أما الرفض الفوري فيحرر كل شيء. - بطاقة A وافقت في الوقت نفسه، لأن لكل مصدر قناته ومطابقه، فتوقف أحدهما لا يمس الآخر. وهذا هو عزل الأعطال Fault Isolation في أبسط صوره.
- القناة تحاول الاتصال وحدها كل ثانيتين، فحين يعود المصدر يعود المسار دون أي تدخل.

وماذا لو كان المصدر متوقفاً ساعة؟
الرفض برمز 91 صحيح تقنياً، لكنه يعني أن كل عملاء البنك المتوقف لا يستطيعون الدفع طوال مدة التوقف. ولهذا تقدم بعض الشبكات والمعالجات خدمة النيابة عن المصدر Stand-In Processing: يوافق المحول أو الشبكة نيابة عن المصدر على عمليات معينة، بحدود ومعايير يتفق عليها المصدر مسبقاً، مثل مبلغ أقصى وأنواع عمليات محددة، ثم يرسل إلى المصدر إشعارات بما وافق عليه حين يعود، ليقيدها في الحسابات. والقرار هنا قرار مخاطر يملكه المصدر: النيابة تحفظ تجربة العميل، لكنها قد توافق على بطاقة رصيدها لا يكفي أو بطاقة أوقفت قبل دقائق. ولم نبن النيابة في المختبر، لكن موضعها واضح في السكربت: في الفرع الذي يرد فيه المحول برمز 91 لأن المصدر غير متصل.
رسائل إدارة الشبكة والإغلاق اليومي
ليست كل رسائل المحول عمليات مالية. فبحسب ملخص المعيار، فئة الرسائل 8 لإدارة الشبكة: تسجيل الدخول Logon وتسجيل الخروج، واختبار الصدى Echo، وتبادل المفاتيح. والمحول يرسل الصدى إلى كل مصدر بانتظام، فيعرف أن الاتصال حي قبل أن تحتاجه عملية، وهذا أدق من انتظار انقطاع TCP الذي قد يتأخر دقائق في بعض الشبكات. وتبادل المفاتيح يحدث برسائل الفئة نفسها، فتتغير مفاتيح المنطقة ZPK وTAK بين المحول وكل شريك دورياً كما شرحنا في مقال HSM.
وهناك لحظة يومية يعرفها كل من عمل في محول: الإغلاق Cutover، أي نهاية يوم العمل وبداية التالي، حين تغلق المجاميع وتبدأ دورة تسوية جديدة. وفي جدول رموز الرد في الملخص رمز خاص لها، 90، «الإغلاق قيد التنفيذ»، والمستحوذ الذي يستلمه يعيد المحاولة بعد دقائق. وبعد الإغلاق تتبادل الأطراف رسائل المطابقة من الفئة 5 بمجاميع اليوم، وكل فرق بين مجموع المحول ومجموع الطرف الآخر يحقق فيه قبل التسوية، كما شرحنا في مقال المطابقة ومقال المقاصة والتسوية.
المفاتيح على كل رابط: الرقم السري والـ MAC في المحول
في المختبر لم نرسل رقماً سرياً، لكن في الواقع تحمل نسبة كبيرة من طلبات الصرافات ونقاط البيع كتلة رقم سري في الحقل 52، والمحول يقف بين مستحوذ يشفرها بمفتاح بينهما ومصدر ينتظرها بمفتاح آخر. فلا يستطيع المحول أن يمرر الحقل 52 كما هو، ولا يجوز له أن يفكه، فيرسله مع المفتاحين الملفوفين إلى HSM الدفع بأمر ترجمة واحد، ويضع الكتلة الجديدة في الرسالة الخارجة، كما رأينا في مقال HSM حين ترجمنا كتلة Mastercard بين مفتاحين. والأمر نفسه مع رمز MAC: كل رابط له مفتاح MAC خاص به، فيتحقق المحول من رمز الرسالة الداخلة بمفتاح المستحوذ، ويحسب رمزاً جديداً للرسالة الخارجة بمفتاح المصدر، لأن الرسالة تغيرت في الطريق، فقد غير المحول رقم التتبع وأضاف الحقل 32.
وهذا يعني أن المحول يحتاج HSM في المسار الحرج لكل عملية تقريباً، فزمن رد الـ HSM يضاف إلى زمن كل طلب، وتوقفه يوقف الطلبات التي تحمل رقماً سرياً. ولهذا يعامل الـ HSM في تصميم المحول كأي مصدر خارجي: له أكثر من جهاز، ومهلة، ومراقبة لزمن الرد، وخطة لما يحدث إذا لم يرد. ولاحظ أن المحول في هذه الصورة يعرف كل المفاتيح العاملة ملفوفة بمفتاح الـ HSM الرئيسي، ولهذا فإن من يخترق سيرفر المحول يستطيع أن يطلب من الـ HSM عمليات باسمه، وهذا سبب آخر لتوصيات FASTCash التي سنراها بعد قليل عن حماية سيرفر المحول نفسه.
الطلب المكرر من المستحوذ
رأينا في مقال الصراف أن الطرف الذي لا يصله رد يعيد المحاولة أو يعكس. والمحول يرى الوجه الآخر لهذا: قد يصله الطلب نفسه مرتين، لأن المستحوذ أعاد إرساله بعد مهلة، أو لأن الاتصال انقطع وعاد. فإذا عامل المحول كل نسخة كطلب جديد، أعطاها رقم تتبع جديداً وأرسلها إلى المصدر، فيحجز المصدر المبلغ مرتين. ولهذا يحتفظ المحول الحقيقي بذاكرة قصيرة للطلبات التي مرت به، بمفتاح من المستحوذ والجهاز ورقم التتبع والوقت، فإذا وصل طلب يطابق طلباً ما زال قائماً، انتظر رده بدل أن يرسل نسخة ثانية، وإذا طابق طلباً انتهى، أعاد الرد نفسه. وهذه فكرة Idempotency التي شرحناها في مقال Idempotency، والفرق أن المفتاح هنا ليس رأساً في طلب HTTP، بل مجموعة حقول يتفق عليها الأطراف في المواصفة. ولاحظ أن خطأ Duplicate key الذي رأيناه في المختبر هو نسخة بدائية من هذه الحماية: المطابق يرفض أن يسجل طلبين بالمفتاح نفسه في وقت واحد، لكنه لا يعرف هل هما طلبان مختلفان تصادفا في الرقم أم الطلب نفسه مكرراً، وهذا ما يجب أن يقرره منطق المحول بالحقول الأخرى.
كيف يختبر محول قبل أن يتصل بشبكة؟
كل سيناريو جربناه في المختبر يحتاج أن يحدث عمداً، فالمصدر الحقيقي لا يتأخر حين تريد، ولا يتوقف في موعد. ولهذا يبني فريق المحول محاكيات للأطراف كالتي كتبناها بسكربت BeanShell: مصدر يوافق ويرفض بحسب البطاقة، ويتأخر بحسب المبلغ، ويمكن إيقافه أثناء الاختبار، ومستحوذ يرسل طلبات متزامنة وأرقاماً مكررة. ومن هذه المحاكيات تبنى اختبارات آلية تعمل مع كل تعديل في جدول التوجيه أو منطق المحول. ثم تأتي اختبارات التوافق مع كل شريك في بيئته التجريبية، بالمواصفة التي يرسلها وببطاقات الاختبار التي يحددها، وأخيراً اختبارات الاعتماد التي تطلبها الشبكة قبل أن تسمح بالاتصال بالإنتاج. وما ظهر في مختبرنا من خطأ Duplicate key والانتظار الذي لا ينتهي هو بالضبط نوع الخطأ الذي لا يظهر في اختبار طلب واحد في كل مرة، ويظهر في أول ساعة ذروة.
ماذا يكتب المحول في سجله؟
المحول يرى كل رسالة كاملة، بما فيها رقم البطاقة وكتلة الرقم السري وبيانات الشريحة. والسجل الذي يكتب كل هذا كما هو يصبح أخطر ملف في الشركة، ويدخل كل من يقرؤه في نطاق PCI DSS كما شرحنا في مقال أمن بيانات البطاقات. ولهذا وضعنا في سجل المحول مستمعاً من jPOS اسمه ProtectedLogListener قبل المستمع الذي يكتب:
<logger name="Q2" class="org.jpos.q2.qbean.LoggerAdaptor">
<!-- mask the card number and track data, remove the PIN block, then print -->
<log-listener class="org.jpos.util.ProtectedLogListener">
<property name="protect" value="2 35 45" />
<property name="wipe" value="52" />
</log-listener>
<log-listener class="org.jpos.util.SimpleLogListener" />
</logger>والخاصية protect تخفي وسط الحقول المذكورة، رقم البطاقة والمسارين 2 و1، وwipe تمسح الحقل 52 كاملاً. والنتيجة في السجل الحقيقي:
<isomsg direction="incoming">
<!-- org.jpos.iso.packager.ISO87APackager -->
<field id="0" value="0100"/>
<field id="2" value="424242______4242"/>
<field id="3" value="000000"/>
<field id="4" value="000000015000"/>
<field id="7" value="1027181629"/>
<field id="11" value="000101"/>
<field id="41" value="POS00001"/>
<field id="42" value="ARABROOTLAB0001"/>
<field id="49" value="682"/>
</isomsg>ولاحظ ترتيب المستمعين: الإخفاء أولاً ثم الكتابة. ولو انعكس الترتيب لكتب الرقم كاملاً ثم أخفي في نسخة لا يقرؤها أحد. ولاحظ أيضاً أن الإخفاء في السجل لا يغني عن مراجعة كل مكان آخر قد تمر فيه الرسالة: سطور الطباعة في السكربتات، ورسائل الأخطاء، وأدوات المراقبة. ففي سكربت المحول نفسه كتبنا الطباعة لتظهر أول ستة أرقام وآخر أربعة فقط، لأن المستمع يحمي سجل jPOS لا سطور print.
حالة موثقة: حين صار المحول هو المهاجم
وهذه الحالة تقلب كل ما بنيناه: المحول الذي يجب أن يمرر رد المصدر بأمانة صار هو الذي يصنع الرد. ورمز MAC على الرسائل لا يكفي هنا، لأن المحول نفسه يملك المفاتيح. والتوصيات التي ذكرها التنبيه للمحول تحديداً تستحق أن تكون قائمة تحقق لأي فريق يشغل واحداً: مصادقة بعاملين لأي دخول إلى سيرفر المحول، وتقسيم الشبكة بجدران نارية وقوائم وصول، وتأمين الروابط مع أنظمة الدفع بشهادات مثل TLS المتبادل Mutual TLS، وتسجيل العمليات ومراجعة السجلات بانتظام، وخط أساس للبرامج والمستخدمين وعمليات الدخول المتوقعة ومراقبة ما يخرج عنه، وخط أساس لأطراف العمليات ومبالغها وتكرارها وتوقيتها. والبند الأخير يعني أن سطر السجل الذي كتبه محولنا لكل طلب، بالمسار والرمز والوقت، ليس للتصحيح فقط، بل هو المادة التي تكتشف منها أن شيئاً غير طبيعي يحدث، كسيل موافقات من مصدر واحد في دقائق.
وفي جهة المصدر توصية من تنبيه 2020 أشرنا إليها في مقال الشريحة: التحقق من الرمز الذي تولده الشريحة في كل طلب، وإرسال رمز رد يولده المصدر، والتحقق منه في الجهاز. فالموافقة المزورة التي يصنعها محول مخترق لا تحمل رمز ARPC صحيحاً، لأنه يحتاج مفتاح المصدر الذي يعيش في HSM المصدر لا في المحول.
كيف يبدو المحول في مؤسسة حقيقية؟
بعد المختبر عرض الفريق على إدارة الشركة الفرق بين المحول الذي بنيناه في ساعة والمحول الذي يحتاجونه في الإنتاج، والقائمة تصلح لأي جهة تقف في وسط رسائل البطاقات:
- التوفر العالي High Availability: أكثر من نسخة من المحول تعمل معاً، ولكل مصدر أكثر من قناة، لأن المحول نقطة يمر بها كل شيء، فتوقفه يوقف الجميع. وحالة الطلبات القائمة وأرقام التتبع يجب ألا تضيع إذا توقفت نسخة.
- التخزين ثم الإرسال: كل عكس وكل إشعار يحفظ في طابور دائم ويعاد حتى يؤكد استلامه.
- جدول التوجيه بيانات لا كود: في قاعدة بيانات بسجل تغييرات ومراجعة، ويحدث دون إعادة تشغيل.
- المهل محسوبة عبر السلسلة: مهلة المحول أقصر من مهلة المستحوذ، وفق قواعد الشبكة.
- الرد على كل رسالة: أي استثناء يتحول إلى رمز رفض، ولا يوجد مسار في الكود ينتهي دون رد.
- HSM للدفع: لترجمة كتل الرقم السري بين مفتاح المستحوذ ومفتاح المصدر، ولرموز MAC، كما في مقال HSM.
- المراقبة: نسبة الرفض برمز 91 لكل مصدر، وزمن الرد، والردود المتأخرة التي أهملها المطابق، والعكوس المعلقة، وخط أساس للأنماط كما في توصيات FASTCash.
- الترخيص: إذا بني على jPOS، فترخيص AGPL يراجع قانونياً قبل أي نشر.
وكان سؤال الإدارة الأخير: هل نبني محولاً أم نشتري خدمة؟ والإجابة ليست تقنية فقط. فالمحول الذي يتصل بشبكة وطنية أو دولية يحتاج اعتمادات واختبارات توافق وترتيبات أمنية مع الشبكة، وفريقاً يراقبه ليلاً ونهاراً. وبحسب ما رأيناه في المختبر، فالكود الذي يوجه الرسائل جزء صغير من العمل، والجزء الكبير هو كل ما يحدث حين لا يسير شيء كما يجب.
أخطاء شائعة Common Mistakes
| الخطأ | لماذا يضر | البديل |
|---|---|---|
| تمرير رقم تتبع المستحوذ كما هو | تعارض المفاتيح، ورمز 96 كما رأينا | رقم من تسلسل المحول وإعادة رقم المستحوذ في الرد |
| استثناء دون رد | المستحوذ ينتظر حتى المهلة، وفي المختبر إلى الأبد | كل خطأ يتحول إلى رمز رفض |
| عكس يرسل مرة واحدة | حجز معلق إذا ضاع | التخزين ثم الإرسال مع 0421 |
| مهلة المحول أطول من مهلة المستحوذ | موافقة تصل بعد أن استسلم المستحوذ | مهل متناقصة عبر السلسلة |
| انتظار المهلة كاملة لمصدر متوقف | خيوط المحول تمتلئ في الذروة | فحص جاهزية القناة والرد فوراً |
| مصدر لا يتذكر العكس الذي سبق طلبه | حجز يبقى بعد عكس وصل مبكراً | حفظ العكوس وإلغاء الطلب المتأخر |
| سجل بالرسائل الخام | أرقام البطاقات والكتل في ملف نصي | إخفاء قبل الكتابة، ومراجعة كل print |
| أحدث إصدار من jPOS دون فحص Java | الإصدار 3.0.0 مترجم لـ Java 22 | إصدار يطابق Java في الإنتاج |
| سيرفر المحول بنظام غير مدعوم ودخول بكلمة مرور فقط | هذا ما وصفه تنبيه FASTCash | نظام مدعوم، وعاملان، وتقسيم الشبكة |
قائمة تحقق قبل تشغيل محول
- جدول التوجيه يطابق أطول بادئة، ويحدث دون إعادة تشغيل، ولكل تغيير مراجعة وسجل.
- كل رسالة تخرج برقم تتبع من المحول، ورقم المستحوذ يعود في الرد.
- كل رسالة تستقبل يرد عليها، وكل استثناء له رمز رفض.
- المهل أقصر من مهلة المستحوذ، وكل مهلة تنتهي بعكس محفوظ يعاد حتى يؤكد.
- جاهزية كل قناة تفحص قبل الإرسال، ولكل مصدر قناة ومطابق منفصلان، والصدى يرسل بانتظام.
- الردود المتأخرة التي لا يطابقها شيء تسجل وتراقب، لا تهمل بصمت.
- السجل يخفي الحقول 2 و35 و45 ويمسح 52 قبل الكتابة، ولا print يطبع رقماً كاملاً.
- سيرفر المحول بنظام مدعوم، والدخول إليه بعاملين، والروابط بـ TLS متبادل، والشبكة مقسمة.
- خط أساس لأنماط العمليات لكل مصدر، وتنبيه على ما يخرج عنه.
- ترخيص المكونات المفتوحة المصدر مراجع قبل النشر.
الخلاصة
وصلنا لنهاية الموضوع، وأهم ما فيه:
- المحول نقطة في الوسط تختصر الاتصالات بين المستحوذين والمصدرين، وفي السعودية تذكر صفحة مدى أن قنوات الشبكة متصلة بنظام مركزي يوجه العمليات إلى المصدر.
- بنينا محولاً بـ jPOS 2.1.10 من ستة ملفات إعداد وسكربت توجيه، وجه بطاقتين إلى مصدرين ورد على BIN مجهول برمز 15 خلال 4 ms.
- قصة العميل أعدناها كما هي: المحول رد برمز 91 بعد 3,008 ms، والمصدر وافق بعد 4,276 ms، والعكس ألغى الحجز، والرد المتأخر أهمله المطابق.
- رقم التتبع نفسه من مستحوذين أعطى موافقتين حين أعاد المحول الترقيم، و
Duplicate keyورمز 96 حين لم يعده، وقبل التقاط الاستثناء لم يأت رد أصلاً. - المصدر المتوقف رفض خلال 5 ms دون أن يمس المصدر الآخر، والقناة تعيد الاتصال وحدها.
- السجل يخفي الرقم قبل الكتابة، وتنبيه FASTCash يذكر أن المحول نفسه قد يصبح الهدف، فيحتاج حماية ومراقبة لا تقل عن البنك.
وبهذا نكون قد مررنا في هذه المرحلة على طريق البطاقة كاملاً: من الجهاز والشريحة، إلى الرقم السري ومفاتيحه، إلى المحول الذي يوجه كل ذلك. وبقية مقالات قسم التقنية المالية والمدفوعات:
دليلكيف يعمل الصراف الآلي ATM من الداخل؟نفتح الصراف: الموزع والأدراج ولوحة الرقم السري ومعيار XFS ورسائل المضيف، ونحاكي سحباً يتعطل فيه الموزع وكيف يصحح العكس الخصم.
دليلالشريحة EMV والدفع اللاتلامسي: ماذا يحدث في ثانيتين؟نفك بيانات الشريحة في الحقل 55 من عينة منشورة، ونعيد حساب رمز ARQC منشور خطوة خطوة، ونرى كيف يقرر الجهاز الاتصال بالبنك أو الموافقة دونه.
دليلوحدات أمن العتاد HSM والرقم السري PIN وإدارة المفاتيحنبني كتلة الرقم السري بمثال Mastercard المنشور داخل SoftHSM، ونرى مكونات المفاتيح وKCV والصيغة 4 وكتل TR-31، ولماذا لا يكفي HSM عام للرقم السري.
دليلرسائل البنوك: ISO 8583 للبطاقات وISO 20022 للتحويلاتنفكك رسالة تفويض بطاقة بايتاً بايتاً، ونبني أمر تحويل بأسماء عربية ونتحقق منه بالمخطط الرسمي، ونرى ما يتعطل في كل معيار.
دليلكيف يعمل الدفع بالبطاقة؟ التفويض Authorization والتسويةمن ضغطة «ادفع» إلى آخر اعتراض: التفويض و3D Secure والتحصيل والتسوية والاسترداد والاعتراض، مع مختبر لأحداث مزود الدفع ومطابقتها.
دليلمنظومة المدفوعات في السعودية: سريع وSARIE ومدى وسدادالأنظمة الوطنية للمدفوعات في المملكة كما تصفها مصادر البنك المركزي، وما يعنيه كل نظام للمطور، مع تحليل تقارير نقاط البيع الأسبوعية.سجل التحديثات
- أكتوبر 2026: كتابة المقال وتشغيل المختبر يوم 27 أكتوبر 2026 بـ jPOS 2.1.10 وBeanShell 2.0b6 على Temurin 21.0.11 في Containers من apache/kafka:4.3.1، ببطاقات اختبار فقط، ومراجعة تنبيهي FASTCash لدى CISA وصفحة مدى لدى البنك المركزي في التاريخ نفسه.