Checkout that doesn't care which chain your buyer is holding funds on.
A headless TypeScript SDK for accepting crypto payments settled to one token, on one chain, of your choosing — while the buyer pays from a single balance spread across whatever chains they actually hold assets on.
One signature. That's the integration.
No registry required to get started — build a payment intent directly against a known settlement token and recipient.
import { createMorva, LocalSigner } from "@morva/sdk";
const morva = createMorva({
particle: {
projectId: process.env.PARTICLE_PROJECT_ID!,
projectClientKey: process.env.PARTICLE_CLIENT_KEY!,
projectAppUuid: process.env.PARTICLE_APP_UUID!,
},
});
const signer = new LocalSigner(buyerPrivateKey);
const session = await morva.connect(signer);
const intent = morva.createDirectIntent({
amount: "4.99",
orderId: "order-123",
settlementToken: "0xaf88…e5831", // USDC, Arbitrum One
settlementRecipient: "0xYourMerchantAddress",
// settlementChainId: 8453, optional — defaults to Arbitrum One
});
const result = await session.pay(intent, {
onStatus: (status) => console.log(status),
});
console.log(result.transactionId, result.explorerUrl);Read the full docsHow it works
Merchant registers once
Settlement token, recipient address, and settlement chain, written to MorvaRegistry. That's the whole setup.
Buyer connects their own wallet
No new address, no deposits. Their EOA is upgraded in place via EIP-7702 the first time it's needed.
One signature
A Particle Universal Account reads their balance across every supported chain and routes liquidity automatically.
Merchant settles
Exactly the token and chain they configured, every time — regardless of what the buyer paid with or where.
Built for the payment, not the wallet
One signature
No network switching, no bridging UI, no second approval. The buyer signs once.
EIP-7702, native
The buyer's own EOA becomes the account — not a separate smart-account address to fund.
Unified balance
Whatever the buyer holds, on whatever chain they hold it — read and spent as one balance.
Fixed settlement
The merchant always receives the exact token and chain they configured. No surprises.
Every payment reports its own status
session.pay(intent, { onStatus }) walks through the same five states on every payment.
API surface
createMorva(config)MorvaEntry point.morva.createDirectIntent(...)PaymentIntentNo registry needed.morva.createPaymentIntent(...)Promise<PaymentIntent>Reads config from the registry.morva.connect(signer)Promise<BuyerSession>Constructs the buyer's Universal Account.session.getUnifiedBalance()Promise<UnifiedBalance>Cross-chain holdings.session.pay(intent, { onStatus })Promise<PaymentResult>Sign → authorize → submit → settle.