← جميع المقالات >
magento 2checkoutknockout jsmagento developmentphpe-commercetutorialfrontend

تخصيص الدفع في ماجنتو 2: دليل خطوة بخطوة (2026)

Share: LinkedIn X Facebook

عملية الدفع في ماجنتو 2 مبنية على تطبيق أحادي الصفحة Knockout.js — إنها قوية ولكن notoriously معقدة للتخصيص. يغطي هذا الدليل كل نهج رئيسي: تجاوزات layout XML، تمديدات مكونات Knockout، إضافة/إزالة الحقول، الخطوات المخصصة، تعديلات الشحن والدفع، وأفضل الممارسات للبقاء متوافقًا مع الترقيات. يمكنك أيضًا الاطلاع على خدمة تطوير ماجنتو 2 الكاملة إذا كنت بحاجة إلى تنفيذ كامل للدفع.

باختصار: يتكون دفع ماجنتو 2 من مكونات UI (Knockout.js)، ومعالجات layout (XML)، ومزودي PHP (مصادر بيانات). يمكنك تخصيصه عبر layout/checkout_index_index.xml و extend/override لمكونات Knockout و الـ plugins على فئات PHP.

جدول المحتويات

  1. فهم بنية الدفع
  2. الأدوات: Layout XML و Knockout ومزودو PHP
  3. إضافة حقل مخصص إلى عنوان الشحن
  4. إزالة حقل من الدفع
  5. تجاوز مكون Knockout.js
  6. إضافة خطوة دفع مخصصة
  7. تخصيص طرق الشحن
  8. تخصيص طرق الدفع
  9. إضافة قواعد التحقق
  10. تعديل ملخص الطلب (الشريط الجانبي)
  11. التوافق مع الإضافات الخارجية
  12. أفضل الممارسات وأمان الترقية
  13. الأسئلة الشائعة

فهم بنية الدفع

دفع ماجنتو 2 هو تطبيق Knockout.js أحادي الصفحة مع هذه الخطوات الأساسية:

الخطوةالمكونالغرض
الشحنMagento_Checkout/js/view/shippingنموذج عنوان الشحن + اختيار طريقة الشحن
الفوترةMagento_Checkout/js/view/billingنموذج عنوان الفوترة
الدفعMagento_Checkout/js/view/paymentقائمة طرق الدفع
الملخصMagento_Checkout/js/view/summaryالشريط الجانبي لمراجعة الطلب

كل خطوة هي مكون UI يتكون من قالب .html ونموذج عرض .js وفئة PHP مزود توفر بيانات الخلفية. إذا كنت جديدًا في بناء وحدات ماجنتو 2 ، ابدأ من هناك — تخصيص الدفع يبني على نفس بنية الوحدة.

الأدوات: Layout XML و Knockout ومزودو PHP

Layout XML

الملف checkout_index_index.xml يتحكم في أي المكونات يتم عرضها:

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="checkout.root">
            <arguments>
                <argument name="jsLayout" xsi:type="array">
                    <!-- components go here -->
                </argument>
            </arguments>
        </referenceBlock>
    </body>
</page>

تمديد مكون Knockout

استخدم extend لإضافة إلى مكون موجود:

define(['Magento_Checkout/js/view/shipping'], function (Component) {
    'use strict';
    return Component.extend({
        // your custom logic
    });
});

مزودو PHP

منطق الخلفية موجود في Magento\Checkout\Block\Checkout\LayoutProcessor أو Processors مخصصة تطبق LayoutProcessorInterface.

إضافة حقل مخصص إلى عنوان الشحن

الخطوة 1 — إنشاء layout XML

