7

I am using the following PHP code to calculate a CRN for BPay:

<?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']);
?>

However it seems that BPAY is version 5 of MOD 10, for which I can't find any documentation. It seems to not be the same as MOD10.

The following numbers where tested:

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

As you can see, none of them match the BPAY numbers.

Rocklan
  • 7,888
  • 3
  • 34
  • 49
RussellHarrower
  • 6,470
  • 21
  • 102
  • 204
  • Trying to not to be mean, did you even google this? Where's the question? Did you check the BPAY API and try to supply them with your customer ID algorithm? – Krista K Jun 15 '12 at 23:29
  • Yes we have, we know we are using the right Luhn but just our maths is a little wrong. – RussellHarrower Jun 17 '12 at 05:44
  • If BPay has an API, they should furnish you with the checksum. I would put effort into getting them to confirm your work. In the interim, maybe the Math SE site would be entertained by this? – Krista K Jun 20 '12 at 16:36
  • Thanks I have posted it on there. – RussellHarrower Jun 21 '12 at 00:24
  • @Dermot so using that PDF, how do I do I do the maths? – RussellHarrower Jul 17 '12 at 12:41
  • @RussellHarrower Im actually still trying to get mine working. I've contacted BPay directly as im finding different algorithm descriptions online, and none of them seem to be able to consistently give me the correct answer. I have coded the above pdf description in c#, I can post it tomorrow. I understand you're using php, but I haven't used anything special in my code so im sure it'll be easy to translate. Will post it from work tomorrow. – Dermot Jul 17 '12 at 14:29
  • BPAY supports a lot of check digit algorithms. These can be configured for a particular BPAY biller code. Your bank should provide you a description of each algorithm which is available. Mostly commonly used is MOD10V01 (luhn), but there is a long list of algorithms. – WW. Nov 20 '14 at 05:28
  • Just a note to everyone who might come here: All of the code I saw below treats CRN's as ints. This is OK for some implementations, but a lot of customers have fixed length CRNs, where the whole length must equal 8. If you use leading 0's, then your number is no longer an int, it's a string - and the leading 0's totally change the checksum digit that you require. So make note if you're using any of the answers from this post - they are technically correct, but may not work depending on the actual implementation that your customer is using. – Mark Henderson Mar 20 '15 at 02:19

7 Answers7

4

This PHP function will generate BPay reference numbers based on the mod10 version 5 algorithm.

Who knows why BPay can't add this to their website. I only found an explanation by googling finding the algorithm being called "MOD10V05" instead of "Mod 10 version 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;

}
b.b3rn4rd
  • 8,494
  • 2
  • 45
  • 57
Nick Adams
  • 108
  • 1
  • 6
  • that worked, Not sure why they cant either. now only wish the bank would inform me when sameone pays with bpay – RussellHarrower Jul 26 '12 at 01:50
  • @RussellHarrower If you mean a real-time notification, then this is fundamentally difficult with BPAY because the payer may be at a different bank and the banks only exchange batch files each night. – WW. Nov 20 '14 at 05:30
  • @nick-adams is there a source reference for MOD10V05? I've discovered the same as you but not seeing an authoritative source (e.g. ISO Standard or similar). Not critical of you, but it just seems like this space isn't well documented online? – jufemaiz Dec 21 '15 at 21:25
1

Here's a way of implementing the "MOD10V5" algorithm (or "mod 10 version 5") using a t-sql user defined function in SQL server. It accepts a Customer ID up to 9 characters long, and return an 11 character CRN (Customer Reference Number).

I also prepended a version number onto the start of my CustomerID, you could do this too if you think you might end up changing it in the future.

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
Rocklan
  • 7,888
  • 3
  • 34
  • 49
  • Just a note for users of this: BPay CRNs are not actually ints, they are strings, as they can have leading 0's and leading 0's totally change the MOD10v05 checksum digit. Modifying this t-sql to account for leading 0's was easy enough thankfully. – Mark Henderson Mar 20 '15 at 02:17
  • Uuuh you know what? I left this comment on the wrong answer. My apologies! – Mark Henderson Mar 21 '15 at 05:34
  • Ha, yeah it happens :) You can probably just delete your comment. – Rocklan Mar 22 '15 at 06:12
1

Modula 10 V1 in PHP. Tested against my Windows dataflex routine and it is the same.

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);
}
Peter Brooks
  • 49
  • 10
1

Here is a ruby class I whipped up quickly for Mod 10 v5

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
Rob Jacoby
  • 56
  • 2
0

This is in C#, but this is what I have so far for BPay check digit generation:

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();
}

I haven't completed testing this yet, I will post back once BPAY get back to me.

EDIT: try this: https://gist.github.com/1287893

Dermot
  • 1,713
  • 2
  • 18
  • 29
  • thanks, i'll take a closer look tonight. I personally don't understand C# but i do understand the private void (which to me = function) – RussellHarrower Jul 18 '12 at 01:31
0

I had to work out a version for javascript, this is what I came up with. It correctly generates the expected numbers in the original question.

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();
Shank
  • 81
  • 6
0

Here is a function I created using vb.net to calculate a mod 10 version 5 check digit

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