web-dev-qa-db-ja.com

Magnetic Stripeからのクレジットカード入力の解析

Magnetic Card Swiperから入力されたクレジットカード文字列を解析する方法を知っている人はいますか?

JavaScriptパーサーを試しましたが、動作しませんでした。これは、入力がどのように見えるかです。

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

Nはクレジットカード番号です。

24
Chase Florell

Magnetic Stripe Card entry @ Wikipedia」を参照してください


トラック1、フォーマットB:

  • 歩哨の開始— 1文字(通常は '%')
  • 形式code = "B" — 1文字(アルファのみ)
  • プライマリアカウント番号(PAN)—最大19文字。通常、ただし常にではありませんが、カードの表面に印刷されているクレジットカード番号と一致します。
  • フィールド区切り文字— 1文字(通常は '^')
  • 名前— 2〜26文字
  • フィールド区切り文字— 1文字(通常は '^')
  • 有効期限— YYMM形式の4文字。
  • サービスコード— 3文字
  • 任意データ—ピン検証キーインジケーター(PVKI、1文字)、PIN検証値(PVV、4文字)、カード検証値またはカード検証コード(CVVまたはCVK、3文字)を含む場合があります)
  • 終了センチネル— 1文字(通常は「?」)
  • 縦方向冗長検査(LRC)— 1文字(ほとんどのリーダーデバイスは、カードがプレゼンテーションレイヤーにスワイプされてもこの値を返さず、リーダーへの入力を検証するためにのみ使用します。)

データが偽物であることを願っています 誰でも入手できます:

  • 名前
  • 賞味期限
  • CVV

よくわかりませんが、クレジットカード番号(または可能性の数)はLRCを使用して計算できます。

25
Alix Axel

ASP.Net/c#でこれを正確に行う方法を示すビデオを作成しました。

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

以下は、おそらく気になるコードのセクションです。

    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);
        }
    }

完全なコードは、上記でリンクしたWebサイトにあります。

7
Mark Hagan

私が覚えていることから:

これは2トラックの磁気ストリップデータです。最初のトラックは%で始まり?で終わり、2番目のトラックは;で始まり?で終わります。これらは開始/終了マーカーです。

最初のトラックは英数字、2番目のトラックは数値、3番目のトラックも数値です(私の記憶が正しい場合)。

開始/終了マーカー間のデータは、磁気ストリップの記録密度に応じて変化する可能性があります。密度が高いほど、1つのトラックに多く記録できます。

正規表現を使用してデータを取得することは、必要な情報を抽出するための信頼できる方法ではない場合があります。

また、すべてのクレジットカードに2つのトラックがあるわけではなく、3つのトラックを使用するカードもあります。

2
t0mm13b

これが私のコードです:

データを取得する最初のリスナー....このデータには、私が助けを求めている検証が必要です。良いスワイプはうまく機能しますが、悪いスワイプはパーサーでエラーを引き起こします。

$('#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?

});

そして、これがパーサーです。すべての変数を破棄して、ブラウザーに残っていないようにすべての変数を破棄できることに注意してください。

    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" );
}
1
Iannazzi

これを試してください: https://github.com/pdamer/CardReader/blob/master/CardReader.js またはこれ: http://blog.cnizz.com/2008/10/ 16/javascript-snippet-for-handling-credit-card-readers /

私はあなたが必要とするものだと思います

1
Ana El Bembo

通常、カードが存在しないトランザクション(つまり、MOTOトランザクション)の場合、cc#、有効期限、および場合によってはCVV(別名CVC2など)が必要になります。トラックデータでは、最初の2つをカードスワイプから取得できます。 CVVはカードに印刷されています。

カード上の名前はそれほど重要ではありません。アクワイアラーとカード所有者が住所確認を使用している場合を除いて、^^の間には、削除できる空白のパディングがある場合があります。

必要な部分はtrack2 NNNNNNNNNNNNNNNN = 1210で、NNNNN =カード番号PAN、1210 =有効期限です。

Track1が空の場合でも(処理で使用されない場合もあります)、引き続き;?を取得するため、2番目の;のインデックスを使用できます。文字列の始まりと=、cc#文字列の終わりとして。 =の後の4文字を有効期限とします。

カード所有者に取引の記録にある何かに署名してもらうことをお勧めします。そうしないと、カード所有者がカードに異議を唱え、チャージバックを行う可能性があります。

また、すべてのクレジットカードに2つのトラックがあるわけではなく、3つのトラックを使用するカードもあります。

Track2のみが処理に使用され、標準化されたフォーマットを持っています。

通常、デビットカードは処理できません(ビザデビットカードなどがなければ)。

追伸CCデータをプレーンテキストで保存しないでください。すべてをメモリまたは強力な暗号化で維持してください。

1
Kakkerlakkie