@three-ws/x402-payment-modal 1.1.0 → 1.2.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/CHANGELOG.md +71 -9
- package/CONTRIBUTING.md +79 -0
- package/LICENSE +38 -180
- package/README.md +238 -63
- package/dist/index.d.ts +14 -3
- package/dist/x402.js +564 -206
- package/dist/x402.min.js +308 -178
- package/docs/EXAMPLES.md +137 -0
- package/docs/api-reference.md +32 -5
- package/docs/architecture.md +7 -1
- package/docs/react.md +163 -0
- package/docs/server-setup.md +63 -6
- package/examples/README.md +2 -1
- package/examples/react/App.jsx +95 -0
- package/examples/react/README.md +34 -31
- package/examples/server-express/server.js +16 -9
- package/examples/solana-crypto-paywall/README.md +81 -0
- package/examples/solana-crypto-paywall/facilitator.mjs +170 -0
- package/examples/solana-crypto-paywall/package.json +17 -0
- package/examples/solana-crypto-paywall/public/index.html +506 -0
- package/examples/solana-crypto-paywall/server.mjs +279 -0
- package/package.json +126 -111
- package/react/index.d.ts +39 -0
- package/react/index.js +112 -0
- package/server/checkout.js +208 -66
- package/server/express.js +7 -4
- package/server/vercel.js +2 -2
- package/src/index.js +563 -205
- package/types/index.d.ts +14 -3
- package/types/server.d.ts +2 -1
- package/examples/react/X402Button.jsx +0 -84
package/README.md
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
# @three-ws/x402-payment-modal
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
> The complete drop-in checkout for any [x402](https://x402.org) paid endpoint — wallet connect, sign, settle, receipt.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@three-ws/x402-payment-modal)
|
|
6
|
+
[](https://www.npmjs.com/package/@three-ws/x402-payment-modal)
|
|
7
|
+
[](https://bundlephobia.com/package/@three-ws/x402-payment-modal)
|
|
8
|
+
[](./LICENSE)
|
|
9
|
+
|
|
10
|
+
**A drop-in payment modal for any [x402](https://x402.org) paid endpoint.** The
|
|
11
|
+
browser client is one ES module with zero runtime dependencies; it turns an HTTP
|
|
12
|
+
`402 Payment Required` challenge into a polished checkout: wallet connect (Phantom
|
|
13
|
+
/ Solflare / Backpack on Solana, MetaMask / any EVM wallet on Base via EIP-3009),
|
|
14
|
+
the `402 → sign → settle` flow, optional SIWX re-entry, client-side spending caps,
|
|
15
|
+
and a receipt. It ships a **server** checkout adapter for the Solana rail
|
|
16
|
+
(`./server`, `./server/express`, `./server/vercel`) and a **React** wrapper
|
|
17
|
+
(`./react`) — pick only the pieces you need. Vanilla JS, no bundler required.
|
|
9
18
|
|
|
10
19
|
```html
|
|
11
20
|
<script type="module" src="https://unpkg.com/@three-ws/x402-payment-modal"></script>
|
|
@@ -41,17 +50,61 @@ throttle retries, and the receipt, so you ship a paid endpoint in minutes.
|
|
|
41
50
|
- **Solana + EVM.** Phantom (Solana) and any injected EVM wallet (Base USDC
|
|
42
51
|
via EIP-3009 `transferWithAuthorization`). The modal picks the right path from
|
|
43
52
|
the 402 challenge.
|
|
44
|
-
- **
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
53
|
+
- **USDC by default, multi-token if you want it.** USDC is the always-on default
|
|
54
|
+
on Solana. Optionally offer a second SPL token (the modal ships a built-in
|
|
55
|
+
`THREE` opt-in) and the buyer gets a token picker — each token is recognized on
|
|
56
|
+
sight (symbol, decimals). See
|
|
57
|
+
[Accepting multiple Solana tokens](#accepting-multiple-solana-tokens).
|
|
48
58
|
- **SIWX re-entry.** If a wallet already paid for a resource, it can sign back in
|
|
49
59
|
instead of paying again. See [docs/siwx.md](docs/siwx.md).
|
|
50
60
|
- **Spending caps.** Optional per-call / hourly / daily caps enforced in the
|
|
51
61
|
browser. See [docs/spending-caps.md](docs/spending-caps.md).
|
|
52
|
-
- **Themeable.** Light + automatic dark mode,
|
|
62
|
+
- **Themeable.** Light + automatic dark mode, a full `--x402-*` design-token
|
|
63
|
+
surface, runtime brand-matching, and a header logo. See
|
|
53
64
|
[docs/theming.md](docs/theming.md).
|
|
54
|
-
- **Accessible.** Focus
|
|
65
|
+
- **Accessible.** Focus trap + restore, `aria-modal`, `aria-live` step
|
|
66
|
+
announcements, `:focus-visible` rings, `prefers-reduced-motion`, keyboard `Esc`
|
|
67
|
+
to close.
|
|
68
|
+
- **Server + React adapters in the box.** Framework-agnostic Solana checkout
|
|
69
|
+
(`./server`) with Express and Vercel adapters, and a first-class React wrapper
|
|
70
|
+
(`./react`) — all behind separate export subpaths, all optional.
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Which x402 modal do I want?
|
|
75
|
+
|
|
76
|
+
This package has a lighter sibling, **[`@three-ws/x402-modal`](https://www.npmjs.com/package/@three-ws/x402-modal)**.
|
|
77
|
+
Both render a drop-in modal; choose by how much you need.
|
|
78
|
+
|
|
79
|
+
| | **`@three-ws/x402-payment-modal`** (this) | `@three-ws/x402-modal` |
|
|
80
|
+
| --- | --- | --- |
|
|
81
|
+
| Goal | Full checkout SDK: client **+ server + React** | Minimal client-only modal |
|
|
82
|
+
| Chains | Solana **and** EVM (Base/Arbitrum/Optimism) | Client modal, EVM-leaning |
|
|
83
|
+
| Solana server adapter | ✅ `./server` + Express + Vercel | ❌ bring your own |
|
|
84
|
+
| React wrapper | ✅ `./react` (`X402Button`, `useX402`) | ❌ |
|
|
85
|
+
| SIWX re-entry | ✅ | — |
|
|
86
|
+
| Client spending caps | ✅ per-call / hour / day | — |
|
|
87
|
+
| Multi-token picker (USDC + opt-in SPL) | ✅ | — |
|
|
88
|
+
| Builder-code (ERC-8021) echo | ✅ | — |
|
|
89
|
+
| Footprint | Larger (more surface) | Smaller, fewer knobs |
|
|
90
|
+
|
|
91
|
+
**Rule of thumb:** taking real Solana payments, want a React component, or need
|
|
92
|
+
caps/SIWX → use **this** package. Just need a tiny EVM paywall button and nothing
|
|
93
|
+
else → the lighter `@three-ws/x402-modal` is enough.
|
|
94
|
+
|
|
95
|
+
## Package subpaths
|
|
96
|
+
|
|
97
|
+
Import only what you use — each subpath is independent and its peer deps are
|
|
98
|
+
optional.
|
|
99
|
+
|
|
100
|
+
| Subpath | Import | Needs |
|
|
101
|
+
| --- | --- | --- |
|
|
102
|
+
| `.` | `@three-ws/x402-payment-modal` | nothing (browser) |
|
|
103
|
+
| `./min` | `@three-ws/x402-payment-modal/min` | nothing — pre-minified bundle |
|
|
104
|
+
| `./server` | `@three-ws/x402-payment-modal/server` | `@solana/web3.js`, `@solana/spl-token` |
|
|
105
|
+
| `./server/express` | `@three-ws/x402-payment-modal/server/express` | + `express` |
|
|
106
|
+
| `./server/vercel` | `@three-ws/x402-payment-modal/server/vercel` | `@solana/web3.js`, `@solana/spl-token` |
|
|
107
|
+
| `./react` | `@three-ws/x402-payment-modal/react` | `react` |
|
|
55
108
|
|
|
56
109
|
---
|
|
57
110
|
|
|
@@ -73,15 +126,28 @@ npm install @three-ws/x402-payment-modal
|
|
|
73
126
|
import { pay, configure } from '@three-ws/x402-payment-modal';
|
|
74
127
|
```
|
|
75
128
|
|
|
76
|
-
|
|
77
|
-
|
|
129
|
+
### Optional peer dependencies
|
|
130
|
+
|
|
131
|
+
The core client has **no runtime dependencies**. The other subpaths declare
|
|
132
|
+
*optional* peer deps — install them only when you use that subpath:
|
|
133
|
+
|
|
134
|
+
| You use… | Install |
|
|
135
|
+
| --- | --- |
|
|
136
|
+
| `./server` or `./server/vercel` (Solana checkout) | `@solana/web3.js @solana/spl-token` |
|
|
137
|
+
| `./server/express` | `@solana/web3.js @solana/spl-token express` |
|
|
138
|
+
| `./react` | `react` (you already have it) |
|
|
78
139
|
|
|
79
140
|
```bash
|
|
141
|
+
# Solana checkout server
|
|
80
142
|
npm install @solana/web3.js @solana/spl-token
|
|
143
|
+
# …and express if you use the Express adapter
|
|
144
|
+
npm install express
|
|
81
145
|
```
|
|
82
146
|
|
|
83
147
|
> EVM-only sites need **nothing** server-side: the wallet signs EIP-3009
|
|
84
|
-
> typed-data entirely in the browser.
|
|
148
|
+
> typed-data entirely in the browser. The Solana/EVM crypto helpers used by the
|
|
149
|
+
> client are loaded lazily from a CDN **only** when a payment is attempted, so
|
|
150
|
+
> they are not in your bundle.
|
|
85
151
|
|
|
86
152
|
---
|
|
87
153
|
|
|
@@ -135,34 +201,34 @@ server.
|
|
|
135
201
|
|
|
136
202
|
---
|
|
137
203
|
|
|
138
|
-
##
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
204
|
+
## Architecture
|
|
205
|
+
|
|
206
|
+
Three independent pieces, each optional except the client:
|
|
207
|
+
|
|
208
|
+
- **Client** (`.` / `./min`) — renders the modal, detects wallets, drives the
|
|
209
|
+
`402 → sign → settle` flow. Zero runtime deps.
|
|
210
|
+
- **Server adapter** (`./server*`) — Solana only. Builds the SPL transfer Phantom
|
|
211
|
+
signs and wraps it into the `X-PAYMENT` envelope. EVM never touches it.
|
|
212
|
+
- **React wrapper** (`./react`) — `X402Button` + `useX402`, SSR-safe.
|
|
213
|
+
|
|
214
|
+
```mermaid
|
|
215
|
+
flowchart TD
|
|
216
|
+
A[User clicks pay] --> B[Client: GET/POST endpoint]
|
|
217
|
+
B --> C{HTTP 402 + accepts?}
|
|
218
|
+
C -->|no, 200| X[Error: not an x402 endpoint]
|
|
219
|
+
C -->|yes| D[discover: show price + network]
|
|
220
|
+
D --> E[connect: pick & connect wallet]
|
|
221
|
+
E --> F{network}
|
|
222
|
+
F -->|EVM| G[sign EIP-3009 typed-data\nbrowser only, no server]
|
|
223
|
+
F -->|Solana| H[POST ./server ?action=prepare]
|
|
224
|
+
H --> I[Phantom signs the v0 tx]
|
|
225
|
+
I --> J[POST ./server ?action=encode → X-PAYMENT]
|
|
226
|
+
G --> K[verify: retry with X-PAYMENT]
|
|
227
|
+
J --> K
|
|
228
|
+
K --> L{settled?}
|
|
229
|
+
L -->|429 throttle| K
|
|
230
|
+
L -->|2xx| M[receipt + result → resolve PayResult]
|
|
231
|
+
L -->|4xx/5xx| N[error + roll back any cap reservation]
|
|
166
232
|
```
|
|
167
233
|
|
|
168
234
|
Full walkthrough in [docs/architecture.md](docs/architecture.md).
|
|
@@ -235,12 +301,18 @@ configure({
|
|
|
235
301
|
| `checkoutOrigin` | `data-x402-checkout-origin` | the script's own origin |
|
|
236
302
|
| `checkoutPath` | `data-x402-checkout-path` | `/api/x402-checkout` |
|
|
237
303
|
| `footerNote` | `data-x402-footer-note` | `x402 · onchain settled` |
|
|
238
|
-
| `brand.name` / `brand.url` | `data-x402-brand-name` / `-brand-url` | `
|
|
239
|
-
| `
|
|
304
|
+
| `brand.name` / `brand.url` | `data-x402-brand-name` / `-brand-url` | `null` (footer link hidden) |
|
|
305
|
+
| `brand.logo` | — | `null` (no header logo) |
|
|
306
|
+
| `theme` | — | `'auto'` (follow the OS) |
|
|
307
|
+
| `cssVars` | — | `null` (use built-in tokens) |
|
|
308
|
+
| `builderCode.wallet` / `.service` | `data-x402-builder-wallet` / `-builder-service` | `''` (no self-attribution) |
|
|
240
309
|
| `esm.solanaWeb3` / `esm.nobleHashesSha3` | — | pinned esm.sh URLs |
|
|
241
310
|
|
|
242
|
-
|
|
243
|
-
|
|
311
|
+
`theme` is `'auto' | 'light' | 'dark'`; `cssVars` is a flat map of `--x402-*`
|
|
312
|
+
design tokens (e.g. `{ '--x402-accent': '#ff5c00', '--x402-radius': '8px' }`) for
|
|
313
|
+
runtime brand-matching — see [docs/theming.md](docs/theming.md). Repoint `esm.*`
|
|
314
|
+
at a self-hosted mirror if a strict Content-Security-Policy blocks `esm.sh`. (The
|
|
315
|
+
EVM/Base path needs no third-party code at all.)
|
|
244
316
|
|
|
245
317
|
---
|
|
246
318
|
|
|
@@ -268,20 +340,62 @@ app.listen(3000);
|
|
|
268
340
|
export { default } from '@three-ws/x402-payment-modal/server/vercel';
|
|
269
341
|
```
|
|
270
342
|
|
|
271
|
-
|
|
272
|
-
`
|
|
343
|
+
Pass `rpcUrls: [...]` (an ordered list, tried with failover) instead of a single
|
|
344
|
+
`rpcUrl` for production — the public RPC is rate-limited and warns once at
|
|
345
|
+
startup. Lower-level helpers (`prepareSolanaCheckout`, `encodeX402Payment`,
|
|
346
|
+
`handleCheckout`, `CheckoutError`, `solanaAccept`) are exported from
|
|
273
347
|
`@three-ws/x402-payment-modal/server`. Full guide:
|
|
274
348
|
[docs/server-setup.md](docs/server-setup.md).
|
|
275
349
|
|
|
276
350
|
---
|
|
277
351
|
|
|
278
|
-
##
|
|
352
|
+
## React
|
|
353
|
+
|
|
354
|
+
The `./react` subpath ships a hook and a button. The browser-only core is
|
|
355
|
+
dynamically imported on first use, so both are **SSR-safe** — nothing runs during
|
|
356
|
+
render or on the server.
|
|
357
|
+
|
|
358
|
+
```jsx
|
|
359
|
+
import { X402Button, useX402 } from '@three-ws/x402-payment-modal/react';
|
|
360
|
+
|
|
361
|
+
// Drop-in button:
|
|
362
|
+
<X402Button
|
|
363
|
+
endpoint="/api/paid/summarize"
|
|
364
|
+
method="POST"
|
|
365
|
+
body={{ url: 'https://en.wikipedia.org/wiki/x402' }}
|
|
366
|
+
merchant="Acme"
|
|
367
|
+
action="Summarize"
|
|
368
|
+
label="Summarize for $0.01"
|
|
369
|
+
onResult={(r) => console.log('paid', r.payment)}
|
|
370
|
+
onError={(e) => console.error(e)}
|
|
371
|
+
/>;
|
|
372
|
+
|
|
373
|
+
// …or drive it yourself with the hook:
|
|
374
|
+
function Buy() {
|
|
375
|
+
const { pay, isPaying, result, error } = useX402({ merchant: 'Acme' });
|
|
376
|
+
return (
|
|
377
|
+
<button disabled={isPaying} onClick={() => pay({ endpoint: '/api/paid/summarize' })}>
|
|
378
|
+
{isPaying ? 'Processing…' : 'Pay'}
|
|
379
|
+
</button>
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
`useX402(defaults)` returns `{ pay, status, result, error, reset, isPaying }`
|
|
385
|
+
where `status` is `idle | paying | done | error`. Full reference:
|
|
386
|
+
[docs/react.md](docs/react.md).
|
|
387
|
+
|
|
388
|
+
---
|
|
279
389
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
390
|
+
## Accepting multiple Solana tokens
|
|
391
|
+
|
|
392
|
+
USDC is the default settlement asset, but an x402 challenge can list more than one
|
|
393
|
+
accepted payment. List a USDC accept **and** a second SPL-token accept on Solana,
|
|
394
|
+
and the modal renders a token picker: the buyer chooses which to pay in, the
|
|
395
|
+
headline price and the transaction follow the choice. No client wiring — the
|
|
396
|
+
checkout endpoint already builds an SPL transfer for whatever mint the chosen
|
|
397
|
+
accept names. The package ships a built-in `THREE` opt-in token as a ready
|
|
398
|
+
example, but any mint works.
|
|
285
399
|
|
|
286
400
|
Build the accepts with the `solanaAccept` helper (no hardcoded mints):
|
|
287
401
|
|
|
@@ -304,9 +418,10 @@ and emits a spec-shaped `accept`. The constants `THREE_MINT`, `USDC_MINT_SOLANA`
|
|
|
304
418
|
and `WELL_KNOWN_SOLANA_TOKENS` are exported too. On the client, the same mints
|
|
305
419
|
are exposed at `window.X402.tokens` for inline merchants.
|
|
306
420
|
|
|
307
|
-
> **
|
|
308
|
-
>
|
|
309
|
-
> THREE server-side. USDC
|
|
421
|
+
> **Non-stable tokens float against the dollar.** The modal can't
|
|
422
|
+
> dollar-denominate a floating-price token for browser-side spending caps — so
|
|
423
|
+
> enforce caps for any non-stable token (like `THREE`) server-side. USDC and
|
|
424
|
+
> other stablecoin caps work in the browser as usual.
|
|
310
425
|
|
|
311
426
|
---
|
|
312
427
|
|
|
@@ -315,10 +430,13 @@ are exposed at `window.X402.tokens` for inline merchants.
|
|
|
315
430
|
- [Architecture](docs/architecture.md) — the full payment lifecycle.
|
|
316
431
|
- [Client API reference](docs/api-reference.md)
|
|
317
432
|
- [Server setup](docs/server-setup.md)
|
|
433
|
+
- [React reference](docs/react.md) — `X402Button` + `useX402`.
|
|
318
434
|
- [Theming](docs/theming.md)
|
|
319
435
|
- [SIWX re-entry](docs/siwx.md)
|
|
320
436
|
- [Spending caps](docs/spending-caps.md)
|
|
437
|
+
- [Examples](docs/EXAMPLES.md) — runnable plain-HTML, React, and Express samples.
|
|
321
438
|
- [Tutorial](TUTORIAL.md) — build a paid endpoint end-to-end.
|
|
439
|
+
- [Contributing](CONTRIBUTING.md)
|
|
322
440
|
|
|
323
441
|
---
|
|
324
442
|
|
|
@@ -334,14 +452,71 @@ Modern evergreen browsers (ES2020, `BigInt`, dynamic `import()`, `fetch`,
|
|
|
334
452
|
- Payments are signed in the user's wallet — this package never sees a private
|
|
335
453
|
key. The Solana checkout server only builds an unsigned transaction and base64-
|
|
336
454
|
wraps the user-signed one; it cannot move funds.
|
|
455
|
+
- **SIWX** re-entry is a gasless CAIP-122 signature, never a transfer. The client
|
|
456
|
+
detects the offer, signs, and submits the proof; the **server** must verify the
|
|
457
|
+
nonce/domain/expiry/signature and check the entitlement — see
|
|
458
|
+
[docs/siwx.md](docs/siwx.md).
|
|
337
459
|
- Spending caps are a **client-side guardrail**, not a security boundary — a user
|
|
338
460
|
can clear `localStorage`. Enforce real limits server-side.
|
|
339
|
-
- Pin the script to a version or self-host it for production
|
|
461
|
+
- Pin the script to a version or self-host it for production (the lazily-loaded
|
|
462
|
+
crypto helpers can be repointed at a mirror via `configure({ esm })` to satisfy
|
|
463
|
+
a strict CSP).
|
|
464
|
+
|
|
465
|
+
## FAQ & troubleshooting
|
|
466
|
+
|
|
467
|
+
**The modal opens but says "Couldn't confirm the price."**
|
|
468
|
+
Discovery hit a response that wasn't an x402 challenge. The endpoint must answer
|
|
469
|
+
the *unpaid* request with HTTP `402` (or an MCP-style `401`) carrying an `accepts`
|
|
470
|
+
array — in the JSON body or a base64 `payment-required` header. A `200` means the
|
|
471
|
+
endpoint isn't paywalled; the modal surfaces that instead of silently succeeding.
|
|
472
|
+
|
|
473
|
+
**Do I need a server?**
|
|
474
|
+
Only for **Solana**. EVM (Base/Arbitrum/Optimism USDC) signs EIP-3009 typed-data
|
|
475
|
+
entirely in the browser. For Solana, mount `./server` so the modal can build the
|
|
476
|
+
SPL transfer Phantom signs — see [docs/server-setup.md](docs/server-setup.md).
|
|
477
|
+
|
|
478
|
+
**Solana payments fail with an RPC error under load.**
|
|
479
|
+
You're on the public RPC (it warns once at startup). Pass a dedicated endpoint —
|
|
480
|
+
`x402CheckoutRouter({ rpcUrls: ['https://your-helius-url', ...] })` — and the
|
|
481
|
+
adapter rotates across the list on transient failures.
|
|
482
|
+
|
|
483
|
+
**A Token-2022 mint (e.g. THREE) wouldn't settle in an older version.**
|
|
484
|
+
Fixed in 1.2.0 — the server now detects each mint's owning program (legacy SPL
|
|
485
|
+
Token vs Token-2022) and derives ATAs and `transferChecked` against the right one.
|
|
486
|
+
|
|
487
|
+
**`esm.sh` is blocked by my Content-Security-Policy.**
|
|
488
|
+
The client lazy-loads `@solana/web3.js` and `@noble/hashes` from a CDN only when a
|
|
489
|
+
payment is attempted. Repoint them at a self-hosted mirror:
|
|
490
|
+
`configure({ esm: { solanaWeb3: '/vendor/solana-web3.js', nobleHashesSha3: '/vendor/noble-sha3.js' } })`.
|
|
491
|
+
EVM-only sites load no third-party code at all.
|
|
492
|
+
|
|
493
|
+
**Why did `pay()` reject?**
|
|
494
|
+
A user dismissing the modal rejects with `err.code === 'cancelled'` — treat it as
|
|
495
|
+
a no-op, not a failure. Any other rejection is a real error. The declarative
|
|
496
|
+
auto-binder swallows cancellation and only fires `x402:error` for genuine failures.
|
|
497
|
+
|
|
498
|
+
**My spending caps don't apply to a non-stablecoin.**
|
|
499
|
+
Browser caps are denominated in micro-USD and the dependency-free client doesn't
|
|
500
|
+
fetch live prices, so caps only bind stablecoins (USDC/USDT/DAI). Enforce caps for
|
|
501
|
+
floating-price tokens server-side — see [docs/spending-caps.md](docs/spending-caps.md).
|
|
502
|
+
|
|
503
|
+
**Can the user pick which token to pay in?**
|
|
504
|
+
Yes — if the 402 challenge lists more than one Solana `accept`, the modal renders
|
|
505
|
+
a token picker. See [Accepting multiple Solana tokens](#accepting-multiple-solana-tokens).
|
|
506
|
+
|
|
507
|
+
---
|
|
508
|
+
|
|
509
|
+
## Related packages
|
|
510
|
+
|
|
511
|
+
- **[`@three-ws/x402-modal`](https://www.npmjs.com/package/@three-ws/x402-modal)** —
|
|
512
|
+
the lighter client-only sibling. See [the comparison above](#which-x402-modal-do-i-want).
|
|
513
|
+
- **[x402 protocol](https://x402.org)** — the open "HTTP 402 Payment Required"
|
|
514
|
+
micropayment standard this package implements (v2 envelope).
|
|
340
515
|
|
|
341
516
|
## License
|
|
342
517
|
|
|
343
|
-
[
|
|
518
|
+
Proprietary — Copyright (c) 2026 nirholas. All Rights Reserved. Unauthorized use, copying, modification, or distribution is prohibited. See [LICENSE](./LICENSE).
|
|
344
519
|
|
|
345
|
-
>
|
|
346
|
-
>
|
|
347
|
-
> <https://github.com/nirholas/
|
|
520
|
+
> Standalone package. Browser client is dependency-free; server/react peer deps
|
|
521
|
+
> are optional. Issues and PRs welcome — see [CONTRIBUTING.md](CONTRIBUTING.md) and
|
|
522
|
+
> <https://github.com/nirholas/x402-payment-modal/issues>.
|
package/dist/index.d.ts
CHANGED
|
@@ -58,12 +58,17 @@ export interface PayResult {
|
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
/** Branding shown in the modal footer. */
|
|
61
|
+
/** Branding shown in the modal header/footer. */
|
|
62
62
|
export interface BrandConfig {
|
|
63
63
|
name?: string;
|
|
64
64
|
url?: string;
|
|
65
|
+
/** Logo shown in the modal header (URL). */
|
|
66
|
+
logo?: string;
|
|
65
67
|
}
|
|
66
68
|
|
|
69
|
+
/** Forced color scheme. `auto` (default) follows the OS preference. */
|
|
70
|
+
export type X402Theme = 'auto' | 'light' | 'dark';
|
|
71
|
+
|
|
67
72
|
/** ERC-8021 builder-code self-attribution echoed when the 402 challenge declares one. */
|
|
68
73
|
export interface BuilderCodeConfig {
|
|
69
74
|
wallet?: string;
|
|
@@ -84,6 +89,10 @@ export interface X402Config {
|
|
|
84
89
|
footerNote?: string;
|
|
85
90
|
builderCode?: BuilderCodeConfig;
|
|
86
91
|
esm?: EsmConfig;
|
|
92
|
+
/** Force the modal's color scheme. Default `auto` (follow the OS). */
|
|
93
|
+
theme?: X402Theme;
|
|
94
|
+
/** Flat map of `--x402-*` design tokens to brand-match at runtime, e.g. `{ '--x402-accent': '#ff5c00' }`. */
|
|
95
|
+
cssVars?: Record<string, string> | null;
|
|
87
96
|
}
|
|
88
97
|
|
|
89
98
|
/**
|
|
@@ -107,8 +116,10 @@ export const version: string;
|
|
|
107
116
|
|
|
108
117
|
/** Solana USDC mint (mainnet). */
|
|
109
118
|
export const USDC_MINT_SOLANA: string;
|
|
110
|
-
/**
|
|
111
|
-
* `accept` using it renders as THREE without merchant-supplied metadata.
|
|
119
|
+
/** THREE — an optional opt-in SPL token mint recognized by the modal so a 402
|
|
120
|
+
* `accept` using it renders as THREE without merchant-supplied metadata. USDC
|
|
121
|
+
* remains the always-on default; this token is used only when an endpoint
|
|
122
|
+
* chooses to accept it. */
|
|
112
123
|
export const THREE_MINT: string;
|
|
113
124
|
|
|
114
125
|
export interface KnownSolanaToken {
|