Save checkout form before free trial redirect

This commit is contained in:
Alex P 2023-01-25 09:42:31 +02:00
parent c8e492ed1e
commit 79f2c9a1fa
No known key found for this signature in database
GPG key ID: 54487A734A204D71
8 changed files with 220 additions and 26 deletions

View file

@ -17,6 +17,7 @@ import {
import {hide, setVisible, setVisibleByClass} from "./modules/Helper/Hiding";
import {isChangePaymentPage} from "./modules/Helper/Subscriptions";
import FreeTrialHandler from "./modules/ActionHandler/FreeTrialHandler";
import FormSaver from './modules/Helper/FormSaver';
// TODO: could be a good idea to have a separate spinner for each gateway,
// but I think we care mainly about the script loading, so one spinner should be enough.
@ -24,11 +25,18 @@ const buttonsSpinner = new Spinner(document.querySelector('.ppc-button-wrapper')
const cardsSpinner = new Spinner('#ppcp-hosted-fields');
const bootstrap = () => {
const checkoutFormSelector = 'form.woocommerce-checkout';
const errorHandler = new ErrorHandler(PayPalCommerceGateway.labels.error.generic);
const spinner = new Spinner();
const creditCardRenderer = new CreditCardRenderer(PayPalCommerceGateway, errorHandler, spinner);
const freeTrialHandler = new FreeTrialHandler(PayPalCommerceGateway, spinner, errorHandler);
const formSaver = new FormSaver(
PayPalCommerceGateway.ajax.save_checkout_form.endpoint,
PayPalCommerceGateway.ajax.save_checkout_form.nonce,
);
const freeTrialHandler = new FreeTrialHandler(PayPalCommerceGateway, checkoutFormSelector, formSaver, spinner, errorHandler);
jQuery('form.woocommerce-checkout input').on('keydown', e => {
if (e.key === 'Enter' && [
@ -88,7 +96,7 @@ const bootstrap = () => {
}
}
const form = document.querySelector('form.woocommerce-checkout');
const form = document.querySelector(checkoutFormSelector);
if (form) {
jQuery('#ppcp-funding-source-form-input').remove();
form.insertAdjacentHTML(

View file

@ -1,44 +1,50 @@
import {PaymentMethods} from "../Helper/CheckoutMethodState";
import errorHandler from "../ErrorHandler";
class FreeTrialHandler {
constructor(
config,
formSelector,
formSaver,
spinner,
errorHandler
) {
this.config = config;
this.formSelector = formSelector;
this.formSaver = formSaver;
this.spinner = spinner;
this.errorHandler = errorHandler;
}
handle()
async handle()
{
this.spinner.block();
fetch(this.config.ajax.vault_paypal.endpoint, {
method: 'POST',
credentials: 'same-origin',
body: JSON.stringify({
nonce: this.config.ajax.vault_paypal.nonce,
return_url: location.href
}),
}).then(res => {
return res.json();
}).then(data => {
try {
await this.formSaver.save(document.querySelector(this.formSelector));
} catch (error) {
console.error(error);
}
try {
const res = await fetch(this.config.ajax.vault_paypal.endpoint, {
method: 'POST',
credentials: 'same-origin',
body: JSON.stringify({
nonce: this.config.ajax.vault_paypal.nonce,
return_url: location.href,
}),
});
const data = await res.json();
if (!data.success) {
this.spinner.unblock();
console.error(data);
this.errorHandler.message(data.data.message);
throw Error(data.data.message);
}
location.href = data.data.approve_link;
}).catch(error => {
} catch (error) {
this.spinner.unblock();
console.error(error);
this.errorHandler.genericError();
});
this.errorHandler.message(data.data.message);
}
}
}
export default FreeTrialHandler;

View file

@ -0,0 +1,26 @@
export default class FormSaver {
constructor(url, nonce) {
this.url = url;
this.nonce = nonce;
}
async save(form) {
const formData = new FormData(form);
const formJsonObj = Object.fromEntries(formData.entries());
const res = await fetch(this.url, {
method: 'POST',
credentials: 'same-origin',
body: JSON.stringify({
nonce: this.nonce,
form: formJsonObj,
}),
});
const data = await res.json();
if (!data.success) {
throw Error(data.data.message);
}
}
}

View file

@ -9,6 +9,8 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Button;
use WooCommerce\PayPalCommerce\Button\Endpoint\SaveCheckoutFormEndpoint;
use WooCommerce\PayPalCommerce\Button\Helper\CheckoutFormSaver;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\Button\Assets\DisabledSmartButton;
use WooCommerce\PayPalCommerce\Button\Assets\SmartButton;
@ -181,6 +183,16 @@ return array(
$logger
);
},
'button.checkout-form-saver' => static function ( ContainerInterface $container ): CheckoutFormSaver {
return new CheckoutFormSaver();
},
'button.endpoint.save-checkout-form' => static function ( ContainerInterface $container ): SaveCheckoutFormEndpoint {
return new SaveCheckoutFormEndpoint(
$container->get( 'button.request-data' ),
$container->get( 'button.checkout-form-saver' ),
$container->get( 'woocommerce.logger.woocommerce' )
);
},
'button.endpoint.data-client-id' => static function( ContainerInterface $container ) : DataClientIdEndpoint {
$request_data = $container->get( 'button.request-data' );
$identity_token = $container->get( 'api.endpoint.identity-token' );

View file

@ -21,6 +21,7 @@ use WooCommerce\PayPalCommerce\Button\Endpoint\ChangeCartEndpoint;
use WooCommerce\PayPalCommerce\Button\Endpoint\CreateOrderEndpoint;
use WooCommerce\PayPalCommerce\Button\Endpoint\DataClientIdEndpoint;
use WooCommerce\PayPalCommerce\Button\Endpoint\RequestData;
use WooCommerce\PayPalCommerce\Button\Endpoint\SaveCheckoutFormEndpoint;
use WooCommerce\PayPalCommerce\Button\Endpoint\StartPayPalVaultingEndpoint;
use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply;
use WooCommerce\PayPalCommerce\Onboarding\Environment;
@ -761,22 +762,26 @@ class SmartButton implements SmartButtonInterface {
'redirect' => wc_get_checkout_url(),
'context' => $this->context(),
'ajax' => array(
'change_cart' => array(
'change_cart' => array(
'endpoint' => \WC_AJAX::get_endpoint( ChangeCartEndpoint::ENDPOINT ),
'nonce' => wp_create_nonce( ChangeCartEndpoint::nonce() ),
),
'create_order' => array(
'create_order' => array(
'endpoint' => \WC_AJAX::get_endpoint( CreateOrderEndpoint::ENDPOINT ),
'nonce' => wp_create_nonce( CreateOrderEndpoint::nonce() ),
),
'approve_order' => array(
'approve_order' => array(
'endpoint' => \WC_AJAX::get_endpoint( ApproveOrderEndpoint::ENDPOINT ),
'nonce' => wp_create_nonce( ApproveOrderEndpoint::nonce() ),
),
'vault_paypal' => array(
'vault_paypal' => array(
'endpoint' => \WC_AJAX::get_endpoint( StartPayPalVaultingEndpoint::ENDPOINT ),
'nonce' => wp_create_nonce( StartPayPalVaultingEndpoint::nonce() ),
),
'save_checkout_form' => array(
'endpoint' => \WC_AJAX::get_endpoint( SaveCheckoutFormEndpoint::ENDPOINT ),
'nonce' => wp_create_nonce( SaveCheckoutFormEndpoint::nonce() ),
),
),
'enforce_vault' => $this->has_subscriptions(),
'can_save_vault_token' => $this->can_save_vault_token(),

View file

@ -9,6 +9,7 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Button;
use WooCommerce\PayPalCommerce\Button\Endpoint\SaveCheckoutFormEndpoint;
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
use WooCommerce\PayPalCommerce\Button\Assets\SmartButtonInterface;
@ -156,6 +157,16 @@ class ButtonModule implements ModuleInterface {
$endpoint->handle_request();
}
);
add_action(
'wc_ajax_' . SaveCheckoutFormEndpoint::ENDPOINT,
static function () use ( $container ) {
$endpoint = $container->get( 'button.endpoint.save-checkout-form' );
assert( $endpoint instanceof SaveCheckoutFormEndpoint );
$endpoint->handle_request();
}
);
}
/**

View file

@ -0,0 +1,94 @@
<?php
/**
* Saves the form data to the WC customer and session.
*
* @package WooCommerce\PayPalCommerce\Button\Endpoint
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Button\Endpoint;
use Exception;
use Psr\Log\LoggerInterface;
use WooCommerce\PayPalCommerce\Button\Helper\CheckoutFormSaver;
/**
* Class SaveCheckoutFormEndpoint
*/
class SaveCheckoutFormEndpoint implements EndpointInterface {
const ENDPOINT = 'ppc-save-checkout-form';
/**
* The Request Data Helper.
*
* @var RequestData
*/
private $request_data;
/**
* The checkout form saver.
*
* @var CheckoutFormSaver
*/
private $checkout_form_saver;
/**
* The logger.
*
* @var LoggerInterface
*/
protected $logger;
/**
* SaveCheckoutFormEndpoint constructor.
*
* @param RequestData $request_data The Request Data Helper.
* @param CheckoutFormSaver $checkout_form_saver The checkout form saver.
* @param LoggerInterface $logger The logger.
*/
public function __construct(
RequestData $request_data,
CheckoutFormSaver $checkout_form_saver,
LoggerInterface $logger
) {
$this->request_data = $request_data;
$this->checkout_form_saver = $checkout_form_saver;
$this->logger = $logger;
}
/**
* Returns the nonce.
*
* @return string
*/
public static function nonce(): string {
return self::ENDPOINT;
}
/**
* Handles the request.
*
* @return bool
*/
public function handle_request(): bool {
try {
$data = $this->request_data->read_request( $this->nonce() );
$this->checkout_form_saver->save( $data['form'] );
wp_send_json_success();
return true;
} catch ( Exception $error ) {
$this->logger->error( 'Checkout form saving failed: ' . $error->getMessage() );
wp_send_json_error(
array(
'message' => $error->getMessage(),
)
);
return false;
}
}
}

View file

@ -0,0 +1,32 @@
<?php
/**
* Saves the form data to the WC customer and session.
*
* @package WooCommerce\PayPalCommerce\Button\Helper
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Button\Helper;
use WC_Checkout;
/**
* Class CheckoutFormSaver
*/
class CheckoutFormSaver extends WC_Checkout {
/**
* Saves the form data to the WC customer and session.
*
* @param array $data The form data.
* @return void
*/
public function save( array $data ) {
foreach ( $data as $key => $value ) {
$_POST[ $key ] = $value;
}
$data = $this->get_posted_data();
$this->update_session( $data );
}
}