From d38f71fb8034371e9a8085a3f50d1006476c3b05 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Tue, 16 Jul 2024 18:55:43 +0200 Subject: [PATCH 1/6] =?UTF-8?q?=E2=9C=A8=20Introduce=20OrderMetaManager=20?= =?UTF-8?q?helper=20class?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Manages access to PayPal order meta data. --- .../src/Helper/OrderMetaManager.php | 90 +++++++++++++++++++ .../src/Helper/OrderMetaManagerInterface.php | 54 +++++++++++ 2 files changed, 144 insertions(+) create mode 100644 modules/ppcp-wc-gateway/src/Helper/OrderMetaManager.php create mode 100644 modules/ppcp-wc-gateway/src/Helper/OrderMetaManagerInterface.php diff --git a/modules/ppcp-wc-gateway/src/Helper/OrderMetaManager.php b/modules/ppcp-wc-gateway/src/Helper/OrderMetaManager.php new file mode 100644 index 000000000..149f81a34 --- /dev/null +++ b/modules/ppcp-wc-gateway/src/Helper/OrderMetaManager.php @@ -0,0 +1,90 @@ +wc_order = $wc_order; + $this->pp_order = $pp_order; + } + + /** + * {@inheritDoc} + */ + public function update_status() : OrderMetaManagerInterface { + $new_status = $this->pp_order->status()->name(); + $old_status = $this->get_status(); + + if ( $new_status !== $old_status ) { + $this->wc_order->update_meta_data( self::STATUS_META_KEY, $new_status ); + + /** + * Fires after the PayPal order status value was updated in the order meta table. + * + * @param WC_Abstract_Order $wc_order The WooCommerce order. + * @param PayPalOrder $pp_order The PayPal order. + * @param string $new_status The new status. + * @param string $old_status The old status. + */ + do_action( + 'woocommerce_paypal_payments_order_status_changed', + $this->wc_order, + $this->pp_order, + $new_status, + $old_status + ); + } + + return $this; + } + + /** + * {@inheritDoc} + */ + public function get_status() : string { + return (string) $this->wc_order->get_meta( self::STATUS_META_KEY ); + } + + /** + * {@inheritDoc} + */ + public function persist() : OrderMetaManagerInterface { + $this->wc_order->save_meta_data(); + + return $this; + } +} diff --git a/modules/ppcp-wc-gateway/src/Helper/OrderMetaManagerInterface.php b/modules/ppcp-wc-gateway/src/Helper/OrderMetaManagerInterface.php new file mode 100644 index 000000000..03bc7572d --- /dev/null +++ b/modules/ppcp-wc-gateway/src/Helper/OrderMetaManagerInterface.php @@ -0,0 +1,54 @@ + Date: Tue, 16 Jul 2024 19:01:46 +0200 Subject: [PATCH 2/6] =?UTF-8?q?=E2=9C=A8=20Use=20the=20new=20OrderMetaMana?= =?UTF-8?q?ger=20to=20store=20the=20status?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-button/src/Endpoint/CreateOrderEndpoint.php | 4 ++++ modules/ppcp-button/src/Helper/EarlyOrderHandler.php | 4 ++++ modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php | 3 +++ modules/ppcp-wc-gateway/src/Processor/OrderMetaTrait.php | 4 ++++ 4 files changed, 15 insertions(+) diff --git a/modules/ppcp-button/src/Endpoint/CreateOrderEndpoint.php b/modules/ppcp-button/src/Endpoint/CreateOrderEndpoint.php index 868f9bf6c..c91937077 100644 --- a/modules/ppcp-button/src/Endpoint/CreateOrderEndpoint.php +++ b/modules/ppcp-button/src/Endpoint/CreateOrderEndpoint.php @@ -35,6 +35,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; +use WooCommerce\PayPalCommerce\WcGateway\Helper\OrderMetaManager; /** * Class CreateOrderEndpoint @@ -327,6 +328,9 @@ public function handle_request(): bool { } if ( 'pay-now' === $data['context'] && is_a( $wc_order, \WC_Order::class ) ) { + $meta = new OrderMetaManager( $wc_order, $order ); + $meta->update_status(); + $wc_order->update_meta_data( PayPalGateway::ORDER_ID_META_KEY, $order->id() ); $wc_order->update_meta_data( PayPalGateway::INTENT_META_KEY, $order->intent() ); diff --git a/modules/ppcp-button/src/Helper/EarlyOrderHandler.php b/modules/ppcp-button/src/Helper/EarlyOrderHandler.php index 46b4ddf41..5061ccf3b 100644 --- a/modules/ppcp-button/src/Helper/EarlyOrderHandler.php +++ b/modules/ppcp-button/src/Helper/EarlyOrderHandler.php @@ -16,6 +16,7 @@ use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor; +use WooCommerce\PayPalCommerce\WcGateway\Helper\OrderMetaManager; /** * Class EarlyOrderHandler @@ -157,6 +158,9 @@ public function configure_session_and_order( int $order_id, Order $order ): Orde WC()->session->set( 'order_awaiting_payment', $order_id ); $wc_order = wc_get_order( $order_id ); + $meta = new OrderMetaManager( $wc_order, $order ); + $meta->update_status(); + $wc_order->update_meta_data( PayPalGateway::ORDER_ID_META_KEY, $order->id() ); $wc_order->update_meta_data( PayPalGateway::INTENT_META_KEY, $order->intent() ); diff --git a/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php b/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php index b2ce4cbc5..f098c8c60 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php @@ -35,6 +35,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsRenderer; use WooCommerce\PayPalCommerce\WcSubscriptions\FreeTrialHandlerTrait; use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper; +use WooCommerce\PayPalCommerce\WcGateway\Helper\OrderMetaManager; /** * Class CreditCardGateway @@ -472,6 +473,8 @@ public function process_payment( $order_id ) { $create_order = $this->capture_card_payment->create_order( $token->get_token(), $custom_id, $invoice_id, $wc_order ); $order = $this->order_endpoint->order( $create_order->id ); + $meta = new OrderMetaManager( $wc_order, $order ); + $meta->update_status(); $wc_order->update_meta_data( PayPalGateway::INTENT_META_KEY, $order->intent() ); if ( $order->intent() === 'AUTHORIZE' ) { diff --git a/modules/ppcp-wc-gateway/src/Processor/OrderMetaTrait.php b/modules/ppcp-wc-gateway/src/Processor/OrderMetaTrait.php index 62a80456a..ecf6802f0 100644 --- a/modules/ppcp-wc-gateway/src/Processor/OrderMetaTrait.php +++ b/modules/ppcp-wc-gateway/src/Processor/OrderMetaTrait.php @@ -14,6 +14,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Helper\OrderTransient; use WooCommerce\PayPalCommerce\Onboarding\Environment; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; +use WooCommerce\PayPalCommerce\WcGateway\Helper\OrderMetaManager; /** * Trait OrderMetaTrait. @@ -34,6 +35,9 @@ protected function add_paypal_meta( Environment $environment, OrderTransient $order_transient = null ): void { + $meta = new OrderMetaManager( $wc_order, $order ); + $meta->update_status(); + $wc_order->update_meta_data( PayPalGateway::ORDER_ID_META_KEY, $order->id() ); $wc_order->update_meta_data( PayPalGateway::INTENT_META_KEY, $order->intent() ); $wc_order->update_meta_data( From 2d8ec716e8b2b2d5865fd7f1e0dffcb8d427e4a3 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 17 Jul 2024 11:59:44 +0200 Subject: [PATCH 3/6] =?UTF-8?q?=E2=9C=85=20Add=20unit=20tests=20for=20the?= =?UTF-8?q?=20OrderMetaManager=20class?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WcGateway/Helper/OrderMetaManagerTest.php | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 tests/PHPUnit/WcGateway/Helper/OrderMetaManagerTest.php diff --git a/tests/PHPUnit/WcGateway/Helper/OrderMetaManagerTest.php b/tests/PHPUnit/WcGateway/Helper/OrderMetaManagerTest.php new file mode 100644 index 000000000..37f2df14d --- /dev/null +++ b/tests/PHPUnit/WcGateway/Helper/OrderMetaManagerTest.php @@ -0,0 +1,75 @@ +wc_order = Mockery::mock(WC_Abstract_Order::class); + $this->pp_order = Mockery::mock(PayPalOrder::class); + $this->manager = new OrderMetaManager($this->wc_order, $this->pp_order); + } + + /** + * Tests that the order status is updated correctly when it has changed. + * Verifies that the new PayPal order status is stored in the WooCommerce order meta data + * and triggers the appropriate WooCommerce action. + */ + public function testStatusOperations() { + $orderStatus = Mockery::mock(OrderStatus::class); + $orderStatus->shouldReceive('name')->andReturn('COMPLETED'); + $this->pp_order->shouldReceive('status')->andReturn($orderStatus); + + $this->wc_order->shouldReceive('get_meta')->with(OrderMetaManagerInterface::STATUS_META_KEY)->andReturn('PENDING')->once(); + $this->wc_order->shouldReceive('update_meta_data')->with(OrderMetaManagerInterface::STATUS_META_KEY, 'COMPLETED')->once(); + + Actions\expectDone('woocommerce_paypal_payments_order_status_changed')->once(); + + $result = $this->manager->update_status(); + $this->assertSame($this->manager, $result); + + $this->wc_order->shouldReceive('get_meta')->with(OrderMetaManagerInterface::STATUS_META_KEY)->andReturn('COMPLETED')->once(); + $this->assertEquals('COMPLETED', $this->manager->get_status()); + } + + /** + * Tests that the order status is not updated when it remains the same. + * Ensures that no redundant update is performed and no WooCommerce action is triggered. + */ + public function testUpdateStatusWithNoChange() { + $orderStatus = Mockery::mock(OrderStatus::class); + $orderStatus->shouldReceive('name')->andReturn('COMPLETED'); + $this->pp_order->shouldReceive('status')->andReturn($orderStatus); + + $this->wc_order->shouldReceive('get_meta')->with(OrderMetaManagerInterface::STATUS_META_KEY)->andReturn('COMPLETED')->once(); + $this->wc_order->shouldNotReceive('update_meta_data'); + + Actions\expectDone('woocommerce_paypal_payments_order_status_changed')->never(); + + $result = $this->manager->update_status(); + $this->assertSame($this->manager, $result); + } + + /** + * Tests that the meta data is correctly persisted. + * Verifies that the save_meta_data method is called on the WooCommerce order. + */ + public function testPersist() { + $this->wc_order->shouldReceive('save_meta_data')->once(); + $result = $this->manager->persist(); + $this->assertSame($this->manager, $result); + } +} From 90390cd40b0065c8c9a3b81af21918fe042b675c Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 17 Jul 2024 15:53:19 +0200 Subject: [PATCH 4/6] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Remove=20interface,=20?= =?UTF-8?q?rename=20ambigous=20method=20name?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/Endpoint/CreateOrderEndpoint.php | 2 +- .../src/Helper/EarlyOrderHandler.php | 2 +- .../src/Gateway/CreditCardGateway.php | 2 +- .../src/Helper/OrderMetaManager.php | 30 ++++++++--- .../src/Helper/OrderMetaManagerInterface.php | 54 ------------------- .../src/Processor/OrderMetaTrait.php | 2 +- 6 files changed, 26 insertions(+), 66 deletions(-) delete mode 100644 modules/ppcp-wc-gateway/src/Helper/OrderMetaManagerInterface.php diff --git a/modules/ppcp-button/src/Endpoint/CreateOrderEndpoint.php b/modules/ppcp-button/src/Endpoint/CreateOrderEndpoint.php index c91937077..b6d76f0fd 100644 --- a/modules/ppcp-button/src/Endpoint/CreateOrderEndpoint.php +++ b/modules/ppcp-button/src/Endpoint/CreateOrderEndpoint.php @@ -329,7 +329,7 @@ public function handle_request(): bool { if ( 'pay-now' === $data['context'] && is_a( $wc_order, \WC_Order::class ) ) { $meta = new OrderMetaManager( $wc_order, $order ); - $meta->update_status(); + $meta->set_status(); $wc_order->update_meta_data( PayPalGateway::ORDER_ID_META_KEY, $order->id() ); $wc_order->update_meta_data( PayPalGateway::INTENT_META_KEY, $order->intent() ); diff --git a/modules/ppcp-button/src/Helper/EarlyOrderHandler.php b/modules/ppcp-button/src/Helper/EarlyOrderHandler.php index 5061ccf3b..6ee410988 100644 --- a/modules/ppcp-button/src/Helper/EarlyOrderHandler.php +++ b/modules/ppcp-button/src/Helper/EarlyOrderHandler.php @@ -159,7 +159,7 @@ public function configure_session_and_order( int $order_id, Order $order ): Orde $wc_order = wc_get_order( $order_id ); $meta = new OrderMetaManager( $wc_order, $order ); - $meta->update_status(); + $meta->set_status(); $wc_order->update_meta_data( PayPalGateway::ORDER_ID_META_KEY, $order->id() ); $wc_order->update_meta_data( PayPalGateway::INTENT_META_KEY, $order->intent() ); diff --git a/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php b/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php index f098c8c60..d79250154 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php @@ -474,7 +474,7 @@ public function process_payment( $order_id ) { $order = $this->order_endpoint->order( $create_order->id ); $meta = new OrderMetaManager( $wc_order, $order ); - $meta->update_status(); + $meta->set_status(); $wc_order->update_meta_data( PayPalGateway::INTENT_META_KEY, $order->intent() ); if ( $order->intent() === 'AUTHORIZE' ) { diff --git a/modules/ppcp-wc-gateway/src/Helper/OrderMetaManager.php b/modules/ppcp-wc-gateway/src/Helper/OrderMetaManager.php index 149f81a34..f2f97c150 100644 --- a/modules/ppcp-wc-gateway/src/Helper/OrderMetaManager.php +++ b/modules/ppcp-wc-gateway/src/Helper/OrderMetaManager.php @@ -11,7 +11,6 @@ use WC_Abstract_Order; use WooCommerce\PayPalCommerce\ApiClient\Entity\Order as PayPalOrder; -use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; /** * Class OrderMetaManager @@ -19,7 +18,10 @@ * Manages metadata for PayPal orders, focusing on a single WooCommerce order * and its associated PayPal order. */ -class OrderMetaManager implements OrderMetaManagerInterface { +class OrderMetaManager { + + public const STATUS_META_KEY = '_ppcp_paypal_status'; + /** * The WooCommerce order. * @@ -35,7 +37,11 @@ class OrderMetaManager implements OrderMetaManagerInterface { private $pp_order; /** - * {@inheritDoc} + * Creates a new instance of the OrderMetaManager, connecting the provided objects - + * a WC_Order and PayPal Order (transaction). + * + * @param WC_Abstract_Order $wc_order The WooCommerce order to manage metadata for. + * @param PayPalOrder $pp_order The associated PayPal order. */ public function __construct( WC_Abstract_Order $wc_order, PayPalOrder $pp_order ) { $this->wc_order = $wc_order; @@ -43,9 +49,13 @@ public function __construct( WC_Abstract_Order $wc_order, PayPalOrder $pp_order } /** - * {@inheritDoc} + * Updates the status metadata for the WooCommerce order based on the PayPal order. + * + * To guarantee that the change is saved to the database, call `::persist()`. + * + * @return self */ - public function update_status() : OrderMetaManagerInterface { + public function set_status() : OrderMetaManager { $new_status = $this->pp_order->status()->name(); $old_status = $this->get_status(); @@ -73,16 +83,20 @@ public function update_status() : OrderMetaManagerInterface { } /** - * {@inheritDoc} + * Retrieves the PayPal order status from the WooCommerce order's metadata. + * + * @return string The PayPal order status. */ public function get_status() : string { return (string) $this->wc_order->get_meta( self::STATUS_META_KEY ); } /** - * {@inheritDoc} + * Persists any pending metadata changes to the database. + * + * @return self */ - public function persist() : OrderMetaManagerInterface { + public function persist() : OrderMetaManager { $this->wc_order->save_meta_data(); return $this; diff --git a/modules/ppcp-wc-gateway/src/Helper/OrderMetaManagerInterface.php b/modules/ppcp-wc-gateway/src/Helper/OrderMetaManagerInterface.php deleted file mode 100644 index 03bc7572d..000000000 --- a/modules/ppcp-wc-gateway/src/Helper/OrderMetaManagerInterface.php +++ /dev/null @@ -1,54 +0,0 @@ -update_status(); + $meta->set_status(); $wc_order->update_meta_data( PayPalGateway::ORDER_ID_META_KEY, $order->id() ); $wc_order->update_meta_data( PayPalGateway::INTENT_META_KEY, $order->intent() ); From 472e9f8694f9a5d0d15fbc362263867ee0193abc Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 17 Jul 2024 16:10:59 +0200 Subject: [PATCH 5/6] =?UTF-8?q?=E2=9C=85=20Adjust=20unit=20tests=20to=20ne?= =?UTF-8?q?w=20class?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WcGateway/Helper/OrderMetaManagerTest.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/PHPUnit/WcGateway/Helper/OrderMetaManagerTest.php b/tests/PHPUnit/WcGateway/Helper/OrderMetaManagerTest.php index 37f2df14d..0da73cf27 100644 --- a/tests/PHPUnit/WcGateway/Helper/OrderMetaManagerTest.php +++ b/tests/PHPUnit/WcGateway/Helper/OrderMetaManagerTest.php @@ -33,15 +33,15 @@ public function testStatusOperations() { $orderStatus->shouldReceive('name')->andReturn('COMPLETED'); $this->pp_order->shouldReceive('status')->andReturn($orderStatus); - $this->wc_order->shouldReceive('get_meta')->with(OrderMetaManagerInterface::STATUS_META_KEY)->andReturn('PENDING')->once(); - $this->wc_order->shouldReceive('update_meta_data')->with(OrderMetaManagerInterface::STATUS_META_KEY, 'COMPLETED')->once(); + $this->wc_order->shouldReceive('get_meta')->with(OrderMetaManager::STATUS_META_KEY)->andReturn('PENDING')->once(); + $this->wc_order->shouldReceive('update_meta_data')->with(OrderMetaManager::STATUS_META_KEY, 'COMPLETED')->once(); Actions\expectDone('woocommerce_paypal_payments_order_status_changed')->once(); - $result = $this->manager->update_status(); + $result = $this->manager->set_status(); $this->assertSame($this->manager, $result); - $this->wc_order->shouldReceive('get_meta')->with(OrderMetaManagerInterface::STATUS_META_KEY)->andReturn('COMPLETED')->once(); + $this->wc_order->shouldReceive('get_meta')->with(OrderMetaManager::STATUS_META_KEY)->andReturn('COMPLETED')->once(); $this->assertEquals('COMPLETED', $this->manager->get_status()); } @@ -54,12 +54,12 @@ public function testUpdateStatusWithNoChange() { $orderStatus->shouldReceive('name')->andReturn('COMPLETED'); $this->pp_order->shouldReceive('status')->andReturn($orderStatus); - $this->wc_order->shouldReceive('get_meta')->with(OrderMetaManagerInterface::STATUS_META_KEY)->andReturn('COMPLETED')->once(); + $this->wc_order->shouldReceive('get_meta')->with(OrderMetaManager::STATUS_META_KEY)->andReturn('COMPLETED')->once(); $this->wc_order->shouldNotReceive('update_meta_data'); Actions\expectDone('woocommerce_paypal_payments_order_status_changed')->never(); - $result = $this->manager->update_status(); + $result = $this->manager->set_status(); $this->assertSame($this->manager, $result); } From c75c25bd75d3839671710331b8835d236b681847 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 18 Jul 2024 11:05:59 +0200 Subject: [PATCH 6/6] =?UTF-8?q?=E2=9C=A8=20Introduce=20factory=20service?= =?UTF-8?q?=20for=20OrderMetaManager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-wc-gateway/services.php | 4 +++ .../src/Factory/OrderMetaManagerFactory.php | 33 +++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 modules/ppcp-wc-gateway/src/Factory/OrderMetaManagerFactory.php diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index f0d848216..4714f0774 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -70,6 +70,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsListener; use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsRenderer; +use WooCommerce\PayPalCommerce\WcGateway\Factory\OrderMetaManagerFactory; return array( 'wcgateway.paypal-gateway' => static function ( ContainerInterface $container ): PayPalGateway { @@ -1686,4 +1687,7 @@ static function( ContainerInterface $container ): DisplayManager { $container->get( 'woocommerce.logger.woocommerce' ) ); }, + 'wcgateway.factory.metadata' => static function ( ContainerInterface $container ): OrderMetaManagerFactory { + return new OrderMetaManagerFactory(); + }, ); diff --git a/modules/ppcp-wc-gateway/src/Factory/OrderMetaManagerFactory.php b/modules/ppcp-wc-gateway/src/Factory/OrderMetaManagerFactory.php new file mode 100644 index 000000000..d2ba62afc --- /dev/null +++ b/modules/ppcp-wc-gateway/src/Factory/OrderMetaManagerFactory.php @@ -0,0 +1,33 @@ +