Files

9.8 KiB
Raw Permalink Blame History

Identity verification UX plan

Task #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.

Limits usage correction — task #22

Visible limits include backend-provided committed (usedMinor) and reserved (reservedMinor) usage, matching Sendico's VisibleLimitSlot. Show both amounts, the current cap and authoritative remaining capacity for each period. A labeled theme-based indicator visualizes used plus reserved against the cap; clamp only the visual ratio, never rewrite the returned monetary values. No capacity is communicated in text when remaining is zero. Daily/monthly demo fixtures now have nonzero activity, retained when caps increase after approval.

This is a display correction within the existing KYC journey, so no additional analytics events or financial data collection are introduced. Tests cover mapping without inferring remaining, unused/exhausted/over-cap/zero-cap ratios and EN/RU usage display at 320px with 1.6 text scaling.