有谁知道如何解析从磁卡Swiper输入的信用卡字符串?
我尝试了一个JavaScript解析器,但从来没有让它工作。这就是输入的样子。
%BNNNNNNNNNNNNNNNN^DOE/JOHN
^1210201901000101000100061000000?;NNNNNNNNNNNNNNNN=12102019010106111001?
N是信用卡号。
答案 0 :(得分:24)
请参阅Magnetic Stripe Card entry @ Wikipedia:
跟踪一,格式B:
- 开始哨兵 - 一个角色(通常是'%')
- 格式代码=“B” - 一个字符(仅限alpha)
- 主帐号(PAN) - 最多19个字符。通常,但不是 总是,匹配信用卡号码 印在卡片正面。
- 字段分隔符 - 一个字符(通常为“^”)
- 名称 - 2到26个字符
- 字段分隔符 - 一个字符(通常为“^”)
- 到期日期 - YYMM格式的四个字符。
- 服务代码 - 三个字符
- 全权委托数据 - 可能包括Pin验证密钥指示符(PVKI, 1个字符),PIN验证值 (PVV,4个字符),卡片验证 价值或卡片验证码(CVV 或CVK,3个字符)
- 结束哨兵 - 一个角色(通常是'?')
- 纵向冗余校验(LRC) - 一个字符(大多数读卡器设备 卡时不要返回此值 被刷到表示层, 并仅用它来验证输入 内部给读者。)
我希望数据是假的,否则任何人都可以获得:
我不确定,但我认为可以使用LRC计算信用卡号(或可能性数)。
答案 1 :(得分:6)
我做得更好了:我制作了一个视频,展示如何使用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 :(得分:2)
从我记忆中来看:
这是一个双轨磁条数据 - 第一首曲目以%
开头,以?
结尾,第二首曲目以;
开头,以?
结尾。这些是开始/结束标记。
第一首曲目是字母数字,第二首曲目是数字曲目,还有第三首曲目也是数字曲目(如果我的记忆正确无误)。
开始/结束标记之间的数据可以根据磁条的记录密度而变化。密度越高,在一条轨道上记录的越多。
使用正则表达式获取数据可能不是一种可靠的方法来选择所需的信息。
并非所有信用卡都有两条轨道,有些使用三条轨道。
答案 3 :(得分:1)
通常,对于不存在卡的交易(即MOTO交易),您将需要cc#,到期以及可能需要CVV(也称为CVC2等)。您可以在轨道数据中从刷卡中获取前2个。 CVV印在卡上。
卡上的名字并不重要。除非您的收单方和持卡人使用地址验证,但您可以在^^之间找到它,但它可能有空格填充,您可以删除。
你想要的部分是track2 NNNNNNNNNNNNNNNN = 1210,其中NNNNN =卡号PAN,1210 =有效期。
即使track1为空(有时它在处理中没有使用),你仍然会得到;?,所以你可以使用第二个的索引;作为字符串的开头,=作为cc#字符串的结尾。在=之后的4个字符作为到期日。
我建议让持卡人在交易记录中签名,否则他们可以对卡进行争议并进行退款。
并非所有信用卡都有两条轨道,有些使用三条轨道。
只有track2用于处理并具有标准化格式。
借记卡一般不能处理(除非他们有签证借记卡或其他东西)。
P.S。您不应该以纯文本格式存储cc数据,因此请尝试将所有内容保存在mem或强加密中。
答案 4 :(得分:1)
试试这个: https://github.com/pdamer/CardReader/blob/master/CardReader.js 或这个: http://blog.cnizz.com/2008/10/16/javascript-snippet-for-handling-credit-card-readers/
我认为你需要什么
答案 5 :(得分: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" );
}