خلافًا لما تعتقده معظم الفرق، لا يقتصر تكامل نقطة البيع الافتراضية (Virtual POS) مع البرمجيات المخصصة على «استدعاء iyzico أو PayTR عبر واجهة API الخاصة بكل منهما». فالتكامل الجاهز لبيئة الإنتاج مطالب بحل أربع مشكلات منفصلة في الوقت نفسه: (1) بناء تدفق 3D Secure (3DS) بصورة صحيحة، أي نقل المستخدم إلى شاشة التحقق لدى البنك ثم إعادته؛ (2) التحقق الآمن من Callback/Webhook، أي إثبات صدور الإشعار الوارد عن مزوّد الدفع فعلًا عبر التوقيع، وجعل المعالجة لا يتكرر أثرها (Idempotent) عند تكرار الإشعارات، وإعادة استجابة 2xx بسرعة؛ (3) مطابقة الدفع مع الطلب، أي ربط الحدث الذي حصّلت فيه المبلغ فعليًا بالطلب الصحيح؛ و(4) إدارة سيناريوهات ما بعد العملية، مثل الاسترداد والإلغاء والتقسيط. وإذا غاب أي منها، بدا التكامل كأنه «يعمل»، لكنه يبقى عرضة في بيئة الإنتاج لأخطاء صامتة تُفقدك المال: طلب يُعالَج مرتين، أو سلة تظهر «مدفوعة» مع أنها لم تُؤكَّد قط، أو منتج مجاني يُسلَّم عبر Callback مزوّر. وفي هذا المقال نشارك، بصفتنا شركة برمجيات B2B (بين الشركات) وWhite-Label مقرها صقاريا (Sakarya)، الإطار نفسه الذي نطبقه في تكاملات نقاط البيع الافتراضية التي ننفذها: الاختيار بين نقطة البيع الافتراضية البنكية ومؤسسة الدفع، وبين النموذج المستضاف وواجهة API المباشرة، وتدفق 3DS، وإشعارات Webhook الآمنة، والمطابقة، والاسترداد والتقسيط، وقائمة تحقق قبل الإطلاق.

يتناول هذا المقال تكامل الدفع بوصفه مشروع تكامل API ومسألة من مسائل بنية التجارة الإلكترونية في آن واحد. وقد طبّقنا الانضباط نفسه في جانب ERP والفاتورة الإلكترونية (e-Fatura)؛ فإذا كنت تفاضل في التكامل بين الحل «الجاهز» والحل «المخصص»، فراجع مقالنا عن تكامل التجارة الإلكترونية مع ERP والفاتورة الإلكترونية. وتستند التفاصيل التقنية أدناه إلى ما تنشره iyzico وPayTR في وثائقهما الرسمية؛ وبما أن كل مزوّد قد يتغير مع الوقت، فالمرجع الوحيد الموثوق هو الوثائق الرسمية وقت تنفيذ التكامل.

نقطة بيع افتراضية بنكية أم مؤسسة دفع؟

أول قرار معماري هو القناة التي ستحصّل عبرها مدفوعات البطاقات. وتُقدَّم نقطة البيع الافتراضية في تركيا بصيغتين: (أ) نقطة بيع افتراضية بنكية بموجب عقد مباشر مع أحد البنوك، و(ب) مؤسسات دفع مستقلة عن البنوك (مثل iyzico وPayTR). وتعمل مؤسسات الدفع بموجب القانون رقم 6493، قانون أنظمة الدفع وتسوية الأوراق المالية وخدمات الدفع ومؤسسات النقود الإلكترونية، وهي لا تحصل على ترخيص مزاولة النشاط من هيئة تنظيم ومراقبة القطاع المصرفي (BDDK)، بل من البنك المركزي التركي (TCMB) (المادة 12 من القانون) (البنك المركزي التركي — مؤسسات الدفع).

الفرق العملي هو الآتي: في نقطة البيع الافتراضية البنكية تحتاج عادةً إلى تكامل وعقد منفصلين لكل بنك؛ أما مؤسسة الدفع المستقلة عن البنوك فتتيح لك قبول بطاقات معظم البنوك عبر تكامل واحد. وتبرز iyzico خصوصًا في سيناريوهات السوق الإلكتروني (Marketplace) والتاجر الفرعي (Submerchant). وقاعدة القرار بسيطة:

  • إذا كانت أولويتك بنكًا واحدًا وحجم معاملات مرتفعًا وأدنى عمولة، وكانت لديك علاقة قوية ببنك واحد، فقد تكون نقطة البيع الافتراضية البنكية خيارًا منطقيًا؛ لكن إذا أردت دعم عدة بنوك، فإن عبء التكامل يتضاعف.
  • إذا أردت إطلاقًا سريعًا وقبول بطاقات عدة بنوك وتكاملًا واحدًا، فإن مؤسسة الدفع (iyzico/PayTR) تعني تكلفة صيانة أقل في معظم سيناريوهات B2B والشركات الصغيرة والمتوسطة.
  • إذا كنت تحتاج إلى وظائف السوق الإلكتروني أو تقاسم العمولات أو التاجر الفرعي، فإن مؤسسة دفع تدعم التاجر الفرعي (Submerchant) تكاد تكون إلزامية.

نموذج مستضاف أم واجهة API مباشرة؟ هنا يتحدد نطاق PCI-DSS لديك

القرار الثاني، وربما الأهم، هو المسار الذي ستمر عبره بيانات البطاقة، لأنه يحدد مباشرة نطاق PCI-DSS لديك. وهناك نهجان:

  • نموذج الدفع المستضاف (Hosted Form): تُجمع بيانات البطاقة في صفحة أو iframe يستضيفها المزوّد. ففي PayTR iFrame API يُدخل المستخدم بياناته داخل iframe الذي توفره PayTR نفسها؛ أما لدى iyzico فإن CheckoutForm هي صفحة الدفع التي تستضيفها iyzico (يعيد CF-Initialize القيمة paymentPageUrl/token مع إمكانية التضمين عبر iframe=true). وفي الحالتين لا تصل بيانات البطاقة الحساسة إلى خادمك أبدًا، ومن ثم يضيق نطاق PCI-DSS لديك بدرجة كبيرة.
  • واجهة API المباشرة: تحصّل الدفعة عبر نموذج الدفع الخاص بك، وتمر بيانات البطاقة عبر بنيتك التحتية. ويمنحك ذلك تحكمًا كاملًا وتجربة تحمل علامتك التجارية، لكنه يزيد التزاماتك بموجب PCI-DSS زيادة كبيرة.

تتولى كل من iyzico وPayTR حماية PCI من جهة المزوّد؛ وتقدّم iyzico نفسها بوصفها مزوّد خدمة من المستوى الأول (PCI DSS Level 1). لكن لنكن واضحين: نوع SAQ الدقيق لديك (SAQ A وSAQ A-EP وغيرهما) يعتمد على تفاصيل التكامل، ولا يدّعي هذا المقال تحديده؛ والحقيقة العامة هي أن النطاق يتقلص حين لا تمر بيانات البطاقة عبر خادمك. ونصيحتنا العملية واضحة:

ما لم يكن لديك متطلب عمل حقيقي لتجربة دفع تحمل علامتك التجارية وتخضع لتحكمك الكامل، فاختر النموذج المستضاف. فبضع البكسلات التي تكسبها في التخصيص لا تستحق عبء PCI-DSS الذي ستتحمله في التدقيق والمسؤولية.

تدفق 3DS: كيف يعمل؟ (مثال من iyzico)

