From c3f7e48d465a21e890f3a509b8fbbae71f3c943e Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Tue, 15 Aug 2023 11:49:54 +0100 Subject: [PATCH 01/17] Add gateway validation for woocommerce_order_status_changed filter. --- modules/ppcp-subscription/src/RenewalHandler.php | 2 +- .../ppcp-wc-gateway/src/Gateway/GatewayRepository.php | 10 ++++++++++ modules/ppcp-wc-gateway/src/WCGatewayModule.php | 9 +++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-subscription/src/RenewalHandler.php b/modules/ppcp-subscription/src/RenewalHandler.php index cd2385499..1364fa365 100644 --- a/modules/ppcp-subscription/src/RenewalHandler.php +++ b/modules/ppcp-subscription/src/RenewalHandler.php @@ -226,7 +226,7 @@ class RenewalHandler { * @param \WC_Customer $customer The customer. * @param \WC_Order $wc_order The current WooCommerce order we want to process. * - * @return PaymentToken|null + * @return PaymentToken|null|false */ private function get_token_for_customer( \WC_Customer $customer, \WC_Order $wc_order ) { /** diff --git a/modules/ppcp-wc-gateway/src/Gateway/GatewayRepository.php b/modules/ppcp-wc-gateway/src/Gateway/GatewayRepository.php index 250b1143f..6362fa260 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/GatewayRepository.php +++ b/modules/ppcp-wc-gateway/src/Gateway/GatewayRepository.php @@ -44,4 +44,14 @@ class GatewayRepository { } ); } + + /** + * Indicates if a given gateway ID is registered. + * + * @param string $gateway_id The gateway ID. + * @return bool + */ + public function exists( string $gateway_id ): bool { + return in_array( $gateway_id, $this->ppcp_gateway_ids, true ); + } } diff --git a/modules/ppcp-wc-gateway/src/WCGatewayModule.php b/modules/ppcp-wc-gateway/src/WCGatewayModule.php index 28c610b7e..0e89cbfd4 100644 --- a/modules/ppcp-wc-gateway/src/WCGatewayModule.php +++ b/modules/ppcp-wc-gateway/src/WCGatewayModule.php @@ -33,6 +33,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Checkout\DisableGateways; use WooCommerce\PayPalCommerce\WcGateway\Endpoint\ReturnUrlEndpoint; use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException; use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway; +use WooCommerce\PayPalCommerce\WcGateway\Gateway\GatewayRepository; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; use WooCommerce\PayPalCommerce\WcGateway\Helper\DCCProductStatus; use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceProductStatus; @@ -355,6 +356,14 @@ class WCGatewayModule implements ModuleInterface { return; } + $gateway_repository = $c->get( 'wcgateway.gateway-repository' ); + assert( $gateway_repository instanceof GatewayRepository ); + + // Only allow to proceed if the payment method is one of our Gateways. + if ( ! $gateway_repository->exists( $wc_order->get_payment_method() ) ) { + return; + } + $intent = strtoupper( (string) $wc_order->get_meta( PayPalGateway::INTENT_META_KEY ) ); $captured = wc_string_to_bool( $wc_order->get_meta( AuthorizedPaymentsProcessor::CAPTURED_META_KEY ) ); if ( $intent !== 'AUTHORIZE' || $captured ) { From 6692c4f2db87e80ba71c2b40c25bbc18e74e23b9 Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Tue, 15 Aug 2023 11:52:47 +0100 Subject: [PATCH 02/17] Removed satackey/action-docker-layer-caching from e2e testing --- .github/workflows/e2e.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 3731a1de4..0bf80c13b 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -12,9 +12,6 @@ jobs: name: PHP ${{ matrix.php-versions }} WC ${{ matrix.wc-versions }} steps: - - uses: satackey/action-docker-layer-caching@v0.0.11 - continue-on-error: true - - uses: ddev/github-action-setup-ddev@v1 with: autostart: false From b8eed0f324d77116c304d1b1c4085aafb5f17254 Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Wed, 16 Aug 2023 11:48:56 +0100 Subject: [PATCH 03/17] Add filters woocommerce_paypal_payments_cart_line_item_name and woocommerce_paypal_payments_order_line_item_name. --- modules/ppcp-api-client/src/ApiModule.php | 15 +++++ .../src/Endpoint/WebhookEndpoint.php | 5 ++ .../src/Processor/OrderProcessor.php | 57 ++++++++++++++++++- .../src/Handler/PaymentCaptureRefunded.php | 5 ++ .../Processor/OrderProcessorTest.php | 4 +- 5 files changed, 84 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-api-client/src/ApiModule.php b/modules/ppcp-api-client/src/ApiModule.php index 95a0efd14..35ae4cc7c 100644 --- a/modules/ppcp-api-client/src/ApiModule.php +++ b/modules/ppcp-api-client/src/ApiModule.php @@ -40,6 +40,21 @@ class ApiModule implements ModuleInterface { WC()->session->set( 'ppcp_fees', $fees ); } ); + + add_filter( + 'ppcp_create_order_request_body_data', + function( array $data ) use ( $c ) { + + foreach ( $data['purchase_units'] as $purchase_unit_index => $purchase_unit ) { + foreach ( $purchase_unit['items'] as $item_index => $item ) { + $data['purchase_units'][ $purchase_unit_index ]['items'][ $item_index ]['name'] = + apply_filters( 'woocommerce_paypal_payments_cart_line_item_name', $item['name'], $item['cart_item_key'] ); + } + } + + return $data; + } + ); } /** diff --git a/modules/ppcp-api-client/src/Endpoint/WebhookEndpoint.php b/modules/ppcp-api-client/src/Endpoint/WebhookEndpoint.php index d8cc06dca..68f433a41 100644 --- a/modules/ppcp-api-client/src/Endpoint/WebhookEndpoint.php +++ b/modules/ppcp-api-client/src/Endpoint/WebhookEndpoint.php @@ -202,6 +202,11 @@ class WebhookEndpoint { $status_code = (int) wp_remote_retrieve_response_code( $response ); if ( 204 !== $status_code ) { $json = null; + /** + * Use in array as consistency check. + * + * @psalm-suppress RedundantConditionGivenDocblockType + */ if ( is_array( $response ) ) { $json = json_decode( $response['body'] ); } diff --git a/modules/ppcp-wc-gateway/src/Processor/OrderProcessor.php b/modules/ppcp-wc-gateway/src/Processor/OrderProcessor.php index 443fd1cbd..fcb74f4ef 100644 --- a/modules/ppcp-wc-gateway/src/Processor/OrderProcessor.php +++ b/modules/ppcp-wc-gateway/src/Processor/OrderProcessor.php @@ -116,6 +116,13 @@ class OrderProcessor { */ private $order_helper; + /** + * Array to store temporary order data changes to restore after processing. + * + * @var array + */ + private $restore_order_data = array(); + /** * OrderProcessor constructor. * @@ -292,8 +299,12 @@ class OrderProcessor { * @return Order */ public function patch_order( \WC_Order $wc_order, Order $order ): Order { + $this->apply_outbound_order_filters( $wc_order ); $updated_order = $this->order_factory->from_wc_order( $wc_order, $order ); - $order = $this->order_endpoint->patch_order_with( $order, $updated_order ); + $this->restore_order_from_filters( $wc_order ); + + $order = $this->order_endpoint->patch_order_with( $order, $updated_order ); + return $order; } @@ -323,4 +334,48 @@ class OrderProcessor { true ); } + + /** + * Applies filters to the WC_Order, so they are reflected only on PayPal Order. + * + * @param WC_Order $wc_order The WoocOmmerce Order. + * @return void + */ + private function apply_outbound_order_filters( WC_Order $wc_order ): void { + $items = $wc_order->get_items(); + + $this->restore_order_data['names'] = array(); + + foreach ( $items as $item ) { + if ( ! $item instanceof \WC_Order_Item ) { + continue; + } + + $original_name = $item->get_name(); + $new_name = apply_filters( 'woocommerce_paypal_payments_order_line_item_name', $original_name, $item->get_id(), $wc_order->get_id() ); + + if ( $new_name !== $original_name ) { + $this->restore_order_data['names'][ $item->get_id() ] = $original_name; + $item->set_name( $new_name ); + } + } + } + + /** + * Restores the WC_Order to it's state before filters. + * + * @param WC_Order $wc_order The WooCommerce Order. + * @return void + */ + private function restore_order_from_filters( WC_Order $wc_order ): void { + if ( is_array( $this->restore_order_data['names'] ) ) { + foreach ( $this->restore_order_data['names'] as $wc_item_id => $original_name ) { + $wc_item = $wc_order->get_item( $wc_item_id, false ); + + if ( $wc_item ) { + $wc_item->set_name( $original_name ); + } + } + } + } } diff --git a/modules/ppcp-webhooks/src/Handler/PaymentCaptureRefunded.php b/modules/ppcp-webhooks/src/Handler/PaymentCaptureRefunded.php index ede513aa2..366fe8d48 100644 --- a/modules/ppcp-webhooks/src/Handler/PaymentCaptureRefunded.php +++ b/modules/ppcp-webhooks/src/Handler/PaymentCaptureRefunded.php @@ -101,6 +101,11 @@ class PaymentCaptureRefunded implements RequestHandler { ) ); if ( is_wp_error( $refund ) ) { + /** + * Helps to asset type. + * + * @psalm-suppress RedundantCondition + */ assert( $refund instanceof WP_Error ); $message = sprintf( 'Order %1$s could not be refunded. %2$s', diff --git a/tests/PHPUnit/WcGateway/Processor/OrderProcessorTest.php b/tests/PHPUnit/WcGateway/Processor/OrderProcessorTest.php index 32a2366ca..97ceca9b5 100644 --- a/tests/PHPUnit/WcGateway/Processor/OrderProcessorTest.php +++ b/tests/PHPUnit/WcGateway/Processor/OrderProcessorTest.php @@ -57,6 +57,7 @@ class OrderProcessorTest extends TestCase ->andReturn($payments); $wcOrder = Mockery::mock(\WC_Order::class); + $wcOrder->expects('get_items')->andReturn([]); $wcOrder->expects('update_meta_data') ->with(PayPalGateway::ORDER_PAYMENT_MODE_META_KEY, 'live'); $wcOrder->shouldReceive('get_id')->andReturn(1); @@ -193,7 +194,8 @@ class OrderProcessorTest extends TestCase ->andReturn($payments); $wcOrder = Mockery::mock(\WC_Order::class); - $orderStatus = Mockery::mock(OrderStatus::class); + $wcOrder->expects('get_items')->andReturn([]); + $orderStatus = Mockery::mock(OrderStatus::class); $orderStatus ->shouldReceive('is') ->with(OrderStatus::APPROVED) From 6e21d965d0ff888bc14fa436932f7292cfaab870 Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Wed, 16 Aug 2023 11:54:44 +0100 Subject: [PATCH 04/17] Removed satackey/action-docker-layer-caching from e2e testing --- .github/workflows/e2e.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 3731a1de4..0bf80c13b 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -12,9 +12,6 @@ jobs: name: PHP ${{ matrix.php-versions }} WC ${{ matrix.wc-versions }} steps: - - uses: satackey/action-docker-layer-caching@v0.0.11 - continue-on-error: true - - uses: ddev/github-action-setup-ddev@v1 with: autostart: false From 23c524538109603643a501415e858f409d574242 Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Wed, 16 Aug 2023 14:07:01 +0100 Subject: [PATCH 05/17] Fix potential notice. --- modules/ppcp-wc-gateway/src/Processor/OrderProcessor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-wc-gateway/src/Processor/OrderProcessor.php b/modules/ppcp-wc-gateway/src/Processor/OrderProcessor.php index fcb74f4ef..1fb10ed8c 100644 --- a/modules/ppcp-wc-gateway/src/Processor/OrderProcessor.php +++ b/modules/ppcp-wc-gateway/src/Processor/OrderProcessor.php @@ -368,7 +368,7 @@ class OrderProcessor { * @return void */ private function restore_order_from_filters( WC_Order $wc_order ): void { - if ( is_array( $this->restore_order_data['names'] ) ) { + if ( is_array( $this->restore_order_data['names'] ?? null ) ) { foreach ( $this->restore_order_data['names'] as $wc_item_id => $original_name ) { $wc_item = $wc_order->get_item( $wc_item_id, false ); From e16a8c4ff300b62053b1b839881cfa58d71ccd79 Mon Sep 17 00:00:00 2001 From: Alex P Date: Thu, 24 Aug 2023 18:14:23 +0300 Subject: [PATCH 06/17] Do not use transient expiration longer than month to support memcached --- .../src/Endpoint/BillingAgreementsEndpoint.php | 2 +- modules/ppcp-compat/src/PPEC/PPECHelper.php | 2 +- modules/ppcp-onboarding/src/Helper/OnboardingUrl.php | 2 +- modules/ppcp-wc-gateway/src/Helper/DCCProductStatus.php | 4 ++-- .../src/Helper/PayUponInvoiceProductStatus.php | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/ppcp-api-client/src/Endpoint/BillingAgreementsEndpoint.php b/modules/ppcp-api-client/src/Endpoint/BillingAgreementsEndpoint.php index 233c41fe2..5ad4665ca 100644 --- a/modules/ppcp-api-client/src/Endpoint/BillingAgreementsEndpoint.php +++ b/modules/ppcp-api-client/src/Endpoint/BillingAgreementsEndpoint.php @@ -135,7 +135,7 @@ class BillingAgreementsEndpoint { ); } finally { $this->is_request_logging_enabled = true; - set_transient( 'ppcp_reference_transaction_enabled', true, 3 * MONTH_IN_SECONDS ); + set_transient( 'ppcp_reference_transaction_enabled', true, MONTH_IN_SECONDS ); } return true; diff --git a/modules/ppcp-compat/src/PPEC/PPECHelper.php b/modules/ppcp-compat/src/PPEC/PPECHelper.php index 0c175979d..791cb7a22 100644 --- a/modules/ppcp-compat/src/PPEC/PPECHelper.php +++ b/modules/ppcp-compat/src/PPEC/PPECHelper.php @@ -98,7 +98,7 @@ class PPECHelper { set_transient( 'ppcp_has_ppec_subscriptions', ! empty( $result ) ? 'true' : 'false', - 3 * MONTH_IN_SECONDS + MONTH_IN_SECONDS ); return ! empty( $result ); diff --git a/modules/ppcp-onboarding/src/Helper/OnboardingUrl.php b/modules/ppcp-onboarding/src/Helper/OnboardingUrl.php index 881d4295f..b00c9b250 100644 --- a/modules/ppcp-onboarding/src/Helper/OnboardingUrl.php +++ b/modules/ppcp-onboarding/src/Helper/OnboardingUrl.php @@ -64,7 +64,7 @@ class OnboardingUrl { * * @var int */ - private $cache_ttl = 3 * MONTH_IN_SECONDS; + private $cache_ttl = MONTH_IN_SECONDS; /** * The TTL for the previous token cache. diff --git a/modules/ppcp-wc-gateway/src/Helper/DCCProductStatus.php b/modules/ppcp-wc-gateway/src/Helper/DCCProductStatus.php index 065f486be..f8d717f08 100644 --- a/modules/ppcp-wc-gateway/src/Helper/DCCProductStatus.php +++ b/modules/ppcp-wc-gateway/src/Helper/DCCProductStatus.php @@ -135,12 +135,12 @@ class DCCProductStatus { $this->settings->set( 'products_dcc_enabled', true ); $this->settings->persist(); $this->current_status_cache = true; - $this->cache->set( self::DCC_STATUS_CACHE_KEY, 'true', 3 * MONTH_IN_SECONDS ); + $this->cache->set( self::DCC_STATUS_CACHE_KEY, 'true', MONTH_IN_SECONDS ); return true; } } - $expiration = 3 * MONTH_IN_SECONDS; + $expiration = MONTH_IN_SECONDS; if ( $this->dcc_applies->for_country_currency() ) { $expiration = 3 * HOUR_IN_SECONDS; } diff --git a/modules/ppcp-wc-gateway/src/Helper/PayUponInvoiceProductStatus.php b/modules/ppcp-wc-gateway/src/Helper/PayUponInvoiceProductStatus.php index 4f0e4ba6c..df70c0779 100644 --- a/modules/ppcp-wc-gateway/src/Helper/PayUponInvoiceProductStatus.php +++ b/modules/ppcp-wc-gateway/src/Helper/PayUponInvoiceProductStatus.php @@ -127,11 +127,11 @@ class PayUponInvoiceProductStatus { $this->settings->set( 'products_pui_enabled', true ); $this->settings->persist(); $this->current_status_cache = true; - $this->cache->set( self::PUI_STATUS_CACHE_KEY, 'true', 3 * MONTH_IN_SECONDS ); + $this->cache->set( self::PUI_STATUS_CACHE_KEY, 'true', MONTH_IN_SECONDS ); return true; } } - $this->cache->set( self::PUI_STATUS_CACHE_KEY, 'false', 3 * MONTH_IN_SECONDS ); + $this->cache->set( self::PUI_STATUS_CACHE_KEY, 'false', MONTH_IN_SECONDS ); $this->current_status_cache = false; return false; From c8e1b3cd7f548e49fbf1e95f08bcd11e06c25315 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 30 Aug 2023 16:58:30 +0200 Subject: [PATCH 07/17] Check for string instead of boolean --- .../ppcp-api-client/src/Endpoint/BillingAgreementsEndpoint.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-api-client/src/Endpoint/BillingAgreementsEndpoint.php b/modules/ppcp-api-client/src/Endpoint/BillingAgreementsEndpoint.php index 233c41fe2..fb6827e6e 100644 --- a/modules/ppcp-api-client/src/Endpoint/BillingAgreementsEndpoint.php +++ b/modules/ppcp-api-client/src/Endpoint/BillingAgreementsEndpoint.php @@ -121,7 +121,7 @@ class BillingAgreementsEndpoint { */ public function reference_transaction_enabled(): bool { try { - if ( get_transient( 'ppcp_reference_transaction_enabled' ) === true ) { + if ( get_transient( 'ppcp_reference_transaction_enabled' ) === '1' ) { return true; } From c38906da9b544eaa8795b3703964c8d853173004 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Fri, 1 Sep 2023 09:48:11 +0200 Subject: [PATCH 08/17] Convert transient vaule to boolean --- .../ppcp-api-client/src/Endpoint/BillingAgreementsEndpoint.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-api-client/src/Endpoint/BillingAgreementsEndpoint.php b/modules/ppcp-api-client/src/Endpoint/BillingAgreementsEndpoint.php index fb6827e6e..271efc28a 100644 --- a/modules/ppcp-api-client/src/Endpoint/BillingAgreementsEndpoint.php +++ b/modules/ppcp-api-client/src/Endpoint/BillingAgreementsEndpoint.php @@ -121,7 +121,7 @@ class BillingAgreementsEndpoint { */ public function reference_transaction_enabled(): bool { try { - if ( get_transient( 'ppcp_reference_transaction_enabled' ) === '1' ) { + if ( wc_string_to_bool( get_transient( 'ppcp_reference_transaction_enabled' ) ) === true ) { return true; } From e819dc68dfa5a1dacfec5d835dced9b32a94c4cc Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 4 Sep 2023 16:33:04 +0200 Subject: [PATCH 09/17] Ensure WC payment token does not exist before creating it --- modules/ppcp-vaulting/services.php | 4 ++ .../ppcp-vaulting/src/PaymentTokenHelper.php | 33 +++++++++ .../src/PaymentTokensMigration.php | 31 ++++----- modules/ppcp-webhooks/services.php | 3 +- .../src/Handler/VaultPaymentTokenCreated.php | 67 ++++++++++++------- 5 files changed, 93 insertions(+), 45 deletions(-) create mode 100644 modules/ppcp-vaulting/src/PaymentTokenHelper.php diff --git a/modules/ppcp-vaulting/services.php b/modules/ppcp-vaulting/services.php index c274c4549..d18768ea2 100644 --- a/modules/ppcp-vaulting/services.php +++ b/modules/ppcp-vaulting/services.php @@ -56,10 +56,14 @@ return array( 'vaulting.payment-token-factory' => function( ContainerInterface $container ): PaymentTokenFactory { return new PaymentTokenFactory(); }, + 'vaulting.payment-token-helper' => function( ContainerInterface $container ): PaymentTokenHelper { + return new PaymentTokenHelper(); + }, 'vaulting.payment-tokens-migration' => function( ContainerInterface $container ): PaymentTokensMigration { return new PaymentTokensMigration( $container->get( 'vaulting.payment-token-factory' ), $container->get( 'vaulting.repository.payment-token' ), + $container->get( 'vaulting.payment-token-helper' ), $container->get( 'woocommerce.logger.woocommerce' ) ); }, diff --git a/modules/ppcp-vaulting/src/PaymentTokenHelper.php b/modules/ppcp-vaulting/src/PaymentTokenHelper.php new file mode 100644 index 000000000..22d973a3b --- /dev/null +++ b/modules/ppcp-vaulting/src/PaymentTokenHelper.php @@ -0,0 +1,33 @@ +get_token() === $token_id ) { + return true; + } + } + + return false; + } +} diff --git a/modules/ppcp-vaulting/src/PaymentTokensMigration.php b/modules/ppcp-vaulting/src/PaymentTokensMigration.php index 69b6c6fda..a7d3511cc 100644 --- a/modules/ppcp-vaulting/src/PaymentTokensMigration.php +++ b/modules/ppcp-vaulting/src/PaymentTokensMigration.php @@ -35,6 +35,13 @@ class PaymentTokensMigration { */ private $payment_token_repository; + /** + * The payment token helper. + * + * @var PaymentTokenHelper + */ + private $payment_token_helper; + /** * The logger. * @@ -47,16 +54,19 @@ class PaymentTokensMigration { * * @param PaymentTokenFactory $payment_token_factory The payment token factory. * @param PaymentTokenRepository $payment_token_repository The payment token repository. + * @param PaymentTokenHelper $payment_token_helper The payment token helper. * @param LoggerInterface $logger The logger. */ public function __construct( PaymentTokenFactory $payment_token_factory, PaymentTokenRepository $payment_token_repository, + PaymentTokenHelper $payment_token_helper, LoggerInterface $logger ) { $this->payment_token_factory = $payment_token_factory; $this->payment_token_repository = $payment_token_repository; $this->logger = $logger; + $this->payment_token_helper = $payment_token_helper; } /** @@ -72,7 +82,7 @@ class PaymentTokensMigration { foreach ( $tokens as $token ) { if ( isset( $token->source()->card ) ) { $wc_tokens = WC_Payment_Tokens::get_customer_tokens( $id, CreditCardGateway::ID ); - if ( $this->token_exist( $wc_tokens, $token ) ) { + if ( $this->payment_token_helper->token_exist( $wc_tokens, $token->id() ) ) { $this->logger->info( 'Token already exist for user ' . (string) $id ); continue; } @@ -97,7 +107,7 @@ class PaymentTokensMigration { } } elseif ( $token->source()->paypal ) { $wc_tokens = WC_Payment_Tokens::get_customer_tokens( $id, PayPalGateway::ID ); - if ( $this->token_exist( $wc_tokens, $token ) ) { + if ( $this->payment_token_helper->token_exist( $wc_tokens, $token->id() ) ) { $this->logger->info( 'Token already exist for user ' . (string) $id ); continue; } @@ -126,21 +136,4 @@ class PaymentTokensMigration { } } } - - /** - * Checks if given PayPal token exist as WC Payment Token. - * - * @param array $wc_tokens WC Payment Tokens. - * @param PaymentToken $token PayPal Token ID. - * @return bool - */ - private function token_exist( array $wc_tokens, PaymentToken $token ): bool { - foreach ( $wc_tokens as $wc_token ) { - if ( $wc_token->get_token() === $token->id() ) { - return true; - } - } - - return false; - } } diff --git a/modules/ppcp-webhooks/services.php b/modules/ppcp-webhooks/services.php index 9223a7348..4d585206a 100644 --- a/modules/ppcp-webhooks/services.php +++ b/modules/ppcp-webhooks/services.php @@ -80,6 +80,7 @@ return array( $order_endpoint = $container->get( 'api.endpoint.order' ); $authorized_payments_processor = $container->get( 'wcgateway.processor.authorized-payments' ); $payment_token_factory = $container->get( 'vaulting.payment-token-factory' ); + $payment_token_helper = $container->get( 'vaulting.payment-token-helper' ); $refund_fees_updater = $container->get( 'wcgateway.helper.refund-fees-updater' ); return array( @@ -95,7 +96,7 @@ return array( new PaymentCaptureRefunded( $logger, $refund_fees_updater ), new PaymentCaptureReversed( $logger ), new PaymentCaptureCompleted( $logger, $order_endpoint ), - new VaultPaymentTokenCreated( $logger, $prefix, $authorized_payments_processor, $payment_token_factory ), + new VaultPaymentTokenCreated( $logger, $prefix, $authorized_payments_processor, $payment_token_factory, $payment_token_helper ), new VaultPaymentTokenDeleted( $logger ), new PaymentCapturePending( $logger ), new PaymentSaleCompleted( $logger ), diff --git a/modules/ppcp-webhooks/src/Handler/VaultPaymentTokenCreated.php b/modules/ppcp-webhooks/src/Handler/VaultPaymentTokenCreated.php index 152932556..893305c75 100644 --- a/modules/ppcp-webhooks/src/Handler/VaultPaymentTokenCreated.php +++ b/modules/ppcp-webhooks/src/Handler/VaultPaymentTokenCreated.php @@ -13,6 +13,7 @@ use Psr\Log\LoggerInterface; use WC_Payment_Token_CC; use WC_Payment_Tokens; use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenFactory; +use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenHelper; use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenPayPal; use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; @@ -54,6 +55,13 @@ class VaultPaymentTokenCreated implements RequestHandler { */ protected $payment_token_factory; + /** + * The payment token helper. + * + * @var PaymentTokenHelper + */ + private $payment_token_helper; + /** * VaultPaymentTokenCreated constructor. * @@ -61,17 +69,20 @@ class VaultPaymentTokenCreated implements RequestHandler { * @param string $prefix The prefix. * @param AuthorizedPaymentsProcessor $authorized_payments_processor The authorized payment processor. * @param PaymentTokenFactory $payment_token_factory The payment token factory. + * @param PaymentTokenHelper $payment_token_helper The payment token helper. */ public function __construct( LoggerInterface $logger, string $prefix, AuthorizedPaymentsProcessor $authorized_payments_processor, - PaymentTokenFactory $payment_token_factory + PaymentTokenFactory $payment_token_factory, + PaymentTokenHelper $payment_token_helper ) { $this->logger = $logger; $this->prefix = $prefix; $this->authorized_payments_processor = $authorized_payments_processor; $this->payment_token_factory = $payment_token_factory; + $this->payment_token_helper = $payment_token_helper; } /** @@ -123,33 +134,39 @@ class VaultPaymentTokenCreated implements RequestHandler { if ( ! is_null( $request['resource'] ) && isset( $request['resource']['id'] ) ) { if ( ! is_null( $request['resource']['source'] ) && isset( $request['resource']['source']['card'] ) ) { - $token = new WC_Payment_Token_CC(); - $token->set_token( $request['resource']['id'] ); - $token->set_user_id( $wc_customer_id ); - $token->set_gateway_id( CreditCardGateway::ID ); + $wc_tokens = WC_Payment_Tokens::get_customer_tokens( $wc_customer_id, CreditCardGateway::ID ); + if ( ! $this->payment_token_helper->token_exist( $wc_tokens, $request['resource']['id'] ) ) { + $token = new WC_Payment_Token_CC(); + $token->set_token( $request['resource']['id'] ); + $token->set_user_id( $wc_customer_id ); + $token->set_gateway_id( CreditCardGateway::ID ); - $token->set_last4( $request['resource']['source']['card']['last_digits'] ?? '' ); - $expiry = explode( '-', $request['resource']['source']['card']['expiry'] ?? '' ); - $token->set_expiry_year( $expiry[0] ?? '' ); - $token->set_expiry_month( $expiry[1] ?? '' ); - $token->set_card_type( $request['resource']['source']['card']['brand'] ?? '' ); - $token->save(); - WC_Payment_Tokens::set_users_default( $wc_customer_id, $token->get_id() ); - } elseif ( isset( $request['resource']['source']['paypal'] ) ) { - $payment_token_paypal = $this->payment_token_factory->create( 'paypal' ); - assert( $payment_token_paypal instanceof PaymentTokenPayPal ); - - $payment_token_paypal->set_token( $request['resource']['id'] ); - $payment_token_paypal->set_user_id( $wc_customer_id ); - $payment_token_paypal->set_gateway_id( PayPalGateway::ID ); - - $email = $request['resource']['source']['paypal']['payer']['email_address'] ?? ''; - if ( $email && is_email( $email ) ) { - $payment_token_paypal->set_email( $email ); + $token->set_last4( $request['resource']['source']['card']['last_digits'] ?? '' ); + $expiry = explode( '-', $request['resource']['source']['card']['expiry'] ?? '' ); + $token->set_expiry_year( $expiry[0] ?? '' ); + $token->set_expiry_month( $expiry[1] ?? '' ); + $token->set_card_type( $request['resource']['source']['card']['brand'] ?? '' ); + $token->save(); + WC_Payment_Tokens::set_users_default( $wc_customer_id, $token->get_id() ); } + } elseif ( isset( $request['resource']['source']['paypal'] ) ) { + $wc_tokens = WC_Payment_Tokens::get_customer_tokens( $wc_customer_id, PayPalGateway::ID ); + if ( ! $this->payment_token_helper->token_exist( $wc_tokens, $request['resource']['id'] ) ) { + $payment_token_paypal = $this->payment_token_factory->create( 'paypal' ); + assert( $payment_token_paypal instanceof PaymentTokenPayPal ); - $payment_token_paypal->save(); - WC_Payment_Tokens::set_users_default( $wc_customer_id, $payment_token_paypal->get_id() ); + $payment_token_paypal->set_token( $request['resource']['id'] ); + $payment_token_paypal->set_user_id( $wc_customer_id ); + $payment_token_paypal->set_gateway_id( PayPalGateway::ID ); + + $email = $request['resource']['source']['paypal']['payer']['email_address'] ?? ''; + if ( $email && is_email( $email ) ) { + $payment_token_paypal->set_email( $email ); + } + + $payment_token_paypal->save(); + WC_Payment_Tokens::set_users_default( $wc_customer_id, $payment_token_paypal->get_id() ); + } } } From 4482809c182a482bd6c929827ec59b397531b5f2 Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Tue, 5 Sep 2023 10:40:50 +0100 Subject: [PATCH 10/17] Fix clearing onboarding links cache on disconnect Add clearing onboarding links cache on uninstall --- .../ppcp-onboarding/src/Helper/OnboardingUrl.php | 5 ++++- modules/ppcp-uninstall/services.php | 6 ++++++ modules/ppcp-uninstall/src/ClearDatabase.php | 9 +++++++++ .../ppcp-uninstall/src/ClearDatabaseInterface.php | 8 ++++++++ modules/ppcp-uninstall/src/UninstallModule.php | 10 +++++++--- .../src/Settings/SettingsListener.php | 15 ++++++++++++--- modules/ppcp-wc-gateway/src/WCGatewayModule.php | 10 ++++++++++ 7 files changed, 56 insertions(+), 7 deletions(-) diff --git a/modules/ppcp-onboarding/src/Helper/OnboardingUrl.php b/modules/ppcp-onboarding/src/Helper/OnboardingUrl.php index 881d4295f..9d4c9aa74 100644 --- a/modules/ppcp-onboarding/src/Helper/OnboardingUrl.php +++ b/modules/ppcp-onboarding/src/Helper/OnboardingUrl.php @@ -346,7 +346,10 @@ class OnboardingUrl { * @return void */ public function delete(): void { - $this->cache->delete( $this->cache_key() ); + $cache_key = $this->cache_key(); + if ( $this->cache->has( $cache_key ) ) { + $this->cache->delete( $cache_key ); + } } /** diff --git a/modules/ppcp-uninstall/services.php b/modules/ppcp-uninstall/services.php index 6f7cda722..ca67dc71b 100644 --- a/modules/ppcp-uninstall/services.php +++ b/modules/ppcp-uninstall/services.php @@ -45,6 +45,12 @@ return array( ); }, + 'uninstall.ppcp-all-action-names' => function( ContainerInterface $container ) : array { + return array( + 'woocommerce_paypal_payments_uninstall', + ); + }, + 'uninstall.clear-db-endpoint' => function( ContainerInterface $container ) : string { return 'ppcp-clear-db'; }, diff --git a/modules/ppcp-uninstall/src/ClearDatabase.php b/modules/ppcp-uninstall/src/ClearDatabase.php index fc09519a5..ed5d73bb6 100644 --- a/modules/ppcp-uninstall/src/ClearDatabase.php +++ b/modules/ppcp-uninstall/src/ClearDatabase.php @@ -31,4 +31,13 @@ class ClearDatabase implements ClearDatabaseInterface { as_unschedule_action( $action_name ); } } + + /** + * {@inheritDoc} + */ + public function clear_actions( array $action_names ): void { + foreach ( $action_names as $action_name ) { + do_action( $action_name ); + } + } } diff --git a/modules/ppcp-uninstall/src/ClearDatabaseInterface.php b/modules/ppcp-uninstall/src/ClearDatabaseInterface.php index 6d6aedb77..34d9b1469 100644 --- a/modules/ppcp-uninstall/src/ClearDatabaseInterface.php +++ b/modules/ppcp-uninstall/src/ClearDatabaseInterface.php @@ -29,4 +29,12 @@ interface ClearDatabaseInterface { */ public function clear_scheduled_actions( array $action_names ): void; + /** + * Clears the given actions. + * + * @param string[] $action_names The list of action names. + * @throws RuntimeException If problem clearing. + */ + public function clear_actions( array $action_names ): void; + } diff --git a/modules/ppcp-uninstall/src/UninstallModule.php b/modules/ppcp-uninstall/src/UninstallModule.php index 649be5c49..25d65ae0f 100644 --- a/modules/ppcp-uninstall/src/UninstallModule.php +++ b/modules/ppcp-uninstall/src/UninstallModule.php @@ -47,8 +47,9 @@ class UninstallModule implements ModuleInterface { $clear_db_endpoint = $container->get( 'uninstall.clear-db-endpoint' ); $option_names = $container->get( 'uninstall.ppcp-all-option-names' ); $scheduled_action_names = $container->get( 'uninstall.ppcp-all-scheduled-action-names' ); + $action_names = $container->get( 'uninstall.ppcp-all-action-names' ); - $this->handleClearDbAjaxRequest( $request_data, $clear_db, $clear_db_endpoint, $option_names, $scheduled_action_names ); + $this->handleClearDbAjaxRequest( $request_data, $clear_db, $clear_db_endpoint, $option_names, $scheduled_action_names, $action_names ); } /** @@ -69,17 +70,19 @@ class UninstallModule implements ModuleInterface { * @param string $nonce The nonce. * @param string[] $option_names The list of option names. * @param string[] $scheduled_action_names The list of scheduled action names. + * @param string[] $action_names The list of action names. */ protected function handleClearDbAjaxRequest( RequestData $request_data, ClearDatabaseInterface $clear_db, string $nonce, array $option_names, - array $scheduled_action_names + array $scheduled_action_names, + array $action_names ): void { add_action( "wc_ajax_{$nonce}", - static function () use ( $request_data, $clear_db, $nonce, $option_names, $scheduled_action_names ) { + static function () use ( $request_data, $clear_db, $nonce, $option_names, $scheduled_action_names, $action_names ) { try { if ( ! current_user_can( 'manage_woocommerce' ) ) { wp_send_json_error( 'Not admin.', 403 ); @@ -91,6 +94,7 @@ class UninstallModule implements ModuleInterface { $clear_db->delete_options( $option_names ); $clear_db->clear_scheduled_actions( $scheduled_action_names ); + $clear_db->clear_actions( $action_names ); wp_send_json_success(); return true; diff --git a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php index b1d057537..6ebda496a 100644 --- a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php +++ b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php @@ -401,9 +401,7 @@ class SettingsListener { $this->webhook_registrar->unregister(); foreach ( $this->signup_link_ids as $key ) { - if ( $this->signup_link_cache->has( $key ) ) { - $this->signup_link_cache->delete( $key ); - } + ( new OnboardingUrl( $this->signup_link_cache, $key, get_current_user_id() ) )->delete(); } } } @@ -638,4 +636,15 @@ class SettingsListener { throw $exception; } } + + /** + * Handles onboarding URLs deletion + */ + public function listen_for_uninstall() { + // Clear onboarding links from cache. + foreach ( $this->signup_link_ids as $key ) { + ( new OnboardingUrl( $this->signup_link_cache, $key, get_current_user_id() ) )->delete(); + } + } + } diff --git a/modules/ppcp-wc-gateway/src/WCGatewayModule.php b/modules/ppcp-wc-gateway/src/WCGatewayModule.php index 477ce303f..7e728f0ec 100644 --- a/modules/ppcp-wc-gateway/src/WCGatewayModule.php +++ b/modules/ppcp-wc-gateway/src/WCGatewayModule.php @@ -392,6 +392,16 @@ class WCGatewayModule implements ModuleInterface { 3 ); + add_action( + 'woocommerce_paypal_payments_uninstall', + static function () use ( $c ) { + $listener = $c->get( 'wcgateway.settings.listener' ); + assert( $listener instanceof SettingsListener ); + + $listener->listen_for_uninstall(); + } + ); + if ( defined( 'WP_CLI' ) && WP_CLI ) { \WP_CLI::add_command( 'pcp settings', From 9960134933b6395acfef182c51a3482896b3fc1a Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Tue, 5 Sep 2023 11:25:49 +0100 Subject: [PATCH 11/17] Fix lint --- modules/ppcp-wc-gateway/src/Settings/SettingsListener.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php index 6ebda496a..bd36ca5d4 100644 --- a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php +++ b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php @@ -640,7 +640,7 @@ class SettingsListener { /** * Handles onboarding URLs deletion */ - public function listen_for_uninstall() { + public function listen_for_uninstall(): void { // Clear onboarding links from cache. foreach ( $this->signup_link_ids as $key ) { ( new OnboardingUrl( $this->signup_link_cache, $key, get_current_user_id() ) )->delete(); From 5b7eed750ff3061f4e7242f99cc9e357a0a096cd Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Tue, 5 Sep 2023 11:53:23 +0100 Subject: [PATCH 12/17] Fix test --- tests/PHPUnit/Onboarding/Helper/OnboardingUrlTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/PHPUnit/Onboarding/Helper/OnboardingUrlTest.php b/tests/PHPUnit/Onboarding/Helper/OnboardingUrlTest.php index db50265d5..8967e9a56 100644 --- a/tests/PHPUnit/Onboarding/Helper/OnboardingUrlTest.php +++ b/tests/PHPUnit/Onboarding/Helper/OnboardingUrlTest.php @@ -118,6 +118,7 @@ class OnboardingUrlTest extends TestCase public function test_delete() { // Expectations + $this->cache->shouldReceive('has')->once(); $this->cache->shouldReceive('delete')->once(); $this->onboardingUrl->delete(); From ca9fd29884aaf90aaf4865532252d82ff4da5178 Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Tue, 5 Sep 2023 12:20:45 +0100 Subject: [PATCH 13/17] Fix tests --- modules/ppcp-onboarding/src/Helper/OnboardingUrl.php | 5 +---- tests/PHPUnit/Onboarding/Helper/OnboardingUrlTest.php | 5 ++--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/modules/ppcp-onboarding/src/Helper/OnboardingUrl.php b/modules/ppcp-onboarding/src/Helper/OnboardingUrl.php index 9d4c9aa74..881d4295f 100644 --- a/modules/ppcp-onboarding/src/Helper/OnboardingUrl.php +++ b/modules/ppcp-onboarding/src/Helper/OnboardingUrl.php @@ -346,10 +346,7 @@ class OnboardingUrl { * @return void */ public function delete(): void { - $cache_key = $this->cache_key(); - if ( $this->cache->has( $cache_key ) ) { - $this->cache->delete( $cache_key ); - } + $this->cache->delete( $this->cache_key() ); } /** diff --git a/tests/PHPUnit/Onboarding/Helper/OnboardingUrlTest.php b/tests/PHPUnit/Onboarding/Helper/OnboardingUrlTest.php index 8967e9a56..ea8b0ab0e 100644 --- a/tests/PHPUnit/Onboarding/Helper/OnboardingUrlTest.php +++ b/tests/PHPUnit/Onboarding/Helper/OnboardingUrlTest.php @@ -3,7 +3,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Onboarding\Helper; -use PHPUnit\Framework\TestCase; +use WooCommerce\PayPalCommerce\TestCase; use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache; use RuntimeException; use function Brain\Monkey\Functions\when; @@ -15,7 +15,7 @@ class OnboardingUrlTest extends TestCase private $user_id = 123; private $onboardingUrl; - protected function setUp(): void + public function setUp(): void { parent::setUp(); @@ -118,7 +118,6 @@ class OnboardingUrlTest extends TestCase public function test_delete() { // Expectations - $this->cache->shouldReceive('has')->once(); $this->cache->shouldReceive('delete')->once(); $this->onboardingUrl->delete(); From 7415fdb32d427d6b60271a81271573f8094364f1 Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Wed, 5 Jul 2023 10:48:29 +0100 Subject: [PATCH 14/17] Do not declare subscription support for PayPal when only ACDC vaulting This reverts commit 01717e740a31b74e1128c1b6356efa002ca10496. --- modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php index c703de904..933e6ce51 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php @@ -227,7 +227,6 @@ class PayPalGateway extends \WC_Payment_Gateway { if ( ( $this->config->has( 'vault_enabled' ) && $this->config->get( 'vault_enabled' ) ) - || ( $this->config->has( 'vault_enabled_dcc' ) && $this->config->get( 'vault_enabled_dcc' ) ) || ( $this->config->has( 'subscriptions_mode' ) && $this->config->get( 'subscriptions_mode' ) === 'subscriptions_api' ) ) { array_push( @@ -244,6 +243,8 @@ class PayPalGateway extends \WC_Payment_Gateway { 'subscription_payment_method_change_admin', 'multiple_subscriptions' ); + } elseif ( $this->config->has( 'vault_enabled_dcc' ) && $this->config->get( 'vault_enabled_dcc' ) ) { + $this->supports[] = 'tokenization'; } } From fb0725df77f790d53d7e18c191bb549d57f59af5 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Thu, 7 Sep 2023 10:08:18 +0200 Subject: [PATCH 15/17] Improve dockblock --- modules/ppcp-vaulting/src/PaymentTokenHelper.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-vaulting/src/PaymentTokenHelper.php b/modules/ppcp-vaulting/src/PaymentTokenHelper.php index 22d973a3b..de940ccf3 100644 --- a/modules/ppcp-vaulting/src/PaymentTokenHelper.php +++ b/modules/ppcp-vaulting/src/PaymentTokenHelper.php @@ -9,16 +9,18 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Vaulting; +use WC_Payment_Token; + /** * Class PaymentTokenHelper */ class PaymentTokenHelper { /** - * Checks if given PayPal token exist as WC Payment Token. + * Checks if given token exist as WC Payment Token. * - * @param array $wc_tokens WC Payment Tokens. - * @param string $token_id PayPal Token ID. + * @param WC_Payment_Token[] $wc_tokens WC Payment Tokens. + * @param string $token_id Payment Token ID. * @return bool */ public function token_exist( array $wc_tokens, string $token_id ): bool { From a53246259547d410d236828d43e3acbce76043f7 Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Fri, 8 Sep 2023 10:32:34 +0100 Subject: [PATCH 16/17] Fix onboarding email with spaces/plus signs. Fix order item when image_url is empty. --- modules/ppcp-api-client/src/Entity/Item.php | 5 ++++- .../src/Settings/SettingsListener.php | 12 +++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-api-client/src/Entity/Item.php b/modules/ppcp-api-client/src/Entity/Item.php index 7d38e5f78..cc739635f 100644 --- a/modules/ppcp-api-client/src/Entity/Item.php +++ b/modules/ppcp-api-client/src/Entity/Item.php @@ -249,9 +249,12 @@ class Item { 'sku' => $this->sku(), 'category' => $this->category(), 'url' => $this->url(), - 'image_url' => $this->image_url(), ); + if ( $this->image_url() ) { + $item['image_url'] = $this->image_url(); + } + if ( $this->tax() ) { $item['tax'] = $this->tax()->to_array(); } diff --git a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php index bd36ca5d4..df6f013a5 100644 --- a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php +++ b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php @@ -211,7 +211,7 @@ class SettingsListener { } $merchant_id = sanitize_text_field( wp_unslash( $_GET['merchantIdInPayPal'] ) ); - $merchant_email = sanitize_text_field( wp_unslash( $_GET['merchantId'] ) ); + $merchant_email = $this->sanitize_onboarding_email( sanitize_text_field( wp_unslash( $_GET['merchantId'] ) ) ); $onboarding_token = sanitize_text_field( wp_unslash( $_GET['ppcpToken'] ) ); $retry_count = isset( $_GET['ppcpRetry'] ) ? ( (int) sanitize_text_field( wp_unslash( $_GET['ppcpRetry'] ) ) ) : 0; // phpcs:enable WordPress.Security.NonceVerification.Missing @@ -278,6 +278,16 @@ class SettingsListener { $this->onboarding_redirect(); } + /** + * Sanitizes the onboarding email. + * + * @param string $email The onboarding email. + * @return string + */ + private function sanitize_onboarding_email( string $email ): string { + return str_replace( ' ', '+', $email ); + } + /** * Redirect to the onboarding URL. * From fa45071a5f7ae0c138a26ef18b57cca7e83a2340 Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Fri, 8 Sep 2023 14:41:21 +0400 Subject: [PATCH 17/17] Don't send `image_url` when it is empty --- modules/ppcp-api-client/src/Entity/Item.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-api-client/src/Entity/Item.php b/modules/ppcp-api-client/src/Entity/Item.php index 7d38e5f78..cc739635f 100644 --- a/modules/ppcp-api-client/src/Entity/Item.php +++ b/modules/ppcp-api-client/src/Entity/Item.php @@ -249,9 +249,12 @@ class Item { 'sku' => $this->sku(), 'category' => $this->category(), 'url' => $this->url(), - 'image_url' => $this->image_url(), ); + if ( $this->image_url() ) { + $item['image_url'] = $this->image_url(); + } + if ( $this->tax() ) { $item['tax'] = $this->tax()->to_array(); }