@voyage_ai/v402-web-ts 0.1.6 → 0.2.1
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 +461 -128
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/react/index.js +1 -1
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +1 -1
- package/dist/react/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -193,12 +193,12 @@ This feature will be provided by the v402pay platform with configurable options
|
|
|
193
193
|
Visit [v402pay Platform](https://v402pay.com) to create your merchant account:
|
|
194
194
|
|
|
195
195
|
1. Register an account
|
|
196
|
-
2. Create
|
|
196
|
+
2. Create checkout configuration:
|
|
197
197
|
- **Callback URL**: API endpoint called after successful payment
|
|
198
198
|
- **Payment Price**: Fee per access (e.g., 0.01 USDC)
|
|
199
|
-
- **Supported Networks**: Solana, Ethereum,
|
|
199
|
+
- **Supported Networks**: Solana, Ethereum, Base, etc.
|
|
200
200
|
- **Recipient Address**: Your receiving wallet address
|
|
201
|
-
3. Get your **
|
|
201
|
+
3. Get your **checkoutId** (for frontend integration)
|
|
202
202
|
|
|
203
203
|
### 2. Install SDK
|
|
204
204
|
|
|
@@ -206,30 +206,78 @@ Visit [v402pay Platform](https://v402pay.com) to create your merchant account:
|
|
|
206
206
|
npm install @voyage_ai/v402-web-ts
|
|
207
207
|
```
|
|
208
208
|
|
|
209
|
-
### 3.
|
|
209
|
+
### 3. Three Usage Methods
|
|
210
210
|
|
|
211
|
-
## Method 1: Use
|
|
211
|
+
## Method 1: Use V402Checkout Component (Recommended, 1 Line of Code)
|
|
212
212
|
|
|
213
|
-
|
|
213
|
+
The easiest way - complete payment UI with wallet connection, payment processing, and result display built-in.
|
|
214
|
+
|
|
215
|
+
```tsx
|
|
216
|
+
import React from 'react';
|
|
217
|
+
import { V402Checkout } from '@voyage_ai/v402-web-ts/react';
|
|
218
|
+
import '@voyage_ai/v402-web-ts/react/styles.css';
|
|
219
|
+
import 'antd/dist/reset.css'; // Ant Design styles
|
|
220
|
+
|
|
221
|
+
export default function PaymentPage() {
|
|
222
|
+
return (
|
|
223
|
+
<V402Checkout
|
|
224
|
+
checkoutId="your-checkout-id" // Get from v402pay platform
|
|
225
|
+
headerInfo={{
|
|
226
|
+
title: 'Premium Content Access',
|
|
227
|
+
subtitle: 'mysite.com',
|
|
228
|
+
tooltipText: 'One-time payment for lifetime access'
|
|
229
|
+
}}
|
|
230
|
+
onPaymentComplete={(result) => {
|
|
231
|
+
console.log('✅ Payment successful!', result);
|
|
232
|
+
// Handle post-payment logic (redirect, show content, etc.)
|
|
233
|
+
}}
|
|
234
|
+
additionalParams={{
|
|
235
|
+
userId: '123',
|
|
236
|
+
// Any custom params to pass to your callback API
|
|
237
|
+
}}
|
|
238
|
+
expectedNetwork="evm" // Optional: 'evm' or 'svm'
|
|
239
|
+
isModal={false} // Optional: true for modal mode, false for full page
|
|
240
|
+
/>
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
**Props:**
|
|
246
|
+
- `checkoutId` (required): Your checkout ID from v402pay platform
|
|
247
|
+
- `headerInfo` (optional): Customize header display
|
|
248
|
+
- `onPaymentComplete` (optional): Callback when payment succeeds
|
|
249
|
+
- `additionalParams` (optional): Custom parameters to pass to your callback API
|
|
250
|
+
- `expectedNetwork` (optional): Force specific network type ('evm' or 'svm')
|
|
251
|
+
- `isModal` (optional): Display as modal (true) or full page (false)
|
|
252
|
+
|
|
253
|
+
## Method 2: Use Built-in Hooks (Custom UI)
|
|
254
|
+
|
|
255
|
+
Perfect for building custom UI while leveraging built-in payment logic.
|
|
214
256
|
|
|
215
257
|
```tsx
|
|
216
258
|
import React from 'react';
|
|
217
259
|
import {
|
|
218
|
-
|
|
260
|
+
usePageNetwork, // Auto-manage network for the page
|
|
219
261
|
usePayment,
|
|
220
262
|
usePaymentInfo,
|
|
221
263
|
WalletConnect
|
|
222
264
|
} from '@voyage_ai/v402-web-ts/react';
|
|
223
|
-
import { makePayment } from '@voyage_ai/v402-web-ts';
|
|
265
|
+
import { makePayment, NetworkType } from '@voyage_ai/v402-web-ts';
|
|
224
266
|
import '@voyage_ai/v402-web-ts/react/styles.css';
|
|
225
267
|
|
|
226
|
-
export default function
|
|
227
|
-
const
|
|
268
|
+
export default function CustomPaymentPage() {
|
|
269
|
+
const checkoutId = 'your-checkout-id';
|
|
270
|
+
|
|
271
|
+
// Fetch payment info to get supported networks
|
|
272
|
+
const { supportedNetworks, isLoading, paymentInfo } = usePaymentInfo(checkoutId);
|
|
273
|
+
|
|
274
|
+
// Auto-manage wallet for this page's expected network
|
|
275
|
+
const { address, networkType, disconnect } = usePageNetwork(
|
|
276
|
+
supportedNetworks[0] || NetworkType.EVM,
|
|
277
|
+
{ autoSwitch: true }
|
|
278
|
+
);
|
|
228
279
|
|
|
229
|
-
// Use React Hooks to manage state
|
|
230
|
-
const { address, networkType } = useWallet();
|
|
231
280
|
const { isProcessing, setIsProcessing, result, setResult, error, setError } = usePayment();
|
|
232
|
-
const { supportedNetworks, isLoading } = usePaymentInfo(merchantId);
|
|
233
281
|
|
|
234
282
|
const handlePayment = async () => {
|
|
235
283
|
if (!networkType) return;
|
|
@@ -238,11 +286,8 @@ export default function PaymentPage() {
|
|
|
238
286
|
setError(null);
|
|
239
287
|
|
|
240
288
|
try {
|
|
241
|
-
|
|
242
|
-
const response = await makePayment(networkType, merchantId);
|
|
289
|
+
const response = await makePayment(networkType, checkoutId);
|
|
243
290
|
const data = await response.json();
|
|
244
|
-
|
|
245
|
-
// Payment successful, data contains your callback API response
|
|
246
291
|
setResult(data);
|
|
247
292
|
console.log('✅ Payment successful!', data);
|
|
248
293
|
} catch (err: any) {
|
|
@@ -256,24 +301,23 @@ export default function PaymentPage() {
|
|
|
256
301
|
<div>
|
|
257
302
|
<h1>Purchase Content</h1>
|
|
258
303
|
|
|
259
|
-
{/* Wallet Connection
|
|
260
|
-
{!isLoading && (
|
|
261
|
-
<WalletConnect
|
|
262
|
-
supportedNetworks={supportedNetworks}
|
|
263
|
-
/>
|
|
304
|
+
{/* Wallet Connection */}
|
|
305
|
+
{!isLoading && !address && (
|
|
306
|
+
<WalletConnect supportedNetworks={supportedNetworks} />
|
|
264
307
|
)}
|
|
265
308
|
|
|
266
|
-
{/*
|
|
309
|
+
{/* Connected State */}
|
|
267
310
|
{address && (
|
|
268
|
-
<
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
311
|
+
<div>
|
|
312
|
+
<p>Connected: {address}</p>
|
|
313
|
+
<button onClick={disconnect}>Disconnect</button>
|
|
314
|
+
<button onClick={handlePayment} disabled={isProcessing}>
|
|
315
|
+
{isProcessing ? 'Processing...' : 'Pay Now'}
|
|
316
|
+
</button>
|
|
317
|
+
</div>
|
|
274
318
|
)}
|
|
275
319
|
|
|
276
|
-
{/*
|
|
320
|
+
{/* Result */}
|
|
277
321
|
{result && (
|
|
278
322
|
<div>
|
|
279
323
|
<h2>Payment Successful! 🎉</h2>
|
|
@@ -287,9 +331,9 @@ export default function PaymentPage() {
|
|
|
287
331
|
}
|
|
288
332
|
```
|
|
289
333
|
|
|
290
|
-
## Method
|
|
334
|
+
## Method 3: Direct Handler Integration (Advanced)
|
|
291
335
|
|
|
292
|
-
If you
|
|
336
|
+
If you have your own wallet connection logic, directly call the payment handler functions.
|
|
293
337
|
|
|
294
338
|
```typescript
|
|
295
339
|
import {
|
|
@@ -300,20 +344,24 @@ import {
|
|
|
300
344
|
|
|
301
345
|
// Solana Payment Example
|
|
302
346
|
async function paySolana() {
|
|
303
|
-
const
|
|
347
|
+
const checkoutId = 'your-checkout-id';
|
|
348
|
+
const endpoint = 'https://v402.onvoyage.ai/api/pay';
|
|
304
349
|
|
|
305
350
|
// Ensure user has connected Phantom wallet
|
|
306
351
|
const wallet = window.solana;
|
|
307
|
-
if (!wallet) {
|
|
308
|
-
|
|
352
|
+
if (!wallet?.isConnected) {
|
|
353
|
+
await wallet.connect();
|
|
309
354
|
}
|
|
310
355
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
// Call SVM payment
|
|
356
|
+
// Call SVM payment handler
|
|
314
357
|
const response = await handleSvmPayment(
|
|
315
|
-
|
|
316
|
-
|
|
358
|
+
endpoint,
|
|
359
|
+
{
|
|
360
|
+
wallet,
|
|
361
|
+
network: 'solana-mainnet', // or 'solana-devnet'
|
|
362
|
+
checkoutId,
|
|
363
|
+
additionalParams: { userId: '123' } // Optional
|
|
364
|
+
}
|
|
317
365
|
);
|
|
318
366
|
|
|
319
367
|
const result = await response.json();
|
|
@@ -322,31 +370,29 @@ async function paySolana() {
|
|
|
322
370
|
|
|
323
371
|
// Ethereum Payment Example
|
|
324
372
|
async function payEthereum() {
|
|
325
|
-
const
|
|
326
|
-
const
|
|
373
|
+
const checkoutId = 'your-checkout-id';
|
|
374
|
+
const endpoint = 'https://v402.onvoyage.ai/api/pay';
|
|
327
375
|
|
|
328
|
-
//
|
|
329
|
-
const provider = new ethers.BrowserProvider(window.ethereum);
|
|
330
|
-
const signer = await provider.getSigner();
|
|
331
|
-
|
|
332
|
-
// Create wallet adapter
|
|
376
|
+
// Get wallet adapter
|
|
333
377
|
const walletAdapter = {
|
|
334
|
-
|
|
335
|
-
signTypedData: async (domain: any, types: any,
|
|
336
|
-
|
|
378
|
+
address: await window.ethereum.request({ method: 'eth_requestAccounts' })[0],
|
|
379
|
+
signTypedData: async (domain: any, types: any, message: any) => {
|
|
380
|
+
// Implement EIP-712 signing
|
|
337
381
|
},
|
|
338
382
|
switchChain: async (chainId: string) => {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
params: [{ chainId }],
|
|
342
|
-
});
|
|
343
|
-
},
|
|
383
|
+
// Implement chain switching
|
|
384
|
+
}
|
|
344
385
|
};
|
|
345
386
|
|
|
346
|
-
// Call EVM payment
|
|
387
|
+
// Call EVM payment handler
|
|
347
388
|
const response = await handleEvmPayment(
|
|
348
|
-
|
|
349
|
-
|
|
389
|
+
endpoint,
|
|
390
|
+
{
|
|
391
|
+
wallet: walletAdapter,
|
|
392
|
+
network: 'base-mainnet', // or 'ethereum-mainnet', 'polygon-mainnet', etc.
|
|
393
|
+
checkoutId,
|
|
394
|
+
additionalParams: { userId: '123' } // Optional
|
|
395
|
+
}
|
|
350
396
|
);
|
|
351
397
|
|
|
352
398
|
const result = await response.json();
|
|
@@ -360,7 +406,7 @@ async function payEthereum() {
|
|
|
360
406
|
|
|
361
407
|
#### `useWallet()`
|
|
362
408
|
|
|
363
|
-
Manage wallet connection state.
|
|
409
|
+
Manage wallet connection state (external store, no Provider needed).
|
|
364
410
|
|
|
365
411
|
```typescript
|
|
366
412
|
const {
|
|
@@ -368,54 +414,129 @@ const {
|
|
|
368
414
|
networkType, // Network type (NetworkType | null)
|
|
369
415
|
isConnecting, // Is connecting (boolean)
|
|
370
416
|
error, // Error message (string | null)
|
|
371
|
-
connect, // Connect wallet
|
|
372
|
-
|
|
373
|
-
|
|
417
|
+
connect, // Connect wallet: (networkType: NetworkType) => Promise<void>
|
|
418
|
+
switchNetwork, // Switch network: (networkType: NetworkType) => Promise<void>
|
|
419
|
+
ensureNetwork, // Ensure network: (networkType: NetworkType) => Promise<void>
|
|
420
|
+
disconnect, // Disconnect wallet: () => void
|
|
421
|
+
clearError // Clear error: () => void
|
|
374
422
|
} = useWallet();
|
|
375
423
|
```
|
|
376
424
|
|
|
425
|
+
**Features:**
|
|
426
|
+
- ✅ No Context Provider needed (uses React 18's `useSyncExternalStore`)
|
|
427
|
+
- ✅ Supports EVM and SVM wallet coexistence
|
|
428
|
+
- ✅ Auto-persists wallet addresses per network
|
|
429
|
+
- ✅ Prevents auto-reconnect after manual disconnect
|
|
430
|
+
|
|
431
|
+
#### `usePageNetwork(expectedNetwork, options?)`
|
|
432
|
+
|
|
433
|
+
Page-level network management - automatically ensures correct network for the page.
|
|
434
|
+
|
|
435
|
+
```typescript
|
|
436
|
+
const wallet = usePageNetwork(
|
|
437
|
+
NetworkType.EVM, // Expected network type for this page
|
|
438
|
+
{
|
|
439
|
+
autoSwitch: true, // Auto-switch to expected network (default: true)
|
|
440
|
+
switchOnMount: true, // Switch on component mount (default: true)
|
|
441
|
+
}
|
|
442
|
+
);
|
|
443
|
+
// Returns same interface as useWallet()
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
**Use Cases:**
|
|
447
|
+
- EVM-only pages: `usePageNetwork(NetworkType.EVM)`
|
|
448
|
+
- SVM-only pages: `usePageNetwork(NetworkType.SVM)`
|
|
449
|
+
- Automatic network management when switching between pages
|
|
450
|
+
|
|
451
|
+
**Example:**
|
|
452
|
+
```tsx
|
|
453
|
+
// Page A - EVM only
|
|
454
|
+
function EvmPage() {
|
|
455
|
+
const { address } = usePageNetwork(NetworkType.EVM);
|
|
456
|
+
return <div>EVM Address: {address}</div>;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// Page B - SVM only
|
|
460
|
+
function SvmPage() {
|
|
461
|
+
const { address } = usePageNetwork(NetworkType.SVM);
|
|
462
|
+
return <div>SVM Address: {address}</div>;
|
|
463
|
+
}
|
|
464
|
+
```
|
|
465
|
+
|
|
377
466
|
#### `usePayment()`
|
|
378
467
|
|
|
379
|
-
Manage payment state.
|
|
468
|
+
Manage payment state (state only, no payment logic).
|
|
380
469
|
|
|
381
470
|
```typescript
|
|
382
471
|
const {
|
|
383
|
-
isProcessing,
|
|
384
|
-
result,
|
|
385
|
-
error,
|
|
472
|
+
isProcessing, // Is processing (boolean)
|
|
473
|
+
result, // Payment result (any)
|
|
474
|
+
error, // Error message (string | null)
|
|
386
475
|
setIsProcessing, // Set processing state
|
|
387
|
-
setResult,
|
|
388
|
-
setError,
|
|
389
|
-
clearResult,
|
|
390
|
-
clearError,
|
|
391
|
-
reset
|
|
476
|
+
setResult, // Set result
|
|
477
|
+
setError, // Set error
|
|
478
|
+
clearResult, // Clear result
|
|
479
|
+
clearError, // Clear error
|
|
480
|
+
reset // Reset all states
|
|
392
481
|
} = usePayment();
|
|
393
482
|
```
|
|
394
483
|
|
|
395
|
-
#### `usePaymentInfo(
|
|
484
|
+
#### `usePaymentInfo(checkoutId, endpoint?, additionalParams?)`
|
|
396
485
|
|
|
397
|
-
Fetch
|
|
486
|
+
Fetch checkout payment configuration from backend.
|
|
398
487
|
|
|
399
488
|
```typescript
|
|
400
489
|
const {
|
|
401
|
-
supportedNetworks, // Supported
|
|
490
|
+
supportedNetworks, // Supported network types (NetworkType[])
|
|
402
491
|
isLoading, // Is loading (boolean)
|
|
403
|
-
error // Error message (string | null)
|
|
404
|
-
|
|
492
|
+
error, // Error message (string | null)
|
|
493
|
+
paymentInfo // Raw payment info from backend (any)
|
|
494
|
+
} = usePaymentInfo(
|
|
495
|
+
'your-checkout-id',
|
|
496
|
+
'https://v402.onvoyage.ai/api/pay', // Optional
|
|
497
|
+
{ userId: '123' } // Optional
|
|
498
|
+
);
|
|
405
499
|
```
|
|
406
500
|
|
|
407
501
|
### React Components
|
|
408
502
|
|
|
503
|
+
#### `<V402Checkout />`
|
|
504
|
+
|
|
505
|
+
Complete checkout component with built-in wallet connection, payment UI, and result display.
|
|
506
|
+
|
|
507
|
+
```tsx
|
|
508
|
+
<V402Checkout
|
|
509
|
+
checkoutId="your-checkout-id" // Required
|
|
510
|
+
headerInfo={{ // Optional
|
|
511
|
+
title: 'Payment Title',
|
|
512
|
+
subtitle: 'yoursite.com',
|
|
513
|
+
tooltipText: 'Payment description'
|
|
514
|
+
}}
|
|
515
|
+
isModal={false} // Optional: modal or full page
|
|
516
|
+
onPaymentComplete={(result) => {}} // Optional: callback
|
|
517
|
+
additionalParams={{ userId: '123' }} // Optional: custom params
|
|
518
|
+
expectedNetwork={NetworkType.EVM} // Optional: force network
|
|
519
|
+
/>
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
**Props:**
|
|
523
|
+
- `checkoutId` (required): Your checkout ID from v402pay
|
|
524
|
+
- `headerInfo` (optional): Customize header display
|
|
525
|
+
- `isModal` (optional): Display as modal (true) or full page (false)
|
|
526
|
+
- `onPaymentComplete` (optional): Callback function on successful payment
|
|
527
|
+
- `additionalParams` (optional): Custom parameters to pass to callback API
|
|
528
|
+
- `expectedNetwork` (optional): Force specific network type
|
|
529
|
+
|
|
409
530
|
#### `<WalletConnect />`
|
|
410
531
|
|
|
411
|
-
|
|
532
|
+
Standalone wallet connection component (used internally by V402Checkout).
|
|
412
533
|
|
|
413
534
|
```tsx
|
|
414
535
|
<WalletConnect
|
|
415
|
-
supportedNetworks={[NetworkType.SOLANA, NetworkType.EVM]}
|
|
416
|
-
className="custom-class"
|
|
417
|
-
onConnect={(address, networkType) => {}}
|
|
418
|
-
onDisconnect={() => {}}
|
|
536
|
+
supportedNetworks={[NetworkType.SOLANA, NetworkType.EVM]}
|
|
537
|
+
className="custom-class"
|
|
538
|
+
onConnect={(address, networkType) => {}}
|
|
539
|
+
onDisconnect={() => {}}
|
|
419
540
|
/>
|
|
420
541
|
```
|
|
421
542
|
|
|
@@ -427,7 +548,7 @@ Ready-to-use wallet connection component.
|
|
|
427
548
|
|
|
428
549
|
### Core Functions
|
|
429
550
|
|
|
430
|
-
#### `makePayment(networkType,
|
|
551
|
+
#### `makePayment(networkType, checkoutId, endpoint?, additionalParams?)`
|
|
431
552
|
|
|
432
553
|
Unified payment entry function that automatically handles different chain payment logic.
|
|
433
554
|
|
|
@@ -435,14 +556,16 @@ Unified payment entry function that automatically handles different chain paymen
|
|
|
435
556
|
import { makePayment, NetworkType } from '@voyage_ai/v402-web-ts';
|
|
436
557
|
|
|
437
558
|
const response = await makePayment(
|
|
438
|
-
NetworkType.
|
|
439
|
-
'your-
|
|
559
|
+
NetworkType.EVM, // or NetworkType.SVM
|
|
560
|
+
'your-checkout-id',
|
|
561
|
+
'https://v402.onvoyage.ai/api/pay', // Optional
|
|
562
|
+
{ userId: '123' } // Optional
|
|
440
563
|
);
|
|
441
564
|
|
|
442
565
|
const result = await response.json();
|
|
443
566
|
```
|
|
444
567
|
|
|
445
|
-
#### `handleSvmPayment(
|
|
568
|
+
#### `handleSvmPayment(endpoint, options)`
|
|
446
569
|
|
|
447
570
|
Handle Solana (SVM) chain payments.
|
|
448
571
|
|
|
@@ -450,12 +573,23 @@ Handle Solana (SVM) chain payments.
|
|
|
450
573
|
import { handleSvmPayment } from '@voyage_ai/v402-web-ts';
|
|
451
574
|
|
|
452
575
|
const response = await handleSvmPayment(
|
|
453
|
-
'
|
|
454
|
-
|
|
576
|
+
'https://v402.onvoyage.ai/api/pay',
|
|
577
|
+
{
|
|
578
|
+
wallet: window.solana, // Phantom wallet
|
|
579
|
+
network: 'solana-mainnet', // or 'solana-devnet'
|
|
580
|
+
checkoutId: 'your-checkout-id',
|
|
581
|
+
additionalParams: { userId: '123' } // Optional
|
|
582
|
+
}
|
|
455
583
|
);
|
|
456
584
|
```
|
|
457
585
|
|
|
458
|
-
|
|
586
|
+
**Options:**
|
|
587
|
+
- `wallet`: Solana wallet adapter (Phantom, Solflare, etc.)
|
|
588
|
+
- `network`: Network name (e.g., 'solana-mainnet', 'solana-devnet')
|
|
589
|
+
- `checkoutId`: Your checkout ID
|
|
590
|
+
- `additionalParams`: Custom parameters for your callback API
|
|
591
|
+
|
|
592
|
+
#### `handleEvmPayment(endpoint, options)`
|
|
459
593
|
|
|
460
594
|
Handle Ethereum (EVM) chain payments.
|
|
461
595
|
|
|
@@ -463,27 +597,54 @@ Handle Ethereum (EVM) chain payments.
|
|
|
463
597
|
import { handleEvmPayment } from '@voyage_ai/v402-web-ts';
|
|
464
598
|
|
|
465
599
|
const response = await handleEvmPayment(
|
|
466
|
-
'
|
|
467
|
-
|
|
600
|
+
'https://v402.onvoyage.ai/api/pay',
|
|
601
|
+
{
|
|
602
|
+
wallet: evmWalletAdapter, // EVM wallet adapter
|
|
603
|
+
network: 'base-mainnet', // Network name
|
|
604
|
+
checkoutId: 'your-checkout-id',
|
|
605
|
+
additionalParams: { userId: '123' } // Optional
|
|
606
|
+
}
|
|
468
607
|
);
|
|
469
608
|
```
|
|
470
609
|
|
|
610
|
+
**Options:**
|
|
611
|
+
- `wallet`: EVM wallet adapter (must implement EvmWalletAdapter interface)
|
|
612
|
+
- `network`: Network name (e.g., 'ethereum-mainnet', 'base-mainnet', 'polygon-mainnet')
|
|
613
|
+
- `checkoutId`: Your checkout ID
|
|
614
|
+
- `additionalParams`: Custom parameters for your callback API
|
|
615
|
+
|
|
471
616
|
### Type Definitions
|
|
472
617
|
|
|
473
618
|
```typescript
|
|
474
|
-
// Network Type
|
|
619
|
+
// Network Type (用于区分钱包类型)
|
|
475
620
|
enum NetworkType {
|
|
476
|
-
|
|
477
|
-
SVM = 'svm',
|
|
478
|
-
|
|
479
|
-
ETHEREUM = 'ethereum'
|
|
621
|
+
EVM = 'evm', // Ethereum Virtual Machine
|
|
622
|
+
SVM = 'svm', // Solana Virtual Machine
|
|
623
|
+
SOLANA = 'solana' // Alias for SVM
|
|
480
624
|
}
|
|
481
625
|
|
|
482
|
-
// Wallet Adapter Interface
|
|
626
|
+
// Solana Wallet Adapter Interface
|
|
483
627
|
interface WalletAdapter {
|
|
484
|
-
|
|
485
|
-
|
|
628
|
+
publicKey?: { toString(): string };
|
|
629
|
+
address?: string;
|
|
630
|
+
signTransaction: (tx: VersionedTransaction) => Promise<VersionedTransaction>;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// EVM Wallet Adapter Interface
|
|
634
|
+
interface EvmWalletAdapter {
|
|
635
|
+
address: string;
|
|
636
|
+
signTypedData: (domain: any, types: any, message: any) => Promise<string>;
|
|
486
637
|
switchChain?: (chainId: string) => Promise<void>;
|
|
638
|
+
getChainId?: () => Promise<string>;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
// Payment Info from Backend
|
|
642
|
+
interface PaymentInfo {
|
|
643
|
+
amount: string;
|
|
644
|
+
currency: string;
|
|
645
|
+
network: string; // Network name (e.g., 'base-mainnet', 'solana-devnet')
|
|
646
|
+
asset: string;
|
|
647
|
+
maxAmountRequired: number;
|
|
487
648
|
}
|
|
488
649
|
```
|
|
489
650
|
|
|
@@ -517,6 +678,46 @@ import { makePayment } from '@voyage_ai/v402-web-ts';
|
|
|
517
678
|
|
|
518
679
|
## 🔧 Advanced Usage
|
|
519
680
|
|
|
681
|
+
### Multi-Network Support in One App
|
|
682
|
+
|
|
683
|
+
The SDK supports EVM and SVM wallets coexisting in the same application.
|
|
684
|
+
|
|
685
|
+
```tsx
|
|
686
|
+
import { usePageNetwork, NetworkType } from '@voyage_ai/v402-web-ts/react';
|
|
687
|
+
|
|
688
|
+
// Page 1: EVM Network
|
|
689
|
+
function EthereumPage() {
|
|
690
|
+
const { address, networkType } = usePageNetwork(NetworkType.EVM);
|
|
691
|
+
|
|
692
|
+
return (
|
|
693
|
+
<div>
|
|
694
|
+
<h2>Ethereum Payment Page</h2>
|
|
695
|
+
<p>Connected: {address}</p>
|
|
696
|
+
{/* EVM payment logic */}
|
|
697
|
+
</div>
|
|
698
|
+
);
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// Page 2: SVM Network
|
|
702
|
+
function SolanaPage() {
|
|
703
|
+
const { address, networkType } = usePageNetwork(NetworkType.SVM);
|
|
704
|
+
|
|
705
|
+
return (
|
|
706
|
+
<div>
|
|
707
|
+
<h2>Solana Payment Page</h2>
|
|
708
|
+
<p>Connected: {address}</p>
|
|
709
|
+
{/* SVM payment logic */}
|
|
710
|
+
</div>
|
|
711
|
+
);
|
|
712
|
+
}
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
**Key Features:**
|
|
716
|
+
- ✅ Wallets cached separately per network
|
|
717
|
+
- ✅ Switching pages automatically switches wallet
|
|
718
|
+
- ✅ Disconnect on one network doesn't affect the other
|
|
719
|
+
- ✅ Refresh page maintains correct network context
|
|
720
|
+
|
|
520
721
|
### Monitor Wallet State Changes
|
|
521
722
|
|
|
522
723
|
```typescript
|
|
@@ -535,15 +736,17 @@ function MyComponent() {
|
|
|
535
736
|
}
|
|
536
737
|
```
|
|
537
738
|
|
|
538
|
-
### Handle Payment
|
|
739
|
+
### Handle Payment Response
|
|
740
|
+
|
|
741
|
+
The payment response includes both your callback API data and payment metadata:
|
|
539
742
|
|
|
540
743
|
```typescript
|
|
541
744
|
const handlePayment = async () => {
|
|
542
745
|
try {
|
|
543
|
-
const response = await makePayment(networkType,
|
|
746
|
+
const response = await makePayment(networkType, checkoutId);
|
|
544
747
|
const data = await response.json();
|
|
545
748
|
|
|
546
|
-
// data
|
|
749
|
+
// data is your callback API response
|
|
547
750
|
// For example:
|
|
548
751
|
// {
|
|
549
752
|
// "success": true,
|
|
@@ -552,12 +755,17 @@ const handlePayment = async () => {
|
|
|
552
755
|
// "expiresAt": "2024-12-31"
|
|
553
756
|
// }
|
|
554
757
|
|
|
758
|
+
// Payment metadata in response headers
|
|
759
|
+
const paymentResponse = response.headers.get('X-PAYMENT-RESPONSE');
|
|
760
|
+
if (paymentResponse) {
|
|
761
|
+
const paymentData = JSON.parse(paymentResponse);
|
|
762
|
+
console.log('Transaction Hash:', paymentData.txHash);
|
|
763
|
+
}
|
|
764
|
+
|
|
555
765
|
if (data.success) {
|
|
556
|
-
// Handle success logic
|
|
557
766
|
showContent(data.content);
|
|
558
767
|
}
|
|
559
768
|
} catch (error) {
|
|
560
|
-
// Handle error
|
|
561
769
|
console.error('Payment failed:', error);
|
|
562
770
|
}
|
|
563
771
|
};
|
|
@@ -566,55 +774,117 @@ const handlePayment = async () => {
|
|
|
566
774
|
### Error Handling
|
|
567
775
|
|
|
568
776
|
```typescript
|
|
569
|
-
import { usePayment } from '@voyage_ai/v402-web-ts/react';
|
|
777
|
+
import { useWallet, usePayment } from '@voyage_ai/v402-web-ts/react';
|
|
778
|
+
import { useEffect } from 'react';
|
|
570
779
|
|
|
571
780
|
function PaymentComponent() {
|
|
572
|
-
const { error, clearError } =
|
|
781
|
+
const { error: walletError, clearError: clearWalletError } = useWallet();
|
|
782
|
+
const { error: paymentError, clearError: clearPaymentError } = usePayment();
|
|
783
|
+
|
|
784
|
+
useEffect(() => {
|
|
785
|
+
if (walletError) {
|
|
786
|
+
console.error('Wallet error:', walletError);
|
|
787
|
+
// Handle wallet connection errors
|
|
788
|
+
}
|
|
789
|
+
}, [walletError]);
|
|
573
790
|
|
|
574
791
|
useEffect(() => {
|
|
575
|
-
if (
|
|
576
|
-
|
|
577
|
-
|
|
792
|
+
if (paymentError) {
|
|
793
|
+
console.error('Payment error:', paymentError);
|
|
794
|
+
// Handle payment errors
|
|
578
795
|
|
|
579
|
-
// Auto clear
|
|
796
|
+
// Auto clear after 3 seconds
|
|
580
797
|
setTimeout(() => {
|
|
581
|
-
|
|
798
|
+
clearPaymentError();
|
|
582
799
|
}, 3000);
|
|
583
800
|
}
|
|
584
|
-
}, [
|
|
801
|
+
}, [paymentError]);
|
|
585
802
|
}
|
|
586
803
|
```
|
|
587
804
|
|
|
588
|
-
|
|
805
|
+
**Common Error Scenarios:**
|
|
806
|
+
- Wallet not installed: `Please install MetaMask/Phantom wallet`
|
|
807
|
+
- User rejected: `User rejected the connection/transaction`
|
|
808
|
+
- Network mismatch: `Chain changed. Please reconnect your wallet.`
|
|
809
|
+
- Insufficient funds: Backend returns payment failure
|
|
810
|
+
- Invalid checkout: `Failed to load payment information`
|
|
589
811
|
|
|
590
|
-
|
|
591
|
-
- Solana Mainnet
|
|
592
|
-
- Solana Devnet
|
|
812
|
+
## 🌐 Network Types vs Network Names
|
|
593
813
|
|
|
594
|
-
###
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
-
|
|
600
|
-
|
|
601
|
-
|
|
814
|
+
### Important Distinction
|
|
815
|
+
|
|
816
|
+
The SDK distinguishes between **Network Type** (wallet type) and **Network Name** (specific blockchain):
|
|
817
|
+
|
|
818
|
+
#### Network Type (钱包类型)
|
|
819
|
+
Used for wallet management - which wallet to connect:
|
|
820
|
+
|
|
821
|
+
```typescript
|
|
822
|
+
enum NetworkType {
|
|
823
|
+
EVM = 'evm', // Ethereum Virtual Machine wallets (MetaMask, etc.)
|
|
824
|
+
SVM = 'svm', // Solana Virtual Machine wallets (Phantom, etc.)
|
|
825
|
+
SOLANA = 'solana' // Alias for SVM
|
|
826
|
+
}
|
|
827
|
+
```
|
|
828
|
+
|
|
829
|
+
**Usage:** `usePageNetwork(NetworkType.EVM)`, `connect(NetworkType.SVM)`
|
|
830
|
+
|
|
831
|
+
#### Network Name (具体网络)
|
|
832
|
+
Specific blockchain for the payment transaction:
|
|
833
|
+
|
|
834
|
+
**SVM Networks:**
|
|
835
|
+
- `solana-mainnet`
|
|
836
|
+
- `solana-devnet`
|
|
837
|
+
|
|
838
|
+
**EVM Networks:**
|
|
839
|
+
- `ethereum-mainnet`
|
|
840
|
+
- `base-mainnet`
|
|
841
|
+
- `polygon-mainnet`
|
|
842
|
+
- `avalanche-mainnet`
|
|
843
|
+
- `bsc-mainnet` (Binance Smart Chain)
|
|
844
|
+
- `arbitrum-mainnet`
|
|
845
|
+
- `optimism-mainnet`
|
|
602
846
|
- And other EVM-compatible chains
|
|
603
847
|
|
|
848
|
+
**Usage:** Used in `handleSvmPayment()`, `handleEvmPayment()`, and backend payment configuration
|
|
849
|
+
|
|
850
|
+
### Example
|
|
851
|
+
|
|
852
|
+
```typescript
|
|
853
|
+
// Network Type: EVM (which wallet to use)
|
|
854
|
+
const { address } = usePageNetwork(NetworkType.EVM);
|
|
855
|
+
|
|
856
|
+
// Network Name: base-mainnet (which blockchain to pay on)
|
|
857
|
+
const response = await handleEvmPayment(endpoint, {
|
|
858
|
+
wallet: evmAdapter,
|
|
859
|
+
network: 'base-mainnet', // ← Network Name
|
|
860
|
+
checkoutId: 'xxx'
|
|
861
|
+
});
|
|
862
|
+
```
|
|
863
|
+
|
|
604
864
|
## 📦 Dependencies
|
|
605
865
|
|
|
606
|
-
###
|
|
866
|
+
### Required Dependencies
|
|
607
867
|
|
|
608
868
|
```json
|
|
609
869
|
{
|
|
610
870
|
"react": ">=18.0.0",
|
|
611
871
|
"@solana/web3.js": "^1.95.0",
|
|
612
872
|
"@solana/spl-token": "^0.4.0",
|
|
613
|
-
"
|
|
873
|
+
"antd": "^5.0.0" // For V402Checkout component
|
|
874
|
+
}
|
|
875
|
+
```
|
|
876
|
+
|
|
877
|
+
### Optional Dependencies
|
|
878
|
+
|
|
879
|
+
```json
|
|
880
|
+
{
|
|
881
|
+
"ethers": "^6.0.0" // Only needed if using EVM networks
|
|
614
882
|
}
|
|
615
883
|
```
|
|
616
884
|
|
|
617
|
-
**Note**:
|
|
885
|
+
**Note**:
|
|
886
|
+
- If you only use `V402Checkout` component, all dependencies are included
|
|
887
|
+
- For custom integration, install only the wallets you need (Solana or EVM)
|
|
618
888
|
|
|
619
889
|
## 🤝 Contributing
|
|
620
890
|
|
|
@@ -630,9 +900,72 @@ MIT License
|
|
|
630
900
|
- [GitHub Repository](https://github.com/voyage_ai/v402-web-ts)
|
|
631
901
|
- [npm Package](https://www.npmjs.com/package/@voyage_ai/v402-web-ts)
|
|
632
902
|
|
|
633
|
-
## 💡
|
|
903
|
+
## 💡 Quick Examples
|
|
904
|
+
|
|
905
|
+
### Minimal Integration (1 Line)
|
|
906
|
+
|
|
907
|
+
```tsx
|
|
908
|
+
import { V402Checkout } from '@voyage_ai/v402-web-ts/react';
|
|
909
|
+
import '@voyage_ai/v402-web-ts/react/styles.css';
|
|
910
|
+
|
|
911
|
+
export default function App() {
|
|
912
|
+
return <V402Checkout checkoutId="your-checkout-id" />;
|
|
913
|
+
}
|
|
914
|
+
```
|
|
915
|
+
|
|
916
|
+
### With Custom Callback
|
|
634
917
|
|
|
635
|
-
|
|
918
|
+
```tsx
|
|
919
|
+
import { V402Checkout } from '@voyage_ai/v402-web-ts/react';
|
|
920
|
+
|
|
921
|
+
export default function App() {
|
|
922
|
+
return (
|
|
923
|
+
<V402Checkout
|
|
924
|
+
checkoutId="your-checkout-id"
|
|
925
|
+
onPaymentComplete={(result) => {
|
|
926
|
+
// Redirect to success page
|
|
927
|
+
window.location.href = `/content/${result.contentId}`;
|
|
928
|
+
}}
|
|
929
|
+
/>
|
|
930
|
+
);
|
|
931
|
+
}
|
|
932
|
+
```
|
|
933
|
+
|
|
934
|
+
### Modal Mode
|
|
935
|
+
|
|
936
|
+
```tsx
|
|
937
|
+
import { useState } from 'react';
|
|
938
|
+
import { V402Checkout } from '@voyage_ai/v402-web-ts/react';
|
|
939
|
+
import { Modal } from 'antd';
|
|
940
|
+
|
|
941
|
+
export default function App() {
|
|
942
|
+
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
943
|
+
|
|
944
|
+
return (
|
|
945
|
+
<>
|
|
946
|
+
<button onClick={() => setIsModalOpen(true)}>
|
|
947
|
+
Purchase Content
|
|
948
|
+
</button>
|
|
949
|
+
|
|
950
|
+
<Modal
|
|
951
|
+
open={isModalOpen}
|
|
952
|
+
onCancel={() => setIsModalOpen(false)}
|
|
953
|
+
footer={null}
|
|
954
|
+
width={520}
|
|
955
|
+
>
|
|
956
|
+
<V402Checkout
|
|
957
|
+
checkoutId="your-checkout-id"
|
|
958
|
+
isModal={true}
|
|
959
|
+
onPaymentComplete={(result) => {
|
|
960
|
+
setIsModalOpen(false);
|
|
961
|
+
// Handle success
|
|
962
|
+
}}
|
|
963
|
+
/>
|
|
964
|
+
</Modal>
|
|
965
|
+
</>
|
|
966
|
+
);
|
|
967
|
+
}
|
|
968
|
+
```
|
|
636
969
|
|
|
637
970
|
---
|
|
638
971
|
|