From d59454067b66c219a4c533b896ac8653c23ca521 Mon Sep 17 00:00:00 2001 From: tavi toporjinschi Date: Tue, 21 Feb 2017 14:07:28 +0000 Subject: [PATCH 1/3] Issue #2827144 by vasike, borisson_, rgpublic, sumanthkumarc, bojanz, andypost: Support multiple payment gateways on Checkout Payment information pane. --- .../CheckoutPane/PaymentInformation.php | 222 ++++++++++-------- 1 file changed, 126 insertions(+), 96 deletions(-) diff --git a/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php index 2b275e1245..c0616254e6 100644 --- a/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php +++ b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php @@ -4,7 +4,6 @@ use Drupal\commerce_checkout\Plugin\Commerce\CheckoutFlow\CheckoutFlowInterface; use Drupal\commerce_checkout\Plugin\Commerce\CheckoutPane\CheckoutPaneBase; -use Drupal\commerce_payment\Entity\PaymentGatewayInterface as EntityPaymentGatewayInterface; use Drupal\commerce_payment\Plugin\Commerce\PaymentGateway\SupportsStoredPaymentMethodsInterface; use Drupal\Component\Utility\Html; use Drupal\Component\Utility\NestedArray; @@ -41,6 +40,20 @@ class PaymentInformation extends CheckoutPaneBase implements ContainerFactoryPlu */ protected $renderer; + /** + * The payment gateway storage. + * + * @var \Drupal\commerce_payment\PaymentGatewayStorageInterface $paymentGatewayStorage + */ + + protected $paymentGatewayStorage; + /** + * The payment method storage. + * + * @var \Drupal\commerce_payment\PaymentMethodStorageInterface $paymentMethodStorage + */ + protected $paymentMethodStorage; + /** * Constructs a new PaymentInformation object. * @@ -62,6 +75,8 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition $this->entityTypeManager = $entity_type_manager; $this->renderer = $renderer; + $this->paymentGatewayStorage = $this->entityTypeManager->getStorage('commerce_payment_gateway'); + $this->paymentMethodStorage = $this->entityTypeManager->getStorage('commerce_payment_method'); } /** @@ -113,97 +128,93 @@ public function buildPaneSummary() { * {@inheritdoc} */ public function buildPaneForm(array $pane_form, FormStateInterface $form_state, array &$complete_form) { - /** @var \Drupal\commerce_payment\PaymentGatewayStorageInterface $payment_gateway_storage */ - $payment_gateway_storage = $this->entityTypeManager->getStorage('commerce_payment_gateway'); /** @var \Drupal\commerce_payment\Entity\PaymentGatewayInterface[] $payment_gateways */ - $payment_gateways = $payment_gateway_storage->loadMultipleForOrder($this->order); + $payment_gateways = $this->paymentGatewayStorage->loadMultipleForOrder($this->order); // When no payment gateways are defined, throw an error and fail reliably. if (empty($payment_gateways)) { drupal_set_message($this->noPaymentGatewayErrorMessage(), 'error'); return []; } - // @todo Support multiple gateways. - /** @var \Drupal\commerce_payment\Entity\PaymentGatewayInterface $payment_gateway */ - $payment_gateway = reset($payment_gateways); - - $pane_form['payment_gateway'] = [ - '#type' => 'value', - '#value' => $payment_gateway->id(), - ]; - - $payment_gateway_plugin = $payment_gateway->getPlugin(); - - if ($payment_gateway_plugin instanceof SupportsStoredPaymentMethodsInterface) { - $this->attachPaymentMethodForm($payment_gateway, $pane_form, $form_state); - } - else { - $store = $this->order->getStore(); - $billing_profile = $this->order->getBillingProfile(); - if (!$billing_profile) { - $billing_profile = Profile::create([ - 'uid' => $this->order->getCustomerId(), - 'type' => 'customer', - ]); - } - $pane_form['billing_information'] = [ - '#type' => 'commerce_profile_select', - '#default_value' => $billing_profile, - '#default_country' => $store->getAddress()->getCountryCode(), - '#available_countries' => $store->getBillingCountries(), - ]; - } - - return $pane_form; - } - - /** - * Creates the payment method selection form for supported gateways. - * - * @param \Drupal\commerce_payment\Entity\PaymentGatewayInterface $payment_gateway - * The payment gateway. - * @param array $pane_form - * The pane form, containing the following basic properties: - * - #parents: Identifies the position of the pane form in the overall - * parent form, and identifies the location where the field values are - * placed within $form_state->getValues(). - * @param \Drupal\Core\Form\FormStateInterface $form_state - * The form state of the parent form. - */ - protected function attachPaymentMethodForm(EntityPaymentGatewayInterface $payment_gateway, array &$pane_form, FormStateInterface $form_state) { - /** @var \Drupal\commerce_payment\PaymentMethodStorageInterface $payment_method_storage */ - $payment_method_storage = $this->entityTypeManager->getStorage('commerce_payment_method'); - $payment_gateway_plugin = $payment_gateway->getPlugin(); $options = []; $default_option = NULL; + /** @var \Drupal\commerce_payment\Entity\PaymentMethodInterface $order_payment_method */ $order_payment_method = $this->order->payment_method->entity; + /** @var \Drupal\commerce_payment\Entity\PaymentGatewayInterface $order_payment_gateway */ + $order_payment_gateway = $this->order->payment_gateway->entity; $customer = $this->order->getCustomer(); - if ($customer) { - $payment_methods = $payment_method_storage->loadReusable($customer, $payment_gateway); - foreach ($payment_methods as $payment_method) { - $options[$payment_method->id()] = $payment_method->label(); + foreach ($payment_gateways as $payment_gateway) { + $payment_gateway_plugin = $payment_gateway->getPlugin(); + // Add existing payment methods for customer. + if ($customer) { + $payment_methods = $this->paymentMethodStorage->loadReusable($customer, $payment_gateway); + foreach ($payment_methods as $payment_method) { + $option_id = $payment_method->id(); + $options[$option_id] = [ + 'id' => $option_id, + 'label' => $payment_method->label(), + 'payment_method_plugin' => $payment_gateway_plugin->getPluginId(), + 'payment_gateway' => $payment_gateway->id(), + ]; + } } // The order's payment method must always be available. if ($order_payment_method && !isset($options[$order_payment_method->id()])) { - $options[$order_payment_method->id()] = $order_payment_method->label(); + $option_id = $order_payment_method->id(); + $options[$option_id] = [ + 'id' => $option_id, + 'label' => $order_payment_method->label(), + 'payment_method_plugin' => $order_payment_method->getType()->getPluginId(), + 'payment_gateway' => $order_payment_gateway->id(), + ]; + } + // New payment method. + $payment_method_types = $payment_gateway_plugin->getPaymentMethodTypes(); + foreach ($payment_method_types as $payment_method_type) { + $option_id = 'new--' . $payment_method_type->getPluginId() . '--' . $payment_gateway->id(); + $option_label = $this->t('@payement_method_label: @payment_gateway_label', [ + '@payement_method_label' => $payment_method_type->getCreateLabel(), + '@payment_gateway_label' => $payment_gateway->label(), + ]); + $options[$option_id] = [ + 'id' => $option_id, + 'label' => $option_label, + 'payment_method_plugin' => $payment_method_type->getPluginId(), + 'payment_gateway' => $payment_gateway->id(), + ]; } } - $payment_method_types = $payment_gateway_plugin->getPaymentMethodTypes(); - foreach ($payment_method_types as $payment_method_type) { - $id = 'new_' . $payment_method_type->getPluginId(); - $options[$id] = $payment_method_type->getCreateLabel(); - } + + // Get the default value. $user_input = $form_state->getUserInput(); $values = NestedArray::getValue($user_input, $pane_form['#parents']); if (!empty($values['payment_method'])) { - $selected_option = $values['payment_method']; + $default_option = $values['payment_method']; + if (substr($default_option, 0, 5) == 'new--') { + list(, , $payment_gateway_id) = explode('--', $default_option); + /** @var \Drupal\commerce_payment\Entity\PaymentGatewayInterface $default_payment_gateway */ + $default_payment_gateway = $this->paymentGatewayStorage->load($payment_gateway_id); + $default_payment_gateway_plugin = $default_payment_gateway->getPlugin(); + } + else { + /** @var \Drupal\commerce_payment\Entity\PaymentMethodInterface $default_payment_method */ + $default_payment_method = $this->paymentMethodStorage->load($default_option); + $default_payment_gateway_plugin = $default_payment_method->getPaymentGateway()->getPlugin(); + } } elseif ($order_payment_method) { - $selected_option = $this->order->payment_method->target_id; + $default_option = $order_payment_method->id(); + $default_payment_gateway_plugin = $order_payment_method->getPaymentGateway()->getPlugin(); + } + elseif ($order_payment_gateway) { + $default_payment_gateway_plugin = $order_payment_gateway->getPlugin(); + $default_payment_method_type = $default_payment_gateway_plugin->getDefaultPaymentMethodType(); + $default_option = 'new--' . $default_payment_method_type->getPluginId() . '--' . $order_payment_gateway->id(); } else { $default_payment_method_type = $payment_gateway_plugin->getDefaultPaymentMethodType(); - $selected_option = 'new_' . $default_payment_method_type->getPluginId(); + $default_option = 'new--' . $default_payment_method_type->getPluginId() . '--' . $payment_gateway->id(); + $default_payment_gateway_plugin = $payment_gateway_plugin; } // Prepare the form for ajax. @@ -214,25 +225,52 @@ protected function attachPaymentMethodForm(EntityPaymentGatewayInterface $paymen $pane_form['payment_method'] = [ '#type' => 'radios', '#title' => $this->t('Payment method'), - '#options' => $options, - '#default_value' => $selected_option, + '#options' => array_column($options, 'label', 'id'), + '#default_value' => $default_option, '#ajax' => [ 'callback' => [get_class($this), 'ajaxRefresh'], 'wrapper' => $pane_form['#wrapper_id'], ], ]; - if (substr($selected_option, 0, 4) == 'new_') { - $payment_method = $payment_method_storage->create([ - 'type' => substr($selected_option, 4), - 'payment_gateway' => $payment_gateway->id(), - 'uid' => $this->order->getCustomerId(), - ]); - $pane_form['add_payment_method'] = [ - '#type' => 'commerce_payment_gateway_form', - '#operation' => 'add-payment-method', - '#default_value' => $payment_method, - ]; + foreach ($options as $option_id => $option) { + $pane_form['payment_method'][$option_id]['#payment_method_plugin'] = $option['payment_method_plugin']; + $pane_form['payment_method'][$option_id]['#payment_gateway'] = $option['payment_gateway']; } + + // Extra elements for new payment methods. + if (substr($default_option, 0, 5) == 'new--' && !empty($options[$default_option])) { + if ($default_payment_gateway_plugin instanceof SupportsStoredPaymentMethodsInterface) { + $payment_method = $this->paymentMethodStorage->create([ + 'type' => $options[$default_option]['payment_method_plugin'], + 'payment_gateway' => $options[$default_option]['payment_gateway'], + 'uid' => $this->order->getCustomerId(), + ]); + $pane_form['add_payment_method'] = [ + '#type' => 'commerce_payment_gateway_form', + '#operation' => 'add-payment-method', + '#default_value' => $payment_method, + ]; + } + else { + $store = $this->order->getStore(); + $billing_profile = $this->order->getBillingProfile(); + if (!$billing_profile) { + $billing_profile = Profile::create([ + 'uid' => $this->order->getCustomerId(), + 'type' => 'customer', + ]); + } + + $pane_form['billing_information'] = [ + '#type' => 'commerce_profile_select', + '#default_value' => $billing_profile, + '#default_country' => $store->getAddress()->getCountryCode(), + '#available_countries' => $store->getBillingCountries(), + ]; + } + } + + return $pane_form; } /** @@ -250,15 +288,8 @@ public static function ajaxRefresh(array $form, FormStateInterface $form_state) public function validatePaneForm(array &$pane_form, FormStateInterface $form_state, array &$complete_form) { $values = $form_state->getValue($pane_form['#parents']); - /** @var \Drupal\commerce_payment\PaymentGatewayStorageInterface $payment_gateway_storage */ - $payment_gateway_storage = $this->entityTypeManager->getStorage('commerce_payment_gateway'); - /** @var \Drupal\commerce_payment\Entity\PaymentGatewayInterface $payment_gateway */ - $payment_gateway = $payment_gateway_storage->load($values['payment_gateway']); - - if ($payment_gateway->getPlugin() instanceof SupportsStoredPaymentMethodsInterface) { - if (!isset($values['payment_method'])) { - $form_state->setError($complete_form, $this->noPaymentGatewayErrorMessage()); - } + if (!isset($values['payment_method'])) { + $form_state->setError($complete_form, $this->noPaymentGatewayErrorMessage()); } } @@ -268,16 +299,14 @@ public function validatePaneForm(array &$pane_form, FormStateInterface $form_sta public function submitPaneForm(array &$pane_form, FormStateInterface $form_state, array &$complete_form) { $values = $form_state->getValue($pane_form['#parents']); - /** @var \Drupal\commerce_payment\PaymentGatewayStorageInterface $payment_gateway_storage */ - $payment_gateway_storage = $this->entityTypeManager->getStorage('commerce_payment_gateway'); + // Get the payment gateway from pane form. + $payment_gateway_id = $pane_form['payment_method'][$values['payment_method']]['#payment_gateway']; /** @var \Drupal\commerce_payment\Entity\PaymentGatewayInterface $payment_gateway */ - $payment_gateway = $payment_gateway_storage->load($values['payment_gateway']); + $payment_gateway = $this->paymentGatewayStorage->load($payment_gateway_id); if ($payment_gateway->getPlugin() instanceof SupportsStoredPaymentMethodsInterface) { if (is_numeric($values['payment_method'])) { - /** @var \Drupal\commerce_payment\PaymentMethodStorageInterface $payment_method_storage */ - $payment_method_storage = $this->entityTypeManager->getStorage('commerce_payment_method'); - $payment_method = $payment_method_storage->load($values['payment_method']); + $payment_method = $this->paymentMethodStorage->load($values['payment_method']); } else { $payment_method = $values['add_payment_method']; @@ -290,6 +319,7 @@ public function submitPaneForm(array &$pane_form, FormStateInterface $form_state } else { $this->order->payment_gateway = $payment_gateway; + $this->order->set('payment_method', NULL); $this->order->setBillingProfile($pane_form['billing_information']['#profile']); } } From ec921d3d439da979f97376d20c31aea72daafa9d Mon Sep 17 00:00:00 2001 From: tavi toporjinschi Date: Thu, 23 Feb 2017 14:24:04 +0000 Subject: [PATCH 2/3] Some cleanup. --- .../src/Plugin/Commerce/CheckoutPane/PaymentInformation.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php index 65392fb6a9..c6176bfa76 100644 --- a/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php +++ b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php @@ -35,14 +35,14 @@ class PaymentInformation extends CheckoutPaneBase implements ContainerFactoryPlu /** * The payment gateway storage. * - * @var \Drupal\commerce_payment\PaymentGatewayStorageInterface $paymentGatewayStorage + * @var \Drupal\commerce_payment\PaymentGatewayStorageInterface */ - protected $paymentGatewayStorage; + /** * The payment method storage. * - * @var \Drupal\commerce_payment\PaymentMethodStorageInterface $paymentMethodStorage + * @var \Drupal\commerce_payment\PaymentMethodStorageInterface */ protected $paymentMethodStorage; From c3ea1eb17479cf4c7861ab289d687410855e389f Mon Sep 17 00:00:00 2001 From: tavi toporjinschi Date: Thu, 23 Feb 2017 16:04:54 +0000 Subject: [PATCH 3/3] Fixes for Payment checkout tests. --- .../Plugin/Commerce/CheckoutPane/PaymentInformation.php | 8 +++++--- .../payment/tests/src/Functional/PaymentCheckoutTest.php | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php index c6176bfa76..87eeebe798 100644 --- a/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php +++ b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php @@ -204,9 +204,11 @@ public function buildPaneForm(array $pane_form, FormStateInterface $form_state, $default_option = 'new--' . $default_payment_method_type->getPluginId() . '--' . $order_payment_gateway->id(); } else { - $default_payment_method_type = $payment_gateway_plugin->getDefaultPaymentMethodType(); - $default_option = 'new--' . $default_payment_method_type->getPluginId() . '--' . $payment_gateway->id(); - $default_payment_gateway_plugin = $payment_gateway_plugin; + /** @var \Drupal\commerce_payment\Entity\PaymentGatewayInterface $default_payment_gateway */ + $first_option = end($options); + $default_payment_gateway = $this->paymentGatewayStorage->load($first_option['payment_gateway']); + $default_payment_gateway_plugin = $default_payment_gateway->getPlugin(); + $default_option = $first_option['id']; } // Prepare the form for ajax. diff --git a/modules/payment/tests/src/Functional/PaymentCheckoutTest.php b/modules/payment/tests/src/Functional/PaymentCheckoutTest.php index 2bf432a40b..a3167ed2ba 100644 --- a/modules/payment/tests/src/Functional/PaymentCheckoutTest.php +++ b/modules/payment/tests/src/Functional/PaymentCheckoutTest.php @@ -89,7 +89,7 @@ protected function setUp() { 'payment_gateway' => $gateway->id(), 'card_type' => 'visa', 'card_number' => '9999', - 'reusable' => FALSE, + 'reusable' => TRUE, ]); }