Menu

Explorer & Settings

Tempo Explorer Submit Project
Back to all projects

moltspay

by Yaqing2023 · Updated 10 days ago

Blockchain payment infrastructure for AI Agents

In the AI payments ecosystem

moltspay is an early-stage TypeScript project in the AI payments / x402 ecosystem, focused on agent-to-agent, ai-agents, blockchain, cdp. It currently has 8 GitHub stars and 4 forks, and sits alongside related tools like piprail, x402-payments-mcp, gold-402, x402charity, moltspay-python, dna-x402.

README.md View on GitHub →

MoltsPay

npm version License: MIT TypeScript

Blockchain payment infrastructure for AI Agents. Turn any skill into a paid service with one JSON file.

MoltsPay enables agent-to-agent commerce using the x402 protocol - HTTP-native payments where AI agents can pay each other for services without human intervention. Built on USDC stablecoins with gasless transactions powered by Coinbase CDP.

Why MoltsPay?

Problem MoltsPay Solution
AI agents can't pay for services x402 protocol - HTTP 402 Payment Required flow
Blockchain payments need gas Gasless - CDP facilitator handles all gas fees
Complex wallet integration One JSON file - add moltspay.services.json to any skill
Payment verification is hard Automatic on-chain verification included

Features

  • Skill Integration - Add moltspay.services.json to any existing skill
  • x402 Protocol - HTTP-native payments (402 Payment Required)
  • Gasless - Both client and server pay no gas (facilitators handle it)
  • Payment Verification - Automatic on-chain verification
  • Secure Wallet - Spending limits, whitelist, and audit logging
  • Multi-chain - Base, Polygon, Solana, BNB, Tempo (mainnet & testnet)
  • Fiat Rail — Alipay (2.0.0) - Accept CNY via Alipay AI Pay from China mainland merchants. CLI-only (Node), browser unsupported. See docs/ALIPAY-RAIL.md
  • Fiat Rail — WeChat Pay (2.1.0) - Accept CNY via WeChat Pay v3 Native (scan-to-pay). SDK-managed recoverable sessions persist QR/order context, poll in the background, and fulfill idempotently. See docs/WECHAT-RAIL-DESIGN.md
  • Balance Rail — Password-Free (2.2.0) - Top up once, then pay with no signature or QR per transaction. Server-custodied SQLite ledger with atomic deducts, hard spending limits, and idempotent retries. See docs/WECHAT-BALANCE-PASSWORDLESS-DESIGN.md
  • Balance Identity & Authentication (2.4.0) - The balance rail gets a real user: accounts are anchored to the WeChat payer's openid at top-up, and each deduction is authorized by a per-request signature (auth_mode: off | shadow | enforce). Knowing a buyer_id is no longer enough to spend — closes the bearer hole. See Balance Authentication below
  • Agent-to-Agent - Complete A2A payment flow support
  • Multi-VM - EVM chains + Solana (SVM) with unified API
  • MCP Server - Expose wallet + payments to Claude Desktop, Cursor, and other MCP hosts

Installation

npm install moltspay@latest

Quick Start

For Service Providers (Selling)

1. Have an existing skill with exported functions:

// index.js (your existing skill)
export async function textToVideo({ prompt }) {
  // your implementation
  return { video_url: "https://..." };
}

2. Add moltspay.services.json:

{
  "provider": {
    "name": "My Video Service",
    "wallet": "0xYOUR_EVM_WALLET_ADDRESS",
    "solana_wallet": "YOUR_SOLANA_ADDRESS",
    "chains": ["base", "polygon", "solana", "bnb"]
  },
  "services": [{
    "id": "text-to-video",
    "function": "textToVideo",
    "price": 0.99,
    "currency": "USDC"
  }]
}

Note: solana_wallet is optional - only needed if accepting Solana payments.

3. Start server:

npx moltspay start ./my-skill --port 3000

That's it! Your skill now accepts x402 payments.

For Clients (Buying)

1. Initialize wallet (one time):

npx moltspay init                  # EVM wallet (Base, Polygon, BNB, etc.)
npx moltspay init --chain solana   # Solana wallet (mainnet & devnet)

2. Fund your wallet (US users):

npx moltspay fund 5
# Opens Coinbase Pay - use debit card or Apple Pay
# USDC arrives in < 1 minutes. No ETH needed!

Or send USDC directly to your wallet address from any exchange.

3. Use paid services:

# Pay on Base (default)
npx moltspay pay https://server.com text-to-video --prompt "a cat dancing"

# Pay on Polygon
npx moltspay pay https://server.com text-to-video --chain polygon --prompt "a cat dancing"

Testnet Quick Start

Want to test before using real money? Use our testnet faucets:

# 1. Create wallet (if you don't have one)
npx moltspay init                  # EVM wallet (Base, Polygon, BNB, etc.)
npx moltspay init --chain solana   # Solana wallet (mainnet & devnet)

# 2. Get free testnet tokens (pick one or all!)
npx moltspay faucet                        # Base Sepolia (1 USDC, once per 24h)
npx moltspay faucet --chain solana_devnet  # Solana devnet (1 USDC)
npx moltspay faucet --chain bnb_testnet    # BNB testnet (1 USDC + 0.001 tBNB for gas)
npx moltspay faucet --chain tempo_moderato # Tempo testnet (1 pathUSD)

# 3. Test payments on different chains
# Base Sepolia (CDP, gasless)
npx moltspay pay https://moltspay.com/a/zen7 text-to-video \
  --chain base_sepolia --prompt "a robot dancing"

# Solana devnet (SPL transfer)
npx moltspay pay https://moltspay.com/a/zen7 text-to-video \
  --chain solana_devnet --prompt "a cat playing piano"

# BNB testnet (gas sponsored)
npx moltspay pay https://moltspay.com/a/zen7 text-to-video \
  --chain bnb_testnet --prompt "a sunset timelapse"

# Tempo Moderato (gas-free, MPP protocol)
npx moltspay pay https://server.com service-id \
  --chain tempo_moderato --prompt "test"

For Web Apps (Browser)

moltspay@1.6.0 adds a browser client. Connect any EIP-1193 wallet (MetaMask, Rainbow, Frame, …) for EVM or a @solana/wallet-adapter wallet (Phantom, Solflare, Backpack, …) for Solana and pay for x402 services directly from the page — no private key ever in browser memory, no CLI wrapper.

Install:

npm install moltspay

Pay with MetaMask:

import { MoltsPayWebClient, eip1193Signer } from 'moltspay/web';

const client = new MoltsPayWebClient({
  signer: eip1193Signer(window.ethereum),
});

// Discover what the provider accepts
const { provider, services } = await client.getServices('https://provider.example.com');

