@dcentralab/d402-client 0.1.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/LICENSE +22 -0
- package/README.md +594 -0
- package/dist/index.d.mts +859 -0
- package/dist/index.d.ts +859 -0
- package/dist/index.js +9021 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +8994 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +56 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Traia
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,594 @@
|
|
|
1
|
+
# @iatp/d402-client
|
|
2
|
+
|
|
3
|
+
**D402 Payment Protocol Client** for TypeScript/JavaScript
|
|
4
|
+
|
|
5
|
+
Complete TypeScript implementation of the D402 payment protocol for HTTP 402 (Payment Required) responses. Designed for **React/Next.js frontends** to call 402-protected APIs directly from the browser with wallet-based payments.
|
|
6
|
+
|
|
7
|
+
Port of Python IATP package to TypeScript with 1:1 API compatibility.
|
|
8
|
+
|
|
9
|
+
## ๐ Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @iatp/d402-client viem wagmi
|
|
13
|
+
# or
|
|
14
|
+
pnpm add @iatp/d402-client viem wagmi
|
|
15
|
+
# or
|
|
16
|
+
yarn add @iatp/d402-client viem wagmi
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Requirements:**
|
|
20
|
+
- React/Next.js app
|
|
21
|
+
- viem ^2.21.0
|
|
22
|
+
- wagmi (for wallet connection)
|
|
23
|
+
- User with Web3 wallet (MetaMask, WalletConnect, etc.)
|
|
24
|
+
|
|
25
|
+
## ๐ Quick Start (React/Next.js)
|
|
26
|
+
|
|
27
|
+
### Step 1: Configure Next.js Polyfills
|
|
28
|
+
|
|
29
|
+
**File:** `next.config.js`
|
|
30
|
+
|
|
31
|
+
```javascript
|
|
32
|
+
module.exports = {
|
|
33
|
+
webpack: (config, { isServer }) => {
|
|
34
|
+
if (!isServer) {
|
|
35
|
+
config.resolve.fallback = {
|
|
36
|
+
...config.resolve.fallback,
|
|
37
|
+
buffer: require.resolve('buffer/'),
|
|
38
|
+
crypto: require.resolve('crypto-browserify'),
|
|
39
|
+
stream: require.resolve('stream-browserify'),
|
|
40
|
+
process: require.resolve('process/browser')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const webpack = require('webpack')
|
|
44
|
+
config.plugins.push(
|
|
45
|
+
new webpack.ProvidePlugin({
|
|
46
|
+
Buffer: ['buffer', 'Buffer'],
|
|
47
|
+
process: 'process/browser'
|
|
48
|
+
})
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
return config
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Install polyfills:**
|
|
57
|
+
```bash
|
|
58
|
+
pnpm add buffer process crypto-browserify stream-browserify
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Step 2: Use in React Component
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
'use client'
|
|
65
|
+
|
|
66
|
+
import { D402Client } from '@iatp/d402-client'
|
|
67
|
+
import { useWalletClient } from 'wagmi'
|
|
68
|
+
import { useState } from 'react'
|
|
69
|
+
|
|
70
|
+
export default function AnalyzeButton() {
|
|
71
|
+
const { data: walletClient } = useWalletClient()
|
|
72
|
+
const [result, setResult] = useState(null)
|
|
73
|
+
const [loading, setLoading] = useState(false)
|
|
74
|
+
|
|
75
|
+
async function analyzeWithPayment() {
|
|
76
|
+
if (!walletClient) {
|
|
77
|
+
alert('Connect your wallet first!')
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
setLoading(true)
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
// Create D402 client with user's wallet
|
|
85
|
+
const client = new D402Client({
|
|
86
|
+
operatorAccount: walletClient.account,
|
|
87
|
+
walletAddress: '0xUserIATPWallet...', // User's IATPWallet
|
|
88
|
+
maxValue: 1000000n // 1 USDC max
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
// Call 402-protected API directly from browser
|
|
92
|
+
const response = await client.fetch('https://sentiment-api.com/analyze', {
|
|
93
|
+
method: 'POST',
|
|
94
|
+
headers: { 'Content-Type': 'application/json' },
|
|
95
|
+
body: JSON.stringify({ text: 'Bitcoin looks bullish' })
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
const data = await response.json()
|
|
99
|
+
setResult(data)
|
|
100
|
+
|
|
101
|
+
} catch (error) {
|
|
102
|
+
console.error('Payment failed:', error)
|
|
103
|
+
} finally {
|
|
104
|
+
setLoading(false)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
<div>
|
|
110
|
+
<button onClick={analyzeWithPayment} disabled={loading || !walletClient}>
|
|
111
|
+
{loading ? 'Processing...' : 'Analyze (0.01 USDC)'}
|
|
112
|
+
</button>
|
|
113
|
+
{result && <pre>{JSON.stringify(result, null, 2)}</pre>}
|
|
114
|
+
</div>
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**What happens:**
|
|
120
|
+
1. User clicks button
|
|
121
|
+
2. D402Client makes request to external API
|
|
122
|
+
3. API returns 402
|
|
123
|
+
4. Client signs payment with user's wallet
|
|
124
|
+
5. Client retries with payment
|
|
125
|
+
6. API returns data
|
|
126
|
+
7. User sees result
|
|
127
|
+
|
|
128
|
+
**All in browser!** No backend needed.
|
|
129
|
+
|
|
130
|
+
## ๐๏ธ Features
|
|
131
|
+
|
|
132
|
+
### 1. IATPWallet Creation
|
|
133
|
+
|
|
134
|
+
Create on-chain wallet with owner/operator separation:
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
import { createIATPWallet } from '@iatp/d402-client'
|
|
138
|
+
import { privateKeyToAccount } from 'viem/accounts'
|
|
139
|
+
|
|
140
|
+
// Owner creates wallet (one-time setup)
|
|
141
|
+
const ownerAccount = privateKeyToAccount('0x...')
|
|
142
|
+
|
|
143
|
+
const result = await createIATPWallet({
|
|
144
|
+
ownerAccount,
|
|
145
|
+
network: 'sepolia',
|
|
146
|
+
rpcUrl: 'https://ethereum-sepolia-rpc.publicnode.com'
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
console.log('Wallet:', result.walletAddress)
|
|
150
|
+
console.log('Operator key:', result.operatorPrivateKey) // Store securely!
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**Returns:**
|
|
154
|
+
- `walletAddress` - IATPWallet contract address
|
|
155
|
+
- `operatorAddress` - Operator's address
|
|
156
|
+
- `operatorPrivateKey` - Operator's private key (store securely!)
|
|
157
|
+
- `transactionHash` - Creation transaction
|
|
158
|
+
- `blockNumber` - Block where wallet was created
|
|
159
|
+
|
|
160
|
+
### 2. Automatic 402 Payment Handling
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
import { D402Client } from '@iatp/d402-client'
|
|
164
|
+
|
|
165
|
+
const client = new D402Client({
|
|
166
|
+
operatorAccount,
|
|
167
|
+
walletAddress: '0xYourWallet...',
|
|
168
|
+
maxValue: 1000000n,
|
|
169
|
+
networkFilter: 'base-mainnet', // Only Base network
|
|
170
|
+
schemeFilter: 'exact' // Only exact payments
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
// Client automatically:
|
|
174
|
+
// 1. Detects 402 responses
|
|
175
|
+
// 2. Parses payment requirements
|
|
176
|
+
// 3. Signs with EIP-712
|
|
177
|
+
// 4. Retries with payment
|
|
178
|
+
const response = await client.fetch('https://paid-api.com/endpoint')
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### 3. Manual Payment Flow (Low-Level)
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
import {
|
|
185
|
+
parsePaymentRequirement,
|
|
186
|
+
signD402Payment,
|
|
187
|
+
encodePayment
|
|
188
|
+
} from '@iatp/d402-client'
|
|
189
|
+
|
|
190
|
+
// 1. Make request
|
|
191
|
+
const response = await fetch('https://api.example.com')
|
|
192
|
+
|
|
193
|
+
if (response.status === 402) {
|
|
194
|
+
// 2. Parse payment requirement
|
|
195
|
+
const requirement = await parsePaymentRequirement(response)
|
|
196
|
+
|
|
197
|
+
// 3. Sign payment
|
|
198
|
+
const signedPayment = await signD402Payment({
|
|
199
|
+
operatorAccount,
|
|
200
|
+
paymentRequirement: requirement,
|
|
201
|
+
walletAddress: '0xYourWallet...'
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
// 4. Encode for header
|
|
205
|
+
const paymentHeader = encodePayment(signedPayment)
|
|
206
|
+
|
|
207
|
+
// 5. Retry with payment
|
|
208
|
+
const paidResponse = await fetch('https://api.example.com', {
|
|
209
|
+
headers: { 'X-Payment': paymentHeader }
|
|
210
|
+
})
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## ๐ง API Reference
|
|
215
|
+
|
|
216
|
+
### D402Client
|
|
217
|
+
|
|
218
|
+
Main class for automatic 402 payment handling.
|
|
219
|
+
|
|
220
|
+
#### Constructor
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
new D402Client(config: D402ClientConfig)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**Config Options:**
|
|
227
|
+
|
|
228
|
+
| Option | Type | Required | Description |
|
|
229
|
+
|--------|------|----------|-------------|
|
|
230
|
+
| `operatorAccount` | `Account` | โ
| Viem account for signing payments (EOA) |
|
|
231
|
+
| `walletAddress` | `0x${string}` | โ | IATPWallet contract address (uses operator address if not provided) |
|
|
232
|
+
| `maxValue` | `bigint` | โ | Max payment in base units (wei). Safety limit to prevent overpaying. |
|
|
233
|
+
| `networkFilter` | `string` | โ | Only select requirements matching this network |
|
|
234
|
+
| `schemeFilter` | `string` | โ | Payment scheme filter (default: "exact") |
|
|
235
|
+
| `paymentRequirementsSelector` | `PaymentSelector` | โ | Custom payment selection logic |
|
|
236
|
+
|
|
237
|
+
#### Methods
|
|
238
|
+
|
|
239
|
+
**`fetch(url, init?): Promise<Response>`**
|
|
240
|
+
|
|
241
|
+
Fetch with automatic 402 payment handling. Same API as native fetch.
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
const response = await client.fetch(url, {
|
|
245
|
+
method: 'POST',
|
|
246
|
+
headers: { 'Content-Type': 'application/json' },
|
|
247
|
+
body: JSON.stringify(data)
|
|
248
|
+
})
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
**`selectPaymentRequirement(requirements): PaymentRequirement`**
|
|
252
|
+
|
|
253
|
+
Select payment requirement from list based on filters.
|
|
254
|
+
|
|
255
|
+
**`getWalletAddress(): 0x${string}`**
|
|
256
|
+
|
|
257
|
+
Get configured wallet address.
|
|
258
|
+
|
|
259
|
+
**`getOperatorAccount(): Account`**
|
|
260
|
+
|
|
261
|
+
Get operator account.
|
|
262
|
+
|
|
263
|
+
**`getMaxValue(): bigint | undefined`**
|
|
264
|
+
|
|
265
|
+
Get maximum payment limit.
|
|
266
|
+
|
|
267
|
+
### Functions
|
|
268
|
+
|
|
269
|
+
**`createIATPWallet(params): Promise<WalletCreationResult>`**
|
|
270
|
+
|
|
271
|
+
Create new IATPWallet contract on-chain.
|
|
272
|
+
|
|
273
|
+
**Parameters:**
|
|
274
|
+
- `ownerAccount: Account` - Owner's viem account
|
|
275
|
+
- `network?: 'sepolia' | 'localhost'` - Network (default: sepolia)
|
|
276
|
+
- `rpcUrl?: string` - Custom RPC URL
|
|
277
|
+
- `operatorPrivateKey?: 0x${string}` - Use specific operator key
|
|
278
|
+
|
|
279
|
+
**`parsePaymentRequirement(response): Promise<PaymentRequirement>`**
|
|
280
|
+
|
|
281
|
+
Parse first payment requirement from 402 response.
|
|
282
|
+
|
|
283
|
+
**`parseAllPaymentRequirements(response): Promise<PaymentRequirement[]>`**
|
|
284
|
+
|
|
285
|
+
Parse all payment requirements from 402 response.
|
|
286
|
+
|
|
287
|
+
**`signD402Payment(params): Promise<SignedPayment>`**
|
|
288
|
+
|
|
289
|
+
Sign payment with EIP-712.
|
|
290
|
+
|
|
291
|
+
**`encodePayment(payment): string`**
|
|
292
|
+
|
|
293
|
+
Encode signed payment to base64 for X-Payment header.
|
|
294
|
+
|
|
295
|
+
**`decodePayment(encoded): SignedPayment`**
|
|
296
|
+
|
|
297
|
+
Decode base64 payment header.
|
|
298
|
+
|
|
299
|
+
### Contract Functions
|
|
300
|
+
|
|
301
|
+
**`getContractAddress(name, network): string | null`**
|
|
302
|
+
|
|
303
|
+
Get contract address for network.
|
|
304
|
+
|
|
305
|
+
**`getContractAbi(name, network): any[] | null`**
|
|
306
|
+
|
|
307
|
+
Get contract ABI for network.
|
|
308
|
+
|
|
309
|
+
**`getContractConfig(name, network): { address, abi } | null`**
|
|
310
|
+
|
|
311
|
+
Get both address and ABI.
|
|
312
|
+
|
|
313
|
+
### Utility Functions
|
|
314
|
+
|
|
315
|
+
**`parseMoney(amount, decimals): bigint`**
|
|
316
|
+
|
|
317
|
+
Convert money string to atomic units.
|
|
318
|
+
|
|
319
|
+
**`usdToUsdc(amount): bigint`**
|
|
320
|
+
|
|
321
|
+
Convert USD to USDC wei (6 decimals).
|
|
322
|
+
|
|
323
|
+
**`generateNonce(): 0x${string}`**
|
|
324
|
+
|
|
325
|
+
Generate random 32-byte nonce.
|
|
326
|
+
|
|
327
|
+
**`getChainId(network): number`**
|
|
328
|
+
|
|
329
|
+
Get chain ID for network name.
|
|
330
|
+
|
|
331
|
+
## ๐ก Usage Patterns
|
|
332
|
+
|
|
333
|
+
### Pattern 1: Frontend Direct (Recommended for Web3 Apps)
|
|
334
|
+
|
|
335
|
+
**React component calls 402 API directly:**
|
|
336
|
+
|
|
337
|
+
```typescript
|
|
338
|
+
'use client'
|
|
339
|
+
|
|
340
|
+
import { D402Client } from '@iatp/d402-client'
|
|
341
|
+
import { useWalletClient } from 'wagmi'
|
|
342
|
+
|
|
343
|
+
export default function PaymentComponent() {
|
|
344
|
+
const { data: walletClient } = useWalletClient()
|
|
345
|
+
|
|
346
|
+
async function callPaidAPI() {
|
|
347
|
+
if (!walletClient) return
|
|
348
|
+
|
|
349
|
+
// User's wallet signs payments
|
|
350
|
+
const client = new D402Client({
|
|
351
|
+
operatorAccount: walletClient.account,
|
|
352
|
+
walletAddress: '0xUserIATPWallet...',
|
|
353
|
+
maxValue: 1000000n
|
|
354
|
+
})
|
|
355
|
+
|
|
356
|
+
// Calls external 402 API directly
|
|
357
|
+
const response = await client.fetch('https://sentiment-api.com/analyze', {
|
|
358
|
+
method: 'POST',
|
|
359
|
+
body: JSON.stringify({ text: 'Bitcoin sentiment' })
|
|
360
|
+
})
|
|
361
|
+
|
|
362
|
+
return await response.json()
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
**Flow:** Browser โ D402Client โ External 402 API
|
|
368
|
+
|
|
369
|
+
**Pros:**
|
|
370
|
+
- โ
User pays from their wallet
|
|
371
|
+
- โ
Fully decentralized
|
|
372
|
+
- โ
No backend needed
|
|
373
|
+
|
|
374
|
+
**Cons:**
|
|
375
|
+
- โ ๏ธ Requires wallet connection
|
|
376
|
+
- โ ๏ธ Needs Next.js polyfill config
|
|
377
|
+
- โ ๏ธ User approves each payment
|
|
378
|
+
|
|
379
|
+
### Pattern 2: Backend Proxy (Alternative)
|
|
380
|
+
|
|
381
|
+
**Backend handles 402 calls, frontend just calls your API:**
|
|
382
|
+
|
|
383
|
+
**Backend (Node.js):**
|
|
384
|
+
```typescript
|
|
385
|
+
import { D402Client } from '@iatp/d402-client'
|
|
386
|
+
import { privateKeyToAccount } from 'viem/accounts'
|
|
387
|
+
|
|
388
|
+
const operatorAccount = privateKeyToAccount(process.env.OPERATOR_KEY!)
|
|
389
|
+
|
|
390
|
+
app.post('/api/analyze', async (req, res) => {
|
|
391
|
+
const client = new D402Client({
|
|
392
|
+
operatorAccount,
|
|
393
|
+
walletAddress: req.user.iatpWallet,
|
|
394
|
+
maxValue: 1000000n
|
|
395
|
+
})
|
|
396
|
+
|
|
397
|
+
const response = await client.fetch('https://sentiment-api.com/analyze', {
|
|
398
|
+
method: 'POST',
|
|
399
|
+
body: JSON.stringify(req.body)
|
|
400
|
+
})
|
|
401
|
+
|
|
402
|
+
res.json(await response.json())
|
|
403
|
+
})
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
**Frontend (simpler):**
|
|
407
|
+
```typescript
|
|
408
|
+
// Just call YOUR backend
|
|
409
|
+
const response = await fetch('/api/analyze', {
|
|
410
|
+
method: 'POST',
|
|
411
|
+
body: JSON.stringify({ text: 'Analyze this' })
|
|
412
|
+
})
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
**Flow:** Browser โ Your Backend โ D402Client โ External 402 API
|
|
416
|
+
|
|
417
|
+
**Pros:**
|
|
418
|
+
- โ
Simpler frontend (no wallet needed)
|
|
419
|
+
- โ
No polyfills needed in frontend
|
|
420
|
+
- โ
Smooth UX (no wallet popups)
|
|
421
|
+
|
|
422
|
+
**Cons:**
|
|
423
|
+
- โ ๏ธ Backend controls payments
|
|
424
|
+
- โ ๏ธ More centralized
|
|
425
|
+
|
|
426
|
+
## ๐ Supported Networks
|
|
427
|
+
|
|
428
|
+
| Network | Chain ID | USDC Address |
|
|
429
|
+
|---------|----------|--------------|
|
|
430
|
+
| Ethereum Mainnet | 1 | `0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48` |
|
|
431
|
+
| Sepolia Testnet | 11155111 | `0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238` |
|
|
432
|
+
| Base | 8453 | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` |
|
|
433
|
+
| Base Sepolia | 84532 | `0x036CbD53842c5426634e7929541eC2318f3dCF7e` |
|
|
434
|
+
| Polygon | 137 | `0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359` |
|
|
435
|
+
| Arbitrum One | 42161 | `0xaf88d065e77c8cC2239327C5EDb3A432268e5831` |
|
|
436
|
+
| Arbitrum Sepolia | 421614 | `0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d` |
|
|
437
|
+
|
|
438
|
+
## ๐ฆ Package Exports
|
|
439
|
+
|
|
440
|
+
```typescript
|
|
441
|
+
// Main client
|
|
442
|
+
export { D402Client } from '@iatp/d402-client'
|
|
443
|
+
|
|
444
|
+
// Wallet management
|
|
445
|
+
export { createIATPWallet } from '@iatp/d402-client'
|
|
446
|
+
|
|
447
|
+
// Payment functions
|
|
448
|
+
export {
|
|
449
|
+
parsePaymentRequirement,
|
|
450
|
+
parseAllPaymentRequirements,
|
|
451
|
+
signD402Payment,
|
|
452
|
+
encodePayment,
|
|
453
|
+
decodePayment
|
|
454
|
+
} from '@iatp/d402-client'
|
|
455
|
+
|
|
456
|
+
// Contract access
|
|
457
|
+
export {
|
|
458
|
+
ContractName,
|
|
459
|
+
getContractAddress,
|
|
460
|
+
getContractAbi,
|
|
461
|
+
getContractConfig
|
|
462
|
+
} from '@iatp/d402-client'
|
|
463
|
+
|
|
464
|
+
// Utilities
|
|
465
|
+
export {
|
|
466
|
+
parseMoney,
|
|
467
|
+
usdToUsdc,
|
|
468
|
+
generateNonce,
|
|
469
|
+
getChainId
|
|
470
|
+
} from '@iatp/d402-client'
|
|
471
|
+
|
|
472
|
+
// Error classes
|
|
473
|
+
export {
|
|
474
|
+
PaymentError,
|
|
475
|
+
PaymentAmountExceededError,
|
|
476
|
+
UnsupportedSchemeError,
|
|
477
|
+
Invalid402ResponseError
|
|
478
|
+
} from '@iatp/d402-client'
|
|
479
|
+
|
|
480
|
+
// Types
|
|
481
|
+
export type {
|
|
482
|
+
D402ClientConfig,
|
|
483
|
+
PaymentRequirement,
|
|
484
|
+
SignedPayment,
|
|
485
|
+
WalletCreationResult
|
|
486
|
+
} from '@iatp/d402-client'
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
## ๐งช Testing
|
|
490
|
+
|
|
491
|
+
```bash
|
|
492
|
+
# Run all tests
|
|
493
|
+
pnpm test
|
|
494
|
+
|
|
495
|
+
# Run specific test file
|
|
496
|
+
pnpm test client.test.ts
|
|
497
|
+
|
|
498
|
+
# Run with coverage
|
|
499
|
+
pnpm test --coverage
|
|
500
|
+
|
|
501
|
+
# Watch mode
|
|
502
|
+
pnpm test --watch
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
**Test Coverage:**
|
|
506
|
+
- โ
17 encoder/decoder tests
|
|
507
|
+
- โ
19 parser tests
|
|
508
|
+
- โ
29 contracts tests
|
|
509
|
+
- โ
24 signer tests
|
|
510
|
+
- โ
14 wallet tests
|
|
511
|
+
- โ
19 client constructor tests
|
|
512
|
+
- โ
22 integration tests
|
|
513
|
+
|
|
514
|
+
**Total: 144 tests**
|
|
515
|
+
|
|
516
|
+
## ๐ Security
|
|
517
|
+
|
|
518
|
+
### Private Key Handling
|
|
519
|
+
|
|
520
|
+
**โ
Safe (Backend):**
|
|
521
|
+
```typescript
|
|
522
|
+
// Store operator key in environment variable
|
|
523
|
+
const operatorAccount = privateKeyToAccount(process.env.OPERATOR_KEY!)
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
**โ Unsafe (Frontend):**
|
|
527
|
+
```typescript
|
|
528
|
+
// Never hardcode private keys in frontend code
|
|
529
|
+
const account = privateKeyToAccount('0xHARDCODED') // DON'T DO THIS!
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
**โ
Safe (Frontend with Wallet):**
|
|
533
|
+
```typescript
|
|
534
|
+
// Use wallet client from wagmi/viem
|
|
535
|
+
const { data: walletClient } = useWalletClient()
|
|
536
|
+
const client = new D402Client({
|
|
537
|
+
operatorAccount: walletClient.account
|
|
538
|
+
})
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
### Payment Security
|
|
542
|
+
|
|
543
|
+
- โ
EIP-712 typed signatures prevent tampering
|
|
544
|
+
- โ
Nonce prevents replay attacks
|
|
545
|
+
- โ
Timestamp bounds (validAfter/validBefore)
|
|
546
|
+
- โ
Amount verification
|
|
547
|
+
- โ
Destination address verification
|
|
548
|
+
- โ
maxValue safety limit
|
|
549
|
+
|
|
550
|
+
## ๐ ๏ธ Development
|
|
551
|
+
|
|
552
|
+
### Build
|
|
553
|
+
|
|
554
|
+
```bash
|
|
555
|
+
pnpm build
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
Outputs:
|
|
559
|
+
- `dist/index.js` - CommonJS
|
|
560
|
+
- `dist/index.mjs` - ES Module
|
|
561
|
+
- `dist/index.d.ts` - TypeScript types
|
|
562
|
+
|
|
563
|
+
### Type Checking
|
|
564
|
+
|
|
565
|
+
```bash
|
|
566
|
+
pnpm typecheck
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
## ๐ Related Packages
|
|
570
|
+
|
|
571
|
+
Part of the IATP-JS monorepo:
|
|
572
|
+
- `@iatp/d402-client` - D402 payment protocol (this package)
|
|
573
|
+
- `@iatp/mcp-client` - MCP integration (planned)
|
|
574
|
+
- `@iatp/a2a-client` - A2A protocol (planned)
|
|
575
|
+
|
|
576
|
+
## ๐ Related Projects
|
|
577
|
+
|
|
578
|
+
- [IATP Python](https://github.com/Traia-IO/IATP) - Python implementation
|
|
579
|
+
- [Viem](https://viem.sh) - TypeScript Web3 library
|
|
580
|
+
- [D402 Protocol](https://docs.cdp.coinbase.com/x402) - HTTP 402 specification
|
|
581
|
+
|
|
582
|
+
## ๐ License
|
|
583
|
+
|
|
584
|
+
MIT
|
|
585
|
+
|
|
586
|
+
## ๐ฌ Support
|
|
587
|
+
|
|
588
|
+
- GitHub Issues: [iatp-js/issues](https://github.com/Traia-IO/iatp-js/issues)
|
|
589
|
+
- Email: support@traia.io
|
|
590
|
+
|
|
591
|
+
---
|
|
592
|
+
|
|
593
|
+
**Made with โค๏ธ by the Traia Team**
|
|
594
|
+
|