@4mica/x402 0.1.0 → 1.0.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 +14 -0
- package/demo/README.md +3 -3
- package/demo/package.json +1 -1
- package/package.json +1 -1
- package/src/client/scheme.ts +16 -1
- package/src/server/facilitator.ts +115 -1
- package/tests/client-scheme.test.ts +101 -0
- package/tests/facilitator.test.ts +174 -0
package/README.md
CHANGED
|
@@ -277,6 +277,20 @@ const client = new x402Client()
|
|
|
277
277
|
const fetchWithPayment = wrapFetchWithPayment(fetch, client);
|
|
278
278
|
```
|
|
279
279
|
|
|
280
|
+
## V2 Validation Policy Notes
|
|
281
|
+
|
|
282
|
+
For x402 V2 requests, include validation policy metadata in `paymentRequirements.extra`:
|
|
283
|
+
|
|
284
|
+
- `validationRegistryAddress`
|
|
285
|
+
- `validatorAddress`
|
|
286
|
+
- `validatorAgentId`
|
|
287
|
+
- `minValidationScore`
|
|
288
|
+
- optional `requiredValidationTag`
|
|
289
|
+
|
|
290
|
+
`validationChainId` is optional. When omitted, the underlying 4mica SDK derives the
|
|
291
|
+
`validation_chain_id` from the CAIP-2 `network` value (for example, `eip155:1`).
|
|
292
|
+
If `validationChainId` is provided, it must match that network chain id.
|
|
293
|
+
|
|
280
294
|
## Complete Example
|
|
281
295
|
|
|
282
296
|
### Server
|
package/demo/README.md
CHANGED
|
@@ -32,7 +32,7 @@ cp .env.example .env
|
|
|
32
32
|
|
|
33
33
|
Required variables:
|
|
34
34
|
- `PRIVATE_KEY`: Your Ethereum private key (with 0x prefix) for Sepolia testnet
|
|
35
|
-
- `PAY_TO_ADDRESS`: Address that will receive payments
|
|
35
|
+
- `PAY_TO_ADDRESS`: Address that will receive payments
|
|
36
36
|
|
|
37
37
|
## Running the Demo
|
|
38
38
|
|
|
@@ -54,7 +54,7 @@ You should see:
|
|
|
54
54
|
```
|
|
55
55
|
x402 Demo Server running on http://localhost:3000
|
|
56
56
|
Protected endpoint: http://localhost:3000/api/premium-data
|
|
57
|
-
Payment required: $0.
|
|
57
|
+
Payment required: $0.01 (4mica credit on Sepolia)
|
|
58
58
|
```
|
|
59
59
|
|
|
60
60
|
### Terminal 2: Run the client
|
|
@@ -82,7 +82,7 @@ PRIVATE_KEY=0xYourPrivateKey yarn client
|
|
|
82
82
|
## What Happens
|
|
83
83
|
|
|
84
84
|
1. **Server** starts with one protected endpoint: `GET /api/premium-data`
|
|
85
|
-
- Requires a payment of $0.
|
|
85
|
+
- Requires a payment of $0.01 in 4mica credits on Sepolia
|
|
86
86
|
- Uses x402 payment protocol
|
|
87
87
|
|
|
88
88
|
2. **Client** makes a request to the protected endpoint:
|
package/demo/package.json
CHANGED
package/package.json
CHANGED
package/src/client/scheme.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
PaymentRequirementsV1,
|
|
6
6
|
X402Flow,
|
|
7
7
|
X402PaymentRequired,
|
|
8
|
+
X402ResourceInfo,
|
|
8
9
|
} from '@4mica/sdk'
|
|
9
10
|
import { Account } from 'viem/accounts'
|
|
10
11
|
import { SUPPORTED_NETWORKS } from '../server/scheme.js'
|
|
@@ -73,9 +74,23 @@ export class FourMicaEvmScheme implements SchemeNetworkClient {
|
|
|
73
74
|
payload: signed.payload as unknown as Record<string, unknown>,
|
|
74
75
|
}
|
|
75
76
|
} else if (x402Version === 2) {
|
|
77
|
+
const resourcePayload =
|
|
78
|
+
paymentRequirements.extra &&
|
|
79
|
+
typeof paymentRequirements.extra === 'object' &&
|
|
80
|
+
'resource' in paymentRequirements.extra &&
|
|
81
|
+
typeof paymentRequirements.extra.resource === 'object' &&
|
|
82
|
+
paymentRequirements.extra.resource !== null
|
|
83
|
+
? (paymentRequirements.extra.resource as Record<string, unknown>)
|
|
84
|
+
: {}
|
|
85
|
+
|
|
86
|
+
const resource: X402ResourceInfo = {
|
|
87
|
+
url: String(resourcePayload.url ?? ''),
|
|
88
|
+
description: String(resourcePayload.description ?? ''),
|
|
89
|
+
mimeType: String(resourcePayload.mimeType ?? ''),
|
|
90
|
+
}
|
|
76
91
|
const paymentRequired: X402PaymentRequired = {
|
|
77
92
|
x402Version: 2,
|
|
78
|
-
resource
|
|
93
|
+
resource,
|
|
79
94
|
accepts: [paymentRequirements],
|
|
80
95
|
}
|
|
81
96
|
const signed = await x402Flow.signPaymentV2(
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FacilitatorConfig, HTTPFacilitatorClient } from '@x402/core/server'
|
|
2
|
-
import { Network, PaymentRequirements } from '@x402/core/types'
|
|
2
|
+
import { Network, PaymentPayload, PaymentRequirements, SettleResponse } from '@x402/core/types'
|
|
3
3
|
|
|
4
4
|
const DEFAULT_FACILITATOR_URL = 'https://x402.4mica.xyz'
|
|
5
5
|
|
|
@@ -21,6 +21,18 @@ export interface OpenTabResponse {
|
|
|
21
21
|
nextReqId: string
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
export interface CertificateResponse {
|
|
25
|
+
claims: string
|
|
26
|
+
signature: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type FourMicaSettleResponse = SettleResponse & {
|
|
30
|
+
certificate?: CertificateResponse
|
|
31
|
+
txHash?: string
|
|
32
|
+
networkId?: string
|
|
33
|
+
error?: string
|
|
34
|
+
}
|
|
35
|
+
|
|
24
36
|
export class OpenTabError extends Error {
|
|
25
37
|
constructor(
|
|
26
38
|
public readonly status: number,
|
|
@@ -75,6 +87,41 @@ export class FourMicaFacilitatorClient extends HTTPFacilitatorClient {
|
|
|
75
87
|
throw new Error(`Facilitator openTab failed (${response.status}): ${JSON.stringify(data)}`)
|
|
76
88
|
}
|
|
77
89
|
|
|
90
|
+
async settle(
|
|
91
|
+
paymentPayload: PaymentPayload,
|
|
92
|
+
paymentRequirements: PaymentRequirements
|
|
93
|
+
): Promise<FourMicaSettleResponse> {
|
|
94
|
+
let headers: Record<string, string> = {
|
|
95
|
+
'Content-Type': 'application/json',
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const authHeaders = await this.createAuthHeaders('settle')
|
|
99
|
+
headers = { ...headers, ...authHeaders.headers }
|
|
100
|
+
|
|
101
|
+
const response = await fetch(`${this.url}/settle`, {
|
|
102
|
+
method: 'POST',
|
|
103
|
+
headers,
|
|
104
|
+
body: JSON.stringify(
|
|
105
|
+
this.safeJson({
|
|
106
|
+
x402Version: paymentPayload.x402Version,
|
|
107
|
+
paymentPayload,
|
|
108
|
+
paymentRequirements,
|
|
109
|
+
})
|
|
110
|
+
),
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
const data = (await response.json()) as Record<string, unknown>
|
|
114
|
+
const normalized = normalizeSettleResponse(data, paymentRequirements)
|
|
115
|
+
|
|
116
|
+
if (!response.ok || !normalized.success) {
|
|
117
|
+
throw new Error(
|
|
118
|
+
`Facilitator settle failed (${response.status}): ${normalized.errorReason ?? normalized.error ?? 'unknown error'}`
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return normalized
|
|
123
|
+
}
|
|
124
|
+
|
|
78
125
|
/**
|
|
79
126
|
* Helper to convert objects to JSON-safe format.
|
|
80
127
|
* Handles BigInt and other non-JSON types.
|
|
@@ -88,3 +135,70 @@ export class FourMicaFacilitatorClient extends HTTPFacilitatorClient {
|
|
|
88
135
|
)
|
|
89
136
|
}
|
|
90
137
|
}
|
|
138
|
+
|
|
139
|
+
function normalizeSettleResponse(
|
|
140
|
+
payload: Record<string, unknown>,
|
|
141
|
+
requirements: PaymentRequirements
|
|
142
|
+
): FourMicaSettleResponse {
|
|
143
|
+
const transaction = String(
|
|
144
|
+
payload.transaction ??
|
|
145
|
+
payload.transactionHash ??
|
|
146
|
+
payload.txHash ??
|
|
147
|
+
payload.tx_hash ??
|
|
148
|
+
payload.hash ??
|
|
149
|
+
''
|
|
150
|
+
)
|
|
151
|
+
const network = String(
|
|
152
|
+
payload.network ?? payload.networkId ?? payload.network_id ?? requirements.network
|
|
153
|
+
) as Network
|
|
154
|
+
const errorReason =
|
|
155
|
+
typeof payload.errorReason === 'string'
|
|
156
|
+
? payload.errorReason
|
|
157
|
+
: typeof payload.error_reason === 'string'
|
|
158
|
+
? payload.error_reason
|
|
159
|
+
: typeof payload.error === 'string'
|
|
160
|
+
? payload.error
|
|
161
|
+
: typeof payload.message === 'string'
|
|
162
|
+
? payload.message
|
|
163
|
+
: undefined
|
|
164
|
+
|
|
165
|
+
const certificate =
|
|
166
|
+
payload.certificate &&
|
|
167
|
+
typeof payload.certificate === 'object' &&
|
|
168
|
+
typeof (payload.certificate as Record<string, unknown>).claims === 'string' &&
|
|
169
|
+
typeof (payload.certificate as Record<string, unknown>).signature === 'string'
|
|
170
|
+
? {
|
|
171
|
+
claims: (payload.certificate as Record<string, string>).claims,
|
|
172
|
+
signature: (payload.certificate as Record<string, string>).signature,
|
|
173
|
+
}
|
|
174
|
+
: undefined
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
success: Boolean(payload.success ?? errorReason === undefined),
|
|
178
|
+
errorReason,
|
|
179
|
+
payer:
|
|
180
|
+
typeof payload.payer === 'string'
|
|
181
|
+
? payload.payer
|
|
182
|
+
: typeof payload.userAddress === 'string'
|
|
183
|
+
? payload.userAddress
|
|
184
|
+
: typeof payload.user_address === 'string'
|
|
185
|
+
? payload.user_address
|
|
186
|
+
: undefined,
|
|
187
|
+
transaction,
|
|
188
|
+
network,
|
|
189
|
+
certificate,
|
|
190
|
+
txHash:
|
|
191
|
+
typeof payload.txHash === 'string'
|
|
192
|
+
? payload.txHash
|
|
193
|
+
: typeof payload.tx_hash === 'string'
|
|
194
|
+
? payload.tx_hash
|
|
195
|
+
: transaction || undefined,
|
|
196
|
+
networkId:
|
|
197
|
+
typeof payload.networkId === 'string'
|
|
198
|
+
? payload.networkId
|
|
199
|
+
: typeof payload.network_id === 'string'
|
|
200
|
+
? payload.network_id
|
|
201
|
+
: network,
|
|
202
|
+
error: typeof payload.error === 'string' ? payload.error : undefined,
|
|
203
|
+
}
|
|
204
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { privateKeyToAccount } from 'viem/accounts'
|
|
3
|
+
|
|
4
|
+
import { FourMicaEvmScheme } from '../src/client/scheme.js'
|
|
5
|
+
|
|
6
|
+
describe('FourMicaEvmScheme', () => {
|
|
7
|
+
afterEach(() => {
|
|
8
|
+
vi.restoreAllMocks()
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
it('passes V2 resource metadata through to the SDK flow', async () => {
|
|
12
|
+
const signPaymentV2 = vi.fn().mockResolvedValue({
|
|
13
|
+
payload: {
|
|
14
|
+
claims: {
|
|
15
|
+
version: 'v2',
|
|
16
|
+
validation_request_hash: '0x' + '11'.repeat(32),
|
|
17
|
+
validation_subject_hash: '0x' + '22'.repeat(32),
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
vi.spyOn(FourMicaEvmScheme as never, 'createX402Flow' as never).mockResolvedValue({
|
|
23
|
+
signPayment: vi.fn(),
|
|
24
|
+
signPaymentV2,
|
|
25
|
+
} as never)
|
|
26
|
+
|
|
27
|
+
const scheme = await FourMicaEvmScheme.create(
|
|
28
|
+
privateKeyToAccount(`0x${'11'.repeat(32)}`)
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
const requirements = {
|
|
32
|
+
scheme: '4mica-credit',
|
|
33
|
+
network: 'eip155:11155111',
|
|
34
|
+
asset: '0x2222222222222222222222222222222222222222',
|
|
35
|
+
amount: '10',
|
|
36
|
+
payTo: '0x1111111111111111111111111111111111111111',
|
|
37
|
+
extra: {
|
|
38
|
+
rpcUrl: 'https://custom.rpc.example',
|
|
39
|
+
validationRegistryAddress: '0x3333333333333333333333333333333333333333',
|
|
40
|
+
validatorAddress: '0x4444444444444444444444444444444444444444',
|
|
41
|
+
validatorAgentId: '7',
|
|
42
|
+
minValidationScore: 80,
|
|
43
|
+
requiredValidationTag: 'hard-finality',
|
|
44
|
+
resource: {
|
|
45
|
+
url: 'https://api.example.com/premium',
|
|
46
|
+
description: 'Premium dataset',
|
|
47
|
+
mimeType: 'application/json',
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const result = await scheme.createPaymentPayload(2, requirements as never)
|
|
53
|
+
|
|
54
|
+
expect(result.x402Version).toBe(2)
|
|
55
|
+
expect(signPaymentV2).toHaveBeenCalledTimes(1)
|
|
56
|
+
|
|
57
|
+
const paymentRequired = signPaymentV2.mock.calls[0]?.[0]
|
|
58
|
+
expect(paymentRequired.resource).toEqual({
|
|
59
|
+
url: 'https://api.example.com/premium',
|
|
60
|
+
description: 'Premium dataset',
|
|
61
|
+
mimeType: 'application/json',
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
const accepted = signPaymentV2.mock.calls[0]?.[1]
|
|
65
|
+
expect(accepted).toMatchObject({
|
|
66
|
+
scheme: '4mica-credit',
|
|
67
|
+
network: 'eip155:11155111',
|
|
68
|
+
asset: '0x2222222222222222222222222222222222222222',
|
|
69
|
+
amount: '10',
|
|
70
|
+
payTo: '0x1111111111111111111111111111111111111111',
|
|
71
|
+
})
|
|
72
|
+
expect(accepted.extra).toMatchObject({
|
|
73
|
+
validationRegistryAddress: '0x3333333333333333333333333333333333333333',
|
|
74
|
+
validatorAddress: '0x4444444444444444444444444444444444444444',
|
|
75
|
+
validatorAgentId: '7',
|
|
76
|
+
minValidationScore: 80,
|
|
77
|
+
requiredValidationTag: 'hard-finality',
|
|
78
|
+
})
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it('rejects unsupported x402 versions', async () => {
|
|
82
|
+
vi.spyOn(FourMicaEvmScheme as never, 'createX402Flow' as never).mockResolvedValue({
|
|
83
|
+
signPayment: vi.fn(),
|
|
84
|
+
signPaymentV2: vi.fn(),
|
|
85
|
+
} as never)
|
|
86
|
+
|
|
87
|
+
const scheme = await FourMicaEvmScheme.create(
|
|
88
|
+
privateKeyToAccount(`0x${'11'.repeat(32)}`)
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
await expect(
|
|
92
|
+
scheme.createPaymentPayload(3, {
|
|
93
|
+
scheme: '4mica-credit',
|
|
94
|
+
network: 'eip155:11155111',
|
|
95
|
+
asset: '0x2222222222222222222222222222222222222222',
|
|
96
|
+
amount: '10',
|
|
97
|
+
payTo: '0x1111111111111111111111111111111111111111',
|
|
98
|
+
} as never)
|
|
99
|
+
).rejects.toThrow('Unsupported x402Version: 3')
|
|
100
|
+
})
|
|
101
|
+
})
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { FourMicaFacilitatorClient } from '../src/server/facilitator.js'
|
|
4
|
+
|
|
5
|
+
describe('FourMicaFacilitatorClient', () => {
|
|
6
|
+
afterEach(() => {
|
|
7
|
+
vi.restoreAllMocks()
|
|
8
|
+
vi.unstubAllGlobals()
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
it('normalizes 4mica settle responses and preserves certificate fields', async () => {
|
|
12
|
+
const fetchMock = vi.fn().mockResolvedValue(
|
|
13
|
+
new Response(
|
|
14
|
+
JSON.stringify({
|
|
15
|
+
success: true,
|
|
16
|
+
txHash: '0xdeadbeef',
|
|
17
|
+
networkId: 'eip155:11155111',
|
|
18
|
+
certificate: {
|
|
19
|
+
claims: '0x' + '11'.repeat(32),
|
|
20
|
+
signature: '0x' + '22'.repeat(96),
|
|
21
|
+
},
|
|
22
|
+
}),
|
|
23
|
+
{
|
|
24
|
+
status: 200,
|
|
25
|
+
headers: { 'content-type': 'application/json' },
|
|
26
|
+
}
|
|
27
|
+
)
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
vi.stubGlobal('fetch', fetchMock)
|
|
31
|
+
|
|
32
|
+
const client = new FourMicaFacilitatorClient({ url: 'https://facilitator.example' })
|
|
33
|
+
|
|
34
|
+
const result = await client.settle(
|
|
35
|
+
{
|
|
36
|
+
x402Version: 2,
|
|
37
|
+
accepted: {
|
|
38
|
+
scheme: '4mica-credit',
|
|
39
|
+
network: 'eip155:11155111',
|
|
40
|
+
asset: '0x2222222222222222222222222222222222222222',
|
|
41
|
+
amount: '10',
|
|
42
|
+
payTo: '0x1111111111111111111111111111111111111111',
|
|
43
|
+
},
|
|
44
|
+
payload: {
|
|
45
|
+
claims: {
|
|
46
|
+
version: 'v2',
|
|
47
|
+
},
|
|
48
|
+
signature: '0x1234',
|
|
49
|
+
scheme: 'eip712',
|
|
50
|
+
},
|
|
51
|
+
} as never,
|
|
52
|
+
{
|
|
53
|
+
scheme: '4mica-credit',
|
|
54
|
+
network: 'eip155:11155111',
|
|
55
|
+
asset: '0x2222222222222222222222222222222222222222',
|
|
56
|
+
amount: '10',
|
|
57
|
+
payTo: '0x1111111111111111111111111111111111111111',
|
|
58
|
+
} as never
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
expect(result.success).toBe(true)
|
|
62
|
+
expect(result.transaction).toBe('0xdeadbeef')
|
|
63
|
+
expect(result.network).toBe('eip155:11155111')
|
|
64
|
+
expect(result.txHash).toBe('0xdeadbeef')
|
|
65
|
+
expect(result.networkId).toBe('eip155:11155111')
|
|
66
|
+
expect(result.certificate).toEqual({
|
|
67
|
+
claims: '0x' + '11'.repeat(32),
|
|
68
|
+
signature: '0x' + '22'.repeat(96),
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
const request = fetchMock.mock.calls[0]?.[1]
|
|
72
|
+
expect(typeof request?.body).toBe('string')
|
|
73
|
+
expect(String(request?.body)).toContain('"x402Version":2')
|
|
74
|
+
expect(String(request?.body)).toContain('"version":"v2"')
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('normalizes alias fields when the facilitator omits txHash/networkId', async () => {
|
|
78
|
+
const fetchMock = vi.fn().mockResolvedValue(
|
|
79
|
+
new Response(
|
|
80
|
+
JSON.stringify({
|
|
81
|
+
success: true,
|
|
82
|
+
transactionHash: '0xabc123',
|
|
83
|
+
network: 'eip155:80002',
|
|
84
|
+
user_address: '0x9999999999999999999999999999999999999999',
|
|
85
|
+
}),
|
|
86
|
+
{
|
|
87
|
+
status: 200,
|
|
88
|
+
headers: { 'content-type': 'application/json' },
|
|
89
|
+
}
|
|
90
|
+
)
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
vi.stubGlobal('fetch', fetchMock)
|
|
94
|
+
|
|
95
|
+
const client = new FourMicaFacilitatorClient({ url: 'https://facilitator.example' })
|
|
96
|
+
const result = await client.settle(
|
|
97
|
+
{
|
|
98
|
+
x402Version: 2,
|
|
99
|
+
accepted: {
|
|
100
|
+
scheme: '4mica-credit',
|
|
101
|
+
network: 'eip155:80002',
|
|
102
|
+
asset: '0x2222222222222222222222222222222222222222',
|
|
103
|
+
amount: '10',
|
|
104
|
+
payTo: '0x1111111111111111111111111111111111111111',
|
|
105
|
+
},
|
|
106
|
+
payload: {
|
|
107
|
+
claims: { version: 'v2' },
|
|
108
|
+
signature: '0x1234',
|
|
109
|
+
scheme: 'eip712',
|
|
110
|
+
},
|
|
111
|
+
} as never,
|
|
112
|
+
{
|
|
113
|
+
scheme: '4mica-credit',
|
|
114
|
+
network: 'eip155:80002',
|
|
115
|
+
asset: '0x2222222222222222222222222222222222222222',
|
|
116
|
+
amount: '10',
|
|
117
|
+
payTo: '0x1111111111111111111111111111111111111111',
|
|
118
|
+
} as never
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
expect(result.success).toBe(true)
|
|
122
|
+
expect(result.transaction).toBe('0xabc123')
|
|
123
|
+
expect(result.txHash).toBe('0xabc123')
|
|
124
|
+
expect(result.network).toBe('eip155:80002')
|
|
125
|
+
expect(result.networkId).toBe('eip155:80002')
|
|
126
|
+
expect(result.payer).toBe('0x9999999999999999999999999999999999999999')
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it('surfaces facilitator errors using normalized reasons', async () => {
|
|
130
|
+
const fetchMock = vi.fn().mockResolvedValue(
|
|
131
|
+
new Response(
|
|
132
|
+
JSON.stringify({
|
|
133
|
+
success: false,
|
|
134
|
+
error_reason: 'unsupported x402Version 2',
|
|
135
|
+
}),
|
|
136
|
+
{
|
|
137
|
+
status: 200,
|
|
138
|
+
headers: { 'content-type': 'application/json' },
|
|
139
|
+
}
|
|
140
|
+
)
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
vi.stubGlobal('fetch', fetchMock)
|
|
144
|
+
|
|
145
|
+
const client = new FourMicaFacilitatorClient({ url: 'https://facilitator.example' })
|
|
146
|
+
|
|
147
|
+
await expect(
|
|
148
|
+
client.settle(
|
|
149
|
+
{
|
|
150
|
+
x402Version: 2,
|
|
151
|
+
accepted: {
|
|
152
|
+
scheme: '4mica-credit',
|
|
153
|
+
network: 'eip155:11155111',
|
|
154
|
+
asset: '0x2222222222222222222222222222222222222222',
|
|
155
|
+
amount: '10',
|
|
156
|
+
payTo: '0x1111111111111111111111111111111111111111',
|
|
157
|
+
},
|
|
158
|
+
payload: {
|
|
159
|
+
claims: { version: 'v2' },
|
|
160
|
+
signature: '0x1234',
|
|
161
|
+
scheme: 'eip712',
|
|
162
|
+
},
|
|
163
|
+
} as never,
|
|
164
|
+
{
|
|
165
|
+
scheme: '4mica-credit',
|
|
166
|
+
network: 'eip155:11155111',
|
|
167
|
+
asset: '0x2222222222222222222222222222222222222222',
|
|
168
|
+
amount: '10',
|
|
169
|
+
payTo: '0x1111111111111111111111111111111111111111',
|
|
170
|
+
} as never
|
|
171
|
+
)
|
|
172
|
+
).rejects.toThrow('unsupported x402Version 2')
|
|
173
|
+
})
|
|
174
|
+
})
|