Update prepaids to support multiple redeemers for a single code

Will remove deprecated properties after updating production.
This commit is contained in:
Matt Lott 2015-08-12 15:51:16 -07:00
parent 49dc71a157
commit 9c39bf7656
9 changed files with 144 additions and 58 deletions

View file

@ -1,12 +1,15 @@
c = require './../schemas'
PrepaidSchema = c.object({title: 'Prepaid', required: ['creator', 'redeemer', 'type']}, {
PrepaidSchema = c.object({title: 'Prepaid', required: ['creator', 'type']}, {
creator: c.objectId(links: [ {rel: 'extra', href: '/db/user/{($)}'} ])
redeemer: c.objectId(links: [ {rel: 'extra', href: '/db/user/{($)}'} ])
redeemers: c.array {}, c.objectId(links: [ {rel: 'extra', href: '/db/user/{($)}'} ])
maxRedeemers: { type: 'integer'}
code: c.shortString(title: "Unique code to redeem")
type: { type: 'string' }
status: { enum: ['active', 'used'], default: 'active' }
properties: {type: 'object'}
# Deprecated
status: { enum: ['active', 'used'], default: 'active' }
redeemer: c.objectId(links: [ {rel: 'extra', href: '/db/user/{($)}'} ])
})
c.extendBasicProperties(PrepaidSchema, 'prepaid')

View file

@ -77,7 +77,7 @@ module.exports = class MainAdminView extends RootView
return unless me.isAdmin()
options =
url: '/db/prepaid/-/create'
data: {type: 'subscription'}
data: {type: 'subscription', maxRedeemers: 1}
method: 'POST'
options.success = (model, response, options) =>
# TODO: Don't hardcode domain.

View file

@ -22,7 +22,7 @@ function createPrepaid()
criteria = {
creator: creatorID,
type: 'subscription',
status: 'active',
maxRedeemers: 1
code: code,
properties: {
couponID: 'free'

View file

@ -174,16 +174,51 @@ class SubscriptionHandler extends Handler
return done({res: 'Database error.', code: 500})
return done({res: 'Prepaid not found', code: 404}) unless prepaid?
return done({res: 'Prepaid not for subscription', code: 403}) unless prepaid.get('type') is 'subscription'
return done({res: 'Prepaid has already been used', code: 403}) unless prepaid.get('status') is 'active'
return done({res: 'Database error.', code: 500}) unless prepaid.get('properties')?.couponID
couponID = prepaid.get('properties').couponID
# Update user
stripeInfo = _.cloneDeep(user.get('stripe') ? {})
stripeInfo.couponID = couponID
stripeInfo.prepaidCode = req.body.stripe.prepaidCode
user.set('stripe', stripeInfo)
@checkForExistingSubscription(req, user, customer, couponID, done)
# Deprecated: status property
if status = prepaid.get('status') and status is 'used'
return done({res: 'Prepaid has already been used', code: 403})
if prepaid.get('redeemers')?.length >= prepaid.get('maxRedeemers')
@logSubscriptionError(user, "Prepaid #{prepaid.id} note active")
return done({res: 'Prepaid not active', code: 403})
unless couponID = prepaid.get('properties')?.couponID
@logSubscriptionError(user, "Prepaid #{prepaid.id} has no couponID")
return done({res: 'Database error.', code: 500})
redeemers = prepaid.get('redeemers') ? []
if _.find(redeemers, (a) -> a.userID?.equals(user.get('_id')))
@logSubscriptionError(user, "Prepaid code already redeemed by #{user.id}")
return done({res: 'Prepaid code already redeemed', code: 403})
# Redeem prepaid code
# Deprecated: status and redeemer properties
prepaid.set('status', 'used')
prepaid.set('redeemer', user.get('_id'))
query = Prepaid.$where("'#{prepaid.get('_id').valueOf()}' === this._id.valueOf() && (!this.redeemers || this.redeemers.length < this.maxRedeemers)")
redeemers.push
userID: user.get('_id')
date: new Date()
update = {redeemers: redeemers}
Prepaid.update query, update, {}, (err, numAffected) =>
if err
@logSubscriptionError(user, 'Prepaid update error. ' + err)
return done({res: 'Database error.', code: 500})
if numAffected > 1
@logSubscriptionError(user, "Prepaid numAffected=#{numAffected} error.")
return done({res: 'Database error.', code: 500})
if numAffected < 1
return done({res: 'Prepaid not active', code: 403})
# Update user
stripeInfo = _.cloneDeep(user.get('stripe') ? {})
stripeInfo.couponID = couponID
stripeInfo.prepaidCode = req.body.stripe.prepaidCode
user.set('stripe', stripeInfo)
@checkForExistingSubscription(req, user, customer, couponID, done)
else
couponID = user.get('stripe')?.couponID
# SALE LOGIC
@ -257,25 +292,7 @@ class SubscriptionHandler extends Handler
if err
@logSubscriptionError(user, 'Stripe user plan saving error. ' + err)
return done({res: 'Database error.', code: 500})
if stripeInfo.prepaidCode?
# Update prepaid to 'used'
Prepaid.findOne code: stripeInfo.prepaidCode, (err, prepaid) =>
if err
@logSubscriptionError(user, 'Prepaid find error. ' + err)
return done({res: 'Database error.', code: 500})
unless prepaid?
@logSubscriptionError(user, "Expected prepaid not found: #{stripeInfo.prepaidCode}")
return done({res: 'Database error.', code: 500})
prepaid.set('status', 'used')
prepaid.set('redeemer', user.get('_id'))
prepaid.save (err) =>
if err
@logSubscriptionError(user, 'Prepaid update error. ' + err)
return done({res: 'Database error.', code: 500})
done()
else
done()
done()
updateStripeRecipientSubscriptions: (req, user, customer, done) ->
return done({res: 'Database error.', code: 500}) unless req.body.stripe?.subscribeEmails?

View file

@ -20,13 +20,14 @@ PrepaidHandler = class PrepaidHandler extends Handler
createPrepaid: (req, res) ->
return @sendForbiddenError(res) unless @hasAccess(req)
return @sendForbiddenError(res) unless req.body.type is 'subscription'
return @sendForbiddenError(res) unless req.body.maxRedeemers > 0
Prepaid.generateNewCode (code) =>
return @sendDatabaseError(res, 'Database error.') unless code
prepaid = new Prepaid
creator: req.user.id
type: req.body.type
status: 'active'
code: code
maxRedeemers: req.body.maxRedeemers
properties:
couponID: 'free'
prepaid.save (err) =>

View file

@ -17,7 +17,7 @@ TrialRequestSchema.pre 'save', (next) ->
prepaid = new Prepaid
creator: @get('reviewer')
type: 'subscription'
status: 'active'
maxRedeemers: 1
code: code
properties:
couponID: 'free'

View file

@ -118,9 +118,11 @@ wrapUpGetUser = (email, user, done) ->
GLOBAL.getURL = (path) ->
return 'http://localhost:3001' + path
GLOBAL.createPrepaid = (type, done) ->
GLOBAL.createPrepaid = (type, maxRedeemers, done) ->
options = uri: GLOBAL.getURL('/db/prepaid/-/create')
options.json = type: type if type?
options.json =
type: type
maxRedeemers: maxRedeemers
request.post options, done
newUserCount = 0

View file

@ -7,7 +7,7 @@ describe '/db/prepaid', ->
verifyPrepaid = (user, prepaid, done) ->
expect(prepaid.creator).toEqual(user.id)
expect(prepaid.type).toEqual('subscription')
expect(prepaid.status).toEqual('active')
expect(prepaid.maxRedeemers).toBeGreaterThan(0)
expect(prepaid.code).toMatch(/^\w{8}$/)
expect(prepaid.properties?.couponID).toEqual('free')
done()
@ -18,7 +18,7 @@ describe '/db/prepaid', ->
done()
it 'Anonymous creates prepaid code', (done) ->
createPrepaid 'subscription', (err, res, body) ->
createPrepaid 'subscription', 1, (err, res, body) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(401)
done()
@ -26,7 +26,7 @@ describe '/db/prepaid', ->
it 'Non-admin creates prepaid code', (done) ->
loginNewUser (user1) ->
expect(user1.isAdmin()).toEqual(false)
createPrepaid 'subscription', (err, res, body) ->
createPrepaid 'subscription', 4, (err, res, body) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(403)
done()
@ -37,7 +37,7 @@ describe '/db/prepaid', ->
user1.save (err, user1) ->
expect(err).toBeNull()
expect(user1.isAdmin()).toEqual(true)
createPrepaid 'subscription', (err, res, body) ->
createPrepaid 'subscription', 1, (err, res, body) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(200)
verifyPrepaid user1, body, done
@ -48,7 +48,7 @@ describe '/db/prepaid', ->
user1.save (err, user1) ->
expect(err).toBeNull()
expect(user1.isAdmin()).toEqual(true)
createPrepaid 'bulldozer', (err, res, body) ->
createPrepaid 'bulldozer', 1, (err, res, body) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(403)
done()
@ -59,7 +59,18 @@ describe '/db/prepaid', ->
user1.save (err, user1) ->
expect(err).toBeNull()
expect(user1.isAdmin()).toEqual(true)
createPrepaid null, (err, res, body) ->
createPrepaid null, 1, (err, res, body) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(403)
done()
it 'Admin creates prepaid code with invalid maxRedeemers', (done) ->
loginNewUser (user1) ->
user1.set('permissions', ['admin'])
user1.save (err, user1) ->
expect(err).toBeNull()
expect(user1.isAdmin()).toEqual(true)
createPrepaid 'subscription', 0, (err, res, body) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(403)
done()
@ -78,7 +89,7 @@ describe '/db/prepaid', ->
user1.save (err, user1) ->
expect(err).toBeNull()
expect(user1.isAdmin()).toEqual(true)
createPrepaid 'subscription', (err, res, prepaid) ->
createPrepaid 'subscription', 1, (err, res, prepaid) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(200)
request.get {uri: prepaidURL}, (err, res, body) ->

View file

@ -542,14 +542,38 @@ describe 'Subscriptions', ->
user1.save (err, user1) ->
expect(err).toBeNull()
expect(user1.isAdmin()).toEqual(true)
createPrepaid 'subscription', (err, res, prepaid) ->
createPrepaid 'subscription', 1, (err, res, prepaid) ->
expect(err).toBeNull()
subscribeUser user1, null, prepaid.code, ->
Prepaid.findById prepaid._id, (err, prepaid) ->
expect(err).toBeNull()
expect(prepaid.get('status')).toEqual('used')
expect(prepaid.get('maxRedeemers')).toEqual(1)
expect(prepaid.get('redeemers')[0].userID).toEqual(user1.get('_id'))
expect(prepaid.get('redeemers')[0].date).toBeLessThan(new Date())
done()
it 'Admin subscribes self with valid prepaid twice', (done) ->
loginNewUser (user1) ->
user1.set('permissions', ['admin'])
user1.save (err, user1) ->
expect(err).toBeNull()
expect(user1.isAdmin()).toEqual(true)
createPrepaid 'subscription', 2, (err, res, prepaid) ->
expect(err).toBeNull()
Prepaid.findById prepaid._id, (err, prepaid) ->
expect(err).toBeNull()
prepaid.set 'redeemers', [{userID: user1.get('_id'), date: new Date()}]
prepaid.save (err) ->
expect(err).toBeNull()
requestBody = user1.toObject()
requestBody.stripe =
planID: 'basic'
requestBody.stripe.prepaidCode = prepaid.get('code')
request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, body) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(403)
done()
it 'User subscribes, deletes themselves, subscription ends', (done) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
@ -596,7 +620,7 @@ describe 'Subscriptions', ->
user1.save (err, user1) ->
expect(err).toBeNull()
expect(user1.isAdmin()).toEqual(true)
createPrepaid 'subscription', (err, res, prepaid) ->
createPrepaid 'subscription', 1, (err, res, prepaid) ->
expect(err).toBeNull()
subscribeUser user1, null, prepaid.code, ->
loginNewUser (user2) ->
@ -607,7 +631,27 @@ describe 'Subscriptions', ->
request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, body) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(403)
done()
Prepaid.findById prepaid._id, (err, prepaid) ->
expect(err).toBeNull()
expect(prepaid.get('redeemers')[0].userID).toEqual(user1.get('_id'))
expect(prepaid.get('redeemers')[0].date).toBeLessThan(new Date())
done()
it 'User2 subscribes with same active prepaid', (done) ->
loginNewUser (user1) ->
user1.set('permissions', ['admin'])
user1.save (err, user1) ->
expect(err).toBeNull()
expect(user1.isAdmin()).toEqual(true)
createPrepaid 'subscription', 2, (err, res, prepaid) ->
expect(err).toBeNull()
subscribeUser user1, null, prepaid.code, ->
loginNewUser (user2) ->
subscribeUser user2, null, prepaid.code, ->
Prepaid.findById prepaid._id, (err, prepaid) ->
expect(err).toBeNull()
expect(prepaid.get('redeemers').length).toEqual(2)
done()
it 'Subscribe normally, subscribe with valid prepaid', (done) ->
stripe.tokens.create {
@ -621,12 +665,14 @@ describe 'Subscriptions', ->
subscribeUser user1, token, null, ->
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
createPrepaid 'subscription', (err, res, prepaid) ->
createPrepaid 'subscription', 1, (err, res, prepaid) ->
expect(err).toBeNull()
subscribeUser user1, null, prepaid.code, ->
Prepaid.findById prepaid._id, (err, prepaid) ->
expect(err).toBeNull()
expect(prepaid.get('status')).toEqual('used')
expect(prepaid.get('maxRedeemers')).toEqual(1)
expect(prepaid.get('redeemers')[0].userID).toEqual(user1.get('_id'))
expect(prepaid.get('redeemers')[0].date).toBeLessThan(new Date())
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
customerID = user1.get('stripe').customerID
@ -652,11 +698,13 @@ describe 'Subscriptions', ->
request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, updatedUser) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(200)
createPrepaid 'subscription', (err, res, prepaid) ->
createPrepaid 'subscription', 1, (err, res, prepaid) ->
subscribeUser user1, null, prepaid.code, ->
Prepaid.findById prepaid._id, (err, prepaid) ->
expect(err).toBeNull()
expect(prepaid.get('status')).toEqual('used')
expect(prepaid.get('maxRedeemers')).toEqual(1)
expect(prepaid.get('redeemers')[0].userID).toEqual(user1.get('_id'))
expect(prepaid.get('redeemers')[0].date).toBeLessThan(new Date())
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
customerID = user1.get('stripe').customerID
@ -673,12 +721,14 @@ describe 'Subscriptions', ->
user1.save (err, user1) ->
expect(err).toBeNull()
expect(user1.isAdmin()).toEqual(true)
createPrepaid 'subscription', (err, res, prepaid) ->
createPrepaid 'subscription', 1, (err, res, prepaid) ->
expect(err).toBeNull()
subscribeUser user1, null, prepaid.code, ->
Prepaid.findById prepaid._id, (err, prepaid) ->
expect(err).toBeNull()
expect(prepaid.get('status')).toEqual('used')
expect(prepaid.get('maxRedeemers')).toEqual(1)
expect(prepaid.get('redeemers')[0].userID).toEqual(user1.get('_id'))
expect(prepaid.get('redeemers')[0].date).toBeLessThan(new Date())
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
unsubscribeUser user1, ->
@ -693,12 +743,14 @@ describe 'Subscriptions', ->
user1.save (err, user1) ->
expect(err).toBeNull()
expect(user1.isAdmin()).toEqual(true)
createPrepaid 'subscription', (err, res, prepaid) ->
createPrepaid 'subscription', 1, (err, res, prepaid) ->
expect(err).toBeNull()
subscribeUser user1, null, prepaid.code, ->
Prepaid.findById prepaid._id, (err, prepaid) ->
expect(err).toBeNull()
expect(prepaid.get('status')).toEqual('used')
expect(prepaid.get('maxRedeemers')).toEqual(1)
expect(prepaid.get('redeemers')[0].userID).toEqual(user1.get('_id'))
expect(prepaid.get('redeemers')[0].date).toBeLessThan(new Date())
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
unsubscribeUser user1, ->
@ -931,7 +983,7 @@ describe 'Subscriptions', ->
user2.save (err, user1) ->
expect(err).toBeNull()
expect(user2.isAdmin()).toEqual(true)
createPrepaid 'subscription', (err, res, prepaid) ->
createPrepaid 'subscription', 1, (err, res, prepaid) ->
expect(err).toBeNull()
requestBody = user2.toObject()
requestBody.stripe =
@ -1103,7 +1155,7 @@ describe 'Subscriptions', ->
user1.save (err, user1) ->
expect(err).toBeNull()
expect(user1.isAdmin()).toEqual(true)
createPrepaid 'subscription', (err, res, prepaid) ->
createPrepaid 'subscription', 1, (err, res, prepaid) ->
expect(err).toBeNull()
subscribeUser user1, null, prepaid.code, ->
stripe.tokens.create {