Shopware 6: Putting Payment Methods in the Shipping Method space

This blogpost is based on Shopware version 6.4.20.2.

One of our customers had the requirement to implement a couple of additional, “special” payment methods. Because this increased the number of payment methods in the shop to 12 in total, the customer suggested to spread the payment methods out to both sides of the checkout, using the shipping method column to hold the new payment methods – and we agreed: That’s a great idea! Especially because we only have a single, default shipping method – so there’s really no functional benefit to listing it in the first place.

Also, as we explained in a previous post, the shipping method is already set on the checkout context, which means we can just remove all the shipping stuff from the checkout page – no extra-hassle involved!

Remove shipping all-together

For starters, we won’t need the shipping form anymore, so we just get rid of it in
@Storefront/storefront/component/shipping/shipping-form.html.twig

{% sw_extends "@Storefront/storefront/component/shipping/shipping-form.html.twig" %}

{% block page_checkout_change_shipping_form_element %}
    {# avoid inserting "changeShippingForm" form element here or anywhere else #}
    {{ block('page_checkout_change_shipping_form_fields') }}
{% endblock %}

Move the payment form “up the DOM” to have it contain the shipping method column

This is important to make the payment method selection work across both columns in the checkout.

Remove the payment form

The first step is to remove the payment form from its original template
@Storefront/storefront/component/payment/payment-form.html.twig

{% sw_extends "@Storefront/storefront/component/payment/payment-form.html.twig" %}

{% block page_checkout_change_payment_form_element %}
    {# avoid inserting "changePaymentForm" element here #}
    {# inserted in storefront/page/checkout/confirm/index.html.twig instead #}
    {{ block('page_checkout_change_payment_form_fields') }}
{% endblock %}

Add it back in, but further up the DOM

To add the payment form back in, we climb up the template hierarchy to
@Storefront/storefront/page/checkout/confirm/index.html.twig

{% sw_extends "@Storefront/storefront/page/checkout/confirm/index.html.twig" %}

{# vars necessary for making form element work here #}
{% set formAjaxSubmitOptions = {
    changeTriggerSelectors: ['.payment-method-input']
} %}
{% set action = 'frontend.checkout.configure' %}
{% set actionPath = path('frontend.checkout.configure') %}
{% set redirect = 'frontend.checkout.confirm.page' %}

{% block page_checkout_confirm_payment_shipping %}
    {# insert "changePaymentForm" form element, so it spans across the entire "confirm-payment-shipping" div #}
    {# necessary to make the radio buttons work across columns #}
    <form id="changePaymentForm"
          name="changePaymentForm"
          action="{{ actionPath }}"
          data-form-csrf-handler="true"
          data-form-auto-submit="true"
          data-form-auto-submit-options='{{ formAjaxSubmitOptions|json_encode }}'
          method="post">

        {# @deprecated tag:v6.5.0 - Block page_checkout_change_payment_form_csrf will be removed. #}
        {% block page_checkout_change_payment_form_csrf %}
            {{ sw_csrf(action) }}
        {% endblock %}

        {% block page_checkout_change_payment_form_redirect %}
            <input type="hidden" name="redirectTo" value="{{ redirect }}">
            <input type="hidden" name="redirectParameters[redirected]" value="0">
        {% endblock %}

        <div class="confirm-payment-shipping">
            <div class="row">
                {{ block('page_checkout_confirm_payment') }}

                {{ block('page_checkout_confirm_shipping') }}
            </div>
        </div>
    </form>
{% endblock %}

Remove the new payment methods from the original form

The next step is to remove the new payment methods from the default payment method column on the left side of the checkout. To do this, we overwrite the @Storefront/storefront/component/payment/payment-fields.html.twig template to override the component_payment_methods block;

{% sw_extends "@Storefront/storefront/component/payment/payment-fields.html.twig" %}

{# altered payment method rendering to exclude new payment methods here #}
{# those will be rendered in storefront/component/shipping/shipping-fields.html.twig instead #}

{% block component_payment_methods %}
    <div class="payment-methods-regular">
        {% block component_payment_method %}
            {% set regularPaymentMethods = {} %}
            {% for payment in page.paymentMethods %}
                {% if payment.handlerIdentifier !== 'Customer\\Payment\\PaymentHandler\\PaymentHandler' %}
                    {% set regularPaymentMethods = regularPaymentMethods|merge({(payment.id): payment}) %}
                {% endif %}
            {% endfor %}

            {% if visiblePaymentMethodsLimit is not same as (null) %}
                {% set visiblePaymentMethods = regularPaymentMethods|slice(0, visiblePaymentMethodsLimit) %}
                {% set hiddenPaymentMethods = regularPaymentMethods|slice(visiblePaymentMethodsLimit) %}
            {% else %}
                {% set visiblePaymentMethods = regularPaymentMethods %}
                {% set hiddenPaymentMethods = {} %}
            {% endif %}

            {% for payment in visiblePaymentMethods %}
                {% sw_include '@Storefront/storefront/component/payment/payment-method.html.twig' %}
            {% endfor %}

            {% block component_payment_method_collapse %}
                {% if hiddenPaymentMethods|length > 0 %}
                    <div class="collapse">
                        {% for payment in hiddenPaymentMethods %}
                            {% sw_include '@Storefront/storefront/component/payment/payment-method.html.twig' %}
                        {% endfor %}
                    </div>

                    {{ block('component_payment_method_collapse_trigger') }}
                {% endif %}
            {% endblock %}
        {% endblock %}
    </div>
{% endblock %}

In our case that’s easy, because the new payment methods all use the same PaymentHandler. So we just loop through the payment methods and remove the new ones, then we split the remaining payment methods between visible and hidden ones (sticking with the Shopware default on that).

Add new payment methods to the shipping method column

Last but not least, we add the new payment methods to the shipping method column – this time sorting out the ones that aren’t using our “special” PaymentHandler.

{% sw_extends "@Storefront/storefront/component/shipping/shipping-fields.html.twig" %}

{# altered shipping method rendering to render customer payment methods instead of shipping methods here #}
{% set selectedPaymentMethodId = context.paymentMethod.id %}{# a workaround for not having to alter field rendering in storefront/component/shipping/shipping-form.html.twig #}
{% block component_shipping_methods %}
    <div class="payment-methods-customer">
        {% block component_shipping_method %}
            {% set customerPaymentMethods = {} %}
            {% for payment in page.paymentMethods %}
                {% if payment.handlerIdentifier === 'Customer\\Payment\\PaymentHandler\\PaymentHandler' %}
                    {% set customerPaymentMethods = customerPaymentMethods|merge({(payment.id): payment}) %}
                {% endif %}
            {% endfor %}

            {% if visibleShippingMethodsLimit is not same as (null) %}
                {% set visiblePaymentMethods = customerPaymentMethods|slice(0, visibleShippingMethodsLimit) %}{# using shippingMethodsLimit here is not an accident, but makes things a little easier #}
                {% set hiddenPaymentMethods = customerPaymentMethods|slice(visibleShippingMethodsLimit) %}
            {% else %}
                {% set visiblePaymentMethods = customerPaymentMethods %}
                {% set hiddenPaymentMethods = {} %}
            {% endif %}

            {% for payment in visiblePaymentMethods %}
                {% sw_include '@Storefront/storefront/component/payment/payment-method.html.twig' %}
            {% endfor %}

            {% block component_shipping_method_collapse %}
                {% if hiddenPaymentMethods|length > 0 %}
                    <div class="collapse">
                        {% for payment in hiddenPaymentMethods %}
                            {% sw_include '@Storefront/storefront/component/payment/payment-method.html.twig' %}
                        {% endfor %}
                    </div>

                    {{ block('component_shipping_method_collapse_trigger') }}
                {% endif %}
            {% endblock %}
        {% endblock %}
    </div>
{% endblock %}

This way, the payment methods are neatly spread across both columns, the JS still works fine and they all use the central payment method template @Storefront/storefront/component/payment/payment-method.html.twig – so if we need to make any additions or changes to our payment methods, this is the template to alter.

One thought on “Shopware 6: Putting Payment Methods in the Shipping Method space

Leave a Reply

Discover more from Winkelwagen

Subscribe now to keep reading and get access to the full archive.

Continue reading