Regular Expression for Credit Card Track Data
Asked Answered
D

6

13

Are there any known regular expressions out there to validate credit card track 1 and track 2 data?

EDIT:

From Wikipedia:

The information on track 1 on financial cards is contained in several formats: A, which is reserved for proprietary use of the card issuer, B, which is described below, C-M, which are reserved for use by ANSI Subcommittee X3B10 and N-Z, which are available for use by individual card issuers:

Track 1, Format B:

  • Start sentinel — one character (generally '%')
  • Format code="B" — one character (alpha only)
  • Primary account number (PAN) — up to 19 characters. Usually, but not always, matches the credit card number printed on the front of the card.
  • Field Separator — one character (generally '^')
  • Name — two to 26 characters
  • Field Separator — one character (generally '^')
  • Expiration date — four characters in the form YYMM.
  • Service code — three characters
  • Discretionary data — may include Pin Verification Key Indicator (PVKI, 1 character), PIN Verification Value (PVV, 4 characters), Card Verification Value or Card Verification Code (CVV or CVK, 3 characters)
  • End sentinel — one character (generally '?')
  • Longitudinal redundancy check (LRC) — it is one character and a validity character calculated from other data on the track. It should be noted that most reader devices do not return this value when the card is swiped to the presentation layer, and use it only to verify the input internally to the reader.

Track 2: This format was developed by the banking industry (ABA). This track is written with a 5-bit scheme (4 data bits + 1 parity), which allows for sixteen possible characters, which are the numbers 0-9, plus the six characters : ; < = > ? . The selection of six punctuation symbols may seem odd, but in fact the sixteen codes simply map to the ASCII range 0x30 through 0x3f, which defines ten digit characters plus those six symbols. The data format is as follows:

  • Start sentinel — one character (generally ';')
  • Primary account number (PAN) — up to 19 characters. Usually, but not always, matches the credit card number printed on the front of the card.
  • Separator — one char (generally '=')
  • Expiration date — four characters in the form YYMM.
  • Service code — three characters
  • Discretionary data — as in track one
  • End sentinel — one character (generally '?')
  • Longitudinal redundancy check (LRC) — it is one character and a validity character calculated from other data on the track. It should be noted that most reader devices do not return this value when the card is swiped to the presentation layer, and use it only to verify the input internally to the reader.
Despinadespise answered 29/3, 2010 at 20:2 Comment(2)
Can you provide some examples of said data?Tutorial
I've been in cc business for a while now and I have a hard time imagining why you'd need this. Generally, since track 1,2,3 are in the issuer's domain you'll often be hard pressed to find data other then PAN, expiration date and service code on the track and name data might as well be junk, I've even come across PANs that don't pass Luhn check. This is why it seems to me that you'll have quite a bit of in-production special cases to fine tune the regex to be just sensitive enough to not discard workable production data that clashes with the iso spec.Irreparable
W
2

I was about to post the same link on regular-expressions.info, for verifying the cc number part of the track.

Now, comes the tricky part. Track data varies in format between card issuers and even card readers. For example the 'separator' characters aren't always the same. Same applies to the end 'sentinels'.

Wikipedia gives a good overview: http://en.wikipedia.org/wiki/Magnetic_stripe_card

With track2, the card number is followed by an '=' (or occasionally a 'D'). Then you have expiry date as MMDD. After that, Track2 has 'discretionary data', which could be anything.

I wouldn't worry too much after this point. If it's track data, you'll be pretty sure by now. I guess it depends on what you are aiming to do with the data.

Anyway, for Track2 you could do a lot worse than adding [=D][0-9]{4} instead of the $ at the end of the cc regex:

^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})[=D][0-9]{4}

For track1, you could do something similar ... Track1 contains more variable data, so can be a touch more complicated.

Good luck!

Winni answered 29/3, 2010 at 20:27 Comment(0)
S
11

Here is a REGEX that works for me to pick both Track 1 and Track 2. Use this with the regex option "Dot does NOT match newline".

^%(?<FC>.)(?<PAN>[\d]{1,19}+)\^(?<NM>.{2,26})\^(?<ED>[\d]{0,4}|\^)(?<SC>[\d]{0,3}|\^)(?<DD>.*)\?|;(?<PAN>[\d]{1,19}+)=(?<ED>[\d]{0,4}|=)(?<SC>[\d]{0,3}|=)(?<DD>.*)\?\Z

I tested with this data (my reader is reading both a Track 1 and Track 2 record, in this order, for the same card I tested with - numbers and name changed below.)

%B5581123456781323^SMITH/JOHN^16071021473810559010203?
;5581123456781323=160710212423468?

The above REGEX uses NAMED CAPTURE GROUPS (the "?" that starts out each (group)) and I see the result (with RegexBuddy) as:

Match 1:    %B5581123456781323^SMITH/JOHN^16071021473810559010203?       0      54
Group "FC": B        1       1
Group "PAN":    5581123456781323         2      16
Group "NM": SMITH/JOHN      19      10
Group "ED": 1607        30       4
Group "SC": 102     34       3
Group "DD": 1473810559010203        37      16

Match 2:    ;5581123456781323=160710212423468?      56      34
Group "FC" did not participate in the match
Group "PAN":    5581123456781323        57      16
Group "NM" did not participate in the match
Group "ED": 1607        74       4
Group "SC": 102     78       3
Group "DD": 12423468        81       8

