Skip to content

feat(realunit): pre-fill registration form from existing KYC data#3780

Closed
TaprootFreak wants to merge 1 commit into
developfrom
cyrill/feat/wallet-status-user-data-fallback
Closed

feat(realunit): pre-fill registration form from existing KYC data#3780
TaprootFreak wants to merge 1 commit into
developfrom
cyrill/feat/wallet-status-user-data-fallback

Conversation

@TaprootFreak
Copy link
Copy Markdown
Collaborator

Summary

GET /v1/realunit/wallet/status now returns userData derived from the user's existing DFX KYC record when no RealUnitRegistration KycStep has been created yet. The app uses this as the source of truth for pre-filling the Aktionariat registration form, replacing the current empty-controllers experience.

Why

Today every user reaching the RealUnit buy screen with completed DFX KYC gets routed through the registration wizard with empty fields — name, birthday, phone, nationality, full address — even though the API has all of those values verified. Worse, completeRegistration calls isPersonalDataMatching (transliterated equality on every personal-data field), so any guess-and-type discrepancy hard-fails the registration request. The user is forced to reproduce their KYC submission byte-for-byte from memory.

Verified against PRD for userData 290795 (Cyrill, KYC 50, three wallets): all fields the form collects (firstname, surname, phone, birthday, nationality, street + houseNumber, location, zip, country) are populated on user_data but unreachable from any user-facing endpoint. This PR is the smallest backend change that unblocks the matching app-side pre-fill PR.

What changed

realunit.service.ts

  • getAddressWalletStatus now falls back to toUserDataDtoFromUserData(userData) when the step-based mapper returns nothing
  • New private toUserDataDtoFromUserData(userData) maps:
    • mail/firstname/surname/phone/birthday → email/name/phoneNumber/birthday
    • nationality.symbol / language.symbol → nationality / lang (lang falls back to EN if not in the supported set)
    • street + houseNumber → addressStreet (concatenated to match the Aktionariat single-line format)
    • zip/location/country.symbol → addressPostalCode/addressCity/addressCountry
    • tin (JSON) → countryAndTINs
    • country.symbol === 'CH' → swissTaxResidence (defensive default; user can override before signing)
    • Builds an internal kycData: KycPersonalData for any subsequent submission round-trip
  • Returns `undefined` when no firstname/surname is present so unverified users still see the empty form (no behaviour change for them)

realunit.controller.ts

  • getWalletStatus loads country, nationality, organizationCountry, language alongside the existing kycSteps relation — needed by the new mapper, no behavioural change for callers that don't depend on these fields

Out of scope

The deeper redesign — driving every onboarding step purely from API-shaped definitions ("show" / "skip" per step, schema-driven forms) — is being scoped separately. This PR is the minimum that converts the current re-typing trap into a one-tap confirmation flow for KYC-completed users.

API as Decision Authority

Consistent with the rule: the server alone decides whether to expose pre-fill data and which fields to send; the app renders what arrives. No new client-side gating logic introduced.

Tests

realunit.service.spec.ts gains a `getAddressWalletStatus (user_data fallback)` block:

  • KYC-verified account → fully populated `userData` with all expected field shapes
  • No firstname/surname → `userData: undefined` (unchanged behaviour preserved)
  • Non-CH residence country → `swissTaxResidence: false` + `addressCountry` mirrors the user's country
  • Unsupported language symbol → `lang: EN` fallback

`npm test -- --testPathPattern=realunit.service` → 18 / 18 passing (14 baseline + 4 new).

Local verification

  • `npm run lint` — clean
  • `npm run format:check` — clean
  • `npm run build` — clean
  • `npm test -- --testPathPattern=realunit.service` — 18 passing

Pair-PR

App-side consumer PR follows on DFXswiss/realunit-app — calls `getWalletStatus()` in the registration page's `initState()` and initializes the form controllers from `userData`.

Users with completed DFX KYC who initiate a RealUnit purchase get
sent through the Aktionariat registration flow, where the form
controllers currently start empty. They must retype data the
backend already holds, and `completeRegistration`'s
`isPersonalDataMatching` rejects any deviation byte-for-byte —
turning a one-tap signing step into a guessing game.

Make `GET /v1/realunit/wallet/status` fall back to the underlying
`user_data` record when no `RealUnitRegistration` KycStep exists
yet. The app already consumes `RealUnitWalletStatusDto.userData`
for the merge-existing-registration path; this extends the same
shape to first-time registrations so the form can pre-fill with
values that will pass server-side validation verbatim.

Controller loads the country / nationality / language relations
needed by the new mapper. Mapper degrades gracefully: no
firstname/surname → `userData: undefined` (back to manual entry,
same as before this PR).
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