♻️ Extract the 3DS check into a new method

This commit is contained in:
Philipp Stracker 2025-03-04 17:29:41 +01:00
parent 8153026e22
commit 4f420a2f8a
No known key found for this signature in database

View file

@ -13,6 +13,7 @@ namespace WooCommerce\PayPalCommerce\Button\Endpoint;
use Exception;
use Psr\Log\LoggerInterface;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
@ -199,29 +200,23 @@ class ApproveOrderEndpoint implements EndpointInterface {
);
}
}
$proceed = $this->threed_secure->proceed_with_order( $order );
if ( ThreeDSecure::RETRY === $proceed ) {
throw new RuntimeException(
__(
'Something went wrong. Please try again.',
'woocommerce-paypal-payments'
)
);
}
if ( ThreeDSecure::REJECT === $proceed ) {
throw new RuntimeException(
__(
'Unfortunately, we can\'t accept your card. Please choose a different payment method.',
'woocommerce-paypal-payments'
)
);
}
// This check will either pass, or throw an exception.
$this->verify_three_d_secure( $order );
$this->session_handler->replace_order( $order );
// Exit the request early.
wp_send_json_success();
}
if ( $this->order_helper->contains_physical_goods( $order ) && ! $order->status()->is( OrderStatus::APPROVED ) && ! $order->status()->is( OrderStatus::CREATED ) ) {
// Verify 3DS details. Throws an error when security check fails.
$this->verify_three_d_secure( $order );
$is_ready = $order->status()->is( OrderStatus::APPROVED )
|| $order->status()->is( OrderStatus::CREATED );
if ( ! $is_ready && $this->order_helper->contains_physical_goods( $order ) ) {
$message = sprintf(
// translators: %s is the id of the order.
__( 'Order %s is not ready for processing yet.', 'woocommerce-paypal-payments' ),
@ -279,4 +274,73 @@ class ApproveOrderEndpoint implements EndpointInterface {
$this->settings->set( 'blocks_final_review_enabled', ! $final_review_enabled_setting );
$this->settings->persist();
}
/**
* Performs a 3DS check to verify the payment is not rejected from PayPal side.
*
* This method only checks, if the payment was rejected:
*
* - No 3DS details are present: The payment can proceed.
* - 3DS details present but no rejected: Payment can proceed.
* - 3DS details with a clear rejected: Payment fails.
*
* @param Order $order The PayPal order to inspect.
* @throws RuntimeException When the 3DS check was rejected.
*/
protected function verify_three_d_secure( Order $order ) : void {
$payment_source = $order->payment_source();
if ( ! $payment_source ) {
// Missing 3DS details.
return;
}
$proceed = ThreeDSecure::NO_DECISION;
$order_status = $order->status();
$source_name = $payment_source->name();
/**
* For GooglePay (and possibly other payment sources) we check the order
* status, as it will clearly indicate if verification is needed.
*
* Note: PayPal is currently investigating this case.
* Maybe the order status is wrong and should be ACCEPTED, in that case,
* we could drop the condition and always run proceed_with_order().
*/
if ( $order_status->is( OrderStatus::PAYER_ACTION_REQUIRED ) ) {
$proceed = $this->threed_secure->proceed_with_order( $order );
} elseif ( 'card' === $source_name ) {
// For credit cards, we also check the 3DS response.
$proceed = $this->threed_secure->proceed_with_order( $order );
}
// Handle the verification result based on the proceed value.
switch ( $proceed ) {
case ThreeDSecure::PROCCEED:
// Check was successful.
return;
case ThreeDSecure::NO_DECISION:
// No rejection. Let's proceed with the payment.
return;
case ThreeDSecure::RETRY:
// Rejection case 1, verification can be retried.
throw new RuntimeException(
__(
'Something went wrong. Please try again.',
'woocommerce-paypal-payments'
)
);
case ThreeDSecure::REJECT:
// Rejection case 2, payment was rejected.
throw new RuntimeException(
__(
'Unfortunately, we can\'t accept your card. Please choose a different payment method.',
'woocommerce-paypal-payments'
)
);
}
}
}