Skip to content
This repository was archived by the owner on Aug 22, 2023. It is now read-only.

Commit 047c879

Browse files
author
Ilya Volodarsky
committed
initial comimt
0 parents  commit 047c879

File tree

9 files changed

+700
-0
lines changed

9 files changed

+700
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

History.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
0.0.1 - February 26, 2014
3+
-------------------------
4+
:sparkles:

Makefile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
node_modules: package.json
3+
@npm install
4+
5+
test: node_modules
6+
@./node_modules/.bin/mocha --reporter spec
7+
8+
.PHONY: test

Readme.md

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
2+
# stripe-cohort
3+
4+
Create Stripe customer cohorts for a useful business overview.
5+
6+
### Overview
7+
8+
If you're interested in getting a cohort based overview of your Stripe customers, then this library might be of help.
9+
10+
It currently supports the following stats (by cohort):
11+
12+
- Total number of customers
13+
- Monthly recurring revenue
14+
- Number of subscriptions
15+
- Subscriptions by plan
16+
17+
## Installation
18+
19+
$ npm install stripe-cohort
20+
21+
## Example
22+
23+
Create a new Stripe cohort (by the customer's `created` date):
24+
25+
```js
26+
cohort(new Date('1/1/2014'), new Date('2/1/2014'), function (err, customers) {
27+
console.log(customers.count() + ' created in January!');
28+
});
29+
```
30+
31+
And the `customers` object lets you query information about this cohort.
32+
33+
#### Number of Customers
34+
35+
You can query the total amount of customers returned:
36+
37+
```js
38+
customers.count();
39+
```
40+
41+
or filter further inside the cohort by the customers' `created` date:
42+
43+
```js
44+
customers.count(new Date('1/15/2014'), new Date('1/24/2014'));
45+
```
46+
47+
#### Customer List
48+
49+
```js
50+
customers.list()
51+
```
52+
```
53+
[
54+
{
55+
id: 'cus_2983jd92d2d',
56+
name: 'Patrick Collison',
57+
..
58+
}
59+
}
60+
]
61+
```
62+
or filter further by the customers' `created` date:
63+
64+
```js
65+
customers.list(new Date('1/15/2014'), new Date('1/24/2014'));
66+
```
67+
68+
or get all the `delinquent` customers:
69+
70+
```js
71+
customers.delinquent().count();
72+
```
73+
74+
#### Subscriptions
75+
76+
You can learn about your active subscriptions too
77+
78+
```js
79+
customers.subscriptions().count();
80+
```
81+
82+
Or the ones created between the date provided:
83+
84+
```js
85+
customers.subscriptions(new Date('1/15/2014'), new Date('1/24/2014')).count();
86+
```
87+
88+
Or just get the list of Stripe subscription objects:
89+
90+
```js
91+
var objects = customers.subscriptions().list();
92+
```
93+
94+
#### Monthly Recurring Revenue
95+
96+
You can get the monthly recurring revenue from the active subscriptions on the customers:
97+
98+
```js
99+
customers.subscriptions().active().mrr();
100+
```
101+
102+
And for the trialing accounts:
103+
104+
```js
105+
customers.subscriptions().trialing().mrr();
106+
```
107+
108+
And for any status really:
109+
110+
```js
111+
customers.subscriptions().status('unpaid').mrr();
112+
```
113+
114+
And you can query the monthly recurring revenue by subscription `start` within a cohort:
115+
116+
```js
117+
customers.subscriptions().active().mrr(new Date('1/15/2014'), new Date('1/16/2014'));
118+
```
119+
120+
Remember that the montly recurring revenue does not equal charges. For example, if a customer upgrades from a $29 plan to a $79 plan today, they will pro-rated for the rest of their billing period. That means you did not make the $79 yet, but you'll make the difference next month. For hard cash, use charges.
121+
122+
#### Plans
123+
124+
It's also interesting to know what plans the subscriptions are being set at. You can select the subscriptions that fall under that plan:
125+
126+
```js
127+
var mrr = customers.subscriptions().active().plan('startup').mrr();
128+
console.log('We made $' + mrr + ' off the startup plan!');
129+
```
130+
131+
## License
132+
133+
MIT

lib/customers.js

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
2+
var Subscriptions = require('./subscriptions');
3+
var util = require('util');
4+
5+
/**
6+
* Expose `Customers`.
7+
*/
8+
9+
module.exports = Customers;
10+
11+
/**
12+
* A cohort of Stripe `customers`.
13+
*
14+
* @param {Array|Customer} customers
15+
*
16+
*/
17+
18+
function Customers (customers) {
19+
this.customers = customers;
20+
}
21+
22+
/**
23+
* Return a new `Customers` thats filtered by `start` and `end`.
24+
*
25+
* @param {Date} start
26+
* @param {Date} end
27+
* @return {Customers}
28+
*/
29+
30+
Customers.prototype.created = function (start, end) {
31+
if (!util.isDate(start)) return this;
32+
if (!util.isDate(end)) end = new Date('1/1/99999');
33+
var s = start.getTime(), e = end.getTime();
34+
return new Customers(this.customers.filter(function (customer) {
35+
var created = customer.created * 1000;
36+
return created >= s && created <= e;
37+
}));
38+
};
39+
40+
/**
41+
* Return a new `Customers` thats filtered by delinquent customers.
42+
*
43+
* @param {Boolean} delinquent
44+
* @return {Customers}
45+
*/
46+
47+
Customers.prototype.delinquent = function (delinquent) {
48+
if (typeof delinquent !== 'boolean') delinquent = true;
49+
return new Customers(this.customers.filter(function (customer) {
50+
return customer.delinquent === delinquent;
51+
}));
52+
};
53+
54+
/**
55+
* Filter customers by `fn`.
56+
*
57+
* @param {Function} fn
58+
* @return {Customers}
59+
*/
60+
61+
Customers.prototype.filter = function (fn) {
62+
return new Customers(this.customers.filter(fn));
63+
};
64+
65+
/**
66+
* Count the customers between `start` and `end`.
67+
*
68+
* @param {Date} start
69+
* @param {Date} end
70+
* @return {Number}
71+
*/
72+
73+
Customers.prototype.list = function (start, end) {
74+
return this.created(start, end).customers;
75+
};
76+
77+
/**
78+
* Count the customers between `start` and `end`.
79+
*
80+
* @param {Date} start
81+
* @param {Date} end
82+
* @return {Number}
83+
*/
84+
85+
Customers.prototype.count = function (start, end) {
86+
return this.created(start, end).customers.length;
87+
};
88+
89+
/**
90+
* Get a list of active subscriptions between `start` and `end`.
91+
*
92+
* @param {Date} start
93+
* @param {Date} end
94+
* @return {Subscriptions}
95+
*/
96+
97+
Customers.prototype.subscriptions = function (start, end) {
98+
var all = [];
99+
this.customers.forEach(function (customer) {
100+
var subscriptions = customer.subscriptions.data;
101+
subscriptions.forEach(function (subscription) {
102+
// create a backwards reference so that we can check discount
103+
subscription.customer = customer;
104+
});
105+
all.push.apply(all, subscriptions);
106+
});
107+
return new Subscriptions(all).started(start, end);
108+
};

lib/index.js

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
2+
var Batch = require('batch');
3+
var Customers = require('./customers');
4+
var debug = require('debug')('stripe-cohort');
5+
var defaults = require('defaults');
6+
var range = require('range-component');
7+
var Stripe = require('stripe');
8+
var unixTime = require('unix-time');
9+
var util = require('util');
10+
11+
/**
12+
* Expose `Cohorter`.
13+
*/
14+
15+
module.exports = Cohorter;
16+
17+
/**
18+
* Initialize a new `Cohorter` the Stripe `key`.
19+
*
20+
* @param {String} key
21+
* @param {Object} options
22+
* @param {Number} count
23+
*/
24+
25+
function Cohorter (key, options) {
26+
if (!(this instanceof Cohorter)) return new Cohorter(key, options);
27+
if (!key) throw new Error('Stripe cohort requires a Stripe key.');
28+
this.stripe = Stripe(key);
29+
this.options = defaults(options, { count: 100, concurrency: 20 });
30+
var self = this;
31+
return function () { self.cohort.apply(self, arguments); };
32+
}
33+
34+
/**
35+
* Create a cohort.
36+
*
37+
* @param {Date} start
38+
* @param {Date} end
39+
* @param {Function} callback
40+
*/
41+
42+
Cohorter.prototype.cohort = function (start, end, callback) {
43+
if (!util.isDate(start)) throw new Error('Start must be a date.');
44+
if (!util.isDate(end)) throw new Error('End must be a date.');
45+
var self = this;
46+
debug('creating cohort [%s - %s] ..', start, end);
47+
this.customers(start, end, function (err, customers) {
48+
if (err) return callback(err);
49+
debug('created cohort');
50+
callback(null, new Customers(customers));
51+
});
52+
};
53+
54+
/**
55+
* Load customers between a `start` and `end` date.
56+
*
57+
* @param {Date} start
58+
* @param {Date} end
59+
* @param {Function} callback
60+
*/
61+
62+
Cohorter.prototype.customers = function (start, end, callback) {
63+
debug('loading customers with created [%s - %s] ..', start, end);
64+
var self = this;
65+
var customers = [];
66+
var page = this.options.count;
67+
// run the first query to get the total unpaginated count
68+
this.query(start, end, 0, function (err, res) {
69+
if (err) return callback(err);
70+
var count = res.count;
71+
customers.push.apply(customers, res.data);
72+
var got = res.data.length;
73+
var left = res.count - got;
74+
// check if we grabbed everything in the first query
75+
if (0 === left) return callback(null, customers);
76+
// there's more, we have to paginate query
77+
var pages = Math.ceil(left / page);
78+
debug('loaded %d customers, %d left in %d pages of %d', got, left, pages, page);
79+
var batch = new Batch();
80+
batch.concurrency(self.options.concurrency);
81+
range(0, pages).forEach(function (i) {
82+
batch.push(function (done) { self.query(start, end, got + (i * page), done); });
83+
});
84+
batch.end(function (err, results) {
85+
if (err) return callback(err);
86+
results.forEach(function (res) {
87+
customers.push.apply(customers, res.data);
88+
});
89+
debug('finished loading all customers in cohort');
90+
callback(null, customers);
91+
});
92+
});
93+
};
94+
95+
/**
96+
* List customers between a `start` and `end` date, with an `offset`.
97+
*
98+
* @param {Date} start
99+
* @param {Date} end
100+
* @param {Number} offset
101+
* @param {Function} callback
102+
*/
103+
104+
Cohorter.prototype.query = function (start, end, offset, callback) {
105+
debug('loading customers with created [%s - %s] with offset %d ..', start, end, offset);
106+
var options = {
107+
created: { gte: unixTime(start), lte: unixTime(end) },
108+
count: this.options.count,
109+
offset: offset
110+
};
111+
this.stripe.customers.list(options, function (err, res) {
112+
if (err) return callback(err);
113+
var customers = res.data;
114+
debug('loaded %d customers with offset %d', customers.length, offset);
115+
callback(null, res);
116+
});
117+
};

0 commit comments

Comments
 (0)