Skip to content

vela-portal

Subscriber self-service portal at portal.velapay.com. Subscribers manage their own subscriptions.

Purpose and Role

  • Subscriber portal for managing active subscriptions.
  • Magic-link and SIWS authentication — subscribers don't need a merchant account.
  • View active subscriptions across all merchants.
  • Cancel subscriptions (mandate cancellation).
  • Switch plans (upgrade/downgrade) via shared checkout-session pattern.
  • Invoice downloads for billing history.
  • Multi-token rendering via TokenConfig resolution (displays amounts in subscriber's preferred token).

Tech Stack

TechnologyVersionPurpose
React19.xUI library
Hono4.12.xAPI layer (Cloudflare Worker)
Cloudflare D1GASubscription state, session management
Cloudflare WorkersCompute runtime
@vela/sdklatestMandate interactions, account deserialization
Better Auth (magic-link)1.5.xMagic-link email authentication
Tailwind CSS4.2.xStyling
wrangler4.78.xCloudflare CLI

Directory Structure

vela-portal/
├── src/
│   ├── app/                   # React SPA
│   │   ├── components/
│   │   │   ├── subscriptions/
│   │   │   │   ├── SubscriptionList.tsx    # Active subscriptions view
│   │   │   │   ├── SubscriptionDetail.tsx  # Single subscription detail
│   │   │   │   ├── SwitchPlanModal.tsx     # Plan upgrade/downgrade
│   │   │   │   └── CancelSubscription.tsx  # Cancellation flow
│   │   │   ├── invoices/
│   │   │   │   └── InvoiceList.tsx         # Invoice download list
│   │   │   └── auth/
│   │   │       ├── MagicLinkForm.tsx       # Magic-link email input
│   │   │       └── SIWSConnect.tsx         # Wallet sign-in option
│   │   ├── hooks/
│   │   │   ├── useSubscriptions.ts         # Subscription data fetching
│   │   │   └── usePlanSwitch.ts            # Plan switch flow
│   │   └── pages/
│   │       ├── dashboard.tsx               # Main dashboard
│   │       ├── subscription.tsx            # Subscription detail
│   │       └── verify.tsx                  # Magic-link verification
│   ├── worker/                # Hono Worker (backend API)
│   │   ├── index.ts           # Worker entry
│   │   ├── auth.ts            # Magic-link + SIWS auth
│   │   ├── session.ts         # Session management
│   │   ├── routes/
│   │   │   ├── subscriptions.ts  # Subscription CRUD
│   │   │   ├── plans.ts          # Plan switching
│   │   │   ├── invoices.ts       # Invoice retrieval
│   │   │   └── checkout.ts       # Checkout session creation (for plan switch)
│   │   └── middleware/
│   ├── db/                    # D1 schema (portal-specific tables)
│   └── lib/
├── wrangler.toml              # D1 binding
├── vite.config.ts
└── package.json

Deployment Target

  • Cloudflare Workers: Hono Worker for API + React SPA for UI.
  • Domain: portal.velapay.com.

Dependencies

What It Depends On

DependencyTypePurpose
@vela/sdknpm packageMandate interactions, account deserialization
vela-dashboardAPI proxyCheckout session creation for plan switching
Cloudflare D1InfrastructureSubscription state, sessions
Better AuthLibraryMagic-link authentication

What Depends on It

Nothing. The portal is a terminal consumer — subscribers use it directly.

Current Status

  • v1.5 complete: Subscriber portal with magic-link auth, subscription management, plan switching.
  • v1.8 complete: Multi-token rendering, Playwright E2E validation.

Notable Design Decisions

Subscribers can authenticate via:

  1. Magic link: Enter email → receive a login link → click to authenticate. Familiar UX for non-crypto users.
  2. SIWS: Sign a message with their wallet. Familiar UX for crypto-native users.

Both methods create a session in D1. Subscribers can use either method to access the same account.

Separate from Dashboard

The portal is separate from the merchant dashboard because:

  • Different audience: Subscribers, not merchants.
  • Different auth: Magic-link/SIWS vs email-primary/org.
  • Different security model: Public-facing with subscriber-scoped access vs internal with org-scoped access.
  • Different deploy cadence: Portal features change independently from merchant features.

Shared Checkout-Session Pattern

Plan switching reuses the checkout-session creation pattern from vela-checkout:

  1. Subscriber selects new plan in portal.
  2. Portal creates a checkout session via dashboard API.
  3. Subscriber is redirected to checkout flow (or inline upgrade).
  4. SDK builds the upgrade instruction (initiate_upgrade).
  5. Session marked complete, webhook fires.

Multi-Token Rendering (v1.8)

Portal renders subscription amounts in the subscriber's preferred token using TokenConfig resolution and SDK helpers (formatAmount/parseAmount). Plans denominated in PYUSD can be displayed in USDC equivalent (client-side oracle lookup).

Invoice Downloads

Subscribers can download invoices for completed billing periods. Invoices are generated from on-chain pull events and stored in D1. PDF generation happens client-side.

Subscription Aggregation

Portal aggregates subscriptions across all merchants for a single subscriber. The subscriber sees all active mandates in one view, regardless of which merchant they subscribed to.

Internal knowledge base for the Vela Labs workspace.