Skip to content

chore(frontend): two buy buttons — unsigned vs signed OnRamper URL (demo, not for merge)#13138

Draft
sbpublic wants to merge 8 commits into
mainfrom
demo/frontend/onramper-unsigned-url
Draft

chore(frontend): two buy buttons — unsigned vs signed OnRamper URL (demo, not for merge)#13138
sbpublic wants to merge 8 commits into
mainfrom
demo/frontend/onramper-unsigned-url

Conversation

@sbpublic

@sbpublic sbpublic commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

⚠️ Demo branch — not intended for merge. It adds a second buy button that deliberately bypasses the OnRamper URL-signing security mechanism, and force-enables the buy widget on every environment (including beta), to compare the signed and unsigned flows without provisioning the backend signing secret.

Motivation

On staging the Buy button shows "Buying tokens is temporarily unavailable … while we further improve the security of this process by introducing URL signing." even though the build-time ONRAMPER_ENABLED flag is on. The widget is additionally gated by a runtime backend check (onramper_enabled), which only returns true once a controller has provisioned the onramper_signing_secret on the backend canister. Until that secret exists, the widget stays hidden.

This branch lets us exercise the OnRamper widget (on staging or beta) before the signing secret is provisioned, and to compare the two URL flows side by side.

Changes

Two buy buttons are rendered where there was one:

  • "Buy" — opens the widget with the fully composed URL unsigned, with no backend dependency.
  • "Buy S" — opens the widget with the production signed URL (HMAC obtained from the backend canister), exactly as the real flow works.

Implementation:

  • onramper.env.ts: ONRAMPER_ENABLED is forced to always-true (was LOCAL || STAGING) so the widget is enabled on beta and production too — required to deploy this demo to beta.
  • onramper.utils.ts: keeps the production signed buildOnramperLink (backend HMAC + &signature=<hex>) and adds buildUnsignedOnramperLink for the frontend-only variant; both share a private composeOnramperUrl helper.
  • OnramperWidget.svelte: a signed prop selects between the async backend-signed URL (with the signing-failure notice fallback) and the synchronous unsigned URL.
  • BuyModalContent.svelte / BuyModal.svelte: forward the signed flag from the button to the widget. The build-time ONRAMPER_ENABLED flag still gates the modal; the runtime onramper_enabled backend pre-check is dropped (signed mode now surfaces signing failures inside the widget instead).
  • Buy.svelte / BuyButton.svelte: render the two buttons; BuyButton gains optional buttonLabel / testId overrides so "Buy S" is distinct.

Tests

  • npm run format, npm run lint -- --max-warnings 0 and npm run check pass for the changed source files. (The unrelated pre-existing svelte-check errors in sol/btc/ic-pub-key modules come from local node_modules version skew and are not from this branch.)
  • onramper.utils.spec.ts passes (the signed builder is unchanged from production). BuyModalContent.spec.ts was updated to the two-mode behavior: unsigned mode renders the iframe without calling the backend, signed mode renders it once signing succeeds, and signed mode falls back to the unavailable notice when signing fails. Affected suites: 37 tests green (onramper.utils, BuyModalContent, BuyButton, hero Actions).

⚠️ OnRamper rejects unsigned widget URLs (Invalid Signature) since April 2025, so the "Buy" (unsigned) button may show an error inside the iframe. The point is that the widget opens with the full composed URL, not that OnRamper accepts it. On a backend without the signing secret, "Buy S" will show the unavailable notice (signing fails) — the same behavior as production today.

⚠️ On beta (and production), OnRamper runs in its non-dev mode: the widget hits https://buy.onramper.com with the prod API key (VITE_ONRAMPER_API_KEY_PROD), not the dev endpoint used on staging.


🤖 Generated with Claude Code — model: Claude Opus 4.8 (claude-opus-4-8)

sbpublic and others added 7 commits June 18, 2026 08:06
Demo branch (not for merge): drop the backend `signOnramperWidgetUrl`
round-trip and the `&signature=` suffix so the widget URL can be composed
entirely on the frontend, without provisioning the signing secret.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Demo branch (not for merge): build the iframe src synchronously now that
`buildOnramperLink` no longer awaits the backend, and drop the now-dead
signing-failure fallback path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Demo branch (not for merge): drop the runtime `onramper_enabled` backend
check (which requires the signing secret) so the widget opens on staging
from the build-time ONRAMPER_ENABLED flag alone, with no backend dependency.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…builder

Demo branch (not for merge): restore the production signed `buildOnramperLink`
(backend HMAC + `&signature=`) and add `buildUnsignedOnramperLink` for the
frontend-only variant, sharing a private `composeOnramperUrl` helper. This lets
the two buy buttons pick signed vs unsigned at call time.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Demo branch (not for merge): a `signed` prop chooses between the async
backend-signed URL (with the signing-failure notice fallback) and the
synchronous unsigned URL.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Demo branch (not for merge): render two buy buttons — the default "Buy" opens
the widget with the unsigned URL, "Buy S" opens it with the production signed
URL. The chosen mode is threaded through BuyModal and BuyModalContent into the
widget. BuyButton gains optional label/testId overrides for the second button.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Demo branch (not for merge): the backend gate moved into the widget, so the
content tests now assert unsigned mode renders the iframe without any backend
call, signed mode renders it once signing succeeds, and signed mode falls back
to the unavailable notice when signing fails.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@sbpublic sbpublic changed the title chore(frontend): open OnRamper buy widget with unsigned URL (demo, not for merge) chore(frontend): two buy buttons — unsigned vs signed OnRamper URL (demo, not for merge) Jun 18, 2026
Demo branch (not for merge): set ONRAMPER_ENABLED to always-true (was
`LOCAL || STAGING`) so the buy widget is enabled on beta too and this demo can
be deployed there. Kept as `true as boolean` so the flag stays mockable in tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

1 participant