从磁条解析信用卡输入
Posted
技术标签:
【中文标题】从磁条解析信用卡输入【英文标题】:Parse Credit Card input from Magnetic Stripe 【发布时间】:2011-01-08 11:26:41 【问题描述】:有人知道如何解析从磁卡刷卡器输入的信用卡字符串吗?
我尝试了一个 javascript 解析器,但始终无法正常工作。这就是输入的样子。
%BNNNNNNNNNNNNNNNN^DOE/JOHN
^1210201901000101000100061000000?;NNNNNNNNNNNNNNNN=12102019010106111001?
N 是信用卡号。
【问题讨论】:
也许他想进入信用卡终端业务? 我的妻子是一名摄影师,我是一名开发人员。我们刚刚注册了 PayPal Website Payments Pro,并希望能够在路上处理 CC(贸易展览等)。 我确实找到了一个用于 FireFox 的 GreaseMonkey 插件,它允许我在 PayPal 虚拟终端上向右滑动。虽然不理想,但如果我无法解决这个问题,它会起作用。 【参考方案1】:见Magnetic Stripe Card entry @ Wikipedia:
曲目一,格式 B:
开始标记 - 一个字符(通常为 '%') 格式代码="B" — 一个字符(仅限字母) 主帐号 (PAN) — 最多 19 个字符。通常,但不是 始终与信用卡号匹配 印在卡片的正面。 字段分隔符 - 一个字符(通常为 '^') 名称 - 2 到 26 个字符 字段分隔符 - 一个字符(通常为 '^') 到期日期 - YYMM 格式的四个字符。 服务代码 - 三个字符 自主数据 — 可能包括 Pin 验证密钥指示器 (PVKI, 1 个字符),PIN 验证值 (PVV,4 个字符),卡片验证 价值或卡验证码 (CVV 或 CVK,3 个字符) 结束标记 - 一个字符(通常为“?”) 纵向冗余校验 (LRC) — 一个字符(大多数阅读器设备 刷卡时不返回此值 被刷到表现层, 并仅使用它来验证输入 内部给读者。)
我希望数据是假的,否则任何人都可以得到:
我不确定,但我认为可以使用 LRC 计算信用卡号(或可能性数)。
【讨论】:
如果您正在写入磁条阅读器,并且使用原始写入模式,那么计算 LRC 算法可能会有点混乱,您必须自己进行,如果您使用正常写入模式,刷卡机将为您处理。但是读取时,刷机会自动计算LRC来验证刷机是否成功。 CVV 未显示在曲目中 -- 是否可计算? CVV 不在曲目中,这是故意的。它为网络所知,并用于验证交易发生时卡是否存在。【参考方案2】:我做得更好:我制作了一个视频,展示了如何使用 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);
完整的代码在我上面链接的那个网站上。
【讨论】:
【参考方案3】:据我所知:
这是一个双磁道磁条数据——第一磁道以%
开始,以?
结束,第二磁道以;
开始,以?
结束。这些是开始/结束标记。
第一首曲目是字母数字,第二首曲目是数字,第三首曲目也是数字(如果我没记错的话)。
开始/结束标记之间的数据可以根据磁条的记录密度而变化。密度越高,可以在一个轨道上记录的越多。
使用正则表达式获取数据可能不是挑选所需信息的可靠方法。
并不是所有的信用卡都有两个轨道,有些使用三个轨道。
【讨论】:
【参考方案4】:一般而言,对于无卡交易(即 MOTO 交易),您需要 cc#、到期时间以及可能的 CVV(又名 CVC2 等)。您可以通过刷卡获得前 2 个,就像在轨道数据中一样。 CVV 印在卡片上。
卡上的名字并不重要。除非您的收单行和持卡人正在使用地址验证,但您可以发现 ^^ 之间可能有空白填充,您可以删除。
您想要的部分是 track2 NNNNNNNNNNNNNNNN=1210,其中 NNNNN=卡号 PAN,1210 = 到期日期。
即使 track1 为空(有时它在处理中未使用),您仍然会得到 ;?,因此您可以使用第二个 ; 的索引作为字符串的开头和 = 作为 cc# 字符串的结尾。以 = 后的 4 个字符为过期。
我建议让持卡人在交易记录中签名,否则他们可能会对卡提出异议并进行退款。
并不是所有的信用卡都有两个轨道,有些使用三个轨道。
只有 track2 用于处理并具有标准化格式。
借记卡通常不能被处理(除非他们有签证借记卡或其他东西)。
附:您不应该以纯文本形式存储 cc 数据,因此请尝试将所有内容保存在内存或强加密中。
【讨论】:
【参考方案5】:试试这个: https://github.com/pdamer/CardReader/blob/master/CardReader.js 或这个: http://blog.cnizz.com/2008/10/16/javascript-snippet-for-handling-credit-card-readers/
我认为你需要什么
【讨论】:
第一个链接是我到处寻找的。它似乎有跟踪数据验证,甚至超时。幸运或不幸的是,这是一段令人印象深刻的代码,这完全超出了我的实现能力。您能否添加一个有关如何使用 CardReader 功能的示例?在我将作为单独帖子添加的轨道阅读代码中,我有一个在 CardReader 中看不到的监听器。 刚刚在下面发布了我的轨道阅读器和解析器代码...需要集成 CardReader 验证【参考方案6】:这是我的代码:
第一个获取数据的侦听器....此数据需要验证,我正在寻求帮助。良好的滑动效果很好,但糟糕的滑动会导致解析器出错。
$('#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" );
【讨论】:
我添加了 if(card_data.indexOf('=') === -1 || card_data.indexOf('^')===-1) 来验证刷卡数据,这样就摆脱了刷卡不好,但这与 auth.net 请求的纵向冗余检查并不完全相同:“必须为从 Track 读取的数据计算纵向冗余检查 (LRC),并与从 Track 读取的 LRC 进行比较Track. 当没有检测到字符奇偶校验错误并且计算和读取的 LRC 匹配时,假定读取 Track 数据没有错误。 - 卡片礼物指南以上是关于从磁条解析信用卡输入的主要内容,如果未能解决你的问题,请参考以下文章