mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-23 07:38:20 -05:00
Move product information to the db
This commit is contained in:
parent
ae1621ea09
commit
7c516c4d9f
19 changed files with 334 additions and 261 deletions
8
app/collections/Products.coffee
Normal file
8
app/collections/Products.coffee
Normal file
|
@ -0,0 +1,8 @@
|
|||
CocoCollection = require './CocoCollection'
|
||||
Product = require 'models/Product'
|
||||
|
||||
module.exports = class Products extends CocoCollection
|
||||
model: Product
|
||||
url: '/db/products'
|
||||
|
||||
getByName: (name) -> @findWhere { name: name }
|
|
@ -244,7 +244,7 @@ module.exports.getCoursePraise = getCoursePraise = ->
|
|||
]
|
||||
praise[_.random(0, praise.length - 1)]
|
||||
|
||||
module.exports.getPrepaidCodeAmount = getPrepaidCodeAmount = (price=999, users=0, months=0) ->
|
||||
module.exports.getPrepaidCodeAmount = getPrepaidCodeAmount = (price=0, users=0, months=0) ->
|
||||
return 0 unless users > 0 and months > 0
|
||||
total = price * users * months
|
||||
total
|
||||
|
|
6
app/models/Product.coffee
Normal file
6
app/models/Product.coffee
Normal file
|
@ -0,0 +1,6 @@
|
|||
CocoModel = require './CocoModel'
|
||||
|
||||
module.exports = class ProductModel extends CocoModel
|
||||
@className: 'Product'
|
||||
@schema: require 'schemas/models/product.schema'
|
||||
urlRoot: '/db/products'
|
13
app/schemas/models/product.schema.coffee
Normal file
13
app/schemas/models/product.schema.coffee
Normal file
|
@ -0,0 +1,13 @@
|
|||
c = require './../schemas'
|
||||
|
||||
module.exports = ProductSchema = {
|
||||
type: 'object'
|
||||
additionalProperties: false
|
||||
properties: {
|
||||
name: { type: 'string' }
|
||||
amount: { type: 'integer', description: 'Cost in cents' }
|
||||
gems: { type: 'integer', description: 'Number of gems awarded' }
|
||||
}
|
||||
}
|
||||
|
||||
c.extendBasicProperties ProductSchema, 'Product'
|
|
@ -39,7 +39,7 @@ block content
|
|||
label.control-label.col-md-2(data-i18n="account_prepaid.purchase_total")
|
||||
.col-md-10
|
||||
p.form-control-static $
|
||||
span#total #{purchase.total.toFixed(2)}
|
||||
span#total #{(purchase.total/100).toFixed(2)}
|
||||
button#purchase-btn.btn.btn-success.pull-right(data-i18n="account_prepaid.purchase_button")
|
||||
.row
|
||||
.col-md-12
|
||||
|
@ -66,7 +66,7 @@ block content
|
|||
button#redeem-code-btn.btn.btn-success(data-i18n="account_prepaid.apply_account")
|
||||
.row
|
||||
.col-md-12
|
||||
.panel.panel-default
|
||||
#codes-panel.panel.panel-default
|
||||
.panel-heading
|
||||
.panel-title
|
||||
a(data-toggle="collapse" href="#codeslist")
|
||||
|
|
|
@ -24,8 +24,11 @@
|
|||
th.free-cell(data-i18n="subscribe.free")
|
||||
th
|
||||
//- TODO: find a better way to localize '$9.99/month'
|
||||
span $#{(view.product.amount / 100)}/
|
||||
span(data-i18n="subscribe.month")
|
||||
if view.basicProduct
|
||||
span $#{(view.basicProduct.get('amount') / 100)}/
|
||||
span(data-i18n="subscribe.month")
|
||||
else
|
||||
span '...'
|
||||
tbody
|
||||
tr
|
||||
td.feature-description
|
||||
|
|
|
@ -82,7 +82,7 @@ block content
|
|||
span(data-i18n="account_prepaid.purchase_total")
|
||||
span.spr : #{view.numberOfStudents}
|
||||
span(data-i18n="courses.enrollments")
|
||||
span.spl x $#{view.pricePerStudent.toFixed(2)} = #{view.getPriceString()}
|
||||
span.spl x $#{(view.pricePerStudent/100).toFixed(2)} = #{view.getPriceString()}
|
||||
|
||||
p.text-center
|
||||
button#purchase-btn.btn.btn-lg.btn-success.uppercase(data-i18n="courses.purchase_now")
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
.modal-dialog
|
||||
.modal-content
|
||||
|
||||
if state === 'purchasing'
|
||||
if view.state === 'purchasing'
|
||||
.alert.alert-info(data-i18n="buy_gems.purchasing")
|
||||
|
||||
else if state === 'retrying'
|
||||
else if view.state === 'retrying'
|
||||
#retrying-alert.alert.alert-danger(data-i18n="buy_gems.retrying")
|
||||
|
||||
else
|
||||
|
@ -12,12 +12,12 @@
|
|||
h1(data-i18n="play.buy_gems")
|
||||
|
||||
#products
|
||||
for product in products
|
||||
for product in view.products.models
|
||||
.product
|
||||
h4 x#{product.gems}
|
||||
h3(data-i18n=product.i18n)
|
||||
button.btn.btn-illustrated.btn-lg(value=product.id)
|
||||
span= product.price
|
||||
h4 x#{product.get('gems')}
|
||||
h3(data-i18n=product.get('i18n'))
|
||||
button.btn.btn-illustrated.btn-lg(value=product.get('name'))
|
||||
span= product.get('priceString')
|
||||
|
||||
.product
|
||||
h4(data-i18n="buy_gems.price") x3500 / mo
|
||||
|
@ -29,20 +29,20 @@
|
|||
else
|
||||
button.start-subscription-button.btn.btn-lg.btn-illustrated.btn-success(data-i18n="subscribe.subscribe_title") Subscribe
|
||||
|
||||
if state === 'declined'
|
||||
if view.state === 'declined'
|
||||
#declined-alert.alert.alert-danger.alert-dismissible
|
||||
span(data-i18n="buy_gems.declined")
|
||||
button.close(type="button" data-dismiss="alert")
|
||||
span(aria-hidden="true") ×
|
||||
|
||||
if state === 'unknown_error'
|
||||
if view.state === 'unknown_error'
|
||||
#error-alert.alert.alert-danger.alert-dismissible
|
||||
button.close(type="button" data-dismiss="alert")
|
||||
span(aria-hidden="true") ×
|
||||
p(data-i18n="loading_error.unknown")
|
||||
p= stateMessage
|
||||
p= view.stateMessage
|
||||
|
||||
if state === 'recovered_charge'
|
||||
if view.state === 'recovered_charge'
|
||||
#recovered-alert.alert.alert-danger.alert-dismissible
|
||||
span(data-i18n="buy_gems.recovered")
|
||||
button.close(type="button" data-dismiss="alert")
|
||||
|
|
|
@ -7,6 +7,7 @@ Prepaid = require '../../models/Prepaid'
|
|||
utils = require 'core/utils'
|
||||
RedeemModal = require 'views/account/PrepaidRedeemModal'
|
||||
forms = require 'core/forms'
|
||||
Products = require 'collections/Products'
|
||||
|
||||
# TODO: remove redeem code modal
|
||||
|
||||
|
@ -26,12 +27,10 @@ module.exports = class PrepaidView extends RootView
|
|||
subscriptions:
|
||||
'stripe:received-token': 'onStripeReceivedToken'
|
||||
|
||||
baseAmount: 9.99
|
||||
|
||||
constructor: (options) ->
|
||||
super(options)
|
||||
@purchase =
|
||||
total: @baseAmount
|
||||
total: 0
|
||||
users: 3
|
||||
months: 3
|
||||
@updateTotal()
|
||||
|
@ -45,6 +44,14 @@ module.exports = class PrepaidView extends RootView
|
|||
@ppcQuery = true
|
||||
@loadPrepaid(@ppc)
|
||||
|
||||
@products = new Products()
|
||||
@supermodel.loadCollection(@products, 'products')
|
||||
|
||||
onLoaded: ->
|
||||
@prepaidProduct = @products.findWhere { name: 'prepaid_subscription' }
|
||||
@updateTotal()
|
||||
super()
|
||||
|
||||
getRenderData: ->
|
||||
c = super()
|
||||
c.purchase = @purchase
|
||||
|
@ -62,7 +69,8 @@ module.exports = class PrepaidView extends RootView
|
|||
noty text: message, layout: 'topCenter', type: type, killer: false, timeout: 5000, dismissQueue: true, maxVisible: 3
|
||||
|
||||
updateTotal: ->
|
||||
@purchase.total = getPrepaidCodeAmount(@baseAmount, @purchase.users, @purchase.months)
|
||||
return unless @prepaidProduct
|
||||
@purchase.total = getPrepaidCodeAmount(@prepaidProduct.get('amount'), @purchase.users, @purchase.months)
|
||||
@renderSelectors("#total", "#users-input", "#months-input")
|
||||
|
||||
# Form Input Callbacks
|
||||
|
@ -99,7 +107,7 @@ module.exports = class PrepaidView extends RootView
|
|||
onClickPurchaseButton: (e) ->
|
||||
return unless $("#users-input").val() >= 3 or $("#months-input").val() >= 3
|
||||
@purchaseTimestamp = new Date().getTime()
|
||||
@stripeAmount = @purchase.total * 100
|
||||
@stripeAmount = @purchase.total
|
||||
@description = "Prepaid Code for " + @purchase.users + " users / " + @purchase.months + " months"
|
||||
|
||||
stripeHandler.open
|
||||
|
@ -179,6 +187,7 @@ module.exports = class PrepaidView extends RootView
|
|||
# console.log 'SUCCESS: Prepaid purchase', model.code
|
||||
@statusMessage "Successfully purchased Prepaid Code!", "success"
|
||||
@codes.add(model)
|
||||
@renderSelectors('#codes-panel')
|
||||
|
||||
@statusMessage "Finalizing purchase...", "information"
|
||||
@supermodel.addRequestResource('purchase_prepaid', options, 0).load()
|
||||
|
|
|
@ -3,16 +3,14 @@ template = require 'templates/core/subscribe-modal'
|
|||
stripeHandler = require 'core/services/stripe'
|
||||
utils = require 'core/utils'
|
||||
AuthModal = require 'views/core/AuthModal'
|
||||
Products = require 'collections/Products'
|
||||
|
||||
module.exports = class SubscribeModal extends ModalView
|
||||
id: 'subscribe-modal'
|
||||
template: template
|
||||
plain: true
|
||||
closesOnClickOutside: false
|
||||
product:
|
||||
amount: 999
|
||||
planID: 'basic'
|
||||
yearAmount: 9900
|
||||
planID: 'basic'
|
||||
|
||||
subscriptions:
|
||||
'stripe:received-token': 'onStripeReceivedToken'
|
||||
|
@ -27,6 +25,13 @@ module.exports = class SubscribeModal extends ModalView
|
|||
constructor: (options) ->
|
||||
super(options)
|
||||
@state = 'standby'
|
||||
@products = new Products()
|
||||
@supermodel.loadCollection(@products, 'products')
|
||||
|
||||
onLoaded: ->
|
||||
@basicProduct = @products.findWhere { name: 'basic_subscription' }
|
||||
@yearProduct = @products.findWhere { name: 'year_subscription' }
|
||||
super()
|
||||
|
||||
afterRender: ->
|
||||
super()
|
||||
|
@ -109,12 +114,13 @@ module.exports = class SubscribeModal extends ModalView
|
|||
@$el.find('.parent-button').popover('hide')
|
||||
|
||||
onClickPurchaseButton: (e) ->
|
||||
return unless @basicProduct and @yearProduct
|
||||
@playSound 'menu-button-click'
|
||||
return @openModalView new AuthModal() if me.get('anonymous')
|
||||
application.tracker?.trackEvent 'Started subscription purchase'
|
||||
options = {
|
||||
description: $.i18n.t('subscribe.stripe_description')
|
||||
amount: @product.amount
|
||||
amount: @basicProduct.get('amount')
|
||||
alipay: if me.get('country') is 'china' or (me.get('preferredLanguage') or 'en-US')[...2] is 'zh' then true else 'auto'
|
||||
alipayReusable: true
|
||||
}
|
||||
|
@ -138,7 +144,7 @@ module.exports = class SubscribeModal extends ModalView
|
|||
application.tracker?.trackEvent 'Started 1 year subscription purchase'
|
||||
options =
|
||||
description: $.i18n.t('subscribe.stripe_description_year_sale')
|
||||
amount: @product.yearAmount
|
||||
amount: @yearProduct.get('amount')
|
||||
alipay: if me.get('country') is 'china' or (me.get('preferredLanguage') or 'en-US')[...2] is 'zh' then true else 'auto'
|
||||
alipayReusable: true
|
||||
@purchasedAmount = options.amount
|
||||
|
@ -148,15 +154,15 @@ module.exports = class SubscribeModal extends ModalView
|
|||
@state = 'purchasing'
|
||||
@render()
|
||||
|
||||
if @purchasedAmount is @product.amount
|
||||
if @purchasedAmount is @basicProduct.get('amount')
|
||||
stripe = _.clone(me.get('stripe') ? {})
|
||||
stripe.planID = @product.planID
|
||||
stripe.planID = @basicProduct.get('planID')
|
||||
stripe.token = e.token.id
|
||||
me.set 'stripe', stripe
|
||||
@listenToOnce me, 'sync', @onSubscriptionSuccess
|
||||
@listenToOnce me, 'error', @onSubscriptionError
|
||||
me.patch({headers: {'X-Change-Plan': 'true'}})
|
||||
else if @purchasedAmount is @product.yearAmount
|
||||
else if @purchasedAmount is @yearProduct.get('amount')
|
||||
# Purchasing a year
|
||||
data =
|
||||
stripe:
|
||||
|
|
|
@ -9,12 +9,13 @@ stripeHandler = require 'core/services/stripe'
|
|||
template = require 'templates/courses/purchase-courses-view'
|
||||
User = require 'models/User'
|
||||
utils = require 'core/utils'
|
||||
Products = require 'collections/Products'
|
||||
|
||||
module.exports = class PurchaseCoursesView extends RootView
|
||||
id: 'purchase-courses-view'
|
||||
template: template
|
||||
numberOfStudents: 30
|
||||
pricePerStudent: 4
|
||||
pricePerStudent: 0
|
||||
|
||||
initialize: (options) ->
|
||||
@listenTo stripeHandler, 'received-token', @onStripeReceivedToken
|
||||
|
@ -29,13 +30,19 @@ module.exports = class PurchaseCoursesView extends RootView
|
|||
@prepaids.comparator = '_id'
|
||||
@prepaids.fetchByCreator(me.id)
|
||||
@supermodel.loadCollection(@prepaids, 'prepaids')
|
||||
@products = new Products()
|
||||
@supermodel.loadCollection(@products, 'products')
|
||||
super(options)
|
||||
|
||||
events:
|
||||
'input #students-input': 'onInputStudentsInput'
|
||||
'click #purchase-btn': 'onClickPurchaseButton'
|
||||
|
||||
onLoaded: ->
|
||||
@pricePerStudent = @products.findWhere({name: 'course'}).get('amount')
|
||||
super()
|
||||
|
||||
getPriceString: -> '$' + (@getPrice()).toFixed(2)
|
||||
getPriceString: -> '$' + (@getPrice()/100).toFixed(2)
|
||||
getPrice: -> @pricePerStudent * @numberOfStudents
|
||||
|
||||
onceClassroomsSync: ->
|
||||
|
@ -80,7 +87,7 @@ module.exports = class PurchaseCoursesView extends RootView
|
|||
application.tracker?.trackEvent 'Started course prepaid purchase', {
|
||||
price: @pricePerStudent, students: @numberOfStudents}
|
||||
stripeHandler.open
|
||||
amount: @numberOfStudents * @pricePerStudent * 100
|
||||
amount: @numberOfStudents * @pricePerStudent
|
||||
description: "Full course access for #{@numberOfStudents} students"
|
||||
bitcoin: true
|
||||
alipay: if me.get('country') is 'china' or (me.get('preferredLanguage') or 'en-US')[...2] is 'zh' then true else 'auto'
|
||||
|
|
|
@ -3,6 +3,7 @@ template = require 'templates/play/modal/buy-gems-modal'
|
|||
stripeHandler = require 'core/services/stripe'
|
||||
utils = require 'core/utils'
|
||||
SubscribeModal = require 'views/core/SubscribeModal'
|
||||
Products = require 'collections/Products'
|
||||
|
||||
module.exports = class BuyGemsModal extends ModalView
|
||||
id: 'buy-gems-modal'
|
||||
|
@ -29,22 +30,21 @@ module.exports = class BuyGemsModal extends ModalView
|
|||
super(options)
|
||||
@timestampForPurchase = new Date().getTime()
|
||||
@state = 'standby'
|
||||
@products = new Products()
|
||||
@products.comparator = 'amount'
|
||||
if application.isIPadApp
|
||||
@products = []
|
||||
Backbone.Mediator.publish 'buy-gems-modal:update-products'
|
||||
else
|
||||
@products = @originalProducts
|
||||
@supermodel.loadCollection(@products, 'products')
|
||||
$.post '/db/payment/check-stripe-charges', (something, somethingElse, jqxhr) =>
|
||||
if jqxhr.status is 201
|
||||
@state = 'recovered_charge'
|
||||
@render()
|
||||
|
||||
getRenderData: ->
|
||||
c = super()
|
||||
c.products = @products
|
||||
c.state = @state
|
||||
c.stateMessage = @stateMessage
|
||||
return c
|
||||
onLoaded: ->
|
||||
@products.reset @products.filter (product) -> _.string.startsWith(product.get('name'), 'gems_')
|
||||
super()
|
||||
|
||||
afterRender: ->
|
||||
super()
|
||||
|
@ -56,19 +56,20 @@ module.exports = class BuyGemsModal extends ModalView
|
|||
@playSound 'game-menu-close'
|
||||
|
||||
onIPadProducts: (e) ->
|
||||
newProducts = []
|
||||
for iapProduct in e.products
|
||||
localProduct = _.find @originalProducts, { id: iapProduct.id }
|
||||
continue unless localProduct
|
||||
localProduct.price = iapProduct.price
|
||||
newProducts.push localProduct
|
||||
@products = _.sortBy newProducts, 'gems'
|
||||
@render()
|
||||
# TODO: Update to handle new products collection
|
||||
# newProducts = []
|
||||
# for iapProduct in e.products
|
||||
# localProduct = _.find @originalProducts, { id: iapProduct.id }
|
||||
# continue unless localProduct
|
||||
# localProduct.price = iapProduct.price
|
||||
# newProducts.push localProduct
|
||||
# @products = _.sortBy newProducts, 'gems'
|
||||
# @render()
|
||||
|
||||
onClickProductButton: (e) ->
|
||||
@playSound 'menu-button-click'
|
||||
productID = $(e.target).closest('button').val()
|
||||
product = _.find @products, { id: productID }
|
||||
product = @products.findWhere { name: productID }
|
||||
|
||||
if application.isIPadApp
|
||||
Backbone.Mediator.publish 'buy-gems-modal:purchase-initiated', { productID: productID }
|
||||
|
@ -76,8 +77,8 @@ module.exports = class BuyGemsModal extends ModalView
|
|||
else
|
||||
application.tracker?.trackEvent 'Started gem purchase', { productID: productID }
|
||||
stripeHandler.open({
|
||||
description: $.t(product.i18n)
|
||||
amount: product.amount
|
||||
description: $.t(product.get('i18n'))
|
||||
amount: product.get('amount')
|
||||
bitcoin: true
|
||||
alipay: if me.get('country') is 'china' or (me.get('preferredLanguage') or 'en-US')[...2] is 'zh' then true else 'auto'
|
||||
})
|
||||
|
@ -86,7 +87,7 @@ module.exports = class BuyGemsModal extends ModalView
|
|||
|
||||
onStripeReceivedToken: (e) ->
|
||||
data = {
|
||||
productID: @productBeingPurchased.id
|
||||
productID: @productBeingPurchased.get('name')
|
||||
stripe: {
|
||||
token: e.token.id
|
||||
timestamp: @timestampForPurchase
|
||||
|
@ -97,8 +98,8 @@ module.exports = class BuyGemsModal extends ModalView
|
|||
jqxhr = $.post('/db/payment', data)
|
||||
jqxhr.done(=>
|
||||
application.tracker?.trackEvent 'Finished gem purchase',
|
||||
productID: @productBeingPurchased.id
|
||||
value: @productBeingPurchased.amount
|
||||
productID: @productBeingPurchased.get('name')
|
||||
value: @productBeingPurchased.get('amount')
|
||||
document.location.reload()
|
||||
)
|
||||
jqxhr.fail(=>
|
||||
|
@ -116,7 +117,7 @@ module.exports = class BuyGemsModal extends ModalView
|
|||
)
|
||||
|
||||
onIAPComplete: (e) ->
|
||||
product = _.find @products, { id: e.productID }
|
||||
product = @products.findWhere { name: e.productID }
|
||||
purchased = me.get('purchased') ? {}
|
||||
purchased = _.clone purchased
|
||||
purchased.gems ?= 0
|
||||
|
|
5
server/models/Product.coffee
Normal file
5
server/models/Product.coffee
Normal file
|
@ -0,0 +1,5 @@
|
|||
mongoose = require('mongoose')
|
||||
config = require '../../server_config'
|
||||
ProductSchema = new mongoose.Schema({}, {strict: false,read:config.mongo.readpref})
|
||||
|
||||
module.exports = mongoose.model('product', ProductSchema)
|
|
@ -1,4 +1,5 @@
|
|||
Payment = require './Payment'
|
||||
Product = require '../models/Product'
|
||||
User = require '../users/User'
|
||||
Handler = require '../commons/Handler'
|
||||
{handlers} = require '../commons/mapping'
|
||||
|
@ -11,30 +12,6 @@ request = require 'request'
|
|||
async = require 'async'
|
||||
apple_utils = require '../lib/apple_utils'
|
||||
|
||||
products = {
|
||||
'gems_5': {
|
||||
amount: 499
|
||||
gems: 5000
|
||||
id: 'gems_5'
|
||||
}
|
||||
|
||||
'gems_10': {
|
||||
amount: 999
|
||||
gems: 11000
|
||||
id: 'gems_10'
|
||||
}
|
||||
|
||||
'gems_20': {
|
||||
amount: 1999
|
||||
gems: 25000
|
||||
id: 'gems_20'
|
||||
}
|
||||
|
||||
'custom': {
|
||||
# amount expected in request body
|
||||
id: 'custom'
|
||||
}
|
||||
}
|
||||
|
||||
PaymentHandler = class PaymentHandler extends Handler
|
||||
modelClass: Payment
|
||||
|
@ -134,33 +111,34 @@ PaymentHandler = class PaymentHandler extends Handler
|
|||
|
||||
payment = @makeNewInstance(req)
|
||||
payment.set 'service', 'ios'
|
||||
product = products[transaction.product_id]
|
||||
|
||||
payment.set 'amount', product.amount
|
||||
payment.set 'gems', product.gems
|
||||
payment.set 'ios', {
|
||||
transactionID: transactionID
|
||||
rawReceipt: receipt
|
||||
localPrice: localPrice
|
||||
}
|
||||
|
||||
validation = @validateDocumentInput(payment.toObject())
|
||||
if validation.valid is false
|
||||
@logPaymentError(req, 'Invalid apple payment object.')
|
||||
return @sendBadInputError(res, validation.errors)
|
||||
|
||||
payment.save((err) =>
|
||||
if err
|
||||
@logPaymentError(req, 'Apple payment save error.'+err)
|
||||
return @sendDatabaseError(res, err)
|
||||
@incrementGemsFor(req.user, product.gems, (err) =>
|
||||
Product.findOne({name: transaction.product_id}).exec (err, product) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
return @sendNotFoundError(res) if not product
|
||||
payment.set 'amount', product.get('amount')
|
||||
payment.set 'gems', product.get('gems')
|
||||
payment.set 'ios', {
|
||||
transactionID: transactionID
|
||||
rawReceipt: receipt
|
||||
localPrice: localPrice
|
||||
}
|
||||
|
||||
validation = @validateDocumentInput(payment.toObject())
|
||||
if validation.valid is false
|
||||
@logPaymentError(req, 'Invalid apple payment object.')
|
||||
return @sendBadInputError(res, validation.errors)
|
||||
|
||||
payment.save((err) =>
|
||||
if err
|
||||
@logPaymentError(req, 'Apple incr db error.'+err)
|
||||
@logPaymentError(req, 'Apple payment save error.'+err)
|
||||
return @sendDatabaseError(res, err)
|
||||
@sendPaymentHipChatMessage user: req.user, payment: payment
|
||||
@sendCreated(res, @formatEntity(req, payment))
|
||||
@incrementGemsFor(req.user, product.get('gems'), (err) =>
|
||||
if err
|
||||
@logPaymentError(req, 'Apple incr db error.'+err)
|
||||
return @sendDatabaseError(res, err)
|
||||
@sendPaymentHipChatMessage user: req.user, payment: payment
|
||||
@sendCreated(res, @formatEntity(req, payment))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -203,7 +181,6 @@ PaymentHandler = class PaymentHandler extends Handler
|
|||
|
||||
|
||||
beginStripePayment: (req, res, timestamp, productID) ->
|
||||
product = products[productID]
|
||||
|
||||
async.parallel([
|
||||
((callback) ->
|
||||
|
@ -218,6 +195,10 @@ PaymentHandler = class PaymentHandler extends Handler
|
|||
charge = _.find recentCharges.data, (c) -> c.metadata.timestamp is timestamp
|
||||
callback(null, charge)
|
||||
)
|
||||
),
|
||||
((callback) ->
|
||||
Product.findOne({name: productID}).exec (err, product) =>
|
||||
callback(err, product)
|
||||
)
|
||||
],
|
||||
|
||||
|
@ -225,7 +206,10 @@ PaymentHandler = class PaymentHandler extends Handler
|
|||
if err
|
||||
@logPaymentError(req, 'Stripe async load db error. '+err)
|
||||
return @sendDatabaseError(res, err)
|
||||
[payment, charge] = results
|
||||
[payment, charge, product] = results
|
||||
|
||||
if not product
|
||||
return @sendNotFoundError(res, 'could not find product with id '+productID)
|
||||
|
||||
if not (payment or charge)
|
||||
# Proceed normally from the beginning
|
||||
|
@ -236,7 +220,7 @@ PaymentHandler = class PaymentHandler extends Handler
|
|||
@recordStripeCharge(req, res, charge)
|
||||
|
||||
else
|
||||
return @sendSuccess(res, @formatEntity(req, payment)) if product.id is 'custom'
|
||||
return @sendSuccess(res, @formatEntity(req, payment)) if product.get('name') is 'custom'
|
||||
|
||||
# Charged Stripe and recorded it. Recalculate gems to make sure credited the purchase.
|
||||
@recalculateGemsFor(req.user, (err) =>
|
||||
|
@ -250,7 +234,7 @@ PaymentHandler = class PaymentHandler extends Handler
|
|||
)
|
||||
|
||||
chargeStripe: (req, res, product) ->
|
||||
amount = parseInt product.amount ? req.body.amount
|
||||
amount = parseInt product.get('amount') ? req.body.amount
|
||||
return @sendError(res, 400, "Invalid amount.") if isNaN(amount)
|
||||
|
||||
stripe.charges.create({
|
||||
|
@ -258,9 +242,9 @@ PaymentHandler = class PaymentHandler extends Handler
|
|||
currency: 'usd'
|
||||
customer: req.user.get('stripe')?.customerID
|
||||
metadata: {
|
||||
productID: product.id
|
||||
productID: product.get('name')
|
||||
userID: req.user._id + ''
|
||||
gems: product.gems
|
||||
gems: product.get('gems')
|
||||
timestamp: parseInt(req.body.stripe?.timestamp)
|
||||
description: req.body.description
|
||||
}
|
||||
|
|
|
@ -15,20 +15,10 @@ User = require '../users/User'
|
|||
{getSponsoredSubsAmount} = require '../../app/core/utils'
|
||||
StripeUtils = require '../lib/stripe_utils'
|
||||
moment = require 'moment'
|
||||
Product = require '../models/Product'
|
||||
|
||||
recipientCouponID = 'free'
|
||||
|
||||
# TODO: rename this to avoid collisions with 'subscriptions' variables
|
||||
subscriptions = {
|
||||
basic: {
|
||||
gems: 3500
|
||||
amount: 999 # For calculating incremental quantity before sub creation
|
||||
}
|
||||
year_sale: {
|
||||
amount: 9900
|
||||
}
|
||||
}
|
||||
|
||||
class SubscriptionHandler extends Handler
|
||||
logSubscriptionError: (user, msg) ->
|
||||
console.warn "Subscription Error: #{user.get('slug')} (#{user._id}): '#{msg}'"
|
||||
|
@ -149,51 +139,56 @@ class SubscriptionHandler extends Handler
|
|||
if err
|
||||
@logSubscriptionError(user, "Purchase year sale Stripe cancel subscription error: #{JSON.stringify(err)}")
|
||||
return @sendDatabaseError(res, err)
|
||||
metadata =
|
||||
type: req.body.type
|
||||
userID: req.user._id + ''
|
||||
gems: subscriptions.basic.gems * 12
|
||||
timestamp: parseInt(req.body.stripe?.timestamp)
|
||||
description: req.body.description
|
||||
|
||||
StripeUtils.createCharge req.user, subscriptions.year_sale.amount, metadata, (err, charge) =>
|
||||
if err
|
||||
@logSubscriptionError(req.user, "Purchase year sale create charge: #{JSON.stringify(err)}")
|
||||
return @sendDatabaseError(res, err)
|
||||
|
||||
StripeUtils.createPayment req.user, charge, (err, payment) =>
|
||||
|
||||
Product.findOne({name: 'year_subscription'}).exec (err, product) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
return @sendNotFoundError(res, 'year_subscription product not found') if not product
|
||||
|
||||
metadata =
|
||||
type: req.body.type
|
||||
userID: req.user._id + ''
|
||||
gems: product.get('gems')
|
||||
timestamp: parseInt(req.body.stripe?.timestamp)
|
||||
description: req.body.description
|
||||
|
||||
StripeUtils.createCharge req.user, product.get('amount'), metadata, (err, charge) =>
|
||||
if err
|
||||
@logSubscriptionError(req.user, "Purchase year sale create payment: #{JSON.stringify(err)}")
|
||||
@logSubscriptionError(req.user, "Purchase year sale create charge: #{JSON.stringify(err)}")
|
||||
return @sendDatabaseError(res, err)
|
||||
|
||||
# Add terminal subscription to User with extensions for existing subscriptions
|
||||
stripeInfo = _.cloneDeep(req.user.get('stripe') ? {})
|
||||
endDate = new Date()
|
||||
if stripeSubscriptionPeriodEndDate
|
||||
endDate = stripeSubscriptionPeriodEndDate
|
||||
else if _.isString(stripeInfo.free) and new Date() < new Date(stripeInfo.free)
|
||||
endDate = new Date(stripeInfo.free)
|
||||
endDate.setUTCFullYear(endDate.getUTCFullYear() + 1)
|
||||
stripeInfo.free = endDate.toISOString().substring(0, 10)
|
||||
req.user.set('stripe', stripeInfo)
|
||||
|
||||
# Add year's worth of gems to User
|
||||
purchased = _.clone(req.user.get('purchased'))
|
||||
purchased ?= {}
|
||||
purchased.gems ?= 0
|
||||
purchased.gems += parseInt(charge.metadata.gems)
|
||||
req.user.set('purchased', purchased)
|
||||
|
||||
req.user.save (err, user) =>
|
||||
|
||||
StripeUtils.createPayment req.user, charge, (err, payment) =>
|
||||
if err
|
||||
@logSubscriptionError(req.user, "User save error: #{JSON.stringify(err)}")
|
||||
@logSubscriptionError(req.user, "Purchase year sale create payment: #{JSON.stringify(err)}")
|
||||
return @sendDatabaseError(res, err)
|
||||
try
|
||||
msg = "Year subscription purchased by #{req.user.get('email')} #{req.user.id}"
|
||||
hipchat.sendHipChatMessage msg, ['tower']
|
||||
catch error
|
||||
@logSubscriptionError(req.user, "Year sub sale HipChat tower msg error: #{JSON.stringify(error)}")
|
||||
@sendSuccess(res, user)
|
||||
|
||||
# Add terminal subscription to User with extensions for existing subscriptions
|
||||
stripeInfo = _.cloneDeep(req.user.get('stripe') ? {})
|
||||
endDate = new Date()
|
||||
if stripeSubscriptionPeriodEndDate
|
||||
endDate = stripeSubscriptionPeriodEndDate
|
||||
else if _.isString(stripeInfo.free) and new Date() < new Date(stripeInfo.free)
|
||||
endDate = new Date(stripeInfo.free)
|
||||
endDate.setUTCFullYear(endDate.getUTCFullYear() + 1)
|
||||
stripeInfo.free = endDate.toISOString().substring(0, 10)
|
||||
req.user.set('stripe', stripeInfo)
|
||||
|
||||
# Add year's worth of gems to User
|
||||
purchased = _.clone(req.user.get('purchased'))
|
||||
purchased ?= {}
|
||||
purchased.gems ?= 0
|
||||
purchased.gems += parseInt(charge.metadata.gems)
|
||||
req.user.set('purchased', purchased)
|
||||
|
||||
req.user.save (err, user) =>
|
||||
if err
|
||||
@logSubscriptionError(req.user, "User save error: #{JSON.stringify(err)}")
|
||||
return @sendDatabaseError(res, err)
|
||||
try
|
||||
msg = "Year subscription purchased by #{req.user.get('email')} #{req.user.id}"
|
||||
hipchat.sendHipChatMessage msg, ['tower']
|
||||
catch error
|
||||
@logSubscriptionError(req.user, "Year sub sale HipChat tower msg error: #{JSON.stringify(error)}")
|
||||
@sendSuccess(res, user)
|
||||
|
||||
subscribeWithPrepaidCode: (req, res) ->
|
||||
return @sendUnauthorizedError(res) unless req.user?
|
||||
|
@ -241,31 +236,35 @@ class SubscriptionHandler extends Handler
|
|||
@logSubscriptionError(user, "Redeem Prepaid Code Stripe cancel subscription error: #{JSON.stringify(err)}")
|
||||
return @sendDatabaseError(res, err)
|
||||
|
||||
# Add terminal subscription to User, extending existing subscriptions
|
||||
# TODO: refactor this into some form useable by both this and purchaseYearSale
|
||||
stripeInfo = _.cloneDeep(req.user.get('stripe') ? {})
|
||||
endDate = new moment()
|
||||
if stripeSubscriptionPeriodEndDate
|
||||
endDate = new moment(stripeSubscriptionPeriodEndDate)
|
||||
else if _.isString(stripeInfo.free) and new moment().isBefore(new moment(stripeInfo.free))
|
||||
endDate = new moment(stripeInfo.free)
|
||||
Product.findOne({name: 'basic_subscription'}).exec (err, product) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
return @sendNotFoundError(res, 'basic_subscription product not found') if not product
|
||||
|
||||
endDate = endDate.add(months, 'months')
|
||||
stripeInfo.free = endDate.toISOString().substring(0, 10)
|
||||
req.user.set('stripe', stripeInfo)
|
||||
|
||||
# Add gems to User
|
||||
purchased = _.clone(req.user.get('purchased'))
|
||||
purchased ?= {}
|
||||
purchased.gems ?= 0
|
||||
purchased.gems += subscriptions.basic.gems * months
|
||||
req.user.set('purchased', purchased)
|
||||
|
||||
req.user.save (err, user) =>
|
||||
if err
|
||||
@logSubscriptionError(req.user, "User save error: #{JSON.stringify(err)}")
|
||||
return @sendDatabaseError(res, err)
|
||||
@sendSuccess(res, user)
|
||||
# Add terminal subscription to User, extending existing subscriptions
|
||||
# TODO: refactor this into some form useable by both this and purchaseYearSale
|
||||
stripeInfo = _.cloneDeep(req.user.get('stripe') ? {})
|
||||
endDate = new moment()
|
||||
if stripeSubscriptionPeriodEndDate
|
||||
endDate = new moment(stripeSubscriptionPeriodEndDate)
|
||||
else if _.isString(stripeInfo.free) and new moment().isBefore(new moment(stripeInfo.free))
|
||||
endDate = new moment(stripeInfo.free)
|
||||
|
||||
endDate = endDate.add(months, 'months')
|
||||
stripeInfo.free = endDate.toISOString().substring(0, 10)
|
||||
req.user.set('stripe', stripeInfo)
|
||||
|
||||
# Add gems to User
|
||||
purchased = _.clone(req.user.get('purchased'))
|
||||
purchased ?= {}
|
||||
purchased.gems ?= 0
|
||||
purchased.gems += product.get('gems') * months
|
||||
req.user.set('purchased', purchased)
|
||||
|
||||
req.user.save (err, user) =>
|
||||
if err
|
||||
@logSubscriptionError(req.user, "User save error: #{JSON.stringify(err)}")
|
||||
return @sendDatabaseError(res, err)
|
||||
@sendSuccess(res, user)
|
||||
|
||||
subscribeUser: (req, user, done) ->
|
||||
if (not req.user) or req.user.isAnonymous() or user.isAnonymous()
|
||||
|
@ -427,18 +426,22 @@ class SubscriptionHandler extends Handler
|
|||
req.body.stripe = stripeInfo
|
||||
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)
|
||||
Product.findOne({name: 'basic_subscription'}).exec (err, product) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
return @sendNotFoundError(res, 'basic_subscription product not found') if not product
|
||||
|
||||
user.save (err) =>
|
||||
if err
|
||||
@logSubscriptionError(user, 'Stripe user plan saving error. ' + err)
|
||||
return done({res: 'Database error.', code: 500})
|
||||
done()
|
||||
if increment
|
||||
purchased = _.clone(user.get('purchased'))
|
||||
purchased ?= {}
|
||||
purchased.gems ?= 0
|
||||
purchased.gems += product.get('gems') # TODO: Put actual subscription amount here
|
||||
user.set('purchased', purchased)
|
||||
|
||||
user.save (err) =>
|
||||
if err
|
||||
@logSubscriptionError(user, 'Stripe user plan saving error. ' + err)
|
||||
return done({res: 'Database error.', code: 500})
|
||||
done()
|
||||
|
||||
updateStripeRecipientSubscriptions: (req, user, customer, done) ->
|
||||
return done({res: 'Database error.', code: 500}) unless req.body.stripe?.subscribeEmails?
|
||||
|
@ -527,36 +530,40 @@ class SubscriptionHandler extends Handler
|
|||
@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()
|
||||
Product.findOne({name: 'basic_subscription'}).exec (err, product) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
return @sendNotFoundError(res, 'basic_subscription product not found') if not product
|
||||
|
||||
tasks = []
|
||||
for sub in stripeRecipients
|
||||
tasks.push createUpdateFn(sub.recipient, sub.increment)
|
||||
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 += product.get('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, product, done)
|
||||
|
||||
async.parallel tasks, (err, results) =>
|
||||
return done(err) if err
|
||||
@updateStripeSponsorSubscription(req, user, customer, done)
|
||||
|
||||
updateStripeSponsorSubscription: (req, user, customer, done) ->
|
||||
updateStripeSponsorSubscription: (req, user, customer, product, done) ->
|
||||
stripeInfo = user.get('stripe') ? {}
|
||||
numSponsored = stripeInfo.recipients.length
|
||||
quantity = getSponsoredSubsAmount(subscriptions.basic.amount, numSponsored, stripeInfo.subscriptionID?)
|
||||
quantity = getSponsoredSubsAmount(product.get('amount'), numSponsored, stripeInfo.subscriptionID?)
|
||||
|
||||
findStripeSubscription customer.id, subscriptionID: stripeInfo.sponsorSubscriptionID, (subscription) =>
|
||||
if stripeInfo.sponsorSubscriptionID? and not subscription?
|
||||
|
@ -656,38 +663,42 @@ class SubscriptionHandler extends Handler
|
|||
@logSubscriptionError(user, 'Unable to find recipient subscription. ')
|
||||
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)
|
||||
return done({res: 'Database error.', code: 500})
|
||||
Product.findOne({name: 'basic_subscription'}).exec (err, product) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
return @sendNotFoundError(res, 'basic_subscription product not found') if not product
|
||||
|
||||
# Cancel Stripe subscription
|
||||
stripe.customers.cancelSubscription stripeInfo.customerID, sponsoredEntry.subscriptionID, (err) =>
|
||||
# Update recipient user
|
||||
deleteUserStripeProp(recipient, 'sponsorID')
|
||||
recipient.save (err) =>
|
||||
if err
|
||||
@logSubscriptionError(user, "Stripe cancel sponsored subscription failed. " + err)
|
||||
@logSubscriptionError(user, 'Recipient user save unsubscribe error. ' + err)
|
||||
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) =>
|
||||
|
||||
# Cancel Stripe subscription
|
||||
stripe.customers.cancelSubscription stripeInfo.customerID, sponsoredEntry.subscriptionID, (err) =>
|
||||
if err
|
||||
@logSubscriptionError(user, 'Sponsor user save unsubscribe error. ' + err)
|
||||
@logSubscriptionError(user, "Stripe cancel sponsored subscription failed. " + 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) =>
|
||||
|
||||
# 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, 'Sponsored subscription quantity update error. ' + JSON.stringify(err))
|
||||
@logSubscriptionError(user, 'Sponsor user save unsubscribe error. ' + err)
|
||||
return done({res: 'Database error.', code: 500})
|
||||
done()
|
||||
|
||||
return done() unless stripeInfo.sponsorSubscriptionID?
|
||||
|
||||
# Update sponsored subscription quantity
|
||||
options =
|
||||
quantity: getSponsoredSubsAmount(product.get('amount'), stripeInfo.recipients.length, stripeInfo.subscriptionID?)
|
||||
stripe.customers.updateSubscription stripeInfo.customerID, stripeInfo.sponsorSubscriptionID, options, (err, subscription) =>
|
||||
if err
|
||||
@logSubscriptionError(user, 'Sponsored subscription quantity update error. ' + JSON.stringify(err))
|
||||
return done({res: 'Database error.', code: 500})
|
||||
done()
|
||||
|
||||
module.exports = new SubscriptionHandler()
|
||||
|
|
|
@ -6,6 +6,7 @@ User = require '../users/User'
|
|||
StripeUtils = require '../lib/stripe_utils'
|
||||
utils = require '../../app/core/utils'
|
||||
mongoose = require 'mongoose'
|
||||
Product = require '../models/Product'
|
||||
|
||||
# TODO: Should this happen on a save() call instead of a prepaid/-/create post?
|
||||
# TODO: Probably a better way to create a unique 8 charactor string property using db voodoo
|
||||
|
@ -17,8 +18,6 @@ PrepaidHandler = class PrepaidHandler extends Handler
|
|||
jsonSchema: require '../../app/schemas/models/prepaid.schema'
|
||||
allowedMethods: ['GET','POST']
|
||||
|
||||
baseAmount: 999
|
||||
|
||||
logError: (user, msg) ->
|
||||
console.warn "Prepaid Error: [#{user.get('slug')} (#{user._id})] '#{msg}'"
|
||||
|
||||
|
@ -134,9 +133,13 @@ PrepaidHandler = class PrepaidHandler extends Handler
|
|||
return @sendBadInputError(res) unless isNaN(months) is false and months > 0
|
||||
return @sendError(res, 403, "Users or Months must be greater than 3") if maxRedeemers < 3 and months < 3
|
||||
|
||||
@purchasePrepaidTerminalSubscription req.user, description, maxRedeemers, months, timestamp, token, (err, prepaid) =>
|
||||
Product.findOne({name: 'prepaid_subscription'}).exec (err, product) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
@sendSuccess(res, prepaid.toObject())
|
||||
return @sendNotFoundError(res, 'prepaid_subscription product not found') if not product
|
||||
|
||||
@purchasePrepaidTerminalSubscription req.user, description, maxRedeemers, months, timestamp, token, product, (err, prepaid) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
@sendSuccess(res, prepaid.toObject())
|
||||
|
||||
else if req.body.type is 'course'
|
||||
maxRedeemers = parseInt(req.body.maxRedeemers)
|
||||
|
@ -145,18 +148,22 @@ PrepaidHandler = class PrepaidHandler extends Handler
|
|||
|
||||
return @sendBadInputError(res) unless isNaN(maxRedeemers) is false and maxRedeemers > 0
|
||||
|
||||
@purchasePrepaidCourse req.user, maxRedeemers, timestamp, token, (err, prepaid) =>
|
||||
# TODO: this badinput detection is fragile, in course instance handler as well
|
||||
return @sendBadInputError(res, err) if err is 'Missing required Stripe token'
|
||||
Product.findOne({name: 'course'}).exec (err, product) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
@sendSuccess(res, prepaid.toObject())
|
||||
return @sendNotFoundError(res, 'course product not found') if not product
|
||||
|
||||
@purchasePrepaidCourse req.user, maxRedeemers, timestamp, token, product, (err, prepaid) =>
|
||||
# TODO: this badinput detection is fragile, in course instance handler as well
|
||||
return @sendBadInputError(res, err) if err is 'Missing required Stripe token'
|
||||
return @sendDatabaseError(res, err) if err
|
||||
@sendSuccess(res, prepaid.toObject())
|
||||
else
|
||||
@sendForbiddenError(res)
|
||||
|
||||
purchasePrepaidCourse: (user, maxRedeemers, timestamp, token, done) ->
|
||||
purchasePrepaidCourse: (user, maxRedeemers, timestamp, token, product, done) ->
|
||||
type = 'course'
|
||||
|
||||
amount = maxRedeemers * 400
|
||||
amount = maxRedeemers * product.get('amount')
|
||||
if amount > 0 and not (token or user.isAdmin())
|
||||
@logError(user, "Purchase prepaid courses missing required Stripe token #{amount}")
|
||||
return done('Missing required Stripe token')
|
||||
|
@ -190,7 +197,7 @@ PrepaidHandler = class PrepaidHandler extends Handler
|
|||
hipchat.sendHipChatMessage msg, ['tower']
|
||||
@createPrepaid(user, type, maxRedeemers, {}, done)
|
||||
|
||||
purchasePrepaidTerminalSubscription: (user, description, maxRedeemers, months, timestamp, token, done) ->
|
||||
purchasePrepaidTerminalSubscription: (user, description, maxRedeemers, months, timestamp, token, product, done) ->
|
||||
type = 'terminal_subscription'
|
||||
|
||||
StripeUtils.getCustomer user, token, (err, customer) =>
|
||||
|
@ -207,7 +214,7 @@ PrepaidHandler = class PrepaidHandler extends Handler
|
|||
months: months
|
||||
productID: "prepaid #{type}"
|
||||
|
||||
amount = utils.getPrepaidCodeAmount(@baseAmount, maxRedeemers, months)
|
||||
amount = utils.getPrepaidCodeAmount(product.get('amount'), maxRedeemers, months)
|
||||
|
||||
StripeUtils.createCharge user, amount, metadata, (err, charge) =>
|
||||
if err
|
||||
|
|
2
server/routes/index.coffee
Normal file
2
server/routes/index.coffee
Normal file
|
@ -0,0 +1,2 @@
|
|||
module.exports.setup = (app) ->
|
||||
app.get('/db/products', require('./db/products').get)
|
|
@ -14,6 +14,7 @@ user = require './server/users/user_handler'
|
|||
logging = require './server/commons/logging'
|
||||
config = require './server_config'
|
||||
auth = require './server/routes/auth'
|
||||
routes = require './server/routes'
|
||||
UserHandler = require './server/users/user_handler'
|
||||
hipchat = require './server/hipchat'
|
||||
global.tv4 = require 'tv4' # required for TreemaUtils to work
|
||||
|
@ -166,6 +167,7 @@ setupFacebookCrossDomainCommunicationRoute = (app) ->
|
|||
res.sendfile path.join(__dirname, 'public', 'channel.html')
|
||||
|
||||
exports.setupRoutes = (app) ->
|
||||
routes.setup(app)
|
||||
app.use app.router
|
||||
|
||||
baseRoute.setup app
|
||||
|
|
|
@ -67,5 +67,14 @@ describe('Server Test Helper', function() {
|
|||
if (err) { console.log(err); }
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('initializes products', function(done) {
|
||||
var request = require('request');
|
||||
request.get(getURL('/db/products'), function(err, res, body) {
|
||||
expect(err).toBe(null);
|
||||
expect(res.statusCode).toBe(200);
|
||||
done();
|
||||
});
|
||||
})
|
||||
});
|
Loading…
Reference in a new issue