@volr/react-ui 0.1.94 → 0.1.95

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.js CHANGED
@@ -1,13 +1,351 @@
1
- import React11, { createContext, useContext, useState, useMemo, useEffect, useCallback, useRef, useId } from 'react';
1
+ import React10, { createContext, useContext, useState, useMemo, useEffect, useCallback, useRef, useId, useReducer } from 'react';
2
2
  import { createPortal } from 'react-dom';
3
- import { useVolrContext, useInternalAuth, usePasskeyEnrollment, useMpcConnection, VolrProvider, useVolrAuthCallback, useVolrPay, useVolrLogin, useDepositListener, createGetNetworkInfo } from '@volr/react';
4
- export { VolrProvider, useDepositListener, usePasskeyEnrollment, useVolr, useVolrLogin } from '@volr/react';
3
+ import { useVolrContext, useInternalAuth, usePasskeyEnrollment, useMpcConnection, VolrProvider, useVolrAuthCallback, useVolrPaymentApi, useVolrLogin, useVolr, useDepositListener, createGetNetworkInfo } from '@volr/react';
4
+ export { VolrProvider, useDepositListener, usePasskeyEnrollment, useVolr, useVolrLogin, useVolrPaymentApi } from '@volr/react';
5
5
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
6
  import { clsx } from 'clsx';
7
7
  import { extendTailwindMerge } from 'tailwind-merge';
8
8
  import { QRCodeSVG } from 'qrcode.react';
9
9
 
