Add the Quick checkpoint flow for rewards - #1948
Open
efstajas wants to merge 2 commits into
Open
Conversation
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.
Contributor
There was a problem hiding this comment.
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/checkpointintro,/wave/checkpoint/verify(Sumsub WebSDK + polling), and/wave/checkpoint/successroutes. - Adds a
/wave/rewardslayout 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
LivenessCheckpointRequiredErrorfor 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.
- 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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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/rewardsthat 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 viabackTo.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
rewards/+layout.tsso it covers both the list and the detail page. Both child loadsawait parent()before touching the rewards API, so a redirect stops them before they'd hit a 403.npm run checkis clean (0 errors; the 177 warnings are pre-existing and none are from these files).