@dj-test/payment-sdk 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 ADDED
@@ -0,0 +1,325 @@
1
+ # Payment SDK
2
+
3
+ wagmi 기반의 결제 SDK로, 두 가지 결제 방식을 지원합니다.
4
+
5
+ ## 주요 기능
6
+
7
+ ### 1. Pay with Wallet (지갑 연결 결제)
8
+ - ✅ 지갑 설치 여부 자동 감지
9
+ - ✅ 다양한 지갑 지원 (MetaMask, WalletConnect, Coinbase Wallet)
10
+ - ✅ 지갑 선택 UI 제공
11
+ - ✅ 실시간 트랜잭션 확인
12
+ - ✅ 네트워크 자동 전환
13
+ - ✅ 송금 주소, 금액, 코인 설정
14
+
15
+ ### 2. Pay with Address (주소 결제)
16
+ - ✅ QR 코드 생성
17
+ - ✅ 주소 복사 기능
18
+ - ✅ 만료 시간 표시
19
+ - ✅ 결제 안내문
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ pnpm add payment-sdk wagmi viem @tanstack/react-query
25
+ ```
26
+
27
+ ## Quick Start
28
+
29
+ ### 1. Wrap your app with Providers
30
+
31
+ ```tsx
32
+ import { PaymentProvider, WalletProvider } from 'payment-sdk';
33
+
34
+ function App() {
35
+ return (
36
+ <WalletProvider>
37
+ <PaymentProvider
38
+ apiKey="your-api-key"
39
+ environment="production"
40
+ baseUrl={process.env.NEXT_PUBLIC_API_URL}
41
+ redirectUrl="https://yoursite.com/payment/success"
42
+ webhookUrl="https://yoursite.com/api/webhook/payment"
43
+ >
44
+ <YourApp />
45
+ </PaymentProvider>
46
+ </WalletProvider>
47
+ );
48
+ }
49
+ ```
50
+
51
+ ### 2. Pay with Wallet
52
+
53
+ ```tsx
54
+ import { PayWithWallet } from 'payment-sdk';
55
+
56
+ function CheckoutPage() {
57
+ return (
58
+ <PayWithWallet
59
+ toAddress="0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
60
+ amount="100"
61
+ coinId="USDC"
62
+ chainId={11155111} // Sepolia testnet
63
+ onSuccess={(txHash) => console.log('Success:', txHash)}
64
+ onError={(error) => console.error('Error:', error)}
65
+ />
66
+ );
67
+ }
68
+ ```
69
+
70
+ ### 3. Pay with Address
71
+
72
+ ```tsx
73
+ import { PayWithAddress } from 'payment-sdk';
74
+
75
+ function PaymentPage() {
76
+ return (
77
+ <PayWithAddress
78
+ walletAddress="0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
79
+ amount="100"
80
+ coinId="USDC"
81
+ chainId={11155111}
82
+ publicOrderId="order-uuid"
83
+ expiresAt="2025-01-04T15:30:00"
84
+ />
85
+ );
86
+ }
87
+ ```
88
+
89
+ ### 4. Use Wallet Hook (Legacy - EVM Only)
90
+
91
+ ```tsx
92
+ import { useWallet } from 'payment-sdk';
93
+
94
+ function WalletComponent() {
95
+ const {
96
+ wallet,
97
+ isConnected,
98
+ connect,
99
+ disconnect,
100
+ connectors,
101
+ sendPayment,
102
+ } = useWallet();
103
+
104
+ return (
105
+ <div>
106
+ {!isConnected ? (
107
+ <button onClick={() => connect('injected')}>
108
+ Connect Wallet
109
+ </button>
110
+ ) : (
111
+ <>
112
+ <p>Connected: {wallet?.address}</p>
113
+ <button onClick={disconnect}>Disconnect</button>
114
+ </>
115
+ )}
116
+ </div>
117
+ );
118
+ }
119
+ ```
120
+
121
+ ### 5. Use Wallet Adapter Hook (Multi-chain Support)
122
+
123
+ ```tsx
124
+ import { useWalletAdapter, WalletTypes } from 'payment-sdk';
125
+
126
+ function MultiChainWallet() {
127
+ const {
128
+ wallet,
129
+ isConnected,
130
+ connect,
131
+ disconnect,
132
+ chainType,
133
+ balance,
134
+ sendPayment,
135
+ } = useWalletAdapter();
136
+
137
+ const connectEvm = () => {
138
+ connect(WalletTypes.EVM, 'injected'); // MetaMask
139
+ };
140
+
141
+ const connectTron = () => {
142
+ connect(WalletTypes.TRON, 'tronlink'); // TronLink
143
+ };
144
+
145
+ return (
146
+ <div>
147
+ {!isConnected ? (
148
+ <>
149
+ <button onClick={connectEvm}>Connect MetaMask</button>
150
+ <button onClick={connectTron}>Connect TronLink</button>
151
+ </>
152
+ ) : (
153
+ <>
154
+ <p>Chain: {chainType}</p>
155
+ <p>Address: {wallet?.address}</p>
156
+ <p>Balance: {balance?.formatted} {balance?.symbol}</p>
157
+ <button onClick={disconnect}>Disconnect</button>
158
+ </>
159
+ )}
160
+ </div>
161
+ );
162
+ }
163
+ ```
164
+
165
+ ### 6. Tron Payment Example
166
+
167
+ ```tsx
168
+ import { PayWithWallet } from 'payment-sdk';
169
+
170
+ function TronPayment() {
171
+ return (
172
+ <PayWithWallet
173
+ toAddress="TXYZoPEU" // Tron address (starts with T)
174
+ amount="100"
175
+ coinId="USDT"
176
+ chainId="mainnet" // Tron mainnet
177
+ onSuccess={(txHash) => console.log('Tron TX:', txHash)}
178
+ onError={(error) => console.error('Error:', error)}
179
+ />
180
+ );
181
+ }
182
+ ```
183
+
184
+ ### 7. Auto-detect Chain by Address
185
+
186
+ ```tsx
187
+ import { useWalletAdapter } from 'payment-sdk';
188
+
189
+ function AutoDetectPayment({ depositAddress }: { depositAddress: string }) {
190
+ // Automatically selects EVM or Tron adapter based on address format
191
+ // 0x... -> EVM adapter
192
+ // T... -> Tron adapter
193
+ const adapter = useWalletAdapter({ depositAddress });
194
+
195
+ return (
196
+ <div>
197
+ <p>Detected Chain: {adapter.chainType}</p>
198
+ <button onClick={() => adapter.connect(adapter.adapterType!, 'injected')}>
199
+ Connect Wallet
200
+ </button>
201
+ </div>
202
+ );
203
+ }
204
+ ```
205
+
206
+ ## Supported Networks
207
+
208
+ ### EVM Chains
209
+ - Ethereum Mainnet (1)
210
+ - Sepolia Testnet (11155111)
211
+ - Polygon Mainnet (137)
212
+ - Polygon Amoy Testnet (80002)
213
+ - Base Mainnet (8453)
214
+ - Base Sepolia (84532)
215
+
216
+ ### Tron Networks
217
+ - Tron Mainnet (`mainnet`)
218
+ - Tron Shasta Testnet (`shasta`)
219
+ - Tron Nile Testnet (`nile`)
220
+
221
+ ## Supported Wallets
222
+
223
+ ### EVM Wallets
224
+ - **Injected Wallets**: MetaMask, Trust Wallet, Brave Wallet
225
+ - **WalletConnect**: 200+ 모바일 지갑
226
+ - **Coinbase Wallet**: Coinbase 공식 지갑
227
+
228
+ ### Tron Wallets
229
+ - **TronLink**: Official Tron wallet extension
230
+ - **TronLink Pro**: Mobile wallet with WalletConnect support
231
+
232
+ ## API Reference
233
+
234
+ ### PaymentProvider Props
235
+
236
+ | Prop | Type | Required | Description |
237
+ |------|------|----------|-------------|
238
+ | apiKey | string | Yes | Your API key |
239
+ | environment | 'production' \| 'sandbox' | Yes | Environment |
240
+ | baseUrl | string | No | Custom API URL |
241
+ | redirectUrl | string | Yes | Success redirect URL |
242
+ | webhookUrl | string | Yes | Webhook endpoint URL |
243
+ | timeout | number | No | Request timeout (default: 30000ms) |
244
+
245
+ ### usePayment Hook
246
+
247
+ Returns:
248
+ - `createOrder(params)` - Create a new payment order
249
+ - `isLoading` - Loading state
250
+ - `error` - Error object
251
+ - `order` - Created order object
252
+ - `errorMessage` - User-friendly error message
253
+
254
+ ### useOrder Hook
255
+
256
+ ```tsx
257
+ const { order, isLoading, error, refetch } = useOrder(publicOrderId);
258
+ ```
259
+
260
+ Returns order details and allows manual refresh.
261
+
262
+ ### Validation Utils
263
+
264
+ ```tsx
265
+ import { validation } from '@your-company/payment-sdk';
266
+
267
+ // Validate withdrawal address
268
+ const result = validation.validateWithdrawalAddress(fromAddress, toAddress);
269
+ if (!result.valid) {
270
+ console.error(result.error);
271
+ }
272
+
273
+ // Validate Ethereum address format
274
+ const isValid = validation.isValidEthereumAddress(address);
275
+ ```
276
+
277
+ ## Security Features
278
+
279
+ ### 1. Order ID Security
280
+ - Uses UUID for public order IDs (unpredictable)
281
+ - Internal database IDs remain hidden
282
+
283
+ ### 2. Withdrawal Validation
284
+ - Prevents sending to your own wallet address
285
+ - Validates address format
286
+ - Validates amount
287
+
288
+ ### 3. API Security
289
+ - HTTPS required for production
290
+ - API key authentication
291
+ - Webhook signature verification
292
+
293
+ ## Payment Flow
294
+
295
+ 1. **Create Order**: Merchant calls `createOrder()`
296
+ 2. **Redirect**: User redirected to payment page
297
+ 3. **Payment**: User sends crypto to wallet address
298
+ 4. **Monitor**: System monitors blockchain for deposit
299
+ 5. **Complete**: Webhook sent to merchant, user redirected to success page
300
+
301
+ ## Environment Variables
302
+
303
+ ```env
304
+ NEXT_PUBLIC_API_URL=https://api.yourpayment.com
305
+ NEXT_PUBLIC_API_KEY=your-api-key-here
306
+ NEXT_PUBLIC_REDIRECT_URL=https://yoursite.com/payment/success
307
+ NEXT_PUBLIC_WEBHOOK_URL=https://yoursite.com/api/webhook/payment
308
+ NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=your-walletconnect-project-id
309
+ ```
310
+
311
+ Get WalletConnect Project ID at [WalletConnect Cloud](https://cloud.walletconnect.com/)
312
+
313
+ ## Examples
314
+
315
+ See the [examples](./examples) directory for complete integration examples.
316
+
317
+ ## Support
318
+
319
+ - Documentation: https://docs.yourpayment.com
320
+ - Email: support@yourcompany.com
321
+ - GitHub: https://github.com/yourcompany/payment-sdk
322
+
323
+ ## License
324
+
325
+ MIT
@@ -0,0 +1,223 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ // src/api/orders.ts
8
+ var orders_exports = {};
9
+ __export(orders_exports, {
10
+ createOrder: () => createOrder,
11
+ getOrder: () => getOrder,
12
+ getOrders: () => getOrders,
13
+ notifyTransaction: () => notifyTransaction
14
+ });
15
+
16
+ // src/api/client.ts
17
+ import axios from "axios";
18
+
19
+ // src/core/constants.ts
20
+ var SDK_VERSION = "1.0.0";
21
+ var API_ENDPOINTS = {
22
+ ORDERS: "/payments",
23
+ PRODUCTS: "/products"
24
+ };
25
+ var ENVIRONMENT_URLS = {
26
+ production: "https://pay.com",
27
+ sandbox: "http://localhost:8080"
28
+ };
29
+ var PAYMENT_DOMAINS = {
30
+ production: "https://pay-home.com",
31
+ sandbox: "http://localhost:3000"
32
+ };
33
+ var DEFAULT_TIMEOUT = 3e4;
34
+ var ORDER_EXPIRY_MINUTES = 30;
35
+ var MIN_WITHDRAWAL_AMOUNT = 0.01;
36
+ var SUPPORTED_CHAINS = {
37
+ ETHEREUM: "1",
38
+ SEPOLIA: "11155111",
39
+ POLYGON: "137"
40
+ };
41
+ var ERROR_CODES = {
42
+ INVALID_API_KEY: "INVALID_API_KEY",
43
+ INVALID_PRODUCT: "INVALID_PRODUCT",
44
+ ORDER_NOT_FOUND: "ORDER_NOT_FOUND",
45
+ ORDER_EXPIRED: "ORDER_EXPIRED",
46
+ INSUFFICIENT_BALANCE: "INSUFFICIENT_BALANCE",
47
+ SELF_TRANSFER: "SELF_TRANSFER",
48
+ INVALID_ADDRESS: "INVALID_ADDRESS",
49
+ NETWORK_ERROR: "NETWORK_ERROR"
50
+ };
51
+
52
+ // src/core/config.ts
53
+ var Config = class _Config {
54
+ constructor() {
55
+ this.config = null;
56
+ }
57
+ static getInstance() {
58
+ if (!_Config.instance) {
59
+ _Config.instance = new _Config();
60
+ }
61
+ return _Config.instance;
62
+ }
63
+ initialize(config2) {
64
+ if (!config2.baseUrl && config2.environment) {
65
+ config2.baseUrl = ENVIRONMENT_URLS[config2.environment];
66
+ }
67
+ if (!config2.timeout) {
68
+ config2.timeout = DEFAULT_TIMEOUT;
69
+ }
70
+ this.config = config2;
71
+ }
72
+ getConfig() {
73
+ if (!this.config) {
74
+ return {
75
+ environment: "sandbox",
76
+ baseUrl: ENVIRONMENT_URLS.sandbox,
77
+ timeout: DEFAULT_TIMEOUT
78
+ };
79
+ }
80
+ return this.config;
81
+ }
82
+ isInitialized() {
83
+ return this.config !== null;
84
+ }
85
+ reset() {
86
+ this.config = null;
87
+ }
88
+ };
89
+ var config = Config.getInstance();
90
+
91
+ // src/types/sdk.ts
92
+ var SDKError = class extends Error {
93
+ constructor(message, code, details) {
94
+ super(message);
95
+ this.code = code;
96
+ this.details = details;
97
+ this.name = "SDKError";
98
+ }
99
+ };
100
+ var APIError = class extends SDKError {
101
+ constructor(message, statusCode, details) {
102
+ super(message, "API_ERROR", details);
103
+ this.statusCode = statusCode;
104
+ this.name = "APIError";
105
+ }
106
+ };
107
+
108
+ // src/api/client.ts
109
+ var APIClient = class {
110
+ constructor() {
111
+ this.instance = null;
112
+ }
113
+ getClient() {
114
+ if (!this.instance) {
115
+ const sdkConfig = config.getConfig();
116
+ this.instance = axios.create({
117
+ baseURL: sdkConfig.baseUrl,
118
+ timeout: sdkConfig.timeout,
119
+ headers: {
120
+ "Content-Type": "application/json"
121
+ },
122
+ withCredentials: true
123
+ // 기존 프로젝트 패턴과 일치
124
+ });
125
+ this.instance.interceptors.request.use(
126
+ (requestConfig) => {
127
+ return requestConfig;
128
+ },
129
+ (error) => Promise.reject(error)
130
+ );
131
+ this.instance.interceptors.response.use(
132
+ (response) => response,
133
+ (error) => {
134
+ const status = error.response?.status;
135
+ const data = error.response?.data;
136
+ const message = data?.message || error.message;
137
+ const details = error.response?.data;
138
+ throw new APIError(message, status, details);
139
+ }
140
+ );
141
+ }
142
+ return this.instance;
143
+ }
144
+ reset() {
145
+ this.instance = null;
146
+ }
147
+ };
148
+ var apiClient = new APIClient();
149
+
150
+ // src/api/orders.ts
151
+ var createOrder = async (productId, redirectUrl, webhookUrl) => {
152
+ const request = {
153
+ productId,
154
+ redirectUrl,
155
+ webhookUrl
156
+ };
157
+ const response = await apiClient.getClient().post(
158
+ API_ENDPOINTS.ORDERS,
159
+ request
160
+ );
161
+ const order = response.data.output;
162
+ return {
163
+ orderId: order.uuid,
164
+ publicOrderId: order.uuid,
165
+ paymentUrl: "",
166
+ // 사용 안 함
167
+ amount: 0,
168
+ // 주문 생성 시점에는 없음
169
+ coinId: "",
170
+ // 주문 생성 시점에는 없음
171
+ chainId: "",
172
+ // 주문 생성 시점에는 없음
173
+ status: order.status,
174
+ expiresAt: "",
175
+ // 주문 생성 시점에는 없음
176
+ redirectUrl: order.redirectUrl || null
177
+ // 서버 응답의 redirectUrl
178
+ };
179
+ };
180
+ var getOrder = async (publicOrderId) => {
181
+ const response = await apiClient.getClient().get(
182
+ `${API_ENDPOINTS.ORDERS}/${publicOrderId}`
183
+ );
184
+ const orderData = response.data.output;
185
+ if (!orderData || typeof orderData !== "object") {
186
+ throw new Error("Order data is null or invalid");
187
+ }
188
+ if (!orderData.orderId || orderData.orderStat === void 0 || orderData.orderStat === null) {
189
+ throw new Error("Order data is incomplete - missing required fields");
190
+ }
191
+ return orderData;
192
+ };
193
+ var getOrders = async () => {
194
+ const response = await apiClient.getClient().get(API_ENDPOINTS.ORDERS);
195
+ return response.data.output;
196
+ };
197
+ var notifyTransaction = async (publicOrderId, txHash) => {
198
+ await apiClient.getClient().post(
199
+ `${API_ENDPOINTS.ORDERS}/${publicOrderId}/transaction`,
200
+ { txHash }
201
+ );
202
+ };
203
+
204
+ export {
205
+ __export,
206
+ SDK_VERSION,
207
+ API_ENDPOINTS,
208
+ ENVIRONMENT_URLS,
209
+ PAYMENT_DOMAINS,
210
+ DEFAULT_TIMEOUT,
211
+ ORDER_EXPIRY_MINUTES,
212
+ MIN_WITHDRAWAL_AMOUNT,
213
+ SUPPORTED_CHAINS,
214
+ ERROR_CODES,
215
+ config,
216
+ SDKError,
217
+ APIError,
218
+ createOrder,
219
+ getOrder,
220
+ getOrders,
221
+ notifyTransaction,
222
+ orders_exports
223
+ };