UNPKG

@ecomplus/storefront-app

Version:

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

407 lines (379 loc) 11.2 kB
import { i19aboutCvvMsg, i19atSight, i19bearersDocument, i19birthdate, i19cardNumber, i19confirmPurchase, i19creditCard, i19holderName, i19interestFree, i19invalidCard, i19invalidCardMsg, i19nameOnCard, i19of, i19parcelIn, i19securityCode, i19validThru } from '@ecomplus/i18n' import { $ecomConfig, i18n, formatMoney } from '@ecomplus/utils' import { $ } from '@ecomplus/storefront-twbs' import * as cardValidator from 'card-validator' import loadPaymentClient from './../../lib/load-payment-client' import InputDate from '#components/InputDate.vue' import InputDocNumber from '#components/InputDocNumber.vue' import CleaveInput from 'vue-cleave-component' const countryCode = $ecomConfig.get('country_code') export default { name: 'CreditCardForm', components: { InputDate, InputDocNumber, CleaveInput }, props: { amount: { type: Object, required: true }, checkHolder: String, canSkipHolderInfo: Boolean, isCompany: Boolean, installmentOptions: Array, gatewayOptions: Array, gatewayOptionsLabel: String, jsClient: Object, jsClientLoad: Promise, isPayerDocRequired: { type: Boolean, default () { return window.ecomCreditCardDocRequired === true } } }, data () { return { canFormatBinInput: false, formattedCardBin: '', cardBinSetTimer: null, card: { bin: '', name: '', date: '', cvv: '', birth: '', doc: '', installment: this.installmentOptions ? 1 : 0 }, canFormatExpInput: false, rawCardExp: '', isLoadingInstallments: false, hasLoadedInstallments: false, loadInstallmentsTimer: null, isWaitingCardHash: false, installmentList: [], alert: { bin: false, date: false, cvv: false }, isNumberValidated: false, isNumberPotentiallyValid: false, activeBrand: '', brands: [ 'visa', 'mastercard', 'american-express', 'elo', 'diners-club', 'hiper', 'hipercard' ], canHideHolderFields: this.canSkipHolderInfo || Boolean(this.checkHolder), loadPromise: this.jsClientLoad || Promise.resolve() } }, computed: { i19aboutCvvMsg: () => i18n(i19aboutCvvMsg), i19atSight: () => i18n(i19atSight).toLowerCase(), i19bearersDocument: () => i18n(i19bearersDocument), i19birthdate: () => i18n(i19birthdate), i19cardNumber: () => i18n(i19cardNumber), i19confirmPurchase: () => i18n(i19confirmPurchase), i19holderName: () => i18n(i19holderName), i19interestFree: () => i18n(i19interestFree).toLowerCase(), i19nameOnCard: () => i18n(i19nameOnCard), i19of: () => i18n(i19of).toLowerCase(), i19parcelIn: () => i18n(i19parcelIn), i19securityCode: () => i18n(i19securityCode), i19validThru: () => i18n(i19validThru), lang: () => $ecomConfig.get('lang'), holderName: { get () { return this.card.name }, set (value) { this.card.name = value.toUpperCase() } }, compareName () { return this.checkHolder.replace(/(\s.*)/, '') }, creditCardRegex () { return new RegExp(i18n(i19creditCard), 'i') } }, methods: { formatMoney, checkName () { if (!this.canSkipHolderInfo && this.checkHolder) { const name = this.card.name.replace(/(\s.*)/, '') if (name === '') { this.canHideHolderFields = true } else if (name.localeCompare(this.compareName, 'en', { sensitivity: 'base' }) === 0) { this.canHideHolderFields = true this.card.doc = this.card.birth = '' } else { this.canHideHolderFields = false } } }, validateDate () { const month = this.card.date.substr(0, 2) const year = this.card.date.substr(2, 2) if (year.length === 2) { return cardValidator.expirationMonth(month).isValid && cardValidator.expirationYear(year).isValid } return false }, validateCvv () { return cardValidator .cvv(this.card.cvv, this.activeBrand !== 'american-express' ? 3 : 4).isValid }, updateInstallmentList () { const cardInstallments = this.jsClient.cc_installments if ( cardInstallments && cardInstallments.function && this.card.bin.length >= 6 && !this.loadInstallmentsTimer ) { this.loadInstallmentsTimer = setTimeout(() => { this.loadInstallmentsTimer = null const installmentList = window[cardInstallments.function]({ number: this.card.bin, amount: this.amount.total }) if (cardInstallments.is_promise) { this.isLoadingInstallments = true installmentList .then(installmentList => { this.installmentList = installmentList if (installmentList.length) { this.card.installment = 1 } this.hasLoadedInstallments = true }) .catch(console.error) .finally(() => { this.isLoadingInstallments = false }) } else { this.installmentList = installmentList } }, 200) } }, generateCardHash () { return this.loadPromise.then(() => { const cardHash = this.jsClient.cc_hash if (cardHash && cardHash.function) { const { name, doc, bin, cvv, date } = this.card return window[cardHash.function]({ name, doc, number: bin, cvc: cvv, month: date.substr(0, 2), year: date.substr(2, 2), brand: this.activeBrand }) } return '' }) }, emitCardData (hash) { const { bin, name, cvv, doc, birth, installment, address } = this.card const transaction = { credit_card: { holder_name: name, last_digits: bin.slice(-4), save: true, cvv: parseInt(cvv, 10), hash } } if (installment) { transaction.installments_number = installment } if (address && address.zip) { transaction.billing_address = address } if (name && doc) { transaction.payer = { fullname: name, doc_number: doc.replace(/\D/g, '') } if (birth) { const dateNumber = (start, ln) => parseInt(birth.substr(start, ln), 10) let day, month, year if (countryCode === 'BR') { day = dateNumber(0, 2) month = dateNumber(2, 2) year = dateNumber(4, 4) } else { day = dateNumber(6, 2) month = dateNumber(4, 2) year = dateNumber(0, 4) } transaction.payer.birth_date = { day, month, year } } } this.$emit('checkout', transaction) }, notifyInvalidCard (customMsg) { let body = i18n(i19invalidCardMsg) if (typeof customMsg === 'string' && customMsg) { body += customMsg } this.$toast({ title: i18n(i19invalidCard), body }) }, submit () { const { alert } = this alert.bin = !this.isNumberPotentiallyValid alert.date = !this.validateDate() alert.cvv = !this.validateCvv() if (!alert.bin && !alert.date && !alert.cvv) { const $form = this.$el if ($form.checkValidity() && this.validateDate() && this.validateCvv()) { if (this.jsClient) { this.isWaitingCardHash = true this.generateCardHash() .then(this.emitCardData) .catch(err => { console.error(err) this.notifyInvalidCard(err.userMsg) }) .finally(() => { this.isWaitingCardHash = false }) } else { this.emitCardData() } } $form.classList.add('was-validated') } else { this.notifyInvalidCard() } } }, watch: { installmentOptions: { handler (installmentOptions) { if (installmentOptions) { this.installmentList = installmentOptions.concat().sort((a, b) => { return a.number - b.number }) } }, immediate: true }, formattedCardBin (formattedBin) { if (formattedBin.length === 1) { if (!this.card.bin) { this.canFormatBinInput = true this.$nextTick(() => { const $binInput = this.$refs.binInput.$el $binInput.focus() setTimeout(() => { $binInput.setSelectionRange(1, 1) }, 10) }) } } if (!this.cardBinSetTimer && formattedBin.replace(/\D/g, '') !== this.card.bin) { this.cardBinSetTimer = setTimeout(() => { this.cardBinSetTimer = null this.card.bin = this.formattedCardBin.replace(/\D/g, '') }, 200) } }, 'card.bin' (bin) { this.isNumberValidated = this.isNumberPotentiallyValid = false const numberCheck = cardValidator.number(bin) if (numberCheck.isPotentiallyValid && numberCheck.card) { if (this.activeBrand !== numberCheck.card.type) { this.activeBrand = numberCheck.card.type if (this.activeBrand) { this.hasLoadedInstallments = false this.$nextTick(this.updateInstallmentList) } } else if (!this.hasLoadedInstallments && !this.isLoadingInstallments) { this.$nextTick(this.updateInstallmentList) } if (numberCheck.isValid) { this.isNumberValidated = this.isNumberPotentiallyValid = true } else { this.isNumberPotentiallyValid = numberCheck.isPotentiallyValid } } else { this.activeBrand = '' } }, rawCardExp (dateStr) { if (dateStr.length === 1) { if (!this.card.date) { this.canFormatExpInput = true this.$nextTick(() => { const $expInput = this.$refs.expInput.$el $expInput.focus() setTimeout(() => { $expInput.setSelectionRange(1, 1) }, 10) }) } } this.card.date = dateStr.length > 5 ? `${dateStr.substr(0, 2)}${dateStr.substr(dateStr.length - 2, 2)}` // mm/YYYY | mmYYYY : dateStr.replace('/', '') // mmyy | mm/yy }, alert: { handler () { this.$el.classList.remove('was-validated') }, deep: true } }, created () { if (this.jsClient && !this.jsClientLoad) { this.loadPromise = loadPaymentClient(this.jsClient) } }, mounted () { const $inputs = this.$el.querySelectorAll('input') for (let i = 0; i < $inputs.length; i++) { if (!$inputs[i].value) { $inputs[i].focus() break } } $(function () { $('[data-toggle="popover"]').popover() }) } }