import ContextHandlerFactory from "./Context/ContextHandlerFactory"; import {createAppleErrors} from "./Helper/applePayError"; import {setVisible} from '../../../ppcp-button/resources/js/modules/Helper/Hiding'; import {setEnabled} from '../../../ppcp-button/resources/js/modules/Helper/ButtonDisabler'; class ApplepayButton { constructor(context, externalHandler, buttonConfig, ppcpConfig) { this.isInitialized = false; this.context = context; this.externalHandler = externalHandler; this.buttonConfig = buttonConfig; this.ppcpConfig = ppcpConfig; this.paymentsClient = null; this.contextHandler = ContextHandlerFactory.create( this.context, this.buttonConfig, this.ppcpConfig ); //PRODUCT DETAIL PAGE if(this.context === 'product') { this.productQuantity = document.querySelector('input.qty').value } this.updated_contact_info = [] this.selectedShippingMethod = [] this.nonce = document.getElementById('woocommerce-process-checkout-nonce').value } init(config) { console.log('[ApplePayButton] init', config); if (this.isInitialized) { return; } this.initEventHandlers(); this.isInitialized = true; this.applePayConfig = config; const isEligible = this.applePayConfig.isEligible; if (isEligible) { this.fetchTransactionInfo().then(() => { const isSubscriptionProduct = this.ppcpConfig.data_client_id.has_subscriptions === true; if(isSubscriptionProduct) { return; } this.addButton(); const id_minicart = "#apple-" + this.buttonConfig.button.mini_cart_wrapper; const id = "#apple-" + this.buttonConfig.button.wrapper; if(this.context === 'mini-cart') { document.querySelector(id_minicart).addEventListener('click', (evt) => { evt.preventDefault(); this.onButtonClick(); }); } else { document.querySelector(id).addEventListener('click', (evt) => { evt.preventDefault(); this.onButtonClick(); }); } }); } console.log('[ApplePayButton] init done', this.buttonConfig.ajax_url); } async fetchTransactionInfo() { this.transactionInfo = await this.contextHandler.transactionInfo(); } /** * Returns configurations relative to this button context. */ contextConfig() { let config = { wrapper: this.buttonConfig.button.wrapper, ppcpStyle: this.ppcpConfig.button.style, //buttonStyle: this.buttonConfig.button.style, ppcpButtonWrapper: this.ppcpConfig.button.wrapper } if (this.context === 'mini-cart') { config.wrapper = this.buttonConfig.button.mini_cart_wrapper; config.ppcpStyle = this.ppcpConfig.button.mini_cart_style; config.buttonStyle = this.buttonConfig.button.mini_cart_style; config.ppcpButtonWrapper = this.ppcpConfig.button.mini_cart_wrapper; } if (['cart-block', 'checkout-block'].indexOf(this.context) !== -1) { config.ppcpButtonWrapper = '#express-payment-method-ppcp-gateway'; } return config; } initEventHandlers() { const { wrapper, ppcpButtonWrapper } = this.contextConfig(); const syncButtonVisibility = () => { const $ppcpButtonWrapper = jQuery(ppcpButtonWrapper); setVisible(wrapper, $ppcpButtonWrapper.is(':visible')); setEnabled(wrapper, !$ppcpButtonWrapper.hasClass('ppcp-disabled')); } jQuery(document).on('ppcp-shown ppcp-hidden ppcp-enabled ppcp-disabled', (ev, data) => { if (jQuery(data.selector).is(ppcpButtonWrapper)) { syncButtonVisibility(); } }); syncButtonVisibility(); } applePaySession(paymentRequest) { const session = new ApplePaySession(4, paymentRequest) session.begin() if (this.buttonConfig.product.needShipping) { session.onshippingmethodselected = this.onshippingmethodselected(session) session.onshippingcontactselected = this.onshippingcontactselected(session) } session.onvalidatemerchant = this.onvalidatemerchant(session); session.onpaymentauthorized = this.onpaymentauthorized(session); } /** * Add a Apple Pay purchase button */ addButton() { console.log('[GooglePayButton] addButton', this.context); const wrapper = (this.context === 'mini-cart') ? this.buttonConfig.button.mini_cart_wrapper : this.buttonConfig.button.wrapper; /*const shape = (this.context === 'mini-cart') ? this.ppcpConfig.button.mini_cart_style.shape : this.ppcpConfig.button.style.shape;*/ const appleContainer = this.context === 'mini-cart' ? document.getElementById("applepay-container-minicart") : document.getElementById("applepay-container"); console.log('[ApplePayButton] addButton', appleContainer) const type = this.buttonConfig.button.type; const language = this.buttonConfig.button.lang; const color = this.buttonConfig.button.color; const id = "apple-" + wrapper; appleContainer.innerHTML = ``; //jQuery(wrapper).addClass('ppcp-button-' + shape); jQuery(wrapper).append(appleContainer); console.log('[ApplePayButton] addButton', wrapper, appleContainer); } //------------------------ // Button click //------------------------ /** * Show Apple Pay payment sheet when Apple Pay payment button is clicked */ onButtonClick() { const paymentDataRequest = this.paymentDataRequest(); console.log('[ApplePayButton] onButtonClick: paymentDataRequest', paymentDataRequest, this.context); this.applePaySession(paymentDataRequest) } paymentDataRequest() { const applepayConfig = this.applePayConfig const buttonConfig = this.buttonConfig let baseRequest = { countryCode: applepayConfig.countryCode, merchantCapabilities: applepayConfig.merchantCapabilities, supportedNetworks: applepayConfig.supportedNetworks, requiredShippingContactFields: ["name", "phone", "email", "postalAddress"], requiredBillingContactFields: ["name", "phone", "email", "postalAddress"] } console.log('[ApplePayButton] paymentDataRequest', applepayConfig, buttonConfig); const paymentDataRequest = Object.assign({}, baseRequest); paymentDataRequest.currencyCode = buttonConfig.shop.currencyCode; paymentDataRequest.total = { label: buttonConfig.shop.totalLabel, type: "final", amount: this.transactionInfo.totalPrice, } return paymentDataRequest } //------------------------ // Payment process //------------------------ onvalidatemerchant(session) { return (applePayValidateMerchantEvent) => { paypal.Applepay().validateMerchant({ validationUrl: applePayValidateMerchantEvent.validationURL }) .then(validateResult => { session.completeMerchantValidation(validateResult.merchantSession); //call backend to update validation to true jQuery.ajax({ url: this.buttonConfig.ajax_url, type: 'POST', data: { action: 'ppcp_validate', validation: true, 'woocommerce-process-checkout-nonce': this.nonce, } }) console.log('validated') }) .catch(validateError => { console.error(validateError); //call backend to update validation to false jQuery.ajax({ url: this.buttonConfig.ajax_url, type: 'POST', data: { action: 'ppcp_validate', validation: false, 'woocommerce-process-checkout-nonce': this.nonce, } }) session.abort(); }); }; } onshippingmethodselected(session) { const ajax_url = this.buttonConfig.ajax_url console.log('[ApplePayButton] onshippingmethodselected'); return (event) => { const data = this.getShippingMethodData(event); jQuery.ajax({ url: ajax_url, method: 'POST', data: data, success: (applePayShippingMethodUpdate, textStatus, jqXHR) => { let response = applePayShippingMethodUpdate.data if (applePayShippingMethodUpdate.success === false) { response.errors = createAppleErrors(response.errors) } console.log('shipping method update response', response, applePayShippingMethodUpdate) 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) } session.completeShippingMethodSelection(response) }, error: (jqXHR, textStatus, errorThrown) => { console.warn(textStatus, errorThrown) session.abort() }, }) }; } onshippingcontactselected(session) { const ajax_url = this.buttonConfig.ajax_url return (event) => { const data = this.getShippingContactData(event); console.log('shipping contact selected', data, event) jQuery.ajax({ url: ajax_url, method: 'POST', data: data, success: (applePayShippingContactUpdate, textStatus, jqXHR) => { let response = applePayShippingContactUpdate.data this.updated_contact_info = event.shippingContact console.log('shipping contact update response', response, applePayShippingContactUpdate, this.updated_contact_info) if (applePayShippingContactUpdate.success === false) { response.errors = createAppleErrors(response.errors) } if (response.newShippingMethods) { this.selectedShippingMethod = response.newShippingMethods[0] } session.completeShippingContactSelection(response) }, error: (jqXHR, textStatus, errorThrown) => { console.warn(textStatus, errorThrown) session.abort() }, }) }; } getShippingContactData(event) { const product_id = this.buttonConfig.product.id; switch (this.context) { case 'product': return { action: 'ppcp_update_shipping_contact', product_id: product_id, caller_page: 'productDetail', product_quantity: this.productQuantity, simplified_contact: event.shippingContact, need_shipping: this.buttonConfig.product.needShipping, 'woocommerce-process-checkout-nonce': this.nonce, }; case 'cart': case 'checkout': case 'cart-block': case 'checkout-block': case 'mini-cart': return { action: 'ppcp_update_shipping_contact', simplified_contact: event.shippingContact, caller_page: 'cart', need_shipping: this.buttonConfig.product.needShipping, 'woocommerce-process-checkout-nonce': this.nonce, }; } } getShippingMethodData(event) { const product_id = this.buttonConfig.product.id; switch (this.context) { case 'product': return { action: 'ppcp_update_shipping_method', shipping_method: event.shippingMethod, product_id: product_id, caller_page: 'productDetail', product_quantity: this.productQuantity, simplified_contact: this.updated_contact_info, 'woocommerce-process-checkout-nonce': this.nonce, } case 'cart': case 'checkout': case 'cart-block': case 'checkout-block': case 'mini-cart': return { action: 'ppcp_update_shipping_method', shipping_method: event.shippingMethod, caller_page: 'cart', simplified_contact: this.updated_contact_info, 'woocommerce-process-checkout-nonce': this.nonce, } } } onpaymentauthorized(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': this.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 } } createOrderInPayPal([], []).then((orderId) => { console.log('createOrderInPayPal', orderId) paypal.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); } 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 }); 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; }*/ } export default ApplepayButton;