Expose MergeProcessing state on RealUnit registration during account merge#3848
Closed
TaprootFreak wants to merge 1 commit into
Closed
Expose MergeProcessing state on RealUnit registration during account merge#3848TaprootFreak wants to merge 1 commit into
TaprootFreak wants to merge 1 commit into
Conversation
davidleomay
approved these changes
Jun 9, 2026
3 tasks
Collaborator
Author
|
Closing: the runtime root cause turned out to be different. PROD App Insights shows GET /v1/realunit/registration returns 200 (state=NewRegistration) and POST /v1/realunit/register/wallet returns 400 'No RealUnit registration found' — the merged account has no prior RealUnit registration, so the email-verification flow calls the wrong endpoint (register/wallet instead of the full register/complete form). This is an app-only fix in the email-verification cubit; the MergeProcessing approach here does not address it. A corrected PR follows. |
This was referenced Jun 9, 2026
TaprootFreak
added a commit
to RealUnitCH/app
that referenced
this pull request
Jun 9, 2026
## Summary
Fixes the post-account-merge "Wallet registration not complete" error
(red banner) in the RealUnit app, the CONTRIBUTING-aligned way.
When a new wallet signs up with an email that already belongs to an
existing **DFX** account (account merge) and that account has **no prior
RealUnit registration**, `GET /v1/realunit/registration` returns
`state=NewRegistration`. The email-verification step, however, called
`registerWallet` (`POST /register/wallet`) **unconditionally** — that
endpoint only *adds* a wallet to an **existing** registration, so the
API returns `400 "No RealUnit registration found"`.
## Root cause — runtime-confirmed (PROD App Insights)
- `GET /v1/realunit/registration` → **200** (state=NewRegistration,
userData present from existing KYC)
- `POST /v1/realunit/register/wallet` → **400 `{"message":"No RealUnit
registration found"}`** (×4 retries)
- recovery: the user reached the KYC registration form → `POST
/v1/realunit/register/complete` → **201**
- DB: the merged account had no `RealUnitRegistration` step until
`register/complete` created it. 7-day breadth: 7× this 400 (affects
every merge into a DFX account without a prior RealUnit registration).
## Fix (app-only) — single source of registration routing
The email-verification flow is reduced to its actual job: **confirm the
merge** (detect the JWT account change) and hand back to the KYC flow.
`KycCubit` is now the only place that interprets the registration
`state` and routes it (addWallet → link wallet, NewRegistration → full
registration form, AlreadyRegistered → forward) — per CONTRIBUTING.md
"API as Decision Authority". This removes the duplicated, unconditional
`register/wallet` call.
Dead code removed accordingly: `_completeRegistration`,
`_mergeDetected`, the `RealUnitRegistrationService` dependency, the
`KycEmailVerificationRegistrationFailure` state, and the now-unused i18n
key `registerEmailVerificationRegistrationFailed` (de + en).
## Tests
- email cubit (simplified): same account → Failure (link not visited);
changed account → Success (merge confirmed; no registration here); retry
(Failure → Success).
- `kyc_step_states_test` updated for the removed state.
## Test plan
- [x] `flutter analyze` clean
- [x] `flutter test --exclude-tags golden` — full suite passes (2312)
- [x] Coverage Floor Gate replicated locally — scoped **lines 100.0%**
(floor 100)
- [ ] DEV end-to-end: merge into a DFX account without RealUnit
registration → no red error → lands on the registration form →
register/complete
Supersedes #711 (minimal variant) and the earlier incorrect
MergeProcessing PRs DFXswiss/api#3848 + #709.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
After an account merge (new RealUnit wallet signs up with an email that already belongs to an existing account),
GET /v1/realunit/registrationbriefly returnedstate=NewRegistrationwith an emptyuserData— becausemergeUserDatahasn't re-parented the merged-in KYC steps/personal data yet. The RealUnit app misreads the absentuserDataas a registration failure and shows a red "wallet registration not complete" error, even though the merge succeeded.This adds a
MergeProcessingregistration state so the client can render a waiting state and poll instead of inferring failure — consistent with "API as Decision Authority".Changes
RealUnitRegistrationState: addMergeProcessing(realunit-registration.dto.ts), document it on thestate@ApiProperty.RealUnitService.getRegistrationInfois now async and returnsMergeProcessing(nouserData) whenaccountMergeService.hasProcessingMerge(userData.id)is true — mirrors the KYC path (KycServiceviahasProcessingMerge).accountMergeServicewas already injected but unused here.getRegistrationInfotests converted to async; mock now provideshasProcessingMerge; new test asserts theMergeProcessingshort-circuit.Behaviour / correctness
executeMergesettingprocessingStartedAtandmergeUserDatafinishing (andcomplete()settingisCompleted). Once complete (or after the 10-min self-heal / expiration), the guard returns false and the normal states resume — no permanent stuck state.Backwards compatibility
Old app versions throw on an unknown
statevalue — but only inside the merge-processing window, where they already fail today (emptyuserData→ error). So there is no regression; only updated app versions get the improved waiting UX.Pair-PR
This is the backend half. The RealUnit app companion PR (consume
MergeProcessing→ render the existing merge-processing waiting screen; make the state enumfromJsontolerant) follows onRealUnitCH/appafter this lands ondevelop(pair-PR discipline).PR completeness
Test plan
npx jest realunit.service(32/32 pass)npm run type-check,eslint,prettier --checkcleanGET /v1/realunit/registrationreturnsstate=MergeProcessingduring the processing window