<!-- Vendor/Module/view/frontend/layout/checkout_index_index.xml -->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="checkout.root">
            <arguments>
                <argument name="jsLayout" xsi:type="array">
                    <item name="components" xsi:type="array">
                        <item name="checkout" xsi:type="array">
                            <item name="children" xsi:type="array">
                                <item name="steps" xsi:type="array">
                                    <item name="children" xsi:type="array">
                                        <item name="shipping-step" xsi:type="array">
                                            <item name="children" xsi:type="array">
                                                <item name="shippingAddress" xsi:type="array">
                                                    <item name="children" xsi:type="array">
                                                        <item name="shipping-address-fieldset" xsi:type="array">
                                                            <item name="children" xsi:type="array">
                                                                <item name="custom_field" xsi:type="array">
                                                                    <item name="config" xsi:type="array">
                                                                        <item name="component" xsi:type="string">uiComponent</item>
                                                                        <item name="template" xsi:type="string">ui/form/field</item>
                                                                        <item name="elementTmpl" xsi:type="string">ui/form/elements/input</item>
                                                                        <item name="label" xsi:type="string">Custom Field</item>
                                                                        <item name="dataScope" xsi:type="string">custom_field</item>
                                                                        <item name="provider" xsi:type="string">checkoutProvider</item>
                                                                        <item name="sortOrder" xsi:type="string">150</item>
                                                                        <item name="validation" xsi:type="array">
                                                                            <item name="required-entry" xsi:type="boolean">true</item>
                                                                        </item>
                                                                    </item>
                                                                </item>
                                                            </item>
                                                        </item>
                                                    </item>
                                                </item>
                                            </item>
                                        </item>
                                    </item>
                                </item>
                            </item>
                        </item>
                    </item>
                </argument>
            </arguments>
        </referenceBlock>
    </body>
</page>

الخطوة 2 — حفظ الحقل عبر LayoutProcessor

namespace Vendor\Module\Block\Checkout;

use Magento\Checkout\Block\Checkout\LayoutProcessorInterface;

class CustomFieldProcessor implements LayoutProcessorInterface
{
    public function process($jsLayout)
    {
        // Field was already added via XML; persist it to the quote
        return $jsLayout;
    }
}

الخطوة 3 — Plugin على ShippingInformationManagement

namespace Vendor\Module\Plugin;

use Magento\Checkout\Model\ShippingInformationManagement;
use Magento\Checkout\Model\Session;
use Magento\Quote\Api\Data\AddressExtensionFactory;

class SaveCustomField
{
    private $checkoutSession;
    private $addressExtensionFactory;

    public function __construct(
        Session $checkoutSession,
        AddressExtensionFactory $addressExtensionFactory
    ) {
        $this->checkoutSession = $checkoutSession;
        $this->addressExtensionFactory = $addressExtensionFactory;
    }

    public function beforeSaveAddressInformation(
        ShippingInformationManagement $subject,
        $cartId,
        \Magento\Checkout\Api\Data\ShippingInformationInterface $shippingInfo
    ) {
        $extAttributes = $shippingInfo->getShippingAddress()->getExtensionAttributes();
        if ($extAttributes === null) {
            $extAttributes = $this->addressExtensionFactory->create();
        }
        $customField = $shippingInfo->getShippingAddress()->getCustomAttribute('custom_field');
        $extAttributes->setCustomField($customField ? $customField->getValue() : '');

        return [$cartId, $shippingInfo];
    }
}

إزالة حقل من الدفع

استخدم LayoutProcessor لإزالة الحقول:

namespace Vendor\Module\Block\Checkout;

use Magento\Checkout\Block\Checkout\LayoutProcessorInterface;

class RemoveFieldProcessor implements LayoutProcessorInterface
{
    public function process($jsLayout)
    {
        $fieldset = &$jsLayout['components']['checkout']['children']['steps']
            ['children']['shipping-step']['children']['shippingAddress']
            ['children']['shipping-address-fieldset']['children'];

        unset($fieldset['company']);        // remove company field
        unset($fieldset['telephone']);      // remove telephone field

        return $jsLayout;
    }
}

تجاوز مكون Knockout.js

لاستبدال مكون الشحن بمكونك الخاص:

// Vendor/Module/view/frontend/web/js/view/custom-shipping.js
define([
    'Magento_Checkout/js/view/shipping',
    'Magento_Checkout/js/model/quote',
    'Magento_Customer/js/model/customer'
], function (Component, quote, customer) {
    'use strict';
    return Component.extend({
        defaults: {
            template: 'Vendor_Module/custom-shipping'
        },

        // Add a custom observable
        initialize: function () {
            this._super();
            this.customMessage = ko.observable('');
            return this;
        },

        // Override the shipping method selection
        setShippingInformation: function () {
            // custom logic before calling parent
            return this._super();
        }
    });
});

سجله في checkout_index_index.xml:

<item name="shipping" xsi:type="array">
    <item name="config" xsi:type="array">
        <item name="component" xsi:type="string">Vendor_Module/js/view/custom-shipping</item>
    </item>
</item>

إضافة خطوة دفع مخصصة

الخطوة 1 — إنشاء مكون الخطوة

// Vendor/Module/view/frontend/web/js/view/custom-step.js
define([
    'uiComponent',
    'Magento_Checkout/js/model/step-navigator',
    'jquery',
    'ko'
], function (Component, stepNavigator, $, ko) {
    'use strict';
    var uniqueId = 'custom-step';

    return Component.extend({
        defaults: {
            template: 'Vendor_Module/custom-step'
        },

        isVisible: ko.observable(false),
        stepCode: uniqueId,
        stepTitle: 'Custom Step',

        initialize: function () {
            this._super();
            stepNavigator.registerStep(
                this.stepCode,
                null,
                this.stepTitle,
                this.isVisible,
                _.bind(this.navigate, this),
                25  // sort order
            );
            return this;
        },

        navigate: function () {
            this.isVisible(true);
        },

        navigateToNextStep: function () {
            this.isVisible(false);
            stepNavigator.next();
        }
    });
});

الخطوة 2 — التسجيل في layout XML

<item name="custom-step" xsi:type="array">
    <item name="config" xsi:type="array">
        <item name="component" xsi:type="string">Vendor_Module/js/view/custom-step</item>
    </item>
</item>

تخصيص طرق الشحن

إخفاء طريقة شحن

namespace Vendor\Module\Plugin;

use Magento\Quote\Api\Data\ShippingMethodInterface;
use Magento\Quote\Model\Cart\ShippingMethodConverter;

class HideShippingMethod
{
    public function afterModelToDataObject(
        ShippingMethodConverter $subject,
        ShippingMethodInterface $result
    ) {
        $code = $result->getCarrierCode() . '_' . $result->getMethodCode();
        if (in_array($code, ['flatrate_flatrate'])) {
            return null;
        }
        return $result;
    }
}

إضافة طريقة شحن مخصصة

أنشئ فئة Carrier تمتد \Magento\Shipping\Model\Carrier\AbstractCarrier ونفذ collectRates()، ثم سجلها عبر config.xml.

تخصيص طرق الدفع

إضافة طريقة دفع مخصصة

  1. أنشئ Model يطبق Magento\Payment\Model\MethodInterface
  2. عرّف config.xml مع تكوين الطريقة
  3. أنشئ قوالب الواجهة الأمامية لنموذج الدفع
  4. سجل مكون Knockout لعرض الدفع

هل تحتاج إلى تكامل بوابة دفع مخصصة؟ راجع خدمة الوحدات المخصصة الخاصة بي لمعالجات الدفع المحلية (Konnect, Flouci, Paymee) وتدفقات الدفع الكاملة.

<!-- config.xml -->
<default>
    <payment>
        <custom_payment>
            <model>Vendor\Module\Model\Payment\CustomPayment</model>
            <title>Custom Payment</title>
            <active>1</active>
            <sort_order>10</sort_order>
            <order_status>pending</order_status>
            <allowspecific>0</allowspecific>
        </custom_payment>
    </payment>
</default>

إضافة قواعد التحقق

أضف قواعد تحقق Knockout مخصصة:

define(['jquery', 'jquery/validate'], function ($) {
    'use strict';
    $.validator.addMethod('custom-rule', function (value) {
        return value && value.length >= 3;
    }, $.mage.__('Value must be at least 3 characters'));
});

ثم ارجع إليها في layout XML الخاص بك:

<item name="validation" xsi:type="array">
    <item name="custom-rule" xsi:type="boolean">true</item>
</item>

تعديل ملخص الطلب (الشريط الجانبي)

تجاوز مكون Knockout summary:

define([
    'Magento_Checkout/js/view/summary/abstract-total',
    'Magento_Checkout/js/model/quote'
], function (Component, quote) {
    'use strict';
    return Component.extend({
        getCustomBlockHtml: function () {
            return '<p class="custom-note">' +
                $t('Your custom message here') + '</p>';
        }
    });
});

التوافق مع الإضافات الخارجية

المشكلةالحل
تعارضات الحقولاستخدم sortOrder للتحكم في موضع الحقل
ترتيب الخطوات معطلسجل الخطوة بوزن ترتيب صحيح
أخطاء JS من التجاوزاتاستخدم extend بدلاً من replace عندما يكون ذلك ممكنًا
بيانات المزود لا تستمرأنشئ plugin على ShippingInformationManagement::saveAddressInformation
تضارب CSSحدد النطاق باستخدام محددات فئة خاصة بالدفع

أفضل الممارسات وأمان الترقية

  • فضّل extend على استبدال المكون بالكامل — يحافظ على عمل الكود عبر تحديثات الإصدارات.
  • استخدم LayoutProcessorInterface لتعديلات الحقول بدلاً من تجاوز القوالب.
  • خزّن البيانات المخصصة في جداول quote_extension عبر سمات extension — يتجنب تعديل الجداول الأساسية.
  • اختبر عبر المتصفحات والأجهزة — يختلف سلوك JS للدفع.
  • تجنب تعديل ملفات Magento_Checkout/js/model/** مباشرة — قم بتمديدها.
  • قم دائمًا بمسح المحتوى الثابت بعد تغييرات JS: php bin/magento setup:static-content:deploy -f.
  • نفّذ compilation كاملة (php bin/magento setup:upgrade && php bin/magento setup:di:compile) بعد إضافة فئات PHP جديدة — راجع الدليل حول setup:upgrade vs di:compile للتفاصيل.
  • لضبط أداء أعمق، راجع خدمة تحسين الأداء الخاصة بي التي تغطي تدقيق FPC و Varnish و Core Web Vitals.

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

لماذا استخدام LayoutProcessor بدلاً من XML لتغييرات الحقول؟

LayoutProcessor أكثر مرونة للمنطق الشرطي (مثال: إظهار حقل فقط لطرق شحن معينة) ويتجنب XML المتداخل بعمق للتغييرات الديناميكية.

كيف أختبر تغييرات الدفع الخاصة بي في وضع المطور؟

php bin/magento deploy:mode:set developer
php bin/magento cache:flush
npm run watch   # if theme uses Grunt

الحقل المخصص الخاص بي لا يُحفظ. ماذا يجب أن أتحقق؟

  1. تحقق من أن dataScope يطابق رمز السمة
  2. plugin على ShippingInformationManagement::saveAddressInformation مسجل
  3. سمات extension مُعلنة بشكل صحيح في extension_attributes.xml
  4. تحقق من وحدة تحكم المتصفح لأخطاء JS

هل يمكنني إضافة خطوة بعد الدفع؟

نعم — سجل خطوتك بترتيب فرز أعلى من خطوة الدفع (الافتراضي: 30) ونفذ منطق التنقل وفقًا لذلك.

الخاتمة

يتطلب تخصيص دفع ماجنتو 2 فهمًا قويًا لبنية مكونات Knockout.js ونظام layout XML ومزودي PHP. باستخدام extend بدلاً من الاستبدال، و LayoutProcessor لتغييرات الحقول، و plugins لاستمرارية البيانات، يمكنك بناء تخصيصات دفع قوية وآمنة للترقية.

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

  • بناء طريقة شحن مخصصة بأسعار ديناميكية
  • تنفيذ دفع بخطوة واحدة باستخدام وحدات خارجية
  • إنشاء خطوة رسالة هدية أو تعليق طلب
  • إضافة التحقق من العنوان عبر Google Maps API