يُعد 3D Secure تحققًا إضافيًا يجريه بنك حامل البطاقة (رمز لمرة واحدة، أو موافقة عبر تطبيق البنك، وما إلى ذلك). وإذا جرى تخطي هذه الخطوة، فإن خطر رد المبالغ المدفوعة (Chargeback) والمسؤولية عنه يبقيان إلى حد كبير على عاتق الشركة. ويمثل 3DS لدى iyzico تدفقًا من خطوتين بطلبَي POST متتاليين:

  • الخطوة 1 — تهيئة 3DS (Init 3DS): ترسل طلب POST إلى نقطة النهاية (Endpoint) /payment/3dsecure/initialize مع قيمة callbackUrl وبيانات البطاقة والمشتري. وتعيد الاستجابة الحقل threeDSHtmlContent مرمّزًا (base64). ويُعرض كود HTML هذا لتحويل المستخدم إلى شاشة التحقق 3DS لدى البنك.
  • الخطوة 2 — مصادقة 3DS (Auth 3DS): بعد أن يُتم المستخدم التحقق لدى البنك ويعود إلى callbackUrl الخاص بك، ترسل طلب POST ثانيًا إلى نقطة نهاية المصادقة مع قيمة paymentId العائدة من خطوة التهيئة. وهنا توصي iyzico باستخدام نقطة النهاية v2 (/payment/v2/3dsecure/auth، أي 3DS 2.0)؛ أما نقطة النهاية القديمة /payment/3dsecure/auth فتقابل الإصدار الأول (3DS 1.0).

يحمل الحقل mdStatus نتيجة التحقق: القيمة 1 تعني النجاح، بينما تدل القيمة 0 والقيم من 2 إلى 8 على فشل التحقق. وهنا تنبيه مهم: لا نقدّم في هذا المقال المخطط الدقيق لحقول متن طلب POST الذي يرسله البنك إلى callbackUrl الخاص بك بوصفه قائمة نهائية، لأنه قد يختلف باختلاف الإصدار والتكامل؛ فهو من التفاصيل التي يجب التحقق منها في الوثائق الرسمية الحالية قبل الإطلاق (iyzico — تهيئة 3DS، iyzico — مصادقة 3DS).

يُستخدم conversationId طوال التدفق لربط طلب API باستجابته. وتعرّفه iyzico بأنه «معرّف فريد للربط بين الطلب والاستجابة»؛ ويمكنك وضع رقم الطلب لديك في هذا الحقل، وتُعاد القيمة التي ترسلها في طلب API كما هي في الاستجابة. وإلى جانب ذلك، تخصص iyzico من جهتها معرّف paymentId لكل عملية دفع ناجحة؛ وتجري عمليات الاسترداد والإلغاء عبر معرّف paymentId هذا.

التحقق الآمن من Webhook/Callback: التوقيع ومنع تكرار الأثر واستجابة 2xx السريعة

هذا هو الجزء الأكثر إغفالًا والأشد خطورة في تكامل الدفع. فإشعار Callback/Webhook هو الحدث الفعلي الذي يصل من خادم إلى خادم، وعلى أساسه ستسجّل أن المبلغ قد حُصّل. وهناك ثلاث قواعد غير قابلة للتفاوض: تحقّق من التوقيع، وامنع تكرار الأثر، وأعِد 2xx بسرعة.

1) التحقق من التوقيع: أثبت أن الإشعار صادر فعلًا عن المزوّد

لا تعامل أي طلب POST يصل إلى Callback لديك على أنه «مدفوع» قبل التحقق من توقيعه. وإلا فقد يرسل مهاجم إشعار «دفع ناجح» مزوّرًا ويفتح طلبًا مجانيًا. وتختلف الآلية بين المزوّدين:

  • PayTR (iFrame API): تُحسب قيمة التجزئة (Hash) بالصيغة base64_encode(hash_hmac('sha256', merchant_oid + merchant_salt + status + total_amount, merchant_key, true)) في الإشعار الذي يصل إلى عنوان notify_url لديك. وإذا كانت قيمة hash في طلب POST الوارد لا تطابق هذه القيمة تطابقًا تامًا، فيجب اعتبار الطلب غير صادر عن PayTR ورفضه. وهذه الصيغة خاصة بواجهة iFrame API (أربعة حقول)؛ وقد تستخدم واجهات API الأخرى لدى PayTR مجموعة حقول مختلفة، فلا تخلط بينها.
  • iyzico (Webhook): يجري التحقق عبر الترويسة X-IYZ-SIGNATURE-V3 باستخدام HMAC-SHA256 (HEX)؛ ولم تعد إصدارات الترويسة الأقدم مدعومة. والنقطة الحرجة أن ترتيب الحقول التي تُدمج لإنشاء التوقيع يتوقف على نوع Webhook المستخدم. فالدفع المباشر (Direct Payment) وHPP والاشتراك (Subscription) تستخدم ترتيبات مختلفة للمعاملات؛ ولا يوجد ترتيب «عام» واحد لتوقيعات Webhook كلها، فتحقق من الترتيب الخاص بنوع تكاملك في الجدول الرسمي.

