codecombat/spec/server/functional/subscription.spec.coffee
phoenixeliot bb6262483f Allow username-only signup for classroom users
Address some code review feedback

Correct error code in test

Don't try to send emails to empty addresses

Add tests for subscriptions

Add tests for Next Steps email

Fix specs

Add reason for disabled test
2016-07-18 10:41:17 -07:00

1751 lines
85 KiB
CoffeeScript

async = require 'async'
config = require '../../../server_config'
require '../common'
appUtils = require '../../../app/core/utils' # Must come after require /common
utils = require '../utils'
mongoose = require 'mongoose'
TRAVIS = process.env.COCO_TRAVIS_TEST
nockUtils = require '../nock-utils'
User = require '../../../server/models/User'
Payment = require '../../../server/models/Payment'
Prepaid = require '../../../server/models/Prepaid'
request = require '../request'
subPrice = 100
subGems = 3500
subGemsBrazil = 1500
# sample data that comes in through the webhook when you subscribe
invoiceChargeSampleEvent = {
id: 'evt_155TBeKaReE7xLUdrKM72O5R',
created: 1417574898,
livemode: false,
type: 'invoice.payment_succeeded',
data: {
object: {
date: 1417574897,
id: 'in_155TBdKaReE7xLUdv8z8ipWl',
period_start: 1417574897,
period_end: 1417574897,
lines: {},
subtotal: 100,
total: 100,
customer: 'cus_5Fz9MVWP2bDPGV',
object: 'invoice',
attempted: true,
closed: true,
forgiven: false,
paid: true,
livemode: false,
attempt_count: 1,
amount_due: 100,
currency: 'usd',
starting_balance: 0,
ending_balance: 0,
next_payment_attempt: null,
webhooks_delivered_at: null,
charge: 'ch_155TBdKaReE7xLUdRU0WcMzR',
discount: null,
application_fee: null,
subscription: 'sub_5Fz99gXrBtreNe',
metadata: {},
statement_description: null,
description: null,
receipt_number: null
}
},
object: 'event',
pending_webhooks: 1,
request: 'iar_5Fz9c4BZJyNNsM',
api_version: '2015-02-18'
}
customerSubscriptionDeletedSampleEvent = {
id: 'evt_155Tj4KaReE7xLUdpoMx0UaA',
created: 1417576970,
livemode: false,
type: 'customer.subscription.deleted',
data: {
object: {
id: 'sub_5FziOkege03vT7',
plan: [Object],
object: 'subscription',
start: 1417576967,
status: 'canceled',
customer: 'cus_5Fzi54gMvGG9Px',
cancel_at_period_end: true,
current_period_start: 1417576967,
current_period_end: 1420255367,
ended_at: 1417576970,
trial_start: null,
trial_end: null,
canceled_at: 1417576970,
quantity: 1,
application_fee_percent: null,
discount: null,
metadata: {}
}
},
object: 'event',
pending_webhooks: 1,
request: 'iar_5FziYQJ4oQdL6w',
api_version: '2015-02-18'
}
describe '/db/user, editing stripe property', ->
afterEach nockUtils.teardownNock
stripe = require('stripe')(config.stripe.secretKey)
userURL = getURL('/db/user')
webhookURL = getURL('/stripe/webhook')
headers = {'X-Change-Plan': 'true'}
it 'clears the db first', (done) ->
clearModels [User, Payment], (err) ->
throw err if err
resetUserIDCounter(100000) # because fixtures depend on dependable user ids
done()
it 'denies anonymous users trying to subscribe', (done) ->
request.get getURL('/auth/whoami'), (err, res, body) ->
body = JSON.parse(body)
body.stripe = { planID: 'basic', token: '12345' }
request.put {uri: userURL, json: body, headers: headers}, (err, res, body) ->
expect(res.statusCode).toBe 403
done()
it 'denies username-only users trying to subscribe', utils.wrap (done) ->
user = yield utils.initUser({ email: undefined, })
yield utils.loginUser(user)
[res, body] = yield request.putAsync(getURL("/db/user/#{user.id}"), { headers, json: { stripe: { planID: 'basic', token: '12345' } } })
expect(res.statusCode).toBe(403)
done()
#- shared data between tests
joeData = null
firstSubscriptionID = null
it 'returns client error when a token fails to charge', (done) ->
nockUtils.setupNock 'db-user-sub-test-1.json', (err, nockDone) ->
stripe.tokens.create {
card: { number: '4000000000000002', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
stripeTokenID = token.id
loginJoe (joe) ->
joeData = joe.toObject()
joeData.stripe = {
token: stripeTokenID
planID: 'basic'
}
request.put {uri: userURL, json: joeData, headers: headers }, (err, res, body) ->
expect(res.statusCode).toBe(402)
nockDone()
done()
it 'creates a subscription when you put a token and plan', (done) ->
nockUtils.setupNock 'db-user-sub-test-2.json', (err, nockDone) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
stripeTokenID = token.id
loginJoe (joe) ->
joeData = joe.toObject()
joeData.stripe = {
token: stripeTokenID
planID: 'basic'
}
request.put {uri: userURL, json: joeData, headers: headers }, (err, res, body) ->
joeData = body
expect(res.statusCode).toBe(200)
expect(joeData.purchased.gems).toBe(subGems)
expect(joeData.stripe.customerID).toBeDefined()
expect(firstSubscriptionID = joeData.stripe.subscriptionID).toBeDefined()
expect(joeData.stripe.planID).toBe('basic')
expect(joeData.stripe.token).toBeUndefined()
nockDone()
done()
it 'records a payment through the webhook', (done) ->
nockUtils.setupNock 'db-user-sub-test-3.json', (err, nockDone) ->
# Don't even want to think about hooking in tests to webhooks, so... put in some data manually
stripe.invoices.list {customer: joeData.stripe.customerID}, (err, invoices) ->
expect(invoices.data.length).toBe(1)
event = _.cloneDeep(invoiceChargeSampleEvent)
event.data.object = invoices.data[0]
request.post {uri: webhookURL, json: event}, (err, res, body) ->
expect(res.statusCode).toBe(201)
Payment.find {}, (err, payments) ->
expect(payments.length).toBe(1)
User.findById joeData._id, (err, user) ->
expect(user.get('purchased').gems).toBe(subGems)
nockDone()
done()
it 'schedules the stripe subscription to be cancelled when stripe.planID is removed from the user', (done) ->
nockUtils.setupNock 'db-user-sub-test-4.json', {keep: {cancel_at_period_end: true}}, (err, nockDone) ->
delete joeData.stripe.planID
request.put {uri: userURL, json: joeData, headers: headers }, (err, res, body) ->
joeData = body
expect(res.statusCode).toBe(200)
expect(joeData.stripe.subscriptionID).toBeDefined()
expect(joeData.stripe.planID).toBeUndefined()
expect(joeData.stripe.customerID).toBeDefined()
stripe.customers.retrieve joeData.stripe.customerID, (err, customer) ->
expect(customer.subscriptions.data.length).toBe(1)
expect(customer.subscriptions.data[0].cancel_at_period_end).toBe(true)
nockDone()
done()
it 'allows you to sign up again using the same customer ID as before, no token necessary', (done) ->
nockUtils.setupNock 'db-user-sub-test-5.json', (err, nockDone) ->
joeData.stripe.planID = 'basic'
request.put {uri: userURL, json: joeData, headers: headers }, (err, res, body) ->
joeData = body
expect(res.statusCode).toBe(200)
expect(joeData.stripe.customerID).toBeDefined()
expect(joeData.stripe.subscriptionID).toBeDefined()
expect(joeData.stripe.subscriptionID).not.toBe(firstSubscriptionID)
expect(joeData.stripe.planID).toBe('basic')
nockDone()
done()
it 'will not have immediately created new payments when signing back up from a cancelled subscription', (done) ->
nockUtils.setupNock 'db-user-sub-test-6.json', (err, nockDone) ->
stripe.invoices.list {customer: joeData.stripe.customerID}, (err, invoices) ->
expect(invoices.data.length).toBe(2)
expect(invoices.data[0].total).toBe(0)
event = _.cloneDeep(invoiceChargeSampleEvent)
event.data.object = invoices.data[0]
request.post {uri: webhookURL, json: event}, (err, res, body) ->
expect(res.statusCode).toBe(200)
Payment.find {}, (err, payments) ->
expect(payments.length).toBe(1)
User.findById joeData._id, (err, user) ->
expect(user.get('purchased').gems).toBe(subGems)
nockDone()
done()
it 'deletes the subscription from the user object when an event about it comes through the webhook', (done) ->
nockUtils.setupNock 'db-user-sub-test-7.json', (err, nockDone) ->
stripe.customers.retrieveSubscription joeData.stripe.customerID, joeData.stripe.subscriptionID, (err, subscription) ->
event = _.cloneDeep(customerSubscriptionDeletedSampleEvent)
event.data.object = subscription
request.post {uri: webhookURL, json: event}, (err, res, body) ->
User.findById joeData._id, (err, user) ->
expect(user.get('purchased').gems).toBe(subGems)
expect(user.get('stripe').subscriptionID).toBeUndefined()
expect(user.get('stripe').planID).toBeUndefined()
nockDone()
done()
it "updates the customer's email when you change the user's email", (done) ->
nockUtils.setupNock 'db-user-sub-test-8.json', {keep: {email: true}}, (err, nockDone) ->
joeData.email = 'newEmail@gmail.com'
request.put {uri: userURL, json: joeData, headers: headers }, (err, res, body) ->
f = -> stripe.customers.retrieve joeData.stripe.customerID, (err, customer) ->
expect(customer.email).toBe('newEmail@gmail.com')
nockDone()
done()
setTimeout(f, 500) # bit of a race condition here, response returns before stripe has been updated
describe 'Subscriptions', ->
# TODO: Test recurring billing via webhooks
# TODO: Test error rollbacks, Stripe is authority
stripe = require('stripe')(config.stripe.secretKey)
userURL = getURL('/db/user')
webhookURL = getURL('/stripe/webhook')
headers = {'X-Change-Plan': 'true'}
invoicesWebHooked = {}
afterEach nockUtils.teardownNock
# Start helpers
getSubscribedQuantity = (numSponsored) ->
return 0 if numSponsored < 1
if numSponsored <= 10
Math.round(numSponsored * subPrice * 0.8)
else
Math.round(10 * subPrice * 0.8 + (numSponsored - 10) * subPrice * 0.6)
getUnsubscribedQuantity = (numSponsored) ->
return 0 if numSponsored < 1
if numSponsored <= 1
subPrice
else if numSponsored <= 11
Math.round(subPrice + (numSponsored - 1) * subPrice * 0.8)
else
Math.round(subPrice + 10 * subPrice * 0.8 + (numSponsored - 11) * subPrice * 0.6)
verifyNotRecipient = (userID, done) ->
User.findById userID, (err, user) ->
expect(err).toBeNull()
if stripeInfo = user.get('stripe')
expect(stripeInfo.sponsorID).toBeUndefined()
done()
verifyNotSponsoring = (sponsorID, recipientID, done) ->
# console.log 'verifyNotSponsoring', sponsorID, recipientID
User.findById sponsorID, (err, sponsor) ->
expect(err).toBeNull()
expect(sponsor).not.toBeNull()
return done() unless sponsor
stripeInfo = sponsor.get('stripe')
return done() unless stripeInfo?.customerID?
checkSubscriptions = (starting_after, done) ->
options = {}
options.starting_after = starting_after if starting_after
stripe.customers.listSubscriptions stripeInfo.customerID, options, (err, subscriptions) ->
expect(err).toBeNull()
for subscription in subscriptions.data
if subscription.plan.id is 'basic'
expect(subscription.metadata.id).not.toEqual(recipientID)
if subscription.plan.id is 'incremental'
expect(subscription.metadata.id).toEqual(sponsorID)
if subscriptions.has_more
checkSubscriptions subscriptions.data[subscriptions.data.length - 1].id, done
else
done()
checkSubscriptions null, done
verifySponsorship = (sponsorUserID, sponsoredUserID, done) ->
# console.log 'verifySponsorship', sponsorUserID, sponsoredUserID
User.findById sponsorUserID, (err, user) ->
expect(err).toBeNull()
expect(user).not.toBeNull()
return done() unless user
sponsorStripe = user.get('stripe')
sponsorCustomerID = sponsorStripe.customerID
numSponsored = sponsorStripe.recipients?.length
expect(sponsorCustomerID).toBeDefined()
expect(sponsorStripe.sponsorSubscriptionID).toBeDefined()
expect(sponsorStripe.token).toBeUndefined()
expect(numSponsored).toBeGreaterThan(0)
# Verify Stripe sponsor subscription data
return done() unless sponsorCustomerID and sponsorStripe.sponsorSubscriptionID
stripe.customers.retrieveSubscription sponsorCustomerID, sponsorStripe.sponsorSubscriptionID, (err, subscription) ->
expect(err).toBeNull()
expect(subscription?).toBe(true)
return done() unless subscription?
expect(subscription.plan.amount).toEqual(1)
expect(subscription.customer).toEqual(sponsorCustomerID)
expect(subscription.quantity).toEqual(appUtils.getSponsoredSubsAmount(subPrice, numSponsored, sponsorStripe.subscriptionID?))
# Verify sponsor payment
# May be greater than expected amount due to multiple subscribes and unsubscribes
paymentQuery =
purchaser: mongoose.Types.ObjectId(sponsorUserID)
recipient: mongoose.Types.ObjectId(sponsorUserID)
"stripe.customerID": sponsorCustomerID
"stripe.subscriptionID": sponsorStripe.sponsorSubscriptionID
expectedAmount = appUtils.getSponsoredSubsAmount(subPrice, numSponsored, sponsorStripe.subscriptionID?)
Payment.find paymentQuery, (err, payments) ->
expect(err).toBeNull()
expect(payments).not.toBeNull()
amount = 0
for payment in payments
amount += payment.get('amount')
expect(payment.get('gems')).toBeUndefined()
# NOTE: this amount may be greater than the expected amount due to proration accumlation
# NOTE: during localy execution, this is usually only 1-2 cents
expect(amount).toBeGreaterThan(expectedAmount - 50)
# Find recipient info from sponsor stripe data
for r in sponsorStripe.recipients
if r.userID is sponsoredUserID
recipientInfo = r
break
expect(recipientInfo).toBeDefined()
expect(recipientInfo.subscriptionID).toBeDefined()
expect(recipientInfo.subscriptionID).not.toEqual(sponsorStripe.sponsorSubscriptionID)
expect(recipientInfo.couponID).toEqual('free')
# Verify Stripe recipient subscription data
return done() unless sponsorCustomerID and recipientInfo.subscriptionID
stripe.customers.retrieveSubscription sponsorCustomerID, recipientInfo.subscriptionID, (err, subscription) ->
expect(err).toBeNull()
expect(subscription.plan.amount).toEqual(subPrice)
expect(subscription.customer).toEqual(sponsorCustomerID)
expect(subscription.quantity).toEqual(1)
expect(subscription.metadata.id).toEqual(sponsoredUserID)
expect(subscription.discount.coupon.id).toEqual(recipientInfo.couponID)
# Verify recipient internal data
User.findById sponsoredUserID, (err, recipient) ->
expect(err).toBeNull()
stripeInfo = recipient.get('stripe')
expect(stripeInfo.sponsorID).toEqual(sponsorUserID)
unless stripeInfo.sponsorSubscriptionID?
expect(stripeInfo.customerID).toBeUndefined()
expect(stripeInfo.token).toBeUndefined()
expect(recipient.get('purchased').gems).toBeGreaterThan(subGems - 1)
expect(recipient.isPremium()).toEqual(true)
# Verify recipient payment
# TODO: Not accurate enough when resubscribing a user
paymentQuery =
purchaser: mongoose.Types.ObjectId(sponsorUserID)
recipient: mongoose.Types.ObjectId(sponsoredUserID)
"stripe.customerID": sponsorCustomerID
Payment.findOne paymentQuery, (err, payment) ->
expect(err).toBeNull()
expect(payment).not.toBeNull()
return done() if payment is null
expect(payment.get('amount')).toEqual(0)
expect(payment.get('gems')).toBeGreaterThan(subGems - 1)
done()
subscribeUser = (user, token, prepaidCode, done) ->
requestBody = user.toObject()
requestBody.stripe =
planID: 'basic'
requestBody.stripe.token = token.id if token?
requestBody.stripe.prepaidCode = prepaidCode if prepaidCode?
request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, body) ->
expect(err).toBeNull()
return done() if err
expect(res.statusCode).toBe(200)
expect(body.stripe).toBeDefined()
return done() unless body.stripe
expect(body.stripe.customerID).toBeDefined()
expect(body.stripe.planID).toBe('basic')
expect(body.stripe.token).toBeUndefined()
if prepaidCode?
expect(body.stripe.prepaidCode).toEqual(prepaidCode)
expect(body.stripe.couponID).toEqual('free')
expect(body.purchased.gems).toBeGreaterThan(subGems - 1)
User.findById user.id, (err, user) ->
stripeInfo = user.get('stripe')
expect(stripeInfo.customerID).toBeDefined()
expect(stripeInfo.planID).toBe('basic')
expect(stripeInfo.token).toBeUndefined()
if prepaidCode?
expect(stripeInfo.prepaidCode).toEqual(prepaidCode)
expect(stripeInfo.couponID).toEqual('free')
expect(user.get('purchased').gems).toBeGreaterThan(subGems - 1)
done()
unsubscribeUser = (user, done) ->
requestBody = user.toObject()
delete requestBody.stripe.planID
delete requestBody.stripe.prepaidCode
request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, body) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(200)
User.findById user.id, (err, user) ->
expect(user.get('stripe').customerID).toBeDefined()
expect(user.get('stripe').planID).toBeUndefined()
expect(user.get('stripe').token).toBeUndefined()
expect(user.get('stripe').subscriptionID).toBeDefined()
return done() unless user.get('stripe').subscriptionID
stripe.customers.retrieveSubscription user.get('stripe').customerID, user.get('stripe').subscriptionID, (err, subscription) ->
expect(err).toBeNull()
expect(subscription).not.toBeNull()
expect(subscription?.cancel_at_period_end).toEqual(true)
done()
subscribeRecipients = (sponsor, recipients, token, done) ->
# console.log 'subscribeRecipients', sponsor.id, (recipient.id for recipient in recipients), token?
requestBody = sponsor.toObject()
requestBody.stripe =
subscribeEmails: (recipient.get('email') for recipient in recipients)
requestBody.stripe.token = token.id if token?
request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, body) ->
expect(err).toBeNull()
return done() if err
expect(res.statusCode).toBe(200)
expect(body.stripe?.customerID).toBeDefined()
updatedUser = body
# Call webhooks for invoices
options = customer: body.stripe?.customerID, limit: 100
stripe.invoices.list options, (err, invoices) ->
expect(err).toBeNull()
expect(invoices).not.toBeNull()
return done(updatedUser) unless invoices?
expect(invoices.has_more).toEqual(false)
makeWebhookCall = (invoice) ->
(callback) ->
event = _.cloneDeep(invoiceChargeSampleEvent)
event.data.object = invoice
# console.log 'Calling webhook', event.type, invoice.id
request.post {uri: webhookURL, json: event}, (err, res, body) ->
callback err
webhookTasks = []
for invoice in invoices.data
unless invoice.id of invoicesWebHooked
invoicesWebHooked[invoice.id] = true
webhookTasks.push makeWebhookCall(invoice)
async.series webhookTasks, (err, results) ->
expect(err?).toEqual(false)
done(updatedUser)
unsubscribeRecipient = (sponsor, recipient, done) ->
# console.log 'unsubscribeRecipient', sponsor.id, recipient.id
stripeInfo = sponsor.get('stripe')
customerID = stripeInfo.customerID
expect(stripeInfo.recipients).toBeDefined()
return done() unless stripeInfo.recipients
for r in stripeInfo.recipients
if r.userID is recipient.id
subscriptionID = r.subscriptionID
break
expect(customerID).toBeDefined()
expect(subscriptionID).toBeDefined()
# Find Stripe subscription
stripe.customers.retrieveSubscription customerID, subscriptionID, (err, subscription) ->
expect(err).toBeNull()
expect(subscription).not.toBeNull()
# Call unsubscribe API
requestBody = sponsor.toObject()
requestBody.stripe = unsubscribeEmail: recipient.get('email')
request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, body) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(200)
done()
# Subscribe a bunch of recipients at once, used for bulk discount testing
class SubbedRecipients
constructor: (@count, @toVerify) ->
@index = 0
@recipients = []
length: ->
@recipients.length
get: (i) ->
@recipients[i]
createRecipients: (done) ->
return done() if @recipients.length is @count
createNewUser (user) =>
@recipients.push user
@createRecipients done
subRecipients: (user1, token=null, done) ->
# console.log 'subRecipients', user1.id, @recipients.length
User.findById user1.id, (err, user1) =>
subscribeRecipients user1, @recipients, token, (updatedUser) =>
verifyIndex = 0
verify = =>
return done(updatedUser) if verifyIndex >= @toVerify.length
verifySponsorship user1.id, @recipients[verifyIndex].id, =>
verifyIndex++
verify()
verify()
# End helpers
# TODO: Use beforeAll()
it 'Clear database users and payments', (done) ->
clearModels [User, Payment], (err) ->
throw err if err
done()
describe 'Personal', ->
it 'Subscribe user with new token', (done) ->
nockUtils.setupNock 'sub-test-01.json', (err, nockDone) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
loginNewUser (user1) ->
subscribeUser user1, token, null, ->
nockDone()
done()
it 'User delete unsubscribes', (done) ->
nockUtils.setupNock 'sub-test-02.json', (err, nockDone) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
loginNewUser (user1) ->
subscribeUser user1, token, null, ->
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
customerID = user1.get('stripe').customerID
subscriptionID = user1.get('stripe').subscriptionID
request.del {uri: "#{userURL}/#{user1.id}"}, (err, res) ->
expect(err).toBeNull()
stripe.customers.retrieveSubscription customerID, subscriptionID, (err, subscription) ->
expect(err).toBeNull()
expect(subscription?.cancel_at_period_end).toEqual(true)
nockDone()
done()
it 'User subscribes, deletes themselves, subscription ends', (done) ->
nockUtils.setupNock 'sub-test-03.json', (err, nockDone) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
loginNewUser (user1) ->
# Subscribe user
subscribeUser user1, token, null, ->
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
customerID = user1.get('stripe').customerID
subscriptionID = user1.get('stripe').subscriptionID
stripe.customers.retrieveSubscription customerID, subscriptionID, (err, subscription) ->
expect(err).toBeNull()
expect(subscription).not.toBeNull()
# Delete user
request.del {uri: "#{userURL}/#{user1.id}"}, (err, res) ->
expect(err).toBeNull()
# Simulate Stripe subscription deleted via webhook
event = _.cloneDeep(customerSubscriptionDeletedSampleEvent)
event.data.object = subscription
request.post {uri: webhookURL, json: event}, (err, res, body) ->
expect(err).toBeNull()
expect(res.statusCode).toEqual(200)
nockDone()
done()
it 'Subscribe with prepaid, then delete', (done) ->
nockUtils.setupNock 'sub-test-04.json', (err, nockDone) ->
loginNewUser (user1) ->
user1.set('permissions', ['admin'])
user1.save (err, user1) ->
expect(err).toBeNull()
expect(user1.isAdmin()).toEqual(true)
createPrepaid 'subscription', 1, 0, (err, res, prepaid) ->
expect(err).toBeNull()
subscribeUser user1, null, prepaid.code, ->
Prepaid.findById prepaid._id, (err, prepaid) ->
expect(err).toBeNull()
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, ->
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
stripeInfo = user1.get('stripe')
expect(stripeInfo.prepaidCode).toEqual(prepaid.get('code'))
expect(stripeInfo.subscriptionID).toBeDefined()
return done() unless stripeInfo.subscriptionID
# Delete subscription
stripe.customers.retrieveSubscription stripeInfo.customerID, stripeInfo.subscriptionID, (err, subscription) ->
expect(err).toBeNull()
event = _.cloneDeep(customerSubscriptionDeletedSampleEvent)
event.data.object = subscription
request.post {uri: webhookURL, json: event}, (err, res, body) ->
expect(err).toBeNull()
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
stripeInfo = user1.get('stripe')
expect(stripeInfo.planID).toBeUndefined()
expect(stripeInfo.prepaidCode).toBeUndefined()
expect(stripeInfo.subscriptionID).toBeUndefined()
nockDone()
done()
it 'Subscribe normally, subscribe with valid prepaid', (done) ->
nockUtils.setupNock 'sub-test-05.json', (err, nockDone) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
loginNewUser (user1) ->
user1.set('permissions', ['admin'])
user1.save (err, user1) ->
expect(err).toBeNull()
expect(user1.isAdmin()).toEqual(true)
subscribeUser user1, token, null, ->
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
createPrepaid 'subscription', 1, 0, (err, res, prepaid) ->
expect(err).toBeNull()
subscribeUser user1, null, prepaid.code, ->
Prepaid.findById prepaid._id, (err, prepaid) ->
expect(err).toBeNull()
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
subscriptionID = user1.get('stripe').subscriptionID
stripe.customers.retrieveSubscription customerID, subscriptionID, (err, subscription) ->
expect(err).toBeNull()
expect(subscription).not.toBeNull()
return done() unless subscription
expect(subscription.discount?.coupon?.id).toEqual('free')
nockDone()
done()
it 'Subscribe with coupon, subscribe with valid prepaid', (done) ->
nockUtils.setupNock 'sub-test-06.json', (err, nockDone) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
loginNewUser (user1) ->
user1.set('permissions', ['admin'])
user1.save (err, user1) ->
requestBody = user1.toObject()
requestBody.stripe =
planID: 'basic'
token: token.id
couponID: '20pct'
request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, updatedUser) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(200)
createPrepaid 'subscription', 1, 0, (err, res, prepaid) ->
subscribeUser user1, null, prepaid.code, ->
Prepaid.findById prepaid._id, (err, prepaid) ->
expect(err).toBeNull()
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
subscriptionID = user1.get('stripe').subscriptionID
stripe.customers.retrieveSubscription customerID, subscriptionID, (err, subscription) ->
expect(err).toBeNull()
expect(subscription).not.toBeNull()
expect(subscription?.discount?.coupon?.id).toEqual('free')
nockDone()
done()
it 'Subscribe with prepaid, then cancel', (done) ->
nockUtils.setupNock 'sub-test-07.json', (err, nockDone) ->
loginNewUser (user1) ->
user1.set('permissions', ['admin'])
user1.save (err, user1) ->
expect(err).toBeNull()
expect(user1.isAdmin()).toEqual(true)
createPrepaid 'subscription', 1, 0, (err, res, prepaid) ->
expect(err).toBeNull()
subscribeUser user1, null, prepaid.code, ->
Prepaid.findById prepaid._id, (err, prepaid) ->
expect(err).toBeNull()
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, ->
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
expect(user1.get('stripe').prepaidCode).toEqual(prepaid.get('code'))
nockDone()
done()
it 'User2 subscribes with used prepaid', (done) ->
nockUtils.setupNock 'sub-test-08.json', (err, nockDone) ->
loginNewUser (user1) ->
user1.set('permissions', ['admin'])
user1.save (err, user1) ->
expect(err).toBeNull()
expect(user1.isAdmin()).toEqual(true)
createPrepaid 'subscription', 1, 0, (err, res, prepaid) ->
expect(err).toBeNull()
subscribeUser user1, null, prepaid.code, ->
loginNewUser (user2) ->
requestBody = user2.toObject()
requestBody.stripe =
planID: 'basic'
requestBody.stripe.prepaidCode = prepaid.code
request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, body) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(403)
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())
nockDone()
done()
it 'User2 subscribes with same active prepaid', (done) ->
nockUtils.setupNock 'sub-test-09.json', (err, nockDone) ->
loginNewUser (user1) ->
user1.set('permissions', ['admin'])
user1.save (err, user1) ->
expect(err).toBeNull()
expect(user1.isAdmin()).toEqual(true)
createPrepaid 'subscription', 2, 0, (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)
nockDone()
done()
it 'Admin subscribes self with valid prepaid', (done) ->
nockUtils.setupNock 'sub-test-10.json', (err, nockDone) ->
loginNewUser (user1) ->
user1.set('permissions', ['admin'])
user1.save (err, user1) ->
expect(err).toBeNull()
expect(user1.isAdmin()).toEqual(true)
createPrepaid 'subscription', 1, 0, (err, res, prepaid) ->
expect(err).toBeNull()
subscribeUser user1, null, prepaid.code, ->
Prepaid.findById prepaid._id, (err, prepaid) ->
expect(err).toBeNull()
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())
nockDone()
done()
it 'Admin subscribes self with valid prepaid twice', (done) ->
nockUtils.setupNock 'sub-test-11.json', (err, nockDone) ->
loginNewUser (user1) ->
user1.set('permissions', ['admin'])
user1.save (err, user1) ->
expect(err).toBeNull()
expect(user1.isAdmin()).toEqual(true)
createPrepaid 'subscription', 2, 0, (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)
nockDone()
done()
it 'Admin subscribes self with invalid prepaid', (done) ->
nockUtils.setupNock 'sub-test-12.json', (err, nockDone) ->
loginNewUser (user1) ->
user1.set('permissions', ['admin'])
user1.save (err, user1) ->
expect(err).toBeNull()
expect(user1.isAdmin()).toEqual(true)
requestBody = user1.toObject()
requestBody.stripe =
planID: 'basic'
requestBody.stripe.prepaidCode = 'MattMatt'
request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, body) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(404)
nockDone()
done()
describe 'Sponsored', ->
it 'Unsubscribed user1 subscribes user2', (done) ->
nockUtils.setupNock 'sub-test-13.json', (err, nockDone) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
createNewUser (user2) ->
loginNewUser (user1) ->
subscribeRecipients user1, [user2], token, (updatedUser) ->
verifySponsorship user1.id, user2.id, ->
nockDone()
done()
it 'Recipient user delete unsubscribes', (done) ->
nockUtils.setupNock 'sub-test-14.json', (err, nockDone) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
createNewUser (user2) ->
loginNewUser (user1) ->
subscribeRecipients user1, [user2], token, (updatedUser) ->
expect(updatedUser).not.toBeNull()
return done() unless updatedUser
customerID = updatedUser.stripe?.customerID
expect(customerID).toBeDefined()
subscriptionID = updatedUser.stripe?.recipients[0]?.subscriptionID
expect(subscriptionID).toBeDefined()
return done() unless customerID and subscriptionID
loginUser user2, (user2) ->
request.del {uri: "#{userURL}/#{user2.id}"}, (err, res) ->
expect(err).toBeNull()
stripe.customers.retrieveSubscription customerID, subscriptionID, (err, subscription) ->
expect(err).not.toBeNull()
expect(subscription).toBeNull()
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
expect(_.isEmpty(user1.get('stripe').recipients))
stripe.customers.retrieveSubscription customerID, user1.get('stripe').sponsorSubscriptionID, (err, subscription) ->
expect(err).toBeNull()
expect(subscription.quantity).toEqual(0)
nockDone()
done()
it 'Subscribed user1 subscribes user2, one token', (done) ->
nockUtils.setupNock 'sub-test-15.json', (err, nockDone) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
createNewUser (user2) ->
loginNewUser (user1) ->
subscribeUser user1, token, null, (updatedUser) ->
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
subscribeRecipients user1, [user2], null, (updatedUser) ->
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
expect(user1.get('stripe').subscriptionID).toBeDefined()
expect(user1.isPremium()).toEqual(true)
verifySponsorship user1.id, user2.id, ->
nockDone()
done()
it 'Clean up sponsorships upon sub cancel after setup sponsor sub fails', (done) ->
nockUtils.setupNock 'sub-test-16.json', (err, nockDone) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
createNewUser (user2) ->
loginNewUser (user1) ->
subscribeUser user1, token, null, (updatedUser) ->
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
subscribeRecipients user1, [user2], null, (updatedUser) ->
# Delete user1 sponsorSubscriptionID to simulate failed sponsor sub
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
stripeInfo = _.cloneDeep(user1.get('stripe') ? {})
delete stripeInfo.sponsorSubscriptionID
user1.set 'stripe', stripeInfo
user1.save (err, user1) ->
expect(err).toBeNull()
User.findById user1.id, (err, user1) ->
unsubscribeRecipient user1, user2, ->
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
expect(user1.get('stripe').subscriptionID).toBeDefined()
expect(_.isEmpty(user1.get('stripe').recipients)).toEqual(true)
expect(user1.isPremium()).toEqual(true)
User.findById user2.id, (err, user2) ->
verifyNotSponsoring user1.id, user2.id, ->
verifyNotRecipient user2.id, ->
nockDone()
done()
it 'Unsubscribed user1 unsubscribes user2 and their sub ends', (done) ->
nockUtils.setupNock 'sub-test-17.json', (err, nockDone) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
createNewUser (user2) ->
loginNewUser (user1) ->
subscribeRecipients user1, [user2], token, (updatedUser) ->
User.findById user1.id, (err, user1) ->
unsubscribeRecipient user1, user2, ->
verifyNotSponsoring user1.id, user2.id, ->
verifyNotRecipient user2.id, ->
nockDone()
done()
it 'Unsubscribed user1 immediately resubscribes user2, one token', (done) ->
nockUtils.setupNock 'sub-test-18.json', (err, nockDone) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
createNewUser (user2) ->
loginNewUser (user1) ->
subscribeRecipients user1, [user2], token, (updatedUser) ->
User.findById user1.id, (err, user1) ->
unsubscribeRecipient user1, user2, ->
subscribeRecipients user1, [user2], null, (updatedUser) ->
verifySponsorship user1.id, user2.id, ->
nockDone()
done()
it 'Sponsored user2 subscribes their sponsor user1', (done) ->
nockUtils.setupNock 'sub-test-19.json', (err, nockDone) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
createNewUser (user2) ->
loginNewUser (user1) ->
subscribeRecipients user1, [user2], token, (updatedUser) ->
loginUser user2, (user2) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
subscribeRecipients user2, [user1], token, (updatedUser) ->
verifySponsorship user1.id, user2.id, ->
verifySponsorship user2.id, user1.id, ->
nockDone()
done()
it 'Unsubscribed user1 subscribes user1', (done) ->
nockUtils.setupNock 'sub-test-20.json', (err, nockDone) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
loginNewUser (user1) ->
requestBody = user1.toObject()
requestBody.stripe =
subscribeEmails: [user1.get('email')]
token: token.id
request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, body) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(200)
User.findById user1.id, (err, user) ->
expect(err).toBeNull()
stripeInfo = user.get('stripe')
expect(stripeInfo.customerID).toBeDefined()
expect(stripeInfo.planID).toBeUndefined()
expect(stripeInfo.subscriptionID).toBeUndefined()
expect(stripeInfo.recipients.length).toEqual(0)
nockDone()
done()
it 'Subscribed user1 unsubscribes user2', (done) ->
nockUtils.setupNock 'sub-test-21.json', (err, nockDone) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
createNewUser (user2) ->
loginNewUser (user1) ->
subscribeUser user1, token, null, (updatedUser) ->
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
subscribeRecipients user1, [user2], null, (updatedUser) ->
User.findById user1.id, (err, user1) ->
unsubscribeRecipient user1, user2, ->
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
expect(user1.get('stripe').subscriptionID).toBeDefined()
expect(user1.isPremium()).toEqual(true)
User.findById user2.id, (err, user2) ->
verifyNotSponsoring user1.id, user2.id, ->
verifyNotRecipient user2.id, ->
nockDone()
done()
it 'Subscribed user1 subscribes user2, unsubscribes themselves', (done) ->
nockUtils.setupNock 'sub-test-22.json', (err, nockDone) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
createNewUser (user2) ->
loginNewUser (user1) ->
subscribeUser user1, token, null, (updatedUser) ->
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
subscribeRecipients user1, [user2], null, (updatedUser) ->
User.findById user1.id, (err, user1) ->
unsubscribeUser user1, ->
verifySponsorship user1.id, user2.id, ->
nockDone()
done()
it 'Sponsored user2 tries to subscribe', (done) ->
nockUtils.setupNock 'sub-test-23.json', (err, nockDone) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
createNewUser (user2) ->
loginNewUser (user1) ->
subscribeRecipients user1, [user2], token, (updatedUser) ->
loginUser user2, (user2) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
requestBody = user2.toObject()
requestBody.stripe =
token: token.id
planID: 'basic'
request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, body) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(403)
nockDone()
done()
it 'Sponsored user2 tries to subscribe with valid prepaid', (done) ->
nockUtils.setupNock 'sub-test-24.json', (err, nockDone) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
createNewUser (user2) ->
loginNewUser (user1) ->
subscribeRecipients user1, [user2], token, (updatedUser) ->
loginUser user2, (user2) ->
user2.set('permissions', ['admin'])
user2.save (err, user1) ->
expect(err).toBeNull()
expect(user2.isAdmin()).toEqual(true)
createPrepaid 'subscription', 1, 0, (err, res, prepaid) ->
expect(err).toBeNull()
requestBody = user2.toObject()
requestBody.stripe =
planID: 'basic'
prepaidCode: prepaid.code
request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, body) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(403)
nockDone()
done()
it 'Sponsored user2 tries to unsubscribe', (done) ->
nockUtils.setupNock 'sub-test-25.json', (err, nockDone) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
createNewUser (user2) ->
loginNewUser (user1) ->
subscribeRecipients user1, [user2], token, (updatedUser) ->
loginUser user2, (user2) ->
requestBody = user2.toObject()
requestBody.stripe =
recipient: user2.id
request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, body) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(200)
verifySponsorship user1.id, user2.id, ->
nockDone()
done()
it 'Cancel sponsor subscription with 2 recipient subscriptions, then subscribe 1 old and 1 new', (done) ->
nockUtils.setupNock 'sub-test-26.json', (err, nockDone) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
createNewUser (user3) ->
createNewUser (user2) ->
loginNewUser (user1) ->
subscribeRecipients user1, [user2, user3], token, (updatedUser) ->
customerID = updatedUser?.stripe?.customerID
subscriptionID = updatedUser?.stripe?.sponsorSubscriptionID
expect(customerID).toBeDefined()
expect(subscriptionID).toBeDefined()
return done() unless customerID and subscriptionID
# Find Stripe sponsor subscription
stripe.customers.retrieveSubscription customerID, subscriptionID, (err, subscription) ->
expect(err).toBeNull()
expect(subscription).not.toBeNull()
# Cancel Stripe sponsor subscription
stripe.customers.cancelSubscription customerID, subscriptionID, (err) ->
expect(err).toBeNull()
# Simulate customer.subscription.deleted webhook event for sponsor subscription
event = _.cloneDeep(customerSubscriptionDeletedSampleEvent)
event.data.object = subscription
request.post {uri: webhookURL, json: event}, (err, res, body) ->
expect(err).toBeNull()
# Should have 2 cancelled recipient subs with cancel_at_period_end = true
# TODO: is this correct, or do we terminate recipient subs immediately now?
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
stripeInfo = user1.get('stripe')
expect(stripeInfo.sponsorSubscriptionID).toBeUndefined()
expect(stripeInfo.recipients).toBeUndefined()
stripe.customers.listSubscriptions stripeInfo.customerID, (err, subscriptions) ->
expect(err).toBeNull()
expect(subscriptions.data.length).toEqual(2)
for sub in subscriptions.data
expect(sub.plan.id).toEqual('basic')
expect(sub.cancel_at_period_end).toEqual(true)
# Subscribe user3 back
User.findById user1.id, (err, user1) ->
subscribeRecipients user1, [user3], null, (updatedUser) ->
verifySponsorship user1.id, user3.id, ->
# Subscribe new user4
createNewUser (user4) ->
loginUser user1, (user1) ->
User.findById user1.id, (err, user1) ->
subscribeRecipients user1, [user4], null, (updatedUser) ->
verifySponsorship user1.id, user4.id, ->
nockDone()
done()
it 'Subscribing two users separately yields proration payment', (done) ->
nockUtils.setupNock 'sub-test-27.json', (err, nockDone) ->
# TODO: Use test plan with low duration + setTimeout to test delay between 2 subscribes
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
createNewUser (user3) ->
createNewUser (user2) ->
loginNewUser (user1) ->
subscribeRecipients user1, [user2], token, (updatedUser) ->
User.findById user1.id, (err, user1) ->
subscribeRecipients user1, [user3], null, (updatedUser) ->
# TODO: What do we expect invoices to show here?
stripe.invoices.list {customer: updatedUser.stripe.customerID}, (err, invoices) ->
expect(err).toBeNull()
# Verify for proration invoice
foundProratedInvoice = false
for invoice in invoices.data
line = invoice.lines.data[0]
if line.type is 'invoiceitem' and line.proration
totalAmount = appUtils.getSponsoredSubsAmount(subPrice, 2, false)
expect(invoice.total).toBeLessThan(totalAmount)
expect(invoice.total).toEqual(totalAmount - subPrice)
Payment.findOne "stripe.invoiceID": invoice.id, (err, payment) ->
expect(err).toBeNull()
expect(payment.get('amount')).toEqual(invoice.total)
nockDone()
done()
foundProratedInvoice = true
break
unless foundProratedInvoice
expect(foundProratedInvoice).toEqual(true)
nockDone()
done()
it 'Invalid subscribeEmails', (done) ->
nockUtils.setupNock 'sub-test-28.json', (err, nockDone) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
loginNewUser (user1) ->
requestBody = user1.toObject()
requestBody.stripe =
subscribeEmails: ['invalid@user.com', 'notemailformat', '', null, undefined]
requestBody.stripe.token = token.id
request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, body) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(200)
expect(body.stripe).toBeDefined()
User.findById user1.id, (err, sponsor) ->
expect(err).toBeNull()
expect(sponsor.get('stripe')).toBeDefined()
expect(sponsor.get('stripe').customerID).toBeDefined()
expect(sponsor.get('stripe').sponsorSubscriptionID).toBeDefined()
expect(sponsor.get('stripe').recipients?.length).toEqual(0)
nockDone()
done()
it 'User1 subscribes user2 then themselves', (done) ->
nockUtils.setupNock 'sub-test-29.json', (err, nockDone) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
createNewUser (user2) ->
loginNewUser (user1) ->
subscribeRecipients user1, [user2], token, (updatedUser) ->
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
verifySponsorship user1.id, user2.id, ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
subscribeUser user1, token, null, (updatedUser) ->
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
expect(user1.get('stripe').subscriptionID).toBeDefined()
expect(user1.isPremium()).toEqual(true)
stripe.customers.listSubscriptions user1.get('stripe').customerID, (err, subscriptions) ->
expect(err).toBeNull()
expect(subscriptions.data.length).toEqual(3)
for sub in subscriptions.data
if sub.plan.id is 'basic'
if sub.discount?.coupon?.id is 'free'
expect(sub.metadata?.id).toEqual(user2.id)
else
expect(sub.metadata?.id).toEqual(user1.id)
else
expect(sub.plan.id).toEqual('incremental')
expect(sub.metadata?.id).toEqual(user1.id)
nockDone()
done()
it 'Subscribe with prepaid, then get sponsored', (done) ->
nockUtils.setupNock 'sub-test-30.json', (err, nockDone) ->
loginNewUser (user1) ->
user1.set('permissions', ['admin'])
user1.save (err, user1) ->
expect(err).toBeNull()
expect(user1.isAdmin()).toEqual(true)
createPrepaid 'subscription', 1, 0, (err, res, prepaid) ->
expect(err).toBeNull()
subscribeUser user1, null, prepaid.code, ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
loginNewUser (user2) ->
requestBody = user2.toObject()
requestBody.stripe =
token: token.id
subscribeEmails: [user1.get('emailLower')]
request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, body) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(200)
User.findById user1.id, (err, user) ->
expect(err).toBeNull()
stripeInfo = user.get('stripe')
expect(stripeInfo.customerID).toBeDefined()
expect(stripeInfo.planID).toBeDefined()
expect(stripeInfo.subscriptionID).toBeDefined()
expect(stripeInfo.sponsorID).toBeUndefined()
nockDone()
done()
describe 'Bulk discounts', ->
# Bulk discount algorithm (includes personal sub):
# 1 100%
# 2-11 80%
# 12+ 60%
it 'Unsubscribed user1 subscribes two users', (done) ->
nockUtils.setupNock 'sub-test-31.json', (err, nockDone) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
createNewUser (user3) ->
createNewUser (user2) ->
loginNewUser (user1) ->
subscribeRecipients user1, [user2, user3], token, (updatedUser) ->
verifySponsorship user1.id, user2.id, ->
verifySponsorship user1.id, user3.id, ->
nockDone()
done()
it 'Subscribed user1 subscribes 2 users, unsubscribes 2', (done) ->
nockUtils.setupNock 'sub-test-32.json', (err, nockDone) ->
recipientCount = 2
recipientsToVerify = [0, 1]
recipients = new SubbedRecipients recipientCount, recipientsToVerify
# Create recipients
recipients.createRecipients ->
expect(recipients.length()).toEqual(recipientCount)
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
# Create sponsor user
loginNewUser (user1) ->
subscribeUser user1, token, null, (updatedUser) ->
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
# Subscribe recipients
recipients.subRecipients user1, null, ->
User.findById user1.id, (err, user1) ->
# Unsubscribe recipient0
unsubscribeRecipient user1, recipients.get(0), ->
User.findById user1.id, (err, user1) ->
stripeInfo = user1.get('stripe')
expect(stripeInfo.recipients.length).toEqual(1)
verifyNotSponsoring user1.id, recipients.get(0).id, ->
verifyNotRecipient recipients.get(0).id, ->
stripe.customers.retrieveSubscription stripeInfo.customerID, stripeInfo.sponsorSubscriptionID, (err, subscription) ->
expect(err).toBeNull()
expect(subscription).not.toBeNull()
expect(subscription?.quantity).toEqual(getSubscribedQuantity(1))
# Unsubscribe recipient1
unsubscribeRecipient user1, recipients.get(1), ->
User.findById user1.id, (err, user1) ->
stripeInfo = user1.get('stripe')
expect(stripeInfo.recipients.length).toEqual(0)
verifyNotSponsoring user1.id, recipients.get(1).id, ->
verifyNotRecipient recipients.get(1).id, ->
stripe.customers.retrieveSubscription stripeInfo.customerID, stripeInfo.sponsorSubscriptionID, (err, subscription) ->
expect(err).toBeNull()
expect(subscription).not.toBeNull()
return done() unless subscription
expect(subscription.quantity).toEqual(0)
nockDone()
done()
it 'Subscribed user1 subscribes 3 users, unsubscribes 2, themselves, then 1', (done) ->
nockUtils.setupNock 'sub-test-33.json', (err, nockDone) ->
recipientCount = 3
recipientsToVerify = [0, 1, 2]
recipients = new SubbedRecipients recipientCount, recipientsToVerify
# Create recipients
recipients.createRecipients ->
expect(recipients.length()).toEqual(recipientCount)
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
# Create sponsor user
loginNewUser (user1) ->
subscribeUser user1, token, null, (updatedUser) ->
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
# Subscribe recipients
recipients.subRecipients user1, null, ->
User.findById user1.id, (err, user1) ->
# Unsubscribe first recipient
unsubscribeRecipient user1, recipients.get(0), ->
User.findById user1.id, (err, user1) ->
stripeInfo = user1.get('stripe')
expect(stripeInfo.customerID).toBeDefined()
expect(stripeInfo.sponsorSubscriptionID).toBeDefined()
return done() unless stripeInfo.customerID and stripeInfo.sponsorSubscriptionID
expect(stripeInfo.recipients.length).toEqual(recipientCount - 1)
verifyNotSponsoring user1.id, recipients.get(0).id, ->
verifyNotRecipient recipients.get(0).id, ->
stripe.customers.retrieveSubscription stripeInfo.customerID, stripeInfo.sponsorSubscriptionID, (err, subscription) ->
expect(err).toBeNull()
expect(subscription).not.toBeNull()
expect(subscription?.quantity).toEqual(getSubscribedQuantity(recipientCount - 1))
# Unsubscribe second recipient
unsubscribeRecipient user1, recipients.get(1), ->
User.findById user1.id, (err, user1) ->
stripeInfo = user1.get('stripe')
expect(stripeInfo.recipients.length).toEqual(recipientCount - 2)
verifyNotSponsoring user1.id, recipients.get(1).id, ->
verifyNotRecipient recipients.get(1).id, ->
stripe.customers.retrieveSubscription stripeInfo.customerID, stripeInfo.sponsorSubscriptionID, (err, subscription) ->
expect(err).toBeNull()
expect(subscription).not.toBeNull()
expect(subscription?.quantity).toEqual(getSubscribedQuantity(recipientCount - 2))
# Unsubscribe self
User.findById user1.id, (err, user1) ->
unsubscribeUser user1, ->
User.findById user1.id, (err, user1) ->
stripeInfo = user1.get('stripe')
expect(stripeInfo.planID).toBeUndefined()
# Unsubscribe third recipient
verifySponsorship user1.id, recipients.get(2).id, ->
unsubscribeRecipient user1, recipients.get(2), ->
User.findById user1.id, (err, user1) ->
stripeInfo = user1.get('stripe')
expect(stripeInfo.recipients.length).toEqual(recipientCount - 3)
verifyNotSponsoring user1.id, recipients.get(2).id, ->
verifyNotRecipient recipients.get(2).id, ->
stripe.customers.retrieveSubscription stripeInfo.customerID, stripeInfo.sponsorSubscriptionID, (err, subscription) ->
expect(err).toBeNull()
expect(subscription).not.toBeNull()
expect(subscription?.quantity).toEqual(getSubscribedQuantity(recipientCount - 3))
nockDone()
done()
# xit 'Unsubscribed user1 subscribes 13 users, unsubcribes 2', (done) ->
# nockUtils.setupNock 'sub-test-34.json', (err, nockDone) ->
# # TODO: Hits the Stripe error 'Request rate limit exceeded'.
# # TODO: Need a better test for 12+ bulk discounts. Or, we could update the bulk disount logic.
# # TODO: verify interim invoices?
# recipientCount = 13
# recipientsToVerify = [0, 1, 10, 11, 12]
# recipients = new SubbedRecipients recipientCount, recipientsToVerify
#
# # Create recipients
# recipients.createRecipients ->
# expect(recipients.length()).toEqual(recipientCount)
#
# stripe.tokens.create {
# card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
# }, (err, token) ->
#
# # Create sponsor user
# loginNewUser (user1) ->
#
# # Subscribe recipients
# recipients.subRecipients user1, token, ->
# User.findById user1.id, (err, user1) ->
#
# # Unsubscribe first recipient
# unsubscribeRecipient user1, recipients.get(0), ->
# User.findById user1.id, (err, user1) ->
#
# stripeInfo = user1.get('stripe')
# expect(stripeInfo.recipients.length).toEqual(recipientCount - 1)
# verifyNotSponsoring user1.id, recipients.get(0).id, ->
# verifyNotRecipient recipients.get(0).id, ->
# stripe.customers.retrieveSubscription stripeInfo.customerID, stripeInfo.sponsorSubscriptionID, (err, subscription) ->
# expect(err).toBeNull()
# expect(subscription).not.toBeNull()
# expect(subscription.quantity).toEqual(getUnsubscribedQuantity(recipientCount - 1))
#
# # Unsubscribe last recipient
# unsubscribeRecipient user1, recipients.get(recipientCount - 1), ->
# User.findById user1.id, (err, user1) ->
# stripeInfo = user1.get('stripe')
# expect(stripeInfo.recipients.length).toEqual(recipientCount - 2)
# verifyNotSponsoring user1.id, recipients.get(recipientCount - 1).id, ->
# verifyNotRecipient recipients.get(recipientCount - 1).id, ->
# stripe.customers.retrieveSubscription stripeInfo.customerID, stripeInfo.sponsorSubscriptionID, (err, subscription) ->
# expect(err).toBeNull()
# expect(subscription).not.toBeNull()
# numSponsored = recipientCount - 2
# if numSponsored <= 1
# expect(subscription.quantity).toEqual(subPrice)
# else if numSponsored <= 11
# expect(subscription.quantity).toEqual(subPrice + (numSponsored - 1) * subPrice * 0.8)
# else
# expect(subscription.quantity).toEqual(subPrice + 10 * subPrice * 0.8 + (numSponsored - 11) * subPrice * 0.6)
# nockDone()
# done()
describe 'APIs', ->
subscriptionURL = getURL('/db/subscription')
it 'year_sale', (done) ->
nockUtils.setupNock 'sub-test-35.json', (err, nockDone) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
loginNewUser (user1) ->
expect(user1.get('stripe')?.free).toBeUndefined()
requestBody =
stripe:
token: token.id
timestamp: new Date()
request.put {uri: "#{subscriptionURL}/-/year_sale", json: requestBody, headers: headers }, (err, res) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(200)
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
stripeInfo = user1.get('stripe')
expect(stripeInfo).toBeDefined()
return done() unless stripeInfo
endDate = new Date()
endDate.setUTCFullYear(endDate.getUTCFullYear() + 1)
expect(stripeInfo.free).toEqual(endDate.toISOString().substring(0, 10))
expect(stripeInfo.customerID).toBeDefined()
expect(user1.get('purchased')?.gems).toEqual(subGems*12)
Payment.findOne 'stripe.customerID': stripeInfo.customerID, (err, payment) ->
expect(err).toBeNull()
expect(payment).toBeTruthy()
expect(payment.get('gems')).toEqual(subGems*12)
nockDone()
done()
it 'year_sale when stripe.free === true', (done) ->
nockUtils.setupNock 'sub-test-36.json', (err, nockDone) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
loginNewUser (user1) ->
user1.set('stripe', {free: true})
user1.save (err, user1) ->
expect(err).toBeNull()
expect(user1.get('stripe')?.free).toEqual(true)
requestBody =
stripe:
token: token.id
timestamp: new Date()
request.put {uri: "#{subscriptionURL}/-/year_sale", json: requestBody, headers: headers }, (err, res) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(200)
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
stripeInfo = user1.get('stripe')
expect(stripeInfo).toBeDefined()
return done() unless stripeInfo
endDate = new Date()
endDate.setUTCFullYear(endDate.getUTCFullYear() + 1)
expect(stripeInfo.free).toEqual(endDate.toISOString().substring(0, 10))
expect(stripeInfo.customerID).toBeDefined()
expect(user1.get('purchased')?.gems).toEqual(subGems*12)
Payment.findOne 'stripe.customerID': stripeInfo.customerID, (err, payment) ->
expect(err).toBeNull()
expect(payment).toBeTruthy()
expect(payment.get('gems')).toEqual(subGems*12)
nockDone()
done()
it 'year_sale when stripe.free < today', (done) ->
nockUtils.setupNock 'sub-test-37.json', (err, nockDone) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
loginNewUser (user1) ->
endDate = new Date()
endDate.setUTCFullYear(endDate.getUTCFullYear() - 1)
user1.set('stripe', {free: endDate.toISOString().substring(0, 10)})
user1.save (err, user1) ->
expect(err).toBeNull()
expect(user1.get('stripe')?.free).toEqual(endDate.toISOString().substring(0, 10))
requestBody =
stripe:
token: token.id
timestamp: new Date()
request.put {uri: "#{subscriptionURL}/-/year_sale", json: requestBody, headers: headers }, (err, res) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(200)
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
stripeInfo = user1.get('stripe')
expect(stripeInfo).toBeDefined()
return done() unless stripeInfo
endDate = new Date()
endDate.setUTCFullYear(endDate.getUTCFullYear() + 1)
expect(stripeInfo.free).toEqual(endDate.toISOString().substring(0, 10))
expect(stripeInfo.customerID).toBeDefined()
expect(user1.get('purchased')?.gems).toEqual(subGems*12)
Payment.findOne 'stripe.customerID': stripeInfo.customerID, (err, payment) ->
expect(err).toBeNull()
expect(payment).toBeTruthy()
expect(payment.get('gems')).toEqual(subGems*12)
nockDone()
done()
it 'year_sale when stripe.free > today', (done) ->
nockUtils.setupNock 'sub-test-38.json', (err, nockDone) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
loginNewUser (user1) ->
endDate = new Date()
endDate.setUTCDate(endDate.getUTCDate() + 5)
user1.set('stripe', {free: endDate.toISOString().substring(0, 10)})
user1.save (err, user1) ->
expect(err).toBeNull()
expect(user1.get('stripe')?.free).toEqual(endDate.toISOString().substring(0, 10))
requestBody =
stripe:
token: token.id
timestamp: new Date()
request.put {uri: "#{subscriptionURL}/-/year_sale", json: requestBody, headers: headers }, (err, res) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(200)
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
stripeInfo = user1.get('stripe')
expect(stripeInfo).toBeDefined()
return done() unless stripeInfo
endDate = new Date()
endDate.setUTCFullYear(endDate.getUTCFullYear() + 1)
endDate.setUTCDate(endDate.getUTCDate() + 5)
expect(stripeInfo.free).toEqual(endDate.toISOString().substring(0, 10))
expect(stripeInfo.customerID).toBeDefined()
expect(user1.get('purchased')?.gems).toEqual(subGems*12)
Payment.findOne 'stripe.customerID': stripeInfo.customerID, (err, payment) ->
expect(err).toBeNull()
expect(payment).toBeTruthy()
expect(payment.get('gems')).toEqual(subGems*12)
nockDone()
done()
it 'year_sale with monthly sub', (done) ->
nockUtils.setupNock 'sub-test-39.json', (err, nockDone) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
loginNewUser (user1) ->
subscribeUser user1, token, null, ->
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
stripeInfo = user1.get('stripe')
stripe.customers.retrieveSubscription stripeInfo.customerID, stripeInfo.subscriptionID, (err, subscription) ->
expect(err).toBeNull()
expect(subscription).not.toBeNull()
stripeSubscriptionPeriodEndDate = new Date(subscription.current_period_end * 1000)
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
requestBody =
stripe:
token: token.id
timestamp: new Date()
request.put {uri: "#{subscriptionURL}/-/year_sale", json: requestBody, headers: headers }, (err, res) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(200)
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
stripeInfo = user1.get('stripe')
expect(stripeInfo).toBeDefined()
return done() unless stripeInfo
endDate = stripeSubscriptionPeriodEndDate
endDate.setUTCFullYear(endDate.getUTCFullYear() + 1)
expect(stripeInfo.free).toEqual(endDate.toISOString().substring(0, 10))
expect(stripeInfo.customerID).toBeDefined()
expect(user1.get('purchased')?.gems).toEqual(subGems+subGems*12)
Payment.findOne 'stripe.customerID': stripeInfo.customerID, (err, payment) ->
expect(err).toBeNull()
expect(payment).toBeTruthy()
expect(payment.get('gems')).toEqual(subGems*12)
nockDone()
done()
it 'year_sale with sponsored sub', (done) ->
nockUtils.setupNock 'sub-test-40.json', (err, nockDone) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
loginNewUser (user1) ->
user1.set('stripe', {sponsorID: 'dummyID'})
user1.save (err, user1) ->
expect(err).toBeNull()
requestBody =
stripe:
token: token.id
timestamp: new Date()
request.put {uri: "#{subscriptionURL}/-/year_sale", json: requestBody, headers: headers }, (err, res) ->
expect(err).toBeNull()
nockDone()
done()
describe 'Countries', ->
it 'Brazil users get Brazil coupon', (done) ->
nockUtils.setupNock 'sub-test-41.json', (err, nockDone) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2030, cvc: '123' }
}, (err, token) ->
loginNewUser (user1) ->
user1.set('country', 'brazil')
user1.save (err, user1) ->
requestBody = user1.toObject()
requestBody.stripe =
planID: 'basic'
token: token.id
request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, updatedUser) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(200)
expect(updatedUser.country).toBe('brazil')
expect(updatedUser.purchased.gems).toBe(subGemsBrazil)
expect(updatedUser.stripe.planID).toBe('basic')
expect(updatedUser.stripe.customerID).toBeTruthy()
stripe.invoices.list {customer: updatedUser.stripe.customerID}, (err, invoices) ->
expect(err).toBeNull()
expect(invoices).not.toBeNull()
expect(invoices.data.length).toBe(1)
expect(invoices.data[0].discount?.coupon).toBeTruthy()
expect(invoices.data[0].discount?.coupon?.id).toBe('brazil')
expect(invoices.data[0].total).toBeLessThan(subPrice)
# Now we hit our webhook to see if the right Payment is made.
event = _.cloneDeep(invoiceChargeSampleEvent)
event.data.object = invoices.data[0]
request.post {uri: webhookURL, json: event}, (err, res, body) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(201)
Payment.findOne 'stripe.customerID': updatedUser.stripe.customerID, (err, payment) ->
expect(err).toBeNull()
expect(payment).toBeTruthy()
return done() unless payment
expect(payment.get('gems')).toEqual(subGemsBrazil)
expect(payment.get('amount')).toBeLessThan(subPrice)
nockDone()
done()