Add express checkout block (wip)

This commit is contained in:
Alex P 2022-12-20 16:04:11 +02:00
parent 0bf18d67c5
commit ea3e5bbeb6
No known key found for this signature in database
GPG key ID: 54487A734A204D71
22 changed files with 3199 additions and 25 deletions

View file

@ -0,0 +1,84 @@
/**
* @param {String} fullName
* @returns {Array}
*/
export const splitFullName = (fullName) => {
fullName = fullName.trim()
if (!fullName.includes(' ')) {
return [fullName, ''];
}
const parts = fullName.split(' ');
const firstName = parts[0];
parts.shift();
const lastName = parts.join(' ');
return [firstName, lastName];
}
/**
* @param {Object} address
* @returns {Object}
*/
export const paypalAddressToWc = (address) => {
const map = {
country_code: 'country',
address_line_1: 'address_1',
address_line_2: 'address_2',
admin_area_1: 'state',
admin_area_2: 'city',
postal_code: 'postcode',
};
const result = {};
Object.entries(map).forEach(([paypalKey, wcKey]) => {
if (address[paypalKey]) {
result[wcKey] = address[paypalKey];
}
})
return result;
}
/**
* @param {Object} shipping
* @returns {Object}
*/
export const paypalShippingToWc = (shipping) => {
const [firstName, lastName] = splitFullName(shipping.name.full_name);
return {
first_name: firstName,
last_name: lastName,
...paypalAddressToWc(shipping.address),
}
}
/**
* @param {Object} payer
* @returns {Object}
*/
export const paypalPayerToWc = (payer) => {
const firstName = payer.name.given_name;
const lastName = payer.name.surname;
const address = payer.address ? paypalAddressToWc(payer.address) : {};
return {
first_name: firstName,
last_name: lastName,
email: payer.email_address,
...address,
}
}
/**
* @param {Object} order
* @returns {Object}
*/
export const paypalOrderToWcShippingAddress = (order) => {
const res = paypalShippingToWc(order.purchase_units[0].shipping);
// use the name from billing if the same, to avoid possible mistakes when splitting full_name
const billingAddress = paypalPayerToWc(order.payer);
if (`${res.first_name} ${res.last_name}` === `${billingAddress.first_name} ${billingAddress.last_name}`) {
res.first_name = billingAddress.first_name;
res.last_name = billingAddress.last_name;
}
return res;
}

View file

@ -0,0 +1,151 @@
import {useEffect, useState} from '@wordpress/element';
import {registerExpressPaymentMethod} from '@woocommerce/blocks-registry';
import {PayPalScriptProvider, PayPalButtons} from "@paypal/react-paypal-js";
import {paypalOrderToWcShippingAddress, paypalPayerToWc} from "./Helper/Address";
const config = wc.wcSettings.getSetting('ppcp-gateway_data');
const PayPalComponent = ({
onClick,
onClose,
onSubmit,
eventRegistration,
emitResponse,
setExpressPaymentError,
}) => {
const {onPaymentProcessing} = eventRegistration;
const {responseTypes} = emitResponse;
const [paypalOrder, setPaypalOrder] = useState(null);
const createOrder = async () => {
try {
const res = await fetch(config.scriptData.ajax.create_order.endpoint, {
method: 'POST',
credentials: 'same-origin',
body: JSON.stringify({
nonce: config.scriptData.ajax.create_order.nonce,
bn_code: '',
context: 'express',
order_id: config.scriptData.order_id,
payment_method: 'ppcp-gateway',
funding_source: 'paypal',
createaccount: false
}),
});
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);
setExpressPaymentError(err.message);
onClose();
throw err;
}
};
const handleApprove = async (data, actions) => {
try {
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: ,
})
});
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)
}
setPaypalOrder(json.data);
onSubmit();
} catch (err) {
console.error(err);
setExpressPaymentError(err.message);
onClose();
throw err;
}
};
const handleClick = () => {
setExpressPaymentError('');
onClick();
};
useEffect(() => {
const unsubscribeProcessing = onPaymentProcessing(() => {
const shippingAddress = paypalOrderToWcShippingAddress(paypalOrder);
const billingAddress = paypalPayerToWc(paypalOrder.payer);
return {
type: responseTypes.SUCCESS,
meta: {
paymentMethodData: {
'paypal_order_id': paypalOrder.id,
},
shippingData: {address: shippingAddress},
billingAddress,
billingData: billingAddress,
},
};
});
return () => {
unsubscribeProcessing();
};
}, [onPaymentProcessing, paypalOrder]);
return (
<PayPalScriptProvider options={config.scriptData.url_params}>
<PayPalButtons
style={config.scriptData.button.style}
onClick={handleClick}
onCancel={onClose}
onError={onClose}
createOrder={createOrder}
onApprove={handleApprove}
/>
</PayPalScriptProvider>
);
}
registerExpressPaymentMethod({
name: config.id,
label: <div dangerouslySetInnerHTML={{__html: config.title}}/>,
content: <PayPalComponent/>,
edit: <b>TODO: editing</b>,
ariaLabel: config.title,
canMakePayment: () => true,
supports: {
features: ['products'],
},
});