Browse Source

Add support for coupon codes (#747)

* [BREAKING] `now domains`: remove `--config` flag

Needed so we can introduce `--coupon`

* Remove test code

* Fix the amount of erased lines upon error

* Make it async

* Add option to clear the output after adding the card

* Tweak success message

* Add support for coupon codes
master
Matheus Fernandes 8 years ago
committed by GitHub
parent
commit
ca1be7b501
  1. 55
      bin/domains/buy.js
  2. 19
      bin/now-billing-add.js
  3. 14
      bin/now-domains.js
  4. 39
      lib/domains.js
  5. 4
      lib/utils/domains/treat-buy-error.js

55
bin/domains/buy.js

@ -5,14 +5,14 @@ const wait = require('../../lib/utils/output/wait')
const cmd = require('../../lib/utils/output/cmd') const cmd = require('../../lib/utils/output/cmd')
const param = require('../../lib/utils/output/param') const param = require('../../lib/utils/output/param')
const info = require('../../lib/utils/output/info') const info = require('../../lib/utils/output/info')
const uid = require('../../lib/utils/output/uid')
const success = require('../../lib/utils/output/success') const success = require('../../lib/utils/output/success')
const stamp = require('../../lib/utils/output/stamp') const stamp = require('../../lib/utils/output/stamp')
const promptBool = require('../../lib/utils/input/prompt-bool') const promptBool = require('../../lib/utils/input/prompt-bool')
const eraseLines = require('../../lib/utils/output/erase-lines') const eraseLines = require('../../lib/utils/output/erase-lines')
const treatBuyError = require('../../lib/utils/domains/treat-buy-error') const treatBuyError = require('../../lib/utils/domains/treat-buy-error')
const NowCreditCards = require('../../lib/credit-cards')
module.exports = async function({ domains, args, currentTeam, user }) { module.exports = async function({ domains, args, currentTeam, user, coupon }) {
const name = args[0] const name = args[0]
let elapsed let elapsed
@ -21,14 +21,54 @@ module.exports = async function({ domains, args, currentTeam, user }) {
} }
const nameParam = param(name) const nameParam = param(name)
elapsed = stamp() let stopSpinner
let stopSpinner = wait(`Checking availability for ${nameParam}`)
let price let price
let period let period
let validCoupon
try { try {
if (coupon) {
stopSpinner = wait(`Validating coupon ${param(coupon)}`)
const creditCards = new NowCreditCards({
apiUrl: domains._agent._url,
token: domains._token,
debug: domains._debug,
currentTeam
})
const [couponInfo, { cards }] = await Promise.all([
domains.coupon(coupon),
creditCards.ls()
])
stopSpinner()
if (!couponInfo.isValid) {
return error(`The coupon ${param(coupon)} is invalid`)
}
if (!couponInfo.canBeUsed) {
return error(`The coupon ${param(coupon)} has already been used`)
}
validCoupon = true
if (cards.length === 0) {
info(
'You have no credit cards on file. Please add one in order to claim your free domain'
)
info(`Your card will ${bold('not')} be charged`)
await require('../now-billing-add')({
creditCards,
currentTeam,
user,
clear: true
})
}
}
elapsed = stamp()
stopSpinner = wait(`Checking availability for ${nameParam}`)
const json = await domains.price(name) const json = await domains.price(name)
price = json.price price = validCoupon ? 0 : json.price
period = json.period period = json.period
} catch (err) { } catch (err) {
stopSpinner() stopSpinner()
@ -61,9 +101,8 @@ module.exports = async function({ domains, args, currentTeam, user }) {
stopSpinner = wait('Purchasing') stopSpinner = wait('Purchasing')
elapsed = stamp() elapsed = stamp()
let domain
try { try {
domain = await domains.buy(name) await domains.buy({ name, coupon })
} catch (err) { } catch (err) {
stopSpinner() stopSpinner()
return treatBuyError(err) return treatBuyError(err)
@ -71,7 +110,7 @@ module.exports = async function({ domains, args, currentTeam, user }) {
stopSpinner() stopSpinner()
success(`Domain purchased and created ${uid(domain.uid)} ${elapsed()}`) success(`Domain ${nameParam} purchased ${elapsed()}`)
info( info(
`You may now use your domain as an alias to your deployments. Run ${cmd( `You may now use your domain as an alias to your deployments. Run ${cmd(
'now alias --help' 'now alias --help'

19
bin/now-billing-add.js

@ -19,7 +19,12 @@ function expDateMiddleware(data) {
return data return data
} }
module.exports = function({ creditCards, currentTeam, user }) { module.exports = async function({
creditCards,
currentTeam,
user,
clear = false
}) {
const state = { const state = {
error: undefined, error: undefined,
cardGroupLabel: `> ${chalk.bold( cardGroupLabel: `> ${chalk.bold(
@ -221,6 +226,10 @@ module.exports = function({ creditCards, currentTeam, user }) {
address1: state.address1.value address1: state.address1.value
}) })
stopSpinner() stopSpinner()
if (clear) {
const linesToClear = state.error ? 15 : 14
process.stdout.write(ansiEscapes.eraseLines(linesToClear))
}
success( success(
`${state.cardNumber `${state.cardNumber
.brand} ending in ${res.last4} was added to ${chalk.bold( .brand} ending in ${res.last4} was added to ${chalk.bold(
@ -229,7 +238,7 @@ module.exports = function({ creditCards, currentTeam, user }) {
) )
} catch (err) { } catch (err) {
stopSpinner() stopSpinner()
const linesToClear = state.error ? 13 : 12 const linesToClear = state.error ? 15 : 14
process.stdout.write(ansiEscapes.eraseLines(linesToClear)) process.stdout.write(ansiEscapes.eraseLines(linesToClear))
state.error = `${chalk.red( state.error = `${chalk.red(
'> Error!' '> Error!'
@ -238,5 +247,9 @@ module.exports = function({ creditCards, currentTeam, user }) {
} }
} }
render().catch(console.error) try {
await render()
} catch (err) {
console.erorr(err)
}
} }

14
bin/now-domains.js

@ -22,11 +22,11 @@ const toHost = require('../lib/to-host')
const { handleError, error } = require('../lib/error') const { handleError, error } = require('../lib/error')
const argv = minimist(process.argv.slice(2), { const argv = minimist(process.argv.slice(2), {
string: ['config', 'token'], string: ['coupon', 'token'],
boolean: ['help', 'debug', 'external', 'force'], boolean: ['help', 'debug', 'external', 'force'],
alias: { alias: {
help: 'h', help: 'h',
config: 'c', coupon: 'c',
debug: 'd', debug: 'd',
external: 'e', external: 'e',
force: 'f', force: 'f',
@ -44,9 +44,6 @@ const help = () => {
${chalk.dim('Options:')} ${chalk.dim('Options:')}
-h, --help Output usage information -h, --help Output usage information
-c ${chalk.bold.underline('FILE')}, --config=${chalk.bold.underline(
'FILE'
)} Config file
-d, --debug Debug mode [off] -d, --debug Debug mode [off]
-e, --external Use external DNS server -e, --external Use external DNS server
-f, --force Skip DNS verification -f, --force Skip DNS verification
@ -142,10 +139,6 @@ const help = () => {
const debug = argv.debug const debug = argv.debug
const apiUrl = argv.url || 'https://api.zeit.co' const apiUrl = argv.url || 'https://api.zeit.co'
if (argv.config) {
cfg.setConfigFile(argv.config)
}
if (argv.help || !subcommand) { if (argv.help || !subcommand) {
help() help()
exit(0) exit(0)
@ -345,7 +338,8 @@ async function run({ token, config: { currentTeam, user } }) {
domains: domain, domains: domain,
args, args,
currentTeam, currentTeam,
user user,
coupon: argv.coupon
}) })
break break
} }

39
lib/domains.js

@ -197,18 +197,12 @@ module.exports = class Domains extends Now {
}) })
} }
async buy(name) { async buy({ name, coupon }) {
if (!name) { if (!name) {
throw new Error('`name` is not defined') throw new Error('`name` is not defined')
} }
const rawBody = { name } const body = JSON.stringify({ name, coupon })
if (name.startsWith('test')) {
rawBody.dev = true
}
const body = JSON.stringify(rawBody)
return this.retry(async (bail, attempt) => { return this.retry(async (bail, attempt) => {
if (this._debug) { if (this._debug) {
@ -238,4 +232,33 @@ module.exports = class Domains extends Now {
return json return json
}) })
} }
async coupon(coupon) {
if (!coupon) {
throw new Error('`coupon` is not defined')
}
const query = encodeQuery({ coupon })
return this.retry(async (bail, attempt) => {
if (this._debug) {
console.time(`> [debug] #${attempt} GET /domains/buy?${query}`)
}
const res = await this._fetch(`/domains/buy?${query}`)
const json = await res.json()
if (res.status !== 200) {
const e = new Error(json.error.message)
e.code = json.error.code
return bail(e)
}
if (this._debug) {
console.timeEnd(`> [debug] #${attempt} GET /domains/buy?${query}`)
}
return json
})
}
} }

4
lib/utils/domains/treat-buy-error.js

@ -18,6 +18,10 @@ module.exports = function(err) {
error('Purchase failed – Unexpected error') error('Purchase failed – Unexpected error')
break break
} }
case 'forbidden_premium': {
error('A coupon cannot be used to register a premium domain')
break
}
default: { default: {
error(err.message) error(err.message)
} }

Loading…
Cancel
Save