2. On-Ramp → Merchant (Top-Up Flow)
How a user tops up a merchant account by on-ramping fiat directly through to the merchant's stealth address — in a single integrated flow.
Actors
| Actor | Role |
|---|---|
| User | End user on the merchant's site wanting to top up |
| Merchant | Operates the merchant platform; generates stealth addresses and confirms payments |
| On-Ramp Provider | Operates the on-ramp service; handles KYC, payment processing, and token delivery |
Components
| Component | Description |
|---|---|
| Merchant Site | User-facing merchant frontend where the top-up flow begins |
| Merchant Backend | Custodial wallet access, stealth address generation, payment scanning |
| On-Ramp Web App | User-facing frontend — KYC UI, payment forms, wallet creation (client-side), consent screens |
| On-Ramp Backend | Processes payments, sends tokens to user's public wallet address |
| On-Chain (Contract) | Manages encrypted balances, redirect registration, and transfers |
Flow Overview
Detailed Flow
ZKP/EB Components and Features Used
| Component / Feature | Used? | How |
|---|---|---|
| Stealth Addresses | Yes | Merchant generates a one-time stealth address per user/session from its stealth meta-address |
| Encrypted Balance (EB) | Yes | Funds forwarded to merchant's stealth address are held as encrypted balance — amount hidden on-chain |
| Autoshielding to zk address | Yes | Required for self-custodial transfer user-merchant. The auto-redirect converts a public transfer into EB — an observer cannot distinguish whether the user encrypted funds for themselves or transferred to another party. This is the key privacy boundary. |
| Custodial wallet infra | Yes | Merchant backend needs access to private key to generate stealth addresses |
Privacy Properties
| Property | Hidden? | How |
|---|---|---|
| Merchant receives funds at unlinkable address | Yes | Stealth address prevents correlation |
| User's on-chain identity hidden from merchant's other customers | Yes | Stealth address prevents correlation |
| User has a self-custodial wallet | Yes | Keys held by user, backup via email |
| Payment amount hidden on-chain | No | Public transfer from OnRamp |
| Auto-redirect indistinguishable from self-encryption | Yes | Once public tokens enter EB via auto-redirect, an observer cannot tell if the user encrypted for themselves or transferred to someone else — self-shield and transfer look identical on-chain |
Requirements
| # | Requirement | Notes |
|---|---|---|
| R1 | On-ramp web app creates self-custodial wallets client-side | Keys never leave the browser; backend only receives the public address |
| R2 | User gives explicit consent before redirect registration | Regulatory and UX requirement |
| R3 | On-chain redirect mechanism auto-forwards funds | No manual step after funding |
| R4 | On-Ramp must send public transfer to user | Required by VISA |
| R5 | High-risk merchant (Visa classification): on-ramp cannot be embedded directly into the merchant's payment flow | Flow must be separated into 3 distinct steps: (1) top-up initiation on merchant site, (2) self-custodial wallet creation, (3) on-ramp on a separate domain. The on-ramp must not appear as part of the merchant's checkout. |
| R6 | Low-risk merchant: entire flow can happen in a single integrated experience | Top-up, wallet creation, KYC, payment, and redirect registration can all occur within one site / embedded flow |
High-Risk Merchant: Multi-Page Architecture (R5)
When the merchant is classified as high-risk by Visa, the on-ramp must not be embedded in the merchant's payment flow. The user journey is split across 3 separate web pages that coordinate via a WebSocket Wallet Connector service.
Pages
| # | Page | Domain / Owner | Responsibility |
|---|---|---|---|
| 0 | Merchant Site | Merchant | Initiates top-up, generates stealth address, opens page 1 via link/redirect |
| 1 | Self-Custodial Wallet | Wallet Provider | Creates/loads the user's self-custodial wallet (client-side), registers redirect, signs consent |
| 2 | On-Ramp | On-Ramp Provider | Runs KYC, processes fiat payment, sends tokens to the user's public wallet address received from page 1 |
Wallet Connector Service
A lightweight WebSocket relay that enables the three pages to exchange messages without sharing a domain or embedding each other.
| Aspect | Detail |
|---|---|
| Transport | WebSocket (persistent, bidirectional) |
| Session | Short-lived session ID created by page 0, passed as URL param to pages 1 & 2 |
| Messages | Typed JSON envelopes (e.g. stealth_address, public_wallet, redirect_registered, tx_complete) |
| Trust model | Connector is a dumb relay — each page independently verifies on-chain state |
| Hosted by | Wallet Provider or neutral infra (not the merchant) |
Architecture
Sequence (High-Risk Flow)
Low-Risk Merchant Alternative
When the merchant is not high-risk, pages 0, 1, and 2 collapse into a single integrated experience (embedded SDK or same-domain flow). The Wallet Connector is not needed — all coordination happens in-process.
Assumptions
| # | Assumption | Impact if Wrong |
|---|---|---|
| A1 | On-ramp web app is like standard payment gate (płatności24), merchant can link to, outside of their app | Could be embedded SDK instead |
| A2 | Auto-redirect registration happens via relayer | User account has to be a smart account, with paymaster |
| A3 | Wallet is part of the on-ramp web app | If self-custody needs better separation, it would be another redirect in the process or an iframe |
| A4 | On-Ramp maintains pre-funded accounts with public amounts | If onramp can initiate payment from EB, it changes design slightly |
| A5 | Merchant can generate unique stealth addresses per user/session, and then L3 support in the merchant offramp step | If not possible, graph is visible on-chain |
| A6 | On-ramp web app accepts merchant stealth address as parameter | If not supported, integrated flow requires a different handoff mechanism |
| A7 | Wallet backup delivered to user's email | If no backup path, user risks losing access to self-custodial wallet, alternative backup options are possible |
| A8 | Merchant can scan stealth addresses and decrypt amounts | If not possible, merchant cannot detect or attribute payments |
Open Questions
| # | Question | Context | Answer |
|---|---|---|---|
| Q1 | Can Merchant go from public amount -> L3? | If yes, then the autoredirected transfer could go to public amount. Payment-merchant link is relying on L3. | |
| Q2 | Do we need to provide interface for user to manage their account and redirect? | They should be able to cancel redirect, it's self-custody | |
| Q3 | How exactly does the user recover/access their self-custodial wallet later? | Shards? Email backup? | |
| Q4 | Can the whole flow happen on the merchant app (via sdk)? | Top-up, never leaving the merchant | |
| Q5 | How does the merchant attribute a stealth address payment to a specific user? | Merchant generated the stealth address for a specific user session — needs to maintain a mapping (stealthAddress → userId) | |
| Q6 | What if KYC fails or payment is reversed after funds are forwarded to the merchant? | Chargeback/clawback mechanism needed. Who bears the risk? | |
| Q7 | Can this flow support recurring top-ups? | User might want to top up monthly without re-doing the full flow. Persistent redirect + saved payment method? | |
| Q8 | How are chargebacks handled once funds have been redirected to the merchant's stealth address? | Card chargebacks can occur days/weeks later. By then, tokens are in the merchant's encrypted wallet. Who bears the loss — On-Ramp or merchant? Is there an escrow period before redirect? | |
| Q9 | When exactly is the merchant informed that payment has been made? | Is it when the on-ramp confirms fiat? When tokens land in the user's public wallet? When the auto-redirect delivers to the stealth address? Merchant needs a clear "payment finality" signal — what triggers it and who sends it? | |
| Q10 | Is "Merchant Backend" actually a payment-rails backend (middleware) that communicates with the merchant's own backend? | Current doc treats the merchant backend as a single actor. In practice, the stealth address generation and payment scanning may live in a shared payment-rails service, with the actual merchant backend behind it. If so, this part of the system needs a separate design doc. |