Merged.
After Width: | Height: | Size: 1.3 KiB |
BIN
app/assets/images/common/button-background-brown-active.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.3 KiB |
BIN
app/assets/images/common/button-background-brown-disabled.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.3 KiB |
BIN
app/assets/images/common/button-background-brown-pressed.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
app/assets/images/common/button-background-fb-active-border.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
app/assets/images/common/button-background-fb-active.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.3 KiB |
BIN
app/assets/images/common/button-background-fb-disabled.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
app/assets/images/common/button-background-fb-pressed-border.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
app/assets/images/common/button-background-fb-pressed.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.3 KiB |
BIN
app/assets/images/common/button-background-gplus-active.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.3 KiB |
BIN
app/assets/images/common/button-background-gplus-disabled.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.3 KiB |
BIN
app/assets/images/common/button-background-gplus-pressed.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
app/assets/images/level/loading_left_wing_1366.jpg
Normal file
After Width: | Height: | Size: 111 KiB |
BIN
app/assets/images/level/loading_left_wing_1920.jpg
Normal file
After Width: | Height: | Size: 174 KiB |
BIN
app/assets/images/level/loading_right_wing_1366.jpg
Normal file
After Width: | Height: | Size: 107 KiB |
BIN
app/assets/images/level/loading_right_wing_1920.jpg
Normal file
After Width: | Height: | Size: 167 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.5 KiB |
BIN
app/assets/images/pages/play/map_dungeon_1366.jpg
Normal file
After Width: | Height: | Size: 346 KiB |
BIN
app/assets/images/pages/play/map_dungeon_1920.jpg
Normal file
After Width: | Height: | Size: 614 KiB |
BIN
app/assets/images/pages/play/map_forest_1366.jpg
Normal file
After Width: | Height: | Size: 441 KiB |
BIN
app/assets/images/pages/play/map_forest_1920.jpg
Normal file
After Width: | Height: | Size: 763 KiB |
|
@ -112,6 +112,13 @@ module.exports = class User extends CocoModel
|
||||||
application.tracker.identify gemPromptGroup: @gemPromptGroup unless me.isAdmin()
|
application.tracker.identify gemPromptGroup: @gemPromptGroup unless me.isAdmin()
|
||||||
@gemPromptGroup
|
@gemPromptGroup
|
||||||
|
|
||||||
|
isPremium: ->
|
||||||
|
return false unless stripe = @get('stripe')
|
||||||
|
return true if stripe.subscriptionID
|
||||||
|
return true if stripe.free is true
|
||||||
|
return true if _.isString(stripe.free) and new Date() < new Date(stripe.free)
|
||||||
|
return false
|
||||||
|
|
||||||
tiersByLevel = [-1, 0, 0.05, 0.14, 0.18, 0.32, 0.41, 0.5, 0.64, 0.82, 0.91, 1.04, 1.22, 1.35, 1.48, 1.65, 1.78, 1.96, 2.1, 2.24, 2.38, 2.55, 2.69, 2.86, 3.03, 3.16, 3.29, 3.42, 3.58, 3.74, 3.89, 4.04, 4.19, 4.32, 4.47, 4.64, 4.79, 4.96,
|
tiersByLevel = [-1, 0, 0.05, 0.14, 0.18, 0.32, 0.41, 0.5, 0.64, 0.82, 0.91, 1.04, 1.22, 1.35, 1.48, 1.65, 1.78, 1.96, 2.1, 2.24, 2.38, 2.55, 2.69, 2.86, 3.03, 3.16, 3.29, 3.42, 3.58, 3.74, 3.89, 4.04, 4.19, 4.32, 4.47, 4.64, 4.79, 4.96,
|
||||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 10.5, 11, 11.5, 12, 12.5, 13, 13.5, 14, 14.5, 15
|
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 10.5, 11, 11.5, 12, 12.5, 13, 13.5, 14, 14.5, 15
|
||||||
]
|
]
|
||||||
|
|
|
@ -24,7 +24,7 @@ module.exports = class PaymentsView extends RootView
|
||||||
c = super()
|
c = super()
|
||||||
c.payments = @payments
|
c.payments = @payments
|
||||||
c.subscribed = me.get('stripe')?.planID
|
c.subscribed = me.get('stripe')?.planID
|
||||||
c.active = me.get('stripe')?.subscriptionID
|
c.active = me.isPremium()
|
||||||
c
|
c
|
||||||
|
|
||||||
onClickStartSubscription: (e) ->
|
onClickStartSubscription: (e) ->
|
||||||
|
|
|
@ -81,7 +81,7 @@ module.exports = class WorldMapView extends RootView
|
||||||
$('body').append($('<img src="http://code.org/api/hour/begin_codecombat.png" style="visibility: hidden;">'))
|
$('body').append($('<img src="http://code.org/api/hour/begin_codecombat.png" style="visibility: hidden;">'))
|
||||||
trackedHourOfCode = true
|
trackedHourOfCode = true
|
||||||
|
|
||||||
@requiresSubscription = @terrain isnt 'dungeon' and not me.get('stripe')?.subscriptionID
|
@requiresSubscription = @terrain isnt 'dungeon' and not me.isPremium()
|
||||||
|
|
||||||
destroy: ->
|
destroy: ->
|
||||||
@setupManager?.destroy()
|
@setupManager?.destroy()
|
||||||
|
|
|
@ -72,7 +72,7 @@ LevelHandler = class LevelHandler extends Handler
|
||||||
Session.findOne(sessionQuery).exec (err, doc) =>
|
Session.findOne(sessionQuery).exec (err, doc) =>
|
||||||
return @sendDatabaseError(res, err) if err
|
return @sendDatabaseError(res, err) if err
|
||||||
return @sendSuccess(res, doc) if doc?
|
return @sendSuccess(res, doc) if doc?
|
||||||
return @sendPaymentRequiredError(res, err) if (not req.user.get('stripe')?.subscriptionID) and level.get('requiresSubscription')
|
return @sendPaymentRequiredError(res, err) if (not req.user.isPremium()) and level.get('requiresSubscription')
|
||||||
@createAndSaveNewSession sessionQuery, req, res
|
@createAndSaveNewSession sessionQuery, req, res
|
||||||
|
|
||||||
createAndSaveNewSession: (sessionQuery, req, res) =>
|
createAndSaveNewSession: (sessionQuery, req, res) =>
|
||||||
|
|
|
@ -8,7 +8,6 @@ sendwithus = require '../sendwithus'
|
||||||
hipchat = require '../hipchat'
|
hipchat = require '../hipchat'
|
||||||
config = require '../../server_config'
|
config = require '../../server_config'
|
||||||
request = require 'request'
|
request = require 'request'
|
||||||
stripe = require('stripe')(config.stripe.secretKey)
|
|
||||||
async = require 'async'
|
async = require 'async'
|
||||||
|
|
||||||
products = {
|
products = {
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
# the stripe property in the user with what's being stored in Stripe.
|
# the stripe property in the user with what's being stored in Stripe.
|
||||||
|
|
||||||
Handler = require '../commons/Handler'
|
Handler = require '../commons/Handler'
|
||||||
config = require '../../server_config'
|
|
||||||
stripe = require('stripe')(config.stripe.secretKey)
|
|
||||||
discountHandler = require './discount_handler'
|
discountHandler = require './discount_handler'
|
||||||
|
|
||||||
subscriptions = {
|
subscriptions = {
|
||||||
|
@ -13,17 +11,17 @@ subscriptions = {
|
||||||
}
|
}
|
||||||
|
|
||||||
class SubscriptionHandler extends Handler
|
class SubscriptionHandler extends Handler
|
||||||
logSubscriptionError: (req, msg) ->
|
logSubscriptionError: (user, msg) ->
|
||||||
console.warn "Subscription Error: #{req.user.get('slug')} (#{req.user._id}): '#{msg}'"
|
console.warn "Subscription Error: #{user.get('slug')} (#{user._id}): '#{msg}'"
|
||||||
|
|
||||||
subscribeUser: (req, user, done) ->
|
subscribeUser: (req, user, done) ->
|
||||||
if (not req.user) or req.user.isAnonymous()
|
if (not req.user) or req.user.isAnonymous() or user.isAnonymous()
|
||||||
return done({res: 'You must be signed in to subscribe.', code: 403})
|
return done({res: 'You must be signed in to subscribe.', code: 403})
|
||||||
|
|
||||||
token = req.body.stripe.token
|
token = req.body.stripe.token
|
||||||
customerID = user.get('stripe')?.customerID
|
customerID = user.get('stripe')?.customerID
|
||||||
if not (token or customerID)
|
if not (token or customerID)
|
||||||
@logSubscriptionError(req, 'Missing stripe token or customer ID.')
|
@logSubscriptionError(user, 'Missing stripe token or customer ID.')
|
||||||
return done({res: 'Missing stripe token or customer ID.', code: 422})
|
return done({res: 'Missing stripe token or customer ID.', code: 422})
|
||||||
|
|
||||||
if token
|
if token
|
||||||
|
@ -31,15 +29,15 @@ class SubscriptionHandler extends Handler
|
||||||
stripe.customers.update customerID, { card: token }, (err, customer) =>
|
stripe.customers.update customerID, { card: token }, (err, customer) =>
|
||||||
if err or not customer
|
if err or not customer
|
||||||
# should not happen outside of test and production polluting each other
|
# should not happen outside of test and production polluting each other
|
||||||
@logSubscriptionError(req, 'Cannot find customer: ', +customer.id + '\n\n' + err)
|
@logSubscriptionError(user, 'Cannot find customer: ', +customer.id + '\n\n' + err)
|
||||||
return done({res: 'Cannot find customer.', code: 404})
|
return done({res: 'Cannot find customer.', code: 404})
|
||||||
@checkForExistingSubscription(req, user, customer, done)
|
@checkForExistingSubscription(req, user, customer, done)
|
||||||
|
|
||||||
else
|
else
|
||||||
newCustomer = {
|
newCustomer = {
|
||||||
card: token
|
card: token
|
||||||
email: req.user.get('email')
|
email: user.get('email')
|
||||||
metadata: { id: req.user._id + '', slug: req.user.get('slug') }
|
metadata: { id: user._id + '', slug: user.get('slug') }
|
||||||
}
|
}
|
||||||
|
|
||||||
stripe.customers.create newCustomer, (err, customer) =>
|
stripe.customers.create newCustomer, (err, customer) =>
|
||||||
|
@ -47,22 +45,22 @@ class SubscriptionHandler extends Handler
|
||||||
if err.type in ['StripeCardError', 'StripeInvalidRequestError']
|
if err.type in ['StripeCardError', 'StripeInvalidRequestError']
|
||||||
return done({res: 'Card error', code: 402})
|
return done({res: 'Card error', code: 402})
|
||||||
else
|
else
|
||||||
@logSubscriptionError(req, 'Stripe customer creation error. '+err)
|
@logSubscriptionError(user, 'Stripe customer creation error. '+err)
|
||||||
return done({res: 'Database error.', code: 500})
|
return done({res: 'Database error.', code: 500})
|
||||||
|
|
||||||
stripeInfo = _.cloneDeep(req.user.get('stripe') ? {})
|
stripeInfo = _.cloneDeep(user.get('stripe') ? {})
|
||||||
stripeInfo.customerID = customer.id
|
stripeInfo.customerID = customer.id
|
||||||
req.user.set('stripe', stripeInfo)
|
user.set('stripe', stripeInfo)
|
||||||
req.user.save (err) =>
|
user.save (err) =>
|
||||||
if err
|
if err
|
||||||
@logSubscriptionError(req, 'Stripe customer id save db error. '+err)
|
@logSubscriptionError(user, 'Stripe customer id save db error. '+err)
|
||||||
return done({res: 'Database error.', code: 500})
|
return done({res: 'Database error.', code: 500})
|
||||||
@checkForExistingSubscription(req, user, customer, done)
|
@checkForExistingSubscription(req, user, customer, done)
|
||||||
|
|
||||||
else
|
else
|
||||||
stripe.customers.retrieve(customerID, (err, customer) =>
|
stripe.customers.retrieve(customerID, (err, customer) =>
|
||||||
if err
|
if err
|
||||||
@logSubscriptionError(req, 'Stripe customer creation error. '+err)
|
@logSubscriptionError(user, 'Stripe customer creation error. '+err)
|
||||||
return done({res: 'Database error.', code: 500})
|
return done({res: 'Database error.', code: 500})
|
||||||
@checkForExistingSubscription(req, user, customer, done)
|
@checkForExistingSubscription(req, user, customer, done)
|
||||||
)
|
)
|
||||||
|
@ -79,14 +77,14 @@ class SubscriptionHandler extends Handler
|
||||||
# subscription a trial period that ends when the cancelled subscription would have ended.
|
# subscription a trial period that ends when the cancelled subscription would have ended.
|
||||||
stripe.customers.cancelSubscription subscription.customer, subscription.id, (err) =>
|
stripe.customers.cancelSubscription subscription.customer, subscription.id, (err) =>
|
||||||
if err
|
if err
|
||||||
@logSubscriptionError(req, 'Stripe cancel subscription error. '+err)
|
@logSubscriptionError(user, 'Stripe cancel subscription error. '+err)
|
||||||
return done({res: 'Database error.', code: 500})
|
return done({res: 'Database error.', code: 500})
|
||||||
|
|
||||||
options = { plan: 'basic', trial_end: subscription.current_period_end }
|
options = { plan: 'basic', trial_end: subscription.current_period_end }
|
||||||
options.coupon = couponID if couponID
|
options.coupon = couponID if couponID
|
||||||
stripe.customers.update req.user.get('stripe').customerID, options, (err, customer) =>
|
stripe.customers.update user.get('stripe').customerID, options, (err, customer) =>
|
||||||
if err
|
if err
|
||||||
@logSubscriptionError(req, 'Stripe customer plan setting error. '+err)
|
@logSubscriptionError(user, 'Stripe customer plan setting error. '+err)
|
||||||
return done({res: 'Database error.', code: 500})
|
return done({res: 'Database error.', code: 500})
|
||||||
|
|
||||||
@updateUser(req, user, customer, false, done)
|
@updateUser(req, user, customer, false, done)
|
||||||
|
@ -98,9 +96,9 @@ class SubscriptionHandler extends Handler
|
||||||
else
|
else
|
||||||
options = { plan: 'basic' }
|
options = { plan: 'basic' }
|
||||||
options.coupon = couponID if couponID
|
options.coupon = couponID if couponID
|
||||||
stripe.customers.update req.user.get('stripe').customerID, options, (err, customer) =>
|
stripe.customers.update user.get('stripe').customerID, options, (err, customer) =>
|
||||||
if err
|
if err
|
||||||
@logSubscriptionError(req, 'Stripe customer plan setting error. '+err)
|
@logSubscriptionError(user, 'Stripe customer plan setting error. '+err)
|
||||||
return done({res: 'Database error.', code: 500})
|
return done({res: 'Database error.', code: 500})
|
||||||
|
|
||||||
@updateUser(req, user, customer, true, done)
|
@updateUser(req, user, customer, true, done)
|
||||||
|
@ -123,25 +121,25 @@ class SubscriptionHandler extends Handler
|
||||||
|
|
||||||
user.save (err) =>
|
user.save (err) =>
|
||||||
if err
|
if err
|
||||||
@logSubscriptionError(req, 'Stripe user plan saving error. '+err)
|
@logSubscriptionError(user, 'Stripe user plan saving error. '+err)
|
||||||
return done({res: 'Database error.', code: 500})
|
return done({res: 'Database error.', code: 500})
|
||||||
req.user?.saveActiveUser 'subscribe'
|
user?.saveActiveUser 'subscribe'
|
||||||
return done()
|
return done()
|
||||||
|
|
||||||
unsubscribeUser: (req, user, done) ->
|
unsubscribeUser: (req, user, done) ->
|
||||||
stripeInfo = _.cloneDeep(user.get('stripe'))
|
stripeInfo = _.cloneDeep(user.get('stripe'))
|
||||||
stripe.customers.cancelSubscription stripeInfo.customerID, stripeInfo.subscriptionID, { at_period_end: true }, (err) =>
|
stripe.customers.cancelSubscription stripeInfo.customerID, stripeInfo.subscriptionID, { at_period_end: true }, (err) =>
|
||||||
if err
|
if err
|
||||||
@logSubscriptionError(req, 'Stripe cancel subscription error. '+err)
|
@logSubscriptionError(user, 'Stripe cancel subscription error. '+err)
|
||||||
return done({res: 'Database error.', code: 500})
|
return done({res: 'Database error.', code: 500})
|
||||||
delete stripeInfo.planID
|
delete stripeInfo.planID
|
||||||
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(req, 'User save unsubscribe error. '+err)
|
@logSubscriptionError(user, 'User save unsubscribe error. '+err)
|
||||||
return done({res: 'Database error.', code: 500})
|
return done({res: 'Database error.', code: 500})
|
||||||
req.user?.saveActiveUser 'unsubscribe'
|
user?.saveActiveUser 'unsubscribe'
|
||||||
return done()
|
return done()
|
||||||
|
|
||||||
module.exports = new SubscriptionHandler()
|
module.exports = new SubscriptionHandler()
|
||||||
|
|
|
@ -179,6 +179,13 @@ UserSchema.methods.register = (done) ->
|
||||||
delighted.addDelightedUser @
|
delighted.addDelightedUser @
|
||||||
@saveActiveUser 'register'
|
@saveActiveUser 'register'
|
||||||
|
|
||||||
|
UserSchema.methods.isPremium = ->
|
||||||
|
return false unless stripe = @get('stripe')
|
||||||
|
return true if stripe.subscriptionID
|
||||||
|
return true if stripe.free is true
|
||||||
|
return true if _.isString(stripe.free) and new Date() < new Date(stripe.free)
|
||||||
|
return false
|
||||||
|
|
||||||
UserSchema.statics.saveActiveUser = (id, event, done=null) ->
|
UserSchema.statics.saveActiveUser = (id, event, done=null) ->
|
||||||
id = mongoose.Types.ObjectId id if _.isString id
|
id = mongoose.Types.ObjectId id if _.isString id
|
||||||
@findById id, (err, user) ->
|
@findById id, (err, user) ->
|
||||||
|
|
|
@ -230,9 +230,19 @@ UserHandler = class UserHandler extends Handler
|
||||||
return @trackActivity(req, res, args[0], args[2], args[3]) if args[1] is 'track' and args[2]
|
return @trackActivity(req, res, args[0], args[2], args[3]) if args[1] is 'track' and args[2]
|
||||||
return @getRemark(req, res, args[0]) if args[1] is 'remark'
|
return @getRemark(req, res, args[0]) if args[1] is 'remark'
|
||||||
return @searchForUser(req, res) if args[1] is 'admin_search'
|
return @searchForUser(req, res) if args[1] is 'admin_search'
|
||||||
|
return @getStripeInfo(req, res, args[0]) if args[1] is 'stripe'
|
||||||
return @sendNotFoundError(res)
|
return @sendNotFoundError(res)
|
||||||
super(arguments...)
|
super(arguments...)
|
||||||
|
|
||||||
|
getStripeInfo: (req, res, handle) ->
|
||||||
|
@getDocumentForIdOrSlug handle, (err, user) =>
|
||||||
|
return @sendNotFoundError(res) if not user
|
||||||
|
return @sendForbiddenError(res) unless req.user and (req.user.isAdmin() or req.user.get('_id').equals(user.get('_id')))
|
||||||
|
return @sendNotFoundError(res) #if not customerID = user.get('stripe')?.customerID
|
||||||
|
stripe.customers.retrieve customerID, (err, customer) =>
|
||||||
|
return @sendDatabaseError(res, err) if err
|
||||||
|
@sendSuccess(res, JSON.stringify(customer, null, '\t'))
|
||||||
|
|
||||||
agreeToCLA: (req, res) ->
|
agreeToCLA: (req, res) ->
|
||||||
return @sendForbiddenError(res) unless req.user
|
return @sendForbiddenError(res) unless req.user
|
||||||
doc =
|
doc =
|
||||||
|
|
|
@ -16,6 +16,8 @@ UserHandler = require './server/users/user_handler'
|
||||||
hipchat = require './server/hipchat'
|
hipchat = require './server/hipchat'
|
||||||
global.tv4 = require 'tv4' # required for TreemaUtils to work
|
global.tv4 = require 'tv4' # required for TreemaUtils to work
|
||||||
global.jsondiffpatch = require 'jsondiffpatch'
|
global.jsondiffpatch = require 'jsondiffpatch'
|
||||||
|
global.stripe = require('stripe')(config.stripe.secretKey)
|
||||||
|
|
||||||
|
|
||||||
productionLogging = (tokens, req, res) ->
|
productionLogging = (tokens, req, res) ->
|
||||||
status = res.statusCode
|
status = res.statusCode
|
||||||
|
|