Add logic of apple to session, validation and shipping

This commit is contained in:
carmenmaymo 2023-08-31 16:43:40 +02:00
parent 63ab1e383d
commit 7394c809b7
No known key found for this signature in database
GPG key ID: 6023F686B0F3102E
4 changed files with 306 additions and 123 deletions

View file

@ -1,4 +1,5 @@
import ContextHandlerFactory from "./Context/ContextHandlerFactory";
import {createAppleErrors} from "./Helper/applePayError";
class ApplepayButton {
@ -9,7 +10,8 @@ class ApplepayButton {
this.externalHandler = externalHandler;
this.buttonConfig = buttonConfig;
this.ppcpConfig = ppcpConfig;
console.log(buttonConfig)
console.log(ppcpConfig)
this.paymentsClient = null;
this.contextHandler = ContextHandlerFactory.create(
@ -17,6 +19,12 @@ class ApplepayButton {
this.buttonConfig,
this.ppcpConfig
);
//PRODUCT DETAIL PAGE
this.productQuantity = document.querySelector('input.qty').value
this.updatedContactInfo = []
this.selectedShippingMethod = []
this.nonce = document.getElementById('woocommerce-process-checkout-nonce').value
}
init(config) {
@ -25,24 +33,15 @@ class ApplepayButton {
return;
}
this.isInitialized = true;
this.applePayConfig = config;
this.allowedPaymentMethods = config.allowedPaymentMethods;
this.baseCardPaymentMethod = this.allowedPaymentMethods[0];
this.initClient();
this.paymentsClient.isReadyToPay(
this.buildReadyToPayRequest(this.allowedPaymentMethods, config)
)
.then((response) => {
if (response.result) {
this.addButton(this.baseCardPaymentMethod);
}
const isEligible = this.applePayConfig.isEligible;
if (isEligible) {
this.addButton();
document.querySelector('#btn-appl').addEventListener('click', (evt) => {
evt.preventDefault()
this.onButtonClick()
})
.catch(function(err) {
console.error(err);
});
}
}
buildReadyToPayRequest(allowedPaymentMethods, baseRequest) {
@ -50,23 +49,28 @@ class ApplepayButton {
allowedPaymentMethods: allowedPaymentMethods,
});
}
initClient() {
this.paymentsClient = new apple.payments.api.PaymentsClient({
environment: 'TEST', // TODO: Use 'PRODUCTION' for real transactions
// add merchant info maybe
paymentDataCallbacks: {
//onPaymentDataChanged: onPaymentDataChanged,
onPaymentAuthorized: this.onPaymentAuthorized.bind(this),
}
});
applePaySession(paymentRequest) {
const session = new ApplePaySession(4, paymentRequest)
session.begin()
const ajaxUrl = this.buttonConfig.ajax_url
const productId = this.buttonConfig.product.id
if (this.buttonConfig.product.needShipping) {
session.onshippingmethodselected = this.onshippingmethodselected(ajaxUrl, productId, session)
session.onshippingcontactselected = this.onshippingcontactselected(ajaxUrl, productId, session)
}
session.onvalidatemerchant = this.onvalidatemerchant(session);
session.onpaymentauthorized = this.onpaymentauthorized(ajaxUrl, productId, session);
}
/**
* Add a Apple Pay purchase button
*/
addButton(baseCardPaymentMethod) {
console.log('[ApplePayButton] addButton', this.context);
addButton() {
const appleContainer = document.getElementById("applepay-container");
appleContainer.innerHTML = '<apple-pay-button id="btn-appl" type="buy" locale="en">';
const wrapper =
(this.context === 'mini-cart')
@ -79,15 +83,7 @@ class ApplepayButton {
: this.ppcpConfig.button.style.shape;
jQuery(wrapper).addClass('ppcp-button-' + shape);
const button =
this.paymentsClient.createButton({
onClick: this.onButtonClick.bind(this),
allowedPaymentMethods: [baseCardPaymentMethod],
buttonType: 'pay',
buttonSizeMode: 'fill',
});
jQuery(wrapper).append(button);
jQuery(wrapper).append(appleContainer);
}
//------------------------
@ -97,28 +93,37 @@ class ApplepayButton {
/**
* Show Apple Pay payment sheet when Apple Pay payment button is clicked
*/
async onButtonClick() {
console.log('[ApplePayButton] onButtonClick', this.context);
const paymentDataRequest = await this.paymentDataRequest();
onButtonClick() {
const paymentDataRequest = this.paymentDataRequest();
console.log('[ApplePayButton] onButtonClick: paymentDataRequest', paymentDataRequest, this.context);
this.paymentsClient.loadPaymentData(paymentDataRequest);
this.applePaySession(paymentDataRequest)
}
async paymentDataRequest() {
let baseRequest = {
apiVersion: 2,
apiVersionMinor: 0
}
paymentDataRequest() {
const applepayConfig = this.applePayConfig
const buttonConfig = this.buttonConfig
const applePayConfig = this.applePayConfig;
const paymentDataRequest = Object.assign({}, baseRequest);
paymentDataRequest.allowedPaymentMethods = applePayConfig.allowedPaymentMethods;
paymentDataRequest.transactionInfo = await this.contextHandler.transactionInfo();
paymentDataRequest.merchantInfo = applePayConfig.merchantInfo;
paymentDataRequest.callbackIntents = ['PAYMENT_AUTHORIZATION'];
return paymentDataRequest;
document.querySelector('input.qty').addEventListener('change', event => {
this.productQuantity = event.currentTarget.value
})
this.productQuantity = parseInt(productQuantity)
const amountWithoutTax = productQuantity * buttonConfig.product.price
return {
countryCode: applepayConfig.countryCode,
merchantCapabilities: applepayConfig.merchantCapabilities,
supportedNetworks: applepayConfig.supportedNetworks,
currencyCode: buttonConfig.shop.currencyCode,
requiredShippingContactFields: ["name", "phone",
"email", "postalAddress"],
requiredBillingContactFields: ["name", "phone", "email",
"postalAddress"],
total: {
label: buttonConfig.shop.totalLabel,
type: "final",
amount: amountWithoutTax,
}
}
}
@ -126,71 +131,255 @@ class ApplepayButton {
// Payment process
//------------------------
onPaymentAuthorized(paymentData) {
console.log('[ApplePayButton] onPaymentAuthorized', this.context);
return this.processPayment(paymentData);
}
async processPayment(paymentData) {
console.log('[ApplePayButton] processPayment', this.context);
return new Promise(async (resolve, reject) => {
try {
let id = await this.contextHandler.createOrder();
console.log('[ApplePayButton] processPayment: createOrder', id, this.context);
const confirmOrderResponse = await paypal.Applepay().confirmOrder({
orderId: id,
paymentMethodData: paymentData.paymentMethodData
onvalidatemerchant(session) {
return (applePayValidateMerchantEvent) => {
applepay.validateMerchant({
validationUrl: applePayValidateMerchantEvent.validationURL
})
.then(validateResult => {
session.completeMerchantValidation(validateResult.merchantSession);
//call backend to update validation to true
console.log('validated')
})
.catch(validateError => {
console.error(validateError);
//call backend to update validation to false
session.abort();
});
console.log('[ApplePayButton] processPayment: confirmOrder', confirmOrderResponse, this.context);
/** Capture the Order on the Server */
if (confirmOrderResponse.status === "APPROVED") {
let approveFailed = false;
await this.contextHandler.approveOrderForContinue({
orderID: id
}, {
restart: () => new Promise((resolve, reject) => {
approveFailed = true;
resolve();
})
});
if (!approveFailed) {
resolve(this.processPaymentResponse('SUCCESS'));
} else {
resolve(this.processPaymentResponse('ERROR', 'PAYMENT_AUTHORIZATION', 'FAILED TO APPROVE'));
};
}
onshippingmethodselected(ajaxUrl, productId, session) {
return function (event) {
jQuery.ajax({
url: ajaxUrl,
method: 'POST',
data: {
action: 'ppcp_update_shipping_method',
shipping_method: event.shippingMethod,
product_id: productId,
caller_page: 'productDetail',
product_quantity: this.productQuantity,
simplified_contact: this.updatedContactInfo,
'woocommerce-process-checkout-nonce': this.nonce,
},
success: (applePayShippingMethodUpdate, textStatus, jqXHR) => {
let response = applePayShippingMethodUpdate.data
this.selectedShippingMethod = event.shippingMethod
//order the response shipping methods, so that the selected shipping method is the first one
let orderedShippingMethods = response.newShippingMethods.sort((a, b) => {
if (a.label === this.selectedShippingMethod.label) {
return -1
}
return 1
})
//update the response.newShippingMethods with the ordered shipping methods
response.newShippingMethods = orderedShippingMethods
if (applePayShippingMethodUpdate.success === false) {
response.errors = createAppleErrors(response.errors)
}
this.completeShippingMethodSelection(response)
},
error: (jqXHR, textStatus, errorThrown) => {
console.warn(textStatus, errorThrown)
session.abort()
},
})
};
}
onshippingcontactselected(ajaxUrl, productId, session) {
return function (event) {
jQuery.ajax({
url: ajaxUrl,
method: 'POST',
data: {
action: 'ppcp_update_shipping_contact',
product_id: productId,
caller_page: 'productDetail',
product_quantity: this.productQuantity,
simplified_contact: event.shippingContact,
need_shipping: this.needShipping,
'woocommerce-process-checkout-nonce': this.nonce,
},
success: (applePayShippingContactUpdate, textStatus, jqXHR) => {
let response = applePayShippingContactUpdate.data
this.updatedContactInfo = event.shippingContact
if (applePayShippingContactUpdate.success === false) {
response.errors = createAppleErrors(response.errors)
}
if (response.newShippingMethods) {
this.selectedShippingMethod = response.newShippingMethods[0]
}
this.completeShippingContactSelection(response)
} else {
resolve(this.processPaymentResponse('ERROR', 'PAYMENT_AUTHORIZATION', 'TRANSACTION FAILED'));
},
error: (jqXHR, textStatus, errorThrown) => {
console.warn(textStatus, errorThrown)
session.abort()
},
})
};
}
onpaymentauthorized(ajaxUrl, productId, session) {
/*return (event) => {
function form() {
return document.querySelector('form.cart');
}
const errorHandler = new ErrorHandler(
PayPalCommerceGateway.labels.error.generic,
document.querySelector('.woocommerce-notices-wrapper')
);
const actionHandler = new SingleProductActionHandler(
PayPalCommerceGateway,
new UpdateCart(
PayPalCommerceGateway.ajax.change_cart.endpoint,
PayPalCommerceGateway.ajax.change_cart.nonce,
),
form(),
errorHandler,
);
let createOrderInPayPal = actionHandler.createOrder()
const processInWooAndCapture = async (data) => {
try {
console.log('processInWooAndCapture', data)
const billingContact = data.billing_contact
const shippingContact = data.shipping_contact
jQuery.ajax({
url: ajaxUrl,
method: 'POST',
data: {
action: 'ppcp_create_order',
'product_id': productId,
'product_quantity': productQuantity,
'shipping_contact': shippingContact,
'billing_contact': billingContact,
'token': event.payment.token,
'shipping_method': selectedShippingMethod,
'woocommerce-process-checkout-nonce': nonce,
'funding_source': 'applepay',
'_wp_http_referer': '/?wc-ajax=update_order_review',
'paypal_order_id': data.paypal_order_id,
},
complete: (jqXHR, textStatus) => {
},
success: (authorizationResult, textStatus, jqXHR) => {
console.log('success authorizationResult', authorizationResult)
if (authorizationResult.result === "success") {
redirectionUrl = authorizationResult.redirect;
//session.completePayment(ApplePaySession.STATUS_SUCCESS)
window.location.href = redirectionUrl
} else {
//session.completePayment(ApplePaySession.STATUS_FAILURE)
}
},
error: (jqXHR, textStatus, errorThrown) => {
console.log('error authorizationResult', errorThrown)
session.completePayment(ApplePaySession.STATUS_FAILURE)
console.warn(textStatus, errorThrown)
session.abort()
},
})
} catch (error) {
console.log(error) // handle error
}
} catch(err) {
resolve(this.processPaymentResponse('ERROR', 'PAYMENT_AUTHORIZATION', err.message));
}
});
createOrderInPayPal([], []).then((orderId) => {
console.log('createOrderInPayPal', orderId)
applepay.confirmOrder(
{
orderId: orderId,
token: event.payment.token,
billingContact: event.payment.billingContact
}
).then(
() => {
session.completePayment(ApplePaySession.STATUS_SUCCESS);
let data = {
billing_contact: event.payment.billingContact,
shipping_contact: event.payment.shippingContact,
paypal_order_id: orderId
}
processInWooAndCapture(data)
}
).catch(err => {
console.error('Error confirming order with applepay token');
session.completePayment(ApplePaySession.STATUS_FAILURE);
console.error(err);
}
);
}).catch((error) => {
console.log(error)
session.abort()
})
};*/
}
/* onPaymentAuthorized(paymentData) {
console.log('[ApplePayButton] onPaymentAuthorized', this.context);
return this.processPayment(paymentData);
}
processPaymentResponse(state, intent = null, message = null) {
let response = {
transactionState: state,
}
async processPayment(paymentData) {
console.log('[ApplePayButton] processPayment', this.context);
if (intent || message) {
response.error = {
intent: intent,
message: message,
}
}
return new Promise(async (resolve, reject) => {
try {
let id = await this.contextHandler.createOrder();
console.log('[ApplePayButton] processPaymentResponse', response, this.context);
console.log('[ApplePayButton] processPayment: createOrder', id, this.context);
return response;
}
const confirmOrderResponse = await paypal.Applepay().confirmOrder({
orderId: id,
paymentMethodData: paymentData.paymentMethodData
});
console.log('[ApplePayButton] processPayment: confirmOrder', confirmOrderResponse, this.context);
/!** Capture the Order on the Server *!/
if (confirmOrderResponse.status === "APPROVED") {
let approveFailed = false;
await this.contextHandler.approveOrderForContinue({
orderID: id
}, {
restart: () => new Promise((resolve, reject) => {
approveFailed = true;
resolve();
})
});
if (!approveFailed) {
resolve(this.processPaymentResponse('SUCCESS'));
} else {
resolve(this.processPaymentResponse('ERROR', 'PAYMENT_AUTHORIZATION', 'FAILED TO APPROVE'));
}
} else {
resolve(this.processPaymentResponse('ERROR', 'PAYMENT_AUTHORIZATION', 'TRANSACTION FAILED'));
}
} catch(err) {
resolve(this.processPaymentResponse('ERROR', 'PAYMENT_AUTHORIZATION', err.message));
}
});
}
processPaymentResponse(state, intent = null, message = null) {
let response = {
transactionState: state,
}
if (intent || message) {
response.error = {
intent: intent,
message: message,
}
}
console.log('[ApplePayButton] processPaymentResponse', response, this.context);
return response;
}*/
}

View file

@ -12,7 +12,6 @@ class ApplepayManager {
this.buttons = [];
buttonModuleWatcher.watchContextBootstrap((bootstrap) => {
console.log('ApplepayManager.js: buttonModuleWatcher.watchContextBootstrap', bootstrap)
const button = new ApplepayButton(
bootstrap.context,
bootstrap.handler,
@ -31,9 +30,7 @@ class ApplepayManager {
init() {
(async () => {
await this.config();
console.log('ApplepayManager.js: init', this.buttons)
for (const button of this.buttons) {
console.log('ApplepayManager.js: init', button)
button.init(this.ApplePayConfig);
}
})();
@ -45,7 +42,6 @@ class ApplepayManager {
*/
async config() {
this.ApplePayConfig = await paypal.Applepay().config();
console.log('ApplepayManager.js: config', this.ApplePayConfig)
return this.ApplePayConfig;
}

View file

@ -33,25 +33,23 @@ import ApplepayManager from "./ApplepayManager";
let paypalLoaded = false;
let applePayLoaded = false;
/* const tryToBoot = () => {
const tryToBoot = () => {
if (!bootstrapped && paypalLoaded && applePayLoaded) {
console.log('Trying to bootstrap')
bootstrapped = true;
bootstrap();
}
}*/
}
// Load ApplePay SDK
/*loadCustomScript({ url: buttonConfig.sdk_url }).then(() => {
console.log('ApplePay SDK loaded', buttonConfig.sdk_url)
loadCustomScript({ url: buttonConfig.sdk_url }).then(() => {
applePayLoaded = true;
//tryToBoot();
});*/
tryToBoot();
});
// Load PayPal
loadPaypalScript(ppcpConfig, () => {
paypalLoaded = true;
//tryToBoot();
tryToBoot();
});
},
);

View file

@ -26,7 +26,7 @@ export const loadPaypalScript = (config, onLoaded) => {
return;
}
options.isLoading = true;
console.log('ScriptLoading.js: loadPaypalScript', config)
// Arm a timeout so the module isn't locked on isLoading state on failure.
let loadingTimeout = setTimeout(() => {
console.error('Failed to load PayPal script.');