Debugging issues with payment gateway integrations in development environments

Today's the end of a financial year (atleast here in India), so I thought I should write a post related to this sector. Now considering that I have a hardcore technical background, it shouldn't come as a surprise if the post rather gets techno-financial. So here's how it goes.

If you have ever worked on an eCommerce product or platform and have written custom code to provide integration with any Payment Gateway, you would know its a pain to write code to integrate with a Payment Gateway, and subsequently having to modify, adapt or debug it after it has been pushed to production.

More than the code or its complexity to integrate with the gateway, the setup of a suitable environment where you can write and debug code for a gateway is a bigger challenge. Most Payment Gateways I have seen (especially those that accept Credit Cards, NetBanking etc) lock down which payment requests are accepted in various ways (for security purposes) often including these constraints:

  1. Payment gateways typically allow payment initiation from a particular domain only, you normally need to submit the domain name to them as part of the setup process.
  2. In most of the cases, the absolute url from where payments can be initiated are also fixed. Some gateways allow only a single initiation url while others allow upto 3 (or sometimes more) initiation urls.
  3. The return url to which the user's browser is redirected back is typically also fixed and has to be submitted to Payment Gateway as part of the setup process.
  4. If your payment gateway features server to server notifications (where the gateway invokes a url directly back to your server), this notification url also is fixed and submitted to gateway during setup.

I would exclude PayPal from this discussion, the interesting thing is PayPal does not enforce any of these constraints (in fact all of these are configurable via the form parameters in payment request itself), and yet PayPal is one of the most secure (and successful) payment gateways out there. But these constraints apply to almost all other major Payment Gateways I have coded for, including CitBank's payment gateway, MasterCard's gateway, BillDesk and TechProcess etc. (the last 2 being significant players in India).

Now each of these constraints poses a problem when you start coding for a gateway, and more so, after you are in production and you need to debug issues or make changes.

Hard-coded domains and/or urls for payment requests means you cannot use localhost for development, because as soon as you initiate a payment request from localhost, you are bound to see error screens from the gateway telling you your request cannot be processed and most would give you a cryptic error code too.

Then hard-coded return urls further mean if you are somehow able to initiate payment from your local development environment, you would be redirected to production site when you would return from the gateway back to your site.

And you practically cannot do anything about this (i.e. somehow make those urls configurable). The only way to change those urls is a length bureaucratic process requiring you to alert your client and request such changes. The client would further ping (most often raise a support ticket with) the payment gateway. The gateway personnel would trigger their internal processes which would most often come back to you for verifications. Sometimes you would need to get escalations. All in all, the entire process could be anywhere from a couple of days to multiple weeks, definitely not the amount of time developers like to keep waiting for making their changes.

For the purpose of this blog post, I would show you how to bypass the 3 restrictions mentioned above so you can write/change code for payment gatway integrations in development environments without needing to have any portion of the production setup changed. I would enumerate the 3 conditions we would bypass:

  1. Pre-defined initiation domains for payments
  2. Pre-defined initiation urls for payments
  3. Pre-defined return urls from payment gateways

The fourth constraint mentioned earlier (pre-defined urls for server-to-server notifications) is something you cannot do much about unless you have a way to distinguish between payments initiated from development and production environments using parameters sent by the payment gateway and you can put a proxy or create a routing rule in front of your production site's box that would route notifications for test payments automatically to your dev environments, something which is well outside the scope of this blog post and won't be discussed further.

I would further discuss 2 scenarios for the development environment:

  1. One where the dev environment is an exact replica of production
  2. Another where the dev environment only contains bits and pieces of production and is not an exact replica.

But first, let's analyze the 3 constraints listed above and see how the payment gateways are able to enforce these rules.

It might come as a surprise to you that the only way payment gateways can enforce these rules is via information sent by your browser in a HTTP request, specifically the Referer/Origin Http headers. As a web developer, you would know that when you switch between pages in your browser by clicking a link on the current page or by submitting a form on the page, your browser would send the url of the current page to the target location of your clicked link (or submitted form) in a Http header. Gateways would compare the browser-reported Http Referer header with the domains/urls submitted to them for your particular setup and based on the match would accept/reject a payment request.

