Add passing data between PayPal order and WC order via transient helper.

Add order notes when ditch oor extra line occurs.
This commit is contained in:
Pedro Silva 2023-08-08 15:20:40 +01:00
parent a43b93ffff
commit 1a0fa269ef
No known key found for this signature in database
GPG key ID: E2EE20C0669D24B3
11 changed files with 245 additions and 7 deletions

View file

@ -17,6 +17,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Factory\PaymentPreferencesFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\PlanFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\ProductFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\ShippingOptionFactory;
use WooCommerce\PayPalCommerce\ApiClient\Helper\OrderTransient;
use WooCommerce\PayPalCommerce\ApiClient\Helper\PurchaseUnitSanitizer;
use WooCommerce\PayPalCommerce\Session\SessionHandler;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
@ -817,6 +818,11 @@ return array(
'api.order-helper' => static function( ContainerInterface $container ): OrderHelper {
return new OrderHelper();
},
'api.helper.order-transient' => static function( ContainerInterface $container ): OrderTransient {
$cache = new Cache( 'ppcp-paypal-bearer' );
$purchase_unit_sanitizer = $container->get( 'api.helper.purchase-unit-sanitizer' );
return new OrderTransient( $cache, $purchase_unit_sanitizer );
},
'api.helper.purchase-unit-sanitizer' => static function( ContainerInterface $container ): PurchaseUnitSanitizer {
$settings = $container->get( 'wcgateway.settings' );
assert( $settings instanceof Settings );

View file

@ -9,10 +9,13 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient;
use WC_Order;
use WooCommerce\PayPalCommerce\ApiClient\Helper\OrderTransient;
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
/**
* Class ApiModule
@ -40,6 +43,30 @@ class ApiModule implements ModuleInterface {
WC()->session->set( 'ppcp_fees', $fees );
}
);
add_action(
'woocommerce_paypal_payments_paypal_order_created',
function ( Order $order ) use ( $c ) {
$transient = $c->has( 'api.helper.order-transient' ) ? $c->get( 'api.helper.order-transient' ) : null;
if ( $transient instanceof OrderTransient ) {
$transient->on_order_created( $order );
}
},
10,
1
);
add_action(
'woocommerce_paypal_payments_woocommerce_order_created',
function ( WC_Order $wc_order, Order $order ) use ( $c ) {
$transient = $c->has( 'api.helper.order-transient' ) ? $c->get( 'api.helper.order-transient' ) : null;
if ( $transient instanceof OrderTransient ) {
$transient->on_woocommerce_order_created( $wc_order, $order );
}
},
10,
2
);
}
/**

View file

@ -281,6 +281,9 @@ class OrderEndpoint {
throw $error;
}
$order = $this->order_factory->from_paypal_response( $json );
do_action( 'woocommerce_paypal_payments_paypal_order_created', $order );
return $order;
}

View file

@ -166,8 +166,11 @@ class PayUponInvoiceOrderEndpoint {
throw new PayPalApiException( $json, $status_code );
}
$order = $this->order_factory->from_paypal_response( $json );
return $this->order_factory->from_paypal_response( $json );
do_action( 'woocommerce_paypal_payments_paypal_order_created', $order );
return $order;
}
/**

View file

@ -0,0 +1,160 @@
<?php
/**
* PayPal order transient helper.
*
* This class is used to pass transient data between the PayPal order and the WooCommerce order.
* These two orders can be created on different requests and at different times so this transient
* data must be persisted between requests.
*
* @package WooCommerce\PayPalCommerce\ApiClient\Helper
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Helper;
use WC_Order;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
/**
* Class OrderHelper
*/
class OrderTransient {
const CACHE_KEY = 'order_transient';
const CACHE_TIMEOUT = DAY_IN_SECONDS; // If necessary we can increase this.
/**
* The Cache.
*
* @var Cache
*/
private $cache;
/**
* The purchase unit sanitizer.
*
* @var PurchaseUnitSanitizer
*/
private $purchase_unit_sanitizer;
/**
* OrderTransient constructor.
*
* @param Cache $cache The Cache.
* @param PurchaseUnitSanitizer $purchase_unit_sanitizer The purchase unit sanitizer.
*/
public function __construct( Cache $cache, PurchaseUnitSanitizer $purchase_unit_sanitizer ) {
$this->cache = $cache;
$this->purchase_unit_sanitizer = $purchase_unit_sanitizer;
}
/**
* Processes the created PayPal order.
*
* @param Order $order The PayPal order.
* @return void
*/
public function on_order_created( Order $order ): void {
$message = $this->purchase_unit_sanitizer->get_last_message();
$this->add_order_note( $order, $message );
}
/**
* Processes the created WooCommerce order.
*
* @param WC_Order $wc_order The WooCommerce order.
* @param Order $order The PayPal order.
* @return void
*/
public function on_woocommerce_order_created( WC_Order $wc_order, Order $order ): void {
$cache_key = $this->cache_key( $order );
if ( ! $cache_key ) {
return;
}
$this->apply_order_notes( $order, $wc_order );
$this->cache->delete( $cache_key );
}
/**
* Adds an order note associated with a PayPal order.
* It can be added to a WooCommerce order associated with this PayPal order in the future.
*
* @param Order $order The PayPal order.
* @param string $message The message to be added to order notes.
* @return void
*/
private function add_order_note( Order $order, string $message ): void {
if ( ! $message ) {
return;
}
$cache_key = $this->cache_key( $order );
if ( ! $cache_key ) {
return;
}
$transient = $this->cache->get( $cache_key );
if ( ! is_array( $transient ) ) {
$transient = array();
}
if ( ! is_array( $transient['notes'] ) ) {
$transient['notes'] = array();
}
$transient['notes'][] = $message;
$this->cache->set( $cache_key, $transient, self::CACHE_TIMEOUT );
}
/**
* Adds an order note associated with a PayPal order.
* It can be added to a WooCommerce order associated with this PayPal order in the future.
*
* @param Order $order The PayPal order.
* @param WC_Order $wc_order The WooCommerce order.
* @return void
*/
private function apply_order_notes( Order $order, WC_Order $wc_order ): void {
$cache_key = $this->cache_key( $order );
if ( ! $cache_key ) {
return;
}
$transient = $this->cache->get( $cache_key );
if ( ! is_array( $transient ) ) {
return;
}
if ( ! is_array( $transient['notes'] ) ) {
return;
}
foreach ( $transient['notes'] as $note ) {
if ( ! is_string( $note ) ) {
continue;
}
$wc_order->add_order_note( $note );
}
}
/**
* Build cache key.
*
* @param Order $order The PayPal order.
* @return string|null
*/
private function cache_key( Order $order ): ?string {
if ( ! $order->id() ) {
return null;
}
return implode( '_', array( self::CACHE_KEY . $order->id() ) );
}
}

View file

@ -68,6 +68,12 @@ class PurchaseUnitSanitizer {
*/
private $extra_line_name;
/**
* The last message. To be added to order notes.
*
* @var string
*/
private $last_message = '';
/**
* PurchaseUnitSanitizer constructor.
@ -201,6 +207,8 @@ class PurchaseUnitSanitizer {
$line_name = $this->extra_line_name;
$roundings_money = new Money( $item_mismatch, $this->currency_code() );
$this->purchase_unit['items'][] = ( new Item( $line_name, $roundings_money, 1 ) )->to_array();
$this->last_message = __( 'Item amount mismatch. Extra line added.', 'woocommerce-paypal-payments' );
}
$item_mismatch = $this->calculate_item_mismatch();
@ -210,6 +218,7 @@ class PurchaseUnitSanitizer {
// Ditch items.
if ( $this->allow_ditch_items && isset( $this->purchase_unit['items'] ) ) {
unset( $this->purchase_unit['items'] );
$this->last_message = __( 'Item amount mismatch. Items ditched.', 'woocommerce-paypal-payments' );
}
}
}
@ -251,6 +260,8 @@ class PurchaseUnitSanitizer {
if ( isset( $this->purchase_unit['amount']['breakdown'] ) ) {
unset( $this->purchase_unit['amount']['breakdown'] );
}
$this->last_message = __( 'Breakdown mismatch. Items and breakdown ditched.', 'woocommerce-paypal-payments' );
}
}
@ -334,4 +345,14 @@ class PurchaseUnitSanitizer {
return $amount_str - $amount_total_str;
}
/**
* Returns the last sanitization message.
*
* @return string
*/
public function get_last_message(): string {
return $this->last_message;
}
}

View file

@ -156,7 +156,7 @@ return array(
$session_handler = $container->get( 'session.handler' );
$settings = $container->get( 'wcgateway.settings' );
$early_order_handler = $container->get( 'button.helper.early-order-handler' );
$registration_needed = $container->get( 'button.current-user-must-register' );
$registration_needed = $container->get( 'button.current-user-must-register' );
$logger = $container->get( 'woocommerce.logger.woocommerce' );
return new CreateOrderEndpoint(
$request_data,

View file

@ -25,6 +25,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\ShippingPreferenceFactory;
use WooCommerce\PayPalCommerce\ApiClient\Helper\OrderTransient;
use WooCommerce\PayPalCommerce\Button\Exception\ValidationException;
use WooCommerce\PayPalCommerce\Button\Validation\CheckoutFormValidator;
use WooCommerce\PayPalCommerce\Button\Helper\EarlyOrderHandler;
@ -330,6 +331,8 @@ class CreateOrderEndpoint implements EndpointInterface {
$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->save_meta_data();
do_action( 'woocommerce_paypal_payments_woocommerce_order_created', $wc_order, $order );
}
wp_send_json_success( $this->make_response( $order ) );

View file

@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\Button\Helper;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use WooCommerce\PayPalCommerce\ApiClient\Helper\OrderTransient;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\Session\SessionHandler;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
@ -163,6 +164,10 @@ class EarlyOrderHandler {
/**
* Patch Order so we have the \WC_Order id added.
*/
return $this->order_processor->patch_order( $wc_order, $order );
$order = $this->order_processor->patch_order( $wc_order, $order );
do_action( 'woocommerce_paypal_payments_woocommerce_order_created', $wc_order, $order );
return $order;
}
}

View file

@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\WcGateway\Processor;
use WC_Order;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
use WooCommerce\PayPalCommerce\ApiClient\Helper\OrderTransient;
use WooCommerce\PayPalCommerce\Onboarding\Environment;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
@ -22,14 +23,16 @@ trait OrderMetaTrait {
/**
* Adds common metadata to the order.
*
* @param WC_Order $wc_order The WC order to which metadata will be added.
* @param Order $order The PayPal order.
* @param Environment $environment The environment.
* @param WC_Order $wc_order The WC order to which metadata will be added.
* @param Order $order The PayPal order.
* @param Environment $environment The environment.
* @param OrderTransient|null $order_transient The order transient helper.
*/
protected function add_paypal_meta(
WC_Order $wc_order,
Order $order,
Environment $environment
Environment $environment,
OrderTransient $order_transient = null
): void {
$wc_order->update_meta_data( PayPalGateway::ORDER_ID_META_KEY, $order->id() );
$wc_order->update_meta_data( PayPalGateway::INTENT_META_KEY, $order->intent() );
@ -43,6 +46,8 @@ trait OrderMetaTrait {
}
$wc_order->save();
do_action( 'woocommerce_paypal_payments_woocommerce_order_created', $wc_order, $order );
}
/**

View file

@ -259,6 +259,11 @@
<code>DAY_IN_SECONDS</code>
</UndefinedConstant>
</file>
<file src="modules/ppcp-api-client/src/Helper/OrderTransient.php">
<UndefinedConstant occurrences="1">
<code>DAY_IN_SECONDS</code>
</UndefinedConstant>
</file>
<file src="modules/ppcp-button/services.php">
<PossiblyFalseArgument occurrences="1">
<code>realpath( __FILE__ )</code>