From 197e70607a6ffcb620b9688ed74d377bd80425c6 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 9 Apr 2024 17:34:51 +0200 Subject: [PATCH] Add payment token for guest free trial subscription (WIP) --- .../ActionHandler/CheckoutActionHandler.js | 7 +- .../ppcp-button/src/Assets/SmartButton.php | 5 ++ .../ppcp-save-payment-methods/services.php | 7 ++ .../Endpoint/CreatePaymentTokenForGuest.php | 90 +++++++++++++++++++ .../src/SavePaymentMethodsModule.php | 11 +++ modules/ppcp-wc-gateway/services.php | 3 +- .../src/Gateway/PayPalGateway.php | 45 +++++++++- 7 files changed, 161 insertions(+), 7 deletions(-) create mode 100644 modules/ppcp-save-payment-methods/src/Endpoint/CreatePaymentTokenForGuest.php diff --git a/modules/ppcp-button/resources/js/modules/ActionHandler/CheckoutActionHandler.js b/modules/ppcp-button/resources/js/modules/ActionHandler/CheckoutActionHandler.js index f3afd4f30..ed5926816 100644 --- a/modules/ppcp-button/resources/js/modules/ActionHandler/CheckoutActionHandler.js +++ b/modules/ppcp-button/resources/js/modules/ActionHandler/CheckoutActionHandler.js @@ -167,22 +167,21 @@ class CheckoutActionHandler { console.error(result) }, onApprove: async ({vaultSetupToken}) => { - const response = await fetch(this.config.ajax.create_payment_token.endpoint, { + const response = await fetch(this.config.ajax.create_payment_token_for_guest.endpoint, { method: "POST", credentials: 'same-origin', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ - nonce: this.config.ajax.create_payment_token.nonce, + nonce: this.config.ajax.create_payment_token_for_guest.nonce, vault_setup_token: vaultSetupToken, - return_url: location.href, }) }) const result = await response.json(); if (result.success === true) { - window.location.href = location.href; + document.querySelector('#place_order').click() return; } diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index db703c7f1..6f73e6d88 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -36,6 +36,7 @@ use WooCommerce\PayPalCommerce\Onboarding\Environment; use WooCommerce\PayPalCommerce\PayLaterBlock\PayLaterBlockModule; use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreatePaymentToken; use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreateSetupToken; +use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreatePaymentTokenForGuest; use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\WcSubscriptions\FreeTrialHandlerTrait; use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper; @@ -1101,6 +1102,10 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages 'endpoint' => \WC_AJAX::get_endpoint( CreatePaymentToken::ENDPOINT ), 'nonce' => wp_create_nonce( CreatePaymentToken::nonce() ), ), + 'create_payment_token_for_guest' => array( + 'endpoint' => \WC_AJAX::get_endpoint( CreatePaymentTokenForGuest::ENDPOINT ), + 'nonce' => wp_create_nonce( CreatePaymentTokenForGuest::nonce() ), + ), ), 'cart_contains_subscription' => $this->subscription_helper->cart_contains_subscription(), 'subscription_plan_id' => $this->subscription_helper->paypal_subscription_id(), diff --git a/modules/ppcp-save-payment-methods/services.php b/modules/ppcp-save-payment-methods/services.php index 2453235c8..a0b43f021 100644 --- a/modules/ppcp-save-payment-methods/services.php +++ b/modules/ppcp-save-payment-methods/services.php @@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\SavePaymentMethods; use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreatePaymentToken; use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreateSetupToken; +use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreatePaymentTokenForGuest; use WooCommerce\PayPalCommerce\SavePaymentMethods\Helper\SavePaymentMethodsApplies; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; @@ -811,4 +812,10 @@ return array( $container->get( 'vaulting.wc-payment-tokens' ) ); }, + 'save-payment-methods.endpoint.create-payment-token-for-guest' => static function ( ContainerInterface $container ): CreatePaymentTokenForGuest { + return new CreatePaymentTokenForGuest( + $container->get( 'button.request-data' ), + $container->get( 'api.endpoint.payment-method-tokens' ) + ); + }, ); diff --git a/modules/ppcp-save-payment-methods/src/Endpoint/CreatePaymentTokenForGuest.php b/modules/ppcp-save-payment-methods/src/Endpoint/CreatePaymentTokenForGuest.php new file mode 100644 index 000000000..e458c96be --- /dev/null +++ b/modules/ppcp-save-payment-methods/src/Endpoint/CreatePaymentTokenForGuest.php @@ -0,0 +1,90 @@ +request_data = $request_data; + $this->payment_method_tokens_endpoint = $payment_method_tokens_endpoint; + } + + /** + * 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 { + $data = $this->request_data->read_request( $this->nonce() ); + + /** + * Suppress ArgumentTypeCoercion + * + * @psalm-suppress ArgumentTypeCoercion + */ + $payment_source = new PaymentSource( + 'token', + (object) array( + 'id' => $data['vault_setup_token'], + 'type' => 'SETUP_TOKEN', + ) + ); + + $result = $this->payment_method_tokens_endpoint->create_payment_token( $payment_source ); + WC()->session->set( 'ppcp_guest_payment_for_free_trial', $result ); + + wp_send_json_success(); + return true; + } +} diff --git a/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php b/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php index 61244b2e2..8daafae4a 100644 --- a/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php +++ b/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php @@ -20,6 +20,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; use WooCommerce\PayPalCommerce\Button\Helper\ContextTrait; use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreatePaymentToken; +use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreatePaymentTokenForGuest; use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreateSetupToken; use WooCommerce\PayPalCommerce\Vaulting\WooCommercePaymentTokens; use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider; @@ -371,6 +372,16 @@ class SavePaymentMethodsModule implements ModuleInterface { } ); + add_action( + 'wc_ajax_' . CreatePaymentTokenForGuest::ENDPOINT, + static function () use ( $c ) { + $endpoint = $c->get( 'save-payment-methods.endpoint.create-payment-token-for-guest' ); + assert( $endpoint instanceof CreatePaymentTokenForGuest ); + + $endpoint->handle_request(); + } + ); + add_action( 'woocommerce_paypal_payments_before_delete_payment_token', function( string $token_id ) use ( $c ) { diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index 83c55ae1a..6bf3d5ecf 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -105,7 +105,8 @@ return array( $container->get( 'api.factory.paypal-checkout-url' ), $container->get( 'wcgateway.place-order-button-text' ), $container->get( 'api.endpoint.payment-tokens' ), - $container->get( 'vaulting.vault-v3-enabled' ) + $container->get( 'vaulting.vault-v3-enabled' ), + $container->get( 'vaulting.wc-payment-tokens' ) ); }, 'wcgateway.credit-card-gateway' => static function ( ContainerInterface $container ): CreditCardGateway { diff --git a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php index d341000dd..20b7a840d 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php @@ -21,6 +21,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; use WooCommerce\PayPalCommerce\Onboarding\Environment; use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Session\SessionHandler; +use WooCommerce\PayPalCommerce\Vaulting\WooCommercePaymentTokens; use WooCommerce\PayPalCommerce\WcSubscriptions\FreeTrialHandlerTrait; use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper; use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository; @@ -194,6 +195,13 @@ class PayPalGateway extends \WC_Payment_Gateway { */ private $vault_v3_enabled; + /** + * WooCommerce payment tokens. + * + * @var WooCommercePaymentTokens + */ + private $wc_payment_tokens; + /** * PayPalGateway constructor. * @@ -216,6 +224,7 @@ class PayPalGateway extends \WC_Payment_Gateway { * @param string $place_order_button_text The text for the standard "Place order" button. * @param PaymentTokensEndpoint $payment_tokens_endpoint Payment tokens endpoint. * @param bool $vault_v3_enabled Whether Vault v3 module is enabled. + * @param WooCommercePaymentTokens $wc_payment_tokens WooCommerce payment tokens. */ public function __construct( SettingsRenderer $settings_renderer, @@ -236,7 +245,8 @@ class PayPalGateway extends \WC_Payment_Gateway { callable $paypal_checkout_url_factory, string $place_order_button_text, PaymentTokensEndpoint $payment_tokens_endpoint, - bool $vault_v3_enabled + bool $vault_v3_enabled, + WooCommercePaymentTokens $wc_payment_tokens ) { $this->id = self::ID; $this->settings_renderer = $settings_renderer; @@ -259,6 +269,7 @@ class PayPalGateway extends \WC_Payment_Gateway { $this->order_endpoint = $order_endpoint; $this->payment_tokens_endpoint = $payment_tokens_endpoint; $this->vault_v3_enabled = $vault_v3_enabled; + $this->wc_payment_tokens = $wc_payment_tokens; if ( $this->onboarded ) { $this->supports = array( 'refunds', 'tokenization' ); @@ -520,7 +531,37 @@ class PayPalGateway extends \WC_Payment_Gateway { $wc_order->save(); } - if ( 'card' !== $funding_source && $this->is_free_trial_order( $wc_order ) && ! $this->subscription_helper->paypal_subscription_id() ) { + if ( + 'card' !== $funding_source + && $this->is_free_trial_order( $wc_order ) + && ! $this->subscription_helper->paypal_subscription_id() + ) { + $ppcp_guest_payment_for_free_trial = WC()->session->get( 'ppcp_guest_payment_for_free_trial') ?? null; + if($this->vault_v3_enabled && $ppcp_guest_payment_for_free_trial) { + $customer_id = $ppcp_guest_payment_for_free_trial->customer->id ?? ''; + if($customer_id) { + update_user_meta( $wc_order->get_customer_id(), '_ppcp_target_customer_id', $customer_id ); + } + + if ( isset( $ppcp_guest_payment_for_free_trial->payment_source->paypal ) ) { + $email = ''; + if ( isset( $ppcp_guest_payment_for_free_trial->payment_source->paypal->email_address ) ) { + $email = $ppcp_guest_payment_for_free_trial->payment_source->paypal->email_address; + } + + $this->wc_payment_tokens->create_payment_token_paypal( + $wc_order->get_customer_id(), + $ppcp_guest_payment_for_free_trial->id, + $email + ); + } + + WC()->session->set( 'ppcp_guest_payment_for_free_trial', null); + + $wc_order->payment_complete(); + return $this->handle_payment_success( $wc_order ); + } + $customer_id = get_user_meta( $wc_order->get_customer_id(), '_ppcp_target_customer_id', true ); if ( $customer_id ) { $customer_tokens = $this->payment_tokens_endpoint->payment_tokens_for_customer( $customer_id );