fin
Version:
Developer CLI for Fin. Fin is the easiest way to launch your own SaaS.
182 lines (160 loc) • 5.74 kB
JavaScript
const ccValidator = require('credit-card')
const { prompt } = require('enquirer')
const cardBrands = require('../card-brands')
const createBillingSource = require('../create-billing-source')
const handleError = require('../handle-error')
const spinner = require('../spinner')
module.exports = (program, client) => {
program
.command('cc <command> [id]')
.alias('billing')
.description('Manages your credit cards and billing methods')
.action(async (cmd, id) => {
program.requireAuthentication()
try {
switch (cmd) {
case 'ls': {
const [
user,
sources
] = await spinner(
Promise.all([
client.getBilling(),
client.listBillingSources()
]),
'Getting billing sources'
)
// TODO: better output formatting
// TODO: better highlighting of default source
console.log(JSON.stringify(sources, null, 2))
console.log(`default source: ${user.stripeDefaultSource}`)
break
}
case 'add': {
const temp = { }
const prompts = [
{
type: 'input',
name: 'name',
message: 'Full Name',
validate: (value) => {
return value.trim().length > 0
}
},
{
type: 'input',
name: 'cardNumber',
message: 'Card Number',
hint: '#### #### #### ####',
format: (value) => {
value = value.replace(/\s/g, '')
value = value.replace(/[^\d]/g, '')
if (value.length >= 4) {
value = value.slice(0, 4) + ' ' + value.slice(4)
if (value.length >= 9) {
value = value.slice(0, 9) + ' ' + value.slice(9)
if (value.length >= 14) {
value = value.slice(0, 14) + ' ' + value.slice(14)
if (value.length <= 19) {
value = value.slice(0, 19)
}
}
}
}
return value
},
validate: (value) => {
value = value.replace(/\s/g, '')
const type = ccValidator.determineCardType(value)
return type && ccValidator.isValidCardNumber(value, type)
},
result: (value) => {
temp.cardNumber = value.replace(/\s/g, '')
temp.type = ccValidator.determineCardType(temp.cardNumber)
temp.brand = cardBrands[temp.type]
return temp.cardNumber
}
},
{
type: 'input',
name: 'ccv',
message: 'CCV',
hint: '###',
validate: (value) => {
const brand = temp.brand.toLowerCase()
return ccValidator.doesCvvMatchType(value, brand)
}
},
{
type: 'input',
name: 'expDate',
message: 'Expiration Date',
hint: 'mm/yyyy',
validate: (value) => {
const parts = value.replace(/\s/g, '').split('/')
return !ccValidator.isExpired(...parts)
},
result: (value) => {
return value.replace(/\s/g, '').split('/')
}
}
]
console.log('Please enter your credit card details:')
const card = await prompt(prompts)
let token
try {
const result = await spinner(
createBillingSource(card),
'Creating secure billing token'
)
token = result.id
} catch (err) {
console.error('Error initializing payment source', err.message)
process.exit(1)
}
const source = await spinner(
client.addBillingSource({ source: token }),
'Attaching billing source to user'
)
console.log(JSON.stringify(source, null, 2))
break
}
case 'rm': {
if (!id) {
console.error('error: you must specify the id of a billing source id to remove')
process.exit(1)
}
const source = await spinner(
client.removeBillingSource(id),
'Removing billing source'
)
console.log(JSON.stringify(source, null, 2))
break
}
case 'set-default': {
if (!id) {
console.error('error: you must specify the id of a billing source id to set-default')
process.exit(1)
}
const source = await spinner(
client.setDefaultBillingSource(id),
'Setting default billing source'
)
console.log(JSON.stringify(source, null, 2))
break
}
default: {
if (!cmd) {
console.error('error: must specify a billing command (ls, add, rm, or set-default)')
} else {
console.error(`error: invalid billing command [${cmd}] (ls, add, rm, or set-default)`)
}
process.exit(1)
}
}
} catch (err) {
handleError(err)
}
})
}