I started this article as an entry in the 2013 Azure Challenge, but changes in my schedule have lead me to drop out of the competition, leaving the article well short of its originally intended length and content. This latest release of the article serves just to clean up its current offering; hopefully transforming it into a standalone article on the subject of PCI-compliant Payment Processing in Javascript. The technique described is very applicable to Azure websites and other cloud service, but I never got to that integration. I will try to get back here and augment this article as soon as my schedule clears.

This article will demonstrate a client-side, out-of-scope, payment processing technique that is available to Windows Azure programmers.

At first glance, collecting credit cards on websites appears pretty simple. Present the necessary input fields on your website in an HTML form; then post the collected information back to your server-side code which passes it on to a bank's gateway in return for lots of money. In a perfect and honorable world, that might be all that is required, but in the real world, handling credit cards is a lot more complicated.

The Payment Card Industry Data Security Standard (PCI DSS) governs the transmission of debit and credit card information. Websites, Website Servers and Database Storage Facilities must be PCI compliant before they are allowed to handle or store cardholder information. There are currently 12 requirements and an expensive audit involved in becoming compliant. Fundamental to the concept of PCI compliance is the edict that from the moment of collection on, no non-compliant server or database may even touch the card holder information. If one does, the entire process becomes invalid.

Azure itself is not PCI compliant. Although incredibly secure both physically and digitally, its data centers do not possess all of the necessary certifications yet. As a result, your website cannot process credit cards under the PCI guidelines, if the azure servers ever touch the collected cardholder information... directly.

This article will demonstrate a client side technique for handling and persisting cardholder information without ever letting that information post back to Azure. Rather, JavaScript in the browser will post the sensitive information to a PCI Compliant data-vault, outside of the Azure network. The data-vault will return a token in its reply, representing a contract between a single card holder and a single credit card merchant. This token can only be used to move money between those two parties. It contains no card holder or merchant account information itself, nor can it be used to access such information from the data vault. By definition, sensitive information goes into the data-vault and never comes back; and the tokens which are returned are extremely limited in their capabilities. From a PCI-compliance point of view, they are not considered sensitive information, so they can be touched by the Azure servers and stored in the Azure Database without compromising their transactions’ compliance.

The credit card industry offers two short-hand terms to categorize the programming techniques which work with a credit card holder’s sensitive information. “In-Scope” techniques directly access the credit card’s number, expiration-date and 3-4 digit security code. Web Servers, which use “In-Scope” techniques, are expected to meet the PCI-Compliance guidelines. “Out-of-Scope” techniques use various client-side techniques, such as cross-browser-ajax-posting and browser-redirection to keep the sensitive information away from your web-server; they allow a different machine, such as the payment gateway’s server, to perform the “In-Scope” information handling. In the process, they off-load much of the PCI compliance burden to that other machine. This article will focus solely on “Out-of-Scope” solutions, which by their nature can safely be used with Azure.

Overview

To demonstrate this client-side technique, I've created http://pay2see.azurewebsites.net, a simple MVC website containing two views and one controller. The index view contains a form to collect credit card information and a button which offers to charge two dollars against the provided credit card. It then displays the inner, “secure” view. There is no model or database attached at this point, but those will be added in the next challenge. This is therefore, a demonstration of credit card processing for a “pay per access” scenario in which the user has to pay each time they want to see the secured page. In the future, we will evolve this to a “subscription” scenario, where the user’s single payment allows them multiple viewings.

DISCLAIMERS

This article is not an advertisement for SlimCD. They are just the payment gateway which I use. I have been told that BrainTree Payments and Stripe provide similar functionality, but I have no experience working with them.

The payment processing code on this article’s azure website is completely functional and capable of transacting real world business, charging real credit cards in real dollars and crediting real merchant accounts with revenues. This article’s azure website however, is configured with a test merchant account so no real world business will occur, nor will real money be involved, when you test the site.

To ease your testing of the site, default values have been loaded into many of the fields on the index view’s credit card entry form. These values are fake and will only work with the test merchant account which has been set up for this site.

I am not guaranteeing that anything in this article is true. I am pretty sure of that my understanding of payment processing is current and that the techniques described here are out-of-scope for PCI compliance. Recheck everything here with your own PCI compliance expert or with the Merchant Service Provider/Bank which provides your merchant account.

TECHNICAL SUMMARY

When the index view’s button is clicked, a JavaScript function first validates that information has been entered in the required fields, then cross-browser-posts that information to the payment gateway for storage and tokenization. The gateway replies to the JavaScript, providing a transaction token which gets stored in a hidden field on the form. The JavaScript then clears the credit card number and security code input fields from the form before submitting the form to its controller which is waiting on an azure webserver. If the JavaScript had not cleared those input fields, we would now be in-scope on Azure, which would be a violation of PCI compliance. Since the JavaScript did clear those fields, the sensitive information has been eliminated while it was still in the user’s browser, so all that Azure can see is the transaction token. This means that our MVC controller, running on Azure, is out of scope.

The controller now uses the transaction token to charge two dollars against the associated credit card. It does this by submitting the token in a “processtransaction” request to the payment gateway. If the gateway then confirms that the charge has succeeded, the controller then changes the user’s browser to the secure view. This fulfills the “pay per access” scenario described above.

If anything goes wrong with the charge such as the card being invalid or over its limit, the controller would resend the index view to the user’s browser, along with an error message describing what went wrong.

At this point in the contest, that is all we will do with the transaction token; charge against it once and then throw it away. In the third challenge, we will persist the transaction token, using its presence as proof of its card holder’s right to revisit the secure page. In the fourth challenge we will be setting up a web service on a virtual machine, and that web service will use the transaction token to periodically recharge the user’s card, sustaining their subscription for accessing the site’s secure page. Transaction tokens are extremely useful tools for implementing a persistent and recurring charge relationship between a specific customer and a specific merchant, and we will explore that further in the later challenges of this contest.

UNDER THE HOOD (clientside : index view)

The heart of this solution is the tokenization step and the heart of that step is the following JavaScript function. This JavaScript is part of the index page of the azure website and gets called on-click of the button on the credit card collection form.

B). That same javascript library contains a function called ProcessTransaction which handles the cross-browser, ajax-style posting. It accepts a json array, containing the merchant account information, the credit card information and a very important transaction type field. In this example, the merchant account information include the username, siteid and priceid lines, which are being loaded from string literals. The credit card information is loaded directly from the form’s inputs, and the transaction type is the string literal “LOAD”.

C). “LOAD” is the command which tells the credit card gateway to store the provided merchant/credit card information and to return a transaction token for future use.

D). The ProcessTransaction() method also accept san additional parameter, a javascript “callback” function. This function will be executed once the ProcessTransaction method completes and will be passed a reply object containing the payment processing results. In this example, the callback function is being used to clear away the sensitive credit card information (E) and to store the transaction token and some other supporting information in hidden fields (F). The last think the callback function attempts is to submit the form back to its controller using a small scrap of jquery (G). (I tried to avoid using anything but the simplest JavaScript in this example, but I had trouble getting my non-jquery anonymous form submission code to work, so I replaced it.)

UNDER THE HOOD (serverside : home controller)

So now we have our index view posting back to our controller with its sensitive fields cleared and with a hidden field called “transactiontoken” containing our newly created transaction token. All we have accomplished so far is to tokenize some data. No moneys have been moved.The next step is to perform the actual charging of the card and that occurs on the server side, far from prying eyes. Let’s look at the index method of the homecontroller.

This C# method accepts posts from the Index view and then, if that view has passed in a transactiontoken value, it uses that token to charge the associated credit card. It does this by calling the C# version of that same ProcessTransactions method, this time using a transtype value of “SALE”. This transtype actually charges the card instead of just tokenizing it.

The highlights of this controller method include…

A). It receives the transaction token from the collection array. This works because out on the html form in the view, the transactiontoken hidden field had a name attribute. ASP.NET MVC loads named inputs into the collection array during post back. Notice that none of the sensitive information inputs have name attributes so they wouldn’t be passed to the server during an unexpected post. The transaction token gets stored in the gateid field of the ProcessTransactionRequest class.Note: Although the payment gateway is nice enough to provide the same processing library in a bunch of different programming languages, each one is a little bit different. For example, the request object for processTransaction() is a json array in javascript, but is a formally typed and instantiated class here in C#. There is a little language specific style in each version of the library which I’ve used, but the underlying ideas remain consistent throughout.

B). In the javascript call, we only loaded the username field, using a very low-privileged access token which was only empowered to run transtype ‘LOAD’. We could have provided it with a more powerful username/password pair like we’re doing here, but it wouldn’t be a best practice. I’ll describe why in the security section below.

C). The client transaction reference is a string which will be stored with the credit card transaction and may potentially show on the card holder’s statement. It is supposed to be a unique identity which the customer can use when talking to the merchant about this transaction. For example, the customer might say “Hi, I’m calling about my credit card purchase on January 3rd, invoice # 10043…” The 10043 is the client transaction reference. It doesn’t have to make any sense to the payment gateway, or the credit card provider, or the bank; but it should make sense to the merchant, helping them identify exactly which event this credit card transaction is paying for. For the sake of this example, I’m just using a random number, but that will change in the next challenge.

D). This time, the transtype value is ‘SALE’ which means (if we weren’t using a test merchant account) real money would be moving from the card holder’s bank to the merchant. Notice that this is also where we set the dollar amount of our sale, instead of out on the client side where a hacker might mess with it.

