/api/administrator/subscriptions/create-coupon (POST)
Account information like email addresses is generated with faker-js it is not real user information.
await global.api.administrator.subscriptions.CreateCoupon.post(req)Returns object
{
"couponid": "CUSTOM18",
"object": "coupon",
"stripeObject": {
"id": "CUSTOM18",
"object": "coupon",
"amount_off": 10,
"created": 1656122269,
"currency": "usd",
"duration": "once",
"duration_in_months": null,
"livemode": false,
"max_redemptions": null,
"metadata": {},
"name": "my coupon",
"percent_off": null,
"redeem_by": null,
"times_redeemed": 0,
"valid": true
},
"active": null,
"appid": "tests_1656122269",
"createdAt": "2022-06-25T01:57:49.668Z",
"updatedAt": "2022-06-25T01:57:49.668Z"
}
Receives
API routes may receive parameters from the URL and POST supporting simple and multipart:
Field | Value | Required | Type |
---|---|---|---|
amount_off | integer, or percent_off | configurable as required | POST |
currency | string, if amount_off | configurable as required | POST |
duration | string | required | POST |
duration_in_months | string | configurable as required | POST |
percent_off | integer, or amount_off | configurable as required | POST |
redeem_by | date in future | configurable as required | POST |
Exceptions
These exceptions are thrown (NodeJS) or returned as JSON (HTTP) if you provide incorrect data or do not meet the requirements:
Exception | Circumstances |
---|---|
duplicate-couponid | invalid posted couponid is already used |
invalid-amount_off | missing posted amount_off |
invalid posted amount_off | |
invalid-couponid | missing posted couponid |
invalid posted couponid is not alphanumeric | |
invalid-currency | missing posted currency |
invalid posted currency | |
invalid-duration | invalid posted duration |
invalid-duration_in_months | missing posted duration_in_months |
invalid posted duration_in_months | |
invalid-name | missing posted name |
invalid-percent_off | invalid posted percent_off |
invalid-redeem_by | invalid posted redeem_by |
NodeJS source (view on github)
const subscriptions = require('../../../../../index.js')
const stripeCache = require('../../../../stripe-cache.js')
module.exports = {
post: async (req) => {
if (!req.body || !req.body.couponid) {
throw new Error('invalid-couponid')
}
if (!req.body.couponid.match(/^[a-zA-Z0-9_]+$/) ||
global.minimumCouponLength > req.body.couponid.length ||
global.maximumCouponLength < req.body.couponid.length) {
throw new Error('invalid-couponid')
}
if (!req.body.name) {
throw new Error('invalid-name')
}
if (req.body.amount_off) {
try {
req.body.amount_off = parseInt(req.body.amount_off, 10)
if (!req.body.amount_off) {
throw new Error('invalid-amount_off')
}
} catch (s) {
throw new Error('invalid-amount_off')
}
if (req.body.amount_off < 0) {
throw new Error('invalid-amount_off')
}
if (!req.body.currency || req.body.currency.length !== 3) {
throw new Error('invalid-currency')
}
} else if (req.body.percent_off) {
try {
req.body.percent_off = parseInt(req.body.percent_off, 10)
if (!req.body.percent_off) {
throw new Error('invalid-percent_off')
}
} catch (s) {
throw new Error('invalid-percent_off')
}
if (req.body.percent_off < 0 || req.body.percent_off > 100) {
throw new Error('invalid-percent_off')
}
}
if (!req.body.amount_off && !req.body.percent_off) {
throw new Error('invalid-amount_off')
}
if (req.body.duration !== 'once' && req.body.duration !== 'repeating' && req.body.duration !== 'forever') {
throw new Error('invalid-duration')
}
if (req.body.duration === 'repeating') {
if (req.body.duration_in_months) {
try {
req.body.duration_in_months = parseInt(req.body.duration_in_months, 10)
if (!req.body.duration_in_months) {
throw new Error('invalid-duration_in_months')
}
} catch (s) {
throw new Error('invalid-duration_in_months')
}
if (req.body.duration_in_months < 1 || req.body.duration_in_months > 24) {
throw new Error('invalid-duration_in_months')
}
} else {
throw new Error('invalid-duration_in_months')
}
}
if (req.body.max_redemptions) {
try {
req.body.max_redemptions = parseInt(req.body.max_redemptions, 10)
if (!req.body.max_redemptions) {
throw new Error('invalid-max_redemptions')
}
} catch (s) {
throw new Error('invalid-max_redemptions')
}
if (req.body.max_redemptions < 0) {
throw new Error('invalid-max_redemptions')
}
}
const couponInfo = {
id: req.body.couponid,
duration: req.body.duration || null,
name: req.body.name
}
if (req.body.redeem_by) {
try {
const redeemDate = new Date(Date.parse(req.body.redeem_by))
const now = new Date()
if (redeemDate.getTime() < now.getTime()) {
throw new Error('invalid-redeem_by')
}
couponInfo.redeem_by = Math.floor(redeemDate.getTime() / 1000)
} catch (error) {
throw new Error('invalid-redeem_by')
}
}
if (req.body.amount_off) {
couponInfo.amount_off = req.body.amount_off
couponInfo.currency = req.body.currency
} else {
couponInfo.percent_off = req.body.percent_off
}
if (req.body.duration_in_months) {
couponInfo.duration_in_months = req.body.duration_in_months
}
if (req.body.max_redemptions) {
couponInfo.max_redemptions = req.body.max_redemptions
}
const coupon = await stripeCache.execute('coupons', 'create', couponInfo, req.stripeKey)
await subscriptions.Storage.Coupon.create({
appid: req.appid || global.appid,
couponid: coupon.id,
stripeObject: coupon
})
req.query = req.query || {}
req.query.couponid = coupon.id
return global.api.administrator.subscriptions.Coupon.get(req)
}
}
Test source (view on github)
/* eslint-env mocha */
const assert = require('assert')
const TestHelper = require('../../../../../test-helper.js')
describe('/api/administrator/subscriptions/create-coupon', function () {
before(TestHelper.disableMetrics)
after(TestHelper.enableMetrics)
describe('exceptions', () => {
describe('invalid-couponid', () => {
it('missing posted couponid', async () => {
const administrator = await TestHelper.createOwner()
const req = TestHelper.createRequest('/api/administrator/subscriptions/create-coupon')
req.account = administrator.account
req.session = administrator.session
req.body = {
couponid: '',
name: 'my coupon',
amount_off: '10',
percent_off: ''
}
let errorMessage
try {
await req.post()
} catch (error) {
errorMessage = error.message
}
assert.strictEqual(errorMessage, 'invalid-couponid')
})
it('invalid posted couponid is not alphanumeric', async () => {
const administrator = await TestHelper.createOwner()
const req = TestHelper.createRequest('/api/administrator/subscriptions/create-coupon')
req.account = administrator.account
req.session = administrator.session
req.body = {
couponid: '#$%@#$%@#$%',
name: 'my coupon',
amount_off: '10',
percent_off: ''
}
let errorMessage
try {
await req.post()
} catch (error) {
errorMessage = error.message
}
assert.strictEqual(errorMessage, 'invalid-couponid')
})
})
describe('duplicate-couponid', () => {
it('invalid posted couponid is already used', async () => {
const administrator = await TestHelper.createOwner()
await TestHelper.createCoupon(administrator, {
couponid: 'CUSTOM1',
name: 'my coupon',
percent_off: '10',
duration: 'once'
})
const req = TestHelper.createRequest('/api/administrator/subscriptions/create-coupon')
req.account = administrator.account
req.session = administrator.session
req.body = {
couponid: 'CUSTOM1',
name: 'my coupon',
percent_off: '10',
duration: 'once'
}
let errorMessage
try {
await req.post()
} catch (error) {
errorMessage = error.message
}
assert.strictEqual(errorMessage, 'duplicate-couponid')
})
})
describe('invalid-name', () => {
it('missing posted name', async () => {
const administrator = await TestHelper.createOwner()
const req = TestHelper.createRequest('/api/administrator/subscriptions/create-coupon')
req.account = administrator.account
req.session = administrator.session
req.body = {
couponid: 'CUSTOM18',
name: '',
amount_off: '10',
currency: 'usd',
duration: 'once'
}
let errorMessage
try {
await req.post()
} catch (error) {
errorMessage = error.message
}
assert.strictEqual(errorMessage, 'invalid-name')
})
})
describe('invalid-amount_off', () => {
it('missing posted amount_off', async () => {
const administrator = await TestHelper.createOwner()
const req = TestHelper.createRequest('/api/administrator/subscriptions/create-coupon')
req.account = administrator.account
req.session = administrator.session
req.body = {
couponid: 'CUSTOM2',
name: 'my coupon',
amount_off: '',
percent_off: ''
}
let errorMessage
try {
await req.post()
} catch (error) {
errorMessage = error.message
}
assert.strictEqual(errorMessage, 'invalid-amount_off')
})
it('invalid posted amount_off', async () => {
const administrator = await TestHelper.createOwner()
const req = TestHelper.createRequest('/api/administrator/subscriptions/create-coupon')
req.account = administrator.account
req.session = administrator.session
req.body = {
couponid: 'CUSTOM3',
name: 'my coupon',
amount_off: 'invalid',
percent_off: ''
}
let errorMessage
try {
await req.post()
} catch (error) {
errorMessage = error.message
}
assert.strictEqual(errorMessage, 'invalid-amount_off')
})
})
describe('invalid-currency', () => {
it('missing posted currency', async () => {
const administrator = await TestHelper.createOwner()
const req = TestHelper.createRequest('/api/administrator/subscriptions/create-coupon')
req.account = administrator.account
req.session = administrator.session
req.body = {
couponid: 'CUSTOM4',
name: 'my coupon',
amount_off: '1',
currency: ''
}
let errorMessage
try {
await req.post()
} catch (error) {
errorMessage = error.message
}
assert.strictEqual(errorMessage, 'invalid-currency')
})
it('invalid posted currency', async () => {
const administrator = await TestHelper.createOwner()
const req = TestHelper.createRequest('/api/administrator/subscriptions/create-coupon')
req.account = administrator.account
req.session = administrator.session
req.body = {
couponid: 'CUSTOM5',
name: 'my coupon',
amount_off: '1',
currency: 'invalid'
}
let errorMessage
try {
await req.post()
} catch (error) {
errorMessage = error.message
}
assert.strictEqual(errorMessage, 'invalid-currency')
})
})
describe('invalid-percent_off', () => {
it('invalid posted percent_off', async () => {
const administrator = await TestHelper.createOwner()
const req = TestHelper.createRequest('/api/administrator/subscriptions/create-coupon')
req.account = administrator.account
req.session = administrator.session
req.body = {
couponid: 'CUSTOM6',
name: 'my coupon',
percent_off: 'invalid'
}
let errorMessage
try {
await req.post()
} catch (error) {
errorMessage = error.message
}
assert.strictEqual(errorMessage, 'invalid-percent_off')
req.body = {
couponid: 'CUSTOM7',
name: 'my coupon',
percent_off: '101'
}
errorMessage = null
try {
await req.post()
} catch (error) {
errorMessage = error.message
}
assert.strictEqual(errorMessage, 'invalid-percent_off')
})
})
describe('invalid-duration', () => {
it('invalid posted duration', async () => {
const administrator = await TestHelper.createOwner()
const req = TestHelper.createRequest('/api/administrator/subscriptions/create-coupon')
req.account = administrator.account
req.session = administrator.session
req.body = {
couponid: 'CUSTOM8',
name: 'my coupon',
amount_off: '10',
currency: 'usd',
duration: 'invalid'
}
let errorMessage
try {
await req.post()
} catch (error) {
errorMessage = error.message
}
assert.strictEqual(errorMessage, 'invalid-duration')
})
})
describe('invalid-duration_in_months', () => {
it('missing posted duration_in_months', async () => {
const administrator = await TestHelper.createOwner()
const req = TestHelper.createRequest('/api/administrator/subscriptions/create-coupon')
req.account = administrator.account
req.session = administrator.session
req.body = {
couponid: 'CUSTOM9',
name: 'my coupon',
amount_off: '10',
currency: 'usd',
duration: 'repeating',
duration_in_months: ''
}
let errorMessage
try {
await req.post()
} catch (error) {
errorMessage = error.message
}
assert.strictEqual(errorMessage, 'invalid-duration_in_months')
})
it('invalid posted duration_in_months', async () => {
const administrator = await TestHelper.createOwner()
const req = TestHelper.createRequest('/api/administrator/subscriptions/create-coupon')
req.account = administrator.account
req.session = administrator.session
req.body = {
couponid: 'CUSTOM10',
name: 'my coupon',
amount_off: '10',
currency: 'usd',
duration: 'repeating',
duration_in_months: 'invalid'
}
let errorMessage
try {
await req.post()
} catch (error) {
errorMessage = error.message
}
assert.strictEqual(errorMessage, 'invalid-duration_in_months')
})
})
describe('invalid-redeem_by', () => {
it('invalid posted redeem_by', async () => {
const administrator = await TestHelper.createOwner()
const now = new Date()
const lastYear = new Date(now.getFullYear() - 1, 1, 12, 47, 33)
const req = TestHelper.createRequest('/api/administrator/subscriptions/create-coupon')
req.account = administrator.account
req.session = administrator.session
req.body = {
couponid: 'CUSTOM11',
name: 'my coupon',
amount_off: '10',
currency: 'usd',
duration: 'repeating',
duration_in_months: '1',
redeem_by: lastYear.toISOString()
}
let errorMessage
try {
await req.post()
} catch (error) {
errorMessage = error.message
}
assert.strictEqual(errorMessage, 'invalid-redeem_by')
})
})
})
describe('receives', () => {
it('required posted duration', async () => {
const administrator = await TestHelper.createOwner()
const req = TestHelper.createRequest('/api/administrator/subscriptions/create-coupon')
req.account = administrator.account
req.session = administrator.session
req.body = {
couponid: 'CUSTOM12',
name: 'my coupon',
amount_off: '10',
currency: 'usd',
duration: 'repeating',
duration_in_months: '8'
}
req.filename = __filename
req.saveResponse = true
const coupon = await req.post()
assert.strictEqual(coupon.stripeObject.duration, 'repeating')
})
it('optionally-required posted amount_off (integer, or percent_off)', async () => {
const administrator = await TestHelper.createOwner()
const req = TestHelper.createRequest('/api/administrator/subscriptions/create-coupon')
req.account = administrator.account
req.session = administrator.session
req.body = {
couponid: 'CUSTOM13',
name: 'my coupon',
amount_off: '10',
currency: 'usd',
duration: 'once'
}
req.filename = __filename
req.saveResponse = true
const coupon = await req.post()
assert.strictEqual(coupon.stripeObject.amount_off, 10)
})
it('optionally-required posted currency (string, if amount_off)', async () => {
const administrator = await TestHelper.createOwner()
const req = TestHelper.createRequest('/api/administrator/subscriptions/create-coupon')
req.account = administrator.account
req.session = administrator.session
req.body = {
couponid: 'CUSTOM14',
name: 'my coupon',
amount_off: '10',
currency: 'aud',
duration: 'once'
}
req.filename = __filename
req.saveResponse = true
const coupon = await req.post()
assert.strictEqual(coupon.stripeObject.currency, 'aud')
})
it('optionally-required posted percent_off (integer, or amount_off)', async () => {
const administrator = await TestHelper.createOwner()
const req = TestHelper.createRequest('/api/administrator/subscriptions/create-coupon')
req.account = administrator.account
req.session = administrator.session
req.body = {
couponid: 'CUSTOM15',
name: 'my coupon',
percent_off: '10',
currency: 'usd',
duration: 'once'
}
req.filename = __filename
req.saveResponse = true
const coupon = await req.post()
assert.strictEqual(coupon.stripeObject.percent_off, 10)
})
it('optionally-required posted redeem_by (date in future)', async () => {
const administrator = await TestHelper.createOwner()
const now = new Date()
const date = new Date(now.getFullYear() + 1, 1, 1, 12, 47, 33)
const timestamp = Math.floor(date.getTime() / 1000)
const req = TestHelper.createRequest('/api/administrator/subscriptions/create-coupon')
req.account = administrator.account
req.session = administrator.session
req.body = {
couponid: 'CUSTOM16',
name: 'my coupon',
percent_off: '10',
currency: 'usd',
duration: 'once',
redeem_by: date.toISOString()
}
req.filename = __filename
req.saveResponse = true
const coupon = await req.post()
assert.strictEqual(coupon.stripeObject.redeem_by, timestamp)
})
it('optionally-required posted duration_in_months', async () => {
const administrator = await TestHelper.createOwner()
const req = TestHelper.createRequest('/api/administrator/subscriptions/create-coupon')
req.account = administrator.account
req.session = administrator.session
req.body = {
couponid: 'CUSTOM17',
name: 'my coupon',
percent_off: '10',
currency: 'usd',
duration: 'repeating',
duration_in_months: '6'
}
req.filename = __filename
req.saveResponse = true
const coupon = await req.post()
assert.strictEqual(coupon.stripeObject.duration_in_months, 6)
})
})
describe('returns', () => {
it('object', async () => {
const administrator = await TestHelper.createOwner()
const req = TestHelper.createRequest('/api/administrator/subscriptions/create-coupon')
req.account = administrator.account
req.session = administrator.session
req.body = {
couponid: 'CUSTOM18',
name: 'my coupon',
amount_off: '10',
currency: 'usd',
duration: 'once'
}
req.filename = __filename
req.saveResponse = true
const coupon = await req.post()
assert.strictEqual(coupon.object, 'coupon')
})
})
})