Skip to content

Instantly share code, notes, and snippets.

@dyllandry
Last active Nov 8, 2021
Embed
What would you like to do?
Gets all upcoming Stripe invoices into a csv that you can open in Excel.
/* Stripe doesn't provide an endpoint to get all upcoming invoices,
* instead you have to fetch them one by one for each subscriber.
* This script gets all upcoming stripe invoices and puts them in
* an easy to read CSV file you can open in excel.
*
* This tool can be useful for companies with few subscriptions to
* see which customer's subscriptions will renew soon so they can maybe
* get ahead of it, maybe provide some extra customer service
* in advance or something.
*/
import Stripe from 'stripe';
import fs from 'fs-extra';
import path from 'path';
import Bottleneck from 'bottleneck';
const reqLimiter = new Bottleneck({ minTime: 40, maxConcurrent: 5 });
/*******************************************
* Replace the below string with your stripe key.
******************************************/
const stripe = new Stripe('PUT YOUR STRIPE KEY HERE', { apiVersion: '2020-08-27' });
main();
export default async function main () {
const subs = await getAllSubs();
const upcomingInvoices = await getUpcomingInvoices({ subs });
const csv = invoicesToCsv({ invoices: upcomingInvoices });
exportCsvFile({ csv });
return 0
}
function exportCsvFile({ csv }: { csv: string }): void {
// This line assumes this file is in a src directory, and is outputting to a sibling dist directory.
const csvPath = path.resolve(__dirname, '..', 'dist', 'upcoming-stripe-payments.csv');
fs.outputFileSync(csvPath, csv);
console.log(`Generated csv with ${csv.split('\n').length} lines to ${csvPath}`);
return;
}
/**
* A Stripe invoice with customer and subscription properties included.
*/
interface InvoiceCusSub extends Stripe.Response<Stripe.Invoice> {
customer: Stripe.Customer
subscription: Stripe.Subscription
}
async function getUpcomingInvoices({ subs } : { subs: Stripe.Subscription[] }): Promise<InvoiceCusSub[]> {
return Promise.all(
subs.map(
(sub, i) => reqLimiter.schedule((() => {
console.log('Invoice request: ' + (i + 1));
return stripe.invoices.retrieveUpcoming({
subscription: sub.id, expand: ['customer', 'subscription']
});
}) as () => Promise<InvoiceCusSub>)
)
);
}
function invoicesToCsv({ invoices } : { invoices: InvoiceCusSub[] }) {
// Sort in ascending order of customer's next payment date (current_period_end).
const orderedInvoices = [ ...invoices ].sort((a, b) => a.subscription.current_period_end - b.subscription.current_period_end);
const header = 'email,subscription,price,renewal date\n';
const rows = orderedInvoices.reduce<string>((acc, invoice) => (
acc + invoiceToCsvLine({ invoice })
), '');
return header + rows;
}
function invoiceToCsvLine({ invoice } : { invoice: InvoiceCusSub }) {
const subscriptionNames = invoice.subscription.items.data.reduce((acc, item) => acc + item.price.id, '');
return `${invoice.customer.email},${subscriptionNames},$${invoice.amount_due/100},${new Date(invoice.subscription.current_period_end * 1000).toLocaleDateString()}\n`;
}
async function getAllSubs() {
const res = await reqLimiter.schedule(() => stripe.subscriptions.list({ limit: 200 }))
return res.data;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment