mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-01 07:02:48 +08:00
Add logic of apple to session, validation and shipping
This commit is contained in:
parent
63ab1e383d
commit
7394c809b7
4 changed files with 306 additions and 123 deletions
|
@ -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;
|
||||
}*/
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
},
|
||||
);
|
||||
|
|
|
@ -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.');
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue