@oobe-protocol-labs/sap-mcp-server 0.2.1 → 0.7.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/.env.example +3 -0
- package/CHANGELOG.md +238 -1
- package/README.md +64 -1
- package/USER_DOCS/00_START_HERE.md +23 -1
- package/USER_DOCS/01_HOSTED_REMOTE_MCP.md +55 -0
- package/USER_DOCS/03_PAYMENTS_X402_PAYSH.md +63 -7
- package/USER_DOCS/04_CLIENT_CONFIGS.md +164 -11
- package/USER_DOCS/06_DESKTOP_GUI_WIZARD.md +262 -0
- package/dist/adapters/mcp/sdk-compat.d.ts.map +1 -1
- package/dist/adapters/mcp/sdk-compat.js +32 -18
- package/dist/adapters/mcp/sdk-compat.js.map +1 -1
- package/dist/config/mcp-client-injection.d.ts +72 -2
- package/dist/config/mcp-client-injection.d.ts.map +1 -1
- package/dist/config/mcp-client-injection.js +691 -15
- package/dist/config/mcp-client-injection.js.map +1 -1
- package/dist/config/setup.js +1 -1
- package/dist/config/setup.js.map +1 -1
- package/dist/config/wizard.d.ts.map +1 -1
- package/dist/config/wizard.js +164 -85
- package/dist/config/wizard.js.map +1 -1
- package/dist/core/constants.d.ts +1 -1
- package/dist/core/constants.js +1 -1
- package/dist/core/logger.js +1 -1
- package/dist/payments/facilitator-rpc-fallback.d.ts +43 -0
- package/dist/payments/facilitator-rpc-fallback.d.ts.map +1 -0
- package/dist/payments/facilitator-rpc-fallback.js +228 -0
- package/dist/payments/facilitator-rpc-fallback.js.map +1 -0
- package/dist/payments/json-rpc.d.ts.map +1 -1
- package/dist/payments/json-rpc.js +2 -1
- package/dist/payments/json-rpc.js.map +1 -1
- package/dist/payments/monetization-gate.d.ts +43 -2
- package/dist/payments/monetization-gate.d.ts.map +1 -1
- package/dist/payments/monetization-gate.js +618 -9
- package/dist/payments/monetization-gate.js.map +1 -1
- package/dist/payments/oobe-facilitator-server.d.ts +12 -1
- package/dist/payments/oobe-facilitator-server.d.ts.map +1 -1
- package/dist/payments/oobe-facilitator-server.js +109 -8
- package/dist/payments/oobe-facilitator-server.js.map +1 -1
- package/dist/payments/pay-sh-spec.js +1 -1
- package/dist/payments/pricing.d.ts.map +1 -1
- package/dist/payments/pricing.js +1 -0
- package/dist/payments/pricing.js.map +1 -1
- package/dist/payments/usage-ledger.d.ts +10 -0
- package/dist/payments/usage-ledger.d.ts.map +1 -1
- package/dist/payments/usage-ledger.js +51 -0
- package/dist/payments/usage-ledger.js.map +1 -1
- package/dist/payments/x402-paid-call.d.ts +53 -0
- package/dist/payments/x402-paid-call.d.ts.map +1 -0
- package/dist/payments/x402-paid-call.js +370 -0
- package/dist/payments/x402-paid-call.js.map +1 -0
- package/dist/prompts/context/sap-agent-context.prompt.js +33 -6
- package/dist/prompts/context/sap-agent-context.prompt.js.map +1 -1
- package/dist/prompts/developer/debug-sap-error.prompt.js +1 -1
- package/dist/prompts/developer/debug-sap-error.prompt.js.map +1 -1
- package/dist/prompts/developer/generate-sap-integration.prompt.js +6 -2
- package/dist/prompts/developer/generate-sap-integration.prompt.js.map +1 -1
- package/dist/prompts/execution-proof/explain-proof-of-execution.prompt.js +1 -1
- package/dist/prompts/execution-proof/explain-proof-of-execution.prompt.js.map +1 -1
- package/dist/prompts/execution-proof/verify-execution-proof.prompt.js +1 -1
- package/dist/prompts/execution-proof/verify-execution-proof.prompt.js.map +1 -1
- package/dist/prompts/payments/create-paid-api.prompt.js +1 -1
- package/dist/prompts/payments/create-paid-api.prompt.js.map +1 -1
- package/dist/prompts/payments/explain-x402-settlement.prompt.js +1 -1
- package/dist/prompts/payments/explain-x402-settlement.prompt.js.map +1 -1
- package/dist/prompts/register-prompts.js +2 -2
- package/dist/prompts/register-prompts.js.map +1 -1
- package/dist/prompts/registry/analyze-sap-agent.prompt.js +1 -1
- package/dist/prompts/registry/analyze-sap-agent.prompt.js.map +1 -1
- package/dist/prompts/registry/register-sap-agent.prompt.js +2 -2
- package/dist/prompts/registry/register-sap-agent.prompt.js.map +1 -1
- package/dist/remote/server.d.ts +150 -1
- package/dist/remote/server.d.ts.map +1 -1
- package/dist/remote/server.js +1176 -84
- package/dist/remote/server.js.map +1 -1
- package/dist/resources/current/sap-current-config.resource.d.ts.map +1 -1
- package/dist/resources/current/sap-current-config.resource.js +22 -2
- package/dist/resources/current/sap-current-config.resource.js.map +1 -1
- package/dist/resources/execution-proof/sap-execution-record.resource.js +1 -1
- package/dist/resources/execution-proof/sap-execution-record.resource.js.map +1 -1
- package/dist/resources/memory/sap-memory.resource.js +1 -1
- package/dist/resources/memory/sap-memory.resource.js.map +1 -1
- package/dist/resources/profile/sap-active-profile.resource.js +1 -1
- package/dist/resources/profile/sap-active-profile.resource.js.map +1 -1
- package/dist/resources/register-resources.js +2 -2
- package/dist/resources/register-resources.js.map +1 -1
- package/dist/resources/registry/sap-agent.resource.js +1 -1
- package/dist/resources/registry/sap-agent.resource.js.map +1 -1
- package/dist/resources/registry/sap-global-registry.resource.js +1 -1
- package/dist/resources/registry/sap-global-registry.resource.js.map +1 -1
- package/dist/resources/reputation/sap-reputation.resource.js +1 -1
- package/dist/resources/reputation/sap-reputation.resource.js.map +1 -1
- package/dist/resources/stats/sap-network-stats.resource.js +1 -1
- package/dist/resources/stats/sap-network-stats.resource.js.map +1 -1
- package/dist/resources/tool-schema/sap-tool-schema.resource.js +1 -1
- package/dist/resources/tool-schema/sap-tool-schema.resource.js.map +1 -1
- package/dist/sap/sap-client-manager.js +6 -6
- package/dist/sap/sap-client-manager.js.map +1 -1
- package/dist/security/tool-permissions.d.ts.map +1 -1
- package/dist/security/tool-permissions.js +1 -0
- package/dist/security/tool-permissions.js.map +1 -1
- package/dist/server/create-server.js +5 -5
- package/dist/server/create-server.js.map +1 -1
- package/dist/server/register-capabilities.js +4 -4
- package/dist/server/register-capabilities.js.map +1 -1
- package/dist/server/server-metadata.js +1 -1
- package/dist/signer/signer-resolver.js +12 -5
- package/dist/signer/signer-resolver.js.map +1 -1
- package/dist/tools/client-sdk-tools.js +7 -7
- package/dist/tools/client-sdk-tools.js.map +1 -1
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +4 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/magicblock-tools.d.ts +28 -0
- package/dist/tools/magicblock-tools.d.ts.map +1 -0
- package/dist/tools/magicblock-tools.js +578 -0
- package/dist/tools/magicblock-tools.js.map +1 -0
- package/dist/tools/profile-tools.d.ts.map +1 -1
- package/dist/tools/profile-tools.js +44 -0
- package/dist/tools/profile-tools.js.map +1 -1
- package/dist/tools/register-tools.d.ts.map +1 -1
- package/dist/tools/register-tools.js +11 -2
- package/dist/tools/register-tools.js.map +1 -1
- package/dist/tools/sap-sdk-tools.js +2 -2
- package/dist/tools/sap-sdk-tools.js.map +1 -1
- package/dist/tools/sap-sns-tools.js +2 -2
- package/dist/tools/sap-sns-tools.js.map +1 -1
- package/dist/tools/skills-tools.d.ts +1 -1
- package/dist/tools/skills-tools.d.ts.map +1 -1
- package/dist/tools/skills-tools.js +10 -1
- package/dist/tools/skills-tools.js.map +1 -1
- package/dist/tools/tool-aliases.d.ts +10 -0
- package/dist/tools/tool-aliases.d.ts.map +1 -0
- package/dist/tools/tool-aliases.js +23 -0
- package/dist/tools/tool-aliases.js.map +1 -0
- package/dist/tools/x402-paid-call-tool.d.ts +12 -0
- package/dist/tools/x402-paid-call-tool.d.ts.map +1 -0
- package/dist/tools/x402-paid-call-tool.js +118 -0
- package/dist/tools/x402-paid-call-tool.js.map +1 -0
- package/dist/tui/config-wizard.js +2 -2
- package/dist/tui/config-wizard.js.map +1 -1
- package/dist/wizard-core/desktop-flow.d.ts +82 -0
- package/dist/wizard-core/desktop-flow.d.ts.map +1 -0
- package/dist/wizard-core/desktop-flow.js +181 -0
- package/dist/wizard-core/desktop-flow.js.map +1 -0
- package/docs/00_README.md +3 -0
- package/docs/03_CONFIGURATION_AND_WIZARD.md +31 -5
- package/docs/06_PAYMENTS_X402_AND_PAYSH.md +70 -7
- package/docs/07_ENDPOINTS_AND_CLIENTS.md +56 -1
- package/docs/10_OPERATIONS_RELEASE_AND_PM2.md +7 -6
- package/docs/13_BOUNTY_PROGRAM_PROPOSAL.md +469 -0
- package/docs/14_DESKTOP_WIZARD_RELEASE.md +264 -0
- package/docs/README.md +104 -0
- package/docs/_sidebar.md +31 -0
- package/docs/magicblock-tools.md +166 -0
- package/docs/x402-protocol-spec.md +984 -0
- package/package.json +23 -2
- package/server.json +71 -0
- package/skills/README.md +3 -2
- package/skills/sap-defi/SKILL.md +8 -0
- package/skills/sap-mcp/SKILL.md +124 -2
- package/skills/sap-payments-x402/SKILL.md +54 -2
|
@@ -0,0 +1,984 @@
|
|
|
1
|
+
# x402 Payment Protocol — Complete Specification
|
|
2
|
+
|
|
3
|
+
**Source**: Extracted from `@x402/core@2.17.0` and `@x402/svm@2.17.0` TypeScript definitions, implementation source, and READMEs.
|
|
4
|
+
**Protocol Version**: V2 (current), V1 (legacy, still supported)
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 1. Protocol Overview
|
|
9
|
+
|
|
10
|
+
x402 is an HTTP-based payment protocol that monetizes API endpoints using the HTTP 402 "Payment Required" status code. The flow involves three actors:
|
|
11
|
+
|
|
12
|
+
- **Resource Server** — the API that requires payment
|
|
13
|
+
- **Client** — the caller that wants to access the resource
|
|
14
|
+
- **Facilitator** — a third-party service that verifies and settles payments on-chain
|
|
15
|
+
|
|
16
|
+
### High-Level Flow
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
Client Server Facilitator
|
|
20
|
+
| | |
|
|
21
|
+
|--- GET /protected-resource ------->| |
|
|
22
|
+
| | |
|
|
23
|
+
|<-- 402 Payment Required -----------| |
|
|
24
|
+
| (PAYMENT-REQUIRED header) | |
|
|
25
|
+
| | |
|
|
26
|
+
| [client constructs payment] | |
|
|
27
|
+
| | |
|
|
28
|
+
|--- GET /protected-resource -------->| |
|
|
29
|
+
| PAYMENT-SIGNATURE: <base64> | |
|
|
30
|
+
| |--- POST /verify ---------->|
|
|
31
|
+
| | {paymentPayload, reqs} |
|
|
32
|
+
| |<-- {isValid: true} ---------|
|
|
33
|
+
| | |
|
|
34
|
+
| [server executes handler] | |
|
|
35
|
+
| |--- POST /settle ---------->|
|
|
36
|
+
| | {paymentPayload, reqs} |
|
|
37
|
+
| |<-- {success: true, tx} ----|
|
|
38
|
+
| | |
|
|
39
|
+
|<-- 200 OK -------------------------| |
|
|
40
|
+
| PAYMENT-RESPONSE: <base64> | |
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 2. HTTP Headers
|
|
46
|
+
|
|
47
|
+
### V2 Protocol (Current — version 2)
|
|
48
|
+
|
|
49
|
+
| Header | Direction | Encoding | Content |
|
|
50
|
+
|---|---|---|---|
|
|
51
|
+
| `PAYMENT-REQUIRED` | Server → Client (on 402) | Base64-encoded JSON | `PaymentRequired` object |
|
|
52
|
+
| `PAYMENT-SIGNATURE` | Client → Server (on retry) | Base64-encoded JSON | `PaymentPayload` object |
|
|
53
|
+
| `PAYMENT-RESPONSE` | Server → Client (on 200) | Base64-encoded JSON | `SettleResponse` object |
|
|
54
|
+
| `Settlement-Overrides` | Server internal | JSON string | Optional partial settlement amount |
|
|
55
|
+
|
|
56
|
+
### V1 Protocol (Legacy — version 1)
|
|
57
|
+
|
|
58
|
+
| Header | Direction | Encoding | Content |
|
|
59
|
+
|---|---|---|---|
|
|
60
|
+
| `X-PAYMENT` | Client → Server (on retry) | Base64-encoded JSON | `PaymentPayloadV1` object |
|
|
61
|
+
| `X-PAYMENT-RESPONSE` | Server → Client (on 200) | Base64-encoded JSON | `SettleResponseV1` object |
|
|
62
|
+
|
|
63
|
+
**Note**: In V1, the `PaymentRequired` object was placed in the **response body** (JSON), not in a header. In V2, it moved to the `PAYMENT-REQUIRED` header.
|
|
64
|
+
|
|
65
|
+
### Encoding Implementation
|
|
66
|
+
|
|
67
|
+
All headers are encoded as:
|
|
68
|
+
```typescript
|
|
69
|
+
// Encode: JSON stringify → Base64
|
|
70
|
+
function encodePaymentSignatureHeader(paymentPayload: PaymentPayload): string {
|
|
71
|
+
return safeBase64Encode(JSON.stringify(paymentPayload));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Decode: Base64 → JSON parse
|
|
75
|
+
function decodePaymentSignatureHeader(header: string): PaymentPayload {
|
|
76
|
+
if (!Base64EncodedRegex.test(header)) throw new Error("Invalid payment signature header");
|
|
77
|
+
return JSON.parse(safeBase64Decode(header));
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The same pattern applies to `encodePaymentRequiredHeader` / `decodePaymentRequiredHeader` and `encodePaymentResponseHeader` / `decodePaymentResponseHeader`.
|
|
82
|
+
|
|
83
|
+
### Server-Side Header Extraction
|
|
84
|
+
|
|
85
|
+
The server checks for the payment header in a case-insensitive manner:
|
|
86
|
+
```typescript
|
|
87
|
+
extractPayment(adapter) {
|
|
88
|
+
const header = adapter.getHeader("payment-signature")
|
|
89
|
+
|| adapter.getHeader("PAYMENT-SIGNATURE");
|
|
90
|
+
if (header) {
|
|
91
|
+
return decodePaymentSignatureHeader(header);
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## 3. The 402 Response Body
|
|
100
|
+
|
|
101
|
+
### Status Code
|
|
102
|
+
- **402** — Payment Required (standard case)
|
|
103
|
+
- **412** — Precondition Failed (special case: `error === "permit2_allowance_required"`)
|
|
104
|
+
|
|
105
|
+
### For Browser Requests (Accept: text/html, User-Agent contains "Mozilla")
|
|
106
|
+
The server returns an HTML paywall page instead of JSON. If `@x402/paywall` is installed, it generates a wallet-connection UI. Otherwise, a fallback HTML page is returned.
|
|
107
|
+
|
|
108
|
+
### For API Clients
|
|
109
|
+
- **Content-Type**: `application/json` (or custom via `unpaidResponseBody` callback)
|
|
110
|
+
- **Body**: Empty object `{}` by default, or custom preview data if `unpaidResponseBody` is configured
|
|
111
|
+
- **Headers**: `PAYMENT-REQUIRED: <base64>` always present
|
|
112
|
+
|
|
113
|
+
### The `PaymentRequired` object (V2):
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
type PaymentRequired = {
|
|
117
|
+
x402Version: 2;
|
|
118
|
+
error?: string; // Error message (e.g., "Payment required", "No matching payment requirements")
|
|
119
|
+
resource: ResourceInfo; // What's being paid for
|
|
120
|
+
accepts: PaymentRequirements[]; // Array of acceptable payment options
|
|
121
|
+
extensions?: Record<string, unknown>; // Server-declared extensions
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
type ResourceInfo = {
|
|
125
|
+
url: string;
|
|
126
|
+
description?: string;
|
|
127
|
+
mimeType?: string;
|
|
128
|
+
serviceName?: string;
|
|
129
|
+
tags?: string[];
|
|
130
|
+
iconUrl?: string;
|
|
131
|
+
};
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### The `PaymentRequired` object (V1):
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
type PaymentRequiredV1 = {
|
|
138
|
+
x402Version: 1;
|
|
139
|
+
error?: string;
|
|
140
|
+
accepts: PaymentRequirementsV1[]; // No top-level `resource` field
|
|
141
|
+
};
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## 4. Payment Requirements Schema
|
|
147
|
+
|
|
148
|
+
### V2 PaymentRequirements (current)
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
type PaymentRequirements = {
|
|
152
|
+
scheme: string; // Payment scheme, e.g., "exact"
|
|
153
|
+
network: Network; // CAIP-2 format, e.g., "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp", "eip155:8453"
|
|
154
|
+
amount: string; // Amount in atomic units (smallest token unit, e.g., lamports/wei)
|
|
155
|
+
asset: string; // Token identifier (mint address, contract address, etc.)
|
|
156
|
+
payTo: string; // Recipient address
|
|
157
|
+
maxTimeoutSeconds: number; // Payment validity window
|
|
158
|
+
extra: Record<string, unknown>; // Scheme-specific metadata (e.g., feePayer for SVM)
|
|
159
|
+
};
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Network format (V2)**: CAIP-2 compliant — `${namespace}:${reference}`, minimum length 3, must contain a colon.
|
|
163
|
+
- Solana mainnet: `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp`
|
|
164
|
+
- Solana devnet: `solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1`
|
|
165
|
+
- Base: `eip155:8453`
|
|
166
|
+
|
|
167
|
+
### V1 PaymentRequirements (legacy)
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
type PaymentRequirementsV1 = {
|
|
171
|
+
scheme: string;
|
|
172
|
+
network: string; // Simple name, e.g., "solana", "solana-devnet" (no CAIP-2)
|
|
173
|
+
maxAmountRequired: string; // V1 uses "maxAmountRequired" instead of "amount"
|
|
174
|
+
resource: string; // V1 includes resource URL directly in requirements
|
|
175
|
+
description: string; // V1 includes description in requirements (not in top-level response)
|
|
176
|
+
mimeType?: string; // V1 includes mimeType in requirements
|
|
177
|
+
outputSchema?: Record<string, unknown>;
|
|
178
|
+
payTo: string;
|
|
179
|
+
maxTimeoutSeconds: number;
|
|
180
|
+
asset: string;
|
|
181
|
+
extra: Record<string, unknown>;
|
|
182
|
+
};
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### V1 vs V2 Key Differences
|
|
186
|
+
|
|
187
|
+
| Field | V1 | V2 |
|
|
188
|
+
|---|---|---|
|
|
189
|
+
| Version marker | `x402Version: 1` | `x402Version: 2` |
|
|
190
|
+
| Amount field | `maxAmountRequired` | `amount` |
|
|
191
|
+
| Network format | Simple names (`solana`, `solana-devnet`) | CAIP-2 (`solana:5eykt4Us...`) |
|
|
192
|
+
| Network validation | Loose (any non-empty string) | Strict (min length 3, must contain `:`) |
|
|
193
|
+
| Resource info | Embedded in each `PaymentRequirements` | Top-level `resource: ResourceInfo` on `PaymentRequired` |
|
|
194
|
+
| `description` | Required field on each requirement | Optional, on top-level `ResourceInfo` |
|
|
195
|
+
| `mimeType` | Optional field on each requirement | Optional, on top-level `ResourceInfo` |
|
|
196
|
+
| `outputSchema` | Present | Removed |
|
|
197
|
+
| Payment header | `X-PAYMENT` | `PAYMENT-SIGNATURE` |
|
|
198
|
+
| Settlement header | `X-PAYMENT-RESPONSE` | `PAYMENT-RESPONSE` |
|
|
199
|
+
| 402 body | `PaymentRequiredV1` JSON in body | `PAYMENT-REQUIRED` header (body is empty/custom) |
|
|
200
|
+
| Extensions | Limited | Full support (`extensions` on `PaymentRequired` and `PaymentPayload`) |
|
|
201
|
+
|
|
202
|
+
### V1→V2 Network Mapping (SVM)
|
|
203
|
+
|
|
204
|
+
```
|
|
205
|
+
solana → solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp
|
|
206
|
+
solana-devnet → solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1
|
|
207
|
+
solana-testnet → solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## 5. Payment Payload Schema
|
|
213
|
+
|
|
214
|
+
### V2 PaymentPayload (what the client sends in `PAYMENT-SIGNATURE`)
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
type PaymentPayload = {
|
|
218
|
+
x402Version: 2;
|
|
219
|
+
resource?: ResourceInfo; // Echo of what's being paid for (optional)
|
|
220
|
+
accepted: PaymentRequirements; // The specific requirements the client chose to satisfy
|
|
221
|
+
payload: Record<string, unknown>; // Scheme-specific payment data
|
|
222
|
+
extensions?: Record<string, unknown>; // Extension echoes/additions
|
|
223
|
+
};
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**For SVM (Solana)**, the `payload` field contains:
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
type ExactSvmPayloadV2 = {
|
|
230
|
+
// (same as V1 currently, reserved for future extensions)
|
|
231
|
+
transaction: string; // Base64-encoded Solana transaction
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
type ExactSvmPayloadV1 = {
|
|
235
|
+
transaction: string; // Base64-encoded Solana transaction
|
|
236
|
+
};
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
The Solana transaction contains:
|
|
240
|
+
- Compute budget instructions (unit limit + priority fee)
|
|
241
|
+
- SPL Token `TransferChecked` instruction (source ATA → destination ATA)
|
|
242
|
+
- Memo instruction (optional)
|
|
243
|
+
- Partial signing by the client; the facilitator adds its signature as fee payer
|
|
244
|
+
|
|
245
|
+
### V1 PaymentPayload
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
type PaymentPayloadV1 = {
|
|
249
|
+
x402Version: 1;
|
|
250
|
+
scheme: string; // e.g., "exact"
|
|
251
|
+
network: Network; // e.g., "solana-devnet"
|
|
252
|
+
payload: Record<string, unknown>; // Scheme-specific payment data
|
|
253
|
+
};
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Key difference: V1 has `scheme` and `network` as top-level fields; V2 has `accepted: PaymentRequirements` (which contains scheme/network/amount/etc).
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## 6. Facilitator Endpoints
|
|
261
|
+
|
|
262
|
+
### Base URL
|
|
263
|
+
Default: `https://x402.org/facilitator`
|
|
264
|
+
Configurable via `HTTPFacilitatorClient({ url: "..." })`
|
|
265
|
+
|
|
266
|
+
### Authentication
|
|
267
|
+
Optional via `createAuthHeaders` callback returning per-path headers:
|
|
268
|
+
```typescript
|
|
269
|
+
{
|
|
270
|
+
verify: Record<string, string>,
|
|
271
|
+
settle: Record<string, string>,
|
|
272
|
+
supported: Record<string, string>,
|
|
273
|
+
bazaar?: Record<string, string>
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
### POST `/verify`
|
|
280
|
+
|
|
281
|
+
**Request:**
|
|
282
|
+
```http
|
|
283
|
+
POST /verify HTTP/1.1
|
|
284
|
+
Content-Type: application/json
|
|
285
|
+
|
|
286
|
+
{
|
|
287
|
+
"x402Version": 2,
|
|
288
|
+
"paymentPayload": { /* PaymentPayload */ },
|
|
289
|
+
"paymentRequirements": { /* PaymentRequirements */ }
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
**Request body type (VerifyRequest):**
|
|
294
|
+
```typescript
|
|
295
|
+
type VerifyRequest = {
|
|
296
|
+
x402Version: number;
|
|
297
|
+
paymentPayload: PaymentPayload;
|
|
298
|
+
paymentRequirements: PaymentRequirements;
|
|
299
|
+
};
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
**Response (200 OK):**
|
|
303
|
+
```json
|
|
304
|
+
{
|
|
305
|
+
"isValid": true,
|
|
306
|
+
"payer": "optional-payer-address"
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
**Response (verify failed — still 200, isValid: false):**
|
|
311
|
+
```json
|
|
312
|
+
{
|
|
313
|
+
"isValid": false,
|
|
314
|
+
"invalidReason": "amount_mismatch",
|
|
315
|
+
"invalidMessage": "Payment amount does not match requirements"
|
|
316
|
+
}
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
**VerifyResponse schema:**
|
|
320
|
+
```typescript
|
|
321
|
+
type VerifyResponse = {
|
|
322
|
+
isValid: boolean;
|
|
323
|
+
invalidReason?: string;
|
|
324
|
+
invalidMessage?: string;
|
|
325
|
+
payer?: string;
|
|
326
|
+
extensions?: Record<string, unknown>;
|
|
327
|
+
extra?: Record<string, unknown>;
|
|
328
|
+
};
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
**Error handling**: If the facilitator returns non-200, and the body contains `isValid`, it's thrown as `VerifyError(statusCode, response)`. Otherwise thrown as generic Error.
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
### POST `/settle`
|
|
336
|
+
|
|
337
|
+
**Request:**
|
|
338
|
+
```http
|
|
339
|
+
POST /settle HTTP/1.1
|
|
340
|
+
Content-Type: application/json
|
|
341
|
+
|
|
342
|
+
{
|
|
343
|
+
"x402Version": 2,
|
|
344
|
+
"paymentPayload": { /* PaymentPayload */ },
|
|
345
|
+
"paymentRequirements": { /* PaymentRequirements */ }
|
|
346
|
+
}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
**Request body type (SettleRequest):**
|
|
350
|
+
```typescript
|
|
351
|
+
type SettleRequest = {
|
|
352
|
+
x402Version: number;
|
|
353
|
+
paymentPayload: PaymentPayload;
|
|
354
|
+
paymentRequirements: PaymentRequirements;
|
|
355
|
+
};
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
**Response (200 OK):**
|
|
359
|
+
```json
|
|
360
|
+
{
|
|
361
|
+
"success": true,
|
|
362
|
+
"transaction": "5xK...signature...",
|
|
363
|
+
"network": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
|
|
364
|
+
"payer": "optional-payer-address",
|
|
365
|
+
"amount": "1000000"
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
**SettleResponse schema:**
|
|
370
|
+
```typescript
|
|
371
|
+
type SettleResponse = {
|
|
372
|
+
success: boolean;
|
|
373
|
+
errorReason?: string;
|
|
374
|
+
errorMessage?: string;
|
|
375
|
+
payer?: string;
|
|
376
|
+
transaction: string; // On-chain transaction signature/hash
|
|
377
|
+
network: Network;
|
|
378
|
+
amount?: string; // Actual settled amount (for partial/upto schemes)
|
|
379
|
+
extensions?: Record<string, unknown>;
|
|
380
|
+
extra?: Record<string, unknown>;
|
|
381
|
+
};
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
**Error handling**: If facilitator returns non-200 with `success` in body, thrown as `SettleError(statusCode, response)`.
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
### GET `/supported`
|
|
389
|
+
|
|
390
|
+
**Request:**
|
|
391
|
+
```http
|
|
392
|
+
GET /supported HTTP/1.1
|
|
393
|
+
Content-Type: application/json
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
**Response:**
|
|
397
|
+
```json
|
|
398
|
+
{
|
|
399
|
+
"kinds": [
|
|
400
|
+
{
|
|
401
|
+
"x402Version": 2,
|
|
402
|
+
"scheme": "exact",
|
|
403
|
+
"network": "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
|
|
404
|
+
"extra": { "feePayer": "FacilitatorWalletAddress..." }
|
|
405
|
+
}
|
|
406
|
+
],
|
|
407
|
+
"extensions": ["bazaar"],
|
|
408
|
+
"signers": {
|
|
409
|
+
"solana:*": ["FacilitatorWallet1...", "FacilitatorWallet2..."]
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
**SupportedResponse schema:**
|
|
415
|
+
```typescript
|
|
416
|
+
type SupportedResponse = {
|
|
417
|
+
kinds: SupportedKind[];
|
|
418
|
+
extensions: string[];
|
|
419
|
+
signers: Record<string, string[]>;
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
type SupportedKind = {
|
|
423
|
+
x402Version: number;
|
|
424
|
+
scheme: string;
|
|
425
|
+
network: Network;
|
|
426
|
+
extra?: Record<string, unknown>;
|
|
427
|
+
};
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
**Retry**: Retries up to 3 times with exponential backoff on HTTP 429, respecting `Retry-After` header.
|
|
431
|
+
|
|
432
|
+
---
|
|
433
|
+
|
|
434
|
+
## 7. Server-Side Payment Processing Flow
|
|
435
|
+
|
|
436
|
+
### Step 1: Request arrives at protected route
|
|
437
|
+
|
|
438
|
+
```
|
|
439
|
+
processHTTPRequest(context) →
|
|
440
|
+
1. Match route against compiled routes (method + path regex)
|
|
441
|
+
2. If no route match → "no-payment-required" (pass through)
|
|
442
|
+
3. Run protected request hooks (can grant access or abort)
|
|
443
|
+
4. Build payment requirements from route config
|
|
444
|
+
5. Enrich extensions if declared
|
|
445
|
+
6. Create PaymentRequired response
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
### Step 2: Check for payment header
|
|
449
|
+
|
|
450
|
+
```
|
|
451
|
+
7. Extract payment from PAYMENT-SIGNATURE header
|
|
452
|
+
8. If no payment header:
|
|
453
|
+
→ Return 402 with PAYMENT-REQUIRED header
|
|
454
|
+
(HTML paywall for browsers, JSON for API clients)
|
|
455
|
+
9. If payment header present:
|
|
456
|
+
→ Find matching requirements from paymentPayload
|
|
457
|
+
→ Validate extension echoes
|
|
458
|
+
→ Call facilitator /verify
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
### Step 3: Verify payment
|
|
462
|
+
|
|
463
|
+
```
|
|
464
|
+
10. verifyPayment(payload, matchingRequirements, extensions, transportContext)
|
|
465
|
+
11. If verify fails → Return 402 with error in PAYMENT-REQUIRED
|
|
466
|
+
12. If verify succeeds → Return "payment-verified" to middleware
|
|
467
|
+
13. Middleware executes route handler
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
### Step 4: Settle after handler completes
|
|
471
|
+
|
|
472
|
+
```
|
|
473
|
+
14. processSettlement(payload, requirements, extensions, transportContext, overrides)
|
|
474
|
+
15. Check for Settlement-Overrides header in response headers
|
|
475
|
+
16. Call facilitator /settle
|
|
476
|
+
17. If settle succeeds:
|
|
477
|
+
→ Add PAYMENT-RESPONSE header to response
|
|
478
|
+
→ Return 200 with actual response body
|
|
479
|
+
18. If settle fails:
|
|
480
|
+
→ Return 402 with settle error in PAYMENT-REQUIRED header
|
|
481
|
+
→ Body: settlement failure response (or custom via settlementFailedResponseBody)
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
---
|
|
485
|
+
|
|
486
|
+
## 8. Client-Side Payment Flow
|
|
487
|
+
|
|
488
|
+
### Step 1: Make initial request
|
|
489
|
+
|
|
490
|
+
```typescript
|
|
491
|
+
const response = await fetch('https://api.example.com/protected');
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
### Step 2: Handle 402 response
|
|
495
|
+
|
|
496
|
+
```typescript
|
|
497
|
+
if (response.status === 402) {
|
|
498
|
+
// Extract PaymentRequired from PAYMENT-REQUIRED header (V2)
|
|
499
|
+
// or from response body (V1)
|
|
500
|
+
const paymentRequired = client.getPaymentRequiredResponse(
|
|
501
|
+
(name) => response.headers.get(name),
|
|
502
|
+
await response.json() // V1 body fallback
|
|
503
|
+
);
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
### Step 3: Create payment payload
|
|
507
|
+
|
|
508
|
+
```typescript
|
|
509
|
+
// Client selects matching requirements and creates payment
|
|
510
|
+
const paymentPayload = await client.createPaymentPayload(paymentRequired);
|
|
511
|
+
// For SVM: builds a Solana transaction with TransferChecked, signs it
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
### Step 4: Retry with payment
|
|
515
|
+
|
|
516
|
+
```typescript
|
|
517
|
+
const headers = client.encodePaymentSignatureHeader(paymentPayload);
|
|
518
|
+
// V2: { "PAYMENT-SIGNATURE": "<base64>" }
|
|
519
|
+
// V1: { "X-PAYMENT": "<base64>" }
|
|
520
|
+
|
|
521
|
+
const paidResponse = await fetch('https://api.example.com/protected', {
|
|
522
|
+
headers: headers
|
|
523
|
+
});
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
### Step 5: Extract settlement
|
|
527
|
+
|
|
528
|
+
```typescript
|
|
529
|
+
if (paidResponse.status === 200) {
|
|
530
|
+
const settlement = client.getPaymentSettleResponse(
|
|
531
|
+
(name) => paidResponse.headers.get(name)
|
|
532
|
+
);
|
|
533
|
+
// V2: decode PAYMENT-RESPONSE header
|
|
534
|
+
// V1: decode X-PAYMENT-RESPONSE header
|
|
535
|
+
console.log('Transaction:', settlement.transaction);
|
|
536
|
+
}
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
### Version-aware header encoding (client-side):
|
|
540
|
+
|
|
541
|
+
```typescript
|
|
542
|
+
encodePaymentSignatureHeader(paymentPayload) {
|
|
543
|
+
switch (paymentPayload.x402Version) {
|
|
544
|
+
case 2:
|
|
545
|
+
return { "PAYMENT-SIGNATURE": encodePaymentSignatureHeader(paymentPayload) };
|
|
546
|
+
case 1:
|
|
547
|
+
return { "X-PAYMENT": encodePaymentSignatureHeader(paymentPayload) };
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
---
|
|
553
|
+
|
|
554
|
+
## 9. SVM (Solana) Implementation Details
|
|
555
|
+
|
|
556
|
+
### `@x402/svm` Package
|
|
557
|
+
|
|
558
|
+
The SVM implementation uses the **"exact"** scheme — meaning the exact amount specified in `PaymentRequirements.amount` must be transferred via SPL Token `TransferChecked`.
|
|
559
|
+
|
|
560
|
+
### Network Identifiers
|
|
561
|
+
|
|
562
|
+
| V1 Name | V2 CAIP-2 |
|
|
563
|
+
|---|---|
|
|
564
|
+
| `solana` | `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` |
|
|
565
|
+
| `solana-devnet` | `solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1` |
|
|
566
|
+
| `solana-testnet` | `solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z` |
|
|
567
|
+
|
|
568
|
+
### USDC Token Mints
|
|
569
|
+
|
|
570
|
+
- Mainnet: `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v`
|
|
571
|
+
- Devnet/Testnet: `4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU`
|
|
572
|
+
|
|
573
|
+
### Transaction Structure
|
|
574
|
+
|
|
575
|
+
The Solana transaction in the payment payload contains:
|
|
576
|
+
|
|
577
|
+
1. **ComputeBudget** instructions:
|
|
578
|
+
- `SetComputeUnitLimit` (default: 20,000 units)
|
|
579
|
+
- `SetComputeUnitPrice` (default: 1 microlamport/CU, max: 5,000,000)
|
|
580
|
+
|
|
581
|
+
2. **SPL Token `TransferChecked`** instruction:
|
|
582
|
+
- Program: `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA` (Token) or `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb` (Token-2022)
|
|
583
|
+
- Source: client's ATA for the payment asset
|
|
584
|
+
- Destination: `payTo` address's ATA
|
|
585
|
+
- Amount: exact `PaymentRequirements.amount` in atomic units
|
|
586
|
+
- Decimals: from token mint
|
|
587
|
+
|
|
588
|
+
3. **Memo** instruction (optional, max 256 bytes):
|
|
589
|
+
- Program: `MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr`
|
|
590
|
+
|
|
591
|
+
4. **Partial signing**: Client signs with their keypair; the facilitator adds its signature as fee payer before submitting on-chain.
|
|
592
|
+
|
|
593
|
+
### Allowed Programs (Static Path)
|
|
594
|
+
|
|
595
|
+
Only these programs are allowed in the transaction (static verification path):
|
|
596
|
+
- `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA` (SPL Token)
|
|
597
|
+
- `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb` (Token-2022)
|
|
598
|
+
- `ComputeBudget111111111111111111111111111111`
|
|
599
|
+
- `MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr`
|
|
600
|
+
- `L2TExMFKdjpN9kozasaurPirfHy9P8sbXoAN1qA3S95` (Lighthouse — Phantom/Solflare wallet protection)
|
|
601
|
+
|
|
602
|
+
### Smart Wallet Verification (Simulation Path)
|
|
603
|
+
|
|
604
|
+
When `enableSmartWalletVerification: true` and static verification rejects:
|
|
605
|
+
1. Assert fee payer is isolated (not in any instruction accounts)
|
|
606
|
+
2. Validate compute budget limits
|
|
607
|
+
3. Simulate transaction with inner instructions
|
|
608
|
+
4. Extract `TransferChecked` from CPI trace
|
|
609
|
+
5. Verify the transfer matches requirements
|
|
610
|
+
|
|
611
|
+
Supported smart wallet programs: Squads Multisig v4, Squads Smart Account, Swig, SPL Governance, Metaplex Core.
|
|
612
|
+
|
|
613
|
+
### Duplicate Settlement Protection
|
|
614
|
+
|
|
615
|
+
A `SettlementCache` prevents the same transaction from being settled twice. Keyed by SHA-256 hash of transaction message bytes. TTL: 120 seconds. Returns `duplicate_settlement` error for concurrent retries.
|
|
616
|
+
|
|
617
|
+
### Facilitator Extra Data
|
|
618
|
+
|
|
619
|
+
The SVM facilitator's `getExtra()` returns:
|
|
620
|
+
```json
|
|
621
|
+
{ "feePayer": "<randomly-selected-facilitator-signer-address>" }
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
This is included in the `SupportedKind.extra` and propagated to `PaymentRequirements.extra.feePayer`, so the client knows which address to use as the transaction fee payer.
|
|
625
|
+
|
|
626
|
+
### Post-Settlement Verification
|
|
627
|
+
|
|
628
|
+
After on-chain confirmation:
|
|
629
|
+
1. Inspect confirmed transaction's inner instructions for `TransferChecked`
|
|
630
|
+
2. Fallback to balance-delta checking if RPC transaction index has lag
|
|
631
|
+
3. Closes TOCTOU gap where simulation could pass but on-chain execution differs
|
|
632
|
+
|
|
633
|
+
---
|
|
634
|
+
|
|
635
|
+
## 10. Settlement Overrides
|
|
636
|
+
|
|
637
|
+
The server can override the settlement amount via the `Settlement-Overrides` response header or `SettlementOverrides` parameter:
|
|
638
|
+
|
|
639
|
+
```typescript
|
|
640
|
+
type SettlementOverrides = {
|
|
641
|
+
amount?: string; // Raw atomic units, percentage ("50%"), or dollar price ("$0.05")
|
|
642
|
+
};
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
Amount resolution:
|
|
646
|
+
- **Raw**: `"1000"` → settle exactly 1000 atomic units
|
|
647
|
+
- **Percent**: `"50%"` → 50% of `PaymentRequirements.amount` (floored)
|
|
648
|
+
- **Dollar**: `"$0.05"` → convert to atomic units using asset decimals (default 6 for USDC)
|
|
649
|
+
|
|
650
|
+
Only valid for schemes that support partial settlement (e.g., `upto` scheme).
|
|
651
|
+
|
|
652
|
+
---
|
|
653
|
+
|
|
654
|
+
## 11. Extensions System
|
|
655
|
+
|
|
656
|
+
Extensions allow protocol-level features to be layered on top of the base payment flow.
|
|
657
|
+
|
|
658
|
+
### Server-declared Extensions
|
|
659
|
+
|
|
660
|
+
```typescript
|
|
661
|
+
// In RouteConfig:
|
|
662
|
+
extensions: {
|
|
663
|
+
"bazaar": { /* discovery metadata */ },
|
|
664
|
+
"gas-sponsor": { /* EIP-2612 gasless */ }
|
|
665
|
+
}
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
These appear in `PaymentRequired.extensions` and the client echoes them back in `PaymentPayload.extensions`.
|
|
669
|
+
|
|
670
|
+
### Extension Validation
|
|
671
|
+
|
|
672
|
+
The server validates that client-echoed extension info preserves server-advertised values:
|
|
673
|
+
- Client may omit extensions entirely (passes)
|
|
674
|
+
- If present, must match server-advertised values for static fields
|
|
675
|
+
- Dynamic fields (nonces, timestamps) are excluded from echo validation
|
|
676
|
+
|
|
677
|
+
### Extension Hooks
|
|
678
|
+
|
|
679
|
+
Extensions can hook into the lifecycle:
|
|
680
|
+
- `enrichDeclaration` — transform declaration before processing
|
|
681
|
+
- `enrichPaymentRequiredResponse` — modify `accepts` (allowlisted: vacant `payTo`/`amount`/`asset` may be filled; `scheme`/`network`/`maxTimeoutSeconds` are immutable)
|
|
682
|
+
- `enrichSettlementResponse` — add to `settleResult.extensions` (facilitator fields are immutable)
|
|
683
|
+
- `onBeforeVerify`, `onAfterVerify`, `onVerifyFailure`
|
|
684
|
+
- `onBeforeSettle`, `onAfterSettle`, `onSettleFailure`
|
|
685
|
+
- `onProtectedRequest` (HTTP transport hook)
|
|
686
|
+
- `onPaymentRequired` (HTTP client transport hook)
|
|
687
|
+
|
|
688
|
+
---
|
|
689
|
+
|
|
690
|
+
## 12. Complete Type Reference
|
|
691
|
+
|
|
692
|
+
### Core Types (V2)
|
|
693
|
+
|
|
694
|
+
```typescript
|
|
695
|
+
type Network = `${string}:${string}`;
|
|
696
|
+
|
|
697
|
+
type PaymentRequirements = {
|
|
698
|
+
scheme: string;
|
|
699
|
+
network: Network;
|
|
700
|
+
asset: string;
|
|
701
|
+
amount: string; // atomic units
|
|
702
|
+
payTo: string;
|
|
703
|
+
maxTimeoutSeconds: number;
|
|
704
|
+
extra: Record<string, unknown>;
|
|
705
|
+
};
|
|
706
|
+
|
|
707
|
+
type PaymentRequired = {
|
|
708
|
+
x402Version: 2;
|
|
709
|
+
error?: string;
|
|
710
|
+
resource: ResourceInfo;
|
|
711
|
+
accepts: PaymentRequirements[];
|
|
712
|
+
extensions?: Record<string, unknown>;
|
|
713
|
+
};
|
|
714
|
+
|
|
715
|
+
type PaymentPayload = {
|
|
716
|
+
x402Version: 2;
|
|
717
|
+
resource?: ResourceInfo;
|
|
718
|
+
accepted: PaymentRequirements;
|
|
719
|
+
payload: Record<string, unknown>;
|
|
720
|
+
extensions?: Record<string, unknown>;
|
|
721
|
+
};
|
|
722
|
+
|
|
723
|
+
type VerifyRequest = {
|
|
724
|
+
x402Version: number;
|
|
725
|
+
paymentPayload: PaymentPayload;
|
|
726
|
+
paymentRequirements: PaymentRequirements;
|
|
727
|
+
};
|
|
728
|
+
|
|
729
|
+
type VerifyResponse = {
|
|
730
|
+
isValid: boolean;
|
|
731
|
+
invalidReason?: string;
|
|
732
|
+
invalidMessage?: string;
|
|
733
|
+
payer?: string;
|
|
734
|
+
extensions?: Record<string, unknown>;
|
|
735
|
+
extra?: Record<string, unknown>;
|
|
736
|
+
};
|
|
737
|
+
|
|
738
|
+
type SettleRequest = {
|
|
739
|
+
x402Version: number;
|
|
740
|
+
paymentPayload: PaymentPayload;
|
|
741
|
+
paymentRequirements: PaymentRequirements;
|
|
742
|
+
};
|
|
743
|
+
|
|
744
|
+
type SettleResponse = {
|
|
745
|
+
success: boolean;
|
|
746
|
+
errorReason?: string;
|
|
747
|
+
errorMessage?: string;
|
|
748
|
+
payer?: string;
|
|
749
|
+
transaction: string;
|
|
750
|
+
network: Network;
|
|
751
|
+
amount?: string;
|
|
752
|
+
extensions?: Record<string, unknown>;
|
|
753
|
+
extra?: Record<string, unknown>;
|
|
754
|
+
};
|
|
755
|
+
|
|
756
|
+
type SupportedKind = {
|
|
757
|
+
x402Version: number;
|
|
758
|
+
scheme: string;
|
|
759
|
+
network: Network;
|
|
760
|
+
extra?: Record<string, unknown>;
|
|
761
|
+
};
|
|
762
|
+
|
|
763
|
+
type SupportedResponse = {
|
|
764
|
+
kinds: SupportedKind[];
|
|
765
|
+
extensions: string[];
|
|
766
|
+
signers: Record<string, string[]>;
|
|
767
|
+
};
|
|
768
|
+
|
|
769
|
+
type ResourceInfo = {
|
|
770
|
+
url: string;
|
|
771
|
+
description?: string;
|
|
772
|
+
mimeType?: string;
|
|
773
|
+
serviceName?: string;
|
|
774
|
+
tags?: string[];
|
|
775
|
+
iconUrl?: string;
|
|
776
|
+
};
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
### Core Types (V1)
|
|
780
|
+
|
|
781
|
+
```typescript
|
|
782
|
+
type PaymentRequirementsV1 = {
|
|
783
|
+
scheme: string;
|
|
784
|
+
network: string; // simple name
|
|
785
|
+
maxAmountRequired: string; // V1 field name
|
|
786
|
+
resource: string;
|
|
787
|
+
description: string;
|
|
788
|
+
mimeType?: string;
|
|
789
|
+
outputSchema?: Record<string, unknown>;
|
|
790
|
+
payTo: string;
|
|
791
|
+
maxTimeoutSeconds: number;
|
|
792
|
+
asset: string;
|
|
793
|
+
extra: Record<string, unknown>;
|
|
794
|
+
};
|
|
795
|
+
|
|
796
|
+
type PaymentRequiredV1 = {
|
|
797
|
+
x402Version: 1;
|
|
798
|
+
error?: string;
|
|
799
|
+
accepts: PaymentRequirementsV1[];
|
|
800
|
+
// No top-level resource field
|
|
801
|
+
};
|
|
802
|
+
|
|
803
|
+
type PaymentPayloadV1 = {
|
|
804
|
+
x402Version: 1;
|
|
805
|
+
scheme: string;
|
|
806
|
+
network: string;
|
|
807
|
+
payload: Record<string, unknown>;
|
|
808
|
+
};
|
|
809
|
+
|
|
810
|
+
type VerifyRequestV1 = {
|
|
811
|
+
x402Version: number; // always 1
|
|
812
|
+
paymentPayload: PaymentPayloadV1;
|
|
813
|
+
paymentRequirements: PaymentRequirementsV1;
|
|
814
|
+
};
|
|
815
|
+
|
|
816
|
+
type SettleRequestV1 = {
|
|
817
|
+
x402Version: number;
|
|
818
|
+
paymentPayload: PaymentPayloadV1;
|
|
819
|
+
paymentRequirements: PaymentRequirementsV1;
|
|
820
|
+
};
|
|
821
|
+
|
|
822
|
+
type SettleResponseV1 = {
|
|
823
|
+
success: boolean;
|
|
824
|
+
errorReason?: string;
|
|
825
|
+
errorMessage?: string;
|
|
826
|
+
payer?: string;
|
|
827
|
+
transaction: string;
|
|
828
|
+
network: Network;
|
|
829
|
+
// No amount field in V1
|
|
830
|
+
};
|
|
831
|
+
```
|
|
832
|
+
|
|
833
|
+
---
|
|
834
|
+
|
|
835
|
+
## 13. Route Configuration (Server-Side)
|
|
836
|
+
|
|
837
|
+
```typescript
|
|
838
|
+
type RoutesConfig = Record<string, RouteConfig> | RouteConfig;
|
|
839
|
+
|
|
840
|
+
interface RouteConfig {
|
|
841
|
+
accepts: PaymentOption | PaymentOption[]; // one or more payment options
|
|
842
|
+
resource?: string; // override resource URL
|
|
843
|
+
description?: string;
|
|
844
|
+
mimeType?: string;
|
|
845
|
+
serviceName?: string;
|
|
846
|
+
tags?: string[];
|
|
847
|
+
iconUrl?: string;
|
|
848
|
+
customPaywallHtml?: string;
|
|
849
|
+
unpaidResponseBody?: (context: HTTPRequestContext) => HTTPResponseBody | Promise<HTTPResponseBody>;
|
|
850
|
+
settlementFailedResponseBody?: (context: HTTPRequestContext, settleResult) => HTTPResponseBody | Promise<HTTPResponseBody>;
|
|
851
|
+
extensions?: Record<string, unknown>;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
interface PaymentOption {
|
|
855
|
+
scheme: string; // "exact"
|
|
856
|
+
payTo: string | DynamicPayTo; // recipient address or function
|
|
857
|
+
price: Price | DynamicPrice; // "$0.01", "1000000", { asset: "USDC", amount: "100000" }
|
|
858
|
+
network: Network; // "solana:5eykt4Us..."
|
|
859
|
+
maxTimeoutSeconds?: number;
|
|
860
|
+
extra?: Record<string, unknown>;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
type Price = Money | AssetAmount;
|
|
864
|
+
type Money = string | number;
|
|
865
|
+
type AssetAmount = { asset: string; amount: string; extra?: Record<string, unknown> };
|
|
866
|
+
```
|
|
867
|
+
|
|
868
|
+
### Route Pattern Matching
|
|
869
|
+
|
|
870
|
+
```
|
|
871
|
+
"GET /api/data" → verb=GET, regex=/^\/api\/data$/i
|
|
872
|
+
"POST /api/*" → verb=POST, regex=/^\/api\/.*?$/i
|
|
873
|
+
"/api/:id" → verb=*, regex=/^\/api\/[^/]+$/i
|
|
874
|
+
"/api/[id]" → verb=*, regex=/^\/api\/[^/]+$/i
|
|
875
|
+
"*" → verb=*, regex=/^.+$/i (matches everything)
|
|
876
|
+
```
|
|
877
|
+
|
|
878
|
+
---
|
|
879
|
+
|
|
880
|
+
## 14. Quick Implementation Guide for SAP MCP Server
|
|
881
|
+
|
|
882
|
+
### Server-side gate (Express middleware pattern):
|
|
883
|
+
|
|
884
|
+
```typescript
|
|
885
|
+
// 1. Create facilitator client
|
|
886
|
+
const facilitator = new HTTPFacilitatorClient({ url: "https://x402.org/facilitator" });
|
|
887
|
+
|
|
888
|
+
// 2. Create resource server + register SVM scheme
|
|
889
|
+
const resourceServer = new x402ResourceServer(facilitator);
|
|
890
|
+
registerExactSvmScheme(resourceServer); // registers "exact" scheme for solana:*
|
|
891
|
+
|
|
892
|
+
// 3. Create HTTP server with routes
|
|
893
|
+
const httpServer = new x402HTTPResourceServer(resourceServer, {
|
|
894
|
+
"POST /mcp": {
|
|
895
|
+
accepts: {
|
|
896
|
+
scheme: "exact",
|
|
897
|
+
network: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
|
|
898
|
+
payTo: agentWalletAddress,
|
|
899
|
+
price: "$0.01", // or raw amount in atomic units
|
|
900
|
+
},
|
|
901
|
+
description: "MCP tool call",
|
|
902
|
+
mimeType: "application/json",
|
|
903
|
+
},
|
|
904
|
+
});
|
|
905
|
+
|
|
906
|
+
// 4. Initialize (fetches /supported from facilitator)
|
|
907
|
+
await httpServer.initialize();
|
|
908
|
+
|
|
909
|
+
// 5. In Express middleware:
|
|
910
|
+
const result = await httpServer.processHTTPRequest(context);
|
|
911
|
+
if (result.type === "payment-error") {
|
|
912
|
+
// Return 402/403 with result.response
|
|
913
|
+
res.status(result.response.status).set(result.response.headers).send(result.response.body);
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
if (result.type === "payment-verified") {
|
|
917
|
+
// Execute handler, then settle
|
|
918
|
+
// ... handler runs ...
|
|
919
|
+
const settleResult = await httpServer.processSettlement(
|
|
920
|
+
result.paymentPayload,
|
|
921
|
+
result.paymentRequirements,
|
|
922
|
+
result.declaredExtensions,
|
|
923
|
+
transportContext
|
|
924
|
+
);
|
|
925
|
+
// Add PAYMENT-RESPONSE header
|
|
926
|
+
res.set(settleResult.headers);
|
|
927
|
+
}
|
|
928
|
+
```
|
|
929
|
+
|
|
930
|
+
### Client-side plugin:
|
|
931
|
+
|
|
932
|
+
```typescript
|
|
933
|
+
// 1. Create client + register SVM scheme
|
|
934
|
+
const coreClient = new x402Client()
|
|
935
|
+
.register("solana:*", new ExactSvmScheme(clientSvmSigner));
|
|
936
|
+
|
|
937
|
+
// 2. Wrap with HTTP client
|
|
938
|
+
const client = new x402HTTPClient(coreClient);
|
|
939
|
+
|
|
940
|
+
// 3. Make request
|
|
941
|
+
const response = await fetch(endpoint);
|
|
942
|
+
if (response.status === 402) {
|
|
943
|
+
const paymentRequired = client.getPaymentRequiredResponse(
|
|
944
|
+
(name) => response.headers.get(name),
|
|
945
|
+
await response.json()
|
|
946
|
+
);
|
|
947
|
+
const paymentPayload = await client.createPaymentPayload(paymentRequired);
|
|
948
|
+
const paidResponse = await fetch(endpoint, {
|
|
949
|
+
headers: client.encodePaymentSignatureHeader(paymentPayload),
|
|
950
|
+
});
|
|
951
|
+
const settlement = client.getPaymentSettleResponse(
|
|
952
|
+
(name) => paidResponse.headers.get(name)
|
|
953
|
+
);
|
|
954
|
+
}
|
|
955
|
+
```
|
|
956
|
+
|
|
957
|
+
---
|
|
958
|
+
|
|
959
|
+
## 15. Coinbase x402 / x402 Foundation
|
|
960
|
+
|
|
961
|
+
The `@x402/*` packages are published by the **x402 Foundation** (originally Coinbase x402, now an independent project at `github.com/x402-foundation/x402`).
|
|
962
|
+
|
|
963
|
+
### Architecture
|
|
964
|
+
|
|
965
|
+
```
|
|
966
|
+
@x402/core — Transport-agnostic client, server, facilitator, schemas
|
|
967
|
+
@x402/svm — Solana implementation (exact scheme, SPL Token transfers)
|
|
968
|
+
@x402/evm — Ethereum/EVM implementation (exact scheme, ERC-20 transfers)
|
|
969
|
+
@x402/avm — Algorand implementation
|
|
970
|
+
@x402/express — Express.js middleware
|
|
971
|
+
@x402/hono — Hono middleware
|
|
972
|
+
@x402/next — Next.js integration
|
|
973
|
+
@x402/axios — Axios interceptor
|
|
974
|
+
@x402/fetch — Fetch wrapper
|
|
975
|
+
@x402/paywall — Browser paywall UI
|
|
976
|
+
```
|
|
977
|
+
|
|
978
|
+
### Default Facilitator
|
|
979
|
+
|
|
980
|
+
`https://x402.org/facilitator` — the x402 Foundation's hosted facilitator that supports both EVM and SVM chains. Self-hosted facilitators are possible using `x402Facilitator` from `@x402/core/facilitator` with chain-specific scheme registrations.
|
|
981
|
+
|
|
982
|
+
### Coinbase Origin
|
|
983
|
+
|
|
984
|
+
The protocol was originally developed at Coinbase as "x402" (HTTP 402 Payments Required). It has since been open-sourced under the x402 Foundation. The GitHub repo at `github.com/coinbase/x402` redirects to `github.com/x402-foundation/x402`.
|