From 2f6240bfb43973bc79a74bde07404709e83c00b6 Mon Sep 17 00:00:00 2001 From: Alex P Date: Thu, 15 Dec 2022 12:37:52 +0200 Subject: [PATCH 01/35] Do not send Paypal-Request-Id The current usage is incorrect and only causes issues without solving any problems (we don't implement any way to retry requests). --- modules/ppcp-api-client/services.php | 9 +- .../src/Endpoint/OrderEndpoint.php | 45 ++----- .../Endpoint/PayUponInvoiceOrderEndpoint.php | 2 +- .../src/Endpoint/PaymentTokenEndpoint.php | 19 +-- .../Repository/PayPalRequestIdRepository.php | 112 ------------------ modules/ppcp-wc-gateway/extensions.php | 2 - .../ppcp-wc-gateway/src/WCGatewayModule.php | 4 +- .../ApiClient/Endpoint/OrderEndpointTest.php | 79 +----------- .../PayPalRequestIdRepositoryTest.php | 58 --------- 9 files changed, 18 insertions(+), 312 deletions(-) delete mode 100644 modules/ppcp-api-client/src/Repository/PayPalRequestIdRepository.php delete mode 100644 tests/PHPUnit/ApiClient/Repository/PayPalRequestIdRepositoryTest.php diff --git a/modules/ppcp-api-client/services.php b/modules/ppcp-api-client/services.php index 0a4a85728..393d868bc 100644 --- a/modules/ppcp-api-client/services.php +++ b/modules/ppcp-api-client/services.php @@ -54,7 +54,6 @@ use WooCommerce\PayPalCommerce\ApiClient\Repository\CustomerRepository; use WooCommerce\PayPalCommerce\ApiClient\Repository\OrderRepository; use WooCommerce\PayPalCommerce\ApiClient\Repository\PartnerReferralsData; use WooCommerce\PayPalCommerce\ApiClient\Repository\PayeeRepository; -use WooCommerce\PayPalCommerce\ApiClient\Repository\PayPalRequestIdRepository; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; return array( @@ -118,8 +117,7 @@ return array( $container->get( 'api.factory.payment-token' ), $container->get( 'api.factory.payment-token-action-links' ), $container->get( 'woocommerce.logger.woocommerce' ), - $container->get( 'api.repository.customer' ), - $container->get( 'api.repository.paypal-request-id' ) + $container->get( 'api.repository.customer' ) ); }, 'api.endpoint.webhook' => static function ( ContainerInterface $container ) : WebhookEndpoint { @@ -187,7 +185,6 @@ return array( $settings = $container->get( 'wcgateway.settings' ); $intent = $settings->has( 'intent' ) && strtoupper( (string) $settings->get( 'intent' ) ) === 'AUTHORIZE' ? 'AUTHORIZE' : 'CAPTURE'; $application_context_repository = $container->get( 'api.repository.application-context' ); - $paypal_request_id = $container->get( 'api.repository.paypal-request-id' ); $subscription_helper = $container->get( 'subscription.helper' ); return new OrderEndpoint( $container->get( 'api.host' ), @@ -197,7 +194,6 @@ return array( $intent, $logger, $application_context_repository, - $paypal_request_id, $subscription_helper ); }, @@ -208,9 +204,6 @@ return array( $container->get( 'woocommerce.logger.woocommerce' ) ); }, - 'api.repository.paypal-request-id' => static function( ContainerInterface $container ) : PayPalRequestIdRepository { - return new PayPalRequestIdRepository(); - }, 'api.repository.application-context' => static function( ContainerInterface $container ) : ApplicationContextRepository { $settings = $container->get( 'wcgateway.settings' ); diff --git a/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php b/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php index 57b8b73d6..997b43413 100644 --- a/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php +++ b/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php @@ -101,13 +101,6 @@ class OrderEndpoint { */ private $bn_code; - /** - * The paypal request id repository. - * - * @var PayPalRequestIdRepository - */ - private $paypal_request_id_repository; - /** * OrderEndpoint constructor. * @@ -118,7 +111,6 @@ class OrderEndpoint { * @param string $intent The intent. * @param LoggerInterface $logger The logger. * @param ApplicationContextRepository $application_context_repository The application context repository. - * @param PayPalRequestIdRepository $paypal_request_id_repository The paypal request id repository. * @param SubscriptionHelper $subscription_helper The subscription helper. * @param string $bn_code The BN Code. */ @@ -130,7 +122,6 @@ class OrderEndpoint { string $intent, LoggerInterface $logger, ApplicationContextRepository $application_context_repository, - PayPalRequestIdRepository $paypal_request_id_repository, SubscriptionHelper $subscription_helper, string $bn_code = '' ) { @@ -143,7 +134,6 @@ class OrderEndpoint { $this->logger = $logger; $this->application_context_repository = $application_context_repository; $this->bn_code = $bn_code; - $this->paypal_request_id_repository = $paypal_request_id_repository; $this->subscription_helper = $subscription_helper; } @@ -169,7 +159,6 @@ class OrderEndpoint { * @param Payer|null $payer The payer off the order. * @param PaymentToken|null $payment_token The payment token. * @param PaymentMethod|null $payment_method The payment method. - * @param string $paypal_request_id The paypal request id. * * @return Order * @throws RuntimeException If the request fails. @@ -179,8 +168,7 @@ class OrderEndpoint { string $shipping_preference, Payer $payer = null, PaymentToken $payment_token = null, - PaymentMethod $payment_method = null, - string $paypal_request_id = '' + PaymentMethod $payment_method = null ): Order { $bearer = $this->bearer->bearer(); $data = array( @@ -219,8 +207,6 @@ class OrderEndpoint { 'body' => wp_json_encode( $data ), ); - $paypal_request_id = $paypal_request_id ? $paypal_request_id : uniqid( 'ppcp-', true ); - $args['headers']['PayPal-Request-Id'] = $paypal_request_id; if ( $this->bn_code ) { $args['headers']['PayPal-Partner-Attribution-Id'] = $this->bn_code; } @@ -260,7 +246,6 @@ class OrderEndpoint { throw $error; } $order = $this->order_factory->from_paypal_response( $json ); - $this->paypal_request_id_repository->set_for_order( $order, $paypal_request_id ); return $order; } @@ -281,10 +266,9 @@ class OrderEndpoint { $args = array( 'method' => 'POST', 'headers' => array( - 'Authorization' => 'Bearer ' . $bearer->token(), - 'Content-Type' => 'application/json', - 'Prefer' => 'return=representation', - 'PayPal-Request-Id' => $this->paypal_request_id_repository->get_for_order( $order ), + 'Authorization' => 'Bearer ' . $bearer->token(), + 'Content-Type' => 'application/json', + 'Prefer' => 'return=representation', ), ); if ( $this->bn_code ) { @@ -356,10 +340,9 @@ class OrderEndpoint { $args = array( 'method' => 'POST', 'headers' => array( - 'Authorization' => 'Bearer ' . $bearer->token(), - 'Content-Type' => 'application/json', - 'Prefer' => 'return=representation', - 'PayPal-Request-Id' => $this->paypal_request_id_repository->get_for_order( $order ), + 'Authorization' => 'Bearer ' . $bearer->token(), + 'Content-Type' => 'application/json', + 'Prefer' => 'return=representation', ), ); if ( $this->bn_code ) { @@ -430,9 +413,8 @@ class OrderEndpoint { $url = trailingslashit( $this->host ) . 'v2/checkout/orders/' . $id; $args = array( 'headers' => array( - 'Authorization' => 'Bearer ' . $bearer->token(), - 'Content-Type' => 'application/json', - 'PayPal-Request-Id' => $this->paypal_request_id_repository->get_for_order_id( $id ), + 'Authorization' => 'Bearer ' . $bearer->token(), + 'Content-Type' => 'application/json', ), ); if ( $this->bn_code ) { @@ -517,12 +499,9 @@ class OrderEndpoint { $args = array( 'method' => 'PATCH', 'headers' => array( - 'Authorization' => 'Bearer ' . $bearer->token(), - 'Content-Type' => 'application/json', - 'Prefer' => 'return=representation', - 'PayPal-Request-Id' => $this->paypal_request_id_repository->get_for_order( - $order_to_update - ), + 'Authorization' => 'Bearer ' . $bearer->token(), + 'Content-Type' => 'application/json', + 'Prefer' => 'return=representation', ), 'body' => wp_json_encode( $patches_array ), ); diff --git a/modules/ppcp-api-client/src/Endpoint/PayUponInvoiceOrderEndpoint.php b/modules/ppcp-api-client/src/Endpoint/PayUponInvoiceOrderEndpoint.php index 29a16302b..2c1d85812 100644 --- a/modules/ppcp-api-client/src/Endpoint/PayUponInvoiceOrderEndpoint.php +++ b/modules/ppcp-api-client/src/Endpoint/PayUponInvoiceOrderEndpoint.php @@ -133,7 +133,7 @@ class PayUponInvoiceOrderEndpoint { 'Content-Type' => 'application/json', 'Prefer' => 'return=representation', 'PayPal-Client-Metadata-Id' => $this->fraudnet->session_id(), - 'PayPal-Request-Id' => uniqid( 'ppcp-', true ), + 'PayPal-Request-Id' => uniqid( 'ppcp-', true ), // Request-Id header is required. ), 'body' => wp_json_encode( $data ), ); diff --git a/modules/ppcp-api-client/src/Endpoint/PaymentTokenEndpoint.php b/modules/ppcp-api-client/src/Endpoint/PaymentTokenEndpoint.php index 9e1b3a74b..a781dd8a8 100644 --- a/modules/ppcp-api-client/src/Endpoint/PaymentTokenEndpoint.php +++ b/modules/ppcp-api-client/src/Endpoint/PaymentTokenEndpoint.php @@ -19,7 +19,6 @@ use WooCommerce\PayPalCommerce\ApiClient\Factory\PaymentTokenActionLinksFactory; use WooCommerce\PayPalCommerce\ApiClient\Factory\PaymentTokenFactory; use Psr\Log\LoggerInterface; use WooCommerce\PayPalCommerce\ApiClient\Repository\CustomerRepository; -use WooCommerce\PayPalCommerce\ApiClient\Repository\PayPalRequestIdRepository; /** * Class PaymentTokenEndpoint @@ -70,13 +69,6 @@ class PaymentTokenEndpoint { */ protected $customer_repository; - /** - * The request id repository. - * - * @var PayPalRequestIdRepository - */ - private $request_id_repository; - /** * PaymentTokenEndpoint constructor. * @@ -86,7 +78,6 @@ class PaymentTokenEndpoint { * @param PaymentTokenActionLinksFactory $payment_token_action_links_factory The PaymentTokenActionLinks factory. * @param LoggerInterface $logger The logger. * @param CustomerRepository $customer_repository The customer repository. - * @param PayPalRequestIdRepository $request_id_repository The request id repository. */ public function __construct( string $host, @@ -94,8 +85,7 @@ class PaymentTokenEndpoint { PaymentTokenFactory $factory, PaymentTokenActionLinksFactory $payment_token_action_links_factory, LoggerInterface $logger, - CustomerRepository $customer_repository, - PayPalRequestIdRepository $request_id_repository + CustomerRepository $customer_repository ) { $this->host = $host; @@ -104,7 +94,6 @@ class PaymentTokenEndpoint { $this->payment_token_action_links_factory = $payment_token_action_links_factory; $this->logger = $logger; $this->customer_repository = $customer_repository; - $this->request_id_repository = $request_id_repository; } /** @@ -243,14 +232,11 @@ class PaymentTokenEndpoint { ), ); - $request_id = uniqid( 'ppcp-vault', true ); - $args = array( 'method' => 'POST', 'headers' => array( 'Authorization' => 'Bearer ' . $bearer->token(), 'Content-Type' => 'application/json', - 'Request-Id' => $request_id, ), 'body' => wp_json_encode( $data ), ); @@ -277,8 +263,6 @@ class PaymentTokenEndpoint { $links = $this->payment_token_action_links_factory->from_paypal_response( $json ); - $this->request_id_repository->set( "ppcp-vault-{$user_id}", $request_id ); - return $links; } @@ -302,7 +286,6 @@ class PaymentTokenEndpoint { 'method' => 'POST', 'headers' => array( 'Authorization' => 'Bearer ' . $bearer->token(), - 'Request-Id' => $this->request_id_repository->get( "ppcp-vault-{$user_id}" ), 'Content-Type' => 'application/json', ), ); diff --git a/modules/ppcp-api-client/src/Repository/PayPalRequestIdRepository.php b/modules/ppcp-api-client/src/Repository/PayPalRequestIdRepository.php deleted file mode 100644 index de0f7e650..000000000 --- a/modules/ppcp-api-client/src/Repository/PayPalRequestIdRepository.php +++ /dev/null @@ -1,112 +0,0 @@ -get( $order_id ); - } - - /** - * Returns the request ID for an order. - * - * @param Order $order The order. - * - * @return string - */ - public function get_for_order( Order $order ): string { - return $this->get_for_order_id( $order->id() ); - } - - /** - * Sets a request ID for a specific order. - * - * @param Order $order The order. - * @param string $request_id The ID. - * - * @return bool - */ - public function set_for_order( Order $order, string $request_id ): bool { - $this->set( $order->id(), $request_id ); - return true; - } - - /** - * Sets a request ID for the given key. - * - * @param string $key The key in the request ID storage. - * @param string $request_id The ID. - */ - public function set( string $key, string $request_id ): void { - $all = $this->all(); - $day_in_seconds = 86400; - $all[ $key ] = array( - 'id' => $request_id, - 'expiration' => time() + 10 * $day_in_seconds, - ); - $all = $this->cleanup( $all ); - update_option( self::KEY, $all ); - } - - /** - * Returns a request ID. - * - * @param string $key The key in the request ID storage. - * - * @return string - */ - public function get( string $key ): string { - $all = $this->all(); - return isset( $all[ $key ] ) ? (string) $all[ $key ]['id'] : ''; - } - - /** - * Return all IDs. - * - * @return array - */ - private function all(): array { - - return (array) get_option( 'ppcp-request-ids', array() ); - } - - /** - * Clean up outdated request IDs. - * - * @param array $all All request IDs. - * - * @return array - */ - private function cleanup( array $all ): array { - - foreach ( $all as $order_id => $value ) { - if ( time() < $value['expiration'] ) { - continue; - } - unset( $all[ $order_id ] ); - } - return $all; - } -} diff --git a/modules/ppcp-wc-gateway/extensions.php b/modules/ppcp-wc-gateway/extensions.php index 68bcf2590..705c043e3 100644 --- a/modules/ppcp-wc-gateway/extensions.php +++ b/modules/ppcp-wc-gateway/extensions.php @@ -72,7 +72,6 @@ return array( $settings = $container->get( 'wcgateway.settings' ); $intent = $settings->has( 'intent' ) && strtoupper( (string) $settings->get( 'intent' ) ) === 'AUTHORIZE' ? 'AUTHORIZE' : 'CAPTURE'; $application_context_repository = $container->get( 'api.repository.application-context' ); - $pay_pal_request_id_repository = $container->get( 'api.repository.paypal-request-id' ); $subscription_helper = $container->get( 'subscription.helper' ); return new OrderEndpoint( $container->get( 'api.host' ), @@ -82,7 +81,6 @@ return array( $intent, $logger, $application_context_repository, - $pay_pal_request_id_repository, $subscription_helper, $bn_code ); diff --git a/modules/ppcp-wc-gateway/src/WCGatewayModule.php b/modules/ppcp-wc-gateway/src/WCGatewayModule.php index 07645ccd5..7f4a62939 100644 --- a/modules/ppcp-wc-gateway/src/WCGatewayModule.php +++ b/modules/ppcp-wc-gateway/src/WCGatewayModule.php @@ -16,7 +16,6 @@ use WooCommerce\PayPalCommerce\AdminNotices\Repository\Repository; use WooCommerce\PayPalCommerce\ApiClient\Entity\Capture; use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus; use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies; -use WooCommerce\PayPalCommerce\ApiClient\Repository\PayPalRequestIdRepository; use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\WcGateway\Admin\FeesRenderer; use WooCommerce\PayPalCommerce\WcGateway\Admin\OrderTablePaymentStatusColumn; @@ -214,7 +213,6 @@ class WCGatewayModule implements ModuleInterface { 'woocommerce_paypal_commerce_gateway_deactivate', static function () use ( $c ) { delete_option( Settings::KEY ); - delete_option( PayPalRequestIdRepository::KEY ); delete_option( 'woocommerce_' . PayPalGateway::ID . '_settings' ); delete_option( 'woocommerce_' . CreditCardGateway::ID . '_settings' ); } @@ -236,6 +234,8 @@ class WCGatewayModule implements ModuleInterface { add_action( 'woocommerce_paypal_payments_gateway_migrate', static function () use ( $c ) { + delete_option( 'ppcp-request-ids' ); + $settings = $c->get( 'wcgateway.settings' ); assert( $settings instanceof Settings ); diff --git a/tests/PHPUnit/ApiClient/Endpoint/OrderEndpointTest.php b/tests/PHPUnit/ApiClient/Endpoint/OrderEndpointTest.php index 4052febd2..ea7938d4f 100644 --- a/tests/PHPUnit/ApiClient/Endpoint/OrderEndpointTest.php +++ b/tests/PHPUnit/ApiClient/Endpoint/OrderEndpointTest.php @@ -24,7 +24,6 @@ use WooCommerce\PayPalCommerce\ApiClient\Factory\OrderFactory; use WooCommerce\PayPalCommerce\ApiClient\Factory\PatchCollectionFactory; use WooCommerce\PayPalCommerce\ApiClient\Helper\ErrorResponse; use WooCommerce\PayPalCommerce\ApiClient\Repository\ApplicationContextRepository; -use WooCommerce\PayPalCommerce\ApiClient\Repository\PayPalRequestIdRepository; use Mockery; use Psr\Log\LoggerInterface; use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper; @@ -67,9 +66,6 @@ class OrderEndpointTest extends TestCase $logger->shouldNotReceive('log'); $logger->shouldReceive('debug'); $applicationContextRepository = Mockery::mock(ApplicationContextRepository::class); - $paypalRequestIdRepository = Mockery::mock(PayPalRequestIdRepository::class); - $paypalRequestIdRepository - ->expects('get_for_order_id')->with($orderId)->andReturn('uniqueRequestId'); $headers = Mockery::mock(Requests_Utility_CaseInsensitiveDictionary::class); $headers->shouldReceive('getAll'); $subscription_helper = Mockery::mock(SubscriptionHelper::class); @@ -82,7 +78,6 @@ class OrderEndpointTest extends TestCase $intent, $logger, $applicationContextRepository, - $paypalRequestIdRepository, $subscription_helper ); @@ -128,9 +123,6 @@ class OrderEndpointTest extends TestCase $logger->shouldReceive('log'); $logger->shouldReceive('debug'); $applicationContextRepository = Mockery::mock(ApplicationContextRepository::class); - $paypalRequestIdRepository = Mockery::mock(PayPalRequestIdRepository::class); - $paypalRequestIdRepository - ->expects('get_for_order_id')->with($orderId)->andReturn('uniqueRequestId'); $headers = Mockery::mock(Requests_Utility_CaseInsensitiveDictionary::class); $headers->shouldReceive('getAll'); $subscription_helper = Mockery::mock(SubscriptionHelper::class); @@ -143,7 +135,6 @@ class OrderEndpointTest extends TestCase $intent, $logger, $applicationContextRepository, - $paypalRequestIdRepository, $subscription_helper ); @@ -182,9 +173,6 @@ class OrderEndpointTest extends TestCase $logger->shouldReceive('log'); $logger->shouldReceive('debug'); $applicationContextRepository = Mockery::mock(ApplicationContextRepository::class); - $paypalRequestIdRepository = Mockery::mock(PayPalRequestIdRepository::class); - $paypalRequestIdRepository - ->expects('get_for_order_id')->with($orderId)->andReturn('uniqueRequestId'); $subscription_helper = Mockery::mock(SubscriptionHelper::class); $testee = new OrderEndpoint( @@ -195,7 +183,6 @@ class OrderEndpointTest extends TestCase $intent, $logger, $applicationContextRepository, - $paypalRequestIdRepository, $subscription_helper ); @@ -248,9 +235,6 @@ class OrderEndpointTest extends TestCase $logger->shouldNotReceive('log'); $logger->shouldReceive('debug'); $applicationContextRepository = Mockery::mock(ApplicationContextRepository::class); - $paypalRequestIdRepository = Mockery::mock(PayPalRequestIdRepository::class); - $paypalRequestIdRepository - ->expects('get_for_order')->with($orderToCapture)->andReturn('uniqueRequestId'); $subscription_helper = Mockery::mock(SubscriptionHelper::class); $testee = new OrderEndpoint( @@ -261,7 +245,6 @@ class OrderEndpointTest extends TestCase $intent, $logger, $applicationContextRepository, - $paypalRequestIdRepository, $subscription_helper ); @@ -317,7 +300,6 @@ class OrderEndpointTest extends TestCase $logger = Mockery::mock(LoggerInterface::class); $logger->shouldNotReceive('log'); $applicationContextRepository = Mockery::mock(ApplicationContextRepository::class); - $paypalRequestIdRepository = Mockery::mock(PayPalRequestIdRepository::class); $subscription_helper = Mockery::mock(SubscriptionHelper::class); $testee = new OrderEndpoint( @@ -328,7 +310,6 @@ class OrderEndpointTest extends TestCase $intent, $logger, $applicationContextRepository, - $paypalRequestIdRepository, $subscription_helper ); @@ -360,9 +341,6 @@ class OrderEndpointTest extends TestCase $logger->shouldReceive('log'); $logger->shouldReceive('debug'); $applicationContextRepository = Mockery::mock(ApplicationContextRepository::class); - $paypalRequestIdRepository = Mockery::mock(PayPalRequestIdRepository::class); - $paypalRequestIdRepository - ->expects('get_for_order')->with($orderToCapture)->andReturn('uniqueRequestId'); $subscription_helper = Mockery::mock(SubscriptionHelper::class); $testee = new OrderEndpoint( @@ -373,7 +351,6 @@ class OrderEndpointTest extends TestCase $intent, $logger, $applicationContextRepository, - $paypalRequestIdRepository, $subscription_helper ); $headers = Mockery::mock(Requests_Utility_CaseInsensitiveDictionary::class); @@ -412,9 +389,6 @@ class OrderEndpointTest extends TestCase $logger->shouldReceive('log'); $logger->shouldReceive('debug'); $applicationContextRepository = Mockery::mock(ApplicationContextRepository::class); - $paypalRequestIdRepository = Mockery::mock(PayPalRequestIdRepository::class); - $paypalRequestIdRepository - ->expects('get_for_order')->with($orderToCapture)->andReturn('uniqueRequestId'); $subscription_helper = Mockery::mock(SubscriptionHelper::class); $testee = new OrderEndpoint( @@ -425,7 +399,6 @@ class OrderEndpointTest extends TestCase $intent, $logger, $applicationContextRepository, - $paypalRequestIdRepository, $subscription_helper ); @@ -466,9 +439,6 @@ class OrderEndpointTest extends TestCase $logger->shouldNotReceive('log'); $logger->shouldReceive('debug'); $applicationContextRepository = Mockery::mock(ApplicationContextRepository::class); - $paypalRequestIdRepository = Mockery::mock(PayPalRequestIdRepository::class); - $paypalRequestIdRepository - ->expects('get_for_order')->with($orderToCapture)->andReturn('uniqueRequestId'); $subscription_helper = Mockery::mock(SubscriptionHelper::class); $testee = Mockery::mock( @@ -481,7 +451,6 @@ class OrderEndpointTest extends TestCase $intent, $logger, $applicationContextRepository, - $paypalRequestIdRepository, $subscription_helper, ] )->makePartial(); @@ -546,9 +515,6 @@ class OrderEndpointTest extends TestCase $logger->shouldNotReceive('log'); $logger->shouldReceive('debug'); $applicationContextRepository = Mockery::mock(ApplicationContextRepository::class); - $paypalRequestIdRepository = Mockery::mock(PayPalRequestIdRepository::class); - $paypalRequestIdRepository - ->expects('get_for_order')->with($orderToUpdate)->andReturn('uniqueRequestId'); $subscription_helper = Mockery::mock(SubscriptionHelper::class); $testee = Mockery::mock( @@ -561,7 +527,6 @@ class OrderEndpointTest extends TestCase $intent, $logger, $applicationContextRepository, - $paypalRequestIdRepository, $subscription_helper, ] )->makePartial(); @@ -588,9 +553,6 @@ class OrderEndpointTest extends TestCase if ($args['headers']['Prefer'] !== 'return=representation') { return false; } - if ($args['headers']['PayPal-Request-Id'] !== 'uniqueRequestId') { - return false; - } $body = json_decode($args['body']); if (! is_array($body) || $body[0] !== 'patch-1' || $body[1] !== 'patch-2') { return false; @@ -651,9 +613,6 @@ class OrderEndpointTest extends TestCase $logger->shouldReceive('log'); $logger->shouldReceive('debug'); $applicationContextRepository = Mockery::mock(ApplicationContextRepository::class); - $paypalRequestIdRepository = Mockery::mock(PayPalRequestIdRepository::class); - $paypalRequestIdRepository - ->expects('get_for_order')->with($orderToUpdate)->andReturn('uniqueRequestId'); $subscription_helper = Mockery::mock(SubscriptionHelper::class); $testee = new OrderEndpoint( @@ -664,7 +623,6 @@ class OrderEndpointTest extends TestCase $intent, $logger, $applicationContextRepository, - $paypalRequestIdRepository, $subscription_helper ); @@ -686,9 +644,6 @@ class OrderEndpointTest extends TestCase if ($args['headers']['Prefer'] !== 'return=representation') { return false; } - if ($args['headers']['PayPal-Request-Id'] !== 'uniqueRequestId') { - return false; - } $body = json_decode($args['body']); if (! is_array($body) || $body[0] !== 'patch-1' || $body[1] !== 'patch-2') { return false; @@ -750,9 +705,6 @@ class OrderEndpointTest extends TestCase $logger->shouldReceive('debug'); $applicationContextRepository = Mockery::mock(ApplicationContextRepository::class); - $paypalRequestIdRepository = Mockery::mock(PayPalRequestIdRepository::class); - $paypalRequestIdRepository - ->expects('get_for_order')->with($orderToUpdate)->andReturn('uniqueRequestId'); $subscription_helper = Mockery::mock(SubscriptionHelper::class); $testee = Mockery::mock( @@ -765,7 +717,6 @@ class OrderEndpointTest extends TestCase $intent, $logger, $applicationContextRepository, - $paypalRequestIdRepository, $subscription_helper ] )->makePartial(); @@ -788,9 +739,6 @@ class OrderEndpointTest extends TestCase if ($args['headers']['Prefer'] !== 'return=representation') { return false; } - if ($args['headers']['PayPal-Request-Id'] !== 'uniqueRequestId') { - return false; - } $body = json_decode($args['body']); if (! is_array($body) || $body[0] !== 'patch-1' || $body[1] !== 'patch-2') { return false; @@ -828,7 +776,6 @@ class OrderEndpointTest extends TestCase $logger = Mockery::mock(LoggerInterface::class); $logger->shouldNotReceive('log'); $applicationContextRepository = Mockery::mock(ApplicationContextRepository::class); - $paypalRequestIdRepository = Mockery::mock(PayPalRequestIdRepository::class); $subscription_helper = Mockery::mock(SubscriptionHelper::class); $testee = new OrderEndpoint( @@ -839,7 +786,6 @@ class OrderEndpointTest extends TestCase $intent, $logger, $applicationContextRepository, - $paypalRequestIdRepository, $subscription_helper ); @@ -889,15 +835,6 @@ class OrderEndpointTest extends TestCase ->expects('current_context') ->with(Matchers::identicalTo(ApplicationContext::SHIPPING_PREFERENCE_NO_SHIPPING)) ->andReturn($applicationContext); - $paypalRequestIdRepository = Mockery::mock(PayPalRequestIdRepository::class); - $paypalRequestIdRepository - ->expects('set_for_order')->andReturnUsing(function ($order, $id) use ($expectedOrder) : bool { - if ($order !== $expectedOrder) { - return false; - } - - return strpos($id, 'ppcp') !== false; - }); $subscription_helper = Mockery::mock(SubscriptionHelper::class); $subscription_helper->shouldReceive('cart_contains_subscription')->andReturn(true); @@ -909,7 +846,6 @@ class OrderEndpointTest extends TestCase $intent, $logger, $applicationContextRepository, - $paypalRequestIdRepository, $subscription_helper ); @@ -998,15 +934,6 @@ class OrderEndpointTest extends TestCase ->expects('current_context') ->with(Matchers::identicalTo(ApplicationContext::SHIPPING_PREFERENCE_GET_FROM_FILE)) ->andReturn($applicationContext); - $paypalRequestIdRepository = Mockery::mock(PayPalRequestIdRepository::class); - $paypalRequestIdRepository - ->expects('set_for_order')->andReturnUsing(function ($order, $id) use ($expectedOrder) : bool { - if ($order !== $expectedOrder) { - return false; - } - - return strpos($id, 'ppcp') !== false; - }); $subscription_helper = Mockery::mock(SubscriptionHelper::class); $subscription_helper->shouldReceive('cart_contains_subscription')->andReturn(true); @@ -1018,7 +945,6 @@ class OrderEndpointTest extends TestCase $intent, $logger, $applicationContextRepository, - $paypalRequestIdRepository, $subscription_helper ); @@ -1084,7 +1010,6 @@ class OrderEndpointTest extends TestCase ->expects('current_context') ->with(Matchers::identicalTo(ApplicationContext::SHIPPING_PREFERENCE_NO_SHIPPING)) ->andReturn($applicationContext); - $paypalRequestIdRepository = Mockery::mock(PayPalRequestIdRepository::class); $subscription_helper = Mockery::mock(SubscriptionHelper::class); $subscription_helper->shouldReceive('cart_contains_subscription')->andReturn(true); @@ -1096,7 +1021,6 @@ class OrderEndpointTest extends TestCase $intent, $logger, $applicationContextRepository, - $paypalRequestIdRepository, $subscription_helper ); @@ -1173,7 +1097,6 @@ class OrderEndpointTest extends TestCase ->expects('current_context') ->with(Matchers::identicalTo(ApplicationContext::SHIPPING_PREFERENCE_GET_FROM_FILE)) ->andReturn($applicationContext); - $paypalRequestIdRepository = Mockery::mock(PayPalRequestIdRepository::class); $subscription_helper = Mockery::mock(SubscriptionHelper::class); $subscription_helper->shouldReceive('cart_contains_subscription')->andReturn(true); @@ -1185,7 +1108,6 @@ class OrderEndpointTest extends TestCase $intent, $logger, $applicationContextRepository, - $paypalRequestIdRepository, $subscription_helper ); @@ -1229,3 +1151,4 @@ class OrderEndpointTest extends TestCase $testee->create([$purchaseUnit], ApplicationContext::SHIPPING_PREFERENCE_GET_FROM_FILE, $payer); } } + diff --git a/tests/PHPUnit/ApiClient/Repository/PayPalRequestIdRepositoryTest.php b/tests/PHPUnit/ApiClient/Repository/PayPalRequestIdRepositoryTest.php deleted file mode 100644 index f27b0ed3e..000000000 --- a/tests/PHPUnit/ApiClient/Repository/PayPalRequestIdRepositoryTest.php +++ /dev/null @@ -1,58 +0,0 @@ -testee = new PayPalRequestIdRepository(); - - when('get_option')->alias(function () { - return $this->data; - }); - when('update_option')->alias(function (string $key, array $data) { - $this->data = $data; - }); - } - - public function testForOrder() - { - $this->testee->set_for_order($this->createPaypalOrder('42'), 'request1'); - $this->testee->set_for_order($this->createPaypalOrder('43'), 'request2'); - - self::assertEquals('request1', $this->testee->get_for_order($this->createPaypalOrder('42'))); - self::assertEquals('request2', $this->testee->get_for_order($this->createPaypalOrder('43'))); - self::assertEquals('', $this->testee->get_for_order($this->createPaypalOrder('41'))); - } - - public function testExpiration() - { - $this->testee->set_for_order($this->createPaypalOrder('42'), 'request1'); - $this->data['42']['expiration'] = time() - 1; - $this->testee->set_for_order($this->createPaypalOrder('43'), 'request2'); - - self::assertEquals('', $this->testee->get_for_order($this->createPaypalOrder('42'))); - self::assertEquals('request2', $this->testee->get_for_order($this->createPaypalOrder('43'))); - } - - private function createPaypalOrder(string $id): Order { - $order = Mockery::mock(Order::class); - $order - ->shouldReceive('id') - ->andReturn($id); - return $order; - } -} From 83c3dca560aa84e9b5d61f1158b24bc98f234c50 Mon Sep 17 00:00:00 2001 From: emilicastells Date: Mon, 2 Jan 2023 15:08:23 +0100 Subject: [PATCH 02/35] Do not check signature type on filter --- modules/ppcp-wc-gateway/src/Gateway/OXXO/OXXO.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-wc-gateway/src/Gateway/OXXO/OXXO.php b/modules/ppcp-wc-gateway/src/Gateway/OXXO/OXXO.php index 2c0034ef4..da512fd2c 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/OXXO/OXXO.php +++ b/modules/ppcp-wc-gateway/src/Gateway/OXXO/OXXO.php @@ -63,7 +63,15 @@ class OXXO { add_filter( 'woocommerce_available_payment_gateways', - function ( array $methods ): array { + /** + * Param types removed to avoid third-party issues. + * + * @psalm-suppress MissingClosureParamType + */ + function ( $methods ) { + if ( ! is_array( $methods ) ) { + return $methods; + } if ( ! $this->checkout_allowed_for_oxxo() ) { unset( $methods[ OXXOGateway::ID ] ); From 143be438ead4cd76c65a57c297332d23f515d5c4 Mon Sep 17 00:00:00 2001 From: Alex P Date: Fri, 13 Jan 2023 16:50:24 +0200 Subject: [PATCH 03/35] Remove .gitignore and other files during package build in all subdirs --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 49a410f3f..1fa2df72e 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "prearchive": "rm -rf $npm_package_name.zip", "archive": "zip -r $npm_package_name.zip . -x **.git/\\* **node_modules/\\*", "postarchive": "yarn run archive:cleanup && rm -rf $npm_package_name && unzip $npm_package_name.zip -d $npm_package_name && rm $npm_package_name.zip && zip -r $npm_package_name.zip $npm_package_name && rm -rf $npm_package_name", - "archive:cleanup": "zip -d $npm_package_name.zip .env* .ddev/\\* .editorconfig tests/\\* .github/\\* .psalm/\\* wordpress_org_assets/\\* \\*.DS_Store README.md .gitattributes .gitignore .travis.yml composer.json composer.lock package.json package-lock.json patchwork.json yarn.lock phpunit.xml.dist .phpunit.result.cache phpcs.xml.dist modules/ppcp-button/.babelrc modules/ppcp-button/package.json modules/ppcp-button/webpack.config.js modules/ppcp-button/yarn.lock vendor/\\*/.idea/\\* vendor/\\*/.gitignore vendor/\\*/.gitattributes vendor/\\*/.travis.yml" + "archive:cleanup": "zip -d $npm_package_name.zip .env* .ddev/\\* \\*.idea/\\* .editorconfig tests/\\* .github/\\* .psalm/\\* wordpress_org_assets/\\* \\*.DS_Store \\*README.md \\*.gitattributes \\*.gitignore \\*composer.json \\*composer.lock patchwork.json phpunit.xml.dist .phpunit.result.cache phpcs.xml* psalm*.xml* \\*.babelrc \\*package.json \\*webpack.config.js \\*yarn.lock \\*.travis.yml" }, "config": { "wp_org_slug": "woocommerce-paypal-payments" From 05237ca93ed70ae5610bd34c606e1bdcfde2901d Mon Sep 17 00:00:00 2001 From: emilicastells Date: Tue, 17 Jan 2023 15:09:36 +0100 Subject: [PATCH 04/35] Remove request id repo from uninstall module --- composer.lock | 426 ++-------------------------- modules/ppcp-uninstall/services.php | 1 - 2 files changed, 28 insertions(+), 399 deletions(-) diff --git a/composer.lock b/composer.lock index e377dddce..ce4259243 100644 --- a/composer.lock +++ b/composer.lock @@ -35,10 +35,6 @@ ], "description": "Promoting container interoperability through standard service providers", "homepage": "https://github.com/container-interop/service-provider", - "support": { - "issues": "https://github.com/container-interop/service-provider/issues", - "source": "https://github.com/container-interop/service-provider/tree/master" - }, "time": "2017-09-20T14:13:36+00:00" }, { @@ -90,10 +86,6 @@ } ], "description": "Interfaces for human readable string interoperation.", - "support": { - "issues": "https://github.com/Dhii/human-readable-interface/issues", - "source": "https://github.com/Dhii/human-readable-interface/tree/v0.2.0-alpha1" - }, "time": "2021-03-05T00:36:01+00:00" }, { @@ -142,10 +134,6 @@ } ], "description": "Interfaces for modules", - "support": { - "issues": "https://github.com/Dhii/module-interface/issues", - "source": "https://github.com/Dhii/module-interface/tree/v0.3.0-alpha2" - }, "time": "2021-08-23T08:23:01+00:00" }, { @@ -189,10 +177,6 @@ } ], "description": "Interfaces for package-related interop", - "support": { - "issues": "https://github.com/Dhii/package-interface/issues", - "source": "https://github.com/Dhii/package-interface/tree/v0.1.0-alpha4" - }, "time": "2021-12-08T15:57:36+00:00" }, { @@ -241,10 +225,6 @@ } ], "description": "A base interface for validators", - "support": { - "issues": "https://github.com/Dhii/validation-interface/issues", - "source": "https://github.com/Dhii/validation-interface/tree/v0.3.0-alpha3" - }, "time": "2021-01-14T16:19:20+00:00" }, { @@ -288,10 +268,6 @@ } ], "description": "Implementation for dealing with SemVer-compliant versions", - "support": { - "issues": "https://github.com/Dhii/versions/issues", - "source": "https://github.com/Dhii/versions/tree/v0.1.0-alpha3" - }, "time": "2021-12-08T16:54:50+00:00" }, { @@ -336,10 +312,6 @@ "container-interop", "psr" ], - "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/1.1.1" - }, "time": "2021-03-05T17:36:06+00:00" }, { @@ -387,9 +359,6 @@ "psr", "psr-3" ], - "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" - }, "time": "2021-05-03T11:20:27+00:00" }, { @@ -430,10 +399,6 @@ } ], "description": "A polyfill for getallheaders.", - "support": { - "issues": "https://github.com/ralouphie/getallheaders/issues", - "source": "https://github.com/ralouphie/getallheaders/tree/develop" - }, "time": "2019-03-08T08:55:37+00:00" }, { @@ -500,9 +465,6 @@ "portable", "shim" ], - "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" - }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -566,10 +528,6 @@ } ], "description": "Composer plugin to merge multiple composer.json files", - "support": { - "issues": "https://github.com/wikimedia/composer-merge-plugin/issues", - "source": "https://github.com/wikimedia/composer-merge-plugin/tree/v2.0.1" - }, "time": "2021-02-24T05:28:06+00:00" }, { @@ -618,10 +576,6 @@ } ], "description": "Interfaces for interop within WordPress", - "support": { - "issues": "https://github.com/wp-oop/wordpress-interface/issues", - "source": "https://github.com/wp-oop/wordpress-interface/tree/v0.1.0-alpha2" - }, "time": "2021-04-30T09:37:37+00:00" } ], @@ -702,11 +656,6 @@ "non-blocking", "promise" ], - "support": { - "irc": "irc://irc.freenode.org/amphp", - "issues": "https://github.com/amphp/amp/issues", - "source": "https://github.com/amphp/amp/tree/v2.6.2" - }, "funding": [ { "url": "https://github.com/amphp", @@ -748,12 +697,12 @@ } }, "autoload": { - "files": [ - "lib/functions.php" - ], "psr-4": { "Amp\\ByteStream\\": "lib" - } + }, + "files": [ + "lib/functions.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -779,11 +728,6 @@ "non-blocking", "stream" ], - "support": { - "irc": "irc://irc.freenode.org/amphp", - "issues": "https://github.com/amphp/byte-stream/issues", - "source": "https://github.com/amphp/byte-stream/tree/v1.8.1" - }, "funding": [ { "url": "https://github.com/amphp", @@ -834,10 +778,6 @@ "runkit", "testing" ], - "support": { - "issues": "https://github.com/antecedent/patchwork/issues", - "source": "https://github.com/antecedent/patchwork/tree/2.1.21" - }, "time": "2022-02-07T07:28:34+00:00" }, { @@ -904,10 +844,6 @@ "test", "testing" ], - "support": { - "issues": "https://github.com/Brain-WP/BrainMonkey/issues", - "source": "https://github.com/Brain-WP/BrainMonkey" - }, "time": "2021-11-11T15:53:55+00:00" }, { @@ -963,10 +899,6 @@ } ], "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", - "support": { - "issues": "https://github.com/composer/package-versions-deprecated/issues", - "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.5" - }, "funding": [ { "url": "https://packagist.com", @@ -1034,10 +966,6 @@ "regex", "regular expression" ], - "support": { - "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/2.1.0" - }, "funding": [ { "url": "https://packagist.com", @@ -1114,11 +1042,6 @@ "validation", "versioning" ], - "support": { - "irc": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.3.2" - }, "funding": [ { "url": "https://packagist.com", @@ -1180,11 +1103,6 @@ "Xdebug", "performance" ], - "support": { - "irc": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/xdebug-handler/issues", - "source": "https://github.com/composer/xdebug-handler/tree/3.0.3" - }, "funding": [ { "url": "https://packagist.com", @@ -1270,10 +1188,6 @@ "stylecheck", "tests" ], - "support": { - "issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues", - "source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer" - }, "time": "2022-02-04T12:51:07+00:00" }, { @@ -1325,10 +1239,6 @@ } ], "description": "A highly ISP-compliant collection of interfaces that represent maps and lists.", - "support": { - "issues": "https://github.com/Dhii/collections-interface/issues", - "source": "https://github.com/Dhii/collections-interface/tree/v0.3.0" - }, "time": "2021-10-06T10:56:09+00:00" }, { @@ -1385,10 +1295,6 @@ "PSR-11", "container" ], - "support": { - "issues": "https://github.com/Dhii/containers/issues", - "source": "https://github.com/Dhii/containers/tree/v0.1.4" - }, "time": "2021-10-06T11:13:51+00:00" }, { @@ -1422,38 +1328,34 @@ "MIT" ], "description": "implementation of xdg base directory specification for php", - "support": { - "issues": "https://github.com/dnoegel/php-xdg-base-dir/issues", - "source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1" - }, "time": "2019-12-04T15:06:13+00:00" }, { "name": "doctrine/instantiator", - "version": "1.4.1", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc" + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc", - "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^9", + "doctrine/coding-standard": "^9 || ^11", "ext-pdo": "*", "ext-phar": "*", "phpbench/phpbench": "^0.16 || ^1", "phpstan/phpstan": "^1.4", "phpstan/phpstan-phpunit": "^1", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.22" + "vimeo/psalm": "^4.30 || ^5.4" }, "type": "library", "autoload": { @@ -1478,10 +1380,6 @@ "constructor", "instantiate" ], - "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.4.1" - }, "funding": [ { "url": "https://www.doctrine-project.org/sponsorship.html", @@ -1496,7 +1394,7 @@ "type": "tidelift" } ], - "time": "2022-03-03T08:28:38+00:00" + "time": "2022-12-30T00:15:36+00:00" }, { "name": "felixfbecker/advanced-json-rpc", @@ -1537,10 +1435,6 @@ } ], "description": "A more advanced JSONRPC implementation", - "support": { - "issues": "https://github.com/felixfbecker/php-advanced-json-rpc/issues", - "source": "https://github.com/felixfbecker/php-advanced-json-rpc/tree/v3.2.1" - }, "time": "2021-06-11T22:34:44+00:00" }, { @@ -1593,10 +1487,6 @@ "php", "server" ], - "support": { - "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", - "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.2" - }, "time": "2022-03-02T22:36:06+00:00" }, { @@ -1645,10 +1535,6 @@ "Result-Type", "result" ], - "support": { - "issues": "https://github.com/GrahamCampbell/Result-Type/issues", - "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.0" - }, "funding": [ { "url": "https://github.com/GrahamCampbell", @@ -1706,10 +1592,6 @@ "keywords": [ "test" ], - "support": { - "issues": "https://github.com/hamcrest/hamcrest-php/issues", - "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1" - }, "time": "2020-07-09T08:09:16+00:00" }, { @@ -1775,10 +1657,6 @@ "test double", "testing" ], - "support": { - "issues": "https://github.com/mockery/mockery/issues", - "source": "https://github.com/mockery/mockery/tree/1.3.6" - }, "time": "2022-09-07T15:05:49+00:00" }, { @@ -1828,10 +1706,6 @@ "object", "object graph" ], - "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" - }, "funding": [ { "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", @@ -1884,25 +1758,20 @@ } ], "description": "Map nested JSON structures onto PHP classes", - "support": { - "email": "cweiske@cweiske.de", - "issues": "https://github.com/cweiske/jsonmapper/issues", - "source": "https://github.com/cweiske/jsonmapper/tree/v4.1.0" - }, "time": "2022-12-08T20:46:14+00:00" }, { "name": "nikic/php-parser", - "version": "v4.15.2", + "version": "v4.15.3", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc" + "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc", - "reference": "f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/570e980a201d8ed0236b0a62ddf2c9cbb2034039", + "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039", "shasum": "" }, "require": { @@ -1941,11 +1810,7 @@ "parser", "php" ], - "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.2" - }, - "time": "2022-11-12T15:38:23+00:00" + "time": "2023-01-16T22:05:37+00:00" }, { "name": "openlss/lib-array2xml", @@ -1994,10 +1859,6 @@ "xml", "xml conversion" ], - "support": { - "issues": "https://github.com/nullivex/lib-array2xml/issues", - "source": "https://github.com/nullivex/lib-array2xml/tree/master" - }, "time": "2019-03-29T20:06:56+00:00" }, { @@ -2054,10 +1915,6 @@ } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "support": { - "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.3" - }, "time": "2021-07-20T11:28:43+00:00" }, { @@ -2105,10 +1962,6 @@ } ], "description": "Library for handling version information and constraints", - "support": { - "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.2.1" - }, "time": "2022-02-21T01:04:05+00:00" }, { @@ -2149,10 +2002,6 @@ "woocommerce", "wordpress" ], - "support": { - "issues": "https://github.com/php-stubs/woocommerce-stubs/issues", - "source": "https://github.com/php-stubs/woocommerce-stubs/tree/v5.9.1" - }, "time": "2022-04-30T06:35:48+00:00" }, { @@ -2196,10 +2045,6 @@ "static analysis", "wordpress" ], - "support": { - "issues": "https://github.com/php-stubs/wordpress-stubs/issues", - "source": "https://github.com/php-stubs/wordpress-stubs/tree/v5.9.5" - }, "time": "2022-11-09T05:32:14+00:00" }, { @@ -2258,10 +2103,6 @@ "phpcs", "standards" ], - "support": { - "issues": "https://github.com/PHPCompatibility/PHPCompatibility/issues", - "source": "https://github.com/PHPCompatibility/PHPCompatibility" - }, "time": "2019-12-27T09:44:58+00:00" }, { @@ -2315,10 +2156,6 @@ "standards", "static analysis" ], - "support": { - "issues": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie/issues", - "source": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie" - }, "time": "2022-10-25T01:46:02+00:00" }, { @@ -2370,10 +2207,6 @@ "static analysis", "wordpress" ], - "support": { - "issues": "https://github.com/PHPCompatibility/PHPCompatibilityWP/issues", - "source": "https://github.com/PHPCompatibility/PHPCompatibilityWP" - }, "time": "2022-10-24T09:00:36+00:00" }, { @@ -2423,10 +2256,6 @@ "reflection", "static analysis" ], - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" - }, "time": "2020-06-27T09:03:43+00:00" }, { @@ -2480,10 +2309,6 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" - }, "time": "2021-10-19T17:43:47+00:00" }, { @@ -2530,10 +2355,6 @@ } ], "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.1" - }, "time": "2022-03-15T21:29:03+00:00" }, { @@ -2595,10 +2416,6 @@ "php", "type" ], - "support": { - "issues": "https://github.com/schmittjoh/php-option/issues", - "source": "https://github.com/schmittjoh/php-option/tree/1.9.0" - }, "funding": [ { "url": "https://github.com/GrahamCampbell", @@ -2672,10 +2489,6 @@ "testing", "xunit" ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/7.0.15" - }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -2732,10 +2545,6 @@ "filesystem", "iterator" ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/2.0.5" - }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -2783,10 +2592,6 @@ "keywords": [ "template" ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/1.2.1" - }, "time": "2015-06-21T13:50:34+00:00" }, { @@ -2836,16 +2641,6 @@ "keywords": [ "timer" ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/2.1.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2020-11-30T08:20:02+00:00" }, { @@ -2895,16 +2690,6 @@ "keywords": [ "tokenizer" ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-token-stream/issues", - "source": "https://github.com/sebastianbergmann/php-token-stream/tree/3.1.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "abandoned": true, "time": "2021-07-26T12:15:06+00:00" }, @@ -2985,10 +2770,6 @@ "testing", "xunit" ], - "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/8.5.31" - }, "funding": [ { "url": "https://phpunit.de/sponsors.html", @@ -3048,16 +2829,6 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2020-11-30T08:15:22+00:00" }, { @@ -3122,10 +2893,6 @@ "compare", "equality" ], - "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/3.0.5" - }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -3188,16 +2955,6 @@ "unidiff", "unified diff" ], - "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/3.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2020-11-30T07:59:04+00:00" }, { @@ -3251,16 +3008,6 @@ "environment", "hhvm" ], - "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/4.2.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2020-11-30T07:53:42+00:00" }, { @@ -3328,10 +3075,6 @@ "export", "exporter" ], - "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.5" - }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -3392,10 +3135,6 @@ "keywords": [ "global state" ], - "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/3.0.2" - }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -3449,16 +3188,6 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2020-11-30T07:40:27+00:00" }, { @@ -3504,16 +3233,6 @@ ], "description": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2020-11-30T07:37:18+00:00" }, { @@ -3567,16 +3286,6 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2020-11-30T07:34:24+00:00" }, { @@ -3619,16 +3328,6 @@ ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/2.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2020-11-30T07:30:19+00:00" }, { @@ -3675,10 +3374,6 @@ ], "description": "Collection of value objects that represent the types of the PHP type system", "homepage": "https://github.com/sebastianbergmann/type", - "support": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/1.1.4" - }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -3728,10 +3423,6 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", - "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/master" - }, "time": "2016-10-03T07:35:21+00:00" }, { @@ -3783,25 +3474,20 @@ "phpcs", "standards" ], - "support": { - "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", - "source": "https://github.com/squizlabs/PHP_CodeSniffer", - "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" - }, "time": "2022-06-18T07:21:10+00:00" }, { "name": "symfony/console", - "version": "v5.4.16", + "version": "v5.4.17", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "8e9b9c8dfb33af6057c94e1b44846bee700dc5ef" + "reference": "58422fdcb0e715ed05b385f70d3e8b5ed4bbd45f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/8e9b9c8dfb33af6057c94e1b44846bee700dc5ef", - "reference": "8e9b9c8dfb33af6057c94e1b44846bee700dc5ef", + "url": "https://api.github.com/repos/symfony/console/zipball/58422fdcb0e715ed05b385f70d3e8b5ed4bbd45f", + "reference": "58422fdcb0e715ed05b385f70d3e8b5ed4bbd45f", "shasum": "" }, "require": { @@ -3870,9 +3556,6 @@ "console", "terminal" ], - "support": { - "source": "https://github.com/symfony/console/tree/v5.4.16" - }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3887,7 +3570,7 @@ "type": "tidelift" } ], - "time": "2022-11-25T14:09:27+00:00" + "time": "2022-12-28T14:15:31+00:00" }, { "name": "symfony/deprecation-contracts", @@ -3937,9 +3620,6 @@ ], "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.2" - }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4019,9 +3699,6 @@ "polyfill", "portable" ], - "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" - }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4100,9 +3777,6 @@ "portable", "shim" ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0" - }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4184,9 +3858,6 @@ "portable", "shim" ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0" - }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4267,9 +3938,6 @@ "portable", "shim" ], - "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" - }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4346,9 +4014,6 @@ "portable", "shim" ], - "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.27.0" - }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4429,9 +4094,6 @@ "interoperability", "standards" ], - "support": { - "source": "https://github.com/symfony/service-contracts/tree/v2.5.2" - }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4450,16 +4112,16 @@ }, { "name": "symfony/string", - "version": "v5.4.15", + "version": "v5.4.17", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "571334ce9f687e3e6af72db4d3b2a9431e4fd9ed" + "reference": "55733a8664b8853b003e70251c58bc8cb2d82a6b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/571334ce9f687e3e6af72db4d3b2a9431e4fd9ed", - "reference": "571334ce9f687e3e6af72db4d3b2a9431e4fd9ed", + "url": "https://api.github.com/repos/symfony/string/zipball/55733a8664b8853b003e70251c58bc8cb2d82a6b", + "reference": "55733a8664b8853b003e70251c58bc8cb2d82a6b", "shasum": "" }, "require": { @@ -4515,9 +4177,6 @@ "utf-8", "utf8" ], - "support": { - "source": "https://github.com/symfony/string/tree/v5.4.15" - }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4532,7 +4191,7 @@ "type": "tidelift" } ], - "time": "2022-10-05T15:16:54+00:00" + "time": "2022-12-12T15:54:21+00:00" }, { "name": "theseer/tokenizer", @@ -4572,10 +4231,6 @@ } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "support": { - "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.1" - }, "funding": [ { "url": "https://github.com/theseer", @@ -4686,10 +4341,6 @@ "inspection", "php" ], - "support": { - "issues": "https://github.com/vimeo/psalm/issues", - "source": "https://github.com/vimeo/psalm/tree/4.30.0" - }, "time": "2022-11-06T20:37:08+00:00" }, { @@ -4760,10 +4411,6 @@ "env", "environment" ], - "support": { - "issues": "https://github.com/vlucas/phpdotenv/issues", - "source": "https://github.com/vlucas/phpdotenv/tree/v5.5.0" - }, "funding": [ { "url": "https://github.com/GrahamCampbell", @@ -4828,10 +4475,6 @@ "check", "validate" ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" - }, "time": "2022-06-03T18:03:27+00:00" }, { @@ -4878,10 +4521,6 @@ } ], "description": "A robust cross-platform utility for normalizing, comparing and modifying file paths.", - "support": { - "issues": "https://github.com/webmozart/path-util/issues", - "source": "https://github.com/webmozart/path-util/tree/2.3.0" - }, "abandoned": "symfony/filesystem", "time": "2015-12-17T08:42:14+00:00" }, @@ -4923,10 +4562,6 @@ "woocommerce", "wordpress" ], - "support": { - "issues": "https://github.com/woocommerce/woocommerce-sniffs/issues", - "source": "https://github.com/woocommerce/woocommerce-sniffs/tree/0.1.3" - }, "time": "2022-02-17T15:34:51+00:00" }, { @@ -4973,11 +4608,6 @@ "standards", "wordpress" ], - "support": { - "issues": "https://github.com/WordPress/WordPress-Coding-Standards/issues", - "source": "https://github.com/WordPress/WordPress-Coding-Standards", - "wiki": "https://github.com/WordPress/WordPress-Coding-Standards/wiki" - }, "time": "2020-05-13T23:57:56+00:00" } ], @@ -4990,9 +4620,9 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": "^7.2 | ^8.0", + "php": "^7.2 | ^8.0, ^7.2 | ^8.0, ^7.2 | ^8.0, ^7.2 | ^8.0, ^7.2 | ^8.0, ^7.2 | ^8.0, ^7.2 | ^8.0, ^7.2 | ^8.0, ^7.2 | ^8.0, ^7.2 | ^8.0, ^7.2 | ^8.0, ^7.2 | ^8.0, ^7.2 | ^8.0, ^7.2 | ^8.0, ^7.2 | ^8.0", "ext-json": "*" }, "platform-dev": [], - "plugin-api-version": "2.3.0" + "plugin-api-version": "1.1.0" } diff --git a/modules/ppcp-uninstall/services.php b/modules/ppcp-uninstall/services.php index 472b64244..054a0d9dc 100644 --- a/modules/ppcp-uninstall/services.php +++ b/modules/ppcp-uninstall/services.php @@ -24,7 +24,6 @@ return array( 'uninstall.ppcp-all-option-names' => function( ContainerInterface $container ) : array { return array( $container->get( 'webhook.last-webhook-storage.key' ), - PayPalRequestIdRepository::KEY, 'woocommerce_ppcp-is_pay_later_settings_migrated', 'woocommerce_' . PayPalGateway::ID . '_settings', 'woocommerce_' . CreditCardGateway::ID . '_settings', From 93edabd4eb5e6aeabb7c0a631cf9acc2881f31c6 Mon Sep 17 00:00:00 2001 From: emilicastells Date: Tue, 17 Jan 2023 15:13:35 +0100 Subject: [PATCH 05/35] Update changelog --- changelog.txt | 1 + readme.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/changelog.txt b/changelog.txt index 714e79634..66dc6830a 100644 --- a/changelog.txt +++ b/changelog.txt @@ -3,6 +3,7 @@ = 2.0.2 - TBD = * Fix - Do not call PayPal get order by ID if it does not exist #1029 * Fix - Type check error conflict with German Market #1056 +* Fix - Backend Storage for the PayPalRequestIdRepository does not scale #983 * Enhancement - Remove plugin data after uninstalling #1075 * Enhancement - Add FraudNet to all payments #1040 * Enhancement - Update "Standard Payments" tab settings #1065 diff --git a/readme.txt b/readme.txt index ad9bcb4dc..eda478f81 100644 --- a/readme.txt +++ b/readme.txt @@ -84,6 +84,7 @@ Follow the steps below to connect the plugin to your PayPal account: = 2.0.2 = * Fix - Do not call PayPal get order by ID if it does not exist #1029 * Fix - Type check error conflict with German Market #1056 +* Fix - Backend Storage for the PayPalRequestIdRepository does not scale #983 * Enhancement - Remove plugin data after uninstalling #1075 * Enhancement - Add FraudNet to all payments #1040 * Enhancement - Update "Standard Payments" tab settings #1065 From 055ffa9370e4aceff3361c84475769f49dd76894 Mon Sep 17 00:00:00 2001 From: emilicastells Date: Wed, 18 Jan 2023 14:39:27 +0100 Subject: [PATCH 06/35] Ensure `WC()->payment_gateways` is not null --- modules/ppcp-wc-gateway/src/Checkout/DisableGateways.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/ppcp-wc-gateway/src/Checkout/DisableGateways.php b/modules/ppcp-wc-gateway/src/Checkout/DisableGateways.php index ad60cb9fb..bbd9b30d3 100644 --- a/modules/ppcp-wc-gateway/src/Checkout/DisableGateways.php +++ b/modules/ppcp-wc-gateway/src/Checkout/DisableGateways.php @@ -99,6 +99,10 @@ class DisableGateways { * @return bool */ private function disable_all_gateways() : bool { + if ( is_null( WC()->payment_gateways ) ) { + return false; + } + foreach ( WC()->payment_gateways->payment_gateways() as $gateway ) { if ( PayPalGateway::ID === $gateway->id && $gateway->enabled !== 'yes' ) { return true; From e0d8715d0c205db0ce3dc74bbe793979b03e8dc1 Mon Sep 17 00:00:00 2001 From: emilicastells Date: Wed, 18 Jan 2023 15:06:54 +0100 Subject: [PATCH 07/35] Update changelog --- changelog.txt | 1 + readme.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/changelog.txt b/changelog.txt index 66dc6830a..39ecc5aa1 100644 --- a/changelog.txt +++ b/changelog.txt @@ -4,6 +4,7 @@ * Fix - Do not call PayPal get order by ID if it does not exist #1029 * Fix - Type check error conflict with German Market #1056 * Fix - Backend Storage for the PayPalRequestIdRepository does not scale #983 +* Fix - Ensure WC()->payment_gateways is not null #1128 * Enhancement - Remove plugin data after uninstalling #1075 * Enhancement - Add FraudNet to all payments #1040 * Enhancement - Update "Standard Payments" tab settings #1065 diff --git a/readme.txt b/readme.txt index eda478f81..c2a417e3e 100644 --- a/readme.txt +++ b/readme.txt @@ -85,6 +85,7 @@ Follow the steps below to connect the plugin to your PayPal account: * Fix - Do not call PayPal get order by ID if it does not exist #1029 * Fix - Type check error conflict with German Market #1056 * Fix - Backend Storage for the PayPalRequestIdRepository does not scale #983 +* Fix - Ensure WC()->payment_gateways is not null #1128 * Enhancement - Remove plugin data after uninstalling #1075 * Enhancement - Add FraudNet to all payments #1040 * Enhancement - Update "Standard Payments" tab settings #1065 From ea201a314020df3a4d12cb6bf03396ddcc734dd5 Mon Sep 17 00:00:00 2001 From: emilicastells Date: Fri, 20 Jan 2023 10:58:43 +0100 Subject: [PATCH 08/35] Disable Fraudnet checkbox when PUI is enabled --- modules/ppcp-wc-gateway/services.php | 10 ++++++++++ .../src/Gateway/PayUponInvoice/PayUponInvoice.php | 5 +++++ .../src/Settings/Fields/connection-tab-fields.php | 1 + 3 files changed, 16 insertions(+) diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index 9515e7862..5c6591632 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -1147,6 +1147,16 @@ return array( return false; }, + 'wcgateway.settings.should-disable-fraudnet-checkbox' => static function( ContainerInterface $container ): bool { + $pui_helper = $container->get( 'wcgateway.pay-upon-invoice-helper' ); + assert( $pui_helper instanceof PayUponInvoiceHelper ); + + if ( $pui_helper->is_pui_gateway_enabled() ) { + return true; + } + + return false; + }, 'wcgateway.settings.tracking-label' => static function ( ContainerInterface $container ): string { $tracking_label = sprintf( // translators: %1$s and %2$s are the opening and closing of HTML tag. diff --git a/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoice.php b/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoice.php index f60e81b75..18ba06048 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoice.php +++ b/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoice.php @@ -139,6 +139,11 @@ class PayUponInvoice { * @throws NotFoundException When setting is not found. */ public function init(): void { + if ( $this->pui_helper->is_pui_gateway_enabled() ) { + $this->settings->set( 'fraudnet_enabled', true ); + $this->settings->persist(); + } + add_filter( 'ppcp_partner_referrals_data', function ( array $data ): array { diff --git a/modules/ppcp-wc-gateway/src/Settings/Fields/connection-tab-fields.php b/modules/ppcp-wc-gateway/src/Settings/Fields/connection-tab-fields.php index 4a16aca77..06c3b826c 100644 --- a/modules/ppcp-wc-gateway/src/Settings/Fields/connection-tab-fields.php +++ b/modules/ppcp-wc-gateway/src/Settings/Fields/connection-tab-fields.php @@ -411,6 +411,7 @@ return function ( ContainerInterface $container, array $fields ): array { ), 'requirements' => array(), 'gateway' => Settings::CONNECTION_TAB_ID, + 'input_class' => $container->get( 'wcgateway.settings.should-disable-fraudnet-checkbox' ) ? array( 'ppcp-disabled-checkbox' ) : array(), ), 'credentials_integration_configuration_heading' => array( From dba6d093a4c64a1269d1e294b85e3b730e2fd1a8 Mon Sep 17 00:00:00 2001 From: emilicastells Date: Mon, 23 Jan 2023 13:25:37 +0100 Subject: [PATCH 09/35] Update changelog --- changelog.txt | 1 + readme.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/changelog.txt b/changelog.txt index 39ecc5aa1..b0b0525b3 100644 --- a/changelog.txt +++ b/changelog.txt @@ -9,6 +9,7 @@ * Enhancement - Add FraudNet to all payments #1040 * Enhancement - Update "Standard Payments" tab settings #1065 * Enhancement - Update PHP 7.2 requirement in all relevant files #1084 +* Enhancement - When PUI is enabled FraudNet should be also enabled #1129 = 2.0.1 - 2022-12-13 = * Fix - Error while syncing tracking data to PayPal -> Sync GZD Tracking #1020 diff --git a/readme.txt b/readme.txt index c2a417e3e..30f8123a3 100644 --- a/readme.txt +++ b/readme.txt @@ -90,6 +90,7 @@ Follow the steps below to connect the plugin to your PayPal account: * Enhancement - Add FraudNet to all payments #1040 * Enhancement - Update "Standard Payments" tab settings #1065 * Enhancement - Update PHP 7.2 requirement in all relevant files #1084 +* Enhancement - When PUI is enabled FraudNet should be also enabled #1129 = 2.0.1 = * Fix - Error while syncing tracking data to PayPal -> Sync GZD Tracking #1020 From 3c7f653ca9c7d501ac7d0563dd88dbf5c612e08d Mon Sep 17 00:00:00 2001 From: emilicastells Date: Mon, 23 Jan 2023 15:45:56 +0100 Subject: [PATCH 10/35] Add `PayPal-Request-Id` if payment source exist --- modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php b/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php index d4fa96385..fcb656dce 100644 --- a/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php +++ b/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php @@ -236,6 +236,10 @@ class OrderEndpoint { $args['headers']['PayPal-Client-Metadata-Id'] = $this->fraudnet->session_id(); } + if ( isset( $data['payment_source'] ) ) { + $args['headers']['PayPal-Request-Id'] = uniqid( 'ppcp-', true ); + } + $response = $this->request( $url, $args ); if ( is_wp_error( $response ) ) { $error = new RuntimeException( From fbaf97eb4f408e133663b8c12cbfa06064d95aac Mon Sep 17 00:00:00 2001 From: emilicastells Date: Mon, 23 Jan 2023 17:12:39 +0100 Subject: [PATCH 11/35] Update changelog --- changelog.txt | 1 + readme.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/changelog.txt b/changelog.txt index b0b0525b3..d89df2550 100644 --- a/changelog.txt +++ b/changelog.txt @@ -10,6 +10,7 @@ * Enhancement - Update "Standard Payments" tab settings #1065 * Enhancement - Update PHP 7.2 requirement in all relevant files #1084 * Enhancement - When PUI is enabled FraudNet should be also enabled #1129 +* Enhancement - Add PayPal-Request-Id if payment source exist #1132 = 2.0.1 - 2022-12-13 = * Fix - Error while syncing tracking data to PayPal -> Sync GZD Tracking #1020 diff --git a/readme.txt b/readme.txt index 30f8123a3..72808d11b 100644 --- a/readme.txt +++ b/readme.txt @@ -91,6 +91,7 @@ Follow the steps below to connect the plugin to your PayPal account: * Enhancement - Update "Standard Payments" tab settings #1065 * Enhancement - Update PHP 7.2 requirement in all relevant files #1084 * Enhancement - When PUI is enabled FraudNet should be also enabled #1129 +* Enhancement - Add PayPal-Request-Id if payment source exist #1132 = 2.0.1 = * Fix - Error while syncing tracking data to PayPal -> Sync GZD Tracking #1020 From 79f2c9a1fa1826e885388d43ededd416cca730a3 Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 25 Jan 2023 09:42:31 +0200 Subject: [PATCH 12/35] Save checkout form before free trial redirect --- modules/ppcp-button/resources/js/button.js | 12 ++- .../modules/ActionHandler/FreeTrialHandler.js | 46 +++++---- .../resources/js/modules/Helper/FormSaver.js | 26 +++++ modules/ppcp-button/services.php | 12 +++ .../ppcp-button/src/Assets/SmartButton.php | 13 ++- modules/ppcp-button/src/ButtonModule.php | 11 +++ .../src/Endpoint/SaveCheckoutFormEndpoint.php | 94 +++++++++++++++++++ .../src/Helper/CheckoutFormSaver.php | 32 +++++++ 8 files changed, 220 insertions(+), 26 deletions(-) create mode 100644 modules/ppcp-button/resources/js/modules/Helper/FormSaver.js create mode 100644 modules/ppcp-button/src/Endpoint/SaveCheckoutFormEndpoint.php create mode 100644 modules/ppcp-button/src/Helper/CheckoutFormSaver.php diff --git a/modules/ppcp-button/resources/js/button.js b/modules/ppcp-button/resources/js/button.js index a118211a2..b77395657 100644 --- a/modules/ppcp-button/resources/js/button.js +++ b/modules/ppcp-button/resources/js/button.js @@ -17,6 +17,7 @@ import { import {hide, setVisible, setVisibleByClass} from "./modules/Helper/Hiding"; import {isChangePaymentPage} from "./modules/Helper/Subscriptions"; import FreeTrialHandler from "./modules/ActionHandler/FreeTrialHandler"; +import FormSaver from './modules/Helper/FormSaver'; // TODO: could be a good idea to have a separate spinner for each gateway, // but I think we care mainly about the script loading, so one spinner should be enough. @@ -24,11 +25,18 @@ const buttonsSpinner = new Spinner(document.querySelector('.ppc-button-wrapper') const cardsSpinner = new Spinner('#ppcp-hosted-fields'); const bootstrap = () => { + const checkoutFormSelector = 'form.woocommerce-checkout'; + const errorHandler = new ErrorHandler(PayPalCommerceGateway.labels.error.generic); const spinner = new Spinner(); const creditCardRenderer = new CreditCardRenderer(PayPalCommerceGateway, errorHandler, spinner); - const freeTrialHandler = new FreeTrialHandler(PayPalCommerceGateway, spinner, errorHandler); + const formSaver = new FormSaver( + PayPalCommerceGateway.ajax.save_checkout_form.endpoint, + PayPalCommerceGateway.ajax.save_checkout_form.nonce, + ); + + const freeTrialHandler = new FreeTrialHandler(PayPalCommerceGateway, checkoutFormSelector, formSaver, spinner, errorHandler); jQuery('form.woocommerce-checkout input').on('keydown', e => { if (e.key === 'Enter' && [ @@ -88,7 +96,7 @@ const bootstrap = () => { } } - const form = document.querySelector('form.woocommerce-checkout'); + const form = document.querySelector(checkoutFormSelector); if (form) { jQuery('#ppcp-funding-source-form-input').remove(); form.insertAdjacentHTML( diff --git a/modules/ppcp-button/resources/js/modules/ActionHandler/FreeTrialHandler.js b/modules/ppcp-button/resources/js/modules/ActionHandler/FreeTrialHandler.js index bee968e96..1caf2db80 100644 --- a/modules/ppcp-button/resources/js/modules/ActionHandler/FreeTrialHandler.js +++ b/modules/ppcp-button/resources/js/modules/ActionHandler/FreeTrialHandler.js @@ -1,44 +1,50 @@ -import {PaymentMethods} from "../Helper/CheckoutMethodState"; -import errorHandler from "../ErrorHandler"; - class FreeTrialHandler { constructor( config, + formSelector, + formSaver, spinner, errorHandler ) { this.config = config; + this.formSelector = formSelector; + this.formSaver = formSaver; this.spinner = spinner; this.errorHandler = errorHandler; } - handle() + async handle() { this.spinner.block(); - fetch(this.config.ajax.vault_paypal.endpoint, { - method: 'POST', - credentials: 'same-origin', - body: JSON.stringify({ - nonce: this.config.ajax.vault_paypal.nonce, - return_url: location.href - }), - }).then(res => { - return res.json(); - }).then(data => { + try { + await this.formSaver.save(document.querySelector(this.formSelector)); + } catch (error) { + console.error(error); + } + + try { + const res = await fetch(this.config.ajax.vault_paypal.endpoint, { + method: 'POST', + credentials: 'same-origin', + body: JSON.stringify({ + nonce: this.config.ajax.vault_paypal.nonce, + return_url: location.href, + }), + }); + + const data = await res.json(); + if (!data.success) { - this.spinner.unblock(); - console.error(data); - this.errorHandler.message(data.data.message); throw Error(data.data.message); } location.href = data.data.approve_link; - }).catch(error => { + } catch (error) { this.spinner.unblock(); console.error(error); - this.errorHandler.genericError(); - }); + this.errorHandler.message(data.data.message); + } } } export default FreeTrialHandler; diff --git a/modules/ppcp-button/resources/js/modules/Helper/FormSaver.js b/modules/ppcp-button/resources/js/modules/Helper/FormSaver.js new file mode 100644 index 000000000..332c4a217 --- /dev/null +++ b/modules/ppcp-button/resources/js/modules/Helper/FormSaver.js @@ -0,0 +1,26 @@ +export default class FormSaver { + constructor(url, nonce) { + this.url = url; + this.nonce = nonce; + } + + async save(form) { + const formData = new FormData(form); + const formJsonObj = Object.fromEntries(formData.entries()); + + const res = await fetch(this.url, { + method: 'POST', + credentials: 'same-origin', + body: JSON.stringify({ + nonce: this.nonce, + form: formJsonObj, + }), + }); + + const data = await res.json(); + + if (!data.success) { + throw Error(data.data.message); + } + } +} diff --git a/modules/ppcp-button/services.php b/modules/ppcp-button/services.php index 0e826fab9..527ce8bf9 100644 --- a/modules/ppcp-button/services.php +++ b/modules/ppcp-button/services.php @@ -9,6 +9,8 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Button; +use WooCommerce\PayPalCommerce\Button\Endpoint\SaveCheckoutFormEndpoint; +use WooCommerce\PayPalCommerce\Button\Helper\CheckoutFormSaver; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; use WooCommerce\PayPalCommerce\Button\Assets\DisabledSmartButton; use WooCommerce\PayPalCommerce\Button\Assets\SmartButton; @@ -181,6 +183,16 @@ return array( $logger ); }, + 'button.checkout-form-saver' => static function ( ContainerInterface $container ): CheckoutFormSaver { + return new CheckoutFormSaver(); + }, + 'button.endpoint.save-checkout-form' => static function ( ContainerInterface $container ): SaveCheckoutFormEndpoint { + return new SaveCheckoutFormEndpoint( + $container->get( 'button.request-data' ), + $container->get( 'button.checkout-form-saver' ), + $container->get( 'woocommerce.logger.woocommerce' ) + ); + }, 'button.endpoint.data-client-id' => static function( ContainerInterface $container ) : DataClientIdEndpoint { $request_data = $container->get( 'button.request-data' ); $identity_token = $container->get( 'api.endpoint.identity-token' ); diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index 265178906..53af6033e 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -21,6 +21,7 @@ use WooCommerce\PayPalCommerce\Button\Endpoint\ChangeCartEndpoint; use WooCommerce\PayPalCommerce\Button\Endpoint\CreateOrderEndpoint; use WooCommerce\PayPalCommerce\Button\Endpoint\DataClientIdEndpoint; use WooCommerce\PayPalCommerce\Button\Endpoint\RequestData; +use WooCommerce\PayPalCommerce\Button\Endpoint\SaveCheckoutFormEndpoint; use WooCommerce\PayPalCommerce\Button\Endpoint\StartPayPalVaultingEndpoint; use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply; use WooCommerce\PayPalCommerce\Onboarding\Environment; @@ -761,22 +762,26 @@ class SmartButton implements SmartButtonInterface { 'redirect' => wc_get_checkout_url(), 'context' => $this->context(), 'ajax' => array( - 'change_cart' => array( + 'change_cart' => array( 'endpoint' => \WC_AJAX::get_endpoint( ChangeCartEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( ChangeCartEndpoint::nonce() ), ), - 'create_order' => array( + 'create_order' => array( 'endpoint' => \WC_AJAX::get_endpoint( CreateOrderEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( CreateOrderEndpoint::nonce() ), ), - 'approve_order' => array( + 'approve_order' => array( 'endpoint' => \WC_AJAX::get_endpoint( ApproveOrderEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( ApproveOrderEndpoint::nonce() ), ), - 'vault_paypal' => array( + 'vault_paypal' => array( 'endpoint' => \WC_AJAX::get_endpoint( StartPayPalVaultingEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( StartPayPalVaultingEndpoint::nonce() ), ), + 'save_checkout_form' => array( + 'endpoint' => \WC_AJAX::get_endpoint( SaveCheckoutFormEndpoint::ENDPOINT ), + 'nonce' => wp_create_nonce( SaveCheckoutFormEndpoint::nonce() ), + ), ), 'enforce_vault' => $this->has_subscriptions(), 'can_save_vault_token' => $this->can_save_vault_token(), diff --git a/modules/ppcp-button/src/ButtonModule.php b/modules/ppcp-button/src/ButtonModule.php index 004071293..1bb9d4fa6 100644 --- a/modules/ppcp-button/src/ButtonModule.php +++ b/modules/ppcp-button/src/ButtonModule.php @@ -9,6 +9,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Button; +use WooCommerce\PayPalCommerce\Button\Endpoint\SaveCheckoutFormEndpoint; use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider; use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface; use WooCommerce\PayPalCommerce\Button\Assets\SmartButtonInterface; @@ -156,6 +157,16 @@ class ButtonModule implements ModuleInterface { $endpoint->handle_request(); } ); + + add_action( + 'wc_ajax_' . SaveCheckoutFormEndpoint::ENDPOINT, + static function () use ( $container ) { + $endpoint = $container->get( 'button.endpoint.save-checkout-form' ); + assert( $endpoint instanceof SaveCheckoutFormEndpoint ); + + $endpoint->handle_request(); + } + ); } /** diff --git a/modules/ppcp-button/src/Endpoint/SaveCheckoutFormEndpoint.php b/modules/ppcp-button/src/Endpoint/SaveCheckoutFormEndpoint.php new file mode 100644 index 000000000..52f5a32c8 --- /dev/null +++ b/modules/ppcp-button/src/Endpoint/SaveCheckoutFormEndpoint.php @@ -0,0 +1,94 @@ +request_data = $request_data; + $this->checkout_form_saver = $checkout_form_saver; + $this->logger = $logger; + } + + /** + * Returns the nonce. + * + * @return string + */ + public static function nonce(): string { + return self::ENDPOINT; + } + + /** + * Handles the request. + * + * @return bool + */ + public function handle_request(): bool { + try { + $data = $this->request_data->read_request( $this->nonce() ); + + $this->checkout_form_saver->save( $data['form'] ); + + wp_send_json_success(); + return true; + } catch ( Exception $error ) { + $this->logger->error( 'Checkout form saving failed: ' . $error->getMessage() ); + + wp_send_json_error( + array( + 'message' => $error->getMessage(), + ) + ); + return false; + } + } +} diff --git a/modules/ppcp-button/src/Helper/CheckoutFormSaver.php b/modules/ppcp-button/src/Helper/CheckoutFormSaver.php new file mode 100644 index 000000000..73f8eeefc --- /dev/null +++ b/modules/ppcp-button/src/Helper/CheckoutFormSaver.php @@ -0,0 +1,32 @@ + $value ) { + $_POST[ $key ] = $value; + } + $data = $this->get_posted_data(); + + $this->update_session( $data ); + } +} From 654d6d6da81c77fbafe973b23b286f940ea88628 Mon Sep 17 00:00:00 2001 From: Alex P Date: Mon, 23 Jan 2023 11:44:45 +0200 Subject: [PATCH 13/35] Validate new user creation --- .../src/Validation/CheckoutFormValidator.php | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/modules/ppcp-button/src/Validation/CheckoutFormValidator.php b/modules/ppcp-button/src/Validation/CheckoutFormValidator.php index fd686b953..0fc579638 100644 --- a/modules/ppcp-button/src/Validation/CheckoutFormValidator.php +++ b/modules/ppcp-button/src/Validation/CheckoutFormValidator.php @@ -39,6 +39,45 @@ class CheckoutFormValidator extends WC_Checkout { // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged @$this->validate_checkout( $data, $errors ); + if ( + apply_filters( 'woocommerce_paypal_payments_early_wc_checkout_account_creation_validation_enabled', true ) && + ! is_user_logged_in() && ( $this->is_registration_required() || ! empty( $data['createaccount'] ) ) + ) { + $username = ! empty( $data['account_username'] ) ? $data['account_username'] : ''; + $email = $data['billing_email'] ?? ''; + + if ( email_exists( $email ) ) { + $errors->add( + 'registration-error-email-exists', + apply_filters( + 'woocommerce_registration_error_email_exists', + // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch + __( 'An account is already registered with your email address. Please log in.', 'woocommerce' ), + $email + ) + ); + } + + if ( $username ) { // Empty username is already checked in validate_checkout, and it can be generated. + $username = sanitize_user( $username ); + if ( empty( $username ) || ! validate_username( $username ) ) { + $errors->add( + 'registration-error-invalid-username', + // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch + __( 'Please enter a valid account username.', 'woocommerce' ) + ); + } + + if ( username_exists( $username ) ) { + $errors->add( + 'registration-error-username-exists', + // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch + __( 'An account is already registered with that username. Please choose another.', 'woocommerce' ) + ); + } + } + } + if ( $errors->has_errors() ) { throw new ValidationException( $errors->get_error_messages() ); } From 3dbba1f00df1aa1aae5e394b57798056c59e6f2a Mon Sep 17 00:00:00 2001 From: Danae Millan Date: Mon, 30 Jan 2023 11:20:35 -0300 Subject: [PATCH 14/35] Update 2.0.2 release date --- changelog.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog.txt b/changelog.txt index d89df2550..ba2a9eee4 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,8 +1,8 @@ *** Changelog *** -= 2.0.2 - TBD = += 2.0.2 - 2023-01-30 = * Fix - Do not call PayPal get order by ID if it does not exist #1029 -* Fix - Type check error conflict with German Market #1056 +* Fix - Type check error conflict with German Market #1056 * Fix - Backend Storage for the PayPalRequestIdRepository does not scale #983 * Fix - Ensure WC()->payment_gateways is not null #1128 * Enhancement - Remove plugin data after uninstalling #1075 From b650878c886f77dc288815a05b2e17e0282043c9 Mon Sep 17 00:00:00 2001 From: Danae Millan <41606954+a-danae@users.noreply.github.com> Date: Mon, 30 Jan 2023 12:31:41 -0300 Subject: [PATCH 15/35] Revert "Update 2.0.2 release date" --- changelog.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog.txt b/changelog.txt index ba2a9eee4..d89df2550 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,8 +1,8 @@ *** Changelog *** -= 2.0.2 - 2023-01-30 = += 2.0.2 - TBD = * Fix - Do not call PayPal get order by ID if it does not exist #1029 -* Fix - Type check error conflict with German Market #1056 +* Fix - Type check error conflict with German Market #1056 * Fix - Backend Storage for the PayPalRequestIdRepository does not scale #983 * Fix - Ensure WC()->payment_gateways is not null #1128 * Enhancement - Remove plugin data after uninstalling #1075 From 6fc12a55a9134dd7e362731995483daa36110d21 Mon Sep 17 00:00:00 2001 From: Alex P Date: Mon, 30 Jan 2023 17:33:24 +0200 Subject: [PATCH 16/35] Make is_checkout()=true during validation to improve compatibility --- .../src/Validation/CheckoutFormValidator.php | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/modules/ppcp-button/src/Validation/CheckoutFormValidator.php b/modules/ppcp-button/src/Validation/CheckoutFormValidator.php index fd686b953..b67c96cc3 100644 --- a/modules/ppcp-button/src/Validation/CheckoutFormValidator.php +++ b/modules/ppcp-button/src/Validation/CheckoutFormValidator.php @@ -32,12 +32,22 @@ class CheckoutFormValidator extends WC_Checkout { foreach ( $data as $key => $value ) { $_POST[ $key ] = $value; } - // And we must call get_posted_data because it handles the shipping address. - $data = $this->get_posted_data(); - // It throws some notices when checking fields etc., also from other plugins via hooks. - // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged - @$this->validate_checkout( $data, $errors ); + $is_checkout = function () { + return true; + }; + // Some plugins/filters check is_checkout(). + add_filter( 'woocommerce_is_checkout', $is_checkout ); + try { + // And we must call get_posted_data because it handles the shipping address. + $data = $this->get_posted_data(); + + // It throws some notices when checking fields etc., also from other plugins via hooks. + // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged + @$this->validate_checkout( $data, $errors ); + } finally { + remove_filter( 'woocommerce_is_checkout', $is_checkout ); + } if ( $errors->has_errors() ) { throw new ValidationException( $errors->get_error_messages() ); From b4097f824c877fe76a04c2188cf1277749ff985f Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 31 Jan 2023 10:33:14 +0200 Subject: [PATCH 17/35] Add patch order data filter --- modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php b/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php index fcb656dce..249f5a106 100644 --- a/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php +++ b/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php @@ -518,6 +518,11 @@ class OrderEndpoint { } } + /** + * The filter can be used to modify the order patching request body data (the final prices, items). + */ + $patches_array = apply_filters( 'ppcp_patch_order_request_body_data', $patches_array ); + $bearer = $this->bearer->bearer(); $url = trailingslashit( $this->host ) . 'v2/checkout/orders/' . $order_to_update->id(); $args = array( From 3d3115f99d4a5353000d9aea7e76c1cb594f33f6 Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 31 Jan 2023 10:35:30 +0200 Subject: [PATCH 18/35] Add filter for controlling the ditching of items/breakdown --- modules/ppcp-api-client/src/Entity/PurchaseUnit.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-api-client/src/Entity/PurchaseUnit.php b/modules/ppcp-api-client/src/Entity/PurchaseUnit.php index b2e642fb4..ad138cc75 100644 --- a/modules/ppcp-api-client/src/Entity/PurchaseUnit.php +++ b/modules/ppcp-api-client/src/Entity/PurchaseUnit.php @@ -284,7 +284,14 @@ class PurchaseUnit { $this->items() ), ); - if ( $ditch_items_when_mismatch && $this->ditch_items_when_mismatch( $this->amount(), ...$this->items() ) ) { + + $ditch = $ditch_items_when_mismatch && $this->ditch_items_when_mismatch( $this->amount(), ...$this->items() ); + /** + * The filter can be used to control when the items and totals breakdown are removed from PayPal order info. + */ + $ditch = apply_filters( 'ppcp_ditch_items_breakdown', $ditch, $this ); + + if ( $ditch ) { unset( $purchase_unit['items'] ); unset( $purchase_unit['amount']['breakdown'] ); } From 08fe74790913063ef8ea20d7a4d72576e46fb102 Mon Sep 17 00:00:00 2001 From: Danae Millan Date: Tue, 31 Jan 2023 20:36:28 -0300 Subject: [PATCH 19/35] Update 2.0.2 release date --- changelog.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog.txt b/changelog.txt index d89df2550..3323524cb 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,8 +1,8 @@ *** Changelog *** -= 2.0.2 - TBD = += 2.0.2 - 2023-01-31 = * Fix - Do not call PayPal get order by ID if it does not exist #1029 -* Fix - Type check error conflict with German Market #1056 +* Fix - Type check error conflict with German Market #1056 * Fix - Backend Storage for the PayPalRequestIdRepository does not scale #983 * Fix - Ensure WC()->payment_gateways is not null #1128 * Enhancement - Remove plugin data after uninstalling #1075 From c3329c5e9dc95fac4cb83babdbe9b4e8669995a0 Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 1 Feb 2023 16:31:43 +0200 Subject: [PATCH 20/35] Add filter for disabling fees on wc order admin pages --- modules/ppcp-wc-gateway/src/WCGatewayModule.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/ppcp-wc-gateway/src/WCGatewayModule.php b/modules/ppcp-wc-gateway/src/WCGatewayModule.php index fe0d20d12..02258b410 100644 --- a/modules/ppcp-wc-gateway/src/WCGatewayModule.php +++ b/modules/ppcp-wc-gateway/src/WCGatewayModule.php @@ -148,6 +148,12 @@ class WCGatewayModule implements ModuleInterface { if ( ! $wc_order instanceof WC_Order ) { return; } + /** + * The filter can be used to remove the rows with PayPal fees in WC orders. + */ + if ( ! apply_filters( 'woocommerce_paypal_payments_show_fees_on_order_admin_page', true, $wc_order ) ) { + return; + } // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo $fees_renderer->render( $wc_order ); From 4f1f326172a404c0083f0055125a11f5a95e9e32 Mon Sep 17 00:00:00 2001 From: Khalil Gharbaoui Date: Thu, 2 Feb 2023 04:36:02 +0100 Subject: [PATCH 21/35] Add option to hide funding resource Trustly --- modules/ppcp-compat/src/PPEC/SettingsImporter.php | 2 +- modules/ppcp-wc-gateway/services.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-compat/src/PPEC/SettingsImporter.php b/modules/ppcp-compat/src/PPEC/SettingsImporter.php index a4f9e9e14..7d87de968 100644 --- a/modules/ppcp-compat/src/PPEC/SettingsImporter.php +++ b/modules/ppcp-compat/src/PPEC/SettingsImporter.php @@ -142,7 +142,7 @@ class SettingsImporter { $value = array_values( array_intersect( array_map( 'strtolower', is_array( $option_value ) ? $option_value : array() ), - array( 'card', 'sepa', 'bancontact', 'blik', 'eps', 'giropay', 'ideal', 'mercadopago', 'mybank', 'p24', 'sofort', 'venmo' ) + array( 'card', 'sepa', 'bancontact', 'blik', 'eps', 'giropay', 'ideal', 'mercadopago', 'mybank', 'p24', 'sofort', 'venmo', 'trustly' ) ) ); diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index 5c6591632..60c11fe87 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -862,6 +862,7 @@ return array( 'p24' => _x( 'Przelewy24', 'Name of payment method', 'woocommerce-paypal-payments' ), 'sofort' => _x( 'Sofort', 'Name of payment method', 'woocommerce-paypal-payments' ), 'venmo' => _x( 'Venmo', 'Name of payment method', 'woocommerce-paypal-payments' ), + 'trustly' => _x( 'Trustly', 'Name of payment method', 'woocommerce-paypal-payments' ), ); }, From b10f1a693a9a4f73d0b84c5da76313a4452fcefe Mon Sep 17 00:00:00 2001 From: Alex P Date: Thu, 2 Feb 2023 15:26:28 +0200 Subject: [PATCH 22/35] Set $_REQUEST before validation --- modules/ppcp-button/src/Validation/CheckoutFormValidator.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-button/src/Validation/CheckoutFormValidator.php b/modules/ppcp-button/src/Validation/CheckoutFormValidator.php index fd686b953..6e70d6d3a 100644 --- a/modules/ppcp-button/src/Validation/CheckoutFormValidator.php +++ b/modules/ppcp-button/src/Validation/CheckoutFormValidator.php @@ -27,10 +27,11 @@ class CheckoutFormValidator extends WC_Checkout { public function validate( array $data ) { $errors = new WP_Error(); - // Some plugins check their fields using $_POST, + // Some plugins check their fields using $_POST or $_REQUEST, // also WC terms checkbox https://github.com/woocommerce/woocommerce/issues/35328 . foreach ( $data as $key => $value ) { - $_POST[ $key ] = $value; + $_POST[ $key ] = $value; + $_REQUEST[ $key ] = $value; } // And we must call get_posted_data because it handles the shipping address. $data = $this->get_posted_data(); From d6bd403b5ad5c62c3c0bdaaad3bb9e310c076f87 Mon Sep 17 00:00:00 2001 From: Alex P Date: Fri, 3 Feb 2023 10:35:38 +0200 Subject: [PATCH 23/35] Call calculate_shipping before validation --- modules/ppcp-button/src/Validation/CheckoutFormValidator.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/ppcp-button/src/Validation/CheckoutFormValidator.php b/modules/ppcp-button/src/Validation/CheckoutFormValidator.php index fd686b953..a620a3010 100644 --- a/modules/ppcp-button/src/Validation/CheckoutFormValidator.php +++ b/modules/ppcp-button/src/Validation/CheckoutFormValidator.php @@ -35,6 +35,9 @@ class CheckoutFormValidator extends WC_Checkout { // And we must call get_posted_data because it handles the shipping address. $data = $this->get_posted_data(); + // Looks like without this WC()->shipping->get_packages() is empty which is used by some plugins. + WC()->cart->calculate_shipping(); + // It throws some notices when checking fields etc., also from other plugins via hooks. // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged @$this->validate_checkout( $data, $errors ); From 838978fcf6f89c8842ff8a95dfdd62cc0bfd6dac Mon Sep 17 00:00:00 2001 From: Alex P Date: Fri, 3 Feb 2023 10:36:48 +0200 Subject: [PATCH 24/35] Retrieve validation errors from notices --- .../src/Validation/CheckoutFormValidator.php | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-button/src/Validation/CheckoutFormValidator.php b/modules/ppcp-button/src/Validation/CheckoutFormValidator.php index a620a3010..f93bc5af2 100644 --- a/modules/ppcp-button/src/Validation/CheckoutFormValidator.php +++ b/modules/ppcp-button/src/Validation/CheckoutFormValidator.php @@ -42,8 +42,26 @@ class CheckoutFormValidator extends WC_Checkout { // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged @$this->validate_checkout( $data, $errors ); - if ( $errors->has_errors() ) { - throw new ValidationException( $errors->get_error_messages() ); + // Some plugins call wc_add_notice directly. + // We should retrieve such notices, and also clear them to avoid duplicates later. + // TODO: Normally WC converts the messages from validate_checkout into notices, + // maybe we should do the same for consistency, but it requires lots of changes in the way we handle/output errors. + $messages = array_merge( + $errors->get_error_messages(), + array_map( + function ( array $notice ): string { + return $notice['notice']; + }, + wc_get_notices( 'error' ) + ) + ); + + if ( wc_notice_count( 'error' ) > 0 ) { + wc_clear_notices(); + } + + if ( $messages ) { + throw new ValidationException( $messages ); } } } From 3fe1a2a274fa156afa36d6cdc3b13e37c2a2654a Mon Sep 17 00:00:00 2001 From: Alex P Date: Mon, 6 Feb 2023 10:06:32 +0200 Subject: [PATCH 25/35] Add white color option --- .../src/Settings/Fields/paypal-smart-button-fields.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-wc-gateway/src/Settings/Fields/paypal-smart-button-fields.php b/modules/ppcp-wc-gateway/src/Settings/Fields/paypal-smart-button-fields.php index 2fb1ea5a6..f064b1e7b 100644 --- a/modules/ppcp-wc-gateway/src/Settings/Fields/paypal-smart-button-fields.php +++ b/modules/ppcp-wc-gateway/src/Settings/Fields/paypal-smart-button-fields.php @@ -49,7 +49,7 @@ return function ( ContainerInterface $container, array $fields ): array { 'description' => sprintf( // translators: %1$s and %2$s are the opening and closing of HTML tag. __( - 'Customize the appearance of the PayPal smart buttons on the + 'Customize the appearance of the PayPal smart buttons on the %1$sCheckout page%5$s, %2$sSingle Product Page%5$s, %3$sCart page%5$s or on %4$sMini Cart%5$s.', 'woocommerce-paypal-payments' ), @@ -163,6 +163,7 @@ return function ( ContainerInterface $container, array $fields ): array { 'blue' => __( 'Blue', 'woocommerce-paypal-payments' ), 'silver' => __( 'Silver', 'woocommerce-paypal-payments' ), 'black' => __( 'Black', 'woocommerce-paypal-payments' ), + 'white' => __( 'White', 'woocommerce-paypal-payments' ), ), 'screens' => array( State::STATE_START, @@ -308,6 +309,7 @@ return function ( ContainerInterface $container, array $fields ): array { 'blue' => __( 'Blue', 'woocommerce-paypal-payments' ), 'silver' => __( 'Silver', 'woocommerce-paypal-payments' ), 'black' => __( 'Black', 'woocommerce-paypal-payments' ), + 'white' => __( 'White', 'woocommerce-paypal-payments' ), ), 'screens' => array( State::STATE_START, @@ -447,6 +449,7 @@ return function ( ContainerInterface $container, array $fields ): array { 'blue' => __( 'Blue', 'woocommerce-paypal-payments' ), 'silver' => __( 'Silver', 'woocommerce-paypal-payments' ), 'black' => __( 'Black', 'woocommerce-paypal-payments' ), + 'white' => __( 'White', 'woocommerce-paypal-payments' ), ), 'screens' => array( State::STATE_START, @@ -586,6 +589,7 @@ return function ( ContainerInterface $container, array $fields ): array { 'blue' => __( 'Blue', 'woocommerce-paypal-payments' ), 'silver' => __( 'Silver', 'woocommerce-paypal-payments' ), 'black' => __( 'Black', 'woocommerce-paypal-payments' ), + 'white' => __( 'White', 'woocommerce-paypal-payments' ), ), 'screens' => array( State::STATE_START, @@ -725,6 +729,7 @@ return function ( ContainerInterface $container, array $fields ): array { 'blue' => __( 'Blue', 'woocommerce-paypal-payments' ), 'silver' => __( 'Silver', 'woocommerce-paypal-payments' ), 'black' => __( 'Black', 'woocommerce-paypal-payments' ), + 'white' => __( 'White', 'woocommerce-paypal-payments' ), ), 'screens' => array( State::STATE_START, From df10747701c57d2f4a76d26eef75835fb63701f8 Mon Sep 17 00:00:00 2001 From: Alex P Date: Mon, 6 Feb 2023 10:13:38 +0200 Subject: [PATCH 26/35] Fix default landing value --- modules/ppcp-wc-gateway/services.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index 5c6591632..3d39da85a 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -593,7 +593,7 @@ return array( 'type' => 'select', 'class' => array(), 'input_class' => array( 'wc-enhanced-select' ), - 'default' => 'gold', + 'default' => ApplicationContext::LANDING_PAGE_LOGIN, 'desc_tip' => true, 'description' => __( 'Type of PayPal page to display.', From abc39d4dbd6cc7ec20c92ba504ab242fd5faf22b Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 7 Feb 2023 10:58:38 +0200 Subject: [PATCH 27/35] Mention PUI only for German shops and add line breaks --- modules/ppcp-wc-gateway/services.php | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index 3d39da85a..630ee757c 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -1158,19 +1158,24 @@ return array( return false; }, 'wcgateway.settings.tracking-label' => static function ( ContainerInterface $container ): string { - $tracking_label = sprintf( - // translators: %1$s and %2$s are the opening and closing of HTML tag. - __( 'Enable shipment tracking information to be sent to PayPal for seller protection features. Required when %1$sPay upon Invoice%2$s is used.', 'woocommerce-paypal-payments' ), - '', - '' - ); + $tracking_label = __( 'Enable shipment tracking information to be sent to PayPal for seller protection features.', 'woocommerce-paypal-payments' ); + + if ( 'DE' === $container->get( 'api.shop.country' ) ) { + $tracking_label .= '
' . sprintf( + // translators: %1$s and %2$s are the opening and closing of HTML tag. + __( 'Required when %1$sPay upon Invoice%2$s is used.', 'woocommerce-paypal-payments' ), + '', + '' + ); + } + $is_tracking_available = $container->get( 'order-tracking.is-tracking-available' ); if ( $is_tracking_available ) { return $tracking_label; } - $tracking_label .= sprintf( + $tracking_label .= '
' . sprintf( // translators: %1$s and %2$s are the opening and closing of HTML tag. __( ' To use tracking features, you must %1$senable tracking on your account%2$s.', From 09b6b411db2d64548ec68432242a9e090415f0ed Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 7 Feb 2023 15:29:53 +0200 Subject: [PATCH 28/35] Call WC validation on free trial click --- modules/ppcp-button/resources/js/button.js | 9 +- .../modules/ActionHandler/FreeTrialHandler.js | 23 ++++ .../js/modules/Helper/FormValidator.js | 31 +++++ modules/ppcp-button/services.php | 15 ++- .../ppcp-button/src/Assets/SmartButton.php | 16 +++ modules/ppcp-button/src/ButtonModule.php | 12 +- .../src/Endpoint/ValidateCheckoutEndpoint.php | 107 ++++++++++++++++++ 7 files changed, 210 insertions(+), 3 deletions(-) create mode 100644 modules/ppcp-button/resources/js/modules/Helper/FormValidator.js create mode 100644 modules/ppcp-button/src/Endpoint/ValidateCheckoutEndpoint.php diff --git a/modules/ppcp-button/resources/js/button.js b/modules/ppcp-button/resources/js/button.js index b77395657..8dc519a6c 100644 --- a/modules/ppcp-button/resources/js/button.js +++ b/modules/ppcp-button/resources/js/button.js @@ -18,6 +18,7 @@ import {hide, setVisible, setVisibleByClass} from "./modules/Helper/Hiding"; import {isChangePaymentPage} from "./modules/Helper/Subscriptions"; import FreeTrialHandler from "./modules/ActionHandler/FreeTrialHandler"; import FormSaver from './modules/Helper/FormSaver'; +import FormValidator from "./modules/Helper/FormValidator"; // TODO: could be a good idea to have a separate spinner for each gateway, // but I think we care mainly about the script loading, so one spinner should be enough. @@ -36,7 +37,13 @@ const bootstrap = () => { PayPalCommerceGateway.ajax.save_checkout_form.nonce, ); - const freeTrialHandler = new FreeTrialHandler(PayPalCommerceGateway, checkoutFormSelector, formSaver, spinner, errorHandler); + const formValidator = PayPalCommerceGateway.early_checkout_validation_enabled ? + new FormValidator( + PayPalCommerceGateway.ajax.validate_checkout.endpoint, + PayPalCommerceGateway.ajax.validate_checkout.nonce, + ) : null; + + const freeTrialHandler = new FreeTrialHandler(PayPalCommerceGateway, checkoutFormSelector, formSaver, formValidator, spinner, errorHandler); jQuery('form.woocommerce-checkout input').on('keydown', e => { if (e.key === 'Enter' && [ diff --git a/modules/ppcp-button/resources/js/modules/ActionHandler/FreeTrialHandler.js b/modules/ppcp-button/resources/js/modules/ActionHandler/FreeTrialHandler.js index 1caf2db80..e24de3bee 100644 --- a/modules/ppcp-button/resources/js/modules/ActionHandler/FreeTrialHandler.js +++ b/modules/ppcp-button/resources/js/modules/ActionHandler/FreeTrialHandler.js @@ -1,14 +1,24 @@ class FreeTrialHandler { + /** + * @param config + * @param formSelector + * @param {FormSaver} formSaver + * @param {FormValidator|null} formValidator + * @param {Spinner} spinner + * @param {ErrorHandler} errorHandler + */ constructor( config, formSelector, formSaver, + formValidator, spinner, errorHandler ) { this.config = config; this.formSelector = formSelector; this.formSaver = formSaver; + this.formValidator = formValidator; this.spinner = spinner; this.errorHandler = errorHandler; } @@ -24,6 +34,19 @@ class FreeTrialHandler { } try { + if (this.formValidator) { + try { + const errors = await this.formValidator.validate(document.querySelector(this.formSelector)); + if (errors.length > 0) { + this.spinner.unblock(); + this.errorHandler.messages(errors); + return; + } + } catch (error) { + console.error(error); + } + } + const res = await fetch(this.config.ajax.vault_paypal.endpoint, { method: 'POST', credentials: 'same-origin', diff --git a/modules/ppcp-button/resources/js/modules/Helper/FormValidator.js b/modules/ppcp-button/resources/js/modules/Helper/FormValidator.js new file mode 100644 index 000000000..af68be7dc --- /dev/null +++ b/modules/ppcp-button/resources/js/modules/Helper/FormValidator.js @@ -0,0 +1,31 @@ +export default class FormValidator { + constructor(url, nonce) { + this.url = url; + this.nonce = nonce; + } + + async validate(form) { + const formData = new FormData(form); + const formJsonObj = Object.fromEntries(formData.entries()); + + const res = await fetch(this.url, { + method: 'POST', + credentials: 'same-origin', + body: JSON.stringify({ + nonce: this.nonce, + form: formJsonObj, + }), + }); + + const data = await res.json(); + + if (!data.success) { + if (data.data.errors) { + return data.data.errors; + } + throw Error(data.data.message); + } + + return []; + } +} diff --git a/modules/ppcp-button/services.php b/modules/ppcp-button/services.php index 527ce8bf9..6e8fe8a18 100644 --- a/modules/ppcp-button/services.php +++ b/modules/ppcp-button/services.php @@ -9,8 +9,10 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Button; -use WooCommerce\PayPalCommerce\Button\Endpoint\SaveCheckoutFormEndpoint; use WooCommerce\PayPalCommerce\Button\Helper\CheckoutFormSaver; +use WooCommerce\PayPalCommerce\Button\Endpoint\SaveCheckoutFormEndpoint; +use WooCommerce\PayPalCommerce\Button\Validation\CheckoutFormValidator; +use WooCommerce\PayPalCommerce\Button\Endpoint\ValidateCheckoutEndpoint; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; use WooCommerce\PayPalCommerce\Button\Assets\DisabledSmartButton; use WooCommerce\PayPalCommerce\Button\Assets\SmartButton; @@ -106,6 +108,7 @@ return array( $currency, $container->get( 'wcgateway.all-funding-sources' ), $container->get( 'button.basic-checkout-validation-enabled' ), + $container->get( 'button.early-wc-checkout-validation-enabled' ), $container->get( 'woocommerce.logger.woocommerce' ) ); }, @@ -210,6 +213,13 @@ return array( $container->get( 'woocommerce.logger.woocommerce' ) ); }, + 'button.endpoint.validate-checkout' => static function ( ContainerInterface $container ): ValidateCheckoutEndpoint { + return new ValidateCheckoutEndpoint( + $container->get( 'button.request-data' ), + $container->get( 'button.validation.wc-checkout-validator' ), + $container->get( 'woocommerce.logger.woocommerce' ) + ); + }, 'button.helper.three-d-secure' => static function ( ContainerInterface $container ): ThreeDSecure { $logger = $container->get( 'woocommerce.logger.woocommerce' ); return new ThreeDSecure( $logger ); @@ -246,4 +256,7 @@ return array( */ return (bool) apply_filters( 'woocommerce_paypal_payments_early_wc_checkout_validation_enabled', true ); }, + 'button.validation.wc-checkout-validator' => static function ( ContainerInterface $container ): CheckoutFormValidator { + return new CheckoutFormValidator(); + }, ); diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index 53af6033e..9635ad5ee 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -23,6 +23,7 @@ use WooCommerce\PayPalCommerce\Button\Endpoint\DataClientIdEndpoint; use WooCommerce\PayPalCommerce\Button\Endpoint\RequestData; use WooCommerce\PayPalCommerce\Button\Endpoint\SaveCheckoutFormEndpoint; use WooCommerce\PayPalCommerce\Button\Endpoint\StartPayPalVaultingEndpoint; +use WooCommerce\PayPalCommerce\Button\Endpoint\ValidateCheckoutEndpoint; use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply; use WooCommerce\PayPalCommerce\Onboarding\Environment; use WooCommerce\PayPalCommerce\Session\SessionHandler; @@ -155,6 +156,13 @@ class SmartButton implements SmartButtonInterface { */ private $basic_checkout_validation_enabled; + /** + * Whether to execute WC validation of the checkout form. + * + * @var bool + */ + protected $early_validation_enabled; + /** * The logger. * @@ -188,6 +196,7 @@ class SmartButton implements SmartButtonInterface { * @param string $currency 3-letter currency code of the shop. * @param array $all_funding_sources All existing funding sources. * @param bool $basic_checkout_validation_enabled Whether the basic JS validation of the form iss enabled. + * @param bool $early_validation_enabled Whether to execute WC validation of the checkout form. * @param LoggerInterface $logger The logger. */ public function __construct( @@ -207,6 +216,7 @@ class SmartButton implements SmartButtonInterface { string $currency, array $all_funding_sources, bool $basic_checkout_validation_enabled, + bool $early_validation_enabled, LoggerInterface $logger ) { @@ -226,6 +236,7 @@ class SmartButton implements SmartButtonInterface { $this->currency = $currency; $this->all_funding_sources = $all_funding_sources; $this->basic_checkout_validation_enabled = $basic_checkout_validation_enabled; + $this->early_validation_enabled = $early_validation_enabled; $this->logger = $logger; } @@ -782,6 +793,10 @@ class SmartButton implements SmartButtonInterface { 'endpoint' => \WC_AJAX::get_endpoint( SaveCheckoutFormEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( SaveCheckoutFormEndpoint::nonce() ), ), + 'validate_checkout' => array( + 'endpoint' => \WC_AJAX::get_endpoint( ValidateCheckoutEndpoint::ENDPOINT ), + 'nonce' => wp_create_nonce( ValidateCheckoutEndpoint::nonce() ), + ), ), 'enforce_vault' => $this->has_subscriptions(), 'can_save_vault_token' => $this->can_save_vault_token(), @@ -871,6 +886,7 @@ class SmartButton implements SmartButtonInterface { 'single_product_buttons_enabled' => $this->settings_status->is_smart_button_enabled_for_location( 'product' ), 'mini_cart_buttons_enabled' => $this->settings_status->is_smart_button_enabled_for_location( 'mini-cart' ), 'basic_checkout_validation_enabled' => $this->basic_checkout_validation_enabled, + 'early_checkout_validation_enabled' => $this->early_validation_enabled, ); if ( $this->style_for_context( 'layout', 'mini-cart' ) !== 'horizontal' ) { diff --git a/modules/ppcp-button/src/ButtonModule.php b/modules/ppcp-button/src/ButtonModule.php index 1bb9d4fa6..573c7012c 100644 --- a/modules/ppcp-button/src/ButtonModule.php +++ b/modules/ppcp-button/src/ButtonModule.php @@ -10,6 +10,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Button; use WooCommerce\PayPalCommerce\Button\Endpoint\SaveCheckoutFormEndpoint; +use WooCommerce\PayPalCommerce\Button\Endpoint\ValidateCheckoutEndpoint; use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider; use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface; use WooCommerce\PayPalCommerce\Button\Assets\SmartButtonInterface; @@ -96,7 +97,7 @@ class ButtonModule implements ModuleInterface { * * @param ContainerInterface $container The Container. */ - private function register_ajax_endpoints( ContainerInterface $container ) { + private function register_ajax_endpoints( ContainerInterface $container ): void { add_action( 'wc_ajax_' . DataClientIdEndpoint::ENDPOINT, static function () use ( $container ) { @@ -167,6 +168,15 @@ class ButtonModule implements ModuleInterface { $endpoint->handle_request(); } ); + + add_action( + 'wc_ajax_' . ValidateCheckoutEndpoint::ENDPOINT, + static function () use ( $container ) { + $endpoint = $container->get( 'button.endpoint.validate-checkout' ); + assert( $endpoint instanceof ValidateCheckoutEndpoint ); + $endpoint->handle_request(); + } + ); } /** diff --git a/modules/ppcp-button/src/Endpoint/ValidateCheckoutEndpoint.php b/modules/ppcp-button/src/Endpoint/ValidateCheckoutEndpoint.php new file mode 100644 index 000000000..7c99bb063 --- /dev/null +++ b/modules/ppcp-button/src/Endpoint/ValidateCheckoutEndpoint.php @@ -0,0 +1,107 @@ +request_data = $request_data; + $this->checkout_form_validator = $checkout_form_validator; + $this->logger = $logger; + } + + /** + * Returns the nonce. + * + * @return string + */ + public static function nonce(): string { + return self::ENDPOINT; + } + + /** + * Handles the request. + * + * @return bool + */ + public function handle_request(): bool { + try { + $data = $this->request_data->read_request( $this->nonce() ); + + $form_fields = $data['form']; + + $this->checkout_form_validator->validate( $form_fields ); + + wp_send_json_success(); + + return true; + } catch ( ValidationException $exception ) { + wp_send_json_error( + array( + 'message' => $exception->getMessage(), + 'errors' => $exception->errors(), + ) + ); + return false; + } catch ( Throwable $error ) { + $this->logger->error( "Form validation execution failed. {$error->getMessage()} {$error->getFile()}:{$error->getLine()}" ); + + wp_send_json_error( + array( + 'message' => $error->getMessage(), + ) + ); + return false; + } + } +} From 50560a96c4b4e1ec7895ed5ee2e123dacc18f399 Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 7 Feb 2023 15:30:59 +0200 Subject: [PATCH 29/35] Add tests --- .../Endpoint/ValidateCheckoutEndpointTest.php | 72 +++++++++++++++++++ tests/PHPUnit/bootstrap.php | 1 + .../e2e/PHPUnit/Validation/ValidationTest.php | 70 ++++++++++++++++++ tests/stubs/WC_Checkout.php | 11 +++ 4 files changed, 154 insertions(+) create mode 100644 tests/PHPUnit/Button/Endpoint/ValidateCheckoutEndpointTest.php create mode 100644 tests/e2e/PHPUnit/Validation/ValidationTest.php create mode 100644 tests/stubs/WC_Checkout.php diff --git a/tests/PHPUnit/Button/Endpoint/ValidateCheckoutEndpointTest.php b/tests/PHPUnit/Button/Endpoint/ValidateCheckoutEndpointTest.php new file mode 100644 index 000000000..39d38bfb0 --- /dev/null +++ b/tests/PHPUnit/Button/Endpoint/ValidateCheckoutEndpointTest.php @@ -0,0 +1,72 @@ +requestData = Mockery::mock(RequestData::class); + $this->formValidator = Mockery::mock(CheckoutFormValidator::class); + $this->logger = Mockery::mock(LoggerInterface::class); + + $this->sut = new ValidateCheckoutEndpoint( + $this->requestData, + $this->formValidator, + $this->logger + ); + + $this->requestData->expects('read_request')->andReturn(['form' => ['field1' => 'value']]); + } + + public function testValid() + { + $this->formValidator->expects('validate')->once(); + + expect('wp_send_json_success')->once(); + + $this->sut->handle_request(); + } + + public function testInvalid() + { + $exception = new ValidationException(['Invalid value']); + $this->formValidator->expects('validate')->once() + ->andThrow($exception); + + expect('wp_send_json_error')->once() + ->with(['message' => $exception->getMessage(), 'errors' => ['Invalid value']]); + + $this->sut->handle_request(); + } + + public function testFailure() + { + $exception = new Exception('BOOM'); + $this->formValidator->expects('validate')->once() + ->andThrow($exception); + + expect('wp_send_json_error')->once() + ->with(['message' => $exception->getMessage()]); + + $this->logger->expects('error')->once(); + + $this->sut->handle_request(); + } +} diff --git a/tests/PHPUnit/bootstrap.php b/tests/PHPUnit/bootstrap.php index a674e4167..75145ac11 100644 --- a/tests/PHPUnit/bootstrap.php +++ b/tests/PHPUnit/bootstrap.php @@ -8,5 +8,6 @@ require_once ROOT_DIR . '/vendor/autoload.php'; require_once TESTS_ROOT_DIR . '/stubs/WC_Payment_Gateway.php'; require_once TESTS_ROOT_DIR . '/stubs/WC_Payment_Gateway_CC.php'; require_once TESTS_ROOT_DIR . '/stubs/WC_Ajax.php'; +require_once TESTS_ROOT_DIR . '/stubs/WC_Checkout.php'; Hamcrest\Util::registerGlobalFunctions(); diff --git a/tests/e2e/PHPUnit/Validation/ValidationTest.php b/tests/e2e/PHPUnit/Validation/ValidationTest.php new file mode 100644 index 000000000..cf6d87f36 --- /dev/null +++ b/tests/e2e/PHPUnit/Validation/ValidationTest.php @@ -0,0 +1,70 @@ +container = $this->getContainer(); + + $this->sut = $this->container->get( 'button.validation.wc-checkout-validator' ); + assert($this->sut instanceof CheckoutFormValidator); + } + + public function testValid() + { + $this->sut->validate([ + 'billing_first_name'=>'John', + 'billing_last_name'=>'Doe', + 'billing_company'=>'', + 'billing_country'=>'DE', + 'billing_address_1'=>'1 Main St', + 'billing_address_2'=>'city1', + 'billing_postcode'=>'11111', + 'billing_city'=>'city1', + 'billing_state'=>'DE-BW', + 'billing_phone'=>'12345678', + 'billing_email'=>'a@gmail.com', + 'terms-field'=>'1', + 'terms'=>'on', + ]); + } + + public function testInvalid() + { + $this->expectException(ValidationException::class); + $this->expectExceptionMessageMatches('/.+First name.+Postcode/i'); + + $this->sut->validate([ + 'billing_first_name'=>'', + 'billing_postcode'=>'ABCDE', + + 'billing_last_name'=>'Doe', + 'billing_company'=>'', + 'billing_country'=>'DE', + 'billing_address_1'=>'1 Main St', + 'billing_address_2'=>'city1', + 'billing_city'=>'city1', + 'billing_state'=>'DE-BW', + 'billing_phone'=>'12345678', + 'billing_email'=>'a@gmail.com', + 'terms-field'=>'1', + 'terms'=>'on', + ]); + } +} diff --git a/tests/stubs/WC_Checkout.php b/tests/stubs/WC_Checkout.php new file mode 100644 index 000000000..85cd8419e --- /dev/null +++ b/tests/stubs/WC_Checkout.php @@ -0,0 +1,11 @@ + Date: Tue, 7 Feb 2023 15:52:58 +0200 Subject: [PATCH 30/35] Add tracking docs url --- modules/ppcp-wc-gateway/services.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index 630ee757c..5a3a2e6d7 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -1158,7 +1158,12 @@ return array( return false; }, 'wcgateway.settings.tracking-label' => static function ( ContainerInterface $container ): string { - $tracking_label = __( 'Enable shipment tracking information to be sent to PayPal for seller protection features.', 'woocommerce-paypal-payments' ); + $tracking_label = sprintf( + // translators: %1$s and %2$s are the opening and closing of HTML tag. + __( 'Enable %1$sshipment tracking information%2$s to be sent to PayPal for seller protection features.', 'woocommerce-paypal-payments' ), + '', + '' + ); if ( 'DE' === $container->get( 'api.shop.country' ) ) { $tracking_label .= '
' . sprintf( From 040c51a2b6ec31495edf3ad79ab1b7b45fbab6af Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 7 Feb 2023 15:53:11 +0200 Subject: [PATCH 31/35] Mention PUI only for German shops and add line breaks --- modules/ppcp-wc-gateway/services.php | 19 +++++++++++++++++++ .../Settings/Fields/connection-tab-fields.php | 7 +------ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index 5a3a2e6d7..90e9a1fcc 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -1157,6 +1157,25 @@ return array( return false; }, + 'wcgateway.settings.fraudnet-label' => static function ( ContainerInterface $container ): string { + $label = sprintf( + // translators: %1$s and %2$s are the opening and closing of HTML tag. + __( 'Manage online risk with %1$sFraudNet%2$s.', 'woocommerce-paypal-payments' ), + '', + '' + ); + + if ( 'DE' === $container->get( 'api.shop.country' ) ) { + $label .= '
' . sprintf( + // translators: %1$s and %2$s are the opening and closing of HTML tag. + __( 'Required when %1$sPay upon Invoice%2$s is used.', 'woocommerce-paypal-payments' ), + '', + '' + ); + } + + return $label; + }, 'wcgateway.settings.tracking-label' => static function ( ContainerInterface $container ): string { $tracking_label = sprintf( // translators: %1$s and %2$s are the opening and closing of HTML tag. diff --git a/modules/ppcp-wc-gateway/src/Settings/Fields/connection-tab-fields.php b/modules/ppcp-wc-gateway/src/Settings/Fields/connection-tab-fields.php index 06c3b826c..3ece9ba40 100644 --- a/modules/ppcp-wc-gateway/src/Settings/Fields/connection-tab-fields.php +++ b/modules/ppcp-wc-gateway/src/Settings/Fields/connection-tab-fields.php @@ -398,12 +398,7 @@ return function ( ContainerInterface $container, array $fields ): array { 'title' => __( 'FraudNet', 'woocommerce-paypal-payments' ), 'type' => 'checkbox', 'desc_tip' => true, - 'label' => sprintf( - // translators: %1$s and %2$s are the opening and closing of HTML tag. - __( 'Manage online risk with %1$sFraudNet%2$s.', 'woocommerce-paypal-payments' ), - '', - '' - ), + 'label' => $container->get( 'wcgateway.settings.fraudnet-label' ), 'description' => __( 'FraudNet is a JavaScript library developed by PayPal and embedded into a merchant’s web page to collect browser-based data to help reduce fraud.', 'woocommerce-paypal-payments' ), 'default' => false, 'screens' => array( From 9839d24a666d31f291d01ca4b162ddf281dc918f Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 7 Feb 2023 15:55:23 +0200 Subject: [PATCH 32/35] Fix fraudnet docs url --- modules/ppcp-wc-gateway/services.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index 90e9a1fcc..8f3305059 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -1161,7 +1161,7 @@ return array( $label = sprintf( // translators: %1$s and %2$s are the opening and closing of HTML tag. __( 'Manage online risk with %1$sFraudNet%2$s.', 'woocommerce-paypal-payments' ), - '', + '', '' ); From 61dafb83d7c8f7de52abc1288587bcb56a09e0a5 Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 8 Feb 2023 18:00:23 +0200 Subject: [PATCH 33/35] Use wp_loaded for fraudnet loading to avoid warnings It needs wcgateway.enabled-ppcp-gateways which calls WC()->payment_gateways->get_available_payment_gateways(), and the hooks inside it can be used by other plugins resulting in warnings about calling functions like get_cart too early One way to fix it is simply to use the wp_loaded hook instead of init, I think it supposed to be a more correct choice if interaction with other plugins (WC) is needed, because it is fired when "WP, all plugins, and the theme are fully loaded and instantiated" --- modules/ppcp-wc-gateway/src/WCGatewayModule.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-wc-gateway/src/WCGatewayModule.php b/modules/ppcp-wc-gateway/src/WCGatewayModule.php index fe0d20d12..ecb4487c8 100644 --- a/modules/ppcp-wc-gateway/src/WCGatewayModule.php +++ b/modules/ppcp-wc-gateway/src/WCGatewayModule.php @@ -252,7 +252,7 @@ class WCGatewayModule implements ModuleInterface { ); add_action( - 'init', + 'wp_loaded', function () use ( $c ) { if ( 'DE' === $c->get( 'api.shop.country' ) ) { ( $c->get( 'wcgateway.pay-upon-invoice' ) )->init(); From 88d85ca87d5ccdc0e57dba864173e62246fd9c5d Mon Sep 17 00:00:00 2001 From: emilicastells Date: Thu, 9 Feb 2023 16:35:53 +0100 Subject: [PATCH 34/35] Get button locations from `smart_button_locations` setting --- modules/ppcp-wc-gateway/services.php | 35 +------ .../src/Assets/FraudNetAssets.php | 93 ++++++++++++++----- 2 files changed, 71 insertions(+), 57 deletions(-) diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index f5be3163f..d4c433404 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -1333,39 +1333,6 @@ return array( return $enabled_ppcp_gateways; }, - 'wcgateway.is-paypal-continuation' => static function ( ContainerInterface $container ): bool { - $session_handler = $container->get( 'session.handler' ); - assert( $session_handler instanceof SessionHandler ); - - $order = $session_handler->order(); - if ( ! $order ) { - return false; - } - $source = $order->payment_source(); - if ( $source && $source->card() ) { - return false; // Ignore for DCC. - } - if ( 'card' === $session_handler->funding_source() ) { - return false; // Ignore for card buttons. - } - return true; - }, - 'wcgateway.current-context' => static function ( ContainerInterface $container ): string { - $context = 'mini-cart'; - if ( is_product() || wc_post_content_has_shortcode( 'product_page' ) ) { - $context = 'product'; - } - if ( is_cart() ) { - $context = 'cart'; - } - if ( is_checkout() && ! $container->get( 'wcgateway.is-paypal-continuation' ) ) { - $context = 'checkout'; - } - if ( is_checkout_pay_page() ) { - $context = 'pay-now'; - } - return $context; - }, 'wcgateway.is-fraudnet-enabled' => static function ( ContainerInterface $container ): bool { $settings = $container->get( 'wcgateway.settings' ); assert( $settings instanceof Settings ); @@ -1380,7 +1347,7 @@ return array( $container->get( 'onboarding.environment' ), $container->get( 'wcgateway.settings' ), $container->get( 'wcgateway.enabled-ppcp-gateways' ), - $container->get( 'wcgateway.current-context' ), + $container->get( 'session.handler' ), $container->get( 'wcgateway.is-fraudnet-enabled' ) ); }, diff --git a/modules/ppcp-wc-gateway/src/Assets/FraudNetAssets.php b/modules/ppcp-wc-gateway/src/Assets/FraudNetAssets.php index 6ab3f7d98..59cda7cd7 100644 --- a/modules/ppcp-wc-gateway/src/Assets/FraudNetAssets.php +++ b/modules/ppcp-wc-gateway/src/Assets/FraudNetAssets.php @@ -10,6 +10,8 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\WcGateway\Assets; use WooCommerce\PayPalCommerce\Onboarding\Environment; +use WooCommerce\PayPalCommerce\Session\SessionHandler; +use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException; use WooCommerce\PayPalCommerce\WcGateway\FraudNet\FraudNet; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PayUponInvoiceGateway; @@ -63,11 +65,11 @@ class FraudNetAssets { protected $enabled_ppcp_gateways; /** - * The current context. + * The session handler * - * @var string + * @var SessionHandler */ - protected $context; + protected $session_handler; /** * True if FraudNet support is enabled in settings, otherwise false. @@ -79,14 +81,14 @@ class FraudNetAssets { /** * Assets constructor. * - * @param string $module_url The url of this module. - * @param string $version The assets version. - * @param FraudNet $fraud_net The FraudNet entity. - * @param Environment $environment The environment. - * @param Settings $settings The Settings. - * @param string[] $enabled_ppcp_gateways The list of enabled PayPal gateways. - * @param string $context The current context. - * @param bool $is_fraudnet_enabled true if FraudNet support is enabled in settings, otherwise false. + * @param string $module_url The url of this module. + * @param string $version The assets version. + * @param FraudNet $fraud_net The FraudNet entity. + * @param Environment $environment The environment. + * @param Settings $settings The Settings. + * @param string[] $enabled_ppcp_gateways The list of enabled PayPal gateways. + * @param SessionHandler $session_handler The session handler. + * @param bool $is_fraudnet_enabled true if FraudNet support is enabled in settings, otherwise false. */ public function __construct( string $module_url, @@ -95,7 +97,7 @@ class FraudNetAssets { Environment $environment, Settings $settings, array $enabled_ppcp_gateways, - string $context, + SessionHandler $session_handler, bool $is_fraudnet_enabled ) { $this->module_url = $module_url; @@ -104,7 +106,7 @@ class FraudNetAssets { $this->environment = $environment; $this->settings = $settings; $this->enabled_ppcp_gateways = $enabled_ppcp_gateways; - $this->context = $context; + $this->session_handler = $session_handler; $this->is_fraudnet_enabled = $is_fraudnet_enabled; } @@ -151,7 +153,7 @@ class FraudNetAssets { $is_pui_gateway_enabled = in_array( PayUponInvoiceGateway::ID, $this->enabled_ppcp_gateways, true ); $is_only_standard_gateway_enabled = $this->enabled_ppcp_gateways === array( PayPalGateway::ID ); - if ( $this->context !== 'checkout' || $is_only_standard_gateway_enabled ) { + if ( $this->context() !== 'checkout' || $is_only_standard_gateway_enabled ) { return $this->is_fraudnet_enabled && $this->are_buttons_enabled_for_context(); } @@ -168,19 +170,64 @@ class FraudNetAssets { if ( ! in_array( PayPalGateway::ID, $this->enabled_ppcp_gateways, true ) ) { return false; } - - $location_prefix = $this->context === 'checkout' ? '' : "{$this->context}_"; - $setting_name = "button_{$location_prefix}enabled"; - $buttons_enabled_for_context = $this->settings->has( $setting_name ) && $this->settings->get( $setting_name ); - - if ( $this->context === 'product' ) { - return $buttons_enabled_for_context || $this->settings->has( 'mini-cart' ) && $this->settings->get( 'mini-cart' ); + try { + $button_locations = $this->settings->get( 'smart_button_locations' ); + } catch ( NotFoundException $exception ) { + return false; } - if ( $this->context === 'pay-now' ) { + if ( $this->context() === 'pay-now' ) { return true; } - return $buttons_enabled_for_context; + if ( $this->context() === 'product' ) { + return in_array( 'product', $button_locations, true ) || in_array( 'mini-cart', $button_locations, true ); + } + + return in_array( $this->context(), $button_locations, true ); + } + + /** + * Get context. + * + * @return string + */ + protected function context(): string { + $context = 'mini-cart'; + if ( is_product() || wc_post_content_has_shortcode( 'product_page' ) ) { + $context = 'product'; + } + if ( is_cart() ) { + $context = 'cart'; + } + if ( is_checkout() && ! $this->is_paypal_continuation() ) { + $context = 'checkout'; + } + if ( is_checkout_pay_page() ) { + $context = 'pay-now'; + } + + return $context; + } + + /** + * Whether is PayPal continuation or not. + * + * @return bool + */ + protected function is_paypal_continuation(): bool { + $order = $this->session_handler->order(); + if ( ! $order ) { + return false; + } + $source = $order->payment_source(); + if ( $source && $source->card() ) { + return false; // Ignore for DCC. + } + if ( 'card' === $this->session_handler->funding_source() ) { + return false; // Ignore for card buttons. + } + + return true; } } From 568b809834b1535ff0f392b10530a26de97347a6 Mon Sep 17 00:00:00 2001 From: emilicastells Date: Mon, 13 Feb 2023 11:01:51 +0100 Subject: [PATCH 35/35] Move context logic to trait --- .../ppcp-button/src/Assets/SmartButton.php | 45 +------------- .../ppcp-button/src/Helper/ContextTrait.php | 62 +++++++++++++++++++ .../src/Assets/FraudNetAssets.php | 48 +------------- 3 files changed, 67 insertions(+), 88 deletions(-) create mode 100644 modules/ppcp-button/src/Helper/ContextTrait.php diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index 53af6033e..5956619fb 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -23,6 +23,7 @@ use WooCommerce\PayPalCommerce\Button\Endpoint\DataClientIdEndpoint; use WooCommerce\PayPalCommerce\Button\Endpoint\RequestData; use WooCommerce\PayPalCommerce\Button\Endpoint\SaveCheckoutFormEndpoint; use WooCommerce\PayPalCommerce\Button\Endpoint\StartPayPalVaultingEndpoint; +use WooCommerce\PayPalCommerce\Button\Helper\ContextTrait; use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply; use WooCommerce\PayPalCommerce\Onboarding\Environment; use WooCommerce\PayPalCommerce\Session\SessionHandler; @@ -41,7 +42,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; */ class SmartButton implements SmartButtonInterface { - use FreeTrialHandlerTrait; + use FreeTrialHandlerTrait, ContextTrait; /** * The Settings status helper. @@ -1067,48 +1068,6 @@ class SmartButton implements SmartButtonInterface { } } - /** - * The current context. - * - * @return string - */ - private function context(): string { - $context = 'mini-cart'; - if ( is_product() || wc_post_content_has_shortcode( 'product_page' ) ) { - $context = 'product'; - } - if ( is_cart() ) { - $context = 'cart'; - } - if ( is_checkout() && ! $this->is_paypal_continuation() ) { - $context = 'checkout'; - } - if ( is_checkout_pay_page() ) { - $context = 'pay-now'; - } - return $context; - } - - /** - * Checks if PayPal payment was already initiated (on the product or cart pages). - * - * @return bool - */ - private function is_paypal_continuation(): bool { - $order = $this->session_handler->order(); - if ( ! $order ) { - return false; - } - $source = $order->payment_source(); - if ( $source && $source->card() ) { - return false; // Ignore for DCC. - } - if ( 'card' === $this->session_handler->funding_source() ) { - return false; // Ignore for card buttons. - } - return true; - } - /** * Whether DCC is enabled or not. * diff --git a/modules/ppcp-button/src/Helper/ContextTrait.php b/modules/ppcp-button/src/Helper/ContextTrait.php new file mode 100644 index 000000000..79abfa181 --- /dev/null +++ b/modules/ppcp-button/src/Helper/ContextTrait.php @@ -0,0 +1,62 @@ +is_paypal_continuation() ) { + $context = 'checkout'; + } + + if ( is_checkout_pay_page() ) { + $context = 'pay-now'; + } + + return $context; + } + + /** + * Checks if PayPal payment was already initiated (on the product or cart pages). + * + * @return bool + */ + private function is_paypal_continuation(): bool { + $order = $this->session_handler->order(); + if ( ! $order ) { + return false; + } + + $source = $order->payment_source(); + if ( $source && $source->card() ) { + return false; // Ignore for DCC. + } + + if ( 'card' === $this->session_handler->funding_source() ) { + return false; // Ignore for card buttons. + } + + return true; + } +} diff --git a/modules/ppcp-wc-gateway/src/Assets/FraudNetAssets.php b/modules/ppcp-wc-gateway/src/Assets/FraudNetAssets.php index 59cda7cd7..63f41e8c3 100644 --- a/modules/ppcp-wc-gateway/src/Assets/FraudNetAssets.php +++ b/modules/ppcp-wc-gateway/src/Assets/FraudNetAssets.php @@ -9,6 +9,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\WcGateway\Assets; +use WooCommerce\PayPalCommerce\Button\Helper\ContextTrait; use WooCommerce\PayPalCommerce\Onboarding\Environment; use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException; @@ -22,6 +23,8 @@ use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; */ class FraudNetAssets { + use ContextTrait; + /** * The URL of this module. * @@ -158,7 +161,6 @@ class FraudNetAssets { } return $is_pui_gateway_enabled ? true : $this->is_fraudnet_enabled; - } /** @@ -186,48 +188,4 @@ class FraudNetAssets { return in_array( $this->context(), $button_locations, true ); } - - /** - * Get context. - * - * @return string - */ - protected function context(): string { - $context = 'mini-cart'; - if ( is_product() || wc_post_content_has_shortcode( 'product_page' ) ) { - $context = 'product'; - } - if ( is_cart() ) { - $context = 'cart'; - } - if ( is_checkout() && ! $this->is_paypal_continuation() ) { - $context = 'checkout'; - } - if ( is_checkout_pay_page() ) { - $context = 'pay-now'; - } - - return $context; - } - - /** - * Whether is PayPal continuation or not. - * - * @return bool - */ - protected function is_paypal_continuation(): bool { - $order = $this->session_handler->order(); - if ( ! $order ) { - return false; - } - $source = $order->payment_source(); - if ( $source && $source->card() ) { - return false; // Ignore for DCC. - } - if ( 'card' === $this->session_handler->funding_source() ) { - return false; // Ignore for card buttons. - } - - return true; - } }