From 36ab073c4121ec78b17ea0a9fd67f4b7ebfb753b Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Tue, 23 Jan 2024 19:00:48 +0400 Subject: [PATCH 01/16] Create a trait for handling 3DS details --- .../Processor/ThreeDSecureHandlingTrait.php | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 modules/ppcp-wc-gateway/src/Processor/ThreeDSecureHandlingTrait.php diff --git a/modules/ppcp-wc-gateway/src/Processor/ThreeDSecureHandlingTrait.php b/modules/ppcp-wc-gateway/src/Processor/ThreeDSecureHandlingTrait.php new file mode 100644 index 000000000..a7ed71792 --- /dev/null +++ b/modules/ppcp-wc-gateway/src/Processor/ThreeDSecureHandlingTrait.php @@ -0,0 +1,76 @@ +payment_source(); + if ( ! $payment_source ) { + return; + } + + $authentication_result = $payment_source->properties()->authentication_result ?? null; + + if ( $authentication_result ) { + $card_authentication_result_factory = new CardAuthenticationResultFactory(); + $result = $card_authentication_result_factory->from_paypal_response( $authentication_result ); + + $three_d_response_order_note_title = __( '3DS authentication result', 'woocommerce-paypal-payments' ); + /* translators: %1$s is 3DS order note title, %2$s is 3DS order note result markup */ + $three_d_response_order_note_format = __( '%1$s %2$s', 'woocommerce-paypal-payments' ); + $three_d_response_order_note_result_format = ''; + $three_d_response_order_note_result = sprintf( + $three_d_response_order_note_result_format, + /* translators: %s is enrollment status */ + sprintf( __( 'Enrollment Status: %s', 'woocommerce-paypal-payments' ), esc_html( $result->enrollment_status() ) ), + /* translators: %s is authentication status */ + sprintf( __( 'Authentication Status: %s', 'woocommerce-paypal-payments' ), esc_html( $result->authentication_result() ) ) + ); + $three_d_response_order_note = sprintf( + $three_d_response_order_note_format, + esc_html( $three_d_response_order_note_title ), + wp_kses_post( $three_d_response_order_note_result ) + ); + $wc_order->add_order_note( $three_d_response_order_note ); + $wc_order->update_meta_data( PayPalGateway::THREE_D_AUTH_RESULT_META_KEY, $three_d ); + $wc_order->save_meta_data(); + + /** + * Fired when the 3DS secure information is added to WC order. + */ + do_action( 'woocommerce_paypal_payments_thee_d_secure_added', $wc_order, $order ); + } + } +} From 498efd72ad17e22102bcfd79ae1bfdc8a5268373 Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Tue, 23 Jan 2024 19:01:28 +0400 Subject: [PATCH 02/16] Add 3DS meta key --- composer.lock | 2 +- modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.lock b/composer.lock index 450c96e96..afd6af3dc 100644 --- a/composer.lock +++ b/composer.lock @@ -4996,5 +4996,5 @@ "ext-json": "*" }, "platform-dev": [], - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php index 4c4bc4671..da490e70d 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php @@ -51,6 +51,7 @@ class PayPalGateway extends \WC_Payment_Gateway { const FEES_META_KEY = '_ppcp_paypal_fees'; const REFUND_FEES_META_KEY = '_ppcp_paypal_refund_fees'; const REFUNDS_META_KEY = '_ppcp_refunds'; + const THREE_D_AUTH_RESULT_META_KEY = '_ppcp_paypal_3DS_auth_result'; /** * The Settings Renderer. From fc270805e283d4525a929ab2e49ba110c401af64 Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Tue, 23 Jan 2024 19:02:28 +0400 Subject: [PATCH 03/16] Add action after the new order status is authorized --- .../src/Processor/PaymentsStatusHandlingTrait.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/ppcp-wc-gateway/src/Processor/PaymentsStatusHandlingTrait.php b/modules/ppcp-wc-gateway/src/Processor/PaymentsStatusHandlingTrait.php index e1f4f9654..e6186badf 100644 --- a/modules/ppcp-wc-gateway/src/Processor/PaymentsStatusHandlingTrait.php +++ b/modules/ppcp-wc-gateway/src/Processor/PaymentsStatusHandlingTrait.php @@ -112,6 +112,10 @@ trait PaymentsStatusHandlingTrait { 'on-hold', __( 'Awaiting payment.', 'woocommerce-paypal-payments' ) ); + /** + * Fired when PayPal order is authorized. + */ + do_action( 'woocommerce_paypal_payments_order_authorized', $wc_order, $authorization ); break; case AuthorizationStatus::DENIED: $wc_order->update_status( From 39f648b084444e1c5e4c97608fda06dd1c0e2e3e Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Tue, 23 Jan 2024 19:03:36 +0400 Subject: [PATCH 04/16] Handle the 3DS details when captured & authorized --- .../ppcp-wc-gateway/src/WCGatewayModule.php | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-wc-gateway/src/WCGatewayModule.php b/modules/ppcp-wc-gateway/src/WCGatewayModule.php index 79a6dc127..fb3168803 100644 --- a/modules/ppcp-wc-gateway/src/WCGatewayModule.php +++ b/modules/ppcp-wc-gateway/src/WCGatewayModule.php @@ -11,10 +11,11 @@ namespace WooCommerce\PayPalCommerce\WcGateway; use Psr\Log\LoggerInterface; use Throwable; +use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache; use WooCommerce\PayPalCommerce\WcGateway\Endpoint\RefreshFeatureStatusEndpoint; -use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper; +use WooCommerce\PayPalCommerce\WcGateway\Processor\ThreeDSecureHandlingTrait; use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider; use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface; use WC_Order; @@ -56,6 +57,8 @@ use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; */ class WCGatewayModule implements ModuleInterface { + use ThreeDSecureHandlingTrait; + /** * {@inheritDoc} */ @@ -92,7 +95,7 @@ class WCGatewayModule implements ModuleInterface { add_action( 'woocommerce_paypal_payments_order_captured', - function ( WC_Order $wc_order, Capture $capture ) { + function ( WC_Order $wc_order, Capture $capture ) use ( $c ) { $breakdown = $capture->seller_receivable_breakdown(); if ( $breakdown ) { $wc_order->update_meta_data( PayPalGateway::FEES_META_KEY, $breakdown->to_array() ); @@ -141,6 +144,19 @@ class WCGatewayModule implements ModuleInterface { ); $wc_order->add_order_note( $cvv_response_order_note ); } + + $order = $c->get( 'session.handler' )->order(); + $this->handle_three_d_secure( $order, $wc_order ); + }, + 10, + 2 + ); + + add_action( + 'woocommerce_paypal_payments_order_authorized', + function ( WC_Order $wc_order, Authorization $authorization ) use ( $c ) { + $order = $c->get( 'session.handler' )->order(); + $this->handle_three_d_secure( $order, $wc_order ); }, 10, 2 From 6fdf524c6b2cd477d50f085a8552b2f433a3d428 Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Tue, 23 Jan 2024 19:07:54 +0400 Subject: [PATCH 05/16] Improve the docs --- .../src/Processor/ThreeDSecureHandlingTrait.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-wc-gateway/src/Processor/ThreeDSecureHandlingTrait.php b/modules/ppcp-wc-gateway/src/Processor/ThreeDSecureHandlingTrait.php index a7ed71792..2c971dbb7 100644 --- a/modules/ppcp-wc-gateway/src/Processor/ThreeDSecureHandlingTrait.php +++ b/modules/ppcp-wc-gateway/src/Processor/ThreeDSecureHandlingTrait.php @@ -20,10 +20,10 @@ use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; trait ThreeDSecureHandlingTrait { /** - * Handles the 3DS secure details. + * Handles the 3DS details. * - * Adds the order note with 3DS secure details. - * Adds the order meta with 3DS secure details. + * Adds the order note with 3DS details. + * Adds the order meta with 3DS details. * * @param Order $order The PayPal order. * @param WC_Order $wc_order The WC order. From 6c40b3da449d2f59fc51cfa18b11c32fcbb06f5a Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Tue, 23 Jan 2024 19:08:55 +0400 Subject: [PATCH 06/16] Improve the docs --- .../ppcp-wc-gateway/src/Processor/ThreeDSecureHandlingTrait.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-wc-gateway/src/Processor/ThreeDSecureHandlingTrait.php b/modules/ppcp-wc-gateway/src/Processor/ThreeDSecureHandlingTrait.php index 2c971dbb7..38c73af8d 100644 --- a/modules/ppcp-wc-gateway/src/Processor/ThreeDSecureHandlingTrait.php +++ b/modules/ppcp-wc-gateway/src/Processor/ThreeDSecureHandlingTrait.php @@ -68,7 +68,7 @@ trait ThreeDSecureHandlingTrait { $wc_order->save_meta_data(); /** - * Fired when the 3DS secure information is added to WC order. + * Fired when the 3DS information is added to WC order. */ do_action( 'woocommerce_paypal_payments_thee_d_secure_added', $wc_order, $order ); } From 676a1501e50f1daedebd5d783a60ebb618e496f6 Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Tue, 23 Jan 2024 19:15:40 +0400 Subject: [PATCH 07/16] Include the Liability Shift --- .../src/Processor/ThreeDSecureHandlingTrait.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-wc-gateway/src/Processor/ThreeDSecureHandlingTrait.php b/modules/ppcp-wc-gateway/src/Processor/ThreeDSecureHandlingTrait.php index 38c73af8d..14b21af72 100644 --- a/modules/ppcp-wc-gateway/src/Processor/ThreeDSecureHandlingTrait.php +++ b/modules/ppcp-wc-gateway/src/Processor/ThreeDSecureHandlingTrait.php @@ -50,9 +50,12 @@ trait ThreeDSecureHandlingTrait { $three_d_response_order_note_result_format = '
  • %1$s
  • %2$s
  • +
  • %3$s
'; $three_d_response_order_note_result = sprintf( $three_d_response_order_note_result_format, + /* translators: %s is liability shift */ + sprintf( __( 'Liability Shift: %s', 'woocommerce-paypal-payments' ), esc_html( $result->liability_shift() ) ), /* translators: %s is enrollment status */ sprintf( __( 'Enrollment Status: %s', 'woocommerce-paypal-payments' ), esc_html( $result->enrollment_status() ) ), /* translators: %s is authentication status */ @@ -64,7 +67,7 @@ trait ThreeDSecureHandlingTrait { wp_kses_post( $three_d_response_order_note_result ) ); $wc_order->add_order_note( $three_d_response_order_note ); - $wc_order->update_meta_data( PayPalGateway::THREE_D_AUTH_RESULT_META_KEY, $three_d ); + $wc_order->update_meta_data( PayPalGateway::THREE_D_AUTH_RESULT_META_KEY, $result->to_array() ); $wc_order->save_meta_data(); /** From eeb0873a9a7509f421d4dae4799491345c73e665 Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Tue, 23 Jan 2024 19:32:07 +0400 Subject: [PATCH 08/16] Include the Card Brand --- .../src/Processor/ThreeDSecureHandlingTrait.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-wc-gateway/src/Processor/ThreeDSecureHandlingTrait.php b/modules/ppcp-wc-gateway/src/Processor/ThreeDSecureHandlingTrait.php index 14b21af72..c7010d0d5 100644 --- a/modules/ppcp-wc-gateway/src/Processor/ThreeDSecureHandlingTrait.php +++ b/modules/ppcp-wc-gateway/src/Processor/ThreeDSecureHandlingTrait.php @@ -39,6 +39,7 @@ trait ThreeDSecureHandlingTrait { } $authentication_result = $payment_source->properties()->authentication_result ?? null; + $card_brand = $payment_source->properties()->brand ?? __( 'N/A', 'woocommerce-paypal-payments' ); if ( $authentication_result ) { $card_authentication_result_factory = new CardAuthenticationResultFactory(); @@ -51,6 +52,7 @@ trait ThreeDSecureHandlingTrait {
  • %1$s
  • %2$s
  • %3$s
  • +
  • %4$s
  • '; $three_d_response_order_note_result = sprintf( $three_d_response_order_note_result_format, @@ -59,15 +61,20 @@ trait ThreeDSecureHandlingTrait { /* translators: %s is enrollment status */ sprintf( __( 'Enrollment Status: %s', 'woocommerce-paypal-payments' ), esc_html( $result->enrollment_status() ) ), /* translators: %s is authentication status */ - sprintf( __( 'Authentication Status: %s', 'woocommerce-paypal-payments' ), esc_html( $result->authentication_result() ) ) + sprintf( __( 'Authentication Status: %s', 'woocommerce-paypal-payments' ), esc_html( $result->authentication_result() ) ), + /* translators: %s is card brand */ + sprintf( __( 'Card Brand: %s', 'woocommerce-paypal-payments' ), esc_html( $card_brand ) ) ); $three_d_response_order_note = sprintf( $three_d_response_order_note_format, esc_html( $three_d_response_order_note_title ), wp_kses_post( $three_d_response_order_note_result ) ); + $wc_order->add_order_note( $three_d_response_order_note ); - $wc_order->update_meta_data( PayPalGateway::THREE_D_AUTH_RESULT_META_KEY, $result->to_array() ); + + $meta_details = array_merge( $result->to_array(), array( 'card_brand' => $card_brand ) ); + $wc_order->update_meta_data( PayPalGateway::THREE_D_AUTH_RESULT_META_KEY, $meta_details ); $wc_order->save_meta_data(); /** From 1acf16a8787b1f9a1cbdaa0b5ee88afe39a3ad25 Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Tue, 30 Jan 2024 17:33:54 +0400 Subject: [PATCH 09/16] Process 3DS meta only for ACDC --- .../ppcp-wc-gateway/src/Processor/ThreeDSecureHandlingTrait.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-wc-gateway/src/Processor/ThreeDSecureHandlingTrait.php b/modules/ppcp-wc-gateway/src/Processor/ThreeDSecureHandlingTrait.php index c7010d0d5..e98c66862 100644 --- a/modules/ppcp-wc-gateway/src/Processor/ThreeDSecureHandlingTrait.php +++ b/modules/ppcp-wc-gateway/src/Processor/ThreeDSecureHandlingTrait.php @@ -34,7 +34,7 @@ trait ThreeDSecureHandlingTrait { ): void { $payment_source = $order->payment_source(); - if ( ! $payment_source ) { + if ( ! $payment_source || $payment_source->name() !== 'card' ) { return; } From 843917f061ae3e7553e8c018bd38524fd18b9f2a Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Tue, 30 Jan 2024 17:39:21 +0400 Subject: [PATCH 10/16] Pass the fraud to Authorization --- modules/ppcp-api-client/services.php | 2 +- .../src/Factory/AuthorizationFactory.php | 23 ++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-api-client/services.php b/modules/ppcp-api-client/services.php index be2872c3d..12e218387 100644 --- a/modules/ppcp-api-client/services.php +++ b/modules/ppcp-api-client/services.php @@ -417,7 +417,7 @@ return array( return new PaymentsFactory( $authorizations_factory, $capture_factory, $refund_factory ); }, 'api.factory.authorization' => static function ( ContainerInterface $container ): AuthorizationFactory { - return new AuthorizationFactory(); + return new AuthorizationFactory( $container->get( 'api.factory.fraud-processor-response' ) ); }, 'api.factory.exchange-rate' => static function ( ContainerInterface $container ): ExchangeRateFactory { return new ExchangeRateFactory(); diff --git a/modules/ppcp-api-client/src/Factory/AuthorizationFactory.php b/modules/ppcp-api-client/src/Factory/AuthorizationFactory.php index c65e764af..d16aee2fb 100644 --- a/modules/ppcp-api-client/src/Factory/AuthorizationFactory.php +++ b/modules/ppcp-api-client/src/Factory/AuthorizationFactory.php @@ -19,6 +19,22 @@ use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; */ class AuthorizationFactory { + /** + * The FraudProcessorResponseFactory factory. + * + * @var FraudProcessorResponseFactory + */ + protected $fraud_processor_response_factory; + + /** + * AuthorizationFactory constructor. + * + * @param FraudProcessorResponseFactory $fraud_processor_response_factory The FraudProcessorResponseFactory factory. + */ + public function __construct( FraudProcessorResponseFactory $fraud_processor_response_factory ) { + $this->fraud_processor_response_factory = $fraud_processor_response_factory; + } + /** * Returns an Authorization based off a PayPal response. * @@ -42,12 +58,17 @@ class AuthorizationFactory { $reason = $data->status_details->reason ?? null; + $fraud_processor_response = isset( $data->processor_response ) ? + $this->fraud_processor_response_factory->from_paypal_response( $data->processor_response ) + : null; + return new Authorization( $data->id, new AuthorizationStatus( $data->status, $reason ? new AuthorizationStatusDetails( $reason ) : null - ) + ), + $fraud_processor_response ); } } From e63baa6c430ae6608b072efa1969b53118dcfae3 Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Tue, 30 Jan 2024 17:42:59 +0400 Subject: [PATCH 11/16] Add the FraudProcessorResponse to Authorization --- .../src/Entity/Authorization.php | 37 ++++++++++++++++--- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/modules/ppcp-api-client/src/Entity/Authorization.php b/modules/ppcp-api-client/src/Entity/Authorization.php index ae8e8f591..007e12475 100644 --- a/modules/ppcp-api-client/src/Entity/Authorization.php +++ b/modules/ppcp-api-client/src/Entity/Authorization.php @@ -28,19 +28,29 @@ class Authorization { */ private $authorization_status; + /** + * The fraud processor response (AVS, CVV ...). + * + * @var FraudProcessorResponse|null + */ + protected $fraud_processor_response; + /** * Authorization constructor. * - * @param string $id The id. - * @param AuthorizationStatus $authorization_status The status. + * @param string $id The id. + * @param AuthorizationStatus $authorization_status The status. + * @param FraudProcessorResponse|null $fraud_processor_response The fraud processor response (AVS, CVV ...). */ public function __construct( string $id, - AuthorizationStatus $authorization_status + AuthorizationStatus $authorization_status, + ?FraudProcessorResponse $fraud_processor_response ) { - $this->id = $id; - $this->authorization_status = $authorization_status; + $this->id = $id; + $this->authorization_status = $authorization_status; + $this->fraud_processor_response = $fraud_processor_response; } /** @@ -71,15 +81,30 @@ class Authorization { $this->authorization_status->is( AuthorizationStatus::PENDING ); } + /** + * Returns the fraud processor response (AVS, CVV ...). + * + * @return FraudProcessorResponse|null + */ + public function fraud_processor_response() : ?FraudProcessorResponse { + return $this->fraud_processor_response; + } + /** * Returns the object as array. * * @return array */ public function to_array(): array { - return array( + $data = array( 'id' => $this->id, 'status' => $this->authorization_status->name(), ); + + if ( $this->fraud_processor_response ) { + $data['fraud_processor_response'] = $this->fraud_processor_response->to_array(); + } + + return $data; } } From 4b16638abd870924290e51d41a5c672a4cb3da53 Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Wed, 31 Jan 2024 16:41:53 +0400 Subject: [PATCH 12/16] Refactor the trait to handle the ACDC order info --- ...p => CreditCardOrderInfoHandlingTrait.php} | 60 ++++++++++++++++++- .../ppcp-wc-gateway/src/WCGatewayModule.php | 46 +++----------- 2 files changed, 65 insertions(+), 41 deletions(-) rename modules/ppcp-wc-gateway/src/Processor/{ThreeDSecureHandlingTrait.php => CreditCardOrderInfoHandlingTrait.php} (53%) diff --git a/modules/ppcp-wc-gateway/src/Processor/ThreeDSecureHandlingTrait.php b/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php similarity index 53% rename from modules/ppcp-wc-gateway/src/Processor/ThreeDSecureHandlingTrait.php rename to modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php index e98c66862..1c65130e8 100644 --- a/modules/ppcp-wc-gateway/src/Processor/ThreeDSecureHandlingTrait.php +++ b/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php @@ -1,6 +1,6 @@ payment_source(); + if ( ! $payment_source || $payment_source->name() !== 'card' ) { + return; + } + + + $fraud_responses = $fraud->to_array(); + $avs_response_order_note_title = __( 'Address Verification Result', 'woocommerce-paypal-payments' ); + /* translators: %1$s is AVS order note title, %2$s is AVS order note result markup */ + $avs_response_order_note_format = __( '%1$s %2$s', 'woocommerce-paypal-payments' ); + $avs_response_order_note_result_format = '
      +
    • %1$s
    • +
        +
      • %2$s
      • +
      • %3$s
      • +
      +
    '; + $avs_response_order_note_result = sprintf( + $avs_response_order_note_result_format, + /* translators: %s is fraud AVS code */ + sprintf( __( 'AVS: %s', 'woocommerce-paypal-payments' ), esc_html( $fraud_responses['avs_code'] ) ), + /* translators: %s is fraud AVS address match */ + sprintf( __( 'Address Match: %s', 'woocommerce-paypal-payments' ), esc_html( $fraud_responses['address_match'] ) ), + /* translators: %s is fraud AVS postal match */ + sprintf( __( 'Postal Match: %s', 'woocommerce-paypal-payments' ), esc_html( $fraud_responses['postal_match'] ) ) + ); + $avs_response_order_note = sprintf( + $avs_response_order_note_format, + esc_html( $avs_response_order_note_title ), + wp_kses_post( $avs_response_order_note_result ) + ); + $wc_order->add_order_note( $avs_response_order_note ); + + $cvv_response_order_note_format = '
    • %1$s
    '; + $cvv_response_order_note = sprintf( + $cvv_response_order_note_format, + /* translators: %s is fraud CVV match */ + sprintf( __( 'CVV2 Match: %s', 'woocommerce-paypal-payments' ), esc_html( $fraud_responses['cvv_match'] ) ) + ); + $wc_order->add_order_note( $cvv_response_order_note ); + } } diff --git a/modules/ppcp-wc-gateway/src/WCGatewayModule.php b/modules/ppcp-wc-gateway/src/WCGatewayModule.php index fb3168803..d47301787 100644 --- a/modules/ppcp-wc-gateway/src/WCGatewayModule.php +++ b/modules/ppcp-wc-gateway/src/WCGatewayModule.php @@ -15,7 +15,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache; use WooCommerce\PayPalCommerce\WcGateway\Endpoint\RefreshFeatureStatusEndpoint; -use WooCommerce\PayPalCommerce\WcGateway\Processor\ThreeDSecureHandlingTrait; +use WooCommerce\PayPalCommerce\WcGateway\Processor\CreditCardOrderInfoHandlingTrait; use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider; use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface; use WC_Order; @@ -57,7 +57,7 @@ use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; */ class WCGatewayModule implements ModuleInterface { - use ThreeDSecureHandlingTrait; + use CreditCardOrderInfoHandlingTrait; /** * {@inheritDoc} @@ -107,45 +107,11 @@ class WCGatewayModule implements ModuleInterface { $wc_order->save_meta_data(); } + $order = $c->get( 'session.handler' )->order(); $fraud = $capture->fraud_processor_response(); if ( $fraud ) { - $fraud_responses = $fraud->to_array(); - $avs_response_order_note_title = __( 'Address Verification Result', 'woocommerce-paypal-payments' ); - /* translators: %1$s is AVS order note title, %2$s is AVS order note result markup */ - $avs_response_order_note_format = __( '%1$s %2$s', 'woocommerce-paypal-payments' ); - $avs_response_order_note_result_format = '
      -
    • %1$s
    • -
        -
      • %2$s
      • -
      • %3$s
      • -
      -
    '; - $avs_response_order_note_result = sprintf( - $avs_response_order_note_result_format, - /* translators: %s is fraud AVS code */ - sprintf( __( 'AVS: %s', 'woocommerce-paypal-payments' ), esc_html( $fraud_responses['avs_code'] ) ), - /* translators: %s is fraud AVS address match */ - sprintf( __( 'Address Match: %s', 'woocommerce-paypal-payments' ), esc_html( $fraud_responses['address_match'] ) ), - /* translators: %s is fraud AVS postal match */ - sprintf( __( 'Postal Match: %s', 'woocommerce-paypal-payments' ), esc_html( $fraud_responses['postal_match'] ) ) - ); - $avs_response_order_note = sprintf( - $avs_response_order_note_format, - esc_html( $avs_response_order_note_title ), - wp_kses_post( $avs_response_order_note_result ) - ); - $wc_order->add_order_note( $avs_response_order_note ); - - $cvv_response_order_note_format = '
    • %1$s
    '; - $cvv_response_order_note = sprintf( - $cvv_response_order_note_format, - /* translators: %s is fraud CVV match */ - sprintf( __( 'CVV2 Match: %s', 'woocommerce-paypal-payments' ), esc_html( $fraud_responses['cvv_match'] ) ) - ); - $wc_order->add_order_note( $cvv_response_order_note ); + $this->handle_fraud( $fraud, $order, $wc_order ); } - - $order = $c->get( 'session.handler' )->order(); $this->handle_three_d_secure( $order, $wc_order ); }, 10, @@ -156,6 +122,10 @@ class WCGatewayModule implements ModuleInterface { 'woocommerce_paypal_payments_order_authorized', function ( WC_Order $wc_order, Authorization $authorization ) use ( $c ) { $order = $c->get( 'session.handler' )->order(); + $fraud = $authorization->fraud_processor_response(); + if ( $fraud ) { + $this->handle_fraud( $fraud, $order, $wc_order ); + } $this->handle_three_d_secure( $order, $wc_order ); }, 10, From c3ae6ad8a6948b8274844fc4878d1ef8b62f48ea Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Wed, 31 Jan 2024 16:51:33 +0400 Subject: [PATCH 13/16] Fix the tests --- tests/PHPUnit/ApiClient/Entity/AuthorizationTest.php | 4 ++-- .../ApiClient/Factory/AuthorizationFactoryTest.php | 11 ++++++++--- .../Processor/AuthorizedPaymentsProcessorTest.php | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/tests/PHPUnit/ApiClient/Entity/AuthorizationTest.php b/tests/PHPUnit/ApiClient/Entity/AuthorizationTest.php index 8843ce002..c5895a2f4 100644 --- a/tests/PHPUnit/ApiClient/Entity/AuthorizationTest.php +++ b/tests/PHPUnit/ApiClient/Entity/AuthorizationTest.php @@ -11,7 +11,7 @@ class AuthorizationTest extends TestCase public function testIdAndStatus() { $authorizationStatus = \Mockery::mock(AuthorizationStatus::class); - $testee = new Authorization('foo', $authorizationStatus); + $testee = new Authorization('foo', $authorizationStatus, null); $this->assertEquals('foo', $testee->id()); $this->assertEquals($authorizationStatus, $testee->status()); @@ -22,7 +22,7 @@ class AuthorizationTest extends TestCase $authorizationStatus = \Mockery::mock(AuthorizationStatus::class); $authorizationStatus->expects('name')->andReturn('CAPTURED'); - $testee = new Authorization('foo', $authorizationStatus); + $testee = new Authorization('foo', $authorizationStatus, null); $expected = [ 'id' => 'foo', diff --git a/tests/PHPUnit/ApiClient/Factory/AuthorizationFactoryTest.php b/tests/PHPUnit/ApiClient/Factory/AuthorizationFactoryTest.php index 12a76a016..71df98376 100644 --- a/tests/PHPUnit/ApiClient/Factory/AuthorizationFactoryTest.php +++ b/tests/PHPUnit/ApiClient/Factory/AuthorizationFactoryTest.php @@ -17,8 +17,9 @@ class AuthorizationFactoryTest extends TestCase 'id' => 'foo', 'status' => 'CAPTURED', ]; + $fraudProcessorResponseFactory = \Mockery::mock(FraudProcessorResponseFactory::class); - $testee = new AuthorizationFactory(); + $testee = new AuthorizationFactory($fraudProcessorResponseFactory); $result = $testee->from_paypal_response($response); $this->assertInstanceOf(Authorization::class, $result); @@ -36,7 +37,9 @@ class AuthorizationFactoryTest extends TestCase 'status' => 'CAPTURED', ]; - $testee = new AuthorizationFactory(); + $fraudProcessorResponseFactory = \Mockery::mock(FraudProcessorResponseFactory::class); + + $testee = new AuthorizationFactory($fraudProcessorResponseFactory); $testee->from_paypal_response($response); } @@ -47,7 +50,9 @@ class AuthorizationFactoryTest extends TestCase 'id' => 'foo', ]; - $testee = new AuthorizationFactory(); + $fraudProcessorResponseFactory = \Mockery::mock(FraudProcessorResponseFactory::class); + + $testee = new AuthorizationFactory($fraudProcessorResponseFactory); $testee->from_paypal_response($response); } } diff --git a/tests/PHPUnit/WcGateway/Processor/AuthorizedPaymentsProcessorTest.php b/tests/PHPUnit/WcGateway/Processor/AuthorizedPaymentsProcessorTest.php index f65338f67..5dd7c588f 100644 --- a/tests/PHPUnit/WcGateway/Processor/AuthorizedPaymentsProcessorTest.php +++ b/tests/PHPUnit/WcGateway/Processor/AuthorizedPaymentsProcessorTest.php @@ -251,7 +251,7 @@ class AuthorizedPaymentsProcessorTest extends TestCase } private function createAuthorization(string $id, string $status): Authorization { - return new Authorization($id, new AuthorizationStatus($status)); + return new Authorization($id, new AuthorizationStatus($status), null); } private function createCapture(string $id, string $status): Capture { From 5c5f459f3b9cd89cf00e5acf90a16569615f6437 Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Wed, 31 Jan 2024 17:03:38 +0400 Subject: [PATCH 14/16] Fix the coding styles --- .../src/Processor/CreditCardOrderInfoHandlingTrait.php | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php b/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php index 1c65130e8..f4d988b04 100644 --- a/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php +++ b/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php @@ -101,7 +101,6 @@ trait CreditCardOrderInfoHandlingTrait { return; } - $fraud_responses = $fraud->to_array(); $avs_response_order_note_title = __( 'Address Verification Result', 'woocommerce-paypal-payments' ); /* translators: %1$s is AVS order note title, %2$s is AVS order note result markup */ From 704bc9cbf06d16e19030819ac0692319f3072e5d Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Wed, 31 Jan 2024 17:18:25 +0400 Subject: [PATCH 15/16] Store the fraud result in meta --- modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php | 1 + .../src/Processor/CreditCardOrderInfoHandlingTrait.php | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php index da490e70d..b787be582 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php @@ -52,6 +52,7 @@ class PayPalGateway extends \WC_Payment_Gateway { const REFUND_FEES_META_KEY = '_ppcp_paypal_refund_fees'; const REFUNDS_META_KEY = '_ppcp_refunds'; const THREE_D_AUTH_RESULT_META_KEY = '_ppcp_paypal_3DS_auth_result'; + const FRAUD_RESULT_META_KEY = '_ppcp_paypal_fraud_result'; /** * The Settings Renderer. diff --git a/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php b/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php index f4d988b04..71e96b90a 100644 --- a/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php +++ b/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php @@ -135,5 +135,13 @@ trait CreditCardOrderInfoHandlingTrait { sprintf( __( 'CVV2 Match: %s', 'woocommerce-paypal-payments' ), esc_html( $fraud_responses['cvv_match'] ) ) ); $wc_order->add_order_note( $cvv_response_order_note ); + + $wc_order->update_meta_data( PayPalGateway::FRAUD_RESULT_META_KEY, $fraud_responses ); + $wc_order->save_meta_data(); + + /** + * Fired when the fraud result information is added to WC order. + */ + do_action( 'woocommerce_paypal_payments_fraud_result_added', $wc_order, $order ); } } From a383a1a166e7e7e80ad77183d36deae4fe282f34 Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Tue, 6 Feb 2024 17:05:36 +0400 Subject: [PATCH 16/16] Check for PayPal order existence --- modules/ppcp-wc-gateway/src/WCGatewayModule.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/modules/ppcp-wc-gateway/src/WCGatewayModule.php b/modules/ppcp-wc-gateway/src/WCGatewayModule.php index d47301787..a4be37d9e 100644 --- a/modules/ppcp-wc-gateway/src/WCGatewayModule.php +++ b/modules/ppcp-wc-gateway/src/WCGatewayModule.php @@ -108,6 +108,10 @@ class WCGatewayModule implements ModuleInterface { } $order = $c->get( 'session.handler' )->order(); + if ( ! $order ) { + return; + } + $fraud = $capture->fraud_processor_response(); if ( $fraud ) { $this->handle_fraud( $fraud, $order, $wc_order ); @@ -122,6 +126,10 @@ class WCGatewayModule implements ModuleInterface { 'woocommerce_paypal_payments_order_authorized', function ( WC_Order $wc_order, Authorization $authorization ) use ( $c ) { $order = $c->get( 'session.handler' )->order(); + if ( ! $order ) { + return; + } + $fraud = $authorization->fraud_processor_response(); if ( $fraud ) { $this->handle_fraud( $fraud, $order, $wc_order );