Appearance
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.
| Area | Choice |
|---|---|
| Framework | Astro 6 |
| Styling | Tailwind CSS v4 (CSS-first config) |
| Interactivity | React 19 islands (only where needed) |
| Deployment | Cloudflare 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.
| Area | Choice |
|---|---|
| Framework | Astro 6 |
| Docs Theme | Starlight 0.38 |
| Deployment | Cloudflare Workers |
Content Architecture
~60 MDX pages organized by audience:
| Section | Audience | Content |
|---|---|---|
| Quickstart | New developers | 5-minute setup, first subscription |
| SDK Reference | Integrators | API docs for every SDK method |
| Architecture | Technical evaluators | ADRs, protocol design, privacy model |
| Program Reference | Protocol developers | Account structs, instructions, error codes |
| Guides | Merchants | Plan creation, webhook setup, analytics |
| x402 Integration | Agent builders | HTTP 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:
- 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. - 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
postMessageto 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/sdkfor transaction construction (using thebrowsersubpath)- 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 access | Via parent page's loader relay | Direct — page connects wallets natively |
| Routing | Single modal, no URL routes | /session/{id}, /link/{slug}, /invoice/{id} |
| Deployment | Script tag on merchant site | Standalone Worker at pay.velapay.com |
| UX model | Modal overlay on merchant's page | Full-page experience, redirects back |
| Auth model | Parent page owns wallet connection | Page owns wallet connection directly |
Key Routes
| Route | Purpose |
|---|---|
/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
| Surface | Deployment Target | Why Separate |
|---|---|---|
vela-web | Cloudflare Workers (static assets) | Marketing site. Different update cadence, different build, zero JS. |
vela-docs | Cloudflare Workers (static assets) | Developer docs. Starlight-specific build pipeline. Independent from app deployments. |
vela-widget | Cloudflare Workers (js.velapay.com) | Embeddable script. Must be independently versioned — merchants embed a specific version. Breaking changes must not affect existing embeds. |
vela-checkout | Cloudflare Worker (pay.velapay.com) | Hosted checkout. Different security boundary (public-facing, Turnstile, rate limits). Session management requires its own D1 access patterns. |
vela-portal | Cloudflare 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)