From 5ffc792ce0ad75f21420d6f3f1bd8d484a22c046 Mon Sep 17 00:00:00 2001 From: dinamiko Date: Tue, 1 Mar 2022 12:39:10 +0100 Subject: [PATCH] Void order when payment is not saved (WIP) --- .../src/SubscriptionModule.php | 12 -- modules/ppcp-vaulting/src/VaultingModule.php | 132 ++++++++++++++++++ 2 files changed, 132 insertions(+), 12 deletions(-) diff --git a/modules/ppcp-subscription/src/SubscriptionModule.php b/modules/ppcp-subscription/src/SubscriptionModule.php index 770d4b967..3be24af3d 100644 --- a/modules/ppcp-subscription/src/SubscriptionModule.php +++ b/modules/ppcp-subscription/src/SubscriptionModule.php @@ -94,18 +94,6 @@ class SubscriptionModule implements ModuleInterface { 20, 2 ); - - add_action('woocommerce_paypal_payments_check_saved_payment', function ($order_id, $customer_id) use ($c) { - $payment_token_repository = $c->get( 'vaulting.repository.payment-token' ); - $settings = $c->get( 'wcgateway.settings' ); - - $tokens = $payment_token_repository->all_for_user_id( $customer_id ); - if($tokens) { - if ( $settings->has( 'intent' ) && strtoupper( (string) $settings->get( 'intent' ) ) === 'CAPTURE' ) { - //$this->authorized_payments_processor->capture_authorized_payment( $wc_order ); - } - } - }, 10, 2); } /** diff --git a/modules/ppcp-vaulting/src/VaultingModule.php b/modules/ppcp-vaulting/src/VaultingModule.php index d74dfc86e..9be1bc08e 100644 --- a/modules/ppcp-vaulting/src/VaultingModule.php +++ b/modules/ppcp-vaulting/src/VaultingModule.php @@ -11,9 +11,13 @@ namespace WooCommerce\PayPalCommerce\Vaulting; use Dhii\Container\ServiceProvider; use Dhii\Modular\Module\ModuleInterface; +use Exception; use Interop\Container\ServiceProviderInterface; use Psr\Container\ContainerInterface; +use RuntimeException; +use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization; use WooCommerce\PayPalCommerce\Vaulting\Endpoint\DeletePaymentTokenEndpoint; +use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; /** * Class StatusReportModule @@ -129,6 +133,46 @@ class VaultingModule implements ModuleInterface { $endpoint->handle_request(); } ); + + add_action( + 'woocommerce_paypal_payments_check_saved_payment', + function ( $order_id, $customer_id ) use ( $container ) { + $payment_token_repository = $container->get( 'vaulting.repository.payment-token' ); + $settings = $container->get( 'wcgateway.settings' ); + $logger = $container->get( 'woocommerce.logger.woocommerce' ); + $authorized_payments_processor = $container->get( 'wcgateway.processor.authorized-payments' ); + $order_endpoint = $container->get( 'api.endpoint.order' ); + $payments_endpoint = $container->get( 'api.endpoint.payments' ); + + $tokens = $payment_token_repository->all_for_user_id( $customer_id ); + if ( $tokens ) { + $this->capture_authorized_payment( + $settings, + $order_id, + $authorized_payments_processor, + $logger, + $customer_id + ); + + return; + } + + $logger->error( "Payment for subscription parent order #{$order_id} was not saved on PayPal." ); + + $wc_order = wc_get_order( $order_id ); + $order = $this->getOrder( $wc_order, $order_endpoint ); + + try { + $this->void_authorizations( $order, $payments_endpoint ); + } catch ( RuntimeException $exception ) { + $logger->warning($exception->getMessage()); + } + + $this->updateFailedStatus( $wc_order, $order_id, $logger ); + }, + 10, + 2 + ); } /** @@ -150,4 +194,92 @@ class VaultingModule implements ModuleInterface { return false; } + + /** + * @param $settings + * @param $order_id + * @param $authorized_payments_processor + * @param $logger + * @param $customer_id + */ + protected function capture_authorized_payment( + $settings, + $order_id, + $authorized_payments_processor, + $logger, + $customer_id + ): void { + if ( $settings->has( 'intent' ) && strtoupper( (string) $settings->get( 'intent' ) ) === 'CAPTURE' ) { + $wc_order = wc_get_order( $order_id ); + $authorized_payments_processor->capture_authorized_payment( $wc_order ); + $logger->info( "Order: #{$order_id} for user: {$customer_id} captured successfully." ); + } + } + + /** + * @param $order + * @param $payments_endpoint + * @throws RuntimeException + */ + protected function void_authorizations( $order, $payments_endpoint ): void { + $purchase_units = $order->purchase_units(); + if ( ! $purchase_units ) { + throw new RuntimeException( 'No purchase units.' ); + } + + $payments = $purchase_units[0]->payments(); + if ( ! $payments ) { + throw new RuntimeException( 'No payments.' ); + } + + $voidable_authorizations = array_filter( + $payments->authorizations(), + function ( Authorization $authorization ): bool { + return $authorization->is_voidable(); + } + ); + if ( ! $voidable_authorizations ) { + throw new RuntimeException( 'No voidable authorizations.' ); + } + + foreach ( $voidable_authorizations as $authorization ) { + $payments_endpoint->void( $authorization ); + } + } + + /** + * @param $wc_order + * @param $order_endpoint + * @return mixed + */ + protected function getOrder( $wc_order, $order_endpoint ) { + $paypal_order_id = $wc_order->get_meta( PayPalGateway::ORDER_ID_META_KEY ); + if ( ! $paypal_order_id ) { + throw new RuntimeException( 'PayPal order ID not found in meta.' ); + } + + return $order_endpoint->order( $paypal_order_id ); + } + + /** + * @param $wc_order + * @param $order_id + * @param $logger + */ + protected function updateFailedStatus( $wc_order, $order_id, $logger ): void { + $error_message = __( 'Could not process order because it was not possible to save the payment on PayPal.', 'woocommerce-paypal-payments' ); + $wc_order->update_status( 'failed', $error_message ); + + $subscriptions = wcs_get_subscriptions_for_order( $order_id ); + foreach ( $subscriptions as $key => $subscription ) { + if ( $subscription->get_parent_id() === $order_id ) { + try { + $subscription->update_status( 'cancelled' ); + break; + } catch ( Exception $exception ) { + $logger->error( "Could not update cancelled status on subscription #{$subscription->get_id()} " . $exception->getMessage() ); + } + } + } + } }