Reading Credit Card Data using Javascript

A credit card reader can be embedded in webpage using a HID USD swiper (Magtek.com)

 

How the reader works

Being identified as a simple HID, the reader does not need a specific driver, and the content of any card you swipe through it is sent to the active application as if the user tapped the respective keys on the keyboard.

This is mainly an advantage, but if you want to control the device directly, it can become a mess to set up. For this article I went down the easy way and dealt with the device as if it were a keyboard.

The other important thing to know about the device is the format used to send the data.

Each magnetic stripe can have two or three tracks on it and the data on each track is sent with a track-dependent char prefixed to it (called Track ID) and with a track-independent char as suffix (called End sentinel). The format is Track ID + Data + ES + ENTER for a successful read and Track ID + 'E' + ES + ENTER if a read error occurred.

The following table resumes the different Track IDs (you can also refer to the PDF linked at the top of the page):

Tab 1: Track IDs and sentinel
Track 1 2 3 Error
Track ID % ; + é
End sentinel _

Note that the end sentinel sent by my device differs for some strange reason from the one indicated in the documentation (which is a ?).

Another not documented behavior is the format of the data if a read error occurred before the track was identified. In this case, the devices sends the common error code (E) prefixed with the é Track ID.

Detecting input from the reader

The input from the reader can easily be detected with a general keypress event. If your application uses the card reader as input device or does not need to detect if the input actually comes from the keyboard or from the reader, a simple check of the Track ID ad and the end sentinel suffices to achieve the goal.

If instead – as it was the case for me and probably for most of the applications – there is the need to differentiate keyboard and card reader input, an easy solution is to measure the time between two successive keypress events and to cancel the reading if the delay is too long.

This simple test page measures the time between two events and provides you with the detailed results and the final average of all delays. In the case of my reader, I measured an average delay of 40 [ms].

The code

I throw together a simple Javascript class to detect and handle the input of the card reader based on jQuery events. The code can be found on GitHub and is licensed under the MIT license.

The usage is very simple, the first step consists to include the jQuery library and the reader class in your HTML page. Then you can initialize the reader and provide some callbacks which will handle the input:

<!-- Include the scripts --> <script type="text/javascript" charset="utf-8" src="scripts/jquery-1.3.2.js"></script> <script type="text/javascript" charset="utf-8" src="scripts/CardReader.js"></script> <!-- Initialize the reader --> <script type="text/javascript"> jQuery(function () { // Create a new reader instance var reader = new CardReader(); // Feed it an object to observe (this could also be a textbox) reader.observe(window); // Errback in case of a reading error reader.cardError(function () { alert("A read error occurred"); }); // Callback in case of a successful reading operation reader.cardRead(function (value) { $('form input#card_number').val(value); $('from').submit(); }); }); </script> 

The class also provides validation hooks. A validation hook is a function which is executed by the reader class before it fires the cardRead event. If all the validation hooks return true, then the value is correct and the event is fired, if instead an hook returns false, then the cardError event is fired:

// Add a new validation hook to the reader reader.validate(function (value) { // Tests if the value is a 4-digit long number var pattern = new RegExp(/^\d{4}$/); return pattern.test(value); }); // Multiple hooks can be added, they get all executed reader.validate(function (value) { // Tests if the value is below 5000 return parseInt(value) < 5000; }); 

Ignoring read errors

During the development of the subsequent part of the application I noticed that many times, a successful read, is followed by a read error, causing the errbacks1 to get executed straight after the callbacks.

To prevent this behavior (caused by the card reader itself), I added a second timeout which starts after a successful read. If a read error is encountered before the expiration of the timeout, it is ignored and not dispatched.

TODOs and possible improvements

  • Remove all calls to console.log from the code
  • Detect the three different Track IDs and provide the callbacks with enough data to act differently based on the track number. ATM the class detects only the first Track and fires all callbacks. The callbacks has no means to know from which track the input was read. Adding this little feature is pretty easy and straightforward;
  • Re-fire the preceding keydown events if the read operation times-out If the user enters a Track ID by pressing the respective key on the keyboard, the class begins to read and then times-out, consuming the user generated event. This means that a user isn’t able to type a Track ID directly into a textbox or another input;
  • Use jQuery custom event triggering instead of my simple array-based dispatching.
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s