← Tous les articles >
magento 2checkoutknockout jsmagento developmentphpe-commercetutorialfrontend

Personnalisation du checkout Magento 2 : Guide pas à pas (2026)

Share: LinkedIn X Facebook

Le checkout de Magento 2 est construit sur une application monopage Knockout.js — puissante mais notoirement complexe à personnaliser. Ce guide couvre toutes les approches principales : surcharges de layout XML, extensions de composants Knockout, ajout/suppression de champs, étapes personnalisées, modifications de l’expédition et du paiement, et bonnes pratiques pour rester compatible avec les mises à jour. Vous pouvez aussi consulter mon service de développement Magento 2 si vous avez besoin d’une implémentation complète du checkout.

En résumé : Le checkout Magento 2 est composé de composants UI (Knockout.js), de handles de layout (XML) et de fournisseurs PHP (sources de données). Vous pouvez le personnaliser via layout/checkout_index_index.xml, l’extend/override des composants Knockout, et les plugins sur les classes PHP.

Table des matières

  1. Comprendre l’architecture du checkout
  2. Outils : Layout XML, Knockout et fournisseurs PHP
  3. Ajouter un champ personnalisé à l’adresse de livraison
  4. Supprimer un champ du checkout
  5. Surcharger un composant Knockout.js
  6. Ajouter une étape de checkout personnalisée
  7. Personnaliser les méthodes d’expédition
  8. Personnaliser les méthodes de paiement
  9. Ajouter des règles de validation
  10. Modifier le récapitulatif de commande (barre latérale)
  11. Compatibilité avec les extensions tierces
  12. Bonnes pratiques et sécurité de mise à jour
  13. FAQ

Comprendre l’architecture du checkout

Le checkout de Magento 2 est une application Knockout.js monopage avec ces étapes principales :

ÉtapeComposantObjectif
ExpéditionMagento_Checkout/js/view/shippingFormulaire d’adresse de livraison + sélection du mode d’expédition
FacturationMagento_Checkout/js/view/billingFormulaire d’adresse de facturation
PaiementMagento_Checkout/js/view/paymentListe des méthodes de paiement
RécapitulatifMagento_Checkout/js/view/summaryBarre latérale de révision de la commande

Chaque étape est un composant UI composé d’un template .html, d’un view-model .js et d’une classe PHP provider qui fournit les données backend. Si vous débutez dans la construction de modules Magento 2 , commencez par là — la personnalisation du checkout s’appuie sur la même structure de module.

Outils : Layout XML, Knockout et fournisseurs PHP

Layout XML

Le fichier checkout_index_index.xml contrôle quels composants sont rendus :

<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>

Extension de composant Knockout

Utilisez extend pour ajouter à un composant existant :

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

Fournisseurs PHP

La logique backend vit dans Magento\Checkout\Block\Checkout\LayoutProcessor ou des Processors personnalisés qui implémentent LayoutProcessorInterface.

Ajouter un champ personnalisé à l’adresse de livraison

Étape 1 — Créer le 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>

Étape 2 — Sauvegarder le champ via 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;
    }
}

Étape 3 — Plugin sur 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];
    }
}

Supprimer un champ du checkout

Utilisez LayoutProcessor pour retirer des champs :

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;
    }
}

Surcharger un composant Knockout.js

Pour remplacer le composant d’expédition par le vôtre :

// 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();
        }
    });
});

Enregistrez-le dans 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>

Ajouter une étape de checkout personnalisée

Étape 1 — Créer le composant d’étape

// 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();
        }
    });
});

Étape 2 — Enregistrer dans le 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>

Personnaliser les méthodes d’expédition

Masquer une méthode d’expédition

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;
    }
}

Ajouter une méthode d’expédition personnalisée

Créez une classe Carrier étendant \Magento\Shipping\Model\Carrier\AbstractCarrier et implémentez collectRates(), puis enregistrez-la via config.xml.

Personnaliser les méthodes de paiement

Ajouter une méthode de paiement personnalisée

  1. Créez un Model implémentant Magento\Payment\Model\MethodInterface
  2. Définissez config.xml avec la configuration de la méthode
  3. Créez des templates frontend pour le formulaire de checkout
  4. Enregistrez un composant Knockout pour le rendu du paiement

Besoin d’une intégration de passerelle personnalisée ? Consultez mon service de modules personnalisés pour les processeurs de paiement locaux (Konnect, Flouci, Paymee) et les flux de checkout complets.

<!-- 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>

Ajouter des règles de validation

Ajoutez des règles de validation Knockout personnalisées :

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'));
});

Puis référencez dans votre layout XML :

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

Modifier le récapitulatif de commande (barre latérale)

Surchargez le composant 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>';
        }
    });
});

Compatibilité avec les extensions tierces

ProblèmeSolution
Conflits de champsUtilisez sortOrder pour contrôler la position des champs
Ordre des étapes perturbéEnregistrez l’étape avec le bon poids de tri
Erreurs JS dues aux surchargesUtilisez extend plutôt que replace quand c’est possible
Données fournisseur non persistantesCréez un plugin sur ShippingInformationManagement::saveAddressInformation
Collisions CSSLimitez les styles avec des sélecteurs de classe spécifiques au checkout

Bonnes pratiques et sécurité de mise à jour

  • Préférez extend au remplacement complet du composant — votre code reste fonctionnel après les mises à jour de version.
  • Utilisez LayoutProcessorInterface pour les modifications de champs plutôt que de surcharger les templates.
  • Stockez les données personnalisées dans les tables quote_extension via les attributs d’extension — évite les modifications des tables principales.
  • Testez sur plusieurs navigateurs et appareils — le JS du checkout varie en comportement.
  • Évitez de modifier directement les fichiers Magento_Checkout/js/model/** — étendez-les.
  • Videz toujours le contenu statique après des modifications JS : php bin/magento setup:static-content:deploy -f.
  • Exécutez une compilation complète (php bin/magento setup:upgrade && php bin/magento setup:di:compile) après avoir ajouté de nouvelles classes PHP — consultez le guide sur setup:upgrade vs di:compile pour les détails.
  • Pour un réglage plus approfondi des performances, consultez mon service d’optimisation des performances couvrant l’audit FPC, Varnish et les Core Web Vitals.

FAQ

Pourquoi utiliser LayoutProcessor plutôt que XML pour les modifications de champs ?

LayoutProcessor est plus flexible pour la logique conditionnelle (par exemple, afficher un champ uniquement pour certaines méthodes d’expédition) et évite le XML profondément imbriqué pour les modifications dynamiques.

Comment tester mes modifications du checkout en mode développeur ?

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

Mon champ personnalisé ne se sauvegarde pas. Que dois-je vérifier ?

  1. Vérifiez que dataScope correspond au code d’attribut
  2. Le plugin sur ShippingInformationManagement::saveAddressInformation est enregistré
  3. Les attributs d’extension sont correctement déclarés dans extension_attributes.xml
  4. Vérifiez la console du navigateur pour les erreurs JS

Puis-je ajouter une étape après le paiement ?

Oui — enregistrez votre étape avec un ordre de tri supérieur à celui de l’étape de paiement (valeur par défaut : 30) et implémentez la logique de navigation en conséquence.

Conclusion

La personnalisation du checkout Magento 2 nécessite une bonne compréhension de son architecture de composants Knockout.js, de son système de layout XML et de ses fournisseurs PHP. En utilisant extend plutôt que le remplacement, LayoutProcessor pour les modifications de champs et des plugins pour la persistence des données, vous pouvez construire des personnalisations de checkout robustes et compatibles avec les mises à jour.

Prochaines étapes :

  • Construire une méthode d’expédition personnalisée avec des tarifs dynamiques
  • Implémenter un checkout en une étape avec des modules tiers
  • Créer une étape de message cadeau ou de commentaire de commande
  • Ajouter une validation d’adresse via l’API Google Maps