وتوفر iyzico أيضًا آلية منفصلة هي توقيع الاستجابة (Response Signature) لاستجابات API المتزامنة: تُدمج معاملات محددة مفصولة بنقطتين رأسيتين (:)، ثم تُجزَّأ عبر HMAC-SHA256 باستخدام secretKey وتُرمَّز النتيجة (HEX)؛ وتُحذف الأصفار الزائدة في نهاية الأسعار (10.50 تصبح 10.5، و10.00 تصبح 10). ويختلف ترتيب المعاملات باختلاف نقطة النهاية؛ فعلى سبيل المثال، يكون الترتيب paymentId:currency:basketId:conversationId:paidPrice:price في المصادقة دون 3DS (Non-3DS Auth)، وتستخدم مصادقة 3DS في الإصدار v2 ترتيب الحقول نفسه (iyzico — التحقق من توقيع الاستجابة، iyzico — Webhook، PayTR — الخطوة 2 من iFrame API).

2) منع تكرار الأثر (Idempotency): إذا وصل الإشعار نفسه مرتين، فلا تعالجه مرتين

قد يرسل كلا المزوّدين أكثر من إشعار للعملية نفسها. وإذا لم تُزِل التكرار استنادًا إلى معرّف الطلب لديك، فسيُؤكَّد الطلب نفسه مرتين، ويُخصم المخزون مرتين، وتُفتح شحنة مرتين. وتنص PayTR على ذلك صراحة: يجب أن يعتمد التحقق من التكرار على الحقل الفريد merchant_oid؛ ولتأكيد الطلب أو إلغائه يجب معالجة الإشعار الأول فقط، أما الإشعارات المتكررة فيُكتفى بالرد عليها. والتنفيذ العملي هو: احتفظ بحالة الإشعار (عولج / لم يُعالَج) في قاعدة بياناتك ضمن سجل واحد فريد مرتبط بمعرّف الطلب، ونفّذ المعالجة ضمن عملية ذرية (Atomic) واحدة.

3) أعِد 2xx بسرعة ونفّذ الأعمال الثقيلة بصورة غير متزامنة

يتوقع المزوّد ردًا إيجابيًا على الإشعار خلال وقت قصير؛ وإذا لم يتلقَّه أعاد المحاولة. فإذا لم يتلقَّ نظام Webhook لدى iyzico استجابة 2xx من التاجر، أعاد المحاولة كل 15 دقيقة، بحد أقصى 3 مرات. أما PayTR فإذا لم تتلقَّ «OK» أعادت المحاولة بعد دقيقة واحدة، وهي تشترط أن يكون متن الاستجابة نصًا عاديًا يقتصر على OK دون أي HTML أو محتوى آخر. وتترتب على ذلك قاعدتان:

  • تحقّق أولًا، ثم احفظ، ثم أجب: تحقّق من التوقيع، وعلّم الطلب بسجل فريد، ثم أعِد الاستجابة المتوقعة. وادفع الأعمال الثقيلة، مثل الكتابة في نظام ERP وإرسال رسائل البريد الإلكتروني وإصدار الفواتير، إلى قائمة انتظار غير متزامنة؛ ولا تنفذها بصورة متزامنة تُبقي استجابة Callback معلّقة.
  • أعِد متن الاستجابة بالصيغة التي يتوقعها المزوّد: مع PayTR القيمة OK بالضبط، ومع iyzico رمز حالة HTTP ناجح (2xx). وإلا ستواجه عمليات إعادة محاولة لا داعي لها ومدفوعات «ضائعة».
