mirror of
https://github.com/sudoxnym/habitica.git
synced 2026-08-03 08:21:07 +00:00
feat(gifts): allow gifting subscriptions
This commit is contained in:
parent
e8dbd5ca1a
commit
5b01fff700
8 changed files with 86 additions and 61 deletions
|
|
@ -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){
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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('/');
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue