【问题标题】:How can I use credit card numbers containing spaces?如何使用包含空格的信用卡号?
【发布时间】:2010-10-26 22:15:30
【问题描述】:

当检测到未经培训的购物者输入了信用卡/借记卡号时,一些精美的网站会显示错误对话框,因为该卡号打印在带有空格的卡上。是否有可能以某种方式编写一个 Java Web 应用程序来处理这些带有空格的数字,就好像它们是正确的一样?

【问题讨论】:

  • 我无法阻止自己指出它是:“为 Sun 从事 Java SE 安全工作的工程师。”谁在问。请告诉我你是在开玩笑,或者这是某种恶作剧……或者现在是凌晨 4 点,而你只是在睡梦中写问题……
  • @viraptor - 我假设这个问题旨在开始讨论或表达我对这些网站的同样烦恼,或者可能是提问者被问到并希望下次指向人们的问题.
  • 不。空格不能轻易从输入中删除。你不能重新排列用户输入——这对用户来说太过分了。如果一个人不能使用该软件,那是用户的问题。他们需要通过手动删除一长串难以理解的随机数字中的空格来学习使用软件。
  • 肯定有一些框架可以为您做到这一点?嗯,让我们看看... JCCNESPX(Java Credit Card Number Evaluation with Spurious XML Plugin)怎么样?
  • 我问这个问题是因为糟糕的网站让我很恼火。这是一个非常常见的可用性错误示例,经常让我恼火。没有充分的借口。我希望任何在网站上工作的人都对这种用户体验问题有所了解。 (FWIW,我在 SE 工作。有一个 Web 服务器,虽然这只是一小部分。以前我做过一些 Web 工作。很多年前,我致力于开发 Web 内容管理系统(Mediasurface)。)

标签: java string usability


【解决方案1】:

我的观点是,任何拒绝带有空格的信用卡号的 Web 应用程序都没有发挥作用。当您收到信用卡号时,很容易做到:

String ccNumber = ccNumber.replaceAll("[\\s-]+", "");

删除空格和破折号(有些也使用这些)。然后验证结果。如果你强迫他们删除你可以很容易做到的空格,你只会惹恼你的用户。

至于如何进行验证,这取决于很多事情,例如您使用的 Web 框架以及您选择的验证选项。例如,Struts 1 可能使用也可能不使用 Apache Commons Validator,而 Spring MVC 将(可能)使用 Spring 验证等等。所以我无法确切告诉你如何验证,但我可以告诉你什么要验证。

首先应该拒绝带有空格的CC号码。大多数人会发现:

4123 0987 8876 2939

更容易阅读:
4123098788762939

如果用户遗漏或输入错误的数字并需要找出他或她的信用卡号码验证失败的原因,这一点非常重要。这篇文章顶部的 replaceAll() 涵盖了这种情况。

第二件事是您以正确的方式显示信用卡号(即使出于安全原因,某些数字已替换为 X)。我建议你通读Anatomy of Credit Card Numbers

该页面为您提供了位数和有效前缀的规则。一个强大的 Web 应用程序将实现这些以便您在尝试使用信用卡号之前判断它是否无效。将信用卡详细信息提交到支付网关最多可能需要 30 秒(或可能更长时间),因此在您确定付款将被接受之前,您不应该这样做。否则会提供非常糟糕的用户体验。如果失败 1-2 次而不是等待,用户很有可能会放弃。

至于显示它们,这取决于位数:

  • 16:4组,每组4个,用空格隔开;
  • 15:就像American Express 卡片,即 4-6-5,每组之间有一个空格;
  • 14:就像Diners Club 卡片,即 4-6-4,每组之间有一个空格;
  • 13:从未见过 13,但 4-5-4 或 4-4-5 或 5-4-4(或可能是 3-3-3-4)会浮现在脑海中。

作为标准验证例程的一部分,在提交处理之前应根据页面中提到的校验和算法验证信用卡号。该页面具有该例程的 Java 实现。

每个接受信用卡付款的网站都应该绝对最低做到以上所有内容,否则你只是放弃了占用户获得的百分比的业务沮丧。

所以简短的版本是两个简单的规则:

  1. 对用户输入尽可能宽容;和
  2. 在提交之前尽一切可能验证信用卡详细信息。

【讨论】:

  • “请不要空格”的网站在我的书中是一个红色警报。他们还做错了什么?你怎么能信任他们的任何个人信息?
  • 也许最好使用删除任何非数字字符的正则表达式。但是,是的,我同意任何不做这些基本工作的网站都不能正常工作!
  • @Phill Sacre,我不同意。不是数字的东西是无效的,应该这样对待。除了空格或破折号,我的意思是。字母是用户输入错误的标志。
  • "此外,您还应该验证 CVC"
  • 实际上,大多数不允许空格的网站都是用户更正的,因为它们将 CC 字段宽度设置为 16,因此您无法输入带有空格的 MC 或 Visa CC#。
【解决方案2】:

我会尽可能去除所有非数字字符,然后检查长度是否有效,然后再通过 Luhn's algorithm 等真实验证运行用户输入。

String ccNumber = input.replaceAll("\\D", "");

String input 中去除所有非数字。

【讨论】:

  • 我认为包含字母字符的抄送号码存在无法通过验证的情况
  • 那是什么情况?如果出现这种情况,软件应该直接拒绝字符,而不是让用户输入这些字符只是为了以后拍他们的手。
  • 嗨 Bryan,情况是字母字符不像“”、“-”、“/”、“.”等那样用作“间隔符”。就我个人而言,我会将信用卡号输入字段中的字母字符解释为“用户正在输入垃圾”。但我想,正如你和其他人所说的那样,它并没有真正造成伤害。
  • 请记住,软件的目标是为用户服务,而不是相反。
  • @Harry:我同意可以提出一个案例。用户不太可能输入 1234a5678b9012c3456。大多数人会认为这是垃圾,即使其中可能嵌入了有效的 CC 编号。但是,去掉所有非数字可以让诸如 1234|5678 |9012|3456 之类的内容通过,而仅允许某些分隔符(如空格和破折号)会导致此示例失败。我宁愿允许以正确顺序包含正确数字的任何内容,也不愿让用户记住我喜欢它的输入方式。
【解决方案3】:

强迫您以特定格式输入信用卡号码(和类似内容)的网站 - 真让我恼火。

那些人只是因为他们(开发人员)懒惰而给他们的客户带来不便。没有理由不接受信用卡号码、电话号码等提供的任何格式的内容。唯一的限制是理解如何解释值需要什么。

您不必关心我输入的是 5555-4444-3333-2222 还是 5555444433332222,如果您不喜欢它们,只需去掉破折号 - 空格也是如此。对于电话号码,除非您要自动拨打该号码,否则您可能甚至都不关心它的格式,所以除非您必须这样做,否则不要惹恼您的用户。

