mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-02-25 05:54:51 -05:00
Cancel recipient subscriptions immediately
This commit is contained in:
parent
593f7a9dd7
commit
5c240d89cd
3 changed files with 62 additions and 40 deletions
|
@ -582,18 +582,26 @@ class SubscriptionHandler extends Handler
|
||||||
email = req.body.stripe.unsubscribeEmail.trim().toLowerCase()
|
email = req.body.stripe.unsubscribeEmail.trim().toLowerCase()
|
||||||
return done({res: 'Database error.', code: 500}) if _.isEmpty(email)
|
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) =>
|
User.findOne {emailLower: email}, (err, recipient) =>
|
||||||
if err
|
if err
|
||||||
@logSubscriptionError(user, "User lookup error. " + err)
|
@logSubscriptionError(user, "User lookup error. " + err)
|
||||||
return done({res: 'Database error.', code: 500})
|
return done({res: 'Database error.', code: 500})
|
||||||
unless recipient
|
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})
|
return done({res: 'Database error.', code: 500})
|
||||||
|
|
||||||
# Check recipient is currently sponsored
|
# Check recipient is currently sponsored
|
||||||
stripeRecipient = recipient.get 'stripe' ? {}
|
stripeRecipient = recipient.get 'stripe' ? {}
|
||||||
if stripeRecipient?.sponsorID isnt user.id
|
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})
|
return done({res: 'Can only unsubscribe sponsored subscriptions.', code: 403})
|
||||||
|
|
||||||
# Find recipient subscription
|
# Find recipient subscription
|
||||||
|
@ -603,22 +611,41 @@ class SubscriptionHandler extends Handler
|
||||||
sponsoredEntry = sponsored
|
sponsoredEntry = sponsored
|
||||||
break
|
break
|
||||||
unless sponsoredEntry?
|
unless sponsoredEntry?
|
||||||
@logSubscriptionError(user, 'Unable to find sponsored subscription. ' + err)
|
@logSubscriptionError(user, 'Unable to find recipient subscription. ')
|
||||||
|
return done({res: 'Database error.', code: 500})
|
||||||
|
|
||||||
|
# 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})
|
return done({res: 'Database error.', code: 500})
|
||||||
|
|
||||||
# Cancel Stripe subscription
|
# Cancel Stripe subscription
|
||||||
stripe.customers.cancelSubscription stripeInfo.customerID, sponsoredEntry.subscriptionID, { at_period_end: true }, (err) =>
|
stripe.customers.cancelSubscription stripeInfo.customerID, sponsoredEntry.subscriptionID, (err) =>
|
||||||
if err or not recipient
|
if err
|
||||||
@logSubscriptionError(user, "Stripe cancel sponsored subscription failed. " + err)
|
@logSubscriptionError(user, "Stripe cancel sponsored subscription failed. " + err)
|
||||||
return done({res: 'Database error.', code: 500})
|
return done({res: 'Database error.', code: 500})
|
||||||
|
|
||||||
|
# Update sponsor user
|
||||||
|
_.remove(stripeInfo.recipients, (s) -> s.userID is recipient.id)
|
||||||
delete stripeInfo.unsubscribeEmail
|
delete stripeInfo.unsubscribeEmail
|
||||||
user.set('stripe', stripeInfo)
|
user.set('stripe', stripeInfo)
|
||||||
req.body.stripe = stripeInfo
|
req.body.stripe = stripeInfo
|
||||||
user.save (err) =>
|
user.save (err) =>
|
||||||
if err
|
if err
|
||||||
@logSubscriptionError(user, 'User save unsubscribe error. ' + err)
|
@logSubscriptionError(user, 'Sponsor user save unsubscribe error. ' + err)
|
||||||
return done({res: 'Database error.', code: 500})
|
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()
|
done()
|
||||||
|
|
||||||
module.exports = new SubscriptionHandler()
|
module.exports = new SubscriptionHandler()
|
||||||
|
|
|
@ -157,6 +157,10 @@ module.exports.setup = (app) ->
|
||||||
unless recipient
|
unless recipient
|
||||||
logStripeWebhookError("Recipient not found #{subscription.metadata.id}")
|
logStripeWebhookError("Recipient not found #{subscription.metadata.id}")
|
||||||
return res.send(500, '')
|
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) =>
|
User.findById recipient.get('stripe').sponsorID, (err, sponsor) =>
|
||||||
if err
|
if err
|
||||||
logStripeWebhookError(err)
|
logStripeWebhookError(err)
|
||||||
|
|
|
@ -259,6 +259,8 @@ describe 'Subscriptions', ->
|
||||||
# console.log 'verifyNotSponsoring', sponsorID, recipientID
|
# console.log 'verifyNotSponsoring', sponsorID, recipientID
|
||||||
User.findById sponsorID, (err, sponsor) ->
|
User.findById sponsorID, (err, sponsor) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
|
expect(sponsor).not.toBeNull()
|
||||||
|
return done() unless sponsor
|
||||||
stripeInfo = sponsor.get('stripe')
|
stripeInfo = sponsor.get('stripe')
|
||||||
return done() unless stripeInfo?.customerID?
|
return done() unless stripeInfo?.customerID?
|
||||||
checkSubscriptions = (starting_after, done) ->
|
checkSubscriptions = (starting_after, done) ->
|
||||||
|
@ -282,6 +284,7 @@ describe 'Subscriptions', ->
|
||||||
User.findById sponsorUserID, (err, user) ->
|
User.findById sponsorUserID, (err, user) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
expect(user).not.toBeNull()
|
expect(user).not.toBeNull()
|
||||||
|
return done() unless user
|
||||||
sponsorStripe = user.get('stripe')
|
sponsorStripe = user.get('stripe')
|
||||||
sponsorCustomerID = sponsorStripe.customerID
|
sponsorCustomerID = sponsorStripe.customerID
|
||||||
numSponsored = sponsorStripe.recipients?.length
|
numSponsored = sponsorStripe.recipients?.length
|
||||||
|
@ -443,7 +446,7 @@ describe 'Subscriptions', ->
|
||||||
expect(err?).toEqual(false)
|
expect(err?).toEqual(false)
|
||||||
done(updatedUser)
|
done(updatedUser)
|
||||||
|
|
||||||
unsubscribeRecipient = (sponsor, recipient, immediately, done) ->
|
unsubscribeRecipient = (sponsor, recipient, done) ->
|
||||||
# console.log 'unsubscribeRecipient', sponsor.id, recipient.id
|
# console.log 'unsubscribeRecipient', sponsor.id, recipient.id
|
||||||
stripeInfo = sponsor.get('stripe')
|
stripeInfo = sponsor.get('stripe')
|
||||||
customerID = stripeInfo.customerID
|
customerID = stripeInfo.customerID
|
||||||
|
@ -467,19 +470,6 @@ describe 'Subscriptions', ->
|
||||||
request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, body) ->
|
request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, body) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
expect(res.statusCode).toBe(200)
|
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
|
# Subscribe a bunch of recipients at once, used for bulk discount testing
|
||||||
|
@ -762,11 +752,11 @@ describe 'Subscriptions', ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
|
|
||||||
User.findById user1.id, (err, user1) ->
|
User.findById user1.id, (err, user1) ->
|
||||||
unsubscribeRecipient user1, user2, true, ->
|
unsubscribeRecipient user1, user2, ->
|
||||||
User.findById user1.id, (err, user1) ->
|
User.findById user1.id, (err, user1) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
expect(user1.get('stripe').subscriptionID).toBeDefined()
|
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)
|
expect(user1.isPremium()).toEqual(true)
|
||||||
User.findById user2.id, (err, user2) ->
|
User.findById user2.id, (err, user2) ->
|
||||||
verifyNotSponsoring user1.id, user2.id, ->
|
verifyNotSponsoring user1.id, user2.id, ->
|
||||||
|
@ -781,7 +771,7 @@ describe 'Subscriptions', ->
|
||||||
loginNewUser (user1) ->
|
loginNewUser (user1) ->
|
||||||
subscribeRecipients user1, [user2], token, (updatedUser) ->
|
subscribeRecipients user1, [user2], token, (updatedUser) ->
|
||||||
User.findById user1.id, (err, user1) ->
|
User.findById user1.id, (err, user1) ->
|
||||||
unsubscribeRecipient user1, user2, true, ->
|
unsubscribeRecipient user1, user2, ->
|
||||||
verifyNotSponsoring user1.id, user2.id, ->
|
verifyNotSponsoring user1.id, user2.id, ->
|
||||||
verifyNotRecipient user2.id, done
|
verifyNotRecipient user2.id, done
|
||||||
|
|
||||||
|
@ -793,7 +783,7 @@ describe 'Subscriptions', ->
|
||||||
loginNewUser (user1) ->
|
loginNewUser (user1) ->
|
||||||
subscribeRecipients user1, [user2], token, (updatedUser) ->
|
subscribeRecipients user1, [user2], token, (updatedUser) ->
|
||||||
User.findById user1.id, (err, user1) ->
|
User.findById user1.id, (err, user1) ->
|
||||||
unsubscribeRecipient user1, user2, false, ->
|
unsubscribeRecipient user1, user2, ->
|
||||||
subscribeRecipients user1, [user2], null, (updatedUser) ->
|
subscribeRecipients user1, [user2], null, (updatedUser) ->
|
||||||
verifySponsorship user1.id, user2.id, done
|
verifySponsorship user1.id, user2.id, done
|
||||||
|
|
||||||
|
@ -846,7 +836,7 @@ describe 'Subscriptions', ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
subscribeRecipients user1, [user2], null, (updatedUser) ->
|
subscribeRecipients user1, [user2], null, (updatedUser) ->
|
||||||
User.findById user1.id, (err, user1) ->
|
User.findById user1.id, (err, user1) ->
|
||||||
unsubscribeRecipient user1, user2, true, ->
|
unsubscribeRecipient user1, user2, ->
|
||||||
User.findById user1.id, (err, user1) ->
|
User.findById user1.id, (err, user1) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
expect(user1.get('stripe').subscriptionID).toBeDefined()
|
expect(user1.get('stripe').subscriptionID).toBeDefined()
|
||||||
|
@ -1138,7 +1128,7 @@ describe 'Subscriptions', ->
|
||||||
User.findById user1.id, (err, user1) ->
|
User.findById user1.id, (err, user1) ->
|
||||||
|
|
||||||
# Unsubscribe recipient0
|
# Unsubscribe recipient0
|
||||||
unsubscribeRecipient user1, recipients.get(0), true, ->
|
unsubscribeRecipient user1, recipients.get(0), ->
|
||||||
User.findById user1.id, (err, user1) ->
|
User.findById user1.id, (err, user1) ->
|
||||||
stripeInfo = user1.get('stripe')
|
stripeInfo = user1.get('stripe')
|
||||||
expect(stripeInfo.recipients.length).toEqual(1)
|
expect(stripeInfo.recipients.length).toEqual(1)
|
||||||
|
@ -1150,7 +1140,7 @@ describe 'Subscriptions', ->
|
||||||
expect(subscription.quantity).toEqual(getSubscribedQuantity(1))
|
expect(subscription.quantity).toEqual(getSubscribedQuantity(1))
|
||||||
|
|
||||||
# Unsubscribe recipient1
|
# Unsubscribe recipient1
|
||||||
unsubscribeRecipient user1, recipients.get(1), true, ->
|
unsubscribeRecipient user1, recipients.get(1), ->
|
||||||
User.findById user1.id, (err, user1) ->
|
User.findById user1.id, (err, user1) ->
|
||||||
stripeInfo = user1.get('stripe')
|
stripeInfo = user1.get('stripe')
|
||||||
expect(stripeInfo.recipients.length).toEqual(0)
|
expect(stripeInfo.recipients.length).toEqual(0)
|
||||||
|
@ -1186,7 +1176,7 @@ describe 'Subscriptions', ->
|
||||||
User.findById user1.id, (err, user1) ->
|
User.findById user1.id, (err, user1) ->
|
||||||
|
|
||||||
# Unsubscribe first recipient
|
# Unsubscribe first recipient
|
||||||
unsubscribeRecipient user1, recipients.get(0), true, ->
|
unsubscribeRecipient user1, recipients.get(0), ->
|
||||||
User.findById user1.id, (err, user1) ->
|
User.findById user1.id, (err, user1) ->
|
||||||
stripeInfo = user1.get('stripe')
|
stripeInfo = user1.get('stripe')
|
||||||
expect(stripeInfo.recipients.length).toEqual(recipientCount - 1)
|
expect(stripeInfo.recipients.length).toEqual(recipientCount - 1)
|
||||||
|
@ -1198,7 +1188,7 @@ describe 'Subscriptions', ->
|
||||||
expect(subscription.quantity).toEqual(getSubscribedQuantity(recipientCount - 1))
|
expect(subscription.quantity).toEqual(getSubscribedQuantity(recipientCount - 1))
|
||||||
|
|
||||||
# Unsubscribe second recipient
|
# Unsubscribe second recipient
|
||||||
unsubscribeRecipient user1, recipients.get(1), true, ->
|
unsubscribeRecipient user1, recipients.get(1), ->
|
||||||
User.findById user1.id, (err, user1) ->
|
User.findById user1.id, (err, user1) ->
|
||||||
stripeInfo = user1.get('stripe')
|
stripeInfo = user1.get('stripe')
|
||||||
expect(stripeInfo.recipients.length).toEqual(recipientCount - 2)
|
expect(stripeInfo.recipients.length).toEqual(recipientCount - 2)
|
||||||
|
@ -1218,7 +1208,7 @@ describe 'Subscriptions', ->
|
||||||
|
|
||||||
# Unsubscribe third recipient
|
# Unsubscribe third recipient
|
||||||
verifySponsorship user1.id, recipients.get(2).id, ->
|
verifySponsorship user1.id, recipients.get(2).id, ->
|
||||||
unsubscribeRecipient user1, recipients.get(2), true, ->
|
unsubscribeRecipient user1, recipients.get(2), ->
|
||||||
User.findById user1.id, (err, user1) ->
|
User.findById user1.id, (err, user1) ->
|
||||||
stripeInfo = user1.get('stripe')
|
stripeInfo = user1.get('stripe')
|
||||||
expect(stripeInfo.recipients.length).toEqual(recipientCount - 3)
|
expect(stripeInfo.recipients.length).toEqual(recipientCount - 3)
|
||||||
|
@ -1252,8 +1242,9 @@ describe 'Subscriptions', ->
|
||||||
User.findById user1.id, (err, user1) ->
|
User.findById user1.id, (err, user1) ->
|
||||||
|
|
||||||
# Unsubscribe first recipient
|
# Unsubscribe first recipient
|
||||||
unsubscribeRecipient user1, recipients.get(0), true, ->
|
unsubscribeRecipient user1, recipients.get(0), ->
|
||||||
User.findById user1.id, (err, user1) ->
|
User.findById user1.id, (err, user1) ->
|
||||||
|
|
||||||
stripeInfo = user1.get('stripe')
|
stripeInfo = user1.get('stripe')
|
||||||
expect(stripeInfo.recipients.length).toEqual(recipientCount - 1)
|
expect(stripeInfo.recipients.length).toEqual(recipientCount - 1)
|
||||||
verifyNotSponsoring user1.id, recipients.get(0).id, ->
|
verifyNotSponsoring user1.id, recipients.get(0).id, ->
|
||||||
|
@ -1264,7 +1255,7 @@ describe 'Subscriptions', ->
|
||||||
expect(subscription.quantity).toEqual(getUnsubscribedQuantity(recipientCount - 1))
|
expect(subscription.quantity).toEqual(getUnsubscribedQuantity(recipientCount - 1))
|
||||||
|
|
||||||
# Unsubscribe last recipient
|
# Unsubscribe last recipient
|
||||||
unsubscribeRecipient user1, recipients.get(recipientCount - 1), true, ->
|
unsubscribeRecipient user1, recipients.get(recipientCount - 1), ->
|
||||||
User.findById user1.id, (err, user1) ->
|
User.findById user1.id, (err, user1) ->
|
||||||
stripeInfo = user1.get('stripe')
|
stripeInfo = user1.get('stripe')
|
||||||
expect(stripeInfo.recipients.length).toEqual(recipientCount - 2)
|
expect(stripeInfo.recipients.length).toEqual(recipientCount - 2)
|
||||||
|
|
Loading…
Reference in a new issue