// Run a paid call — user sees one wallet signature prompt
const result = await client.pay(
  'https://provider.example.com',
  'text-to-video',
  { prompt: 'a cat dancing' },
  { chain: 'base' }
);

Pay with Phantom (Solana):

import { MoltsPayWebClient, solanaSigner } from 'moltspay/web';
import { useWallet } from '@solana/wallet-adapter-react';

const wallet = useWallet();
const client = new MoltsPayWebClient({ signer: solanaSigner(wallet) });

await client.pay(serverUrl, 'text-to-video', params, { chain: 'solana_devnet' });

EVM + Solana in one client — use composeSigners so the same MoltsPayWebClient instance routes to whichever signer matches the picked chain:

import { MoltsPayWebClient, composeSigners, eip1193Signer, solanaSigner } from 'moltspay/web';

const client = new MoltsPayWebClient({
  signer: composeSigners(
    eip1193Signer(window.ethereum),
    solanaSigner(phantomAdapter),
  ),
});

Chain coverage. All 8 chains the CLI supports work from the browser with one signature prompt each:

Chain Scheme User gas? Notes
base / polygon / base_sepolia EIP-3009 transferWithAuthorization No (gasless) Provider submits on success
tempo_moderato EIP-2612 permit No (gasless) Browser never switches to Tempo; server's settler submits permit + transferFrom
bnb / bnb_testnet MoltsPay PaymentIntent One-time approve Call client.approveBnb({ chain, spender, token }) once, then intent signatures are gasless
solana / solana_devnet SPL transfer No if provider sets a fee payer wallet.signTransaction signs, provider submits

BNB approval flow. The first payment on BNB throws NeedsApprovalError with the details needed to approve — catch it, call approveBnb(), and retry:

try {
  await client.pay(url, service, params, { chain: 'bnb' });
} catch (err) {
  if (err instanceof NeedsApprovalError) {
    await client.approveBnb({
      chain: 'bnb',
      spender: err.details.spender,
      token: err.details.token,
    });
    // User paid ~0.001 BNB gas for the approve. Retry now succeeds without further approve.
    await client.pay(url, service, params, { chain: 'bnb' });
  }
}

Tempo note. Tempo Moderato pathUSD is a native precompile that implements EIP-2612 permit but not EIP-3009. The web client dispatches to the permit path automatically when the server advertises scheme: "permit". The user signs typed data; the provider's settler submits the permit() + transferFrom() transactions on-chain. MetaMask never prompts for a chain switch because the browser wallet doesn't touch Tempo at all.

Solana mainnet RPC. The public api.mainnet-beta.solana.com endpoint returns 403 to browser requests, so any Solana-mainnet traffic needs an authenticated RPC. Supply one via solanaRpc:

const client = new MoltsPayWebClient({
  signer: composeSigners(
    eip1193Signer(window.ethereum),
    solanaSigner(phantomAdapter),
  ),
  solanaRpc: {
    solana: 'https://mainnet.helius-rpc.com/?api-key=YOUR_KEY',
    // solana_devnet falls back to `api.devnet.solana.com` automatically
  },
});

Accepts any Helius / QuickNode / Alchemy / Triton / self-hosted URL. Per-chain — override only solana if you only use mainnet. Devnet (api.devnet.solana.com) still serves browsers and does not need an override.

Error classes — every error exposes a code field so you can branch without string-matching:

import {
  NeedsApprovalError,        // code: 'NEEDS_APPROVAL'       — BNB, call approveBnb()
  UnsupportedChainError,     // code: 'UNSUPPORTED_CHAIN'    — user picked a chain the server doesn't accept
  PaymentRejectedError,      // code: 'PAYMENT_REJECTED'     — user cancelled in the wallet
  InsufficientBalanceError,  // code: 'INSUFFICIENT_BALANCE' — not enough native gas for BNB approve
  SpendingLimitExceededError,// code: 'SPENDING_LIMIT_EXCEEDED' — only when you opt in to SpendingLedger
  ServerError,               // code: 'SERVER_ERROR'         — provider returned non-2xx
  MoltsPayError,             // base class
} from 'moltspay/web';

Spending limits (opt-in). Off by default. External wallets already prompt per-signature; per-browser localStorage limits don't sync across devices. If you want a session-level cap anyway:

const client = new MoltsPayWebClient({
  signer: eip1193Signer(window.ethereum),
  spendingLimits: { maxPerTx: 5, maxPerDay: 50 },
});

Provider CORS. Providers must enable CORS on MoltsPayServer for browser callers — without it the 402 challenge header is invisible to the browser:

new MoltsPayServer({
  // ...
  cors: true,                              // allow all origins, or
  cors: ['https://myapp.example.com'],     // explicit allowlist, or
  cors: { origins: [...], maxAge: 86400 }, // fine-grained
});

Servers advertising for the web need cors enabled; CLI callers are unaffected either way.

Security posture. No private key ever enters browser memory. No filesystem access, no ~/.moltspay/ — the wallet is always external. The signer object the client receives only has permission to sign typed data and (for BNB) submit one approve transaction, which the user explicitly confirms in the wallet UI.

Reference demo. examples/web/ is a runnable React + Vite app that exercises every path above. cd examples/web && npm install && npm run dev — it connects to https://moltspay.com/a/zen7 by default. See examples/web/README.md for the full matrix of tested wallets + chains.

MCP Server (For AI Assistants)

MoltsPay ships an MCP (Model Context Protocol) stdio server that lets MCP-compatible hosts (Cursor, Windsurf, Claude Code, Zed, etc.) browse services, check wallet status, and pay for x402 services on your behalf.

It is a thin wrapper around MoltsPayClient — wallet custody, spending limits, and all payment protocols (x402, MPP, Solana, BNB) are reused from the SDK.

Setup

1. Create a wallet and set spending limits (the MCP server refuses to start without a wallet):

npx moltspay init
npx moltspay config --max-per-tx 2 --max-per-day 10
npx moltspay fund 5    # or: npx moltspay faucet for testnet

2. Point your MCP host at the moltspay-mcp binary over stdio:

npx -y moltspay-mcp              # normal mode
npx -y moltspay-mcp --dry-run    # preview payments without signing

Each host has its own config file for registering stdio MCP servers — check your host's docs for the exact location. For a safer first run, use --dry-run so moltspay_pay returns a preview instead of spending real funds.

Tools

Tool What it does Destructive?
moltspay_status Wallet address, balances across all supported chains, spending limits No
moltspay_services Fetch services manifest from a provider URL; optional query/maxPrice filter No
moltspay_pay Execute an x402/MPP/SOL/BNB payment and return the service result Yes
moltspay_config Read or update maxPerTx / maxPerDay limits Updates config file

Safety Layers