E). After ProcessTransaction() completes, we extract the returned values from the reply class instance and load them into the viewbag. The secure view which we are about to call, displays those returned values as a simple receipt.

F). If ProcessTransaction() returns an approval, we send the secure view to the customer’s browser.

G). If not, we update the error message and send them another copy of the index view.

A FEW WORDS ABOUT SECURITY

It might seem a little strange that we’re communicating with the payment gateway twice for a single transaction. We’re calling the gateway once from clientside JavaScript, to tokenize the collected card information, but we’re not actually charging anything at that point. Then, on the serverside, we’re performing the actual charge using the newly created token. The reason for this extra work can be summed up in a single word, security.

EVERYTHING ON THE CLIENT SIDE IS VISIBLE: Look at the second line in section B of the JavaScript example. That’s an access credential to the payment gateway, a key for submitting requests and doing business in a particular merchant’s name. Go to the example website, right click anywhere and choose View Source. You will find that exact same line, openly visible, exposed for the entire world to see. As you look at the rest of that exposed source, you’ll find other exposed treasures such as the processTransactions() endpoint through which transactions can be fed. A malicious mind is barely necessary to imagine the kinds of trouble that can grow from this; until you factor in that the access credentials which we have exposed here, are nearly impotent.

THE PAYMENT GATEWAY CREDENTIAL THAT GETS EXPOSED ON THE CLIENT SIDE IS A ONE TRICK PONY…It can only submit requests of transtype, ‘LOAD’. All other transtypes have been shut off at the gateway and will be ignored. The only thing which the exposed code and values can do, is submit information for tokenization…

AND TOKENIZATION IS A BLACK HOLE…Requests with a transtype of ‘LOAD’ accept any stream of text and only return a token in reply. They do no validation of the provided information, returning a few easily calculated values such as the last four digits of the card number and it’s mathematically determined card-type. The token is the only potentially valuable morsel of information returned, and it is only valuable to someone with a higher-privileged access credential on the same merchant account.

Nothing that a hacker might get from the information exposed on the client-side, will help them in their attempts at theft or mischief.

On the server-side, safely behind Azure’s defenses, a second call to the payment gateway can be attempted using a higher-privileged access id. That more powerful credential, the one that can actually perform sales, remains safe and secure, on the server side, in the controller code shown in the second code sample.

The integrity of this arrangement is subtle but undeniably beautiful. On the client side, in the user’s browser, we can touch the sensitive credit card information directly, but the only payment gateway credential available there is limited to tokenization. On the server side, where we have credentials with the power to move real money, we have only a token to the sensitive credit card information and it is tied to the site owner’s merchant account. We can only move money between a single credit card and a single merchant. Neither side of the arrangement ever has enough information to make it worth breaking into. Only in the real time and instantly-transient synchronicity of both sides, is there an opportunity for commerce, and even then, it can only be between credit card holder and the merchant they intend to patronize.

Neither side provides a potential thief with anything worthy of stealing.

There are other reasons for splitting the activity as we have, but I don’t want to turn this article into an educational resource for hackers. As with so many security issues, attempts to warn the potential victims often have the side effect of educating the perpetrators. Discuss the issues with your payment gateway service provider before exposing more than is absolutely necessary, and even then, periodically review your security exposure for flaws.

Closing

Good Luck to everyone left in the Azure Challenge!It has been a pleasure competing with and against you all!

Share

About the Author

I've been programming in one form or another for more than 25 years, most recently in C# and MsSQL.I'm co-organizer of the South Florida Windows 8 Developers Meetup Group which meets in Fort Lauderdale about once per month. I've got a mixed bag of professional programming experiences including a couple years in the payment processing industry and a couple decades in retail point of sale development. I'm new to Azure but won't be for long.

I couldn't agree with you more! With your permission, I would like to add your "Security is a moving target" statement to my article.Your warning to continually revisit the issue of security and the links are significant contributions to this subject matter. My original intention was to cover only the technical aspects of the client side technique, but given the possibility that readers might not have a payment processing background, I guess I should cover the "Be Afraid... Be Very Afraid" aspect of moving money on the web.I'm a little behind on the contest requirements right now, but when I update my article for the current challenge, I will make sure to upgrade the security warnings accordingly.Thanks for your contribution!- Colt

Feel free to use my words. Having been in this payments "game" for years before PCI I find that security is very under rated by nearly every company whether online or not. You are likely only to find a major emphasis on security from companies that either have "real world security professionals" or that have experienced a public exposure of information loss and were 'forced' to spend. My experience shows that no company is willing to pay for something, in this case security, that does not have a measurable return on investment. However to the contrary of paying up front is the multiples paid as 'real world millions' after the fact to protect their business and thus revenue. Security is one of those funny things that is very difficult to justify for a business as it is near impossible to measure. Take for example if your doctor advised you to have a procedure to better your life longevity but it cost well beyond your budget would you do it? Now take that same procedure after a near life ending event and thus bringing in to perspective the importance for said procedure but now it costs exponentially more based on the 'instant' need to execute - You are likely to find the needed funds given what is at stake. My analogy can relate to many well known publicly documented payments breaches and countless others not known. I could go on forever but I'll end with what I hope puts some importance on the matter as my experience reflects although this is generalized information on the security topic and not code specific.

