Skip to content

Swiss ql bill update for 2.3 version - #8

Open
dx-aleksei-ushakov wants to merge 1 commit into
24.2.2+from
swiss-qr-bill-update
Open

dx-aleksei-ushakov wants to merge 1 commit into
24.2.2+from
swiss-qr-bill-update

Conversation

@dx-aleksei-ushakov

Copy link
Copy Markdown

No description provided.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Invalid account/reference combinations, unsafe amount clamping, and overly permissive parsing can produce or accept non-conforming payment data.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Updates Swiss QR-bill handling for guideline v2.3 with stricter validation and SWICO billing support.

Changes:

  • Adds checksum, character-set, length, and address validation.
  • Enforces structured addresses and valid account/reference formats.
  • Adds SWICO billing-information serialization and parsing.
File summaries
File Description
Validation.cs Adds field-specific exceptions.
TestData.cs Replaces placeholder account and address data.
FieldValidation.cs Adds character and length validation.
Enums.cs Deprecates combined addresses.
SwicoBillingInformation.cs Adds SWICO billing models and parsing.
QRBillDataItem.cs Updates formatting and validation.
PaymentReferenceAccountNumber.cs Validates reference checksums.
CreditorAccountNumber.cs Validates IBAN checksums and QR-IIDs.
AlternativeProcedures.cs Validates procedure lengths.
Address.cs Supports textual address components and structured-only output.
ChecksumValidator.cs Implements Mod10 and Mod97 validation.
Review details

Suppressed comments (2)

CustomControls.SwissQRBill/SwissQRBill/ChecksumValidator.cs:44

  • ISO 7064 conversion is defined for ASCII 0-9 and A-Z, but char.IsDigit/char.IsLetter admit Unicode characters and the subtraction then assigns them invalid numeric values. Because the IBAN shape check does not constrain the BBAN characters, a non-ASCII account can reach this path and be treated as a checksum candidate.
                if(char.IsDigit(c)) {
                    numericValue = c - '0';
                    remainder = (remainder * 10 + numericValue) % 97;
                } else if(char.IsLetter(c)) {
                    numericValue = char.ToUpperInvariant(c) - 'A' + 10;
                    remainder = (remainder * 100 + numericValue) % 97;

CustomControls.SwissQRBill/SwissQRBill/TestData.cs:124

  • This factory combines a standard IBAN (IID 44995) with a 27-digit QR reference. The updated ReferenceType logic therefore returns NON while serialization still writes the reference, producing an invalid payload and no longer matching BillFromRealData. Use either a valid RF creditor reference with this IBAN or a valid QR-IBAN with this QR reference, and update the constant accordingly.
                Reference = new PaymentReferenceAccountNumber("210000000003139471430009017"),
  • Files reviewed: 11/11 changed files
  • Comments generated: 9
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

};

public static bool IsValidMod10Recursive(string reference) {
if(reference == null || reference.Length != 27 || !reference.All(char.IsDigit))
Comment on lines +58 to +60
if(CreditorAccountNumber.NumberFormat == AccountNumberFormat.QR_IBAN)
return Reference.NumberFormat == AccountNumberFormat.QRReference ? ReferenceType.QRR : ReferenceType.NON;
return Reference.NumberFormat == AccountNumberFormat.CreditorReference ? ReferenceType.SCOR : ReferenceType.NON;
Comment on lines +134 to +135
if(amount.Value > MaxAmount)
return MaxAmount;
Comment on lines +44 to +45
if(!string.IsNullOrEmpty(value) && !Regex.IsMatch(value, "^[A-Za-z]{2}$"))
throw ValidationError.FieldException("Country", "Must be a two-letter country code (ISO 3166-1).");
stringBuilder.AppendLine(DebtorInformation.ConvertToQRCodeDataString());
stringBuilder.AppendLine(ReferenceType.ToString());
stringBuilder.AppendLine(Reference == null ? string.Empty : Reference.ConvertToQRCodeDataString());
FieldValidation.ValidateCombinedLength(AdditionalInformation, StructuredInformation, 140, "AdditionalInformation + StructuredInformation (combined)");
Comment on lines +48 to +50
return string.IsNullOrEmpty(InvoiceNumber) && InvoiceDate == null && string.IsNullOrEmpty(CustomerReference)
&& string.IsNullOrEmpty(VatNumber) && VatDate == null && VatRate == null
&& VatDetails.Count == 0 && ImportVatDetails.Count == 0 && PaymentConditions.Count == 0;
Comment on lines +148 to +150
case 30:
info.VatNumber = raw;
break;
Comment on lines +169 to +170
} else if(decimal.TryParse(raw, NumberStyles.Number, CultureInfo.InvariantCulture, out rate)) {
info.VatRate = rate;
public const string BillWithFullSectionFieldsEmpty = "SPC\r\n0200\r\n1\r\nCH5800791123000889012\r\nS\r\nSchreinerei Habegger & Sohne\r\nUetlibergstrasse\r\n138\r\n8045\r\nZuric\r\nCH\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nEUR\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nSCOR\r\nRF18539007547034\r\n\r\nEPD\r\n";
public const string BillWithTwoProcedures = "SPC\r\n0200\r\n1\r\nCH5800791123000889012\r\nS\r\nSchreinerei Habegger & Sohne\r\nUetlibergstrasse\r\n138\r\n8045\r\nZuric\r\nCH\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n287.30\r\nEUR\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nSCOR\r\nRF18539007547034\r\nRechnungsnr. 10978 / Auftragsrnr. 3987\r\nEPD\r\n//bill information\r\nName AV1: UV;UltraPay005;12345\r\nName AV2: XY;XYService;54321";
public const string BillWithOneProcedure = "SPC\r\n0200\r\n1\r\nCH5800791123000889012\r\nS\r\nSchreinerei Habegger & Sohne\r\nUetlibergstrasse\r\n138\r\n8045\r\nZuric\r\nCH\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n287.30\r\nEUR\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nSCOR\r\nRF18539007547034\r\nRechnungsnr. 10978 / Auftragsrnr. 3987\r\nEPD\r\n//bill information\r\nName AV1: XY;XYService;54321";
public const string BillFromRealData = "SPC\r\n0200\r\n1\r\nCH4944995599000899901\r\nS\r\nHenri Schmid Service Switzerland AG\r\nMuseumstrasse\r\n258\r\n2501\r\nBiel\r\nCH\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n8690.00\r\nCHF\r\nS\r\nPia-Maria Rutschmann-Schnyder\r\nGrosse Marktgasse\r\n28\r\n9400\r\nRorschach\r\nCH\r\nSCOR\r\n210000000003139471430009017\r\nAuftrag vom 25.10.2019##S1/01/20170309/11/10201409/20/14000/22/36958/30/CH10646546/40/1020/41/3010\r\nEPD\r\n";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants