2014-12-02 20:01:35 -08:00
# Not paired with a document in the DB, just handles coordinating between
# the stripe property in the user with what's being stored in Stripe.
2015-04-07 15:00:50 -07:00
mongoose = require ' mongoose '
2015-03-13 15:19:20 -07:00
async = require ' async '
2015-03-27 11:22:21 -07:00
config = require ' ../../server_config '
2014-12-02 20:01:35 -08:00
Handler = require ' ../commons/Handler '
2014-12-04 17:41:17 -08:00
discountHandler = require ' ./discount_handler '
2015-03-19 15:02:45 -07:00
Prepaid = require ' ../prepaids/Prepaid '
2015-03-13 15:19:20 -07:00
User = require ' ../users/User '
{ findStripeSubscription } = require ' ../lib/utils '
{ getSponsoredSubsAmount } = require ' ../../app/core/utils '
2014-12-02 20:01:35 -08:00
2015-03-13 15:19:20 -07:00
recipientCouponID = ' free '
2014-12-02 20:01:35 -08:00
subscriptions = {
basic: {
gems: 3500
2015-03-13 15:19:20 -07:00
amount: 999 # For calculating incremental quantity before sub creation
2014-12-02 20:01:35 -08:00
}
}
class SubscriptionHandler extends Handler
2014-12-05 15:49:38 -08:00
logSubscriptionError: (user, msg) ->
console . warn " Subscription Error: #{ user . get ( ' slug ' ) } ( #{ user . _id } ): ' #{ msg } ' "
2014-12-02 20:01:35 -08:00
2015-03-27 11:22:21 -07:00
getByRelationship: (req, res, args...) ->
2015-04-07 15:00:50 -07:00
return @ getSubscribers ( req , res ) if args [ 1 ] is ' subscribers '
2015-03-27 11:22:21 -07:00
return @ getSubscriptions ( req , res ) if args [ 1 ] is ' subscriptions '
super ( arguments . . . )
2015-04-07 15:00:50 -07:00
getSubscribers: (req, res) ->
return @ sendForbiddenError ( res ) unless req . user and req . user . isAdmin ( )
maxReturnCount = req . body . maxCount or 20
# @subscribers ?= []
# return @sendSuccess(res, @subscribers) unless _.isEmpty(@subscribers)
@subscribers = [ ]
subscriberIDs = [ ]
customersProcessed = 0
nextBatch = (starting_after, done) =>
options = limit: 100
options.starting_after = starting_after if starting_after
stripe . customers . list options , (err, customers) =>
return done ( err ) if err
customersProcessed += customers . data . length
for customer in customers . data
break unless @ subscribers . length < maxReturnCount
continue unless customer ? . subscriptions ? . data ? . length > 0
for subscription in customer . subscriptions . data
continue unless subscription . plan . id is ' basic '
amount = subscription . plan . amount
if subscription ? . discount ? . coupon ?
if subscription . discount . coupon . percent_off
amount = amount * ( 100 - subscription . discount . coupon . percent_off ) / 100 ;
else if subscription . discount . coupon . amount_off
amount -= subscription . discount . coupon . amount_off
else if customer . discount ? . coupon ?
if customer . discount . coupon . percent_off
amount = amount * ( 100 - customer . discount . coupon . percent_off ) / 100
else if customer . discount . coupon . amount_off
amount -= customer . discount . coupon . amount_off
continue unless amount > 0
subscriber = start: new Date ( subscription . start * 1000 )
if subscription . metadata ? . id ?
subscriber.userID = subscription . metadata . id
subscriberIDs . push subscription . metadata . id
if subscription . cancel_at_period_end
subscriber.cancel = new Date ( subscription . canceled_at * 1000 )
subscriber.end = new Date ( subscription . current_period_end * 1000 )
@ subscribers . push ( subscriber )
if customers . has_more and @ subscribers . length < maxReturnCount
return nextBatch ( customers . data [ customers . data . length - 1 ] . id , done )
else
return done ( )
nextBatch null , (err) =>
return @ sendDatabaseError ( res , err ) if err
User . find { _id: { $in: subscriberIDs } } , (err, users) =>
return @ sendDatabaseError ( res , err ) if err
for user in users
subscriber.user = user for subscriber in @ subscribers when subscriber . userID is user . id
@ sendSuccess ( res , @ subscribers )
2015-03-27 11:22:21 -07:00
getSubscriptions: (req, res) ->
# Returns a list of active subscriptions
# TODO: does not handle customers with 11+ active subscriptions
# TODO: does not track sponsored subs, only basic
# TODO: does not return free subs
# TODO: add tests
# TODO: aggregate this data daily instead of providing it on demand
# TODO: take date range as input
return @ sendForbiddenError ( res ) unless req . user and req . user . isAdmin ( )
2015-03-27 15:27:22 -07:00
# @subs ?= []
2015-03-27 11:22:21 -07:00
# return @sendSuccess(res, @subs) unless _.isEmpty(@subs)
2015-03-27 15:27:22 -07:00
@subs = [ ]
2015-03-27 11:22:21 -07:00
customersProcessed = 0
nextBatch = (starting_after, done) =>
options = limit: 100
options.starting_after = starting_after if starting_after
stripe . customers . list options , (err, customers) =>
return done ( err ) if err
customersProcessed += customers . data . length
for customer in customers . data
continue unless customer ? . subscriptions ? . data ? . length > 0
for subscription in customer . subscriptions . data
continue unless subscription . plan . id is ' basic '
amount = subscription . plan . amount
if subscription ? . discount ? . coupon ?
if subscription . discount . coupon . percent_off
amount = amount * ( 100 - subscription . discount . coupon . percent_off ) / 100 ;
else if subscription . discount . coupon . amount_off
amount -= subscription . discount . coupon . amount_off
else if customer . discount ? . coupon ?
if customer . discount . coupon . percent_off
amount = amount * ( 100 - customer . discount . coupon . percent_off ) / 100
else if customer . discount . coupon . amount_off
amount -= customer . discount . coupon . amount_off
continue unless amount > 0
sub = start: new Date ( subscription . start * 1000 )
if subscription . cancel_at_period_end
sub.cancel = new Date ( subscription . canceled_at * 1000 )
2015-04-07 15:00:50 -07:00
sub.end = new Date ( subscription . current_period_end * 1000 )
2015-03-27 11:22:21 -07:00
@ subs . push ( sub )
# Can't fetch all the test Stripe data
if customers . has_more and ( config . isProduction or customersProcessed < 500 )
return nextBatch ( customers . data [ customers . data . length - 1 ] . id , done )
else
return done ( )
nextBatch null , (err) =>
return @ sendDatabaseError ( res , err ) if err
@ sendSuccess ( res , @ subs )
2014-12-02 20:01:35 -08:00
subscribeUser: (req, user, done) ->
2014-12-05 15:49:38 -08:00
if ( not req . user ) or req . user . isAnonymous ( ) or user . isAnonymous ( )
2014-12-04 13:07:00 -08:00
return done ( { res: ' You must be signed in to subscribe. ' , code: 403 } )
2014-12-05 14:11:38 -08:00
token = req . body . stripe . token
2015-03-19 15:02:45 -07:00
prepaidCode = req . body . stripe . prepaidCode
2014-12-04 17:41:17 -08:00
customerID = user . get ( ' stripe ' ) ? . customerID
2015-03-19 15:02:45 -07:00
if not ( token or customerID or prepaidCode )
@ logSubscriptionError ( user , ' Missing Stripe token or customer ID or prepaid code ' )
return done ( { res: ' Missing Stripe token or customer ID or prepaid code ' , code: 422 } )
2014-12-02 20:01:35 -08:00
2015-03-19 15:02:45 -07:00
# Get Stripe customer
if customerID
if token
2014-12-04 17:41:17 -08:00
stripe . customers . update customerID , { card: token } , (err, customer) =>
2014-12-05 14:11:38 -08:00
if err or not customer
# should not happen outside of test and production polluting each other
2015-03-13 15:19:20 -07:00
@ logSubscriptionError ( user , ' Cannot find customer: ' + customerID + ' \n \n ' + err )
2014-12-05 14:11:38 -08:00
return done ( { res: ' Cannot find customer. ' , code: 404 } )
2015-03-19 15:02:45 -07:00
@ checkForCoupon ( req , user , customer , done )
2014-12-04 17:41:17 -08:00
else
2015-03-19 15:02:45 -07:00
stripe . customers . retrieve customerID , (err, customer) =>
2014-12-04 17:41:17 -08:00
if err
2015-03-19 15:02:45 -07:00
@ logSubscriptionError ( user , ' Stripe customer retrieve error. ' + err )
return done ( { res: ' Database error. ' , code: 500 } )
@ checkForCoupon ( req , user , customer , done )
2014-12-02 20:01:35 -08:00
else
2015-03-19 15:02:45 -07:00
options =
email: user . get ( ' email ' )
metadata: { id: user . _id + ' ' , slug: user . get ( ' slug ' ) }
options.card = token if token ?
stripe . customers . create options , (err, customer) =>
2014-12-02 20:01:35 -08:00
if err
2015-03-19 15:02:45 -07:00
if err . type in [ ' StripeCardError ' , ' StripeInvalidRequestError ' ]
return done ( { res: ' Card error ' , code: 402 } )
else
@ logSubscriptionError ( user , ' Stripe customer creation error. ' + err )
return done ( { res: ' Database error. ' , code: 500 } )
2014-12-02 20:01:35 -08:00
2015-03-19 15:02:45 -07:00
stripeInfo = _ . cloneDeep ( user . get ( ' stripe ' ) ? { } )
stripeInfo.customerID = customer . id
user . set ( ' stripe ' , stripeInfo )
user . save (err) =>
if err
@ logSubscriptionError ( user , ' Stripe customer id save db error. ' + err )
return done ( { res: ' Database error. ' , code: 500 } )
@ checkForCoupon ( req , user , customer , done )
checkForCoupon: (req, user, customer, done) ->
2015-03-13 15:19:20 -07:00
# Check if user is subscribing someone else
if req . body . stripe ? . subscribeEmails ?
return @ updateStripeRecipientSubscriptions req , user , customer , done
if user . get ( ' stripe ' ) ? . sponsorID
return done ( { res: ' You already have a sponsored subscription. ' , code: 403 } )
2015-03-19 15:02:45 -07:00
if req . body ? . stripe ? . prepaidCode
Prepaid . findOne code: req . body . stripe . prepaidCode , (err, prepaid) =>
if err
@ logSubscriptionError ( user , ' Prepaid lookup error. ' + err )
return done ( { res: ' Database error. ' , code: 500 } )
return done ( { res: ' Prepaid not found ' , code: 404 } ) unless prepaid ?
return done ( { res: ' Prepaid not for subscription ' , code: 403 } ) unless prepaid . get ( ' type ' ) is ' subscription '
return done ( { res: ' Prepaid has already been used ' , code: 403 } ) unless prepaid . get ( ' status ' ) is ' active '
return done ( { res: ' Database error. ' , code: 500 } ) unless prepaid . get ( ' properties ' ) ? . couponID
couponID = prepaid . get ( ' properties ' ) . couponID
# Update user
stripeInfo = _ . cloneDeep ( user . get ( ' stripe ' ) ? { } )
stripeInfo.couponID = couponID
stripeInfo.prepaidCode = req . body . stripe . prepaidCode
user . set ( ' stripe ' , stripeInfo )
@ checkForExistingSubscription ( req , user , customer , couponID , done )
else
couponID = user . get ( ' stripe ' ) ? . couponID
# SALE LOGIC
# overwrite couponID with another for everyone-sales
#couponID = 'hoc_399' if not couponID
@ checkForExistingSubscription ( req , user , customer , couponID , done )
2014-12-09 06:06:14 -08:00
2015-03-19 15:02:45 -07:00
checkForExistingSubscription: (req, user, customer, couponID, done) ->
2015-03-13 15:19:20 -07:00
findStripeSubscription customer . id , subscriptionID: user . get ( ' stripe ' ) ? . subscriptionID , (subscription) =>
2014-12-03 09:13:51 -08:00
2015-03-13 15:19:20 -07:00
if subscription
2014-12-02 20:01:35 -08:00
2015-03-13 15:19:20 -07:00
if subscription . cancel_at_period_end
# Things are a little tricky here. Can't re-enable a cancelled subscription,
# so it needs to be deleted, but also don't want to charge for the new subscription immediately.
# So delete the cancelled subscription (no at_period_end given here) and give the new
# subscription a trial period that ends when the cancelled subscription would have ended.
stripe . customers . cancelSubscription subscription . customer , subscription . id , (err) =>
2014-12-02 20:01:35 -08:00
if err
2015-03-13 15:19:20 -07:00
@ logSubscriptionError ( user , ' Stripe cancel subscription error. ' + err )
2014-12-02 20:01:35 -08:00
return done ( { res: ' Database error. ' , code: 500 } )
2015-03-13 15:19:20 -07:00
options = { plan: ' basic ' , metadata: { id: user . id } , trial_end: subscription . current_period_end }
options.coupon = couponID if couponID
stripe . customers . createSubscription customer . id , options , (err, subscription) =>
if err
@ logSubscriptionError ( user , ' Stripe customer plan setting error. ' + err )
return done ( { res: ' Database error. ' , code: 500 } )
@ updateUser ( req , user , customer , subscription , false , done )
2014-12-03 09:13:51 -08:00
2015-03-19 15:02:45 -07:00
else if couponID
# Update subscription with given couponID
stripe . customers . updateSubscription customer . id , subscription . id , coupon: couponID , (err, subscription) =>
if err
@ logSubscriptionError ( user , ' Stripe update subscription coupon error. ' + err )
return done ( { res: ' Database error. ' , code: 500 } )
@ updateUser ( req , user , customer , subscription , false , done )
2015-03-13 15:19:20 -07:00
else
2015-03-19 15:02:45 -07:00
# Skip creating the subscription
@ updateUser ( req , user , customer , subscription , false , done )
2014-12-03 09:13:51 -08:00
2015-03-13 15:19:20 -07:00
else
options = { plan: ' basic ' , metadata: { id: user . id } }
options.coupon = couponID if couponID
stripe . customers . createSubscription customer . id , options , (err, subscription) =>
if err
@ logSubscriptionError ( user , ' Stripe customer plan setting error. ' + err )
return done ( { res: ' Database error. ' , code: 500 } )
2014-12-03 09:13:51 -08:00
2015-03-13 15:19:20 -07:00
@ updateUser ( req , user , customer , subscription , true , done )
updateUser: (req, user, customer, subscription, increment, done) ->
2014-12-02 20:01:35 -08:00
stripeInfo = _ . cloneDeep ( user . get ( ' stripe ' ) ? { } )
stripeInfo.planID = ' basic '
stripeInfo.subscriptionID = subscription . id
2014-12-04 17:41:17 -08:00
stripeInfo.customerID = customer . id
2015-03-13 15:19:20 -07:00
# To make sure things work for admins, who are mad with power
# And, so Handler.saveChangesToDocument doesn't undo all our saves here
req.body.stripe = stripeInfo
2014-12-02 20:01:35 -08:00
user . set ( ' stripe ' , stripeInfo )
if increment
purchased = _ . clone ( user . get ( ' purchased ' ) )
purchased ? = { }
purchased . gems ? = 0
purchased . gems += subscriptions . basic . gems # TODO: Put actual subscription amount here
user . set ( ' purchased ' , purchased )
user . save (err) =>
if err
2015-03-13 15:19:20 -07:00
@ logSubscriptionError ( user , ' Stripe user plan saving error. ' + err )
2014-12-02 20:01:35 -08:00
return done ( { res: ' Database error. ' , code: 500 } )
2015-03-19 15:02:45 -07:00
if stripeInfo . prepaidCode ?
# Update prepaid to 'used'
Prepaid . findOne code: stripeInfo . prepaidCode , (err, prepaid) =>
if err
@ logSubscriptionError ( user , ' Prepaid find error. ' + err )
return done ( { res: ' Database error. ' , code: 500 } )
unless prepaid ?
@ logSubscriptionError ( user , " Expected prepaid not found: #{ stripeInfo . prepaidCode } " )
return done ( { res: ' Database error. ' , code: 500 } )
prepaid . set ( ' status ' , ' used ' )
prepaid . set ( ' redeemer ' , user . get ( ' _id ' ) )
prepaid . save (err) =>
if err
@ logSubscriptionError ( user , ' Prepaid update error. ' + err )
return done ( { res: ' Database error. ' , code: 500 } )
done ( )
else
done ( )
2014-12-03 09:13:51 -08:00
2015-03-13 15:19:20 -07:00
updateStripeRecipientSubscriptions: (req, user, customer, done) ->
return done ( { res: ' Database error. ' , code: 500 } ) unless req . body . stripe ? . subscribeEmails ?
emails = req . body . stripe . subscribeEmails . map ( (email) -> email . trim ( ) . toLowerCase ( ) unless _ . isEmpty ( email ) )
_ . remove ( emails , (email) -> _ . isEmpty ( email ) )
User . find { emailLower: { $in: emails } } , (err, recipients) =>
if err
@ logSubscriptionError ( user , " User lookup error. " + err )
return done ( { res: ' Database error. ' , code: 500 } )
createUpdateFn = (recipient) =>
(done) =>
# Find existing recipient subscription
findStripeSubscription customer . id , userID: recipient . id , (subscription) =>
if subscription
if subscription . cancel_at_period_end
# Things are a little tricky here. Can't re-enable a cancelled subscription,
# so it needs to be deleted, but also don't want to charge for the new subscription immediately.
# So delete the cancelled subscription (no at_period_end given here) and give the new
# subscription a trial period that ends when the cancelled subscription would have ended.
stripe . customers . cancelSubscription subscription . customer , subscription . id , (err) =>
if err
@ logSubscriptionError ( user , ' Stripe cancel subscription error. ' + err )
return done ( { res: ' Database error. ' , code: 500 } )
options =
plan: ' basic '
coupon: recipientCouponID
metadata: { id: recipient . id }
trial_end: subscription . current_period_end
stripe . customers . createSubscription customer . id , options , (err, subscription) =>
if err
@ logSubscriptionError ( user , ' Stripe new subscription error. ' + err )
return done ( { res: ' Database error. ' , code: 500 } )
done ( null , recipient: recipient , subscription: subscription , increment: false )
else
# Can skip creating the subscription
done ( null , recipient: recipient , subscription: subscription , increment: false )
else
options =
plan: ' basic '
coupon: recipientCouponID
metadata: { id: recipient . id }
stripe . customers . createSubscription customer . id , options , (err, subscription) =>
if err
@ logSubscriptionError ( user , ' Stripe new subscription error. ' + err )
return done ( { res: ' Database error. ' , code: 500 } )
done ( null , recipient: recipient , subscription: subscription , increment: true )
tasks = [ ]
for recipient in recipients
continue if recipient . id is user . id
continue if recipient . get ( ' stripe ' ) ? . subscriptionID ?
continue if recipient . get ( ' stripe ' ) ? . sponsorID ? and recipient . get ( ' stripe ' ) ? . sponsorID isnt user . id
tasks . push createUpdateFn ( recipient )
# NOTE: async.parellel yields this error:
# Subscription Error: user23 (54fe3c8fea98978efa469f3b): 'Stripe new subscription error. Error: Request rate limit exceeded'
async . series tasks , (err, results) =>
return done ( err ) if err
@ updateCocoRecipientSubscriptions ( req , user , customer , results , done )
updateCocoRecipientSubscriptions: (req, user, customer, stripeRecipients, done) ->
# Update recipients list
stripeInfo = _ . cloneDeep ( user . get ( ' stripe ' ) ? { } )
stripeInfo . recipients ? = [ ]
stripeRecipientIDs = ( sub . recipient . id for sub in stripeRecipients )
_ . remove ( stripeInfo . recipients , (s) -> s . userID in stripeRecipientIDs )
for sub in stripeRecipients
stripeInfo . recipients . push
userID: sub . recipient . id
subscriptionID: sub . subscription . id
couponID: recipientCouponID
# TODO: how does token get removed for personal subs?
delete stripeInfo . subscribeEmails
delete stripeInfo . token
req.body.stripe = stripeInfo
user . set ( ' stripe ' , stripeInfo )
user . save (err) =>
if err
@ logSubscriptionError ( user , ' User saving stripe error. ' + err )
return done ( { res: ' Database error. ' , code: 500 } )
createUpdateFn = (recipient, increment) =>
(done) =>
# Update recipient
stripeInfo = _ . cloneDeep ( recipient . get ( ' stripe ' ) ? { } )
stripeInfo.sponsorID = user . id
recipient . set ' stripe ' , stripeInfo
if increment
purchased = _ . clone ( recipient . get ( ' purchased ' ) )
purchased ? = { }
purchased . gems ? = 0
purchased . gems += subscriptions . basic . gems
recipient . set ( ' purchased ' , purchased )
recipient . save (err) =>
if err
@ logSubscriptionError ( user , ' Stripe user saving stripe error. ' + err )
return done ( { res: ' Database error. ' , code: 500 } )
done ( )
tasks = [ ]
for sub in stripeRecipients
tasks . push createUpdateFn ( sub . recipient , sub . increment )
async . parallel tasks , (err, results) =>
return done ( err ) if err
@ updateStripeSponsorSubscription ( req , user , customer , done )
updateStripeSponsorSubscription: (req, user, customer, done) ->
stripeInfo = user . get ( ' stripe ' ) ? { }
numSponsored = stripeInfo . recipients . length
quantity = getSponsoredSubsAmount ( subscriptions . basic . amount , numSponsored , stripeInfo . subscriptionID ? )
findStripeSubscription customer . id , subscriptionID: stripeInfo . sponsorSubscriptionID , (subscription) =>
if stripeInfo . sponsorSubscriptionID ? and not subscription ?
@ logSubscriptionError ( user , " Internal sponsor subscription #{ stripeInfo . sponsorSubscriptionID } not found on Stripe customer #{ customer . id } " )
return done ( { res: ' Database error. ' , code: 500 } )
if subscription
return done ( ) if quantity is subscription . quantity # E.g. cancelled sub has been resubbed
options = quantity: quantity
stripe . customers . updateSubscription customer . id , stripeInfo . sponsorSubscriptionID , options , (err, subscription) =>
if err
@ logSubscriptionError ( user , ' Stripe updating subscription quantity error. ' + err )
return done ( { res: ' Database error. ' , code: 500 } )
# Invoice proration immediately
stripe . invoices . create customer: customer . id , (err, invoice) =>
if err
@ logSubscriptionError ( user , ' Stripe proration invoice error. ' + err )
return done ( { res: ' Database error. ' , code: 500 } )
done ( )
else
options =
plan: ' incremental '
metadata: { id: user . id }
quantity: quantity
stripe . customers . createSubscription customer . id , options , (err, subscription) =>
if err
@ logSubscriptionError ( user , ' Stripe new subscription error. ' + err )
return done ( { res: ' Database error. ' , code: 500 } )
@ updateCocoSponsorSubscription ( req , user , subscription , done )
updateCocoSponsorSubscription: (req, user, subscription, done) ->
stripeInfo = _ . cloneDeep ( user . get ( ' stripe ' ) ? { } )
stripeInfo.sponsorSubscriptionID = subscription . id
req.body.stripe = stripeInfo
user . set ( ' stripe ' , stripeInfo )
user . save (err) =>
if err
@ logSubscriptionError ( user , ' Saving user stripe error. ' + err )
return done ( { res: ' Database error. ' , code: 500 } )
done ( )
2014-12-02 20:01:35 -08:00
unsubscribeUser: (req, user, done) ->
2015-03-13 15:19:20 -07:00
# Check if user is subscribing someone else
return @ unsubscribeRecipient ( req , user , done ) if req . body . stripe ? . unsubscribeEmail ?
stripeInfo = _ . cloneDeep ( user . get ( ' stripe ' ) ? { } )
2014-12-02 20:01:35 -08:00
stripe . customers . cancelSubscription stripeInfo . customerID , stripeInfo . subscriptionID , { at_period_end: true } , (err) =>
if err
2015-03-13 15:19:20 -07:00
@ logSubscriptionError ( user , ' Stripe cancel subscription error. ' + err )
2014-12-02 20:01:35 -08:00
return done ( { res: ' Database error. ' , code: 500 } )
delete stripeInfo . planID
user . set ( ' stripe ' , stripeInfo )
req.body.stripe = stripeInfo
user . save (err) =>
if err
2015-03-13 15:19:20 -07:00
@ logSubscriptionError ( user , ' User save unsubscribe error. ' + err )
return done ( { res: ' Database error. ' , code: 500 } )
done ( )
unsubscribeRecipient: (req, user, done) ->
return done ( { res: ' Database error. ' , code: 500 } ) unless req . body . stripe ? . unsubscribeEmail ?
email = req . body . stripe . unsubscribeEmail . trim ( ) . toLowerCase ( )
return done ( { res: ' Database error. ' , code: 500 } ) if _ . isEmpty ( email )
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 )
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 )
return done ( { res: ' Can only unsubscribe sponsored subscriptions. ' , code: 403 } )
# Find recipient subscription
stripeInfo = _ . cloneDeep ( user . get ( ' stripe ' ) ? { } )
for sponsored in stripeInfo . recipients
if sponsored . userID is recipient . id
sponsoredEntry = sponsored
break
unless sponsoredEntry ?
@ logSubscriptionError ( user , ' Unable to find sponsored subscription. ' + err )
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 )
2014-12-02 20:01:35 -08:00
return done ( { res: ' Database error. ' , code: 500 } )
2015-03-13 15:19:20 -07:00
delete stripeInfo . unsubscribeEmail
user . set ( ' stripe ' , stripeInfo )
req.body.stripe = stripeInfo
user . save (err) =>
if err
@ logSubscriptionError ( user , ' User save unsubscribe error. ' + err )
return done ( { res: ' Database error. ' , code: 500 } )
done ( )
2014-12-02 20:01:35 -08:00
2014-12-03 09:13:51 -08:00
module.exports = new SubscriptionHandler ( )