Sending a Xero invoice by Fax with Phaxio

Riley James
9 min readNov 15, 2017

--

“Can you send me that via fax” are not words I have ever heard. But… supposed one day I do? Well FINALLY! I have the solution.

Yes, faxes are very niche, but they still get used. It’s not something that most businesses ever need to think about, and it’s not a feature on many tech product roadmaps. Which is why it’s great to know that something like Phaxio exists. If your business is one of the few that needs to frequently (or at least regularly) send faxes, you can build the feature yourself.

While I chose to build an integration to a fax machine, this guide could also be extrapolated to: “Connect Xero API to XXX API via AWS Lambda”.

So read on if you’re thinking about it, you’ll likely run into the same issues.

What did I use?

Why did I choose to use Lambda aka ‘serverless’:

  • Because serverless is so hot right now, and I wanted to experience it firsthand. Particularly after seeing Neil Grooby & Jordan Walsh build their XD Hax 17 projects in the same manner
  • The projects goal was to connect two API’s, which seemed to suit a serverless infrastructure, ie: no database, disk storage, no state to maintain, no message queues. It’s one simple function.
  • I’d heard a lot of good things about Python and wanted a chance to play with it. And it was a pleasure.

Show me the code:

Here is it, one function. You can view the full repo on github, but the gist below holds all the important stuff. The rest of the repo is related to Python Lambda.

The code is commented, so you should be able to read through it with ease.

Lambda functions are a single function, in this case handler. Our handler function handles a single argument, invoice_id.

I’m not going to reiterate what the code above does, the comments pretty clearly explain that. What I will go into, is how you can get it working for you.

Note: I’m using OSX (mac) and assuming you have Python (2.7, mac default) & Pip installed already & an AWS Account.

Step 1: Clone the repo and setup python

git clone https://github.com/rjaus/XeroPhaxio XeroPhaxio
cd XeroPhaxio
mkvirtualenv XeroPhaxio
pip install python-lambda
pip install pyxero
pip install phaxio

Step 2: Create a Xero Private App:

Xero Private App’s require a public/private key to be generated

mkdir keys
cd keys
openssl genrsa -out lambdaprivatekey.pem 1024
openssl req -new -x509 -key lambdaprivatekey.pem -out publickey.cer -days 1825

You can now visit the Xero Developer Portal, sign in and create your Xero Private App (connect to your org, or the Demo Company). You will upload the publickey.cer file you just generated.

Now add your newly generated Xero Consumer Key to your config.yml file.

Step 3: Create a Phaxio account

Sign up for an account at Phaxio and create a set of API keys, put them in the config.yml file.

You can create live or test keys, I’m assuming the test keys do not actually send any faxes. If my assumption is wrong, my mother (only person I know with a non-mobile number) has been receiving a lot of fax calls. Sorry mum.

sorry mum

Step 4: Create an AWS User

Login to the AWS Console, head to IAM, then create a new user for programatic access. Put your newly created account key & account key secret into the same config.yml file.

Now you have everything setup locally:

You can try our your lambda function using Python Lambda, simply

lambda -v invoke

Your function will be run locally, you will get some output:

(XeroPhaxio) mac123:XeroPhaxio riley.james$ lambda invoke -v{'body': 'Fax Sent Successfully', 'headers': {'Access-Control-Allow-Origin': '*', 'Content-Type': 'application/json', 'Access-Control-Allow-Credentials': True}, 'statusCode': 200}execution time: 11.91537404sfunction execution timeout: 15s

It works!!

Now it’s time to deploy it to AWS Lambda: Easy.

lambda deploy

Unless of course you’re using a non-linux machine. Then you’ll need to spin up a docker image in order to deploy. See instructions at the end.

This takes a while. What it’s actually doing is installing all the included packages with Pip, putting them in a zip file with your code, and then uploading it to AWS Lambda.

So now you have a lambda function, and you can create and run test events against it via the AWS Lambda UI.

Your lambda function should look similar to this

Chrome Extension

Now we need to make this a little more usable. We’re going to create a Chrome Extension to call our lambda function. Problem: You can not call a lambda function directly, so we need to setup an API Gateway to expose our Lambda function to the internet.

The Chrome extension is relatively simple, but it gets the job done.

All the rest of the functionality is within the Lambda function. The repo for the chrome extension is available here.

To install the chrome extension, firstly you need to be using chrome or chromium as your browser. If so, follow these instructions:

  1. Clone the repo, or download as a zip.
  2. Navigate to your extensions: chrome://extensions/
  3. Check “Developer mode”
  4. Click “Load unpacked extension” and select the folder you downloaded your extension to.
Success! Extension has been installed.

Setup an API Gateway:

Now, the final setup step is to create an API Gateway and connect it to your Lambda function.

Tip: Do not create your API Gateway as a “Lambda Proxy Integration”, this will not support CORS headers, and therefore you won’t be able to send requests to it via the Chrome extension. If you take a look at the gist at the start of this blog post you’ll notice the CORS headers are actually added within the Lambda function itself, not via the API Gateway.

Step 1: Visit the AWS API Gateway setup.

Step 2: From the ‘Actions’ menu, select ‘Create a resource’ (I chose /Phaxio) and ensure you check “Enable API Gateway CORS”

Step 3: Select your new resource, and from the ‘Actions’ menu, select ‘Create a method’. Select ‘POST’ as the method type. Select “Lambda Function” as your integration type, select the region of your lambda function and then start typing the name of your function in the box provided. It should auto-complete to match your function. If not, maybe you selected the wrong region?

Step 4: Deploy API

From the ‘Actions’ menu select ‘Deploy API’. This will create a stage, default ‘prod’, and generate a url via which you can invoke your API.

Optional Step: Create an API Key

From the menu on the left, below your API Gateways, select ‘API Keys’. Create an API Key, it will require a few steps, setting up a usage plan, etc. Once you have successfully generated an API key. Go back to your API Gateway and select your POST method. Select ‘Method Request’ and you will presented with some more options. Here you can set ‘API Key’ required to TRUE, which will now require the an API Key to be presented in the header of the request in order to successfully make request to this method.

You will need to redeploy your API after updating this method.

Lets try it out!

  • Login to Xero
  • Create a new Contact, with a fax number.
  • Create an AccRec Invoice for your new Contact
  • Send the fax
Yo Fax, let me introduce you to cloud accounting

And now, a fax machine somewhere is receiving and printing your invoice.

What’s actually happening?

Diagram built with Draw.io
  • Chrome extension extracts the invoice_id and sends a post request to the API Gateway.
  • API Gateway passes the request to our Lambda function
  • Lambda function makes request to Xero’s API to retrieve the invoice, validate a fax number exists and retrieve the PDF copy of the invoice
  • Lambda function sends the PDF copy of the invoice & the fax number to Phaxio’s API
  • Phaxio queues a fax to be sent to the number provided

Lets review:

Creating a lambda function, with Lambda Python, and deploying it was relatively easy. The more time consuming part was putting all the other bits together to make it useful: API Gateway, Chrome Extension. Going serverless required a lot more work that I expected, but now that the hard work is done, there is nothing to worry about.

Using Python Lambda:

This library makes life super easy to get stared with python and lambda. It sets up your project as required for AWS lambda, allows easy local execution of your lambda function and deploys to lambda in a single command.

It does have a few limitations:

  • Seems most suitable for a single function. You could have lots of folders/projects but then deploying would become a pain
  • It’s most suitable for Linux based development. As it compiles / packages locally and then uploads to lambda. If you’re on Mac or windows you will need to compile packages in docker before deploying to lambda.

For bigger projects it would be worth looking into something more comprehensive such as Chalice, Zappa, Gordon. One good reason to use Python Lambda tho is the fact that it doesn’t do everything. You need to setup the API Gateway, VPC, subnets, etc, manually. Which can be painful, but does lead to a better understanding of what is actually going on behind the scenes.

Lambda & files on disk

When initially scoping the project, I deemed I wouldn’t need to store anything on disk. As the project developed tho I ran into two cases where I would need to store something. 1. The Private Key file for the Xero Private App, 2. A copy of the PDF invoice, in order to send to Phaxio. Number 1 is unfortunately unavoidable, number 2 could have been avoided by making changes to the Phaxio python library. Both could be resolved by storing these files in an S3 bucket, but adds another service to picture, more complexity. That’s when I discovered that lambda functions have 512mb of ephemeral (temporary) disk storage. And you can include files (such as the private key file) in your lambda package and it will be available to the function (obvious in hindsight).

Non-Linux Users: Using Docker to compile packages:

The first time I used lambda-python was on ubuntu, so there was no need to use docker to compile packages. Simply running lambda deploy retrieved all the python packages for linux, packaged them into a zip and deployed them to AWS Lambda.

If you’re using Windows or Mac, you’re going to start seeing invalid ELF headers errors when running your lambda function on AWS. This is due to the packages being compiled locally on your windows/mac machine not being compatible with AWS Lambda environment (running linux).

Two solutions:

  1. Use a linux box
  2. Use docker to spin up a linux image and deploy from it.

For option two, I’ve taken notes from these two guides.

  1. Install docker
  2. launch a docker container with:
docker run -v /path/to/your/code/on/local/machine:/working -it lambci/lambda:build-python2.7 bash

Now inside your docker instance, move into the working directory you setup, install your python libraries with pip.

cd /working
pip install python-lambda
pip install pyxero
pip install phaxio

Now you’re ready to deploy with python-lambda

lambda deploy

And that’s it.

If you have questions, or something isn’t clear. Write a response or tweet XeroAPI

--

--

Riley James
Riley James

Responses (1)