package qrbill import ( "crypto/rand" "fmt" "time" ) func generateReference() string { // 1) Build the 26-digit payload from time + random suffix now := time.Now().UTC() ts := now.Format("20060102150405") // YYYYMMDDHHMMSS -> 14 chars ns := now.Nanosecond() // 0..999999999 // format nanoseconds as 9 digits, zero padded nsStr := fmt.Sprintf("%09d", ns) // 3-digit random suffix (000..999) to reach 26 digits randBytes := make([]byte, 2) // we'll read 2 bytes and map to 0..999 rand.Read(randBytes) // convert to number 0..65535 then mod 1000 rnum := int(randBytes[0])<<8 | int(randBytes[1]) rnum = rnum % 1000 randStr := fmt.Sprintf("%03d", rnum) // compose 26-digit payload: 14 + 9 + 3 = 26 payload := ts + nsStr + randStr // Safety: ensure payload is 26 digits (should always be true) if len(payload) != 26 { panic(fmt.Errorf("internal error: payload length %d != 26", len(payload))) } // 2) Compute Modulo-10 recursive check digit check := mod10RecursiveChecksum(payload) // 3) Build full 27-digit reference full := payload + fmt.Sprintf("%d", check) // 4) Format with grouping: 2-5-5-5-5-5 formatted := fmt.Sprintf("%s %s %s %s %s %s", full[0:2], full[2:7], full[7:12], full[12:17], full[17:22], full[22:27], ) return formatted } // mod10RecursiveChecksum computes the Mod10 recursive check digit for a numeric string. // Implements the table-based recursive mod10 described in Swiss Annex B / ISR implementations. func mod10RecursiveChecksum(digits string) int { // table as used in ISR/QR implementations arrTable := [10]int{0, 9, 4, 6, 8, 2, 7, 1, 3, 5} carry := 0 for i := 0; i < len(digits); i++ { ch := digits[i] // assume digits are ASCII '0'..'9' n := int(ch - '0') // update carry idx := (carry + n) % 10 carry = arrTable[idx] } // final check digit return (10 - carry) % 10 }