codecombat/test/server/functional/payment.spec.coffee

264 lines
No EOL
17 KiB
CoffeeScript

testReceipt = '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'
config = require '../../../server_config'
require '../common'
describe '/db/payment', ->
request = require 'request'
paymentURL = getURL('/db/payment')
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.cards.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.cards.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()
)