Samuel1 ona/nim payment unavailable in a browser - #12
Merged
Samuel1-ona merged 2 commits intoSep 2, 2026
Conversation
…allet The NIM panel rendered nothing outside Nimiq Pay, because a browser has no injected Nimiq provider to pay with. That is also where every share link lands: somebody clicking a taunt on X arrives in a desktop browser, where NIM payment did not exist at all. The Web Wallet closes that gap through the Hub — a popup at hub.nimiq.com where the user's own wallet signs. `checkout()` both signs AND broadcasts, and returns the transaction hash, which is precisely the receipt the settler already goes looking for. Nothing on the server changes, and that is the point. `/api/nim/settle` verifies a payment by reading the funding transaction from a Nimiq node — "the payer controls their client, so their claim to have paid is not evidence" — so it neither knows nor cares which transport produced the transaction. Same quote, same HMAC tag in the data field, same `settledNimTx` guard, same confirmation wait. The quote route, the settle route and the contract are untouched. What changes is one client seam. `sendNimWithData` now dispatches on `isNimiqPay()`: the mini-app SDK inside Nimiq Pay, `lib/nimiqHub.ts` in a browser. Both return a hash, both throw `NimiqProviderError`, so callers keep one contract and `useNimPayment` is unchanged. Three constraints held: - **Bundle isolation, in both directions.** The Hub is reached through a dynamic `import()`, mirroring the rule that keeps `@nimiq/mini-app-sdk` out of a browser's graph — Nimiq Pay runs a 2018 Android System WebView and will never open a popup. Verified against the BUILT output, not just the source: the Hub SDK lands in its own 26 KB lazy chunk and is absent from both shared first-load chunks. Only the endpoint constant reaches the page chunk. - **The endpoint decides the network.** `checkout` broadcasts as it signs, so no client-side check can undo a testnet payment after the fact, and the settler pins mainnet (networkId 24). Pointing at the mainnet Hub is the only real guard, so that is the default and a test asserts it. - **Popups need a user gesture.** The panel already pauses for an explicit pay tap, so this is satisfied by the existing flow rather than by new code. One existing test changed meaning rather than breaking: `sendNimWithData` "throws outside Nimiq Pay" was correct while the SDK was the only transport. It now asserts the hand-off instead, with the mini-app dialog still never raised outside Nimiq Pay, plus a new case proving validation runs before either transport is reached. Verification: 15 new tests covering the transport (hash out, treasury/Luna/tag in, a cancelled popup becoming NimiqProviderError, a missing hash refused rather than reported as success) and the bundle boundary in both directions, each validation case paired with a control asserting the Hub was never reached. Full suite 894 passing, up from 838. `tsc --noEmit` clean and `next build` passes. Installing @nimiq/hub-api also re-linked ox@0.14.5, which had been in the pnpm store but not linked into apps/web/node_modules — so the type-check and the 3 test files that failed on main for that reason are green here. That was an environment fault, not a code change. NOT verified automatically, and it needs a manual pass on the preview deploy before the demo: the live popup flow. No test can prove a real Web Wallet payment reaches the settler — the Hub is mocked here. Worth one real end-to-end run in a browser, and note that NIM_MAX_ORDER_USD_MICROS must be correct in that environment or every re-buy is refused (see 92315dc). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdXsPDjCuFTx4BXd7qc1ow
The NIM panel reused the mini-app copy in a browser, so a player paying through the Web Wallet was told to "Confirm the payment in Nimiq Pay" — an app they are not in and may not have. Introduced by ec01202, which opened the panel to browsers without splitting the strings that name the host. The panel now resolves which wallet it is talking to once, after mount, and the two strings follow from that. The browser line also warns that the window may pause: the Hub runs a light client and sits on "Syncing consensus…" before it can broadcast, and an unexplained wait on a payment screen reads as a hang — which is what it looked like in testing. Verification: full suite 894 passing, `tsc --noEmit` clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdXsPDjCuFTx4BXd7qc1ow
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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.
The hole, and the fix
What this does NOT do / residual risk
Judgement calls
Issues
Closes #
Refs #
Stacking / conflicts
Verification evidence
Remaining ops steps
Checklist
mainlint/typecheck/testpass locally on the current headClosesissue — all met.env.example/ examples / error strings updated for the world this createsmain, lockfile regenerated (never hand-resolved)money-path-checklist.mdrun; payout logic compared against the other side