moltspay_pay is the only tool that moves money. Three guards stack on top of the MCP host's own tool-approval prompt:

  1. SDK spending limitsmaxPerTx / maxPerDay enforced before signing.
  2. Dry-run mode — launch with --dry-run and payments return a preview instead of signing.
  3. Confirmation gate — set MOLTSPAY_MCP_REQUIRE_CONFIRM=1 to require a second tool call (confirmed: true) for any payment exceeding maxPerTx / 10.

Private keys and mnemonics are never exposed over MCP — wallet creation stays on the CLI (npx moltspay init) by design. See docs/MCP-USAGE.md for full tool arguments and troubleshooting.

Payment Protocols

MoltsPay supports multiple payment protocols, each optimized for different chains:

Protocol Chains Gas Description
x402 + CDP Base, Polygon Gasless (CDP pays) HTTP 402 + EIP-3009 signatures
x402 + SOL Solana Gasless (server pays) HTTP 402 + SPL transfer
x402 + BNB BNB Gasless (server pays) HTTP 402 + EIP-712 intent signing
MPP Tempo Gas-free native HTTP 402 + WWW-Authenticate
x402 + Alipay Alipay No blockchain gas HTTP 402 + Alipay AI Pay proof
x402 + WeChat Pay WeChat Pay Native No blockchain gas HTTP 402 + scan-to-pay QR, poll-based verify

How x402 Protocol Works

Client                         Server                      CDP Facilitator
  |                              |                              |
  | POST /execute                |                              |
  | -------------------------------------------------->   |                              |
  |                              |                              |
  | 402 + payment requirements   |                              |
  | <--------------------------------------------------   |                              |
  |                              |                              |
  | [Sign EIP-3009 - NO GAS]     |                              |
  |                              |                              |
  | POST + X-Payment header      |                              |
  | -------------------------------------------------->   | Verify signature             |
  |                              | -------------------------------------------------->   |
  |                              |                              |
  |                              | Execute transfer (pays gas)  |
  |                              | <--------------------------------------------------   |
  |                              |                              |
  | 200 OK + result              |                              |
  | <--------------------------------------------------   |                              |

Key insight: Client signs a payment authorization, server submits it. Neither party pays gas - the CDP facilitator handles settlement.

How MPP Protocol Works (Tempo)

MPP (Machine Payments Protocol) is simpler - client executes the transfer directly:

Client                         Server
  |                              |
  | POST /service                |
  | -------------------------------------------------->   |
  |                              |
  | 402 + WWW-Authenticate       |
  | <--------------------------------------------------   |
  |                              |
  | [Execute TIP-20 transfer]    |
  | [No gas needed on Tempo]     |
  |                              |
  | POST + Authorization: Payment|
  | -------------------------------------------------->   |
  |                              |
  | [Server verifies on-chain]   |
  |                              |
  | 200 OK + Payment-Receipt     |
  | <--------------------------------------------------   |

Key insight: On Tempo, the client executes the transfer directly (gas-free), then retries with the transaction hash. No CDP facilitator needed.

How Solana Protocol Works

Client                         Server (Fee Payer)          Solana Network
  |                              |                              |
  | POST /execute                |                              |
  | -------------------------------------------------->   |                              |
  |                              |                              |
  | 402 + payment requirements   |                              |
  | (includes solana_wallet)     |                              |
  | <--------------------------------------------------   |                              |
  |                              |                              |
  | [Sign SPL Transfer]          |                              |
  | [NO GAS - just signing]      |                              |
  |                              |                              |
  | POST + X-Payment (signature) |                              |
  | -------------------------------------------------->   | Execute transfer             |
  |                              | (server pays ~$0.001 SOL)    |
  |                              | -------------------------------------------------->   |
  |                              |                              |
  | 200 OK + result              |                              |
  | <--------------------------------------------------   |                              |

Key insight: Client only signs the SPL transfer (gasless). Server acts as fee payer and executes the transaction on-chain.

How BNB Protocol Works

Client                         Server                      BNB Network
  |                              |                              |
  | POST /execute                |                              |
  | -------------------------------------------------->   |                              |
  |                              |                              |
  | 402 + payment requirements   |                              |
  | (includes bnbSpender)        |                              |
  | <--------------------------------------------------   |                              |
  |                              |                              |
  | [Sign EIP-712 intent]        |                              |
  | [NO GAS - just signing]      |                              |
  |                              |                              |
  | POST + X-Payment (signature) |                              |
  | -------------------------------------------------->   | Execute transferFrom         |
  |                              | (server pays ~$0.0001 gas)   |
  |                              | -------------------------------------------------->   |
  |                              |                              |
  | 200 OK + result              |                              |
  | <--------------------------------------------------   |                              |

Key insight: Client only signs an intent (gasless). Server executes the actual transfer and pays the minimal gas (~$0.0001). This is the "pay-for-success" model - payment only happens if service succeeds.

Skill Structure

MoltsPay reads your skill's existing structure:

my-skill/
+------ package.json           # MoltsPay reads "main" field
+------ index.js               # Your existing exports
+------ moltspay.services.json # Only file you add!

Your functions stay untouched. Just add the JSON config.

Services Manifest Schema

{
  "$schema": "https://moltspay.com/schemas/services.json",
  "provider": {
    "name": "Service Name",
    "description": "Optional description",
    "wallet": "0x...",
    "chains": ["base", "polygon"]
  },
  "services": [{
    "id": "service-id",
    "name": "Human Readable Name",
    "description": "What it does",
    "function": "exportedFunctionName",
    "price": 0.99,
    "currency": "USDC",
    "input": {
      "prompt": { "type": "string", "required": true }
    },
    "output": {
      "result_url": { "type": "string" }
    }
  }]
}

Multi-Chain Configuration

Accept payments on multiple chains by specifying a chains array:

{
  "provider": {
    "wallet": "0x...",
    "chains": ["base", "polygon"]
  }
}

Clients can then choose which chain to pay on:

npx moltspay pay https://server.com service-id --chain polygon --prompt "..."

If no --chain is specified, the client uses the first chain in the provider's list.

Validate Your Config

npx moltspay validate ./my-skill

Server Setup

1. Get CDP credentials from https://portal.cdp.coinbase.com/

2. Create ~/.moltspay/.env:

CDP_API_KEY_ID=your-key-id
CDP_API_KEY_SECRET=your-secret

3. Configure chains in your manifest:

{
  "provider": {
    "wallet": "0x...",
    "chains": [
      { "chain": "base", "network": "eip155:8453", "tokens": ["USDC"] },
      { "chain": "base_sepolia", "network": "eip155:84532", "tokens": ["USDC"] }
    ]
  }
}

4. Start server:

npx moltspay start ./my-skill --port 3000

