Cancel recipient subscriptions immediately

This commit is contained in:
Matt Lott 2015-06-17 16:31:40 -07:00
parent 593f7a9dd7
commit 5c240d89cd
3 changed files with 62 additions and 40 deletions

View file

@ -582,18 +582,26 @@ class SubscriptionHandler extends Handler
email = req.body.stripe.unsubscribeEmail.trim().toLowerCase()
return done({res: 'Database error.', code: 500}) if _.isEmpty(email)
deleteUserStripeProp = (user, propName) ->
stripeInfo = _.cloneDeep(user.get('stripe') ? {})
delete stripeInfo[propName]
if _.isEmpty stripeInfo
user.set 'stripe', undefined
else
user.set 'stripe', stripeInfo
User.findOne {emailLower: email}, (err, recipient) =>
if err
@logSubscriptionError(user, "User lookup error. " + err)
return done({res: 'Database error.', code: 500})
unless recipient
@logSubscriptionError(user, "Recipient #{req.body.stripe.recipient} not found. " + err)
@logSubscriptionError(user, "Recipient #{email} not found.")
return done({res: 'Database error.', code: 500})
# Check recipient is currently sponsored
stripeRecipient = recipient.get 'stripe' ? {}
if stripeRecipient?.sponsorID isnt user.id
@logSubscriptionError(user, "Recipient #{req.body.stripe.recipient} not found. " + err)
@logSubscriptionError(user, "Recipient #{req.body.stripe.recipient} not found. ")
return done({res: 'Can only unsubscribe sponsored subscriptions.', code: 403})
# Find recipient subscription
@ -603,22 +611,41 @@ class SubscriptionHandler extends Handler
sponsoredEntry = sponsored
break
unless sponsoredEntry?
@logSubscriptionError(user, 'Unable to find sponsored subscription. ' + err)
@logSubscriptionError(user, 'Unable to find recipient subscription. ')
return done({res: 'Database error.', code: 500})
# Cancel Stripe subscription
stripe.customers.cancelSubscription stripeInfo.customerID, sponsoredEntry.subscriptionID, { at_period_end: true }, (err) =>
if err or not recipient
@logSubscriptionError(user, "Stripe cancel sponsored subscription failed. " + err)
# Update recipient user
deleteUserStripeProp(recipient, 'sponsorID')
recipient.save (err) =>
if err
@logSubscriptionError(user, 'Recipient user save unsubscribe error. ' + err)
return done({res: 'Database error.', code: 500})
delete stripeInfo.unsubscribeEmail
user.set('stripe', stripeInfo)
req.body.stripe = stripeInfo
user.save (err) =>
# Cancel Stripe subscription
stripe.customers.cancelSubscription stripeInfo.customerID, sponsoredEntry.subscriptionID, (err) =>
if err
@logSubscriptionError(user, 'User save unsubscribe error. ' + err)
@logSubscriptionError(user, "Stripe cancel sponsored subscription failed. " + err)
return done({res: 'Database error.', code: 500})
done()
# Update sponsor user
_.remove(stripeInfo.recipients, (s) -> s.userID is recipient.id)
delete stripeInfo.unsubscribeEmail
user.set('stripe', stripeInfo)
req.body.stripe = stripeInfo
user.save (err) =>
if err
@logSubscriptionError(user, 'Sponsor user save unsubscribe error. ' + err)
return done({res: 'Database error.', code: 500})
return done() unless stripeInfo.sponsorSubscriptionID?
# Update sponsored subscription quantity
options =
quantity: getSponsoredSubsAmount(subscriptions.basic.amount, stripeInfo.recipients.length, stripeInfo.subscriptionID?)
stripe.customers.updateSubscription stripeInfo.customerID, stripeInfo.sponsorSubscriptionID, options, (err, subscription) =>
if err
logStripeWebhookError(err)
return res.send(500, '')
done()
module.exports = new SubscriptionHandler()

View file

@ -157,6 +157,10 @@ module.exports.setup = (app) ->
unless recipient
logStripeWebhookError("Recipient not found #{subscription.metadata.id}")
return res.send(500, '')
# Recipient cancellations are immediate, no work to perform if recipient's sponsorID is already gone
return res.send(200, '') unless recipient.get('stripe')?.sponsorID?
User.findById recipient.get('stripe').sponsorID, (err, sponsor) =>
if err
logStripeWebhookError(err)

View file