As a web developer, this should ring a bell to you. Anything that is submitted by a browser to a server can be manipulated and changed in various ways and at various points.

So let's now discuss the 2 dev scenarios one by one:

1) Your dev environment is an exact replica of production
Let's assume your production domain is www.mydomain.com and you are developing on a local machine using localhost domain. The rest of the details (specifically the exact urls) are insignificant here as server-relative urls would be identical on production and your development box.

Now the first thing you need to do is make your local machine respond to ww.mydomain.com, which is actually very simple than it sounds. If you are on Windows, open your hosts file in a text-editor (C:\Windows\System32\drivers\etc\hosts) and add this line towards the bottom of the file:

127.0.0.1 www.mydomain.com

If on Linux, please do the same in /etc/hosts file (this is the typical location of this file on Linux distros, please consult your distro documentation if you can't find it here). Please consult your OS documentation for this file if you are using another OS, like Solaris or OS X.

Next please create a virtual host/website in your web-server software with the Host name set to www.mydomain.com and pointing to code on your local disk. Here's a screenshot for this on IIS 7.x:

IIS 7.x production domain pointing to local code

On Apache, you can create this vhost:

<VirtualHost 127.0.0.1>
    ServerAdmin webmaster@www.mydomain.com
    DocumentRoot "D:\Projects\Blog\WebTest"
    ServerName www.mydomain.com
	<Directory "D:\Projects\Blog\WebTest">
		Options Indexes FollowSymLinks
		AllowOverride All
		Allow from all
	</Directory>
</VirtualHost>

And that should be it, you are all set (you can search on how to do this if you are using another web-server software). For all purposes, your local machine is now www.mydomain.com when accessed from any browser from your local machine only and you can use your favorite development IDE to run, debug and step-through your payment integration code. The beauty of this set-up is when you return from payment gateway and the gateway sends you to a url on www.mydomain.com (via your browser), it would still be your local code that would be executing because www.mydomain.com is your local system for your browser due to the host entry created earlier.

Please note if your production site is set to use HTTPS for payments (which would be true in more than 90% of the cases), there's an additional step of configuring a certificate for www.mydomain.com on your web-server software. This is pretty easy, please search the web (or leave a comment below), if you need assistance doing so for your web-server.

That's it for scenario 1), let's turn our attention now to the more complex and challenging scenario 2), where you don't have a replica of production.

 

2) Your dev environment is significantly different from production setup
This can happen for a variety of reasons, I would simply put forward one such scenario I faced.
A year and a half back, we inherited legacy portal for ICSI, that we were supposed to revamp and re-develop. However we were supposed to immediately cater to the payment issues on the portal where a majority of payments did not complete successfully.

Due to various regulatory and technical issues, we did not get a copy of the portal running on live domain. All we had was source code for only those pages from the portal that initiated payments and handled users returning from payment gateway and we were expected to fix any issues with these files.

More than anything else, the challenge was to create a new mini-portal locally, mimicking the production workflow scenario, which included sending exact payment initiation urls as Referer to payment gateway and handling redirection back from the gateway.

After some thought, I was able to devise a way to make this work using a proxy between my browser and web-server that would modify reqeusts to make them appear genuine to the gateway and and modify responses to make browser behave the way I needed for testing and debugging. Due to its extensibility and ability to control requests/responses via a variety of rules, I chose Fiddler as my proxy tool to accomplish this.

To avoid ambiguity, I would list sample urls here that we were working with at that time:

  1. The production initiation url for payments was:
    https://www.mydomain.com/StudentMemberPages/ICSIPaygate.aspx
  2. The payment gateway url was:
    https://www.citibank.co.in/servlets/TransReq 
  3. Our local url where we were testing fixed versions of the code was:
    https://localhost/DesktopModules/Icsi/Payment/CitiPayment.aspx
  4. This is a Citi Payment gateway example and you would know Citi payment gateway uses same url for payment initiation and return back from gateway.

I started by setting up a HTTPS certificate locally for localhost. Thereafter I opened Fiddler and ensured it was set as system proxy while it was running.

Now the next thing I needed was when a request is sent to CitiBank, I needed to set a pre-defined Referer header no matter the url sent by browser. For this, I added this rule in OnBeforeRequest method in Fiddler Script (I am assuming you know how Fiddler rules work, if not please check this page first):

 

