LuhnCalc and bpay MOD10 version 5


LuhnCalc and bpay MOD10 version 5

我使用以下PHP代码来计算BPay的CRN:

<?php
function LuhnCalc($number) {
  $chars = array_reverse(str_split($number, 1));
  $odd = array_intersect_key($chars, array_fill_keys(range(1, count($chars), 2), null));
  $even = array_intersect_key($chars, array_fill_keys(range(0, count($chars), 2), null));
  $even = array_map(function($n) { return ($n >= 5)?2 * $n - 9:2 * $n; }, $even);
  $total = array_sum($odd) + array_sum($even);
  return ((floor($total / 10) + 1) * 10 - $total) % 10;
}
print LuhnCalc($_GET['num']);
?>

但是,BPAY 似乎是 MOD 5 的版本 10,我找不到任何文档。它似乎与 MOD10 不同。

测试的以下数字:

2005,1597,3651,0584,9675

bPAY
2005 = 20052
1597 = 15976
3651 = 36514
0584 = 05840
9675 = 96752
MY CODE 
2005 = 20057 
1597 = 15974 
3651 = 36517 
0584 = 05843 
9675 = 96752

如您所见,它们都与BPAY数字不匹配。

这个 PHP 函数将基于 mod10 版本 5 算法生成 BPay 参考编号。

谁知道为什么 BPay 不能将其添加到他们的网站。我只是通过谷歌搜索找到算法被称为"MOD10V05"而不是"Mod 10 版本 5"才找到解释。

function generateBpayRef($number) {
    $number = preg_replace("/'D/", "", $number);
    // The seed number needs to be numeric
    if(!is_numeric($number)) return false;
    // Must be a positive number
    if($number <= 0) return false;
    // Get the length of the seed number
    $length = strlen($number);
    $total = 0;
    // For each character in seed number, sum the character multiplied by its one based array position (instead of normal PHP zero based numbering)
    for($i = 0; $i < $length; $i++) $total += $number{$i} * ($i + 1);
    // The check digit is the result of the sum total from above mod 10
    $checkdigit = fmod($total, 10);
    // Return the original seed plus the check digit
    return $number . $checkdigit;
}

这是一种在 SQL Server 中使用 t-sql 用户定义函数实现"MOD10V5"算法(或"mod 10 版本 5")的方法。它接受最多 9 个字符的客户 ID,并返回 11 个字符的 CRN(客户参考编号)。

我还在我的 CustomerID 的开头预置了一个版本号,如果您认为将来可能会更改它,您也可以这样做。

CREATE Function [dbo].[CalculateBPayCRN]
(
    @CustomerID nvarchar(9)
)
RETURNS varchar(11)
AS
BEGIN
    DECLARE @NewCRN nvarchar(11)
    DECLARE @Multiplier TINYINT 
    DECLARE @Sum int
    DECLARE @SubTotal int  
    DECLARE @CheckDigit int  
    DECLARE @ReturnVal BIGINT
    SELECT @Multiplier = 1
    SELECT @SubTotal = 0
    -- If it's less than 9 characters, pad it with 0's, then prepend a '1'
    SELECT  @NewCRN = '1' + right('000000000'+ rtrim(@CustomerID), 9)
    -- loop through each digit in the @NewCRN, multiple it by the correct weighting and subtotal it:
    WHILE @Multiplier <= LEN(@NewCRN)  
    BEGIN  
        SET @Sum =  CAST(SUBSTRING(@NewCRN,@Multiplier,1) AS TINYINT) * @Multiplier  
        SET @SubTotal = @SubTotal + @Sum  
        SET @Multiplier = @Multiplier + 1  
    END 
    -- mod 10 the subtotal and the result is our check digit
    SET @CheckDigit = @SubTotal % 10  
    SELECT @ReturnVal = @NewCRN + cast(@CheckDigit as varchar)
    RETURN @ReturnVal
END
GO

PHP 中的 Modula 10 V1。针对我的 Windows dataflex 例程进行了测试,它是相同的。

