150 lines
9.0 KiB
Markdown
150 lines
9.0 KiB
Markdown
# 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.
|