【讨论】:

    【解决方案4】:

    很遗憾,没有。 Java 无法处理这些要求,因为在 x86 芯片上模拟 Java 虚拟机涉及大量开销,没有空间为 Perl 的正则表达式等有用的构造留下空间,因此可以做到:

    $input =~ s/\D//g;
    

    Java 几年前曾尝试添加正则表达式,但它们只运行在不再使用的 PowerPC 芯片上。问题是所有正则表达式都必须作为字符串包含,而不是作为一流的语言结构,因此需要双反斜杠,但众所周知,反斜杠在 x86 架构的主要操作系统上意味着不同的东西。

    我的建议是升级到 Perl。众所周知,Scheme 能够处理这种情况并在竞争中提供巨大优势,但 Scheme 只能在 LISP 机器上运行。

    【讨论】:

    • 很有趣地得到了我的,但这只是因为我现在有一杯好咖啡:)
    • 很有趣,我因为反常取消了某人的反对票。
    • 最后的方案评论让我觉得它超出了边缘:)
    【解决方案5】:

    很遗憾没有,这就是为什么那些花哨的网站需要向未经培训的购物者显示错误对话框:强制购物者以机器喜欢的格式重新输入他们的号码。

    为什么,如果只有机器可以做“数据处理”,那么机器本身就可以改变数据格式!或者,如果没有“未经训练的”购物者这样的东西就好了!唉!

    【讨论】:

    • Chris,显然 技术上 在客户端的 JavaScript 或服务器端的 Java 中都是可行的。您的意思是合法不可能修改输入吗?用户输入的任何转换都是非法的?
    • 我在开玩笑……开玩笑……事实上,我在撒谎……但我敢肯定,提出所谓“问题”的 OP ',会知道的。
    【解决方案6】:

    您的问题似乎很奇怪,但我认为它应该像通过验证功能运行用户输入的信用卡号码一样简单,该功能首先会删除所有空格。

    无论是否使用正则表达式,这在任何现代语言中都是相当微不足道的。

    【讨论】:

      【解决方案7】:

      简单。

      • 您的输入空间是包含所有字符的某个字符集中的字符列表。
      • 您的输出空间是某个字符集中的字符列表,其中仅包含数字。

      为了解决这个问题,我们创建了一个只包含数字 0 到 9 的中间空间。我们可以为这个有限集创建一个新的枚举。我们将其称为手指空间,因为奇怪的是它包含的成员数量与我们的手指数量相同。

      然后我们编写两个函数。

      1. 将输入空间转换为手指空间
      2. 将手指空间转换为输出空间

      当我们将输入空间减少到手指空间时,我们只需删除手指空间中未找到的任何字符。从手指空间到输出空间的转换更加容易。我们只是在输出空间中找到相同的数字。

      诀窍在于它适用于所有字符集。我还没有弄清楚如何确定某个字符是否与我的手指组中的成员匹配。也许我应该把它作为一个问题发布。

      【讨论】:

      • 哇哇哇,你在跳过步骤!仅仅因为输入空间和输出空间相同,并不意味着您应该缩短流程。您希望一切都被明确定义,因此手指空间清晰明确。
      【解决方案8】:

      汤姆,

      问题在技术上解决了,还是从理论上讲吧。

      这里有两种思想流派。我不认为说“如果用户无法弄清楚那是他们的问题”是一个可以接受的答案。

      1. 对您的用户输入要坚定,并且只接受格式正确的信用卡号。这需要让用户一直停留在页面上,直到他们一切正常。
      2. 通过假设他们的意图并调整他们的输入来更加宽容(只需确保给他们一个确认屏幕以验证新输入)。

      在我看来,#2 是要走的路,您可以使用正则表达式(如上所述)将所有空格、特殊字符等从 cc# 字段中拉出,并让用户不必输入他们的再次提供信息。

      无论哪种方式,您都应告知用户正确的输入形式(即 xxxx-xx-xxxx)

      根据经验,我倾向于欣赏在处理用户输入的方式上更加优雅的网站。

      有关正则表达式的更多提示,请查看正则表达式。信息

      祝你好运,

      -罗伯特

      【讨论】:

        【解决方案9】:

        有许多选项,但最合乎逻辑的似乎是只做一个简单的字符串替换,用一个封闭字符替换所有空格,即''。这会将信用卡字符串减少为一长串数字..然后处理掉

        【讨论】:

          【解决方案10】:

          此网站上的广告... 只需 49.95 美元,您就可以拥有一个与此在线商店兼容的新特殊键盘。单击此处将新键盘添加到您的购物车并结帐。结帐时,请在指定字段中输入您的信用卡号。请不要输入数字之间的空格,因为我们的商店不知道如何处理数字之间的空格。

          【讨论】:

            【解决方案11】:

            我会假设这是一个真实的问题,即使它看起来像是某种巨魔或笑话。

            您应该为您的界面建模,以便用户本能地以受控方式执行输入。简单的说,就是先问他们卡的种类,然后在输入表单上格式化输入,匹配卡。例如,假设像 Visa 或 Mastercard 这样的 16 位卡,显示 4 个输入框,由空格或破折号分隔,每个输入框限制输入 4 个字符,并在用户输入第四位数字后自动移动到系列中的下一个框。

            它在页面上应该如下所示:

            卡号:
            [1234] - [1234] - [1234] - [1234]

            卡号:
            [1234] - [123456] - [12345]

            【讨论】:

            • 你知道,一家真正的信用卡公司自己在前一周也这样做了。我打错了(可能漏掉了一个数字?)并且因为这些框和焦点飞来飞去而经历了一场磨难。
            • 是的,你需要聪明地转移焦点,如果你做了一个愚蠢的实现,只是将焦点转移到字符串长度等于 4 的键输入上,那么删除一个真正的 PITA编号并替换它。
            • 在某些不可预见的情况下,制作一个会导致尴尬的 UI 确实很容易。尤其是在“敌对环境”中。
            【解决方案12】:

            似乎只有一个人提到了 Luhn 或 mod 10 算法

            http://en.wikipedia.org/wiki/Luhn_algorithm

            【讨论】:

            • 是否需要多次提及?
            【解决方案13】:

            当然。压缩空间。可能有无数种方法可以做到这一点;我很想使用 String.split() 将其分解为空格,然后连接产生的四个字符串。或者使用 StringBuffer.indexOf() 和 .delteCharAt()。

            ...或者如 Cletus 所说,使用 replaceAll()。

            【讨论】:

              【解决方案14】:

              您可以通过使用onkeypress 事件来使用javascript 验证来检查最后一个字符是否有效,如果不正确,则将其删除,甚至可能会闪现一条消息,指出输入了无效字符。这种方式永远不会输入无效数字。它还可以自动输入您想要的格式的分隔符(空格或-)。

              【讨论】:

                【解决方案15】:

                早在 1998 年,我就在一家只允许使用 Visa 的商店中编写了这对 Perl 函数(……实际上是在 Visa 中……)。

                sub mod10_checkdigit
                {
                    my($acct) = @_;
                    die "invalid account number in BRPS::mod10_checkdigit"
                        unless $acct =~ m%^\d+$%;
                    my(@digits) = split //, $acct;
                    my($len) = scalar(@digits);
                    print "# ($len) @digits\n" if ($ENV{PERL_BRPS_DEBUG});
                    my($i, $sum, $chk);
                    my($mul) = (($len % 2) == 1) ? 1 : 2;
                    $len--;
                    for ($i = 0; $i < $len; $i++)
                    {
                        my($val) = $mul * $digits[$i];
                        # Note that we need the digital root of the value, but $val is not
                        # greater than 18 (because $digits[$i] <= 9 and $mul <= 2).
                        $val -= 9 if ($val >= 10);
                        $sum += $val;
                        print "# $i: $digits[$i] * $mul => $val => $sum\n" if ($ENV{PERL_BRPS_DEBUG});
                        $mul = 3 - $mul;
                    }
                    $chk = 10 - ($sum % 10);
                    $chk = 0 if ($chk == 10);
                    return $chk;
                }
                
                sub validate_account
                {
                    my($acct) = @_;
                    # Strip leading and trailing blanks
                    $acct =~ s/^\s*(\S.*\S)\s*$/$1/;
                    my($clean) = $acct;
                    # Check that account number is string of digits, blanks and dashes
                    return undef, "account number is not a sequence of digits, blanks and dashes"
                        unless $acct =~ m/^[- \d]+$/;
                    return undef, "account number is not a Visa account number"
                        unless $acct =~ m/^4/;
                    # Remove non-digits
                    $clean =~ s/\D//g;
                    return undef, "account number is neither 13 nor 16 digits"
                        unless length($clean) == 16 || length($clean) == 13;
                    # Punctuators must be reasonably consistent!
                    return undef, "invalid punctuation pattern"
                        unless ($acct =~ m/^\d{16}$/o or $acct =~ m/^\d{13}$/o or
                                $acct =~ m/^\d{4}[- ]\d{4}[- ]\d{4}[- ]\d{4}$/o or
                                $acct =~ m/^\d{4}[- ]\d{3}[- ]\d{3}[- ]\d{3}$/o);
                    # Determine check digit
                    my($chk) = mod10_checkdigit($clean);
                    return undef, "check digit on account number is incorrect"
                        unless $clean =~ m/$chk$/;
                    return $clean, "ok";
                }
                

                允许可信的信用卡号码通过。概括起来处理万事达卡、发现卡、美国运通卡也不难。

                咆哮

                我确实喜欢那些坚持要求我以内部格式输入数据的网站。 Dammit - 将数字存储为一个大整数并将其作为纯数字字符串发送;这对计算机来说很好。但请让我输入可识别的人类可读格式 - 甚至以人类可读的格式重新呈现数据。处理信用卡号码的网站太懒惰了。

                【讨论】:

                  【解决方案16】:

                  如果您指的是 javascript,则可以使用“前进到下一个输入”方法:

                  这是 HTML:

                  <form id="ccform" action="cc_submit.php" method="post">
                      <fieldset id="ccnumber">
                          <input id="firstset" type="text" maxlength="4" />
                          <input id="secondset" type="text" maxlength="4" />
                          <input id="thirdset" type="text" maxlength="4" />
                          <input id="fourthset" type="text" maxlength="4" />
                      </fieldset>
                  </form>
                  

                  这是 JS:

                  var ccfields;
                  
                  function moveToNext(e) {
                      var field = e.currentTarget;
                      var chars = field.value.length;
                      var setnumb = Number(field.id.substr(3,1)) - 1;
                      if(chars >= 4 && setnumb < 3) {
                          ccfields[setnumb + 1].focus();
                      }
                  }
                  
                  window.onload = function() {
                      ccfields = document.getElementById("ccnumber").getElementsByTagName("input");
                      for (var i = 0; i < ccfields.length; i++) {
                          ccfields[i].onkeyup = moveToNext;
                      }
                  };
                  

                  当然,您需要添加一个检查非数字的函数和一个用于获取四个字段并将它们合并为一个字符串以传回表单的函数。使用像 Jquery 这样的 js 库来确保以相同的方式处理事件并简化对输入的遍历也不是一个坏主意,这样您就可以使用“名称”等属性而不会造成任何混淆。

                  但一般来说,如果人们看到 4 个字段,则更容易输入他们的数字,并且对于那些认为“啊,我必须为每个数字都使用鼠标”的访问者来说(或者至少我是) 很高兴该页面足够智能,可以知道移动到下一个字段。

                  【讨论】:

                  • 如果我执行左箭头、shift tab 甚至退格之类的操作,是否会导致问题?另外,我的一张卡有 19 位数字。
                  • 请不要 - 这是一个可怕的想法。当网站认为您已在此类字段中完成输入时,它们会尝试“提供帮助”并移至下一个字段,它们会做两件事。第一,他们让人们跳到下一个字段,因为他们知道他们已经完成跳过字段并且必须返回。第二,如果你打错了,他们几乎不可能更正一个字段。
                  【解决方案17】:

                  解决方案 1:只需放入 4 个可以输入 4 位数字的文本框。就像输入软件的许可证密钥的方式一样。您可以在输入空格字符或制表符后触发框更改为下一行。

                  解决方案 2:使用上述任一 cmets 中提到的正则表达式。问题是,如果它是一个 Web 应用程序,您将很容易受到注入攻击。

                  【讨论】:

                    【解决方案18】:

                    JavaTM 非常适合服务器端编程,而 javascript 在客户端可能有用;例如在验证期间。

                    有效信用卡号的长度在 12(例如 Maestro)和 19(例如 Solo、Switch)之间变化。客户端 javascript 可以找出卡号是否有效(只有数字(带有破折号或空格),适合发行者权限,校验和的确定,...)并从“人类可读”执行 1:1 映射(例如

                    American Express    3400 0100 2000 009
                    

                    ) 到内部表示,例如

                    <input ... id="ccid" value="340001002000009">
                    <input ... id="ccissuer" value="AMEX">
                    

                    一旦信用卡信息验证通过验证输入,值可以透明地转换为内部形式on-submit

                    可以在网上找到一个简单的 javascript 函数的例子,它可以对这个问题进行一些检查,例如这里http://www.braemoor.co.uk/software/creditcard.shtml

                    关于答案的描述就这么多了。

                    【讨论】:

                      猜你喜欢
                      • 2023-03-17
                      • 2012-04-09
                      • 1970-01-01
                      • 1970-01-01
                      • 2016-08-18
                      • 1970-01-01
                      • 2018-05-22
                      • 2013-05-10
                      • 2011-11-02
                      相关资源
                      最近更新 更多