Server does NOT need a private key - the x402 facilitator handles settlement.

Chain Auto-Detection

The server automatically detects which chain to verify payments on based on the client's payment header:

  • Client pays with --chain base -> Server verifies on Base mainnet
  • Client pays with --chain base_sepolia -> Server verifies on Base Sepolia

No USE_MAINNET env var needed! Just configure your accepted chains in the manifest.

Testnet Setup (Providers)

To accept testnet payments, add base_sepolia to your chains array:

{
  "provider": {
    "wallet": "0x...",
    "chains": ["base", "base_sepolia"]
  }
}

Clients can then pay using --chain base_sepolia and get free testnet USDC via npx moltspay faucet.

CLI Reference

# === Client Commands ===
npx moltspay init                    # Create wallet (EVM + Solana)
npx moltspay fund <amount>           # Fund wallet via Coinbase (US)
npx moltspay faucet                  # Get free testnet USDC (Base Sepolia)
npx moltspay faucet --chain solana_devnet   # Get Solana devnet USDC
npx moltspay faucet --chain bnb_testnet     # Get BNB testnet USDC + tBNB
npx moltspay faucet --chain tempo_moderato  # Get Tempo testnet tokens
npx moltspay status                  # Check balance (all chains)
npx moltspay transfer <to> <amount>  # Transfer USDC/USDT out to any address (e.g. an exchange)
npx moltspay config                  # Update limits
npx moltspay services <url>          # List provider's services
npx moltspay pay <url> <service>     # Pay and execute service

# === Transfer / Withdraw (2.4.0) ===
# Move USDC/USDT out of your wallet to any address (e.g. an exchange deposit address).
npx moltspay transfer <to> <amount>                       # 5 USDC on Base (default), interactive confirm
npx moltspay transfer <to> 10 --token USDT --chain bnb     # USDT on BNB Chain
npx moltspay transfer <to> 5 --chain polygon --yes --json  # non-interactive (agents/scripts)
# EVM chains only (base/polygon/bnb/...); a normal on-chain transfer, so the wallet needs a
# little native gas (ETH/BNB/POL). The transfer network must match the receiver's deposit network.

# === Service Discovery ===
npx moltspay services                           # List all from registry
npx moltspay services https://provider.com      # List from specific provider
npx moltspay services -q "video"                # Search by keyword
npx moltspay services --max-price 1.00          # Filter by max price
npx moltspay services --type api_service        # Filter by type
npx moltspay services --tag ai                  # Filter by tag
npx moltspay services --json                    # Output as JSON

# === Pay with Chain Selection ===
npx moltspay pay <url> <service> --chain base          # Pay on Base (default)
npx moltspay pay <url> <service> --chain polygon       # Pay on Polygon
npx moltspay pay <url> <service> --chain base_sepolia  # Pay on Base testnet
npx moltspay pay <url> <service> --chain solana        # Pay on Solana
npx moltspay pay <url> <service> --chain solana_devnet # Pay on Solana devnet
npx moltspay pay <url> <service> --chain bnb           # Pay on BNB
npx moltspay pay <url> <service> --chain bnb_testnet   # Pay on BNB testnet
npx moltspay pay <url> <service> --chain tempo_moderato # Pay on Tempo
npx moltspay pay <url> <service> --rail alipay         # Pay with Alipay AI Pay
npx moltspay pay <url> <service> --rail wechat         # Pay with WeChat Pay Native QR
npx moltspay pay <url> <service> --rail balance        # Pay password-free from prepaid balance

# === BNB One-Time Approve ===
# BNB payments need a one-time spender approval first (the spender comes from the 402 response).
npx moltspay approve --spender <address> --chain bnb   # Approve, then `pay --chain bnb` is gasless
npx moltspay approve --spender <address>               # --chain defaults to bnb_testnet

# === WeChat Recoverable Sessions ===
npx moltspay wechat start <url> <service> --prompt "..."   # Start order, return QR metadata immediately
npx moltspay wechat status <session-or-out_trade_no>        # Query/recover a pending session
npx moltspay wechat fulfill <session-or-out_trade_no>       # Idempotently fulfill if paid
npx moltspay wechat cancel <session-or-out_trade_no>        # Mark local session cancelled
npx moltspay wechat list                                   # List persisted WeChat sessions

# === Custodial Balance (Password-Free) ===
npx moltspay balance set-buyer <id>                        # Persist the buyer id (account label)
npx moltspay balance query <url>                           # Balance, limits, today's spend, bound signer/openid
npx moltspay balance transactions <url>                    # Ledger history, newest first
npx moltspay balance topup <url> <amount> --rail crypto --tx-hash 0x...   # Credit from a settled payment (crypto/wechat/alipay)

# --- Identity & auth (2.4.0) ---
npx moltspay balance whoami [url]                          # Show local signer address (and, vs a server, the account's bound signer/openid)
npx moltspay balance bind <url>                            # Bind the local signer to an account (runs a minimal top-up to establish identity)

# --- Recoverable WeChat-funded top-up (for chat agents; non-blocking) ---
npx moltspay balance topup-order <url> --pack 2.00         # Mint a WeChat pack order, emit the QR, exit immediately
npx moltspay balance topup-confirm <out_trade_no>          # Confirm + credit a pending order later (idempotent on out_trade_no)
npx moltspay balance topup-status <out_trade_no>           # Query one pending order
npx moltspay balance topup-list                            # List pending top-up orders
npx moltspay balance topup-pack <url>                      # Blocking variant: mint pack, wait for the scan, credit inline

# === Server Commands ===
npx moltspay start <skill-dir>       # Start server
npx moltspay stop                    # Stop server
npx moltspay validate <path>         # Validate manifest

# === Options ===
--port <port>                        # Server port (default 3000)
--chain <chain>                      # Chain: base, polygon, solana, bnb, tempo_moderato, + testnets
--rail <rail>                        # Rail: alipay, wechat, or balance (password-free)
--token <token>                      # Token: USDC, USDT
--max-per-tx <amount>                # Spending limit per transaction
--max-per-day <amount>               # Daily spending limit
--config-dir <dir>                   # Custom wallet directory

Programmatic Usage

Client

import { MoltsPayClient } from 'moltspay/client';

// Initialize client (uses wallet from ~/.moltspay/wallet.json)
const client = new MoltsPayClient();

// Standard service call (params wrapped in { params: {...} })
const result = await client.pay(
  'https://server.com',
  'text-to-video',
  { prompt: 'a cat dancing' },
  { chain: 'base' }
);

console.log(result.video_url);

Custom Input Formats (rawData)

Some services have custom input formats instead of the standard { params: { prompt } }. Use rawData: true to send your data at the top level:

// Service expects: { text: "...", target_lang: "..." }
// NOT: { params: { text: "...", target_lang: "..." } }

