Last active
November 8, 2021 14:29
-
-
Save dyllandry/54e5e66a4a85b8195b30aaa1de74e9dc to your computer and use it in GitHub Desktop.
Gets all upcoming Stripe invoices into a csv that you can open in Excel.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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