Skip to content

Web, Docs, Widget, Checkout & Portal

The public-facing surfaces of VelaPay. Each is a separate deployment with different constraints, audiences, and technical requirements.


vela-web — Landing Page

The marketing site at velapay.com. Built for public product storytelling, SEO, and conversion.

AreaChoice
FrameworkAstro 6
StylingTailwind CSS v4 (CSS-first config)
InteractivityReact 19 islands (only where needed)
DeploymentCloudflare Workers

Zero JS Strategy

Astro ships zero JS by default. Interactive components are loaded as React islands only where needed:

  • Pricing toggle (monthly/annual comparison)
  • Wallet connect (for "Try it now" CTA)
  • Any other interactive elements are individual Astro islands

This means the landing page loads fast, indexes well, and doesn't ship a React bundle for static content. Tailwind v4's CSS-first configuration uses @theme directives in CSS instead of a tailwind.config.js file.

Why Astro

  • Zero JS by default — fastest possible page load for a marketing site
  • Cloudflare acquired Astro (Jan 2026) — first-class Workers support
  • Built-in Fonts API and CSP API in v6
  • Content-focused: markdown/MDX pages, image optimization, sitemap generation

vela-docs — Developer Documentation

The developer docs surface at docs.velapay.com. Built for SDK reference, quickstart guides, and architecture documentation.

AreaChoice
FrameworkAstro 6
Docs ThemeStarlight 0.38
DeploymentCloudflare Workers

Content Architecture

~60 MDX pages organized by audience:

SectionAudienceContent
QuickstartNew developers5-minute setup, first subscription
SDK ReferenceIntegratorsAPI docs for every SDK method
ArchitectureTechnical evaluatorsADRs, protocol design, privacy model
Program ReferenceProtocol developersAccount structs, instructions, error codes
GuidesMerchantsPlan creation, webhook setup, analytics
x402 IntegrationAgent buildersHTTP 402 adapter usage

Why Starlight

  • Built-in search via Pagefind (client-side, no external service)
  • Navigation, dark/light mode, i18n support out of the box
  • Zero JS by default (Astro-based)
  • Cloudflare uses Starlight for their own docs — production-proven

vela-widget — Embeddable Checkout Widget

The embeddable JS widget that merchants add to their own websites. Loads as a modal overlay.

Split Architecture

The widget has two parts:

  1. Loader script (~2.6KB gzipped): tiny <script> tag that merchants embed on their page. Detects installed wallets via the parent page's Wallet Standard API.
  2. Checkout iframe: full React checkout app loaded in a sandboxed iframe. Hosted on Cloudflare Workers at js.velapay.com.

Wallet Relay via postMessage

Wallet access is the key architectural challenge. The iframe cannot directly access the parent page's wallet connection. The solution:

  • Loader script on the parent page detects wallets via Wallet Standard
  • When the checkout iframe needs a signature, it sends a postMessage to the parent
  • Parent relays the signing request to the wallet extension
  • Signed transaction is relayed back to the iframe via postMessage

This split ensures the merchant's page never gives the iframe direct wallet access, and the iframe never needs to bundle wallet adapter code.

Dependencies

  • @vela/sdk for transaction construction (using the browser subpath)
  • React 19 for the checkout UI (WalletStep → ReviewStep → ConfirmStep → SuccessScreen)
  • Bun build for the final bundled output

vela-checkout — Hosted Checkout

Standalone Cloudflare Worker at pay.velapay.com. A full-page hosted checkout experience, separate from the embeddable widget.

Why a Separate Deployment

vela-widget (embed)vela-checkout (hosted)
Wallet accessVia parent page's loader relayDirect — page connects wallets natively
RoutingSingle modal, no URL routes/session/{id}, /link/{slug}, /invoice/{id}
DeploymentScript tag on merchant siteStandalone Worker at pay.velapay.com
UX modelModal overlay on merchant's pageFull-page experience, redirects back
Auth modelParent page owns wallet connectionPage owns wallet connection directly

Key Routes

RoutePurpose
/session/{id}Hosted checkout page (merchant redirects subscriber here)
/link/{slug}Permanent payment link (creates fresh session per visit)
/invoice/{id}Hosted invoice view with PDF download

Service Binding

The checkout Worker binds to the dashboard backend for session data and invoice management:

toml
[[services]]
binding = "DASHBOARD"
service = "vela-dashboard-api"

Features

  • Checkout sessions with configurable TTL (30 min – 24 hr)
  • Payment links with conversion analytics
  • Auto-generated invoices with PDF delivery
  • Failed payment handling with grace periods and retry
  • Turnstile bot protection
  • Rate limiting per-merchant and per-IP

vela-portal — Customer Portal

Standalone Cloudflare Worker at portal.velapay.com. Self-service subscription management for subscribers.

Key Features

  • View active subscriptions across all merchants
  • Cancel subscription (revokes mandate on-chain)
  • Switch plans (creates new mandate + revokes old in single flow)
  • Payment history with invoice links and tx signatures
  • Download invoice PDFs
  • Multi-wallet linking (see mandates across multiple wallets)

Auth Model

  • SIWS (Sign-In With Solana) standard — domain binding prevents phishing
  • Magic link fallback — email-based auth if subscriber provided email at checkout
  • Session tiers — 4-hour browsing, fresh SIWS for destructive actions (cancel, switch)
  • "Remember this device" — optional 7-day session with re-auth for destructive ops

Service Binding

Like checkout, portal binds to the dashboard backend:

toml
[[services]]
binding = "DASHBOARD"
service = "vela-dashboard-worker"

Portal also uses KV for session management:

toml
[[kv_namespaces]]
binding = "PORTAL_SESSIONS"
id = "placeholder-portal-sessions-kv"

Why Each Surface Deserves Its Own Deployment

SurfaceDeployment TargetWhy Separate
vela-webCloudflare Workers (static assets)Marketing site. Different update cadence, different build, zero JS.
vela-docsCloudflare Workers (static assets)Developer docs. Starlight-specific build pipeline. Independent from app deployments.
vela-widgetCloudflare Workers (js.velapay.com)Embeddable script. Must be independently versioned — merchants embed a specific version. Breaking changes must not affect existing embeds.
vela-checkoutCloudflare Worker (pay.velapay.com)Hosted checkout. Different security boundary (public-facing, Turnstile, rate limits). Session management requires its own D1 access patterns.
vela-portalCloudflare Worker (portal.velapay.com)Customer-facing auth surface. SIWS domain binding means it must be on its own domain. Different session model (magic links + SIWS vs. email + wallet).

Each surface has a distinct:

  • Audience (merchants, developers, subscribers, prospects)
  • Security boundary (admin auth vs. merchant auth vs. subscriber auth vs. public)
  • Domain (velapay.com, docs.velapay.com, pay.velapay.com, portal.velapay.com, js.velapay.com)
  • Deploy cadence (marketing changes don't need to ship with protocol updates)
  • Scaling profile (landing page = mostly static; checkout = bursty; portal = steady low traffic)

Internal knowledge base for the Vela Labs workspace.