mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-02-25 05:54:51 -05:00
🐛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:
parent
9a8da859df
commit
2561bc4caf
2 changed files with 81 additions and 14 deletions
|
@ -11,6 +11,9 @@ module.exports.setup = (app) ->
|
||||||
# Cache customer -> user ID map (increases test perf considerably)
|
# Cache customer -> user ID map (increases test perf considerably)
|
||||||
customerUserMap = {}
|
customerUserMap = {}
|
||||||
|
|
||||||
|
logStripeWebhookError = (msg) ->
|
||||||
|
console.warn "Stripe Webhook Error: #{msg}"
|
||||||
|
|
||||||
app.post '/stripe/webhook', (req, res) ->
|
app.post '/stripe/webhook', (req, res) ->
|
||||||
|
|
||||||
# Subscription renewal events:
|
# Subscription renewal events:
|
||||||
|
@ -102,6 +105,24 @@ module.exports.setup = (app) ->
|
||||||
|
|
||||||
subscription = req.body.data.object
|
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) ->
|
checkNormalSubscription = (done) ->
|
||||||
User.findOne {'stripe.subscriptionID': subscription.id}, (err, user) ->
|
User.findOne {'stripe.subscriptionID': subscription.id}, (err, user) ->
|
||||||
return done() unless user
|
return done() unless user
|
||||||
|
@ -112,17 +133,28 @@ module.exports.setup = (app) ->
|
||||||
delete stripeInfo.subscriptionID
|
delete stripeInfo.subscriptionID
|
||||||
user.set('stripe', stripeInfo)
|
user.set('stripe', stripeInfo)
|
||||||
user.save (err) =>
|
user.save (err) =>
|
||||||
return res.send(500, '') if err
|
if err
|
||||||
|
logStripeWebhookError(err)
|
||||||
|
return res.send(500, '')
|
||||||
return res.send(200, '')
|
return res.send(200, '')
|
||||||
|
|
||||||
checkRecipientSubscription = (done) ->
|
checkRecipientSubscription = (done) ->
|
||||||
return done() unless subscription.plan.id is 'basic'
|
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) =>
|
User.findById subscription.metadata.id, (err, recipient) =>
|
||||||
return res.send(500, '') if err
|
if err
|
||||||
return res.send(500, '') unless recipient
|
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) =>
|
User.findById recipient.get('stripe').sponsorID, (err, sponsor) =>
|
||||||
return res.send(500, '') if err
|
if err
|
||||||
return res.send(500, '') unless sponsor
|
logStripeWebhookError(err)
|
||||||
|
return res.send(500, '')
|
||||||
|
unless sponsor
|
||||||
|
logStripeWebhookError("Sponsor not found #{recipient.get('stripe').sponsorID}")
|
||||||
|
return res.send(500, '')
|
||||||
|
|
||||||
# Update sponsor subscription
|
# Update sponsor subscription
|
||||||
stripeInfo = _.cloneDeep(sponsor.get('stripe') ? {})
|
stripeInfo = _.cloneDeep(sponsor.get('stripe') ? {})
|
||||||
|
@ -130,12 +162,16 @@ module.exports.setup = (app) ->
|
||||||
options =
|
options =
|
||||||
quantity: utils.getSponsoredSubsAmount(subscription.plan.amount, stripeInfo.recipients.length, stripeInfo.subscriptionID?)
|
quantity: utils.getSponsoredSubsAmount(subscription.plan.amount, stripeInfo.recipients.length, stripeInfo.subscriptionID?)
|
||||||
stripe.customers.updateSubscription stripeInfo.customerID, stripeInfo.sponsorSubscriptionID, options, (err, subscription) =>
|
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
|
# Update sponsor user
|
||||||
sponsor.set 'stripe', stripeInfo
|
sponsor.set 'stripe', stripeInfo
|
||||||
sponsor.save (err) =>
|
sponsor.save (err) =>
|
||||||
return res.send(500, '') if err
|
if err
|
||||||
|
logStripeWebhookError(err)
|
||||||
|
return res.send(500, '')
|
||||||
|
|
||||||
# Update recipient user
|
# Update recipient user
|
||||||
stripeInfo = recipient.get('stripe')
|
stripeInfo = recipient.get('stripe')
|
||||||
|
@ -145,7 +181,9 @@ module.exports.setup = (app) ->
|
||||||
else
|
else
|
||||||
recipient.set 'stripe', stripeInfo
|
recipient.set 'stripe', stripeInfo
|
||||||
recipient.save (err) =>
|
recipient.save (err) =>
|
||||||
return res.send(500, '') if err
|
if err
|
||||||
|
logStripeWebhookError(err)
|
||||||
|
return res.send(500, '')
|
||||||
return res.send(200, '')
|
return res.send(200, '')
|
||||||
|
|
||||||
checkSponsorSubscription = (done) ->
|
checkSponsorSubscription = (done) ->
|
||||||
|
@ -165,7 +203,9 @@ module.exports.setup = (app) ->
|
||||||
|
|
||||||
# Cancel all recipient subscriptions
|
# Cancel all recipient subscriptions
|
||||||
async.parallel (createUpdateFn(sub) for sub in stripeInfo.recipients), (err, results) =>
|
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
|
# Update sponsor user
|
||||||
delete stripeInfo.sponsorSubscriptionID
|
delete stripeInfo.sponsorSubscriptionID
|
||||||
|
@ -175,10 +215,13 @@ module.exports.setup = (app) ->
|
||||||
else
|
else
|
||||||
sponsor.set 'stripe', stripeInfo
|
sponsor.set 'stripe', stripeInfo
|
||||||
sponsor.save (err) =>
|
sponsor.save (err) =>
|
||||||
return res.send(500, '') if err
|
if err
|
||||||
|
logStripeWebhookError(err)
|
||||||
|
return res.send(500, '')
|
||||||
done()
|
done()
|
||||||
|
|
||||||
# TODO: use async.series for this
|
# TODO: use async.series for this
|
||||||
|
checkUserExists ->
|
||||||
checkNormalSubscription ->
|
checkNormalSubscription ->
|
||||||
checkRecipientSubscription ->
|
checkRecipientSubscription ->
|
||||||
checkSponsorSubscription ->
|
checkSponsorSubscription ->
|
||||||
|
|
|
@ -681,6 +681,30 @@ describe 'Subscriptions', ->
|
||||||
expect(stripeInfo.subscriptionID).toBeUndefined()
|
expect(stripeInfo.subscriptionID).toBeUndefined()
|
||||||
done()
|
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', ->
|
describe 'Sponsored', ->
|
||||||
it 'Unsubscribed user1 subscribes user2', (done) ->
|
it 'Unsubscribed user1 subscribes user2', (done) ->
|
||||||
|
|
Loading…
Reference in a new issue