@ -259,6 +259,8 @@ describe 'Subscriptions', ->
# console.log 'verifyNotSponsoring', sponsorID, recipientID
User.findById sponsorID, (err, sponsor) ->
expect(err).toBeNull()
expect(sponsor).not.toBeNull()
return done() unless sponsor
stripeInfo = sponsor.get('stripe')
return done() unless stripeInfo?.customerID?
checkSubscriptions = (starting_after, done) ->
@ -282,6 +284,7 @@ describe 'Subscriptions', ->
User.findById sponsorUserID, (err, user) ->
expect(err).toBeNull()
expect(user).not.toBeNull()
return done() unless user
sponsorStripe = user.get('stripe')
sponsorCustomerID = sponsorStripe.customerID
numSponsored = sponsorStripe.recipients?.length
@ -443,7 +446,7 @@ describe 'Subscriptions', ->
expect(err?).toEqual(false)
done(updatedUser)
unsubscribeRecipient = (sponsor, recipient, immediately, done) ->
unsubscribeRecipient = (sponsor, recipient, done) ->
# console.log 'unsubscribeRecipient', sponsor.id, recipient.id
stripeInfo = sponsor.get('stripe')
customerID = stripeInfo.customerID
@ -467,20 +470,7 @@ describe 'Subscriptions', ->
request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, body) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(200)
# Simulate subscription ending after cancellation
return done() unless immediately
# Simulate subscription cancelling at period end
stripe.customers.cancelSubscription customerID, subscriptionID, (err) ->
expect(err).toBeNull()
# Simulate customer.subscription.deleted webhook event
event = _.cloneDeep(customerSubscriptionDeletedSampleEvent)
event.data.object = subscription
request.post {uri: webhookURL, json: event}, (err, res, body) ->
expect(err).toBeNull()
done()
done()
# Subscribe a bunch of recipients at once, used for bulk discount testing
class SubbedRecipients
@ -762,11 +752,11 @@ describe 'Subscriptions', ->
expect(err).toBeNull()
User.findById user1.id, (err, user1) ->
unsubscribeRecipient user1, user2, true, ->
unsubscribeRecipient user1, user2, ->
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
expect(user1.get('stripe').subscriptionID).toBeDefined()
expect(user1.get('stripe').recipients).toBeUndefined()
expect(_.isEmpty(user1.get('stripe').recipients)).toEqual(true)
expect(user1.isPremium()).toEqual(true)
User.findById user2.id, (err, user2) ->
verifyNotSponsoring user1.id, user2.id, ->
@ -781,7 +771,7 @@ describe 'Subscriptions', ->
loginNewUser (user1) ->
subscribeRecipients user1, [user2], token, (updatedUser) ->
User.findById user1.id, (err, user1) ->
unsubscribeRecipient user1, user2, true, ->
unsubscribeRecipient user1, user2, ->
verifyNotSponsoring user1.id, user2.id, ->
verifyNotRecipient user2.id, done
@ -793,7 +783,7 @@ describe 'Subscriptions', ->
loginNewUser (user1) ->
subscribeRecipients user1, [user2], token, (updatedUser) ->
User.findById user1.id, (err, user1) ->
unsubscribeRecipient user1, user2, false, ->
unsubscribeRecipient user1, user2, ->
subscribeRecipients user1, [user2], null, (updatedUser) ->
verifySponsorship user1.id, user2.id, done
@ -846,7 +836,7 @@ describe 'Subscriptions', ->
expect(err).toBeNull()
subscribeRecipients user1, [user2], null, (updatedUser) ->
User.findById user1.id, (err, user1) ->
unsubscribeRecipient user1, user2, true, ->
unsubscribeRecipient user1, user2, ->
User.findById user1.id, (err, user1) ->
expect(err).toBeNull()
expect(user1.get('stripe').subscriptionID).toBeDefined()
@ -1138,7 +1128,7 @@ describe 'Subscriptions', ->
User.findById user1.id, (err, user1) ->
# Unsubscribe recipient0
unsubscribeRecipient user1, recipients.get(0), true, ->
unsubscribeRecipient user1, recipients.get(0), ->
User.findById user1.id, (err, user1) ->
stripeInfo = user1.get('stripe')
expect(stripeInfo.recipients.length).toEqual(1)
@ -1150,7 +1140,7 @@ describe 'Subscriptions', ->
expect(subscription.quantity).toEqual(getSubscribedQuantity(1))
# Unsubscribe recipient1
unsubscribeRecipient user1, recipients.get(1), true, ->
unsubscribeRecipient user1, recipients.get(1), ->
User.findById user1.id, (err, user1) ->
stripeInfo = user1.get('stripe')
expect(stripeInfo.recipients.length).toEqual(0)
@ -1186,7 +1176,7 @@ describe 'Subscriptions', ->
User.findById user1.id, (err, user1) ->
# Unsubscribe first recipient
unsubscribeRecipient user1, recipients.get(0), true, ->
unsubscribeRecipient user1, recipients.get(0), ->
User.findById user1.id, (err, user1) ->
stripeInfo = user1.get('stripe')
expect(stripeInfo.recipients.length).toEqual(recipientCount - 1)
@ -1198,7 +1188,7 @@ describe 'Subscriptions', ->
expect(subscription.quantity).toEqual(getSubscribedQuantity(recipientCount - 1))
# Unsubscribe second recipient
unsubscribeRecipient user1, recipients.get(1), true, ->
unsubscribeRecipient user1, recipients.get(1), ->
User.findById user1.id, (err, user1) ->
stripeInfo = user1.get('stripe')
expect(stripeInfo.recipients.length).toEqual(recipientCount - 2)
@ -1218,7 +1208,7 @@ describe 'Subscriptions', ->
# Unsubscribe third recipient
verifySponsorship user1.id, recipients.get(2).id, ->
unsubscribeRecipient user1, recipients.get(2), true, ->
unsubscribeRecipient user1, recipients.get(2), ->
User.findById user1.id, (err, user1) ->
stripeInfo = user1.get('stripe')
expect(stripeInfo.recipients.length).toEqual(recipientCount - 3)
@ -1252,8 +1242,9 @@ describe 'Subscriptions', ->
User.findById user1.id, (err, user1) ->
# Unsubscribe first recipient
unsubscribeRecipient user1, recipients.get(0), true, ->
unsubscribeRecipient user1, recipients.get(0), ->
User.findById user1.id, (err, user1) ->
stripeInfo = user1.get('stripe')
expect(stripeInfo.recipients.length).toEqual(recipientCount - 1)
verifyNotSponsoring user1.id, recipients.get(0).id, ->
@ -1264,7 +1255,7 @@ describe 'Subscriptions', ->
expect(subscription.quantity).toEqual(getUnsubscribedQuantity(recipientCount - 1))
# Unsubscribe last recipient
unsubscribeRecipient user1, recipients.get(recipientCount - 1), true, ->
unsubscribeRecipient user1, recipients.get(recipientCount - 1), ->
User.findById user1.id, (err, user1) ->
stripeInfo = user1.get('stripe')
expect(stripeInfo.recipients.length).toEqual(recipientCount - 2)