Hi there. I noticed that the PayPal Smart Payment Buttons are output in the main checkout region, but it feels more consistent UX to have them in the checkout flow footer, i.e. form actions. I'm wondering if the way I'm moving them opens any potential security issues or could be done in a more future-proof way?

Note that this uses Hook Event Dispatcher so that it can be implemented in an object-oriented fashion, but it's the same as a hook_form_BASE_FORM_ID_alter() otherwise.

This is in MODULE.services.yml:

  # Event subscriber to alter Commerce checkout flow PayPal buttons location.
  MODULE.base_form_commerce_checkout_flow_paypal_alter_event_subscriber:
    class: Drupal\MODULE\EventSubscriber\Form\CommerceCheckoutFlowPayPalBaseFormAlterEventSubscriber
    tags:
      - { name: 'event_subscriber' }

And this is the PHP file MODULE/src/EventSubscriber/Form/CommerceCheckoutFlowPayPalBaseFormAlterEventSubscriber.php:

<?php

namespace Drupal\MODULE\EventSubscriber\Form;

use Drupal\core_event_dispatcher\Event\Form\FormBaseAlterEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Event subscriber to alter Commerce checkout flow PayPal buttons location.
 *
 * This moves the PayPal Smart Payment Buttons from the main form to the actions
 * so that it's output to the checkout footer for more consistent UX with the
 * other checkout steps.
 *
 * @see \commerce_paypal_form_commerce_checkout_flow_alter()
 *   PayPal Smart Payment Buttons added in this alter hook.
 */
class CommerceCheckoutFlowPayPalBaseFormAlterEventSubscriber implements EventSubscriberInterface {

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents(): array {
    return [
      'hook_event_dispatcher.form_base_commerce_checkout_flow.alter' =>
        'onBaseFormAlter',
    ];
  }

  /**
   * Alter the 'commerce_checkout_flow' base form.
   *
   * @param \Drupal\core_event_dispatcher\Event\Form\FormBaseAlterEvent $event
   *   The event object.
   */
  public function onBaseFormAlter(FormBaseAlterEvent $event): void {

    /** @var array */
    $form = &$event->getForm();

    // Don't do anything if these don't exist so we don't cause any errors.
    if (
      !isset($form['actions']) ||
      !isset($form['paypal_smart_payment_buttons'])
    ) {
      return;
    }

    $form['actions']['paypal_smart_payment_buttons'] =
      $form['paypal_smart_payment_buttons'];

    // Remove the original PayPal buttons now that we've copied them to actions.
    unset($form['paypal_smart_payment_buttons']);

    // Remove the default next button as the PayPal buttons are used instead.
    unset($form['actions']['next']);

    // Commerce PayPal sets this to false to hide it, so undo that.
    unset($form['actions']['#access']);

  }

}

Comments

Ambient.Impact created an issue.