diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index 12e7abfbd..17e9810ab 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -990,11 +990,21 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages if ( $this->settings->has( '3d_secure_contingency' ) ) { $value = $this->settings->get( '3d_secure_contingency' ); if ( $value ) { - return $value; + return $this->return_3ds_contingency( $value ); } } - return 'SCA_WHEN_REQUIRED'; + return $this->return_3ds_contingency( 'SCA_WHEN_REQUIRED' ); + } + + /** + * Processes and returns the 3D Secure contingency. + * + * @param string $contingency The ThreeD secure contingency. + * @return string + */ + private function return_3ds_contingency( string $contingency ): string { + return apply_filters( 'woocommerce_paypal_payments_three_d_secure_contingency', $contingency ); } /** diff --git a/modules/ppcp-button/src/Endpoint/CreateOrderEndpoint.php b/modules/ppcp-button/src/Endpoint/CreateOrderEndpoint.php index c286b9f20..980e03770 100644 --- a/modules/ppcp-button/src/Endpoint/CreateOrderEndpoint.php +++ b/modules/ppcp-button/src/Endpoint/CreateOrderEndpoint.php @@ -329,6 +329,15 @@ class CreateOrderEndpoint implements EndpointInterface { if ( 'pay-now' === $data['context'] && is_a( $wc_order, \WC_Order::class ) ) { $wc_order->update_meta_data( PayPalGateway::ORDER_ID_META_KEY, $order->id() ); $wc_order->update_meta_data( PayPalGateway::INTENT_META_KEY, $order->intent() ); + + $payer = $order->payer(); + if ( $payer ) { + $payer_email = $payer->email_address(); + if ( $payer_email ) { + $wc_order->update_meta_data( PayPalGateway::ORDER_PAYER_EMAIL_META_KEY, $payer_email ); + } + } + $wc_order->save_meta_data(); do_action( 'woocommerce_paypal_payments_woocommerce_order_created', $wc_order, $order ); diff --git a/modules/ppcp-button/src/Helper/EarlyOrderHandler.php b/modules/ppcp-button/src/Helper/EarlyOrderHandler.php index 03d8fbeed..adff6f56c 100644 --- a/modules/ppcp-button/src/Helper/EarlyOrderHandler.php +++ b/modules/ppcp-button/src/Helper/EarlyOrderHandler.php @@ -159,6 +159,15 @@ class EarlyOrderHandler { $wc_order = wc_get_order( $order_id ); $wc_order->update_meta_data( PayPalGateway::ORDER_ID_META_KEY, $order->id() ); $wc_order->update_meta_data( PayPalGateway::INTENT_META_KEY, $order->intent() ); + + $payer = $order->payer(); + if ( $payer && $wc_order instanceof \WC_Order ) { + $payer_email = $payer->email_address(); + if ( $payer_email ) { + $wc_order->update_meta_data( PayPalGateway::ORDER_PAYER_EMAIL_META_KEY, $payer_email ); + } + } + $wc_order->save_meta_data(); /** diff --git a/modules/ppcp-button/src/Helper/ThreeDSecure.php b/modules/ppcp-button/src/Helper/ThreeDSecure.php index 66c89e4be..1991112b5 100644 --- a/modules/ppcp-button/src/Helper/ThreeDSecure.php +++ b/modules/ppcp-button/src/Helper/ThreeDSecure.php @@ -57,21 +57,24 @@ class ThreeDSecure { * * @link https://developer.paypal.com/docs/business/checkout/add-capabilities/3d-secure/#authenticationresult * - * @param Order $order The order for which the decission is needed. + * @param Order $order The order for which the decision is needed. * * @return int */ public function proceed_with_order( Order $order ): int { + + do_action( 'woocommerce_paypal_payments_three_d_secure_before_check', $order ); + $payment_source = $order->payment_source(); if ( ! $payment_source ) { - return self::NO_DECISION; + return $this->return_decision( self::NO_DECISION, $order ); } if ( ! ( $payment_source->properties()->brand ?? '' ) ) { - return self::NO_DECISION; + return $this->return_decision( self::NO_DECISION, $order ); } if ( ! ( $payment_source->properties()->authentication_result ?? '' ) ) { - return self::NO_DECISION; + return $this->return_decision( self::NO_DECISION, $order ); } $authentication_result = $payment_source->properties()->authentication_result ?? null; @@ -81,18 +84,31 @@ class ThreeDSecure { $this->logger->info( '3DS Authentication Result: ' . wc_print_r( $result->to_array(), true ) ); if ( $result->liability_shift() === AuthResult::LIABILITY_SHIFT_POSSIBLE ) { - return self::PROCCEED; + return $this->return_decision( self::PROCCEED, $order ); } if ( $result->liability_shift() === AuthResult::LIABILITY_SHIFT_UNKNOWN ) { - return self::RETRY; + return $this->return_decision( self::RETRY, $order ); } if ( $result->liability_shift() === AuthResult::LIABILITY_SHIFT_NO ) { - return $this->no_liability_shift( $result ); + return $this->return_decision( $this->no_liability_shift( $result ), $order ); } } - return self::NO_DECISION; + return $this->return_decision( self::NO_DECISION, $order ); + } + + /** + * Processes and returns a ThreeD secure decision. + * + * @param int $decision The ThreeD secure decision. + * @param Order $order The PayPal Order object. + * @return int + */ + public function return_decision( int $decision, Order $order ) { + $decision = apply_filters( 'woocommerce_paypal_payments_three_d_secure_decision', $decision, $order ); + do_action( 'woocommerce_paypal_payments_three_d_secure_after_check', $order, $decision ); + return $decision; } /** diff --git a/modules/ppcp-card-fields/src/CardFieldsModule.php b/modules/ppcp-card-fields/src/CardFieldsModule.php index 213134857..6835108c3 100644 --- a/modules/ppcp-card-fields/src/CardFieldsModule.php +++ b/modules/ppcp-card-fields/src/CardFieldsModule.php @@ -115,17 +115,19 @@ class CardFieldsModule implements ModuleInterface { $settings = $c->get( 'wcgateway.settings' ); assert( $settings instanceof Settings ); + $three_d_secure_contingency = + $settings->has( '3d_secure_contingency' ) + ? apply_filters( 'woocommerce_paypal_payments_three_d_secure_contingency', $settings->get( '3d_secure_contingency' ) ) + : ''; + if ( - $settings->has( '3d_secure_contingency' ) - && ( - $settings->get( '3d_secure_contingency' ) === 'SCA_ALWAYS' - || $settings->get( '3d_secure_contingency' ) === 'SCA_WHEN_REQUIRED' - ) + $three_d_secure_contingency === 'SCA_ALWAYS' + || $three_d_secure_contingency === 'SCA_WHEN_REQUIRED' ) { $data['payment_source']['card'] = array( 'attributes' => array( 'verification' => array( - 'method' => $settings->get( '3d_secure_contingency' ), + 'method' => $three_d_secure_contingency, ), ), ); diff --git a/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php b/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php index 59010f6ca..667175aaf 100644 --- a/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php +++ b/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php @@ -281,7 +281,11 @@ class SavePaymentMethodsModule implements ModuleInterface { $settings = $c->get( 'wcgateway.settings' ); assert( $settings instanceof Settings ); - $verification_method = $settings->has( '3d_secure_contingency' ) ? $settings->get( '3d_secure_contingency' ) : ''; + + $verification_method = + $settings->has( '3d_secure_contingency' ) + ? apply_filters( 'woocommerce_paypal_payments_three_d_secure_contingency', $settings->get( '3d_secure_contingency' ) ) + : ''; $change_payment_method = wc_clean( wp_unslash( $_GET['change_payment_method'] ?? '' ) ); // phpcs:ignore WordPress.Security.NonceVerification diff --git a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php index 73afb085b..2a04aa478 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php @@ -48,6 +48,7 @@ class PayPalGateway extends \WC_Payment_Gateway { const ORDER_ID_META_KEY = '_ppcp_paypal_order_id'; const ORDER_PAYMENT_MODE_META_KEY = '_ppcp_paypal_payment_mode'; const ORDER_PAYMENT_SOURCE_META_KEY = '_ppcp_paypal_payment_source'; + const ORDER_PAYER_EMAIL_META_KEY = '_ppcp_paypal_payer_email'; const FEES_META_KEY = '_ppcp_paypal_fees'; const REFUND_FEES_META_KEY = '_ppcp_paypal_refund_fees'; const REFUNDS_META_KEY = '_ppcp_refunds'; diff --git a/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php b/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php index 32569c60b..281045099 100644 --- a/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php +++ b/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php @@ -52,6 +52,7 @@ trait CreditCardOrderInfoHandlingTrait {
  • %1$s
  • %2$s
  • %3$s
  • +
  • %4$s
  • '; $three_d_response_order_note_result = sprintf( $three_d_response_order_note_result_format, @@ -60,7 +61,9 @@ trait CreditCardOrderInfoHandlingTrait { /* 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 card last digits */ + sprintf( __( 'Card Last Digits: %s', 'woocommerce-paypal-payments' ), esc_html( $payment_source->properties()->last_digits ?? '' ) ) ); $three_d_response_order_note = sprintf( $three_d_response_order_note_format, @@ -76,7 +79,7 @@ trait CreditCardOrderInfoHandlingTrait { /** * Fired when the 3DS information is added to WC order. */ - do_action( 'woocommerce_paypal_payments_thee_d_secure_added', $wc_order, $order ); + do_action( 'woocommerce_paypal_payments_three_d_secure_added', $wc_order, $order ); } } diff --git a/modules/ppcp-wc-gateway/src/Processor/OrderMetaTrait.php b/modules/ppcp-wc-gateway/src/Processor/OrderMetaTrait.php index b18468d7b..1d9b87a2f 100644 --- a/modules/ppcp-wc-gateway/src/Processor/OrderMetaTrait.php +++ b/modules/ppcp-wc-gateway/src/Processor/OrderMetaTrait.php @@ -45,6 +45,14 @@ trait OrderMetaTrait { $wc_order->update_meta_data( PayPalGateway::ORDER_PAYMENT_SOURCE_META_KEY, $payment_source ); } + $payer = $order->payer(); + if ( $payer ) { + $payer_email = $payer->email_address(); + if ( $payer_email ) { + $wc_order->update_meta_data( PayPalGateway::ORDER_PAYER_EMAIL_META_KEY, $payer_email ); + } + } + $wc_order->save(); do_action( 'woocommerce_paypal_payments_woocommerce_order_created', $wc_order, $order ); diff --git a/modules/ppcp-wc-gateway/src/WCGatewayModule.php b/modules/ppcp-wc-gateway/src/WCGatewayModule.php index e82f65e9e..03241f555 100644 --- a/modules/ppcp-wc-gateway/src/WCGatewayModule.php +++ b/modules/ppcp-wc-gateway/src/WCGatewayModule.php @@ -448,6 +448,20 @@ class WCGatewayModule implements ModuleInterface { delete_transient( 'ppcp_reference_transaction_enabled' ); } ); + + add_action( + 'woocommerce_admin_order_data_after_billing_address', + function ( \WC_Order $wc_order ) { + if ( ! apply_filters( 'woocommerce_paypal_payments_order_details_show_paypal_email', true ) ) { + return; + } + + $email = $wc_order->get_meta( PayPalGateway::ORDER_PAYER_EMAIL_META_KEY ) ?: ''; + if ( $email ) { + echo '

    ' . esc_html__( 'PayPal buyer account', 'woocommerce-paypal-payments' ) . ':
    ' . esc_attr( $email ) . '

    '; + } + } + ); } /** diff --git a/tests/PHPUnit/Vaulting/VaultedCreditCardHandlerTest.php b/tests/PHPUnit/Vaulting/VaultedCreditCardHandlerTest.php index fb728709a..c7d176c8e 100644 --- a/tests/PHPUnit/Vaulting/VaultedCreditCardHandlerTest.php +++ b/tests/PHPUnit/Vaulting/VaultedCreditCardHandlerTest.php @@ -92,6 +92,8 @@ class VaultedCreditCardHandlerTest extends TestCase $customer = Mockery::mock(WC_Customer::class); $payer = Mockery::mock(Payer::class); + $payer->shouldReceive('email_address'); + $this->payerFactory->shouldReceive('from_wc_order') ->andReturn($payer); $this->shippingPreferenceFactory->shouldReceive('from_state') @@ -100,6 +102,7 @@ class VaultedCreditCardHandlerTest extends TestCase $order = Mockery::mock(Order::class); $order->shouldReceive('id')->andReturn('1'); $order->shouldReceive('intent')->andReturn('CAPTURE'); + $order->shouldReceive('payer')->andReturn($payer); $paymentSource = Mockery::mock(PaymentSource::class); $paymentSource->shouldReceive('name')->andReturn('card'); diff --git a/tests/PHPUnit/WcGateway/Gateway/OXXO/OXXOGatewayTest.php b/tests/PHPUnit/WcGateway/Gateway/OXXO/OXXOGatewayTest.php index abff51a10..0f70f801c 100644 --- a/tests/PHPUnit/WcGateway/Gateway/OXXO/OXXOGatewayTest.php +++ b/tests/PHPUnit/WcGateway/Gateway/OXXO/OXXOGatewayTest.php @@ -89,6 +89,7 @@ private $testee; $order->shouldReceive('id')->andReturn('1'); $order->shouldReceive('intent'); $order->shouldReceive('payment_source'); + $order->shouldReceive('payer'); $this->orderEndpoint ->shouldReceive('create') diff --git a/tests/PHPUnit/WcGateway/Processor/OrderProcessorTest.php b/tests/PHPUnit/WcGateway/Processor/OrderProcessorTest.php index d27ec6ff7..95f1b7b48 100644 --- a/tests/PHPUnit/WcGateway/Processor/OrderProcessorTest.php +++ b/tests/PHPUnit/WcGateway/Processor/OrderProcessorTest.php @@ -93,6 +93,7 @@ class OrderProcessorTest extends TestCase $currentOrder ->shouldReceive('payment_source') ->andReturn(null); + $currentOrder->shouldReceive('payer'); $wcOrder ->shouldReceive('get_meta') @@ -230,6 +231,7 @@ class OrderProcessorTest extends TestCase $currentOrder ->shouldReceive('payment_source') ->andReturn(null); + $currentOrder->shouldReceive('payer'); $wcOrder ->shouldReceive('get_meta') @@ -357,6 +359,7 @@ class OrderProcessorTest extends TestCase $currentOrder ->shouldReceive('purchase_units') ->andReturn([$purchaseUnit]); + $currentOrder->shouldReceive('payer'); $wcOrder ->shouldReceive('get_meta')