My First Payment Integration: my Pain Points

Integrating payments into an application is difficult. With considerations such as PCI compliance, documentation, support and recovering from failed transactions a developer has a lot to keep them up at night. This is especially hard for inexperienced developers looking to integrate an application for the first time.

As a recent graduate (2016) I had no experience with payment integrations when I started working with The Payment Works as a software engineer. But one of my first tasks was to build our Demo Shop, a tool where developers can compare checkout flows with a real Payment Gateway side-by-side with a Sim from our service, Testing Pays. In this case, the Payment Gateway we chose was Realex, which meant I had to integrate with their API. This gave me my first taste of working with payments and fresh insight into a Payment Gateway. I was lucky that this was a greenfield project so I had the flexibility I needed to change the project as I went along and learn from my mistakes. And I had many lessons to learn.

The Start

Project setup went smoothly, I set up an ember.js app for the frontend (no, not react, you read that right) and used a very simple php API with our own fork of OmniPay Realex for the backend.

The Pain

Getting Realex taking payments was the next step and not as simple as I had originally thought it would be. I spent half a day trying to get Realex to accept my request, as it would always end up returning validation errors which were at times less than helpful. At one point I was battling with a “bad request” error from Realex's sandbox - not much use in that message - so switched to our own Realex API Sim in order to find out which params were at fault.

The Testing Pays sim telling me exactly what was wrong with my Realex request

The Testing Pays sim telling me exactly what was wrong with my Realex request

Immediately the error stood out, turns out I had made an error when padding out the amount field. This little error had cost me a lot of time but thankfully had a very simple fix. Note that in order to gain the experience of working with a real payment processor, I tried to avoid using our own Realex Sim. Turning to it only if there was an issue I couldn’t figure out or the Realex system was taking far too long.

Now with the Payment Gateway finally taking payments there was just one more step. Resolving validation errors with the form and handling different types of error responses. The first turned out to be the easiest by far (highly recommend ember-credit-cards for building payment forms with ember.js) .

Handling potential errors was much more difficult. Firstly I had to go hunting for the Realex error codes (which I found here) and then match those codes to the expected functionality. Once I knew what the error was I could attempt to retry the payment or give the user ways to resolve the problem. My issue was I could only get 5 different responses from Realex’s sandbox.

Realex, much like Stripe relies on using credit card numbers for testing. Generally you will fill in the payments form and use a given credit card number. Depending on the credit card number you will get back a certain response. Both Stripe and Realex use this method in order to provide a way of testing your payment forms. However this involves a lot of copy and pasting between your form and their website/file. You also have to manage a list of card numbers and the type of card they belong to (Visa, MasterCard, American Express etc). I found this quite tedious and at times confusing.

As a result once I had covered the 5 responses (one of which gives a "success" response) provided by the Realex test cards, I switched over to our sim to do the rest. Yes, I wanted to try and integrate the whole thing without the simulator but there was no easy way to recreate the responses with the form bar manually setting them off. But even then some responses I couldn’t duplicate manually, such as a Declined Pending Offline Validation error. So I turned to the Simulator for the sake of speed.

Working with the simulator I am able to pass in a cent value and retrieve a particular response based on that. For example:

  • I pass in 0 cent (€xx.00) to get a success Realex response (Auth code=00; "The transaction has processed and you may proceed with the sale") .
  • I pass in 40 cent (€xx.40) to get a Realex failure due to a bank communication error (Error code 200; "bank communication error")
  • I pass in 31 cent (€xx.31) to get a Realex failure due to specific card condidations (Error code 102; "Transaction Declined Pending Offline Authorisation")

This made life way easier for me since I didn’t have to re-create the error on the form or build a set of mocks using tools like, or Instead I was able to leverage a complete set of responses and watch the requests hit the sim in real-time our Live Tail feature.

From here it was a simple case of wiring up the responses to the given instructions, debugging, debugging and more debugging. Luckily integrating with TestingPays was as simple as changing the URL I was pointing at so no major issues there.

Take Aways?

Integrating a Payments Gateway was definitely more difficult than I had originally thought it would be and luckily I had expert advice from colleagues if needed (The Payment Works... guess what we do?). So my experience was not as rough as it could have been. And I will say that some Payment Gateways particularly Stripe, which I worked on as my next assignment, definitely provide a much smoother learning curve. However error handling and error recovery is still a nightmare when it comes to payments, even with Stripe (the gold standard for documentation). Documentation on this area is especially lousy, and testing on gateway sandboxes is very, very limited and frustrating. Not to even mention the lack of information around standards and best practices - my colleagues advice really helped me out there.

I hope you found this post helpful or even a little encouraging if you were in a similar situation as I was . If you have any questions or queries feel free to drop me a question at Until next time.