Skip to content
This repository was archived by the owner on Aug 22, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ cohort(new Date('1/1/2014'), new Date('2/1/2014'), function (err, customers) {

And the returned `customers` object lets you dive deeper into the cohort.

#### Options

You can include options as the second param when initializing a cohort.

* `ignoreStripeFees` (default:false) If `true`, this will include the Stripe fees in your MRR.

#### Number of Customers

You can query the total amount of customers returned:
Expand Down Expand Up @@ -129,4 +135,4 @@ console.log('We made $' + mrr + ' off the startup plan!');

## License

MIT
MIT
14 changes: 8 additions & 6 deletions lib/customers.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ module.exports = Customers;
* A cohort of Stripe `customers`.
*
* @param {Array|Customer} customers
* @param {Object} options
*
*/

function Customers (customers) {
function Customers (customers, options) {
this.customers = customers;
this.options = options;
}

/**
Expand All @@ -34,7 +36,7 @@ Customers.prototype.created = function (start, end) {
return new Customers(this.customers.filter(function (customer) {
var created = customer.created * 1000;
return created >= s && created <= e;
}));
}), this.options);
};

/**
Expand All @@ -48,7 +50,7 @@ Customers.prototype.delinquent = function (delinquent) {
if (typeof delinquent !== 'boolean') delinquent = true;
return new Customers(this.customers.filter(function (customer) {
return customer.delinquent === delinquent;
}));
}), this.options);
};

/**
Expand All @@ -59,7 +61,7 @@ Customers.prototype.delinquent = function (delinquent) {
*/

Customers.prototype.filter = function (fn) {
return new Customers(this.customers.filter(fn));
return new Customers(this.customers.filter(fn), this.options);
};

/**
Expand Down Expand Up @@ -104,5 +106,5 @@ Customers.prototype.subscriptions = function (start, end) {
});
all.push.apply(all, subscriptions);
});
return new Subscriptions(all).started(start, end);
};
return new Subscriptions(all, this.options).started(start, end);
};
7 changes: 4 additions & 3 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@ module.exports = Cohorter;
* @param {String} key
* @param {Object} options
* @param {Number} count
* @param {Boolean} ignoreStripeFees
*/

function Cohorter (key, options) {
if (!(this instanceof Cohorter)) return new Cohorter(key, options);
if (!key) throw new Error('Stripe cohort requires a Stripe key.');
this.stripe = Stripe(key);
this.options = defaults(options, { count: 100, concurrency: 1 });
this.options = defaults(options, { count: 100, concurrency: 1, ignoreStripeFees: false });
var self = this;
return function () { self.cohort.apply(self, arguments); };
}
Expand All @@ -47,7 +48,7 @@ Cohorter.prototype.cohort = function (start, end, callback) {
this.customers(start, end, function (err, customers) {
if (err) return callback(err);
debug('created cohort');
callback(null, new Customers(customers));
callback(null, new Customers(customers, self.options));
});
};

Expand Down Expand Up @@ -114,4 +115,4 @@ Cohorter.prototype.query = function (start, end, offset, callback) {
debug('loaded %d customers with offset %d', customers.length, offset);
callback(null, res);
});
};
};
60 changes: 43 additions & 17 deletions lib/subscriptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ module.exports = Subscriptions;
* A list of Stripe `subscriptions`.
*
* @param {Array|Customer} subscriptions
* @param {Object} options
*
*/

function Subscriptions (subscriptions) {
function Subscriptions (subscriptions, options) {
this.subscriptions = subscriptions;
this.options = options;
}

/**
Expand Down Expand Up @@ -57,7 +59,7 @@ Subscriptions.prototype.started = function (start, end) {
if (subscription.status !== 'active') return false;
var started = subscription.start * 1000;
return started >= s && started <= e;
}));
}), this.options);
};

/**
Expand All @@ -70,7 +72,7 @@ Subscriptions.prototype.started = function (start, end) {
Subscriptions.prototype.status = function (status) {
return new Subscriptions(this.subscriptions.filter(function (subscription) {
return subscription.status === status;
}));
}), this.options);
};

/**
Expand All @@ -83,7 +85,7 @@ Subscriptions.prototype.status = function (status) {
Subscriptions.prototype.plan = function (planId) {
return new Subscriptions(this.subscriptions.filter(function (subscription) {
return subscription.plan.id === planId;
}));
}), this.options);
};

/**
Expand All @@ -97,7 +99,7 @@ Subscriptions.prototype.plan = function (planId) {
Subscriptions.prototype.withoutPlan = function (planId) {
return new Subscriptions(this.subscriptions.filter(function (subscription) {
return subscription.plan.id !== planId;
}));
}), this.options);
};

/**
Expand All @@ -112,7 +114,7 @@ Subscriptions.prototype.paid = function (amount) {
if (!amount) amount = 1;
return new Subscriptions(this.subscriptions.filter(function (subscription) {
return subscription.plan.amount >= amount;
}));
}), this.options);
};

/**
Expand Down Expand Up @@ -151,10 +153,13 @@ Subscriptions.prototype.print = function () {
for (var i = 0; i < list.length; i += 1) {
var s = list[i];
var c = s.customer;
var a = Math.round(amount(s) * 100) / 100.0;
var a = Math.round(amountMrr(s, this.options) * 100) / 100.0;
console.log([c.email, s.status, s.plan.name, '$' + a].join(' - '));
total += a;
}

total = Math.round(total * 100) / 100.0; // to two decimal points

console.log('Total MRR: $' + total);
};

Expand All @@ -168,7 +173,7 @@ Subscriptions.prototype.print = function () {

Subscriptions.prototype.mrr = function (start, end) {
var mrr = this.started(start, end).subscriptions.reduce(function (memo, subscription) {
return memo + amountMrr(subscription);
return memo + amountMrr(subscription, this.options);
}, 0.00);

return Math.round(mrr * 100) / 100.0; // to two decimal points
Expand All @@ -179,26 +184,47 @@ Subscriptions.prototype.mrr = function (start, end) {
* and discounts.
*
* @param {Subscription} subscription
* @param {Object} options
* @return {Number}
*/

function amountMrr (subscription) {
function amountMrr (subscription, options) {
var res = (subscription.plan.amount / 100.0);
res = normalizeMonths(subscription, res);
if (res > 0.0) {
var discount = subscription.customer.discount;
if (discount && discount.coupon) {
var coupon = discount.coupon;
if (coupon.valid) {
if (coupon.amount_off) res -= (coupon.amount_off / 100.0);
else if (coupon.percent_off) res *= (1.00 - (coupon.percent_off / 100.0));
}
res = applyDiscount(res, subscription.discount);
res = applyDiscount(res, subscription.customer.discount);

if(!options.ignoreStripeFees && res > 0) {
res *= (1.00 - 0.029); // Stripe fees (2.9%)
res -= 0.30; // Stripe fees (30 cents)
}
res *= (1.00 - 0.029); // Stripe fees
}
return res;
}

/**
* Apply a specified discount
*
* @param {Discount} discount
* @param {Number} res
* @return {Number}
*/

function applyDiscount (res, discount) {
if (discount && discount.coupon) {
var coupon = discount.coupon;

var amount_off = coupon.amount_off / 100.0;
if(amount_off && amount_off > res) amount_off = res;

if (coupon.amount_off) res -= amount_off;
else if (coupon.percent_off) res *= (1.00 - (coupon.percent_off / 100.0));
}
return res;
}


//
function normalizeMonths (subscription, res) {
var interval = subscription.plan.interval;
Expand Down