mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-04-02 16:21:01 -04:00
Merge branch 'master' into production
This commit is contained in:
commit
448f4fce88
10 changed files with 443 additions and 58 deletions
app
assets/images/pages/account/subscription
locale
styles/account
templates/account
views/account
scripts/analytics/mongodb/queries
server/payments
test/server/functional
BIN
app/assets/images/pages/account/subscription/teacher-banner.png
Normal file
BIN
app/assets/images/pages/account/subscription/teacher-banner.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 80 KiB |
|
@ -402,7 +402,7 @@
|
|||
subscribe:
|
||||
comparison_blurb: "Sharpen your skills with a CodeCombat subscription!"
|
||||
feature1: "100+ basic levels across 4 worlds"
|
||||
feature2: "10 powerful <strong>new heroes</strong> with unique skills!"
|
||||
feature2: "9 powerful <strong>new heroes</strong> with unique skills!" # {changed}
|
||||
feature3: "70+ bonus levels"
|
||||
feature4: "<strong>3500 bonus gems</strong> every month!"
|
||||
feature5: "Video tutorials"
|
||||
|
@ -439,12 +439,17 @@
|
|||
payment_methods_blurb2: "If you require an alternate form of payment, please contact"
|
||||
sale_already_subscribed: "You're already subscribed!"
|
||||
sale_blurb1: "Save 35%"
|
||||
sale_blurb2: "off regular subscription price!"
|
||||
sale_blurb2: "off regular subscription price of $120 for a whole year!" # {changed}
|
||||
sale_button: "Sale!"
|
||||
sale_button_title: "Save 35% when you purchase a 1 year subscription"
|
||||
sale_click_here: "Click Here"
|
||||
sale_ends: "Ends"
|
||||
sale_extended: "*Existing subscriptions will be extended by 1 year."
|
||||
sale_feature_here: "Here's what you'll get:"
|
||||
sale_feature4: "<strong>42,000 bonus gems</strong> awarded immediately!"
|
||||
sale_continue: "Ready to continue adventuring?"
|
||||
sale_paid: "Payment received. Thanks!"
|
||||
sale_limited_time: "Limited time offer!"
|
||||
sale_new_heroes: "New heroes!"
|
||||
sale_title: "Back to School Sale"
|
||||
sale_view_button: "Buy 1 year subscription for"
|
||||
stripe_description: "Monthly Subscription"
|
||||
|
@ -613,7 +618,7 @@
|
|||
sub_includes_2: "70+ practice levels"
|
||||
sub_includes_3: "Video tutorials"
|
||||
sub_includes_4: "Premium email support"
|
||||
sub_includes_5: "10 new heroes with unique skills to master"
|
||||
sub_includes_5: "9 new heroes with unique skills to master" # {changed}
|
||||
sub_includes_6: "3500 bonus gems every month"
|
||||
sub_includes_7: "Private Clans"
|
||||
monitor_progress_title: "How do I monitor student progress?"
|
||||
|
|
|
@ -611,7 +611,7 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
|
|||
private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student."
|
||||
private_clans_2: "Pour créer un Clan privé, veuillez vous référer à la boîte à cocher 'Faire un clan privé' pendant la création du"
|
||||
private_clans_3: "."
|
||||
who_for_title: "A qui CodeCombat est t-il destiné ?"
|
||||
who_for_title: "A qui CodeCombat est-il destiné ?"
|
||||
who_for_1: "Nous recommandons CodeCombat pour les élèves âgés de 9 ans ou plus. Aucune expérience préalable de programmation n'est requise."
|
||||
who_for_2: "Nous avons conçu CodeCombat pour plaire à la fois aux garçons et aux filles."
|
||||
material_title: "Quelle quantité de contenu y a t-il ?"
|
||||
|
|
|
@ -1,7 +1,30 @@
|
|||
#subscription-sale-view
|
||||
.above-blurb
|
||||
margin: 0 16% 0 16%
|
||||
.center
|
||||
text-align: center
|
||||
.feature-description
|
||||
font-size: 16px
|
||||
.sale-blurb
|
||||
font-size: 22px
|
||||
.sale-ends
|
||||
position: relative
|
||||
left: 0px
|
||||
top: 50px
|
||||
font-size: 18px
|
||||
line-height: 18px
|
||||
background-color: red
|
||||
color: white
|
||||
margin: 0 7px
|
||||
border-radius: 3px
|
||||
padding: 6px
|
||||
width: 200px
|
||||
text-align: center
|
||||
-ms-transform: rotate(330deg) /* IE 9 */
|
||||
-moz-transform: rotate(330deg) /* Firefox */
|
||||
-webkit-transform: rotate(330deg) /* Safari and Chrome */
|
||||
-o-transform: rotate(330deg) /* Opera */
|
||||
.sub-extended
|
||||
font-size: 12px
|
||||
#pay-button
|
||||
font-size: 18px
|
||||
|
|
|
@ -12,9 +12,7 @@ block content
|
|||
a(href="/account/subscription", data-i18n="account.subscription")
|
||||
li.active(data-i18n="account.sale") Sale
|
||||
|
||||
if me.get('anonymous')
|
||||
p(data-i18n="account_settings.not_logged_in")
|
||||
else if me.hasSubscription()
|
||||
if hasSubscription
|
||||
h1(data-i18n="subscribe.sale_already_subscribed")
|
||||
span.spr(data-i18n="subscribe.sale_continue")
|
||||
a(href="/play", data-i18n="subscribe.sale_click_here")
|
||||
|
@ -22,25 +20,47 @@ block content
|
|||
if state === 'purchasing'
|
||||
.alert.alert-info(data-i18n="account_invoices.purchasing")
|
||||
else
|
||||
div.sale-ends
|
||||
if new Date() < saleEndDate
|
||||
span.spr(data-i18n="subscribe.sale_ends")
|
||||
span #{moment(saleEndDate).fromNow()}
|
||||
else
|
||||
span(data-i18n="subscribe.sale_limited_time")
|
||||
h1.center(data-i18n="subscribe.sale_title")
|
||||
p.center
|
||||
img(src="/images/pages/account/subscription/teacher-banner.png")
|
||||
br
|
||||
p.center.sale-blurb
|
||||
strong.spr(data-i18n="subscribe.sale_blurb1")
|
||||
span(data-i18n="subscribe.sale_blurb2")
|
||||
.above-blurb
|
||||
p.sale-blurb
|
||||
strong.spr(data-i18n="subscribe.sale_blurb1")
|
||||
span(data-i18n="subscribe.sale_blurb2")
|
||||
br
|
||||
div
|
||||
.sale-blurb(data-i18n="subscribe.sale_feature_here")
|
||||
ul
|
||||
li.feature-description(data-i18n="[html]subscribe.feature2")
|
||||
li.feature-description(data-i18n="subscribe.feature3")
|
||||
li.feature-description(data-i18n="[html]subscribe.sale_feature4")
|
||||
li.feature-description(data-i18n="subscribe.feature5")
|
||||
li.feature-description(data-i18n="[html]subscribe.feature7")
|
||||
div.sub-extended(data-i18n="subscribe.sale_extended")
|
||||
br
|
||||
p.center
|
||||
button.btn.btn-success#pay-button #{payButtonText}
|
||||
table.above-blurb(cellpadding="8")
|
||||
tr
|
||||
td(colspan=2)
|
||||
h2.center(data-i18n="subscribe.sale_new_heroes")
|
||||
each hero in heroes
|
||||
if hero.get('extendedName') !== 'Captain Anya Weston' && hero.get('extendedName') !== 'Sir Tharin Thunderfist'
|
||||
tr
|
||||
td
|
||||
img(src="http://codecombat.com#{hero.getPortraitURL()}")
|
||||
td
|
||||
p.sale-blurb #{hero.name}
|
||||
p #{hero.class}
|
||||
p #{hero.description}
|
||||
br
|
||||
if !state || state !== 'invoice_paid'
|
||||
p.center
|
||||
button.btn.btn-success#pay-button #{payButtonText}
|
||||
br
|
||||
else if state === 'invoice_paid'
|
||||
div.center
|
||||
span.spr(data-i18n="subscribe.sale_continue")
|
||||
a(href="/play", data-i18n="subscribe.sale_click_here")
|
||||
br
|
||||
#declined-alert.alert.alert-success.alert-dismissible
|
||||
button.close(type="button" data-dismiss="alert")
|
||||
span(aria-hidden="true") ×
|
||||
p(data-i18n="subscribe.sale_paid")
|
||||
if state === 'declined'
|
||||
#declined-alert.alert.alert-danger.alert-dismissible
|
||||
span(data-i18n="account_invoices.declined")
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
RootView = require 'views/core/RootView'
|
||||
template = require 'templates/account/subscription-sale-view'
|
||||
app = require 'core/application'
|
||||
AuthModal = require 'views/core/AuthModal'
|
||||
CocoCollection = require 'collections/CocoCollection'
|
||||
stripeHandler = require 'core/services/stripe'
|
||||
ThangType = require 'models/ThangType'
|
||||
utils = require 'core/utils'
|
||||
|
||||
module.exports = class SubscriptionSaleView extends RootView
|
||||
id: "subscription-sale-view"
|
||||
template: template
|
||||
yearSaleAmount: 7900
|
||||
saleEndDate: new Date('2015-09-05')
|
||||
|
||||
events:
|
||||
'click #pay-button': 'onPayButton'
|
||||
|
@ -19,15 +24,34 @@ module.exports = class SubscriptionSaleView extends RootView
|
|||
@description = $.i18n.t('subscribe.stripe_description_year_sale')
|
||||
displayAmount = (@yearSaleAmount / 100).toFixed(2)
|
||||
@payButtonText = "#{$.i18n.t('subscribe.sale_view_button')} $#{displayAmount}"
|
||||
@heroes = new CocoCollection([], {model: ThangType})
|
||||
@heroes.url = '/db/thang.type?view=heroes'
|
||||
@heroes.setProjection ['original','name','heroClass','description', 'gems','extendedName','i18n']
|
||||
@heroes.comparator = 'gems'
|
||||
@listenToOnce @heroes, 'sync', @onHeroesLoaded
|
||||
@supermodel.loadCollection(@heroes, 'heroes')
|
||||
|
||||
getRenderData: ->
|
||||
c = super()
|
||||
c.hasSubscription = me.get('stripe')?.sponsorID
|
||||
c.heroes = @heroes.models
|
||||
c.payButtonText = @payButtonText
|
||||
c.saleEndDate = @saleEndDate
|
||||
c.state = @state
|
||||
c.stateMessage = @stateMessage
|
||||
c
|
||||
|
||||
onHeroesLoaded: ->
|
||||
@formatHero hero for hero in @heroes.models
|
||||
|
||||
formatHero: (hero) ->
|
||||
hero.name = utils.i18n hero.attributes, 'extendedName'
|
||||
hero.name ?= utils.i18n hero.attributes, 'name'
|
||||
hero.description = utils.i18n hero.attributes, 'description'
|
||||
hero.class = hero.get('heroClass') or 'Warrior'
|
||||
|
||||
onPayButton: ->
|
||||
return @openModalView new AuthModal() if me.isAnonymous()
|
||||
@state = undefined
|
||||
@stateMessage = undefined
|
||||
@render()
|
||||
|
@ -53,10 +77,8 @@ module.exports = class SubscriptionSaleView extends RootView
|
|||
jqxhr = $.post('/db/subscription/-/year_sale', data)
|
||||
jqxhr.done (data, textStatus, jqXHR) =>
|
||||
application.tracker?.trackEvent 'Finished sale landing page subscription purchase', value: @yearSaleAmount
|
||||
me.set 'stripe', data?.stripe if data?.stripe?
|
||||
@state = 'invoice_paid'
|
||||
@stateMessage = undefined
|
||||
@render?()
|
||||
me.fetch(cache: false).always =>
|
||||
app.router.navigate '/play', trigger: true
|
||||
jqxhr.fail (xhr, textStatus, errorThrown) =>
|
||||
console.error 'We got an error subscribing with Stripe from our server:', textStatus, errorThrown
|
||||
application.tracker?.trackEvent 'Failed to finish 1 year subscription purchase', status: textStatus
|
||||
|
|
126
scripts/analytics/mongodb/queries/customerPolls.js
Normal file
126
scripts/analytics/mongodb/queries/customerPolls.js
Normal file
|
@ -0,0 +1,126 @@
|
|||
// Compare customer polls to user polls
|
||||
|
||||
// TODO: Only looks at subscribers currently, but it should look at other payments as well.
|
||||
|
||||
// Usage:
|
||||
// mongo <address>:<port>/<database> <script file> -u <username> -p <password>
|
||||
|
||||
var scriptStartTime = new Date();
|
||||
try {
|
||||
var polls = getPolls();
|
||||
var pollRecords = getPollRecords();
|
||||
var users = getUsers(Object.keys(pollRecords));
|
||||
|
||||
print("Crunching the data...");
|
||||
// answerCounts format {<pollID>: {name: <name>, answers: {<answer key>: {customer: <count>, user: <count>}}, totals: {customer: <count>, user: <count>}}
|
||||
var answerCounts = {};
|
||||
for (var userID in pollRecords) {
|
||||
for (var pollID in pollRecords[userID].polls) {
|
||||
var answer = pollRecords[userID].polls[pollID];
|
||||
if (!answerCounts[pollID]) {
|
||||
answerCounts[pollID] = {
|
||||
name: polls[pollID].name,
|
||||
answers: {},
|
||||
totals: {customer: 0, user: 0}
|
||||
};
|
||||
}
|
||||
if (!answerCounts[pollID].answers[answer]) answerCounts[pollID].answers[answer] = {customer: 0, user: 0};
|
||||
if (isCustomer(users[userID])) {
|
||||
answerCounts[pollID].answers[answer].customer++;
|
||||
answerCounts[pollID].totals.customer++;
|
||||
}
|
||||
else {
|
||||
answerCounts[pollID].answers[answer].user++;
|
||||
answerCounts[pollID].totals.user++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var pollID in answerCounts) {
|
||||
var pollName = answerCounts[pollID].name;
|
||||
print(pollName);
|
||||
var customerTotal = answerCounts[pollID].totals.customer;
|
||||
var userTotal = answerCounts[pollID].totals.user;
|
||||
for (var answer in answerCounts[pollID].answers) {
|
||||
var customerCount = answerCounts[pollID].answers[answer].customer;
|
||||
var customerPercentage = parseFloat(customerCount) / customerTotal;
|
||||
// customerPercentage = (customerPercentage * 100).toFixed(2);
|
||||
var userCount = answerCounts[pollID].answers[answer].user;
|
||||
var userPercentage = parseFloat(userCount) / userTotal;
|
||||
// userPercentage = (userPercentage * 100).toFixed(2);
|
||||
print(answer + "\t" + customerCount + "\t" + customerTotal + "\t" + customerPercentage + "\t" + userCount + "\t" + userTotal + "\t" + userPercentage);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(err) {
|
||||
log("ERROR: " + err);
|
||||
printjson(err);
|
||||
}
|
||||
finally {
|
||||
log("Script runtime: " + (new Date() - scriptStartTime));
|
||||
}
|
||||
|
||||
function getPolls()
|
||||
{
|
||||
print("Fetching polls...");
|
||||
var polls = {};
|
||||
var cursor = db.polls.find({}, {name: 1});
|
||||
while (cursor.hasNext()) {
|
||||
var doc = cursor.next();
|
||||
polls[doc._id.valueOf()] = doc;
|
||||
}
|
||||
return polls;
|
||||
}
|
||||
|
||||
function getPollRecords()
|
||||
{
|
||||
print("Fetching poll records...");
|
||||
var pollRecords = {};
|
||||
var cursor = db['user.polls.records'].find({}, {polls: 1, user: 1});
|
||||
while (cursor.hasNext()) {
|
||||
var doc = cursor.next();
|
||||
pollRecords[doc.user] = doc;
|
||||
}
|
||||
return pollRecords;
|
||||
}
|
||||
|
||||
function getUsers(userIDs)
|
||||
{
|
||||
print("Fetching users...");
|
||||
var userObjectIds = [];
|
||||
for (var i = 0; i < userIDs.length; i++) {
|
||||
userObjectIds.push(ObjectId(userIDs[i]));
|
||||
}
|
||||
var users = {};
|
||||
var cursor = db.users.find({$and: [{_id: {$in: userObjectIds}}, {stripe: {$exists: true}}]}, {stripe: 1});
|
||||
while (cursor.hasNext()) {
|
||||
var doc = cursor.next();
|
||||
users[doc._id.valueOf()] = doc;
|
||||
}
|
||||
return users;
|
||||
}
|
||||
|
||||
// *** Helper functions ***
|
||||
|
||||
function log(str) {
|
||||
print(new Date().toISOString() + " " + str);
|
||||
}
|
||||
|
||||
function objectIdWithTimestamp(timestamp) {
|
||||
// Convert string date to Date object (otherwise assume timestamp is a date)
|
||||
if (typeof(timestamp) == 'string') timestamp = new Date(timestamp);
|
||||
// Convert date object to hex seconds since Unix epoch
|
||||
var hexSeconds = Math.floor(timestamp/1000).toString(16);
|
||||
// Create an ObjectId with that hex timestamp
|
||||
var constructedObjectId = ObjectId(hexSeconds + "0000000000000000");
|
||||
return constructedObjectId
|
||||
}
|
||||
|
||||
function isCustomer(user) {
|
||||
if (!user) return false;
|
||||
var stripe = user.stripe;
|
||||
if (!stripe) return false;
|
||||
if (stripe.sponsorID || stripe.subscriptionID || stripe.free === true) return true;
|
||||
if (typeof stripe.free === 'string' && new Date() < new Date(stripe.free)) return true;
|
||||
return false;
|
||||
}
|
|
@ -307,6 +307,7 @@ PaymentHandler = class PaymentHandler extends Handler
|
|||
if err
|
||||
@logPaymentError(req, 'Stripe incr db error. '+err)
|
||||
return @sendDatabaseError(res, err)
|
||||
@sendPaymentHipChatMessage user: req.user, payment: payment
|
||||
@sendCreated(res, @formatEntity(req, payment))
|
||||
)
|
||||
)
|
||||
|
|
|
@ -7,6 +7,7 @@ mongoose = require 'mongoose'
|
|||
async = require 'async'
|
||||
config = require '../../server_config'
|
||||
Handler = require '../commons/Handler'
|
||||
hipchat = require '../hipchat'
|
||||
discountHandler = require './discount_handler'
|
||||
Prepaid = require '../prepaids/Prepaid'
|
||||
User = require '../users/User'
|
||||
|
@ -118,48 +119,78 @@ class SubscriptionHandler extends Handler
|
|||
|
||||
purchaseYearSale: (req, res) ->
|
||||
return @sendForbiddenError(res) unless req.user?
|
||||
return @sendForbiddenError(res) if req.user?.hasSubscription()
|
||||
return @sendForbiddenError(res) if req.user?.get('stripe')?.sponsorID
|
||||
|
||||
cancelSubscriptionImmediately = (user, subscription, done) =>
|
||||
return done() unless user and subscription
|
||||
stripe.customers.cancelSubscription subscription.customer, subscription.id, (err) =>
|
||||
return done(err) if err
|
||||
stripeInfo = _.cloneDeep(user.get('stripe') ? {})
|
||||
delete stripeInfo.planID
|
||||
delete stripeInfo.prepaidCode
|
||||
delete stripeInfo.subscriptionID
|
||||
user.set('stripe', stripeInfo)
|
||||
user.save (err) =>
|
||||
return done(err) if err
|
||||
done()
|
||||
|
||||
StripeUtils.getCustomer req.user, req.body.stripe?.token, (err, customer) =>
|
||||
if err
|
||||
@logSubscriptionError(req.user, "Purchase year sale get customer: #{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)
|
||||
findStripeSubscription customer.id, subscriptionID: req.user.get('stripe')?.subscriptionID, (subscription) =>
|
||||
stripeSubscriptionPeriodEndDate = new Date(subscription.current_period_end * 1000) if subscription
|
||||
|
||||
StripeUtils.createPayment req.user, charge, (err, payment) =>
|
||||
cancelSubscriptionImmediately req.user, subscription, (err) =>
|
||||
if err
|
||||
@logSubscriptionError(req.user, "Purchase year sale create payment: #{JSON.stringify(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
|
||||
|
||||
# Add terminal subscription to User
|
||||
endDate = new Date()
|
||||
endDate.setUTCFullYear(endDate.getUTCFullYear() + 1)
|
||||
stripeInfo = _.cloneDeep(req.user.get('stripe') ? {})
|
||||
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.createCharge req.user, subscriptions.year_sale.amount, metadata, (err, charge) =>
|
||||
if err
|
||||
@logSubscriptionError(req.user, "User save error: #{JSON.stringify(err)}")
|
||||
@logSubscriptionError(req.user, "Purchase year sale create charge: #{JSON.stringify(err)}")
|
||||
return @sendDatabaseError(res, err)
|
||||
@sendSuccess(res, user)
|
||||
|
||||
StripeUtils.createPayment req.user, charge, (err, payment) =>
|
||||
if err
|
||||
@logSubscriptionError(req.user, "Purchase year sale create payment: #{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) =>
|
||||
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)
|
||||
|
||||
subscribeUser: (req, user, done) ->
|
||||
if (not req.user) or req.user.isAnonymous() or user.isAnonymous()
|
||||
|
|
|
@ -1400,3 +1400,160 @@ describe 'Subscriptions', ->
|
|||
expect(payment).toBeDefined()
|
||||
expect(payment.get('gems')).toEqual(3500*12)
|
||||
done()
|
||||
|
||||
it 'year_sale when stripe.free === true', (done) ->
|
||||
stripe.tokens.create {
|
||||
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
|
||||
}, (err, token) ->
|
||||
loginNewUser (user1) ->
|
||||
user1.set('stripe', {free: true})
|
||||
user1.save (err, user1) ->
|
||||
expect(err).toBeNull()
|
||||
expect(user1.get('stripe')?.free).toEqual(true)
|
||||
requestBody =
|
||||
stripe:
|
||||
token: token.id
|
||||
timestamp: new Date()
|
||||
request.put {uri: "#{subscriptionURL}/-/year_sale", json: requestBody, headers: headers }, (err, res) ->
|
||||
expect(err).toBeNull()
|
||||
expect(res.statusCode).toBe(200)
|
||||
User.findById user1.id, (err, user1) ->
|
||||
expect(err).toBeNull()
|
||||
stripeInfo = user1.get('stripe')
|
||||
expect(stripeInfo).toBeDefined()
|
||||
return done() unless stripeInfo
|
||||
endDate = new Date()
|
||||
endDate.setUTCFullYear(endDate.getUTCFullYear() + 1)
|
||||
expect(stripeInfo.free).toEqual(endDate.toISOString().substring(0, 10))
|
||||
expect(stripeInfo.customerID).toBeDefined()
|
||||
expect(user1.get('purchased')?.gems).toEqual(3500*12)
|
||||
Payment.findOne 'stripe.customerID': stripeInfo.customerID, (err, payment) ->
|
||||
expect(err).toBeNull()
|
||||
expect(payment).toBeDefined()
|
||||
expect(payment.get('gems')).toEqual(3500*12)
|
||||
done()
|
||||
|
||||
it 'year_sale when stripe.free < today', (done) ->
|
||||
stripe.tokens.create {
|
||||
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
|
||||
}, (err, token) ->
|
||||
loginNewUser (user1) ->
|
||||
endDate = new Date()
|
||||
endDate.setUTCFullYear(endDate.getUTCFullYear() - 1)
|
||||
user1.set('stripe', {free: endDate.toISOString().substring(0, 10)})
|
||||
user1.save (err, user1) ->
|
||||
expect(err).toBeNull()
|
||||
expect(user1.get('stripe')?.free).toEqual(endDate.toISOString().substring(0, 10))
|
||||
requestBody =
|
||||
stripe:
|
||||
token: token.id
|
||||
timestamp: new Date()
|
||||
request.put {uri: "#{subscriptionURL}/-/year_sale", json: requestBody, headers: headers }, (err, res) ->
|
||||
expect(err).toBeNull()
|
||||
expect(res.statusCode).toBe(200)
|
||||
User.findById user1.id, (err, user1) ->
|
||||
expect(err).toBeNull()
|
||||
stripeInfo = user1.get('stripe')
|
||||
expect(stripeInfo).toBeDefined()
|
||||
return done() unless stripeInfo
|
||||
endDate = new Date()
|
||||
endDate.setUTCFullYear(endDate.getUTCFullYear() + 1)
|
||||
expect(stripeInfo.free).toEqual(endDate.toISOString().substring(0, 10))
|
||||
expect(stripeInfo.customerID).toBeDefined()
|
||||
expect(user1.get('purchased')?.gems).toEqual(3500*12)
|
||||
Payment.findOne 'stripe.customerID': stripeInfo.customerID, (err, payment) ->
|
||||
expect(err).toBeNull()
|
||||
expect(payment).toBeDefined()
|
||||
expect(payment.get('gems')).toEqual(3500*12)
|
||||
done()
|
||||
|
||||
it 'year_sale when stripe.free > today', (done) ->
|
||||
stripe.tokens.create {
|
||||
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
|
||||
}, (err, token) ->
|
||||
loginNewUser (user1) ->
|
||||
endDate = new Date()
|
||||
endDate.setUTCDate(endDate.getUTCDate() + 5)
|
||||
user1.set('stripe', {free: endDate.toISOString().substring(0, 10)})
|
||||
user1.save (err, user1) ->
|
||||
expect(err).toBeNull()
|
||||
expect(user1.get('stripe')?.free).toEqual(endDate.toISOString().substring(0, 10))
|
||||
requestBody =
|
||||
stripe:
|
||||
token: token.id
|
||||
timestamp: new Date()
|
||||
request.put {uri: "#{subscriptionURL}/-/year_sale", json: requestBody, headers: headers }, (err, res) ->
|
||||
expect(err).toBeNull()
|
||||
expect(res.statusCode).toBe(200)
|
||||
User.findById user1.id, (err, user1) ->
|
||||
expect(err).toBeNull()
|
||||
stripeInfo = user1.get('stripe')
|
||||
expect(stripeInfo).toBeDefined()
|
||||
return done() unless stripeInfo
|
||||
endDate = new Date()
|
||||
endDate.setUTCFullYear(endDate.getUTCFullYear() + 1)
|
||||
endDate.setUTCDate(endDate.getUTCDate() + 5)
|
||||
expect(stripeInfo.free).toEqual(endDate.toISOString().substring(0, 10))
|
||||
expect(stripeInfo.customerID).toBeDefined()
|
||||
expect(user1.get('purchased')?.gems).toEqual(3500*12)
|
||||
Payment.findOne 'stripe.customerID': stripeInfo.customerID, (err, payment) ->
|
||||
expect(err).toBeNull()
|
||||
expect(payment).toBeDefined()
|
||||
expect(payment.get('gems')).toEqual(3500*12)
|
||||
done()
|
||||
|
||||
it 'year_sale with monthly sub', (done) ->
|
||||
stripe.tokens.create {
|
||||
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
|
||||
}, (err, token) ->
|
||||
loginNewUser (user1) ->
|
||||
subscribeUser user1, token, null, ->
|
||||
User.findById user1.id, (err, user1) ->
|
||||
expect(err).toBeNull()
|
||||
stripeInfo = user1.get('stripe')
|
||||
stripe.customers.retrieveSubscription stripeInfo.customerID, stripeInfo.subscriptionID, (err, subscription) ->
|
||||
expect(err).toBeNull()
|
||||
expect(subscription).not.toBeNull()
|
||||
stripeSubscriptionPeriodEndDate = new Date(subscription.current_period_end * 1000)
|
||||
stripe.tokens.create {
|
||||
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
|
||||
}, (err, token) ->
|
||||
requestBody =
|
||||
stripe:
|
||||
token: token.id
|
||||
timestamp: new Date()
|
||||
request.put {uri: "#{subscriptionURL}/-/year_sale", json: requestBody, headers: headers }, (err, res) ->
|
||||
expect(err).toBeNull()
|
||||
expect(res.statusCode).toBe(200)
|
||||
User.findById user1.id, (err, user1) ->
|
||||
expect(err).toBeNull()
|
||||
stripeInfo = user1.get('stripe')
|
||||
expect(stripeInfo).toBeDefined()
|
||||
return done() unless stripeInfo
|
||||
endDate = stripeSubscriptionPeriodEndDate
|
||||
endDate.setUTCFullYear(endDate.getUTCFullYear() + 1)
|
||||
expect(stripeInfo.free).toEqual(endDate.toISOString().substring(0, 10))
|
||||
expect(stripeInfo.customerID).toBeDefined()
|
||||
expect(user1.get('purchased')?.gems).toEqual(3500+3500*12)
|
||||
Payment.findOne 'stripe.customerID': stripeInfo.customerID, (err, payment) ->
|
||||
expect(err).toBeNull()
|
||||
expect(payment).toBeDefined()
|
||||
expect(payment.get('gems')).toEqual(3500*12)
|
||||
done()
|
||||
|
||||
it 'year_sale with sponsored sub', (done) ->
|
||||
stripe.tokens.create {
|
||||
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
|
||||
}, (err, token) ->
|
||||
loginNewUser (user1) ->
|
||||
user1.set('stripe', {sponsorID: 'dummyID'})
|
||||
user1.save (err, user1) ->
|
||||
expect(err).toBeNull()
|
||||
requestBody =
|
||||
stripe:
|
||||
token: token.id
|
||||
timestamp: new Date()
|
||||
request.put {uri: "#{subscriptionURL}/-/year_sale", json: requestBody, headers: headers }, (err, res) ->
|
||||
console.log err
|
||||
expect(err).toBeNull()
|
||||
done()
|
||||
|
|
Loading…
Add table
Reference in a new issue