const result = await client.pay(
  'https://server.com',
  'translate',
  { text: 'Hello world', target_lang: 'es' },
  { 
    chain: 'base_sepolia',
    rawData: true  // Send data at top level
  }
);

// Server receives: { service: "translate", text: "Hello world", target_lang: "es", chain: "base_sepolia" }

PayOptions Reference

interface PayOptions {
  token?: 'USDC' | 'USDT';     // Token to pay with (default: USDC)
  autoSelect?: boolean;         // Auto-select token based on balance
  chain?: string;               // Chain: base, polygon, solana, bnb, tempo_moderato, + testnets
  rawData?: boolean;            // Send data at top level (for custom input formats)
}

CLI Equivalent

# Standard format (uses { params: { prompt } })
npx moltspay pay https://server.com text-to-video --prompt "a cat dancing"

# Custom format (uses rawData, sends at top level)
npx moltspay pay https://server.com translate --data '{"text": "Hello", "target_lang": "es"}'

Server

import { MoltsPayServer } from 'moltspay/server';

const server = new MoltsPayServer('./moltspay.services.json');

// Register custom handler (optional - usually loaded from skill)
server.skill('text-to-video', async (params) => {
  // implementation
  return { video_url: '...' };
});

server.listen(3000);

Supported Chains

Chain ID Type Facilitator Gas Model
Base 8453 Mainnet CDP Gasless (CDP pays)
Polygon 137 Mainnet CDP Gasless (CDP pays)
Base Sepolia 84532 Testnet CDP Gasless (CDP pays)
Solana - Mainnet SOL Gasless (server pays)
Solana Devnet - Testnet SOL Gasless (server pays)
BNB 56 Mainnet BNB Gasless (server pays)
BNB Testnet 97 Testnet BNB Gasless (server pays)
Tempo Moderato 42431 Testnet Tempo Gas-free native

Notes:

  • Ethereum mainnet NOT recommended (gas too expensive)
  • Each chain uses a specialized facilitator for optimal UX

Facilitator Architecture

A facilitator is the entity that executes the on-chain payment and pays the gas fees. MoltsPay supports two types:

Type Facilitator Who Pays Gas? Trust Model
External CDP (Coinbase) Coinbase Trust Coinbase infrastructure
Self-hosted SOL, BNB, Tempo Your server Trust your own server

External Facilitator (CDP):

  • Uses Coinbase Developer Platform as a third-party settlement service
  • Coinbase handles all on-chain execution and gas fees
  • Requires CDP API credentials
  • Supported chains: Base, Polygon

Self-hosted Facilitator (SOL, BNB, Tempo):

  • Your MoltsPay server acts as the facilitator
  • Server pays gas fees (~$0.001 per tx)
  • No external dependency - fully self-sovereign
  • You control the entire payment flow

Why Self-hosted is More Decentralized:

Aspect CDP (External) Self-hosted
Single point of failure Coinbase down = everyone stuck Each provider independent
Censorship risk Coinbase can block accounts Cannot be censored
Dependency Relies on third-party Fully autonomous

This self-hosted approach is a key innovation: any service provider can become their own facilitator without relying on third-party infrastructure. Unlike CDP where all users depend on Coinbase, self-hosted facilitators create a truly decentralized network with no single point of failure.

Note: Clients never need to know the facilitator's private keys. They only sign their own payments - the facilitator handles settlement transparently.

Facilitators Explained

Facilitator Chains How It Works
CDP Base, Polygon Client signs EIP-3009, Coinbase executes transfer
SOL Solana Client signs SPL transfer, server executes as fee payer
BNB BNB Client signs EIP-712 intent, server executes transfer
Tempo Tempo Moderato Client executes TIP-20 transfer (native gas-free)

Solana Support

