412 errors on Xero API using Classic ASP

I started to see errors on our calls to the Xero API, with status 412 returned. The initial clue was in this Xero Community post.

On windows 7 machines or older machines, you may need to install a fix to ensure that windows can handle TLS 1.1 or 1.2. I usually recommend this update as a first step as it mitigates not knowing how the underlying code, wrapper may work.

This led me to a post about TLS versions, with a useful piece of code to check the TLS version of your server:

<%
Set objHttp = Server.CreateObject("MSXML2.ServerXMLHTTP.6.0")
objHttp.open "GET", "https://howsmyssl.com/a/check", False
objHttp.Send
Response.Write objHttp.responseText 
Set objHttp = Nothing 
%>

Running this I found the particular VM was showing TLS1.0, which is not good. I recalled I’ve seen something similar a few weeks ago on another project which was calling the Vimeo API, which now requires TLS1.2 or above.

This particular server is 2008 R2, and there’s a patch to upgrade your TLS to 1.2.

I used the Windows Catalog Update link in the article plus used the Registry EasyFix to sort the registry key out.

Now the TLS test code above is returning

"tls_version":"TLS 1.2"

And my calls to the Xero API are working.

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
url="https://api.xero.com/api.xro/2.0/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
Randomize
' build the parameters

params=""
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 https://www.example-code.com/asp/rsa_signWithPEM.asp 
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>"
Response.End
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>"
Response.End
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
url=url&"?"&params
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"
objXMLHTTP.Send() 
respText=objXMLHTTP.ResponseText
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)
Else
strHex = "%" & Hex(intAsc)
End If

strRetVal = strRetVal & strHex
Else
strRetVal = strRetVal & strChr
End If
Next

encode = strRetVal
else
encode = ""
end if
End Function

%>

Tin foil hats or a new way of life..

Twenty years ago the prime time television news started to report financial highlights to the masses, the “Aussie dollar is up against the greenback and the ASX is down 300 points”. Up until then there was no general consciousness in the broader population about the everyday status of the financial markets and systems.

That changed with the rise of compulsory superannuation and the float of Telstra in the mid-90s, all of a sudden ‘mum and dad’ investors emerged, through their super funds or Telstra shares they had a stake in listed Australian companies. Whilst the complexity of the financial system is really the purview of only a few, the masses felt a connection, and thus the media filled that demand – although I’d challenge you to find more than a small percentage of people on a suburban street who could explain the long term bond market, or the difference between going long or short on a stock.

Over the past few months there’s been a sense of deja vu with blockchain and crypt-currencies. Recently some media has been adding the price of Bitcoin to their financial reports – sparked almost certainly by the stratospheric rise (and subsequent fall) of the Bitcoin price. This despite only a tiny fraction of the population even owning crypt currency and an even smaller percentage who could explain crypto currencies and more importantly the technology which enables those currencies to exist.

So what is blockchain?

Blockchain is a technology concept to enable the creation and maintenance of a robust audit trail of transactions or data. By robust I mean it is not possible to compromise the integrity of the information, and the identity of people altering the data is definitive.

Here’s a simple accounting analogy. I’m old enough that my first management role was with a business that still kept its accounts on paper (yes, gasp). We didn’t have Quickbooks or Xero – in fact we didn’t even have computers on everyone’s desks when I commenced.

The accounts were maintained in a paper ledger, where we recorded credits and debits and a running balance. If we later discovered a mistake, we could go back up the columns of numbers, alter the incorrect item, then update each subsequent number that relied on the amended item. The obvious flaw here is that there are absolutely no controls:

  • anyone could change the numbers
  • there was no way of being sure numbers had or had not changed
  • nobody knew who had changed the numbers
  • if the building burnt down, or we were burgled, or I spilt my morning coffee on my desk, we would lose all records of our transactions.

Blockchain resolves all of those failings by introducing integrity, identity and secure storage into the process.

Blockchain enables you to maintain a register of data, where each change or transaction occurs in sequential order, and each change is constructed in such a way it relies on the preceding transaction to remain valid – hence the idea of a ‘chain’.

Blockchain means that if I go back up the chain of transactions and alter the data, every subsequent transaction that flowed from that transaction I amended will be rendered invalid.

Blockchain uses very strong encryption (SHA256 for those in the know) and private ‘key’ passwords to authenticate each person who adds to or amends the chain.

Blockchain uses a ‘distributed’ model, whereby copies of your chain are held on many computers around the world, with changes synchronising across the network, so even if a few computers fail, your data remains safe.

Who invented blockchain?

