mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-28 01:55:38 -05:00
Merge branch 'master' into production
This commit is contained in:
commit
6fb5f270b3
15 changed files with 166 additions and 110 deletions
|
@ -15,7 +15,8 @@ module.exports = class LevelSetupManager extends CocoClass
|
||||||
super()
|
super()
|
||||||
@supermodel = @options.supermodel ? new SuperModel()
|
@supermodel = @options.supermodel ? new SuperModel()
|
||||||
@session = @options.session
|
@session = @options.session
|
||||||
@loadLevel()
|
unless @level = @options.level
|
||||||
|
@loadLevel()
|
||||||
if @session
|
if @session
|
||||||
@fillSessionWithDefaults()
|
@fillSessionWithDefaults()
|
||||||
else
|
else
|
||||||
|
|
|
@ -79,8 +79,8 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
|
||||||
adjust_volume: "Ajustar el volumen"
|
adjust_volume: "Ajustar el volumen"
|
||||||
campaign_multiplayer: "Arenas Multijugador"
|
campaign_multiplayer: "Arenas Multijugador"
|
||||||
campaign_multiplayer_description: "... en las que programas cara a cara contra otros jugadores."
|
campaign_multiplayer_description: "... en las que programas cara a cara contra otros jugadores."
|
||||||
# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas"
|
campaign_old_multiplayer: "(Obsoleto) Antiguas Arenas Multijugador"
|
||||||
# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas."
|
campaign_old_multiplayer_description: "Reliquias de una era más civilizada. Ninguna simulación es ejecutada para estas arenas multijugador antiguas y sin héroes."
|
||||||
|
|
||||||
share_progress_modal:
|
share_progress_modal:
|
||||||
blurb: "¡Estás haciendo un gran progreso! Cuéntale a alguien cuánto has aprendido con CodeCombat." # {change}
|
blurb: "¡Estás haciendo un gran progreso! Cuéntale a alguien cuánto has aprendido con CodeCombat." # {change}
|
||||||
|
@ -247,7 +247,7 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
|
||||||
victory_saving_progress: "Guardando Progreso"
|
victory_saving_progress: "Guardando Progreso"
|
||||||
victory_go_home: "Ir al Inicio"
|
victory_go_home: "Ir al Inicio"
|
||||||
victory_review: "¡Cuéntanos más!"
|
victory_review: "¡Cuéntanos más!"
|
||||||
# victory_review_placeholder: "How was the level?"
|
victory_review_placeholder: "¿Cómo estuvo el nivel?"
|
||||||
victory_hour_of_code_done: "¿Has acabado?"
|
victory_hour_of_code_done: "¿Has acabado?"
|
||||||
victory_hour_of_code_done_yes: "¡Si, he terminado con mi Hora de Código!"
|
victory_hour_of_code_done_yes: "¡Si, he terminado con mi Hora de Código!"
|
||||||
victory_experience_gained: "XP Ganada"
|
victory_experience_gained: "XP Ganada"
|
||||||
|
@ -284,7 +284,7 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
|
||||||
time_goto: "Ir a:"
|
time_goto: "Ir a:"
|
||||||
non_user_code_problem_title: "No se puede cargar el nivel"
|
non_user_code_problem_title: "No se puede cargar el nivel"
|
||||||
infinite_loop_title: "Loop infinito detectado"
|
infinite_loop_title: "Loop infinito detectado"
|
||||||
# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know."
|
infinite_loop_description: "El código inicial para construir el mundo no terminó de ejecutarse. Probablemente es muy lento o tiene un loop infinito. O puede ser un bug. Puedes tratar ejecutando este código nuevamente o reiniciar el código a su estado por defecto. Si eso no lo arregla, haznos saber."
|
||||||
check_dev_console: "Tú puedes también abrir la consola de desarrollo para ver que puede salir mal."
|
check_dev_console: "Tú puedes también abrir la consola de desarrollo para ver que puede salir mal."
|
||||||
check_dev_console_link: "(instrucciones)"
|
check_dev_console_link: "(instrucciones)"
|
||||||
infinite_loop_try_again: "Intentar nuevamente"
|
infinite_loop_try_again: "Intentar nuevamente"
|
||||||
|
@ -294,7 +294,7 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
|
||||||
tip_scrub_shortcut: "Ctrl+[ y Ctrl+] rebobina y avance rápido." # {change}
|
tip_scrub_shortcut: "Ctrl+[ y Ctrl+] rebobina y avance rápido." # {change}
|
||||||
tip_guide_exists: "Haga click en la guía en la parte superior de la página para obtener información útil"
|
tip_guide_exists: "Haga click en la guía en la parte superior de la página para obtener información útil"
|
||||||
tip_open_source: "¡CodeCombat es 100% código abierto!"
|
tip_open_source: "¡CodeCombat es 100% código abierto!"
|
||||||
# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!"
|
tip_tell_friends: "¿Disfrutando de CodeCombat? ¡Cuéntale a tus amigos acerca de nosotros!"
|
||||||
tip_beta_launch: "CodeCombat lanzó su beta en Octubre del 2013."
|
tip_beta_launch: "CodeCombat lanzó su beta en Octubre del 2013."
|
||||||
tip_think_solution: "Piensa en la solución, no en el problema."
|
tip_think_solution: "Piensa en la solución, no en el problema."
|
||||||
tip_theory_practice: "En teoría, no hay diferencia entre la teoría y la práctica. Pero en la práctica, si la hay. - Yogi Berra"
|
tip_theory_practice: "En teoría, no hay diferencia entre la teoría y la práctica. Pero en la práctica, si la hay. - Yogi Berra"
|
||||||
|
@ -338,7 +338,7 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
|
||||||
tip_recurse: "Iterar es humano, recursar es divino. - L. Peter Deutsch"
|
tip_recurse: "Iterar es humano, recursar es divino. - L. Peter Deutsch"
|
||||||
tip_free_your_mind: "Tienes que dejar ir todo, Neo. Miedo, duda, e incredulidad. Libera tu mente. - Morpheus"
|
tip_free_your_mind: "Tienes que dejar ir todo, Neo. Miedo, duda, e incredulidad. Libera tu mente. - Morpheus"
|
||||||
tip_strong_opponents: "Hasta los oponentes mas fuertes siempre tienen una debilidad. - Itachi Uchiha"
|
tip_strong_opponents: "Hasta los oponentes mas fuertes siempre tienen una debilidad. - Itachi Uchiha"
|
||||||
# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen."
|
tip_paper_and_pen: "Antes de comenzar a codificar, siempre puedes planear con una hoja de papel y un lapicero."
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
inventory_tab: "Inventario"
|
inventory_tab: "Inventario"
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
c = require './../schemas'
|
c = require './../schemas'
|
||||||
|
|
||||||
PrepaidSchema = c.object({title: 'Prepaid', required: ['creator', 'redeemer', 'type']}, {
|
PrepaidSchema = c.object({title: 'Prepaid', required: ['creator', 'type']}, {
|
||||||
creator: c.objectId(links: [ {rel: 'extra', href: '/db/user/{($)}'} ])
|
creator: c.objectId(links: [ {rel: 'extra', href: '/db/user/{($)}'} ])
|
||||||
redeemer: c.objectId(links: [ {rel: 'extra', href: '/db/user/{($)}'} ])
|
redeemers: c.array {}, c.objectId(links: [ {rel: 'extra', href: '/db/user/{($)}'} ])
|
||||||
|
maxRedeemers: { type: 'integer'}
|
||||||
code: c.shortString(title: "Unique code to redeem")
|
code: c.shortString(title: "Unique code to redeem")
|
||||||
type: { type: 'string' }
|
type: { type: 'string' }
|
||||||
status: { enum: ['active', 'used'], default: 'active' }
|
|
||||||
properties: {type: 'object'}
|
properties: {type: 'object'}
|
||||||
|
# Deprecated
|
||||||
|
status: { enum: ['active', 'used'], default: 'active' }
|
||||||
|
redeemer: c.objectId(links: [ {rel: 'extra', href: '/db/user/{($)}'} ])
|
||||||
})
|
})
|
||||||
|
|
||||||
c.extendBasicProperties(PrepaidSchema, 'prepaid')
|
c.extendBasicProperties(PrepaidSchema, 'prepaid')
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
extends /templates/base
|
|
||||||
|
|
||||||
block content
|
|
||||||
|
|
||||||
h1(data-i18n="play.choose_your_level") Choose Your Level
|
|
||||||
p
|
|
||||||
span(data-i18n="play.adventurer_prefix") You can jump to any level below, or discuss the levels on
|
|
||||||
a(href="http://discourse.codecombat.com/category/adventurer", data-i18n="play.adventurer_forum") the Adventurer forum
|
|
||||||
span(data-i18n="play.adventurer_suffix") .
|
|
||||||
|
|
||||||
.row
|
|
||||||
|
|
||||||
each campaign in campaigns
|
|
||||||
.campaign-container.col-sm-6
|
|
||||||
h1
|
|
||||||
a(href="/play/#{campaign.levels[0].levelPath || 'level'}/#{campaign.levels[0].id}", data-i18n="play.campaign_#{campaign.id}")= campaign.name
|
|
||||||
p.campaign-description(data-i18n="[html]play.campaign_#{campaign.id}_description")!= campaign.description
|
|
||||||
each level in campaign.levels
|
|
||||||
a(href=level.disabled ? "/play" : "/play/#{level.levelPath || 'level'}/#{level.id}", disabled=level.disabled, class=levelStatusMap[level.id] || '')
|
|
||||||
.level.row
|
|
||||||
if level.image
|
|
||||||
img.level-image(src="#{level.image}", alt="#{level.name}")
|
|
||||||
else
|
|
||||||
img.level-image(src="/images/generic-icon.png", alt="#{level.name}")
|
|
||||||
.level-info
|
|
||||||
h3= level.name + (level.disabled ? " (Coming soon!)" : "")
|
|
||||||
.level-description= level.description
|
|
||||||
span(data-i18n="play.level_difficulty") Difficulty:
|
|
||||||
each i in Array(level.difficulty)
|
|
||||||
i.icon-star
|
|
||||||
- var playCount = levelPlayCountMap[level.id]
|
|
||||||
if playCount
|
|
||||||
div
|
|
||||||
span.spr #{playCount.sessions}
|
|
||||||
span(data-i18n="play.players") players
|
|
||||||
span.spr , #{Math.round(playCount.playtime / 3600)}
|
|
||||||
span(data-i18n="play.hours_played") hours played
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ module.exports = class MainAdminView extends RootView
|
||||||
return unless me.isAdmin()
|
return unless me.isAdmin()
|
||||||
options =
|
options =
|
||||||
url: '/db/prepaid/-/create'
|
url: '/db/prepaid/-/create'
|
||||||
data: {type: 'subscription'}
|
data: {type: 'subscription', maxRedeemers: 1}
|
||||||
method: 'POST'
|
method: 'POST'
|
||||||
options.success = (model, response, options) =>
|
options.success = (model, response, options) =>
|
||||||
# TODO: Don't hardcode domain.
|
# TODO: Don't hardcode domain.
|
||||||
|
|
|
@ -97,12 +97,12 @@ module.exports = class ControlBarView extends CocoView
|
||||||
c.homeLink = @homeLink
|
c.homeLink = @homeLink
|
||||||
c
|
c
|
||||||
|
|
||||||
showGameMenuModal: ->
|
showGameMenuModal: (tab='guide') ->
|
||||||
gameMenuModal = new GameMenuModal level: @level, session: @session, supermodel: @supermodel
|
gameMenuModal = new GameMenuModal level: @level, session: @session, supermodel: @supermodel, showTab: tab
|
||||||
@openModalView gameMenuModal
|
@openModalView gameMenuModal
|
||||||
@listenToOnce gameMenuModal, 'change-hero', ->
|
@listenToOnce gameMenuModal, 'change-hero', ->
|
||||||
@setupManager?.destroy()
|
@setupManager?.destroy()
|
||||||
@setupManager = new LevelSetupManager({supermodel: @supermodel, levelID: @levelID, parent: @, session: @session})
|
@setupManager = new LevelSetupManager({supermodel: @supermodel, level: @level, levelID: @levelID, parent: @, session: @session})
|
||||||
@setupManager.open()
|
@setupManager.open()
|
||||||
|
|
||||||
onClickHome: (e) ->
|
onClickHome: (e) ->
|
||||||
|
@ -111,7 +111,7 @@ module.exports = class ControlBarView extends CocoView
|
||||||
Backbone.Mediator.publish 'router:navigate', route: @homeLink, viewClass: @homeViewClass, viewArgs: @homeViewArgs
|
Backbone.Mediator.publish 'router:navigate', route: @homeLink, viewClass: @homeViewClass, viewArgs: @homeViewArgs
|
||||||
|
|
||||||
onClickMultiplayer: (e) ->
|
onClickMultiplayer: (e) ->
|
||||||
@openModalView new GameMenuModal showTab: 'multiplayer', level: @level, session: @session, supermodel: @supermodel
|
@showGameMenuModal 'multiplayer'
|
||||||
|
|
||||||
onClickSignupButton: ->
|
onClickSignupButton: ->
|
||||||
window.tracker?.trackEvent 'Started Signup', category: 'Play Level', label: 'Control Bar', level: @levelID
|
window.tracker?.trackEvent 'Started Signup', category: 'Play Level', label: 'Control Bar', level: @levelID
|
||||||
|
|
|
@ -279,7 +279,7 @@ module.exports = class PlayLevelView extends RootView
|
||||||
e.session.set 'heroConfig', {"thangType":sorcerer,"inventory":{"misc-0":"53e2396a53457600003e3f0f","programming-book":"546e266e9df4a17d0d449be5","minion":"54eb5dbc49fa2d5c905ddf56","feet":"53e214f153457600003e3eab","right-hand":"54eab7f52b7506e891ca7202","left-hand":"5463758f3839c6e02811d30f","wrists":"54693797a2b1f53ce79443e9","gloves":"5469425ca2b1f53ce7944421","torso":"546d4a549df4a17d0d449a97","neck":"54693274a2b1f53ce79443c9","eyes":"546941fda2b1f53ce794441d","head":"546d4ca19df4a17d0d449abf"}}
|
e.session.set 'heroConfig', {"thangType":sorcerer,"inventory":{"misc-0":"53e2396a53457600003e3f0f","programming-book":"546e266e9df4a17d0d449be5","minion":"54eb5dbc49fa2d5c905ddf56","feet":"53e214f153457600003e3eab","right-hand":"54eab7f52b7506e891ca7202","left-hand":"5463758f3839c6e02811d30f","wrists":"54693797a2b1f53ce79443e9","gloves":"5469425ca2b1f53ce7944421","torso":"546d4a549df4a17d0d449a97","neck":"54693274a2b1f53ce79443c9","eyes":"546941fda2b1f53ce794441d","head":"546d4ca19df4a17d0d449abf"}}
|
||||||
else if e.level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop'] and not _.size e.session.get('heroConfig')?.inventory ? {}
|
else if e.level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop'] and not _.size e.session.get('heroConfig')?.inventory ? {}
|
||||||
@setupManager?.destroy()
|
@setupManager?.destroy()
|
||||||
@setupManager = new LevelSetupManager({supermodel: @supermodel, levelID: @levelID, parent: @, session: @session})
|
@setupManager = new LevelSetupManager({supermodel: @supermodel, level: @level, levelID: @levelID, parent: @, session: @session})
|
||||||
@setupManager.open()
|
@setupManager.open()
|
||||||
|
|
||||||
@onRealTimeMultiplayerLevelLoaded e.session if e.level.get('type') in ['hero-ladder', 'course-ladder']
|
@onRealTimeMultiplayerLevelLoaded e.session if e.level.get('type') in ['hero-ladder', 'course-ladder']
|
||||||
|
@ -577,7 +577,7 @@ module.exports = class PlayLevelView extends RootView
|
||||||
if slot and not inventory[slot]
|
if slot and not inventory[slot]
|
||||||
# Open up the inventory modal so they can equip the new item
|
# Open up the inventory modal so they can equip the new item
|
||||||
@setupManager?.destroy()
|
@setupManager?.destroy()
|
||||||
@setupManager = new LevelSetupManager({supermodel: @supermodel, levelID: @levelID, parent: @, session: @session, hadEverChosenHero: true})
|
@setupManager = new LevelSetupManager({supermodel: @supermodel, level: @level, levelID: @levelID, parent: @, session: @session, hadEverChosenHero: true})
|
||||||
@setupManager.open()
|
@setupManager.open()
|
||||||
|
|
||||||
# Start Real-time Multiplayer ######################################################
|
# Start Real-time Multiplayer ######################################################
|
||||||
|
|
|
@ -6,6 +6,7 @@ SpellPaletteEntryView = require './SpellPaletteEntryView'
|
||||||
LevelComponent = require 'models/LevelComponent'
|
LevelComponent = require 'models/LevelComponent'
|
||||||
ThangType = require 'models/ThangType'
|
ThangType = require 'models/ThangType'
|
||||||
GameMenuModal = require 'views/play/menu/GameMenuModal'
|
GameMenuModal = require 'views/play/menu/GameMenuModal'
|
||||||
|
LevelSetupManager = require 'lib/LevelSetupManager'
|
||||||
|
|
||||||
N_ROWS = 4
|
N_ROWS = 4
|
||||||
|
|
||||||
|
@ -319,10 +320,16 @@ module.exports = class SpellPaletteView extends CocoView
|
||||||
|
|
||||||
onClickHelp: (e) ->
|
onClickHelp: (e) ->
|
||||||
application.tracker?.trackEvent 'Spell palette help clicked', levelID: @level.get('slug')
|
application.tracker?.trackEvent 'Spell palette help clicked', levelID: @level.get('slug')
|
||||||
@openModalView new GameMenuModal showTab: 'guide', level: @level, session: @session, supermodel: @supermodel
|
gameMenuModal = new GameMenuModal showTab: 'guide', level: @level, session: @session, supermodel: @supermodel
|
||||||
|
@openModalView gameMenuModal
|
||||||
|
@listenToOnce gameMenuModal, 'change-hero', ->
|
||||||
|
@setupManager?.destroy()
|
||||||
|
@setupManager = new LevelSetupManager({supermodel: @supermodel, level: @level, levelID: @level.get('slug'), parent: @, session: @session})
|
||||||
|
@setupManager.open()
|
||||||
|
|
||||||
destroy: ->
|
destroy: ->
|
||||||
entry.destroy() for entry in @entries
|
entry.destroy() for entry in @entries
|
||||||
@toggleBackground = null
|
@toggleBackground = null
|
||||||
$(window).off 'resize', @onResize
|
$(window).off 'resize', @onResize
|
||||||
|
@setupManager?.destroy()
|
||||||
super()
|
super()
|
||||||
|
|
|
@ -22,7 +22,7 @@ function createPrepaid()
|
||||||
criteria = {
|
criteria = {
|
||||||
creator: creatorID,
|
creator: creatorID,
|
||||||
type: 'subscription',
|
type: 'subscription',
|
||||||
status: 'active',
|
maxRedeemers: 1
|
||||||
code: code,
|
code: code,
|
||||||
properties: {
|
properties: {
|
||||||
couponID: 'free'
|
couponID: 'free'
|
||||||
|
|
|
@ -174,16 +174,51 @@ class SubscriptionHandler extends Handler
|
||||||
return done({res: 'Database error.', code: 500})
|
return done({res: 'Database error.', code: 500})
|
||||||
return done({res: 'Prepaid not found', code: 404}) unless prepaid?
|
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 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
|
# Deprecated: status property
|
||||||
stripeInfo = _.cloneDeep(user.get('stripe') ? {})
|
if status = prepaid.get('status') and status is 'used'
|
||||||
stripeInfo.couponID = couponID
|
return done({res: 'Prepaid has already been used', code: 403})
|
||||||
stripeInfo.prepaidCode = req.body.stripe.prepaidCode
|
|
||||||
user.set('stripe', stripeInfo)
|
if prepaid.get('redeemers')?.length >= prepaid.get('maxRedeemers')
|
||||||
@checkForExistingSubscription(req, user, customer, couponID, done)
|
@logSubscriptionError(user, "Prepaid #{prepaid.id} note active")
|
||||||
|
return done({res: 'Prepaid not active', code: 403})
|
||||||
|
unless couponID = prepaid.get('properties')?.couponID
|
||||||
|
@logSubscriptionError(user, "Prepaid #{prepaid.id} has no couponID")
|
||||||
|
return done({res: 'Database error.', code: 500})
|
||||||
|
|
||||||
|
redeemers = prepaid.get('redeemers') ? []
|
||||||
|
if _.find(redeemers, (a) -> a.userID?.equals(user.get('_id')))
|
||||||
|
@logSubscriptionError(user, "Prepaid code already redeemed by #{user.id}")
|
||||||
|
return done({res: 'Prepaid code already redeemed', code: 403})
|
||||||
|
|
||||||
|
# Redeem prepaid code
|
||||||
|
|
||||||
|
# Deprecated: status and redeemer properties
|
||||||
|
prepaid.set('status', 'used')
|
||||||
|
prepaid.set('redeemer', user.get('_id'))
|
||||||
|
|
||||||
|
query = Prepaid.$where("'#{prepaid.get('_id').valueOf()}' === this._id.valueOf() && (!this.redeemers || this.redeemers.length < this.maxRedeemers)")
|
||||||
|
redeemers.push
|
||||||
|
userID: user.get('_id')
|
||||||
|
date: new Date()
|
||||||
|
update = {redeemers: redeemers}
|
||||||
|
Prepaid.update query, update, {}, (err, numAffected) =>
|
||||||
|
if err
|
||||||
|
@logSubscriptionError(user, 'Prepaid update error. ' + err)
|
||||||
|
return done({res: 'Database error.', code: 500})
|
||||||
|
if numAffected > 1
|
||||||
|
@logSubscriptionError(user, "Prepaid numAffected=#{numAffected} error.")
|
||||||
|
return done({res: 'Database error.', code: 500})
|
||||||
|
if numAffected < 1
|
||||||
|
return done({res: 'Prepaid not active', code: 403})
|
||||||
|
|
||||||
|
# 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
|
else
|
||||||
couponID = user.get('stripe')?.couponID
|
couponID = user.get('stripe')?.couponID
|
||||||
# SALE LOGIC
|
# SALE LOGIC
|
||||||
|
@ -257,25 +292,7 @@ class SubscriptionHandler extends Handler
|
||||||
if err
|
if err
|
||||||
@logSubscriptionError(user, '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})
|
||||||
|
done()
|
||||||
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()
|
|
||||||
|
|
||||||
updateStripeRecipientSubscriptions: (req, user, customer, done) ->
|
updateStripeRecipientSubscriptions: (req, user, customer, done) ->
|
||||||
return done({res: 'Database error.', code: 500}) unless req.body.stripe?.subscribeEmails?
|
return done({res: 'Database error.', code: 500}) unless req.body.stripe?.subscribeEmails?
|
||||||
|
|
|
@ -20,13 +20,14 @@ PrepaidHandler = class PrepaidHandler extends Handler
|
||||||
createPrepaid: (req, res) ->
|
createPrepaid: (req, res) ->
|
||||||
return @sendForbiddenError(res) unless @hasAccess(req)
|
return @sendForbiddenError(res) unless @hasAccess(req)
|
||||||
return @sendForbiddenError(res) unless req.body.type is 'subscription'
|
return @sendForbiddenError(res) unless req.body.type is 'subscription'
|
||||||
|
return @sendForbiddenError(res) unless req.body.maxRedeemers > 0
|
||||||
Prepaid.generateNewCode (code) =>
|
Prepaid.generateNewCode (code) =>
|
||||||
return @sendDatabaseError(res, 'Database error.') unless code
|
return @sendDatabaseError(res, 'Database error.') unless code
|
||||||
prepaid = new Prepaid
|
prepaid = new Prepaid
|
||||||
creator: req.user.id
|
creator: req.user.id
|
||||||
type: req.body.type
|
type: req.body.type
|
||||||
status: 'active'
|
|
||||||
code: code
|
code: code
|
||||||
|
maxRedeemers: req.body.maxRedeemers
|
||||||
properties:
|
properties:
|
||||||
couponID: 'free'
|
couponID: 'free'
|
||||||
prepaid.save (err) =>
|
prepaid.save (err) =>
|
||||||
|
|
|
@ -17,7 +17,7 @@ TrialRequestSchema.pre 'save', (next) ->
|
||||||
prepaid = new Prepaid
|
prepaid = new Prepaid
|
||||||
creator: @get('reviewer')
|
creator: @get('reviewer')
|
||||||
type: 'subscription'
|
type: 'subscription'
|
||||||
status: 'active'
|
maxRedeemers: 1
|
||||||
code: code
|
code: code
|
||||||
properties:
|
properties:
|
||||||
couponID: 'free'
|
couponID: 'free'
|
||||||
|
|
|
@ -118,9 +118,11 @@ wrapUpGetUser = (email, user, done) ->
|
||||||
GLOBAL.getURL = (path) ->
|
GLOBAL.getURL = (path) ->
|
||||||
return 'http://localhost:3001' + path
|
return 'http://localhost:3001' + path
|
||||||
|
|
||||||
GLOBAL.createPrepaid = (type, done) ->
|
GLOBAL.createPrepaid = (type, maxRedeemers, done) ->
|
||||||
options = uri: GLOBAL.getURL('/db/prepaid/-/create')
|
options = uri: GLOBAL.getURL('/db/prepaid/-/create')
|
||||||
options.json = type: type if type?
|
options.json =
|
||||||
|
type: type
|
||||||
|
maxRedeemers: maxRedeemers
|
||||||
request.post options, done
|
request.post options, done
|
||||||
|
|
||||||
newUserCount = 0
|
newUserCount = 0
|
||||||
|
|
|
@ -7,7 +7,7 @@ describe '/db/prepaid', ->
|
||||||
verifyPrepaid = (user, prepaid, done) ->
|
verifyPrepaid = (user, prepaid, done) ->
|
||||||
expect(prepaid.creator).toEqual(user.id)
|
expect(prepaid.creator).toEqual(user.id)
|
||||||
expect(prepaid.type).toEqual('subscription')
|
expect(prepaid.type).toEqual('subscription')
|
||||||
expect(prepaid.status).toEqual('active')
|
expect(prepaid.maxRedeemers).toBeGreaterThan(0)
|
||||||
expect(prepaid.code).toMatch(/^\w{8}$/)
|
expect(prepaid.code).toMatch(/^\w{8}$/)
|
||||||
expect(prepaid.properties?.couponID).toEqual('free')
|
expect(prepaid.properties?.couponID).toEqual('free')
|
||||||
done()
|
done()
|
||||||
|
@ -18,7 +18,7 @@ describe '/db/prepaid', ->
|
||||||
done()
|
done()
|
||||||
|
|
||||||
it 'Anonymous creates prepaid code', (done) ->
|
it 'Anonymous creates prepaid code', (done) ->
|
||||||
createPrepaid 'subscription', (err, res, body) ->
|
createPrepaid 'subscription', 1, (err, res, body) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
expect(res.statusCode).toBe(401)
|
expect(res.statusCode).toBe(401)
|
||||||
done()
|
done()
|
||||||
|
@ -26,7 +26,7 @@ describe '/db/prepaid', ->
|
||||||
it 'Non-admin creates prepaid code', (done) ->
|
it 'Non-admin creates prepaid code', (done) ->
|
||||||
loginNewUser (user1) ->
|
loginNewUser (user1) ->
|
||||||
expect(user1.isAdmin()).toEqual(false)
|
expect(user1.isAdmin()).toEqual(false)
|
||||||
createPrepaid 'subscription', (err, res, body) ->
|
createPrepaid 'subscription', 4, (err, res, body) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
expect(res.statusCode).toBe(403)
|
expect(res.statusCode).toBe(403)
|
||||||
done()
|
done()
|
||||||
|
@ -37,7 +37,7 @@ describe '/db/prepaid', ->
|
||||||
user1.save (err, user1) ->
|
user1.save (err, user1) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
expect(user1.isAdmin()).toEqual(true)
|
expect(user1.isAdmin()).toEqual(true)
|
||||||
createPrepaid 'subscription', (err, res, body) ->
|
createPrepaid 'subscription', 1, (err, res, body) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
expect(res.statusCode).toBe(200)
|
expect(res.statusCode).toBe(200)
|
||||||
verifyPrepaid user1, body, done
|
verifyPrepaid user1, body, done
|
||||||
|
@ -48,7 +48,7 @@ describe '/db/prepaid', ->
|
||||||
user1.save (err, user1) ->
|
user1.save (err, user1) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
expect(user1.isAdmin()).toEqual(true)
|
expect(user1.isAdmin()).toEqual(true)
|
||||||
createPrepaid 'bulldozer', (err, res, body) ->
|
createPrepaid 'bulldozer', 1, (err, res, body) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
expect(res.statusCode).toBe(403)
|
expect(res.statusCode).toBe(403)
|
||||||
done()
|
done()
|
||||||
|
@ -59,7 +59,18 @@ describe '/db/prepaid', ->
|
||||||
user1.save (err, user1) ->
|
user1.save (err, user1) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
expect(user1.isAdmin()).toEqual(true)
|
expect(user1.isAdmin()).toEqual(true)
|
||||||
createPrepaid null, (err, res, body) ->
|
createPrepaid null, 1, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(403)
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'Admin creates prepaid code with invalid maxRedeemers', (done) ->
|
||||||
|
loginNewUser (user1) ->
|
||||||
|
user1.set('permissions', ['admin'])
|
||||||
|
user1.save (err, user1) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(user1.isAdmin()).toEqual(true)
|
||||||
|
createPrepaid 'subscription', 0, (err, res, body) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
expect(res.statusCode).toBe(403)
|
expect(res.statusCode).toBe(403)
|
||||||
done()
|
done()
|
||||||
|
@ -78,7 +89,7 @@ describe '/db/prepaid', ->
|
||||||
user1.save (err, user1) ->
|
user1.save (err, user1) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
expect(user1.isAdmin()).toEqual(true)
|
expect(user1.isAdmin()).toEqual(true)
|
||||||
createPrepaid 'subscription', (err, res, prepaid) ->
|
createPrepaid 'subscription', 1, (err, res, prepaid) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
expect(res.statusCode).toBe(200)
|
expect(res.statusCode).toBe(200)
|
||||||
request.get {uri: prepaidURL}, (err, res, body) ->
|
request.get {uri: prepaidURL}, (err, res, body) ->
|
||||||
|
|
|
@ -542,14 +542,38 @@ describe 'Subscriptions', ->
|
||||||
user1.save (err, user1) ->
|
user1.save (err, user1) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
expect(user1.isAdmin()).toEqual(true)
|
expect(user1.isAdmin()).toEqual(true)
|
||||||
createPrepaid 'subscription', (err, res, prepaid) ->
|
createPrepaid 'subscription', 1, (err, res, prepaid) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
subscribeUser user1, null, prepaid.code, ->
|
subscribeUser user1, null, prepaid.code, ->
|
||||||
Prepaid.findById prepaid._id, (err, prepaid) ->
|
Prepaid.findById prepaid._id, (err, prepaid) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
expect(prepaid.get('status')).toEqual('used')
|
expect(prepaid.get('maxRedeemers')).toEqual(1)
|
||||||
|
expect(prepaid.get('redeemers')[0].userID).toEqual(user1.get('_id'))
|
||||||
|
expect(prepaid.get('redeemers')[0].date).toBeLessThan(new Date())
|
||||||
done()
|
done()
|
||||||
|
|
||||||
|
it 'Admin subscribes self with valid prepaid twice', (done) ->
|
||||||
|
loginNewUser (user1) ->
|
||||||
|
user1.set('permissions', ['admin'])
|
||||||
|
user1.save (err, user1) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(user1.isAdmin()).toEqual(true)
|
||||||
|
createPrepaid 'subscription', 2, (err, res, prepaid) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
Prepaid.findById prepaid._id, (err, prepaid) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
prepaid.set 'redeemers', [{userID: user1.get('_id'), date: new Date()}]
|
||||||
|
prepaid.save (err) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
requestBody = user1.toObject()
|
||||||
|
requestBody.stripe =
|
||||||
|
planID: 'basic'
|
||||||
|
requestBody.stripe.prepaidCode = prepaid.get('code')
|
||||||
|
request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(403)
|
||||||
|
done()
|
||||||
|
|
||||||
it 'User subscribes, deletes themselves, subscription ends', (done) ->
|
it 'User subscribes, deletes themselves, subscription ends', (done) ->
|
||||||
stripe.tokens.create {
|
stripe.tokens.create {
|
||||||
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
|
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
|
||||||
|
@ -596,7 +620,7 @@ describe 'Subscriptions', ->
|
||||||
user1.save (err, user1) ->
|
user1.save (err, user1) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
expect(user1.isAdmin()).toEqual(true)
|
expect(user1.isAdmin()).toEqual(true)
|
||||||
createPrepaid 'subscription', (err, res, prepaid) ->
|
createPrepaid 'subscription', 1, (err, res, prepaid) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
subscribeUser user1, null, prepaid.code, ->
|
subscribeUser user1, null, prepaid.code, ->
|
||||||
loginNewUser (user2) ->
|
loginNewUser (user2) ->
|
||||||
|
@ -607,7 +631,27 @@ describe 'Subscriptions', ->
|
||||||
request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, body) ->
|
request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, body) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
expect(res.statusCode).toBe(403)
|
expect(res.statusCode).toBe(403)
|
||||||
done()
|
Prepaid.findById prepaid._id, (err, prepaid) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(prepaid.get('redeemers')[0].userID).toEqual(user1.get('_id'))
|
||||||
|
expect(prepaid.get('redeemers')[0].date).toBeLessThan(new Date())
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'User2 subscribes with same active prepaid', (done) ->
|
||||||
|
loginNewUser (user1) ->
|
||||||
|
user1.set('permissions', ['admin'])
|
||||||
|
user1.save (err, user1) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(user1.isAdmin()).toEqual(true)
|
||||||
|
createPrepaid 'subscription', 2, (err, res, prepaid) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
subscribeUser user1, null, prepaid.code, ->
|
||||||
|
loginNewUser (user2) ->
|
||||||
|
subscribeUser user2, null, prepaid.code, ->
|
||||||
|
Prepaid.findById prepaid._id, (err, prepaid) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(prepaid.get('redeemers').length).toEqual(2)
|
||||||
|
done()
|
||||||
|
|
||||||
it 'Subscribe normally, subscribe with valid prepaid', (done) ->
|
it 'Subscribe normally, subscribe with valid prepaid', (done) ->
|
||||||
stripe.tokens.create {
|
stripe.tokens.create {
|
||||||
|
@ -621,12 +665,14 @@ describe 'Subscriptions', ->
|
||||||
subscribeUser user1, token, null, ->
|
subscribeUser user1, token, null, ->
|
||||||
User.findById user1.id, (err, user1) ->
|
User.findById user1.id, (err, user1) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
createPrepaid 'subscription', (err, res, prepaid) ->
|
createPrepaid 'subscription', 1, (err, res, prepaid) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
subscribeUser user1, null, prepaid.code, ->
|
subscribeUser user1, null, prepaid.code, ->
|
||||||
Prepaid.findById prepaid._id, (err, prepaid) ->
|
Prepaid.findById prepaid._id, (err, prepaid) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
expect(prepaid.get('status')).toEqual('used')
|
expect(prepaid.get('maxRedeemers')).toEqual(1)
|
||||||
|
expect(prepaid.get('redeemers')[0].userID).toEqual(user1.get('_id'))
|
||||||
|
expect(prepaid.get('redeemers')[0].date).toBeLessThan(new Date())
|
||||||
User.findById user1.id, (err, user1) ->
|
User.findById user1.id, (err, user1) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
customerID = user1.get('stripe').customerID
|
customerID = user1.get('stripe').customerID
|
||||||
|
@ -652,11 +698,13 @@ describe 'Subscriptions', ->
|
||||||
request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, updatedUser) ->
|
request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, updatedUser) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
expect(res.statusCode).toBe(200)
|
expect(res.statusCode).toBe(200)
|
||||||
createPrepaid 'subscription', (err, res, prepaid) ->
|
createPrepaid 'subscription', 1, (err, res, prepaid) ->
|
||||||
subscribeUser user1, null, prepaid.code, ->
|
subscribeUser user1, null, prepaid.code, ->
|
||||||
Prepaid.findById prepaid._id, (err, prepaid) ->
|
Prepaid.findById prepaid._id, (err, prepaid) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
expect(prepaid.get('status')).toEqual('used')
|
expect(prepaid.get('maxRedeemers')).toEqual(1)
|
||||||
|
expect(prepaid.get('redeemers')[0].userID).toEqual(user1.get('_id'))
|
||||||
|
expect(prepaid.get('redeemers')[0].date).toBeLessThan(new Date())
|
||||||
User.findById user1.id, (err, user1) ->
|
User.findById user1.id, (err, user1) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
customerID = user1.get('stripe').customerID
|
customerID = user1.get('stripe').customerID
|
||||||
|
@ -673,12 +721,14 @@ describe 'Subscriptions', ->
|
||||||
user1.save (err, user1) ->
|
user1.save (err, user1) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
expect(user1.isAdmin()).toEqual(true)
|
expect(user1.isAdmin()).toEqual(true)
|
||||||
createPrepaid 'subscription', (err, res, prepaid) ->
|
createPrepaid 'subscription', 1, (err, res, prepaid) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
subscribeUser user1, null, prepaid.code, ->
|
subscribeUser user1, null, prepaid.code, ->
|
||||||
Prepaid.findById prepaid._id, (err, prepaid) ->
|
Prepaid.findById prepaid._id, (err, prepaid) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
expect(prepaid.get('status')).toEqual('used')
|
expect(prepaid.get('maxRedeemers')).toEqual(1)
|
||||||
|
expect(prepaid.get('redeemers')[0].userID).toEqual(user1.get('_id'))
|
||||||
|
expect(prepaid.get('redeemers')[0].date).toBeLessThan(new Date())
|
||||||
User.findById user1.id, (err, user1) ->
|
User.findById user1.id, (err, user1) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
unsubscribeUser user1, ->
|
unsubscribeUser user1, ->
|
||||||
|
@ -693,12 +743,14 @@ describe 'Subscriptions', ->
|
||||||
user1.save (err, user1) ->
|
user1.save (err, user1) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
expect(user1.isAdmin()).toEqual(true)
|
expect(user1.isAdmin()).toEqual(true)
|
||||||
createPrepaid 'subscription', (err, res, prepaid) ->
|
createPrepaid 'subscription', 1, (err, res, prepaid) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
subscribeUser user1, null, prepaid.code, ->
|
subscribeUser user1, null, prepaid.code, ->
|
||||||
Prepaid.findById prepaid._id, (err, prepaid) ->
|
Prepaid.findById prepaid._id, (err, prepaid) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
expect(prepaid.get('status')).toEqual('used')
|
expect(prepaid.get('maxRedeemers')).toEqual(1)
|
||||||
|
expect(prepaid.get('redeemers')[0].userID).toEqual(user1.get('_id'))
|
||||||
|
expect(prepaid.get('redeemers')[0].date).toBeLessThan(new Date())
|
||||||
User.findById user1.id, (err, user1) ->
|
User.findById user1.id, (err, user1) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
unsubscribeUser user1, ->
|
unsubscribeUser user1, ->
|
||||||
|
@ -931,7 +983,7 @@ describe 'Subscriptions', ->
|
||||||
user2.save (err, user1) ->
|
user2.save (err, user1) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
expect(user2.isAdmin()).toEqual(true)
|
expect(user2.isAdmin()).toEqual(true)
|
||||||
createPrepaid 'subscription', (err, res, prepaid) ->
|
createPrepaid 'subscription', 1, (err, res, prepaid) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
requestBody = user2.toObject()
|
requestBody = user2.toObject()
|
||||||
requestBody.stripe =
|
requestBody.stripe =
|
||||||
|
@ -1103,7 +1155,7 @@ describe 'Subscriptions', ->
|
||||||
user1.save (err, user1) ->
|
user1.save (err, user1) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
expect(user1.isAdmin()).toEqual(true)
|
expect(user1.isAdmin()).toEqual(true)
|
||||||
createPrepaid 'subscription', (err, res, prepaid) ->
|
createPrepaid 'subscription', 1, (err, res, prepaid) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
subscribeUser user1, null, prepaid.code, ->
|
subscribeUser user1, null, prepaid.code, ->
|
||||||
stripe.tokens.create {
|
stripe.tokens.create {
|
||||||
|
|
Loading…
Reference in a new issue