Разбор кредитной карты с магнитной полосы - PullRequest
21 голосов
/ 23 января 2010

Кто-нибудь знает, как проанализировать ввод строки кредитной карты с магнитной карты Swiper?

Я попробовал парсер JavaScript, но так и не заработал. Вот как выглядит вход.

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

N - номер кредитной карты.

Ответы [ 6 ]

24 голосов
/ 23 января 2010

См. Запись Карта с магнитной полосой в Википедии :


Трек первый, формат B:

  • Начать страж - один символ (обычно '%')
  • Формат кода = "B" - один символ (только альфа) * ​​1016 *
  • Основной номер счета (PAN) - до 19 символов. Обычно, но не всегда соответствует номеру кредитной карты напечатано на лицевой стороне карточки.
  • Разделитель полей - один символ (обычно '^')
  • Имя - от двух до 26 символов
  • Разделитель полей - один символ (обычно '^')
  • Срок годности - четыре символа в форме ГГММ.
  • Сервисный код - три символа
  • Дискреционные данные - могут включать в себя ключевой индикатор проверки PIN (PVKI, 1 символ), значение подтверждения PIN (PVV, 4 символа), Проверка карты Значение или код подтверждения карты (CVV или CVK, 3 символа)
  • Конечный страж - один символ (обычно '?')
  • Проверка продольной избыточности (LRC) - один символ (большинство считывающих устройств) не возвращайте это значение, когда карта проведен до уровня представления, и использовать его только для проверки ввода внутренне к читателю.)

Я надеюсь, что данные являются поддельными, в противном случае Кто угодно может получить:

  • Имя
  • Срок годности

И я не уверен, но я думаю, что номер кредитной карты (или # возможностей) можно вычислить с помощью LRC.

6 голосов
/ 09 декабря 2011

Я сделал вас лучше: я сделал видео, показывающее, как сделать это с 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);
        }
    }

Полный код находится на том сайте, на который я ссылался выше.

2 голосов
/ 23 января 2010

Из того, что я помню:

Это данные с двумя дорожками магнитной полосы - первая дорожка начинается с % и заканчивается ?, вторая дорожка начинается с ; и заканчивается ?. Это маркеры начала / конца.

Первая дорожка буквенно-цифровая, вторая дорожка числовая, и есть третья дорожка, которая также числовая (если моя память работает правильно).

Данные между маркерами начала / конца могут изменяться в зависимости от плотности записи магнитной полосы. Чем выше плотность, тем больше она может быть записана на одну дорожку.

Использование регулярных выражений для получения данных может быть ненадежным способом получения необходимой информации.

И не все кредитные карты имеют ровно две дорожки, некоторые используют три дорожки.

1 голос
/ 24 декабря 2014

вот мой код:

1-й слушатель, чтобы получить данные .... эти данные нуждаются в проверке, по которой я ищу помощи. Хороший удар работает нормально, но плохой удар вызовет ошибку в синтаксическом анализаторе.

$('#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 голос
/ 20 сентября 2014
1 голос
/ 02 июня 2010

Как правило, для транзакции без карты (т.е. транзакции MOTO) вам понадобится cc #, срок действия и, возможно, CVV (он же CVC2 и т. Д.). Вы можете получить первые 2 из карт, как это в данных трека. CVV напечатан на карте.

Имя на карточке не имеет большого значения. Если ваш приобретатель и владелец карты не используют проверку адреса, но вы можете обнаружить, что между ^^ может быть заполнение пробелами, которые вы можете удалить.

Требуемая деталь: track2 NNNNNNNNNNNNNNNN = 1210, где NNNNN = номер карты PAN, а 1210 = срок действия.

Даже если track1 пуст (что иногда так и не используется при обработке), вы все равно получите;?, Так что вы можете использовать индекс второго; как начало строки и = как конец строки cc #. С 4 символами после = в качестве срока действия.

Я бы посоветовал владельцу карты подписать что-либо в записи о транзакции, в противном случае они могут оспорить карту и произвести возврат.

И не все кредитные карты имеют ровно две дорожки, некоторые используют три дорожки.

Только track2 используется для обработки и имеет стандартизированный формат.

Дебетовые карты, как правило, не могут быть обработаны (если у них нет визовой дебетовой карты или чего-либо еще).

P.S. Вы не должны хранить данные cc в виде простого текста, поэтому старайтесь хранить все в памяти или надежном шифровании.

...