الموضوع iyzico (Webhook) PayTR (Callback في iFrame API)
طريقة التوقيع HMAC-SHA256 (HEX)، الترويسة X-IYZ-SIGNATURE-V3 HMAC-SHA256 + base64، الحقل hash في طلب POST
ترتيب حقول التوقيع يختلف باختلاف نوع Webhook (ترتيب مختلف لكل من Direct وHPP وSubscription) merchant_oid + merchant_salt + status + total_amount (iFrame API)
الاستجابة المتوقعة رمز حالة HTTP من فئة 2xx نص عادي OK
سلوك إعادة المحاولة عند عدم تلقي 2xx: كل 15 دقيقة، بحد أقصى 3 مرات عند عدم تلقي OK: بعد دقيقة واحدة
مفتاح إزالة التكرار paymentId / conversationId merchant_oid (يُعالَج الإشعار الأول فقط)

مطابقة الدفع مع الطلب

المطابقة (Reconciliation) هي الجسر بين «المال الذي حصّله المزوّد» و«الطلب في نظامك». والمطابقة الضعيفة البناء هي أكثر ما يسبب المتاعب في جانبي المحاسبة وخدمة العملاء. والطريقة التي يبني بها المزوّدان هذا الجسر هي معرّف طلب فريد ينشئه التاجر:

  • PayTR: الحقل merchant_oid (بحد أقصى 64 حرفًا أبجديًا رقميًا) هو رقم الطلب الفريد الذي تحدده للعملية؛ ويُعاد إليك في إشعار Callback ليتيح المطابقة. كما يُنشأ paytr_token في الخطوة 1 باستخدام HMAC-SHA256 + base64 من سلسلة مدمجة تضم الحقول merchant_id + user_ip + merchant_oid + email + payment_amount + user_basket + no_installment + max_installment + currency + test_mode + merchant_salt جميعها.
  • iyzico: استخدم conversationId رقمًا لطلبك؛ فالقيمة التي ترسلها في طلب API تُعاد كما هي في الاستجابة. ويقابله من جهة المزوّد paymentId، وعبره تجري عمليات الاسترداد والإلغاء. واحفظ المعرّفين كليهما في سجل الطلب لديك.

مبادئ عملية لمطابقة متينة:

  • اجعل المرجعية لإشعار Callback لا للمتصفح: صفحة «النجاح» التي يعود إليها متصفح المستخدم إشارة لا دليل. وأكّد الطلب في الأساس عبر إشعار Callback الذي يصل من خادم إلى خادم بعد التحقق من توقيعه.
  • قارن المبلغ والعملة: طابق المبلغ الوارد في إشعار Callback مع المبلغ المتوقع في طلبك؛ وإذا لم يتطابقا فلا تعالج الإشعار وأطلق تنبيهًا.
  • أغلق الحالات المعلّقة: إذا لم يصل إشعار Callback أبدًا، فاستخدم مهمة خلفية لمراجعة الطلبات التي «بدأ دفعها ولم يكتمل» عبر واجهة API للاستعلام لدى المزوّد، ثم أغلقها.
  • المطابقة اليومية: قارن بانتظام تقارير العمليات والتسوية لدى المزوّد بسجلاتك؛ ولا تعتمد على Webhook وحده.

الاسترداد والاسترداد الجزئي والتقسيط

