Reading Credit Card Data using Javascript

A credit card reader can be embedded in webpage using a HID USD swiper (


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.

Wowza Media Systems – Logging to a Database

Logging to a MySQL Database

This is a guide to setting up logging to a MySQL database using log4j. PostgreSQL is covered in this article.

Note: Database logging is provided by the log4j open source logging system which Wowza uses for its logging framework. There have been reports of issues with database logging when the connection to the database is lost. The log4j system will not automatically try to reconnect to the database server.

First download the MySQL JDBC Connector here, then open the zip file and extract mysql-connector-java-5.1.8-bin.jar to [wowza-install-dir]/lib.

In MySQL, create a catalog named “wowzalogs”. Then create the following table to store all Wowza log fields. (Table attributes have the same name as log4j fields but without hyphens.)

CREATE TABLE  `wowzalogs`.`accesslog` (
  `logid` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `date` varchar(100) DEFAULT NULL,
  `time` varchar(100) DEFAULT NULL,
  `tz` varchar(100) DEFAULT NULL,
  `xevent` varchar(20) DEFAULT NULL,
  `xcategory` varchar(20) DEFAULT NULL,
  `xseverity` varchar(100) DEFAULT NULL,
  `xstatus` varchar(100) DEFAULT NULL,
  `xctx` varchar(100) DEFAULT NULL,
  `xcomment` varchar(255) DEFAULT NULL,
  `xvhost` varchar(100) DEFAULT NULL,
  `xapp` varchar(100) DEFAULT NULL,
  `xappinst` varchar(100) DEFAULT NULL,
  `xduration` varchar(100) DEFAULT NULL,
  `sip` varchar(100) DEFAULT NULL,
  `sport` varchar(100) DEFAULT NULL,
  `suri` varchar(255) DEFAULT NULL,
  `cip` varchar(100) DEFAULT NULL,
  `cproto` varchar(100) DEFAULT NULL,
  `creferrer` varchar(255) DEFAULT NULL,
  `cuseragent` varchar(100) DEFAULT NULL,
  `cclientid` varchar(25) DEFAULT NULL,
  `csbytes` varchar(20) DEFAULT NULL,
  `scbytes` varchar(20) DEFAULT NULL,
  `xstreamid` varchar(20) DEFAULT NULL,
  `xspos` varchar(20) DEFAULT NULL,
  `csstreambytes` varchar(20) DEFAULT NULL,
  `scstreambytes` varchar(20) DEFAULT NULL,
  `xsname` varchar(100) DEFAULT NULL,
  `xsnamequery` varchar(100) DEFAULT NULL,
  `xfilename` varchar(100) DEFAULT NULL,
  `xfileext` varchar(100) DEFAULT NULL,
  `xfilesize` varchar(100) DEFAULT NULL,
  `xfilelength` varchar(100) DEFAULT NULL,
  `xsuri` varchar(255) DEFAULT NULL,
  `xsuristem` varchar(255) DEFAULT NULL,
  `xsuriquery` varchar(255) DEFAULT NULL,
  `csuristem` varchar(255) DEFAULT NULL,
  `csuriquery` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`logid`)




Open /conf/ In the first line, add a new root category “SQ”:

log4j.rootCategory=INFO, stdout, serverAccess, serverError, SQ

In the same file, copy in the following appender. You will have to modify the password at least, and possibly the URL for the location of the database, and the user as necessary:

log4j.appender.SQ.sql=INSERT INTO accesslog (date, time, tz, xevent, xcategory, xseverity,xstatus,xctx,xcomment,xvhost,xapp,xappinst,xduration,sip,sport,suri,cip,cproto,creferrer,cuseragent,cclientid,csbytes,scbytes,xstreamid, xspos,csstreambytes,scstreambytes,xsname,xsnamequery,xfilename,xfileext,xfilesize,xfilelength,xsuri,xsuristem,xsuriquery,csuristem,csuriquery) VALUES ('%X{date}', '%X{time}', '%X{tz}', '%X{x-event}', '%X{x-category}', '%X{x-severity}', '%X{x-status}', '%X{x-ctx}', '%X{x-comment}', '%X{x-vhost}', '%X{x-app}', '%X{x-appinst}', '%X{x-duration}', '%X{s-ip}', '%X{sport}', '%X{s-uri}', '%X{c-ip}', '%X{c-proto}', '%X{c-referrer}', '%X{c-user-agent}', '%X{c-client-id}', '%X{cs-bytes}', '%X{scbytes}', '%X{x-stream-id}', '%X{x-spos}','%X{cs-stream-bytes}', '%X{sc-stream-bytes}', '%X{x-sname}', '%X{x-sname-query}', '%X{x-file-name}', '%X{x-file-ext}', '%X{x-file-size}', '%X{x-file-length}','%X{x-suri}','%X{x-suri-stem}', '%X{x-suri-query}', '%X{c-suri-stem}', '%X{c-suri-query}');

Then restart Wowza and check the table:
select * from accesslog;

NOTE: You should start Wowza in console or IDE to see any errors that occur

NOTE: Some fields might need to be longer to accommodate all possible values.

Moving to from is very good VPS service which offer good value for money if you are comfortable with command line. In my two years experience with them i had occasional outage. Over all very satisfactory.

Now i m planning to move over to and buy their EX service root server. I hope to utilize my VPS experience to install a Xen Server 5.6 Sp2 on it. Then may be install couple of bunch of VMs to do testing and Dev machines for the consultants,

Automated Install Xenserver Hetzner for EX4S Guest Networking Enabled

Skip to end of metadata

  • Hetzner’s gateway setup requires special network settings (all XenServer releases)
  • the XenServer 5.6 unattended installation has a bug so the installed system will not boot
  • XenServer 5.6 FP1 has a problem with the RealTek 8169 NIC which are used in Hetzner’s servers

This article explains how to circumvent these problems. Some workarounds are needed even when installing from a physical CD (by using Hetzner’s LARA console).

This How-To also works for XenServer 5.61 SP2

Copy contents of the installation CD to a directory on a webserver

Copy the contents of the Citrix XenServer installation CD to a directory on a web server. This directory must be accessible by ip address (in this example it ishttp://, so you might need to set up a default virtual host.

# mount -o loop XenServer-5.6.1-fp1-install-cd.iso /mnt
# cp -a /mnt xenserver

Create answerfile

In the xenserver directory, create a new file xenserver.xml containing the following XML code. Make sure to enter your own hostname, root password, ip address, subnet mask, gateway and name server ip addresses. The source URL is of course the URL to the installation directory you have just created. It must contain the ip address, not the domain name.

<installation mode="fresh" srtype="lvm">
 <primary-disk gueststorage="yes">sda</primary-disk>
 <source type ="url"></source>
 <!-- No Post install scripts configured -->
 <admin-interface name="eth0" proto="static">

Configure new server for unattended installation

The new XenServer host must be initialized with an arbitrary Linux system. Its only purpose is to boot a special XenServer kernel, which in turn will start the actual installation process by downloading the answerfile and the installation packages from the web server.

When installing the Linux system, do not use a software raid. There seems to be a bug in the XenServer 5.6 installer: if the Linux system used to boot the XenServer installer had software raid turned on, the installed XenServer system was corrupt. Files were missing or had binary data in them.

To install the initial Linux system, do the following for a Hetzner root server:

  1. boot the server in the rescue console 

  2. wipe all disks so that they do not have partitions of type 0xfd (Linux raid autodetect):
    # dd if=/dev/zero of=/dev/sda count=256
    # dd if=/dev/zero of=/dev/sdb count=256

    Note that this will destroy all data on your disks. 

  3. install Linux using the installimage command. I was using CentOS 5.5 32bit with good success. Remember to turn off the software raid option, this is done by editing the configuration to SW RAID = 0

  4. After installing Linux, mount the boot partition:
    # mount /dev/sda3 /mnt
    # mount /dev/sda2 /mnt/boot
  5. Replace the file /mnt/boot/grub/menu.lst with the following content:
    timeout 5
    default 0
    title Install Xenserver
    root (hd0,1)
    kernel /boot/xen.gz dom0_mem=752M acpi=off nosmp noapic noirqbalance
    module /boot/vmlinuz answerfile= install
    module /boot/install.img

    Note that the file references the answerfile created earlier. The URL must contain the ip address, a domain name will not work. 

  6. if there is a file grub.conf, copy menu.lst to it 

  7. some files must be copied from the XenServer installation directory into the /boot directory. On the server containing the XenServer installation files (the web server), do the following:
    scp xenserver/install.img xenserver/boot/vmlinuz xenserver/boot/xen.gz newserver.mydomain:/mnt/boot
    root@newserver.mydomain's password: ********
    install.img     100%   26MB  12.9MB/s   00:02
    vmlinuz         100% 2058KB   2.0MB/s   00:00
    xen.gz          100%  585KB 585.2KB/s   00:00
  8. to see whether the XenServer installer actually retrieves the files from the web server, run the tail command:
    # tail -f access_log.2011-04-16-00_00_00  | grep xenserver - - [16/Apr/2011:11:36:11 +0200] "GET /download/xenserver/xenserver.xml HTTP/1.1" 200 871 "-" "Python-urllib/2.4" - - [16/Apr/2011:11:36:23 +0200] "GET /download/xenserver/XS-REPOSITORY-LIST HTTP/1.1" 200 21 "-" "Python-urllib/2.4" - - [16/Apr/2011:11:36:23 +0200] "GET /download/xenserver/XS-REPOSITORY HTTP/1.1" 404 230 "-" "Python-urllib/2.4" - - [16/Apr/2011:11:36:23 +0200] "GET /download/xenserver/XS-PACKAGES HTTP/1.1" 404 228 "-" "Python-urllib/2.4" - - [16/Apr/2011:11:36:23 +0200] "GET /download/xenserver/packages/XS-REPOSITORY HTTP/1.1" 404 239 "-" "Python-urllib/2.4" - - [16/Apr/2011:11:36:23 +0200] "GET /download/xenserver/packages/XS-PACKAGES HTTP/1.1" 404 237 "-" "Python-urllib/2.4" - - [16/Apr/2011:11:36:23 +0200] "GET /download/xenserver/packages.main/XS-REPOSITORY HTTP/1.1" 200 145 "-" "Python-urllib/2.4" - - [16/Apr/2011:11:36:23 +0200] "GET /download/xenserver/packages.main/XS-PACKAGES HTTP/1.1" 200 3720 "-" "Python-urllib/2.4"
  9. if you want to install XenServer 5.5, you can now restart the server. You should see the XenServer installer accessing the installation files a couple of minutes later. After the automatic reboot you should be able to connect to the new server via XenCenter 

  10. if you are installing XenServer 5.6, the configuration must be modified before the reboot. Otherwise XenServer will hang during reboot and there is no chance to log in and fix the settings. therefore the Hetzner rescue system must be set up before the XenServer installation finishes. The easiest way to do this is: log into the Hetzner server administration, restart the server, and right after you see the installer accessing the packages from the web server, turn on the rescue system for this server. This way, when the freshly installed XenServer reboots after the installation has finished, it will boot into the rescue mode. 

  11. in the rescue console, mount the disks again. In the file /etc/rc.sysinit, find the following line and comment it out:
    # [ -x /sbin/nash ] && echo "raidautorun /dev/md0" | nash --quiet
  12. After that, edit /etc/modprobe.conf and add the line
    options r8169 use_dac=1
  13. Finally, reboot. The new XenServer 5.6 FP1 system should now be accessible from XenCenter


7. Makse sure your /etc/sysctl.conf has this :

# Kernel sysctl configuration file for Red Hat Linux


# For binary values, 0 is disabled, 1 is enabled. See sysctl(8) and

# sysctl.conf(5) for more details.

# Controls IP packet forwarding

net.ipv4.ip_forward = 1

# Controls proxy arp

net.ipv4.conf.default.proxy_arp = 1

# Turn off redirects

net.ipv4.conf.all.send_redirects = 0

net.ipv4.conf.default.send_redirects = 0

net.ipv4.conf.lo.send_redirects = 0

net.ipv4.conf.xenbr0.send_redirects = 0

and validated with ’systcl -p’

# sysctl -p

net.ipv4.ip_forward = 1

net.ipv4.conf.default.proxy_arp = 1

net.ipv4.conf.all.send_redirects = 0

net.ipv4.conf.default.send_redirects = 0

net.ipv4.conf.lo.send_redirects = 0

net.ipv4.conf.xenbr0.send_redirects = 0

net.ipv4.conf.default.rp_filter = 1

net.ipv4.icmp_echo_ignore_broadcasts = 1

net.ipv4.conf.default.accept_source_route = 0

net.ipv4.conf.all.accept_redirects = 0

net.ipv4.conf.all.send_redirects = 0

kernel.sysrq = 1

kernel.core_uses_pid = 1

net.ipv4.tcp_syncookies = 1

kernel.msgmnb = 65536

kernel.msgmax = 65536

kernel.shmmax = 4294967295

kernel.shmall = 268435456

vm.dirty_ratio = 5

kernel.printk = 4 4 1 4

8. Disable SeLinux with

# system-config-securitylevel-tui

9. add your additional subnetwork to your xenserver

# ip addr add dev xenbr0

10. shutdown the iptables

# service iptables stop

11. Create Dummy interface as we would use this as gateway for all Guest machine (Linux or Windows)

let say you have this Ip subnet from Hetzner until



HostMin: (this one we would use as gatway for our guesthost)


# nano /etc/sysconfig/network-scripts/ifcfg-xenbr0:1







12. bring our xenguest gateway ip up

#ifup xenbr0:1

13. now you can use this range until