codecombat/server/payments/subscription_handler.coffee

555 lines
25 KiB
CoffeeScript
Raw Normal View History

2014-12-02 23:01:35 -05: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.
log = require 'winston'
2015-05-01 12:37:43 -04:00
MongoClient = require('mongodb').MongoClient
mongoose = require 'mongoose'
2015-03-13 18:19:20 -04:00
async = require 'async'
config = require '../../server_config'
2014-12-02 23:01:35 -05:00
Handler = require '../commons/Handler'
discountHandler = require './discount_handler'
Prepaid = require '../prepaids/Prepaid'
2015-03-13 18:19:20 -04:00
User = require '../users/User'
{findStripeSubscription} = require '../lib/utils'
{getSponsoredSubsAmount} = require '../../app/core/utils'
2014-12-02 23:01:35 -05:00
2015-03-13 18:19:20 -04:00
recipientCouponID = 'free'
# TODO: rename this to avoid collisions with 'subscriptions' variables
2014-12-02 23:01:35 -05:00
subscriptions = {
basic: {
gems: 3500
2015-03-13 18:19:20 -04:00
amount: 999 # For calculating incremental quantity before sub creation
2014-12-02 23:01:35 -05:00
}
}
class SubscriptionHandler extends Handler
logSubscriptionError: (user, msg) ->
console.warn "Subscription Error: #{user.get('slug')} (#{user._id}): '#{msg}'"
2014-12-02 23:01:35 -05:00
getByRelationship: (req, res, args...) ->
return @getStripeEvents(req, res) if args[1] is 'stripe_events'
return @getStripeInvoices(req, res) if args[1] is 'stripe_invoices'
return @getStripeSubscriptions(req, res) if args[1] is 'stripe_subscriptions'
return @getSubscribers(req, res) if args[1] is 'subscribers'
super(arguments...)
getStripeEvents: (req, res) ->
# console.log 'subscription_handler getStripeEvents', req.body?.options
return @sendForbiddenError(res) unless req.user?.isAdmin()
stripe.events.list req.body.options, (err, events) =>
return @sendDatabaseError(res, err) if err
@sendSuccess(res, events)
getStripeInvoices: (req, res) ->
# console.log 'subscription_handler getStripeInvoices'
return @sendForbiddenError(res) unless req.user?.isAdmin()
# TODO: this caching mechanism doesn't work in production (multiple calls or app servers?)
# TODO: cache older invoices in the analytics database instead
# @oldInvoices ?= {}
# buildInvoicesFromCache = (newInvoices) =>
# data = (invoice for invoiceID, invoice of @oldInvoices)
# data = data.concat(newInvoices)
# data.sort (a, b) -> if a.date > b.date then -1 else 1
# {has_more: false, data: data}
# oldInvoiceCutoffDays = 16 # Dependent on Stripe subscription payment retries
# oldInvoiceCutoffDate = new Date()
# oldInvoiceCutoffDate.setUTCDate(oldInvoiceCutoffDate.getUTCDate() - oldInvoiceCutoffDays)
stripe.invoices.list req.body.options, (err, invoices) =>
return @sendDatabaseError(res, err) if err
# newInvoices = []
# for invoice, i in invoices.data
# if new Date(invoice.date * 1000) < oldInvoiceCutoffDate
# if invoice.id of @oldInvoices
# # Rest of the invoices should be cached, return from cache
# cachedInvoices = buildInvoicesFromCache(newInvoices)
# return @sendSuccess(res, cachedInvoices)
# else
# # Cache older invoices
# @oldInvoices[invoice.id] = invoice
# else
# # Keep track of new invoices for this page of invoices
# newInvoices.push(invoice)
@sendSuccess(res, invoices)
getStripeSubscriptions: (req, res) ->
# console.log 'subscription_handler getStripeSubscriptions'
return @sendForbiddenError(res) unless req.user?.isAdmin()
stripeSubscriptions = []
createGetSubFn = (customerID, subscriptionID) =>
(done) =>
stripe.customers.retrieveSubscription customerID, subscriptionID, (err, subscription) =>
# TODO: return error instead of ignore?
stripeSubscriptions.push(subscription) unless err
done()
tasks = []
for subscription in req.body.subscriptions
tasks.push createGetSubFn(subscription.customerID, subscription.subscriptionID)
async.parallel tasks, (err, results) =>
return @sendDatabaseError(res, err) if err
@sendSuccess(res, stripeSubscriptions)
getSubscribers: (req, res) ->
# console.log 'subscription_handler getSubscribers'
return @sendForbiddenError(res) unless req.user?.isAdmin()
2015-04-23 16:34:41 -04:00
subscriberUserIDs = req.body.ids or []
2015-05-01 12:37:43 -04:00
2015-04-23 16:34:41 -04:00
User.find {_id: {$in: subscriberUserIDs}}, (err, users) =>
return @sendDatabaseError(res, err) if err
2015-04-23 16:34:41 -04:00
userMap = {}
2015-05-01 12:37:43 -04:00
userMap[user.id] = user.toObject() for user in users
try
# Get conversion data directly from analytics database and add it to results
url = "mongodb://#{config.mongo.analytics_host}:#{config.mongo.analytics_port}/#{config.mongo.analytics_db}"
MongoClient.connect url, (err, db) =>
2015-05-01 12:37:43 -04:00
if err
log.debug 'Analytics connect error: ' + err
2015-05-01 12:37:43 -04:00
return @sendDatabaseError(res, err)
userEventMap = {}
2015-05-01 19:08:12 -04:00
events = ['Finished subscription purchase', 'Show subscription modal']
query = {$and: [{user: {$in: subscriberUserIDs}}, {event: {$in: events}}]}
db.collection('log').find(query).sort({_id: -1}).each (err, doc) =>
if err
db.close()
return @sendDatabaseError(res, err)
if (doc)
userEventMap[doc.user] ?= []
userEventMap[doc.user].push doc
else
db.close()
for userID, eventList of userEventMap
finishedPurchase = false
for event in eventList
finishedPurchase = true if event.event is 'Finished subscription purchase'
if finishedPurchase
if event.event is 'Show subscription modal' and event.properties?.level?
userMap[userID].conversion = event.properties.level
break
else if event.event is 'Show subscription modal' and event.properties?.label in ['buy gems modal', 'check private clan', 'create clan']
userMap[userID].conversion = event.properties.label
break
@sendSuccess(res, userMap)
catch err
log.debug 'Analytics error:\n' + err
@sendSuccess(res, userMap)
2014-12-02 23:01:35 -05:00
subscribeUser: (req, user, done) ->
if (not req.user) or req.user.isAnonymous() or user.isAnonymous()
return done({res: 'You must be signed in to subscribe.', code: 403})
token = req.body.stripe.token
prepaidCode = req.body.stripe.prepaidCode
customerID = user.get('stripe')?.customerID
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 23:01:35 -05:00
# Get Stripe customer
if customerID
if token
stripe.customers.update customerID, { card: token }, (err, customer) =>
if err or not customer
# should not happen outside of test and production polluting each other
2015-03-13 18:19:20 -04:00
@logSubscriptionError(user, 'Cannot find customer: ' + customerID + '\n\n' + err)
return done({res: 'Cannot find customer.', code: 404})
@checkForCoupon(req, user, customer, done)
else
stripe.customers.retrieve customerID, (err, customer) =>
if err
@logSubscriptionError(user, 'Stripe customer retrieve error. ' + err)
return done({res: 'Database error.', code: 500})
@checkForCoupon(req, user, customer, done)
2014-12-02 23:01:35 -05:00
else
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 23:01:35 -05:00
if err
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 23:01:35 -05: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 18:19:20 -04: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})
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 09:06:14 -05:00
checkForExistingSubscription: (req, user, customer, couponID, done) ->
2015-03-13 18:19:20 -04:00
findStripeSubscription customer.id, subscriptionID: user.get('stripe')?.subscriptionID, (subscription) =>
2014-12-03 12:13:51 -05:00
2015-03-13 18:19:20 -04:00
if subscription
2014-12-02 23:01:35 -05:00
2015-03-13 18:19:20 -04: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 23:01:35 -05:00
if err
2015-03-13 18:19:20 -04:00
@logSubscriptionError(user, 'Stripe cancel subscription error. ' + err)
2014-12-02 23:01:35 -05:00
return done({res: 'Database error.', code: 500})
2015-03-13 18:19:20 -04: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 12:13:51 -05: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 18:19:20 -04:00
else
# Skip creating the subscription
@updateUser(req, user, customer, subscription, false, done)
2014-12-03 12:13:51 -05:00
2015-03-13 18:19:20 -04: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 12:13:51 -05:00
2015-03-13 18:19:20 -04:00
@updateUser(req, user, customer, subscription, true, done)
updateUser: (req, user, customer, subscription, increment, done) ->
2014-12-02 23:01:35 -05:00
stripeInfo = _.cloneDeep(user.get('stripe') ? {})
stripeInfo.planID = 'basic'
stripeInfo.subscriptionID = subscription.id
stripeInfo.customerID = customer.id
2015-03-13 18:19:20 -04: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 23:01:35 -05: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 18:19:20 -04:00
@logSubscriptionError(user, 'Stripe user plan saving error. ' + err)
2014-12-02 23:01:35 -05:00
return done({res: 'Database error.', code: 500})
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 12:13:51 -05:00
2015-03-13 18:19:20 -04: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.parallel yields this error:
2015-03-13 18:19:20 -04:00
# 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 23:01:35 -05:00
unsubscribeUser: (req, user, done) ->
2015-03-13 18:19:20 -04: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 23:01:35 -05:00
stripe.customers.cancelSubscription stripeInfo.customerID, stripeInfo.subscriptionID, { at_period_end: true }, (err) =>
if err
2015-03-13 18:19:20 -04:00
@logSubscriptionError(user, 'Stripe cancel subscription error. ' + err)
2014-12-02 23:01:35 -05: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 18:19:20 -04: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)
deleteUserStripeProp = (user, propName) ->
stripeInfo = _.cloneDeep(user.get('stripe') ? {})
delete stripeInfo[propName]
if _.isEmpty stripeInfo
user.set 'stripe', undefined
else
user.set 'stripe', stripeInfo
2015-03-13 18:19:20 -04:00
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 #{email} not found.")
2015-03-13 18:19:20 -04:00
return done({res: 'Database error.', code: 500})
# Check recipient is currently sponsored
stripeRecipient = recipient.get 'stripe' ? {}
if stripeRecipient?.sponsorID isnt user.id
2015-06-18 18:02:15 -04:00
@logSubscriptionError(user, "Recipient #{recipient.id} not sponsored by #{user.id}. ")
2015-03-13 18:19:20 -04:00
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 recipient subscription. ')
2015-03-13 18:19:20 -04:00
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)
2014-12-02 23:01:35 -05:00
return done({res: 'Database error.', code: 500})
2015-03-13 18:19:20 -04:00
# Cancel Stripe subscription
stripe.customers.cancelSubscription stripeInfo.customerID, sponsoredEntry.subscriptionID, (err) =>
2015-03-13 18:19:20 -04:00
if err
@logSubscriptionError(user, "Stripe cancel sponsored subscription failed. " + err)
2015-03-13 18:19:20 -04:00
return done({res: 'Database error.', code: 500})
# 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)
2015-06-18 13:30:30 -04:00
return done({res: 'Database error.', code: 500})
done()
2014-12-02 23:01:35 -05:00
2014-12-03 12:13:51 -05:00
module.exports = new SubscriptionHandler()