Thanks! You are absolutely correct in your assessment of the usual corporate attitude towards security and other non-revenue-producing investments. The doctor metaphor is apt. Not sure that I will get to the article enhancement before the challenge deadline, but I will update it in any case, and credit you in the enhanced security section.Be Safe Out There!- Colt

Great Idea! I haven't used WebMatrix yet but I'll definitely look at it.I don't mind the extra work, but I am a capitalist at heart, so the deciding factor for me would be whether there is a paying market for webmatrix templates. I can see wrapping my code up into a number of prefab business models such as subscription-management, pre-paid account, credit-line. If WebMatrix has embraced the idea of starting point templates and if it has a reasonable following of programmers, I will definitely provide payment solutions for them.Thanks for the idea!- Colt

Wow! Great Catch! I accidentally put the reporting url in my sample. I've changed it to the https endpoint in my project and republished and edited the article as well.So in answer to your second question, yes they have a secure endpoint and I should have been using it.

As for hiding more of the subscription details, yes, I think so.When I log onto slimcd's merchant console, where I set up the P7RCWAYW access-credential, I see that there are fields which allow me to define default siteid and priceid values for that username value. In theory, that would allow me to remove the siteid and priceid lines from the post (or maybe send nothing values in the existing lines). I play around with that and update my article with my findings. If it works, the only exposure would be the username line, which I'm pretty certain has to be there. I have however limited its privileges to LOAD transtypes only, as I described in the Security Section at the bottom of my article.

Good point. That line is probably a little too strong as I doubt any police officers will be involved. The way I phrased it pretty much matches the way it has always been described to me, but the reality probably involves cancelled merchant accounts and fines rather than any direct police involvement. I'll check with my information sources concerning the actual effects of failing to be PCI Compliant, as I have never experienced it myself. Once I know more, I will add to this comment thread.In the meantime, I'll go rephrase that line so that it's less offensive.Thanks for pointing it out.- Henry

Most of my reply to this thread has been redacted in response to the original poster's gracious withdrawal of some comments that I found objectionable. One point that I made during our conversation deserves to remain here as I believe it adds value to my article...

The PCI guidelines make sense to me, not only for the sake of the merchants who use my software and their customers, but also for my own sake, minimizing my liability as the software's creator. By never touching or storing the sensitive data, I remove myself from the chain of blame should any theft or other mischief occur. That just makes sense to me and whether it (PCI) is "legally" mandated or not. I believe it is a best practice.

I'm really interested and curious to see your article come to completion, having been involved in a PCI audit as the manager of a web development team.

I asked our auditor about the possibility of being PCI compliant while using Azure. His response was that it would be extremely difficult to be compliant due to the lack of knowledge of the security settings used on the devices in the cloud. I would think that this would also relate to storage of any of the PCI-related data on a mobile device.

The idea of making 'tokens' of the data sounds intriguing, but I'm curious as to whether auditors would find this satisfactory. Thus, my question of whether your process has/will pass an audit. Have you discussed your processing with any auditors to see if they would allow your processing to pass? Again, having been involved in the process, I'm curious.

I appreciate your concerns as I have had the same difficulties in the past. I agree with your auditor's response, both for his stated reasons and because of the security risk inherent in shared hosting, whether it is cloud-based or not. To my knowledge Azure cannot handle "In-Scope" payment processing.

The particular data vault that I have been using is SlimCD.com and they've been using this tokenizing approach for some time, but only for "Out-of-Scope" scenarios. Special effort has to be made to keep the sensitive information away from non-compliant servers. The card information goes directly from the client's browser to the fully audited and PCI-compliant data vault, and NEVER COMES BACK. My server side code, running on Azure or elsewhere, never sees anything except the token, the care holder's separately entered name, and the last four digits of the card number. I've been told by the staff at SlimCD, that this meets the PCI requirements.

I've done some additional research and found that SlimCD is not the only company offering out of scope solutions. Stripe, Square and Braintree each seem to have similar products. I don't have any experience with any of them, but the presence of multiple players in the field is reassuring. That having been said, I'm very happy with SlimCD because they answer the phone when I call, and have answers. I'll check with them today and see if they'd object to my posting their contact information in this comment area. (I can't imagine they would, but I have to ask). I'm sure they can provide you with specific examples of clients who have used their techniques and passed audits.