mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-27 09:35:39 -05:00
Sponsored sub cleanup on unsubscribe
Sponsored subscriptions can be partially set up when the incremental subscription charge fails. This cleans up the sponsored subscriptions when one of the recipient subscriptions is cancelled. Related to #2786
This commit is contained in:
parent
5a272f8bed
commit
9828bd5d1d
3 changed files with 90 additions and 23 deletions
|
@ -592,7 +592,7 @@ class SubscriptionHandler extends Handler
|
|||
|
||||
# Check recipient is currently sponsored
|
||||
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)
|
||||
return done({res: 'Can only unsubscribe sponsored subscriptions.', code: 403})
|
||||
|
||||
|
|
|
@ -141,6 +141,15 @@ module.exports.setup = (app) ->
|
|||
checkRecipientSubscription = (done) ->
|
||||
return done() unless subscription.plan.id is 'basic'
|
||||
return done() unless subscription.metadata?.id # Shouldn't be possible
|
||||
|
||||
deleteUserStripeProp = (user, propName) ->
|
||||
stripeInfo = _.cloneDeep(user.get('stripe') ? {})
|
||||
delete stripeInfo[propName]
|
||||
if _.isEmpty stripeInfo
|
||||
user.set 'stripe', undefined
|
||||
else
|
||||
user.set 'stripe', stripeInfo
|
||||
|
||||
User.findById subscription.metadata.id, (err, recipient) =>
|
||||
if err
|
||||
logStripeWebhookError(err)
|
||||
|
@ -158,33 +167,55 @@ module.exports.setup = (app) ->
|
|||
|
||||
# Update sponsor subscription
|
||||
stripeInfo = _.cloneDeep(sponsor.get('stripe') ? {})
|
||||
_.remove(stripeInfo.recipients, (s) -> s.userID is recipient.id)
|
||||
options =
|
||||
quantity: utils.getSponsoredSubsAmount(subscription.plan.amount, stripeInfo.recipients.length, stripeInfo.subscriptionID?)
|
||||
unless stripeInfo.sponsorSubscriptionID
|
||||
# TODO: fix #2786 error for a particular customer which doesn't have this
|
||||
console.error "Couldn't find sponsorSubscriptionID from stripeInfo", stripeInfo, 'for customer', stripeInfo.customerID, 'with options', options, 'and subscription', subscription, 'for user', recipient.id, 'with sponsor', sponsor.id
|
||||
return res.send(500, '')
|
||||
stripe.customers.updateSubscription stripeInfo.customerID, stripeInfo.sponsorSubscriptionID, options, (err, subscription) =>
|
||||
if err
|
||||
logStripeWebhookError(err)
|
||||
return res.send(500, '')
|
||||
stripeInfo.recipients ?= []
|
||||
|
||||
# Update sponsor user
|
||||
sponsor.set 'stripe', stripeInfo
|
||||
sponsor.save (err) =>
|
||||
if stripeInfo.sponsorSubscriptionID
|
||||
_.remove(stripeInfo.recipients, (s) -> s.userID is recipient.id)
|
||||
options =
|
||||
quantity: utils.getSponsoredSubsAmount(subscription.plan.amount, stripeInfo.recipients.length, stripeInfo.subscriptionID?)
|
||||
stripe.customers.updateSubscription stripeInfo.customerID, stripeInfo.sponsorSubscriptionID, options, (err, subscription) =>
|
||||
if err
|
||||
logStripeWebhookError(err)
|
||||
return res.send(500, '')
|
||||
|
||||
# Update recipient user
|
||||
stripeInfo = recipient.get('stripe')
|
||||
delete stripeInfo.sponsorID
|
||||
if _.isEmpty stripeInfo
|
||||
recipient.set 'stripe', undefined
|
||||
else
|
||||
recipient.set 'stripe', stripeInfo
|
||||
recipient.save (err) =>
|
||||
# Update sponsor user
|
||||
sponsor.set 'stripe', stripeInfo
|
||||
sponsor.save (err) =>
|
||||
if err
|
||||
logStripeWebhookError(err)
|
||||
return res.send(500, '')
|
||||
|
||||
# Update recipient user
|
||||
deleteUserStripeProp recipient, 'sponsorID'
|
||||
recipient.save (err) =>
|
||||
if err
|
||||
logStripeWebhookError(err)
|
||||
return res.send(500, '')
|
||||
return res.send(200, '')
|
||||
else
|
||||
# Remove sponsorships from sponsor and recipients
|
||||
console.error "Couldn't find sponsorSubscriptionID from stripeInfo", stripeInfo, 'for customer', stripeInfo.customerID, 'with options', options, 'and subscription', subscription, 'for user', recipient.id, 'with sponsor', sponsor.id
|
||||
|
||||
# Update recipients
|
||||
createUpdateFn = (recipientID) ->
|
||||
(callback) ->
|
||||
User.findById recipientID, (err, recipient) =>
|
||||
if err
|
||||
logStripeWebhookError(err)
|
||||
return callback(err)
|
||||
|
||||
deleteUserStripeProp recipient, 'sponsorID'
|
||||
recipient.save (err) =>
|
||||
logStripeWebhookError(err) if err
|
||||
callback(err)
|
||||
async.parallel (createUpdateFn(recipient.userID) for recipient in stripeInfo.recipients), (err, results) =>
|
||||
if err
|
||||
logStripeWebhookError(err)
|
||||
return res.send(500, '')
|
||||
|
||||
# Update sponsor
|
||||
deleteUserStripeProp sponsor, 'recipients'
|
||||
sponsor.save (err) =>
|
||||
if err
|
||||
logStripeWebhookError(err)
|
||||
return res.send(500, '')
|
||||
|
|
|
@ -291,6 +291,7 @@ describe 'Subscriptions', ->
|
|||
expect(numSponsored).toBeGreaterThan(0)
|
||||
|
||||
# Verify Stripe sponsor subscription data
|
||||
return done() unless sponsorCustomerID and sponsorStripe.sponsorSubscriptionID
|
||||
stripe.customers.retrieveSubscription sponsorCustomerID, sponsorStripe.sponsorSubscriptionID, (err, subscription) ->
|
||||
expect(err).toBeNull()
|
||||
expect(subscription.plan.amount).toEqual(1)
|
||||
|
@ -328,6 +329,7 @@ describe 'Subscriptions', ->
|
|||
expect(recipientInfo.couponID).toEqual('free')
|
||||
|
||||
# Verify Stripe recipient subscription data
|
||||
return done() unless sponsorCustomerID and recipientInfo.subscriptionID
|
||||
stripe.customers.retrieveSubscription sponsorCustomerID, recipientInfo.subscriptionID, (err, subscription) ->
|
||||
expect(err).toBeNull()
|
||||
expect(subscription.plan.amount).toEqual(subPrice)
|
||||
|
@ -445,6 +447,8 @@ describe 'Subscriptions', ->
|
|||
# console.log 'unsubscribeRecipient', sponsor.id, recipient.id
|
||||
stripeInfo = sponsor.get('stripe')
|
||||
customerID = stripeInfo.customerID
|
||||
expect(stripeInfo.recipients).toBeDefined()
|
||||
return done() unless stripeInfo.recipients
|
||||
for r in stripeInfo.recipients
|
||||
if r.userID is recipient.id
|
||||
subscriptionID = r.subscriptionID
|
||||
|
@ -739,6 +743,38 @@ describe 'Subscriptions', ->
|
|||
expect(user1.isPremium()).toEqual(true)
|
||||
verifySponsorship user1.id, user2.id, done
|
||||
|
||||
it 'Clean up sponsorships upon sub cancel after setup sponsor sub fails', (done) ->
|
||||
stripe.tokens.create {
|
||||
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
|
||||
}, (err, token) ->
|
||||
createNewUser (user2) ->
|
||||
loginNewUser (user1) ->
|
||||
subscribeUser user1, token, null, (updatedUser) ->
|
||||
User.findById user1.id, (err, user1) ->
|
||||
expect(err).toBeNull()
|
||||
subscribeRecipients user1, [user2], null, (updatedUser) ->
|
||||
|
||||
# Delete user1 sponsorSubscriptionID to simulate failed sponsor sub
|
||||
User.findById user1.id, (err, user1) ->
|
||||
expect(err).toBeNull()
|
||||
stripeInfo = _.cloneDeep(user1.get('stripe') ? {})
|
||||
delete stripeInfo.sponsorSubscriptionID
|
||||
user1.set 'stripe', stripeInfo
|
||||
user1.save (err, user1) ->
|
||||
expect(err).toBeNull()
|
||||
|
||||
User.findById user1.id, (err, user1) ->
|
||||
unsubscribeRecipient user1, user2, true, ->
|
||||
User.findById user1.id, (err, user1) ->
|
||||
expect(err).toBeNull()
|
||||
expect(user1.get('stripe').subscriptionID).toBeDefined()
|
||||
expect(user1.get('stripe').recipients).toBeUndefined()
|
||||
expect(user1.isPremium()).toEqual(true)
|
||||
User.findById user2.id, (err, user2) ->
|
||||
verifyNotSponsoring user1.id, user2.id, ->
|
||||
verifyNotRecipient user2.id, done
|
||||
|
||||
|
||||
it 'Unsubscribed user1 unsubscribes user2 and their sub ends', (done) ->
|
||||
stripe.tokens.create {
|
||||
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
|
||||
|
|
Loading…
Reference in a new issue