diff --git a/modules/payment/tests/modules/commerce_payment_test/config/schema/commerce_payment_test.schema.yml b/modules/payment/tests/modules/commerce_payment_test/config/schema/commerce_payment_test.schema.yml index cee53b9c..cebbe2fb 100644 --- a/modules/payment/tests/modules/commerce_payment_test/config/schema/commerce_payment_test.schema.yml +++ b/modules/payment/tests/modules/commerce_payment_test/config/schema/commerce_payment_test.schema.yml @@ -4,3 +4,9 @@ commerce_payment.commerce_payment_gateway.plugin.test_onsite: api_key: type: string label: 'API key' +commerce_payment.commerce_payment_gateway.plugin.test_double_save_offsite_redirect: + type: commerce_payment_gateway_configuration + mapping: + redirect_method: + type: string + label: 'Redirect method' diff --git a/modules/payment/tests/modules/commerce_payment_test/src/Plugin/Commerce/PaymentGateway/TestDoubleSaveOffsiteRedirect.php b/modules/payment/tests/modules/commerce_payment_test/src/Plugin/Commerce/PaymentGateway/TestDoubleSaveOffsiteRedirect.php new file mode 100644 index 00000000..99189cc2 --- /dev/null +++ b/modules/payment/tests/modules/commerce_payment_test/src/Plugin/Commerce/PaymentGateway/TestDoubleSaveOffsiteRedirect.php @@ -0,0 +1,54 @@ +setData('test_double_save_offsite_redirect', ['test' => TRUE]); + + // Check if testing save _before_ a payment is added. + if ($state->get('test_double_save_offsite_redirect_when_to_save') === 'before') { + $order->save(); + } + parent::onReturn($order, $request); + + // Check if testing save _after_ a payment is added. + if ($state->get('test_double_save_offsite_redirect_when_to_save') === 'after') { + $order->save(); + } + } + +} diff --git a/modules/payment/tests/src/FunctionalJavascript/OffsiteRedirectOnReturnOrderSaveTest.php b/modules/payment/tests/src/FunctionalJavascript/OffsiteRedirectOnReturnOrderSaveTest.php new file mode 100644 index 00000000..d7f14572 --- /dev/null +++ b/modules/payment/tests/src/FunctionalJavascript/OffsiteRedirectOnReturnOrderSaveTest.php @@ -0,0 +1,130 @@ +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\PaymentGateway $gateway */ + $gateway = PaymentGateway::create([ + 'id' => 'offsite', + 'label' => 'Off-site', + 'plugin' => 'test_double_save_offsite_redirect', + 'configuration' => [ + // PayPal uses GET, follow its pattern for this test. + 'redirect_method' => 'get', + 'payment_method_types' => ['credit_card'], + ], + ]); + $gateway->save(); + } + + /** + * Tests checkout with an off-site gateway that saves an order in onReturn. + * + * @dataProvider providerForWhenToSaveOrder + */ + public function testCheckoutWithOffsiteRedirectPost($when_to_save) { + $state = $this->container->get('state'); + $state->set('test_double_save_offsite_redirect_when_to_save', $when_to_save); + + $this->drupalGet($this->product->toUrl()->toString()); + $this->submitForm([], 'Add to cart'); + $this->drupalGet('checkout/1'); + + $this->submitForm([ + 'payment_information[billing_information][address][0][address][given_name]' => 'Johnny', + 'payment_information[billing_information][address][0][address][family_name]' => 'Appleseed', + 'payment_information[billing_information][address][0][address][address_line1]' => '123 New York Drive', + 'payment_information[billing_information][address][0][address][locality]' => 'New York City', + 'payment_information[billing_information][address][0][address][administrative_area]' => 'NY', + 'payment_information[billing_information][address][0][address][postal_code]' => '10001', + ], 'Continue to review'); + $this->assertSession()->pageTextContains('Payment information'); + $this->assertSession()->pageTextContains('Example'); + $this->assertSession()->pageTextContains('Johnny Appleseed'); + $this->assertSession()->pageTextContains('123 New York Drive'); + $this->submitForm([], 'Pay and complete purchase'); + $this->assertSession()->pageTextContains('Your order number is 1. You can view your order on your account page when logged in.'); + + $order = Order::load(1); + $this->assertEquals('offsite', $order->get('payment_gateway')->target_id); + $this->assertFalse($order->isLocked()); + $this->assertTrue($order->isPaid()); + $this->assertTrue($order->getData('paid_event_dispatched')); + + // Test the data was set and preserved from our test gateway. + $this->assertEquals($order->getData('test_double_save_offsite_redirect'), [ + 'test' => TRUE, + ]); + } + + /** + * Data provider for ::testCheckoutWithOffsiteRedirectPost. + * + * @return array + * The test parameters. + */ + public function providerForWhenToSaveOrder() { + return [ + ['before'], + ['after'], + ]; + } + +} diff --git a/modules/payment/tests/src/Kernel/Entity/PaymentTest.php b/modules/payment/tests/src/Kernel/Entity/PaymentTest.php index c7475e2c..90aa89bd 100644 --- a/modules/payment/tests/src/Kernel/Entity/PaymentTest.php +++ b/modules/payment/tests/src/Kernel/Entity/PaymentTest.php @@ -197,19 +197,19 @@ class PaymentTest extends CommerceKernelTestBase { 'state' => 'completed', ]); $payment->save(); - $this->order = $this->reloadEntity($this->order); + $this->assertEquals(new Price('30', 'USD'), $this->order->getTotalPaid()); $this->assertEquals(new Price('0', 'USD'), $this->order->getBalance()); $payment->setRefundedAmount(new Price('15', 'USD')); $payment->setState('partially_refunded'); $payment->save(); - $this->order = $this->reloadEntity($this->order); + $this->assertEquals(new Price('15', 'USD'), $this->order->getTotalPaid()); $this->assertEquals(new Price('15', 'USD'), $this->order->getBalance()); $payment->delete(); - $this->order = $this->reloadEntity($this->order); + $this->assertEquals(new Price('0', 'USD'), $this->order->getTotalPaid()); $this->assertEquals(new Price('30', 'USD'), $this->order->getBalance()); } diff --git a/modules/payment/tests/src/Kernel/OrderPaidSubscriberTest.php b/modules/payment/tests/src/Kernel/OrderPaidSubscriberTest.php index b1d320af..10c6b1e0 100644 --- a/modules/payment/tests/src/Kernel/OrderPaidSubscriberTest.php +++ b/modules/payment/tests/src/Kernel/OrderPaidSubscriberTest.php @@ -106,7 +106,8 @@ class OrderPaidSubscriberTest extends CommerceKernelTestBase { ]); $payment->save(); - $this->order = $this->reloadEntity($this->order); + // We explicitly do not reload the order here to ensure that the object + // reference is in tact. The order object should match our expected values. $this->assertEquals('draft', $this->order->getState()->getId()); $this->assertEmpty($this->order->getOrderNumber()); $this->assertEmpty($this->order->getPlacedTime()); @@ -135,13 +136,13 @@ class OrderPaidSubscriberTest extends CommerceKernelTestBase { $payment = Payment::create([ 'type' => 'payment_default', 'payment_gateway' => $offsite_gateway->id(), - 'order_id' => $this->order->id(), 'amount' => $this->order->getTotalPrice(), 'state' => 'completed', ]); $payment->save(); - $this->order = $this->reloadEntity($this->order); + // We explicitly do not reload the order here to ensure that the object + // reference is in tact. The order object should match our expected values. $this->assertEquals('completed', $this->order->getState()->getId()); $this->assertFalse($this->order->isLocked()); $this->assertNotEmpty($this->order->getOrderNumber()); diff --git a/modules/payment_example/src/Plugin/Commerce/PaymentGateway/OffsiteRedirect.php b/modules/payment_example/src/Plugin/Commerce/PaymentGateway/OffsiteRedirect.php index 8cedb543..f699273c 100644 --- a/modules/payment_example/src/Plugin/Commerce/PaymentGateway/OffsiteRedirect.php +++ b/modules/payment_example/src/Plugin/Commerce/PaymentGateway/OffsiteRedirect.php @@ -74,7 +74,7 @@ class OffsiteRedirect extends OffsitePaymentGatewayBase { // @todo Add examples of request validation. $payment_storage = $this->entityTypeManager->getStorage('commerce_payment'); $payment = $payment_storage->create([ - 'state' => 'authorization', + 'state' => 'completed', 'amount' => $order->getBalance(), 'payment_gateway' => $this->entityId, 'order_id' => $order->id(),