Skip to main content

BPAY โ€” Bill Payment System

Overviewโ€‹

BPAY is Australia's bill payment scheme that allows customers to pay bills electronically using a Biller Code and a Customer Reference Number (CRN). It operates over the BECS infrastructure and is one of the highest-volume payment methods in Australia.

  • Operator: BPAY Group (subsidiary of AusPayNet)
  • Launched: 1997
  • Volume: ~1 billion transactions per year
  • Infrastructure: Built on BECS Direct Entry
  • Availability: Payments initiated 24/7; processing next business day

How BPAY Worksโ€‹

Customer (Payer)
โ”‚
โ”‚ 1. Gets bill with:
โ”‚ - Biller Code (4-6 digits)
โ”‚ - CRN (up to 20 digits)
โ”‚ - Amount
โ”‚
โ”‚ 2. Logs into banking app / phone banking
โ”‚ 3. Selects "Pay Bill" โ†’ enters Biller Code + CRN + Amount
โ”‚
โ–ผ
Customer's Bank (Financial Institution)
โ”‚
โ”‚ 4. Validates Biller Code against BPAY register
โ”‚ 5. Validates CRN format (Luhn check variant)
โ”‚ 6. Debits customer account
โ”‚ 7. Batches into BPAY file
โ”‚
โ–ผ
BPAY Clearing (via BECS)
โ”‚
โ”‚ 8. Files submitted by 5 PM cut-off
โ”‚ 9. BPAY clears and routes to biller's bank
โ”‚
โ–ผ
Biller's Bank
โ”‚
โ”‚ 10. Credits biller's account
โ”‚ 11. Provides remittance file to biller
โ”‚ (Biller Code + CRN + Amount + Date)
โ”‚
โ–ผ
Biller (Creditor)
โ”‚
โ”‚ 12. Matches CRN to invoice / customer record
โ”‚ 13. Marks invoice as paid

BPAY Key Identifiersโ€‹

Biller Codeโ€‹

- 4 to 6 digit number assigned by BPAY
- Uniquely identifies the biller organisation
- Registered in the BPAY biller directory
- Printed on every bill/invoice

Example: Biller Code 12345 = AGL Energy

Customer Reference Number (CRN)โ€‹

- Up to 20 digits
- Set by the biller; unique per customer or invoice
- Contains a check digit (mod-10 Luhn variant)
- Printed alongside Biller Code on the bill

Examples:
Account-based CRN: Customer account number
Invoice-based CRN: Invoice number
Property-based CRN: Property ID + check digit

CRN Validation (Luhn-like check)โ€‹

public boolean validateCrn(String crn) {
// BPAY uses a Luhn variant (weight pattern 1,3,7,9...)
int[] weights = {1, 3, 7, 9};
int sum = 0;
for (int i = 0; i < crn.length() - 1; i++) {
sum += Character.getNumericValue(crn.charAt(i))
* weights[i % 4];
}
int checkDigit = (10 - (sum % 10)) % 10;
return checkDigit == Character.getNumericValue(
crn.charAt(crn.length() - 1));
}

BPAY File Formatโ€‹

BPAY uses a proprietary flat file based on BECS DE format with BPAY-specific extensions:

Record Type 0 โ€” File Header
Record Type 2 โ€” BPAY Credit Transaction
Fields:
โ”œโ”€โ”€ BSB (biller's bank BSB)
โ”œโ”€โ”€ Account (biller's account)
โ”œโ”€โ”€ Amount (in cents)
โ”œโ”€โ”€ Biller Code
โ”œโ”€โ”€ CRN (Customer Reference Number)
โ”œโ”€โ”€ Payment date
โ””โ”€โ”€ Lodgement reference
Record Type 7 โ€” File Trailer (net totals)

BPAY Processing Windowsโ€‹

EventTime (AEST)
Payment initiated by customer24/7
Bank submission cut-off5:00 PM
BPAY clearing & routingOvernight
Credit to biller's accountNext business day AM
Remittance file to billerNext business day AM

Important: Payments made after 5 PM are processed the next business day. Some banks apply a "same-day BPAY" service for payments before a specific intraday cut-off (e.g., 2 PM).


BPAY vs NPP vs Direct Debitโ€‹

FeatureBPAYNPP / OskoBECS Direct Debit
InitiatorCustomer (push)Customer (push)Biller (pull)
SpeedNext business day< 15 secondsNext business day
IdentifierBiller Code + CRNPayID / BSB+AccountBSB + Account
RemittanceBiller Code + CRN280 chars free textLodgement ref
Dispute window7 daysN/A7 days
Bill-specificโœ… YesโŒ GenericโŒ Generic

BPAY Remittance to Billerโ€‹

After clearing, the biller receives a remittance file containing:

For each payment:
- Biller Code
- CRN (Customer Reference Number) โ† key for matching to invoice
- Amount paid
- Payment date
- Customer's bank BSB
- Processing date

The biller's accounts receivable system must match these records to open invoices using the CRN.


BPAY Viewโ€‹

BPAY View is an e-billing extension that allows billers to present digital bills directly in customers' banking apps:

Biller generates bill
โ”‚ [Electronic bill via BPAY View]
โ–ผ
Customer sees bill in banking app
โ”‚ Biller Code, CRN, amount pre-filled
โ–ผ
Customer clicks "Pay" (no manual data entry)
โ”‚
โ–ผ
Standard BPAY payment flow

BPAY Error & Exception Codesโ€‹

CodeDescription
01Invalid Biller Code
02Invalid CRN
03Amount mismatch (if biller restricts amount)
04Biller not accepting payments
05Duplicate payment
06Account closed / invalid

Java Spring Integration Notesโ€‹

@Service
public class BpayPaymentService {

public BpayPaymentResult processPayment(BpayPaymentRequest request) {
// 1. Validate Biller Code
BillerDetails biller = billerRegistry
.findByCode(request.getBillerCode())
.orElseThrow(() -> new InvalidBillerCodeException(request.getBillerCode()));

// 2. Validate CRN check digit
if (!crnValidator.validate(request.getCrn())) {
throw new InvalidCrnException(request.getCrn());
}

// 3. Validate amount (some billers restrict to exact amount)
if (biller.isFixedAmount() &&
!biller.getFixedAmount().equals(request.getAmount())) {
throw new AmountMismatchException();
}

// 4. Check balance
accountService.checkSufficientFunds(
request.getDebtorAccountId(), request.getAmount());

// 5. Debit customer account
String ledgerRef = ledgerService.postDebit(
request.getDebtorAccountId(), request.getAmount(),
"BPAY " + request.getBillerCode() + " " + request.getCrn());

// 6. Add to BPAY batch for clearing
bpayBatchService.addToBatch(BpayBatchItem.builder()
.billerCode(request.getBillerCode())
.crn(request.getCrn())
.amount(request.getAmount())
.billerBsb(biller.getBsb())
.billerAccount(biller.getAccountNumber())
.build());

return BpayPaymentResult.success(ledgerRef);
}
}