Commit b2d993d6 authored by Jonathan Sacksick's avatar Jonathan Sacksick Committed by Jonathan Sacksick
Browse files

Issue #3184254 by recidive, jsacksick, dwkitchen, mglaman: Separate stored...

Issue #3184254 by recidive, jsacksick, dwkitchen, mglaman: Separate stored payment methods from new in the Payment Checkout Pane.
parent b4ca2e69
Loading
Loading
Loading
Loading
+67 −10
Original line number Diff line number Diff line
@@ -159,6 +159,7 @@ class PaymentInformation extends CheckoutPaneBase {
    $payment_gateway_storage = $this->entityTypeManager->getStorage('commerce_payment_gateway');
    // Load the payment gateways. This fires an event for filtering the
    // available gateways, and then evaluates conditions on all remaining ones.
    /** @var \Drupal\commerce_payment\Entity\PaymentGatewayInterface[] $payment_gateways */
    $payment_gateways = $payment_gateway_storage->loadMultipleForOrder($this->order);
    // Can't proceed without any payment gateways.
    if (empty($payment_gateways)) {
@@ -178,6 +179,17 @@ class PaymentInformation extends CheckoutPaneBase {
    $option_labels = array_map(function (PaymentOption $option) {
      return $option->getLabel();
    }, $options);
    $stored_options = $new_options = [];
    // Separate the stored payment options from the new ones.
    foreach ($options as $id => $option) {
      if (!empty($option->getPaymentMethodId())) {
        $stored_options[$id] = $option;
      }
      else {
        $new_options[$id] = $option;
      }
    }
    // Try to get a payment method from user input.
    $parents = array_merge($pane_form['#parents'], ['payment_method']);
    $default_option_id = NestedArray::getValue($form_state->getUserInput(), $parents);
    if ($default_option_id && isset($options[$default_option_id])) {
@@ -186,26 +198,62 @@ class PaymentInformation extends CheckoutPaneBase {
    else {
      $default_option = $this->paymentOptionsBuilder->selectDefaultOption($this->order, $options);
    }

    $pane_form['#after_build'][] = [get_class($this), 'clearValues'];
    $pane_form['payment_method'] = [

    // First build the stored payment method options if any.
    if (!empty($stored_options)) {
      $pane_form['stored_payment_method'] = [
        '#type' => 'radios',
        '#title' => $this->t('Your payment methods'),
        '#parents' => $parents,
        '#options' => array_intersect_key($option_labels, $stored_options),
        '#default_value' => isset($stored_options[$default_option->getId()]) ? $default_option->getId() : NULL,
        '#ajax' => [
          'callback' => [static::class, 'ajaxRefresh'],
          'wrapper' => $pane_form['#id'],
        ],
        // Because we reuse the same "name" for the stored payment method
        // and new payment method form elements, Drupal gets confused during
        // validation, so we have to pretend the element is already validated.
        // Note that we have our own validation in validatePaneForm().
        '#validated' => TRUE,
      ];

      // Add a class to each individual radio, to help themers.
      foreach ($stored_options as $option) {
        $pane_form['stored_payment_method'][$option->getId()]['#attributes']['class'][] = "payment-method--stored";
      }
    }

    $pane_form['new_payment_method'] = [
      '#type' => 'radios',
      '#title' => $this->t('Payment method'),
      '#options' => $option_labels,
      '#default_value' => $default_option->getId(),
      '#title' => empty($stored_options) ? $this->t('Select a payment method') : $this->t('Add a new payment method'),
      '#options' => array_intersect_key($option_labels, $new_options),
      '#default_value' => isset($new_options[$default_option->getId()]) ? $default_option->getId() : NULL,
      '#parents' => $parents,
      '#ajax' => [
        'callback' => [get_class($this), 'ajaxRefresh'],
        'callback' => [static::class, 'ajaxRefresh'],
        'wrapper' => $pane_form['#id'],
      ],
      '#access' => count($options) > 1,
      // Only show the new payment method options if there are more than one or
      // if there are stored options available.
      '#access' => count($new_options) > 1 || count($stored_options) > 0,
      // Because we reuse the same "name" for the stored payment method
      // and new payment method form elements, Drupal gets confused during
      // validation, so we have to pretend the element is already validated.
      // Note that we have our own validation in validatePaneForm().
      '#validated' => TRUE,
    ];

    // Add a class to each individual radio, to help themers.
    foreach ($options as $option) {
      $class_name = $option->getPaymentMethodId() ? 'stored' : 'new';
      $pane_form['payment_method'][$option->getId()]['#attributes']['class'][] = "payment-method--$class_name";
    foreach ($new_options as $option) {
      $pane_form['new_payment_method'][$option->getId()]['#attributes']['class'][] = "payment-method--new";
    }

    // Store the options for submitPaneForm().
    $pane_form['#payment_options'] = $options;
    // Store the default payment option for validatePaneForm().
    $pane_form['#default_option'] = $default_option;

    // If this is an existing payment method, return the pane form.
    // Editing payment methods at checkout is not supported.
@@ -347,6 +395,15 @@ class PaymentInformation extends CheckoutPaneBase {
    if (!isset($values['payment_method'])) {
      $form_state->setError($complete_form, $this->noPaymentGatewayErrorMessage());
    }
    // Because we disabled Drupal's default validation, we have to perform
    // our own, to ensure the selected payment method is a valid option.
    elseif (!isset($pane_form['#payment_options'][$values['payment_method']])) {
      if (isset($pane_form['#default_option'])) {
        $values['payment_method'] = $pane_form['#default_option']->getId();
        $form_state->setValue($pane_form['#parents'], $values);
      }
      $form_state->setError($complete_form, $this->t('An illegal choice has been detected. Please contact the site administrator.'));
    }
  }

  /**