🐛Fix Stripe web hook sub deleted for deleted user

Add check for deleted user in subscription deleted Stripe web hook
event handler, and equivalent test case.
Add logging for web hook 500 errors to aid future debugging.
This commit is contained in:
Matt Lott 2015-03-20 16:14:28 -07:00
parent 9a8da859df
commit 2561bc4caf
2 changed files with 81 additions and 14 deletions

View file

@ -11,6 +11,9 @@ module.exports.setup = (app) ->
# Cache customer -> user ID map (increases test perf considerably)
customerUserMap = {}
logStripeWebhookError = (msg) ->
console.warn "Stripe Webhook Error: #{msg}"
app.post '/stripe/webhook', (req, res) ->
# Subscription renewal events:
@ -102,6 +105,24 @@ module.exports.setup = (app) ->
subscription = req.body.data.object
checkUserExists = (done) ->
stripe.customers.retrieve subscription.customer, (err, customer) =>
if err
logStripeWebhookError("Failed to retrieve #{subscription.customer}")
return res.send(500, '')
unless customer?.metadata?.id
logStripeWebhookError("Customer with no metadata.id #{subscription.customer}")
return res.send(500, '')
User.findById customer.metadata.id, (err, user) =>
if err
logStripeWebhookError(err)
return res.send(500, '')
unless user
logStripeWebhookError("User not found #{customer.metadata.id}")
return res.send(500, '')
return res.send(200, '') if user.get('deleted') is true
done()
checkNormalSubscription = (done) ->
User.findOne {'stripe.subscriptionID': subscription.id}, (err, user) ->
return done() unless user
@ -112,17 +133,28 @@ module.exports.setup = (app) ->
delete stripeInfo.subscriptionID
user.set('stripe', stripeInfo)
user.save (err) =>
return res.send(500, '') if err
if err
logStripeWebhookError(err)
return res.send(500, '')
return res.send(200, '')
checkRecipientSubscription = (done) ->
return done() unless subscription.plan.id is 'basic'
return done() unless subscription.metadata?.id # Shouldn't be possible
User.findById subscription.metadata.id, (err, recipient) =>
return res.send(500, '') if err
return res.send(500, '') unless recipient
if err
logStripeWebhookError(err)
return res.send(500, '')
unless recipient
logStripeWebhookError("Recipient not found #{subscription.metadata.id}")
return res.send(500, '')
User.findById recipient.get('stripe').sponsorID, (err, sponsor) =>
return res.send(500, '') if err
return res.send(500, '') unless sponsor
if err
logStripeWebhookError(err)
return res.send(500, '')
unless sponsor
logStripeWebhookError("Sponsor not found #{recipient.get('stripe').sponsorID}")
return res.send(500, '')
# Update sponsor subscription
stripeInfo = _.cloneDeep(sponsor.get('stripe') ? {})
@ -130,12 +162,16 @@ module.exports.setup = (app) ->
options =
quantity: utils.getSponsoredSubsAmount(subscription.plan.amount, stripeInfo.recipients.length, stripeInfo.subscriptionID?)
stripe.customers.updateSubscription stripeInfo.customerID, stripeInfo.sponsorSubscriptionID, options, (err, subscription) =>
return res.send(500, '') if err
if err
logStripeWebhookError(err)
return res.send(500, '')
# Update sponsor user
sponsor.set 'stripe', stripeInfo
sponsor.save (err) =>
return res.send(500, '') if err
if err
logStripeWebhookError(err)
return res.send(500, '')
# Update recipient user
stripeInfo = recipient.get('stripe')
@ -145,7 +181,9 @@ module.exports.setup = (app) ->
else
recipient.set 'stripe', stripeInfo
recipient.save (err) =>
return res.send(500, '') if err
if err
logStripeWebhookError(err)
return res.send(500, '')
return res.send(200, '')
checkSponsorSubscription = (done) ->
@ -165,7 +203,9 @@ module.exports.setup = (app) ->
# Cancel all recipient subscriptions
async.parallel (createUpdateFn(sub) for sub in stripeInfo.recipients), (err, results) =>
return res.send(500, '') if err
if err
logStripeWebhookError(err)
return res.send(500, '')
# Update sponsor user
delete stripeInfo.sponsorSubscriptionID
@ -175,10 +215,13 @@ module.exports.setup = (app) ->
else
sponsor.set 'stripe', stripeInfo
sponsor.save (err) =>
return res.send(500, '') if err
if err
logStripeWebhookError(err)
return res.send(500, '')
done()
# TODO: use async.series for this
checkUserExists ->
checkNormalSubscription ->
checkRecipientSubscription ->
checkSponsorSubscription ->

View file

@ -681,6 +681,30 @@ describe 'Subscriptions', ->
expect(stripeInfo.subscriptionID).toBeUndefined()
done()
it 'User subscribes, deletes themselves, subscription ends', (done) ->
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)
done()
describe 'Sponsored', ->
it 'Unsubscribed user1 subscribes user2', (done) ->