Skip to content

refactor(kyc): consume API validation pattern instead of hardcoded swissPaymentText mirror#739

Draft
TaprootFreak wants to merge 1 commit into
stagingfrom
feat/api-validation-formats
Draft

refactor(kyc): consume API validation pattern instead of hardcoded swissPaymentText mirror#739
TaprootFreak wants to merge 1 commit into
stagingfrom
feat/api-validation-formats

Conversation

@TaprootFreak

Copy link
Copy Markdown
Contributor

What

Deletes the hardcoded swissPaymentText regex mirror (lib/packages/utils/swiss_payment_text.dart) and validates name/address fields against the pattern the API serves at GET /v1/config instead.

  • DfxConfigService — fetches { formats: { swissPaymentText: { pattern, flags } } }, compiles it to a Dart RegExp, caches it (30 min TTL), and exposes a synchronous isSwissPaymentText(value) for form validators.
  • Warmed at startup in finishSetup (fire-and-forget; failure just defers to the server). Dropped on network switch alongside the other reference-data caches.
  • All 12 call sites in the KYC registration + settings-edit forms now call getIt<DfxConfigService>().isSwissPaymentText(...).

Why

Per CONTRIBUTING.md → "API as Decision Authority", which characters are valid is a backend-owned rule the app should render, not re-implement. The mirror silently drifted whenever Config.formats.swissPaymentText changed, and it encoded an allowed-character decision client-side.

It also fixes the foreign-postal-code class of bugs: NL 1011 AB, UK EC1A 1BB, CA K1A 0B1, IE D02 AF30 now validate correctly because the rule comes from the API, not a client guess.

Backward compatibility

If the pattern hasn't loaded yet (pre-rollout backend, offline, or first instant after launch) the validator returns true and defers to the server's authoritative @IsSwissPaymentText() — the app is never more restrictive than the API and never needs a hardcoded fallback. Mirrors the "legacy backend tolerance" rule.

Pair-PR / merge order

Server-side counterpart: DFXswiss/api#3911 (DFXswiss/api#3911), which adds GET /v1/config.

⚠️ Draft until #3911 is merged on develop and deployed to DEV. Merging this ahead means DEV serves no /v1/config and the app stays on the graceful server-defers-path until the backend catches up (no regression, just no inline check yet).

Tests

test/packages/service/dfx/dfx_config_service_test.dart:

  • load fetches /v1/config and caches; invalidateCache forces a refetch; non-200 throws.
  • pre-load → validator returns true (defers to server).
  • post-load → full accept/reject matrix (Swiss diacritics, foreign ZIPs, newline accepted; emoji, Cyrillic, CJK, tab/CR, non-Swiss diacritics rejected).
  • "applies whatever pattern the API serves" — a digits-only server pattern is honoured verbatim, proving the rule isn't reconstructed client-side.

Verified locally on m5me (Flutter 3.41.6): dart format, flutter analyze (whole project, clean), and the service test all green. Additionally ran a live end-to-end check against a real DFX API booted on m5me serving this branch of #3911 — the app DTO parsed the actual /v1/config response and validated the full matrix identically to the deleted mirror.

…issPaymentText mirror

The app kept a byte-for-byte copy of the API's swissPaymentText regex in
lib/packages/utils/swiss_payment_text.dart to validate name/address fields
inline. That mirror silently drifts whenever the backend's
Config.formats.swissPaymentText changes, and it encodes an allowed-character
rule the API owns.

Replace it with a DfxConfigService that fetches the authoritative pattern from
GET /v1/config, compiles it at runtime and validates synchronously. The pattern
is warmed at startup and cached (30 min TTL); it is dropped on network switch
alongside the other reference-data caches. If the pattern hasn't loaded yet the
validator returns true and defers to the server's authoritative
@IsSwissPaymentText() check, so the app is never more restrictive than the API
and needs no hardcoded fallback.

This also fixes the foreign-postal-code class of bugs: NL "1011 AB", UK
"EC1A 1BB", CA "K1A 0B1", IE "D02 AF30" now validate correctly because the rule
comes from the API rather than a client guess.

Deletes the local mirror and its test in the same PR. Server-side change:
DFXswiss/api#3911.
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.

1 participant