// Close 3Ds Dialog function onClose() { const threedsElement = document.getElementById(โthreedsโ); threedsElement.innerHTML = โโ; } // Handle 3Ds Payload async function onHandle3Ds(payload, orderId) { const { liabilityShifted, liabilityShift } = payload; if (liabilityShift === โPOSSIBLEโ) { await onApproveCallback(orderId); } else if (liabilityShifted === false || liabilityShifted === undefined) { document.getElementById(โthirdsโ).innerHTML = `
`; } } async function createOrderCallback() { resultMessage(โโ); try { const response = await fetch(โ/api/ordersโ, { method: โPOSTโ, headers: { โContent-Typeโ: โapplication/jsonโ, }, // use the โbodyโ param to optionally pass additional order information // like product ids and quantities body: JSON.stringify({ cart: [ { id: โYOUR_PRODUCT_IDโ, quantity: โYOUR_PRODUCT_QUANTITYโ, }, ], }), }); const orderData = await response.json(); if (orderData.id) { return orderData.id; } else { const errorDetail = orderData?.details?.[0]; const errorMessage = errorDetail ? `${errorDetail.issue} ${errorDetail.description} (${orderData.debug_id})` : JSON.stringify(orderData); throw new Error(errorMessage); } } catch (error) { console.error(error); resultMessage(`Could not initiate PayPal Checkoutโฆ
${error}`);
}
}
async function onApproveCallback(orderId) {
console.log(“orderId”, orderId);
const threedsElement = document.getElementById(“threeds”);
threedsElement.innerHTML = “”;
try {
const response = await fetch(`/api/orders/${orderId}/capture`, {
method: “POST”,
headers: {
“Content-Type”: “application/json”,
},
});
const orderData = await response.json();
// Three cases to handle:
// (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart()
// (2) Other non-recoverable errors -> Show a failure message
// (3) Successful transaction -> Show confirmation or thank you message
const transaction =
orderData?.purchase_units?.[0]?.payments?.captures?.[0] ||
orderData?.purchase_units?.[0]?.payments?.authorizations?.[0];
const errorDetail = orderData?.details?.[0];
// this actions.restart() behavior only applies to the Buttons component
if (errorDetail?.issue === “INSTRUMENT_DECLINED” && !data.card && actions) {
// (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart()
// recoverable state, per https://developer.paypal.com/docs/checkout/standard/customize/handle-funding-failures/
return actions.restart();
} else if (
errorDetail ||
!transaction ||
transaction.status === “DECLINED”
) {
// (2) Other non-recoverable errors -> Show a failure message
let errorMessage;
if (transaction) {
errorMessage = `Transaction ${transaction.status}: ${transaction.id}`;
} else if (errorDetail) {
errorMessage = `${errorDetail.description} (${orderData.debug_id})`;
} else {
errorMessage = JSON.stringify(orderData);
}
throw new Error(errorMessage);
} else {
// (3) Successful transaction -> Show confirmation or thank you message
// Or go to another URL: actions.redirect(‘thank_you.html’);
resultMessage(
`Transaction ${transaction.status}: ${transaction.id}
See console for all available details`, ); console.log( โCapture resultโ, orderData, JSON.stringify(orderData, null, 2), ); } } catch (error) { console.error(error); resultMessage( `Sorry, your transaction could not be processedโฆ
${error}`, ); } } window.paypal .Buttons({ style: { shape: โpillโ, layout: โverticalโ, }, createOrder: createOrderCallback, onApprove: (data) => onApproveCallback(data.orderID), }) .render(โ #paypal-button-containerโ); // Example function to show a result to the user. Your site's UI library can be used instead. function resultMessage(message) { const container = document.querySelector(โ#result-messageโ); container.innerHTML = message; } // If this returns false or the card fields aren't visible, see Step #1. if (window.paypal.HostedFields.isEligible()) { let orderId; // Renders card fields window.paypal.HostedFields.render({ // Call your server to set up the transaction createOrder: async (data, actions) => { orderId = await createOrderCallback(data, actions); return orderId; }, styles: { โ.validโ: { color: โgreenโ, }, โ.invalidโ: { color: โredโ, }, }, fields: { number: { selector: โ#card-numberโ, placeholder: โ 4111 1111 1111 1111โ, }, cvv: { selector: โ#cvvโ, placeholder: โ123โ, }, expirationDate: { selector: โ#expiration-dateโ, placeholder: โMM/YYโ, }, }, }) .then((cardFields) => { document .querySelector(โ#card-formโ) .addEventListener(โsubmitโ, async (event) => { event.preventDefault(); try { const { value: cardHolderName } = document. getElementById(โcard-holder-nameโ); const { value: streetAddress } = document.getElementById( โcard-billing-address-streetโ, ); const { value: extendedAddress } = document.getElementById( โcard-billing- address-unitโ, ); const { value: region } = document.getElementById( โcard-billing-address-stateโ, ); const { value: locality } = document.getElementById( โcard-billing-address-cityโ, ); const { value: postalCode } = document.getElementById( โcard-billing-address-zipโ, ); const { value: countryCodeAlpha2 } = document.getElementById( โcard-billing-address-countryโ, ); const payload = await cardFields.submit({ cardHolderName, contingencies: [โSCA_ALWAYSโ], billingAddress: { streetAddress, extendedAddress, region, locality, postalCode, countryCodeAlpha2, }, }); await onHandle3Ds(payload, orderId); } catch (error) { alert(โPayment could not be captured! โ + JSON.stringify(error)); } }); }); } else { // Hides card fields if the merchant isn't eligible document.querySelector(โ#card-formโ).style = โdisplay: noneโ; }


