Skip to content

feat(kyc): validate name and address against Swiss payment character set#589

Merged
TaprootFreak merged 1 commit into
RealUnitCH:chore/post-580-followupsfrom
Blume1977:fix/swiss-payment-text-validation
May 26, 2026
Merged

feat(kyc): validate name and address against Swiss payment character set#589
TaprootFreak merged 1 commit into
RealUnitCH:chore/post-580-followupsfrom
Blume1977:fix/swiss-payment-text-validation

Conversation

@Blume1977
Copy link
Copy Markdown
Contributor

Summary

Mirror the API-side @IsSwissPaymentText() validator (DFXswiss/api#3766) in the four user flows that write into user_data:

  • KYC registration — personal step (firstName, lastName)
  • KYC registration — address step (street, houseNumber, postalCode, city)
  • Settings → edit name (firstName, lastName)
  • Settings → edit address (street, houseNumber, postalCode, city)

Users now get immediate field-level feedback in the form if they enter a character outside the SIX SIG IG QR-Bill v2.3 permitted character set, instead of submitting and waiting for a generic 400 from the backend.

Why

DFX business decision: only characters valid in Swiss payment systems are supported end-to-end. Pairs with the umlaut-preservation fix in DFXswiss/api#3766 — now that diacritics survive storage and PDF rendering, we also lock the input surface so CJK / Cyrillic / Arabic / emoji never enter the system.

Character set

[\x20-\x7E] (printable ASCII)
+ À Á Â Ä Ç È É Ê Ë Ì Í Î Ï Ñ Ò Ó Ô Ö Ù Ú Û Ü Ý ß
+ à á â ä ç è é ê ë ì í î ï ñ ò ó ô ö ù ú û ü ý
+ \n (line feed)

Covers German, French, Italian, Romansh. Regex is byte-for-byte aligned with the API regex (Config.formats.swissPaymentText) so any input the client accepts the backend also accepts.

Changes

  • lib/packages/utils/swiss_payment_text.dart: new isSwissPaymentText(String?) helper (empty/null = valid).
  • Four screen files: add the helper call to existing validator: callbacks; missing validator on house-number in settings edit-address page added.
  • assets/languages/strings_en.arb, strings_de.arb: new swissPaymentTextInvalid translation key, inserted in alphabetical order.
  • Generated lib/generated/i18n.dart is gitignored — built by dart run tool/generate_localization.dart after pulling.

Test plan

  • dart run tool/generate_localization.dart runs without errors after checkout
  • flutter analyze — passes (verified locally, no new issues)
  • Type Rüttimann / Münchwilen / Genève / Saint-Légier in any of the four flows → no error, form accepts
  • Type 王小明 / Иван / 日本語 / an emoji → field shows the Swiss-payment-text error message
  • Submit a valid form → API call succeeds (i.e. client and server regex match)
  • German UI shows German error message; English UI shows English message
  • flutter test — pre-existing golden failures unchanged (verified locally — same 88 failures on clean develop)

Mirror the API-side @IsSwissPaymentText() validator in the four flows
that submit user-typed name/address into user_data: KYC registration
personal step, KYC registration address step, settings name edit,
settings address edit. Users get immediate field-level feedback for
characters outside the SIX SIG IG QR-Bill v2.3 permitted character set
(printable ASCII plus the diacritics for the four Swiss national
languages) instead of a generic 400 from the backend.

Pair-PR with DFXswiss/api#3766.
@TaprootFreak TaprootFreak changed the base branch from develop to chore/post-580-followups May 26, 2026 15:36
@TaprootFreak TaprootFreak marked this pull request as ready for review May 26, 2026 15:37
@TaprootFreak TaprootFreak merged commit bf2ab05 into RealUnitCH:chore/post-580-followups May 26, 2026
8 checks passed
TaprootFreak added a commit that referenced this pull request May 26, 2026
Add the missing test coverage for `lib/packages/utils/swiss_payment_text.dart`
(introduced in #589) so the regex character set is locked in against
regressions when the API-side `Config.formats.swissPaymentText` drifts.

33 cases across 8 groups:

- empty / null → valid (matches the docstring contract: callers chain a
  non-empty check)
- printable ASCII (0x20–0x7E): digits, punctuation, full 95-char band
- uppercase + lowercase Latin diacritics for CH / DE / FR / IT
- real Swiss names + addresses (Rüttimann, Münchwilen, Genève,
  Saint-Légier, D'Hauterive, François)
- non-Latin scripts rejected (CJK, Cyrillic, Arabic, Hebrew, emoji)
- mixed-script attacks (Latin word with a Cyrillic homoglyph е inside)
- whitespace: newline accepted (multi-line memos), tab + CR rejected
- uncommon Latin diacritics rejected (Polish ąć, Portuguese ã,
  Norwegian øå, French ligature œ, French Ÿ) — explicitly documents
  what is NOT in the Swiss payment set so the regex is hand-verifiable

Run: `flutter test --no-pub test/packages/utils/swiss_payment_text_test.dart`
→ 33/33 pass.
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