From f75846952ddb0ac1857a7d37073a1d59be7807cf Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 3 Jan 2024 15:44:24 +0100 Subject: [PATCH] Change subscription payment method (WIP) --- .../src/VaultedCreditCardHandler.php | 13 ----- .../src/Gateway/CreditCardGateway.php | 32 +++++++++++- .../src/RenewalHandler.php | 49 +++++++++++++++-- .../src/WcSubscriptionsModule.php | 52 +++++++++++++------ 4 files changed, 110 insertions(+), 36 deletions(-) diff --git a/modules/ppcp-vaulting/src/VaultedCreditCardHandler.php b/modules/ppcp-vaulting/src/VaultedCreditCardHandler.php index 59e60b912..3261e4b8e 100644 --- a/modules/ppcp-vaulting/src/VaultedCreditCardHandler.php +++ b/modules/ppcp-vaulting/src/VaultedCreditCardHandler.php @@ -143,19 +143,6 @@ public function handle_payment( string $saved_credit_card, WC_Order $wc_order ): WC_Order { - if ( - // phpcs:ignore WordPress.Security.NonceVerification.Missing - isset( $_POST['woocommerce_change_payment'] ) - && $this->subscription_helper->has_subscription( $wc_order->get_id() ) - && $this->subscription_helper->is_subscription_change_payment() - && $saved_credit_card - ) { - $wc_order->update_meta_data( 'payment_token_id', $saved_credit_card ); - $wc_order->save(); - - return $wc_order; - } - $tokens = $this->payment_token_repository->all_for_user_id( $wc_order->get_customer_id() ); $selected_token = null; foreach ( $tokens as $token ) { diff --git a/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php b/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php index 5b35aef1a..aaa92abdb 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php @@ -12,6 +12,7 @@ use Exception; use Psr\Log\LoggerInterface; use WC_Order; +use WC_Payment_Tokens; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentsEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; @@ -377,11 +378,11 @@ public function process_payment( $order_id ) { } /** - * If customer has chosen a saved credit card payment. + * If customer has chosen a saved credit card payment from checkout page. */ // phpcs:ignore WordPress.Security.NonceVerification.Missing $saved_credit_card = wc_clean( wp_unslash( $_POST['saved_credit_card'] ?? '' ) ); - if ( $saved_credit_card ) { + if ( $saved_credit_card && is_checkout() ) { try { $wc_order = $this->vaulted_credit_card_handler->handle_payment( $saved_credit_card, @@ -395,6 +396,33 @@ public function process_payment( $order_id ) { } } + /** + * If customer is changing subscription payment. + */ + if ( + // phpcs:ignore WordPress.Security.NonceVerification.Missing + isset( $_POST['woocommerce_change_payment'] ) + && $this->subscription_helper->has_subscription( $wc_order->get_id() ) + && $this->subscription_helper->is_subscription_change_payment() + && $saved_credit_card + ) { + $payment_token = WC_Payment_Tokens::get($saved_credit_card); + if($payment_token) { + $wc_order->add_payment_token($payment_token); + $wc_order->save(); + + return $this->handle_payment_success( $wc_order ); + } + + wc_add_notice( __( 'Could not change payment.', 'woocommerce-paypal-payments' ), 'error' ); + + return array( + 'result' => 'failure', + 'redirect' => wc_get_checkout_url(), + 'errorMessage' => __( 'Could not change payment.', 'woocommerce-paypal-payments' ), + ); + } + /** * If the WC_Order is paid through the approved webhook. */ diff --git a/modules/ppcp-wc-subscriptions/src/RenewalHandler.php b/modules/ppcp-wc-subscriptions/src/RenewalHandler.php index 7440074f9..adee662f2 100644 --- a/modules/ppcp-wc-subscriptions/src/RenewalHandler.php +++ b/modules/ppcp-wc-subscriptions/src/RenewalHandler.php @@ -9,6 +9,9 @@ namespace WooCommerce\PayPalCommerce\WcSubscriptions; +use WC_Customer; +use WC_Order; +use WC_Payment_Tokens; use WC_Subscription; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Entity\ApplicationContext; @@ -194,8 +197,8 @@ private function process_order( \WC_Order $wc_order ): void { 'renewal' ); - $token = $this->get_token_for_customer( $customer, $wc_order ); - if ( $token ) { + $token_id = $this->token_id( $wc_order, $customer ); + if ( $token_id ) { if ( $wc_order->get_payment_method() === CreditCardGateway::ID ) { $stored_credentials = array( 'payment_initiator' => 'MERCHANT', @@ -215,7 +218,7 @@ private function process_order( \WC_Order $wc_order ): void { $payment_source = new PaymentSource( 'card', (object) array( - 'vault_id' => $token->id(), + 'vault_id' => $token_id, 'stored_credential' => $stored_credentials, ) ); @@ -244,11 +247,23 @@ private function process_order( \WC_Order $wc_order ): void { return; } + $payment_source = new PaymentSource( + 'paypal', + (object) array( + 'vault_id' => $token_id, + ) + ); + $order = $this->order_endpoint->create( array( $purchase_unit ), $shipping_preference, $payer, - $token + null, + '', + ApplicationContext::USER_ACTION_CONTINUE, + '', + array(), + $payment_source ); $this->handle_paypal_order( $wc_order, $order ); @@ -399,4 +414,30 @@ private function handle_paypal_order( \WC_Order $wc_order, Order $order ): void $this->authorized_payments_processor->capture_authorized_payment( $wc_order ); } } + + /** + * Returns a payment token id for the given order or customer. + * + * @param WC_Order $wc_order WC order. + * @param WC_Customer $customer WC customer. + * @return string + */ + private function token_id( WC_Order $wc_order, WC_Customer $customer ): string { + $token_id = ''; + + $tokens = $wc_order->get_payment_tokens(); + if ( $tokens ) { + $token = WC_Payment_Tokens::get( $tokens[0] ); + if ( $token ) { + $token_id = $token->get_token(); + } + } + + if ( ! $token_id ) { + $token = $this->get_token_for_customer( $customer, $wc_order ); + $token_id = $token->id(); + } + + return $token_id; + } } diff --git a/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php b/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php index c5fb04541..393a7ef8f 100644 --- a/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php +++ b/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php @@ -11,7 +11,7 @@ use Psr\Log\LoggerInterface; use WC_Order; -use WC_Subscription; +use WC_Payment_Tokens; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository; use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider; @@ -115,6 +115,10 @@ function ( $subscription ) use ( $c ) { * @psalm-suppress MissingClosureParamType */ function ( $description, $id ) use ( $c ) { + if ( $c->has( 'save-payment-methods.eligible' ) && $c->get( 'save-payment-methods.eligible' ) ) { + return $description; + } + $payment_token_repository = $c->get( 'vaulting.repository.payment-token' ); $settings = $c->get( 'wcgateway.settings' ); $subscription_helper = $c->get( 'wc-subscriptions.helper' ); @@ -133,6 +137,10 @@ function ( $description, $id ) use ( $c ) { * @psalm-suppress MissingClosureParamType */ function ( $default_fields, $id ) use ( $c ) { + if ( $c->has( 'save-payment-methods.eligible' ) && $c->get( 'save-payment-methods.eligible' ) ) { + return $default_fields; + } + $payment_token_repository = $c->get( 'vaulting.repository.payment-token' ); $settings = $c->get( 'wcgateway.settings' ); $subscription_helper = $c->get( 'wc-subscriptions.helper' ); @@ -142,6 +150,27 @@ function ( $default_fields, $id ) use ( $c ) { 20, 2 ); + + add_filter( + 'woocommerce_available_payment_gateways', + function( array $methods ): array { + if ( ! is_wc_endpoint_url( 'order-pay' ) ) { + return $methods; + } + + $paypal_tokens = WC_Payment_Tokens::get_customer_tokens( get_current_user_id(), PayPalGateway::ID ); + if ( ! $paypal_tokens ) { + unset( $methods[ PayPalGateway::ID ] ); + } + + $card_tokens = WC_Payment_Tokens::get_customer_tokens( get_current_user_id(), CreditCardGateway::ID ); + if ( ! $card_tokens ) { + unset( $methods[ CreditCardGateway::ID ] ); + } + + return $methods; + } + ); } /** @@ -269,35 +298,24 @@ protected function display_saved_credit_cards( array $default_fields, SubscriptionHelper $subscription_helper ) { - if ( $settings->has( 'vault_enabled_dcc' ) && $settings->get( 'vault_enabled_dcc' ) && $subscription_helper->is_subscription_change_payment() && CreditCardGateway::ID === $id ) { - $tokens = $payment_token_repository->all_for_user_id( get_current_user_id() ); - if ( ! $tokens || ! $payment_token_repository->tokens_contains_card( $tokens ) ) { - $default_fields = array(); - $default_fields['saved-credit-card'] = esc_html__( - 'No Credit Card saved, in order to use a saved Credit Card you first need to create it through a purchase.', - 'woocommerce-paypal-payments' - ); - return $default_fields; - } - + $tokens = WC_Payment_Tokens::get_customer_tokens( get_current_user_id(), CreditCardGateway::ID ); $output = sprintf( '

';