Files
vercel/packages/now-cli/src/commands/billing/add.js
Steven b0ad5238f7 Create monorepo (#2812)
* Move now-cli to /packages/now-cli

* Fix .gitignore paths

* Add now-client

* Add lerna to top level

* Add scripts

* Update codeowners

* Fix `/now-cli/build.ts` script

* Fix circleci path to artifacts

* Use relative paths

* Fix path to scripts

* Add test-lint script

* Add missing return type

* Fix typo in test-lint

* Fix string match in shell scripts

* Fix path to hugo

* Add package node_modules

* Delete lock files in packages, use root yarn.lock

* Add missing b.js file

* Add test-integration-now-dev script

* Add missing test files

* Add missing integration test script

* Add missing test files

* Delete travis.yml

* Fix ts-jest in now-client

* Add support for Node 8 (ES2015 target)

* Add support for Node 8

* Add polyfill for Node 8

* Fix polyfill for Node 8

* Only run coverage for now-cli

* Add packages from now-builders

* Run integration tests for builders

* Add node_modules to cache

* Add root readme.md

* Move readme to top level

* Add yarn bootstrap

* Add bootstrap step

* Add dist to `persist_to_workspace`

* Fix 08-yarn-npm integration test

* Remove duplicate path

* Change stdio to inherit

* Add back store_artifacts

* testing - remove bootstrap step

* Add back now-build-utils

* Remove bootstrap step

* Fix test again

* Add console.log()

* Fix lint

* Use local ncc version

* Install go

* Revert changes to stdio and console.log()

* Add missing now-go test

* Add missing integration tests

* Add --runInBand flag

* Fix now-node-bridge persistence

* Add missing symlinks

* Add codeowners

* Consolidate into single run.sh function

* Run uniq

* Fix typo

* Change now-routing-utils to test-unit

* Special case test for node 8

* Add docs from builders

* Only run script for modified packages

* Add test-integration-once which only runs once

* Fix set intersection
2019-08-23 23:57:00 +00:00

172 lines
4.9 KiB
JavaScript

import ansiEscapes from 'ansi-escapes';
import chalk from 'chalk';
import ccValidator from 'credit-card';
import textInput from '../../util/input/text';
import cardBrands from '../../util/billing/card-brands';
import success from '../../util/output/success';
import wait from '../../util/output/wait';
import chars from '../../util/output/chars';
import rightPad from '../../util/output/right-pad';
import error from '../../util/output/error';
const expDateMiddleware = data => data;
export default async function({ creditCards, clear = false, contextName }) {
const state = {
error: undefined,
cardGroupLabel: `> ${chalk.bold(
`Enter your card details for ${chalk.bold(contextName)}`
)}`,
name: {
label: rightPad('Full Name', 12),
placeholder: 'John Appleseed',
validateValue: data => data.trim().length > 0
},
cardNumber: {
label: rightPad('Number', 12),
mask: 'cc',
placeholder: '#### #### #### ####',
validateKeypress: (data, value) => /\d/.test(data) && value.length < 19,
validateValue: data => {
data = data.replace(/ /g, '');
const type = ccValidator.determineCardType(data);
if (!type) {
return false;
}
return ccValidator.isValidCardNumber(data, type);
}
},
ccv: {
label: rightPad('CCV', 12),
mask: 'ccv',
placeholder: '###',
validateValue: data => {
const brand = state.cardNumber.brand.toLowerCase();
return ccValidator.doesCvvMatchType(data, brand);
}
},
expDate: {
label: rightPad('Exp. Date', 12),
mask: 'expDate',
placeholder: 'mm / yyyy',
middleware: expDateMiddleware,
validateValue: data => !ccValidator.isExpired(...data.split(' / '))
}
};
async function render() {
for (const key in state) {
if (!Object.hasOwnProperty.call(state, key)) {
continue;
}
const piece = state[key];
if (typeof piece === 'string') {
console.log(piece);
} else if (typeof piece === 'object') {
let result;
try {
/* eslint-disable no-await-in-loop */
result = await textInput({
label: `- ${piece.label}`,
initialValue: piece.initialValue || piece.value,
placeholder: piece.placeholder,
mask: piece.mask,
validateKeypress: piece.validateKeypress,
validateValue: piece.validateValue,
autoComplete: piece.autoComplete
});
piece.value = result;
if (key === 'cardNumber') {
let brand = cardBrands[ccValidator.determineCardType(result)];
piece.brand = brand;
if (brand === 'American Express') {
state.ccv.placeholder = '#'.repeat(4);
} else {
state.ccv.placeholder = '#'.repeat(3);
}
brand = chalk.cyan(`[${brand}]`);
const masked = chalk.gray('#### '.repeat(3)) + result.split(' ')[3];
process.stdout.write(
`${chalk.cyan(chars.tick)} ${piece.label}${masked} ${brand}\n`
);
} else if (key === 'ccv') {
process.stdout.write(
`${chalk.cyan(chars.tick)} ${piece.label}${'*'.repeat(
result.length
)}\n`
);
} else if (key === 'expDate') {
let text = result.split(' / ');
text = text[0] + chalk.gray(' / ') + text[1];
process.stdout.write(
`${chalk.cyan(chars.tick)} ${piece.label}${text}\n`
);
} else {
process.stdout.write(
`${chalk.cyan(chars.tick)} ${piece.label}${result}\n`
);
}
} catch (err) {
if (err.message === 'USER_ABORT') {
process.exit(1);
} else {
console.error(error(err));
}
}
}
}
console.log(''); // New line
const stopSpinner = wait('Saving card');
try {
const res = await creditCards.add({
name: state.name.value,
cardNumber: state.cardNumber.value,
ccv: state.ccv.value,
expDate: state.expDate.value
});
stopSpinner();
if (clear) {
const linesToClear = state.error ? 15 : 14;
process.stdout.write(ansiEscapes.eraseLines(linesToClear));
}
console.log(
success(
`${state.cardNumber.brand ||
state.cardNumber.card.brand} ending in ${res.last4 ||
res.card.last4} was added to ${chalk.bold(contextName)}`
)
);
} catch (err) {
stopSpinner();
const linesToClear = state.error ? 15 : 14;
process.stdout.write(ansiEscapes.eraseLines(linesToClear));
state.error = `${chalk.red(
'> Error!'
)} ${err.message} Please make sure the info is correct`;
await render();
}
}
try {
await render();
} catch (err) {
console.erorr(err);
}
}