@voyage_ai/v402-web-ts 0.1.2 → 0.1.3
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/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +139 -98
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +122 -80
- package/dist/index.mjs.map +1 -1
- package/dist/react/index.d.mts +50 -1
- package/dist/react/index.d.ts +50 -1
- package/dist/react/index.js +1559 -136
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +1541 -112
- package/dist/react/index.mjs.map +1 -1
- package/dist/react/styles.css +1 -168
- package/package.json +33 -10
- package/dist/react/components/WalletConnect.tsx +0 -152
- package/dist/react/hooks/usePayment.ts +0 -109
- package/dist/react/hooks/usePaymentInfo.ts +0 -128
- package/dist/react/hooks/useWallet.ts +0 -174
- package/dist/react/hooks/useWalletStore.ts +0 -61
- package/dist/react/index.ts +0 -38
- package/dist/react/store/walletStore.ts +0 -181
- package/dist/react/styles/inline-styles.ts +0 -238
package/dist/react/styles.css
CHANGED
|
@@ -1,168 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* x402 React Components - Default Styles
|
|
3
|
-
*
|
|
4
|
-
* ⚠️ DEPRECATED: This CSS file is no longer needed!
|
|
5
|
-
*
|
|
6
|
-
* All styles are now inline within the components.
|
|
7
|
-
* You can safely remove any imports of this file:
|
|
8
|
-
* - import '@voyage_ai/v402-web-ts/react/styles.css' ❌
|
|
9
|
-
* - import '@voyage_ai/v402-web-ts/styles.css' ❌
|
|
10
|
-
*
|
|
11
|
-
* Just use the components directly - styles are built-in! ✅
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
.x402-wallet-connect {
|
|
15
|
-
width: 100%;
|
|
16
|
-
max-width: 500px;
|
|
17
|
-
margin: 0 auto;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
.x402-wallet-section,
|
|
21
|
-
.x402-wallet-info {
|
|
22
|
-
padding: 2rem;
|
|
23
|
-
border: 1px solid #e0e0e0;
|
|
24
|
-
border-radius: 8px;
|
|
25
|
-
background: #ffffff;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
.x402-section-title {
|
|
29
|
-
margin: 0 0 1.5rem 0;
|
|
30
|
-
font-size: 1.5rem;
|
|
31
|
-
font-weight: 600;
|
|
32
|
-
text-align: center;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
.x402-wallet-buttons {
|
|
36
|
-
display: flex;
|
|
37
|
-
flex-direction: column;
|
|
38
|
-
gap: 1rem;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
.x402-wallet-option {
|
|
42
|
-
display: flex;
|
|
43
|
-
flex-direction: column;
|
|
44
|
-
gap: 0.5rem;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
.x402-connect-button,
|
|
48
|
-
.x402-disconnect-button,
|
|
49
|
-
.x402-pay-button {
|
|
50
|
-
padding: 0.75rem 1.5rem;
|
|
51
|
-
font-size: 1rem;
|
|
52
|
-
font-weight: 500;
|
|
53
|
-
border: none;
|
|
54
|
-
border-radius: 6px;
|
|
55
|
-
cursor: pointer;
|
|
56
|
-
transition: all 0.2s;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
.x402-connect-button {
|
|
60
|
-
background: #0070f3;
|
|
61
|
-
color: white;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
.x402-connect-button:hover:not(:disabled) {
|
|
65
|
-
background: #0051cc;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
.x402-connect-button:disabled {
|
|
69
|
-
background: #ccc;
|
|
70
|
-
cursor: not-allowed;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
.x402-disconnect-button {
|
|
74
|
-
background: #ff4444;
|
|
75
|
-
color: white;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
.x402-disconnect-button:hover {
|
|
79
|
-
background: #cc0000;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
.x402-pay-button {
|
|
83
|
-
background: #00d084;
|
|
84
|
-
color: white;
|
|
85
|
-
width: 100%;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
.x402-pay-button:hover:not(:disabled) {
|
|
89
|
-
background: #00a869;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
.x402-pay-button:disabled {
|
|
93
|
-
background: #ccc;
|
|
94
|
-
cursor: not-allowed;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
.x402-install-link {
|
|
98
|
-
display: inline-block;
|
|
99
|
-
padding: 0.5rem;
|
|
100
|
-
font-size: 0.875rem;
|
|
101
|
-
color: #0070f3;
|
|
102
|
-
text-decoration: none;
|
|
103
|
-
text-align: center;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
.x402-install-link:hover {
|
|
107
|
-
text-decoration: underline;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
.x402-wallet-address {
|
|
111
|
-
display: flex;
|
|
112
|
-
flex-direction: column;
|
|
113
|
-
gap: 0.5rem;
|
|
114
|
-
margin-bottom: 1rem;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
.x402-wallet-label {
|
|
118
|
-
font-size: 0.875rem;
|
|
119
|
-
color: #666;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
.x402-address {
|
|
123
|
-
font-family: monospace;
|
|
124
|
-
font-size: 1rem;
|
|
125
|
-
font-weight: 500;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
.x402-wallet-actions {
|
|
129
|
-
margin: 1rem 0;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
.x402-hint {
|
|
133
|
-
margin-top: 1rem;
|
|
134
|
-
font-size: 0.875rem;
|
|
135
|
-
color: #666;
|
|
136
|
-
text-align: center;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
.x402-error {
|
|
140
|
-
margin-top: 1rem;
|
|
141
|
-
padding: 0.75rem;
|
|
142
|
-
background: #ffe0e0;
|
|
143
|
-
color: #cc0000;
|
|
144
|
-
border-radius: 4px;
|
|
145
|
-
font-size: 0.875rem;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/* Dark mode support */
|
|
149
|
-
@media (prefers-color-scheme: dark) {
|
|
150
|
-
.x402-wallet-section,
|
|
151
|
-
.x402-wallet-info {
|
|
152
|
-
background: #1a1a1a;
|
|
153
|
-
border-color: #333;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
.x402-section-title {
|
|
157
|
-
color: #fff;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
.x402-wallet-label {
|
|
161
|
-
color: #aaa;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
.x402-hint {
|
|
165
|
-
color: #aaa;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
1
|
+
*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.absolute{position:absolute}.relative{position:relative}.inset-0{inset:0}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.mb-6{margin-bottom:1.5rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.h-10{height:2.5rem}.h-12{height:3rem}.h-16{height:4rem}.h-screen{height:100vh}.w-10{width:2.5rem}.w-12{width:3rem}.w-16{width:4rem}.min-w-0{min-width:0}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.overflow-hidden{overflow:hidden}.break-all{word-break:break-all}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.bg-black{background-color:rgb(0 0 0/var(--tw-bg-opacity,1))}.bg-black,.bg-gray-50{--tw-bg-opacity:1}.bg-gray-50{background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.py-10{padding-top:2.5rem;padding-bottom:2.5rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.text-center{text-align:center}.text-sm{font-size:.875rem;line-height:1.25rem}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.text-blue-600{color:rgb(37 99 235/var(--tw-text-opacity,1))}.text-blue-600,.text-white{--tw-text-opacity:1}.text-white{color:rgb(255 255 255/var(--tw-text-opacity,1))}.underline{text-decoration-line:underline}.opacity-40{opacity:.4}.outline{outline-style:solid}.blur-xl{--tw-blur:blur(24px);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.hover\:text-blue-700:hover{--tw-text-opacity:1;color:rgb(29 78 216/var(--tw-text-opacity,1))}
|
package/package.json
CHANGED
|
@@ -1,28 +1,38 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voyage_ai/v402-web-ts",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "v402pay platform frontend SDK for seamless Web3 payment integration with Solana and Ethereum support",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
10
11
|
"import": "./dist/index.mjs",
|
|
11
|
-
"require": "./dist/index.js"
|
|
12
|
-
"types": "./dist/index.d.ts"
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
13
|
},
|
|
14
14
|
"./react": {
|
|
15
|
+
"types": "./dist/react/index.d.ts",
|
|
16
|
+
"style": "./dist/react/styles.css",
|
|
15
17
|
"import": "./dist/react/index.mjs",
|
|
16
|
-
"require": "./dist/react/index.js"
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
"require": "./dist/react/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./react/styles.css": "./dist/react/styles.css"
|
|
19
21
|
},
|
|
22
|
+
"sideEffects": [
|
|
23
|
+
"*.css",
|
|
24
|
+
"dist/react/styles.css",
|
|
25
|
+
"dist/react/index.js",
|
|
26
|
+
"dist/react/index.mjs"
|
|
27
|
+
],
|
|
20
28
|
"files": [
|
|
21
29
|
"dist",
|
|
22
30
|
"README.md"
|
|
23
31
|
],
|
|
24
32
|
"scripts": {
|
|
25
|
-
"build": "
|
|
33
|
+
"build:css": "tailwindcss -i ./src/react/styles.css -o ./dist/react/styles.css --minify",
|
|
34
|
+
"build:js": "tsup",
|
|
35
|
+
"build": "npm run build:js && npm run build:css",
|
|
26
36
|
"dev": "tsup --watch",
|
|
27
37
|
"type-check": "tsc --noEmit",
|
|
28
38
|
"prepublishOnly": "npm run build",
|
|
@@ -44,14 +54,22 @@
|
|
|
44
54
|
"author": "",
|
|
45
55
|
"license": "MIT",
|
|
46
56
|
"peerDependencies": {
|
|
47
|
-
"
|
|
48
|
-
"@solana/web3.js": "^1.95.0",
|
|
57
|
+
"@ant-design/icons": "^5.0.0",
|
|
49
58
|
"@solana/spl-token": "^0.4.0",
|
|
50
|
-
"
|
|
59
|
+
"@solana/web3.js": "^1.95.0",
|
|
60
|
+
"antd": "^5.0.0",
|
|
61
|
+
"ethers": "^6.0.0",
|
|
62
|
+
"react": ">=18.0.0"
|
|
51
63
|
},
|
|
52
64
|
"peerDependenciesMeta": {
|
|
53
65
|
"react": {
|
|
54
66
|
"optional": true
|
|
67
|
+
},
|
|
68
|
+
"antd": {
|
|
69
|
+
"optional": true
|
|
70
|
+
},
|
|
71
|
+
"@ant-design/icons": {
|
|
72
|
+
"optional": true
|
|
55
73
|
}
|
|
56
74
|
},
|
|
57
75
|
"dependencies": {
|
|
@@ -59,8 +77,13 @@
|
|
|
59
77
|
"zod": "^3.22.0"
|
|
60
78
|
},
|
|
61
79
|
"devDependencies": {
|
|
80
|
+
"@ant-design/icons": "^5.0.0",
|
|
62
81
|
"@types/node": "^20.0.0",
|
|
63
82
|
"@types/react": "^18.0.0",
|
|
83
|
+
"antd": "^5.0.0",
|
|
84
|
+
"autoprefixer": "^10.4.22",
|
|
85
|
+
"postcss": "^8.5.6",
|
|
86
|
+
"tailwindcss": "^3.4.18",
|
|
64
87
|
"tsup": "^8.0.0",
|
|
65
88
|
"typescript": "^5.0.0"
|
|
66
89
|
}
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WalletConnect Component
|
|
3
|
-
*
|
|
4
|
-
* Pre-built wallet connection UI component with inline styles
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
'use client';
|
|
8
|
-
|
|
9
|
-
import React, {useState} from 'react';
|
|
10
|
-
import {NetworkType} from '../../types';
|
|
11
|
-
import {formatAddress, getNetworkDisplayName, getWalletInstallUrl, isWalletInstalled,} from '../../utils';
|
|
12
|
-
import {useWallet} from '../hooks/useWalletStore';
|
|
13
|
-
import {
|
|
14
|
-
buttonsContainerStyle,
|
|
15
|
-
containerStyle,
|
|
16
|
-
getAddressStyle,
|
|
17
|
-
getConnectButtonStyle,
|
|
18
|
-
getDisconnectButtonStyle,
|
|
19
|
-
getErrorStyle,
|
|
20
|
-
getHintStyle,
|
|
21
|
-
getInstallLinkStyle,
|
|
22
|
-
getLabelStyle,
|
|
23
|
-
getSectionStyle,
|
|
24
|
-
getTitleStyle,
|
|
25
|
-
walletActionsStyle,
|
|
26
|
-
walletAddressStyle,
|
|
27
|
-
walletOptionStyle,
|
|
28
|
-
} from '../styles/inline-styles';
|
|
29
|
-
|
|
30
|
-
export interface WalletConnectProps {
|
|
31
|
-
supportedNetworks?: NetworkType[];
|
|
32
|
-
className?: string;
|
|
33
|
-
onConnect?: (address: string, networkType: NetworkType) => void;
|
|
34
|
-
onDisconnect?: () => void;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Pre-built wallet connection component
|
|
39
|
-
*
|
|
40
|
-
* @example
|
|
41
|
-
* ```tsx
|
|
42
|
-
* import { WalletConnect } from '../react';
|
|
43
|
-
*
|
|
44
|
-
* function App() {
|
|
45
|
-
* return (
|
|
46
|
-
* <WalletConnect
|
|
47
|
-
* supportedNetworks={[NetworkType.SOLANA, NetworkType.EVM]}
|
|
48
|
-
* onConnect={(address, network) => console.log('Connected:', address)}
|
|
49
|
-
* />
|
|
50
|
-
* );
|
|
51
|
-
* }
|
|
52
|
-
* ```
|
|
53
|
-
*/
|
|
54
|
-
export function WalletConnect({
|
|
55
|
-
supportedNetworks = [NetworkType.SOLANA, NetworkType.EVM],
|
|
56
|
-
className = '',
|
|
57
|
-
onConnect,
|
|
58
|
-
onDisconnect,
|
|
59
|
-
}: WalletConnectProps) {
|
|
60
|
-
const {address, networkType, isConnecting, error, connect, disconnect} = useWallet();
|
|
61
|
-
const [hoveredButton, setHoveredButton] = useState<string | null>(null);
|
|
62
|
-
const [hoveredLink, setHoveredLink] = useState<string | null>(null);
|
|
63
|
-
|
|
64
|
-
const handleConnect = async (network: NetworkType) => {
|
|
65
|
-
try {
|
|
66
|
-
await connect(network);
|
|
67
|
-
// Note: address state won't be updated yet due to async setState
|
|
68
|
-
// The parent component will re-render when address updates
|
|
69
|
-
} catch (err) {
|
|
70
|
-
// Error is already set in hook
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
const handleDisconnect = () => {
|
|
75
|
-
disconnect();
|
|
76
|
-
onDisconnect?.();
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
return (
|
|
80
|
-
<div style={{ ...containerStyle, ...(className ? {} : {}) }} className={className}>
|
|
81
|
-
{!address ? (
|
|
82
|
-
<div style={getSectionStyle()}>
|
|
83
|
-
<h3 style={getTitleStyle()}>Connect Wallet</h3>
|
|
84
|
-
|
|
85
|
-
{supportedNetworks.length === 0 ? (
|
|
86
|
-
<p style={getHintStyle()}>Please install a supported wallet extension</p>
|
|
87
|
-
) : (
|
|
88
|
-
<div style={buttonsContainerStyle}>
|
|
89
|
-
{supportedNetworks.map((network) => {
|
|
90
|
-
const installed = isWalletInstalled(network);
|
|
91
|
-
return (
|
|
92
|
-
<div key={network} style={walletOptionStyle}>
|
|
93
|
-
<button
|
|
94
|
-
style={getConnectButtonStyle(isConnecting || !installed, hoveredButton === network)}
|
|
95
|
-
onClick={() => handleConnect(network)}
|
|
96
|
-
disabled={isConnecting || !installed}
|
|
97
|
-
onMouseEnter={() => setHoveredButton(network)}
|
|
98
|
-
onMouseLeave={() => setHoveredButton(null)}
|
|
99
|
-
>
|
|
100
|
-
{isConnecting ? 'Connecting...' : getNetworkDisplayName(network)}
|
|
101
|
-
</button>
|
|
102
|
-
{!installed && (
|
|
103
|
-
<a
|
|
104
|
-
href={getWalletInstallUrl(network)}
|
|
105
|
-
target="_blank"
|
|
106
|
-
rel="noopener noreferrer"
|
|
107
|
-
style={getInstallLinkStyle(hoveredLink === network)}
|
|
108
|
-
onMouseEnter={() => setHoveredLink(network)}
|
|
109
|
-
onMouseLeave={() => setHoveredLink(null)}
|
|
110
|
-
>
|
|
111
|
-
Install Wallet
|
|
112
|
-
</a>
|
|
113
|
-
)}
|
|
114
|
-
</div>
|
|
115
|
-
);
|
|
116
|
-
})}
|
|
117
|
-
</div>
|
|
118
|
-
)}
|
|
119
|
-
|
|
120
|
-
{error && <p style={getErrorStyle()}>{error}</p>}
|
|
121
|
-
|
|
122
|
-
<p style={getHintStyle()}>
|
|
123
|
-
To switch accounts, please change it in your wallet extension
|
|
124
|
-
</p>
|
|
125
|
-
</div>
|
|
126
|
-
) : (
|
|
127
|
-
<div style={getSectionStyle()}>
|
|
128
|
-
<div style={walletAddressStyle}>
|
|
129
|
-
<span style={getLabelStyle()}>
|
|
130
|
-
Connected {networkType && `(${getNetworkDisplayName(networkType)})`}
|
|
131
|
-
</span>
|
|
132
|
-
<span style={getAddressStyle()}>{formatAddress(address)}</span>
|
|
133
|
-
</div>
|
|
134
|
-
<div style={walletActionsStyle}>
|
|
135
|
-
<button
|
|
136
|
-
style={getDisconnectButtonStyle(hoveredButton === 'disconnect')}
|
|
137
|
-
onClick={handleDisconnect}
|
|
138
|
-
onMouseEnter={() => setHoveredButton('disconnect')}
|
|
139
|
-
onMouseLeave={() => setHoveredButton(null)}
|
|
140
|
-
>
|
|
141
|
-
Disconnect
|
|
142
|
-
</button>
|
|
143
|
-
</div>
|
|
144
|
-
<p style={getHintStyle()}>
|
|
145
|
-
Switch account in your wallet to change address
|
|
146
|
-
</p>
|
|
147
|
-
</div>
|
|
148
|
-
)}
|
|
149
|
-
</div>
|
|
150
|
-
);
|
|
151
|
-
}
|
|
152
|
-
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* usePayment Hook
|
|
3
|
-
*
|
|
4
|
-
* React hook for payment state management
|
|
5
|
-
* Provides state only - you control the payment flow
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import {useCallback, useState} from 'react';
|
|
9
|
-
|
|
10
|
-
export interface UsePaymentReturn {
|
|
11
|
-
// State
|
|
12
|
-
isProcessing: boolean;
|
|
13
|
-
result: any;
|
|
14
|
-
error: string | null;
|
|
15
|
-
|
|
16
|
-
// State setters
|
|
17
|
-
setIsProcessing: (value: boolean) => void;
|
|
18
|
-
setResult: (value: any) => void;
|
|
19
|
-
setError: (value: string | null) => void;
|
|
20
|
-
|
|
21
|
-
// Helpers
|
|
22
|
-
clearResult: () => void;
|
|
23
|
-
clearError: () => void;
|
|
24
|
-
reset: () => void;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Hook for managing payment state
|
|
29
|
-
*
|
|
30
|
-
* This hook only manages state - you control the payment logic.
|
|
31
|
-
* Use SDK's handleSvmPayment/handleEvmPayment directly for full control.
|
|
32
|
-
*
|
|
33
|
-
* @example
|
|
34
|
-
* ```tsx
|
|
35
|
-
* import { usePayment, useWallet } from '../react';
|
|
36
|
-
* import { handleSvmPayment } from '@/app/sdk';
|
|
37
|
-
*
|
|
38
|
-
* function PaymentButton() {
|
|
39
|
-
* const { networkType } = useWallet();
|
|
40
|
-
* const { isProcessing, setIsProcessing, result, setResult, error, setError } = usePayment();
|
|
41
|
-
*
|
|
42
|
-
* const handlePay = async () => {
|
|
43
|
-
* if (!networkType) return;
|
|
44
|
-
*
|
|
45
|
-
* setIsProcessing(true);
|
|
46
|
-
* setError(null);
|
|
47
|
-
*
|
|
48
|
-
* try {
|
|
49
|
-
* const response = await handleSvmPayment('/api/endpoint', {
|
|
50
|
-
* wallet: window.solana,
|
|
51
|
-
* network: 'solana-devnet',
|
|
52
|
-
* });
|
|
53
|
-
*
|
|
54
|
-
* const data = await response.json();
|
|
55
|
-
* setResult(data);
|
|
56
|
-
*
|
|
57
|
-
* // Your custom logic here
|
|
58
|
-
* console.log('Payment success!');
|
|
59
|
-
* } catch (err: any) {
|
|
60
|
-
* setError(err.message);
|
|
61
|
-
* } finally {
|
|
62
|
-
* setIsProcessing(false);
|
|
63
|
-
* }
|
|
64
|
-
* };
|
|
65
|
-
*
|
|
66
|
-
* return (
|
|
67
|
-
* <div>
|
|
68
|
-
* <button onClick={handlePay} disabled={isProcessing}>
|
|
69
|
-
* {isProcessing ? 'Processing...' : 'Pay'}
|
|
70
|
-
* </button>
|
|
71
|
-
* {error && <p>Error: {error}</p>}
|
|
72
|
-
* {result && <pre>{JSON.stringify(result, null, 2)}</pre>}
|
|
73
|
-
* </div>
|
|
74
|
-
* );
|
|
75
|
-
* }
|
|
76
|
-
* ```
|
|
77
|
-
*/
|
|
78
|
-
export function usePayment(): UsePaymentReturn {
|
|
79
|
-
const [isProcessing, setIsProcessing] = useState(false);
|
|
80
|
-
const [result, setResult] = useState<any>(null);
|
|
81
|
-
const [error, setError] = useState<string | null>(null);
|
|
82
|
-
|
|
83
|
-
const clearResult = useCallback(() => {
|
|
84
|
-
setResult(null);
|
|
85
|
-
}, []);
|
|
86
|
-
|
|
87
|
-
const clearError = useCallback(() => {
|
|
88
|
-
setError(null);
|
|
89
|
-
}, []);
|
|
90
|
-
|
|
91
|
-
const reset = useCallback(() => {
|
|
92
|
-
setIsProcessing(false);
|
|
93
|
-
setResult(null);
|
|
94
|
-
setError(null);
|
|
95
|
-
}, []);
|
|
96
|
-
|
|
97
|
-
return {
|
|
98
|
-
isProcessing,
|
|
99
|
-
result,
|
|
100
|
-
error,
|
|
101
|
-
setIsProcessing,
|
|
102
|
-
setResult,
|
|
103
|
-
setError,
|
|
104
|
-
clearResult,
|
|
105
|
-
clearError,
|
|
106
|
-
reset,
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* usePaymentInfo Hook
|
|
3
|
-
*
|
|
4
|
-
* React hook for fetching payment information from endpoint
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import {useEffect, useState} from 'react';
|
|
8
|
-
import type {PaymentRequirements} from 'x402/types';
|
|
9
|
-
import {NetworkType} from '../../types';
|
|
10
|
-
import {getSupportedNetworkTypes, parsePaymentRequired} from '../../utils';
|
|
11
|
-
import {PROD_BACK_URL} from "../../types/common";
|
|
12
|
-
|
|
13
|
-
export interface UsePaymentInfoReturn {
|
|
14
|
-
// State
|
|
15
|
-
paymentInfo: PaymentRequirements[] | null;
|
|
16
|
-
supportedNetworks: NetworkType[];
|
|
17
|
-
isLoading: boolean;
|
|
18
|
-
error: string | null;
|
|
19
|
-
|
|
20
|
-
// Actions
|
|
21
|
-
refetch: () => Promise<void>;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Hook for fetching payment information
|
|
26
|
-
*
|
|
27
|
-
* @param endpoint - API endpoint to fetch payment info from
|
|
28
|
-
* @param merchantId - @see our website to apply
|
|
29
|
-
* @param additionalParams - Optional additional parameters to send with the request (default: {})
|
|
30
|
-
*
|
|
31
|
-
* @example
|
|
32
|
-
* ```tsx
|
|
33
|
-
* function PaymentInfo() {
|
|
34
|
-
* const { paymentInfo, supportedNetworks, isLoading } = usePaymentInfo('/api/protected');
|
|
35
|
-
*
|
|
36
|
-
* if (isLoading) return <p>Loading...</p>;
|
|
37
|
-
*
|
|
38
|
-
* return (
|
|
39
|
-
* <div>
|
|
40
|
-
* <p>Supported networks:</p>
|
|
41
|
-
* {supportedNetworks.map(net => <span key={net}>{net}</span>)}
|
|
42
|
-
* </div>
|
|
43
|
-
* );
|
|
44
|
-
* }
|
|
45
|
-
* ```
|
|
46
|
-
*
|
|
47
|
-
* @example
|
|
48
|
-
* ```tsx
|
|
49
|
-
* // With additional parameters
|
|
50
|
-
* function PaymentInfo() {
|
|
51
|
-
* const { paymentInfo } = usePaymentInfo(
|
|
52
|
-
* 'merchant-id',
|
|
53
|
-
* '/api/protected',
|
|
54
|
-
* { userId: '123', customField: 'value' }
|
|
55
|
-
* );
|
|
56
|
-
* }
|
|
57
|
-
* ```
|
|
58
|
-
*/
|
|
59
|
-
export function usePaymentInfo(
|
|
60
|
-
merchantId: string,
|
|
61
|
-
endpoint: string = PROD_BACK_URL,
|
|
62
|
-
additionalParams?: Record<string, any>
|
|
63
|
-
): UsePaymentInfoReturn {
|
|
64
|
-
const [paymentInfo, setPaymentInfo] = useState<PaymentRequirements[] | null>(null);
|
|
65
|
-
const [supportedNetworks, setSupportedNetworks] = useState<NetworkType[]>([]);
|
|
66
|
-
const [isLoading, setIsLoading] = useState(true);
|
|
67
|
-
const [error, setError] = useState<string | null>(null);
|
|
68
|
-
|
|
69
|
-
const fetchPaymentInfo = async () => {
|
|
70
|
-
setIsLoading(true);
|
|
71
|
-
setError(null);
|
|
72
|
-
|
|
73
|
-
try {
|
|
74
|
-
// 使用新变量而不是修改参数
|
|
75
|
-
const fullEndpoint = `${endpoint}/${merchantId}`;
|
|
76
|
-
|
|
77
|
-
// 准备请求配置
|
|
78
|
-
const requestInit: RequestInit = {
|
|
79
|
-
method: 'POST',
|
|
80
|
-
...(additionalParams && Object.keys(additionalParams).length > 0
|
|
81
|
-
? {
|
|
82
|
-
body: JSON.stringify(additionalParams),
|
|
83
|
-
headers: {
|
|
84
|
-
'Content-Type': 'application/json',
|
|
85
|
-
},
|
|
86
|
-
}
|
|
87
|
-
: {}),
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
const response = await fetch(fullEndpoint, requestInit);
|
|
91
|
-
|
|
92
|
-
if (response.status === 402) {
|
|
93
|
-
const body = await response.json();
|
|
94
|
-
const payment = parsePaymentRequired(body);
|
|
95
|
-
|
|
96
|
-
if (payment) {
|
|
97
|
-
setPaymentInfo(payment);
|
|
98
|
-
|
|
99
|
-
const networks = getSupportedNetworkTypes(payment);
|
|
100
|
-
setSupportedNetworks(networks);
|
|
101
|
-
}
|
|
102
|
-
} else {
|
|
103
|
-
// No payment required
|
|
104
|
-
setPaymentInfo(null);
|
|
105
|
-
setSupportedNetworks([]);
|
|
106
|
-
}
|
|
107
|
-
} catch (err: any) {
|
|
108
|
-
setError(err.message || 'Failed to fetch payment info');
|
|
109
|
-
} finally {
|
|
110
|
-
setIsLoading(false);
|
|
111
|
-
}
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
useEffect(() => {
|
|
115
|
-
fetchPaymentInfo();
|
|
116
|
-
// Note: additionalParams is not in dependencies to avoid unnecessary re-fetches
|
|
117
|
-
// If you need dynamic additionalParams, wrap it with useMemo or use refetch() manually
|
|
118
|
-
}, [endpoint, merchantId]);
|
|
119
|
-
|
|
120
|
-
return {
|
|
121
|
-
paymentInfo,
|
|
122
|
-
supportedNetworks,
|
|
123
|
-
isLoading,
|
|
124
|
-
error,
|
|
125
|
-
refetch: fetchPaymentInfo,
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
|