UNPKG

@mason-api/javascript-sdk

Version:

Mason component rendering library

138 lines (123 loc) 3.93 kB
import _ from 'lodash'; import update from 'immutability-helper'; import { CONFIG, TREE } from '@mason-api/utils'; const SECRET_KEY = 'secretKey'; const PUBLISHABLE_KEY = 'publishableKey'; const stripeElementsLookUpTable = { 'card': 'card', 'card-cvc': 'cardCvc', 'card-expiry': 'cardExpiry', 'card-number': 'cardNumber', }; // inputs with these names will be sent to stripe along with card data const stripeCardKeys = [ 'address_city', 'address_country', 'address_line1', 'address_line2', 'address_state', 'address_zip', 'country', 'name', ]; export const schema = { id: 'stripe', name: 'Stripe', keys: { PUBLISHABLE_KEY, SECRET_KEY, }, requiredKeys: [PUBLISHABLE_KEY], spec: [ { key: PUBLISHABLE_KEY, value: '' }, { key: SECRET_KEY, private: true, value: '' }, ], }; let stripe; function mountElements(e) { const forms = e.target.querySelectorAll('form[name="stripe"]'); _.forEach(forms, (form) => { const elements = stripe.elements(); _.forEach(stripeElementsLookUpTable, (name) => { const input = form.querySelector(`div[data-name="${name}"]`); if (input) { const element = elements.create(name); element.mount(input); } }); }); } export const has = config => !_.isEmpty(CONFIG.getValueForIntegration(config, schema.id, PUBLISHABLE_KEY)); export const init = getContext => next => (config) => { if (!stripe) { if (!_.isFunction(Stripe)) { throw new Error('Stripe not found. Please include Stripe.js script, visit https://stripe.com/docs/stripe-js/reference#including-stripejs for more details'); } else { stripe = Stripe(CONFIG.getValueForIntegration(config, schema.id, PUBLISHABLE_KEY)); } } let nextConfig = { ...config }; _.forEach(_.keys(nextConfig.data), (configKey) => { const { data: { [configKey]: { tree } } } = config; let nextTree = { ...tree }; const forms = TREE.search(nextTree, { tag: 'form', p: { name: 'stripe' } }); // first we replace any stripe inputs with their react-elements container _.forEach(forms, (form) => { _.forEach(stripeElementsLookUpTable, (type, id) => { const input = _.first(TREE.search(form.c, { p: { name: _.camelCase(id) } }, form.path)); if (input) { nextTree = TREE.replaceNode(nextTree, update(input, { tag: { $set: 'div' }, p: { $merge: { 'data-name': input.p.name }, $unset: ['name'] }, })); } }); }); const buttons = TREE.search(nextTree, { tag: 'button', p: { _function: 'stripe:createToken' } }); _.forEach(buttons, (button) => { nextTree = TREE.setNodeProp(nextTree, button.path, '_function', 'submitForm'); }); nextConfig = update(nextConfig, { data: { [configKey]: { tree: { $set: nextTree }, }, }, }); }); return next(nextConfig); }; export const render = getContext => next => (config, configSubpath, target, props) => { target.addEventListener('render', mountElements); return next(config, configSubpath, target, { ...props, willSendData: (form, name) => { if (name === 'stripe') { // this integration will only process forms named stripe return stripe.createToken(_.pick(form.data, stripeCardKeys)).then(({ token }) => { const nextForm = update(form, { data: { $merge: { stripeToken: token.id, }, }, }); if (_.isFunction(props.willSendData)) { return props.willSendData(nextForm, name); } return nextForm; }); } // if it's not named stripe, still must call ownProps.willSendData if (_.isFunction(props.willSendData)) { return props.willSendData(form, name); } return form; } }); }; export default { has, init, render, };