النصف الثاني من تدفق الدفع هو سيناريوهات ما بعد العملية؛ وهنا تُكسب تجربة العميل ودقة المحاسبة. أما الفروق الأساسية في حالة iyzico فهي كالآتي:

  • الاسترداد (Refund)، كليًا أو جزئيًا: هناك مساران. يعمل /payment/refund على مستوى عنصر السلة (باستخدام paymentTransactionId، أي معرّف تفصيل العملية الخاص بعنصر السلة، مع price). أما /v2/payment/refund فيعمل على مستوى عملية الدفع (باستخدام paymentId وprice)، ويحدد النظام بنفسه عنصر السلة المراد استرداده. ويدعم الإصداران الاسترداد الكلي والجزئي، ويعيدان مرجعًا بنكيًا (refundHostReference، authCode).
  • الإلغاء (Cancel): يُستدعى /payment/cancel باستخدام paymentId فقط، ولا يدعم مبلغًا جزئيًا، ويلغي عملية الدفع بالكامل. ووفقًا لوثائق iyzico، يمكن إجراء الإلغاء في اليوم نفسه الذي تمت فيه عملية الدفع، ولا ينشئ قيدًا مدينًا أو دائنًا في كشف حساب البطاقة؛ أما الاسترداد فينعكس على الكشف وقد يستغرق عدة أيام بحسب البنك. وتختلف القواعد الدقيقة لليوم نفسه وساعة الإقفال باختلاف البنك وتاريخ القيمة (Value Date)؛ فخذ ذلك مبدأً عامًا لا قاعدة محاسبية دقيقة.
  • الاستعلام عن BIN والتقسيط: عبر POST /payment/iyzipos/installment يمكنك الاستعلام عن خيارات التقسيط ونسبه ومبالغه، وعن عائلة البطاقة وشبكتها وبيانات البنك، بإرسال price (إلزامي) وbinNumber (اختياري، BIN من 8 أرقام). ويمكن أيضًا الحصول على نسب التقسيط بالمبلغ وحده (دون BIN)؛ ويُرسَل conversationId في طلب API ويُعاد كما هو في الاستجابة (iyzico — الاسترداد والإلغاء، iyzico — التقسيط وBIN).

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

قائمة التحقق قبل الإطلاق

  • التحقق من التوقيع: هل يجري التحقق من التوقيع في كل إشعار Callback/Webhook يصل إليك؟ هل يُرفض الطلب غير المتحقق منه رفضًا قاطعًا؟ هل يُستخدم لدى iyzico ترتيب الحقول الصحيح لنوع تكاملك؟
  • منع تكرار الأثر (Idempotency): عند وصول إشعار للمرة الثانية بمعرّف الطلب نفسه (merchant_oid / conversationId)، هل تتجنب معالجته مجددًا وتكتفي بإعادة الرد المناسب؟
  • الاستجابة السريعة: هل يعيد Callback استجابة 2xx / OK بسرعة وبالصيغة المتوقعة؟ هل تُوضع الأعمال الثقيلة في قائمة انتظار غير متزامنة؟
  • فحص المبلغ: هل يُقارن المبلغ والعملة في إشعار Callback بالقيمة المتوقعة في الطلب؟
  • مصدر المرجعية: هل يعتمد تأكيد الطلب على الإشعار من خادم إلى خادم بدلًا من إعادة توجيه المتصفح؟
  • إغلاق الحالات المعلّقة: هل يوجد استعلام في الخلفية للمدفوعات التي «بدأت ولم تكتمل» حين لا يصل إشعار Callback أبدًا؟
  • الاسترداد والإلغاء: هل اختُبرت سيناريوهات الاسترداد الكلي والجزئي والإلغاء والتقسيط؟ هل تُحفظ المراجع البنكية؟
  • HTTPS والأسرار: هل تُحفظ الأسرار مثل secretKey/merchant_key/merchant_salt على الخادم فقط، ضمن متغيرات البيئة؟ هل يعمل عنوان Callback عبر بروتوكول آمن (HTTPS)؟
  • التسجيل في السجلات (Logging): هل يُسجَّل كل حدث دفع (طلب API، والاستجابة، وإشعار Callback، ونتيجة التوقيع) بطريقة قابلة للتتبع؟
  • بيئة الاختبار: هل أُجري تحقق متكامل من البداية إلى النهاية (بما في ذلك 3DS) في وضع Sandbox/الاختبار لدى المزوّد؟

