Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Save transaction status in order meta (526) #2427

Draft
wants to merge 7 commits into
base: trunk
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions modules/ppcp-button/src/Endpoint/CreateOrderEndpoint.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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->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() );

Expand Down
4 changes: 4 additions & 0 deletions modules/ppcp-button/src/Helper/EarlyOrderHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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->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() );

Expand Down
4 changes: 4 additions & 0 deletions modules/ppcp-wc-gateway/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsListener;
use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsRenderer;
use WooCommerce\PayPalCommerce\Applepay\ApplePayGateway;
use WooCommerce\PayPalCommerce\WcGateway\Factory\OrderMetaManagerFactory;

return array(
'wcgateway.paypal-gateway' => static function ( ContainerInterface $container ): PayPalGateway {
Expand Down Expand Up @@ -1719,4 +1720,7 @@ static function( ContainerInterface $container ): DisplayManager {
$container->get( 'woocommerce.logger.woocommerce' )
);
},
'wcgateway.factory.metadata' => static function ( ContainerInterface $container ): OrderMetaManagerFactory {
return new OrderMetaManagerFactory();
},
);
33 changes: 33 additions & 0 deletions modules/ppcp-wc-gateway/src/Factory/OrderMetaManagerFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php
/**
* The OrderMetaManager factory.
*
* @package WooCommerce\PayPalCommerce\WcGateway\Factory
*/

declare( strict_types = 1 );

namespace WooCommerce\PayPalCommerce\WcGateway\Factory;

use WooCommerce\PayPalCommerce\WcGateway\Helper\OrderMetaManager;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
use WC_Order;

/**
* Class OrderMetaManagerFactory
*/
class OrderMetaManagerFactory {

/**
* Returns a new OrderMetaManager instance based off a WooCommerce order and an API Order
* object.
*
* @param WC_Order $wc_order The WooCommerce order.
* @param Order $order The order object.
*
* @return OrderMetaManager
*/
public function from_api_order( WC_Order $wc_order, Order $order ) : OrderMetaManager {
return new OrderMetaManager( $wc_order, $order );
}
}
3 changes: 3 additions & 0 deletions modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -502,6 +503,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->set_status();
$wc_order->update_meta_data( PayPalGateway::INTENT_META_KEY, $order->intent() );

if ( $order->intent() === 'AUTHORIZE' ) {
Expand Down
104 changes: 104 additions & 0 deletions modules/ppcp-wc-gateway/src/Helper/OrderMetaManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?php
/**
* Access order meta-data.
*
* @package WooCommerce\PayPalCommerce\WcGateway\Helper
*/

declare( strict_types = 1 );

namespace WooCommerce\PayPalCommerce\WcGateway\Helper;

use WC_Abstract_Order;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order as PayPalOrder;

/**
* Class OrderMetaManager
*
* Manages metadata for PayPal orders, focusing on a single WooCommerce order
* and its associated PayPal order.
*/
class OrderMetaManager {

public const STATUS_META_KEY = '_ppcp_paypal_status';

/**
* The WooCommerce order.
*
* @var WC_Abstract_Order
*/
private $wc_order;

/**
* The PayPal order.
*
* @var PayPalOrder
*/
private $pp_order;

/**
* 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;
$this->pp_order = $pp_order;
}

/**
* 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 set_status() : OrderMetaManager {
$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;
}

/**
* 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 );
}

/**
* Persists any pending metadata changes to the database.
*
* @return self
*/
public function persist() : OrderMetaManager {
$this->wc_order->save_meta_data();

return $this;
}
}
4 changes: 4 additions & 0 deletions modules/ppcp-wc-gateway/src/Processor/OrderMetaTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -34,6 +35,9 @@ protected function add_paypal_meta(
Environment $environment,
OrderTransient $order_transient = null
): void {
$meta = new OrderMetaManager( $wc_order, $order );
$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() );
$wc_order->update_meta_data(
Expand Down
75 changes: 75 additions & 0 deletions tests/PHPUnit/WcGateway/Helper/OrderMetaManagerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php
declare( strict_types = 1 );

namespace WooCommerce\PayPalCommerce\WcGateway\Helper;

use Mockery;
use WC_Abstract_Order;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order as PayPalOrder;
use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
use WooCommerce\PayPalCommerce\TestCase;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
use Brain\Monkey\Actions;

class OrderMetaManagerTest extends TestCase {
private $wc_order;
private $pp_order;
private $manager;

public function setUp() : void {
parent::setUp();
$this->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(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->set_status();
$this->assertSame($this->manager, $result);

$this->wc_order->shouldReceive('get_meta')->with(OrderMetaManager::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(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->set_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);
}
}
Loading