All three wrappers speak the same contract and take the same configuration. The
walkthrough below is React Native; Swift and Kotlin mirror it.
Install
Present the sheet
config is every deposit modal prop that survives a JSON hop, under the same names, and it is not a mount-time value — change it and the page reconfigures in place, which is how appearance follows an app the user can re-theme mid-flow.
mode is deposit or withdraw. Any other value, claim included, is refused with an unsupported-mode error frame naming the mode, rather than a blank page.
What each prop turns on
Capabilities come from the handlers you pass, so the page never offers a row your app cannot serve.
Omitting one is not a degraded flow, it is a smaller one — QR and manual transfer need no wallet at all.
openUrl must present a browser over your app
expo-web-browser’s openBrowserAsync, or react-native-inappbrowser-reborn. Never Linking.openURL, which hands the user to whichever app claims the scheme, and never the web view itself, which would put a payment page on the same document as the bridge.
Answer when the browser is presented, not when it is dismissed — a dismissal-tied answer has to survive your activity being killed behind the browser. Nothing about the deposit depends on that answer: our own order-status route drives the tracker while the user is elsewhere.
Without openUrl the page offers no card row at all. That is deliberate — card verification routinely refuses an embedded web view, and a payment method that fails at the point of paying is worse than one never offered.
Your proxy must allow our origin
Every backend call from a mobile integration carrieshttps://deposit.rhinestone.dev as its Origin, not your app’s domain. A proxy with an explicit CORS allow-list rejects all of them at preflight — the whole sheet dead, with no request in your logs and no error naming CORS. See mobile integrations call from our origin; it is a one-line change, and it belongs in your proxy before your first mobile build.
Errors your handlers throw
ThrowuserRejected() when the user said no, walletUnavailable() when you could not reach the wallet, and submissionUncertain() when an app switch or a process death left you unable to say whether a transaction was broadcast.
The last one matters more than it looks. Without it you have to guess in one direction or the other, and the safe-looking guess — reporting failure — is the one that double-spends: the page re-offers its button and the user sends again. submissionUncertain() has copy that does not invite a retry, and anything else thrown from a sending handler is treated the same way for the same reason.
A deposit that outlives the sheet
The OS kills a backgrounded web view routinely. WhileDepositSheet is mounted it polls the same deposit route the page uses and reports terminal deposits through onDepositSettled, including one that both started and finished while the page was dead.
Once your process is gone, nothing native is running to notice.
Pinning the page
The wrapper points at the page tracking the npm@latest modal. embedUrl={EMBED_URL_DEV} points at the @dev snapshot instead — useful while you build against an unreleased change, and worth dropping before you ship.
The page adapts to the wrapper at runtime: both sides announce their protocol version and capabilities during the handshake, and the page serves whichever version it is talking to. That is what keeps a protocol bug fixable for an app already in the store, where you cannot ship a wrapper update on our schedule.
App store review
Both stores accept a hosted funding flow inside a web view as a component of a larger app. Two rules carry that position, and breaking either inverts it:- A deposited balance must not unlock anything in your app. Apple 3.1.1 names cryptocurrency wallets explicitly as a mechanism that may not unlock content or functionality.
- Do not charge an app-level fee on top of a deposit.
- Apple requires an organization developer account for wallet functionality. An individual account cannot ship this at all.
- Google treats non-custodial wallets as out of scope of its cryptocurrency exchange policy, but the Financial features declaration is still mandatory.
- Do not enroll in Google Play’s link-out or alternative billing programs for the on-ramp. Enrolling concedes that Play’s billing policy applies and pulls reporting and service fees onto payments that never touched Play billing.
- Google requires the site owner’s permission to present a site you do not own in a web view. Ask us for it — it covers our hosted page in your app.
- Give the reviewer a funded test account, say in the review notes that the card path deliberately opens the system browser, and name a country where the on-ramp is available. Region-dependent availability otherwise reads as a broken feature.
Store policies change and this is not legal advice. Check the current guidelines
before you submit.