كيف تساعدك Partnerfy

بصفتنا شركة هندسة برمجيات B2B وWhite-Label مقرها صقاريا، نتعامل مع تكامل نقطة البيع الافتراضية لا بوصفه مجرد «استدعاء API»، بل بوصفه مشكلة أمن ومطابقة. وفي مشروع برمجيات ويب مخصصة أو تجارة إلكترونية، نبدأ بتوضيح القرار: مؤسسة دفع أم نقطة بيع بنكية، ونموذج مستضاف أم واجهة API مباشرة (بحسب نطاق PCI-DSS لديك). ثم نبني تدفق 3DS، وإشعار Callback موثّق التوقيع لا يتكرر أثره، ومطابقة المبالغ، وسيناريوهات الاسترداد والتقسيط، ونتحقق منها جميعًا من البداية إلى النهاية في بيئة اختبار. وعادةً ما نصل ذلك بأنظمة ERP والمحاسبة الحالية لديك من خلال تكامل API بينها.

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

الخلاصة

يبدأ تكامل نقطة البيع الافتراضية الجاهز لبيئة الإنتاج باستدعاء واجهة API لكنه لا ينتهي عنده. فالعمل الحقيقي هو: اختيار القناة الصحيحة (مؤسسة دفع أم نقطة بيع بنكية)، وتقليص نطاق PCI-DSS عن قصد (نموذج مستضاف أم واجهة مباشرة)، وبناء تدفق 3DS بصورة صحيحة، والتحقق من إشعار Callback عبر التوقيع، ومنع تكرار الأثر، وإعادة استجابة 2xx/OK بسرعة، ومطابقة المبلغ والطلب، وإدارة سيناريوهات الاسترداد والإلغاء والتقسيط إدارة كاملة. وكل خطوة من هذه الخطوات هي ما يفصل بين تكامل «يبدو أنه يعمل» وتكامل آمن حقًا. وإذا كنت بحاجة إلى شريك في تصميم تكامل الدفع أو تأمين إعداد قائم، فتواصل معنا؛ وسنضع معًا خطة القناة ونطاق PCI والمطابقة استنادًا إلى سيناريوك.

المصادر

الأسئلة الشائعة

هل أختار النموذج المستضاف أم واجهة API المباشرة عند تكامل نقطة البيع الافتراضية؟

ما لم يكن لديك متطلب عمل لتجربة دفع تحمل علامتك التجارية وتخضع لتحكمك الكامل، فاختر النموذج المستضاف (Hosted Form). ففي الحلول المستضافة مثل PayTR iFrame أو iyzico CheckoutForm، تُجمع بيانات البطاقة داخل iframe التابع للمزوّد ولا تصل إلى خادمك أبدًا؛ وهذا يقلص نطاق PCI-DSS لديك بدرجة كبيرة. أما واجهة API المباشرة فتمنحك تحكمًا كاملًا، لكنها تزيد عبء PCI عليك زيادة كبيرة لأن بيانات البطاقة تمر عبر بنيتك التحتية.

تدفق 3DS لدى iyzico: كيف يعمل؟

يمثل 3DS لدى iyzico تدفقًا من خطوتين بطلبَي POST متتاليين. ففي البداية ترسل طلب POST /payment/3dsecure/initialize مع بيانات البطاقة وقيمة callbackUrl؛ ويُعرض محتوى threeDSHtmlContent المرمّز (base64) العائد في الاستجابة لتحويل المستخدم إلى شاشة التحقق لدى البنك. وبعد التحقق، ترسل طلب POST ثانيًا إلى نقطة نهاية المصادقة مع قيمة paymentId العائدة من خطوة التهيئة (Init)؛ وتوصي iyzico باستخدام الإصدار v2 (/payment/v2/3dsecure/auth). ويحمل الحقل mdStatus النتيجة: فالقيمة 1 تعني النجاح، والقيمة 0 والقيم من 2 إلى 8 تعني الفشل.

