@doujins/payments-ui 0.1.17 → 0.1.20
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.cjs +442 -385
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +64 -17
- package/dist/index.d.ts +64 -17
- package/dist/index.js +443 -385
- package/dist/index.js.map +1 -1
- package/package.json +13 -13
package/dist/index.cjs
CHANGED
|
@@ -23,7 +23,6 @@ var LabelPrimitive = require('@radix-ui/react-label');
|
|
|
23
23
|
var SelectPrimitive = require('@radix-ui/react-select');
|
|
24
24
|
var ScrollAreaPrimitive = require('@radix-ui/react-scroll-area');
|
|
25
25
|
var TabsPrimitive = require('@radix-ui/react-tabs');
|
|
26
|
-
var QRCode = require('qrcode');
|
|
27
26
|
var AlertDialogPrimitive = require('@radix-ui/react-alert-dialog');
|
|
28
27
|
var CheckboxPrimitive = require('@radix-ui/react-checkbox');
|
|
29
28
|
var splToken = require('@solana/spl-token');
|
|
@@ -56,7 +55,6 @@ var LabelPrimitive__namespace = /*#__PURE__*/_interopNamespace(LabelPrimitive);
|
|
|
56
55
|
var SelectPrimitive__namespace = /*#__PURE__*/_interopNamespace(SelectPrimitive);
|
|
57
56
|
var ScrollAreaPrimitive__namespace = /*#__PURE__*/_interopNamespace(ScrollAreaPrimitive);
|
|
58
57
|
var TabsPrimitive__namespace = /*#__PURE__*/_interopNamespace(TabsPrimitive);
|
|
59
|
-
var QRCode__default = /*#__PURE__*/_interopDefault(QRCode);
|
|
60
58
|
var AlertDialogPrimitive__namespace = /*#__PURE__*/_interopNamespace(AlertDialogPrimitive);
|
|
61
59
|
var CheckboxPrimitive__namespace = /*#__PURE__*/_interopNamespace(CheckboxPrimitive);
|
|
62
60
|
|
|
@@ -294,9 +292,6 @@ var createClient = (config) => {
|
|
|
294
292
|
deletePaymentMethod(id) {
|
|
295
293
|
return request("DELETE", `/me/payment-methods/${id}`, {});
|
|
296
294
|
},
|
|
297
|
-
activatePaymentMethod(id) {
|
|
298
|
-
return request("PUT", `/me/payment-methods/${id}/activate`, {});
|
|
299
|
-
},
|
|
300
295
|
checkout(payload, idempotencyKey) {
|
|
301
296
|
const key = idempotencyKey ?? crypto.randomUUID();
|
|
302
297
|
return request("POST", "/checkout", {
|
|
@@ -306,8 +301,8 @@ var createClient = (config) => {
|
|
|
306
301
|
}
|
|
307
302
|
});
|
|
308
303
|
},
|
|
309
|
-
cancelSubscription(feedback) {
|
|
310
|
-
return request("POST",
|
|
304
|
+
cancelSubscription(subscriptionId, feedback) {
|
|
305
|
+
return request("POST", `/me/subscriptions/${subscriptionId}/cancel`, {
|
|
311
306
|
body: feedback ? { feedback } : void 0
|
|
312
307
|
});
|
|
313
308
|
},
|
|
@@ -325,19 +320,18 @@ var createClient = (config) => {
|
|
|
325
320
|
);
|
|
326
321
|
return normalizeList(result);
|
|
327
322
|
},
|
|
328
|
-
updateSubscriptionPaymentMethod(
|
|
329
|
-
return request("PUT",
|
|
323
|
+
updateSubscriptionPaymentMethod(subscriptionId, paymentMethodId) {
|
|
324
|
+
return request("PUT", `/me/subscriptions/${subscriptionId}/payment-method`, {
|
|
330
325
|
body: {
|
|
331
|
-
|
|
332
|
-
payment_method_id: payload.payment_method_id
|
|
326
|
+
payment_method_id: paymentMethodId
|
|
333
327
|
}
|
|
334
328
|
});
|
|
335
329
|
},
|
|
336
|
-
resumeSubscription() {
|
|
337
|
-
return request("POST",
|
|
330
|
+
resumeSubscription(subscriptionId) {
|
|
331
|
+
return request("POST", `/me/subscriptions/${subscriptionId}/resume`);
|
|
338
332
|
},
|
|
339
|
-
changeSubscription(payload) {
|
|
340
|
-
return request("POST",
|
|
333
|
+
changeSubscription(subscriptionId, payload) {
|
|
334
|
+
return request("POST", `/me/subscriptions/${subscriptionId}/change-tier`, {
|
|
341
335
|
body: payload
|
|
342
336
|
});
|
|
343
337
|
},
|
|
@@ -351,64 +345,23 @@ var createClient = (config) => {
|
|
|
351
345
|
});
|
|
352
346
|
return normalizeList(result);
|
|
353
347
|
},
|
|
354
|
-
async getSolanaTokens() {
|
|
348
|
+
async getSolanaTokens(params) {
|
|
355
349
|
const response = await request(
|
|
356
350
|
"GET",
|
|
357
|
-
"/solana/tokens"
|
|
351
|
+
"/solana/tokens",
|
|
352
|
+
{
|
|
353
|
+
query: {
|
|
354
|
+
checkout_session_id: params?.checkoutSessionId,
|
|
355
|
+
wallet_id: params?.walletId,
|
|
356
|
+
wallet: params?.wallet,
|
|
357
|
+
price_id: params?.priceId
|
|
358
|
+
}
|
|
359
|
+
}
|
|
358
360
|
);
|
|
359
361
|
if (Array.isArray(response)) {
|
|
360
362
|
return response;
|
|
361
363
|
}
|
|
362
364
|
return response.tokens ?? [];
|
|
363
|
-
},
|
|
364
|
-
async createSolanaPayIntent(payload) {
|
|
365
|
-
const response = await request("POST", "/solana/pay", {
|
|
366
|
-
body: {
|
|
367
|
-
price_id: payload.priceId,
|
|
368
|
-
token: payload.token,
|
|
369
|
-
...payload.userWallet ? { user_wallet: payload.userWallet } : {}
|
|
370
|
-
}
|
|
371
|
-
});
|
|
372
|
-
return response;
|
|
373
|
-
},
|
|
374
|
-
async getSolanaPayStatus(reference) {
|
|
375
|
-
const response = await request(
|
|
376
|
-
"GET",
|
|
377
|
-
`/solana/pay/${reference}`
|
|
378
|
-
);
|
|
379
|
-
if (response.status === "confirmed") {
|
|
380
|
-
return {
|
|
381
|
-
status: "confirmed",
|
|
382
|
-
payment_id: response.payment_id ?? "",
|
|
383
|
-
transaction: response.signature ?? null,
|
|
384
|
-
intent_id: response.intent_id
|
|
385
|
-
};
|
|
386
|
-
}
|
|
387
|
-
if (response.status === "expired") {
|
|
388
|
-
return {
|
|
389
|
-
status: "failed",
|
|
390
|
-
payment_id: response.payment_id ?? "",
|
|
391
|
-
transaction: response.signature ?? null,
|
|
392
|
-
intent_id: response.intent_id,
|
|
393
|
-
error_message: "Payment intent expired"
|
|
394
|
-
};
|
|
395
|
-
}
|
|
396
|
-
return {
|
|
397
|
-
status: "pending",
|
|
398
|
-
payment_id: response.payment_id ?? "",
|
|
399
|
-
transaction: response.signature ?? null,
|
|
400
|
-
intent_id: response.intent_id
|
|
401
|
-
};
|
|
402
|
-
},
|
|
403
|
-
async getPaymentStatus(id) {
|
|
404
|
-
try {
|
|
405
|
-
return await request("GET", `/payment/status/${id}`);
|
|
406
|
-
} catch (error) {
|
|
407
|
-
if (error instanceof ClientApiError && error.status === 404) {
|
|
408
|
-
return null;
|
|
409
|
-
}
|
|
410
|
-
throw error;
|
|
411
|
-
}
|
|
412
365
|
}
|
|
413
366
|
};
|
|
414
367
|
};
|
|
@@ -1426,201 +1379,12 @@ var usePaymentNotifications = () => {
|
|
|
1426
1379
|
notifyError
|
|
1427
1380
|
};
|
|
1428
1381
|
};
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
const onErrorRef = React4.useRef(onError);
|
|
1435
|
-
const [intent, setIntent] = React4.useState(null);
|
|
1436
|
-
const [qrDataUri, setQrDataUri] = React4.useState(null);
|
|
1437
|
-
const [isLoading, setIsLoading] = React4.useState(false);
|
|
1438
|
-
const [error, setError] = React4.useState(null);
|
|
1439
|
-
const [timeRemaining, setTimeRemaining] = React4.useState(0);
|
|
1440
|
-
const [refreshNonce, setRefreshNonce] = React4.useState(0);
|
|
1441
|
-
const pollRef = React4.useRef(null);
|
|
1442
|
-
const countdownRef = React4.useRef(null);
|
|
1443
|
-
const clearTimers = React4.useCallback(() => {
|
|
1444
|
-
if (pollRef.current) {
|
|
1445
|
-
clearInterval(pollRef.current);
|
|
1446
|
-
pollRef.current = null;
|
|
1447
|
-
}
|
|
1448
|
-
if (countdownRef.current) {
|
|
1449
|
-
clearInterval(countdownRef.current);
|
|
1450
|
-
countdownRef.current = null;
|
|
1451
|
-
}
|
|
1452
|
-
}, []);
|
|
1453
|
-
React4.useEffect(() => {
|
|
1454
|
-
return () => {
|
|
1455
|
-
clearTimers();
|
|
1456
|
-
};
|
|
1457
|
-
}, [clearTimers]);
|
|
1458
|
-
const resetState = React4.useCallback(
|
|
1459
|
-
(message) => {
|
|
1460
|
-
clearTimers();
|
|
1461
|
-
setIntent(null);
|
|
1462
|
-
setQrDataUri(null);
|
|
1463
|
-
setTimeRemaining(0);
|
|
1464
|
-
setError(message ?? null);
|
|
1465
|
-
},
|
|
1466
|
-
[clearTimers]
|
|
1467
|
-
);
|
|
1468
|
-
React4.useEffect(() => {
|
|
1469
|
-
onSuccessRef.current = onSuccess;
|
|
1470
|
-
}, [onSuccess]);
|
|
1471
|
-
React4.useEffect(() => {
|
|
1472
|
-
onErrorRef.current = onError;
|
|
1473
|
-
}, [onError]);
|
|
1474
|
-
const handleError = React4.useCallback(
|
|
1475
|
-
(message, notifyParent = false) => {
|
|
1476
|
-
console.error("[payments-ui] Solana Pay QR error:", message);
|
|
1477
|
-
clearTimers();
|
|
1478
|
-
resetState(message);
|
|
1479
|
-
if (notifyParent) {
|
|
1480
|
-
onErrorRef.current?.(message);
|
|
1481
|
-
}
|
|
1482
|
-
},
|
|
1483
|
-
[clearTimers, resetState]
|
|
1484
|
-
);
|
|
1485
|
-
const handleSuccess = React4.useCallback(
|
|
1486
|
-
(status) => {
|
|
1487
|
-
clearTimers();
|
|
1488
|
-
resetState(null);
|
|
1489
|
-
console.log("[payments-ui] Solana Pay QR confirmed", {
|
|
1490
|
-
paymentId: status.payment_id,
|
|
1491
|
-
intentId: status.intent_id
|
|
1492
|
-
});
|
|
1493
|
-
const paymentId = status.payment_id ?? void 0;
|
|
1494
|
-
const txId = status.transaction ?? void 0;
|
|
1495
|
-
onSuccessRef.current?.(paymentId, txId);
|
|
1496
|
-
},
|
|
1497
|
-
[clearTimers, resetState]
|
|
1498
|
-
);
|
|
1499
|
-
const pollStatus = React4.useCallback(
|
|
1500
|
-
async (reference) => {
|
|
1501
|
-
try {
|
|
1502
|
-
const status = await client.getSolanaPayStatus(reference);
|
|
1503
|
-
if (status.status === "confirmed") {
|
|
1504
|
-
handleSuccess(status);
|
|
1505
|
-
}
|
|
1506
|
-
if (status.status === "failed") {
|
|
1507
|
-
handleError(
|
|
1508
|
-
status.error_message || "Payment failed. Please try again.",
|
|
1509
|
-
true
|
|
1510
|
-
);
|
|
1511
|
-
}
|
|
1512
|
-
} catch (err) {
|
|
1513
|
-
console.error("Failed to poll Solana Pay status:", err);
|
|
1514
|
-
}
|
|
1515
|
-
},
|
|
1516
|
-
[handleError, handleSuccess, client]
|
|
1517
|
-
);
|
|
1518
|
-
const startCountdown = React4.useCallback(
|
|
1519
|
-
(expiresAt, reference) => {
|
|
1520
|
-
const updateTime = () => {
|
|
1521
|
-
const remaining = Math.max(0, Math.floor(expiresAt - Date.now() / 1e3));
|
|
1522
|
-
setTimeRemaining(remaining);
|
|
1523
|
-
if (remaining === 0) {
|
|
1524
|
-
handleError("Payment intent expired. Please generate a new QR code.");
|
|
1525
|
-
}
|
|
1526
|
-
};
|
|
1527
|
-
updateTime();
|
|
1528
|
-
countdownRef.current = setInterval(updateTime, 1e3);
|
|
1529
|
-
pollRef.current = setInterval(() => void pollStatus(reference), 4e3);
|
|
1530
|
-
},
|
|
1531
|
-
[handleError, pollStatus]
|
|
1382
|
+
|
|
1383
|
+
// src/hooks/useSolanaQrPayment.ts
|
|
1384
|
+
var useSolanaQrPayment = (_options) => {
|
|
1385
|
+
throw new Error(
|
|
1386
|
+
'useSolanaQrPayment is deprecated. The standalone Solana Pay QR endpoints have been removed. Use the checkout session flow instead: POST /checkout with processor="solana".'
|
|
1532
1387
|
);
|
|
1533
|
-
const renderQr = React4.useCallback(async (qrIntent) => {
|
|
1534
|
-
try {
|
|
1535
|
-
const dataUri = await QRCode__default.default.toDataURL(qrIntent.url, {
|
|
1536
|
-
width: 320,
|
|
1537
|
-
margin: 1,
|
|
1538
|
-
color: {
|
|
1539
|
-
dark: "#0f1116",
|
|
1540
|
-
light: "#ffffff"
|
|
1541
|
-
}
|
|
1542
|
-
});
|
|
1543
|
-
setQrDataUri(dataUri);
|
|
1544
|
-
} catch (err) {
|
|
1545
|
-
console.error("Failed to render QR code:", err);
|
|
1546
|
-
handleError("Unable to render QR code");
|
|
1547
|
-
}
|
|
1548
|
-
}, [handleError]);
|
|
1549
|
-
React4.useEffect(() => {
|
|
1550
|
-
let cancelled = false;
|
|
1551
|
-
const generateIntent = async () => {
|
|
1552
|
-
clearTimers();
|
|
1553
|
-
if (!tokenSymbol || !priceId) {
|
|
1554
|
-
resetState(null);
|
|
1555
|
-
setIsLoading((prev) => prev ? false : prev);
|
|
1556
|
-
return;
|
|
1557
|
-
}
|
|
1558
|
-
try {
|
|
1559
|
-
setIsLoading(true);
|
|
1560
|
-
setError(null);
|
|
1561
|
-
console.log("[payments-ui] Requesting Solana Pay QR intent", {
|
|
1562
|
-
priceId,
|
|
1563
|
-
token: tokenSymbol
|
|
1564
|
-
});
|
|
1565
|
-
const nextIntent = await client.createSolanaPayIntent({
|
|
1566
|
-
priceId,
|
|
1567
|
-
token: tokenSymbol
|
|
1568
|
-
});
|
|
1569
|
-
if (cancelled) return;
|
|
1570
|
-
setIntent(nextIntent);
|
|
1571
|
-
setTimeRemaining(
|
|
1572
|
-
Math.max(0, Math.floor(nextIntent.expires_at - Date.now() / 1e3))
|
|
1573
|
-
);
|
|
1574
|
-
await renderQr(nextIntent);
|
|
1575
|
-
console.log("[payments-ui] Solana Pay QR ready", {
|
|
1576
|
-
reference: nextIntent.reference
|
|
1577
|
-
});
|
|
1578
|
-
const reference = nextIntent.reference;
|
|
1579
|
-
if (typeof reference === "string" && reference.length > 0) {
|
|
1580
|
-
startCountdown(nextIntent.expires_at, reference);
|
|
1581
|
-
pollStatus(reference);
|
|
1582
|
-
} else {
|
|
1583
|
-
handleError("Payment reference missing from intent", true);
|
|
1584
|
-
}
|
|
1585
|
-
} catch (err) {
|
|
1586
|
-
if (cancelled) return;
|
|
1587
|
-
console.error("Failed to generate Solana Pay QR intent:", err);
|
|
1588
|
-
const message = err instanceof Error ? err.message : "Unable to create Solana Pay QR code";
|
|
1589
|
-
handleError(message);
|
|
1590
|
-
} finally {
|
|
1591
|
-
if (!cancelled) {
|
|
1592
|
-
setIsLoading(false);
|
|
1593
|
-
}
|
|
1594
|
-
}
|
|
1595
|
-
};
|
|
1596
|
-
void generateIntent();
|
|
1597
|
-
return () => {
|
|
1598
|
-
cancelled = true;
|
|
1599
|
-
clearTimers();
|
|
1600
|
-
};
|
|
1601
|
-
}, [
|
|
1602
|
-
clearTimers,
|
|
1603
|
-
handleError,
|
|
1604
|
-
pollStatus,
|
|
1605
|
-
priceId,
|
|
1606
|
-
renderQr,
|
|
1607
|
-
resetState,
|
|
1608
|
-
client,
|
|
1609
|
-
startCountdown,
|
|
1610
|
-
tokenSymbol,
|
|
1611
|
-
refreshNonce
|
|
1612
|
-
]);
|
|
1613
|
-
const refresh = React4.useCallback(() => {
|
|
1614
|
-
setRefreshNonce((value) => value + 1);
|
|
1615
|
-
}, []);
|
|
1616
|
-
return {
|
|
1617
|
-
intent,
|
|
1618
|
-
qrDataUri,
|
|
1619
|
-
isLoading,
|
|
1620
|
-
error,
|
|
1621
|
-
timeRemaining,
|
|
1622
|
-
refresh
|
|
1623
|
-
};
|
|
1624
1388
|
};
|
|
1625
1389
|
var Card = React4__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1626
1390
|
"div",
|
|
@@ -1681,7 +1445,7 @@ var QRCodePayment = ({
|
|
|
1681
1445
|
onPaymentError,
|
|
1682
1446
|
onPaymentSuccess
|
|
1683
1447
|
}) => {
|
|
1684
|
-
|
|
1448
|
+
React4__namespace.default.useCallback(
|
|
1685
1449
|
(paymentId, txId) => {
|
|
1686
1450
|
if (!paymentId && !txId) {
|
|
1687
1451
|
onPaymentError("Missing payment confirmation details");
|
|
@@ -1692,12 +1456,7 @@ var QRCodePayment = ({
|
|
|
1692
1456
|
},
|
|
1693
1457
|
[onPaymentError, onPaymentSuccess]
|
|
1694
1458
|
);
|
|
1695
|
-
const { intent, qrDataUri, isLoading, error, timeRemaining, refresh } = useSolanaQrPayment(
|
|
1696
|
-
priceId,
|
|
1697
|
-
selectedToken,
|
|
1698
|
-
onError: onPaymentError,
|
|
1699
|
-
onSuccess: handleQrSuccess
|
|
1700
|
-
});
|
|
1459
|
+
const { intent, qrDataUri, isLoading, error, timeRemaining, refresh } = useSolanaQrPayment();
|
|
1701
1460
|
if (!selectedToken) {
|
|
1702
1461
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-md border border-dashed bg-muted/10 px-4 py-6 text-center text-sm text-muted-foreground", children: "Select a token to continue." });
|
|
1703
1462
|
}
|
|
@@ -1788,49 +1547,88 @@ var PaymentStatus = ({
|
|
|
1788
1547
|
}
|
|
1789
1548
|
return null;
|
|
1790
1549
|
};
|
|
1791
|
-
var useSupportedTokens = () => {
|
|
1550
|
+
var useSupportedTokens = (query) => {
|
|
1792
1551
|
const { client } = usePaymentContext();
|
|
1793
1552
|
const [tokens, setTokens] = React4.useState([]);
|
|
1794
1553
|
const [isLoading, setIsLoading] = React4.useState(false);
|
|
1795
1554
|
const [error, setError] = React4.useState(null);
|
|
1796
1555
|
const [lastFetched, setLastFetched] = React4.useState(null);
|
|
1797
1556
|
const CACHE_DURATION = 5 * 60 * 1e3;
|
|
1798
|
-
const
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
const
|
|
1811
|
-
const
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
setIsLoading(
|
|
1827
|
-
|
|
1828
|
-
|
|
1557
|
+
const cacheRef = React4.useRef(
|
|
1558
|
+
/* @__PURE__ */ new Map()
|
|
1559
|
+
);
|
|
1560
|
+
const queryKey = React4.useMemo(() => {
|
|
1561
|
+
const checkoutSessionId = query?.checkoutSessionId?.trim() || "";
|
|
1562
|
+
const walletId = query?.walletId?.trim() || "";
|
|
1563
|
+
const wallet = query?.wallet?.trim() || "";
|
|
1564
|
+
const priceId = query?.priceId?.trim() || "";
|
|
1565
|
+
return [checkoutSessionId, walletId, wallet, priceId].join("|");
|
|
1566
|
+
}, [query?.checkoutSessionId, query?.walletId, query?.wallet, query?.priceId]);
|
|
1567
|
+
const fetchSupportedTokens = React4.useCallback(
|
|
1568
|
+
async (overrideQuery) => {
|
|
1569
|
+
const checkoutSessionId = overrideQuery?.checkoutSessionId ?? query?.checkoutSessionId;
|
|
1570
|
+
const walletId = overrideQuery?.walletId ?? query?.walletId;
|
|
1571
|
+
const wallet = overrideQuery?.wallet ?? query?.wallet;
|
|
1572
|
+
const priceId = overrideQuery?.priceId ?? query?.priceId;
|
|
1573
|
+
const key = [
|
|
1574
|
+
checkoutSessionId?.trim() || "",
|
|
1575
|
+
walletId?.trim() || "",
|
|
1576
|
+
wallet?.trim() || "",
|
|
1577
|
+
priceId?.trim() || ""
|
|
1578
|
+
].join("|");
|
|
1579
|
+
const cached = cacheRef.current.get(key);
|
|
1580
|
+
if (cached && Date.now() - cached.fetchedAt < CACHE_DURATION) {
|
|
1581
|
+
setTokens(cached.tokens);
|
|
1582
|
+
setLastFetched(cached.fetchedAt);
|
|
1583
|
+
return cached.tokens;
|
|
1584
|
+
}
|
|
1585
|
+
setIsLoading(true);
|
|
1586
|
+
setError(null);
|
|
1587
|
+
try {
|
|
1588
|
+
console.log("payments-ui: fetching supported Solana tokens", {
|
|
1589
|
+
checkoutSessionId: checkoutSessionId || void 0,
|
|
1590
|
+
walletId: walletId || void 0,
|
|
1591
|
+
wallet: wallet || void 0,
|
|
1592
|
+
priceId: priceId || void 0
|
|
1593
|
+
});
|
|
1594
|
+
const tokens2 = await client.getSolanaTokens({
|
|
1595
|
+
checkoutSessionId,
|
|
1596
|
+
walletId,
|
|
1597
|
+
wallet,
|
|
1598
|
+
priceId
|
|
1599
|
+
});
|
|
1600
|
+
const sortedTokens = [...tokens2].sort(
|
|
1601
|
+
(a, b) => a.symbol.localeCompare(b.symbol)
|
|
1602
|
+
);
|
|
1603
|
+
const fetchedAt = Date.now();
|
|
1604
|
+
cacheRef.current.set(key, { tokens: sortedTokens, fetchedAt });
|
|
1605
|
+
setTokens(sortedTokens);
|
|
1606
|
+
console.log("payments-ui: loaded supported tokens", {
|
|
1607
|
+
count: sortedTokens.length
|
|
1608
|
+
});
|
|
1609
|
+
setLastFetched(fetchedAt);
|
|
1610
|
+
return sortedTokens;
|
|
1611
|
+
} catch (error2) {
|
|
1612
|
+
const errorMessage = error2 instanceof Error ? error2.message : "Failed to fetch supported tokens";
|
|
1613
|
+
setError(errorMessage);
|
|
1614
|
+
console.error("Failed to fetch supported tokens:", error2);
|
|
1615
|
+
return [];
|
|
1616
|
+
} finally {
|
|
1617
|
+
setIsLoading(false);
|
|
1618
|
+
}
|
|
1619
|
+
},
|
|
1620
|
+
[
|
|
1621
|
+
CACHE_DURATION,
|
|
1622
|
+
client,
|
|
1623
|
+
query?.checkoutSessionId,
|
|
1624
|
+
query?.priceId,
|
|
1625
|
+
query?.wallet,
|
|
1626
|
+
query?.walletId
|
|
1627
|
+
]
|
|
1628
|
+
);
|
|
1829
1629
|
React4.useEffect(() => {
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
}
|
|
1833
|
-
}, [tokens.length, fetchSupportedTokens]);
|
|
1630
|
+
void fetchSupportedTokens();
|
|
1631
|
+
}, [fetchSupportedTokens, queryKey]);
|
|
1834
1632
|
const getTokenBySymbol = React4.useCallback(
|
|
1835
1633
|
(symbol) => {
|
|
1836
1634
|
return tokens.find((token) => token.symbol === symbol);
|
|
@@ -1862,9 +1660,10 @@ var useSupportedTokens = () => {
|
|
|
1862
1660
|
return tokens.filter((token) => ["USDC", "PYUSD"].includes(token.symbol));
|
|
1863
1661
|
}, [tokens]);
|
|
1864
1662
|
const refreshTokens = React4.useCallback(async () => {
|
|
1663
|
+
cacheRef.current.delete(queryKey);
|
|
1865
1664
|
setLastFetched(null);
|
|
1866
|
-
return await fetchSupportedTokens();
|
|
1867
|
-
}, [fetchSupportedTokens]);
|
|
1665
|
+
return await fetchSupportedTokens({ ...query });
|
|
1666
|
+
}, [fetchSupportedTokens, query, queryKey]);
|
|
1868
1667
|
const isCacheStale = React4.useCallback(() => {
|
|
1869
1668
|
if (!lastFetched) return true;
|
|
1870
1669
|
return Date.now() - lastFetched > CACHE_DURATION;
|
|
@@ -1933,21 +1732,34 @@ var useSupportedTokens = () => {
|
|
|
1933
1732
|
var SolanaPaymentView = ({
|
|
1934
1733
|
priceId,
|
|
1935
1734
|
usdAmount,
|
|
1735
|
+
checkoutSessionId,
|
|
1736
|
+
walletId,
|
|
1737
|
+
wallet,
|
|
1936
1738
|
onSuccess,
|
|
1937
1739
|
onError,
|
|
1938
1740
|
onClose
|
|
1939
1741
|
}) => {
|
|
1742
|
+
const { publicKey } = walletAdapterReact.useWallet();
|
|
1940
1743
|
const { notifyStatus, notifyError, notifySuccess } = usePaymentNotifications();
|
|
1941
1744
|
const [paymentState, setPaymentState] = React4.useState("selecting");
|
|
1942
1745
|
const [errorMessage, setErrorMessage] = React4.useState(null);
|
|
1943
1746
|
const [transactionId, setTransactionId] = React4.useState(null);
|
|
1944
|
-
const [tokenAmount, setTokenAmount] = React4.useState(0);
|
|
1945
1747
|
const [selectedTokenSymbol, setSelectedTokenSymbol] = React4.useState(null);
|
|
1748
|
+
const walletAddress = React4.useMemo(() => {
|
|
1749
|
+
if (wallet && wallet.trim()) return wallet.trim();
|
|
1750
|
+
if (!publicKey) return void 0;
|
|
1751
|
+
return publicKey.toBase58();
|
|
1752
|
+
}, [publicKey, wallet]);
|
|
1946
1753
|
const {
|
|
1947
1754
|
tokens,
|
|
1948
1755
|
isLoading: tokensLoading,
|
|
1949
1756
|
error: tokensError
|
|
1950
|
-
} = useSupportedTokens(
|
|
1757
|
+
} = useSupportedTokens({
|
|
1758
|
+
checkoutSessionId,
|
|
1759
|
+
walletId,
|
|
1760
|
+
wallet: walletAddress,
|
|
1761
|
+
priceId
|
|
1762
|
+
});
|
|
1951
1763
|
const selectedToken = React4.useMemo(() => {
|
|
1952
1764
|
if (!tokens.length) return null;
|
|
1953
1765
|
const explicit = tokens.find((token) => token.symbol === selectedTokenSymbol);
|
|
@@ -1959,6 +1771,31 @@ var SolanaPaymentView = ({
|
|
|
1959
1771
|
setSelectedTokenSymbol(defaultToken.symbol);
|
|
1960
1772
|
}
|
|
1961
1773
|
}, [tokens, selectedTokenSymbol]);
|
|
1774
|
+
const selectedTokenAmount = React4.useMemo(() => {
|
|
1775
|
+
if (!selectedToken || usdAmount === 0) return 0;
|
|
1776
|
+
const quote = selectedToken.quote?.token_amount;
|
|
1777
|
+
if (typeof quote === "string") {
|
|
1778
|
+
const parsed = Number.parseFloat(quote);
|
|
1779
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
1780
|
+
}
|
|
1781
|
+
const price = selectedToken.price ?? 0;
|
|
1782
|
+
if (!price || price <= 0) return 0;
|
|
1783
|
+
return usdAmount / price;
|
|
1784
|
+
}, [selectedToken, usdAmount]);
|
|
1785
|
+
const formatTokenAmount = React4.useCallback((token) => {
|
|
1786
|
+
const quote = token.quote?.token_amount;
|
|
1787
|
+
if (typeof quote === "string" && quote.trim()) {
|
|
1788
|
+
const parsed = Number.parseFloat(quote);
|
|
1789
|
+
if (Number.isFinite(parsed)) {
|
|
1790
|
+
return token.symbol === "SOL" ? parsed.toFixed(4) : parsed.toFixed(2);
|
|
1791
|
+
}
|
|
1792
|
+
return quote;
|
|
1793
|
+
}
|
|
1794
|
+
const price = token.price ?? 0;
|
|
1795
|
+
if (!price || price <= 0) return "0";
|
|
1796
|
+
const approx = usdAmount / price;
|
|
1797
|
+
return token.symbol === "SOL" ? approx.toFixed(4) : approx.toFixed(2);
|
|
1798
|
+
}, [usdAmount]);
|
|
1962
1799
|
const handlePaymentSuccess = React4.useCallback(
|
|
1963
1800
|
(result, txId) => {
|
|
1964
1801
|
const resolvedTx = txId || (typeof result === "string" ? result : result.transaction_id);
|
|
@@ -2012,18 +1849,6 @@ var SolanaPaymentView = ({
|
|
|
2012
1849
|
resetState();
|
|
2013
1850
|
onClose?.();
|
|
2014
1851
|
}, [paymentState, onClose, resetState]);
|
|
2015
|
-
React4.useEffect(() => {
|
|
2016
|
-
if (!selectedToken || usdAmount === 0) {
|
|
2017
|
-
setTokenAmount(0);
|
|
2018
|
-
return;
|
|
2019
|
-
}
|
|
2020
|
-
const price = selectedToken.price ?? 0;
|
|
2021
|
-
if (!price || price <= 0) {
|
|
2022
|
-
setTokenAmount(0);
|
|
2023
|
-
return;
|
|
2024
|
-
}
|
|
2025
|
-
setTokenAmount(usdAmount / price);
|
|
2026
|
-
}, [usdAmount, selectedToken]);
|
|
2027
1852
|
const handleTokenChange = React4.useCallback((value) => {
|
|
2028
1853
|
setSelectedTokenSymbol(value);
|
|
2029
1854
|
}, []);
|
|
@@ -2034,7 +1859,7 @@ var SolanaPaymentView = ({
|
|
|
2034
1859
|
{
|
|
2035
1860
|
state: paymentState,
|
|
2036
1861
|
usdAmount,
|
|
2037
|
-
solAmount:
|
|
1862
|
+
solAmount: selectedTokenAmount,
|
|
2038
1863
|
onRetry: handleRetry,
|
|
2039
1864
|
onClose: handleClose,
|
|
2040
1865
|
errorMessage,
|
|
@@ -2062,9 +1887,9 @@ var SolanaPaymentView = ({
|
|
|
2062
1887
|
usdAmount.toFixed(2),
|
|
2063
1888
|
" USD"
|
|
2064
1889
|
] }),
|
|
2065
|
-
selectedToken &&
|
|
1890
|
+
selectedToken && selectedTokenAmount > 0 && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-muted-foreground", children: [
|
|
2066
1891
|
"\u2248 ",
|
|
2067
|
-
|
|
1892
|
+
selectedTokenAmount.toFixed(selectedToken.symbol === "SOL" ? 4 : 2),
|
|
2068
1893
|
" ",
|
|
2069
1894
|
selectedToken.symbol
|
|
2070
1895
|
] })
|
|
@@ -2073,13 +1898,22 @@ var SolanaPaymentView = ({
|
|
|
2073
1898
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-foreground", children: "Select token" }),
|
|
2074
1899
|
/* @__PURE__ */ jsxRuntime.jsxs(Select, { value: selectedToken?.symbol ?? "", onValueChange: handleTokenChange, children: [
|
|
2075
1900
|
/* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { className: "border bg-transparent", children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, { placeholder: "Select token" }) }),
|
|
2076
|
-
/* @__PURE__ */ jsxRuntime.jsx(SelectContent, { className: "max-h-64", children: tokens.map((token) => /* @__PURE__ */ jsxRuntime.
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
1901
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectContent, { className: "max-h-64", children: tokens.map((token) => /* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: token.symbol, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex w-full items-center justify-between gap-4", children: [
|
|
1902
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
1903
|
+
token.name,
|
|
1904
|
+
" (",
|
|
1905
|
+
token.symbol,
|
|
1906
|
+
")"
|
|
1907
|
+
] }),
|
|
1908
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
1909
|
+
formatTokenAmount(token),
|
|
1910
|
+
" ",
|
|
1911
|
+
token.symbol,
|
|
1912
|
+
token.balance?.amount ? ` \xB7 bal ${token.balance.amount} ${token.symbol}` : null
|
|
1913
|
+
] })
|
|
1914
|
+
] }) }, token.symbol)) })
|
|
1915
|
+
] }),
|
|
1916
|
+
!walletAddress && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "Connect a wallet to show token balances." })
|
|
2083
1917
|
] }),
|
|
2084
1918
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2085
1919
|
QRCodePayment,
|
|
@@ -2136,6 +1970,8 @@ var defaultPaymentExperienceTranslations = {
|
|
|
2136
1970
|
var PaymentExperience = ({
|
|
2137
1971
|
priceId,
|
|
2138
1972
|
usdAmount,
|
|
1973
|
+
checkoutSessionId,
|
|
1974
|
+
walletId,
|
|
2139
1975
|
onNewCardPayment,
|
|
2140
1976
|
onSavedMethodPayment,
|
|
2141
1977
|
onCcbillPayment,
|
|
@@ -2145,6 +1981,7 @@ var PaymentExperience = ({
|
|
|
2145
1981
|
onSolanaSuccess,
|
|
2146
1982
|
onSolanaError,
|
|
2147
1983
|
initialMode = "cards",
|
|
1984
|
+
userEmail,
|
|
2148
1985
|
translations
|
|
2149
1986
|
}) => {
|
|
2150
1987
|
const t = {
|
|
@@ -2167,6 +2004,11 @@ var PaymentExperience = ({
|
|
|
2167
2004
|
const [billingDetails, setBillingDetails] = React4.useState(null);
|
|
2168
2005
|
const [ccbillStatus, setCcbillStatus] = React4.useState("idle");
|
|
2169
2006
|
const [ccbillError, setCcbillError] = React4.useState(null);
|
|
2007
|
+
const [ccbillModalOpen, setCcbillModalOpen] = React4.useState(false);
|
|
2008
|
+
const [ccbillBilling, setCcbillBilling] = React4.useState(() => {
|
|
2009
|
+
let defaultEmail = userEmail ?? "";
|
|
2010
|
+
return { ...defaultBillingDetails, email: defaultEmail };
|
|
2011
|
+
});
|
|
2170
2012
|
const { notifyStatus, notifyError } = usePaymentNotifications();
|
|
2171
2013
|
React4.useEffect(() => {
|
|
2172
2014
|
setActiveTab(showStored ? "saved" : "new");
|
|
@@ -2187,6 +2029,7 @@ var PaymentExperience = ({
|
|
|
2187
2029
|
setBillingDetails(null);
|
|
2188
2030
|
setCcbillStatus("idle");
|
|
2189
2031
|
setCcbillError(null);
|
|
2032
|
+
setCcbillModalOpen(false);
|
|
2190
2033
|
}
|
|
2191
2034
|
}, [showNewCard]);
|
|
2192
2035
|
const handleBillingChange = React4.useCallback((billing) => {
|
|
@@ -2194,6 +2037,42 @@ var PaymentExperience = ({
|
|
|
2194
2037
|
setCcbillError(null);
|
|
2195
2038
|
setCcbillStatus("idle");
|
|
2196
2039
|
}, []);
|
|
2040
|
+
const openCcbillModal = React4.useCallback(() => {
|
|
2041
|
+
setCcbillError(null);
|
|
2042
|
+
setCcbillStatus("idle");
|
|
2043
|
+
setCcbillBilling((prev) => {
|
|
2044
|
+
let email = prev.email;
|
|
2045
|
+
if (billingDetails && billingDetails.email) {
|
|
2046
|
+
email = billingDetails.email;
|
|
2047
|
+
} else if (typeof window !== "undefined") {
|
|
2048
|
+
try {
|
|
2049
|
+
const config = window.paymentsUiConfig || {};
|
|
2050
|
+
if (config.defaultUser && config.defaultUser.email) {
|
|
2051
|
+
email = config.defaultUser.email;
|
|
2052
|
+
}
|
|
2053
|
+
} catch {
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
if (billingDetails) {
|
|
2057
|
+
return {
|
|
2058
|
+
...billingDetails,
|
|
2059
|
+
email,
|
|
2060
|
+
provider: billingDetails.provider ?? prev.provider ?? defaultBillingDetails.provider
|
|
2061
|
+
};
|
|
2062
|
+
}
|
|
2063
|
+
return { ...prev, email, provider: prev.provider ?? defaultBillingDetails.provider };
|
|
2064
|
+
});
|
|
2065
|
+
setCcbillModalOpen(true);
|
|
2066
|
+
}, [billingDetails]);
|
|
2067
|
+
const handleCcbillFieldChange = React4.useCallback(
|
|
2068
|
+
(field, value) => {
|
|
2069
|
+
setCcbillBilling((prev) => {
|
|
2070
|
+
const provider = prev.provider ?? billingDetails?.provider ?? defaultBillingDetails.provider;
|
|
2071
|
+
return { ...prev, [field]: value, provider };
|
|
2072
|
+
});
|
|
2073
|
+
},
|
|
2074
|
+
[billingDetails]
|
|
2075
|
+
);
|
|
2197
2076
|
const isBillingComplete = React4.useCallback((billing) => {
|
|
2198
2077
|
if (!billing) return false;
|
|
2199
2078
|
return Boolean(
|
|
@@ -2255,9 +2134,8 @@ var PaymentExperience = ({
|
|
|
2255
2134
|
);
|
|
2256
2135
|
const handleCcbillPayment = React4.useCallback(async () => {
|
|
2257
2136
|
if (!onCcbillPayment) return;
|
|
2258
|
-
if (!
|
|
2137
|
+
if (!isBillingComplete(ccbillBilling)) {
|
|
2259
2138
|
const message = t.errorRequiredFields;
|
|
2260
|
-
setActiveTab("new");
|
|
2261
2139
|
setCcbillStatus("error");
|
|
2262
2140
|
setCcbillError(message);
|
|
2263
2141
|
notifyStatus("error", { source: "ccbill" });
|
|
@@ -2268,8 +2146,9 @@ var PaymentExperience = ({
|
|
|
2268
2146
|
setCcbillStatus("processing");
|
|
2269
2147
|
setCcbillError(null);
|
|
2270
2148
|
notifyStatus("processing", { source: "ccbill" });
|
|
2271
|
-
await onCcbillPayment({ billing:
|
|
2149
|
+
await onCcbillPayment({ billing: ccbillBilling });
|
|
2272
2150
|
setCcbillStatus("success");
|
|
2151
|
+
setCcbillModalOpen(false);
|
|
2273
2152
|
notifyStatus("success", { source: "ccbill" });
|
|
2274
2153
|
} catch (error) {
|
|
2275
2154
|
const message = resolveErrorMessageByCode(
|
|
@@ -2283,7 +2162,7 @@ var PaymentExperience = ({
|
|
|
2283
2162
|
notifyError(message);
|
|
2284
2163
|
}
|
|
2285
2164
|
}, [
|
|
2286
|
-
|
|
2165
|
+
ccbillBilling,
|
|
2287
2166
|
isBillingComplete,
|
|
2288
2167
|
notifyError,
|
|
2289
2168
|
notifyStatus,
|
|
@@ -2310,6 +2189,145 @@ var PaymentExperience = ({
|
|
|
2310
2189
|
},
|
|
2311
2190
|
[onSolanaError]
|
|
2312
2191
|
);
|
|
2192
|
+
const renderCcbillModal = () => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2193
|
+
Dialog,
|
|
2194
|
+
{
|
|
2195
|
+
open: ccbillModalOpen,
|
|
2196
|
+
onOpenChange: (open) => {
|
|
2197
|
+
setCcbillModalOpen(open);
|
|
2198
|
+
if (!open) {
|
|
2199
|
+
setCcbillStatus("idle");
|
|
2200
|
+
setCcbillError(null);
|
|
2201
|
+
}
|
|
2202
|
+
},
|
|
2203
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(DialogContent, { className: "sm:max-w-lg", style: { zIndex: 9999, border: "none" }, children: [
|
|
2204
|
+
/* @__PURE__ */ jsxRuntime.jsx(DialogHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { children: t.payWithCcbill }) }),
|
|
2205
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-3", children: [
|
|
2206
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-2 sm:grid-cols-2 sm:gap-3", children: [
|
|
2207
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-2", children: [
|
|
2208
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "ccbill-first-name", children: t.firstName }),
|
|
2209
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2210
|
+
Input,
|
|
2211
|
+
{
|
|
2212
|
+
id: "ccbill-first-name",
|
|
2213
|
+
value: ccbillBilling.firstName,
|
|
2214
|
+
onChange: (e) => handleCcbillFieldChange("firstName", e.target.value)
|
|
2215
|
+
}
|
|
2216
|
+
)
|
|
2217
|
+
] }),
|
|
2218
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-2", children: [
|
|
2219
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "ccbill-last-name", children: t.lastName }),
|
|
2220
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2221
|
+
Input,
|
|
2222
|
+
{
|
|
2223
|
+
id: "ccbill-last-name",
|
|
2224
|
+
value: ccbillBilling.lastName,
|
|
2225
|
+
onChange: (e) => handleCcbillFieldChange("lastName", e.target.value)
|
|
2226
|
+
}
|
|
2227
|
+
)
|
|
2228
|
+
] })
|
|
2229
|
+
] }),
|
|
2230
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-2", children: [
|
|
2231
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "ccbill-email", children: t.email }),
|
|
2232
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2233
|
+
Input,
|
|
2234
|
+
{
|
|
2235
|
+
id: "ccbill-email",
|
|
2236
|
+
type: "email",
|
|
2237
|
+
value: ccbillBilling.email,
|
|
2238
|
+
onChange: (e) => handleCcbillFieldChange("email", e.target.value)
|
|
2239
|
+
}
|
|
2240
|
+
)
|
|
2241
|
+
] }),
|
|
2242
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-2", children: [
|
|
2243
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "ccbill-address", children: t.address }),
|
|
2244
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2245
|
+
Input,
|
|
2246
|
+
{
|
|
2247
|
+
id: "ccbill-address",
|
|
2248
|
+
value: ccbillBilling.address1,
|
|
2249
|
+
onChange: (e) => handleCcbillFieldChange("address1", e.target.value)
|
|
2250
|
+
}
|
|
2251
|
+
)
|
|
2252
|
+
] }),
|
|
2253
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-2 sm:grid-cols-2 sm:gap-3", children: [
|
|
2254
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-2", children: [
|
|
2255
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "ccbill-city", children: t.city }),
|
|
2256
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2257
|
+
Input,
|
|
2258
|
+
{
|
|
2259
|
+
id: "ccbill-city",
|
|
2260
|
+
value: ccbillBilling.city,
|
|
2261
|
+
onChange: (e) => handleCcbillFieldChange("city", e.target.value)
|
|
2262
|
+
}
|
|
2263
|
+
)
|
|
2264
|
+
] }),
|
|
2265
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-2", children: [
|
|
2266
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "ccbill-state", children: t.state }),
|
|
2267
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2268
|
+
Input,
|
|
2269
|
+
{
|
|
2270
|
+
id: "ccbill-state",
|
|
2271
|
+
value: ccbillBilling.stateRegion ?? "",
|
|
2272
|
+
onChange: (e) => handleCcbillFieldChange("stateRegion", e.target.value)
|
|
2273
|
+
}
|
|
2274
|
+
)
|
|
2275
|
+
] })
|
|
2276
|
+
] }),
|
|
2277
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-2 sm:grid-cols-2 sm:gap-3", children: [
|
|
2278
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-2", children: [
|
|
2279
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "ccbill-zip", children: t.postalCode }),
|
|
2280
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2281
|
+
Input,
|
|
2282
|
+
{
|
|
2283
|
+
id: "ccbill-zip",
|
|
2284
|
+
value: ccbillBilling.postalCode,
|
|
2285
|
+
onChange: (e) => handleCcbillFieldChange("postalCode", e.target.value)
|
|
2286
|
+
}
|
|
2287
|
+
)
|
|
2288
|
+
] }),
|
|
2289
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-2", children: [
|
|
2290
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "ccbill-country", children: t.country }),
|
|
2291
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2292
|
+
Select,
|
|
2293
|
+
{
|
|
2294
|
+
value: ccbillBilling.country,
|
|
2295
|
+
onValueChange: (value) => handleCcbillFieldChange("country", value),
|
|
2296
|
+
children: [
|
|
2297
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { id: "ccbill-country", children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, { placeholder: t.selectCountry || "Select a country" }) }),
|
|
2298
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectContent, { children: countries.map((option) => /* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: option.code, children: option.name }, option.code)) })
|
|
2299
|
+
]
|
|
2300
|
+
}
|
|
2301
|
+
)
|
|
2302
|
+
] })
|
|
2303
|
+
] }),
|
|
2304
|
+
ccbillError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive", children: ccbillError }),
|
|
2305
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2 pt-2", children: [
|
|
2306
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2307
|
+
Button,
|
|
2308
|
+
{
|
|
2309
|
+
variant: "ghost",
|
|
2310
|
+
type: "button",
|
|
2311
|
+
onClick: () => setCcbillModalOpen(false),
|
|
2312
|
+
disabled: ccbillStatus === "processing",
|
|
2313
|
+
children: t.cancel
|
|
2314
|
+
}
|
|
2315
|
+
),
|
|
2316
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2317
|
+
Button,
|
|
2318
|
+
{
|
|
2319
|
+
className: "min-w-[140px] bg-emerald-600 text-white hover:bg-emerald-500",
|
|
2320
|
+
type: "button",
|
|
2321
|
+
disabled: ccbillStatus === "processing",
|
|
2322
|
+
onClick: handleCcbillPayment,
|
|
2323
|
+
children: ccbillStatus === "processing" ? t.processingCcbill : t.payWithCcbill
|
|
2324
|
+
}
|
|
2325
|
+
)
|
|
2326
|
+
] })
|
|
2327
|
+
] })
|
|
2328
|
+
] })
|
|
2329
|
+
}
|
|
2330
|
+
);
|
|
2313
2331
|
const renderSavedTab = () => {
|
|
2314
2332
|
if (!showStored) {
|
|
2315
2333
|
return /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: t.savedUnavailable });
|
|
@@ -2373,12 +2391,13 @@ var PaymentExperience = ({
|
|
|
2373
2391
|
{
|
|
2374
2392
|
className: "w-full",
|
|
2375
2393
|
variant: "outline",
|
|
2394
|
+
type: "button",
|
|
2376
2395
|
disabled: ccbillStatus === "processing",
|
|
2377
|
-
onClick:
|
|
2396
|
+
onClick: openCcbillModal,
|
|
2378
2397
|
children: ccbillStatus === "processing" ? t.processingCcbill : t.payWithCcbill
|
|
2379
2398
|
}
|
|
2380
2399
|
),
|
|
2381
|
-
|
|
2400
|
+
renderCcbillModal()
|
|
2382
2401
|
] });
|
|
2383
2402
|
};
|
|
2384
2403
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6 pt-4", children: [
|
|
@@ -2391,6 +2410,8 @@ var PaymentExperience = ({
|
|
|
2391
2410
|
{
|
|
2392
2411
|
priceId,
|
|
2393
2412
|
usdAmount,
|
|
2413
|
+
checkoutSessionId,
|
|
2414
|
+
walletId,
|
|
2394
2415
|
onSuccess: handleSolanaSuccess,
|
|
2395
2416
|
onError: handleSolanaError,
|
|
2396
2417
|
onClose: exitSolanaView
|
|
@@ -2494,9 +2515,37 @@ var useSubscriptionActions = () => {
|
|
|
2494
2515
|
},
|
|
2495
2516
|
[client]
|
|
2496
2517
|
);
|
|
2518
|
+
const subscribeWithCCBill = React4.useCallback(
|
|
2519
|
+
async ({
|
|
2520
|
+
priceId,
|
|
2521
|
+
billing,
|
|
2522
|
+
provider,
|
|
2523
|
+
processor = "ccbill",
|
|
2524
|
+
idempotencyKey
|
|
2525
|
+
}) => {
|
|
2526
|
+
const payload = {
|
|
2527
|
+
price_id: ensurePrice(priceId),
|
|
2528
|
+
provider,
|
|
2529
|
+
payment: {
|
|
2530
|
+
processor,
|
|
2531
|
+
email: billing.email,
|
|
2532
|
+
first_name: billing.firstName,
|
|
2533
|
+
last_name: billing.lastName,
|
|
2534
|
+
address1: billing.address1,
|
|
2535
|
+
city: billing.city,
|
|
2536
|
+
state: billing.stateRegion,
|
|
2537
|
+
zip: billing.postalCode,
|
|
2538
|
+
country: billing.country
|
|
2539
|
+
}
|
|
2540
|
+
};
|
|
2541
|
+
return client.checkout(payload, idempotencyKey);
|
|
2542
|
+
},
|
|
2543
|
+
[client]
|
|
2544
|
+
);
|
|
2497
2545
|
return {
|
|
2498
2546
|
subscribeWithCard,
|
|
2499
|
-
subscribeWithSavedMethod
|
|
2547
|
+
subscribeWithSavedMethod,
|
|
2548
|
+
subscribeWithCCBill
|
|
2500
2549
|
};
|
|
2501
2550
|
};
|
|
2502
2551
|
var defaultTranslations2 = {
|
|
@@ -2509,6 +2558,8 @@ var SubscriptionCheckoutModal = ({
|
|
|
2509
2558
|
onOpenChange,
|
|
2510
2559
|
priceId,
|
|
2511
2560
|
usdAmount = 0,
|
|
2561
|
+
checkoutSessionId,
|
|
2562
|
+
walletId,
|
|
2512
2563
|
planName,
|
|
2513
2564
|
amountLabel,
|
|
2514
2565
|
billingPeriodLabel,
|
|
@@ -2523,7 +2574,7 @@ var SubscriptionCheckoutModal = ({
|
|
|
2523
2574
|
}) => {
|
|
2524
2575
|
const [showSuccess, setShowSuccess] = React4.useState(false);
|
|
2525
2576
|
const [idempotencyKey, setIdempotencyKey] = React4.useState(() => crypto.randomUUID());
|
|
2526
|
-
const { subscribeWithCard, subscribeWithSavedMethod } = useSubscriptionActions();
|
|
2577
|
+
const { subscribeWithCard, subscribeWithSavedMethod, subscribeWithCCBill } = useSubscriptionActions();
|
|
2527
2578
|
const t = {
|
|
2528
2579
|
...defaultTranslations2,
|
|
2529
2580
|
...translations
|
|
@@ -2598,10 +2649,10 @@ var SubscriptionCheckoutModal = ({
|
|
|
2598
2649
|
handleCheckoutResponse(response);
|
|
2599
2650
|
};
|
|
2600
2651
|
const handleCcbillPayment = async ({ billing }) => {
|
|
2601
|
-
const response = await
|
|
2652
|
+
const response = await subscribeWithCCBill({
|
|
2602
2653
|
billing,
|
|
2654
|
+
provider,
|
|
2603
2655
|
idempotencyKey,
|
|
2604
|
-
processor: "ccbill",
|
|
2605
2656
|
priceId: ensurePrice()
|
|
2606
2657
|
});
|
|
2607
2658
|
handleCheckoutResponse(response);
|
|
@@ -2630,11 +2681,14 @@ var SubscriptionCheckoutModal = ({
|
|
|
2630
2681
|
{
|
|
2631
2682
|
usdAmount,
|
|
2632
2683
|
priceId: priceId ?? "",
|
|
2684
|
+
checkoutSessionId,
|
|
2685
|
+
walletId,
|
|
2633
2686
|
initialMode,
|
|
2634
2687
|
onSolanaError: solanaError,
|
|
2635
2688
|
onSolanaSuccess: solanaSuccess,
|
|
2636
2689
|
enableNewCard: Boolean(priceId),
|
|
2637
2690
|
enableStoredMethods: Boolean(priceId),
|
|
2691
|
+
userEmail: userEmail ?? void 0,
|
|
2638
2692
|
enableSolanaPay: enableSolanaPay && Boolean(priceId),
|
|
2639
2693
|
onNewCardPayment: priceId ? handleNewCardPayment : void 0,
|
|
2640
2694
|
onSavedMethodPayment: priceId ? handleSavedMethodPayment : void 0,
|
|
@@ -3095,6 +3149,7 @@ var defaultTranslations3 = {
|
|
|
3095
3149
|
cancellationFailed: "Cancellation failed"
|
|
3096
3150
|
};
|
|
3097
3151
|
var CancelMembershipDialog = ({
|
|
3152
|
+
subscriptionId,
|
|
3098
3153
|
minReasonLength = 15,
|
|
3099
3154
|
onCancelled,
|
|
3100
3155
|
onNotify,
|
|
@@ -3144,13 +3199,17 @@ var CancelMembershipDialog = ({
|
|
|
3144
3199
|
}
|
|
3145
3200
|
setIsSubmitting(true);
|
|
3146
3201
|
try {
|
|
3147
|
-
await client.cancelSubscription(cancelReason.trim());
|
|
3202
|
+
await client.cancelSubscription(subscriptionId, cancelReason.trim());
|
|
3148
3203
|
notify({
|
|
3149
3204
|
title: t.membershipCancelled,
|
|
3150
3205
|
description: t.cancellationSuccess,
|
|
3151
3206
|
status: "success"
|
|
3152
3207
|
});
|
|
3153
|
-
|
|
3208
|
+
try {
|
|
3209
|
+
await onCancelled?.();
|
|
3210
|
+
} catch (callbackError) {
|
|
3211
|
+
console.warn("[payments-ui] cancellation callback failed", callbackError);
|
|
3212
|
+
}
|
|
3154
3213
|
handleOpenChange(false);
|
|
3155
3214
|
} catch (error) {
|
|
3156
3215
|
const message = error instanceof Error ? error.message : "Unable to cancel membership";
|
|
@@ -3242,15 +3301,19 @@ var defaultTranslations4 = {
|
|
|
3242
3301
|
status: "Status"
|
|
3243
3302
|
};
|
|
3244
3303
|
var BillingHistory = ({
|
|
3304
|
+
subscriptionId,
|
|
3245
3305
|
pageSize = 10,
|
|
3246
3306
|
initialPage = 1,
|
|
3247
3307
|
enableCancel = true,
|
|
3308
|
+
locale,
|
|
3309
|
+
onCancelled,
|
|
3248
3310
|
onNotify,
|
|
3249
3311
|
translations: customTranslations
|
|
3250
3312
|
}) => {
|
|
3251
3313
|
const { client } = usePaymentContext();
|
|
3252
3314
|
const notify = onNotify ?? notifyDefault2;
|
|
3253
3315
|
const t = { ...defaultTranslations4, ...customTranslations };
|
|
3316
|
+
const resolvedLocale = locale && locale.trim().length > 0 ? locale : typeof navigator !== "undefined" && navigator.language ? navigator.language : "en-US";
|
|
3254
3317
|
const [isExpanded, setIsExpanded] = React4.useState(false);
|
|
3255
3318
|
const observerRef = React4.useRef(null);
|
|
3256
3319
|
const loadMoreRef = React4.useRef(null);
|
|
@@ -3299,19 +3362,38 @@ var BillingHistory = ({
|
|
|
3299
3362
|
const data = historyQuery.data;
|
|
3300
3363
|
return data?.pages ?? [];
|
|
3301
3364
|
}, [historyQuery.data]);
|
|
3302
|
-
const
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3365
|
+
const dateFormatter = React4.useMemo(() => {
|
|
3366
|
+
try {
|
|
3367
|
+
return new Intl.DateTimeFormat(resolvedLocale, {
|
|
3368
|
+
year: "numeric",
|
|
3369
|
+
month: "short",
|
|
3370
|
+
day: "numeric"
|
|
3371
|
+
});
|
|
3372
|
+
} catch {
|
|
3373
|
+
return new Intl.DateTimeFormat("en-US", {
|
|
3374
|
+
year: "numeric",
|
|
3375
|
+
month: "short",
|
|
3376
|
+
day: "numeric"
|
|
3377
|
+
});
|
|
3378
|
+
}
|
|
3379
|
+
}, [resolvedLocale]);
|
|
3380
|
+
const formatDate = (value) => {
|
|
3381
|
+
const parsed = new Date(value);
|
|
3382
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
3383
|
+
return value;
|
|
3384
|
+
}
|
|
3385
|
+
return dateFormatter.format(parsed);
|
|
3386
|
+
};
|
|
3307
3387
|
const formatAmount = (amount, currency) => {
|
|
3388
|
+
const normalizedAmount = amount / 100;
|
|
3389
|
+
const normalizedCurrency = typeof currency === "string" ? currency.toUpperCase() : currency;
|
|
3308
3390
|
try {
|
|
3309
|
-
return new Intl.NumberFormat(
|
|
3391
|
+
return new Intl.NumberFormat(resolvedLocale, {
|
|
3310
3392
|
style: "currency",
|
|
3311
|
-
currency
|
|
3312
|
-
}).format(
|
|
3393
|
+
currency: normalizedCurrency
|
|
3394
|
+
}).format(normalizedAmount);
|
|
3313
3395
|
} catch {
|
|
3314
|
-
return `${
|
|
3396
|
+
return `${normalizedAmount.toFixed(2)} ${currency}`;
|
|
3315
3397
|
}
|
|
3316
3398
|
};
|
|
3317
3399
|
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "border-0 bg-black/30 shadow-2xl backdrop-blur-xl", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 sm:p-6", children: [
|
|
@@ -3332,7 +3414,7 @@ var BillingHistory = ({
|
|
|
3332
3414
|
children: /* @__PURE__ */ jsxRuntime.jsx(CardContent, { className: "p-0 pt-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
3333
3415
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between", children: [
|
|
3334
3416
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: t.reviewActivity }),
|
|
3335
|
-
enableCancel && /* @__PURE__ */ jsxRuntime.jsx(CancelMembershipDialog, { onNotify: notify })
|
|
3417
|
+
enableCancel && subscriptionId && /* @__PURE__ */ jsxRuntime.jsx(CancelMembershipDialog, { subscriptionId, onNotify: notify, onCancelled })
|
|
3336
3418
|
] }),
|
|
3337
3419
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-[300px] overflow-y-auto rounded-lg border ", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-x-auto", children: historyQuery.isLoading ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "p-4 text-center text-sm text-muted-foreground", children: t.loading }) : historyQuery.isError ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "p-4 text-center text-sm text-destructive", children: t.error }) : /* @__PURE__ */ jsxRuntime.jsxs(Table, { children: [
|
|
3338
3420
|
/* @__PURE__ */ jsxRuntime.jsx(TableHeader, { children: /* @__PURE__ */ jsxRuntime.jsxs(TableRow, { className: "", children: [
|
|
@@ -3414,8 +3496,7 @@ var PaymentMethodsSection = ({
|
|
|
3414
3496
|
list: (params) => client.listPaymentMethods({ limit: params.pageSize }),
|
|
3415
3497
|
create: (payload) => client.createPaymentMethod(payload),
|
|
3416
3498
|
update: (id, payload) => client.updatePaymentMethod(id, payload),
|
|
3417
|
-
remove: (id) => client.deletePaymentMethod(id)
|
|
3418
|
-
activate: (id) => client.activatePaymentMethod(id)
|
|
3499
|
+
remove: (id) => client.deletePaymentMethod(id)
|
|
3419
3500
|
};
|
|
3420
3501
|
const [isModalOpen, setIsModalOpen] = React4.useState(false);
|
|
3421
3502
|
const [deletingId, setDeletingId] = React4.useState(null);
|
|
@@ -3467,20 +3548,6 @@ var PaymentMethodsSection = ({
|
|
|
3467
3548
|
},
|
|
3468
3549
|
onSettled: () => setDeletingId(null)
|
|
3469
3550
|
});
|
|
3470
|
-
reactQuery.useMutation({
|
|
3471
|
-
mutationFn: (id) => paymentMethods.activate(id),
|
|
3472
|
-
onSuccess: () => {
|
|
3473
|
-
notify({ title: t.defaultPaymentMethodUpdated, status: "success" });
|
|
3474
|
-
void queryClient.invalidateQueries({ queryKey });
|
|
3475
|
-
},
|
|
3476
|
-
onError: (error) => {
|
|
3477
|
-
notify({
|
|
3478
|
-
title: t.unableToSetDefault,
|
|
3479
|
-
description: error.message,
|
|
3480
|
-
status: "destructive"
|
|
3481
|
-
});
|
|
3482
|
-
}
|
|
3483
|
-
});
|
|
3484
3551
|
React4.useEffect(() => {
|
|
3485
3552
|
if (!isModalOpen) {
|
|
3486
3553
|
createMutation.reset();
|
|
@@ -3699,10 +3766,7 @@ var SubscriptionsSection = ({
|
|
|
3699
3766
|
setCancelDialogOpen(false);
|
|
3700
3767
|
}, [activeSubId]);
|
|
3701
3768
|
const updatePaymentMethodMutation = reactQuery.useMutation({
|
|
3702
|
-
mutationFn: (payload) => client.updateSubscriptionPaymentMethod(
|
|
3703
|
-
subscription_id: payload.subscriptionId,
|
|
3704
|
-
payment_method_id: payload.paymentMethodId
|
|
3705
|
-
}),
|
|
3769
|
+
mutationFn: (payload) => client.updateSubscriptionPaymentMethod(payload.subscriptionId, payload.paymentMethodId),
|
|
3706
3770
|
onSuccess: () => {
|
|
3707
3771
|
notify({ title: t.paymentMethodUpdated, status: "success" });
|
|
3708
3772
|
void queryClient.invalidateQueries({ queryKey: subscriptionsQueryKey });
|
|
@@ -3716,7 +3780,7 @@ var SubscriptionsSection = ({
|
|
|
3716
3780
|
}
|
|
3717
3781
|
});
|
|
3718
3782
|
const resumeSubscriptionMutation = reactQuery.useMutation({
|
|
3719
|
-
mutationFn: () => client.resumeSubscription(),
|
|
3783
|
+
mutationFn: (subscriptionId) => client.resumeSubscription(subscriptionId),
|
|
3720
3784
|
onSuccess: () => {
|
|
3721
3785
|
notify({ title: t.resumeSuccess, status: "success" });
|
|
3722
3786
|
void queryClient.invalidateQueries({ queryKey: subscriptionsQueryKey });
|
|
@@ -3730,7 +3794,7 @@ var SubscriptionsSection = ({
|
|
|
3730
3794
|
}
|
|
3731
3795
|
});
|
|
3732
3796
|
reactQuery.useMutation({
|
|
3733
|
-
mutationFn: (payload) => client.changeSubscription({ price_id: payload.priceId }),
|
|
3797
|
+
mutationFn: (payload) => client.changeSubscription(payload.subscriptionId, { price_id: payload.priceId }),
|
|
3734
3798
|
onSuccess: () => {
|
|
3735
3799
|
notify({ title: t.planChanged, status: "success" });
|
|
3736
3800
|
void queryClient.invalidateQueries({ queryKey: subscriptionsQueryKey });
|
|
@@ -3883,7 +3947,7 @@ var SubscriptionsSection = ({
|
|
|
3883
3947
|
Button,
|
|
3884
3948
|
{
|
|
3885
3949
|
variant: "secondary",
|
|
3886
|
-
onClick: () => resumeSubscriptionMutation.mutate(),
|
|
3950
|
+
onClick: () => resumeSubscriptionMutation.mutate(activeSubscription.id),
|
|
3887
3951
|
disabled: resumeSubscriptionMutation.isPending,
|
|
3888
3952
|
className: "rounded-full px-4",
|
|
3889
3953
|
children: [
|
|
@@ -3948,6 +4012,7 @@ var SubscriptionsSection = ({
|
|
|
3948
4012
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3949
4013
|
CancelMembershipDialog,
|
|
3950
4014
|
{
|
|
4015
|
+
subscriptionId: activeSubscription?.id ?? "",
|
|
3951
4016
|
translations: cancelTranslations,
|
|
3952
4017
|
onNotify,
|
|
3953
4018
|
open: cancelDialogOpen,
|
|
@@ -4310,18 +4375,10 @@ var usePaymentStatus = (options = {}) => {
|
|
|
4310
4375
|
[connection]
|
|
4311
4376
|
);
|
|
4312
4377
|
const checkPaymentStatus = React4.useCallback(
|
|
4313
|
-
async (
|
|
4314
|
-
|
|
4315
|
-
return await client.getPaymentStatus(id);
|
|
4316
|
-
} catch (error2) {
|
|
4317
|
-
console.error("Failed to check payment status:", {
|
|
4318
|
-
purchaseId: id,
|
|
4319
|
-
error: error2
|
|
4320
|
-
});
|
|
4321
|
-
return null;
|
|
4322
|
-
}
|
|
4378
|
+
async (_id) => {
|
|
4379
|
+
return null;
|
|
4323
4380
|
},
|
|
4324
|
-
[
|
|
4381
|
+
[]
|
|
4325
4382
|
);
|
|
4326
4383
|
const startMonitoring = React4.useCallback(async () => {
|
|
4327
4384
|
if (isMonitoringRef.current || !transactionId && !purchaseId) {
|