Extract actions from button components

This commit is contained in:
Emili Castells Guasch 2024-12-04 16:28:59 +01:00
parent 855a9caef6
commit e37d362302
2 changed files with 385 additions and 296 deletions

View file

@ -4,7 +4,6 @@ import {
mergeWcAddress, mergeWcAddress,
paypalAddressToWc, paypalAddressToWc,
paypalOrderToWcAddresses, paypalOrderToWcAddresses,
paypalSubscriptionToWcAddresses,
} from '../Helper/Address'; } from '../Helper/Address';
import { convertKeysToSnakeCase } from '../Helper/Helper'; import { convertKeysToSnakeCase } from '../Helper/Helper';
import buttonModuleWatcher from '../../../../ppcp-button/resources/js/modules/ButtonModuleWatcher'; import buttonModuleWatcher from '../../../../ppcp-button/resources/js/modules/ButtonModuleWatcher';
@ -13,6 +12,14 @@ import {
cartHasSubscriptionProducts, cartHasSubscriptionProducts,
isPayPalSubscription, isPayPalSubscription,
} from '../Helper/Subscription'; } from '../Helper/Subscription';
import {
createOrder,
createSubscription,
createVaultSetupToken,
handleApprove,
handleApproveSubscription,
onApproveSavePayment,
} from '../paypal-config';
const PAYPAL_GATEWAY_ID = 'ppcp-gateway'; const PAYPAL_GATEWAY_ID = 'ppcp-gateway';
@ -114,156 +121,6 @@ export const PayPalComponent = ( {
setContinuationFilled( true ); setContinuationFilled( true );
}, [ shippingData, continuationFilled ] ); }, [ shippingData, continuationFilled ] );
const createOrder = async ( data ) => {
try {
const requestBody = {
nonce: config.scriptData.ajax.create_order.nonce,
bn_code: '',
context: config.scriptData.context,
payment_method: 'ppcp-gateway',
funding_source: window.ppcpFundingSource ?? 'paypal',
createaccount: false,
...( data?.paymentSource && {
payment_source: data.paymentSource,
} ),
};
const res = await fetch(
config.scriptData.ajax.create_order.endpoint,
{
method: 'POST',
credentials: 'same-origin',
body: JSON.stringify( requestBody ),
}
);
const json = await res.json();
if ( ! json.success ) {
if ( json.data?.details?.length > 0 ) {
throw new Error(
json.data.details
.map( ( d ) => `${ d.issue } ${ d.description }` )
.join( '<br/>' )
);
} else if ( json.data?.message ) {
throw new Error( json.data.message );
}
throw new Error( config.scriptData.labels.error.generic );
}
return json.data.id;
} catch ( err ) {
console.error( err );
onError( err.message );
onClose();
throw err;
}
};
const createSubscription = async ( data, actions ) => {
let planId = config.scriptData.subscription_plan_id;
if (
config.scriptData
.variable_paypal_subscription_variation_from_cart !== ''
) {
planId =
config.scriptData
.variable_paypal_subscription_variation_from_cart;
}
return actions.subscription.create( {
plan_id: planId,
} );
};
const handleApproveSubscription = async ( data, actions ) => {
try {
const subscription = await actions.subscription.get();
if ( subscription ) {
const addresses =
paypalSubscriptionToWcAddresses( subscription );
const promises = [
// save address on server
wp.data.dispatch( 'wc/store/cart' ).updateCustomerData( {
billing_address: addresses.billingAddress,
shipping_address: addresses.shippingAddress,
} ),
];
if ( shouldHandleShippingInPayPal() ) {
// set address in UI
promises.push(
wp.data
.dispatch( 'wc/store/cart' )
.setBillingAddress( addresses.billingAddress )
);
if ( shippingData.needsShipping ) {
promises.push(
wp.data
.dispatch( 'wc/store/cart' )
.setShippingAddress( addresses.shippingAddress )
);
}
}
await Promise.all( promises );
}
setPaypalOrder( subscription );
const res = await fetch(
config.scriptData.ajax.approve_subscription.endpoint,
{
method: 'POST',
credentials: 'same-origin',
body: JSON.stringify( {
nonce: config.scriptData.ajax.approve_subscription
.nonce,
order_id: data.orderID,
subscription_id: data.subscriptionID,
} ),
}
);
const json = await res.json();
if ( ! json.success ) {
if (
typeof actions !== 'undefined' &&
typeof actions.restart !== 'undefined'
) {
return actions.restart();
}
if ( json.data?.message ) {
throw new Error( json.data.message );
}
throw new Error( config.scriptData.labels.error.generic );
}
if ( ! shouldskipFinalConfirmation() ) {
location.href = getCheckoutRedirectUrl();
} else {
setGotoContinuationOnError( true );
enforcePaymentMethodForCart();
onSubmit();
}
} catch ( err ) {
console.error( err );
onError( err.message );
onClose();
throw err;
}
};
const getCheckoutRedirectUrl = () => { const getCheckoutRedirectUrl = () => {
const checkoutUrl = new URL( config.scriptData.redirect ); const checkoutUrl = new URL( config.scriptData.redirect );
// sometimes some browsers may load some kind of cached version of the page, // sometimes some browsers may load some kind of cached version of the page,
@ -275,87 +132,6 @@ export const PayPalComponent = ( {
return checkoutUrl.toString(); return checkoutUrl.toString();
}; };
const handleApprove = async ( data, actions ) => {
try {
const order = await actions.order.get();
if ( order ) {
const addresses = paypalOrderToWcAddresses( order );
const promises = [
// save address on server
wp.data.dispatch( 'wc/store/cart' ).updateCustomerData( {
billing_address: addresses.billingAddress,
shipping_address: addresses.shippingAddress,
} ),
];
if ( shouldHandleShippingInPayPal() ) {
// set address in UI
promises.push(
wp.data
.dispatch( 'wc/store/cart' )
.setBillingAddress( addresses.billingAddress )
);
if ( shippingData.needsShipping ) {
promises.push(
wp.data
.dispatch( 'wc/store/cart' )
.setShippingAddress( addresses.shippingAddress )
);
}
}
await Promise.all( promises );
}
setPaypalOrder( order );
const res = await fetch(
config.scriptData.ajax.approve_order.endpoint,
{
method: 'POST',
credentials: 'same-origin',
body: JSON.stringify( {
nonce: config.scriptData.ajax.approve_order.nonce,
order_id: data.orderID,
funding_source: window.ppcpFundingSource ?? 'paypal',
} ),
}
);
const json = await res.json();
if ( ! json.success ) {
if (
typeof actions !== 'undefined' &&
typeof actions.restart !== 'undefined'
) {
return actions.restart();
}
if ( json.data?.message ) {
throw new Error( json.data.message );
}
throw new Error( config.scriptData.labels.error.generic );
}
if ( ! shouldskipFinalConfirmation() ) {
location.href = getCheckoutRedirectUrl();
} else {
setGotoContinuationOnError( true );
enforcePaymentMethodForCart();
onSubmit();
}
} catch ( err ) {
console.error( err );
onError( err.message );
onClose();
throw err;
}
};
useEffect( () => { useEffect( () => {
const unsubscribe = onCheckoutValidation( () => { const unsubscribe = onCheckoutValidation( () => {
if ( config.scriptData.continuation ) { if ( config.scriptData.continuation ) {
@ -600,11 +376,25 @@ export const PayPalComponent = ( {
buttonModuleWatcher.registerContextBootstrap( buttonModuleWatcher.registerContextBootstrap(
config.scriptData.context, config.scriptData.context,
{ {
createOrder: () => { createOrder: ( data ) => {
return createOrder(); return createOrder( data, config, onError, onClose );
}, },
onApprove: ( data, actions ) => { onApprove: ( data, actions ) => {
return handleApprove( data, actions ); return handleApprove(
data,
actions,
config,
shouldHandleShippingInPayPal,
shippingData,
setPaypalOrder,
shouldskipFinalConfirmation,
getCheckoutRedirectUrl,
setGotoContinuationOnError,
enforcePaymentMethodForCart,
onSubmit,
onError,
onClose
);
}, },
} }
); );
@ -660,61 +450,6 @@ export const PayPalComponent = ( {
}; };
}; };
const createVaultSetupToken = async () => {
return fetch( config.scriptData.ajax.create_setup_token.endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify( {
nonce: config.scriptData.ajax.create_setup_token.nonce,
payment_method: 'ppcp-gateway',
} ),
} )
.then( ( response ) => response.json() )
.then( ( result ) => {
return result.data.id;
} )
.catch( ( err ) => {
console.error( err );
} );
};
const onApproveSavePayment = async ( { vaultSetupToken } ) => {
let endpoint =
config.scriptData.ajax.create_payment_token_for_guest.endpoint;
let bodyContent = {
nonce: config.scriptData.ajax.create_payment_token_for_guest.nonce,
vault_setup_token: vaultSetupToken,
};
if ( config.scriptData.user.is_logged_in ) {
endpoint = config.scriptData.ajax.create_payment_token.endpoint;
bodyContent = {
nonce: config.scriptData.ajax.create_payment_token.nonce,
vault_setup_token: vaultSetupToken,
is_free_trial_cart: config.scriptData.is_free_trial_cart,
};
}
const response = await fetch( endpoint, {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify( bodyContent ),
} );
const result = await response.json();
if ( result.success === true ) {
onSubmit();
}
console.error( result );
};
if ( if (
cartHasSubscriptionProducts( config.scriptData ) && cartHasSubscriptionProducts( config.scriptData ) &&
config.scriptData.is_free_trial_cart config.scriptData.is_free_trial_cart
@ -725,8 +460,10 @@ export const PayPalComponent = ( {
onClick={ handleClick } onClick={ handleClick }
onCancel={ onClose } onCancel={ onClose }
onError={ onClose } onError={ onClose }
createVaultSetupToken={ createVaultSetupToken } createVaultSetupToken={ () => createVaultSetupToken( config ) }
onApprove={ onApproveSavePayment } onApprove={ ( { vaultSetupToken } ) =>
onApproveSavePayment( vaultSetupToken, config, onSubmit )
}
/> />
); );
} }
@ -739,8 +476,26 @@ export const PayPalComponent = ( {
onClick={ handleClick } onClick={ handleClick }
onCancel={ onClose } onCancel={ onClose }
onError={ onClose } onError={ onClose }
createSubscription={ createSubscription } createSubscription={ ( data, actions ) =>
onApprove={ handleApproveSubscription } createSubscription( data, actions, config )
}
onApprove={ ( data, actions ) =>
handleApproveSubscription(
data,
actions,
config,
shouldHandleShippingInPayPal,
shippingData,
setPaypalOrder,
shouldskipFinalConfirmation,
getCheckoutRedirectUrl,
setGotoContinuationOnError,
enforcePaymentMethodForCart,
onSubmit,
onError,
onClose
)
}
onShippingOptionsChange={ getOnShippingOptionsChange( onShippingOptionsChange={ getOnShippingOptionsChange(
fundingSource fundingSource
) } ) }
@ -758,8 +513,26 @@ export const PayPalComponent = ( {
onClick={ handleClick } onClick={ handleClick }
onCancel={ onClose } onCancel={ onClose }
onError={ onClose } onError={ onClose }
createOrder={ createOrder } createOrder={ ( data ) =>
onApprove={ handleApprove } createOrder( data, config, onError, onClose )
}
onApprove={ ( data, actions ) =>
handleApprove(
data,
actions,
config,
shouldHandleShippingInPayPal,
shippingData,
setPaypalOrder,
shouldskipFinalConfirmation,
getCheckoutRedirectUrl,
setGotoContinuationOnError,
enforcePaymentMethodForCart,
onSubmit,
onError,
onClose
)
}
onShippingOptionsChange={ getOnShippingOptionsChange( onShippingOptionsChange={ getOnShippingOptionsChange(
fundingSource fundingSource
) } ) }

View file

@ -0,0 +1,316 @@
import {
paypalOrderToWcAddresses,
paypalSubscriptionToWcAddresses,
} from './Helper/Address';
export const createOrder = async ( data, config, onError, onClose ) => {
try {
const requestBody = {
nonce: config.scriptData.ajax.create_order.nonce,
bn_code: '',
context: config.scriptData.context,
payment_method: 'ppcp-gateway',
funding_source: window.ppcpFundingSource ?? 'paypal',
createaccount: false,
...( data?.paymentSource && {
payment_source: data.paymentSource,
} ),
};
const res = await fetch( config.scriptData.ajax.create_order.endpoint, {
method: 'POST',
credentials: 'same-origin',
body: JSON.stringify( requestBody ),
} );
const json = await res.json();
if ( ! json.success ) {
if ( json.data?.details?.length > 0 ) {
throw new Error(
json.data.details
.map( ( d ) => `${ d.issue } ${ d.description }` )
.join( '<br/>' )
);
} else if ( json.data?.message ) {
throw new Error( json.data.message );
}
throw new Error( config.scriptData.labels.error.generic );
}
return json.data.id;
} catch ( err ) {
console.error( err );
onError( err.message );
onClose();
throw err;
}
};
export const handleApprove = async (
data,
actions,
config,
shouldHandleShippingInPayPal,
shippingData,
setPaypalOrder,
shouldskipFinalConfirmation,
getCheckoutRedirectUrl,
setGotoContinuationOnError,
enforcePaymentMethodForCart,
onSubmit,
onError,
onClose
) => {
try {
const order = await actions.order.get();
if ( order ) {
const addresses = paypalOrderToWcAddresses( order );
const promises = [
// save address on server
wp.data.dispatch( 'wc/store/cart' ).updateCustomerData( {
billing_address: addresses.billingAddress,
shipping_address: addresses.shippingAddress,
} ),
];
if ( shouldHandleShippingInPayPal() ) {
// set address in UI
promises.push(
wp.data
.dispatch( 'wc/store/cart' )
.setBillingAddress( addresses.billingAddress )
);
if ( shippingData.needsShipping ) {
promises.push(
wp.data
.dispatch( 'wc/store/cart' )
.setShippingAddress( addresses.shippingAddress )
);
}
}
await Promise.all( promises );
}
setPaypalOrder( order );
const res = await fetch(
config.scriptData.ajax.approve_order.endpoint,
{
method: 'POST',
credentials: 'same-origin',
body: JSON.stringify( {
nonce: config.scriptData.ajax.approve_order.nonce,
order_id: data.orderID,
funding_source: window.ppcpFundingSource ?? 'paypal',
} ),
}
);
const json = await res.json();
if ( ! json.success ) {
if (
typeof actions !== 'undefined' &&
typeof actions.restart !== 'undefined'
) {
return actions.restart();
}
if ( json.data?.message ) {
throw new Error( json.data.message );
}
throw new Error( config.scriptData.labels.error.generic );
}
if ( ! shouldskipFinalConfirmation() ) {
location.href = getCheckoutRedirectUrl();
} else {
setGotoContinuationOnError( true );
enforcePaymentMethodForCart();
onSubmit();
}
} catch ( err ) {
console.error( err );
onError( err.message );
onClose();
throw err;
}
};
export const createSubscription = async ( data, actions, config ) => {
let planId = config.scriptData.subscription_plan_id;
if (
config.scriptData.variable_paypal_subscription_variation_from_cart !==
''
) {
planId =
config.scriptData.variable_paypal_subscription_variation_from_cart;
}
return actions.subscription.create( {
plan_id: planId,
} );
};
export const handleApproveSubscription = async (
data,
actions,
config,
shouldHandleShippingInPayPal,
shippingData,
setPaypalOrder,
shouldskipFinalConfirmation,
getCheckoutRedirectUrl,
setGotoContinuationOnError,
enforcePaymentMethodForCart,
onSubmit,
onError,
onClose
) => {
try {
const subscription = await actions.subscription.get();
if ( subscription ) {
const addresses = paypalSubscriptionToWcAddresses( subscription );
const promises = [
// save address on server
wp.data.dispatch( 'wc/store/cart' ).updateCustomerData( {
billing_address: addresses.billingAddress,
shipping_address: addresses.shippingAddress,
} ),
];
if ( shouldHandleShippingInPayPal() ) {
// set address in UI
promises.push(
wp.data
.dispatch( 'wc/store/cart' )
.setBillingAddress( addresses.billingAddress )
);
if ( shippingData.needsShipping ) {
promises.push(
wp.data
.dispatch( 'wc/store/cart' )
.setShippingAddress( addresses.shippingAddress )
);
}
}
await Promise.all( promises );
}
setPaypalOrder( subscription );
const res = await fetch(
config.scriptData.ajax.approve_subscription.endpoint,
{
method: 'POST',
credentials: 'same-origin',
body: JSON.stringify( {
nonce: config.scriptData.ajax.approve_subscription.nonce,
order_id: data.orderID,
subscription_id: data.subscriptionID,
} ),
}
);
const json = await res.json();
if ( ! json.success ) {
if (
typeof actions !== 'undefined' &&
typeof actions.restart !== 'undefined'
) {
return actions.restart();
}
if ( json.data?.message ) {
throw new Error( json.data.message );
}
throw new Error( config.scriptData.labels.error.generic );
}
if ( ! shouldskipFinalConfirmation() ) {
location.href = getCheckoutRedirectUrl();
} else {
setGotoContinuationOnError( true );
enforcePaymentMethodForCart();
onSubmit();
}
} catch ( err ) {
console.error( err );
onError( err.message );
onClose();
throw err;
}
};
export const createVaultSetupToken = async ( config ) => {
return fetch( config.scriptData.ajax.create_setup_token.endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify( {
nonce: config.scriptData.ajax.create_setup_token.nonce,
payment_method: 'ppcp-gateway',
} ),
} )
.then( ( response ) => response.json() )
.then( ( result ) => {
return result.data.id;
} )
.catch( ( err ) => {
console.error( err );
} );
};
export const onApproveSavePayment = async (
vaultSetupToken,
config,
onSubmit
) => {
let endpoint =
config.scriptData.ajax.create_payment_token_for_guest.endpoint;
let bodyContent = {
nonce: config.scriptData.ajax.create_payment_token_for_guest.nonce,
vault_setup_token: vaultSetupToken,
};
if ( config.scriptData.user.is_logged_in ) {
endpoint = config.scriptData.ajax.create_payment_token.endpoint;
bodyContent = {
nonce: config.scriptData.ajax.create_payment_token.nonce,
vault_setup_token: vaultSetupToken,
is_free_trial_cart: config.scriptData.is_free_trial_cart,
};
}
const response = await fetch( endpoint, {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify( bodyContent ),
} );
const result = await response.json();
if ( result.success === true ) {
onSubmit();
}
console.error( result );
};