mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-04-07 02:25:29 -04:00
Admin dashboard perf
Find subscription cancellations via events API instead of walking each customer and their subscriptions.
This commit is contained in:
parent
559b9533c4
commit
502a47e912
3 changed files with 56 additions and 38 deletions
app
server/payments
|
@ -46,3 +46,8 @@
|
|||
z-index: 3
|
||||
background-color: blanchedalmond
|
||||
font-size: 10pt
|
||||
|
||||
.subscribers-tbody
|
||||
font-size: 9pt
|
||||
td
|
||||
padding: 2px
|
||||
|
|
|
@ -42,9 +42,10 @@ block content
|
|||
if !subscribers || subscribers.length < 1
|
||||
h4 Fetching recent subscribers...
|
||||
else
|
||||
table.table.table-condensed
|
||||
table.table.table-striped.table-condensed
|
||||
thead
|
||||
tr
|
||||
th Sub ID
|
||||
th User Start
|
||||
th Sub Start
|
||||
if subscriberCancelled
|
||||
|
@ -59,9 +60,11 @@ block content
|
|||
th Last Level
|
||||
th Age
|
||||
th Spoken
|
||||
tbody
|
||||
tbody.subscribers-tbody
|
||||
each subscriber in subscribers
|
||||
tr
|
||||
td
|
||||
a(href="https://dashboard.stripe.com/customers/#{subscriber.customerID}", target="_blank")= subscriber.subscriptionID
|
||||
td= subscriber.user.dateCreated.substring(0, 10)
|
||||
td= subscriber.start.substring(0, 10)
|
||||
td
|
||||
|
|
|
@ -31,50 +31,57 @@ class SubscriptionHandler extends Handler
|
|||
|
||||
getCancellations: (req, res) =>
|
||||
# console.log 'subscription_handler getCancellations'
|
||||
return @sendForbiddenError(res) unless req.user and req.user.isAdmin()
|
||||
@cancellations = []
|
||||
return @sendForbiddenError(res) unless req.user?.isAdmin()
|
||||
|
||||
earliestEventDate = new Date()
|
||||
earliestEventDate.setUTCMonth(earliestEventDate.getUTCMonth() - 1)
|
||||
earliestEventDate.setUTCDate(earliestEventDate.getUTCDate() - 8)
|
||||
|
||||
cancellationEvents = []
|
||||
nextBatch = (starting_after, done) =>
|
||||
options = limit: 100
|
||||
options.starting_after = starting_after if starting_after
|
||||
stripe.customers.list options, (err, customers) =>
|
||||
options.type = 'customer.subscription.updated'
|
||||
options.created = gte: Math.floor(earliestEventDate.getTime() / 1000)
|
||||
stripe.events.list options, (err, events) =>
|
||||
return done(err) if err
|
||||
|
||||
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
|
||||
|
||||
if subscription.cancel_at_period_end
|
||||
@cancellations.push
|
||||
cancel: new Date(subscription.canceled_at * 1000)
|
||||
subID: subscription.id
|
||||
|
||||
if customers.has_more
|
||||
# console.log 'Fetching more customers', Object.keys(@cancellations).length
|
||||
return nextBatch(customers.data[customers.data.length - 1].id, done)
|
||||
for event in events.data
|
||||
continue unless event.data?.object?.cancel_at_period_end is true and event.data?.previous_attributes.cancel_at_period_end is false
|
||||
continue if event.data?.object?.discount?
|
||||
continue unless event.data?.object?.plan?.id is 'basic'
|
||||
continue unless event.data?.object?.id?
|
||||
cancellationEvents.push
|
||||
subscriptionID: event.data.object.id
|
||||
customerID: event.data.object.customer
|
||||
if events.has_more
|
||||
# console.log 'Fetching more cancellation events', cancellationEvents.length
|
||||
return nextBatch(events.data[events.data.length - 1].id, done)
|
||||
else
|
||||
return done()
|
||||
|
||||
nextBatch null, (err) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
@sendSuccess(res, @cancellations)
|
||||
|
||||
cancellations = []
|
||||
createCheckSubFn = (customerID, subscriptionID) =>
|
||||
(done) =>
|
||||
stripe.customers.retrieveSubscription customerID, subscriptionID, (err, subscription) =>
|
||||
return done() if err
|
||||
return unless subscription?.cancel_at_period_end
|
||||
cancellations.push
|
||||
cancel: new Date(subscription.canceled_at * 1000)
|
||||
subID: subscription.id
|
||||
done()
|
||||
tasks = []
|
||||
for cancellationEvent in cancellationEvents
|
||||
tasks.push createCheckSubFn(cancellationEvent.customerID, cancellationEvent.subscriptionID)
|
||||
async.parallel tasks, (err, results) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
@sendSuccess(res, cancellations)
|
||||
|
||||
getSubscribers: (req, res) ->
|
||||
# console.log 'subscription_handler getSubscribers'
|
||||
return @sendForbiddenError(res) unless req.user and req.user.isAdmin()
|
||||
return @sendForbiddenError(res) unless req.user?.isAdmin()
|
||||
|
||||
maxReturnCount = req.body.maxCount or 20
|
||||
|
||||
|
@ -112,7 +119,10 @@ class SubscriptionHandler extends Handler
|
|||
|
||||
continue unless amount > 0
|
||||
|
||||
subscriber = start: new Date(subscription.start * 1000)
|
||||
subscriber =
|
||||
customerID: customer.id
|
||||
start: new Date(subscription.start * 1000)
|
||||
subscriptionID: subscription.id
|
||||
if subscription.metadata?.id?
|
||||
subscriber.userID = subscription.metadata.id
|
||||
subscriberIDs.push subscription.metadata.id
|
||||
|
@ -143,7 +153,7 @@ class SubscriptionHandler extends Handler
|
|||
# TODO: take date range as input
|
||||
# TODO: are ended counts correct for today? E.g. retries may complicate things.
|
||||
|
||||
return @sendForbiddenError(res) unless req.user and req.user.isAdmin()
|
||||
return @sendForbiddenError(res) unless req.user?.isAdmin()
|
||||
|
||||
@invoices ?= [] # Keep sorted newest to oldest
|
||||
newInvoices = []
|
||||
|
@ -423,7 +433,7 @@ class SubscriptionHandler extends Handler
|
|||
continue if recipient.get('stripe')?.sponsorID? and recipient.get('stripe')?.sponsorID isnt user.id
|
||||
tasks.push createUpdateFn(recipient)
|
||||
|
||||
# NOTE: async.parellel yields this error:
|
||||
# NOTE: async.parallel 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
|
||||
|
|
Loading…
Add table
Reference in a new issue