catarse/catarse.js

View on GitHub
legacy/src/c/payment-credit-card.js

Summary

Maintainability
F
5 days
Test Coverage
import m from 'mithril';
import prop from 'mithril/stream';
import _ from 'underscore';
import h from '../h';
import tooltip from './tooltip';
import creditCardVM from '../vms/credit-card-vm';
import projectVM from '../vms/project-vm';
import creditCardInput from './credit-card-input';
import inlineError from './inline-error';
import subscriptionEditModal from './subscription-edit-modal';
import commonPaymentVM from '../vms/common-payment-vm';

const I18nScope = _.partial(h.i18nScope, 'projects.contributions.edit');
const I18nIntScope = _.partial(h.i18nScope, 'projects.contributions.edit_international');

const paymentCreditCard = {
    oninit: function(vnode) {
        const vm = vnode.attrs.vm,
            isSubscriptionEdit = vnode.attrs.isSubscriptionEdit || prop(false),
            subscriptionEditConfirmed = prop(false),
            showSubscriptionModal = prop(false),
            loadingInstallments = prop(true),
            loadingSavedCreditCards = prop(true),
            selectedCreditCard = prop({ id: -1 }),
            selectedInstallment = prop('1'),
            showForm = prop(false),
            creditCardType = prop('unknown'),
            documentMask = _.partial(h.mask, '999.999.999-99'),
            documentCompanyMask = _.partial(h.mask, '99.999.999/9999-99');

        const sendSubscriptionPayment = (creditCard, subscriptionVM, commonData) => {
            if (!isSubscriptionEdit()) {
                commonPaymentVM.sendCreditCardPayment(creditCard, subscriptionVM, commonData, vnode.attrs.addressVM);

                return false;
            }

            if (!subscriptionEditConfirmed() && !vnode.attrs.isReactivation()) {
                showSubscriptionModal(true);

                return false;
            }

            const data = _.extend({}, commonData, { subscription_id: vnode.attrs.subscriptionId() });

            commonPaymentVM.sendCreditCardPayment(
                selectedCreditCard,
                subscriptionVM,
                data,
                vnode.attrs.addressVM
            );

            return false;
        };

        const handleValidity = (isValid, errorObj) => {
            if (!isValid) {
                vm.creditCardFields.errors().push(errorObj);
            } else {
                const errorsWithout = _.reject(vm.creditCardFields.errors(), err => _.isEqual(err, errorObj));
                vm.creditCardFields.errors(errorsWithout);
            }
        };

        const checkcvv = () => {
            const isValid = creditCardVM.validateCardcvv(vm.creditCardFields.cvv(), creditCardType()),
                errorObj = { field: 'cvv', message: window.I18n.t('errors.inline.creditcard_cvv', scope()) };

            handleValidity(isValid, errorObj);
        };

        const checkExpiry = () => {
            const isValid = creditCardVM.validateCardExpiry(vm.creditCardFields.expMonth(), vm.creditCardFields.expYear()),
                errorObj = { field: 'expiry', message: window.I18n.t('errors.inline.creditcard_expiry', scope()) };

            handleValidity(isValid, errorObj);
        };

        const checkCreditCard = () => {
            const isValid = creditCardVM.validateCardNumber(vm.creditCardFields.number()),
                errorObj = { field: 'number', message: window.I18n.t('errors.inline.creditcard_number', scope()) };

            handleValidity(isValid, errorObj);
        };

        const checkCardOwnerDocument = () => {
            const document = vm.creditCardFields.cardOwnerDocument(),
                striped = String(document).replace(/[\.|\-|\/]*/g, '');
            let isValid = false,
                errorMessage = '';

            if (document.length > 14) {
                isValid = h.validateCnpj(document);
                errorMessage = 'CNPJ inválido.';
            } else {
                isValid = h.validateCpf(striped);
                errorMessage = 'CPF inválido.';
            }

            handleValidity(isValid, { field: 'cardOwnerDocument', message: errorMessage });
        };

        const checkCreditCardName = () => {
            const trimmedString = vm.creditCardFields.name().replace(/ /g, '');
            const charsOnly = /^[a-zA-ZàèìòùÀÈÌÒÙáéíóúýÁÉÍÓÚÝâêîôûÂÊÎÔÛãñõÃÑÕäëïöüÿÄËÏÖÜŸçÇßØøÅåÆæœ]*$/;
            const errorObj = { field: 'name', message: window.I18n.t('errors.inline.creditcard_name', scope()) };
            const isValid = !(_.isEmpty(trimmedString) || !charsOnly.test(trimmedString));

            handleValidity(isValid, errorObj);
        };
        const onSubmit = () => {
            vm.creditCardFields.errors([]);

            if (selectedCreditCard().id === -1) {
                if (!vm.isInternational()) {
                    checkCardOwnerDocument();
                }
                checkExpiry();
                checkcvv();
                checkCreditCard();
                checkCreditCardName();
            }

            if (vm.creditCardFields.errors().length === 0) {
                if (vnode.attrs.isSubscription) {
                    const commonData = {
                        rewardCommonId: vnode.attrs.reward_common_id,
                        userCommonId: vnode.attrs.user_common_id,
                        projectCommonId: vnode.attrs.project_common_id,
                        amount: vnode.attrs.value * 100
                    };
                    sendSubscriptionPayment(selectedCreditCard, vm, commonData);
                } else {
                    vm.sendPayment(selectedCreditCard, selectedInstallment, vnode.attrs.contribution_id, vnode.attrs.project_id);
                }
            }

            return false;
        };

        const applyCreditCardNameMask = _.compose(vm.creditCardFields.name, h.noNumbersMask);

        const applyCvvMask = (value) => {
            const setValue = h.numbersOnlyMask(value.substr(0, 4));

            return vm.creditCardFields.cvv(setValue);
        };

        const applyDocumentMask = (value) => {
            if (value.length > 14) {
                vm.creditCardFields.cardOwnerDocument(documentCompanyMask(value));
            } else {
                vm.creditCardFields.cardOwnerDocument(documentMask(value));
            }
        };


        const fieldHasError = (fieldName) => {
            const fieldWithError = _.findWhere(vm.creditCardFields.errors(), { field: fieldName });

            return fieldWithError ? m(inlineError, { message: fieldWithError.message }) : '';
        };

        const buildTooltip = tooltipText => m(tooltip, {
            el: '.tooltip-wrapper.fa.fa-question-circle.fontcolor-secondary',
            text: tooltipText,
            width: 380
        });

        const isCreditCardSelected = (card, idx) => selectedCreditCard() === card;

        const loadPagarme = (vnode) => {
            const script = document.createElement('script');
            script.src = '//assets.pagar.me/pagarme-js/4.8/pagarme.min.js';
            document.body.appendChild(script);
            script.onload = () => {
                vm.pagarme(window.pagarme);
            };
        };

        const selectCreditCard = (card) => {
            selectedCreditCard(card);

            if (card.id === -1) {
                showForm(true);
            } else {
                showForm(false);
            }
        };

        const scope = attr => vm.isInternational()
                   ? I18nIntScope(attr)
                   : I18nScope(attr);

        // Sum the total amount of installments with taxes and returns a formated string
        const totalAmountOfInstallment = (installments, selectedIndex) => h.formatNumber(installments[selectedIndex - 1].total_amount, 2);

        if (!vnode.attrs.isSubscription) {
            vm.getInstallments(vnode.attrs.contribution_id)
                .then(() => {
                    loadingInstallments(false);
                    m.redraw();
                });
        }

        if (!vnode.attrs.hideSave) {
            vm.getSavedCreditCards(vnode.attrs.user_id)
                .then((savedCards) => {
                    loadingSavedCreditCards(false);
                    selectCreditCard(savedCards[0]);
                    m.redraw();
                });
        } else {
            showForm(true);
        }

        vnode.state = {
            vm,
            onSubmit,
            fieldHasError,
            buildTooltip,
            loadingInstallments,
            loadingSavedCreditCards,
            installments: vm.installments,
            selectedInstallment,
            savedCreditCards: vm.savedCreditCards,
            creditCard: vm.creditCardFields,
            creditCardType,
            checkCreditCard,
            checkCreditCardName,
            applyCreditCardNameMask,
            applyCreditCardMask: vm.applyCreditCardMask,
            applyDocumentMask,
            checkCardOwnerDocument,
            applyCvvMask,
            checkcvv,
            selectCreditCard,
            isCreditCardSelected,
            expMonths: vm.expMonthOptions(),
            expYears: vm.expYearOptions(),
            loadPagarme,
            scope,
            totalAmountOfInstallment,
            showForm,
            showSubscriptionModal,
            sendSubscriptionPayment,
            subscriptionEditConfirmed,
            isSubscriptionEdit
        };
    },
    view: function({state, attrs}) {
        const isInternational = state.vm.isInternational();

        return m('.w-form.u-marginbottom-40', {
            oncreate: state.loadPagarme
        }, [
            m('form[method="post"][name="email-form"]', {
                onsubmit: state.onSubmit
            }, [
                (!attrs.hideSave && !state.loadingSavedCreditCards() && (state.savedCreditCards().length > 1)) ?

                    m('.my-credit-cards.w-form.back-payment-form-creditcard.records-choice.u-marginbottom-40',
                        _.map(state.savedCreditCards(), (card, idx) => m(`div#credit-card-record-${idx}.creditcard-records`, {
                            style: 'cursor:pointer;',
                            onclick: () => state.selectCreditCard(card)
                        }, [
                            m('.w-row', [
                                m('.w-col.w-col-1',
                                    m('.back-payment-credit-card-radio-field.w-clearfix.w-radio', [
                                        m('input', {
                                            checked: state.isCreditCardSelected(card, idx),
                                            name: 'payment_subscription_card',
                                            type: 'radio',
                                            value: card.card_key
                                        })
                                    ])
                                ),
                                card.id === -1 ?
                                m('.w-col.w-col-11',
                                    m('.fontsize-small.fontweight-semibold.fontcolor-secondary', window.I18n.t('credit_card.use_another', state.scope()))
                                ) : [
                                    m('.w-col.w-col-2',
                                        m('.fontsize-small.fontweight-semibold.text-success', card.card_brand.toUpperCase())
                                    ),
                                    m('.w-col.w-col-5',
                                        m('.fontsize-small.fontweight-semibold.u-marginbottom-20', `XXXX.XXXX.XXXX.${card.last_digits}`)
                                    ),
                                    m('.w-clearfix.w-col.w-col-4', [
                                        (state.loadingInstallments() || (state.installments().length <= 1)) ? '' :
                                            m('select.w-select.text-field.text-field-creditcard', {
                                                onchange: m.withAttr('value', state.selectedInstallment),
                                                value: state.selectedInstallment()
                                            }, _.map(state.installments(), installment => m('option', { value: installment.number },
                                                `${installment.number} X R$ ${ h.formatNumber(installment.amount, 2) } ${window.I18n.t(`credit_card.installments_number.${installment.number}`, state.scope())}`
                                            ))
                                        ),
                                        (
                                        state.selectedInstallment() > 1 ?
                                                m('.fontsize-small.lineheight-looser.fontweight-semibold.fontcolor-secondary', [
                                                    window.I18n.t('credit_card.total', state.scope()), `R$ ${state.totalAmountOfInstallment(state.installments(), state.selectedInstallment())}`
                                                ])
                                            : ''
                                        )
                                    ])
                                ]
                            ])
                        ])
                    )
                )
                : !attrs.hideSave && state.loadingSavedCreditCards() ? m('.fontsize-small.u-marginbottom-40', window.I18n.t('credit_card.loading', state.scope())) : '',
                !state.showForm() ? '' : m('#credit-card-payment-form.u-marginbottom-40', [
                    m('div#credit-card-name', [
                        m('.w-row', [
                            m((isInternational ? '.w-col.w-col-12' : '.w-col.w-col-6.w-col-tiny-6.w-sub-col-middle'), [
                                m('label.field-label.fontweight-semibold[for="credit-card-name"]',
                                  window.I18n.t('credit_card.name', state.scope())
                                 ),
                                m('.fontsize-smallest.fontcolor-terciary.u-marginbottom-10.field-label-tip.u-marginbottom-10',
                                  window.I18n.t('credit_card.name_tip', state.scope())
                                 ),
                                m('input.w-input.text-field[name="credit-card-name"][type="text"]', {
                                    onfocus: state.vm.resetCreditCardFieldError('name'),
                                    class: state.fieldHasError('name') ? 'error' : '',
                                    onblur: state.checkCreditCardName,
                                    onkeyup: m.withAttr('value', state.applyCreditCardNameMask),
                                    value: state.creditCard.name()
                                }),
                                state.fieldHasError('name')
                            ]),
                            (!isInternational ?
                             m('.w-col.w-col-6.w-col-tiny-6.w-sub-col-middle', [
                                 m('label.field-label.fontweight-semibold[for="credit-card-document"]',
                                   window.I18n.t('credit_card.document', state.scope())
                                  ),
                                 m('.fontsize-smallest.fontcolor-terciary.u-marginbottom-10.field-label-tip.u-marginbottom-10',
                                   window.I18n.t('credit_card.document_tip', state.scope())
                                  ),
                                 m('input.w-input.text-field[name="credit-card-document"][id="credit-card-document"]', {
                                     onfocus: state.vm.resetCreditCardFieldError('cardOwnerDocument'),
                                     class: state.fieldHasError('cardOwnerDocument') ? 'error' : '',
                                     onblur: state.checkCardOwnerDocument,
                                     onkeyup: m.withAttr('value', state.applyDocumentMask),
                                     value: state.creditCard.cardOwnerDocument(),
                                     name: 'card-owner-document'
                                 }),
                                 state.fieldHasError('cardOwnerDocument')
                             ]) : '')
                        ])
                    ]),
                    m('div#credit-card-number', [
                        m('label.field-label.fontweight-semibold[for="credit-card-number"]',
                            window.I18n.t('credit_card.number', state.scope())
                        ),
                        m('.fontsize-smallest.fontcolor-terciary.u-marginbottom-10.field-label-tip.u-marginbottom-10',
                            window.I18n.t('credit_card.number_tip', state.scope())
                        ),
                        m(creditCardInput, {
                            onfocus: state.vm.resetCreditCardFieldError('number'),
                            onblur: state.checkCreditCard,
                            class: state.fieldHasError('number') ? 'error' : '',
                            value: state.creditCard.number,
                            name: 'credit-card-number',
                            type: state.creditCardType
                        }),
                        state.fieldHasError('number')
                    ]),
                    m('div#credit-card-date', [
                        m('label.field-label.fontweight-semibold[for="expiration-date"]', [
                            window.I18n.t('credit_card.expiry', state.scope())
                        ]),
                        m('.fontsize-smallest.fontcolor-terciary.u-marginbottom-10.field-label-tip.u-marginbottom-10',
                            window.I18n.t('credit_card.expiry_tip', state.scope())
                        ),
                        m('.w-row', [
                            m('.w-col.w-col-6.w-col-tiny-6.w-sub-col-middle',
                                m('select.w-select.text-field[name="expiration-date_month"]', {
                                    onfocus: state.vm.resetCreditCardFieldError('expiry'),
                                    class: state.fieldHasError('expiry') ? 'error' : '',
                                    onchange: m.withAttr('value', state.creditCard.expMonth),
                                    value: state.creditCard.expMonth()
                                }, _.map(state.expMonths, month => m('option', { value: month[0] }, month[1])))
                            ),
                            m('.w-col.w-col-6.w-col-tiny-6',
                                m('select.w-select.text-field[name="expiration-date_year"]', {
                                    onfocus: state.vm.resetCreditCardFieldError('expiry'),
                                    class: state.fieldHasError('expiry') ? 'error' : '',
                                    onchange: m.withAttr('value', state.creditCard.expYear),
                                    onblur: state.checkExpiry,
                                    value: state.creditCard.expYear()
                                }, _.map(state.expYears, year => m('option', { value: year }, year)))
                            ),
                            m('.w-col.w-col-12', state.fieldHasError('expiry'))
                        ])
                    ]),
                    m('div#credit-card-cvv', [
                        m('label.field-label.fontweight-semibold[for="credit-card-cvv"]', [
                            window.I18n.t('credit_card.cvv', state.scope()),
                            state.buildTooltip(window.I18n.t('credit_card.cvv_tooltip', state.scope()))
                        ]),
                        m('.fontsize-smallest.fontcolor-terciary.u-marginbottom-10.field-label-tip.u-marginbottom-10',
                            window.I18n.t('credit_card.cvv_tip', state.scope())
                        ),
                        m('.w-row', [
                            m('.w-col.w-col-8.w-col-tiny-6.w-sub-col-middle',
                                m('input.w-input.text-field[name="credit-card-cvv"][type="tel"]', {
                                    onfocus: state.vm.resetCreditCardFieldError('cvv'),
                                    class: state.fieldHasError('cvv') ? 'error' : '',
                                    onkeyup: m.withAttr('value', state.applyCvvMask),
                                    onblur: state.checkcvv,
                                    value: state.creditCard.cvv()
                                }),
                                state.fieldHasError('cvv')
                            ),
                            m('.w-col.w-col-4.w-col-tiny-6.u-text-center',
                                m('img[src="https://daks2k3a4ib2z.cloudfront.net/54b440b85608e3f4389db387/57298c1c7e99926e77127bdd_cvv-card.jpg"][width="176"]')
                            )
                        ])
                    ]),
                    (
                        (projectVM.isSubscription() || (state.loadingInstallments() || (state.installments().length <= 1))) ? 
                            '' 
                        : 
                            m('.w-row', [
                                m('.w-clearfix.w-col.w-col-6', [
                                    m('label.field-label.fontweight-semibold[for="split"]',
                                        window.I18n.t('credit_card.installments', state.scope())
                                    ),
                                    m('select.text-field.text-field-creditcard.w-select[name="split"]', {
                                        onchange: m.withAttr('value', state.selectedInstallment),
                                        value: state.selectedInstallment()
                                    }, _.map(state.installments(), installment => m(`option[value="${installment.number}"]`,
                                            `${installment.number} X R$ ${ h.formatNumber(installment.amount, 2) } ${window.I18n.t(`credit_card.installments_number.${installment.number}`, state.scope())}`
                                    ))),
                                    (
                                        state.selectedInstallment() > 1 ?
                                            m('.fontsize-small.lineheight-looser.fontweight-semibold.fontcolor-secondary', [
                                                window.I18n.t('credit_card.total', state.scope()), `R$ ${state.totalAmountOfInstallment(state.installments(), state.selectedInstallment())}`
                                            ])
                                            : ''
                                    )
                                ]),
                                m('.w-col.w-col-6')
                            ])
                    ),
                    attrs.hideSave ? '' : m('.card.card-terciary.u-radius.u-margintop-30',
                        m('.fontsize-small.w-clearfix.w-checkbox', [
                            m('input#payment_save_card.w-checkbox-input[type="checkbox"][name="payment_save_card"]', {
                                onchange: m.withAttr('checked', state.creditCard.save),
                                checked: state.creditCard.save()
                            }),
                            m('label.w-form-label[for="payment_save_card"]',
                                window.I18n.t('credit_card.save_card', state.scope())
                            )
                        ])
                    )
                ]),
                m('.w-row', [
                    m('.w-col.w-col-8.w-col-push-2', [
                        (
                            !_.isEmpty(state.vm.submissionError()) ? 
                                (
                                    m('.card.card-error.u-radius.zindex-10.u-marginbottom-30.fontsize-smaller',
                                        m('.u-marginbottom-10.fontweight-bold', m.trust(state.vm.submissionError()))) 
                                )
                            : 
                                ''
                        ),
                        (
                            state.vm.isLoading() ? 
                                h.loader() 
                            : 
                                m('input.btn.btn-large.u-marginbottom-20[type="submit"]', { 
                                    value: (
                                        state.isSubscriptionEdit() && !attrs.isReactivation() ? 
                                            window.I18n.t('subscription_edit', state.scope())
                                        : 
                                            window.I18n.t('credit_card.finish_payment', state.scope())
                                    )
                                })
                        ),
                        m('.fontsize-smallest.u-text-center.u-marginbottom-30',
                            m.trust(
                                window.I18n.t('credit_card.terms_of_use_agreement', state.scope())
                            )
                        )
                    ])
                ]),
                state.showSubscriptionModal()
                    ? m(subscriptionEditModal,
                        {
                            attrs,
                            vm: state.vm,
                            showModal: state.showSubscriptionModal,
                            confirm: state.subscriptionEditConfirmed,
                            paymentMethod: 'credit_card',
                            pay: state.onSubmit
                        }
                    ) : null
            ])
        ]);
    }
};

export default paymentCreditCard;