Stablecoin Checkout docs
v1.0.0
Live demoOperator console demo Get help
● Set up · Keys and settings

Configuration
env files and console settings.

Where each setting lives: server settings in the server env file, public build settings in VITE_* variables, and everything that changes behaviour day to day in the operator console.

01Three places, one rule

WhereWhat goes thereA change needs
Server env (apps/server/.env, /etc/mikodes-checkout/server.env or deploy/.env)RPC URLs, fee addresses, operator key, console key, URLs, pathsA server restart
VITE_* at build timeAPI origin, public RPC, Base router address for the browserRebuilding the web apps
Operator consoleFees, tiers, fee credit, referral, branding, legal, checkout behaviour, chains and assets, onboarding, webhooks, sanctions mode, integrationsSave in the console. No restart.
VITE_ values are public

Everything in a VITE_* variable is compiled into JavaScript that every visitor downloads. Never put a paid RPC key, an API key or any secret there. The browser gets a read-only proxy (/public/solana/prep/:id) so your RPC URL stays on the server.

02Server env

Template: apps/server/.env.example (Docker: deploy/.env.example). Keys and addresses have no defaults; empty means disabled. The server refuses to start with an invalid value and names it.

VariablePurposeDefault
SOLANA_RPC_URL RequiredYour Solana RPC. Enables Solana.empty
SOLANA_CHAINsolana-devnet or solana-mainnet: which network that RPC serves.solana-devnet
OPERATOR_SOLANA_ADDRESSWhere your fee goes on Solana (public address). Empty = no fee on Solana.empty
BASE_RPC_URL, BASE_CHAINYour Base RPC; base-sepolia or base-mainnet.empty, base-sepolia
BASE_ROUTER_ADDRESSYour deployed MikodesPayRouter. Required when BASE_RPC_URL is set (how).empty
BASE_CONFIRMATIONSBlocks before a Base payment counts as confirmed.12
OPERATOR_BASE_ADDRESSWhere your fee goes on Base. Empty = no fee on Base.empty
OPERATOR_ADMIN_KEYOperator API key, at least 32 characters. Empty disables every /v1/operator endpoint. Generated by the setup script.empty
ADMIN_SECRET_KEY32 bytes (openssl rand -hex 32). Encrypts console secrets (Telegram token, SMTP URL) at rest. Empty = console off.empty
PUBLIC_CHECKOUT_URLCheckout base URL; invoices get checkoutUrl = <this>/pay/<id>.empty
PUBLIC_ADMIN_URLBase URL of the portal, used in invite, verification and Telegram texts.empty
CORS_ORIGINSComma-separated browser origins allowed to call the API (checkout, admin, landing).empty
PORT, HOSTWhere the server listens. 127.0.0.1 behind a reverse proxy, 0.0.0.0 in a container.8787, 127.0.0.1
TRUST_PROXYtrue only behind a reverse proxy you control (uses X-Forwarded-For for rate limits).false
DATABASE_PATHSQLite file../data/checkout.sqlite
ADMIN_DATABASE_PATHConsole storage.admin.sqlite next to DATABASE_PATH
ADMIN_UI_DIR, OPS_UI_DIRBuilt console and product pages, if not next to the server.found automatically
SANCTIONS_LIST_PATHSanctions address list (how). Empty = screening disabled, with a warning at every start.empty
DEMO_MODEtrue enables POST /demo/invoices (testnets only, labelled DEMO). Keep false in production.false
WATCH_INTERVAL_MSChain watcher poll interval (250 to 60000).2000
WEBHOOK_ALLOW_PRIVATEtrue only for local development with a webhook receiver on localhost (turns off SSRF protection). Never in production.empty
SESSION_COOKIE_INSECURELocal development over plain http only: drops the Secure flag of portal session cookies.empty
TELEGRAM_BOT_TOKEN, SMTP_URL, MAIL_FROMNotifications. You can also set them in Console → Integrations (stored encrypted); MAIL_FROM is required with SMTP.empty
LOG_LEVELfatal … trace, or silent.info

Demo only (read by scripts/demo-reset.mjs): DEMO_MERCHANT_SOLANA_ADDRESS, DEMO_MERCHANT_BASE_ADDRESS.

03Web app build settings

VariableAppPurpose
VITE_API_URLcheckout, portal, interim portalAPI origin. Empty = same origin (behind Caddy).
VITE_SOLANA_RPC_URLcheckout, portalOptional public, keyless Solana RPC for the browser: pre-sign simulation in the checkout, refund signing in the portal.
VITE_BASE_ROUTER_ADDRESScheckoutSame router address as the server. Empty = Base wallet pay off.
VITE_WALLETCONNECT_PROJECT_IDcheckoutOptional. Empty = WalletConnect not offered; injected wallets (MetaMask, Rabby …) and Coinbase Wallet still work. Enabling it also needs @walletconnect/ethereum-provider, which you install and licence-check yourself.
VITE_MOCKcheckoutLocal development only (mock API and wallet). A production build refuses it.
VITE_CHECKOUT_URL, VITE_BASEportalPublic checkout origin for links; optional sub-path such as /portal/.

Templates: apps/checkout/.env.example, apps/portal/.env.example.

04Caddy settings

Read from the Caddy env file (bare metal) or deploy/.env (Docker), documented in the header of deploy/caddy/Caddyfile:

VariablePurpose
PAY_DOMAIN RequiredCheckout, public API and merchant API, e.g. pay.example.com.
ADMIN_DOMAINConsole (/admin, /ops), operator API and the interim merchant portal. Unset = internal port 8082 only.
ADMIN_ALLOW_IPSSpace-separated CIDRs allowed on the admin domain. Default: everyone.
SITE_DOMAINLanding page. Unset = internal port 8083 only.
API_UPSTREAM, WEB_ROOTServer address (127.0.0.1:8787 or server:8787) and the folder with checkout/, admin/, landing/.

05Settings in the console

Everything else is edited in the operator console and applies without a restart: Operator console lists every section and field. Money fields start off: the default fee is 0 bps, tiers are empty and the referral share is 0.