Skip to content

Instantly share code, notes, and snippets.

@ashleyrudland
Last active August 21, 2023 05:40
Show Gist options
  • Save ashleyrudland/88c27c0289ecc78561b15d07c1fe9793 to your computer and use it in GitHub Desktop.
Save ashleyrudland/88c27c0289ecc78561b15d07c1fe9793 to your computer and use it in GitHub Desktop.
Find churn reasons by sucking out all Stripe subscriptions (includes all subscriptions)
const fs = require('fs');
const moment = require('moment');
const stripe = require('../stripe');
const hashCode = (str) => {
let hash = 0;
if (typeof str !== 'string' || str.length === 0) {
return String(hash);
}
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash;
}
hash = hash >>> 0;
return Number(hash).toString(32).toLowerCase();
};
const VAT_RATE = 0;
const exportSubscriptionData = async () => {
let hasMore = false;
let lastId = '';
let page = 1;
const subs = [];
const collection = [];
while (subs.length === 0 || hasMore) {
console.log(`getting page ${page} of subs...`)
const opt = {
limit: 100,
expand: ['data.customer'],
status: 'all',
};
if (lastId) opt.starting_after = lastId;
const result = await stripe.subscriptions.list(opt);
hasMore = result.has_more;
if (result && result.data?.length > 0) {
result.data.forEach((item) => {
subs.push(item);
let amount = item.plan.amount;
if (item.discount && item.discount.coupon) {
amount = (amount * (1 + VAT_RATE)) * (1 - (item.discount.coupon.percent_off / 100));
}
amount = amount / 100;
let interval = item.plan.interval;
if (item.plan.interval_count === 3) {
interval = 'quarter';
}
let monthly = amount;
if (interval === 'quarter') {
monthly = amount / 3;
} else if (interval === 'year') {
monthly = amount / 12;
}
monthly = monthly;
const discount = item.discount && item.discount.coupon ? item.discount.coupon.id : '';
collection.push({
customer: hashCode(item.customer.email),
start: moment(item.start_date * 1000).format('YYYY-MM-DD'),
end: item.canceled_at ? moment(item.canceled_at * 1000).format('YYYY-MM-DD') : '',
status: item.status,
plan: item.plan.id,
amount,
interval,
monthly,
discount,
cancellation_reason: item.cancellation_details && item.cancellation_details.feedback ? item.cancellation_details.feedback : '',
cancellation_comment: item.cancellation_details && item.cancellation_details.comment ? item.cancellation_details.comment : '',
});
})
lastId = result.data[result.data.length - 1].id
} else {
break
}
page += 1
console.log(`${collection.length} subs collected...`);
}
collection.sort((a, b) => new Date(b.start) - new Date(a.start));
let csv = 'Customer,Start,End,Status,Plan,Amount,Interval,Monthly,Discount,Cancellation Reason,Cancellation Comment\n';
csv += collection.map((item) => `${item.customer},${item.start},${item.end},${item.status},${item.plan},${item.amount.toFixed(2)},${item.interval},${item.monthly.toFixed(2)},${item.discount},${item.cancellation_reason},"${item.cancellation_comment}"`).join('\n');
fs.writeFileSync('./subscription.csv', csv, { encoding: 'utf8' });
};
exportSubscriptionData().then(() => process.exit());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment