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() )