From 8415c3f0878a436692ba2287c013934693d99867 Mon Sep 17 00:00:00 2001 From: Paul Dale Smith <paul.smith@freelygive.org.uk> Date: Tue, 6 Jun 2023 13:57:53 +0100 Subject: [PATCH 1/2] Rerolled patch. #2998065 @MrDaleSmith --- .../CheckoutPane/PaymentInformation.php | 30 ++- .../Commerce/CheckoutPane/PaymentProcess.php | 24 +- .../src/Functional/PaymentCheckoutTest.php | 220 ++++++++++++++++++ 3 files changed, 269 insertions(+), 5 deletions(-) create mode 100644 modules/payment/tests/src/Functional/PaymentCheckoutTest.php diff --git a/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php index 35943946c..d6ecd8266 100644 --- a/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php +++ b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php @@ -105,7 +105,7 @@ class PaymentInformation extends CheckoutPaneBase { */ public function buildPaneSummary() { $billing_profile = $this->order->getBillingProfile(); - if ($this->order->isPaid() || $this->order->getTotalPrice()->isZero()) { + if (!$this->needsPayment()) { if ($billing_profile) { // Only the billing information was collected. $view_builder = $this->entityTypeManager->getViewBuilder('profile'); @@ -147,7 +147,7 @@ class PaymentInformation extends CheckoutPaneBase { * {@inheritdoc} */ public function buildPaneForm(array $pane_form, FormStateInterface $form_state, array &$complete_form) { - if (!$this->order->getTotalPrice() || $this->order->isPaid() || $this->order->getTotalPrice()->isZero()) { + if (!$this->order->getTotalPrice() || !$this->needsPayment() ) { // No payment is needed if the order is free or has already been paid. // In that case, collect just the billing information. $pane_form['#title'] = $this->t('Billing information'); @@ -394,7 +394,7 @@ class PaymentInformation extends CheckoutPaneBase { * {@inheritdoc} */ public function validatePaneForm(array &$pane_form, FormStateInterface $form_state, array &$complete_form) { - if (!$this->order->getTotalPrice() || $this->order->isPaid() || $this->order->getTotalPrice()->isZero()) { + if (!$this->order->getTotalPrice() || !$this->needsPayment()) { return; } @@ -426,7 +426,7 @@ class PaymentInformation extends CheckoutPaneBase { // The billing profile is provided either because the order is free, // or the selected gateway does not support stored payment methods. // If it's the former, stop here. - if ($this->order->isPaid() || $this->order->getTotalPrice()->isZero()) { + if (!$this->needsPayment()) { return; } } @@ -531,4 +531,26 @@ class PaymentInformation extends CheckoutPaneBase { return $message; } + /** + * Checks whether or not the order still needs payment. + * + * The order doesn't needs payment if: + * - The order is already paid; + * - The order has no total price; + * - The order has a total price of zero. + * + * @return bool + * TRUE if the order still need to be paid, FALSE otherwise. + */ + protected function needsPayment() { + if ($this->order->isPaid()) { + return FALSE; + } + if (!$this->order->getTotalPrice() || $this->order->getTotalPrice()->isZero()) { + // No total price or a total price of zero. + return FALSE; + } + return TRUE; + } + } diff --git a/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentProcess.php b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentProcess.php index d329c4059..3e40276bd 100644 --- a/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentProcess.php +++ b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentProcess.php @@ -142,7 +142,7 @@ class PaymentProcess extends CheckoutPaneBase { * {@inheritdoc} */ public function isVisible() { - if ($this->order->isPaid() || !$this->order->getTotalPrice() || $this->order->getTotalPrice()->isZero()) { + if (!$this->needsPayment()) { // No payment is needed if the order is free or has already been paid. return FALSE; } @@ -293,6 +293,28 @@ class PaymentProcess extends CheckoutPaneBase { return $step_id; } + /** + * Checks whether or not the order still needs payment. + * + * The order doesn't needs payment if: + * - The order is already paid; + * - The order has no total price; + * - The order has a total price of zero. + * + * @return bool + * TRUE if the order still need to be paid, FALSE otherwise. + */ + protected function needsPayment() { + if ($this->order->isPaid()) { + return FALSE; + } + if (!$this->order->getTotalPrice() || $this->order->getTotalPrice()->isZero()) { + // No total price or a total price of zero. + return FALSE; + } + return TRUE; + } + /** * Creates the payment to be processed. * diff --git a/modules/payment/tests/src/Functional/PaymentCheckoutTest.php b/modules/payment/tests/src/Functional/PaymentCheckoutTest.php new file mode 100644 index 000000000..2257ce7b7 --- /dev/null +++ b/modules/payment/tests/src/Functional/PaymentCheckoutTest.php @@ -0,0 +1,220 @@ +<?php + +namespace Drupal\Tests\commerce_payment\Functional; + +use Drupal\commerce_order\Entity\OrderItem; +use Drupal\commerce_payment\Entity\PaymentGateway; +use Drupal\Tests\commerce\Functional\CommerceBrowserTestBase; + +/** + * Tests the integration between payments and checkout. + * + * @group commerce + */ +class PaymentCheckoutTest extends CommerceBrowserTestBase { + + /** + * The current user. + * + * @var \Drupal\Core\Session\AccountInterface + */ + protected $account; + + /** + * The product. + * + * @var \Drupal\commerce_product\Entity\ProductInterface + */ + protected $product; + + /** + * A non-reusable order payment method. + * + * @var \Drupal\commerce_payment\Entity\PaymentMethodInterface + */ + protected $orderPaymentMethod; + + /** + * The default profile's address. + * + * @var array + */ + protected $defaultAddress = [ + 'country_code' => 'US', + 'administrative_area' => 'SC', + 'locality' => 'Greenville', + 'postal_code' => '29616', + 'address_line1' => '9 Drupal Ave', + 'given_name' => 'Bryan', + 'family_name' => 'Centarro', + ]; + + /** + * Modules to enable. + * + * @var array + */ + public static $modules = [ + 'commerce_product', + 'commerce_cart', + 'commerce_checkout', + 'commerce_payment', + 'commerce_payment_example', + ]; + + /** + * {@inheritdoc} + */ + protected function getAdministratorPermissions() { + return array_merge([ + 'administer profile', + ], parent::getAdministratorPermissions()); + } + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + + $this->store->set('billing_countries', ['FR', 'US']); + $this->store->save(); + + $variation = $this->createEntity('commerce_product_variation', [ + 'type' => 'default', + 'sku' => strtolower($this->randomMachineName()), + 'price' => [ + 'number' => '39.99', + 'currency_code' => 'USD', + ], + ]); + + /** @var \Drupal\commerce_product\Entity\ProductInterface $product */ + $this->product = $this->createEntity('commerce_product', [ + 'type' => 'default', + 'title' => 'My product', + 'variations' => [$variation], + 'stores' => [$this->store], + ]); + + /** @var \Drupal\commerce_payment\Entity\PaymentGatewayInterface $skipped_gateway */ + $skipped_gateway = PaymentGateway::create([ + 'id' => 'onsite_skipped', + 'label' => 'On-site Skipped', + 'plugin' => 'example_onsite', + 'configuration' => [ + 'api_key' => '2342fewfsfs', + 'payment_method_types' => ['credit_card'], + ], + 'conditions' => [ + [ + 'plugin' => 'order_total_price', + 'configuration' => [ + 'operator' => '<', + 'amount' => [ + 'number' => '1.00', + 'currency_code' => 'USD', + ], + ], + ], + ], + ]); + $skipped_gateway->save(); + + /** @var \Drupal\commerce_payment\Entity\PaymentGatewayInterface $payment_gateway */ + $payment_gateway = PaymentGateway::create([ + 'id' => 'onsite', + 'label' => 'On-site', + 'plugin' => 'example_onsite', + 'configuration' => [ + 'api_key' => '2342fewfsfs', + 'payment_method_types' => ['credit_card'], + ], + ]); + $payment_gateway->save(); + + /** @var \Drupal\commerce_payment\Entity\PaymentGatewayInterface $payment_gateway */ + $payment_gateway = PaymentGateway::create([ + 'id' => 'offsite', + 'label' => 'Off-site', + 'plugin' => 'example_offsite_redirect', + 'configuration' => [ + 'redirect_method' => 'post', + 'payment_method_types' => ['credit_card'], + ], + ]); + $payment_gateway->save(); + + /** @var \Drupal\commerce_payment\Entity\PaymentGatewayInterface $payment_gateway */ + $payment_gateway = PaymentGateway::create([ + 'id' => 'manual', + 'label' => 'Manual', + 'plugin' => 'manual', + 'configuration' => [ + 'display_label' => 'Cash on delivery', + 'instructions' => [ + 'value' => 'Sample payment instructions.', + 'format' => 'plain_text', + ], + ], + ]); + $payment_gateway->save(); + + $default_profile = $this->createEntity('profile', [ + 'type' => 'customer', + 'uid' => $this->adminUser->id(), + 'address' => $this->defaultAddress, + ]); + $profile = $this->createEntity('profile', [ + 'type' => 'customer', + 'uid' => 0, + 'address' => [ + 'country_code' => 'US', + 'postal_code' => '53177', + 'locality' => 'Milwaukee', + 'address_line1' => 'Pabst Blue Ribbon Dr', + 'administrative_area' => 'WI', + 'given_name' => 'Frederick', + 'family_name' => 'Pabst', + ], + ]); + $payment_method = $this->createEntity('commerce_payment_method', [ + 'uid' => $this->adminUser->id(), + 'type' => 'credit_card', + 'payment_gateway' => 'onsite', + 'card_type' => 'visa', + 'card_number' => '1111', + 'billing_profile' => $profile, + 'reusable' => TRUE, + 'expires' => strtotime('2028/03/24'), + ]); + $payment_method->setBillingProfile($profile); + $payment_method->save(); + + $this->orderPaymentMethod = $this->createEntity('commerce_payment_method', [ + 'type' => 'credit_card', + 'payment_gateway' => 'onsite', + 'card_type' => 'visa', + 'card_number' => '9999', + 'reusable' => FALSE, + ]); + } + + /** + * Tests checkout after removing all order items from an order. + * + * We expect that the customer gets redirected to the cart when the order + * no longer has order items. + */ + public function testCheckoutAfterRemovingOrderItem() { + $this->drupalGet($this->product->toUrl()->toString()); + $this->submitForm([], 'Add to cart'); + $this->drupalGet('checkout/1'); + + // Now remove the order item and go to checkout again. + OrderItem::load(1)->delete(); + $this->drupalGet('checkout/1'); + $this->submitForm([], 'Continue to review'); + } + +} -- GitLab From 21e2bd473f07e72a9a3c4ba6896d62ca31ac2250 Mon Sep 17 00:00:00 2001 From: arnemichiels <arne.michiels@makeitfly.group> Date: Wed, 14 May 2025 12:08:39 +0200 Subject: [PATCH 2/2] Small merge fix --- .../src/Plugin/Commerce/CheckoutPane/PaymentInformation.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php index 22e9f374e..231e4bcd1 100644 --- a/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php +++ b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php @@ -134,6 +134,9 @@ class PaymentInformation extends PaymentCheckoutPaneBase { */ public function buildPaneSummary() { $billing_profile = $this->order->getBillingProfile(); + /** @var \Drupal\commerce_payment\Entity\PaymentGatewayInterface $payment_gateway */ + $payment_gateway = $this->order->get('payment_gateway')->entity; + $summary = []; if (!$this->needsPayment()) { if ($billing_profile) { // Only the billing information was collected. -- GitLab