简体   繁体   English

从磁条中分析信用卡输入

[英]Parse Credit Card input from Magnetic Stripe

Does anyone know how to parse a credit card string input from a Magnetic Card Swiper? 有谁知道如何解析磁卡Swiper的信用卡串输入?

I tried a JavaScript parser but never got it to work. 我尝试了一个JavaScript解析器,但从来没有让它工作。 This is what the input looks like. 这就是输入的样子。

%BNNNNNNNNNNNNNNNN^DOE/JOHN
^1210201901000101000100061000000?;NNNNNNNNNNNNNNNN=12102019010106111001?

The N's are the credit card number. N是信用卡号。

See the Magnetic Stripe Card entry @ Wikipedia : 请参阅磁条卡条目@ Wikipedia


Track one, Format B: 跟踪一,格式B:

  • Start sentinel — one character (generally '%') 开始哨兵 - 一个角色(通常是'%')
  • Format code="B" — one character (alpha only) 格式代码=“B” - 一个字符(仅限alpha)
  • Primary account number (PAN) — up to 19 characters. 主帐号(PAN) - 最多19个字符。 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 名称 - 2到26个字符
  • Field Separator — one character (generally '^') 字段分隔符 - 一个字符(通常为“^”)
  • Expiration date — four characters in the form YYMM. 到期日期 - 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) 全权委托数据 - 可能包括Pin验证密钥指示符(PVKI,1个字符),PIN验证值(PVV,4个字符),卡验证值或卡验证码(CVV或CVK,3个字符)
  • End sentinel — one character (generally '?') 结束哨兵 - 一个角色(一般是'?')
  • Longitudinal redundancy check (LRC) — one character (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.) 纵向冗余校验(LRC) - 一个字符(大多数读卡器设备在将卡刷到表示层时不返回此值,并仅用于验证读卡器内部的输入。)

I hope the data is fake, otherwise Anyone could get the: 我希望数据是假的,否则任何人都可以得到:

  • Name 名称
  • Expiration Date 截止日期
  • CVV CVV

And I'm not sure but I think the credit card number (or # of possibilities) can be computed using the LRC. 我不确定,但我认为可以使用LRC计算信用卡号(或可能性数)。

I did you one better: I made a video showing how to do exactly this with ASP.Net/c#: 我做得更好了:我制作了一个视频,展示了如何使用ASP.Net/c#完成此操作:

http://www.markhagan.me/Samples/CreditCardSwipeMagneticStripProcessing http://www.markhagan.me/Samples/CreditCardSwipeMagneticStripProcessing

Here is the section of code that you probably care about: 以下是您可能关心的代码部分:

    protected void CardReader_OTC(object sender, EventArgs e)
    {
        bool CaretPresent = false;
        bool EqualPresent = false;

        CaretPresent = CardReader.Text.Contains("^");
        EqualPresent = CardReader.Text.Contains("=");

        if (CaretPresent)
        {
            string[] CardData = CardReader.Text.Split('^');
            //B1234123412341234^CardUser/John^030510100000019301000000877000000?

            PersonName.Text = FormatName(CardData[1]);
            CardNumber.Text = FormatCardNumber(CardData[0]);
            CardExpiration.Text = CardData[2].Substring(2, 2) + "/" + CardData[2].Substring(0, 2);
        }
        else if (EqualPresent)
        {
            string[] CardData = CardReader.Text.Split('=');
            //1234123412341234=0305101193010877?

            CardNumber.Text = FormatCardNumber(CardData[0]);
            CardExpiration.Text = CardData[1].Substring(2, 2) + "/" + CardData[1].Substring(0, 2);
        }
    }

The complete code is on that website I linked above. 完整的代码在我上面链接的网站上。

From what I can remember: 从我记忆中:

That is a two-track magnetic strip data - first track starts with % and ends with ? 这是一个双轨磁条数据 - 第一个轨道以%开头并以?结尾? , the second track starts with ; ,第二首曲目以; and ends with ? 结束? . These are Start/End markers. 这些是开始/结束标记。

The first track is alphanumeric, the second track is numeric, and there is a third track which is numeric also (if my memory serves correct). 第一个轨道是字母数字,第二个轨道是数字,第三个轨道也是数字(如果我的记忆正确)。

The data between the start/end markers can be variable depending on the recording density of the magnetic strip. 开始/结束标记之间的数据可以根据磁条的记录密度而变化。 The higher the density, the more it can be recorded on one track. 密度越高,在一条轨道上记录的越多。

Using a regex to get at the data may not be a reliable method to pick out the information required. 使用正则表达式获取数据可能不是一个可靠的方法来挑选所需的信息。

And not all credit cards have exactly two tracks, some uses three tracks. 并非所有信用卡都有两条轨道,有些使用三条轨道。

here is my code: 这是我的代码:

1st the listener to get the data.... this data needs validation which i am looking for help on. 第一个获取数据的监听器....这个数据需要验证,我正在寻求帮助。 A good swipe works fine, but a bad swipe will cause an error in the parser. 一个好的滑动工作正常,但错误的滑动​​将导致解析器中的错误。

$('#cc-dialog-form').keypress(function(e) 
{

    var charCode = e.which;
    //ie? evt = e || window.event;
    track_start = '%';
    finished = false;
    timeout = 100;
    track_start_code = track_start.charCodeAt(0);
    //console.log('Track_start_code: ' + track_start_code);

    //console.log('keycode ' + e.keycode);


    //console.log('charcode ' + charCode);
    //console.log('track_start_code ' + track_start_code);
    if (charCode == track_start_code)
    {
        collect_track_data = true;
            $('#offline_cc_entry').hide();
            $('#cc_online').hide();
            $('#Manual_CC_DATA').hide();
            $('#cc_loading_image').show();      

    }
    if (collect_track_data)
    {   
        if (charCode == $.ui.keyCode.ENTER) 
        {
            //all done
            //console.log( card_data);
            collect_track_data = false;
            $('#cc_loading_image').hide();
            $('#Manual_CC_DATA').show();
            //console.log("Track Data: " + card_data);


            process_swipe_cc_payment(card_data);
            card_data = '';

        }
        else
        {
            card_data = card_data + String.fromCharCode(charCode);
            console.log(card_data);
            if (e.preventDefault) e.preventDefault();
            e.returnValue=false;
            return false;
        }
    }
    else
    {
        //i am guessing this will be regular input?
        if (charCode == $.ui.keyCode.ENTER) 
        {
             process_keyed_or_offline_CC_payment();
        }
    }
    //console.log("which: " + e.which);
    //console.log("keyCode: " + e.keyCode);
    //track and collect data here?

});

And here is the parser.... note I put it all in one function so I can destroy all the variables so they are not lingering in a browser. 这里是解析器....注意我把它全部放在一个函数中,所以我可以销毁所有变量,这样它们就不会在浏览器中挥之不去。

    parse_data = true;
if (parse_data)
{

var parsed_card_data = {};
parsed_card_data['card_data'] = card_data;
var tracks = card_data.split("?");

//console.log ("tracks");
//console.log (tracks);
parsed_card_data['track1'] = tracks[0];
parsed_card_data['track2'] = tracks[1];
//if there is a third track we might find it under tracks[2]

//splitting the card data OPTION 1

var track1_parsed = tracks[0].split("^");

//console.log (track1_parsed);



//track1 data....
var card_number_track1 = track1_parsed[0].substring(2);


parsed_card_data['card_number_track1'] = card_number_track1;

var details2_1 = tracks[1].split(";");
details2_1 = details2_1[1].split("=");


var exp_date_track_1 = details2_1[1];
exp_date_track_1 = exp_date_track_1.substring(0, exp_date_track_1.length - 1);
exp_date_track_1 = exp_date_track_1.substring(2, 4) + "/" + exp_date_track_1.substring(0,2);
parsed_card_data['exp_track1'] = exp_date_track_1;



//now check if track one matches track 2...

track2_parsed = tracks[1].split("=");


card_number_track_2 = track2_parsed[0].substring(1);



parsed_card_data['card_number_track_2'] = card_number_track_2;
exp_date_track_2 = track2_parsed[1].substring(0,4);
exp_date_track_2 = exp_date_track_2.substring(2, 4) + "/" + exp_date_track_2.substring(0,2);
parsed_card_data['exp_date_track_2'] = exp_date_track_2;


var primary_account_number =  card_number_track1.substring(0,1);


if(card_number_track1 == card_number_track_2 &&  exp_date_track_1 == exp_date_track_2)
{
        //now make a security feature showing the last 4 digits only....
    parsed_card_data['secure_card_number'] = "xxxx " + card_number_track1.substring(card_number_track1.length-4, card_number_track1.length);




    if(card_number_track1.length == 15)
    {
        parsed_card_data['card_type'] = "American Express"; 
    }
    else if(primary_account_number == 4)
    {
        parsed_card_data['card_type'] = "Visa";
    }
    else if(primary_account_number == 5)
    {
        parsed_card_data['card_type'] = "Master Card";
    }
    else if(primary_account_number == 6)
    {
        parsed_card_data['card_type'] = "Discover";
    }
    else
    {
        parsed_card_data['card_type'] = false;
    }

    var names_1 = track1_parsed[1].split("/");
    parsed_card_data['first_name'] = names_1[1].trim();
    parsed_card_data['last_name'] = names_1[0].trim();


    //console.log("return Data");
    //console.log(return_data);

}
else
{
    parsed_card_data = false;
}

    //zero out the variables...

    tracks = '';
    track1_parsed = '';
    card_number_track1 = '';
    details2_1 = '';
    exp_date_track_1 = '';
    track2_parsed = '';
    card_number_track_2 = '';
    exp_date_track_2 = '';
    primary_account_number = '';
}

