Reading data from Xero API with Classic ASP

It is possible to read data from the Xero API using Classic ASP, there’s little online at all to help you, other than a couple of cryptic hints, including this post which was the start of my adventure. Only problem being it refers to a Git repo which the files have vanished from.

Before you proceed, please note, you must have the Chilikat activeX COM installed – which I’d heartily recomend anyway, it’s the Swiss Army Knife for Classic ASP development, I’ve used Chilikat for everything from reading email from Gmail accounts, to reading/writing CSV files, to encrypting/decrypting data.

Let’s walk through the steps.

Create a public/private key pair

You’re going to need a public/private key pair, Xero has good instructions on creating these. I’m on a Mac so I can use openSSL, you can skip the creation of the pfx file as we only need the cer and the pem files.

openssl genrsa -out privatekey.pem 1024
openssl req -new -x509 -key privatekey.pem -out publickey.cer -days 1825

You’ll need to place the PEM file somewhere that can be read by your ASP code, so it’ll need to be in a folder on your server. The cer file you’ll need when registering your application in Xero.

Register a Xero Application

I’m working with a Private Application in Xero. First step is to register your application – note I already had a Xero account, but Xero does give you the ability to create a test account.

As part of registering your account you will upload your cer public key file. Once the application is registered you’ll see a Consumer Key value – grab that, you will need it later. Ignore the Consumer Secret, we’re not using this.

Call the Xero API

Here’s the code I use to obtain a list of all the invoices in my Xero account. I’ve tried to add full comments to explain what is happening.

My starting point for my adventure was a post in the Xero Community ‘Has anyone managed to get the API working using Classic ASP?‘. And my thanks to Ben Snape who even though his ASP solution had vanished from Github, still pointed me in the right direction, and I’ve borrowed his encoding and timestamp functions.

Quick explanation of a couple of things:

  • The nonce value is a one time number, basically you just need to send a unique number each time. I’ve gone with a simple random number.
  • The timestamp is a standard epoch number – just remember to shift your local time to UTC before generating, I’m 11 hours ahead of UTC at the moment on my local machine so you’ll need to adjust to suit your server (My production servers are UTC, I’ll tweak my code eventually to cope with the fact it’s UTC on production but not UTC on my local).
  • It’s is correct you use the consumer key for both the oauth_consumer_key and oauth_token parameters.
Dim consumerKey,url,respText,params,crypt,baseSignature,pkey,success,rsa,signature,pkeyXml,hexSig
dim objXMLHTTP

' URL to get a list of my invoices
' your consumer key provided by Xero when you register your application
consumerKey = "[Your Xero consumer key]"

' we need a random number (or a unique number) for every call - that's the nonce value
' build the parameters

params = params & "oauth_consumer_key="&consumerKey
params = params & "&oauth_nonce="&int((9999999-99999+1)*rnd+99999)
params = params & "&oauth_signature_method=RSA-SHA1"
params = params & "&oauth_timestamp="&makeTimeStamp()
params = params & "&oauth_token="&consumerKey

' this is the string we will make a signature from
baseSignature = "GET&"&encode(url)&"&"&encode(params)
response.write baseSignature&"<hr>"

' Load the private key from the RSA PEM file
' Using example from 
set pkey = Server.CreateObject("Chilkat_9_5_0.PrivateKey")
success = pkey.LoadPemFile(server.MapPath("keys/privatekey.pem"))
If (success <> 1) Then
Response.Write "<pre>" & Server.HTMLEncode( rsa.LastErrorText) & "</pre>"
End If
' Get the private key in XML format
pkeyXml = pkey.GetXml()

set rsa = Server.CreateObject("Chilkat_9_5_0.Rsa")
success = rsa.UnlockComponent("[Your unlock code]") 'important, need to unlock
' Import the private key into the RSA component
success = rsa.ImportPrivateKey(pkeyXml)
If (success <> 1) Then
Response.Write "<pre>" & Server.HTMLEncode( rsa.LastErrorText) & "</pre>"
End If
' we want a base64 output NOT hex
rsa.EncodingMode = "base64"
rsa.LittleEndian = 0
signature = rsa.SignStringENC(baseSignature,"sha-1")
Response.Write "<pre>" & Server.HTMLEncode( rsa.LastErrorText) & "</pre>"
response.write "signature="&signature&"<Hr>"

' add the signature to the parameters
params = params & "&oauth_signature="&encode(signature)
response.write "params="&params&"<Hr>"

' add params to the url
response.write url&"<hr>"

' fire this at Xero
set objXMLHTTP = Server.CreateObject("Msxml2.ServerXMLHTTP.6.0")
objXMLHTTP.Open "GET", url, False
objXMLHTTP.SetRequestHeader "Content-Type","application/json"
response.write respText&"<hr>"
set objXMLHTTP=nothing

Function makeTimeStamp()
Dim dteFrom : dteFrom = "01/01/1970 00:00:00 AM"
Dim dteNow : dteNow = Now()
dteNow = DateAdd("n", -(11*60), dteNow) ' shift back 11 hours to UTC
makeTimeStamp = DateDiff("s", dteFrom, dteNow)
End Function

Public Function encode(s)
if s <> "" then
Dim strTmpVal : strTmpVal = s
Dim strRetVal : strRetVal = ""
Dim intAsc : intAsc = 0
Dim strHex : strHex = ""

Dim i, strChr : For i = 1 To Len(strTmpVal)
strChr = Mid(strTmpVal, i, 1)

If InStr(1, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~", strChr) = 0 Then
intAsc = Asc(strChr)

If intAsc < 32 Or intAsc > 126 Then
strHex = encodeURIComponent(strChr)
strHex = "%" & Hex(intAsc)
End If

strRetVal = strRetVal & strHex
strRetVal = strRetVal & strChr
End If

encode = strRetVal
encode = ""
end if
End Function


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s