Pundits will tell you blockchain is going to revolutionise society as we know it, touching our lives eventually in a myriad of ways. So it’s rather astounding that the identity of the person who kicked all this furore off is not known. The origins of blockchain lie with a paper published by a Satoshi Nakamoto. Problem is, we have no idea who Satoshi Nakamoto is. At one stage some overenthusiastic media outlets tracked down a possible person in California, camped outside his home and plastered your TV screens with his picture. Turns out the poor chap was not Mr Satoshi Nakamoto, and had no idea what blockchain is. There’s also a perhaps opportunist Australian character Craig Wright who endeavoured to claim credit.

What’s the difference between crypto currency and blockchain?

Crypto currency is the first wide spread, publicly visible use of blockchain, because blockchain is very well suited to maintaining financial transaction information, as I’ve highlighted above.  But they are not the same thing. I can use Quickbooks to maintain my accounts in many different currencies. Blockchain is the ‘ledger’ system which enables a currency to exist.

At the end of the day any currency is simply a promise for an exchange of value. I give you $2, you give me a Mars Bar. Ever since the Knights Templar acted as the world’s first Western Union, we’ve used representations of value to enable transactions. Crypto currencies are just the next phase. Bitcoin currency is the most well known, but there are actually hundreds of crypto currencies which have popped up. There are many other uses for blockchain beyond currencies.

Should I retire to my fallout shelter?

No. Much as the emergence of the internet has fundamentally changed how we live our lives (although tough to explain to my children life before the net), or sequencing the genome is generating new understandings of human biology affecting many branches of medicine, blockchain has the potential to be another fundamental change to the way many things in our life work – even if it’s not immediately obvious to the average person on the street. You can skip the tin foil hats.

WordPress on Azure is really slow

I’ve recently set up a couple of WordPress blogs on Azure using the standard WordPress package from the Azure marketplace. Both times the sites have been really slow. The problem appears to be if you configure the database to a different region than the web app. Which is really easy to do because there’s no mySQL available on Australian Azure so you are told to pick the closest, which is SE Asia. Yet by default I always will add web apps onto Australian nodes. I thus wind up with the web app in Australia and the DB in Asia.

I experimented by spinning up a new WordPress with both the web app and database on SE Asia node – significant increase in speed.

Finally solved the mystery of why isNumeric doesn’t work how you might think

In Classic ASP there is the function isNumeric, which one uses to test if a value is a number. So for example:

Response.write IsNumeric("21")  // true
Response.write IsNumeric("David")  // false

Seems straightforward so far. Then you try this:

Response.write IsNumeric("15447D")  // true

Every so often this little quirk would popup. This morning it’s been driving me mad because I had a set of string values that were mixtures of alphanumeric, or numeric values. I wanted to discriminate between numbers and not-numbers and store the values in different columns in a database table. Using isNumeric though kept throwing errors – it tested 15447D as being a number, and of course once I tried to insert that into an integer column an error was thrown.

Finally figured how what’s happening after a little digging online. isNumeric also considers hex values to be valid numbers. 15447D is hex for 1393789. Now the lights go on – only wondered about this for 20 years.

Apparently this is also the case for octal values.

So a custom function along these lines is the answer:

Function myIsNumeric(ByVal Value)
	Dim regEx
	Set regEx = New RegExp
	regEx.pattern = "^(0|[1-9][0-9]*)$"
	myIsNumeric = Regex.Test(Value)
End Function

Configure FTPS on IIS7 for SSL file transfers

I’ll presume you already have an SSL certificate installed to your IIS web server and thus have your web site running with https://www.domain.com. This post is about how to enable SSL for your FTP connections to the server.

Set the SSL cert for your FTP server and site

  • Go to IIS Manager
  • Click the Server name at top of left tree and open FTP SSL Settings icon
  • In the SSL Certificate drop down select your certificate
  • Check the Require SSL connections
  • Expand the list of Sites in the left tree and select your FTP site
  • Open FTP SSL Settings icon
  • In the SSL Certificate drop down select your certificate
  • Check the Require SSL connections

Yes I know we’ve done this in two places, but it does appear to be necessary.

Set the Bindings for the FTP Site

  • Go to IIS Manager
  • Expand the list of Sites in the left tree and select your FTP site
  • Over on the right click Bindings…
  • Click Add button, add Type = FTP, IP Address = All Unassigned, Port = 21 and Host Name = http://www.domain.com (as in whatever your domain is)

The following ports work is because you need to specify and open the data ports.

