From f75846952ddb0ac1857a7d37073a1d59be7807cf Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 3 Jan 2024 15:44:24 +0100 Subject: [PATCH 01/14] 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 @@ class VaultedCreditCardHandler { 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 @@ namespace WooCommerce\PayPalCommerce\WcGateway\Gateway; 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 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC { } /** - * 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 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC { } } + /** + * 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 @@ declare(strict_types=1); 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 @@ class RenewalHandler { '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 @@ class RenewalHandler { $payment_source = new PaymentSource( 'card', (object) array( - 'vault_id' => $token->id(), + 'vault_id' => $token_id, 'stored_credential' => $stored_credentials, ) ); @@ -244,11 +247,23 @@ class RenewalHandler { 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 @@ class RenewalHandler { $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 @@ namespace WooCommerce\PayPalCommerce\WcSubscriptions; 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 @@ class WcSubscriptionsModule implements ModuleInterface { * @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 @@ class WcSubscriptionsModule implements ModuleInterface { * @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 @@ class WcSubscriptionsModule implements ModuleInterface { 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 @@ class WcSubscriptionsModule implements ModuleInterface { 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( '

'; From e23f50a5f3085bc87c09e4b9eb1abc433da7f0e2 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 3 Jan 2024 16:57:23 +0100 Subject: [PATCH 02/14] Add change subscription payment for PayPal payment --- .../src/Gateway/PayPalGateway.php | 19 ++++++++++--- .../src/RenewalHandler.php | 8 +++--- .../src/WcSubscriptionsModule.php | 27 +++++++------------ 3 files changed, 29 insertions(+), 25 deletions(-) diff --git a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php index 741436c22..4c4bc4671 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php @@ -12,11 +12,11 @@ namespace WooCommerce\PayPalCommerce\WcGateway\Gateway; use Exception; use Psr\Log\LoggerInterface; use WC_Order; +use WC_Payment_Tokens; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus; use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken; use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; -use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; use WooCommerce\PayPalCommerce\Onboarding\Environment; use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Session\SessionHandler; @@ -522,10 +522,21 @@ class PayPalGateway extends \WC_Payment_Gateway { // phpcs:ignore WordPress.Security.NonceVerification.Missing $saved_paypal_payment = wc_clean( wp_unslash( $_POST['saved_paypal_payment'] ?? '' ) ); if ( $saved_paypal_payment ) { - $wc_order->update_meta_data( 'payment_token_id', $saved_paypal_payment ); - $wc_order->save(); + $payment_token = WC_Payment_Tokens::get( $saved_paypal_payment ); + if ( $payment_token ) { + $wc_order->add_payment_token( $payment_token ); + $wc_order->save(); - return $this->handle_payment_success( $wc_order ); + 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' ), + ); } } diff --git a/modules/ppcp-wc-subscriptions/src/RenewalHandler.php b/modules/ppcp-wc-subscriptions/src/RenewalHandler.php index adee662f2..650329714 100644 --- a/modules/ppcp-wc-subscriptions/src/RenewalHandler.php +++ b/modules/ppcp-wc-subscriptions/src/RenewalHandler.php @@ -427,9 +427,11 @@ class RenewalHandler { $tokens = $wc_order->get_payment_tokens(); if ( $tokens ) { - $token = WC_Payment_Tokens::get( $tokens[0] ); - if ( $token ) { - $token_id = $token->get_token(); + foreach ( $tokens as $token_id ) { + $token = WC_Payment_Tokens::get( $token_id ); + if ( $token && $token->get_gateway_id() === $wc_order->get_payment_method() ) { + return $token->get_token(); + } } } diff --git a/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php b/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php index 393a7ef8f..d15906d46 100644 --- a/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php +++ b/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php @@ -251,30 +251,21 @@ class WcSubscriptionsModule implements ModuleInterface { && PayPalGateway::ID === $id && $subscription_helper->is_subscription_change_payment() ) { - $tokens = $payment_token_repository->all_for_user_id( get_current_user_id() ); - if ( ! $tokens || ! $payment_token_repository->tokens_contains_paypal( $tokens ) ) { - return esc_html__( - 'No PayPal payments saved, in order to use a saved payment you first need to create it through a purchase.', - 'woocommerce-paypal-payments' - ); - } - + $tokens = WC_Payment_Tokens::get_customer_tokens( get_current_user_id(), PayPalGateway::ID ); $output = sprintf( '

'; + $output .= '

'; - return $output; + return $output; } return $description; From 16eebecc5a836b72a6df8353fd93d3e8a54f1a43 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 8 Jan 2024 11:38:00 +0100 Subject: [PATCH 03/14] Add change subscription payment for vault v3 --- .../src/Gateway/CreditCardGateway.php | 21 ++++++++++++------- .../src/WcSubscriptionsModule.php | 4 ---- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php b/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php index aaa92abdb..66f370124 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php @@ -400,18 +400,25 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC { * If customer is changing subscription payment. */ if ( - // phpcs:ignore WordPress.Security.NonceVerification.Missing + // phpcs:disable 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(); + $saved_credit_card = wc_clean( wp_unslash( $_POST['wc-ppcp-credit-card-gateway-payment-token'] ?? '' ) ); + if ( ! $saved_credit_card ) { + $saved_credit_card = wc_clean( wp_unslash( $_POST['saved_credit_card'] ?? '' ) ); + // phpcs:enable WordPress.Security.NonceVerification.Missing + } - return $this->handle_payment_success( $wc_order ); + if ( $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' ); diff --git a/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php b/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php index 6049e3823..c8f0f99a6 100644 --- a/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php +++ b/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php @@ -117,10 +117,6 @@ class WcSubscriptionsModule implements ModuleInterface { * @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' ); From dc9ad87b3e6796e85d1883dd6abb03cb42894a20 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 8 Jan 2024 12:40:56 +0100 Subject: [PATCH 04/14] Allow create payment when changing subscription payment method (WIP) --- .../ppcp-button/src/Helper/ContextTrait.php | 13 +++ .../resources/js/add-payment-method.js | 87 ++++++++++--------- .../src/SavePaymentMethodsModule.php | 2 +- 3 files changed, 59 insertions(+), 43 deletions(-) diff --git a/modules/ppcp-button/src/Helper/ContextTrait.php b/modules/ppcp-button/src/Helper/ContextTrait.php index a02a79709..9b5874bfa 100644 --- a/modules/ppcp-button/src/Helper/ContextTrait.php +++ b/modules/ppcp-button/src/Helper/ContextTrait.php @@ -212,6 +212,19 @@ trait ContextTrait { return $page_id && is_page( $page_id ) && isset( $wp->query_vars['add-payment-method'] ); } + /** + * Checks whether this user is changing the payment method for a subscription. + * + * @return bool + */ + private function is_subscription_change_payment_method_page(): bool { + if ( isset( $_GET['change_payment_method'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification + return wcs_is_subscription( wc_clean( wp_unslash( $_GET['change_payment_method'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification + } + + return false; + } + /** * Checks if it is the block editor page. */ diff --git a/modules/ppcp-save-payment-methods/resources/js/add-payment-method.js b/modules/ppcp-save-payment-methods/resources/js/add-payment-method.js index d861421e4..6be53940c 100644 --- a/modules/ppcp-save-payment-methods/resources/js/add-payment-method.js +++ b/modules/ppcp-save-payment-methods/resources/js/add-payment-method.js @@ -38,54 +38,57 @@ document.addEventListener( .then((paypal) => { errorHandler.clear(); - paypal.Buttons( - { - createVaultSetupToken: async () => { - const response = await fetch(ppcp_add_payment_method.ajax.create_setup_token.endpoint, { - method: "POST", - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - nonce: ppcp_add_payment_method.ajax.create_setup_token.nonce, + const paypalButtonContainer = document.querySelector(`#ppc-button-${PaymentMethods.PAYPAL}-save-payment-method`); + if(paypalButtonContainer) { + paypal.Buttons( + { + createVaultSetupToken: async () => { + const response = await fetch(ppcp_add_payment_method.ajax.create_setup_token.endpoint, { + method: "POST", + credentials: 'same-origin', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + nonce: ppcp_add_payment_method.ajax.create_setup_token.nonce, + }) }) - }) - const result = await response.json() - if (result.data.id) { - return result.data.id - } + const result = await response.json() + if (result.data.id) { + return result.data.id + } - errorHandler.message(ppcp_add_payment_method.error_message); - }, - onApprove: async ({vaultSetupToken}) => { - const response = await fetch(ppcp_add_payment_method.ajax.create_payment_token.endpoint, { - method: "POST", - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - nonce: ppcp_add_payment_method.ajax.create_payment_token.nonce, - vault_setup_token: vaultSetupToken, + errorHandler.message(ppcp_add_payment_method.error_message); + }, + onApprove: async ({vaultSetupToken}) => { + const response = await fetch(ppcp_add_payment_method.ajax.create_payment_token.endpoint, { + method: "POST", + credentials: 'same-origin', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + nonce: ppcp_add_payment_method.ajax.create_payment_token.nonce, + vault_setup_token: vaultSetupToken, + }) }) - }) - const result = await response.json(); - if(result.success === true) { - window.location.href = ppcp_add_payment_method.payment_methods_page; - return; + const result = await response.json(); + if(result.success === true) { + window.location.href = ppcp_add_payment_method.payment_methods_page; + return; + } + + errorHandler.message(ppcp_add_payment_method.error_message); + }, + onError: (error) => { + console.error(error) + errorHandler.message(ppcp_add_payment_method.error_message); } - - errorHandler.message(ppcp_add_payment_method.error_message); }, - onError: (error) => { - console.error(error) - errorHandler.message(ppcp_add_payment_method.error_message); - } - }, - ).render(`#ppc-button-${PaymentMethods.PAYPAL}-save-payment-method`); + ).render(`#ppc-button-${PaymentMethods.PAYPAL}-save-payment-method`); + } const cardField = paypal.CardFields({ createVaultSetupToken: async () => { @@ -167,7 +170,7 @@ document.addEventListener( } } - document.querySelector('#place_order').addEventListener("click", (event) => { + document.querySelector('#place_order')?.addEventListener("click", (event) => { event.preventDefault(); cardField.submit() diff --git a/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php b/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php index 3501d71aa..9b055b415 100644 --- a/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php +++ b/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php @@ -176,7 +176,7 @@ class SavePaymentMethodsModule implements ModuleInterface { add_action( 'wp_enqueue_scripts', function() use ( $c ) { - if ( ! is_user_logged_in() || ! $this->is_add_payment_method_page() ) { + if ( ! is_user_logged_in() || ! ( $this->is_add_payment_method_page() || $this->is_subscription_change_payment_method_page() ) ) { return; } From 018b497611f1e8bb28d30702ebac186a5a1179cb Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 8 Jan 2024 14:37:51 +0100 Subject: [PATCH 05/14] Move create wc card payment method to helper class --- .../src/Endpoint/CreatePaymentToken.php | 22 +++--------- .../src/WooCommercePaymentTokens.php | 35 +++++++++++++++++++ 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/modules/ppcp-save-payment-methods/src/Endpoint/CreatePaymentToken.php b/modules/ppcp-save-payment-methods/src/Endpoint/CreatePaymentToken.php index 6698c4340..61fcae9da 100644 --- a/modules/ppcp-save-payment-methods/src/Endpoint/CreatePaymentToken.php +++ b/modules/ppcp-save-payment-methods/src/Endpoint/CreatePaymentToken.php @@ -98,7 +98,8 @@ class CreatePaymentToken implements EndpointInterface { $result = $this->payment_method_tokens_endpoint->payment_tokens( $payment_source ); if ( is_user_logged_in() && isset( $result->customer->id ) ) { - update_user_meta( get_current_user_id(), '_ppcp_target_customer_id', $result->customer->id ); + $current_user_id = get_current_user_id(); + update_user_meta( $current_user_id, '_ppcp_target_customer_id', $result->customer->id ); if ( isset( $result->payment_source->paypal ) ) { $email = ''; @@ -107,29 +108,14 @@ class CreatePaymentToken implements EndpointInterface { } $this->wc_payment_tokens->create_payment_token_paypal( - get_current_user_id(), + $current_user_id, $result->id, $email ); } if ( isset( $result->payment_source->card ) ) { - $token = new \WC_Payment_Token_CC(); - $token->set_token( $result->id ); - $token->set_user_id( get_current_user_id() ); - $token->set_gateway_id( CreditCardGateway::ID ); - - $token->set_last4( $result->payment_source->card->last_digits ?? '' ); - $expiry = explode( '-', $result->payment_source->card->expiry ?? '' ); - $token->set_expiry_year( $expiry[0] ?? '' ); - $token->set_expiry_month( $expiry[1] ?? '' ); - - $brand = $result->payment_source->card->brand ?? __( 'N/A', 'woocommerce-paypal-payments' ); - if ( $brand ) { - $token->set_card_type( $brand ); - } - - $token->save(); + $this->wc_payment_tokens->create_payment_token_card( $current_user_id, $result ); } } diff --git a/modules/ppcp-save-payment-methods/src/WooCommercePaymentTokens.php b/modules/ppcp-save-payment-methods/src/WooCommercePaymentTokens.php index 4fb066b6e..d9e13ce33 100644 --- a/modules/ppcp-save-payment-methods/src/WooCommercePaymentTokens.php +++ b/modules/ppcp-save-payment-methods/src/WooCommercePaymentTokens.php @@ -11,10 +11,13 @@ namespace WooCommerce\PayPalCommerce\SavePaymentMethods; use Exception; use Psr\Log\LoggerInterface; +use stdClass; +use WC_Payment_Token_CC; use WC_Payment_Tokens; use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenFactory; use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenHelper; use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenPayPal; +use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; /** @@ -99,4 +102,36 @@ class WooCommercePaymentTokens { ); } } + + /** + * Creates a WC Payment Token for Credit Card payment. + * + * @param int $customer_id The WC customer ID. + * @param stdClass $payment_token The Credit Card payment token. + * + * @return void + */ + public function create_payment_token_card( int $customer_id, stdClass $payment_token ): void { + $wc_tokens = WC_Payment_Tokens::get_customer_tokens( $customer_id, CreditCardGateway::ID ); + if ( $this->payment_token_helper->token_exist( $wc_tokens, $payment_token->id ) ) { + return; + } + + $token = new WC_Payment_Token_CC(); + $token->set_token( $payment_token->id ); + $token->set_user_id( get_current_user_id() ); + $token->set_gateway_id( CreditCardGateway::ID ); + + $token->set_last4( $payment_token->payment_source->card->last_digits ?? '' ); + $expiry = explode( '-', $payment_token->payment_source->card->expiry ?? '' ); + $token->set_expiry_year( $expiry[0] ?? '' ); + $token->set_expiry_month( $expiry[1] ?? '' ); + + $brand = $payment_token->payment_source->card->brand ?? __( 'N/A', 'woocommerce-paypal-payments' ); + if ( $brand ) { + $token->set_card_type( $brand ); + } + + $token->save(); + } } From f081fc2392e01e1a6b93a3e729c936f0b1fb3808 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 8 Jan 2024 14:39:44 +0100 Subject: [PATCH 06/14] Move create wc card payment method to helper class --- .../src/WooCommercePaymentTokens.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modules/ppcp-save-payment-methods/src/WooCommercePaymentTokens.php b/modules/ppcp-save-payment-methods/src/WooCommercePaymentTokens.php index d9e13ce33..849e1e56e 100644 --- a/modules/ppcp-save-payment-methods/src/WooCommercePaymentTokens.php +++ b/modules/ppcp-save-payment-methods/src/WooCommercePaymentTokens.php @@ -132,6 +132,13 @@ class WooCommercePaymentTokens { $token->set_card_type( $brand ); } + try { + $token->save(); + } catch ( Exception $exception ) { + $this->logger->error( + "Could not create WC payment token card for customer {$customer_id}. " . $exception->getMessage() + ); + } $token->save(); } } From 0262c28580e175fdcc0b052eca6183ca0b979218 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 8 Jan 2024 17:47:52 +0100 Subject: [PATCH 07/14] Add save card payment for changing subscription payment method (WIP) --- .../resources/js/add-payment-method.js | 27 +++++++ .../src/Endpoint/CreatePaymentToken.php | 9 ++- .../src/SavePaymentMethodsModule.php | 22 ++++-- .../src/WooCommercePaymentTokens.php | 16 ++-- modules/ppcp-wc-subscriptions/services.php | 8 +- .../SubscriptionChangePaymentMethod.php | 75 +++++++++++++++++++ .../src/WcSubscriptionsModule.php | 11 +++ 7 files changed, 149 insertions(+), 19 deletions(-) create mode 100644 modules/ppcp-wc-subscriptions/src/Endpoint/SubscriptionChangePaymentMethod.php diff --git a/modules/ppcp-save-payment-methods/resources/js/add-payment-method.js b/modules/ppcp-save-payment-methods/resources/js/add-payment-method.js index 6be53940c..4a7ab80de 100644 --- a/modules/ppcp-save-payment-methods/resources/js/add-payment-method.js +++ b/modules/ppcp-save-payment-methods/resources/js/add-payment-method.js @@ -128,6 +128,33 @@ document.addEventListener( const result = await response.json(); if(result.success === true) { + if(ppcp_add_payment_method.is_subscription_change_payment_page) { + const subscriptionId = ppcp_add_payment_method.subscription_id_to_change_payment; + if(subscriptionId && result.data) { + const req = await fetch(ppcp_add_payment_method.ajax.subscription_change_payment_method.endpoint, { + method: "POST", + credentials: 'same-origin', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + nonce: ppcp_add_payment_method.ajax.subscription_change_payment_method.nonce, + subscription_id: subscriptionId, + payment_method: getCurrentPaymentMethod(), + wc_payment_token_id: result.data + }) + }); + + const res = await req.json(); + if (res.success === true) { + window.location.href = `${ppcp_add_payment_method.view_subscriptions_page}/${subscriptionId}`; + return; + } + } + + return; + } + window.location.href = ppcp_add_payment_method.payment_methods_page; return; } diff --git a/modules/ppcp-save-payment-methods/src/Endpoint/CreatePaymentToken.php b/modules/ppcp-save-payment-methods/src/Endpoint/CreatePaymentToken.php index 61fcae9da..3a4807ec2 100644 --- a/modules/ppcp-save-payment-methods/src/Endpoint/CreatePaymentToken.php +++ b/modules/ppcp-save-payment-methods/src/Endpoint/CreatePaymentToken.php @@ -80,7 +80,8 @@ class CreatePaymentToken implements EndpointInterface { */ public function handle_request(): bool { try { - $data = $this->request_data->read_request( $this->nonce() ); + $data = $this->request_data->read_request( $this->nonce() ); + $wc_token_id = 0; /** * Suppress ArgumentTypeCoercion @@ -107,7 +108,7 @@ class CreatePaymentToken implements EndpointInterface { $email = $result->payment_source->paypal->email_address; } - $this->wc_payment_tokens->create_payment_token_paypal( + $wc_token_id = $this->wc_payment_tokens->create_payment_token_paypal( $current_user_id, $result->id, $email @@ -115,11 +116,11 @@ class CreatePaymentToken implements EndpointInterface { } if ( isset( $result->payment_source->card ) ) { - $this->wc_payment_tokens->create_payment_token_card( $current_user_id, $result ); + $wc_token_id = $this->wc_payment_tokens->create_payment_token_card( $current_user_id, $result ); } } - wp_send_json_success( $result ); + wp_send_json_success( $wc_token_id ); return true; } catch ( Exception $exception ) { wp_send_json_error(); diff --git a/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php b/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php index 9b055b415..b34092732 100644 --- a/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php +++ b/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php @@ -28,6 +28,7 @@ use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; +use WooCommerce\PayPalCommerce\WcSubscriptions\Endpoint\SubscriptionChangePaymentMethod; /** * Class SavePaymentMethodsModule @@ -211,13 +212,16 @@ class SavePaymentMethodsModule implements ModuleInterface { 'ppcp-add-payment-method', 'ppcp_add_payment_method', array( - 'client_id' => $c->get( 'button.client_id' ), - 'merchant_id' => $c->get( 'api.merchant_id' ), - 'id_token' => $id_token, - 'payment_methods_page' => wc_get_account_endpoint_url( 'payment-methods' ), - 'error_message' => __( 'Could not save payment method.', 'woocommerce-paypal-payments' ), - 'verification_method' => $verification_method, - 'ajax' => array( + 'client_id' => $c->get( 'button.client_id' ), + 'merchant_id' => $c->get( 'api.merchant_id' ), + 'id_token' => $id_token, + 'payment_methods_page' => wc_get_account_endpoint_url( 'payment-methods' ), + 'view_subscriptions_page' => wc_get_account_endpoint_url( 'view-subscription' ), + 'is_subscription_change_payment_page' => $this->is_subscription_change_payment_method_page(), + 'subscription_id_to_change_payment' => $this->is_subscription_change_payment_method_page() ? (int) $_GET['change_payment_method'] : 0, + 'error_message' => __( 'Could not save payment method.', 'woocommerce-paypal-payments' ), + 'verification_method' => $verification_method, + 'ajax' => array( 'create_setup_token' => array( 'endpoint' => \WC_AJAX::get_endpoint( CreateSetupToken::ENDPOINT ), 'nonce' => wp_create_nonce( CreateSetupToken::nonce() ), @@ -226,6 +230,10 @@ class SavePaymentMethodsModule implements ModuleInterface { 'endpoint' => \WC_AJAX::get_endpoint( CreatePaymentToken::ENDPOINT ), 'nonce' => wp_create_nonce( CreatePaymentToken::nonce() ), ), + 'subscription_change_payment_method' => array( + 'endpoint' => \WC_AJAX::get_endpoint( SubscriptionChangePaymentMethod::ENDPOINT ), + 'nonce' => wp_create_nonce( SubscriptionChangePaymentMethod::nonce() ), + ), ), ) ); diff --git a/modules/ppcp-save-payment-methods/src/WooCommercePaymentTokens.php b/modules/ppcp-save-payment-methods/src/WooCommercePaymentTokens.php index 849e1e56e..520099994 100644 --- a/modules/ppcp-save-payment-methods/src/WooCommercePaymentTokens.php +++ b/modules/ppcp-save-payment-methods/src/WooCommercePaymentTokens.php @@ -70,17 +70,17 @@ class WooCommercePaymentTokens { * @param string $token The PayPal payment token. * @param string $email The PayPal customer email. * - * @return void + * @return int */ public function create_payment_token_paypal( int $customer_id, string $token, string $email - ): void { + ): int { $wc_tokens = WC_Payment_Tokens::get_customer_tokens( $customer_id, PayPalGateway::ID ); if ( $this->payment_token_helper->token_exist( $wc_tokens, $token ) ) { - return; + return 0; } $payment_token_paypal = $this->payment_token_factory->create( 'paypal' ); @@ -101,6 +101,8 @@ class WooCommercePaymentTokens { "Could not create WC payment token PayPal for customer {$customer_id}. " . $exception->getMessage() ); } + + return $payment_token_paypal->get_id(); } /** @@ -109,12 +111,12 @@ class WooCommercePaymentTokens { * @param int $customer_id The WC customer ID. * @param stdClass $payment_token The Credit Card payment token. * - * @return void + * @return int */ - public function create_payment_token_card( int $customer_id, stdClass $payment_token ): void { + public function create_payment_token_card( int $customer_id, stdClass $payment_token ): int { $wc_tokens = WC_Payment_Tokens::get_customer_tokens( $customer_id, CreditCardGateway::ID ); if ( $this->payment_token_helper->token_exist( $wc_tokens, $payment_token->id ) ) { - return; + return 0; } $token = new WC_Payment_Token_CC(); @@ -139,6 +141,8 @@ class WooCommercePaymentTokens { "Could not create WC payment token card for customer {$customer_id}. " . $exception->getMessage() ); } + $token->save(); + return $token->get_id(); } } diff --git a/modules/ppcp-wc-subscriptions/services.php b/modules/ppcp-wc-subscriptions/services.php index 47aaf1772..86599b3f2 100644 --- a/modules/ppcp-wc-subscriptions/services.php +++ b/modules/ppcp-wc-subscriptions/services.php @@ -9,10 +9,9 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\WcSubscriptions; -use WooCommerce\PayPalCommerce\PayPalSubscriptions\DeactivatePlanEndpoint; -use WooCommerce\PayPalCommerce\PayPalSubscriptions\SubscriptionsApiHandler; use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; +use WooCommerce\PayPalCommerce\WcSubscriptions\Endpoint\SubscriptionChangePaymentMethod; use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper; return array( @@ -45,4 +44,9 @@ return array( $endpoint = $container->get( 'api.endpoint.payment-token' ); return new PaymentTokenRepository( $factory, $endpoint ); }, + 'wc-subscriptions.endpoint.subscription-change-payment-method' => static function( ContainerInterface $container ): SubscriptionChangePaymentMethod { + return new SubscriptionChangePaymentMethod( + $container->get( 'button.request-data' ) + ); + }, ); diff --git a/modules/ppcp-wc-subscriptions/src/Endpoint/SubscriptionChangePaymentMethod.php b/modules/ppcp-wc-subscriptions/src/Endpoint/SubscriptionChangePaymentMethod.php new file mode 100644 index 000000000..cb277e146 --- /dev/null +++ b/modules/ppcp-wc-subscriptions/src/Endpoint/SubscriptionChangePaymentMethod.php @@ -0,0 +1,75 @@ +request_data = $request_data; + } + + /** + * Returns the nonce. + * + * @return string + */ + public static function nonce(): string { + return self::ENDPOINT; + } + + /** + * Handles the request. + * + * @return bool + * @throws Exception On Error. + */ + public function handle_request(): bool { + try { + $data = $this->request_data->read_request( $this->nonce() ); + + $subscription = wcs_get_subscription( $data['subscription_id'] ); + $subscription->set_payment_method( $data['payment_method'] ); + + $wc_payment_token = WC_Payment_Tokens::get( $data['wc_payment_token_id'] ); + if ( $wc_payment_token ) { + $subscription->add_payment_token( $wc_payment_token ); + $subscription->save(); + } + + wp_send_json_success(); + return true; + } catch ( Exception $exception ) { + wp_send_json_error(); + return false; + } + } +} diff --git a/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php b/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php index c8f0f99a6..1d86d8832 100644 --- a/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php +++ b/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php @@ -23,6 +23,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; use WooCommerce\PayPalCommerce\WcGateway\Processor\TransactionIdHandlingTrait; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; +use WooCommerce\PayPalCommerce\WcSubscriptions\Endpoint\SubscriptionChangePaymentMethod; use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper; /** @@ -169,6 +170,16 @@ class WcSubscriptionsModule implements ModuleInterface { return $methods; } ); + + add_action( + 'wc_ajax_' . SubscriptionChangePaymentMethod::ENDPOINT, + static function () use ( $c ) { + $endpoint = $c->get( 'wc-subscriptions.endpoint.subscription-change-payment-method' ); + assert( $endpoint instanceof SubscriptionChangePaymentMethod ); + + $endpoint->handle_request(); + } + ); } /** From dfd3f0cbe9fd30b4107b09a4b0e1f3b77ea7e376 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 9 Jan 2024 12:15:48 +0100 Subject: [PATCH 08/14] Ensure change payment method only submit card fields for new payment --- .../resources/js/add-payment-method.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modules/ppcp-save-payment-methods/resources/js/add-payment-method.js b/modules/ppcp-save-payment-methods/resources/js/add-payment-method.js index 4a7ab80de..4e67aaf1b 100644 --- a/modules/ppcp-save-payment-methods/resources/js/add-payment-method.js +++ b/modules/ppcp-save-payment-methods/resources/js/add-payment-method.js @@ -198,6 +198,13 @@ document.addEventListener( } document.querySelector('#place_order')?.addEventListener("click", (event) => { + if ( + getCurrentPaymentMethod() !== 'ppcp-credit-card-gateway' + || document.querySelector('input[name="wc-ppcp-credit-card-gateway-payment-token"]:checked')?.value !== 'new' + ) { + return; + } + event.preventDefault(); cardField.submit() From cd8cfaf0b845c2c48fe7f555b0ed3e3eff9e0ed5 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 9 Jan 2024 16:18:16 +0100 Subject: [PATCH 09/14] Fix add payment method conflict with button event --- .../resources/js/add-payment-method.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-save-payment-methods/resources/js/add-payment-method.js b/modules/ppcp-save-payment-methods/resources/js/add-payment-method.js index 4e67aaf1b..6c2c51783 100644 --- a/modules/ppcp-save-payment-methods/resources/js/add-payment-method.js +++ b/modules/ppcp-save-payment-methods/resources/js/add-payment-method.js @@ -198,9 +198,10 @@ document.addEventListener( } document.querySelector('#place_order')?.addEventListener("click", (event) => { + const cardPaymentToken = document.querySelector('input[name="wc-ppcp-credit-card-gateway-payment-token"]:checked')?.value; if ( getCurrentPaymentMethod() !== 'ppcp-credit-card-gateway' - || document.querySelector('input[name="wc-ppcp-credit-card-gateway-payment-token"]:checked')?.value !== 'new' + || cardPaymentToken && cardPaymentToken !== 'new' ) { return; } From 51aa79af11fde7bf448cc79d11ce6db4a561919e Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Thu, 11 Jan 2024 12:29:36 +0100 Subject: [PATCH 10/14] Disable save to account checkout if subscription in the cart --- .../js/modules/Renderer/CardFieldsRenderer.js | 8 +++++++ .../ppcp-button/src/Assets/SmartButton.php | 1 + .../resources/js/add-payment-method.js | 8 +++++++ .../src/WcSubscriptionsModule.php | 24 +++++++++---------- 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/modules/ppcp-button/resources/js/modules/Renderer/CardFieldsRenderer.js b/modules/ppcp-button/resources/js/modules/Renderer/CardFieldsRenderer.js index 4cf07a0bf..3287c7492 100644 --- a/modules/ppcp-button/resources/js/modules/Renderer/CardFieldsRenderer.js +++ b/modules/ppcp-button/resources/js/modules/Renderer/CardFieldsRenderer.js @@ -112,6 +112,14 @@ class CardFieldsRenderer { show(buttonSelector); + if(this.defaultConfig.cart_contains_subscription) { + const saveToAccount = document.querySelector('#wc-ppcp-credit-card-gateway-new-payment-method'); + if(saveToAccount) { + saveToAccount.checked = true; + saveToAccount.disabled = true; + } + } + document.querySelector(buttonSelector).addEventListener("click", (event) => { event.preventDefault(); this.spinner.block(); diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index 24393d6b4..2f6a24781 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -1056,6 +1056,7 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages 'endpoint' => \WC_AJAX::get_endpoint( CartScriptParamsEndpoint::ENDPOINT ), ), ), + 'cart_contains_subscription' => $this->subscription_helper->cart_contains_subscription(), 'subscription_plan_id' => $this->subscription_helper->paypal_subscription_id(), 'variable_paypal_subscription_variations' => $this->subscription_helper->variable_paypal_subscription_variations(), 'subscription_product_allowed' => $this->subscription_helper->checkout_subscription_product_allowed(), diff --git a/modules/ppcp-save-payment-methods/resources/js/add-payment-method.js b/modules/ppcp-save-payment-methods/resources/js/add-payment-method.js index 6c2c51783..8aca64f7c 100644 --- a/modules/ppcp-save-payment-methods/resources/js/add-payment-method.js +++ b/modules/ppcp-save-payment-methods/resources/js/add-payment-method.js @@ -28,6 +28,14 @@ document.addEventListener( init() }); + if(ppcp_add_payment_method.is_subscription_change_payment_page) { + const saveToAccount = document.querySelector('#wc-ppcp-credit-card-gateway-new-payment-method'); + if(saveToAccount) { + saveToAccount.checked = true; + saveToAccount.disabled = true; + } + } + setTimeout(() => { loadScript({ clientId: ppcp_add_payment_method.client_id, diff --git a/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php b/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php index 1d86d8832..6e3e25937 100644 --- a/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php +++ b/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php @@ -152,8 +152,11 @@ class WcSubscriptionsModule implements ModuleInterface { add_filter( 'woocommerce_available_payment_gateways', - function( array $methods ): array { - if ( ! is_wc_endpoint_url( 'order-pay' ) ) { + function( array $methods ) use ( $c ) : array { + if ( + ! is_wc_endpoint_url( 'order-pay' ) + || $c->has( 'save-payment-methods.eligible' ) && $c->get( 'save-payment-methods.eligible' ) + ) { return $methods; } @@ -261,18 +264,15 @@ class WcSubscriptionsModule implements ModuleInterface { && $subscription_helper->is_subscription_change_payment() ) { $tokens = WC_Payment_Tokens::get_customer_tokens( get_current_user_id(), PayPalGateway::ID ); - $output = sprintf( - '

', $token->get_id() ); + $output .= sprintf( '', $token->get_meta( 'email' ) ?? '' ); + $output .= ''; } - $output .= '

'; + $output .= ''; return $output; } From 8a5e823801406dd0e5adeae133fd2c60e8c53107 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Fri, 12 Jan 2024 09:58:35 +0100 Subject: [PATCH 11/14] Fix phpunit --- .../Vaulting/VaultedCreditCardHandlerTest.php | 18 ------------------ .../Gateway/CreditCardGatewayTest.php | 2 ++ 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/tests/PHPUnit/Vaulting/VaultedCreditCardHandlerTest.php b/tests/PHPUnit/Vaulting/VaultedCreditCardHandlerTest.php index fa92f6f89..fb728709a 100644 --- a/tests/PHPUnit/Vaulting/VaultedCreditCardHandlerTest.php +++ b/tests/PHPUnit/Vaulting/VaultedCreditCardHandlerTest.php @@ -68,24 +68,6 @@ class VaultedCreditCardHandlerTest extends TestCase ); } - public function testHandlePaymentChangingPayment() - { - $_POST['woocommerce_change_payment'] = 1; - $wcOrder = Mockery::mock(\WC_Order::class); - $wcOrder->shouldReceive('get_id')->andReturn(1); - $wcOrder->shouldReceive('update_meta_data') - ->with('payment_token_id', 'abc123') - ->andReturn(1); - $wcOrder->shouldReceive('save')->andReturn(1); - $this->subscriptionHelper->shouldReceive('has_subscription')->andReturn(true); - $this->subscriptionHelper->shouldReceive('is_subscription_change_payment')->andReturn(true); - - $customer = Mockery::mock(WC_Customer::class); - - $result = $this->testee->handle_payment('abc123', $wcOrder, $customer); - $this->assertInstanceOf(\WC_Order::class, $result); - } - public function testHandlePayment() { $_POST['woocommerce_change_payment'] = null; diff --git a/tests/PHPUnit/WcGateway/Gateway/CreditCardGatewayTest.php b/tests/PHPUnit/WcGateway/Gateway/CreditCardGatewayTest.php index f76438f98..15689d08e 100644 --- a/tests/PHPUnit/WcGateway/Gateway/CreditCardGatewayTest.php +++ b/tests/PHPUnit/WcGateway/Gateway/CreditCardGatewayTest.php @@ -109,6 +109,8 @@ class CreditCardGatewayTest extends TestCase $session->shouldReceive('set')->andReturn([]); $session->shouldReceive('get')->andReturn(''); + when('is_checkout')->justReturn(true); + $savedCreditCard = 'abc123'; $_POST['saved_credit_card'] = $savedCreditCard; From eb7e921d44ece11da6de3e72e0afaf90d2c8fdea Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Fri, 12 Jan 2024 10:48:50 +0100 Subject: [PATCH 12/14] Fix psalm --- .../SubscriptionChangePaymentMethod.php | 20 ++++++++++++------- .../src/WcSubscriptionsModule.php | 4 +++- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/modules/ppcp-wc-subscriptions/src/Endpoint/SubscriptionChangePaymentMethod.php b/modules/ppcp-wc-subscriptions/src/Endpoint/SubscriptionChangePaymentMethod.php index cb277e146..7f3df3601 100644 --- a/modules/ppcp-wc-subscriptions/src/Endpoint/SubscriptionChangePaymentMethod.php +++ b/modules/ppcp-wc-subscriptions/src/Endpoint/SubscriptionChangePaymentMethod.php @@ -10,6 +10,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\WcSubscriptions\Endpoint; use Exception; +use WC_Order; use WC_Payment_Tokens; use WooCommerce\PayPalCommerce\Button\Endpoint\EndpointInterface; use WooCommerce\PayPalCommerce\Button\Endpoint\RequestData; @@ -57,16 +58,21 @@ class SubscriptionChangePaymentMethod implements EndpointInterface { $data = $this->request_data->read_request( $this->nonce() ); $subscription = wcs_get_subscription( $data['subscription_id'] ); - $subscription->set_payment_method( $data['payment_method'] ); + if ( $subscription instanceof WC_Order ) { + $subscription->set_payment_method( $data['payment_method'] ); - $wc_payment_token = WC_Payment_Tokens::get( $data['wc_payment_token_id'] ); - if ( $wc_payment_token ) { - $subscription->add_payment_token( $wc_payment_token ); - $subscription->save(); + $wc_payment_token = WC_Payment_Tokens::get( $data['wc_payment_token_id'] ); + if ( $wc_payment_token ) { + $subscription->add_payment_token( $wc_payment_token ); + $subscription->save(); + } + + wp_send_json_success(); + return true; } - wp_send_json_success(); - return true; + wp_send_json_error(); + return false; } catch ( Exception $exception ) { wp_send_json_error(); return false; diff --git a/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php b/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php index 6e3e25937..0f2fa2e21 100644 --- a/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php +++ b/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php @@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\WcSubscriptions; use Psr\Log\LoggerInterface; use WC_Order; +use WC_Payment_Token_CC; use WC_Payment_Tokens; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository; @@ -309,13 +310,14 @@ class WcSubscriptionsModule implements ModuleInterface { esc_html__( 'Select a saved Credit Card payment', 'woocommerce-paypal-payments' ) ); foreach ( $tokens as $token ) { + if ( $token instanceof WC_Payment_Token_CC ) { $output .= sprintf( '', $token->get_id(), $token->get_card_type(), $token->get_last4() ); - + } } $output .= '

'; From f35373f73dfae625a19cbab9d1cc4b4131b8a3a5 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Fri, 12 Jan 2024 11:02:13 +0100 Subject: [PATCH 13/14] Fix phpcs --- .../ppcp-save-payment-methods/src/SavePaymentMethodsModule.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php b/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php index b34092732..be10dccb2 100644 --- a/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php +++ b/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php @@ -208,6 +208,7 @@ class SavePaymentMethodsModule implements ModuleInterface { assert( $settings instanceof Settings ); $verification_method = $settings->has( '3d_secure_contingency' ) ? $settings->get( '3d_secure_contingency' ) : ''; + $change_payment_method = wc_clean( wp_unslash( $_GET['change_payment_method'] ?? 0 ) ); // phpcs:ignore WordPress.Security.NonceVerification wp_localize_script( 'ppcp-add-payment-method', 'ppcp_add_payment_method', @@ -218,7 +219,7 @@ class SavePaymentMethodsModule implements ModuleInterface { 'payment_methods_page' => wc_get_account_endpoint_url( 'payment-methods' ), 'view_subscriptions_page' => wc_get_account_endpoint_url( 'view-subscription' ), 'is_subscription_change_payment_page' => $this->is_subscription_change_payment_method_page(), - 'subscription_id_to_change_payment' => $this->is_subscription_change_payment_method_page() ? (int) $_GET['change_payment_method'] : 0, + 'subscription_id_to_change_payment' => $this->is_subscription_change_payment_method_page() ? (int) $change_payment_method : 0, 'error_message' => __( 'Could not save payment method.', 'woocommerce-paypal-payments' ), 'verification_method' => $verification_method, 'ajax' => array( From 65ba26e7cc297b42a6615ee20bc6382c6b9cb4bc Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Fri, 12 Jan 2024 11:05:52 +0100 Subject: [PATCH 14/14] Fix psalm --- .../ppcp-save-payment-methods/src/SavePaymentMethodsModule.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php b/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php index be10dccb2..4f3fc1fb1 100644 --- a/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php +++ b/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php @@ -208,7 +208,8 @@ class SavePaymentMethodsModule implements ModuleInterface { assert( $settings instanceof Settings ); $verification_method = $settings->has( '3d_secure_contingency' ) ? $settings->get( '3d_secure_contingency' ) : ''; - $change_payment_method = wc_clean( wp_unslash( $_GET['change_payment_method'] ?? 0 ) ); // phpcs:ignore WordPress.Security.NonceVerification + $change_payment_method = wc_clean( wp_unslash( $_GET['change_payment_method'] ?? '' ) ); // phpcs:ignore WordPress.Security.NonceVerification + wp_localize_script( 'ppcp-add-payment-method', 'ppcp_add_payment_method',