if(parsed_card_data)
{
    //console.log(parsed_card_data);
    $('#card_type').val(parsed_card_data['card_type']);
    $('#credit_card_number').val(parsed_card_data['secure_card_number']);
    $('#expiration').val(parsed_card_data['exp']);
    $('#card_holder').val(parsed_card_data['first_name']+ " " + parsed_card_data['last_name']);

    //parsed_card_data['track1'] is basically what we want???

    $('#CC_SWIPE_INSTRUCTIONS').hide();
    $('#CC_DATA').hide();
    $('#cc_loading_image').show();



    var post_string = {};
    post_string['ajax_request'] = 'CREDIT_CARD_PAYMENT';
    post_string['amount'] = $('#cc_input').val();
    post_string['card_data'] = parsed_card_data;
    post_string['pos_sales_invoice_id'] = pos_sales_invoice_id;
    post_string['pos_payment_gateway_id'] = $('#pos_payment_gateway_id').val();
    post_string['line'] = 'online';
    post_string['swipe'] = 'swipe';

    card_data = '';
                parsed_card_data = {};
    var url = 'ajax_requests.php';
    $.ajax({
            type: 'POST',
            url: url,
            data: post_string,
            async: true,
            success:    function(response) 
            {
                $('#cc_loading_image').hide();
                console.log(response);
                $('#CC_RESPONSE').show();
                $('#CC_RESPONSE').html(response);
                //here we would update the payment table - currently we will just refresh

                post_string = '';

            }
            });
    post_string = '';
}
else
{
    //error
    alert("Read Error");
    $( "#cc-dialog-form" ).dialog( "close" );
}

Generally for a card-not present transaction (ie MOTO transactions) you will need cc#, expiry and possibly the CVV (aka CVC2 etc). 通常,对于不存在卡的交易(即MOTO交易),您将需要cc#,到期以及可能需要CVV(也称为CVC2等)。 You can obtain the first 2 from a card-swipe as this in the track data. 您可以在轨道数据中从刷卡中获取前2个。 CVV is printed on the card. CVV印在卡上。

Name on card doesn't matter so much. 卡上的名字并不重要。 Unless your acquirer and the cardholder are using address verification, but you can find that between ^^, it may have white space padding which you can remove. 除非您的收单方和持卡人使用地址验证,但您可以在^^之间找到它,但它可能有空白填充,您可以删除。

The part you want is track2 NNNNNNNNNNNNNNNN=1210 where NNNNN=card number PAN, and 1210 = Expiry date. 你想要的部分是track2 NNNNNNNNNNNNNNNN = 1210,其中NNNNN =卡号PAN,1210 =有效期。

Even if track1 is empty (which sometimes it is as it's not used in processing), you will still get the ;?, so you could use the index of the second ; 即使track1为空(有时它是因为它不用于处理),你仍然会得到;?,所以你可以使用第二个的索引; as start of the string and = as the end of the cc# string. 作为字符串的开头,=作为cc#字符串的结尾。 With the 4 characters after the = as the expiry. 在=之后的4个字符作为到期日。

I would advise getting the card holder to sign something in record of the transaction otherwise they could dispute the card and do a charge-back. 我建议让持卡人在交易记录中签名,否则他们可以对卡进行争议并进行退款。

And not all credit cards have exactly two tracks, some uses three tracks. 并非所有信用卡都有两条轨道,有些使用三条轨道。

Only track2 is used for processing and has a standardized format. 只有track2用于处理并具有标准化格式。

Debit cards can't generally be processed (unless they have a visa-debit card or something). 借记卡一般不能处理(除非他们有签证借记卡或其他东西)。

PS you shouldn't store cc data in plain text, so try and keep everything in mem or strong encryption. PS你不应该以纯文本形式存储cc数据,因此请尝试将所有内容保存在mem或强加密中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM