mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-24 16:17:57 -05:00
482b66b8a4
Replace mock data with some real functionality.
437 lines
23 KiB
CoffeeScript
437 lines
23 KiB
CoffeeScript
testReceipt = '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'
|
|
|
|
config = require '../../../server_config'
|
|
require '../common'
|
|
|
|
describe '/db/payment', ->
|
|
request = require 'request'
|
|
paymentURL = getURL('/db/payment')
|
|
checkChargesURL = getURL('/db/payment/check-stripe-charges')
|
|
customPaymentURL = getURL('/db/payment/custom')
|
|
|
|
firstApplePayment = {
|
|
apple: {
|
|
rawReceipt: testReceipt
|
|
transactionID: '1000000131274439'
|
|
localPrice: '$5.00'
|
|
}
|
|
}
|
|
|
|
secondApplePayment = {
|
|
apple: {
|
|
rawReceipt: testReceipt
|
|
transactionID: '1000000131283608'
|
|
localPrice: '$10.00'
|
|
}
|
|
}
|
|
|
|
paymentCreated = null
|
|
|
|
it 'clears the db first', (done) ->
|
|
clearModels [User, Payment], (err) ->
|
|
throw err if err
|
|
done()
|
|
|
|
describe 'posting Apple IAPs', ->
|
|
|
|
it 'denies anonymous users trying to pay', (done) ->
|
|
request.get getURL('/auth/whoami'), ->
|
|
request.post {uri: paymentURL, json: firstApplePayment}, (err, res, body) ->
|
|
expect(res.statusCode).toBe 403
|
|
done()
|
|
|
|
it 'creates a payment object and credits gems to the user', (done) ->
|
|
loginJoe ->
|
|
request.post {uri: paymentURL, json: firstApplePayment}, (err, res, body) ->
|
|
paymentCreated = body?._id
|
|
expect(res.statusCode).toBe 201
|
|
User.findOne({name:'Joe'}).exec(err, (err, user) ->
|
|
expect(user.get('purchased')?.gems).toBe(5000)
|
|
done()
|
|
)
|
|
|
|
it 'is idempotent', (done) ->
|
|
loginJoe ->
|
|
request.post {uri: paymentURL, json: firstApplePayment}, (err, res, body) ->
|
|
expect(body._id is paymentCreated).toBe(true)
|
|
expect(res.statusCode).toBe 200
|
|
User.findOne({name:'Joe'}).exec(err, (err, user) ->
|
|
expect(user.get('purchased')?.gems).toBe(5000)
|
|
done()
|
|
)
|
|
|
|
it 'prevents other users from reusing payment receipts', (done) ->
|
|
loginSam ->
|
|
request.post {uri: paymentURL, json: firstApplePayment}, (err, res, body) ->
|
|
expect(res.statusCode).toBe 403
|
|
done()
|
|
|
|
it 'processes only the transactionID that is given', (done) ->
|
|
loginJoe ->
|
|
request.post {uri: paymentURL, json: secondApplePayment}, (err, res, body) ->
|
|
expect(body._id is paymentCreated).toBe(false)
|
|
expect(res.statusCode).toBe 201
|
|
User.findOne({name:'Joe'}).exec(err, (err, user) ->
|
|
expect(user.get('purchased')?.gems).toBe(16000)
|
|
done()
|
|
)
|
|
|
|
describe 'posting Stripe purchases', ->
|
|
stripe = require('stripe')(config.stripe.secretKey)
|
|
|
|
charge = null
|
|
joeID = null
|
|
timestamp = new Date().getTime()
|
|
stripeTokenID = null
|
|
joeData = null
|
|
|
|
it 'clears the db first', (done) ->
|
|
clearModels [User, Payment], (err) ->
|
|
throw err if err
|
|
done()
|
|
|
|
it 'handles a purchase', (done) ->
|
|
stripe.tokens.create({
|
|
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
|
|
}, (err, token) ->
|
|
stripeTokenID = token.id
|
|
loginJoe (joe) ->
|
|
joeID = joe.get('_id')+''
|
|
data = {
|
|
productID: 'gems_5'
|
|
stripe: {
|
|
token: token.id
|
|
timestamp: timestamp
|
|
}
|
|
}
|
|
request.post {uri: paymentURL, json: data }, (err, res, body) ->
|
|
expect(res.statusCode).toBe 201
|
|
expect(body.stripe.chargeID).toBeDefined()
|
|
expect(body.stripe.timestamp).toBe(timestamp)
|
|
expect(body.stripe.customerID).toBeDefined()
|
|
expect(body.gems).toBe(5000)
|
|
expect(body.amount).toBe(499)
|
|
expect(body.productID).toBe('gems_5')
|
|
expect(body.service).toBe('stripe')
|
|
expect(body.recipient).toBe(joeID)
|
|
expect(body.purchaser).toBe(joeID)
|
|
User.findById(joe.get('_id'), (err, user) ->
|
|
expect(user.get('purchased').gems).toBe(5000)
|
|
expect(user.get('stripe').customerID).toBe(body.stripe.customerID)
|
|
done()
|
|
)
|
|
)
|
|
|
|
it 'ignores repeated purchases', (done) ->
|
|
data = { productID: 'gems_5', stripe: { token: stripeTokenID, timestamp: timestamp } }
|
|
request.post {uri: paymentURL, json: data }, (err, res, body) ->
|
|
expect(res.statusCode).toBe 200
|
|
Payment.count({}, (err, count) ->
|
|
expect(count).toBe(1)
|
|
User.findById(joeID, (err, user) ->
|
|
expect(user.get('purchased').gems).toBe(5000)
|
|
done()
|
|
)
|
|
)
|
|
|
|
it 'allows a new charge on the existing customer', (done) ->
|
|
data = { productID: 'gems_5', stripe: { timestamp: new Date().getTime() } }
|
|
request.post {uri: paymentURL, json: data }, (err, res, body) ->
|
|
expect(res.statusCode).toBe 201
|
|
Payment.count {}, (err, count) ->
|
|
expect(count).toBe(2)
|
|
User.findById joeID, (err, user) ->
|
|
joeData = user.toObject()
|
|
expect(user.get('purchased').gems).toBe(10000)
|
|
done()
|
|
|
|
it "updates the customer's card when you submit a new token", (done) ->
|
|
stripe.customers.retrieve joeData.stripe.customerID, (err, customer) ->
|
|
originalCustomerID = customer.id
|
|
originalCardID = customer.sources.data[0].id
|
|
stripe.tokens.create {
|
|
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
|
|
}, (err, token) ->
|
|
data = { productID: 'gems_5', stripe: { timestamp: new Date().getTime(), token: token.id } }
|
|
request.post {uri: paymentURL, json: data }, (err, res, body) ->
|
|
expect(res.statusCode).toBe(201)
|
|
User.findById joeID, (err, user) ->
|
|
joeData = user.toObject()
|
|
expect(joeData.stripe.customerID).toBe(originalCustomerID)
|
|
stripe.customers.retrieve joeData.stripe.customerID, (err, customer) ->
|
|
expect(customer.sources.data[0].id).not.toBe(originalCardID)
|
|
done()
|
|
|
|
it 'clears the db', (done) ->
|
|
clearModels [User, Payment], (err) ->
|
|
throw err if err
|
|
done()
|
|
|
|
it 'recovers from breaking between charge and document creation', (done) ->
|
|
stripe.tokens.create({
|
|
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
|
|
}, (err, token) ->
|
|
|
|
data = {
|
|
productID: 'gems_5'
|
|
stripe: { token: token.id, timestamp: timestamp }
|
|
breakAfterCharging: true
|
|
}
|
|
|
|
loginJoe (joe) ->
|
|
request.post {uri: paymentURL, json: data }, (err, res, body) ->
|
|
expect(res.statusCode).toBe 500
|
|
|
|
data = _.omit data, 'breakAfterCharging'
|
|
request.post {uri: paymentURL, json: data }, (err, res, body) ->
|
|
expect(res.statusCode).toBe 201
|
|
|
|
Payment.count({}, (err, count) ->
|
|
expect(count).toBe(1)
|
|
User.findById(joe.get('_id'), (err, user) ->
|
|
expect(user.get('purchased').gems).toBe(5000)
|
|
done()
|
|
)
|
|
)
|
|
)
|
|
|
|
it 'clears the db', (done) ->
|
|
clearModels [User, Payment], (err) ->
|
|
throw err if err
|
|
done()
|
|
|
|
# Testing card numbers are here: https://stripe.com/docs/testing
|
|
|
|
it 'handles card that attaches to customer but fails to be charged', (done) ->
|
|
stripe.tokens.create({
|
|
card: { number: '4000000000000341', exp_month: 12, exp_year: 2020, cvc: '123' }
|
|
}, (err, token) ->
|
|
loginJoe (joe) ->
|
|
timestamp = new Date().getTime()
|
|
data = { productID: 'gems_5', stripe: { token: token.id, timestamp: timestamp } }
|
|
request.post {uri: paymentURL, json: data }, (err, res, body) ->
|
|
expect(res.statusCode).toBe(402)
|
|
done()
|
|
)
|
|
|
|
it 'handles card that always is declined with card_declined code', (done) ->
|
|
stripe.tokens.create({
|
|
card: { number: '4000000000000002', exp_month: 12, exp_year: 2020, cvc: '123' }
|
|
}, (err, token) ->
|
|
loginJoe (joe) ->
|
|
timestamp = new Date().getTime()
|
|
data = { productID: 'gems_5', stripe: { token: token.id, timestamp: timestamp } }
|
|
request.post {uri: paymentURL, json: data }, (err, res, body) ->
|
|
expect(res.statusCode).toBe(402)
|
|
done()
|
|
)
|
|
|
|
it 'handles card that always is declined with incorrect_cvc code', (done) ->
|
|
stripe.tokens.create({
|
|
card: { number: '4000000000000127', exp_month: 12, exp_year: 2020, cvc: '123' }
|
|
}, (err, token) ->
|
|
loginJoe (joe) ->
|
|
timestamp = new Date().getTime()
|
|
data = { productID: 'gems_5', stripe: { token: token.id, timestamp: timestamp } }
|
|
request.post {uri: paymentURL, json: data }, (err, res, body) ->
|
|
expect(res.statusCode).toBe(402)
|
|
done()
|
|
)
|
|
|
|
it 'handles card that always is declined with expired_card code', (done) ->
|
|
stripe.tokens.create({
|
|
card: { number: '4000000000000069', exp_month: 12, exp_year: 2020, cvc: '123' }
|
|
}, (err, token) ->
|
|
loginJoe (joe) ->
|
|
timestamp = new Date().getTime()
|
|
data = { productID: 'gems_5', stripe: { token: token.id, timestamp: timestamp } }
|
|
request.post {uri: paymentURL, json: data }, (err, res, body) ->
|
|
expect(res.statusCode).toBe(402)
|
|
done()
|
|
)
|
|
|
|
it 'handles card that always is declined with processing_error code', (done) ->
|
|
stripe.tokens.create({
|
|
card: { number: '4000000000000119', exp_month: 12, exp_year: 2020, cvc: '123' }
|
|
}, (err, token) ->
|
|
loginJoe (joe) ->
|
|
timestamp = new Date().getTime()
|
|
data = { productID: 'gems_5', stripe: { token: token.id, timestamp: timestamp } }
|
|
request.post {uri: paymentURL, json: data }, (err, res, body) ->
|
|
expect(res.statusCode).toBe(402)
|
|
done()
|
|
)
|
|
|
|
describe '/db/payment/check-stripe-charges', ->
|
|
stripe = require('stripe')(config.stripe.secretKey)
|
|
|
|
it 'clears the db', (done) ->
|
|
clearModels [User, Payment], (err) ->
|
|
throw err if err
|
|
done()
|
|
|
|
it 'finds and records charges which are not in our db', (done) ->
|
|
timestamp = new Date().getTime()
|
|
stripe.tokens.create {
|
|
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
|
|
}, (err, token) ->
|
|
|
|
data = {
|
|
productID: 'gems_5'
|
|
stripe: { token: token.id, timestamp: timestamp }
|
|
breakAfterCharging: true
|
|
}
|
|
|
|
loginJoe (joe) ->
|
|
request.post {uri: paymentURL, json: data }, (err, res, body) ->
|
|
expect(res.statusCode).toBe 500
|
|
|
|
request.post { uri: checkChargesURL }, (err, res, body) ->
|
|
expect(res.statusCode).toBe 201
|
|
Payment.count({}, (err, count) ->
|
|
expect(count).toBe(1)
|
|
User.findById(joe.get('_id'), (err, user) ->
|
|
expect(user.get('purchased').gems).toBe(5000)
|
|
done()
|
|
)
|
|
)
|
|
|
|
describe '/db/payment/custom', ->
|
|
stripe = require('stripe')(config.stripe.secretKey)
|
|
|
|
it 'clears the db', (done) ->
|
|
clearModels [User, Payment], (err) ->
|
|
throw err if err
|
|
done()
|
|
|
|
it 'handles a custom purchase with description', (done) ->
|
|
timestamp = new Date().getTime()
|
|
amount = 5000
|
|
description = 'A sweet Coco t-shirt'
|
|
|
|
stripe.tokens.create({
|
|
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
|
|
}, (err, token) ->
|
|
stripeTokenID = token.id
|
|
loginJoe (joe) ->
|
|
joeID = joe.get('_id') + ''
|
|
data = {
|
|
amount: amount
|
|
description: description
|
|
stripe: {
|
|
token: token.id
|
|
timestamp: timestamp
|
|
}
|
|
}
|
|
request.post {uri: customPaymentURL, json: data }, (err, res, body) ->
|
|
expect(res.statusCode).toBe 201
|
|
expect(body.stripe.chargeID).toBeDefined()
|
|
expect(body.stripe.timestamp).toBe(timestamp)
|
|
expect(body.stripe.customerID).toBeDefined()
|
|
expect(body.description).toEqual(description)
|
|
expect(body.amount).toEqual(amount)
|
|
expect(body.productID).toBe('custom')
|
|
expect(body.service).toBe('stripe')
|
|
expect(body.recipient).toBe(joeID)
|
|
expect(body.purchaser).toBe(joeID)
|
|
User.findById(joe.get('_id'), (err, user) ->
|
|
expect(user.get('stripe').customerID).toBe(body.stripe.customerID)
|
|
|
|
criteria =
|
|
recipient: user.get('_id')
|
|
purchaser: user.get('_id')
|
|
amount: amount
|
|
description: description
|
|
service: 'stripe'
|
|
"stripe.customerID": user.get('stripe').customerID
|
|
Payment.findOne criteria, (err, payment) ->
|
|
expect(err).toBeNull()
|
|
expect(payment).not.toBeNull()
|
|
done()
|
|
)
|
|
)
|
|
|
|
it 'handles a custom purchase without description', (done) ->
|
|
timestamp = new Date().getTime()
|
|
amount = 73000
|
|
|
|
stripe.tokens.create({
|
|
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
|
|
}, (err, token) ->
|
|
stripeTokenID = token.id
|
|
loginJoe (joe) ->
|
|
joeID = joe.get('_id') + ''
|
|
data = {
|
|
amount: amount
|
|
stripe: {
|
|
token: token.id
|
|
timestamp: timestamp
|
|
}
|
|
}
|
|
request.post {uri: customPaymentURL, json: data }, (err, res, body) ->
|
|
expect(res.statusCode).toBe 201
|
|
expect(body.stripe.chargeID).toBeDefined()
|
|
expect(body.stripe.timestamp).toBe(timestamp)
|
|
expect(body.stripe.customerID).toBeDefined()
|
|
expect(body.amount).toEqual(amount)
|
|
expect(body.productID).toBe('custom')
|
|
expect(body.service).toBe('stripe')
|
|
expect(body.recipient).toBe(joeID)
|
|
expect(body.purchaser).toBe(joeID)
|
|
User.findById(joe.get('_id'), (err, user) ->
|
|
expect(user.get('stripe').customerID).toBe(body.stripe.customerID)
|
|
|
|
criteria =
|
|
recipient: user.get('_id')
|
|
purchaser: user.get('_id')
|
|
amount: amount
|
|
service: 'stripe'
|
|
"stripe.customerID": user.get('stripe').customerID
|
|
Payment.findOne criteria, (err, payment) ->
|
|
expect(err).toBeNull()
|
|
expect(payment).not.toBeNull()
|
|
done()
|
|
)
|
|
)
|
|
|
|
it 'handles a custom purchase with invalid amount', (done) ->
|
|
timestamp = new Date().getTime()
|
|
amount = 'free?'
|
|
|
|
stripe.tokens.create({
|
|
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
|
|
}, (err, token) ->
|
|
stripeTokenID = token.id
|
|
loginJoe (joe) ->
|
|
joeID = joe.get('_id') + ''
|
|
data = {
|
|
amount: amount
|
|
stripe: {
|
|
token: token.id
|
|
timestamp: timestamp
|
|
}
|
|
}
|
|
request.post {uri: customPaymentURL, json: data }, (err, res, body) ->
|
|
expect(res.statusCode).toBe(400)
|
|
done()
|
|
)
|
|
|
|
it 'handles a custom purchase with no amount', (done) ->
|
|
timestamp = new Date().getTime()
|
|
|
|
stripe.tokens.create({
|
|
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
|
|
}, (err, token) ->
|
|
stripeTokenID = token.id
|
|
loginJoe (joe) ->
|
|
joeID = joe.get('_id') + ''
|
|
data = {
|
|
stripe: {
|
|
token: token.id
|
|
timestamp: timestamp
|
|
}
|
|
}
|
|
request.post {uri: customPaymentURL, json: data }, (err, res, body) ->
|
|
expect(res.statusCode).toBe(400)
|
|
done()
|
|
)
|