How We Built VeloCards: A Virtual Card Platform for Spending Crypto Online
VeloCards architecture: crypto-to-fiat conversion, on-chain settlement, and why we ditched CEX rails.
The first VeloCards transaction was a small SaaS purchase paid for in ETH that had been sitting in a wallet for years. The card worked. The merchant got paid in dollars. The user didn't touch an exchange, didn't wait for withdrawals, didn't do anything except tap a button in our app.
That moment — watching a real crypto-to-fiat purchase clear in production — was the payoff for months of work we almost gave up on twice.
This is how we built VeloCards. The architecture, the dead ends, the KYC tradeoffs that still keep me up at night, and why we ended up routing around centralized exchanges entirely. BitPay and Crypto.com Card don't publish this stuff, so we will.
The Problem We Were Solving
Crypto is easy to hold. It's hard to spend. Building a crypto virtual card platform that actually works requires solving the fiat conversion problem at the infrastructure level.
The standard path in early 2026: move your ETH to Coinbase, wait 3-5 days for the withdrawal hold, convert to USD, withdraw to your bank, wait another 2-3 days, then spend from your debit card. Or use a crypto card product that locks you into holding their native token and declines at a meaningful fraction of merchants because their BIN has a fraud reputation problem.
Our users — mostly freelancers and remote workers who get paid in crypto — wanted something simpler. Hold crypto in your own wallet. Tap a button. Get a card number. Buy stuff. No exchange accounts. No withdrawal waits. No token-gating.
The targets: minimize time from crypto deposit to spendable card balance, keep the conversion spread tight, and land on a BIN that doesn't get treated as high-risk by major merchants. Hard to hit all three without building the rails yourself.
What We Tried First (And Why It Failed)
The obvious move was CEX rails. Take the user's crypto, deposit it to our Coinbase Commerce merchant account, convert to USD via their API, and fund virtual cards through Stripe Issuing. We spent six weeks building this.
Here's why it didn't work.
Coinbase Commerce deposits take 10-30 minutes to confirm on their end, even after the on-chain transaction settles. Then the conversion to USD adds another API call with its own latency. By the time we could fund a card, the user had been waiting 15-45 minutes. Nobody wants to wait 45 minutes to buy something online.
Worse: Coinbase's spread on the conversion was non-trivial and varied with volume and time of day. Add Stripe Issuing's per-transaction fees and we were stacking margin on margin. That's BitPay territory. We weren't building a better product — we were building a worse clone.
The breaking point came when Coinbase froze our merchant account for 72 hours over a compliance review we weren't warned about. No deposits, no conversions, nothing. Our beta users couldn't top up their cards for three days. We ate the support cost apologizing.
That weekend we decided: if we're building a crypto virtual card platform, we need to control the settlement layer, not rent it from an exchange that can freeze us whenever they feel like it. This mirrors the approach we took with VeloCalls call tracking infrastructure — own the critical path.
The Architecture
Three layers. Same philosophy we used for ClickzProtect — keep it boring until boring stops working.
Crypto acceptance layer — on-chain wallets plus DEX routing. On the user-facing side, VeloCards accepts Bitcoin, Ethereum, and USDT. Under the hood, when crypto lands we detect it via RPC polling (yes, polling — webhooks were unreliable), route the value into USDC through a DEX aggregator, and credit the user's balance. The aggregator picks the best route across multiple DEXs so we're not eating single-pool slippage.
We use 1inch for EVM chains and Jupiter for Solana liquidity. Both have reliable APIs, predictable pricing, and don't require us to hold inventory. (I spent way too long evaluating aggregators. In hindsight, just pick one and move on — they're all within rounding error of each other on most swaps.) The swap happens in the same workflow as the deposit detection — user sends ETH, we route to USDC, balance updates. Solana is faster; Ethereum mainnet is slower during gas spikes.
Card issuance layer — VelocityPay. This is where things get interesting. We built VelocityPay as our own card-issuing stack earlier in 2025, originally for a different product. Turns out having your own BIN program and issuer relationship means you control everything: the card UX, the fee structure, the authorization logic, and — crucially — the BIN reputation.
Stripe Issuing and Marqeta are fine for most use cases. But when you're funding cards from crypto-to-fiat flows, issuers get nervous. They want to see your AML controls, your source-of-funds documentation, your chargeback rates. Having VelocityPay in-house meant we could build the compliance story from scratch, tune the authorization rules ourselves, and avoid the "we're pausing your program while we review" conversations that kill crypto card startups.
Honestly? Building our own issuing layer was overkill for the first version. We got lucky it paid off. If I'm being real, I'd probably recommend most teams start with Marqeta and migrate later — we just happened to have VelocityPay already half-built from a different project.
Settlement layer — USDC treasury with fiat sweeps. When a card transaction authorizes, we don't move money instantly. We hold USDC in a treasury wallet, and every 4 hours we sweep the day's settlement obligations to our issuer's funding account via Circle's APIs. The issuer pays the merchants in fiat; we backstop the issuer with our USDC-to-USD conversion.
Why USDC as the intermediate asset? Stability (no holding volatile assets in the treasury), liquidity (deep pools on every chain we support), and Circle's compliance footprint (makes the banking conversations easier). We considered USDT but the redemption process is slower and the regulatory posture is murkier.
USDC isn't perfect. Circle's reserve attestations have gaps and the whole "we can freeze your USDC" thing makes me uncomfortable. But the alternatives are worse. Pick your poison.
The schema is simpler than ClickzProtect:
-- User balances in USDC cents (1 USDC = 100 balance units)
create table balances (
user_id uuid primary key,
usdc_cents bigint not null default 0,
pending_cents bigint not null default 0, -- in-flight deposits
updated_at timestamptz not null default now()
);
-- Deposit events from on-chain detection
create table deposits (
id bigserial primary key,
user_id uuid not null,
chain text not null,
tx_hash text not null unique,
input_asset text not null,
input_amount numeric not null,
usdc_amount numeric not null,
spread_bps smallint not null,
status text check (status in ('pending','confirmed','failed')),
created_at timestamptz not null default now()
);
-- Card transactions (mirrored from VelocityPay)
create table card_txns (
id bigserial primary key,
user_id uuid not null,
card_id text not null,
merchant_name text,
amount_cents int not null,
currency text not null default 'USD',
status text check (status in ('authorized','settled','declined','reversed')),
decline_reason text,
created_at timestamptz not null default now()
);
Nothing exotic. Postgres on Railway, same stack as everything else we run. The whole system — crypto detection, swap execution, balance updates, card webhooks — runs on a single Railway instance. We'll split it when we need to. We don't need to yet. (For analytics, we pipe everything through JustAnalytics — same pattern across all VDL products.)
Is it overengineered in some places? Yeah, probably. The deposits table has columns we've never used. But it works.
The KYC Tradeoffs
Here's the part nobody talks about in the "how to build crypto virtual card platform" posts: KYC destroys conversion.
Full KYC — ID upload, selfie verification, address confirmation — has a brutal drop-off in any crypto funnel. Users are crypto-native. They hate uploading government ID to random apps. (I get it. I hate it too.)
But card networks require KYC for any meaningful spending limits. Our issuer requires KYC. AML regulations require KYC. You can't just skip it.
So we built a starter tier that lets users try the product without ID upload:
Email-only starter. User signs up with email, verifies it, and gets one virtual card with a $100 lifetime spending limit and a $500 daily cap. No physical card. No ID upload. Most users start here — it's the lowest-friction way to see whether the product actually works for their use case.
KYC-verified. User uploads a government ID, we run it through a verification API, and the spending limits go unlimited. Unlimited cards too. This is where any real volume happens, and where the per-tier deposit and card-creation fees start to matter — the fee schedule is tiered by annual spend, so heavier users land on lower percentages.
The insight: the starter tier is a loss leader. We make almost nothing on a user capped at $100 lifetime. But the conversion from starter → KYC once someone wants to actually run ad spend or pay annual SaaS bills is much higher than it would be if we'd demanded ID upload at signup. The funnel works because we let people start easy.
If we'd launched with full KYC required — like BitPay and Crypto.com do — I don't think we'd have a product. The starter-tier bypass is the whole top-of-funnel.
On-Chain Settlement vs. CEX Rails: Why We Switched
After six months, here's how on-chain settlement compares to the CEX approach we abandoned:
| Dimension | CEX Rails (Coinbase) | On-Chain Settlement |
|---|---|---|
| Deposit to spendable | Tens of minutes | Sub-minute on most chains |
| Conversion spread | Stacked exchange + issuer margins | Tight DEX-aggregator routing |
| Downtime exposure | Exchange freezes hit us | We own the rails |
| Custody risk | Exchange holds funds | We hold funds |
| Regulatory clarity | Depends on exchange | We own the compliance story |
The speed difference alone is worth the engineering cost. When a user can deposit ETH and buy something a minute later, they actually use the product. When they have to wait half an hour, they go back to their regular debit card.
We also don't wake up worrying about Coinbase freezing our account. I can't overstate how much that matters when you're trying to run a reliable product.
The tradeoff? We own the risk now. If our treasury wallet gets hacked, that's on us. No exchange to blame, no insurance to fall back on. Sleep better in some ways, worse in others.
What Worked
A few things landed the way we hoped, and one surprised us.
The on-chain settlement path is dramatically faster than the CEX flow we tried first. Users deposit, the value routes into USDC, and the card balance is spendable — no withdrawal hold, no manual conversion, no waiting on an exchange's compliance queue.
Owning our own BIN through VelocityPay was the unexpected win. We'd assumed merchant acceptance would be roughly in line with other crypto cards on the market. It's better — turns out having a BIN that doesn't share fraud history with every other crypto-card user matters more than we expected. Our users aren't getting declined at checkout because someone else on the same BIN range was running fraud last month.
For context on how this fits with our other products — the same infrastructure patterns (Postgres, Railway, Cloudflare) power JustAnalytics, VeloCalls, and ClickzProtect. More on running multiple products in our post on building 8 active SaaS products.
What We'd Change
Three things, starting over.
Build VelocityPay first. We wasted six weeks on the Coinbase/Stripe approach before realizing we needed our own issuing stack. If I knew then what I know now, I'd have started with the card-issuance layer and worked backward to crypto acceptance. The issuer relationship is the bottleneck — everything else is just plumbing.
Launch with the starter tier from day one. We launched requiring ID upload at signup, watched the funnel hemorrhage at the KYC step, and scrambled to add the email-only starter tier later. Should've been there from the start. Crypto users are allergic to KYC and you need a funnel that respects that.
Get native BTC funding live sooner. We've now got BTC, ETH, and USDT live as user-facing funding options, which is what most of the demand actually was. Getting native Bitcoin settlement to the point where it was reliable took longer than we expected — the wrapped-BTC shortcuts on EVM are janky and we didn't want to ship that. Worth it in the end, but every week we delayed BTC funding was volume we left on the table.
The architecture is right. The path we took was longer than it needed to be. Shipping is about learning the problem while you're building the solution — and accepting that you'll build some things twice.
Look, we're not the best team to build this. We made dumb mistakes. The six weeks on Coinbase was embarrassing in hindsight. But we shipped, and the thing works, and people are spending crypto online without thinking about exchanges. That's the whole point.
Frequently Asked Questions
How do you build crypto virtual card platform infrastructure without relying on CEX rails?
On-chain settlement with stablecoin liquidity pools. We accept the user's crypto (BTC, ETH, or USDT on the user-facing side), route the value into USDC via a DEX aggregator (1inch for EVM chains, Jupiter for Solana), and hold USDC in a treasury wallet. When the card authorizes, we pull from the USDC balance to fund fiat settlement through our card issuer. CEX rails add hours of withdrawal delay and expose you to exchange custody risk. On-chain settlement clears much faster.
What KYC level do you need for crypto-to-fiat card issuance?
Depends on your card network and issuer. For VeloCards, we went with tiered access: an email-only starter tier (one card, $100 lifetime spending limit, $500 daily cap pre-KYC) lets users try the product without uploading ID, and KYC verification unlocks unlimited cards and unlimited spending. Letting crypto-native users start without ID upload dramatically improves top-of-funnel conversion.
Why route through USDC instead of using BTC or ETH directly for settlement?
Stability and liquidity. We didn't want to hold volatile assets in the treasury — a 20% ETH drawdown between deposit and settlement would have eaten the whole margin. USDC has deep pools on every chain we support, and Circle's compliance footprint makes the banking conversations easier. Users still fund with BTC, ETH, or USDT; USDC is just the internal intermediate asset on the way to the issuer.
Why did you build your own card issuance layer instead of using Crypto.com or BitPay?
Margin and control. BitPay takes a meaningful spread on conversions. Crypto.com Card requires users to hold CRO tokens for better rates. We wanted a white-label product where VDL controls the UX, the fee structure, and the settlement timing. Building on VelocityPay's issuing infrastructure let us set our own spread, control the instant-card flow, and avoid locking users into someone else's token ecosystem.
Follow the Studio
Velocity Digital Labs is a multi-product studio building 8 active SaaS products with a 1-founder + 1-manager + N-AI-agents structure. Receipts, dollar-signs, cap-table-honest. No VC platform-play — just shipping.