Merge remote-tracking branch 'origin/main' into kyc-69

This commit is contained in:
Stephan D committed 2026-10-23 12:46:56 +02:00
commit b644a10663
27 files changed
+1004 -84

No files matched your search

+63
View File
@@ -296,3 +296,66 @@ real token was checked read-only and remains unused and unexpired, with its
account pending verification, ready for the user to reopen the link. PR #61 has
not been merged into remote main; this local deployment does not imply a main
branch merge.
### Accepted signup delivery (#62)
Task [#62](https://git.sendico.io/tech/sxchnge/issues/62), branch
`email-verification-62`, delivered in
[PR #63](https://git.sendico.io/tech/sxchnge/pulls/63). On October 23, signup
returned HTTP 202 and the local
SMTP provider accepted its message with status 250, while the user reported an
internal-error screen immediately after Create account. No subsequent
verification request from that attempt reached the BFF. The precise original
client exception was not captured; an ordinary isolated release reproduction
succeeded. The regression below deliberately injects an unavailable follow-up
send service to cover the post-acceptance failure path without claiming that
injected exception was the original cause.
Backend and preview signup mappers now return a `VerificationDelivery` domain
receipt with the recipient and simulation status. It means that signup accepted
the email-queue operation, not inbox delivery, account activation or a session.
The signup provider exposes it through its operation result. The form captures
the receipt before clearing password controllers, whose change listeners reset
auth state, and passes it as transient router extra. The verification provider
initializes directly in the sent state; mounting the page performs no send.
This replaces the backend adapter's hidden one-use signup cache. Every explicit
resend now invokes its service, including the first one after signup.
Routes without a receipt retain their existing behavior: unverified login may
request an email, a fresh verification page asks for an address, and emailed
links run confirmation. Recipient changes invalidate the displayed claim until
another send succeeds. New receipts replace stale recipients. The route codec
discards the receipt when serializing history, so reloads do not invent a queued
result or retain the address. No token or password is included in the receipt.
The existing email-delivery requested/succeeded signals now follow the accepted
signup receipt inside the instrumented service instead of a synthetic send on
page mount. They stay in the signup journey, occur once for the accepted
operation, and remain intermediate queue-acceptance signals. Resends retain
separate attempts and failures; confirmation remains the terminal outcome.
Simulation and privacy rules are unchanged, including the in-memory analytics
and cross-device correlation limitations above.
Frontend review covered the complete app/auth contribution and preserved the
DTO, mapper, service, domain model, provider and UI boundaries. Regressions cover
accepted signup followed by unavailable resend, explicit retry, normalized
receipt mapping, provider initialization/reset, recipient replacement, transient
history, event correlation and exclusion of form values from analytics. EN/RU
flow tests run at 390px with 1.5x text scaling; existing keyboard, autofill and
accessibility suites also pass. Changed-file Dart format checks, analysis in
`frontend/app` and `frontend/packages/sxchnge_auth`, and `make test-frontend`
(221 tests) pass. The new injected-failure regression failed on the previous
implementation and passes with the receipt handoff. A release-browser fixture
with the same backend adapter and a failing resend service shows Check your
inbox after signup with no send call, then the expected error only after an
explicit resend. It sends no email. Native Safari was not automated.
The frontend image was rebuilt with freshly resolved stable digest pins and
recreated without restarting BFF. The served JavaScript matches the new image
at SHA-256 `0a45ce6f0b4658b1f03d8e39b9e39fd3fbfcf6f1626cb3f17705dce2da130a6c`.
Normal browser-trust HTTPS serves the frontend and API with HTTP 200.
`sh infra/local/tools.sh verify` passes all seven services, Vault/database
checks and authenticated Sendico currency/ledger reads. No new customer email,
activation or payment was submitted for this change, and no security scan was
requested or run. The user's pre-existing browser tab must reload to use the
new bundle; their original Safari exception remains unobserved.
+27
View File
@@ -302,6 +302,33 @@ that contradicts displayed balances or usage without explaining the fixtures.
- Keep unrelated accounts/history accessible during a pending operation. Do not
block the whole product behind an exchange spinner.
## Empty currency accounts (task #66)
The shared empty-account message used on Accounts and transfer screens explains
that no currency accounts have been set up and directs the customer to support
to request setup. English and Russian no longer suggest that refreshing creates
accounts or imply that automatic provisioning is underway. This follows the
existing support guidance for missing funding and payout setup. The application
has no configured support contact channel; the message therefore provides text
guidance without a nonfunctional contact button or invented contact details.
UX review: the copy names the missing resource and the next step, retains the
existing wrapping text and accessible reading order, and does not alter loading,
error or populated-state behavior. Existing manual refresh remains available for
accounts provisioned externally. This copy-only change adds no controls or
journey outcomes and requires no new analytics events. Existing transaction
journey signals and their production measurement limitations below still apply;
support resolution and external account provisioning are not measured in-app.
Validation: `flutter gen-l10n`, `flutter analyze` in `frontend/app`,
`make test-frontend` (217 tests), and `git diff --check` passed. No Dart source
changed, so Dart formatting was not applicable. Existing responsive workspace
tests cover Russian at 320px with enlarged text; this copy-only change does not
add a dedicated empty-state rendering or native screen-reader test. No security
scan was requested. Delivery: [task #66](https://git.sendico.io/tech/sxchnge/issues/66),
branch `wallet-66`, [PR #68](https://git.sendico.io/tech/sxchnge/pulls/68).
Merge and deployment remain separate.
## Journey measurement plan
Reuse the typed analytics abstraction and maintain a separate journey per active
+48
View File
@@ -211,3 +211,51 @@ smoke testing on the local demo opened the selector, searched for Poland and
confirmed that the profile displays Poland after selection. Focused and full
journey tests verify the stored `PL` code. Generated localization/build outputs
remain ignored and uncommitted.
## Birthday entry — task #67
Task [#67](https://git.sendico.io/tech/sxchnge/issues/67), branch `kyc-67`.
Delivery: [PR #70](https://git.sendico.io/tech/sxchnge/pulls/70).
This change is stacked on the residence-country selector in PR #65; merge #65
before the birthday PR.
The profile uses labeled Day, Month and Year controls. Day and year accept numeric
entry with birthday autofill hints; month selection uses localized month names
and standard Material keyboard/touch interaction. Customers can enter their birth
year directly. No calendar paging or transport-format punctuation is required.
The controls share a row when space allows and stack at narrow widths or enlarged
text sizes. English and Russian labels, month names and errors follow the app locale.
Valid calendar dates are normalized to the existing `IdentityProfile.dateOfBirth`
`YYYY-MM-DD` value through `IdentityProvider.updateProfile`. Partial, impossible
and future dates clear that profile value so Continue cannot retain an earlier
valid birthday. Local input components remain visible while correcting a date;
complete invalid dates receive a specific live-region error below the group.
Leap years, zero day/year and calendar rollover are checked. Today's date follows
the existing model policy; no minimum age or arbitrary oldest birth year is added.
Prefilled dates, locale changes and external profile replacement synchronize the
controls. Back navigation restores the valid saved date. Busy state disables all
three controls. Controllers are disposed with the widget.
The existing DTO, mapper, service, domain model and provider boundaries are retained.
No dependency, transport or backend change is needed. This remains part of the KYC
preparation-to-submission journey defined above: existing acceptance, failure,
retry and abandonment signals apply, without new field events or date collection.
The existing server-side review measurement gaps remain unchanged.
UX review and automated checks cover entry/correction, leap dates, invalid and
future-date recovery, numeric restrictions, prefill/replacement/clearing, locale
switching, disabled state, Tab/Enter/arrow-key month selection, Escape cancellation,
and Russian controls/errors at 320px with 1.6 text scale. Labeled and minimum-size
tap-target guidelines pass. Integration checks verify that invalid dates block
Continue, Back preserves the birthday, and EN/RU flows still reach submission
acceptance. Native screen-reader and mobile keyboard/autofill testing remain
release checks; widget semantics are not a claim of device-level validation.
Validation: all four changed Dart files pass the format check; `flutter analyze`
in `frontend/app` reports no issues; `make test-frontend` passes all 230 tests,
including the seven new birthday widget tests and the new navigation/gating test.
`flutter build web` passes, including its Wasm compatibility dry run. Browser
smoke testing of the release build entered 23 April 1990, verified the inline
error for 31 April and restored 23 April without losing month or year.
Generated localization and build outputs remain ignored and uncommitted.