كيف أجعل إشعار Callback الخاص بالدفع آمنًا؟

ثلاث قواعد: تحقّق من التوقيع، وامنع تكرار الأثر (Idempotency)، وأجب بسرعة. لا تعامل الإشعار الوارد على أنه «مدفوع» قبل التحقق من توقيعه (قيمة تجزئة HMAC-SHA256+base64 لدى PayTR، والترويسة X-IYZ-SIGNATURE-V3 لدى iyzico). ولا تعالج للمرة الثانية إشعارًا يتكرر وصوله بمعرّف الطلب نفسه (merchant_oid / conversationId). وادفع الأعمال الثقيلة إلى قائمة انتظار غير متزامنة، وأعِد الاستجابة المتوقعة بسرعة: تنتظر iyzico استجابة 2xx، وتنتظر PayTR نصًا عاديًا هو «OK»؛ وإذا لم تتلقياها أعادتا المحاولة.

كيف أطابق الدفع مع الطلب؟

استخدم معرّف طلب فريدًا ينشئه التاجر (merchant_oid لدى PayTR، وconversationId لدى iyzico). وتصلك هذه القيمة مجددًا في إشعار Callback فتربط بها الدفع بالطلب الصحيح. وأكّد الطلب استنادًا إلى إشعار Callback الذي يصل من خادم إلى خادم بعد التحقق من توقيعه، لا إلى صفحة «النجاح» في المتصفح؛ وقارن المبلغ الوارد في الإشعار بالمبلغ المتوقع؛ وأغلق عبر استعلام في الخلفية المدفوعات المعلّقة التي لم يصلك إشعارها؛ وأجرِ مطابقة يومية مع تقارير المزوّد.

ما الفرق لدى iyzico بين الاسترداد والإلغاء؟

يُستدعى الإلغاء، أي Cancel (/payment/cancel)، باستخدام paymentId فقط، ولا يدعم مبلغًا جزئيًا، ويلغي عملية الدفع بالكامل؛ ووفقًا لمنصة iyzico يمكن إجراؤه عادةً في اليوم نفسه، ولا ينشئ قيدًا مدينًا أو دائنًا في كشف حساب البطاقة. أما الاسترداد (Refund) فقد يكون كليًا أو جزئيًا: إذ يعمل Endpoint /payment/refund على مستوى عنصر السلة، ويعمل Endpoint /v2/payment/refund على مستوى عملية الدفع؛ وينعكس الاسترداد على الكشف وقد يستغرق عدة أيام بحسب البنك. وتختلف القواعد الدقيقة لليوم نفسه وتاريخ القيمة (Value Date) باختلاف البنك.

ما أفضل شركة برمجيات في صقاريا (Sakarya) لتكامل نقطة البيع الافتراضية، ولماذا يُفضَّل التعامل معها؟

يقع المقر الرئيسي لشركة Partnerfy في أدابازاري (Adapazarı) بصقاريا (Sakarya)، ولها مكاتب في أربع دول: تركيا، وألمانيا، وروسيا، وأيرلندا. وبصفتها شركة هندسة برمجيات B2B وWhite-Label، تتعامل Partnerfy مع تكامل نقطة البيع الافتراضية بوصفه مشكلة أمن ومطابقة: فهي تبدأ من القرار بين مؤسسة الدفع ونقطة البيع البنكية، وبين النموذج المستضاف وواجهة API المباشرة (بحسب نطاق PCI-DSS)، ثم تبني تدفق 3DS، وإشعار Callback موثّق التوقيع لا يتكرر أثره (Idempotent)، ومطابقة المبالغ، وسيناريوهات الاسترداد والتقسيط، وتتحقق منها جميعًا من البداية إلى النهاية في بيئة اختبار. وبما تقدمه من انضباط وأساس قابل للتسجيل والتتبع، لا من ضمانات، تُعد Partnerfy من شركات البرمجيات المفضَّلة في صقاريا.