Note the second match does NOT identify FC (format code) and NM (name) in the Track 2 (match 2) since they are not used in track 2.

If your regex engine does not support NAMED GROUPS, just kill the "?" part of each of the capturing groups. Then, use position to determine each group.

Also, my single SWIPE contains BOTH track 1 and track 2 (in that order, track 1, a crlf and then track 2). According to the Wikipedia link in the original question, cards can have up to 3 tracks and readers might read tracks 1 and 2 both (or one or the other) and rarely track 3.

For this reason, I think it's a safe bet to use a REGEX that looks for both track 1 and track 2 and if you get both, you can ignore track 2 (since track 1 has more data) or whatever you wish.

Because both tracks are present in my swipes, the REGEX engine will return 2 matches with my REGEX above (assuming no read error from the reader and a reader that supports both tracks). In my case, this does not bother me and I'll simply plan to use the "first match" and ignore the second.

If you're interested only in track 1, use this regex:

^%(?<FC>.)(?<PAN>[\d]{1,19}+)\^(?<NM>.{2,26})\^(?<ED>[\d]{0,4}|\^)(?<SC>[\d]{0,3}|\^)(?<DD>.*)\?\Z

If you're interested only in track 2, use the regex:

^;(?<PAN>[\d]{1,19}+)=(?<ED>[\d]{0,4}|=)(?<SC>[\d]{0,3}|=)(?<DD>.*)\?\Z

But I see no harm in checking for both and then using the first one you get, or perhaps comparing track 1 to track 2 as an additional error checking step perhaps.

Sorry to answer what seems to be answered!

Smallage answered 19/11, 2013 at 21:6 Comment(1)
Looking into the issue more, it appears some gift cards have 0 characters for the name, despite Wikipedia saying it is 2 to 26 characters.Ackler
W
2

I was about to post the same link on regular-expressions.info, for verifying the cc number part of the track.

Now, comes the tricky part. Track data varies in format between card issuers and even card readers. For example the 'separator' characters aren't always the same. Same applies to the end 'sentinels'.

Wikipedia gives a good overview: http://en.wikipedia.org/wiki/Magnetic_stripe_card

With track2, the card number is followed by an '=' (or occasionally a 'D'). Then you have expiry date as MMDD. After that, Track2 has 'discretionary data', which could be anything.

I wouldn't worry too much after this point. If it's track data, you'll be pretty sure by now. I guess it depends on what you are aiming to do with the data.

Anyway, for Track2 you could do a lot worse than adding [=D][0-9]{4} instead of the $ at the end of the cc regex:

^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})[=D][0-9]{4}

For track1, you could do something similar ... Track1 contains more variable data, so can be a touch more complicated.

Good luck!

Winni answered 29/3, 2010 at 20:27 Comment(0)
D
2

The following two regular expressions seem to validate the track 1 and track 2 data. Note that these regular expressions make assumptions that the characters used are the ones that are "generally" used in the Wikipedia information above.

Track 1:  ^%B\d{0,19}\^[\w\s\/]{2,26}\^\d{7}\w*\?$

Assumes that % and ? are the sentinel characters and that ^ is used as the field separator character. Also assumes that the account number, date, and service code are digits.

Track 2:  ;\d{0,19}=\d{7}\w*\?

Assumes that ; and ? are the sentinel characters and that = is the field separator character. Also assumes that the account number, date, and service code are digits.

I tested these expressions using track data read from a MagTek card reader. The following two sets of track data match what was read from the reader and validate against the two regular expressions above (the numbers have obviously been changed):

%B1234567891234567^SMITH/JOHN                ^15024041234567891234?
;1234567891234567=152024041234567891234?
Despinadespise answered 30/3, 2010 at 13:48 Comment(0)
P
1

Track 1, Format B translates to

^%B[^\^\W]{0,19}\^[^\^]{2,26}\^\d{4}\w{3}[^?]+\?\w?$

with some assumptions as to what constitutes a valid character.

Of course there are no checks whether the data is actually meaningful, and the LRC (if present) also can't be validated.

Can you check this against some real data and see if it works?

Track 2 translates to

;[^=]{0,19}=\d{4}\w{3}[^?]+\?\w?
Polyhydroxy answered 29/3, 2010 at 20:25 Comment(2)
I tested these against the data from our card reader, but they didn't work.Despinadespise
It appears the "discretionary data" field allows all the alternatives, and not just one of them. I have changed the regexes. Now they should work.Polyhydroxy
T
0

Note: Account number in track1 can contain spaces for American Express cards. So :

^%(?.)(?[\d\s]{1,19}+)\^(?.{2,26})\^(?[\d]{0,4}|\^)(?[\d]{0,3}|\^)(?.*)\?|;(?[\d]{1,19}+)=(?[\d]{0,4}|=)(?[\d]{0,3}|=)(?.*)\?\Z
Thornie answered 22/4, 2017 at 18:53 Comment(0)
H
0

pattern = re.compile(r'\b\d{16}\b')

# Replace matches with asterisks
blurred_text = pattern.sub(lambda match: '*' * 12 + match.group()[12:], text)
Hallett answered 10/6, 2024 at 19:44 Comment(1)
Need to write not only codeMaggiore

© 2022 - 2025 — McMap. All rights reserved.