function generateBpayRef($number) {
    //Mod 10 v1
    $number = preg_replace("/'D/", "", $number);
    // The seed number needs to be numeric
    if(!is_numeric($number)) return false;
    // Must be a positive number
    if($number <= 0) return false;
    $stringMemberNo =  "$number";
    $stringMemberNo = str_pad($stringMemberNo, 6, "0", STR_PAD_LEFT);  
    //echo " Padded Number is $stringMemberNo  ";
    $crn = $stringMemberNo;
    for($i=0;$i<7;$i++){
           $crnval = substr($crn,(5-$i),1);
           $iPartVal = $iWeight * $crnval;
           if($iPartVal>9){
             //echo " Greater than 9: $iPartVal ";
             $firstChar = substr($iPartVal,0,1); 
             $secondChar = substr($iPartVal,1,1);                           
             $iPartVal=$firstChar+$secondChar;
             //$iPartVal -= 9;
           }
           $iSum+=$iPartVal;
           $iWeight++;
           if ($iWeight>2){$iWeight=1;}
           //echo " CRN: $crnval ] Weight: $iWeight ] Part: $iPartVal ] SUM: $iSum ";
    }
    $iSum %= 10;
    if($iSum==0){
        //echo " zero check  is $iSum ";                    
        //return $iSum;
    }
    else{
        //return 10-$iSum;
        $iSum=(10-$iSum);
    }
    //echo " Check is a $iSum ";                                        
    $BpayMemberNo  = $stringMemberNo . $iSum ;
    echo " New: $BpayMemberNo ";
    return ($BpayMemberNo);
}

这是我为 Mod 10 v5 快速编写的 Ruby 类

module Bpay
  class CRN
    attr_accessor :number, :crn
    class << self
      def calculate_for(number)
        new(number).crn
      end
    end
    def initialize(number)
      @number = number
      calculate
    end
    def calculate
      raise ArgumentError, "The number '#{number}' is not valid" unless valid?
      digits      = number.to_s.scan(/'d/).map { |x| x.to_i }
      raise ArgumentError, "The number '#{number}' must be at least 2 digits in length" if digits.size < 2
      check_digit = digits.each_with_index.map { |d, i| d * (i + 1) }.inject(:+) % 10
      @crn = "#{number}#{check_digit}"
    end
    def valid?
      return false unless !!Integer(number.to_s) rescue false
      return false if number.to_i <= 0
      true
    end
  end
end

这是在 C# 中,但这是我到目前为止对 BPay 校验位生成的:

private void btnBPayGenerate_Click(object sender, EventArgs e)
{
    var originalChars = txtBPayNumber.Text.ToCharArray();
    List<int> oddDigits = new List<int>();
    List<int> evenDigits = new List<int>();
    int oddTotal = 0, evenTotal = 0, total = 0, checkDigit ;
    const int oddMultiplier = 3;
    const int modulus = 10;
    bool isOdd = true;
    for (int x = 0; x < originalChars.Length; x++)
    {
        if(isOdd)
                oddDigits.Add(Int32.Parse(originalChars[x].ToString()));
            else
                evenDigits.Add(Int32.Parse(originalChars[x].ToString()));
            isOdd = !isOdd;
        }
        foreach (var digit in oddDigits)
            oddTotal += digit;
        foreach (var digit in evenDigits)
            evenTotal += digit;
        oddTotal = oddTotal * oddMultiplier;
        total = oddTotal + evenTotal;
        checkDigit = (modulus - (total % modulus));
lblBPayResult.Text = txtBPayNumber.Text + checkDigit.ToString();
}

还没有完成测试,一旦BPAY回复我,我会回发。

编辑:试试这个:https://gist.github.com/1287893

我必须为 javascript 制定一个版本,这就是我想出的。它在原始问题中正确生成预期数字。

var startingNumber = 2005;
var reference = startingNumber.toString();
var subTotal = 0;
for (var x = 0; x < reference.length; x++) {
    subTotal += (x + 1) * reference.charAt(x);
}
var digit = subTotal % 10;
var bpayReference = reference + digit.toString();
这是我

使用 vb.net 创建的函数来计算 mod 10 版本 5 校验位

Private Function CalcCheckDigit(ByRef psBaseNumber As String) As String
    Dim lCheckDigit, iLoop As Integer
    Dim dCalcNumber As Double
    lCheckDigit = 0
    dCalcNumber = 0
    For iLoop = 0 To (psBaseNumber.Length - 1)
        lCheckDigit = lCheckDigit + (psBaseNumber.Substring(iLoop, 1) * (iLoop + 1))
    Next iLoop
    lCheckDigit = lCheckDigit Mod 10
    CalcCheckDigit = psBaseNumber & CStr(lCheckDigit)
End Function