Configure Firewall Ports

  • Go to IIS Manager
  • Click the Server name at top of left tree and open FTP Firewall Support icon
  • The Data Channel Port Range may be set already, if not make it ‘7000-7003’
  • Set the External IP Address of Firewall to the public IP of your server
  • Go to Windows Firewall
  • Add a new Inbound Rule, named something like FTPS for IIS
  • On the Protocols and Ports tab select Protocol Type = TCP, and Local Port = Specific Ports, with the range 7000-7003
  • Leave Remote Port  = All Ports

This allowed me to FTP into my server, with FTPS selected. Don’t forget because you have set the Bindings for the FTP site to your domain, the user name in your FTP program needs to be domain name and user name separated by a pipe, for example http://www.domain.com|userName.

 

How not to win friends (and customers) as a SaaS business

We’ve been giving Sodeco a try recently at ParentPaperwork, like most of these online tools there is a 14 day trial, after which you need to sign up for a subscription. At the end of the trial we decided not to subscribe, we were not really seeing a marked result, and we have a great deal of other social and content promotion work underway, so adding another tool to the list was only going to happen if we really could see value returned.

My problem arose when time came NOT to pay. A few days ago I received the usual prompt that my trial had ended and we needed to pay. Screen Shot 2015-04-24 at 10.42.04 am

Notice the wording in the email. I’m presented with two options – either I can subscribe, or my account will be deactivated. I’m not a big fan of web sites having my personal information unless really necessary, so ‘deactivation’ doesn’t really fit the bill for me. And given I’m in the SaaS game I’m presuming that leaves me open to ongoing marketing approaches.

So I go to Socedo and login, thinking I must be able to locate an option to delete my account.

Screen Shot 2015-04-22 at 6.07.48 am

Unfortunately not. After login a popup modal window opens presenting three subscription choices. I cannot close the modal, nor access my account information. I do not think this is acceptable, and I said so in an exchange with a Socedo support person. They deleted my account as I requested.

Socedo’s tactics are blatant, they are coming from the view that the best way to sell their product to a customer is to give the customer no choice. Companies need to recognise customers must be given choice – and one of those is not to purchase a product. Companies must also recognise that users must have the right to control their personal information. I will not do business with an online company that does not offer me the option to delete my account and the associated personal data they are holding.

I reckon Socedo would win many more friends with an option that says “Not Today Thanks”, if the customer selects this, then there’s a magic opportunity to re-orient the relationship into something that can still be ongoing. Perhaps “That’s fine, but we’d love to stay in touch, would you mind if we added you to our newsletter list. Oh, and would you mind telling us why you don’t want to subscribe at this time.”

One of our main challenges in SaaS (or indeed marketing any product) is understanding why a customer chooses not to purchase. If we are smart, we can see these customers as opportunities.

 

Using grunt-prompt with grunt-git

Screen Shot 2014-10-10 at 12.15.11 AM

I’ve become a big fan of Grunt in recent months, and now have all my current main projects set up with Grunt for linting, minifying, revving, pushing to Git etc. It means I have a nice smooth build and deployment pathway, and saves me heaps of time.

I’m using grunt-git to push to Github, and started with a default commit message along the lines:

gitcommit: {
   'src-master': {
       options: {
          verbose: true,
          message: 'Commit src-master <%= grunt.template.today("isoDateTime") %> \n',
          noVerify: true,
          noStatus: false,
          ignoreEmpty:true
       },
       files: {
          src: ['.']
       }
    }
 }

But that’s not much use because it means I’m not adding my own commit message describing whatever it is I’ve just changed. Nor do I have any outlet for frustration and thus no chance of making it onto commitlogsfromlastnight.com.

So then I came across grunt-prompt, which does just what you expect, it creates a prompt as part of your tasks so you can input something. Then everything came together nicely:

// prompt for a commit message
 prompt: {
    commit: {
       options: {
          questions: [{
                     config: 'gitmessage',
                     type: 'input',
                     message: 'Commit Message'
                     }]
       }
    }
 },
// commit changes to github
gitcommit: {
   'src-master': {
       options: {
          verbose: true,
          message: '<%=grunt.config("gitmessage")%>',
          noVerify: true,
          noStatus: false,
          ignoreEmpty:true
       },
       files: {
          src: ['.']
       }
   }
}

 

Git – ignore files starting with period / full stop / dot

I use Git on my Mac, but some repositories are on the Windows drive of my Mac. Mac creates system files in Windows for each of the files, for example default.html will wind up with another file in the same folder called .default.html. This is all a bit annoying in Git which picks up on these system files, even though I have no interest in then.

But you can exclude them. Edit your .gitignore file and add:

.*
!/.gitignore