UNPKG

@ecomplus/storefront-app

Version:

Vue.js ecommerce app with cart, checkout and account pages

422 lines (389 loc) 12 kB
import { i19anyPaymentMethodMsg, i19changePaymentMethod, i19checkout, i19chooseSubscriptionPeriod, i19generateBillet, i19interestFree, i19ofDiscount, i19onFreight, i19paymentError, i19paymentErrorMsg, i19recurrent, i19subscription, i19total, i19tryAgain, i19upTo } from '@ecomplus/i18n' import { i18n, price as getPrice, formatMoney, $ecomConfig } from '@ecomplus/utils' import { modules } from '@ecomplus/client' import ecomCart from '@ecomplus/shopping-cart' import loadPaymentClient from '../../lib/load-payment-client' import { sortApps } from '../../lib/utils' import CreditCardForm from '../CreditCardForm.vue' export default { name: 'PaymentMethods', components: { CreditCardForm }, props: { amount: { type: Object, required: true }, cartItems: Array, customer: Object, paymentGateways: { type: Array, default () { return window.ecomPaymentGateways || [] } }, defaultAppId: { type: Number, default () { return window.ecomDefaultPaymentApp } }, appsSort: { type: Array, default () { return window.ecomPaymentApps || [] } }, canUpdateGateways: { type: Boolean, default: !(window.ecomPaymentGateways && window.ecomPaymentGateways.length) }, canGroupRecurrentGateways: { type: Boolean, default: window.ecomGroupRecurrentGateways !== false }, ecomCart: { type: Object, default () { return ecomCart } } }, data () { return { isWaiting: false, fetching: null, processingAppId: undefined, hasLoaded: false, selectedGateway: -1, loadedClients: {} } }, computed: { i19anyPaymentMethodMsg: () => i18n(i19anyPaymentMethodMsg), i19changePaymentMethod: () => i18n(i19changePaymentMethod), i19chooseSubscriptionPeriod: () => i18n(i19chooseSubscriptionPeriod), i19checkout: () => i18n(i19checkout), i19generateBillet: () => i18n(i19generateBillet), i19interestFree: () => i18n(i19interestFree), i19ofDiscount: () => i18n(i19ofDiscount), i19onFreight: () => i18n(i19onFreight), i19recurrent: () => i18n(i19recurrent), i19subscription: () => i18n(i19subscription), i19total: () => i18n(i19total), i19tryAgain: () => i18n(i19tryAgain), i19upTo: () => i18n(i19upTo), items () { return this.cartItems || this.ecomCart.data.items }, paymentGateway () { return this.paymentGateways[this.selectedGateway] || {} }, jsClient () { return this.paymentGateway.js_client }, jsClientLoad () { const { amount, customer, items, loadedClients, selectedGateway } = this if (amount.total) { return loadedClients[selectedGateway].then(runOnloadExpression => { if (this.$refs.gatewayContainer) { this.$refs.gatewayContainer.innerHTML = this.jsClient.container_html } const payload = runOnloadExpression({ amount, customer, items }) const transactionPromise = this.jsClient.transaction_promise if (transactionPromise && selectedGateway === this.selectedGateway) { try { window[transactionPromise] .then(this.checkout) .catch(err => { console.error(err) this.$toast({ title: i18n(i19paymentError), body: i18n(i19paymentErrorMsg), variant: 'danger' }) }) } catch (err) { console.error(err) } } return payload }) } else { return Promise.resolve() } }, canShowGatewayIcon () { return this.selectedGateway === -1 || !this.jsClient || !this.jsClient.container_html }, shouldUseCardForm () { switch (this.paymentGateway.payment_method.code) { case 'credit_card': return true case 'debit_card': case 'balance_on_intermediary': return Boolean(this.jsClient) } return false }, cardFormGatewayOptions () { if (this.paymentGateway.type === 'recurrence' && this.canGroupRecurrentGateways) { return this.paymentGateways.filter(({ type }) => type === 'recurrence') } return null }, isCompany () { return this.customer && this.customer.registry_type !== 'p' }, customerName () { return this.customer && this.customer.name && this.customer.name.given_name } }, methods: { formatMoney, checkListedGateway (gateway, i) { if (gateway.payment_method.code !== 'loyalty_points') { if (this.canGroupRecurrentGateways) { const checkRecurrentCardGateway = (gateway) => { return gateway.type === 'recurrence' && gateway.payment_method.code === 'credit_card' } if (checkRecurrentCardGateway(gateway)) { return i === this.paymentGateways.findIndex((gateway) => { return checkRecurrentCardGateway(gateway) }) } } return true } return false }, checkShownGateway (gateway, i) { return this.selectedGateway === -1 || this.selectedGateway === i || (this.canGroupRecurrentGateways && gateway.type === 'recurrence' && this.paymentGateway.type === 'recurrence') }, gatewayIcon (gateway) { switch (gateway.payment_method.code) { case 'credit_card': return 'credit-card' case 'banking_billet': return 'barcode' } return 'money-check' }, installmentOption (gateway) { let bestOption if (gateway.installment_options) { gateway.installment_options.forEach(option => { if ( !bestOption || (!option.tax && bestOption.tax) || (option.tax === bestOption.tax && option.number > bestOption.number) ) { bestOption = option } }) } return bestOption }, setupGatewayClient (paymentGateway, gatewayIndex) { const jsClient = paymentGateway.js_client if (jsClient) { this.loadedClients[gatewayIndex] = loadPaymentClient(jsClient, true) } }, postHandleGateways () { this.paymentGateways.forEach(this.setupGatewayClient) if (!this.hasLoaded && this.paymentGateways.length) { this.hasLoaded = true if (this.defaultAppId && this.selectedGateway === -1) { this.selectedGateway = this.paymentGateways.findIndex(gateway => { return gateway.app_id === this.defaultAppId }) } } }, parsePaymentOptions (listResult = [], isUpdatingSelected) { let { paymentGateways } = this if (!isUpdatingSelected) { paymentGateways = [] this.loadedClients = {} } if (listResult.length) { if (Array.isArray(this.appsSort) && this.appsSort.length) { sortApps(listResult, this.appsSort) } listResult.forEach(appResult => { const { validated, error, response } = appResult if (validated && !error) { response.payment_gateways.forEach(gateway => { const paymentGateway = { app_id: appResult.app_id, installment_option: this.installmentOption(gateway), ...gateway } if (!isUpdatingSelected) { if (paymentGateway.type !== 'recurrence') { paymentGateways.push(paymentGateway) } else { paymentGateways.unshift(paymentGateway) } } else { this.setupGatewayClient(paymentGateway, this.selectedGateway) paymentGateways[this.selectedGateway] = paymentGateway isUpdatingSelected = false } }) } }) } this.$emit('update:payment-gateways', paymentGateways) }, fetchPaymentGateways (appId = null, isRetry = false) { let url = '/list_payments.json' const method = 'POST' const { items } = this const amount = this.amount ? { ...this.amount } : {} if (typeof amount.subtotal !== 'number') { amount.total = amount.subtotal = items .reduce((subtotal, item) => subtotal + getPrice(item) * item.quantity, 0) if (amount.freight) { amount.total += amount.freight } if (amount.discount) { amount.total -= amount.discount } } const data = { items, amount, domain: window.location.hostname, can_fetch_when_selected: true, currency_id: items[0].currency_id || $ecomConfig.get('currency'), currency_symbol: items[0].currency_symbol || $ecomConfig.get('currency_symbol') } if (!isRetry && this.customer) { data.customer = {} for (const prop in this.customer) { const val = this.customer[prop] if (val && (typeof val !== 'object' || Object.keys(val).length)) { data.customer[prop] = val } } } if (appId > 0) { url += `?app_id=${appId}` if (this.paymentGateway.payment_method) { data.payment_method = this.paymentGateway.payment_method } } if (!this.isWaiting || this.processingAppId !== appId) { this.isWaiting = true this.processingAppId = appId setTimeout(() => { this.fetching = modules({ url, method, data }) .then(({ data }) => { this.parsePaymentOptions(data.result, Boolean(appId && this.selectedGateway >= 0)) }) .catch(err => { console.error(err) if (!isRetry) { setTimeout(() => { this.fetchPaymentGateways(appId, true) }, err.response && err.response.status === 400 ? 50 : 500) } }) .finally(() => { this.isWaiting = false }) }, appId ? 5 : 50) } }, onCardFormSelectGateway (gateway) { this.selectedGateway = this.paymentGateways.findIndex((paymentGateway) => { return gateway === paymentGateway }) }, handleCheckout () { if (!this.jsClient || !this.jsClient.transaction_promise || !this.jsClientLoad.toString()) { this.checkout() } }, checkout (transaction) { this.$emit('checkout', { ...this.paymentGateway, ...transaction }) } }, watch: { selectedGateway: { handler (gatewayIndex) { const { paymentGateway, loadedClients } = this const proceed = () => { this.$emit('select-gateway', paymentGateway) if (paymentGateway.fetch_when_selected) { this.fetchPaymentGateways(paymentGateway.app_id) } } if (loadedClients[gatewayIndex]) { loadedClients[gatewayIndex].then(proceed) } else { proceed() } }, immediate: true }, paymentGateways: { handler (paymentGateways) { if (paymentGateways.length) { this.postHandleGateways() } }, immediate: true }, 'amount.total' (total, oldTotal) { if ( ((total && !oldTotal) || Math.abs(total - oldTotal) > 0.1) && this.selectedGateway === -1 && this.canUpdateGateways ) { if (!this.isWaiting) { this.fetchPaymentGateways() } else { this.fetching.then(this.fetchPaymentGateways) } } } }, created () { if (!this.paymentGateways.length) { this.fetchPaymentGateways() } } }