Merge pull request #2062 from woocommerce/PCP-2143-reauthorize-authorized-payments

reauthorize authorized payments (2143)
This commit is contained in:
Emili Castells 2024-03-11 17:24:06 +01:00 committed by GitHub
commit 0ce740ba62
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 354 additions and 9 deletions

View file

@ -22,6 +22,7 @@ use WooCommerce\PayPalCommerce\Common\Pattern\SingletonDecorator;
use WooCommerce\PayPalCommerce\Onboarding\Environment;
use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingOptionsRenderer;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\WcGateway\Admin\RenderReauthorizeAction;
use WooCommerce\PayPalCommerce\WcGateway\Endpoint\RefreshFeatureStatusEndpoint;
use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
@ -384,19 +385,25 @@ return array(
$notice = $container->get( 'wcgateway.notice.authorize-order-action' );
$settings = $container->get( 'wcgateway.settings' );
$subscription_helper = $container->get( 'wc-subscriptions.helper' );
$amount_factory = $container->get( 'api.factory.amount' );
return new AuthorizedPaymentsProcessor(
$order_endpoint,
$payments_endpoint,
$logger,
$notice,
$settings,
$subscription_helper
$subscription_helper,
$amount_factory
);
},
'wcgateway.admin.render-authorize-action' => static function ( ContainerInterface $container ): RenderAuthorizeAction {
$column = $container->get( 'wcgateway.admin.orders-payment-status-column' );
return new RenderAuthorizeAction( $column );
},
'wcgateway.admin.render-reauthorize-action' => static function ( ContainerInterface $container ): RenderReauthorizeAction {
$column = $container->get( 'wcgateway.admin.orders-payment-status-column' );
return new RenderReauthorizeAction( $column );
},
'wcgateway.admin.order-payment-status' => static function ( ContainerInterface $container ): PaymentStatusOrderDetail {
$column = $container->get( 'wcgateway.admin.orders-payment-status-column' );
return new PaymentStatusOrderDetail( $column );

View file

@ -9,9 +9,6 @@ declare( strict_types=1 );
namespace WooCommerce\PayPalCommerce\WcGateway\Admin;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
use WooCommerce\PayPalCommerce\WcGateway\Processor\AuthorizedPaymentsProcessor;
/**
* Class RenderAuthorizeAction
*/

View file

@ -0,0 +1,67 @@
<?php
/**
* Renders the order action "Reauthorize PayPal payment"
*
* @package WooCommerce\PayPalCommerce\WcGateway\Admin
*/
declare( strict_types=1 );
namespace WooCommerce\PayPalCommerce\WcGateway\Admin;
/**
* Class RenderReauthorizeAction
*/
class RenderReauthorizeAction {
/**
* The capture info column.
*
* @var OrderTablePaymentStatusColumn
*/
private $column;
/**
* PaymentStatusOrderDetail constructor.
*
* @param OrderTablePaymentStatusColumn $column The capture info column.
*/
public function __construct( OrderTablePaymentStatusColumn $column ) {
$this->column = $column;
}
/**
* Renders the action into the $order_actions array based on the WooCommerce order.
*
* @param array $order_actions The actions to render into.
* @param \WC_Order $wc_order The order for which to render the action.
*
* @return array
*/
public function render( array $order_actions, \WC_Order $wc_order ) : array {
if ( ! $this->should_render_for_order( $wc_order ) ) {
return $order_actions;
}
$order_actions['ppcp_reauthorize_order'] = esc_html__(
'Reauthorize PayPal payment',
'woocommerce-paypal-payments'
);
return $order_actions;
}
/**
* Whether the action should be rendered for a certain WooCommerce order.
*
* @param \WC_Order $order The Woocommerce order.
*
* @return bool
*/
private function should_render_for_order( \WC_Order $order ) : bool {
$status = $order->get_status();
$not_allowed_statuses = array( 'refunded', 'cancelled', 'failed' );
return $this->column->should_render_for_order( $order ) &&
! $this->column->is_captured( $order ) &&
! in_array( $status, $not_allowed_statuses, true );
}
}

View file

@ -10,6 +10,8 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\WcGateway\Processor;
use Exception;
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use WooCommerce\PayPalCommerce\ApiClient\Factory\AmountFactory;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
use WC_Order;
@ -91,6 +93,20 @@ class AuthorizedPaymentsProcessor {
*/
private $subscription_helper;
/**
* The amount factory.
*
* @var AmountFactory
*/
private $amount_factory;
/**
* The reauthorization failure reason.
*
* @var string
*/
private $reauthorization_failure_reason = '';
/**
* AuthorizedPaymentsProcessor constructor.
*
@ -100,6 +116,7 @@ class AuthorizedPaymentsProcessor {
* @param AuthorizeOrderActionNotice $notice The notice.
* @param ContainerInterface $config The settings.
* @param SubscriptionHelper $subscription_helper The subscription helper.
* @param AmountFactory $amount_factory The amount factory.
*/
public function __construct(
OrderEndpoint $order_endpoint,
@ -107,7 +124,8 @@ class AuthorizedPaymentsProcessor {
LoggerInterface $logger,
AuthorizeOrderActionNotice $notice,
ContainerInterface $config,
SubscriptionHelper $subscription_helper
SubscriptionHelper $subscription_helper,
AmountFactory $amount_factory
) {
$this->order_endpoint = $order_endpoint;
@ -116,6 +134,7 @@ class AuthorizedPaymentsProcessor {
$this->notice = $notice;
$this->config = $config;
$this->subscription_helper = $subscription_helper;
$this->amount_factory = $amount_factory;
}
/**
@ -249,6 +268,67 @@ class AuthorizedPaymentsProcessor {
}
}
/**
* Reauthorizes an authorized payment for an WooCommerce order.
*
* @param WC_Order $wc_order The WooCommerce order.
*
* @return string The status or reauthorization id.
*/
public function reauthorize_payment( WC_Order $wc_order ): string {
$this->reauthorization_failure_reason = '';
try {
$order = $this->paypal_order_from_wc_order( $wc_order );
} catch ( Exception $exception ) {
$this->logger->error( 'Could not get PayPal order from WC order: ' . $exception->getMessage() );
if ( $exception->getCode() === 404 ) {
return self::NOT_FOUND;
}
return self::INACCESSIBLE;
}
$amount = $this->amount_factory->from_wc_order( $wc_order );
$authorizations = $this->all_authorizations( $order );
$uncaptured_authorizations = $this->authorizations_to_capture( ...$authorizations );
if ( ! $uncaptured_authorizations ) {
if ( $this->captured_authorizations( ...$authorizations ) ) {
$this->logger->info( 'Authorizations already captured.' );
return self::ALREADY_CAPTURED;
}
$this->logger->info( 'Bad authorization.' );
return self::BAD_AUTHORIZATION;
}
$authorization = end( $uncaptured_authorizations );
try {
$this->payments_endpoint->reauthorize( $authorization->id(), new Money( $amount->value(), $amount->currency_code() ) );
} catch ( PayPalApiException $exception ) {
$this->reauthorization_failure_reason = $exception->details()[0]->description ?? null;
$this->logger->error( 'Reauthorization failed: ' . $exception->name() . ' | ' . $this->reauthorization_failure_reason );
return self::FAILED;
} catch ( Exception $exception ) {
$this->logger->error( 'Failed to capture authorization: ' . $exception->getMessage() );
return self::FAILED;
}
return self::SUCCESSFUL;
}
/**
* The reason for a failed reauthorization.
*
* @return string
*/
public function reauthorization_failure_reason(): string {
return $this->reauthorization_failure_reason;
}
/**
* Voids authorizations for the given PayPal order.
*
@ -392,4 +472,5 @@ class AuthorizedPaymentsProcessor {
}
);
}
}

View file

@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\WcGateway;
use Psr\Log\LoggerInterface;
use Throwable;
use WooCommerce\PayPalCommerce\AdminNotices\Entity\Message;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache;
@ -614,13 +615,18 @@ class WCGatewayModule implements ModuleInterface {
return $order_actions;
}
$render = $container->get( 'wcgateway.admin.render-authorize-action' );
$render_reauthorize = $container->get( 'wcgateway.admin.render-reauthorize-action' );
$render_authorize = $container->get( 'wcgateway.admin.render-authorize-action' );
/**
* Renders the authorize action in the select field.
*
* @var RenderAuthorizeAction $render
*/
return $render->render( $order_actions, $theorder );
return $render_reauthorize->render(
$render_authorize->render( $order_actions, $theorder ),
$theorder
);
}
);
@ -637,6 +643,36 @@ class WCGatewayModule implements ModuleInterface {
$authorized_payments_processor->capture_authorized_payment( $wc_order );
}
);
add_action(
'woocommerce_order_action_ppcp_reauthorize_order',
static function ( WC_Order $wc_order ) use ( $container ) {
$admin_notices = $container->get( 'admin-notices.repository' );
assert( $admin_notices instanceof Repository );
/**
* The authorized payments processor.
*
* @var AuthorizedPaymentsProcessor $authorized_payments_processor
*/
$authorized_payments_processor = $container->get( 'wcgateway.processor.authorized-payments' );
if ( $authorized_payments_processor->reauthorize_payment( $wc_order ) !== AuthorizedPaymentsProcessor::SUCCESSFUL ) {
$message = sprintf(
'%1$s %2$s',
esc_html__( 'Reauthorization with PayPal failed: ', 'woocommerce-paypal-payments' ),
$authorized_payments_processor->reauthorization_failure_reason() ?: ''
);
$admin_notices->persist( new Message( $message, 'error' ) );
} else {
$admin_notices->persist( new Message( 'Payment reauthorized.', 'info' ) );
$wc_order->add_order_note(
__( 'Payment reauthorized.', 'woocommerce-paypal-payments' )
);
}
}
);
}
/**