Downloading and saving an invoice (or receipt) as PDF via the Intuit Quickbooks API

Into

This issue took me some hours to figure out, mainly due to not sending the correct request headers and not converting the response correctly.

The Intuit Docs

Getting an invoice (or SalesReceipt, or other document) from the Intuit Quickbooks Online Api should be easy, if you have the Invoice Id (which is different from the DocNumber displayed in the "No." in Quickbooks Online!).

Clojure (may help with Java too)

However, querying the correctly with clj-http and saving the pdf to disk can be quite tricky. The code sample below should work in most cases. Be sure to send your access token and the "Content-type application/pdf" header. Accept headers won't work. It's also essential to coerce the response to a stream with {:as :stream}.


(require '[clj-http.client :as client])
(require '[clojure.java.io :as io])

(defn get-qbo-invoice-pdf []

  (with-open [input (io/input-stream (:body (client/get "https://sandbox-quickbooks.api.intuit.com/v3/company/123456789101112/invoice/12345/pdf?minorversion=62" {:headers {"Authorization" (str "Bearer " <your access token>) "Content-type" "application/pdf"} :as :stream}))) ;;the last two items here are essential!
  
              output
              (io/output-stream (io/file "local_temp.pdf"))]
    (io/copy input output)))
	

This mess should of course be sliced and diced into smaller, reusable functions.

So in the with-open block vector you define both streams. {:as :stream} coerces the response into a stream, the usual string response from client/get is useless here. Then simply write to the open file with io/copy.

The stream coercion is achieved with this clojure code which should be easily adapted to Java code if needed:

(defn force-stream
  "Force b as InputStream if it is a ByteArray."
  ^InputStream [b]
  (if (instance? InputStream b)
    b
    (ByteArrayInputStream. b)))

Hope it helps.

Posted: 10 October 2021

comments powered by Disqus