What API designers could learn from the payments industry
We all know the Fallacies of Distributed Computing:
- The network is reliable.
- Latency is zero.
- Bandwidth is infinite.
- The network is secure.
- Topology doesn't change.
- There is one administrator.
- Transport cost is zero.
- The network is homogeneous.
I think there's a 9th: REST API designers know the meaning of the word Fallacy.
So to clarify and as a public service, we need to start talking about distributed computing facts instead:
- The network IS NOT reliable.
- Latency IS NOT zero.
- Bandwidth is limited.
- The network is not secure (you should know that already)
- Topology does change, and at the worst possible time.
- The administrator is dead.
- Moving bits around the net has a cost.
- The network heterogeneous.
Interesting enough, a very old protocol, ISO-8583, designed in the 80s to support slow 300 and 1200 bps dialup links is extremely aware of these facts and work around these problems with a very simple and asynchronous message flow.
Take a look any popular REST payments API, you usually call a method to authorize a transaction and you pass parameters like this:
- Card and Expiration (or a token)
- Amount, perhaps currency
- A description
Lovely, simple, and wrong!
Using that popular design, you POST the transaction and pray to get a response from the server. If you get a response, either a 200, 201, or even a 500 from the server, everything is alright, but if the request times out, or you go down, or the servers goes down, or the ISP is reconfiguring a router, you can't really tell what happened. If you're lucky and the server didn't receive your request, then that's fine, you can retry it, but if the server did receive your request, and authorized against its upper level acquirer, then you'll have an angry cardholder with a hold in its account, and perhaps even a debit (because I don't see many payment gateways accepting reconciliation messages or settlement files).
In ISO-8583, when you send an authorization request or a financial request and you don't get a response, you queue a reversal in a persistent store and forward (SAF) queue. So the next time you contact the server, before sending a new transaction, you send that reversal. If you receive a response from the server, but the response comes late and you have already timed out, you also send a 'late-response' reversal to the server.
In the same way, when you post a transaction that already took place (i.e. adjusting a previously approved transaction for a different amount, something that happens all the time at restaurants that support tips, or gas pump transactions where you approve for $100 but then complete for $20), and you don't get a response, you send a retransmission of the same transaction, as many times as necessary in order to deal with transient network problems.
In order to support reversals and retransmissions, you need a transaction unique identifier. Different networks use different transaction identifiers, either the Terminal ID + a Serial Trace Audit Number, or a Retrieval Reference Number, or in ISO-8583 v2003 the new Transaction life cycle identification data, the client generates a unique identifier so that when you send a follow-up transaction, you can send the server a reference to the original one.
I believe all payment APIs out there (including those super very cool ones) should consider adding three things:
A new parameter, RRN
Client code could generate a UUID and use it as the RRN (Retrieval Reference Number)
Support reversals (DELETE)
Could be as simple as adding a new verb, DELETE, where the only parameter can be -- along with authentication data -- the RRN
Support for retransmissions (PUT)
If your initial transaction was a POST, I propose that you also accept a PUT, with the same parameters. On the server side the difference between the POST and the PUT operations would be just an extra step to make sure that you didn't process the original POST, and return the original auth code if that was the case.
Of course, if you're designing a new super cool minimalistic REST API you probably don't listen to people with grey hair, but just in case, my 2c :)