Solana uses the SolanaFacilitator with SPL token transfers. Key differences:

  • Gasless for users - Server acts as fee payer (~$0.001 SOL per tx)
  • Separate wallet - Solana uses ed25519 keys (different from EVM's secp256k1)
  • Wallet stored at ~/.moltspay/wallet-solana.json
  • Token - Official Circle USDC SPL token
# Initialize includes Solana wallet automatically
npx moltspay init

# Check Solana balance
npx moltspay status

# Get free devnet USDC
npx moltspay faucet --chain solana_devnet

# Pay on Solana
npx moltspay pay https://server.com service-id --chain solana --prompt "test"

USDC Addresses:

Network Mint Address
Mainnet EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
Devnet 4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU

BNB Chain Support

BNB uses the BNBFacilitator with a pre-approval flow. Since CDP doesn't support BNB, we use a different approach:

  1. Client approves a spender once (a one-time on-chain approve, costs ~0.001 BNB gas)
  2. Client signs an EIP-712 payment intent (no gas needed)
  3. Server validates and executes the transfer
  4. Server sponsors gas (~$0.0001 per tx)
# Get free testnet USDC + tBNB for gas
npx moltspay faucet --chain bnb_testnet

# One-time approve (required before the first BNB payment).
# The spender address comes from the server's 402 response; a first `pay` also
# prints it if you haven't approved yet.
npx moltspay approve --spender 0xSPENDER --chain bnb_testnet

# Pay on BNB (client pays no gas after the approve!)
npx moltspay pay https://server.com service-id --chain bnb_testnet --prompt "test"

# Check BNB balance
npx moltspay status

Important: BNB tokens use 18 decimals (not 6 like Base/Polygon).

Token Addresses (BNB Mainnet):

Token Address
USDC 0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d
USDT 0x55d398326f99059fF775485246999027B3197955

Tempo Testnet

Tempo Moderato is a gas-free testnet that supports the MPP (Machine Payments Protocol). Perfect for testing agent-to-agent payments without any gas fees.

Tempo Stablecoins:

  • pathUSD (USDC equivalent): 0x20c0000000000000000000000000000000000000
  • alphaUSD (USDT equivalent): 0x20c0000000000000000000000000000000000001
# Get free Tempo testnet tokens
npx moltspay faucet --chain tempo_moderato

# Pay on Tempo (gas-free!)
npx moltspay pay https://server.com service-id --chain tempo_moderato --prompt "test"

# Check Tempo balance
npx moltspay status

Explorer: https://explore.testnet.tempo.xyz

Fiat Rail (Alipay / CNY)

Since 2.0.0, MoltsPay supports a fiat payment rail via Alipay AI Pay, settling in CNY alongside the existing USDC crypto rails. It uses the same HTTP 402 flow, so a service can price in USDC, CNY, or both — a purely additive change that leaves existing crypto-only services untouched.

Unlike the crypto rails, alipay is a fiat rail, not a blockchain: there is no gas and no on-chain settlement — the buyer pays in the Alipay app and the merchant settles in CNY. The scheme is alipay-aipay, signed with RSA2.

⚠️ Node CLI only. The Alipay client wraps the alipay-bot CLI and requires Node ≥ 22. The browser build (moltspay/web) throws UnsupportedChainError for alipay. Provider/server config is 100% native TypeScript.

Provider Setup (Selling in CNY)

Add "alipay" to chains, configure provider.alipay with your merchant keys, and add a per-service alipay block with the CNY price. Crypto and fiat can coexist:

{
  "provider": {
    "name": "My Service",
    "wallet": "0x...",
    "chains": ["base", "alipay"],
    "alipay": {
      "seller_id": "2088xxxxxxxxxxxx",
      "app_id": "2021xxxxxxxxxxxx",
      "seller_name": "My Shop",
      "service_id_default": "API_XXXXXXXXXXXXXXXX",
      "private_key_path": "./cert/app_private_key.pem",
      "alipay_public_key_path": "./cert/alipay_public_key.pem"
    }
  },
  "services": [{
    "id": "my-service",
    "function": "handleRequest",
    "price": 0.50,
    "currency": "USDC",
    "alipay": { "price_cny": "7.00", "goods_name": "My Service" }
  }]
}

provider.alipay (required when chains includes alipay):

Field Req Description
seller_id Merchant Alipay ID (16 digits)
app_id Application ID from the Alipay Open Platform (16 digits)
seller_name Merchant display name (shown in the 402 challenge)
service_id_default Default service_id for this provider (e.g. API_XXXXXXXXXXXXXXXX)
private_key_path Path to the RSA2 merchant private key PEM (relative to the manifest)
alipay_public_key_path Path to the Alipay platform public key PEM
gateway_url Open API gateway (default production; use the sandbox URL for testing)
sign_type Signature algorithm — only RSA2 is supported

services[].alipay (set on each service that accepts CNY):

Field Req Description
price_cny CNY price as a decimal string in yuan, e.g. "7.00" = 7 CNY (NOT cents)
goods_name Goods name shown to the buyer in the Alipay app
service_id Per-service override (defaults to provider.alipay.service_id_default)

⚠️ price (USDC) and price_cny (CNY) are two independent prices — MoltsPay does no FX conversion. You decide whether USDC 0.50 is equivalent to CNY 7.00. The merchant-backend unit price must match price_cny or the challenge is rejected.

Client Usage (Paying in CNY)

The Alipay client relies on the alipay-bot CLI, which is auto-provisioned on install from the official Alipay CDN (postinstall, best-effort). Set MOLTSPAY_SKIP_CLI_INSTALL=1 to skip it, or install it manually later:

# Manual / CI install of the bundled CLI
npx -y @alipay/agent-payment install-cli
alipay-bot --version          # expect >= 0.3.15

# One-time: open & authorize the Alipay wallet (scan the returned QR in the app)
npx moltspay alipay apply
npx moltspay alipay bind -c "<auth-code>"
npx moltspay alipay check

# Pay a service in CNY
npx moltspay pay https://server.com my-service --rail alipay --prompt "..."

🔐 Keep keys safe. The RSA2 private key authorizes collection on your merchant account. Store the PEM files outside version control and reference them by path in the manifest.

See docs/ALIPAY-RAIL.md for the full reference (402 flow, error codes, end-to-end example, and known issues).

Fiat Rail (WeChat Pay / CNY)

Since 2.1.0, MoltsPay supports a second fiat payment rail via WeChat Pay v3 Native (scan-to-pay), settling in CNY alongside USDC and Alipay. It uses the same HTTP 402 flow, so a service can price in USDC, Alipay-CNY, WeChat-CNY, or any combination — purely additive.

Like alipay, wechat is a fiat rail, not a blockchain. The scheme is wechatpay-native, requests are signed with SHA256-RSA (WECHATPAY2-SHA256-RSA2048), and amounts are sent to WeChat in fen (the manifest still uses yuan decimal strings for consistency).

⚠️ Not a fully autonomous payer. WeChat Pay has no agent-payment product equivalent to Alipay's alipay-bot. The flow is: the server issues a payer-agnostic code_url (Native, no openid — anyone can scan), the SDK client persists the session and polls, a human scans and pays, and the server verifies the order (trade_state === SUCCESS). It is one code, one payment — issue a new code to collect again. See docs/WECHAT-RAIL-DESIGN.md.

Provider Setup (Selling in CNY)

Add "wechat" to chains, configure provider.wechat with your merchant credentials, and add a per-service wechat block with the CNY price:

{
  "provider": {
    "name": "My Service",
    "wallet": "0x...",
    "chains": ["base", "wechat"],
    "wechat": {
      "mchid": "1900000001",
      "appid": "wx8888888888888888",
      "serial_no": "5157F09EFDC096DE15EBE81A47057A72...",
      "private_key_path": "./cert/wechat_apiclient_key.pem",
      "platform_public_key_path": "./cert/wechat_platform_cert.pem",
      "apiv3_key": "your32byteapiv3keyhere0123456789",
      "notify_url": "https://your.host/wechat/notify"
    }
  },
  "services": [{
    "id": "my-service",
    "function": "handleRequest",
    "price": 0.50,
    "currency": "USDC",
    "wechat": { "price_cny": "7.00", "description": "My Service" }
  }]
}

provider.wechat (required when chains includes wechat):

Field Req Description
mchid Merchant id
appid App id (official account / mini-program / app)
serial_no Merchant API certificate serial number
private_key_path Path to the merchant RSA private key PEM (relative to the manifest)
notify_url Async result notify URL (required by Native order create even when polling)
platform_public_key_path WeChat platform certificate / public key PEM — enables response signature verification
apiv3_key APIv3 key (32 bytes) — only needed for callback decryption (Phase 2)
api_base Open API base URL (default https://api.mch.weixin.qq.com)

services[].wechat (set on each service that accepts CNY):

Field Req Description
price_cny CNY price as a decimal string in yuan, e.g. "7.00" = 7 CNY (NOT fen)
description Order description shown to the payer in the WeChat app

⚠️ price (USDC) and price_cny (CNY) are independent prices — MoltsPay does no FX conversion.

How It Works

  1. An unpaid request to /execute returns 402 with a wechatpay-native entry in accepts[], carrying extra.code_url (weixin://wxpay/bizpayurl?pr=...) and extra.out_trade_no.
  2. The SDK client persists a payment session before surfacing the QR. A channel runtime can show the returned PNG/MEDIA image and continue independently of the original tool call.
  3. A human scans and pays. The Native code_url is a QR payload, not a normal HTTPS checkout link.
  4. The SDK client polls by re-requesting /execute with an X-Payment payload echoing out_trade_no; the server verifies via order query and, on SUCCESS, runs the skill and confirms settlement.
  5. If the process is interrupted, the session can be resumed by payment_session_id or out_trade_no; fulfill is idempotent and returns the stored result once completed.

Buyer SDK Usage

Blocking terminal-style payment:

import { MoltsPayClient } from 'moltspay/client';

const client = new MoltsPayClient();
const result = await client.pay(
  'https://server.com',
  'my-service',
  { prompt: 'hello' },
  {
    rail: 'wechat',
    onPaymentPending: ({ paymentUrl, tradeNo }) => {
      console.log('Render this as a QR:', paymentUrl, tradeNo);
    },
  },
);

Recoverable channel/session payment:

const session = await client.startWechatPayment(
  'https://server.com',
  'my-service',
  { prompt: 'hello' },
  {
    rail: 'wechat',
    autoPoll: true,
    onPaymentPending: ({ paymentUrl, tradeNo }) => {
      // Render paymentUrl as QR and send it to the current channel.
    },
    onWechatPaymentCompleted: async (completed) => {
      // Send completed.resultBody, parsed as needed, back to the channel.
    },
    onWechatPaymentFailed: async (failed) => {
      // Notify timeout/cancel/failure.
    },
  },
);

console.log(session.paymentSessionId, session.outTradeNo);

CLI recoverable flow:

npx moltspay wechat start https://server.com my-service --prompt "hello" --json
npx moltspay wechat status mpay_sess_...
npx moltspay wechat fulfill mpay_sess_...

See examples/wechat-native-pay.ts for a runnable scenario-A demo (mock by default; WECHAT_REAL=1 hits the live gateway).

🔐 Keep keys safe. The merchant private key and APIv3 key authorize collection on your merchant account. Store them outside version control and reference them by path.

Balance Rail (Password-Free Payments)

Since 2.2.0, MoltsPay supports a third payment mode: a custodial balance rail (password-free). A buyer tops up once — via any existing rail — and subsequent purchases are deducted from a server-custodied balance: no signature, no QR scan, no password per transaction. It uses the same HTTP 402 flow (scheme/network balance), so a service can offer crypto, Alipay-CNY, WeChat-CNY, and balance simultaneously — purely additive.

Unlike the other rails, nothing settles externally at purchase time: the ledger lives in SQLite on the provider server (Node's built-in node:sqlite, zero new dependencies). Enabling the rail requires Node.js >= 22.5 on the server; servers that don't enable it keep the package's node >= 18 floor.

⚠️ Custodial trust model. Buyer funds are held by the provider. Since 2.4.0, spending is authorized by a per-request signature, not a bare buyer_id — see Balance Authentication below. With auth_mode: enforce, knowing a buyer_id is not enough to spend. (auth_mode defaults to off for backward compatibility, where the id retains bearer semantics.) See docs/WECHAT-BALANCE-PASSWORDLESS-DESIGN.md.

Balance Authentication (2.4.0)

Before 2.4.0 the balance rail identified a buyer by a single plaintext buyer_id: anyone who knew it could spend that balance (bearer semantics), and the same person writing it two ways split into two accounts. 2.4.0 gives the rail a real identity, in two complementary halves:

  • Identity anchor = WeChat openid. On a WeChat-funded top-up, the server extracts payer.openid from the (signature-verified) order query and records it on the account. Accounts are anchored to the person who actually paid, not to a self-reported string — this removes the account-splitting class of bug at the source. Binding is observational and never overwrites a conflicting openid.
  • Spend credential = a per-request signature. Each deduction carries an EIP-191 signature over a canonical message (domain moltspay-balance-auth:v1 / balance-deduct / buyer_id / request_id / service / timestamp). The server recovers the signer address, TOFU-binds it to the account on first use, and verifies it thereafter. The amount is deliberately not signed (the service id fixes the price server-side); a request_id + a ±5-minute timestamp window bound replay.

provider.balance.auth_mode — staged rollout, not on by default:

Mode Behavior
off (default) Signature is not checked. Backward-compatible; buyer_id keeps bearer semantics.
shadow Verify and log what it would reject, without blocking. Use this to confirm every client is signing before tightening.
enforce Reject unsigned / invalid / wrong-signer requests with 401.

The client signs with its EVM wallet, or — for balance-only clients with no crypto wallet — a per-configDir identity key auto-created at <configDir>/balance-identity.key (0600). Inspect and bind identity with moltspay balance whoami / moltspay balance bind.

Foundation — WeChat response verification. The openid anchor is only trustworthy if the order-query response is verified against the WeChat platform certificate. Configure provider.wechat.platform_public_key_path (present ⇒ every response is verified, and a verification failure throws); without it, the identity anchor is not trustworthy.

Trust model — one agent key spends for all its users. The shipped model is agent-custodial: a single agent key is bound to every account it tops up (signer_address is one-to-many). Accounts stay separated by openid, but whoever holds the agent's key can spend every bound user's balance — protect balance-identity.key accordingly. There is currently no revocation/freeze API (operator-only, via the ledger DB); per-user key isolation is a future upgrade.

WeChat-Funded Password-Free (Scan Once, Then Password-Free)

Since 2.3.0, the WeChat rail can fund the balance instead of charging per transaction. The buyer scans once to load a top-up pack (e.g. ¥20); every subsequent purchase deducts from the balance with no scan, no password, until it runs low and one more scan tops it up. WeChat has no autonomous payer, so "password-free" lives on the balance side — the first purchase against an empty balance still needs one scan, but it buys a pack, not a single item.

Set the ledger currency to CNY (WeChat payer_total credits 1:1 as fen — no FX), define top-up packs, and the client handles the rest:

{
  "provider": {
    "name": "My Service",
    "wallet": "0x...",
    "chains": ["balance", "wechat"],
    "balance": {
      "db_path": "./data/balance-cny.sqlite",
      "currency": "CNY",
      "single_limit": "50.00",
      "daily_limit": "200.00",
      "topup_packs": ["20.00", "50.00"],
      "default_pack": "20.00",
      "auto_topup_max": "50.00"
    },
    "wechat": {
      "mchid": "1900000001",
      "appid": "wx8888888888888888",
      "serial_no": "...",
      "private_key_path": "/abs/apiclient_key.pem",
      "notify_url": "https://your-host/wechat/notify",
      "platform_public_key_path": "/abs/wechat_platform_cert.pem",
      "apiv3_key": "<32-byte apiv3 key>"
    }
  },
  "services": [{
    "id": "my-service",
    "function": "handleRequest",
    "price": 3.99,
    "currency": "CNY",
    "balance": { "price": "3.99" }
  }]
}

New provider.balance fields (2.3.0):

Field Req Description
currency Set to "CNY" to fund via WeChat 1:1 (fen). A ledger remembers its currency and refuses to start on a mismatch
topup_packs Offered recharge amounts (yuan strings), e.g. ["20.00","50.00"]
default_pack Pack the client auto-selects when funds are short (must be in topup_packs)
auto_topup_max Ceiling on client auto-top-up without explicit pack selection

Top-ups are confirmed by polling the WeChat order query (trade_state === SUCCESS) and credited idempotently on out_trade_no. The async callback webhook (POST /wechat/notify) is not implemented yet — polling is the only confirmation path today, which is safe but adds latency. Configure platform_public_key_path regardless: it makes the server verify WeChat's response signatures, which is what makes the payer.openid behind balance identity trustworthy. Usage — a single pay command:

npx moltspay balance set-buyer my-buyer-id
# First purchase: prints a pack QR, waits for the scan, credits, completes.
# Later purchases: password-free, no QR — deducted from the balance.
npx moltspay pay https://server.com my-service '{"prompt":"hello"}'

Money-Safety Design

  • Integer-cent accounting — no floating-point drift.
  • Atomic check-and-deduct — a single SQLite transaction (UPDATE ... WHERE balance_sat >= ?); a balance can never go negative under concurrency.
  • Triple idempotency, each enforced by a unique index: deducts replay on the client's request_id, top-ups replay on the external settlement reference, refunds replay per deduct. No retry can double-charge, double-credit, or double-refund.
  • Server-side hard limits per buyer: per-transaction (default 5.00) and daily (default 10.00).
  • Automatic refund — the deduct lands before the skill runs; if the skill fails, the server refunds it in the same request.
  • Pure 402 challenges — unlike the QR rails, a 402 response mints nothing, so unpaid challenges have no side effects.
  • Full audit trail — every top-up, deduct, and refund is a permanent ledger row, queryable per buyer.

Provider Setup

Add "balance" to chains, configure provider.balance, and add a per-service balance block (its price defaults to the service's top-level price):

{
  "provider": {
    "name": "My Service",
    "wallet": "0x...",
    "chains": ["base", "balance"],
    "balance": {
      "db_path": "./data/balance.sqlite",
      "currency": "USD",
      "single_limit": "5.00",
      "daily_limit": "10.00"
    }
  },
  "services": [{
    "id": "my-service",
    "function": "handleRequest",
    "price": 3.99,
    "currency": "USDC",
    "balance": { "price": "3.99" }
  }]
}

provider.balance (required when chains includes balance):

Field Req Description
db_path SQLite ledger file path (relative to the manifest); created on first start
currency Ledger quote currency (default USD)
single_limit Default per-transaction limit for new buyers (default "5.00")
daily_limit Default daily limit for new buyers (default "10.00")

services[].balance:

Field Req Description
price Price in the ledger currency as a decimal string; defaults to the service's price

The server also exposes balance-management endpoints:

Endpoint Description
GET /balance?buyer_id= Balance, limits, and today's spend
POST /balance/topup Verify an externally settled payment and credit the ledger
POST /balance/refund Reverse a deduct (operator/agent use)
GET /balance/transactions?buyer_id= Ledger history, newest first

⚠️ Protect these endpoints (reverse-proxy auth / IP allowlist). In particular, the alipay top-up path is operator-trusted in this MVP: Alipay AI-Pay verification requires the buyer-side payment_proof, so the server credits a reported trade_no without gateway verification. The crypto path verifies the tx_hash on-chain (receipt + transfer to the provider wallet + amount) and the wechat path queries the order for SUCCESS; all three are idempotent on the external reference.

How It Works

  1. An unpaid request to /execute returns 402 with a balance entry in accepts[] (pure — nothing is minted).
  2. The client re-requests with an X-Payment payload carrying { buyer_id, request_id }, where request_id is a fresh client-generated UUID.
  3. The server deducts atomically first (idempotent on request_id), then runs the skill, and refunds automatically if the skill fails. This is the inverse of the QR rails (verify paid → run → confirm).
  4. The response carries the ledger transaction id in X-Payment-Response.

Buyer Usage

# One-time: persist your buyer id (account label). Under auth_mode=enforce, spending also
# requires the bound signing key — see "Balance Authentication" above.
npx moltspay balance set-buyer my-buyer-id

# Top up once, via any settled rail
npx moltspay balance topup https://server.com 10.00 --rail crypto --tx-hash 0xabc... --chain base
npx moltspay balance topup https://server.com 10.00 --rail wechat --out-trade-no MP1719...
npx moltspay balance topup https://server.com 10.00 --rail alipay --trade-no 2026...

# Then pay password-free — no wallet, no QR
npx moltspay pay https://server.com my-service '{"prompt":"hello"}' --rail balance

# Check balance and history
npx moltspay balance query https://server.com
npx moltspay balance transactions https://server.com

SDK equivalent:

import { MoltsPayClient } from 'moltspay/client';

const client = new MoltsPayClient();
client.setBuyerId('my-buyer-id'); // persisted in config.json

await client.topupBalance('https://server.com', {
  rail: 'crypto', amount: '10.00', txHash: '0xabc...', chain: 'base',
});

const result = await client.pay('https://server.com', 'my-service',
  { prompt: 'hello' }, { rail: 'balance' });

const info = await client.getBuyerBalance('https://server.com');
console.log(info.balance, info.today_spent);

Live Example: Zen7 Video Generation

Live service at https://moltspay.com/a/zen7

Services:

  • text-to-video - $0.01 USDC - Generate video from text prompt
  • image-to-video - $1.49 USDC - Animate a static image

Supported Chains: Base, Polygon, Solana, BNB, Tempo (mainnet & testnet)

Try it:

# List services
npx moltspay services https://moltspay.com/a/zen7

# Pay on Base (default)
npx moltspay pay https://moltspay.com/a/zen7 text-to-video --prompt "a happy cat"

# Pay on different chains
npx moltspay pay https://moltspay.com/a/zen7 text-to-video --chain polygon --prompt "a happy cat"
npx moltspay pay https://moltspay.com/a/zen7 text-to-video --chain bnb_testnet --prompt "a happy cat"
npx moltspay pay https://moltspay.com/a/zen7 text-to-video --chain solana_devnet --prompt "a happy cat"

Use Cases

  • AI Video Generation - Pay per video generated
  • Image Processing - Pay for AI image editing/enhancement
  • Data APIs - Monetize proprietary datasets
  • Compute Services - Sell GPU time to other agents
  • Content Generation - AI writing, music, code generation

Related Projects

Community & Support

Join our Discord for help, feedback, and updates:

--> MoltsPay Discord

Or visit the #moltspay-support channel directly.

Links

License

MIT

All Coinbase projects →