diff --git a/public/js/controllers/groupsCtrl.js b/public/js/controllers/groupsCtrl.js index e4a0a8638f..9efd8826eb 100644 --- a/public/js/controllers/groupsCtrl.js +++ b/public/js/controllers/groupsCtrl.js @@ -133,7 +133,7 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' $scope.gift = { type: 'gems', gems: {amount:0, fromBalance:true}, - subscription: {}, + subscription: {months:1}, message:'' }; $scope.sendGift = function(uuid, gift){ diff --git a/public/js/controllers/rootCtrl.js b/public/js/controllers/rootCtrl.js index 7c5b6c3ed4..7fc249754e 100644 --- a/public/js/controllers/rootCtrl.js +++ b/public/js/controllers/rootCtrl.js @@ -140,19 +140,21 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$ } $rootScope.encodeGift = function(uuid, gift){ - return JSON.stringify({uuid:uuid, amount:gift.gems.amount, message:gift.message}); + gift.uuid = uuid; + return JSON.stringify(gift); } $rootScope.showStripe = function(data) { + var isSub = data.subscription || (data.gift && data.gift.type=='subscription'); StripeCheckout.open({ key: window.env.STRIPE_PUB_KEY, address: false, - amount: data.gift ? data.gift.gems.amount/4*100 : 500, - name: data.subscription ? window.env.t('subscribe') : window.env.t('checkout'), - description: data.subscription ? - window.env.t('buySubsText') : - window.env.t('donationDesc'), - panelLabel: data.subscription ? window.env.t('subscribe') : window.env.t('checkout'), + amount: !data.gift ? 500 : // 500 = $5 + data.gift.type=='subscription' ? Content.subscriptionBlocks[data.gift.subscription.months].price*100: + data.gift.gems.amount/4*100, + name: isSub ? window.env.t('subscribe') : window.env.t('checkout'), + description: isSub ? window.env.t('buySubsText') : window.env.t('donationDesc'), + panelLabel: isSub ? window.env.t('subscribe') : window.env.t('checkout'), token: function(res) { var url = '/stripe/checkout'; if (data.gift) url += '?gift=' + $rootScope.encodeGift(data.uuid, data.gift); diff --git a/src/controllers/members.js b/src/controllers/members.js index 61ec8c8cd0..e0a8b653b9 100644 --- a/src/controllers/members.js +++ b/src/controllers/members.js @@ -30,9 +30,14 @@ api.getMember = function(req, res, next) { } api.sendMessage = function(user, member, data){ - var msg = data.amount - ? "`Hello " + member.profile.name + ", " + user.profile.name + " has sent you " + data.amount + " gems!` " + data.message - : data.message; + var msg; + if (!data.type) { + msg = data.message + } else { + msg = "`Hello " + member.profile.name + ", " + user.profile.name + " has sent you "; + msg += (data.type=='gems') ? data.gems.amount + " gems!`" : data.subscription.months + " months of subscription!`"; + msg += data.message; + } shared.refPush(member.inbox.messages, groups.chatDefaults(msg, user)); member.inbox.newMessages++; member._v++; @@ -76,7 +81,7 @@ api.sendGift = function(req, res, next){ return cb({code: 401, err: "Amount must be within 0 and your current number of gems."}); member.balance += amt; user.balance -= amt; - api.sendMessage(user, member, {amount:req.body.gems.amount, message:req.body.message}); + api.sendMessage(user, member, req.body); return async.parallel([ function (cb2) { member.save(cb2) }, function (cb2) { user.save(cb2) } diff --git a/src/controllers/payments/index.js b/src/controllers/payments/index.js index 23e5925114..332f2cdf45 100644 --- a/src/controllers/payments/index.js +++ b/src/controllers/payments/index.js @@ -25,26 +25,37 @@ function revealMysteryItems(user) { }); } -exports.createSubscription = function(user, data) { - if (!user.purchased.plan) user.purchased.plan = {}; - var p = user.purchased.plan; +exports.createSubscription = function(data, cb) { + var recipient = data.gift ? data.gift.member : data.user; + if (!recipient.purchased.plan) recipient.purchased.plan = {}; + var p = recipient.purchased.plan; _(p).merge({ // override with these values planId:'basic_earned', customerId: data.customerId, dateUpdated: new Date(), gemsBought: 0, paymentMethod: data.paymentMethod, - extraMonths: p.extraMonths + (p.dateTerminated ? moment(p.dateTerminated).diff(new Date(),'months',true) : 0), + extraMonths: +p.extraMonths + + +(p.dateTerminated ? moment(p.dateTerminated).diff(new Date(),'months',true) : 0) + + +(data.gift ? data.gift.subscription.months : 0), dateTerminated: null }).defaults({ // allow non-override if a plan was previously used dateCreated: new Date(), mysteryItems: [] }); - revealMysteryItems(user); - if(isProduction) utils.txnEmail(user, 'subscription-begins'); - user.purchased.txnCount++; - utils.ga.event('subscribe', data.paymentMethod).send(); - utils.ga.transaction(data.customerId, 5).item(5, 1, data.paymentMethod.toLowerCase() + '-subscription', data.paymentMethod + " > Stripe").send(); + revealMysteryItems(recipient); + if(isProduction) { + utils.txnEmail(data.user, 'subscription-begins'); + //TODO proper ga.event / transaction + utils.ga.event('subscribe', data.paymentMethod).send(); + utils.ga.transaction(data.customerId, 5).item(5, 1, data.paymentMethod.toLowerCase() + '-subscription', data.paymentMethod + " > Stripe").send(); + } + data.user.purchased.txnCount++; + if (data.gift) members.sendMessage(data.user, data.gift.member, data.gift); + async.parallel([ + function(cb2){data.user.save(cb2)}, + function(cb2){data.gift ? data.gift.member.save(cb2) : cb2(null);} + ], cb); } /** @@ -65,7 +76,7 @@ exports.cancelSubscription = function(user, data) { } exports.buyGems = function(data, cb) { - var amt = data.gift ? data.gift.amount/4 : 5; + var amt = data.gift ? data.gift.gems.amount/4 : 5; (data.gift ? data.gift.member : data.user).balance += amt; data.user.purchased.txnCount++; if(isProduction) { diff --git a/src/controllers/payments/paypal.js b/src/controllers/payments/paypal.js index b264f32325..1277b7c967 100644 --- a/src/controllers/payments/paypal.js +++ b/src/controllers/payments/paypal.js @@ -8,6 +8,7 @@ var payments = require('./index'); var logger = require('../../logging'); var ipn = require('paypal-ipn'); var paypal = require('paypal-rest-sdk'); +var shared = require('habitrpg-shared'); // This is the plan.id for paypal subscriptions. You have to set up billing plans via their REST sdk (they don't have // a web interface for billing-plan creation), see ./paypalBillingSetup.js for how. After the billing plan is created @@ -68,8 +69,13 @@ exports.executeBillingAgreement = function(req,res,next){ exports.createPayment = function(req, res, next) { // if we're gifting to a user, put it in session for the `execute()` - req.session.gift = req.query.gift ? JSON.parse(req.query.gift) : undefined; - var price = req.session.gift ? Number(req.session.gift.amount/4).toFixed(2) : 5.00; + var gift = req.session.gift = req.query.gift ? JSON.parse(req.query.gift) : undefined; + var price = !gift ? 5.00 + : gift.type=='gems' ? Number(gift.gems.amount/4).toFixed(2) + : Number(shared.content.subscriptionBlocks[gift.subscription.months].price).toFixed(2); + var description = !gift ? "HabitRPG Gems" + : gift.type=='gems' ? "HabitRPG Gems (Gift)" + : gift.subscription.months + "mo. HabitRPG Subscription (Gift)"; var create_payment = { "intent": "sale", "payer": { @@ -82,7 +88,7 @@ exports.createPayment = function(req, res, next) { "transactions": [{ "item_list": { "items": [{ - "name": "HabitRPG Gems", + "name": description, //"sku": "1", "price": price, "currency": "USD", @@ -93,7 +99,7 @@ exports.createPayment = function(req, res, next) { "currency": "USD", "total": price }, - "description": "HabitRPG Gems" + "description": description }] }; paypal.payment.create(create_payment, function (err, payment) { @@ -118,12 +124,17 @@ exports.executePayment = function(req, res, next) { ], cb); }, function(results, cb){ - if (_.isEmpty(results[0])) - return cb("user not found when completing paypal transaction"); - if (gift) gift.member = results[1]; - payments.buyGems({user:results[0], customerId:PayerID, paymentMethod:'Paypal', gift:gift}, cb); + if (_.isEmpty(results[0])) return cb("User not found when completing paypal transaction"); + var data = {user:results[0], customerId:PayerID, paymentMethod:'Paypal', gift:gift} + var method = 'buyGems'; + if (gift) { + gift.member = results[1]; + if (gift.type=='subscription') method = 'createSubscription'; + data.paymentMethod = 'Gift'; + } + payments[method](data, cb); } - ],function(err, results){ + ],function(err){ if (err) return next(parseErr(err)); res.redirect('/'); }) diff --git a/src/controllers/payments/stripe.js b/src/controllers/payments/stripe.js index 404ed80ad9..549c58bfb0 100644 --- a/src/controllers/payments/stripe.js +++ b/src/controllers/payments/stripe.js @@ -3,6 +3,7 @@ var stripe = require("stripe")(nconf.get('STRIPE_API_KEY')); var async = require('async'); var payments = require('./index'); var User = require('mongoose').model('User'); +var shared = require('habitrpg-shared'); /* Setup Stripe response when posting payment @@ -23,27 +24,32 @@ exports.checkout = function(req, res, next) { }, cb); } else { stripe.charges.create({ - amount: gift ? ""+gift.amount/4*100 : "500", //"500" = $5 + amount: !gift ? "500" //"500" = $5 + : gift.type=='subscription' ? ""+shared.content.subscriptionBlocks[gift.subscription.months].price*100 + : ""+gift.gems.amount/4*100, currency: "usd", card: token }, cb); } }, function(response, cb) { - if (req.query.plan) { - payments.createSubscription(user, {customerId: response.id, paymentMethod: 'Stripe'}); - user.save(cb); - } else { - async.waterfall([ - function(cb2){ User.findById(gift ? gift.uuid : undefined, cb2) }, - function(member, cb2){ - if (gift) gift.member = member; - payments.buyGems({user:user, customerId:response.id, paymentMethod:'Stripe', gift:gift},cb2); + if (req.query.plan) + return payments.createSubscription({user:user, customerId:response.id, paymentMethod:'Stripe'}, cb); + async.waterfall([ + function(cb2){ User.findById(gift ? gift.uuid : undefined, cb2) }, + function(member, cb2){ + var data = {user:user, customerId:response.id, paymentMethod:'Stripe', gift:gift}; + var method = 'buyGems'; + if (gift) { + gift.member = member; + if (gift.type=='subscription') method = 'createSubscription'; + data.paymentMethod = 'Gift'; } - ], cb); - } + payments[method](data, cb2); + } + ], cb); } - ], function(err, saved){ + ], function(err){ if (err) return res.send(500, err.toString()); // don't json this, let toString() handle errors res.send(200); user = token = null; diff --git a/src/models/user.js b/src/models/user.js index e973fa9a12..92be197914 100644 --- a/src/models/user.js +++ b/src/models/user.js @@ -92,7 +92,7 @@ var UserSchema = new Schema({ mobileChat: Boolean, plan: { planId: String, - paymentMethod: String, //enum: ['Paypal','Stripe', '']} + paymentMethod: String, //enum: ['Paypal','Stripe', 'Gift', '']} customerId: String, dateCreated: Date, diff --git a/views/shared/modals/members.jade b/views/shared/modals/members.jade index 94c31ec0c8..60eaf2f79f 100644 --- a/views/shared/modals/members.jade +++ b/views/shared/modals/members.jade @@ -75,22 +75,12 @@ script(type='text/ng-template', id='modals/send-gift.html') .panel-heading Subscription .panel-body .form-group - .radio - label - input(type="radio", name="subRadio", value="1", ng-model='gift.subscription.months') - | 1 Month ($5) - .radio - label - input(type="radio", name="subRadio", value="3", ng-model='gift.subscription.months') - | 3 Months ($15) - .radio - label - input(type="radio", name="subRadio", value="6", ng-model='gift.subscription.months') - | 6 Months ($30) - .radio - label - input(type="radio", name="subRadio", value="12", ng-model='gift.subscription.months') - | 1 Year ($48) + each block in env.Content.subscriptionBlocks + .radio + label + input(type="radio", name="subRadio", value="#{block.months}", ng-model='gift.subscription.months') + | #{block.months} Month(s): $#{block.price} + textarea.form-control(rows='3', ng-model='gift.message', placeholder='Personal message (optional)') .modal-footer