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 param = require('../../lib/utils/output/param')
const info = require('../../lib/utils/output/info')
const uid = require('../../lib/utils/output/uid')
const success = require('../../lib/utils/output/success')
const stamp = require('../../lib/utils/output/stamp')
const promptBool = require('../../lib/utils/input/prompt-bool')
const eraseLines = require('../../lib/utils/output/erase-lines')
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]
let elapsed
@ -21,14 +21,54 @@ module.exports = async function({ domains, args, currentTeam, user }) {
}
const nameParam = param(name)
elapsed = stamp()
let stopSpinner = wait(`Checking availability for ${nameParam}`)
let stopSpinner
let price
let period
let validCoupon
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)
price = json.price
price = validCoupon ? 0 : json.price
period = json.period
} catch (err) {
stopSpinner()
@ -61,9 +101,8 @@ module.exports = async function({ domains, args, currentTeam, user }) {
stopSpinner = wait('Purchasing')
elapsed = stamp()
let domain
try {
domain = await domains.buy(name)
await domains.buy({ name, coupon })
} catch (err) {
stopSpinner()
return treatBuyError(err)
@ -71,7 +110,7 @@ module.exports = async function({ domains, args, currentTeam, user }) {
stopSpinner()
success(`Domain purchased and created ${uid(domain.uid)} ${elapsed()}`)
success(`Domain ${nameParam} purchased ${elapsed()}`)
info(
`You may now use your domain as an alias to your deployments. Run ${cmd(
'now alias --help'

19
bin/now-billing-add.js

@ -19,7 +19,12 @@ function expDateMiddleware(data) {
return data
}
module.exports = function({ creditCards, currentTeam, user }) {
module.exports = async function({
creditCards,
currentTeam,
user,
clear = false
}) {
const state = {
error: undefined,
cardGroupLabel: `> ${chalk.bold(
@ -221,6 +226,10 @@ module.exports = function({ creditCards, currentTeam, user }) {
address1: state.address1.value
})
stopSpinner()
if (clear) {
const linesToClear = state.error ? 15 : 14
process.stdout.write(ansiEscapes.eraseLines(linesToClear))
}
success(
`${state.cardNumber
.brand} ending in ${res.last4} was added to ${chalk.bold(
@ -229,7 +238,7 @@ module.exports = function({ creditCards, currentTeam, user }) {
)
} catch (err) {
stopSpinner()
const linesToClear = state.error ? 13 : 12
const linesToClear = state.error ? 15 : 14
process.stdout.write(ansiEscapes.eraseLines(linesToClear))
state.error = `${chalk.red(
'> 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 argv = minimist(process.argv.slice(2), {
string: ['config', 'token'],
string: ['coupon', 'token'],
boolean: ['help', 'debug', 'external', 'force'],
alias: {
help: 'h',
config: 'c',
coupon: 'c',
debug: 'd',
external: 'e',
force: 'f',
@ -44,9 +44,6 @@ const help = () => {
${chalk.dim('Options:')}
-h, --help Output usage information
-c ${chalk.bold.underline('FILE')}, --config=${chalk.bold.underline(
'FILE'
)} Config file
-d, --debug Debug mode [off]
-e, --external Use external DNS server
-f, --force Skip DNS verification
@ -142,10 +139,6 @@ const help = () => {
const debug = argv.debug
const apiUrl = argv.url || 'https://api.zeit.co'
if (argv.config) {
cfg.setConfigFile(argv.config)
}
if (argv.help || !subcommand) {
help()
exit(0)
@ -345,7 +338,8 @@ async function run({ token, config: { currentTeam, user } }) {
domains: domain,
args,
currentTeam,
user
user,
coupon: argv.coupon
})
break
}

39
lib/domains.js

@ -197,18 +197,12 @@ module.exports = class Domains extends Now {
})
}
async buy(name) {
async buy({ name, coupon }) {
if (!name) {
throw new Error('`name` is not defined')
}
const rawBody = { name }
if (name.startsWith('test')) {
rawBody.dev = true
}
const body = JSON.stringify(rawBody)
const body = JSON.stringify({ name, coupon })
return this.retry(async (bail, attempt) => {
if (this._debug) {
@ -238,4 +232,33 @@ module.exports = class Domains extends Now {
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')
break
}
case 'forbidden_premium': {
error('A coupon cannot be used to register a premium domain')
break
}
default: {
error(err.message)
}

Loading…
Cancel
Save