DEV Community

Takuya Matsuyama
Takuya Matsuyama

Posted on

A batch-JS for downloading invoice PDFs from Stripe

I'm running a small business that uses Stripe for accepting payments from users.
While the Stripe dashboard is quite useful, it doesn't provide a quick way to download invoice PDF files for accounting purposes.
So, I wrote a batch script to do so:

import initStripe from 'stripe'
import path from 'path'
import fs from 'fs'
import { exec } from 'child_process'
import moment from 'moment'

const stripe = initStripe('sk_live_****************')

function log(...args) {
  const datetime = moment().format('YYYY-MM-DDTHH:mm:ss')
  console.log.apply(console, [datetime, ...args])
}

async function run() {
  let starting_after = undefined
  let res

  do {
    res = await stripe.invoices.list({
      created: {
        gte: new Date('2020-01-01T00:00:00'),
        lt: new Date('2020-12-31T23:59:59')
      },
      starting_after,
      limit: 10
    })
    const items = res.data
    log('got', items.length, 'items', { starting_after })
    await Promise.all(
      items.map(async (invoice) => {
        try {
          if (invoice.amount_paid <= 0) return
          if (invoice.status !== 'paid') {
            log('Invoice is not paid:', invoice.id, invoice.status)
            return
          }

          log('Processing invoice:', invoice.id)

          const paidAt = new Date(invoice.status_transitions.paid_at * 1000)
          const dest = path.resolve(
            __dirname,
            'invoices',
            '2020',
            moment(paidAt).format('YYYYMMDD-') + invoice.id + '.pdf'
          )
          if (fs.existsSync(dest)) return
          const res = await new Promise((resolve, reject) =>
            exec(
              `/usr/local/bin/wget '${invoice.invoice_pdf}' -O '${dest}'`,
              (err, stdout, _stderr) => {
                err ? reject(err) : resolve(stdout)
              }
            )
          )
          log('Downloaded:', dest, res)
        } catch (e) {
          log(e.stack)
          log('Failed to process invoice:', invoice)
          throw e
        }
      })
    )
    starting_after = items[items.length - 1].id
  } while (res.has_more)
}

run().then(
  function () {
    log('DONE')
  },
  function (err) {
    log('ERROR:', err.stack || err)
    if (err.response) {
      log(err.response)
    }
  }
)
Enter fullscreen mode Exit fullscreen mode

How it works

It retrieves a list of the invoices of the given term by specifying created.gte and created.lt parameters:

    res = await stripe.invoices.list({
      created: {
        gte: new Date('2020-01-01T00:00:00'),
        lt: new Date('2020-12-31T23:59:59')
      },
      starting_after,
      limit: 10
    })
Enter fullscreen mode Exit fullscreen mode

In this example, it retrieves all the invoices issued in 2020.
It only processes the invoices with paid status by checking invoice.status and invoice.amount_paid.
An invoice data has invoice_pdf field, which is a URL to the invoice PDF.
Since downloading a PDF takes time because Stripe generates it on-demand, the script processes up to 10 items at once.
Then, it downloads to ./invoices/2020/. You can change it.

Hope that helps!

Top comments (2)

Collapse
 
luchodamico profile image
Luciano

Hello! Thanks in advance for this! Im wondering how do i run the script. Im working in macos and tried compiling with node but is not working. Can you guide me so i can test it with my account? Thanks again!

Collapse
 
lafaa profile image
Lafaa

Bravo!
There were a few adjustments needed to make it work on Windows (especially tricky was understanding I had to remove the single quotes in the wget command), but thank you!