10
- // src/providers/VolrUIProvider.tsx
10
+ var __getOwnPropNames = Object.getOwnPropertyNames;
11
+ var __esm = (fn, res) => function __init() {
12
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
13
+ };
14
+
15
+ // ../node_modules/viem/_esm/constants/abis.js
16
+ var universalResolverErrors, erc20Abi;
17
+ var init_abis = __esm({
18
+ "../node_modules/viem/_esm/constants/abis.js"() {
19
+ universalResolverErrors = [
20
+ {
21
+ inputs: [
22
+ {
23
+ name: "dns",
24
+ type: "bytes"
25
+ }
26
+ ],
27
+ name: "DNSDecodingFailed",
28
+ type: "error"
29
+ },
30
+ {
31
+ inputs: [
32
+ {
33
+ name: "ens",
34
+ type: "string"
35
+ }
36
+ ],
37
+ name: "DNSEncodingFailed",
38
+ type: "error"
39
+ },
40
+ {
41
+ inputs: [],
42
+ name: "EmptyAddress",
43
+ type: "error"
44
+ },
45
+ {
46
+ inputs: [
47
+ {
48
+ name: "status",
49
+ type: "uint16"
50
+ },
51
+ {
52
+ name: "message",
53
+ type: "string"
54
+ }
55
+ ],
56
+ name: "HttpError",
57
+ type: "error"
58
+ },
59
+ {
60
+ inputs: [],
61
+ name: "InvalidBatchGatewayResponse",
62
+ type: "error"
63
+ },
64
+ {
65
+ inputs: [
66
+ {
67
+ name: "errorData",
68
+ type: "bytes"
69
+ }
70
+ ],
71
+ name: "ResolverError",
72
+ type: "error"
73
+ },
74
+ {
75
+ inputs: [
76
+ {
77
+ name: "name",
78
+ type: "bytes"
79
+ },
80
+ {
81
+ name: "resolver",
82
+ type: "address"
83
+ }
84
+ ],
85
+ name: "ResolverNotContract",
86
+ type: "error"
87
+ },
88
+ {
89
+ inputs: [
90
+ {
91
+ name: "name",
92
+ type: "bytes"
93
+ }
94
+ ],
95
+ name: "ResolverNotFound",
96
+ type: "error"
97
+ },
98
+ {
99
+ inputs: [
100
+ {
101
+ name: "primary",
102
+ type: "string"
103
+ },
104
+ {
105
+ name: "primaryAddress",
106
+ type: "bytes"
107
+ }
108
+ ],
109
+ name: "ReverseAddressMismatch",
110
+ type: "error"
111
+ },
112
+ {
113
+ inputs: [
114
+ {
115
+ internalType: "bytes4",
116
+ name: "selector",
117
+ type: "bytes4"
118
+ }
119
+ ],
120
+ name: "UnsupportedResolverProfile",
121
+ type: "error"
122
+ }
123
+ ];
124
+ [
125
+ ...universalResolverErrors,
126
+ {
127
+ name: "resolveWithGateways",
128
+ type: "function",
129
+ stateMutability: "view",
130
+ inputs: [
131
+ { name: "name", type: "bytes" },
132
+ { name: "data", type: "bytes" },
133
+ { name: "gateways", type: "string[]" }
134
+ ],
135
+ outputs: [
136
+ { name: "", type: "bytes" },
137
+ { name: "address", type: "address" }
138
+ ]
139
+ }
140
+ ];
141
+ [
142
+ ...universalResolverErrors,
143
+ {
144
+ name: "reverseWithGateways",
145
+ type: "function",
146
+ stateMutability: "view",
147
+ inputs: [
148
+ { type: "bytes", name: "reverseName" },
149
+ { type: "uint256", name: "coinType" },
150
+ { type: "string[]", name: "gateways" }
151
+ ],
152
+ outputs: [
153
+ { type: "string", name: "resolvedName" },
154
+ { type: "address", name: "resolver" },
155
+ { type: "address", name: "reverseResolver" }
156
+ ]
157
+ }
158
+ ];
159
+ erc20Abi = [
160
+ {
161
+ type: "event",
162
+ name: "Approval",
163
+ inputs: [
164
+ {
165
+ indexed: true,
166
+ name: "owner",
167
+ type: "address"
168
+ },
169
+ {
170
+ indexed: true,
171
+ name: "spender",
172
+ type: "address"
173
+ },
174
+ {
175
+ indexed: false,
176
+ name: "value",
177
+ type: "uint256"
178
+ }
179
+ ]
180
+ },
181
+ {
182
+ type: "event",
183
+ name: "Transfer",
184
+ inputs: [
185
+ {
186
+ indexed: true,
187
+ name: "from",
188
+ type: "address"
189
+ },
190
+ {
191
+ indexed: true,
192
+ name: "to",
193
+ type: "address"
194
+ },
195
+ {
196
+ indexed: false,
197
+ name: "value",
198
+ type: "uint256"
199
+ }
200
+ ]
201
+ },
202
+ {
203
+ type: "function",
204
+ name: "allowance",
205
+ stateMutability: "view",
206
+ inputs: [
207
+ {
208
+ name: "owner",
209
+ type: "address"
210
+ },
211
+ {
212
+ name: "spender",
213
+ type: "address"
214
+ }
215
+ ],
216
+ outputs: [
217
+ {
218
+ type: "uint256"
219
+ }
220
+ ]
221
+ },
222
+ {
223
+ type: "function",
224
+ name: "approve",
225
+ stateMutability: "nonpayable",
226
+ inputs: [
227
+ {
228
+ name: "spender",
229
+ type: "address"
230
+ },
231
+ {
232
+ name: "amount",
233
+ type: "uint256"
234
+ }
235
+ ],
236
+ outputs: [
237
+ {
238
+ type: "bool"
239
+ }
240
+ ]
241
+ },
242
+ {
243
+ type: "function",
244
+ name: "balanceOf",
245
+ stateMutability: "view",
246
+ inputs: [
247
+ {
248
+ name: "account",
249
+ type: "address"
250
+ }
251
+ ],
252
+ outputs: [
253
+ {
254
+ type: "uint256"
255
+ }
256
+ ]
257
+ },
258
+ {
259
+ type: "function",
260
+ name: "decimals",
261
+ stateMutability: "view",
262
+ inputs: [],
263
+ outputs: [
264
+ {
265
+ type: "uint8"
266
+ }
267
+ ]
268
+ },
269
+ {
270
+ type: "function",
271
+ name: "name",
272
+ stateMutability: "view",
273
+ inputs: [],
274
+ outputs: [
275
+ {
276
+ type: "string"
277
+ }
278
+ ]
279
+ },
280
+ {
281
+ type: "function",
282
+ name: "symbol",
283
+ stateMutability: "view",
284
+ inputs: [],
285
+ outputs: [
286
+ {
287
+ type: "string"
288
+ }
289
+ ]
290
+ },
291
+ {
292
+ type: "function",
293
+ name: "totalSupply",
294
+ stateMutability: "view",
295
+ inputs: [],
296
+ outputs: [
297
+ {
298
+ type: "uint256"
299
+ }
300
+ ]
301
+ },
302
+ {
303
+ type: "function",
304
+ name: "transfer",
305
+ stateMutability: "nonpayable",
306
+ inputs: [
307
+ {
308
+ name: "recipient",
309
+ type: "address"
310
+ },
311
+ {
312
+ name: "amount",
313
+ type: "uint256"
314
+ }
315
+ ],
316
+ outputs: [
317
+ {
318
+ type: "bool"
319
+ }
320
+ ]
321
+ },
322
+ {
323
+ type: "function",
324
+ name: "transferFrom",
325
+ stateMutability: "nonpayable",
326
+ inputs: [
327
+ {
328
+ name: "sender",
329
+ type: "address"
330
+ },
331
+ {
332
+ name: "recipient",
333
+ type: "address"
334
+ },
335
+ {
336
+ name: "amount",
337
+ type: "uint256"
338
+ }
339
+ ],
340
+ outputs: [
341
+ {
342
+ type: "bool"
343
+ }
344
+ ]
345
+ }
346
+ ];
347
+ }
348
+ });
11
349
 
12
350
  // src/i18n/locales/en.ts
13
351
  var en = {
@@ -971,7 +1309,7 @@ var variantMap = {
971
1309
  ghost: { backgroundColor: "transparent", color: "var(--volr-text-secondary)", border: "none" },
972
1310
  outline: { backgroundColor: "transparent", color: "var(--volr-text-secondary)", border: "1px solid var(--volr-border)" }
973
1311
  };
974
- var Button = React11.forwardRef(
1312
+ var Button = React10.forwardRef(
975
1313
  ({ variant = "primary", size = "md", fullWidth, className, style, disabled, children, ...props }, ref) => {
976
1314
  const { accentColor } = useVolrUI();
977
1315
  const sizeStyle = sizeMap[size];
@@ -2159,8 +2497,12 @@ function SigninModal({ isOpen, onClose, onError }) {
2159
2497
  }
2160
2498
  setCurrentScreen("passkey-setup");
2161
2499
  };
2162
- const handleSiweSuccess = (_data) => {
2163
- onClose();
2500
+ const handleSiweSuccess = (data) => {
2501
+ if (data.keyStorageType) {
2502
+ onClose();
2503
+ return;
2504
+ }
2505
+ setCurrentScreen("passkey-setup");
2164
2506
  };
2165
2507
  const handlePasskeyComplete = () => {
2166
2508
  onClose();
@@ -2421,7 +2763,7 @@ function AssetSelectView({
2421
2763
  }) })
2422
2764
  ] });
2423
2765
  }
2424
- var TextLinkButton = React11.forwardRef(({ showArrow = false, className, children, ...props }, ref) => {
2766
+ var TextLinkButton = React10.forwardRef(({ showArrow = false, className, children, ...props }, ref) => {
2425
2767
  return /* @__PURE__ */ jsxs(
2426
2768
  "button",
2427
2769
  {
@@ -3097,6 +3439,302 @@ var DepositModal = ({
3097
3439
  )
3098
3440
  ] });
3099
3441
  };
3442
+
3443
+ // ../node_modules/viem/_esm/index.js
3444
+ init_abis();
3445
+
3446
+ // src/hooks/usePaymentModalState.ts
3447
+ var initialState = {
3448
+ step: "info",
3449
+ processingStep: "signing",
3450
+ balance: "0",
3451
+ isBalanceLoading: true,
3452
+ isPaying: false,
3453
+ currentPayment: null,
3454
+ txHash: void 0,
3455
+ error: void 0,
3456
+ logoUrl: void 0,
3457
+ receiverAddress: null,
3458
+ showDeposit: false
3459
+ };
3460
+ function paymentModalReducer(state, action) {
3461
+ switch (action.type) {
3462
+ case "RESET":
3463
+ return { ...initialState };
3464
+ case "INIT_PAYMENT":
3465
+ return {
3466
+ ...initialState,
3467
+ currentPayment: action.payment,
3468
+ isBalanceLoading: true
3469
+ };
3470
+ case "SET_BALANCE":
3471
+ return { ...state, balance: action.balance };
3472
+ case "SET_BALANCE_LOADING":
3473
+ return { ...state, isBalanceLoading: action.loading };
3474
+ case "SET_LOGO_URL":
3475
+ return { ...state, logoUrl: action.logoUrl };
3476
+ case "SET_RECEIVER_ADDRESS":
3477
+ return { ...state, receiverAddress: action.receiverAddress };
3478
+ case "START_PAYMENT":
3479
+ return {
3480
+ ...state,
3481
+ isPaying: true,
3482
+ step: "processing",
3483
+ processingStep: "signing",
3484
+ error: void 0
3485
+ };
3486
+ case "SET_PROCESSING_STEP":
3487
+ return { ...state, processingStep: action.step };
3488
+ case "SET_TX_HASH":
3489
+ return { ...state, txHash: action.txHash };
3490
+ case "PAYMENT_SUCCESS":
3491
+ return {
3492
+ ...state,
3493
+ isPaying: false,
3494
+ step: "result",
3495
+ currentPayment: action.payment
3496
+ };
3497
+ case "PAYMENT_ERROR":
3498
+ return {
3499
+ ...state,
3500
+ isPaying: false,
3501
+ step: "result",
3502
+ error: action.error
3503
+ };
3504
+ case "RETRY":
3505
+ return {
3506
+ ...state,
3507
+ step: "info",
3508
+ error: void 0,
3509
+ isBalanceLoading: true
3510
+ };
3511
+ case "SHOW_DEPOSIT":
3512
+ return { ...state, showDeposit: true };
3513
+ case "HIDE_DEPOSIT":
3514
+ return { ...state, showDeposit: false, isBalanceLoading: true };
3515
+ default:
3516
+ return state;
3517
+ }
3518
+ }
3519
+ function usePaymentModalState(open, onOpenChange) {
3520
+ const { user } = useVolrContext();
3521
+ const { client } = useInternalAuth();
3522
+ const { evm } = useVolr();
3523
+ const { paymentOptions } = useVolrModal();
3524
+ const { updatePaymentToProcessing, pollPaymentStatus } = useVolrPaymentApi();
3525
+ const [state, dispatch] = useReducer(paymentModalReducer, initialState);
3526
+ const fetchBalance = useCallback(
3527
+ async (payment) => {
3528
+ if (!user?.evmAddress) {
3529
+ dispatch({ type: "SET_BALANCE_LOADING", loading: false });
3530
+ return;
3531
+ }
3532
+ dispatch({ type: "SET_BALANCE_LOADING", loading: true });
3533
+ try {
3534
+ const evmClient = evm(payment.token.chainId);
3535
+ const tokenAddress = payment.token.id.split("_")[1];
3536
+ let balanceRaw;
3537
+ if (tokenAddress === "native") {
3538
+ balanceRaw = await evmClient.getBalance(user.evmAddress);
3539
+ } else {
3540
+ balanceRaw = await evmClient.readContract({
3541
+ address: tokenAddress,
3542
+ abi: erc20Abi,
3543
+ functionName: "balanceOf",
3544
+ args: [user.evmAddress]
3545
+ });
3546
+ }
3547
+ const balanceNum = Number(balanceRaw) / Math.pow(10, payment.token.decimals);
3548
+ const formattedBalance = balanceNum.toLocaleString(void 0, {
3549
+ minimumFractionDigits: 0,
3550
+ maximumFractionDigits: payment.token.decimals > 6 ? 6 : payment.token.decimals
3551
+ });
3552
+ dispatch({ type: "SET_BALANCE", balance: formattedBalance });
3553
+ } catch (err) {
3554
+ console.error("Failed to fetch balance:", err);
3555
+ dispatch({ type: "SET_BALANCE", balance: "0" });
3556
+ } finally {
3557
+ dispatch({ type: "SET_BALANCE_LOADING", loading: false });
3558
+ }
3559
+ },
3560
+ [user, evm]
3561
+ );
3562
+ const fetchBranding = useCallback(async () => {
3563
+ try {
3564
+ const response = await client.get("/auth/branding");
3565
+ dispatch({ type: "SET_LOGO_URL", logoUrl: response.logoUrl });
3566
+ } catch {
3567
+ }
3568
+ }, [client]);
3569
+ const fetchPaymentConfig = useCallback(async () => {
3570
+ try {
3571
+ const response = await client.get(
3572
+ "/payments/config"
3573
+ );
3574
+ dispatch({
3575
+ type: "SET_RECEIVER_ADDRESS",
3576
+ receiverAddress: response.receiverAddress
3577
+ });
3578
+ } catch (err) {
3579
+ console.error("Failed to fetch payment config:", err);
3580
+ dispatch({ type: "SET_RECEIVER_ADDRESS", receiverAddress: null });
3581
+ }
3582
+ }, [client]);
3583
+ useEffect(() => {
3584
+ if (open && paymentOptions) {
3585
+ dispatch({ type: "INIT_PAYMENT", payment: paymentOptions.payment });
3586
+ fetchBalance(paymentOptions.payment);
3587
+ fetchBranding();
3588
+ fetchPaymentConfig();
3589
+ } else if (!open) {
3590
+ dispatch({ type: "RESET" });
3591
+ }
3592
+ }, [open, paymentOptions, fetchBalance, fetchBranding, fetchPaymentConfig]);
3593
+ const handlePay = useCallback(async () => {
3594
+ const { currentPayment, receiverAddress } = state;
3595
+ if (!currentPayment || !user?.evmAddress || !paymentOptions || !receiverAddress) {
3596
+ return;
3597
+ }
3598
+ dispatch({ type: "START_PAYMENT" });
3599
+ try {
3600
+ const evmClient = evm(currentPayment.token.chainId);
3601
+ const tokenAddress = currentPayment.token.id.split("_")[1];
3602
+ dispatch({ type: "SET_PROCESSING_STEP", step: "broadcasting" });
3603
+ let result;
3604
+ if (tokenAddress === "native") {
3605
+ result = await evmClient.sendTransaction({
3606
+ to: receiverAddress,
3607
+ data: "0x",
3608
+ value: BigInt(currentPayment.amount)
3609
+ });
3610
+ } else {
3611
+ result = await evmClient.sendBatch([
3612
+ {
3613
+ target: tokenAddress,
3614
+ abi: erc20Abi,
3615
+ functionName: "transfer",
3616
+ args: [
3617
+ receiverAddress,
3618
+ BigInt(currentPayment.amount)
3619
+ ]
3620
+ }
3621
+ ]);
3622
+ }
3623
+ dispatch({ type: "SET_TX_HASH", txHash: result.txHash ?? "" });
3624
+ dispatch({ type: "SET_PROCESSING_STEP", step: "confirming" });
3625
+ await updatePaymentToProcessing(currentPayment.id, result.txId);
3626
+ paymentOptions.onProcessing?.({
3627
+ ...currentPayment,
3628
+ txHash: result.txHash ?? "",
3629
+ status: "PROCESSING"
3630
+ });
3631
+ const confirmedPayment = await pollPaymentStatus(currentPayment.id);
3632
+ dispatch({ type: "PAYMENT_SUCCESS", payment: confirmedPayment });
3633
+ if (confirmedPayment.status === "CONFIRMED") {
3634
+ paymentOptions.onComplete?.(confirmedPayment);
3635
+ } else if (confirmedPayment.status === "FAILED") {
3636
+ const failError = {
3637
+ code: "PAYMENT_FAILED",
3638
+ message: "Transaction failed on-chain"
3639
+ };
3640
+ dispatch({ type: "PAYMENT_ERROR", error: failError });
3641
+ paymentOptions.onError?.(failError);
3642
+ }
3643
+ } catch (err) {
3644
+ console.error("Payment failed:", err);
3645
+ const paymentError = {
3646
+ code: err.code || "PAYMENT_FAILED",
3647
+ message: err.message || "Payment failed. Please try again."
3648
+ };
3649
+ dispatch({ type: "PAYMENT_ERROR", error: paymentError });
3650
+ paymentOptions?.onError?.(paymentError);
3651
+ }
3652
+ }, [
3653
+ state,
3654
+ user,
3655
+ paymentOptions,
3656
+ evm,
3657
+ updatePaymentToProcessing,
3658
+ pollPaymentStatus
3659
+ ]);
3660
+ const handleDeposit = useCallback(() => {
3661
+ dispatch({ type: "SHOW_DEPOSIT" });
3662
+ }, []);
3663
+ const handleDepositClose = useCallback(
3664
+ (depositOpen) => {
3665
+ if (!depositOpen) {
3666
+ dispatch({ type: "HIDE_DEPOSIT" });
3667
+ if (state.currentPayment) {
3668
+ fetchBalance(state.currentPayment);
3669
+ }
3670
+ }
3671
+ },
3672
+ [state.currentPayment, fetchBalance]
3673
+ );
3674
+ const handleDone = useCallback(() => {
3675
+ if (state.currentPayment?.status === "CONFIRMED") {
3676
+ paymentOptions?.onComplete?.(state.currentPayment);
3677
+ }
3678
+ onOpenChange(false);
3679
+ }, [state.currentPayment, paymentOptions, onOpenChange]);
3680
+ const handleRetry = useCallback(() => {
3681
+ dispatch({ type: "RETRY" });
3682
+ if (state.currentPayment) {
3683
+ fetchBalance(state.currentPayment);
3684
+ }
3685
+ }, [state.currentPayment, fetchBalance]);
3686
+ const handleClose = useCallback(() => {
3687
+ if (state.step === "processing") {
3688
+ return;
3689
+ }
3690
+ if (state.step === "info") {
3691
+ paymentOptions?.onCancel?.();
3692
+ }
3693
+ onOpenChange(false);
3694
+ }, [state.step, paymentOptions, onOpenChange]);
3695
+ const handleReport = useCallback(async () => {
3696
+ const { currentPayment, error, step, txHash } = state;
3697
+ if (!currentPayment || !error) return;
3698
+ try {
3699
+ await client.post(`/payments/${currentPayment.id}/report`, {
3700
+ errorCode: error.code,
3701
+ errorMessage: error.message,
3702
+ platform: "web",
3703
+ context: {
3704
+ step,
3705
+ txHash,
3706
+ tokenId: currentPayment.token.id,
3707
+ chainId: currentPayment.token.chainId,
3708
+ amount: currentPayment.amount
3709
+ }
3710
+ });
3711
+ alert("Error report submitted. Thank you for your feedback.");
3712
+ } catch (err) {
3713
+ console.error("Failed to submit error report:", err);
3714
+ alert(
3715
+ `Error Code: ${error.code}
3716
+
3717
+ Message: ${error.message}
3718
+
3719
+ Please contact support with this information.`
3720
+ );
3721
+ }
3722
+ }, [state, client]);
3723
+ const canClose = state.step !== "processing";
3724
+ const isConfigured = state.receiverAddress !== null;
3725
+ return {
3726
+ state,
3727
+ handlePay,
3728
+ handleDeposit,
3729
+ handleDepositClose,
3730
+ handleDone,
3731
+ handleRetry,
3732
+ handleClose,
3733
+ handleReport,
3734
+ canClose,
3735
+ isConfigured
3736
+ };
3737
+ }
3100
3738
  var PaymentSkeleton = () => {
3101
3739
  return /* @__PURE__ */ jsxs("div", { className: "volr:p-6 volr:space-y-6 volr:animate-pulse", children: [
3102
3740
  /* @__PURE__ */ jsxs("div", { className: "volr:flex volr:items-center volr:gap-4", children: [
@@ -3412,10 +4050,11 @@ var PaymentResultView = ({
3412
4050
  payment,
3413
4051
  error,
3414
4052
  onDone,
3415
- onRetry
4053
+ onRetry,
4054
+ onReport
3416
4055
  }) => {
3417
4056
  const isSuccess = payment.status === "CONFIRMED";
3418
- payment.status === "FAILED" || !!error;
4057
+ const isFailed = payment.status === "FAILED" || !!error;
3419
4058
  return /* @__PURE__ */ jsxs("div", { className: "volr:p-6 volr:space-y-6", children: [
3420
4059
  /* @__PURE__ */ jsx("div", { className: "volr:flex volr:justify-center volr:py-4", children: isSuccess ? /* @__PURE__ */ jsx("div", { className: "volr:w-16 volr:h-16 volr:rounded-full volr:bg-green-100 volr:flex volr:items-center volr:justify-center", children: /* @__PURE__ */ jsx(
3421
4060
  "svg",
@@ -3454,7 +4093,11 @@ var PaymentResultView = ({
3454
4093
  ) }) }),
3455
4094
  /* @__PURE__ */ jsxs("div", { className: "volr:text-center", children: [
3456
4095
  /* @__PURE__ */ jsx("h3", { className: "volr:text-lg volr:font-semibold volr:text-slate-900", children: isSuccess ? "Payment Complete" : "Payment Failed" }),
3457
- /* @__PURE__ */ jsx("p", { className: "volr:text-sm volr:text-slate-500 volr:mt-1", children: isSuccess ? "Your transaction has been confirmed" : error?.message || "The transaction could not be completed" })
4096
+ /* @__PURE__ */ jsx("p", { className: "volr:text-sm volr:text-slate-500 volr:mt-1", children: isSuccess ? "Your transaction has been confirmed" : error?.message || "The transaction could not be completed" }),
4097
+ isFailed && error?.code && /* @__PURE__ */ jsxs("p", { className: "volr:text-xs volr:text-slate-400 volr:mt-1 volr:font-mono", children: [
4098
+ "Error code: ",
4099
+ error.code
4100
+ ] })
3458
4101
  ] }),
3459
4102
  isSuccess && payment.txHash && /* @__PURE__ */ jsxs("div", { className: "volr:bg-slate-50 volr:rounded-lg volr:p-4 volr:space-y-2", children: [
3460
4103
  /* @__PURE__ */ jsxs("div", { className: "volr:flex volr:justify-between volr:text-sm", children: [
@@ -3483,7 +4126,10 @@ var PaymentResultView = ({
3483
4126
  ] }),
3484
4127
  /* @__PURE__ */ jsx("div", { className: "volr:space-y-3", children: isSuccess ? /* @__PURE__ */ jsx(Button, { onClick: onDone, className: "volr:w-full", children: "Done" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
3485
4128
  /* @__PURE__ */ jsx(Button, { onClick: onRetry, className: "volr:w-full", children: "Try Again" }),
3486
- /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: onDone, className: "volr:w-full", children: "Cancel" })
4129
+ /* @__PURE__ */ jsxs("div", { className: "volr:flex volr:gap-3", children: [
4130
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: onReport, className: "volr:flex-1", children: "Report Issue" }),
4131
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: onDone, className: "volr:flex-1", children: "Cancel" })
4132
+ ] })
3487
4133
  ] }) })
3488
4134
  ] });
3489
4135
  };
@@ -3491,139 +4137,61 @@ var PaymentModal = ({
3491
4137
  open,
3492
4138
  onOpenChange
3493
4139
  }) => {
3494
- const { user } = useVolrContext();
3495
- const { client } = useInternalAuth();
3496
- const { paymentOptions, close: closeModal } = useVolrModal();
3497
- const { updatePaymentToProcessing, checkPayment, pollPaymentStatus } = useVolrPay();
3498
- const [step, setStep] = useState("info");
3499
- const [processingStep, setProcessingStep] = useState("signing");
3500
- const [balance, setBalance] = useState("0");
3501
- const [isBalanceLoading, setIsBalanceLoading] = useState(true);
3502
- const [isPaying, setIsPaying] = useState(false);
3503
- const [currentPayment, setCurrentPayment] = useState(null);
3504
- const [txHash, setTxHash] = useState();
3505
- const [error, setError] = useState();
3506
- const [logoUrl, setLogoUrl] = useState();
3507
- useEffect(() => {
3508
- if (open && paymentOptions) {
3509
- setStep("info");
3510
- setProcessingStep("signing");
3511
- setCurrentPayment(paymentOptions.payment);
3512
- setError(void 0);
3513
- setTxHash(void 0);
3514
- fetchBalance(paymentOptions.payment);
3515
- fetchBranding();
3516
- } else if (!open) {
3517
- setCurrentPayment(null);
3518
- setBalance("0");
3519
- setIsBalanceLoading(true);
3520
- }
3521
- }, [open, paymentOptions]);
3522
- const fetchBalance = async (payment) => {
3523
- if (!user?.evmAddress) {
3524
- setIsBalanceLoading(false);
3525
- return;
3526
- }
3527
- setIsBalanceLoading(true);
3528
- try {
3529
- const response = await client.get(
3530
- `/wallet/balance?chainId=${payment.token.chainId}&address=${user.evmAddress}&tokenId=${payment.token.id}`
3531
- ).catch(() => ({ balance: "0" }));
3532
- const balanceNum = Number(response.balance) / Math.pow(10, payment.token.decimals);
3533
- setBalance(balanceNum.toLocaleString(void 0, {
3534
- minimumFractionDigits: 0,
3535
- maximumFractionDigits: payment.token.decimals > 6 ? 6 : payment.token.decimals
3536
- }));
3537
- } catch (err) {
3538
- console.error("Failed to fetch balance:", err);
3539
- setBalance("0");
3540
- } finally {
3541
- setIsBalanceLoading(false);
3542
- }
3543
- };
3544
- const fetchBranding = async () => {
3545
- try {
3546
- const response = await client.get("/auth/branding");
3547
- setLogoUrl(response.logoUrl);
3548
- } catch {
3549
- }
3550
- };
3551
- const handlePay = useCallback(async () => {
3552
- if (!currentPayment || !user?.evmAddress || !paymentOptions) return;
3553
- setIsPaying(true);
3554
- setStep("processing");
3555
- setProcessingStep("signing");
3556
- setError(void 0);
3557
- try {
3558
- const configResponse = await client.get(
3559
- `/payments/config`
3560
- ).catch(() => null);
3561
- if (!configResponse?.receiverAddress) {
3562
- throw new Error("Payment not configured. Please contact the app developer.");
3563
- }
3564
- setProcessingStep("broadcasting");
3565
- const mockTxHash = `0x${Math.random().toString(16).slice(2)}`;
3566
- setTxHash(mockTxHash);
3567
- setProcessingStep("confirming");
3568
- await new Promise((resolve) => setTimeout(resolve, 2e3));
3569
- const finalPayment = {
3570
- ...currentPayment,
3571
- status: "CONFIRMED",
3572
- txHash: mockTxHash,
3573
- confirmedAt: (/* @__PURE__ */ new Date()).toISOString()
3574
- };
3575
- setCurrentPayment(finalPayment);
3576
- setStep("result");
3577
- paymentOptions.onComplete?.(finalPayment);
3578
- } catch (err) {
3579
- console.error("Payment failed:", err);
3580
- const paymentError = {
3581
- code: "PAYMENT_FAILED",
3582
- message: err.message || "Payment failed. Please try again."
3583
- };
3584
- setError(paymentError);
3585
- setStep("result");
3586
- paymentOptions?.onError?.(paymentError);
3587
- } finally {
3588
- setIsPaying(false);
3589
- }
3590
- }, [currentPayment, user, paymentOptions, client, updatePaymentToProcessing]);
3591
- const handleDeposit = useCallback(() => {
3592
- closeModal();
3593
- }, [closeModal]);
3594
- const handleDone = useCallback(() => {
3595
- if (currentPayment) {
3596
- paymentOptions?.onComplete?.(currentPayment);
3597
- }
3598
- onOpenChange(false);
3599
- }, [currentPayment, paymentOptions, onOpenChange]);
3600
- const handleRetry = useCallback(() => {
3601
- setStep("info");
3602
- setError(void 0);
3603
- if (currentPayment) {
3604
- fetchBalance(currentPayment);
3605
- }
3606
- }, [currentPayment]);
3607
- const handleClose = useCallback(() => {
3608
- if (step === "processing") {
3609
- return;
3610
- }
3611
- if (step === "info") {
3612
- paymentOptions?.onCancel?.();
3613
- }
3614
- onOpenChange(false);
3615
- }, [step, paymentOptions, onOpenChange]);
4140
+ const { paymentOptions } = useVolrModal();
4141
+ const {
4142
+ state,
4143
+ handlePay,
4144
+ handleDeposit,
4145
+ handleDepositClose,
4146
+ handleDone,
4147
+ handleRetry,
4148
+ handleClose,
4149
+ handleReport,
4150
+ canClose,
4151
+ isConfigured
4152
+ } = usePaymentModalState(open, onOpenChange);
4153
+ const {
4154
+ step,
4155
+ processingStep,
4156
+ balance,
4157
+ isBalanceLoading,
4158
+ isPaying,
4159
+ currentPayment,
4160
+ txHash,
4161
+ error,
4162
+ logoUrl,
4163
+ showDeposit
4164
+ } = state;
4165
+ if (showDeposit && currentPayment) {
4166
+ return /* @__PURE__ */ jsx(
4167
+ DepositModal,
4168
+ {
4169
+ open: true,
4170
+ onOpenChange: handleDepositClose,
4171
+ asset: {
4172
+ chainId: currentPayment.token.chainId,
4173
+ symbol: currentPayment.token.symbol
4174
+ }
4175
+ }
4176
+ );
4177
+ }
3616
4178
  if (!paymentOptions || !currentPayment) {
3617
4179
  return /* @__PURE__ */ jsxs(Modal, { open, onOpenChange, children: [
3618
4180
  /* @__PURE__ */ jsx(ModalHeader, { onClose: () => onOpenChange(false) }),
3619
4181
  /* @__PURE__ */ jsx(PaymentSkeleton, {})
3620
4182
  ] });
3621
4183
  }
4184
+ if (!isConfigured && step === "info") {
4185
+ return /* @__PURE__ */ jsxs(Modal, { open, onOpenChange: handleClose, children: [
4186
+ /* @__PURE__ */ jsx(ModalHeader, { onClose: handleClose }),
4187
+ /* @__PURE__ */ jsx("div", { className: "volr:p-6 volr:text-center", children: /* @__PURE__ */ jsx("p", { className: "volr:text-sm volr:text-red-600", children: "Payment not configured. Please contact the app developer." }) })
4188
+ ] });
4189
+ }
3622
4190
  return /* @__PURE__ */ jsxs(Modal, { open, onOpenChange: handleClose, children: [
3623
4191
  /* @__PURE__ */ jsx(
3624
4192
  ModalHeader,
3625
4193
  {
3626
- onClose: step !== "processing" ? handleClose : void 0,
4194
+ onClose: canClose ? handleClose : void 0,
3627
4195
  title: step === "info" ? "Confirm Payment" : void 0
3628
4196
  }
3629
4197
  ),
@@ -3646,12 +4214,13 @@ var PaymentModal = ({
3646
4214
  payment: currentPayment,
3647
4215
  error,
3648
4216
  onDone: handleDone,
3649
- onRetry: handleRetry
4217
+ onRetry: handleRetry,
4218
+ onReport: handleReport
3650
4219
  }
3651
4220
  )
3652
4221
  ] });
3653
4222
  };
3654
- var VolrUIContext = React11.createContext(null);
4223
+ var VolrUIContext = React10.createContext(null);
3655
4224
  var useVolrUI = () => {
3656
4225
  const context = useContext(VolrUIContext);
3657
4226
  if (!context) {
@@ -3979,7 +4548,7 @@ function OnboardingChecker({
3979
4548
  }) {
3980
4549
  const { user, provider, isLoading } = useVolrContext();
3981
4550
  const { isOpen: isModalOpen } = useVolrModal();
3982
- const modalWasOpened = React11.useRef(false);
4551
+ const modalWasOpened = React10.useRef(false);
3983
4552
  useEffect(() => {
3984
4553
  if (isModalOpen) {
3985
4554
  modalWasOpened.current = true;
@@ -4088,7 +4657,105 @@ async function getCurrentChainId() {
4088
4657
  });
4089
4658
  return parseInt(chainIdHex, 16);
4090
4659
  }
4660
+ var paymentPromiseResolvers = /* @__PURE__ */ new Map();
4661
+ function useVolrPay() {
4662
+ const { open: openModal } = useVolrModal();
4663
+ const { createPayment, checkPayment, getPaymentHistory, cancelPayment } = useVolrPaymentApi();
4664
+ const currentPaymentIdRef = useRef(null);
4665
+ const pay = useCallback(
4666
+ async (options) => {
4667
+ const payment = await createPayment({
4668
+ amount: options.amount,
4669
+ item: options.item,
4670
+ referenceId: options.referenceId,
4671
+ metadata: options.metadata,
4672
+ idempotencyKey: options.idempotencyKey,
4673
+ expiresInSec: options.expiresInSec
4674
+ });
4675
+ currentPaymentIdRef.current = payment.id;
4676
+ options.handlers?.onCreated?.({ id: payment.id });
4677
+ const waitPromise = new Promise((resolve, reject) => {
4678
+ paymentPromiseResolvers.set(payment.id, { resolve, reject });
4679
+ });
4680
+ const modalOptions = {
4681
+ payment,
4682
+ options: {
4683
+ amount: options.amount,
4684
+ item: options.item,
4685
+ referenceId: options.referenceId,
4686
+ metadata: options.metadata,
4687
+ idempotencyKey: options.idempotencyKey,
4688
+ expiresInSec: options.expiresInSec
4689
+ },
4690
+ onComplete: (result) => {
4691
+ currentPaymentIdRef.current = null;
4692
+ const resolver = paymentPromiseResolvers.get(payment.id);
4693
+ if (resolver) {
4694
+ resolver.resolve(result);
4695
+ paymentPromiseResolvers.delete(payment.id);
4696
+ }
4697
+ if (result.status === "CONFIRMED") {
4698
+ options.handlers?.onSuccess?.(result);
4699
+ }
4700
+ },
4701
+ onProcessing: (processingPayment) => {
4702
+ options.handlers?.onProcessing?.({
4703
+ id: processingPayment.id,
4704
+ txHash: processingPayment.txHash
4705
+ });
4706
+ },
4707
+ onError: (error) => {
4708
+ currentPaymentIdRef.current = null;
4709
+ const resolver = paymentPromiseResolvers.get(payment.id);
4710
+ if (resolver) {
4711
+ resolver.reject(new Error(error.message));
4712
+ paymentPromiseResolvers.delete(payment.id);
4713
+ }
4714
+ options.handlers?.onError?.(error);
4715
+ },
4716
+ onCancel: () => {
4717
+ currentPaymentIdRef.current = null;
4718
+ const resolver = paymentPromiseResolvers.get(payment.id);
4719
+ if (resolver) {
4720
+ resolver.resolve({
4721
+ ...payment,
4722
+ status: "CANCELLED"
4723
+ });
4724
+ paymentPromiseResolvers.delete(payment.id);
4725
+ }
4726
+ options.handlers?.onCancel?.();
4727
+ }
4728
+ };
4729
+ openModal({ mode: "payment", payment: modalOptions });
4730
+ return {
4731
+ id: payment.id,
4732
+ status: payment.status,
4733
+ wait: () => waitPromise,
4734
+ cancel: async () => {
4735
+ await cancelPayment(payment.id);
4736
+ currentPaymentIdRef.current = null;
4737
+ const resolver = paymentPromiseResolvers.get(payment.id);
4738
+ if (resolver) {
4739
+ resolver.resolve({
4740
+ ...payment,
4741
+ status: "CANCELLED"
4742
+ });
4743
+ paymentPromiseResolvers.delete(payment.id);
4744
+ }
4745
+ options.handlers?.onCancel?.();
4746
+ }
4747
+ };
4748
+ },
4749
+ [createPayment, cancelPayment, openModal]
4750
+ );
4751
+ return {
4752
+ pay,
4753
+ checkPayment,
4754
+ getPaymentHistory,
4755
+ isPaymentInProgress: currentPaymentIdRef.current !== null
4756
+ };
4757
+ }
4091
4758
 
4092
- export { I18nContext, I18nProvider, MpcConnectView, PasskeyEnrollView, VolrUIProvider, getCurrentChainId, useI18n, useSwitchNetwork, useTranslation, useVolrModal, useVolrUI, useVolrUIOptional };
4759
+ export { I18nContext, I18nProvider, MpcConnectView, PasskeyEnrollView, VolrUIProvider, getCurrentChainId, useI18n, useSwitchNetwork, useTranslation, useVolrModal, useVolrPay, useVolrUI, useVolrUIOptional };
4093
4760
  //# sourceMappingURL=index.js.map
4094
4761
  //# sourceMappingURL=index.js.map