Files
sxchnge/docs/ux/kyc.md
T

150 lines
9.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Identity verification UX plan
Task [#20](https://git.sendico.io/tech/sxchnge/issues/20). Plan recorded before implementation.
## User outcome and scope
Understand current access, decide whether higher limits are useful, and submit two
readable photos for a human review. No KYC is a valid account state. Email
confirmation and identity verification remain separate. The existing app is a demo:
this release simulates review, holds photos only in memory and never uploads them.
It must say so before selection and submission; use sample images only.
## Journey
1. **Verification & limits**: status, active tier, basic versus verified daily and
monthly limits, and one relevant next action. Preview policy is explicitly
labeled as fixtures in the stub service. A draft, pending review,
correction request or denial keeps basic limits; only approval enables the
verified tier. This is presentation, never financial authorization.
2. **Passport**: photograph the open identity/photo page of a valid passport;
all edges visible, readable text, no glare or fingers covering details.
3. **Selfie with passport**: face and open passport visible in the same photo,
unobstructed and in focus. Explain the difference with an icon and guidance.
Each slot supports JPEG/PNG up to 10 MiB, preview, replacement and removal.
Validate content/decodability, size and dimensions, not just extensions.
4. **Review**: both previews, ability to return and replace, explicit confirmation
that photos belong to the user, then submit for human review. No automatic
approval, invented review SLA or instant increase in limits.
5. **Review status**: pending message, unchanged limits and manual refresh. A
correctable rejection has a specific safe reason and a fresh submission path;
final denial offers support guidance and no futile resubmit control. Approval
clearly shows the active higher tier. No forced upgrade or dead-end gate.
Back preserves the in-memory draft; leaving the flow asks whether to discard
selected photos. Refresh/closing clears a demo session. Successful submission
releases photo bytes. Never put images, paths or filenames in routes, analytics,
logs, preferences or task records. Demo decision controls are explicitly separate
from customer actions and operate only with the dummy service.
## State and recovery contract
| State | Tier | Action |
|---|---|---|
| Not verified | Basic | Start optional verification |
| Local draft | Basic | Continue / discard |
| Loading status | Unknown | Wait; don't assert basic/verified before response |
| Status unavailable | Last known, marked stale | Retry; block new submission until refreshed |
| In review | Basic | Refresh; continue account use |
| Changes requested | Basic | Reason: unreadable passport, unreadable selfie, expired passport; replace and resubmit |
| Denied | Basic | Explain decision category; contact support for review |
| Verified | Verified | See increased limits |
Picker cancellation is neutral. Invalid type, oversized/corrupt/too-small image,
permission/read failure and unavailable submission have distinct recoverable
messages. Keep valid photos on failed replacement/submission. Serialize operations,
prevent duplicate submits, ignore results after disposal. Server adapters must
provide idempotent submissions and current status/revision, enforce account and
email access, handle session expiry and rate limits, and validate images again.
Do not infer identity validity from client image checks.
## Measurement
Use existing typed AnalyticsService/JourneyRecorder. A KYC submission journey
starts when the user starts/restarts verification. Semantic stages: preparation,
passport selection, selfie selection, submission. Replacements/retries are
operations in the same journey. Confirmed service acceptance is the terminal
submission outcome, **not identity approval**; demo outcomes are simulated.
Explicit draft discard/navigation or 30 minutes of settled inactivity marks
abandonment. Do not count time waiting for human review as abandonment. Review
status refresh is a separate terminal observation, not a new approval conversion.
Measure start-to-submission conversion, per-stage failures and recovery, elapsed
submission time and explicit/inactivity abandonment. Only enum reason categories,
random journey IDs, operation sequence and elapsed time are collected. No photo
content/metadata, names, emails, document numbers, URLs or exception strings.
Disable third-party session replay/autocapture on identity routes when PostHog is
added. Production time-to-decision and approval rate require server-side review
events correlated by an opaque submission ID; frontend refresh cannot establish
those metrics. Cross-session resume requires the future backend.
## Visual and accessibility plan
Reuse theme colors/typography, rounded panels, locale selector and GoRouter.
Single scrollable mobile column; constrained desktop reading width. Text labels
and icons accompany status colors. Visible numbered steps, back/skip controls,
minimum touch targets, keyboard-accessible buttons, live-region operation/error
feedback, image semantic labels without filenames. Check EN/RU, 320px viewport,
large text, success/failure paths and analytics isolation before delivery.
## Production integration prerequisites
This frontend scope does not implement a staff backoffice, document storage or a
real KYC policy. An authenticated backend must own entitlements, limits and review
transitions. Provide private upload/download authorization, human-review roles,
audit trail, safe rejection categories, retention/deletion policy, user privacy
notice/support destination, idempotency and malware/content checks before enabling
real documents. No production claims, deadlines or statutory requirements are
invented here. Native camera capture can later complement the shared file picker.
## Sendico limits alignment
Inspected Sendico ADR 0001 and `api/proto/limits/v1/limits.proto`
(`ListVisibleLimits`, `VisibleLimitSlot`). No matching Flutter limits provider was
found in that checkout. Follow its ownership boundary: LimitsService.fetch()
returns backend-evaluated tier, visible caps, remaining capacity and upgrade
comparison. LimitsProvider loads/refreshes those models; widgets never derive
active limits from KYC. The dummy service performs the backend stand-in evaluation.
The visible projection does not reserve capacity or authorize payments.
## Implementation and review evidence
- Optional `/identity` demo entry from login and confirmed-email screens. No live
session is implied. GoRouter exit guard and discard confirmation protect drafts;
reload/closing the browser remains destructive to this explicitly transient demo.
- Shared package uses transport DTO → mapper → service → model → provider. Limits
are independently fetched and expose current/remaining/upgrade values.
- Typed analytics separates submission from status observation; picker cancellation
has a neutral cancelled signal. Simulated submission is not real identity approval.
- Review fixed a Flutter Web incompatibility: encoded ImageDescriptor dimensions
are unsupported. Image metadata is now read before bounded decoding using the
cross-platform image package. Actual browser file selection with a generated
640×640 PNG succeeds for both slots and reaches pending review with basic limits.
- EN/RU widget journeys cover approval, correction/resubmission, denial/support,
errors, discard, and 320px width at 1.6 text scale. Controls use theme colors and
labels; headings/errors expose live-region semantics, step changes move focus
and scroll context. Native VoiceOver/TalkBack and device camera QA remain release
checks for future mobile consumers; no camera capture is implemented here.
- Photo previews evict their image-cache entry on replacement/disposal. Providers
release photo references on exit/submission/disposal. No secure-memory-erasure
guarantee or browser-termination telemetry is claimed.
## Measurement definitions for dashboards
The denominator for submission conversion is journeys containing a requested
`kycPreparation` step; status-only refresh journeys are excluded. Numerator is a
terminal `kycSubmission` service outcome, counted once per journey. Exclude
`simulated=true` from production dashboards. Stage failure rate divides failed
operations by requested operations of that stage; cancelled pickers are reported
separately. Compare recovered submissions within the same journey, and calculate
duration from the first preparation request to acceptance. Explicit exit and
settled 30-minute inactivity are separate abandonment categories. Refresh after an
uncertain submission reconciles server status but cannot prove an end-to-end
submission conversion; production correlation needs the backend submission ID.
Final validation: format check (27 Dart files), analyzer in all three affected
packages, `make test-frontend` (86 tests) and release web build passed. The UX
checklist was reviewed against the implemented demo scope; production/backend
and native assistive-technology gaps are recorded above rather than represented
as complete.