moltspay 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -134,6 +134,148 @@ npx moltspay pay https://server.com service-id \
134
134
  --chain tempo_moderato --prompt "test"
135
135
  ```
136
136
 
137
+ ### For Web Apps (Browser)
138
+
139
+ `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.
140
+
141
+ **Install:**
142
+
143
+ ```bash
144
+ npm install moltspay
145
+ ```
146
+
147
+ **Pay with MetaMask:**
148
+
149
+ ```ts
150
+ import { MoltsPayWebClient, eip1193Signer } from 'moltspay/web';
151
+
152
+ const client = new MoltsPayWebClient({
153
+ signer: eip1193Signer(window.ethereum),
154
+ });
155
+
156
+ // Discover what the provider accepts
157
+ const { provider, services } = await client.getServices('https://provider.example.com');
158
+
159
+ // Run a paid call — user sees one wallet signature prompt
160
+ const result = await client.pay(
161
+ 'https://provider.example.com',
162
+ 'text-to-video',
163
+ { prompt: 'a cat dancing' },
164
+ { chain: 'base' }
165
+ );
166
+ ```
167
+
168
+ **Pay with Phantom (Solana):**
169
+
170
+ ```ts
171
+ import { MoltsPayWebClient, solanaSigner } from 'moltspay/web';
172
+ import { useWallet } from '@solana/wallet-adapter-react';
173
+
174
+ const wallet = useWallet();
175
+ const client = new MoltsPayWebClient({ signer: solanaSigner(wallet) });
176
+
177
+ await client.pay(serverUrl, 'text-to-video', params, { chain: 'solana_devnet' });
178
+ ```
179
+
180
+ **EVM + Solana in one client** — use `composeSigners` so the same `MoltsPayWebClient` instance routes to whichever signer matches the picked chain:
181
+
182
+ ```ts
183
+ import { MoltsPayWebClient, composeSigners, eip1193Signer, solanaSigner } from 'moltspay/web';
184
+
185
+ const client = new MoltsPayWebClient({
186
+ signer: composeSigners(
187
+ eip1193Signer(window.ethereum),
188
+ solanaSigner(phantomAdapter),
189
+ ),
190
+ });
191
+ ```
192
+
193
+ **Chain coverage.** All 8 chains the CLI supports work from the browser with one signature prompt each:
194
+
195
+ | Chain | Scheme | User gas? | Notes |
196
+ |---|---|---|---|
197
+ | `base` / `polygon` / `base_sepolia` | EIP-3009 `transferWithAuthorization` | No (gasless) | Provider submits on success |
198
+ | `tempo_moderato` | EIP-2612 `permit` | No (gasless) | Browser never switches to Tempo; server's settler submits permit + transferFrom |
199
+ | `bnb` / `bnb_testnet` | MoltsPay `PaymentIntent` | One-time approve | Call `client.approveBnb({ chain, spender, token })` once, then intent signatures are gasless |
200
+ | `solana` / `solana_devnet` | SPL transfer | No if provider sets a fee payer | `wallet.signTransaction` signs, provider submits |
201
+
202
+ **BNB approval flow.** The first payment on BNB throws `NeedsApprovalError` with the details needed to approve — catch it, call `approveBnb()`, and retry:
203
+
204
+ ```ts
205
+ try {
206
+ await client.pay(url, service, params, { chain: 'bnb' });
207
+ } catch (err) {
208
+ if (err instanceof NeedsApprovalError) {
209
+ await client.approveBnb({
210
+ chain: 'bnb',
211
+ spender: err.details.spender,
212
+ token: err.details.token,
213
+ });
214
+ // User paid ~0.001 BNB gas for the approve. Retry now succeeds without further approve.
215
+ await client.pay(url, service, params, { chain: 'bnb' });
216
+ }
217
+ }
218
+ ```
219
+
220
+ **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.
221
+
222
+ **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`:
223
+
224
+ ```ts
225
+ const client = new MoltsPayWebClient({
226
+ signer: composeSigners(
227
+ eip1193Signer(window.ethereum),
228
+ solanaSigner(phantomAdapter),
229
+ ),
230
+ solanaRpc: {
231
+ solana: 'https://mainnet.helius-rpc.com/?api-key=YOUR_KEY',
232
+ // solana_devnet falls back to `api.devnet.solana.com` automatically
233
+ },
234
+ });
235
+ ```
236
+
237
+ 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.
238
+
239
+ **Error classes** — every error exposes a `code` field so you can branch without string-matching:
240
+
241
+ ```ts
242
+ import {
243
+ NeedsApprovalError, // code: 'NEEDS_APPROVAL' — BNB, call approveBnb()
244
+ UnsupportedChainError, // code: 'UNSUPPORTED_CHAIN' — user picked a chain the server doesn't accept
245
+ PaymentRejectedError, // code: 'PAYMENT_REJECTED' — user cancelled in the wallet
246
+ InsufficientBalanceError, // code: 'INSUFFICIENT_BALANCE' — not enough native gas for BNB approve
247
+ SpendingLimitExceededError,// code: 'SPENDING_LIMIT_EXCEEDED' — only when you opt in to SpendingLedger
248
+ ServerError, // code: 'SERVER_ERROR' — provider returned non-2xx
249
+ MoltsPayError, // base class
250
+ } from 'moltspay/web';
251
+ ```
252
+
253
+ **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:
254
+
255
+ ```ts
256
+ const client = new MoltsPayWebClient({
257
+ signer: eip1193Signer(window.ethereum),
258
+ spendingLimits: { maxPerTx: 5, maxPerDay: 50 },
259
+ });
260
+ ```
261
+
262
+ **Provider CORS.** Providers must enable CORS on `MoltsPayServer` for browser callers — without it the 402 challenge header is invisible to the browser:
263
+
264
+ ```ts
265
+ new MoltsPayServer({
266
+ // ...
267
+ cors: true, // allow all origins, or
268
+ cors: ['https://myapp.example.com'], // explicit allowlist, or
269
+ cors: { origins: [...], maxAge: 86400 }, // fine-grained
270
+ });
271
+ ```
272
+
273
+ Servers advertising for the web need `cors` enabled; CLI callers are unaffected either way.
274
+
275
+ **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.
276
+
277
+ **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`](examples/web/README.md) for the full matrix of tested wallets + chains.
278
+
137
279
  ## MCP Server (For AI Assistants)
138
280
 
139
281
  MoltsPay ships an [MCP (Model Context Protocol)](https://modelcontextprotocol.io) 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.