@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 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", "/me/subscriptions/cancel", {
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(payload) {
329
- return request("PUT", "/me/subscriptions/payment-method", {
323
+ updateSubscriptionPaymentMethod(subscriptionId, paymentMethodId) {
324
+ return request("PUT", `/me/subscriptions/${subscriptionId}/payment-method`, {
330
325
  body: {
331
- subscription_id: payload.subscription_id,
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", "/me/subscriptions/resume");
330
+ resumeSubscription(subscriptionId) {
331
+ return request("POST", `/me/subscriptions/${subscriptionId}/resume`);
338
332
  },
339
- changeSubscription(payload) {
340
- return request("POST", "/me/subscriptions/change", {
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
- var useSolanaQrPayment = (options) => {
1430
- const { priceId, selectedToken, onSuccess, onError } = options;
1431
- const { client } = usePaymentContext();
1432
- const tokenSymbol = selectedToken?.symbol ?? null;
1433
- const onSuccessRef = React4.useRef(onSuccess);
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
- const handleQrSuccess = React4__namespace.default.useCallback(
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 tokensRef = React4.useRef(tokens);
1799
- const lastFetchedRef = React4.useRef(lastFetched);
1800
- tokensRef.current = tokens;
1801
- lastFetchedRef.current = lastFetched;
1802
- const fetchSupportedTokens = React4.useCallback(async () => {
1803
- if (tokensRef.current.length > 0 && lastFetchedRef.current && Date.now() - lastFetchedRef.current < CACHE_DURATION) {
1804
- return tokensRef.current;
1805
- }
1806
- setIsLoading(true);
1807
- setError(null);
1808
- try {
1809
- console.log("payments-ui: fetching supported Solana tokens");
1810
- const tokens2 = await client.getSolanaTokens();
1811
- const sortedTokens = [...tokens2].sort(
1812
- (a, b) => a.symbol.localeCompare(b.symbol)
1813
- );
1814
- setTokens(sortedTokens);
1815
- console.log("payments-ui: loaded supported tokens", {
1816
- count: sortedTokens.length
1817
- });
1818
- setLastFetched(Date.now());
1819
- return sortedTokens;
1820
- } catch (error2) {
1821
- const errorMessage = error2 instanceof Error ? error2.message : "Failed to fetch supported tokens";
1822
- setError(errorMessage);
1823
- console.error("Failed to fetch supported tokens:", error2);
1824
- return [];
1825
- } finally {
1826
- setIsLoading(false);
1827
- }
1828
- }, [client]);
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
- if (tokens.length === 0) {
1831
- fetchSupportedTokens();
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: tokenAmount,
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 && tokenAmount > 0 && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-muted-foreground", children: [
1890
+ selectedToken && selectedTokenAmount > 0 && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-muted-foreground", children: [
2066
1891
  "\u2248 ",
2067
- tokenAmount.toFixed(selectedToken.symbol === "SOL" ? 4 : 2),
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.jsxs(SelectItem, { value: token.symbol, children: [
2077
- token.name,
2078
- " (",
2079
- token.symbol,
2080
- ")"
2081
- ] }, token.symbol)) })
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 (!billingDetails || !isBillingComplete(billingDetails)) {
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: billingDetails });
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
- billingDetails,
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: handleCcbillPayment,
2396
+ onClick: openCcbillModal,
2378
2397
  children: ccbillStatus === "processing" ? t.processingCcbill : t.payWithCcbill
2379
2398
  }
2380
2399
  ),
2381
- ccbillError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive", children: ccbillError })
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 subscribeWithCard({
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
- onCancelled?.();
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 formatDate = (value) => new Date(value).toLocaleDateString("en-US", {
3303
- year: "numeric",
3304
- month: "short",
3305
- day: "numeric"
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("en-US", {
3391
+ return new Intl.NumberFormat(resolvedLocale, {
3310
3392
  style: "currency",
3311
- currency
3312
- }).format(amount);
3393
+ currency: normalizedCurrency
3394
+ }).format(normalizedAmount);
3313
3395
  } catch {
3314
- return `${amount.toFixed(2)} ${currency}`;
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 (id) => {
4314
- try {
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
- [client]
4381
+ []
4325
4382
  );
4326
4383
  const startMonitoring = React4.useCallback(async () => {
4327
4384
  if (isMonitoringRef.current || !transactionId && !purchaseId) {