{syntaxhighlighter brush: csharp;fontsize: 100; first-line: 1; }static function OnBeforeRequest(oSession: Session) { //CitiBank if (oSession.HostnameIs("www.test.citibank.co.in") || oSession.HostnameIs("www.citibank.co.in")) { oSession.oRequest["Referer"] = "https://www.mydomain.com/StudentMemberPages/ICSIPaygate.aspx"; oSession.oRequest["Origin"] = "https://www.mydomain.com"; } }{/syntaxhighlighter}

This rule would ensure Citi gateway sees the request as originating from the valid initiation url.

Next I needed rule(s) which would execute my local code when control returns from Citi gateway back to www.mydomain.com. For this, these 2 rules were needed:

 

{syntaxhighlighter brush: csharp;fontsize: 100; first-line: 1; }static function OnBeforeRequest(oSession: Session) { //This rule makes the browser to send request to localhost domain during return from payment gateway if (oSession.isHTTPS && oSession.HostnameIs("www.mydomain.com")) { oSession.hostname = "localhost"; } //This rule changes the path of return url to your desired local url if (oSession.url == "www.mydomain.com/StudentMemberPages/ICSIPaygate.aspx") { oSession.url = "localhost/DesktopModules/Icsi/Payment/CitiPayment.aspx"; } }{/syntaxhighlighter}

And when I placed all these 3 rules together inside OnBeforeRequest method, I was able to execute an end-to-end workflow for testing Citi payments on my local machine.

 

I will take another example here (a more complex one) where initiation and return urls are different (the example is for TechProcess payment gateway):

  1. The production payment initiation url was:
    https://www.mydomain.com/StudentMemberPages/TechProcessPayment.aspx
  2. My local payment initiation url was:
    https://localhost/DesktopModules/Icsi/Payment/TechProcessPayment.aspx 
  3. The payment gateway url was:
    https://www.tpsl-india.in/PaymentGateway/CheckGatewayEnter.jsp
  4. The production return url was:
    https://www.mydomain.com/StudentMemberPages/TechProcessAckPrint.aspx
  5. My local return url was:
    https://localhost/DesktopModules/Icsi/Payment/TechProcessPaymentReturn.aspx

Here are the rules to accomplish a TechProcess payment workflow on local machine (in a single function, please see comments in code below to understand what each rule does):

 

{syntaxhighlighter brush: csharp;fontsize: 100; first-line: 1; }static function OnBeforeRequest(oSession: Session) { //TechProcess //This rule modified the request to make gateway believe it is originating from the actual source if (oSession.HostnameIs("www.tpsl-india.in")) { oSession.oRequest["Referer"] = "https://www.mydomain.com/StudentMemberPages/TechProcessPayment.aspx"; oSession.oRequest["Origin"] = "https://www.mydomain.com"; } //Both rules below modify return from payment gateway. //This rule makes the browser to send request to localhost domain during return from payment gateway if (oSession.isHTTPS && oSession.HostnameIs("www.mydomain.com")) { oSession.hostname = "localhost"; } //This rule changes the path of return url to your desired local url if (oSession.url == "www.mydomain.com/StudentMemberPages/TechProcessAckPrint.aspx") { oSession.url = "localhost/DesktopModules/Icsi/Payment/TechProcessPaymentReturn.aspx"; } }{/syntaxhighlighter}

 

I know this blog post is getting rather lengthy (and doesn't contain enough images to make a more engaging read). So I would try to wrap it up here believing enough pointers have been provided to enable you to customize your environment for wirting/modifying code for payment gateway integrations.

If you feel a section of this post needs more elaborate explanation or your scenario is not quite covered here and you are not still not sure how to setup your enviornment, please feel free to leave a comment below, and I would either try to update the post to incorporate your scenario or otherwise reply to your comment directly.

 

Comments

Nice post! I appreciate you for releasing this blog on the right time . And thanks for the modifying code for payment gateway integrations. It's of great piece of help/information.

Glad to know that payment gateways typically allow payment initiation from a particular domain only, you normally need to submit the domain name to them as part of the setup process.

Well, a website develop must be aware of debugging the issues with payment gateway. This post will help the beginners to learn the technical aspects more clearly. Thanks!