ruby/rails detect financial track data and return nil/empty string

223 Views Asked by At

I read through similar stackoverflow questions to understand financial track card data.

I think the issue I am facing might be slightly different or maybe I am really weak in regex.

Now we have a service that returns track data accidentally instead of the guest name.

My goal is every time I receive track data I display "" empty string, else return the guest name.( This is a temp solution until we fix the root cause)

This is what my regular expressions is but looks like it doesn't detect track data.

irb(main):043:0> guestname="%4234242xx12^TEST/GUEST L      ^324532635645744646462"

irb(main):044:0> (/[(%[bB])(;)]\d{3,}.{9,}[(^.+^)(=)].+\?.{,2}/.match(guestname)) ? "" : guestname
=> "%4234242xx12^TEST/GUEST L      ^324532635645744646462"

(Not real data)

Now, looking at the wiki for track data information I want to cover most cases, if not all:

https://en.wikipedia.org/wiki/Magnetic_stripe_card#Financial_cards

Could some help with my regex. This is what I have:

/[(%[bB])(;)]\d{3,}.{9,}[(^.+^)(=)].+\?.{,2}/

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 — 2 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 CVC, 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.

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 digits. The first digit specifies the interchange rules, the second specifies authorisation processing and the third specifies the range of services

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. 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.

1

There are 1 best solutions below

2
On
  1. Your example input string does not contain format code after first sentinel.
  2. You are trying to parse html-encoded version, which is weird.

So, I would start with html decoding. E.g. with Nokogiri:

▶ guestname="&#x0025;4234242xx12&#x005E;TEST&#x002F;GUEST L      &#x005E;324532635645744646462"
#⇒ "&#x0025;4234242xx12&#x005E;TEST&#x002F;GUEST L      &#x005E;324532635645744646462"
▶ parsed = Nokogiri::HTML.parse(guestname).text
#⇒ "%4234242xx12^TEST/GUEST L      ^324532635645744646462"

OK, now we at least have a leading percent. Now let us ask ourselves: how many users have a guest name starting with a percent sign? I bet none. You might re-check yourself by running a query against your database. Since it is a temporary solution, I would definitely shut the perfectionism up and go with:

▶ parsed =~ /\A%/ ? '' : parsed

Hope it helps.