Skip to content

Add the Quick checkpoint flow for rewards - #1948

Open
efstajas wants to merge 2 commits into
mainfrom
wave-quick-checkpoint
Open

Add the Quick checkpoint flow for rewards#1948
efstajas wants to merge 2 commits into
mainfrom
wave-quick-checkpoint

Conversation

@efstajas

@efstajas efstajas commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Adds the "Quick checkpoint" flow — a short identity check users may be asked to complete before viewing or withdrawing their rewards.

The API decides on its own whether a check is due; this PR is the UI for it, plus a guard on /wave/rewards that sends the user to the flow when the API says one is needed. Without the guard those pages would break for anyone the API is holding back.

Flow

Follows the same shape as phone verification, which is the closest existing precedent:

  • /wave/checkpoint — intro, with a Learn more link.
  • /wave/checkpoint/verify — the Sumsub WebSDK, then a "Checking…" state.
  • /wave/checkpoint/success — confetti, then back to wherever they came from via backTo.

Copy leads with how quick it is and that withdrawal is available immediately afterwards, so it reads as a speed bump rather than another verification.

Waiting for the result

Once the selfie is submitted the page polls the API until there's a result. The API is the only authority on the outcome, so the client just asks it every 2s.

Polling starts as soon as the SDK mounts rather than waiting on an SDK event — if we didn't recognise an event, the poll still resolves the flow rather than leaving someone stuck on a spinner. The SDK's own events only drive the switch to the spinner UI. The iframe is hidden rather than destroyed during the wait, since tearing it down mid-review can abort an upload it's still finishing.

A failed check returns to the intro with retry wording. Users who can't currently retry get a "contact support" message and no retry button.

Also

A typed error in the API client for the 403 the API returns while a check is outstanding, so one falling due mid-session is actionable rather than a generic 403.

Notes for review

  • Nothing changes for anyone until this is switched on server-side, and the API change needs to go out first.
  • The client deliberately parses only the few response fields it needs and never reproduces any of the API's decision logic.
  • The guard lives in rewards/+layout.ts so it covers both the list and the detail page. Both child loads await parent() before touching the rewards API, so a redirect stops them before they'd hit a 403.
  • npm run check is clean (0 errors; the 177 warnings are pre-existing and none are from these files).

Adds a short identity check that users may be asked to complete before
viewing or withdrawing rewards, plus a guard on /wave/rewards that sends
them to it whenever the API says one is due. Without the guard those pages
would break for anyone the API is holding back.

The flow follows the same shape as phone verification: an intro step
explaining what's being asked, the check itself via the Sumsub WebSDK, then
a success step back to wherever the user came from. Copy leads with how
quick it is and that withdrawal is available immediately afterwards, since
this is meant to read as a speed bump rather than another verification.

Once the check is submitted the page polls the API until it has a result.
The API is the only authority on the outcome, and polling starts as soon as
the SDK mounts rather than waiting on an SDK event, so an event we don't
recognise can't leave someone stuck on a spinner. A failed check returns to
the intro with retry wording; users who can't retry are pointed at support.

The client parses only the response fields it needs and never reproduces any
of the API's decision logic.

Also adds a typed error to the API client so a check that falls due
mid-session surfaces as something callers can act on rather than a generic
403.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new “Quick checkpoint” identity-check flow in Wave and guards the rewards pages so users who are due for a checkpoint are redirected into the flow instead of hitting blocked rewards endpoints.

Changes:

  • Introduces /wave/checkpoint intro, /wave/checkpoint/verify (Sumsub WebSDK + polling), and /wave/checkpoint/success routes.
  • Adds a /wave/rewards layout guard that checks liveness checkpoint status and redirects to the flow when unsatisfied.
  • Adds a typed Wave API client for liveness checkpoints and a dedicated LivenessCheckpointRequiredError for actionable 403s.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/routes/(pages)/wave/(flows)/checkpoint/+layout.ts Loads checkpoint status for the flow and enforces login.
src/routes/(pages)/wave/(flows)/checkpoint/+layout.svelte Sets flow page metadata (title).
src/routes/(pages)/wave/(flows)/checkpoint/+page.ts Intro-step load: redirects away when already satisfied; provides backTo.
src/routes/(pages)/wave/(flows)/checkpoint/+page.svelte Intro UI with retry/locked messaging and Learn more / Start check actions.
src/routes/(pages)/wave/(flows)/checkpoint/verify/+page.ts Verify-step load: starts/resumes a liveness session and provides Sumsub token.
src/routes/(pages)/wave/(flows)/checkpoint/verify/+page.svelte Mounts Sumsub WebSDK and polls backend until checkpoint resolves.
src/routes/(pages)/wave/(flows)/checkpoint/success/+page.ts Success-step load: prevents success UI unless checkpoint is satisfied.
src/routes/(pages)/wave/(flows)/checkpoint/success/+page.svelte Success UI with confetti and “Continue” to backTo.
src/routes/(pages)/wave/(base-layout)/rewards/+layout.ts Guards rewards routes by redirecting into checkpoint when required.
src/lib/utils/wave/liveness.ts Adds typed liveness checkpoint status/session API calls.
src/lib/utils/wave/call.ts Adds LivenessCheckpointRequiredError and throws it on the backend’s checkpoint-required 403 code.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/routes/(pages)/wave/(flows)/checkpoint/verify/+page.svelte Outdated
Comment thread src/routes/(pages)/wave/(flows)/checkpoint/verify/+page.svelte
Comment thread src/routes/(pages)/wave/(flows)/checkpoint/+page.svelte
- Treat an 'expired' challenge as terminal in the verify page's poll loop.
  Nothing will ever resolve a superseded attempt, so the loop would have spun
  indefinitely; it now returns to the intro like a failed one does.
- Correct the poll loop's comment, which read as though the first request
  fires on mount. The point being made was that the loop doesn't depend on
  SDK events, so say that instead.

Left the intro's failure copy keyed on 'rejected' only, and noted why:
'expired' means abandoned or superseded rather than failed, so "that check
didn't go through" would be the wrong thing to tell that user.
@efstajas
efstajas requested a lite review from Copilot August 6, 2026 20:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants