@ton-pay/ui-react 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/ilya/work/tonpay/ton-pay/packages/ui-react/dist/index.js","../src/components/ton-pay-button/TonPayButton.tsx","../src/utils/index.ts","../src/components/icons/index.tsx","../src/components/notification/Notification.tsx","../src/components/notification/ErrorTransactionNotification.tsx","../src/components/payment-modal/PaymentModal.tsx","../src/components/bottom-sheet/BottomSheet.tsx","../src/components/bottom-sheet/BottomSheet.css","../src/components/payment-modal/PaymentModal.css","../src/hooks/useMoonPayIframe.ts","../../api/src/common/const.ts","../../api/src/common/get-base-url.ts","../../api/src/create-moonpay-transfer/create-moonpay-transfer.ts","../../api/src/check-moonpay-availability/check-moonpay-availability.ts","../../api/src/utils/verify-signature.ts","../src/components/ton-pay-button/styles.ts","../src/hooks/useTonPay.ts"],"names":["jsxs","jsx","useState","useCallback","useTonAddress","useTonConnectUI"],"mappings":"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","file":"/Users/ilya/work/tonpay/ton-pay/packages/ui-react/dist/index.js","sourcesContent":[null,"import * as React from 'react';\nimport { useEffect, useState, useCallback } from 'react';\nimport { useTonAddress, useTonConnectUI } from '@tonconnect/ui-react';\nimport type { TonPayButtonProps } from '../../types';\nimport { classNames, toCssSize, getUserIp } from '../../utils';\nimport { TonIcon, DisconnectIcon } from '../icons';\nimport {\n NotificationRoot,\n ErrorTransactionNotification,\n} from '../notification';\nimport { PaymentModal } from '../payment-modal/PaymentModal';\nimport { useMoonPayIframe } from '../../hooks/useMoonPayIframe';\nimport { PRESETS, injectStyles } from './styles';\n\ninjectStyles();\n\nexport const TonPayButton: React.FC<TonPayButtonProps> = ({\n handlePay,\n isLoading = false,\n variant = 'long',\n preset,\n bgColor,\n textColor,\n borderRadius = 8,\n fontFamily = 'inherit',\n width = 300,\n height = 44,\n text,\n loadingText = 'Processing...',\n style,\n className,\n disabled = false,\n onError,\n showErrorNotification = true,\n amount,\n currency,\n apiKey,\n isOnRampAvailable = false,\n onCardPaymentSuccess,\n itemTitle,\n}) => {\n const address = useTonAddress(true);\n const [tonConnectUI] = useTonConnectUI();\n\n const [showMenu, setShowMenu] = useState(false);\n const [isModalOpen, setIsModalOpen] = useState(false);\n const [onRampAvailable, setOnRampAvailable] = useState(false);\n const [errorMessage, setErrorMessage] = useState<string | null>(null);\n const [internalLoading, setInternalLoading] = useState(false);\n const [userIp, setUserIp] = useState('');\n // We store the redirectToWallet function from ton-connect's onRequestSent callback\n // This allows us to manually trigger the wallet redirect when the user clicks \"Click here\",\n // which is necessary when automatic redirection fails due to platform limitations.\n const [redirectToWallet, setRedirectToWallet] = useState<(() => void) | null>(\n null,\n );\n\n const { checkAvailability, fetchOnRampLink } = useMoonPayIframe({\n apiKey,\n chain: 'mainnet',\n });\n\n useEffect(() => {\n getUserIp().then(setUserIp);\n }, []);\n\n useEffect(() => {\n if (!errorMessage) return;\n const timerId = setTimeout(() => setErrorMessage(null), 3000);\n return () => clearTimeout(timerId);\n }, [errorMessage]);\n\n useEffect(() => {\n let isActive = true;\n\n const check = async () => {\n if (isOnRampAvailable && apiKey && userIp) {\n setInternalLoading(true);\n try {\n const parsedAmount =\n typeof amount === 'string' ? parseFloat(amount) : amount || 0;\n const available = await checkAvailability(\n parsedAmount,\n currency || 'TON',\n userIp,\n );\n if (isActive) setOnRampAvailable(available);\n } catch {\n if (isActive) setOnRampAvailable(false);\n } finally {\n if (isActive) setInternalLoading(false);\n }\n } else {\n if (isActive) setOnRampAvailable(false);\n }\n };\n\n check();\n return () => {\n isActive = false;\n };\n }, [apiKey, amount, currency, isOnRampAvailable, checkAvailability, userIp]);\n\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (showMenu && !(event.target as Element).closest('.tp-wrap')) {\n setShowMenu(false);\n }\n };\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, [showMenu]);\n\n const handleDisconnect = useCallback(() => {\n tonConnectUI.disconnect();\n }, [tonConnectUI]);\n\n const handlePayWithCrypto = useCallback(async () => {\n // We keep the modal open to show processing state\n try {\n setRedirectToWallet(null);\n await handlePay((redirect) => setRedirectToWallet(() => redirect));\n // If success, we can close it or wait for parent to update state\n // setIsModalOpen(false);\n } catch (err) {\n onError?.(err);\n if (showErrorNotification) {\n const raw =\n typeof err === 'object' && err && 'message' in err\n ? String((err as Error).message)\n : String(err ?? '');\n setErrorMessage(raw || 'Wallet connection modal closed');\n }\n }\n }, [handlePay, onError, showErrorNotification]);\n\n const onPayClick = useCallback(async () => {\n if (onRampAvailable) {\n setIsModalOpen(true);\n } else {\n handlePayWithCrypto();\n }\n }, [onRampAvailable, handlePayWithCrypto]);\n\n const handleRetry = useCallback(() => {\n handlePayWithCrypto();\n }, [handlePayWithCrypto]);\n\n const handleFetchOnRampLink = useCallback(\n async (_providerId: string) => {\n return fetchOnRampLink({\n amount: typeof amount === 'string' ? parseFloat(amount) : amount || 0,\n asset: currency || 'TON',\n recipientAddr: address,\n userIp,\n redirectURL: '',\n });\n },\n [fetchOnRampLink, amount, currency, address, userIp],\n );\n\n const presetConfig = preset ? PRESETS[preset] : null;\n const finalBgColor =\n bgColor ?? presetConfig?.bgColor ?? PRESETS.default.bgColor;\n const finalTextColor =\n textColor ?? presetConfig?.textColor ?? PRESETS.default.textColor;\n\n const cssVars: Record<string, string | number | undefined> = {\n '--tp-bg': finalBgColor,\n '--tp-text': finalTextColor,\n '--tp-radius':\n typeof borderRadius === 'number' ? `${borderRadius}px` : borderRadius,\n '--tp-font': fontFamily,\n '--tp-width': toCssSize(width),\n '--tp-height': toCssSize(height),\n };\n\n const isDisabled = isLoading || disabled || internalLoading;\n const showDropdown = !onRampAvailable && !!address && !isLoading;\n\n const renderContent = () => {\n if (text) return <span>{text}</span>;\n\n if (variant === 'short') {\n return (\n <div className=\"tp-btn-content\">\n <TonIcon />\n <span>Pay</span>\n </div>\n );\n }\n\n return (\n <div className=\"tp-btn-content\">\n <span>Pay with</span>\n <TonIcon />\n <span>Pay</span>\n </div>\n );\n };\n\n return (\n <div\n style={{ ...cssVars, ...style } as React.CSSProperties}\n className={classNames('tp-wrap', className)}\n >\n <div className=\"tp-btn-container\">\n <button\n type=\"button\"\n className={classNames(\n 'tp-btn',\n isLoading && 'processing',\n isLoading && 'loading',\n showDropdown ? 'with-menu' : 'no-menu',\n )}\n onClick={isDisabled ? undefined : onPayClick}\n disabled={isDisabled}\n >\n {isLoading ? (\n <div className=\"tp-btn-content\">\n <span className=\"tp-spinner\" />\n <span>{loadingText}</span>\n </div>\n ) : (\n renderContent()\n )}\n </button>\n {showDropdown && (\n <button\n type=\"button\"\n className=\"tp-arrow\"\n onClick={(e) => {\n e.stopPropagation();\n setShowMenu(!showMenu);\n }}\n disabled={isDisabled}\n >\n ▼\n </button>\n )}\n </div>\n\n {isLoading && (\n <div className=\"tp-retry-text\">\n Did the wallet fail to open?{' '}\n <span\n className=\"tp-retry-link\"\n onClick={() =>\n redirectToWallet ? redirectToWallet() : handleRetry()\n }\n >\n Click here\n </span>\n .\n </div>\n )}\n\n {showMenu && showDropdown && (\n <div className=\"tp-menu\">\n <div className=\"tp-menu-arrow\" />\n <div className=\"tp-menu-address\">\n {address?.slice(0, 4)}...{address?.slice(-4)}\n </div>\n <button\n className=\"tp-menu-item danger\"\n onClick={() => {\n handleDisconnect();\n setShowMenu(false);\n }}\n >\n <div className=\"tp-menu-icon\">\n <DisconnectIcon size={16} />\n </div>\n <span>Disconnect</span>\n </button>\n </div>\n )}\n\n <PaymentModal\n isOpen={isModalOpen}\n onClose={() => setIsModalOpen(false)}\n onPayWithCrypto={handlePayWithCrypto}\n walletAddress={address}\n onDisconnect={handleDisconnect}\n amount={amount ? String(amount) : '0.1'}\n currency={currency || 'TON'}\n itemTitle={itemTitle}\n fetchOnRampLink={handleFetchOnRampLink}\n onRampAvailable={onRampAvailable}\n onPaymentSuccess={onCardPaymentSuccess}\n isLoading={isLoading}\n />\n\n {errorMessage && (\n <NotificationRoot>\n <ErrorTransactionNotification text={errorMessage} />\n </NotificationRoot>\n )}\n </div>\n );\n};\n","export function shortenAddress(\n address: string | null | undefined,\n head: number = 4,\n tail: number = 4,\n): string {\n if (!address || typeof address !== 'string') return '';\n if (address.length <= head + tail + 3) return address;\n return `${address.slice(0, head)}...${address.slice(-tail)}`;\n}\n\nexport function toCssSize(value?: number | string): string | undefined {\n if (value === undefined) return undefined;\n return typeof value === 'number' ? `${value}px` : value;\n}\n\nexport function isGradient(value: string): boolean {\n return value.includes('gradient(');\n}\n\nexport function getMenuBgColor(bg: string): string {\n return isGradient(bg) ? '#0098EA' : bg;\n}\n\nexport function classNames(\n ...classes: (string | undefined | null | false)[]\n): string {\n return classes.filter(Boolean).join(' ');\n}\n\nexport async function getUserIp(): Promise<string> {\n try {\n const response = await fetch('https://api.ipify.org?format=json');\n const data = await response.json();\n return data.ip;\n } catch (error) {\n console.error('Failed to fetch user IP:', error);\n return '';\n }\n}\n","import * as React from 'react';\n\ninterface IconProps {\n size?: number;\n color?: string;\n className?: string;\n}\n\nexport const TonIcon: React.FC<IconProps> = ({ size = 24, className }) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 26\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n aria-hidden\n >\n <g clipPath=\"url(#ton-icon-clip)\">\n <path\n d=\"M12 24C18.6274 24 24 18.6274 24 12C24 5.37257 18.6274 0 12 0C5.37257 0 0 5.37257 0 12C0 18.6274 5.37257 24 12 24Z\"\n fill=\"#0098EA\"\n />\n <path\n d=\"M12 24C18.6274 24 24 18.6274 24 12C24 5.37257 18.6274 0 12 0C5.37257 0 0 5.37257 0 12C0 18.6274 5.37257 24 12 24Z\"\n fill=\"white\"\n />\n <path\n d=\"M16.0972 6.69763H7.9022C6.39543 6.69763 5.4404 8.32299 6.19846 9.63695L11.2561 18.4033C11.5862 18.9757 12.4133 18.9757 12.7433 18.4033L17.802 9.63695C18.559 8.32509 17.604 6.69763 16.0982 6.69763H16.0972ZM11.252 15.7744L10.1505 13.6426L7.49278 8.88922C7.31746 8.58497 7.53401 8.1951 7.90117 8.1951H11.251V15.7754L11.252 15.7744ZM16.5046 8.88819L13.8479 13.6437L12.7464 15.7744V8.19407H16.0962C16.4633 8.19407 16.6799 8.58395 16.5046 8.88819Z\"\n fill=\"#0098EA\"\n />\n </g>\n <defs>\n <clipPath id=\"ton-icon-clip\">\n <rect width=\"24\" height=\"26\" fill=\"white\" />\n </clipPath>\n </defs>\n </svg>\n);\n\nexport const TonIconBlue: React.FC<IconProps> = ({ size = 24, className }) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n aria-hidden\n >\n <path\n d=\"M12 24C18.6274 24 24 18.6274 24 12C24 5.37257 18.6274 0 12 0C5.37257 0 0 5.37257 0 12C0 18.6274 5.37257 24 12 24Z\"\n fill=\"#0098EA\"\n />\n <path\n d=\"M16.0972 6.69763H7.9022C6.39543 6.69763 5.4404 8.32299 6.19846 9.63695L11.2561 18.4033C11.5862 18.9757 12.4133 18.9757 12.7433 18.4033L17.802 9.63695C18.559 8.32509 17.604 6.69763 16.0982 6.69763H16.0972ZM11.252 15.7744L10.1505 13.6426L7.49278 8.88922C7.31746 8.58497 7.53401 8.1951 7.90117 8.1951H11.251V15.7754L11.252 15.7744ZM16.5046 8.88819L13.8479 13.6437L12.7464 15.7744V8.19407H16.0962C16.4633 8.19407 16.6799 8.58395 16.5046 8.88819Z\"\n fill=\"white\"\n />\n </svg>\n);\n\nexport const CopyIcon: React.FC<IconProps> = ({\n size = 24,\n color = 'currentColor',\n className,\n}) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n aria-hidden\n >\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M7.76228 2.09998H10.2378C11.0458 2.09997 11.7067 2.09996 12.2438 2.14384C12.7997 2.18926 13.3017 2.28614 13.7706 2.52505C14.5045 2.89896 15.1011 3.49558 15.475 4.22941C15.7139 4.6983 15.8108 5.20038 15.8562 5.75629C15.9001 6.29337 15.9001 6.95422 15.9001 7.76227V8.1H16.2377C17.0457 8.09999 17.7066 8.09998 18.2437 8.14386C18.7996 8.18928 19.3017 8.28616 19.7705 8.52507C20.5044 8.89898 21.101 9.4956 21.4749 10.2294C21.7138 10.6983 21.8107 11.2004 21.8561 11.7563C21.9 12.2934 21.9 12.9542 21.9 13.7623V16.2377C21.9 17.0458 21.9 17.7066 21.8561 18.2437C21.8107 18.7996 21.7138 19.3017 21.4749 19.7706C21.101 20.5044 20.5044 21.101 19.7705 21.4749C19.3017 21.7138 18.7996 21.8107 18.2437 21.8561C17.7066 21.9 17.0458 21.9 16.2378 21.9H13.7623C12.9543 21.9 12.2934 21.9 11.7563 21.8561C11.2004 21.8107 10.6983 21.7138 10.2294 21.4749C9.49561 21.101 8.89898 20.5044 8.52508 19.7706C8.28616 19.3017 8.18928 18.7996 8.14386 18.2437C8.09998 17.7066 8.09999 17.0458 8.1 16.2377V15.9H7.76227C6.95426 15.9 6.29335 15.9 5.75629 15.8561C5.20038 15.8107 4.6983 15.7138 4.22941 15.4749C3.49558 15.101 2.89896 14.5044 2.52505 13.7705C2.28614 13.3017 2.18926 12.7996 2.14384 12.2437C2.09996 11.7066 2.09997 11.0458 2.09998 10.2377V7.76228C2.09997 6.95424 2.09996 6.29336 2.14384 5.75629C2.18926 5.20038 2.28614 4.6983 2.52505 4.22941C2.89896 3.49558 3.49558 2.89896 4.22941 2.52505C4.6983 2.28614 5.20038 2.18926 5.75629 2.14384C6.29336 2.09996 6.95425 2.09997 7.76228 2.09998ZM8.1 14.1V13.7623C8.09999 12.9542 8.09998 12.2934 8.14386 11.7563C8.18928 11.2004 8.28616 10.6983 8.52508 10.2294C8.89898 9.4956 9.49561 8.89898 10.2294 8.52507C10.6983 8.28616 11.2004 8.18928 11.7563 8.14386C12.2934 8.09998 12.9542 8.09999 13.7623 8.1H14.1001V7.79998C14.1001 6.94505 14.0994 6.35798 14.0622 5.90287C14.0259 5.45827 13.9593 5.21944 13.8712 5.0466C13.6699 4.65146 13.3486 4.3302 12.9535 4.12886C12.7806 4.04079 12.5418 3.97419 12.0972 3.93786C11.6421 3.90068 11.055 3.89998 10.2001 3.89998H7.79998C6.94505 3.89998 6.35798 3.90068 5.90287 3.93786C5.45827 3.97419 5.21944 4.04079 5.0466 4.12886C4.65146 4.3302 4.3302 4.65146 4.12886 5.0466C4.04079 5.21944 3.97419 5.45827 3.93786 5.90287C3.90068 6.35798 3.89998 6.94505 3.89998 7.79998V10.2C3.89998 11.0549 3.90068 11.642 3.93786 12.0971C3.97419 12.5417 4.04079 12.7805 4.12886 12.9534C4.3302 13.3485 4.65146 13.6698 5.0466 13.8711C5.21944 13.9592 5.45827 14.0258 5.90287 14.0621C6.35798 14.0993 6.94505 14.1 7.79998 14.1H8.1ZM11.0466 10.1289C11.2195 10.0408 11.4583 9.97421 11.9029 9.93788C12.358 9.9007 12.9451 9.9 13.8 9.9H16.2C17.0549 9.9 17.642 9.9007 18.0971 9.93788C18.5417 9.97421 18.7805 10.0408 18.9534 10.1289C19.3485 10.3302 19.6698 10.6515 19.8711 11.0466C19.9592 11.2195 20.0258 11.4583 20.0621 11.9029C20.0993 12.358 20.1 12.9451 20.1 13.8V16.2C20.1 17.0549 20.0993 17.642 20.0621 18.0971C20.0258 18.5417 19.9592 18.7805 19.8711 18.9534C19.6698 19.3485 19.3485 19.6698 18.9534 19.8711C18.7805 19.9592 18.5417 20.0258 18.0971 20.0621C17.642 20.0993 17.0549 20.1 16.2 20.1H13.8C12.9451 20.1 12.358 20.0993 11.9029 20.0621C11.4583 20.0258 11.2195 19.9592 11.0466 19.8711C10.6515 19.6698 10.3302 19.3485 10.1289 18.9534C10.0408 18.7805 9.97421 18.5417 9.93788 18.0971C9.9007 17.642 9.9 17.0549 9.9 16.2V13.8C9.9 12.9451 9.9007 12.358 9.93788 11.9029C9.97421 11.4583 10.0408 11.2195 10.1289 11.0466C10.3302 10.6515 10.6515 10.3302 11.0466 10.1289Z\"\n fill={color}\n />\n </svg>\n);\n\nexport const DisconnectIcon: React.FC<IconProps> = ({\n size = 24,\n color = 'currentColor',\n className,\n}) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n aria-hidden\n >\n <path\n d=\"M8.7624 3.10001C7.95435 3.1 7.29349 3.09999 6.75642 3.14387C6.2005 3.18929 5.69842 3.28617 5.22954 3.52508C4.4957 3.89899 3.89908 4.49561 3.52517 5.22944C3.28626 5.69833 3.18938 6.20041 3.14396 6.75632C3.10008 7.2934 3.10009 7.95424 3.1001 8.76229V15.2377C3.10009 16.0458 3.10008 16.7066 3.14396 17.2437C3.18938 17.7996 3.28626 18.3017 3.52517 18.7706C3.89908 19.5044 4.4957 20.101 5.22954 20.4749C5.69842 20.7138 6.2005 20.8107 6.75642 20.8561C7.29349 20.9 7.95434 20.9 8.76239 20.9H12.0001C12.4972 20.9 12.9001 20.4971 12.9001 20C12.9001 19.503 12.4972 19.1 12.0001 19.1H8.8001C7.94517 19.1 7.3581 19.0993 6.90299 19.0621C6.45839 19.0258 6.21956 18.9592 6.04672 18.8711C5.65158 18.6698 5.33032 18.3485 5.12898 17.9534C5.04092 17.7805 4.97431 17.5417 4.93798 17.0971C4.9008 16.642 4.9001 16.0549 4.9001 15.2V8.80001C4.9001 7.94508 4.9008 7.35801 4.93798 6.9029C4.97431 6.4583 5.04092 6.21947 5.12898 6.04663C5.33032 5.65149 5.65158 5.33023 6.04672 5.12889C6.21956 5.04082 6.45839 4.97422 6.90299 4.93789C7.3581 4.90071 7.94517 4.90001 8.8001 4.90001H12.0001C12.4972 4.90001 12.9001 4.49706 12.9001 4.00001C12.9001 3.50295 12.4972 3.10001 12.0001 3.10001H8.7624Z\"\n fill={color}\n />\n <path\n d=\"M17.6364 7.3636C17.2849 7.01212 16.7151 7.01212 16.3636 7.3636C16.0121 7.71507 16.0121 8.28492 16.3636 8.63639L18.8272 11.1H9.00001C8.50295 11.1 8.10001 11.5029 8.10001 12C8.10001 12.497 8.50295 12.9 9.00001 12.9H18.8272L16.3636 15.3636C16.0121 15.7151 16.0121 16.2849 16.3636 16.6364C16.7151 16.9879 17.2849 16.9879 17.6364 16.6364L21.6364 12.6364C21.9879 12.2849 21.9879 11.7151 21.6364 11.3636L17.6364 7.3636Z\"\n fill={color}\n />\n </svg>\n);\n\nexport const CloseIcon: React.FC<IconProps> = ({\n size = 24,\n color = 'currentColor',\n className,\n}) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 32 32\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n aria-hidden\n >\n <rect width=\"32\" height=\"32\" rx=\"8\" fill=\"#F4F4F4\" />\n <path\n d=\"M8.99933 22.251L22.2501 9.00026\"\n stroke=\"#7A7A7A\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n />\n <path\n d=\"M8.99933 9.08296L22.2501 22.3337\"\n stroke=\"#7A7A7A\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n />\n </svg>\n);\n\nexport const BackIcon: React.FC<IconProps> = ({\n size = 24,\n color = 'currentColor',\n className,\n}) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n aria-hidden\n >\n <path\n d=\"M19 12H5\"\n stroke={color}\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n <path\n d=\"M12 19L5 12L12 5\"\n stroke={color}\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n);\n\nexport const CheckIcon: React.FC<IconProps> = ({\n size = 24,\n color = 'currentColor',\n className,\n}) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n aria-hidden\n >\n <path\n d=\"M20 6L9 17L4 12\"\n stroke={color}\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n);\n\nexport const ErrorIcon: React.FC<IconProps> = ({\n size = 24,\n color = 'currentColor',\n className,\n}) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n aria-hidden\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke={color} strokeWidth=\"2\" />\n <path d=\"M12 8V12\" stroke={color} strokeWidth=\"2\" strokeLinecap=\"round\" />\n <circle cx=\"12\" cy=\"16\" r=\"1\" fill={color} />\n </svg>\n);\n\nexport const MenuIcon: React.FC<IconProps> = ({\n size = 24,\n color = 'currentColor',\n className,\n}) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n aria-hidden\n >\n <circle cx=\"12\" cy=\"12\" r=\"2\" fill={color} />\n <circle cx=\"19\" cy=\"12\" r=\"2\" fill={color} />\n <circle cx=\"5\" cy=\"12\" r=\"2\" fill={color} />\n </svg>\n);\n\nexport const ErrorDotIcon: React.FC<{ color?: string }> = ({\n color = '#FF5252',\n}) => (\n <svg\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden\n >\n <circle cx=\"12\" cy=\"12\" r=\"11\" fill={color} />\n <path\n d=\"M7.864 9.136A1.5 1.5 0 0 1 9.136 7.864L12 10.727 14.864 7.864A1.5 1.5 0 0 1 16.136 9.136L13.273 12 16.136 14.864a1.5 1.5 0 0 1-2.272 2.272L12 13.273 9.136 17.136A1.5 1.5 0 1 1 7.864 14.864L10.727 12 7.864 9.136Z\"\n fill=\"#fff\"\n />\n </svg>\n);\n\nexport const SpinnerIcon: React.FC<{ className?: string }> = ({\n className,\n}) => <span className={className} aria-hidden />;\n\nexport const CardIcon: React.FC<IconProps> = ({ size = 20, className }) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n aria-hidden\n >\n <path\n d=\"M15.4688 5H4.53125C3.68693 5 3 5.71369 3 6.59091V13.4091C3 14.2863 3.68693 15 4.53125 15H15.4688C16.3131 15 17 14.2863 17 13.4091V6.59091C17 5.71369 16.3131 5 15.4688 5ZM4.53125 5.90909H15.4688C15.8306 5.90909 16.125 6.21497 16.125 6.59091V7.5H3.875V6.59091C3.875 6.21497 4.16941 5.90909 4.53125 5.90909ZM15.4688 14.0909H4.53125C4.16941 14.0909 3.875 13.785 3.875 13.4091V8.40909H16.125V13.4091C16.125 13.785 15.8306 14.0909 15.4688 14.0909Z\"\n fill=\"white\"\n />\n <path\n d=\"M6.33333 13H5.66667C5.2985 13 5 12.7015 5 12.3333V11.6667C5 11.2985 5.2985 11 5.66667 11H6.33333C6.7015 11 7 11.2985 7 11.6667V12.3333C7 12.7015 6.7015 13 6.33333 13Z\"\n fill=\"white\"\n />\n </svg>\n);\n\nexport const CryptoIcon: React.FC<IconProps> = ({ size = 20, className }) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n aria-hidden\n >\n <path\n d=\"M14.3896 3.5H5.61006C3.99582 3.5 2.97267 5.24122 3.7848 6.64885L9.20318 16.0401C9.55683 16.6533 10.4429 16.6533 10.7965 16.0401L16.216 6.64885C17.027 5.24347 16.0028 3.5 14.3896 3.5ZM9.19879 13.2238L8.01872 10.9401L5.17144 5.84782C4.98361 5.52189 5.21561 5.10422 5.60896 5.10422H9.19772V13.225L9.19879 13.2238ZM14.826 5.84671L11.9798 10.9412L10.7998 13.2238V5.10312H14.3885C14.7818 5.10312 15.0138 5.52078 14.826 5.84671Z\"\n fill=\"white\"\n />\n </svg>\n);\n\nexport const InfoIcon: React.FC<IconProps> = ({\n size = 24,\n color = 'currentColor',\n className,\n}) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n aria-hidden\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke={color} strokeWidth=\"2\" />\n <path d=\"M12 16V12\" stroke={color} strokeWidth=\"2\" strokeLinecap=\"round\" />\n <circle cx=\"12\" cy=\"8\" r=\"1\" fill={color} />\n </svg>\n);\n\nexport const TonPayLogo: React.FC<IconProps> = ({ className }) => (\n <div\n className={className}\n style={{ display: 'flex', alignItems: 'center', gap: '4px' }}\n >\n <TonIconBlue size={26} />\n <span\n style={{\n fontWeight: 600,\n fontSize: '18px',\n lineHeight: '24px',\n color: '#000000',\n letterSpacing: '-0.5px',\n }}\n >\n Pay\n </span>\n </div>\n);\n","import * as React from 'react';\nimport type { NotificationProps } from '../../types';\n\nconst notificationStyles = `\n.tp-noti-root{position:fixed;top:16px;right:16px;z-index:10000;display:flex;flex-direction:column;gap:10px}\n.tp-noti-card{width:256px;padding:12px 16px;display:flex;gap:9px;align-items:flex-start;background:#ffffff;color:#111827;border-radius:16px;box-shadow:0 4px 24px rgba(0,0,0,.16);animation:tp-fade-in .2s ease}\n.tp-noti-content{flex:1;min-width:0}\n.tp-noti-title{font-size:15px;font-weight:700;line-height:20px;margin:0}\n.tp-noti-text{margin-top:4px;color:#6b7280;font-size:13px;line-height:18px;word-break:break-word}\n.tp-noti-icon{width:24px;height:24px;margin-top:2px;flex:0 0 auto}\n`;\n\nlet stylesInjected = false;\n\nfunction injectNotificationStyles(): void {\n if (typeof document === 'undefined' || stylesInjected) return;\n const style = document.createElement('style');\n style.id = 'tonpay-notification-styles';\n style.textContent = notificationStyles;\n document.head.appendChild(style);\n stylesInjected = true;\n}\n\ninjectNotificationStyles();\n\nexport const NotificationCard: React.FC<NotificationProps> = ({\n title,\n text,\n icon,\n className,\n style,\n}) => {\n return (\n <div\n className={['tp-noti-card', className].filter(Boolean).join(' ')}\n style={style}\n >\n <div className=\"tp-noti-content\">\n <h3 className=\"tp-noti-title\">{title}</h3>\n {text && <div className=\"tp-noti-text\">{text}</div>}\n </div>\n {icon && <div className=\"tp-noti-icon\">{icon}</div>}\n </div>\n );\n};\n\nexport const NotificationRoot: React.FC<{ children?: React.ReactNode }> = ({\n children,\n}) => {\n return <div className=\"tp-noti-root\">{children}</div>;\n};\n","import * as React from 'react';\nimport { ErrorDotIcon } from '../icons';\nimport { NotificationCard } from './Notification';\n\ninterface ErrorTransactionNotificationProps {\n text?: string;\n className?: string;\n style?: React.CSSProperties;\n}\n\nexport const ErrorTransactionNotification: React.FC<\n ErrorTransactionNotificationProps\n> = ({ text, className, style }) => {\n return (\n <NotificationCard\n title=\"Transaction cancelled\"\n text={text}\n icon={<ErrorDotIcon />}\n className={className}\n style={style}\n />\n );\n};\n","import * as React from 'react';\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport BottomSheet from '../bottom-sheet/BottomSheet';\nimport {\n TonIconBlue,\n CloseIcon,\n BackIcon,\n CheckIcon,\n ErrorIcon,\n MenuIcon,\n CardIcon,\n CryptoIcon,\n TonPayLogo,\n} from '../icons';\nimport type { PaymentModalProps, PaymentViewState } from '../../types';\nimport './PaymentModal.css';\n\nconst PROVIDER = { id: 'moonpay', name: 'Moonpay', iconClass: 'icon-moonpay' };\nconst IFRAME_LOAD_TIMEOUT = 30000;\n\nexport const PaymentModal: React.FC<PaymentModalProps> = ({\n isOpen,\n onClose,\n onPayWithCrypto,\n amount = '0.1',\n currency = 'TON',\n itemTitle,\n walletAddress,\n onDisconnect,\n fetchOnRampLink,\n onRampAvailable = false,\n onPaymentSuccess,\n isLoading = false,\n}) => {\n const [view, setView] = useState<PaymentViewState>('main');\n const [isMobile, setIsMobile] = useState(false);\n const [showMenu, setShowMenu] = useState(false);\n const [onRampLink, setOnRampLink] = useState<string | null>(null);\n const [onRampError, setOnRampError] = useState<string | null>(null);\n const [isOnRampLoading, setIsOnRampLoading] = useState(false);\n const [iframeLoaded, setIframeLoaded] = useState(false);\n const [iframeError, setIframeError] = useState<string | null>(null);\n const [sheetDetent, setSheetDetent] = useState<number[]>([0.55]);\n\n const iframeTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const fetchStartedRef = useRef(false);\n const contentRef = useRef<HTMLDivElement>(null);\n\n const handlePaymentSuccess = useCallback(() => {\n if (iframeTimeoutRef.current) {\n clearTimeout(iframeTimeoutRef.current);\n }\n setView('success');\n onPaymentSuccess?.();\n setTimeout(() => {\n onClose();\n }, 2000);\n }, [onClose, onPaymentSuccess]);\n\n const handlePaymentError = useCallback((errorMessage: string) => {\n if (iframeTimeoutRef.current) {\n clearTimeout(iframeTimeoutRef.current);\n }\n setIframeError(errorMessage);\n setView('error');\n }, []);\n\n const handleRetry = useCallback(() => {\n setOnRampLink(null);\n setOnRampError(null);\n setIframeError(null);\n setIframeLoaded(false);\n fetchStartedRef.current = false;\n setView('card');\n }, []);\n\n useEffect(() => {\n const handleMessage = (event: MessageEvent) => {\n if (event.data?.type === 'TONPAY_PAYMENT_SUCCESS') {\n handlePaymentSuccess();\n }\n if (event.data?.type === 'TONPAY_PAYMENT_ERROR') {\n const payload = event.data.payload;\n handlePaymentError(payload?.message || 'Payment failed');\n }\n if (event.data?.type === 'TONPAY_IFRAME_LOADED') {\n setIframeLoaded(true);\n if (iframeTimeoutRef.current) {\n clearTimeout(iframeTimeoutRef.current);\n }\n }\n if (event.data?.type === 'TONPAY_MOONPAY_EVENT') {\n const payload = event.data.payload;\n if (\n payload?.type === 'onTransactionCompleted' ||\n payload?.eventName === 'transactionCompleted'\n ) {\n handlePaymentSuccess();\n }\n if (\n payload?.type === 'onTransactionFailed' ||\n payload?.eventName === 'transactionFailed'\n ) {\n handlePaymentError(payload?.message || 'Transaction failed');\n }\n }\n };\n\n window.addEventListener('message', handleMessage);\n return () => window.removeEventListener('message', handleMessage);\n }, [handlePaymentSuccess, handlePaymentError]);\n\n useEffect(() => {\n if (view !== 'card') {\n fetchStartedRef.current = false;\n }\n }, [view]);\n\n useEffect(() => {\n if (\n view === 'card' &&\n !onRampLink &&\n !onRampError &&\n fetchOnRampLink &&\n !fetchStartedRef.current\n ) {\n fetchStartedRef.current = true;\n setIsOnRampLoading(true);\n setOnRampError(null);\n setIframeLoaded(false);\n setIframeError(null);\n\n fetchOnRampLink(PROVIDER.id)\n .then((link: string) => {\n setOnRampLink(link);\n iframeTimeoutRef.current = setTimeout(() => {\n if (!iframeLoaded) {\n handlePaymentError(\n 'Payment service is taking too long to load. Please try again.',\n );\n }\n }, IFRAME_LOAD_TIMEOUT);\n })\n .catch((err: any) => {\n const errorMsg = err?.message || 'Failed to initialize payment';\n setOnRampError(errorMsg);\n fetchStartedRef.current = false;\n })\n .finally(() => setIsOnRampLoading(false));\n }\n\n return () => {\n if (iframeTimeoutRef.current) {\n clearTimeout(iframeTimeoutRef.current);\n }\n };\n }, [\n view,\n onRampLink,\n onRampError,\n fetchOnRampLink,\n iframeLoaded,\n handlePaymentError,\n ]);\n\n useEffect(() => {\n const checkMobile = () => {\n setIsMobile(window.innerWidth < 768);\n };\n checkMobile();\n window.addEventListener('resize', checkMobile);\n return () => window.removeEventListener('resize', checkMobile);\n }, []);\n\n useEffect(() => {\n if (isOpen) {\n setView('main');\n setShowMenu(false);\n setOnRampLink(null);\n setOnRampError(null);\n setIframeLoaded(false);\n setIframeError(null);\n if (iframeTimeoutRef.current) {\n clearTimeout(iframeTimeoutRef.current);\n }\n }\n }, [isOpen]);\n\n useEffect(() => {\n if (!isMobile || !isOpen) return;\n\n const updateHeight = () => {\n if (view === 'card') {\n setSheetDetent((prev) => (prev[0] === 0.9 ? prev : [0.9]));\n return;\n }\n if (contentRef.current) {\n const height = contentRef.current.scrollHeight;\n const windowHeight = window.innerHeight;\n const detent = Math.min((height + 40) / windowHeight, 0.95);\n setSheetDetent((prev) =>\n Math.abs(prev[0] - detent) < 0.01 ? prev : [detent],\n );\n }\n };\n\n setTimeout(updateHeight, 50);\n\n const observer = new ResizeObserver(updateHeight);\n if (contentRef.current) {\n observer.observe(contentRef.current);\n }\n\n return () => observer.disconnect();\n }, [view, isMobile, isOpen]);\n\n const handleBack = () => setView('main');\n\n const handleIframeLoad = () => {\n setIframeLoaded(true);\n if (iframeTimeoutRef.current) {\n clearTimeout(iframeTimeoutRef.current);\n }\n };\n\n const handleIframeError = () => {\n handlePaymentError('Failed to load payment service. Please try again.');\n };\n\n const renderHeader = () => (\n <div className=\"pm-header\">\n <div className=\"pm-header-left\">\n {view !== 'main' ? (\n <button className=\"pm-back-btn\" onClick={handleBack}>\n <BackIcon />\n </button>\n ) : (\n <TonPayLogo />\n )}\n </div>\n {view !== 'main' && <div className=\"pm-title\">New Purchase</div>}\n <div className=\"pm-header-right\">\n {walletAddress && view === 'main' && (\n <div style={{ position: 'relative' }}>\n <button\n className=\"pm-close-btn\"\n onClick={() => setShowMenu(!showMenu)}\n >\n <MenuIcon />\n </button>\n {showMenu && (\n <div className=\"pm-menu-dropdown\">\n <div className=\"pm-menu-item disabled\">\n {walletAddress.slice(0, 4)}...{walletAddress.slice(-4)}\n </div>\n <div\n className=\"pm-menu-item danger\"\n onClick={() => {\n onDisconnect?.();\n setShowMenu(false);\n }}\n >\n Disconnect\n </div>\n </div>\n )}\n </div>\n )}\n <button className=\"pm-close-btn\" onClick={onClose}>\n <CloseIcon size={32} />\n </button>\n </div>\n </div>\n );\n\n const renderMainView = () => (\n <div className=\"pm-body-main\">\n <div className=\"pm-main-container\">\n <h2 className=\"pm-main-title\">New Purchase</h2>\n <div className=\"pm-amount-container\">\n <span className=\"pm-amount-label\">Amount</span>\n <div className=\"pm-amount-value\">\n {amount} {currency} <TonIconBlue />\n </div>\n </div>\n {itemTitle && (\n <div className=\"pm-order-info\">\n <span className=\"pm-order-text\">{itemTitle}</span>\n </div>\n )}\n </div>\n <div className=\"pm-actions-card\">\n <div className=\"pm-actions\">\n <button\n className={`pm-btn ${isLoading ? 'processing' : 'pm-btn-primary'}`}\n onClick={isLoading ? undefined : onPayWithCrypto}\n disabled={isLoading}\n >\n {isLoading ? (\n <div\n style={{ display: 'flex', alignItems: 'center', gap: '4px' }}\n >\n <div className=\"pm-btn-spinner\" />\n <span>Processing</span>\n </div>\n ) : (\n <div\n style={{ display: 'flex', alignItems: 'center', gap: '4px' }}\n >\n <CryptoIcon />\n <span>Pay with Crypto</span>\n </div>\n )}\n </button>\n\n {isLoading && (\n <div className=\"pm-retry-link\">\n Did the wallet fail to open?{' '}\n <span className=\"pm-retry-action\" onClick={onPayWithCrypto}>\n Click here\n </span>\n .\n </div>\n )}\n\n {onRampAvailable && !isLoading && (\n <button\n className=\"pm-btn pm-btn-black\"\n onClick={() => setView('card')}\n >\n <CardIcon />\n <span>Pay with Card</span>\n </button>\n )}\n </div>\n {onRampAvailable && !isLoading && (\n <div className=\"pm-footer\">\n <span className=\"pm-footer-text\">Processed by {PROVIDER.name}</span>\n </div>\n )}\n </div>\n </div>\n );\n\n const renderCardView = () => (\n <div className=\"pm-iframe-container\">\n {isOnRampLoading && (\n <div className=\"pm-loading-container\">\n <div className=\"pm-spinner\" />\n <p>Loading {PROVIDER.name}...</p>\n </div>\n )}\n {onRampError && (\n <div className=\"pm-error-inline\">\n <ErrorIcon />\n <p>{onRampError}</p>\n <button className=\"pm-btn pm-btn-outline\" onClick={handleRetry}>\n Try Again\n </button>\n </div>\n )}\n {onRampLink && !isOnRampLoading && !onRampError && (\n <>\n {!iframeLoaded && (\n <div className=\"pm-loading-overlay\">\n <div className=\"pm-spinner\" />\n </div>\n )}\n <iframe\n src={onRampLink}\n title={PROVIDER.name}\n width=\"100%\"\n height=\"100%\"\n frameBorder=\"0\"\n allow=\"accelerometer; autoplay; camera; gyroscope; payment\"\n onLoad={handleIframeLoad}\n onError={handleIframeError}\n style={{ opacity: iframeLoaded ? 1 : 0 }}\n />\n </>\n )}\n </div>\n );\n\n const renderSuccessView = () => (\n <div className=\"pm-success-container\">\n <div className=\"pm-success-icon\">\n <CheckIcon />\n </div>\n <h2 className=\"pm-success-title\">Payment Successful</h2>\n <p className=\"pm-success-text\">Your purchase is being processed</p>\n </div>\n );\n\n const renderErrorView = () => (\n <div className=\"pm-error-container\">\n <div className=\"pm-error-icon\">\n <ErrorIcon />\n </div>\n <h2 className=\"pm-error-title\">Payment Failed</h2>\n <p className=\"pm-error-text\">{iframeError || 'Something went wrong'}</p>\n <div className=\"pm-error-actions\">\n <button className=\"pm-btn pm-btn-primary\" onClick={handleRetry}>\n Try Again\n </button>\n <button className=\"pm-btn pm-btn-outline\" onClick={onPayWithCrypto}>\n Pay with Crypto Instead\n </button>\n </div>\n </div>\n );\n\n const renderContent = () => (\n <div\n className=\"pm-content\"\n style={{ height: view === 'card' ? '100%' : 'auto' }}\n >\n {view !== 'success' && view !== 'error' && renderHeader()}\n {view === 'main' && renderMainView()}\n {view === 'card' && renderCardView()}\n {view === 'success' && renderSuccessView()}\n {view === 'error' && renderErrorView()}\n </div>\n );\n\n if (isMobile) {\n return (\n <BottomSheet\n isOpen={isOpen}\n onClose={onClose}\n detents={sheetDetent}\n initialDetent={0}\n enableSwipeToClose={view === 'main'}\n >\n <div\n ref={contentRef}\n style={{ height: view === 'card' ? '100%' : 'auto' }}\n >\n {renderContent()}\n </div>\n </BottomSheet>\n );\n }\n\n if (!isOpen) return null;\n\n return (\n <div className=\"pm-desktop-overlay\" onClick={onClose}>\n <div className=\"pm-desktop-modal\" onClick={(e) => e.stopPropagation()}>\n {renderContent()}\n </div>\n </div>\n );\n};\n","import * as React from 'react';\nimport { useEffect, useRef, useState, useCallback } from 'react';\nimport './BottomSheet.css';\nimport type { BottomSheetProps } from '../../types';\n\nconst BottomSheet: React.FC<BottomSheetProps> = ({\n isOpen,\n onClose,\n detents = [0.5, 0.9],\n initialDetent = 0,\n children,\n className = '',\n backdropClassName = '',\n handleClassName = '',\n contentClassName = '',\n enableBackdropClose = true,\n enableSwipeToClose = true,\n maxHeight = '90vh',\n minHeight = '20vh',\n}) => {\n const sheetRef = useRef<HTMLDivElement>(null);\n const contentRef = useRef<HTMLDivElement>(null);\n const [currentDetent, setCurrentDetent] = useState(initialDetent);\n const [isDragging, setIsDragging] = useState(false);\n const [startY, setStartY] = useState(0);\n const [currentY, setCurrentY] = useState(0);\n const [isScrolling, setIsScrolling] = useState(false);\n const [sheetHeight, setSheetHeight] = useState(0);\n const [isClosing, setIsClosing] = useState(false);\n\n const touchStartYRef = useRef(0);\n const touchStartTimeRef = useRef(0);\n const mouseStartTimeRef = useRef(0);\n const scrollTopRef = useRef(0);\n const canDragRef = useRef(true);\n const closeTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const openTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const isClosingRef = useRef(false);\n const isSnappingRef = useRef(false);\n const targetDetentRef = useRef<number | null>(null);\n\n const getDetentValue = useCallback(\n (index: number): number => {\n const clampedIndex = Math.max(0, Math.min(index, detents.length - 1));\n return detents[clampedIndex];\n },\n [detents],\n );\n\n const calculateSheetHeight = useCallback((): number => {\n if (!sheetRef.current) return 0;\n const viewportHeight = window.innerHeight;\n const detentValue = getDetentValue(currentDetent);\n return viewportHeight * detentValue;\n }, [currentDetent, getDetentValue]);\n\n useEffect(() => {\n if (isOpen) {\n if (isClosingRef.current) return;\n\n isClosingRef.current = false;\n setIsClosing(false);\n setCurrentDetent(initialDetent);\n\n if (openTimeoutRef.current) {\n clearTimeout(openTimeoutRef.current);\n }\n\n setSheetHeight(0);\n\n openTimeoutRef.current = setTimeout(() => {\n const viewportHeight = window.innerHeight;\n const detentValue = getDetentValue(initialDetent);\n setSheetHeight(viewportHeight * detentValue);\n openTimeoutRef.current = null;\n }, 10);\n } else {\n if (openTimeoutRef.current) {\n clearTimeout(openTimeoutRef.current);\n openTimeoutRef.current = null;\n }\n setSheetHeight(0);\n setCurrentDetent(initialDetent);\n }\n }, [isOpen, initialDetent, getDetentValue]);\n\n const handleClose = useCallback(() => {\n if (isClosing || isClosingRef.current) return;\n\n isClosingRef.current = true;\n setIsClosing(true);\n setSheetHeight(0);\n setCurrentDetent(initialDetent);\n\n if (closeTimeoutRef.current) {\n clearTimeout(closeTimeoutRef.current);\n }\n\n closeTimeoutRef.current = setTimeout(() => {\n isClosingRef.current = false;\n setIsClosing(false);\n onClose();\n }, 200);\n }, [isClosing, onClose, initialDetent]);\n\n useEffect(() => {\n if (\n isOpen &&\n !isDragging &&\n !isSnappingRef.current &&\n targetDetentRef.current === null\n ) {\n const expectedHeight = calculateSheetHeight();\n const heightDiff = Math.abs(sheetHeight - expectedHeight);\n if (heightDiff > 1) {\n setSheetHeight(expectedHeight);\n }\n }\n }, [isOpen, isDragging, currentDetent, sheetHeight, calculateSheetHeight]);\n\n const findNearestDetent = useCallback(\n (position: number): number => {\n const viewportHeight = window.innerHeight;\n const normalizedPosition = position / viewportHeight;\n\n let nearestIndex = 0;\n let minDistance = Math.abs(normalizedPosition - detents[0]);\n\n detents.forEach((detent, index) => {\n const distance = Math.abs(normalizedPosition - detent);\n if (distance < minDistance) {\n minDistance = distance;\n nearestIndex = index;\n }\n });\n\n return nearestIndex;\n },\n [detents],\n );\n\n const snapToDetent = useCallback(\n (detentIndex: number) => {\n const clampedIndex = Math.max(\n 0,\n Math.min(detentIndex, detents.length - 1),\n );\n isSnappingRef.current = true;\n targetDetentRef.current = clampedIndex;\n const viewportHeight = window.innerHeight;\n const newHeight = viewportHeight * getDetentValue(clampedIndex);\n setCurrentDetent(clampedIndex);\n setSheetHeight(newHeight);\n setTimeout(() => {\n isSnappingRef.current = false;\n targetDetentRef.current = null;\n }, 200);\n },\n [detents, getDetentValue],\n );\n\n const handleTouchStart = useCallback(\n (e: React.TouchEvent) => {\n if (!isOpen || isClosing) return;\n\n const touch = e.touches[0];\n touchStartYRef.current = touch.clientY;\n touchStartTimeRef.current = Date.now();\n setStartY(touch.clientY);\n setCurrentY(touch.clientY);\n\n if (contentRef.current) {\n scrollTopRef.current = contentRef.current.scrollTop;\n setIsScrolling(false);\n canDragRef.current = scrollTopRef.current === 0;\n } else {\n canDragRef.current = true;\n }\n },\n [isOpen, isClosing],\n );\n\n const calculateNewHeight = useCallback(\n (touchY: number): number => {\n const viewportHeight = window.innerHeight;\n const baseHeight = viewportHeight * getDetentValue(currentDetent);\n const delta = touchStartYRef.current - touchY;\n const maxDetentHeight = viewportHeight * Math.max(...detents);\n const minDetentHeight = viewportHeight * Math.min(...detents);\n const isAtMaxDetent = currentDetent === detents.length - 1;\n const isAtMinDetent = currentDetent === 0;\n const isDraggingDown = delta < 0;\n\n let newHeight = baseHeight + delta;\n\n if (isAtMaxDetent) {\n newHeight = Math.max(0, Math.min(maxDetentHeight, newHeight));\n } else if (isAtMinDetent) {\n if (isDraggingDown && enableSwipeToClose) {\n newHeight = Math.max(0, Math.min(maxDetentHeight, newHeight));\n } else {\n newHeight = Math.max(\n minDetentHeight,\n Math.min(maxDetentHeight, newHeight),\n );\n }\n } else {\n newHeight = Math.max(\n minDetentHeight,\n Math.min(maxDetentHeight, newHeight),\n );\n }\n\n return newHeight;\n },\n [currentDetent, detents, enableSwipeToClose, getDetentValue],\n );\n\n const handleTouchMoveNative = useCallback(\n (e: TouchEvent) => {\n if (!isOpen || isClosing) return;\n\n const touch = e.touches[0];\n if (!touch) return;\n\n const deltaY = touch.clientY - touchStartYRef.current;\n const currentScrollTop = contentRef.current?.scrollTop ?? 0;\n const isContentScrollable = contentRef.current\n ? contentRef.current.scrollHeight > contentRef.current.clientHeight\n : false;\n\n if (!contentRef.current) return;\n\n const touchTarget = e.target as HTMLElement;\n const isTouchingContent = contentRef.current.contains(touchTarget);\n const isTouchingHandle =\n touchTarget.closest('.bottom-sheet-handle-container') !== null;\n\n if (isDragging) {\n if (e.cancelable) e.preventDefault();\n setCurrentY(touch.clientY);\n setSheetHeight(calculateNewHeight(touch.clientY));\n return;\n }\n\n if (isTouchingHandle && Math.abs(deltaY) > 5) {\n if (e.cancelable) e.preventDefault();\n setIsDragging(true);\n setIsScrolling(false);\n setCurrentY(touch.clientY);\n setSheetHeight(calculateNewHeight(touch.clientY));\n return;\n }\n\n if (isTouchingContent && !isDragging) {\n if (currentScrollTop > 0) return;\n if (deltaY < 0 && isContentScrollable) return;\n\n if (\n currentScrollTop === 0 &&\n deltaY > 0 &&\n canDragRef.current &&\n Math.abs(deltaY) > 5\n ) {\n if (e.cancelable) e.preventDefault();\n setIsDragging(true);\n setIsScrolling(false);\n setCurrentY(touch.clientY);\n setSheetHeight(calculateNewHeight(touch.clientY));\n }\n }\n },\n [isOpen, isDragging, isClosing, calculateNewHeight],\n );\n\n const handleTouchEnd = useCallback(\n (e?: React.TouchEvent | TouchEvent) => {\n if (!isOpen || isClosing) return;\n if (isScrolling) {\n setIsScrolling(false);\n return;\n }\n if (!isDragging) return;\n\n setIsDragging(false);\n const viewportHeight = window.innerHeight;\n const currentPosition = sheetHeight / viewportHeight;\n const minDetentValue = Math.min(...detents);\n const maxDetentValue = Math.max(...detents);\n const swipeDuration = Date.now() - touchStartTimeRef.current;\n\n const finalTouchY =\n e && 'changedTouches' in e && e.changedTouches[0]\n ? e.changedTouches[0].clientY\n : currentY;\n const swipeDistance = finalTouchY - touchStartYRef.current;\n const swipeVelocity =\n Math.abs(swipeDistance) / Math.max(swipeDuration, 1);\n const isAtMaxDetent = currentDetent === detents.length - 1;\n const isAtMinDetent = currentDetent === 0;\n const isSwipingDown = swipeDistance > 0;\n const isSwipingUp = swipeDistance < 0;\n\n if (isAtMaxDetent && isSwipingDown && enableSwipeToClose) {\n if (\n swipeDistance > 50 ||\n swipeVelocity > 0.3 ||\n currentPosition < maxDetentValue * 0.7\n ) {\n handleClose();\n return;\n }\n }\n\n if (isAtMinDetent && detents.length > 1) {\n const nextDetentIndex = Math.min(1, detents.length - 1);\n const nextDetentValue = detents[nextDetentIndex];\n const midpoint = (minDetentValue + nextDetentValue) / 2;\n\n const shouldGoToNextDetent =\n (isSwipingUp &&\n (Math.abs(swipeDistance) > 30 || swipeVelocity > 0.2)) ||\n currentPosition >= midpoint;\n\n if (shouldGoToNextDetent) {\n snapToDetent(nextDetentIndex);\n return;\n }\n }\n\n const shouldClose =\n enableSwipeToClose &&\n (currentPosition < minDetentValue * 0.5 ||\n (swipeDistance > 100 && swipeVelocity > 0.5) ||\n sheetHeight < viewportHeight * 0.15);\n\n if (shouldClose) {\n handleClose();\n return;\n }\n\n const nearestDetentIndex = findNearestDetent(sheetHeight);\n snapToDetent(nearestDetentIndex);\n },\n [\n isOpen,\n isDragging,\n isClosing,\n isScrolling,\n sheetHeight,\n currentY,\n currentDetent,\n detents,\n enableSwipeToClose,\n handleClose,\n findNearestDetent,\n snapToDetent,\n ],\n );\n\n const handleMouseDown = useCallback(\n (e: React.MouseEvent) => {\n if (!isOpen || isClosing) return;\n\n mouseStartTimeRef.current = Date.now();\n setStartY(e.clientY);\n setCurrentY(e.clientY);\n\n if (contentRef.current) {\n scrollTopRef.current = contentRef.current.scrollTop;\n setIsScrolling(false);\n canDragRef.current = scrollTopRef.current === 0;\n } else {\n canDragRef.current = true;\n }\n },\n [isOpen, isClosing],\n );\n\n const handleMouseMove = useCallback(\n (e: MouseEvent) => {\n if (!isOpen || !isDragging || isClosing) return;\n\n const viewportHeight = window.innerHeight;\n const baseHeight = viewportHeight * getDetentValue(currentDetent);\n const delta = startY - e.clientY;\n const maxDetentHeight = viewportHeight * Math.max(...detents);\n const minDetentHeight = viewportHeight * Math.min(...detents);\n const isAtMaxDetent = currentDetent === detents.length - 1;\n const isAtMinDetent = currentDetent === 0;\n\n let newHeight = baseHeight + delta;\n\n if (isAtMaxDetent) {\n newHeight = Math.max(0, Math.min(maxDetentHeight, newHeight));\n } else if (isAtMinDetent) {\n newHeight = Math.max(\n minDetentHeight,\n Math.min(maxDetentHeight, newHeight),\n );\n } else {\n newHeight = Math.max(\n minDetentHeight,\n Math.min(maxDetentHeight, newHeight),\n );\n }\n\n setSheetHeight(newHeight);\n setCurrentY(e.clientY);\n },\n [\n isOpen,\n isDragging,\n startY,\n currentDetent,\n detents,\n getDetentValue,\n isClosing,\n ],\n );\n\n const handleMouseUp = useCallback(() => {\n if (!isOpen || !isDragging || isClosing) return;\n\n setIsDragging(false);\n const viewportHeight = window.innerHeight;\n const currentPosition = sheetHeight / viewportHeight;\n const minDetentValue = Math.min(...detents);\n const maxDetentValue = Math.max(...detents);\n const swipeDuration = Date.now() - mouseStartTimeRef.current;\n const swipeDistance = currentY - startY;\n const swipeVelocity = Math.abs(swipeDistance) / Math.max(swipeDuration, 1);\n const isAtMaxDetent = currentDetent === detents.length - 1;\n const isAtMinDetent = currentDetent === 0;\n const isSwipingDown = swipeDistance > 0;\n const isSwipingUp = swipeDistance < 0;\n\n if (isAtMaxDetent && isSwipingDown && enableSwipeToClose) {\n if (\n swipeDistance > 50 ||\n swipeVelocity > 0.3 ||\n currentPosition < maxDetentValue * 0.7\n ) {\n handleClose();\n return;\n }\n }\n\n if (isAtMinDetent && detents.length > 1) {\n const nextDetentIndex = Math.min(1, detents.length - 1);\n const nextDetentValue = detents[nextDetentIndex];\n const midpoint = (minDetentValue + nextDetentValue) / 2;\n\n if (isSwipingUp && (swipeDistance < -30 || swipeVelocity > 0.2)) {\n snapToDetent(nextDetentIndex);\n return;\n }\n\n if (currentPosition >= midpoint) {\n snapToDetent(nextDetentIndex);\n return;\n }\n }\n\n const shouldClose =\n enableSwipeToClose &&\n (currentPosition < minDetentValue * 0.5 ||\n (swipeDistance > 100 && swipeVelocity > 0.5) ||\n sheetHeight < viewportHeight * 0.15);\n\n if (shouldClose) {\n handleClose();\n return;\n }\n\n const nearestDetentIndex = findNearestDetent(sheetHeight);\n snapToDetent(nearestDetentIndex);\n }, [\n isOpen,\n isDragging,\n isClosing,\n sheetHeight,\n currentY,\n startY,\n currentDetent,\n detents,\n enableSwipeToClose,\n handleClose,\n findNearestDetent,\n snapToDetent,\n ]);\n\n useEffect(() => {\n if (isDragging) {\n document.addEventListener('mousemove', handleMouseMove);\n document.addEventListener('mouseup', handleMouseUp);\n return () => {\n document.removeEventListener('mousemove', handleMouseMove);\n document.removeEventListener('mouseup', handleMouseUp);\n };\n }\n }, [isDragging, handleMouseMove, handleMouseUp]);\n\n const handleTouchEndNative = useCallback(\n (e: TouchEvent) => {\n handleTouchEnd(e);\n },\n [handleTouchEnd],\n );\n\n useEffect(() => {\n const sheetElement = sheetRef.current;\n if (!sheetElement || !isOpen) return;\n\n sheetElement.addEventListener('touchmove', handleTouchMoveNative, {\n passive: false,\n });\n sheetElement.addEventListener('touchend', handleTouchEndNative, {\n passive: false,\n });\n\n return () => {\n sheetElement.removeEventListener('touchmove', handleTouchMoveNative);\n sheetElement.removeEventListener('touchend', handleTouchEndNative);\n };\n }, [isOpen, handleTouchMoveNative, handleTouchEndNative]);\n\n useEffect(() => {\n if (isOpen) {\n document.body.style.overflow = 'hidden';\n } else {\n document.body.style.overflow = '';\n }\n\n return () => {\n document.body.style.overflow = '';\n if (closeTimeoutRef.current) {\n clearTimeout(closeTimeoutRef.current);\n closeTimeoutRef.current = null;\n }\n if (openTimeoutRef.current) {\n clearTimeout(openTimeoutRef.current);\n openTimeoutRef.current = null;\n }\n isClosingRef.current = false;\n };\n }, [isOpen]);\n\n if (!isOpen && !isClosing) return null;\n\n const maxHeightValue =\n typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight;\n const minHeightValue =\n typeof minHeight === 'number' ? `${minHeight}px` : minHeight;\n const currentHeight = `${sheetHeight}px`;\n\n const calculateBackdropOpacity = (): number => {\n if (!isOpen) return 0.5;\n const viewportHeight = window.innerHeight;\n const maxDetentHeight = viewportHeight * Math.max(...detents);\n if (maxDetentHeight === 0) return 0.5;\n const progress = Math.max(0, Math.min(1, sheetHeight / maxDetentHeight));\n return progress * 0.5;\n };\n\n const calculateBackdropBlur = (): number => {\n if (!isOpen) return 8;\n const viewportHeight = window.innerHeight;\n const maxDetentHeight = viewportHeight * Math.max(...detents);\n if (maxDetentHeight === 0) return 8;\n const progress = Math.max(0, Math.min(1, sheetHeight / maxDetentHeight));\n return progress * 8;\n };\n\n const backdropOpacity = calculateBackdropOpacity();\n const backdropBlur = calculateBackdropBlur();\n\n const backdropStyle: React.CSSProperties = {\n backgroundColor: `rgba(0, 0, 0, ${backdropOpacity})`,\n backdropFilter: `blur(${backdropBlur}px)`,\n WebkitBackdropFilter: `blur(${backdropBlur}px)`,\n ...(isDragging\n ? {}\n : {\n transition:\n 'background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1), backdrop-filter 0.3s cubic-bezier(0.4, 0, 0.2, 1)',\n }),\n };\n\n return (\n <div\n className={`bottom-sheet-backdrop ${backdropClassName} ${isClosing ? 'closing' : ''}`}\n onClick={enableBackdropClose ? handleClose : undefined}\n style={backdropStyle}\n >\n <div\n ref={sheetRef}\n className={`bottom-sheet ${className} ${isDragging ? 'dragging' : ''} ${isClosing ? 'closing' : ''}`}\n style={{\n height: currentHeight,\n maxHeight: maxHeightValue,\n minHeight: minHeightValue,\n }}\n onClick={(e) => e.stopPropagation()}\n onTouchStart={handleTouchStart}\n onMouseDown={handleMouseDown}\n >\n <div className=\"bottom-sheet-handle-container\">\n <div\n className={`bottom-sheet-handle ${handleClassName}`}\n onClick={(e) => {\n e.stopPropagation();\n handleClose();\n }}\n />\n </div>\n <div\n ref={contentRef}\n className={`bottom-sheet-content ${contentClassName} ${isScrolling ? 'scrolling' : ''}`}\n >\n {children}\n </div>\n </div>\n </div>\n );\n};\n\nexport default BottomSheet;\n","import styleInject from '#style-inject';styleInject(\".bottom-sheet-backdrop {\\n position: fixed;\\n top: 0;\\n left: 0;\\n right: 0;\\n bottom: 0;\\n background-color: rgba(0, 0, 0, 0.5);\\n backdrop-filter: blur(8px);\\n -webkit-backdrop-filter: blur(8px);\\n z-index: 1000;\\n display: flex;\\n align-items: flex-end;\\n justify-content: center;\\n animation: fadeIn 0.3s cubic-bezier(0.4, 0, 0.2, 1);\\n}\\n.bottom-sheet-backdrop.closing {\\n animation: fadeOut 0.3s cubic-bezier(0.4, 0, 0.2, 1);\\n}\\n@keyframes fadeIn {\\n from {\\n opacity: 0;\\n backdrop-filter: blur(0px);\\n -webkit-backdrop-filter: blur(0px);\\n }\\n to {\\n opacity: 1;\\n backdrop-filter: blur(8px);\\n -webkit-backdrop-filter: blur(8px);\\n }\\n}\\n@keyframes fadeOut {\\n from {\\n opacity: 1;\\n backdrop-filter: blur(8px);\\n -webkit-backdrop-filter: blur(8px);\\n }\\n to {\\n opacity: 0;\\n backdrop-filter: blur(0px);\\n -webkit-backdrop-filter: blur(0px);\\n }\\n}\\n.bottom-sheet {\\n position: relative;\\n width: 100%;\\n max-width: 100%;\\n background-color: #ffffff;\\n border-radius: 20px 20px 0 0;\\n box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.15);\\n display: flex;\\n flex-direction: column;\\n overflow: hidden;\\n touch-action: none;\\n transition: height 0.3s cubic-bezier(0.4, 0, 0.2, 1), transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);\\n will-change: height, transform;\\n transform: translateY(0);\\n}\\n.bottom-sheet.dragging {\\n transition: none;\\n}\\n.bottom-sheet.closing {\\n animation: slideDown 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards;\\n transition: height 0.3s cubic-bezier(0.4, 0, 0.2, 1);\\n}\\n@keyframes slideDown {\\n from {\\n transform: translateY(0);\\n }\\n to {\\n transform: translateY(100%);\\n }\\n}\\n.bottom-sheet-handle-container {\\n width: 100%;\\n padding: 12px 0 8px;\\n display: flex;\\n justify-content: center;\\n align-items: center;\\n flex-shrink: 0;\\n cursor: grab;\\n}\\n.bottom-sheet-handle-container:active {\\n cursor: grabbing;\\n}\\n.bottom-sheet-handle {\\n width: 80px;\\n height: 4px;\\n background-color: #d1d5db;\\n border-radius: 2px;\\n cursor: pointer;\\n}\\n.bottom-sheet-content {\\n flex: 1;\\n overflow-y: auto;\\n overflow-x: hidden;\\n -webkit-overflow-scrolling: touch;\\n overscroll-behavior: contain;\\n padding: 0;\\n min-height: 0;\\n}\\n.bottom-sheet-content.scrolling {\\n touch-action: pan-y;\\n}\\n@media (prefers-color-scheme: dark) {\\n .bottom-sheet {\\n background-color: #1c2633;\\n box-shadow: 0 -4px 24px rgba(0, 0, 0, 0.4);\\n }\\n .bottom-sheet-handle {\\n background-color: #4b5563;\\n }\\n}\\n\")","import styleInject from '#style-inject';styleInject(\":where(.pm-content, .pm-desktop-modal) {\\n color: #000000;\\n --pm-bg: #ffffff;\\n --pm-text: #000000;\\n --pm-text-secondary: #666666;\\n --pm-text-muted: #8c8c8c;\\n --pm-border: rgba(0, 0, 0, 0.1);\\n --pm-order-bg: #e9f5fa;\\n --pm-order-border: rgba(0, 100, 153, 0.03);\\n --pm-order-text: #004062;\\n --pm-hover-bg: #f9f9f9;\\n --pm-iframe-bg: #f9f9f9;\\n --pm-spinner-track: #e5e7eb;\\n --pm-actions-border: hsl(0, 0%, 92%);\\n}\\n.pm-content {\\n display: flex;\\n flex-direction: column;\\n align-items: center;\\n width: 100%;\\n font-family: \\\"Inter\\\", sans-serif;\\n}\\n@media (prefers-color-scheme: dark) {\\n :where(.pm-content, .pm-desktop-modal) {\\n color: #ffffff;\\n --pm-bg: #1f2937;\\n --pm-text: #ffffff;\\n --pm-text-secondary: #9ca3af;\\n --pm-text-muted: #6b7280;\\n --pm-border: rgba(255, 255, 255, 0.1);\\n --pm-order-bg: rgba(255, 255, 255, 0.05);\\n --pm-order-border: rgba(255, 255, 255, 0.1);\\n --pm-order-text: #ffffff;\\n --pm-hover-bg: rgba(255, 255, 255, 0.05);\\n --pm-iframe-bg: #111827;\\n --pm-spinner-track: #374151;\\n --pm-actions-border: rgba(255, 255, 255, 0.1);\\n }\\n}\\n.pm-header {\\n display: flex;\\n flex-direction: row;\\n justify-content: space-between;\\n align-items: center;\\n width: 100%;\\n padding: 16px;\\n box-sizing: border-box;\\n position: relative;\\n}\\n.bottom-sheet .pm-header {\\n padding: 0 16px;\\n}\\n.pm-header-left,\\n.pm-header-right {\\n display: flex;\\n align-items: center;\\n gap: 8px;\\n min-width: 40px;\\n}\\n.pm-header-right {\\n justify-content: flex-end;\\n}\\n.pm-title {\\n font-weight: 600;\\n font-size: 18px;\\n line-height: 22px;\\n text-align: center;\\n flex: 1;\\n}\\n.pm-close-btn,\\n.pm-back-btn {\\n background: none;\\n border: none;\\n cursor: pointer;\\n padding: 4px;\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n color: var(--pm-text, #000000);\\n opacity: 0.6;\\n}\\n.pm-close-btn:hover,\\n.pm-back-btn:hover {\\n opacity: 1;\\n}\\n.pm-body-main {\\n display: flex;\\n flex-direction: column;\\n align-items: center;\\n width: 100%;\\n padding: 0;\\n gap: 0;\\n box-sizing: border-box;\\n}\\n.pm-main-container {\\n display: flex;\\n flex-direction: column;\\n gap: 8px;\\n width: 100%;\\n padding: 0 16px;\\n}\\n.pm-main-title {\\n font-size: 18px;\\n font-weight: 600;\\n line-height: 20px;\\n text-align: center;\\n margin: 0 0 8px;\\n color: var(--pm-text, #000000);\\n}\\n.pm-amount-container {\\n display: flex;\\n flex-direction: column;\\n align-items: center;\\n gap: 4px;\\n margin-bottom: 16px;\\n}\\n.pm-amount-label {\\n font-weight: 500;\\n font-size: 12px;\\n line-height: 15px;\\n color: var(--pm-text-muted, #8c8c8c);\\n}\\n.pm-amount-value {\\n display: flex;\\n align-items: center;\\n gap: 8px;\\n font-weight: 600;\\n font-size: 24px;\\n line-height: 29px;\\n color: var(--pm-text, #000000);\\n}\\n.pm-order-info {\\n display: flex;\\n flex-direction: row;\\n justify-content: center;\\n align-items: center;\\n width: 100%;\\n height: 36px;\\n padding: 8px 16px;\\n margin-bottom: 24px;\\n gap: 4px;\\n background: var(--pm-order-bg, #e9f5fa);\\n border-radius: 9px;\\n box-sizing: border-box;\\n border: 1px solid var(--pm-order-border, rgba(0, 100, 153, 0.03));\\n}\\n.pm-order-text {\\n font-family: \\\"Inter\\\", sans-serif;\\n font-style: normal;\\n font-weight: 400;\\n font-size: 12px;\\n line-height: 15px;\\n text-align: center;\\n color: var(--pm-order-text, #004062);\\n flex: none;\\n}\\n.pm-actions-card {\\n width: 100%;\\n background: #f9f9f9;\\n border-top: 1px solid var(--pm-actions-border, hsl(0, 0%, 92%));\\n padding: 16px;\\n box-sizing: border-box;\\n}\\n@media (prefers-color-scheme: dark) {\\n .pm-actions-card {\\n background: var(--pm-hover-bg, rgba(255, 255, 255, 0.05));\\n }\\n}\\n.pm-actions {\\n display: flex;\\n flex-direction: column;\\n gap: 8px;\\n width: 100%;\\n}\\n.pm-btn {\\n display: flex;\\n flex-direction: row;\\n justify-content: center;\\n align-items: center;\\n padding: 13px 10px 10px;\\n gap: 4px;\\n width: 100%;\\n height: 44px;\\n border-radius: 8px;\\n border: none;\\n cursor: pointer;\\n font-weight: 500;\\n font-size: 14px;\\n line-height: 19px;\\n transition: opacity 0.2s;\\n}\\n.pm-btn:hover {\\n opacity: 0.9;\\n}\\n.pm-btn-primary {\\n background: #0098ea;\\n color: #ffffff;\\n}\\n.pm-btn-black {\\n background: #000000;\\n color: #ffffff;\\n}\\n.pm-btn.processing {\\n background: #e6e6e6;\\n color: #7a7a7a;\\n cursor: default;\\n}\\n.pm-btn.processing:hover {\\n opacity: 1;\\n}\\n.pm-footer {\\n display: flex;\\n flex-direction: row;\\n justify-content: center;\\n align-items: center;\\n gap: 8px;\\n margin-top: 12px;\\n font-size: 12px;\\n line-height: 15px;\\n}\\n.pm-footer-text {\\n color: var(--pm-text-muted, #808080);\\n}\\n.pm-footer-link {\\n color: #0098ea;\\n cursor: pointer;\\n text-decoration: none;\\n}\\n.pm-footer-link:hover {\\n text-decoration: underline;\\n}\\n.pm-desktop-overlay {\\n position: fixed;\\n top: 0;\\n left: 0;\\n right: 0;\\n bottom: 0;\\n background: rgba(0, 0, 0, 0.44);\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n z-index: 1000;\\n animation: fadeIn 0.2s ease-out;\\n}\\n.pm-desktop-modal {\\n width: 100%;\\n max-width: 414px;\\n margin: 16px;\\n background: var(--pm-bg, #ffffff);\\n border-radius: 32px;\\n box-shadow: 0px 4px 24px rgba(0, 0, 0, 0.25);\\n overflow: hidden;\\n position: relative;\\n animation: scaleIn 0.2s ease-out;\\n}\\n@media (prefers-color-scheme: dark) {\\n .pm-desktop-modal {\\n box-shadow: 0px 4px 32px rgba(0, 0, 0, 0.5);\\n }\\n}\\n@keyframes scaleIn {\\n from {\\n transform: scale(0.95);\\n opacity: 0;\\n }\\n to {\\n transform: scale(1);\\n opacity: 1;\\n }\\n}\\n.pm-iframe-container {\\n width: 100%;\\n height: 100%;\\n min-height: 400px;\\n display: flex;\\n flex-direction: column;\\n background: var(--pm-iframe-bg, #f9f9f9);\\n position: relative;\\n}\\n.pm-iframe-container iframe {\\n flex: 1;\\n width: 100%;\\n height: 100%;\\n border: none;\\n display: block;\\n}\\n.pm-menu-dropdown {\\n position: absolute;\\n top: 100%;\\n right: 0;\\n background: var(--pm-bg, #ffffff);\\n border: 1px solid var(--pm-border, rgba(0, 0, 0, 0.1));\\n border-radius: 8px;\\n box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.15);\\n z-index: 1001;\\n min-width: 150px;\\n overflow: hidden;\\n}\\n@media (prefers-color-scheme: dark) {\\n .pm-menu-dropdown {\\n box-shadow: 0px 4px 16px rgba(0, 0, 0, 0.4);\\n }\\n}\\n.pm-menu-item {\\n padding: 10px 16px;\\n font-size: 14px;\\n cursor: pointer;\\n color: var(--pm-text, #000000);\\n}\\n.pm-menu-item:hover {\\n background: var(--pm-hover-bg, #f9f9f9);\\n}\\n.pm-menu-item.danger {\\n color: #e74c3c;\\n}\\n.pm-menu-item.disabled {\\n cursor: default;\\n color: var(--pm-text-muted, #8c8c8c);\\n}\\n.pm-menu-item.disabled:hover {\\n background: transparent;\\n}\\n.icon-moonpay {\\n background: #7d00ff;\\n color: white;\\n}\\n.icon-onramper {\\n background: #000000;\\n color: white;\\n}\\n.icon-transak {\\n background:\\n linear-gradient(\\n 120deg,\\n #348bed 22.91%,\\n #2b80e8 36.09%,\\n #1461db 60.25%,\\n #0e57d7 66.11%);\\n color: white;\\n}\\n.icon-mercurio {\\n background: #000000;\\n color: white;\\n}\\n.pm-success-container {\\n display: flex;\\n flex-direction: column;\\n align-items: center;\\n justify-content: center;\\n padding: 60px 24px;\\n text-align: center;\\n}\\n.pm-success-icon {\\n width: 64px;\\n height: 64px;\\n background: #0098ea;\\n border-radius: 50%;\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n margin-bottom: 20px;\\n color: white;\\n}\\n.pm-success-icon svg {\\n width: 32px;\\n height: 32px;\\n}\\n.pm-success-title {\\n font-size: 24px;\\n font-weight: 600;\\n color: var(--pm-text, #000000);\\n margin: 0 0 8px;\\n}\\n.pm-success-text {\\n font-size: 16px;\\n color: var(--pm-text-secondary, #666666);\\n margin: 0;\\n}\\n.pm-error-container {\\n display: flex;\\n flex-direction: column;\\n align-items: center;\\n justify-content: center;\\n padding: 60px 24px;\\n text-align: center;\\n}\\n.pm-error-icon {\\n width: 64px;\\n height: 64px;\\n background: #e74c3c;\\n border-radius: 50%;\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n margin-bottom: 20px;\\n color: white;\\n}\\n.pm-error-icon svg {\\n width: 32px;\\n height: 32px;\\n}\\n.pm-error-title {\\n font-size: 24px;\\n font-weight: 600;\\n color: var(--pm-text, #000000);\\n margin: 0 0 8px;\\n}\\n.pm-error-text {\\n font-size: 16px;\\n color: var(--pm-text-secondary, #666666);\\n margin: 0 0 24px;\\n max-width: 300px;\\n}\\n.pm-error-actions {\\n display: flex;\\n flex-direction: column;\\n gap: 12px;\\n width: 100%;\\n max-width: 280px;\\n}\\n.pm-error-inline {\\n display: flex;\\n flex-direction: column;\\n align-items: center;\\n justify-content: center;\\n height: 100%;\\n padding: 40px 20px;\\n text-align: center;\\n color: #e74c3c;\\n}\\n.pm-error-inline svg {\\n width: 48px;\\n height: 48px;\\n margin-bottom: 16px;\\n}\\n.pm-error-inline p {\\n margin: 0 0 20px;\\n color: var(--pm-text-secondary, #666666);\\n font-size: 14px;\\n}\\n.pm-btn-outline {\\n background: transparent;\\n color: #0098ea;\\n border: 1px solid #0098ea;\\n}\\n.pm-btn-outline:hover {\\n background: rgba(0, 152, 234, 0.08);\\n}\\n.pm-loading-container {\\n display: flex;\\n flex-direction: column;\\n align-items: center;\\n justify-content: center;\\n height: 100%;\\n color: var(--pm-text-muted, #6b7280);\\n}\\n.pm-loading-container p {\\n margin: 16px 0 0;\\n font-size: 14px;\\n}\\n.pm-loading-overlay {\\n position: absolute;\\n top: 0;\\n left: 0;\\n right: 0;\\n bottom: 0;\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n background: var(--pm-iframe-bg, #f9f9f9);\\n z-index: 1;\\n}\\n.pm-spinner {\\n width: 32px;\\n height: 32px;\\n border: 3px solid var(--pm-spinner-track, #e5e7eb);\\n border-top-color: #0098ea;\\n border-radius: 50%;\\n animation: pm-spin 0.8s linear infinite;\\n}\\n.pm-btn-spinner {\\n width: 16px;\\n height: 16px;\\n border: 2px solid rgba(122, 122, 122, 0.3);\\n border-top-color: #7a7a7a;\\n border-radius: 50%;\\n animation: pm-spin 0.8s linear infinite;\\n}\\n@keyframes pm-spin {\\n to {\\n transform: rotate(360deg);\\n }\\n}\\n.pm-retry-link {\\n font-family: \\\"Inter\\\", sans-serif;\\n font-style: normal;\\n font-weight: 400;\\n font-size: 12px;\\n line-height: 15px;\\n text-align: center;\\n color: #004062;\\n margin-top: 0px;\\n}\\n.pm-retry-action {\\n cursor: pointer;\\n text-decoration: underline;\\n color: #0098ea;\\n}\\n@media (prefers-color-scheme: dark) {\\n .pm-header-left span {\\n color: #ffffff !important;\\n }\\n .pm-close-btn svg rect {\\n fill: rgba(255, 255, 255, 0.1) !important;\\n }\\n .pm-close-btn svg path {\\n stroke: #ffffff !important;\\n }\\n .pm-retry-link {\\n color: var(--pm-text-muted, #8c8c8c) !important;\\n }\\n .pm-btn.processing {\\n background: rgba(255, 255, 255, 0.1) !important;\\n color: var(--pm-text-muted, #8c8c8c) !important;\\n }\\n}\\n\")","import * as React from 'react';\nimport { createMoonpayTransfer, checkMoonpayAvailability } from '@ton-pay/api';\nimport type {\n CreateMoonpayTransferParams,\n MoonpayGeoResult,\n MoonpayAmountLimits,\n} from '@ton-pay/api';\nimport type { UseMoonPayIframeOptions } from '../types';\n\nexport function useMoonPayIframe({\n apiKey,\n chain = 'mainnet',\n}: UseMoonPayIframeOptions) {\n const [loading, setLoading] = React.useState(false);\n const [error, setError] = React.useState<string | null>(null);\n const [link, setLink] = React.useState<string | null>(null);\n const [geoResult, setGeoResult] = React.useState<MoonpayGeoResult | null>(\n null,\n );\n const [limits, setLimits] = React.useState<MoonpayAmountLimits | null>(null);\n\n const checkAvailability = React.useCallback(\n async (\n amount: number,\n asset: string,\n ipAddress: string,\n ): Promise<boolean> => {\n if (!apiKey) return false;\n\n setLoading(true);\n setError(null);\n\n try {\n const availability = await checkMoonpayAvailability(\n { asset, ipAddress },\n { apiKey, chain },\n );\n setGeoResult(availability.geo);\n setLimits(availability.limits);\n\n if (!availability.geo.isBuyAllowed) {\n return false;\n }\n\n const limitsData = availability.limits;\n\n if (!limitsData?.quoteCurrency) {\n return false;\n }\n\n const { minBuyAmount, maxBuyAmount } = limitsData.quoteCurrency;\n\n if (amount < minBuyAmount || amount > maxBuyAmount) {\n return false;\n }\n\n return true;\n } catch {\n return false;\n } finally {\n setLoading(false);\n }\n },\n [apiKey, chain],\n );\n\n const fetchOnRampLink = React.useCallback(\n async (params: CreateMoonpayTransferParams): Promise<string> => {\n if (!apiKey) throw new Error('API Key is required');\n\n setLoading(true);\n setError(null);\n\n try {\n const response = await createMoonpayTransfer(params, { apiKey, chain });\n setLink(response.link);\n return response.link;\n } catch (e) {\n const msg =\n e instanceof Error ? e.message : 'Failed to generate OnRamp link';\n setError(msg);\n throw new Error(msg);\n } finally {\n setLoading(false);\n }\n },\n [apiKey, chain],\n );\n\n return {\n loading,\n error,\n link,\n fetchOnRampLink,\n checkAvailability,\n geoResult,\n limits,\n };\n}\n","export const BASE_URL = 'https://pay.ton.org';\nexport const TESTNET_BASE_URL = 'https://testnet.pay.ton.org';\n","import type { Chain } from '../types/chain';\nimport { BASE_URL, TESTNET_BASE_URL } from './const';\n\nexport const getBaseUrl = (chain?: Chain) => {\n // only for testing. do not use in production. do not use in docs\n // @ts-ignore\n if (\n typeof process !== 'undefined' &&\n process.env &&\n process.env.TONPAY_BASE_URL\n ) {\n // @ts-ignore\n return process.env.TONPAY_BASE_URL;\n }\n if (!chain || chain === 'mainnet') {\n return BASE_URL;\n }\n return TESTNET_BASE_URL;\n};\n","import { getBaseUrl } from '../common/get-base-url';\nimport type { APIOptions } from '../types/api-options';\nimport type {\n CreateMoonpayTransferParams,\n CreateMoonpayTransferResponse,\n} from '../types/create-moonpay-transfer';\n\n/**\n * Creates a MoonPay payment link for buying crypto\n * @param params - the parameters for the MoonPay transfer\n * @param options - the options for the transfer (requires API key)\n * @returns the payment link, geo restrictions, and amount limits\n */\nexport const createMoonpayTransfer = async (\n params: CreateMoonpayTransferParams,\n options: APIOptions,\n): Promise<CreateMoonpayTransferResponse> => {\n if (!options?.apiKey) {\n throw new Error('API key is required for MoonPay transfers');\n }\n\n const baseUrl = getBaseUrl(options.chain);\n const headers = {\n 'Content-Type': 'application/json',\n 'x-api-key': options.apiKey,\n };\n\n const response = await fetch(\n `${baseUrl}/api/merchant/v1/create-moonpay-transfer`,\n {\n method: 'POST',\n body: JSON.stringify(params),\n headers,\n },\n );\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to create MoonPay transfer: ${errorText}`, {\n cause: response.statusText,\n });\n }\n\n const data = await response.json();\n\n if (data.link && data.link.startsWith('/')) {\n data.link = `${baseUrl}${data.link}`;\n }\n\n return data;\n};\n","import { getBaseUrl } from '../common/get-base-url';\nimport type { APIOptions } from '../types/api-options';\nimport type {\n CheckMoonpayAvailabilityParams,\n CheckMoonpayAvailabilityResponse,\n} from '../types/check-moonpay-availability';\n\nexport const checkMoonpayAvailability = async (\n params: CheckMoonpayAvailabilityParams,\n options?: APIOptions,\n): Promise<CheckMoonpayAvailabilityResponse> => {\n const baseUrl = getBaseUrl(options?.chain);\n const headers = {\n 'Content-Type': 'application/json',\n ...(options?.apiKey ? { 'x-api-key': options.apiKey } : {}),\n };\n\n const response = await fetch(`${baseUrl}/api/external/moonpay/check`, {\n method: 'POST',\n body: JSON.stringify(params),\n headers,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to check MoonPay availability: ${errorText}`, {\n cause: response.statusText,\n });\n }\n\n return response.json();\n};\n","import * as CryptoJS from 'crypto-js';\n\n/**\n * Verifies the HMAC-SHA256 signature of a payload\n * @param payload - Raw JSON string or object to verify\n * @param signature - The signature from X-TON Pay-Signature header\n * @param apiSecret - Your TON Pay webhook API secret\n * @returns true if signature is valid, false otherwise\n *\n * @example\n * ```typescript\n * import { verifySignature } from \"@ton-pay/api\";\n *\n * // With raw string\n * app.post(\"/webhook\", (req, res) => {\n * const signature = req.headers[\"x-tonpay-signature\"] as string;\n * const payload = JSON.stringify(req.body);\n *\n * if (!verifySignature(payload, signature, YOUR_API_SECRET)) {\n * return res.status(401).json({ error: \"Invalid signature\" });\n * }\n *\n * res.status(200).json({ received: true });\n * });\n *\n * // With object (will be stringified automatically)\n * app.post(\"/webhook\", (req, res) => {\n * const signature = req.headers[\"x-tonpay-signature\"] as string;\n *\n * if (!verifySignature(req.body, signature, YOUR_API_SECRET)) {\n * return res.status(401).json({ error: \"Invalid signature\" });\n * }\n *\n * res.status(200).json({ received: true });\n * });\n * ```\n */\nexport function verifySignature(\n payload: string | object,\n signature: string,\n apiSecret: string,\n): boolean {\n const payloadString =\n typeof payload === 'string' ? payload : JSON.stringify(payload);\n\n const hmac = CryptoJS.HmacSHA256(payloadString, apiSecret);\n const expectedSignature = `sha256=${hmac.toString(CryptoJS.enc.Hex)}`;\n\n return signature === expectedSignature;\n}\n","import type { TonPayButtonPresetConfig, TonPayPreset } from '../../types';\n\nexport const PRESETS: Record<TonPayPreset, TonPayButtonPresetConfig> = {\n default: {\n bgColor: '#0098EA',\n textColor: '#FFFFFF',\n },\n gradient: {\n bgColor: 'linear-gradient(91.69deg, #2A82EB 8.9%, #0355CF 158.29%)',\n textColor: '#FFFFFF',\n },\n};\n\nexport const buttonStyles = `\n@keyframes tp-pulse{0%{opacity:1;transform:scale(1)}50%{opacity:.5;transform:scale(1.02)}100%{opacity:1;transform:scale(1)}}\n@keyframes tp-fade-in{from{opacity:0;transform:translateY(-4px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}\n@keyframes tp-spin{to{transform:rotate(360deg)}}\n\n.tp-wrap{display:inline-flex;flex-direction:column;position:relative;width:var(--tp-width,300px);max-width:100%;--tp-menu-bg:#ffffff;--tp-menu-text:#111827;--tp-menu-muted:#6b7280;--tp-menu-hover:rgba(0,0,0,.06);--tp-menu-border:rgba(0,0,0,.08);--tp-menu-shadow:0 8px 24px rgba(0,0,0,.12)}\n@media(prefers-color-scheme:dark){.tp-wrap{--tp-menu-bg:#1C2633;--tp-menu-text:#F9FAFB;--tp-menu-muted:#9CA3AF;--tp-menu-hover:rgba(255,255,255,.08);--tp-menu-border:rgba(255,255,255,.1);--tp-menu-shadow:0 8px 32px rgba(0,0,0,.4)}}\n.tp-btn-container{display:flex;flex-direction:row;width:100%}\n.tp-btn{display:flex;flex-direction:column;justify-content:center;align-items:center;padding:13px 10px;gap:10px;flex:1;min-height:var(--tp-height,44px);background:var(--tp-bg,#0098EA);color:var(--tp-text,#fff);border:none;border-radius:var(--tp-radius,8px) 0 0 var(--tp-radius,8px);cursor:pointer;transition:filter .12s ease,transform .12s ease;font-family:var(--tp-font,inherit);font-style:normal;font-weight:500;font-size:20px;line-height:25px;text-align:center;position:relative}\n.tp-btn.with-menu{padding-left:calc(10px + (var(--tp-height,44px))/2)}\n.tp-btn.no-menu{border-radius:var(--tp-radius,8px)}\n.tp-btn-content{display:flex;flex-direction:row;align-items:center;padding:0;gap:5px;white-space:nowrap;margin-top:-4px}\n.tp-btn:hover:not(:disabled){filter:brightness(0.92)}\n.tp-btn:active:not(:disabled){filter:brightness(0.85);transform:translateY(1px)}\n.tp-btn:disabled{cursor:not-allowed;opacity:.85}\n.tp-btn.loading{animation:none}\n.tp-btn.processing{background:#E6E6E6;color:#7A7A7A;cursor:default;opacity:1}\n.tp-btn.processing .tp-spinner{border-color:rgba(122,122,122,0.3);border-top-color:#7A7A7A}\n\n.tp-retry-text{font-weight:400;font-size:12px;line-height:15px;text-align:center;color:#004062;margin-top:8px}\n.tp-retry-link{cursor:pointer;text-decoration:underline;color:#0098EA}\n\n.tp-arrow{display:flex;align-items:center;justify-content:center;padding:13px 10px;min-width:calc(var(--tp-height,44px));min-height:var(--tp-height,44px);background:var(--tp-bg,#0098EA);color:var(--tp-text,#fff);border:none;border-left:1px solid rgba(255,255,255,.2);border-radius:0 var(--tp-radius,8px) var(--tp-radius,8px) 0;cursor:pointer;transition:filter .12s ease,transform .12s ease;font-size:14px}\n.tp-arrow:hover:not(:disabled){filter:brightness(0.92)}\n.tp-arrow:active:not(:disabled){filter:brightness(0.85);transform:translateY(1px)}\n.tp-arrow:disabled{cursor:not-allowed;opacity:.85;transition:none;filter:none;transform:none}\n\n.tp-menu{position:absolute;right:0;top:calc(100% + 8px);width:256px;background:var(--tp-menu-bg);color:var(--tp-menu-text);border:1px solid var(--tp-menu-border);border-radius:var(--tp-menu-radius,16px);padding:8px;box-shadow:var(--tp-menu-shadow);z-index:1000;animation:tp-fade-in .15s ease}\n.tp-menu-arrow{position:absolute;top:-8px;right:20px;width:0;height:0;border-style:solid;border-width:0 8px 8px 8px;border-color:transparent transparent var(--tp-menu-bg) transparent;filter:drop-shadow(0 -1px 1px rgba(0,0,0,.08))}\n.tp-menu-address{padding:.5rem .75rem;font-size:.85rem;color:var(--tp-menu-muted);cursor:default;user-select:text}\n.tp-menu-item{display:flex;align-items:center;gap:8px;width:100%;height:40px;padding-left:12px;padding-right:12px;border:none;background:transparent;text-align:left;cursor:pointer;font-size:15px;font-weight:590;color:var(--tp-menu-text);transition:background-color .15s ease,transform .1s ease-in-out;border-radius:8px;margin:2px}\n.tp-menu-item:hover:not(:disabled){background:var(--tp-menu-hover)}\n.tp-menu-item:active{transform:scale(0.96)}\n.tp-menu-item.danger{color:#e74c3c}\n.tp-menu-item.danger:hover:not(:disabled){background:rgba(231,76,60,.12);color:#c0392b}\n.tp-menu-item:disabled{cursor:default;opacity:1;color:var(--tp-menu-muted)}\n.tp-menu-item:disabled:hover{background:transparent}\n.tp-menu-icon{width:24px;height:24px;display:flex;align-items:center;justify-content:center;color:currentColor}\n.tp-menu-item:disabled .tp-menu-icon{opacity:.5}\n\n.tp-spinner{border:2px solid rgba(255,255,255,.35);border-top-color:var(--tp-text,#fff);border-radius:50%;width:18px;height:18px;animation:tp-spin .6s linear infinite}\n`;\n\nlet stylesInjected = false;\n\nexport function injectStyles(): void {\n if (typeof document === 'undefined' || stylesInjected) return;\n const style = document.createElement('style');\n style.id = 'tonpay-button-styles';\n style.textContent = buttonStyles;\n document.head.appendChild(style);\n stylesInjected = true;\n}\n","import * as React from 'react';\nimport {\n useTonAddress,\n useTonConnectModal,\n useTonConnectUI,\n} from '@tonconnect/ui-react';\nimport type { GetMessageFn, PayInfo } from '../types';\n\nconst WALLET_CONNECTION_TIMEOUT = 5 * 60 * 1000;\nconst TX_VALID_DURATION = 5 * 60;\n\nexport function useTonPay() {\n const address = useTonAddress(true);\n const modal = useTonConnectModal();\n const [tonConnectUI] = useTonConnectUI();\n\n const waitForWalletConnection = React.useCallback((): Promise<string> => {\n return new Promise((resolve, reject) => {\n if (address) {\n resolve(address);\n return;\n }\n\n modal.open();\n\n const unsubscribe = tonConnectUI.onStatusChange((wallet) => {\n if (wallet?.account) {\n unsubscribe();\n unsubscribeModal();\n resolve(wallet.account.address);\n }\n });\n\n const unsubscribeModal = tonConnectUI.onModalStateChange((state) => {\n if (state.status === 'closed') {\n unsubscribe();\n unsubscribeModal();\n reject(new Error('Wallet connection modal closed'));\n }\n });\n\n setTimeout(() => {\n unsubscribe();\n unsubscribeModal();\n reject(new Error('Wallet connection timeout'));\n }, WALLET_CONNECTION_TIMEOUT);\n });\n }, [address, modal, tonConnectUI]);\n\n const pay = React.useCallback(\n async <T extends object = object>(\n getMessage: GetMessageFn<T>,\n options?: { onRequestSent?: (redirectToWallet: () => void) => void },\n ): Promise<PayInfo<T>> => {\n const walletAddress = await waitForWalletConnection();\n const validUntil = Math.floor(Date.now() / 1e3) + TX_VALID_DURATION;\n const messageResult = await getMessage(walletAddress);\n\n const txResult = await tonConnectUI.sendTransaction(\n {\n messages: [messageResult.message],\n validUntil,\n from: walletAddress,\n },\n {\n onRequestSent: options?.onRequestSent,\n },\n );\n\n return { ...messageResult, txResult };\n },\n [waitForWalletConnection, tonConnectUI],\n );\n\n return { pay, address };\n}\n"]}
1
+ {"version":3,"sources":["/Users/ilya/work/tonpay/ton-pay/packages/ui-react/dist/index.js","../src/components/ton-pay-button/TonPayButton.tsx","../src/utils/index.ts","../src/components/icons/index.tsx","../src/components/notification/Notification.tsx","../src/components/notification/ErrorTransactionNotification.tsx","../src/components/payment-modal/PaymentModal.tsx","../src/components/bottom-sheet/BottomSheet.tsx","../src/components/bottom-sheet/BottomSheet.css","../src/components/payment-modal/PaymentModal.css","../src/hooks/useMoonPayIframe.ts","../../api/src/common/const.ts","../../api/src/common/get-base-url.ts","../../api/src/create-moonpay-transfer/create-moonpay-transfer.ts","../../api/src/check-moonpay-availability/check-moonpay-availability.ts","../../api/src/utils/verify-signature.ts","../src/components/ton-pay-button/styles.ts","../src/hooks/useTonPay.ts"],"names":["jsxs","jsx","useState","useCallback","useTonAddress","useTonConnectUI"],"mappings":"AAAA;ACAA,0HAAuB;AACvB;AACA,+CAA+C;ADE/C;AACA;AEKO,SAAS,SAAA,CAAU,KAAA,EAA6C;AACrE,EAAA,GAAA,CAAI,MAAA,IAAU,KAAA,CAAA,EAAW,OAAO,KAAA,CAAA;AAChC,EAAA,OAAO,OAAO,MAAA,IAAU,SAAA,EAAW,CAAA,EAAA;AACrC;AAYU;AAC4B,EAAA;AACtC;AAEmD;AAC7C,EAAA;AAC2B,IAAA;AACI,IAAA;AACrB,IAAA;AACE,EAAA;AACA,IAAA;AACP,IAAA;AACT,EAAA;AACF;AFfsC;AACA;AGxBf;AA4BnB;AAf4C;AACvC,EAAA;AACG,EAAA;AACE,EAAA;AACZ,EAAA;AAEA;AAAC,EAAA;AAAA,EAAA;AACQ,IAAA;AACC,IAAA;AACA,IAAA;AACH,IAAA;AACC,IAAA;AACN,IAAA;AACW,IAAA;AAEX,IAAA;AAAG,sBAAA;AACD,wBAAA;AAAC,UAAA;AAAA,UAAA;AACG,YAAA;AACI,YAAA;AAAA,UAAA;AACR,QAAA;AACA,wBAAA;AAAC,UAAA;AAAA,UAAA;AACG,YAAA;AACI,YAAA;AAAA,UAAA;AACR,QAAA;AACF,MAAA;AAEE,sBAAA;AAGF,IAAA;AAAA,EAAA;AACF;AAG4D;AAC3D,EAAA;AAAA,EAAA;AACQ,IAAA;AACC,IAAA;AACA,IAAA;AACH,IAAA;AACC,IAAA;AACN,IAAA;AACW,IAAA;AAEX,IAAA;AAAA,sBAAA;AAAC,QAAA;AAAA,QAAA;AACG,UAAA;AACG,UAAA;AAAA,QAAA;AACP,MAAA;AACA,sBAAA;AAAC,QAAA;AAAA,QAAA;AACG,UAAA;AACG,UAAA;AAAA,QAAA;AACP,MAAA;AAAA,IAAA;AAAA,EAAA;AACF;AA0BkD;AAC3C,EAAA;AACC,EAAA;AACR,EAAA;AAEA;AAAC,EAAA;AAAA,EAAA;AACQ,IAAA;AACC,IAAA;AACA,IAAA;AACH,IAAA;AACC,IAAA;AACN,IAAA;AACW,IAAA;AAEX,IAAA;AAAA,sBAAA;AAAC,QAAA;AAAA,QAAA;AACG,UAAA;AACI,UAAA;AAAA,QAAA;AACR,MAAA;AACA,sBAAA;AAAC,QAAA;AAAA,QAAA;AACG,UAAA;AACI,UAAA;AAAA,QAAA;AACR,MAAA;AAAA,IAAA;AAAA,EAAA;AACF;AAG6C;AACtC,EAAA;AACC,EAAA;AACR,EAAA;AAEA;AAAC,EAAA;AAAA,EAAA;AACQ,IAAA;AACC,IAAA;AACA,IAAA;AACH,IAAA;AACC,IAAA;AACN,IAAA;AACW,IAAA;AAEX,IAAA;AAAM,sBAAA;AACN,sBAAA;AAAC,QAAA;AAAA,QAAA;AACG,UAAA;AACK,UAAA;AACK,UAAA;AACE,UAAA;AAAA,QAAA;AAChB,MAAA;AACA,sBAAA;AAAC,QAAA;AAAA,QAAA;AACG,UAAA;AACK,UAAA;AACK,UAAA;AACE,UAAA;AAAA,QAAA;AAChB,MAAA;AAAA,IAAA;AAAA,EAAA;AACF;AAG4C;AACrC,EAAA;AACC,EAAA;AACR,EAAA;AAEA;AAAC,EAAA;AAAA,EAAA;AACQ,IAAA;AACC,IAAA;AACA,IAAA;AACH,IAAA;AACC,IAAA;AACN,IAAA;AACW,IAAA;AAEX,IAAA;AAAA,sBAAA;AAAC,QAAA;AAAA,QAAA;AACG,UAAA;AACM,UAAA;AACI,UAAA;AACE,UAAA;AACC,UAAA;AAAA,QAAA;AACjB,MAAA;AACA,sBAAA;AAAC,QAAA;AAAA,QAAA;AACG,UAAA;AACM,UAAA;AACI,UAAA;AACE,UAAA;AACC,UAAA;AAAA,QAAA;AACjB,MAAA;AAAA,IAAA;AAAA,EAAA;AACF;AAG6C;AACtC,EAAA;AACC,EAAA;AACR,EAAA;AAEA;AAAC,EAAA;AAAA,EAAA;AACQ,IAAA;AACC,IAAA;AACA,IAAA;AACH,IAAA;AACC,IAAA;AACN,IAAA;AACW,IAAA;AAEX,IAAA;AAAC,MAAA;AAAA,MAAA;AACG,QAAA;AACM,QAAA;AACI,QAAA;AACE,QAAA;AACC,QAAA;AAAA,MAAA;AACjB,IAAA;AAAA,EAAA;AACF;AAG6C;AACtC,EAAA;AACC,EAAA;AACR,EAAA;AAEA;AAAC,EAAA;AAAA,EAAA;AACQ,IAAA;AACC,IAAA;AACA,IAAA;AACH,IAAA;AACC,IAAA;AACN,IAAA;AACW,IAAA;AAEX,IAAA;AAAQ,sBAAA;AACF,sBAAA;AACE,sBAAA;AAAmC,IAAA;AAAA,EAAA;AAC7C;AAG4C;AACrC,EAAA;AACC,EAAA;AACR,EAAA;AAEA;AAAC,EAAA;AAAA,EAAA;AACQ,IAAA;AACC,IAAA;AACA,IAAA;AACH,IAAA;AACC,IAAA;AACN,IAAA;AACW,IAAA;AAEX,IAAA;AAAQ,sBAAA;AACA,sBAAA;AACA,sBAAA;AAAkC,IAAA;AAAA,EAAA;AAC5C;AAGyD;AACjD,EAAA;AAER;AAAC,EAAA;AAAA,EAAA;AACO,IAAA;AACC,IAAA;AACC,IAAA;AACH,IAAA;AACC,IAAA;AACK,IAAA;AAEX,IAAA;AAAQ,sBAAA;AACR,sBAAA;AAAC,QAAA;AAAA,QAAA;AACG,UAAA;AACG,UAAA;AAAA,QAAA;AACP,MAAA;AAAA,IAAA;AAAA,EAAA;AACF;AAOyD;AACxD,EAAA;AAAA,EAAA;AACQ,IAAA;AACC,IAAA;AACA,IAAA;AACH,IAAA;AACC,IAAA;AACN,IAAA;AACW,IAAA;AAEX,IAAA;AAAA,sBAAA;AAAC,QAAA;AAAA,QAAA;AACG,UAAA;AACG,UAAA;AAAA,QAAA;AACP,MAAA;AACA,sBAAA;AAAC,QAAA;AAAA,QAAA;AACG,UAAA;AACG,UAAA;AAAA,QAAA;AACP,MAAA;AAAA,IAAA;AAAA,EAAA;AACF;AAG2D;AAC1D,EAAA;AAAA,EAAA;AACQ,IAAA;AACC,IAAA;AACA,IAAA;AACH,IAAA;AACC,IAAA;AACN,IAAA;AACW,IAAA;AAEX,IAAA;AAAC,MAAA;AAAA,MAAA;AACG,QAAA;AACG,QAAA;AAAA,MAAA;AACP,IAAA;AAAA,EAAA;AACF;AAwBA;AAAC,EAAA;AAAA,EAAA;AACC,IAAA;AAC0B,IAAA;AAE1B,IAAA;AAAC,sBAAA;AACD,sBAAA;AAAC,QAAA;AAAA,QAAA;AACQ,UAAA;AACO,YAAA;AACF,YAAA;AACE,YAAA;AACL,YAAA;AACQ,YAAA;AACjB,UAAA;AACD,UAAA;AAAA,QAAA;AAED,MAAA;AAAA,IAAA;AAAA,EAAA;AACF;AHqBoC;AACA;AIxWf;AAqCjBA;AAlCqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASN;AAEqB;AAChB,EAAA;AACD,EAAA;AACZ,EAAA;AACS,EAAA;AACW,EAAA;AACd,EAAA;AACnB;AAEyB;AAEqC;AAC5D,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACI;AAEFA,EAAAA;AAAC,IAAA;AAAA,IAAA;AAC6B,MAAA;AAC5B,MAAA;AAEA,MAAA;AAAC,wBAAA;AACE,0BAAA;AACQC,UAAAA;AACX,QAAA;AACU,QAAA;AAAmC,MAAA;AAAA,IAAA;AAC/C,EAAA;AAEJ;AAE2E;AACzE,EAAA;AACI;AACI,EAAA;AACV;AJsWsC;AACA;AKzZf;AAiBX;AALL;AAEHA,EAAAA;AAAC,IAAA;AAAA,IAAA;AACO,MAAA;AACN,MAAA;AACO,MAAA;AACP,MAAA;AACA,MAAA;AAAA,IAAA;AACF,EAAA;AAEJ;ALkZsC;AACA;AMzaf;AACJ;AN2amB;AACA;AO7af;AACe;AP+aA;AACA;AQjbd;AAA4B;ARobd;AACA;AO8JhCD;AA9kB2C;AAC/C,EAAA;AACA,EAAA;AACmB,EAAA;AACH,EAAA;AAChB,EAAA;AACY,EAAA;AACQ,EAAA;AACF,EAAA;AACC,EAAA;AACG,EAAA;AACD,EAAA;AACT,EAAA;AACA,EAAA;AACR;AACwC,EAAA;AACE,EAAA;AACxB,EAAA;AACc,EAAA;AACR,EAAA;AACI,EAAA;AACE,EAAA;AACA,EAAA;AACA,EAAA;AAEH,EAAA;AACG,EAAA;AACA,EAAA;AACL,EAAA;AACC,EAAA;AAC2C,EAAA;AACD,EAAA;AACvC,EAAA;AACC,EAAA;AACgB,EAAA;AAE3B,EAAA;AACM,IAAA;AACK,MAAA;AACH,MAAA;AAC7B,IAAA;AACQ,IAAA;AACV,EAAA;AAE6B,EAAA;AACG,IAAA;AACA,IAAA;AACV,IAAA;AACI,IAAA;AACQ,EAAA;AAElB,EAAA;AACF,IAAA;AACgB,MAAA;AAEH,MAAA;AACL,MAAA;AACY,MAAA;AAEF,MAAA;AACE,QAAA;AAC9B,MAAA;AAEgB,MAAA;AAES,MAAA;AACO,QAAA;AACV,QAAA;AACL,QAAA;AACU,QAAA;AACtB,MAAA;AACA,IAAA;AACuB,MAAA;AACE,QAAA;AACH,QAAA;AAC3B,MAAA;AACgB,MAAA;AACc,MAAA;AAChC,IAAA;AACyB,EAAA;AAEK,EAAA;AACA,IAAA;AAEP,IAAA;AACN,IAAA;AACD,IAAA;AACc,IAAA;AAED,IAAA;AACE,MAAA;AAC/B,IAAA;AAE0B,IAAA;AACD,MAAA;AACL,MAAA;AACV,MAAA;AACJ,IAAA;AACgB,EAAA;AAER,EAAA;AAIX,IAAA;AAGsB,MAAA;AACK,MAAA;AACR,MAAA;AACW,QAAA;AAC/B,MAAA;AACF,IAAA;AACsB,EAAA;AAEE,EAAA;AACM,IAAA;AACE,MAAA;AACH,MAAA;AAER,MAAA;AACQ,MAAA;AAEF,MAAA;AACG,QAAA;AACE,QAAA;AACZ,UAAA;AACC,UAAA;AACjB,QAAA;AACD,MAAA;AAEM,MAAA;AACT,IAAA;AACQ,IAAA;AACV,EAAA;AAEqB,EAAA;AACM,IAAA;AACG,MAAA;AACxB,QAAA;AAC8B,QAAA;AAChC,MAAA;AACwB,MAAA;AACE,MAAA;AACI,MAAA;AACZ,MAAA;AACW,MAAA;AACL,MAAA;AACP,MAAA;AACS,QAAA;AACE,QAAA;AACtB,MAAA;AACR,IAAA;AACwB,IAAA;AAC1B,EAAA;AAEyB,EAAA;AACE,IAAA;AACG,MAAA;AAED,MAAA;AACM,MAAA;AACH,MAAA;AACL,MAAA;AACE,MAAA;AAED,MAAA;AACC,QAAA;AACH,QAAA;AACC,QAAA;AAChB,MAAA;AACgB,QAAA;AACvB,MAAA;AACF,IAAA;AACkB,IAAA;AACpB,EAAA;AAE2B,EAAA;AACG,IAAA;AACI,MAAA;AACX,MAAA;AACU,MAAA;AACL,MAAA;AACA,MAAA;AACF,MAAA;AACA,MAAA;AACS,MAAA;AAEF,MAAA;AAEV,MAAA;AACY,QAAA;AACL,MAAA;AACF,QAAA;AACI,UAAA;AACnB,QAAA;AACY,UAAA;AACf,YAAA;AAC0B,YAAA;AAC5B,UAAA;AACF,QAAA;AACK,MAAA;AACY,QAAA;AACf,UAAA;AAC0B,UAAA;AAC5B,QAAA;AACF,MAAA;AAEO,MAAA;AACT,IAAA;AACyB,IAAA;AAC3B,EAAA;AAE8B,EAAA;AACT,IAAA;AACS,MAAA;AAED,MAAA;AACb,MAAA;AAEmB,MAAA;AACN,MAAA;AACG,MAAA;AAIH,MAAA;AAEH,MAAA;AACI,MAAA;AAExB,MAAA;AAEc,MAAA;AACM,QAAA;AACK,QAAA;AACV,QAAA;AACf,QAAA;AACF,MAAA;AAE6B,MAAA;AACP,QAAA;AACF,QAAA;AACE,QAAA;AACK,QAAA;AACV,QAAA;AACf,QAAA;AACF,MAAA;AAE0B,MAAA;AACE,QAAA;AACR,QAAA;AAIhB,QAAA;AAIoB,UAAA;AACF,UAAA;AACE,UAAA;AACK,UAAA;AACV,UAAA;AACjB,QAAA;AACF,MAAA;AACF,IAAA;AACgC,IAAA;AAClC,EAAA;AAEuB,EAAA;AACkB,IAAA;AACX,MAAA;AACT,MAAA;AACK,QAAA;AACpB,QAAA;AACF,MAAA;AACiB,MAAA;AAEE,MAAA;AACW,MAAA;AACN,MAAA;AACQ,MAAA;AACA,MAAA;AACD,MAAA;AAGxB,MAAA;AAGe,MAAA;AAEX,MAAA;AACW,MAAA;AACA,MAAA;AACA,MAAA;AACF,MAAA;AAEC,MAAA;AAGjB,QAAA;AAGY,UAAA;AACZ,UAAA;AACF,QAAA;AACF,MAAA;AAE6B,MAAA;AACE,QAAA;AACL,QAAA;AACN,QAAA;AAGf,QAAA;AAIuB,QAAA;AACI,UAAA;AAC5B,UAAA;AACF,QAAA;AACF,MAAA;AAGE,MAAA;AAKe,MAAA;AACH,QAAA;AACZ,QAAA;AACF,MAAA;AAE2B,MAAA;AACI,MAAA;AACjC,IAAA;AACA,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAEwB,EAAA;AACG,IAAA;AACG,MAAA;AAEE,MAAA;AACT,MAAA;AACE,MAAA;AAEG,MAAA;AACC,QAAA;AACH,QAAA;AACC,QAAA;AAChB,MAAA;AACgB,QAAA;AACvB,MAAA;AACF,IAAA;AACkB,IAAA;AACpB,EAAA;AAEwB,EAAA;AACH,IAAA;AACa,MAAA;AAEA,MAAA;AACX,MAAA;AACM,MAAA;AACD,MAAA;AACA,MAAA;AACF,MAAA;AACA,MAAA;AAEO,MAAA;AAEV,MAAA;AACY,QAAA;AACL,MAAA;AACP,QAAA;AACf,UAAA;AAC0B,UAAA;AAC5B,QAAA;AACK,MAAA;AACY,QAAA;AACf,UAAA;AAC0B,UAAA;AAC5B,QAAA;AACF,MAAA;AAEwB,MAAA;AACH,MAAA;AACvB,IAAA;AACA,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAEkC,EAAA;AACF,IAAA;AAEX,IAAA;AACW,IAAA;AACN,IAAA;AACQ,IAAA;AACA,IAAA;AACD,IAAA;AACE,IAAA;AACF,IAAA;AACT,IAAA;AACA,IAAA;AACA,IAAA;AACF,IAAA;AAEC,IAAA;AAGjB,MAAA;AAGY,QAAA;AACZ,QAAA;AACF,MAAA;AACF,IAAA;AAE6B,IAAA;AACE,MAAA;AACG,MAAA;AACd,MAAA;AAEE,MAAA;AACU,QAAA;AAC5B,QAAA;AACF,MAAA;AAEuB,MAAA;AACO,QAAA;AAC5B,QAAA;AACF,MAAA;AACF,IAAA;AAGE,IAAA;AAKe,IAAA;AACH,MAAA;AACZ,MAAA;AACF,IAAA;AAE2B,IAAA;AACI,IAAA;AAC9B,EAAA;AACD,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACD,EAAA;AAEe,EAAA;AACE,IAAA;AACY,MAAA;AACA,MAAA;AACb,MAAA;AACkB,QAAA;AACA,QAAA;AAC/B,MAAA;AACF,IAAA;AAC+B,EAAA;AAEJ,EAAA;AACR,IAAA;AACD,MAAA;AAClB,IAAA;AACe,IAAA;AACjB,EAAA;AAEgB,EAAA;AACgB,IAAA;AACA,IAAA;AAEA,IAAA;AACnB,MAAA;AACV,IAAA;AAC6B,IAAA;AACnB,MAAA;AACV,IAAA;AAEY,IAAA;AACE,MAAA;AACA,MAAA;AACf,IAAA;AACiC,EAAA;AAEnB,EAAA;AACF,IAAA;AACqB,MAAA;AAC1B,IAAA;AAC0B,MAAA;AACjC,IAAA;AAEa,IAAA;AACoB,MAAA;AACF,MAAA;AACE,QAAA;AACH,QAAA;AAC5B,MAAA;AAC4B,MAAA;AACE,QAAA;AACH,QAAA;AAC3B,MAAA;AACuB,MAAA;AACzB,IAAA;AACS,EAAA;AAEuB,EAAA;AAGzB,EAAA;AAEA,EAAA;AAC2B,EAAA;AAEH,EAAA;AACX,IAAA;AACU,IAAA;AACN,IAAA;AACU,IAAA;AACA,IAAA;AAChB,IAAA;AACpB,EAAA;AAE4C,EAAA;AACtB,IAAA;AACU,IAAA;AACN,IAAA;AACU,IAAA;AACA,IAAA;AAChB,IAAA;AACpB,EAAA;AAEwB,EAAA;AACH,EAAA;AAEsB,EAAA;AACP,IAAA;AACV,IAAA;AACM,IAAA;AAG1B,IAAA;AAEI,MAAA;AACJ,IAAA;AACN,EAAA;AAGEC,EAAAA;AAAC,IAAA;AAAA,IAAA;AACY,MAAA;AACoB,MAAA;AACxB,MAAA;AAEPD,MAAAA;AAAC,QAAA;AAAA,QAAA;AACM,UAAA;AACsB,UAAA;AACpB,UAAA;AACG,YAAA;AACG,YAAA;AACA,YAAA;AACb,UAAA;AACkB,UAAA;AACJ,UAAA;AACD,UAAA;AAEb,UAAA;AAAC,4BAAA;AACE,cAAA;AAAA,cAAA;AACY,gBAAA;AACK,gBAAA;AACI,kBAAA;AACN,kBAAA;AACd,gBAAA;AAAA,cAAA;AAEJ,YAAA;AACAC,4BAAAA;AAAC,cAAA;AAAA,cAAA;AACM,gBAAA;AACM,gBAAA;AAEV,gBAAA;AAAA,cAAA;AACH,YAAA;AAAA,UAAA;AAAA,QAAA;AACF,MAAA;AAAA,IAAA;AACF,EAAA;AAEJ;AAEe;APqUuB;AACA;ASz7Bd;AAA4B;AT47Bd;AACA;AMjsBtB;AA3OwB;AACZ;AAE8B;AACxD,EAAA;AACA,EAAA;AACA,EAAA;AACS,EAAA;AACE,EAAA;AACX,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACkB,EAAA;AAClB,EAAA;AACY,EAAA;AACR;AAC+C,EAAA;AACnBC,EAAAA;AACAA,EAAAA;AACIA,EAAAA;AACF,EAAA;AACV,EAAA;AACY,EAAA;AACF,EAAA;AACA,EAAA;AAEoC,EAAA;AACvC,EAAA;AACe,EAAA;AAEjBC,EAAAA;AACG,IAAA;AACE,MAAA;AAChC,IAAA;AACiB,IAAA;AACE,oBAAA;AACF,IAAA;AACP,MAAA;AACH,IAAA;AACqB,EAAA;AAEHA,EAAAA;AACK,IAAA;AACE,MAAA;AAChC,IAAA;AAC2B,IAAA;AACZ,IAAA;AACZ,EAAA;AAE2B,EAAA;AACZ,IAAA;AACC,IAAA;AACA,IAAA;AACE,IAAA;AACK,IAAA;AACZ,IAAA;AACX,EAAA;AAEW,EAAA;AACiC,IAAA;AACpB,MAAA;AACF,QAAA;AACvB,MAAA;AACyB,MAAA;AACI,QAAA;AACC,QAAA;AAC9B,MAAA;AACyB,MAAA;AACH,QAAA;AACU,QAAA;AACf,UAAA;AACf,QAAA;AACF,MAAA;AACyB,MAAA;AACI,QAAA;AAEP,QAAA;AAGG,UAAA;AACvB,QAAA;AAEoB,QAAA;AAGU,UAAA;AAC9B,QAAA;AACF,MAAA;AACF,IAAA;AAEwB,IAAA;AACJ,IAAA;AACI,EAAA;AAEV,EAAA;AACO,IAAA;AACO,MAAA;AAC5B,IAAA;AACO,EAAA;AAEO,EAAA;AAGX,IAAA;AAKyB,MAAA;AACH,MAAA;AACJ,MAAA;AACE,MAAA;AACF,MAAA;AAGhB,MAAA;AACmB,QAAA;AACS,QAAA;AACN,UAAA;AACjB,YAAA;AACE,cAAA;AACF,YAAA;AACF,UAAA;AACoB,QAAA;AAEH,MAAA;AACG,QAAA;AACC,QAAA;AACG,QAAA;AAEb,MAAA;AACnB,IAAA;AAEa,IAAA;AACmB,MAAA;AACE,QAAA;AAChC,MAAA;AACF,IAAA;AACC,EAAA;AACD,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACD,EAAA;AAEe,EAAA;AACY,IAAA;AACQ,MAAA;AAClC,IAAA;AACY,IAAA;AACsB,IAAA;AACd,IAAA;AACjB,EAAA;AAEW,EAAA;AACF,IAAA;AACI,MAAA;AACG,MAAA;AACC,MAAA;AACC,MAAA;AACE,MAAA;AACF,MAAA;AACW,MAAA;AACE,QAAA;AAChC,MAAA;AACF,IAAA;AACS,EAAA;AAEK,EAAA;AACY,IAAA;AAEC,IAAA;AACJ,MAAA;AACY,QAAA;AAC/B,QAAA;AACF,MAAA;AACwB,MAAA;AACI,QAAA;AACE,QAAA;AACH,QAAA;AACzB,QAAA;AACiB,UAAA;AACjB,QAAA;AACF,MAAA;AACF,IAAA;AAE2B,IAAA;AAEN,IAAA;AACG,IAAA;AACM,MAAA;AAC9B,IAAA;AAEiC,IAAA;AACR,EAAA;AAEM,EAAA;AAEF,EAAA;AACT,IAAA;AACU,IAAA;AACE,MAAA;AAChC,IAAA;AACF,EAAA;AAEgC,EAAA;AACX,IAAA;AACrB,EAAA;AAGE,EAAA;AACO,oBAAA;AASe,IAAA;AACf,oBAAA;AACwB,MAAA;AAEvBF,wBAAAA;AAAC,UAAA;AAAA,UAAA;AACW,YAAA;AACK,YAAA;AAEfA,YAAAA;AAAU,UAAA;AACZ,QAAA;AAEED,QAAAA;AACG,0BAAA;AAC0B,YAAA;AAAE,YAAA;AAA0B,YAAA;AACvD,UAAA;AACAC,0BAAAA;AAAC,YAAA;AAAA,YAAA;AACW,cAAA;AACK,cAAA;AACE,gCAAA;AACE,gBAAA;AACnB,cAAA;AACD,cAAA;AAAA,YAAA;AAED,UAAA;AACF,QAAA;AAEJ,MAAA;AAED,sBAAA;AAGH,IAAA;AACF,EAAA;AAIA,EAAA;AACO,oBAAA;AACC,sBAAA;AACC,sBAAA;AACF,wBAAA;AACA,wBAAA;AACE,UAAA;AAAO,UAAA;AAAE,UAAA;AAAS,UAAA;AAAE,0BAAA;AACvB,QAAA;AACF,MAAA;AAEEA,MAAAA;AAIJ,IAAA;AACK,oBAAA;AAEDA,sBAAAA;AAAC,QAAA;AAAA,QAAA;AACsB,UAAA;AACA,UAAA;AACX,UAAA;AAGR,UAAA;AAAC,YAAA;AAAA,YAAA;AACmB,cAAA;AAElB,cAAA;AAAC,gCAAA;AACA,gCAAA;AAAe,cAAA;AAAA,YAAA;AAGlBD,UAAAA;AAAC,YAAA;AAAA,YAAA;AACmB,cAAA;AAElB,cAAA;AAAC,gCAAA;AACA,gCAAA;AAAoB,cAAA;AAAA,YAAA;AACvB,UAAA;AAAA,QAAA;AAEJ,MAAA;AAGEA,MAAAA;AAA+B,QAAA;AACA,QAAA;AAC5B,wBAAA;AAEM,QAAA;AAET,MAAA;AAGmB,MAAA;AAClB,QAAA;AAAA,QAAA;AACW,UAAA;AACa,UAAA;AAEvB,UAAA;AAAC,4BAAA;AACA,4BAAA;AAAkB,UAAA;AAAA,QAAA;AACrB,MAAA;AAGN,IAAA;AACF,EAAA;AAIA,EAAA;AAEI,IAAA;AACO,sBAAA;AACF,sBAAA;AAAA,QAAA;AAAkB,QAAA;AAAK,QAAA;AAAG,MAAA;AAC/B,IAAA;AAGAA,IAAAA;AACG,sBAAA;AACG,sBAAA;AACH,sBAAA;AAGH,IAAA;AAEiC,IAAA;AAG7B,MAAA;AAIFC,sBAAAA;AAAC,QAAA;AAAA,QAAA;AACM,UAAA;AACW,UAAA;AACV,UAAA;AACC,UAAA;AACK,UAAA;AACN,UAAA;AACE,UAAA;AACC,UAAA;AACS,UAAA;AAAqB,QAAA;AACzC,MAAA;AACF,IAAA;AAEJ,EAAA;AAIA,EAAA;AACO,oBAAA;AAGD,oBAAA;AACD,oBAAA;AACL,EAAA;AAIA,EAAA;AACO,oBAAA;AAGD,oBAAA;AACD,oBAAA;AACE,oBAAA;AACF,sBAAA;AAGA,sBAAA;AAGH,IAAA;AACF,EAAA;AAIA,EAAA;AAAC,IAAA;AAAA,IAAA;AACW,MAAA;AACgB,MAAA;AAEzB,MAAA;AAAsB,QAAA;AACH,QAAA;AACA,QAAA;AACG,QAAA;AACF,QAAA;AAAgB,MAAA;AAAA,IAAA;AACvC,EAAA;AAGY,EAAA;AAEVA,IAAAA;AAAC,MAAA;AAAA,MAAA;AACC,QAAA;AACA,QAAA;AACS,QAAA;AACM,QAAA;AACc,QAAA;AAE7BA,QAAAA;AAAC,UAAA;AAAA,UAAA;AACM,YAAA;AACqB,YAAA;AAEX,YAAA;AAAA,UAAA;AACjB,QAAA;AAAA,MAAA;AACF,IAAA;AAEJ,EAAA;AAEoB,EAAA;AAGjB,EAAA;AAML;ANk2BsC;AACA;AUnyCf;AVqyCe;AACA;AWtyCd;AACQ;AXwyCM;AACA;AYvyCO;AAItB,EAAA;AAKA,IAAA;AACrB,EAAA;AACmC,EAAA;AAC1B,IAAA;AACT,EAAA;AACO,EAAA;AACT;AZkyCsC;AACA;AavyCpC;AAGsB,EAAA;AACJ,IAAA;AAClB,EAAA;AAEmC,EAAA;AACnB,EAAA;AACE,IAAA;AACK,IAAA;AACvB,EAAA;AAEuB,EAAA;AACX,IAAA;AACV,IAAA;AACU,MAAA;AACmB,MAAA;AAC3B,MAAA;AACF,IAAA;AACF,EAAA;AAEkB,EAAA;AACiB,IAAA;AACjB,IAAA;AACE,MAAA;AACjB,IAAA;AACH,EAAA;AAEiC,EAAA;AAEN,EAAA;AACS,IAAA;AACpC,EAAA;AAEO,EAAA;AACT;AbiyCsC;AACA;Ac50CpC;AAGoC,EAAA;AACpB,EAAA;AACE,IAAA;AACQ,IAAA;AAC1B,EAAA;AAEgC,EAAA;AACtB,IAAA;AACmB,IAAA;AAC3B,IAAA;AACD,EAAA;AAEiB,EAAA;AACiB,IAAA;AACjB,IAAA;AACE,MAAA;AACjB,IAAA;AACH,EAAA;AAEqB,EAAA;AACvB;Ady0CsC;AACA;Aez2CZ;Af22CY;AACA;AUn2CL;AAC/B,EAAA;AACQ,EAAA;AACkB;AACU,EAAA;AACJ,EAAA;AACF,EAAA;AACU,EAAA;AACtC,IAAA;AACF,EAAA;AACkC,EAAA;AAEF,EAAA;AAI5B,IAAA;AAEoB,MAAA;AAEL,MAAA;AACF,MAAA;AAET,MAAA;AACyB,QAAA;AACN,UAAA;AACH,UAAA;AAClB,QAAA;AAC6B,QAAA;AACA,QAAA;AAEP,QAAA;AACb,UAAA;AACT,QAAA;AAEmB,QAAA;AAEF,QAAA;AACR,UAAA;AACT,QAAA;AAEsB,QAAA;AAEO,QAAA;AACpB,UAAA;AACT,QAAA;AAEO,QAAA;AACD,MAAA;AACC,QAAA;AACP,MAAA;AACgB,QAAA;AAClB,MAAA;AACF,IAAA;AACc,IAAA;AAChB,EAAA;AAE8B,EAAA;AACoC,IAAA;AACjC,MAAA;AAEd,MAAA;AACF,MAAA;AAET,MAAA;AACqB,QAAA;AACF,QAAA;AACL,QAAA;AACN,MAAA;AAEK,QAAA;AACH,QAAA;AACO,QAAA;AACnB,MAAA;AACgB,QAAA;AAClB,MAAA;AACF,IAAA;AACc,IAAA;AAChB,EAAA;AAEO,EAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACF;AVm1CsC;AACA;AgBp7CiC;AAC5D,EAAA;AACE,IAAA;AACE,IAAA;AACb,EAAA;AACU,EAAA;AACC,IAAA;AACE,IAAA;AACb,EAAA;AACF;AAE4B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA2CP;AAEgB;AACX,EAAA;AACD,EAAA;AACZ,EAAA;AACS,EAAA;AACW,EAAA;AACd,EAAA;AACnB;AhBm7CsC;AACA;AC5zC9BD;AA3KK;AAE6C;AACxD,EAAA;AACY,EAAA;AACF,EAAA;AACV,EAAA;AACA,EAAA;AACA,EAAA;AACe,EAAA;AACF,EAAA;AACL,EAAA;AACC,EAAA;AACT,EAAA;AACc,EAAA;AACd,EAAA;AACA,EAAA;AACW,EAAA;AACX,EAAA;AACwB,EAAA;AACxB,EAAA;AACA,EAAA;AACA,EAAA;AACoB,EAAA;AACpB,EAAA;AACA,EAAA;AACI;AAC8B,EAAA;AACX,EAAA;AAESE,EAAAA;AACE,EAAA;AACV,EAAA;AACY,EAAA;AACZ,EAAA;AACIA,EAAAA;AAIH,EAAA;AACvB,IAAA;AACF,EAAA;AAE2B,EAAA;AACzB,IAAA;AACO,IAAA;AACR,EAAA;AAEe,EAAA;AACY,IAAA;AACvB,EAAA;AAEW,EAAA;AACK,IAAA;AACc,IAAA;AACA,IAAA;AAClB,EAAA;AAED,EAAA;AACC,IAAA;AAEW,IAAA;AACC,MAAA;AACA,QAAA;AACnB,QAAA;AAEO,UAAA;AACe,UAAA;AACtB,YAAA;AACY,YAAA;AACZ,YAAA;AACF,UAAA;AACc,UAAA;AACR,QAAA;AACQ,UAAA;AACd,QAAA;AACc,UAAA;AAChB,QAAA;AACK,MAAA;AACS,QAAA;AAChB,MAAA;AACF,IAAA;AAEM,IAAA;AACO,IAAA;AACA,MAAA;AACb,IAAA;AAC4B,EAAA;AAEd,EAAA;AACc,IAAA;AACiB,MAAA;AACxB,QAAA;AACnB,MAAA;AACF,IAAA;AAC0B,IAAA;AACJ,IAAA;AACX,EAAA;AAEYC,EAAAA;AACC,IAAA;AACT,EAAA;AAEWA,EAAAA;AAEtB,IAAA;AACsB,MAAA;AACM,MAAA;AAGlB,IAAA;AACC,sBAAA;AACc,MAAA;AAER,QAAA;AAGM,QAAA;AACzB,MAAA;AACF,IAAA;AACsB,EAAA;AAEO,EAAA;AACR,IAAA;AACA,MAAA;AACd,IAAA;AACe,MAAA;AACtB,IAAA;AACmB,EAAA;AAEW,EAAA;AACV,IAAA;AACE,EAAA;AAEMA,EAAAA;AACG,IAAA;AACN,MAAA;AACK,QAAA;AACP,QAAA;AACJ,QAAA;AACf,QAAA;AACa,QAAA;AACd,MAAA;AACH,IAAA;AAC0B,IAAA;AAC5B,EAAA;AAE8B,EAAA;AAEjB,EAAA;AAEE,EAAA;AAE8C,EAAA;AAChD,IAAA;AACE,IAAA;AAEJ,IAAA;AACI,IAAA;AACgB,IAAA;AACE,IAAA;AACjC,EAAA;AAEgC,EAAA;AACV,EAAA;AAEM,EAAA;AACTF,IAAAA;AAEQ,IAAA;AAEpB,MAAA;AACE,wBAAA;AACA,wBAAA;AACH,MAAA;AAEJ,IAAA;AAGG,IAAA;AACO,sBAAA;AACG,sBAAA;AACH,sBAAA;AACR,IAAA;AAEJ,EAAA;AAGED,EAAAA;AAAC,IAAA;AAAA,IAAA;AAC+B,MAAA;AACR,MAAA;AAEtB,MAAA;AAAC,wBAAA;AACCC,0BAAAA;AAAC,YAAA;AAAA,YAAA;AACM,cAAA;AACM,cAAA;AACT,gBAAA;AACa,gBAAA;AACA,gBAAA;AACE,gBAAA;AACjB,cAAA;AACsB,cAAA;AACZ,cAAA;AAGR,cAAA;AACG,gCAAA;AACA,gCAAA;AAGW,cAAA;AAAA,YAAA;AAElB,UAAA;AAEE,UAAA;AAAC,YAAA;AAAA,YAAA;AACM,cAAA;AACK,cAAA;AACM,cAAA;AACI,gBAAA;AACG,gBAAA;AACvB,cAAA;AACU,cAAA;AACX,cAAA;AAAA,YAAA;AAED,UAAA;AAEJ,QAAA;AAGED,QAAAA;AAA+B,UAAA;AACA,UAAA;AAC7BC,0BAAAA;AAAC,YAAA;AAAA,YAAA;AACW,cAAA;AAER,cAAA;AAEH,cAAA;AAAA,YAAA;AAED,UAAA;AAAO,UAAA;AAET,QAAA;AAIA,QAAA;AACG,0BAAA;AACA,0BAAA;AACqB,4BAAA;AAAE,YAAA;AAAqB,4BAAA;AAC7C,UAAA;AACAD,0BAAAA;AAAC,YAAA;AAAA,YAAA;AACW,cAAA;AACK,cAAA;AACI,gBAAA;AACA,gBAAA;AACnB,cAAA;AAEA,cAAA;AAAC,gCAAA;AAGA,gCAAA;AAAe,cAAA;AAAA,YAAA;AAClB,UAAA;AACF,QAAA;AAGFC,wBAAAA;AAAC,UAAA;AAAA,UAAA;AACS,YAAA;AACO,YAAA;AACE,YAAA;AACF,YAAA;AACD,YAAA;AACU,YAAA;AACF,YAAA;AACtB,YAAA;AACiB,YAAA;AACjB,YAAA;AACkB,YAAA;AAClB,YAAA;AAAA,UAAA;AACF,QAAA;AAGE,QAAA;AAEA,MAAA;AAAA,IAAA;AAEJ,EAAA;AAEJ;ADg8CsC;AACA;AiB7uDf;AACvB;AACEG;AACA;AACAC;AACK;AAG+B;AACR;AAEF;AACQ,EAAA;AACD,EAAA;AACVA,EAAAA;AAEe,EAAA;AACP,IAAA;AACd,MAAA;AACI,QAAA;AACf,QAAA;AACF,MAAA;AAEW,MAAA;AAES,MAAA;AACG,QAAA;AACP,UAAA;AACK,UAAA;AACM,UAAA;AACzB,QAAA;AACD,MAAA;AAEwB,MAAA;AACF,QAAA;AACP,UAAA;AACK,UAAA;AACA,UAAA;AACnB,QAAA;AACD,MAAA;AAEgB,MAAA;AACH,QAAA;AACK,QAAA;AACA,QAAA;AACS,MAAA;AAC7B,IAAA;AAC8B,EAAA;AAEf,EAAA;AAIU,IAAA;AACI,MAAA;AACE,MAAA;AACF,MAAA;AAEL,MAAA;AACrB,QAAA;AAC2B,UAAA;AACzB,UAAA;AACM,UAAA;AACR,QAAA;AACA,QAAA;AAC0B,UAAA;AAC1B,QAAA;AACF,MAAA;AAE2B,MAAA;AAC7B,IAAA;AAC0B,IAAA;AAC5B,EAAA;AAEsB,EAAA;AACxB;AjBguDsC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/Users/ilya/work/tonpay/ton-pay/packages/ui-react/dist/index.js","sourcesContent":[null,"import * as React from 'react';\nimport { useEffect, useState, useCallback } from 'react';\nimport { useTonAddress, useTonConnectUI } from '@tonconnect/ui-react';\nimport type { TonPayButtonProps } from '../../types';\nimport { classNames, toCssSize, getUserIp } from '../../utils';\nimport { TonIcon, DisconnectIcon } from '../icons';\nimport {\n NotificationRoot,\n ErrorTransactionNotification,\n} from '../notification';\nimport { PaymentModal } from '../payment-modal/PaymentModal';\nimport { useMoonPayIframe } from '../../hooks/useMoonPayIframe';\nimport { PRESETS, injectStyles } from './styles';\n\ninjectStyles();\n\nexport const TonPayButton: React.FC<TonPayButtonProps> = ({\n handlePay,\n isLoading = false,\n variant = 'long',\n preset,\n bgColor,\n textColor,\n borderRadius = 8,\n fontFamily = 'inherit',\n width = 300,\n height = 44,\n text,\n loadingText = 'Processing...',\n style,\n className,\n disabled = false,\n onError,\n showErrorNotification = true,\n amount,\n currency,\n apiKey,\n isOnRampAvailable = false,\n onCardPaymentSuccess,\n itemTitle,\n}) => {\n const address = useTonAddress(true);\n const [tonConnectUI] = useTonConnectUI();\n\n const [showMenu, setShowMenu] = useState(false);\n const [isModalOpen, setIsModalOpen] = useState(false);\n const [onRampAvailable, setOnRampAvailable] = useState(false);\n const [errorMessage, setErrorMessage] = useState<string | null>(null);\n const [internalLoading, setInternalLoading] = useState(false);\n const [userIp, setUserIp] = useState('');\n // We store the redirectToWallet function from ton-connect's onRequestSent callback\n // This allows us to manually trigger the wallet redirect when the user clicks \"Click here\",\n // which is necessary when automatic redirection fails due to platform limitations.\n const [redirectToWallet, setRedirectToWallet] = useState<(() => void) | null>(\n null,\n );\n\n const { checkAvailability, fetchOnRampLink } = useMoonPayIframe({\n apiKey,\n chain: 'mainnet',\n });\n\n useEffect(() => {\n getUserIp().then(setUserIp);\n }, []);\n\n useEffect(() => {\n if (!errorMessage) return;\n const timerId = setTimeout(() => setErrorMessage(null), 3000);\n return () => clearTimeout(timerId);\n }, [errorMessage]);\n\n useEffect(() => {\n let isActive = true;\n\n const check = async () => {\n if (isOnRampAvailable && apiKey && userIp) {\n setInternalLoading(true);\n try {\n const parsedAmount =\n typeof amount === 'string' ? parseFloat(amount) : amount || 0;\n const available = await checkAvailability(\n parsedAmount,\n currency || 'TON',\n userIp,\n );\n if (isActive) setOnRampAvailable(available);\n } catch {\n if (isActive) setOnRampAvailable(false);\n } finally {\n if (isActive) setInternalLoading(false);\n }\n } else {\n if (isActive) setOnRampAvailable(false);\n }\n };\n\n check();\n return () => {\n isActive = false;\n };\n }, [apiKey, amount, currency, isOnRampAvailable, checkAvailability, userIp]);\n\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (showMenu && !(event.target as Element).closest('.tp-wrap')) {\n setShowMenu(false);\n }\n };\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, [showMenu]);\n\n const handleDisconnect = useCallback(() => {\n tonConnectUI.disconnect();\n }, [tonConnectUI]);\n\n const handlePayWithCrypto = useCallback(async () => {\n // We keep the modal open to show processing state\n try {\n setRedirectToWallet(null);\n await handlePay((redirect) => setRedirectToWallet(() => redirect));\n // If success, we can close it or wait for parent to update state\n // setIsModalOpen(false);\n } catch (err) {\n onError?.(err);\n if (showErrorNotification) {\n const raw =\n typeof err === 'object' && err && 'message' in err\n ? String((err as Error).message)\n : String(err ?? '');\n setErrorMessage(raw || 'Wallet connection modal closed');\n }\n }\n }, [handlePay, onError, showErrorNotification]);\n\n const onPayClick = useCallback(async () => {\n if (onRampAvailable) {\n setIsModalOpen(true);\n } else {\n handlePayWithCrypto();\n }\n }, [onRampAvailable, handlePayWithCrypto]);\n\n const handleRetry = useCallback(() => {\n handlePayWithCrypto();\n }, [handlePayWithCrypto]);\n\n const handleFetchOnRampLink = useCallback(\n async (_providerId: string) => {\n return fetchOnRampLink({\n amount: typeof amount === 'string' ? parseFloat(amount) : amount || 0,\n asset: currency || 'TON',\n recipientAddr: address,\n userIp,\n redirectURL: '',\n });\n },\n [fetchOnRampLink, amount, currency, address, userIp],\n );\n\n const presetConfig = preset ? PRESETS[preset] : null;\n const finalBgColor =\n bgColor ?? presetConfig?.bgColor ?? PRESETS.default.bgColor;\n const finalTextColor =\n textColor ?? presetConfig?.textColor ?? PRESETS.default.textColor;\n\n const cssVars: Record<string, string | number | undefined> = {\n '--tp-bg': finalBgColor,\n '--tp-text': finalTextColor,\n '--tp-radius':\n typeof borderRadius === 'number' ? `${borderRadius}px` : borderRadius,\n '--tp-font': fontFamily,\n '--tp-width': toCssSize(width),\n '--tp-height': toCssSize(height),\n };\n\n const isDisabled = isLoading || disabled || internalLoading;\n const showDropdown = !onRampAvailable && !!address && !isLoading;\n\n const renderContent = () => {\n if (text) return <span>{text}</span>;\n\n if (variant === 'short') {\n return (\n <div className=\"tp-btn-content\">\n <TonIcon bgColor={finalBgColor} textColor={finalTextColor} />\n <span>Pay</span>\n </div>\n );\n }\n\n return (\n <div className=\"tp-btn-content\">\n <span>Pay with</span>\n <TonIcon bgColor={finalBgColor} textColor={finalTextColor} />\n <span>Pay</span>\n </div>\n );\n };\n\n return (\n <div\n style={{ ...cssVars, ...style } as React.CSSProperties}\n className={classNames('tp-wrap', className)}\n >\n <div className=\"tp-btn-container\">\n <button\n type=\"button\"\n className={classNames(\n 'tp-btn',\n isLoading && 'processing',\n isLoading && 'loading',\n showDropdown ? 'with-menu' : 'no-menu',\n )}\n onClick={isDisabled ? undefined : onPayClick}\n disabled={isDisabled}\n >\n {isLoading ? (\n <div className=\"tp-btn-content\">\n <span className=\"tp-spinner\" />\n <span>{loadingText}</span>\n </div>\n ) : (\n renderContent()\n )}\n </button>\n {showDropdown && (\n <button\n type=\"button\"\n className=\"tp-arrow\"\n onClick={(e) => {\n e.stopPropagation();\n setShowMenu(!showMenu);\n }}\n disabled={isDisabled}\n >\n ▼\n </button>\n )}\n </div>\n\n {isLoading && (\n <div className=\"tp-retry-text\">\n Did the wallet fail to open?{' '}\n <span\n className=\"tp-retry-link\"\n onClick={() =>\n redirectToWallet ? redirectToWallet() : handleRetry()\n }\n >\n Click here\n </span>\n .\n </div>\n )}\n\n {showMenu && showDropdown && (\n <div className=\"tp-menu\">\n <div className=\"tp-menu-arrow\" />\n <div className=\"tp-menu-address\">\n {address?.slice(0, 4)}...{address?.slice(-4)}\n </div>\n <button\n className=\"tp-menu-item danger\"\n onClick={() => {\n handleDisconnect();\n setShowMenu(false);\n }}\n >\n <div className=\"tp-menu-icon\">\n <DisconnectIcon size={16} />\n </div>\n <span>Disconnect</span>\n </button>\n </div>\n )}\n\n <PaymentModal\n isOpen={isModalOpen}\n onClose={() => setIsModalOpen(false)}\n onPayWithCrypto={handlePayWithCrypto}\n walletAddress={address}\n onDisconnect={handleDisconnect}\n amount={amount ? String(amount) : '0.1'}\n currency={currency || 'TON'}\n itemTitle={itemTitle}\n fetchOnRampLink={handleFetchOnRampLink}\n onRampAvailable={onRampAvailable}\n onPaymentSuccess={onCardPaymentSuccess}\n isLoading={isLoading}\n />\n\n {errorMessage && (\n <NotificationRoot>\n <ErrorTransactionNotification text={errorMessage} />\n </NotificationRoot>\n )}\n </div>\n );\n};\n","export function shortenAddress(\n address: string | null | undefined,\n head: number = 4,\n tail: number = 4,\n): string {\n if (!address || typeof address !== 'string') return '';\n if (address.length <= head + tail + 3) return address;\n return `${address.slice(0, head)}...${address.slice(-tail)}`;\n}\n\nexport function toCssSize(value?: number | string): string | undefined {\n if (value === undefined) return undefined;\n return typeof value === 'number' ? `${value}px` : value;\n}\n\nexport function isGradient(value: string): boolean {\n return value.includes('gradient(');\n}\n\nexport function getMenuBgColor(bg: string): string {\n return isGradient(bg) ? '#0098EA' : bg;\n}\n\nexport function classNames(\n ...classes: (string | undefined | null | false)[]\n): string {\n return classes.filter(Boolean).join(' ');\n}\n\nexport async function getUserIp(): Promise<string> {\n try {\n const response = await fetch('https://api.ipify.org?format=json');\n const data = await response.json();\n return data.ip;\n } catch (error) {\n console.error('Failed to fetch user IP:', error);\n return '';\n }\n}\n","import * as React from 'react';\n\ninterface IconProps {\n size?: number;\n color?: string;\n className?: string;\n}\n\ntype TonIconProps = Omit<IconProps, 'color'> & {\n bgColor?: string;\n textColor?: string;\n};\n\nexport const TonIcon: React.FC<TonIconProps> = ({\n size = 24,\n bgColor = '#0098EA',\n textColor = 'white',\n className,\n}) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 26\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n aria-hidden\n >\n <g clipPath=\"url(#ton-icon-clip)\">\n <path\n d=\"M12 24C18.6274 24 24 18.6274 24 12C24 5.37257 18.6274 0 12 0C5.37257 0 0 5.37257 0 12C0 18.6274 5.37257 24 12 24Z\"\n fill={bgColor}\n />\n <path\n d=\"M16.0972 6.69763H7.9022C6.39543 6.69763 5.4404 8.32299 6.19846 9.63695L11.2561 18.4033C11.5862 18.9757 12.4133 18.9757 12.7433 18.4033L17.802 9.63695C18.559 8.32509 17.604 6.69763 16.0982 6.69763H16.0972ZM11.252 15.7744L10.1505 13.6426L7.49278 8.88922C7.31746 8.58497 7.53401 8.1951 7.90117 8.1951H11.251V15.7754L11.252 15.7744ZM16.5046 8.88819L13.8479 13.6437L12.7464 15.7744V8.19407H16.0962C16.4633 8.19407 16.6799 8.58395 16.5046 8.88819Z\"\n fill={textColor}\n />\n </g>\n <defs>\n <clipPath id=\"ton-icon-clip\">\n <rect width=\"24\" height=\"26\" fill=\"white\" />\n </clipPath>\n </defs>\n </svg>\n);\n\nexport const TonIconBlue: React.FC<IconProps> = ({ size = 24, className }) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n aria-hidden\n >\n <path\n d=\"M12 24C18.6274 24 24 18.6274 24 12C24 5.37257 18.6274 0 12 0C5.37257 0 0 5.37257 0 12C0 18.6274 5.37257 24 12 24Z\"\n fill=\"#0098EA\"\n />\n <path\n d=\"M16.0972 6.69763H7.9022C6.39543 6.69763 5.4404 8.32299 6.19846 9.63695L11.2561 18.4033C11.5862 18.9757 12.4133 18.9757 12.7433 18.4033L17.802 9.63695C18.559 8.32509 17.604 6.69763 16.0982 6.69763H16.0972ZM11.252 15.7744L10.1505 13.6426L7.49278 8.88922C7.31746 8.58497 7.53401 8.1951 7.90117 8.1951H11.251V15.7754L11.252 15.7744ZM16.5046 8.88819L13.8479 13.6437L12.7464 15.7744V8.19407H16.0962C16.4633 8.19407 16.6799 8.58395 16.5046 8.88819Z\"\n fill=\"white\"\n />\n </svg>\n);\n\nexport const CopyIcon: React.FC<IconProps> = ({\n size = 24,\n color = 'currentColor',\n className,\n}) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n aria-hidden\n >\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M7.76228 2.09998H10.2378C11.0458 2.09997 11.7067 2.09996 12.2438 2.14384C12.7997 2.18926 13.3017 2.28614 13.7706 2.52505C14.5045 2.89896 15.1011 3.49558 15.475 4.22941C15.7139 4.6983 15.8108 5.20038 15.8562 5.75629C15.9001 6.29337 15.9001 6.95422 15.9001 7.76227V8.1H16.2377C17.0457 8.09999 17.7066 8.09998 18.2437 8.14386C18.7996 8.18928 19.3017 8.28616 19.7705 8.52507C20.5044 8.89898 21.101 9.4956 21.4749 10.2294C21.7138 10.6983 21.8107 11.2004 21.8561 11.7563C21.9 12.2934 21.9 12.9542 21.9 13.7623V16.2377C21.9 17.0458 21.9 17.7066 21.8561 18.2437C21.8107 18.7996 21.7138 19.3017 21.4749 19.7706C21.101 20.5044 20.5044 21.101 19.7705 21.4749C19.3017 21.7138 18.7996 21.8107 18.2437 21.8561C17.7066 21.9 17.0458 21.9 16.2378 21.9H13.7623C12.9543 21.9 12.2934 21.9 11.7563 21.8561C11.2004 21.8107 10.6983 21.7138 10.2294 21.4749C9.49561 21.101 8.89898 20.5044 8.52508 19.7706C8.28616 19.3017 8.18928 18.7996 8.14386 18.2437C8.09998 17.7066 8.09999 17.0458 8.1 16.2377V15.9H7.76227C6.95426 15.9 6.29335 15.9 5.75629 15.8561C5.20038 15.8107 4.6983 15.7138 4.22941 15.4749C3.49558 15.101 2.89896 14.5044 2.52505 13.7705C2.28614 13.3017 2.18926 12.7996 2.14384 12.2437C2.09996 11.7066 2.09997 11.0458 2.09998 10.2377V7.76228C2.09997 6.95424 2.09996 6.29336 2.14384 5.75629C2.18926 5.20038 2.28614 4.6983 2.52505 4.22941C2.89896 3.49558 3.49558 2.89896 4.22941 2.52505C4.6983 2.28614 5.20038 2.18926 5.75629 2.14384C6.29336 2.09996 6.95425 2.09997 7.76228 2.09998ZM8.1 14.1V13.7623C8.09999 12.9542 8.09998 12.2934 8.14386 11.7563C8.18928 11.2004 8.28616 10.6983 8.52508 10.2294C8.89898 9.4956 9.49561 8.89898 10.2294 8.52507C10.6983 8.28616 11.2004 8.18928 11.7563 8.14386C12.2934 8.09998 12.9542 8.09999 13.7623 8.1H14.1001V7.79998C14.1001 6.94505 14.0994 6.35798 14.0622 5.90287C14.0259 5.45827 13.9593 5.21944 13.8712 5.0466C13.6699 4.65146 13.3486 4.3302 12.9535 4.12886C12.7806 4.04079 12.5418 3.97419 12.0972 3.93786C11.6421 3.90068 11.055 3.89998 10.2001 3.89998H7.79998C6.94505 3.89998 6.35798 3.90068 5.90287 3.93786C5.45827 3.97419 5.21944 4.04079 5.0466 4.12886C4.65146 4.3302 4.3302 4.65146 4.12886 5.0466C4.04079 5.21944 3.97419 5.45827 3.93786 5.90287C3.90068 6.35798 3.89998 6.94505 3.89998 7.79998V10.2C3.89998 11.0549 3.90068 11.642 3.93786 12.0971C3.97419 12.5417 4.04079 12.7805 4.12886 12.9534C4.3302 13.3485 4.65146 13.6698 5.0466 13.8711C5.21944 13.9592 5.45827 14.0258 5.90287 14.0621C6.35798 14.0993 6.94505 14.1 7.79998 14.1H8.1ZM11.0466 10.1289C11.2195 10.0408 11.4583 9.97421 11.9029 9.93788C12.358 9.9007 12.9451 9.9 13.8 9.9H16.2C17.0549 9.9 17.642 9.9007 18.0971 9.93788C18.5417 9.97421 18.7805 10.0408 18.9534 10.1289C19.3485 10.3302 19.6698 10.6515 19.8711 11.0466C19.9592 11.2195 20.0258 11.4583 20.0621 11.9029C20.0993 12.358 20.1 12.9451 20.1 13.8V16.2C20.1 17.0549 20.0993 17.642 20.0621 18.0971C20.0258 18.5417 19.9592 18.7805 19.8711 18.9534C19.6698 19.3485 19.3485 19.6698 18.9534 19.8711C18.7805 19.9592 18.5417 20.0258 18.0971 20.0621C17.642 20.0993 17.0549 20.1 16.2 20.1H13.8C12.9451 20.1 12.358 20.0993 11.9029 20.0621C11.4583 20.0258 11.2195 19.9592 11.0466 19.8711C10.6515 19.6698 10.3302 19.3485 10.1289 18.9534C10.0408 18.7805 9.97421 18.5417 9.93788 18.0971C9.9007 17.642 9.9 17.0549 9.9 16.2V13.8C9.9 12.9451 9.9007 12.358 9.93788 11.9029C9.97421 11.4583 10.0408 11.2195 10.1289 11.0466C10.3302 10.6515 10.6515 10.3302 11.0466 10.1289Z\"\n fill={color}\n />\n </svg>\n);\n\nexport const DisconnectIcon: React.FC<IconProps> = ({\n size = 24,\n color = 'currentColor',\n className,\n}) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n aria-hidden\n >\n <path\n d=\"M8.7624 3.10001C7.95435 3.1 7.29349 3.09999 6.75642 3.14387C6.2005 3.18929 5.69842 3.28617 5.22954 3.52508C4.4957 3.89899 3.89908 4.49561 3.52517 5.22944C3.28626 5.69833 3.18938 6.20041 3.14396 6.75632C3.10008 7.2934 3.10009 7.95424 3.1001 8.76229V15.2377C3.10009 16.0458 3.10008 16.7066 3.14396 17.2437C3.18938 17.7996 3.28626 18.3017 3.52517 18.7706C3.89908 19.5044 4.4957 20.101 5.22954 20.4749C5.69842 20.7138 6.2005 20.8107 6.75642 20.8561C7.29349 20.9 7.95434 20.9 8.76239 20.9H12.0001C12.4972 20.9 12.9001 20.4971 12.9001 20C12.9001 19.503 12.4972 19.1 12.0001 19.1H8.8001C7.94517 19.1 7.3581 19.0993 6.90299 19.0621C6.45839 19.0258 6.21956 18.9592 6.04672 18.8711C5.65158 18.6698 5.33032 18.3485 5.12898 17.9534C5.04092 17.7805 4.97431 17.5417 4.93798 17.0971C4.9008 16.642 4.9001 16.0549 4.9001 15.2V8.80001C4.9001 7.94508 4.9008 7.35801 4.93798 6.9029C4.97431 6.4583 5.04092 6.21947 5.12898 6.04663C5.33032 5.65149 5.65158 5.33023 6.04672 5.12889C6.21956 5.04082 6.45839 4.97422 6.90299 4.93789C7.3581 4.90071 7.94517 4.90001 8.8001 4.90001H12.0001C12.4972 4.90001 12.9001 4.49706 12.9001 4.00001C12.9001 3.50295 12.4972 3.10001 12.0001 3.10001H8.7624Z\"\n fill={color}\n />\n <path\n d=\"M17.6364 7.3636C17.2849 7.01212 16.7151 7.01212 16.3636 7.3636C16.0121 7.71507 16.0121 8.28492 16.3636 8.63639L18.8272 11.1H9.00001C8.50295 11.1 8.10001 11.5029 8.10001 12C8.10001 12.497 8.50295 12.9 9.00001 12.9H18.8272L16.3636 15.3636C16.0121 15.7151 16.0121 16.2849 16.3636 16.6364C16.7151 16.9879 17.2849 16.9879 17.6364 16.6364L21.6364 12.6364C21.9879 12.2849 21.9879 11.7151 21.6364 11.3636L17.6364 7.3636Z\"\n fill={color}\n />\n </svg>\n);\n\nexport const CloseIcon: React.FC<IconProps> = ({\n size = 24,\n color = 'currentColor',\n className,\n}) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 32 32\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n aria-hidden\n >\n <rect width=\"32\" height=\"32\" rx=\"8\" fill=\"#F4F4F4\" />\n <path\n d=\"M8.99933 22.251L22.2501 9.00026\"\n stroke=\"#7A7A7A\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n />\n <path\n d=\"M8.99933 9.08296L22.2501 22.3337\"\n stroke=\"#7A7A7A\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n />\n </svg>\n);\n\nexport const BackIcon: React.FC<IconProps> = ({\n size = 24,\n color = 'currentColor',\n className,\n}) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n aria-hidden\n >\n <path\n d=\"M19 12H5\"\n stroke={color}\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n <path\n d=\"M12 19L5 12L12 5\"\n stroke={color}\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n);\n\nexport const CheckIcon: React.FC<IconProps> = ({\n size = 24,\n color = 'currentColor',\n className,\n}) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n aria-hidden\n >\n <path\n d=\"M20 6L9 17L4 12\"\n stroke={color}\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n);\n\nexport const ErrorIcon: React.FC<IconProps> = ({\n size = 24,\n color = 'currentColor',\n className,\n}) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n aria-hidden\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke={color} strokeWidth=\"2\" />\n <path d=\"M12 8V12\" stroke={color} strokeWidth=\"2\" strokeLinecap=\"round\" />\n <circle cx=\"12\" cy=\"16\" r=\"1\" fill={color} />\n </svg>\n);\n\nexport const MenuIcon: React.FC<IconProps> = ({\n size = 24,\n color = 'currentColor',\n className,\n}) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n aria-hidden\n >\n <circle cx=\"12\" cy=\"12\" r=\"2\" fill={color} />\n <circle cx=\"19\" cy=\"12\" r=\"2\" fill={color} />\n <circle cx=\"5\" cy=\"12\" r=\"2\" fill={color} />\n </svg>\n);\n\nexport const ErrorDotIcon: React.FC<{ color?: string }> = ({\n color = '#FF5252',\n}) => (\n <svg\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden\n >\n <circle cx=\"12\" cy=\"12\" r=\"11\" fill={color} />\n <path\n d=\"M7.864 9.136A1.5 1.5 0 0 1 9.136 7.864L12 10.727 14.864 7.864A1.5 1.5 0 0 1 16.136 9.136L13.273 12 16.136 14.864a1.5 1.5 0 0 1-2.272 2.272L12 13.273 9.136 17.136A1.5 1.5 0 1 1 7.864 14.864L10.727 12 7.864 9.136Z\"\n fill=\"#fff\"\n />\n </svg>\n);\n\nexport const SpinnerIcon: React.FC<{ className?: string }> = ({\n className,\n}) => <span className={className} aria-hidden />;\n\nexport const CardIcon: React.FC<IconProps> = ({ size = 20, className }) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n aria-hidden\n >\n <path\n d=\"M15.4688 5H4.53125C3.68693 5 3 5.71369 3 6.59091V13.4091C3 14.2863 3.68693 15 4.53125 15H15.4688C16.3131 15 17 14.2863 17 13.4091V6.59091C17 5.71369 16.3131 5 15.4688 5ZM4.53125 5.90909H15.4688C15.8306 5.90909 16.125 6.21497 16.125 6.59091V7.5H3.875V6.59091C3.875 6.21497 4.16941 5.90909 4.53125 5.90909ZM15.4688 14.0909H4.53125C4.16941 14.0909 3.875 13.785 3.875 13.4091V8.40909H16.125V13.4091C16.125 13.785 15.8306 14.0909 15.4688 14.0909Z\"\n fill=\"white\"\n />\n <path\n d=\"M6.33333 13H5.66667C5.2985 13 5 12.7015 5 12.3333V11.6667C5 11.2985 5.2985 11 5.66667 11H6.33333C6.7015 11 7 11.2985 7 11.6667V12.3333C7 12.7015 6.7015 13 6.33333 13Z\"\n fill=\"white\"\n />\n </svg>\n);\n\nexport const CryptoIcon: React.FC<IconProps> = ({ size = 20, className }) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n aria-hidden\n >\n <path\n d=\"M14.3896 3.5H5.61006C3.99582 3.5 2.97267 5.24122 3.7848 6.64885L9.20318 16.0401C9.55683 16.6533 10.4429 16.6533 10.7965 16.0401L16.216 6.64885C17.027 5.24347 16.0028 3.5 14.3896 3.5ZM9.19879 13.2238L8.01872 10.9401L5.17144 5.84782C4.98361 5.52189 5.21561 5.10422 5.60896 5.10422H9.19772V13.225L9.19879 13.2238ZM14.826 5.84671L11.9798 10.9412L10.7998 13.2238V5.10312H14.3885C14.7818 5.10312 15.0138 5.52078 14.826 5.84671Z\"\n fill=\"white\"\n />\n </svg>\n);\n\nexport const InfoIcon: React.FC<IconProps> = ({\n size = 24,\n color = 'currentColor',\n className,\n}) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n aria-hidden\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke={color} strokeWidth=\"2\" />\n <path d=\"M12 16V12\" stroke={color} strokeWidth=\"2\" strokeLinecap=\"round\" />\n <circle cx=\"12\" cy=\"8\" r=\"1\" fill={color} />\n </svg>\n);\n\nexport const TonPayLogo: React.FC<IconProps> = ({ className }) => (\n <div\n className={className}\n style={{ display: 'flex', alignItems: 'center', gap: '4px' }}\n >\n <TonIconBlue size={26} />\n <span\n style={{\n fontWeight: 600,\n fontSize: '18px',\n lineHeight: '24px',\n color: '#000000',\n letterSpacing: '-0.5px',\n }}\n >\n Pay\n </span>\n </div>\n);\n","import * as React from 'react';\nimport type { NotificationProps } from '../../types';\n\nconst notificationStyles = `\n.tp-noti-root{position:fixed;top:16px;right:16px;z-index:10000;display:flex;flex-direction:column;gap:10px}\n.tp-noti-card{width:256px;padding:12px 16px;display:flex;gap:9px;align-items:flex-start;background:#ffffff;color:#111827;border-radius:16px;box-shadow:0 4px 24px rgba(0,0,0,.16);animation:tp-fade-in .2s ease}\n.tp-noti-content{flex:1;min-width:0}\n.tp-noti-title{font-size:15px;font-weight:700;line-height:20px;margin:0}\n.tp-noti-text{margin-top:4px;color:#6b7280;font-size:13px;line-height:18px;word-break:break-word}\n.tp-noti-icon{width:24px;height:24px;margin-top:2px;flex:0 0 auto}\n`;\n\nlet stylesInjected = false;\n\nfunction injectNotificationStyles(): void {\n if (typeof document === 'undefined' || stylesInjected) return;\n const style = document.createElement('style');\n style.id = 'tonpay-notification-styles';\n style.textContent = notificationStyles;\n document.head.appendChild(style);\n stylesInjected = true;\n}\n\ninjectNotificationStyles();\n\nexport const NotificationCard: React.FC<NotificationProps> = ({\n title,\n text,\n icon,\n className,\n style,\n}) => {\n return (\n <div\n className={['tp-noti-card', className].filter(Boolean).join(' ')}\n style={style}\n >\n <div className=\"tp-noti-content\">\n <h3 className=\"tp-noti-title\">{title}</h3>\n {text && <div className=\"tp-noti-text\">{text}</div>}\n </div>\n {icon && <div className=\"tp-noti-icon\">{icon}</div>}\n </div>\n );\n};\n\nexport const NotificationRoot: React.FC<{ children?: React.ReactNode }> = ({\n children,\n}) => {\n return <div className=\"tp-noti-root\">{children}</div>;\n};\n","import * as React from 'react';\nimport { ErrorDotIcon } from '../icons';\nimport { NotificationCard } from './Notification';\n\ninterface ErrorTransactionNotificationProps {\n text?: string;\n className?: string;\n style?: React.CSSProperties;\n}\n\nexport const ErrorTransactionNotification: React.FC<\n ErrorTransactionNotificationProps\n> = ({ text, className, style }) => {\n return (\n <NotificationCard\n title=\"Transaction cancelled\"\n text={text}\n icon={<ErrorDotIcon />}\n className={className}\n style={style}\n />\n );\n};\n","import * as React from 'react';\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport BottomSheet from '../bottom-sheet/BottomSheet';\nimport {\n TonIconBlue,\n CloseIcon,\n BackIcon,\n CheckIcon,\n ErrorIcon,\n MenuIcon,\n CardIcon,\n CryptoIcon,\n TonPayLogo,\n} from '../icons';\nimport type { PaymentModalProps, PaymentViewState } from '../../types';\nimport './PaymentModal.css';\n\nconst PROVIDER = { id: 'moonpay', name: 'Moonpay', iconClass: 'icon-moonpay' };\nconst IFRAME_LOAD_TIMEOUT = 30000;\n\nexport const PaymentModal: React.FC<PaymentModalProps> = ({\n isOpen,\n onClose,\n onPayWithCrypto,\n amount = '0.1',\n currency = 'TON',\n itemTitle,\n walletAddress,\n onDisconnect,\n fetchOnRampLink,\n onRampAvailable = false,\n onPaymentSuccess,\n isLoading = false,\n}) => {\n const [view, setView] = useState<PaymentViewState>('main');\n const [isMobile, setIsMobile] = useState(false);\n const [showMenu, setShowMenu] = useState(false);\n const [onRampLink, setOnRampLink] = useState<string | null>(null);\n const [onRampError, setOnRampError] = useState<string | null>(null);\n const [isOnRampLoading, setIsOnRampLoading] = useState(false);\n const [iframeLoaded, setIframeLoaded] = useState(false);\n const [iframeError, setIframeError] = useState<string | null>(null);\n const [sheetDetent, setSheetDetent] = useState<number[]>([0.55]);\n\n const iframeTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const fetchStartedRef = useRef(false);\n const contentRef = useRef<HTMLDivElement>(null);\n\n const handlePaymentSuccess = useCallback(() => {\n if (iframeTimeoutRef.current) {\n clearTimeout(iframeTimeoutRef.current);\n }\n setView('success');\n onPaymentSuccess?.();\n setTimeout(() => {\n onClose();\n }, 2000);\n }, [onClose, onPaymentSuccess]);\n\n const handlePaymentError = useCallback((errorMessage: string) => {\n if (iframeTimeoutRef.current) {\n clearTimeout(iframeTimeoutRef.current);\n }\n setIframeError(errorMessage);\n setView('error');\n }, []);\n\n const handleRetry = useCallback(() => {\n setOnRampLink(null);\n setOnRampError(null);\n setIframeError(null);\n setIframeLoaded(false);\n fetchStartedRef.current = false;\n setView('card');\n }, []);\n\n useEffect(() => {\n const handleMessage = (event: MessageEvent) => {\n if (event.data?.type === 'TONPAY_PAYMENT_SUCCESS') {\n handlePaymentSuccess();\n }\n if (event.data?.type === 'TONPAY_PAYMENT_ERROR') {\n const payload = event.data.payload;\n handlePaymentError(payload?.message || 'Payment failed');\n }\n if (event.data?.type === 'TONPAY_IFRAME_LOADED') {\n setIframeLoaded(true);\n if (iframeTimeoutRef.current) {\n clearTimeout(iframeTimeoutRef.current);\n }\n }\n if (event.data?.type === 'TONPAY_MOONPAY_EVENT') {\n const payload = event.data.payload;\n if (\n payload?.type === 'onTransactionCompleted' ||\n payload?.eventName === 'transactionCompleted'\n ) {\n handlePaymentSuccess();\n }\n if (\n payload?.type === 'onTransactionFailed' ||\n payload?.eventName === 'transactionFailed'\n ) {\n handlePaymentError(payload?.message || 'Transaction failed');\n }\n }\n };\n\n window.addEventListener('message', handleMessage);\n return () => window.removeEventListener('message', handleMessage);\n }, [handlePaymentSuccess, handlePaymentError]);\n\n useEffect(() => {\n if (view !== 'card') {\n fetchStartedRef.current = false;\n }\n }, [view]);\n\n useEffect(() => {\n if (\n view === 'card' &&\n !onRampLink &&\n !onRampError &&\n fetchOnRampLink &&\n !fetchStartedRef.current\n ) {\n fetchStartedRef.current = true;\n setIsOnRampLoading(true);\n setOnRampError(null);\n setIframeLoaded(false);\n setIframeError(null);\n\n fetchOnRampLink(PROVIDER.id)\n .then((link: string) => {\n setOnRampLink(link);\n iframeTimeoutRef.current = setTimeout(() => {\n if (!iframeLoaded) {\n handlePaymentError(\n 'Payment service is taking too long to load. Please try again.',\n );\n }\n }, IFRAME_LOAD_TIMEOUT);\n })\n .catch((err: any) => {\n const errorMsg = err?.message || 'Failed to initialize payment';\n setOnRampError(errorMsg);\n fetchStartedRef.current = false;\n })\n .finally(() => setIsOnRampLoading(false));\n }\n\n return () => {\n if (iframeTimeoutRef.current) {\n clearTimeout(iframeTimeoutRef.current);\n }\n };\n }, [\n view,\n onRampLink,\n onRampError,\n fetchOnRampLink,\n iframeLoaded,\n handlePaymentError,\n ]);\n\n useEffect(() => {\n const checkMobile = () => {\n setIsMobile(window.innerWidth < 768);\n };\n checkMobile();\n window.addEventListener('resize', checkMobile);\n return () => window.removeEventListener('resize', checkMobile);\n }, []);\n\n useEffect(() => {\n if (isOpen) {\n setView('main');\n setShowMenu(false);\n setOnRampLink(null);\n setOnRampError(null);\n setIframeLoaded(false);\n setIframeError(null);\n if (iframeTimeoutRef.current) {\n clearTimeout(iframeTimeoutRef.current);\n }\n }\n }, [isOpen]);\n\n useEffect(() => {\n if (!isMobile || !isOpen) return;\n\n const updateHeight = () => {\n if (view === 'card') {\n setSheetDetent((prev) => (prev[0] === 0.9 ? prev : [0.9]));\n return;\n }\n if (contentRef.current) {\n const height = contentRef.current.scrollHeight;\n const windowHeight = window.innerHeight;\n const detent = Math.min((height + 40) / windowHeight, 0.95);\n setSheetDetent((prev) =>\n Math.abs(prev[0] - detent) < 0.01 ? prev : [detent],\n );\n }\n };\n\n setTimeout(updateHeight, 50);\n\n const observer = new ResizeObserver(updateHeight);\n if (contentRef.current) {\n observer.observe(contentRef.current);\n }\n\n return () => observer.disconnect();\n }, [view, isMobile, isOpen]);\n\n const handleBack = () => setView('main');\n\n const handleIframeLoad = () => {\n setIframeLoaded(true);\n if (iframeTimeoutRef.current) {\n clearTimeout(iframeTimeoutRef.current);\n }\n };\n\n const handleIframeError = () => {\n handlePaymentError('Failed to load payment service. Please try again.');\n };\n\n const renderHeader = () => (\n <div className=\"pm-header\">\n <div className=\"pm-header-left\">\n {view !== 'main' ? (\n <button className=\"pm-back-btn\" onClick={handleBack}>\n <BackIcon />\n </button>\n ) : (\n <TonPayLogo />\n )}\n </div>\n {view !== 'main' && <div className=\"pm-title\">New Purchase</div>}\n <div className=\"pm-header-right\">\n {walletAddress && view === 'main' && (\n <div style={{ position: 'relative' }}>\n <button\n className=\"pm-close-btn\"\n onClick={() => setShowMenu(!showMenu)}\n >\n <MenuIcon />\n </button>\n {showMenu && (\n <div className=\"pm-menu-dropdown\">\n <div className=\"pm-menu-item disabled\">\n {walletAddress.slice(0, 4)}...{walletAddress.slice(-4)}\n </div>\n <div\n className=\"pm-menu-item danger\"\n onClick={() => {\n onDisconnect?.();\n setShowMenu(false);\n }}\n >\n Disconnect\n </div>\n </div>\n )}\n </div>\n )}\n <button className=\"pm-close-btn\" onClick={onClose}>\n <CloseIcon size={32} />\n </button>\n </div>\n </div>\n );\n\n const renderMainView = () => (\n <div className=\"pm-body-main\">\n <div className=\"pm-main-container\">\n <h2 className=\"pm-main-title\">New Purchase</h2>\n <div className=\"pm-amount-container\">\n <span className=\"pm-amount-label\">Amount</span>\n <div className=\"pm-amount-value\">\n {amount} {currency} <TonIconBlue />\n </div>\n </div>\n {itemTitle && (\n <div className=\"pm-order-info\">\n <span className=\"pm-order-text\">{itemTitle}</span>\n </div>\n )}\n </div>\n <div className=\"pm-actions-card\">\n <div className=\"pm-actions\">\n <button\n className={`pm-btn ${isLoading ? 'processing' : 'pm-btn-primary'}`}\n onClick={isLoading ? undefined : onPayWithCrypto}\n disabled={isLoading}\n >\n {isLoading ? (\n <div\n style={{ display: 'flex', alignItems: 'center', gap: '4px' }}\n >\n <div className=\"pm-btn-spinner\" />\n <span>Processing</span>\n </div>\n ) : (\n <div\n style={{ display: 'flex', alignItems: 'center', gap: '4px' }}\n >\n <CryptoIcon />\n <span>Pay with Crypto</span>\n </div>\n )}\n </button>\n\n {isLoading && (\n <div className=\"pm-retry-link\">\n Did the wallet fail to open?{' '}\n <span className=\"pm-retry-action\" onClick={onPayWithCrypto}>\n Click here\n </span>\n .\n </div>\n )}\n\n {onRampAvailable && !isLoading && (\n <button\n className=\"pm-btn pm-btn-black\"\n onClick={() => setView('card')}\n >\n <CardIcon />\n <span>Pay with Card</span>\n </button>\n )}\n </div>\n </div>\n </div>\n );\n\n const renderCardView = () => (\n <div className=\"pm-iframe-container\">\n {isOnRampLoading && (\n <div className=\"pm-loading-container\">\n <div className=\"pm-spinner\" />\n <p>Loading {PROVIDER.name}...</p>\n </div>\n )}\n {onRampError && (\n <div className=\"pm-error-inline\">\n <ErrorIcon />\n <p>{onRampError}</p>\n <button className=\"pm-btn pm-btn-outline\" onClick={handleRetry}>\n Try Again\n </button>\n </div>\n )}\n {onRampLink && !isOnRampLoading && !onRampError && (\n <>\n {!iframeLoaded && (\n <div className=\"pm-loading-overlay\">\n <div className=\"pm-spinner\" />\n </div>\n )}\n <iframe\n src={onRampLink}\n title={PROVIDER.name}\n width=\"100%\"\n height=\"100%\"\n frameBorder=\"0\"\n allow=\"accelerometer; autoplay; camera; gyroscope; payment\"\n onLoad={handleIframeLoad}\n onError={handleIframeError}\n style={{ opacity: iframeLoaded ? 1 : 0 }}\n />\n </>\n )}\n </div>\n );\n\n const renderSuccessView = () => (\n <div className=\"pm-success-container\">\n <div className=\"pm-success-icon\">\n <CheckIcon />\n </div>\n <h2 className=\"pm-success-title\">Payment Successful</h2>\n <p className=\"pm-success-text\">Your purchase is being processed</p>\n </div>\n );\n\n const renderErrorView = () => (\n <div className=\"pm-error-container\">\n <div className=\"pm-error-icon\">\n <ErrorIcon />\n </div>\n <h2 className=\"pm-error-title\">Payment Failed</h2>\n <p className=\"pm-error-text\">{iframeError || 'Something went wrong'}</p>\n <div className=\"pm-error-actions\">\n <button className=\"pm-btn pm-btn-primary\" onClick={handleRetry}>\n Try Again\n </button>\n <button className=\"pm-btn pm-btn-outline\" onClick={onPayWithCrypto}>\n Pay with Crypto Instead\n </button>\n </div>\n </div>\n );\n\n const renderContent = () => (\n <div\n className=\"pm-content\"\n style={{ height: view === 'card' ? '100%' : 'auto' }}\n >\n {view !== 'success' && view !== 'error' && renderHeader()}\n {view === 'main' && renderMainView()}\n {view === 'card' && renderCardView()}\n {view === 'success' && renderSuccessView()}\n {view === 'error' && renderErrorView()}\n </div>\n );\n\n if (isMobile) {\n return (\n <BottomSheet\n isOpen={isOpen}\n onClose={onClose}\n detents={sheetDetent}\n initialDetent={0}\n enableSwipeToClose={view === 'main'}\n >\n <div\n ref={contentRef}\n style={{ height: view === 'card' ? '100%' : 'auto' }}\n >\n {renderContent()}\n </div>\n </BottomSheet>\n );\n }\n\n if (!isOpen) return null;\n\n return (\n <div className=\"pm-desktop-overlay\" onClick={onClose}>\n <div className=\"pm-desktop-modal\" onClick={(e) => e.stopPropagation()}>\n {renderContent()}\n </div>\n </div>\n );\n};\n","import * as React from 'react';\nimport { useEffect, useRef, useState, useCallback } from 'react';\nimport './BottomSheet.css';\nimport type { BottomSheetProps } from '../../types';\n\nconst BottomSheet: React.FC<BottomSheetProps> = ({\n isOpen,\n onClose,\n detents = [0.5, 0.9],\n initialDetent = 0,\n children,\n className = '',\n backdropClassName = '',\n handleClassName = '',\n contentClassName = '',\n enableBackdropClose = true,\n enableSwipeToClose = true,\n maxHeight = '90vh',\n minHeight = '20vh',\n}) => {\n const sheetRef = useRef<HTMLDivElement>(null);\n const contentRef = useRef<HTMLDivElement>(null);\n const [currentDetent, setCurrentDetent] = useState(initialDetent);\n const [isDragging, setIsDragging] = useState(false);\n const [startY, setStartY] = useState(0);\n const [currentY, setCurrentY] = useState(0);\n const [isScrolling, setIsScrolling] = useState(false);\n const [sheetHeight, setSheetHeight] = useState(0);\n const [isClosing, setIsClosing] = useState(false);\n\n const touchStartYRef = useRef(0);\n const touchStartTimeRef = useRef(0);\n const mouseStartTimeRef = useRef(0);\n const scrollTopRef = useRef(0);\n const canDragRef = useRef(true);\n const closeTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const openTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const isClosingRef = useRef(false);\n const isSnappingRef = useRef(false);\n const targetDetentRef = useRef<number | null>(null);\n\n const getDetentValue = useCallback(\n (index: number): number => {\n const clampedIndex = Math.max(0, Math.min(index, detents.length - 1));\n return detents[clampedIndex];\n },\n [detents],\n );\n\n const calculateSheetHeight = useCallback((): number => {\n if (!sheetRef.current) return 0;\n const viewportHeight = window.innerHeight;\n const detentValue = getDetentValue(currentDetent);\n return viewportHeight * detentValue;\n }, [currentDetent, getDetentValue]);\n\n useEffect(() => {\n if (isOpen) {\n if (isClosingRef.current) return;\n\n isClosingRef.current = false;\n setIsClosing(false);\n setCurrentDetent(initialDetent);\n\n if (openTimeoutRef.current) {\n clearTimeout(openTimeoutRef.current);\n }\n\n setSheetHeight(0);\n\n openTimeoutRef.current = setTimeout(() => {\n const viewportHeight = window.innerHeight;\n const detentValue = getDetentValue(initialDetent);\n setSheetHeight(viewportHeight * detentValue);\n openTimeoutRef.current = null;\n }, 10);\n } else {\n if (openTimeoutRef.current) {\n clearTimeout(openTimeoutRef.current);\n openTimeoutRef.current = null;\n }\n setSheetHeight(0);\n setCurrentDetent(initialDetent);\n }\n }, [isOpen, initialDetent, getDetentValue]);\n\n const handleClose = useCallback(() => {\n if (isClosing || isClosingRef.current) return;\n\n isClosingRef.current = true;\n setIsClosing(true);\n setSheetHeight(0);\n setCurrentDetent(initialDetent);\n\n if (closeTimeoutRef.current) {\n clearTimeout(closeTimeoutRef.current);\n }\n\n closeTimeoutRef.current = setTimeout(() => {\n isClosingRef.current = false;\n setIsClosing(false);\n onClose();\n }, 200);\n }, [isClosing, onClose, initialDetent]);\n\n useEffect(() => {\n if (\n isOpen &&\n !isDragging &&\n !isSnappingRef.current &&\n targetDetentRef.current === null\n ) {\n const expectedHeight = calculateSheetHeight();\n const heightDiff = Math.abs(sheetHeight - expectedHeight);\n if (heightDiff > 1) {\n setSheetHeight(expectedHeight);\n }\n }\n }, [isOpen, isDragging, currentDetent, sheetHeight, calculateSheetHeight]);\n\n const findNearestDetent = useCallback(\n (position: number): number => {\n const viewportHeight = window.innerHeight;\n const normalizedPosition = position / viewportHeight;\n\n let nearestIndex = 0;\n let minDistance = Math.abs(normalizedPosition - detents[0]);\n\n detents.forEach((detent, index) => {\n const distance = Math.abs(normalizedPosition - detent);\n if (distance < minDistance) {\n minDistance = distance;\n nearestIndex = index;\n }\n });\n\n return nearestIndex;\n },\n [detents],\n );\n\n const snapToDetent = useCallback(\n (detentIndex: number) => {\n const clampedIndex = Math.max(\n 0,\n Math.min(detentIndex, detents.length - 1),\n );\n isSnappingRef.current = true;\n targetDetentRef.current = clampedIndex;\n const viewportHeight = window.innerHeight;\n const newHeight = viewportHeight * getDetentValue(clampedIndex);\n setCurrentDetent(clampedIndex);\n setSheetHeight(newHeight);\n setTimeout(() => {\n isSnappingRef.current = false;\n targetDetentRef.current = null;\n }, 200);\n },\n [detents, getDetentValue],\n );\n\n const handleTouchStart = useCallback(\n (e: React.TouchEvent) => {\n if (!isOpen || isClosing) return;\n\n const touch = e.touches[0];\n touchStartYRef.current = touch.clientY;\n touchStartTimeRef.current = Date.now();\n setStartY(touch.clientY);\n setCurrentY(touch.clientY);\n\n if (contentRef.current) {\n scrollTopRef.current = contentRef.current.scrollTop;\n setIsScrolling(false);\n canDragRef.current = scrollTopRef.current === 0;\n } else {\n canDragRef.current = true;\n }\n },\n [isOpen, isClosing],\n );\n\n const calculateNewHeight = useCallback(\n (touchY: number): number => {\n const viewportHeight = window.innerHeight;\n const baseHeight = viewportHeight * getDetentValue(currentDetent);\n const delta = touchStartYRef.current - touchY;\n const maxDetentHeight = viewportHeight * Math.max(...detents);\n const minDetentHeight = viewportHeight * Math.min(...detents);\n const isAtMaxDetent = currentDetent === detents.length - 1;\n const isAtMinDetent = currentDetent === 0;\n const isDraggingDown = delta < 0;\n\n let newHeight = baseHeight + delta;\n\n if (isAtMaxDetent) {\n newHeight = Math.max(0, Math.min(maxDetentHeight, newHeight));\n } else if (isAtMinDetent) {\n if (isDraggingDown && enableSwipeToClose) {\n newHeight = Math.max(0, Math.min(maxDetentHeight, newHeight));\n } else {\n newHeight = Math.max(\n minDetentHeight,\n Math.min(maxDetentHeight, newHeight),\n );\n }\n } else {\n newHeight = Math.max(\n minDetentHeight,\n Math.min(maxDetentHeight, newHeight),\n );\n }\n\n return newHeight;\n },\n [currentDetent, detents, enableSwipeToClose, getDetentValue],\n );\n\n const handleTouchMoveNative = useCallback(\n (e: TouchEvent) => {\n if (!isOpen || isClosing) return;\n\n const touch = e.touches[0];\n if (!touch) return;\n\n const deltaY = touch.clientY - touchStartYRef.current;\n const currentScrollTop = contentRef.current?.scrollTop ?? 0;\n const isContentScrollable = contentRef.current\n ? contentRef.current.scrollHeight > contentRef.current.clientHeight\n : false;\n\n if (!contentRef.current) return;\n\n const touchTarget = e.target as HTMLElement;\n const isTouchingContent = contentRef.current.contains(touchTarget);\n const isTouchingHandle =\n touchTarget.closest('.bottom-sheet-handle-container') !== null;\n\n if (isDragging) {\n if (e.cancelable) e.preventDefault();\n setCurrentY(touch.clientY);\n setSheetHeight(calculateNewHeight(touch.clientY));\n return;\n }\n\n if (isTouchingHandle && Math.abs(deltaY) > 5) {\n if (e.cancelable) e.preventDefault();\n setIsDragging(true);\n setIsScrolling(false);\n setCurrentY(touch.clientY);\n setSheetHeight(calculateNewHeight(touch.clientY));\n return;\n }\n\n if (isTouchingContent && !isDragging) {\n if (currentScrollTop > 0) return;\n if (deltaY < 0 && isContentScrollable) return;\n\n if (\n currentScrollTop === 0 &&\n deltaY > 0 &&\n canDragRef.current &&\n Math.abs(deltaY) > 5\n ) {\n if (e.cancelable) e.preventDefault();\n setIsDragging(true);\n setIsScrolling(false);\n setCurrentY(touch.clientY);\n setSheetHeight(calculateNewHeight(touch.clientY));\n }\n }\n },\n [isOpen, isDragging, isClosing, calculateNewHeight],\n );\n\n const handleTouchEnd = useCallback(\n (e?: React.TouchEvent | TouchEvent) => {\n if (!isOpen || isClosing) return;\n if (isScrolling) {\n setIsScrolling(false);\n return;\n }\n if (!isDragging) return;\n\n setIsDragging(false);\n const viewportHeight = window.innerHeight;\n const currentPosition = sheetHeight / viewportHeight;\n const minDetentValue = Math.min(...detents);\n const maxDetentValue = Math.max(...detents);\n const swipeDuration = Date.now() - touchStartTimeRef.current;\n\n const finalTouchY =\n e && 'changedTouches' in e && e.changedTouches[0]\n ? e.changedTouches[0].clientY\n : currentY;\n const swipeDistance = finalTouchY - touchStartYRef.current;\n const swipeVelocity =\n Math.abs(swipeDistance) / Math.max(swipeDuration, 1);\n const isAtMaxDetent = currentDetent === detents.length - 1;\n const isAtMinDetent = currentDetent === 0;\n const isSwipingDown = swipeDistance > 0;\n const isSwipingUp = swipeDistance < 0;\n\n if (isAtMaxDetent && isSwipingDown && enableSwipeToClose) {\n if (\n swipeDistance > 50 ||\n swipeVelocity > 0.3 ||\n currentPosition < maxDetentValue * 0.7\n ) {\n handleClose();\n return;\n }\n }\n\n if (isAtMinDetent && detents.length > 1) {\n const nextDetentIndex = Math.min(1, detents.length - 1);\n const nextDetentValue = detents[nextDetentIndex];\n const midpoint = (minDetentValue + nextDetentValue) / 2;\n\n const shouldGoToNextDetent =\n (isSwipingUp &&\n (Math.abs(swipeDistance) > 30 || swipeVelocity > 0.2)) ||\n currentPosition >= midpoint;\n\n if (shouldGoToNextDetent) {\n snapToDetent(nextDetentIndex);\n return;\n }\n }\n\n const shouldClose =\n enableSwipeToClose &&\n (currentPosition < minDetentValue * 0.5 ||\n (swipeDistance > 100 && swipeVelocity > 0.5) ||\n sheetHeight < viewportHeight * 0.15);\n\n if (shouldClose) {\n handleClose();\n return;\n }\n\n const nearestDetentIndex = findNearestDetent(sheetHeight);\n snapToDetent(nearestDetentIndex);\n },\n [\n isOpen,\n isDragging,\n isClosing,\n isScrolling,\n sheetHeight,\n currentY,\n currentDetent,\n detents,\n enableSwipeToClose,\n handleClose,\n findNearestDetent,\n snapToDetent,\n ],\n );\n\n const handleMouseDown = useCallback(\n (e: React.MouseEvent) => {\n if (!isOpen || isClosing) return;\n\n mouseStartTimeRef.current = Date.now();\n setStartY(e.clientY);\n setCurrentY(e.clientY);\n\n if (contentRef.current) {\n scrollTopRef.current = contentRef.current.scrollTop;\n setIsScrolling(false);\n canDragRef.current = scrollTopRef.current === 0;\n } else {\n canDragRef.current = true;\n }\n },\n [isOpen, isClosing],\n );\n\n const handleMouseMove = useCallback(\n (e: MouseEvent) => {\n if (!isOpen || !isDragging || isClosing) return;\n\n const viewportHeight = window.innerHeight;\n const baseHeight = viewportHeight * getDetentValue(currentDetent);\n const delta = startY - e.clientY;\n const maxDetentHeight = viewportHeight * Math.max(...detents);\n const minDetentHeight = viewportHeight * Math.min(...detents);\n const isAtMaxDetent = currentDetent === detents.length - 1;\n const isAtMinDetent = currentDetent === 0;\n\n let newHeight = baseHeight + delta;\n\n if (isAtMaxDetent) {\n newHeight = Math.max(0, Math.min(maxDetentHeight, newHeight));\n } else if (isAtMinDetent) {\n newHeight = Math.max(\n minDetentHeight,\n Math.min(maxDetentHeight, newHeight),\n );\n } else {\n newHeight = Math.max(\n minDetentHeight,\n Math.min(maxDetentHeight, newHeight),\n );\n }\n\n setSheetHeight(newHeight);\n setCurrentY(e.clientY);\n },\n [\n isOpen,\n isDragging,\n startY,\n currentDetent,\n detents,\n getDetentValue,\n isClosing,\n ],\n );\n\n const handleMouseUp = useCallback(() => {\n if (!isOpen || !isDragging || isClosing) return;\n\n setIsDragging(false);\n const viewportHeight = window.innerHeight;\n const currentPosition = sheetHeight / viewportHeight;\n const minDetentValue = Math.min(...detents);\n const maxDetentValue = Math.max(...detents);\n const swipeDuration = Date.now() - mouseStartTimeRef.current;\n const swipeDistance = currentY - startY;\n const swipeVelocity = Math.abs(swipeDistance) / Math.max(swipeDuration, 1);\n const isAtMaxDetent = currentDetent === detents.length - 1;\n const isAtMinDetent = currentDetent === 0;\n const isSwipingDown = swipeDistance > 0;\n const isSwipingUp = swipeDistance < 0;\n\n if (isAtMaxDetent && isSwipingDown && enableSwipeToClose) {\n if (\n swipeDistance > 50 ||\n swipeVelocity > 0.3 ||\n currentPosition < maxDetentValue * 0.7\n ) {\n handleClose();\n return;\n }\n }\n\n if (isAtMinDetent && detents.length > 1) {\n const nextDetentIndex = Math.min(1, detents.length - 1);\n const nextDetentValue = detents[nextDetentIndex];\n const midpoint = (minDetentValue + nextDetentValue) / 2;\n\n if (isSwipingUp && (swipeDistance < -30 || swipeVelocity > 0.2)) {\n snapToDetent(nextDetentIndex);\n return;\n }\n\n if (currentPosition >= midpoint) {\n snapToDetent(nextDetentIndex);\n return;\n }\n }\n\n const shouldClose =\n enableSwipeToClose &&\n (currentPosition < minDetentValue * 0.5 ||\n (swipeDistance > 100 && swipeVelocity > 0.5) ||\n sheetHeight < viewportHeight * 0.15);\n\n if (shouldClose) {\n handleClose();\n return;\n }\n\n const nearestDetentIndex = findNearestDetent(sheetHeight);\n snapToDetent(nearestDetentIndex);\n }, [\n isOpen,\n isDragging,\n isClosing,\n sheetHeight,\n currentY,\n startY,\n currentDetent,\n detents,\n enableSwipeToClose,\n handleClose,\n findNearestDetent,\n snapToDetent,\n ]);\n\n useEffect(() => {\n if (isDragging) {\n document.addEventListener('mousemove', handleMouseMove);\n document.addEventListener('mouseup', handleMouseUp);\n return () => {\n document.removeEventListener('mousemove', handleMouseMove);\n document.removeEventListener('mouseup', handleMouseUp);\n };\n }\n }, [isDragging, handleMouseMove, handleMouseUp]);\n\n const handleTouchEndNative = useCallback(\n (e: TouchEvent) => {\n handleTouchEnd(e);\n },\n [handleTouchEnd],\n );\n\n useEffect(() => {\n const sheetElement = sheetRef.current;\n if (!sheetElement || !isOpen) return;\n\n sheetElement.addEventListener('touchmove', handleTouchMoveNative, {\n passive: false,\n });\n sheetElement.addEventListener('touchend', handleTouchEndNative, {\n passive: false,\n });\n\n return () => {\n sheetElement.removeEventListener('touchmove', handleTouchMoveNative);\n sheetElement.removeEventListener('touchend', handleTouchEndNative);\n };\n }, [isOpen, handleTouchMoveNative, handleTouchEndNative]);\n\n useEffect(() => {\n if (isOpen) {\n document.body.style.overflow = 'hidden';\n } else {\n document.body.style.overflow = '';\n }\n\n return () => {\n document.body.style.overflow = '';\n if (closeTimeoutRef.current) {\n clearTimeout(closeTimeoutRef.current);\n closeTimeoutRef.current = null;\n }\n if (openTimeoutRef.current) {\n clearTimeout(openTimeoutRef.current);\n openTimeoutRef.current = null;\n }\n isClosingRef.current = false;\n };\n }, [isOpen]);\n\n if (!isOpen && !isClosing) return null;\n\n const maxHeightValue =\n typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight;\n const minHeightValue =\n typeof minHeight === 'number' ? `${minHeight}px` : minHeight;\n const currentHeight = `${sheetHeight}px`;\n\n const calculateBackdropOpacity = (): number => {\n if (!isOpen) return 0.5;\n const viewportHeight = window.innerHeight;\n const maxDetentHeight = viewportHeight * Math.max(...detents);\n if (maxDetentHeight === 0) return 0.5;\n const progress = Math.max(0, Math.min(1, sheetHeight / maxDetentHeight));\n return progress * 0.5;\n };\n\n const calculateBackdropBlur = (): number => {\n if (!isOpen) return 8;\n const viewportHeight = window.innerHeight;\n const maxDetentHeight = viewportHeight * Math.max(...detents);\n if (maxDetentHeight === 0) return 8;\n const progress = Math.max(0, Math.min(1, sheetHeight / maxDetentHeight));\n return progress * 8;\n };\n\n const backdropOpacity = calculateBackdropOpacity();\n const backdropBlur = calculateBackdropBlur();\n\n const backdropStyle: React.CSSProperties = {\n backgroundColor: `rgba(0, 0, 0, ${backdropOpacity})`,\n backdropFilter: `blur(${backdropBlur}px)`,\n WebkitBackdropFilter: `blur(${backdropBlur}px)`,\n ...(isDragging\n ? {}\n : {\n transition:\n 'background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1), backdrop-filter 0.3s cubic-bezier(0.4, 0, 0.2, 1)',\n }),\n };\n\n return (\n <div\n className={`bottom-sheet-backdrop ${backdropClassName} ${isClosing ? 'closing' : ''}`}\n onClick={enableBackdropClose ? handleClose : undefined}\n style={backdropStyle}\n >\n <div\n ref={sheetRef}\n className={`bottom-sheet ${className} ${isDragging ? 'dragging' : ''} ${isClosing ? 'closing' : ''}`}\n style={{\n height: currentHeight,\n maxHeight: maxHeightValue,\n minHeight: minHeightValue,\n }}\n onClick={(e) => e.stopPropagation()}\n onTouchStart={handleTouchStart}\n onMouseDown={handleMouseDown}\n >\n <div className=\"bottom-sheet-handle-container\">\n <div\n className={`bottom-sheet-handle ${handleClassName}`}\n onClick={(e) => {\n e.stopPropagation();\n handleClose();\n }}\n />\n </div>\n <div\n ref={contentRef}\n className={`bottom-sheet-content ${contentClassName} ${isScrolling ? 'scrolling' : ''}`}\n >\n {children}\n </div>\n </div>\n </div>\n );\n};\n\nexport default BottomSheet;\n","import styleInject from '#style-inject';styleInject(\".bottom-sheet-backdrop {\\n position: fixed;\\n top: 0;\\n left: 0;\\n right: 0;\\n bottom: 0;\\n background-color: rgba(0, 0, 0, 0.5);\\n backdrop-filter: blur(8px);\\n -webkit-backdrop-filter: blur(8px);\\n z-index: 1000;\\n display: flex;\\n align-items: flex-end;\\n justify-content: center;\\n animation: fadeIn 0.3s cubic-bezier(0.4, 0, 0.2, 1);\\n}\\n.bottom-sheet-backdrop.closing {\\n animation: fadeOut 0.3s cubic-bezier(0.4, 0, 0.2, 1);\\n}\\n@keyframes fadeIn {\\n from {\\n opacity: 0;\\n backdrop-filter: blur(0px);\\n -webkit-backdrop-filter: blur(0px);\\n }\\n to {\\n opacity: 1;\\n backdrop-filter: blur(8px);\\n -webkit-backdrop-filter: blur(8px);\\n }\\n}\\n@keyframes fadeOut {\\n from {\\n opacity: 1;\\n backdrop-filter: blur(8px);\\n -webkit-backdrop-filter: blur(8px);\\n }\\n to {\\n opacity: 0;\\n backdrop-filter: blur(0px);\\n -webkit-backdrop-filter: blur(0px);\\n }\\n}\\n.bottom-sheet {\\n position: relative;\\n width: 100%;\\n max-width: 100%;\\n background-color: #ffffff;\\n border-radius: 20px 20px 0 0;\\n box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.15);\\n display: flex;\\n flex-direction: column;\\n overflow: hidden;\\n touch-action: none;\\n transition: height 0.3s cubic-bezier(0.4, 0, 0.2, 1), transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);\\n will-change: height, transform;\\n transform: translateY(0);\\n}\\n.bottom-sheet.dragging {\\n transition: none;\\n}\\n.bottom-sheet.closing {\\n animation: slideDown 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards;\\n transition: height 0.3s cubic-bezier(0.4, 0, 0.2, 1);\\n}\\n@keyframes slideDown {\\n from {\\n transform: translateY(0);\\n }\\n to {\\n transform: translateY(100%);\\n }\\n}\\n.bottom-sheet-handle-container {\\n width: 100%;\\n padding: 12px 0 8px;\\n display: flex;\\n justify-content: center;\\n align-items: center;\\n flex-shrink: 0;\\n cursor: grab;\\n}\\n.bottom-sheet-handle-container:active {\\n cursor: grabbing;\\n}\\n.bottom-sheet-handle {\\n width: 80px;\\n height: 4px;\\n background-color: #d1d5db;\\n border-radius: 2px;\\n cursor: pointer;\\n}\\n.bottom-sheet-content {\\n flex: 1;\\n overflow-y: auto;\\n overflow-x: hidden;\\n -webkit-overflow-scrolling: touch;\\n overscroll-behavior: contain;\\n padding: 0;\\n min-height: 0;\\n}\\n.bottom-sheet-content.scrolling {\\n touch-action: pan-y;\\n}\\n@media (prefers-color-scheme: dark) {\\n .bottom-sheet {\\n background-color: #1c2633;\\n box-shadow: 0 -4px 24px rgba(0, 0, 0, 0.4);\\n }\\n .bottom-sheet-handle {\\n background-color: #4b5563;\\n }\\n}\\n\")","import styleInject from '#style-inject';styleInject(\":where(.pm-content, .pm-desktop-modal) {\\n color: #000000;\\n --pm-bg: #ffffff;\\n --pm-text: #000000;\\n --pm-text-secondary: #666666;\\n --pm-text-muted: #8c8c8c;\\n --pm-border: rgba(0, 0, 0, 0.1);\\n --pm-order-bg: #e9f5fa;\\n --pm-order-border: rgba(0, 100, 153, 0.03);\\n --pm-order-text: #004062;\\n --pm-hover-bg: #f9f9f9;\\n --pm-iframe-bg: #f9f9f9;\\n --pm-spinner-track: #e5e7eb;\\n --pm-actions-border: hsl(0, 0%, 92%);\\n}\\n.pm-content {\\n display: flex;\\n flex-direction: column;\\n align-items: center;\\n width: 100%;\\n font-family: \\\"Inter\\\", sans-serif;\\n}\\n@media (prefers-color-scheme: dark) {\\n :where(.pm-content, .pm-desktop-modal) {\\n color: #ffffff;\\n --pm-bg: #1f2937;\\n --pm-text: #ffffff;\\n --pm-text-secondary: #9ca3af;\\n --pm-text-muted: #6b7280;\\n --pm-border: rgba(255, 255, 255, 0.1);\\n --pm-order-bg: rgba(255, 255, 255, 0.05);\\n --pm-order-border: rgba(255, 255, 255, 0.1);\\n --pm-order-text: #ffffff;\\n --pm-hover-bg: rgba(255, 255, 255, 0.05);\\n --pm-iframe-bg: #111827;\\n --pm-spinner-track: #374151;\\n --pm-actions-border: rgba(255, 255, 255, 0.1);\\n }\\n}\\n.pm-header {\\n display: flex;\\n flex-direction: row;\\n justify-content: space-between;\\n align-items: center;\\n width: 100%;\\n padding: 16px;\\n box-sizing: border-box;\\n position: relative;\\n}\\n.bottom-sheet .pm-header {\\n padding: 0 16px;\\n}\\n.pm-header-left,\\n.pm-header-right {\\n display: flex;\\n align-items: center;\\n gap: 8px;\\n min-width: 40px;\\n}\\n.pm-header-right {\\n justify-content: flex-end;\\n}\\n.pm-title {\\n font-weight: 600;\\n font-size: 18px;\\n line-height: 22px;\\n text-align: center;\\n flex: 1;\\n}\\n.pm-close-btn,\\n.pm-back-btn {\\n background: none;\\n border: none;\\n cursor: pointer;\\n padding: 4px;\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n color: var(--pm-text, #000000);\\n opacity: 0.6;\\n}\\n.pm-close-btn:hover,\\n.pm-back-btn:hover {\\n opacity: 1;\\n}\\n.pm-body-main {\\n display: flex;\\n flex-direction: column;\\n align-items: center;\\n width: 100%;\\n padding: 0;\\n gap: 0;\\n box-sizing: border-box;\\n}\\n.pm-main-container {\\n display: flex;\\n flex-direction: column;\\n gap: 8px;\\n width: 100%;\\n padding: 0 16px;\\n}\\n.pm-main-title {\\n font-size: 18px;\\n font-weight: 600;\\n line-height: 20px;\\n text-align: center;\\n margin: 0 0 8px;\\n color: var(--pm-text, #000000);\\n}\\n.pm-amount-container {\\n display: flex;\\n flex-direction: column;\\n align-items: center;\\n gap: 4px;\\n margin-bottom: 16px;\\n}\\n.pm-amount-label {\\n font-weight: 500;\\n font-size: 12px;\\n line-height: 15px;\\n color: var(--pm-text-muted, #8c8c8c);\\n}\\n.pm-amount-value {\\n display: flex;\\n align-items: center;\\n gap: 8px;\\n font-weight: 600;\\n font-size: 24px;\\n line-height: 29px;\\n color: var(--pm-text, #000000);\\n}\\n.pm-order-info {\\n display: flex;\\n flex-direction: row;\\n justify-content: center;\\n align-items: center;\\n width: 100%;\\n height: 36px;\\n padding: 8px 16px;\\n margin-bottom: 24px;\\n gap: 4px;\\n background: var(--pm-order-bg, #e9f5fa);\\n border-radius: 9px;\\n box-sizing: border-box;\\n border: 1px solid var(--pm-order-border, rgba(0, 100, 153, 0.03));\\n}\\n.pm-order-text {\\n font-family: \\\"Inter\\\", sans-serif;\\n font-style: normal;\\n font-weight: 400;\\n font-size: 12px;\\n line-height: 15px;\\n text-align: center;\\n color: var(--pm-order-text, #004062);\\n flex: none;\\n}\\n.pm-actions-card {\\n width: 100%;\\n background: #f9f9f9;\\n border-top: 1px solid var(--pm-actions-border, hsl(0, 0%, 92%));\\n padding: 16px;\\n box-sizing: border-box;\\n}\\n@media (prefers-color-scheme: dark) {\\n .pm-actions-card {\\n background: var(--pm-hover-bg, rgba(255, 255, 255, 0.05));\\n }\\n}\\n.pm-actions {\\n display: flex;\\n flex-direction: column;\\n gap: 8px;\\n width: 100%;\\n}\\n.pm-btn {\\n display: flex;\\n flex-direction: row;\\n justify-content: center;\\n align-items: center;\\n padding: 13px 10px 10px;\\n gap: 4px;\\n width: 100%;\\n height: 47px;\\n border-radius: 8px;\\n border: none;\\n cursor: pointer;\\n font-weight: 500;\\n font-size: 14px;\\n line-height: 19px;\\n transition: opacity 0.2s;\\n}\\n.pm-btn:hover {\\n opacity: 0.9;\\n}\\n.pm-btn-primary {\\n background: #0098ea;\\n color: #ffffff;\\n}\\n.pm-btn-black {\\n background: #000000;\\n color: #ffffff;\\n}\\n.pm-btn.processing {\\n background: #e6e6e6;\\n color: #7a7a7a;\\n cursor: default;\\n}\\n.pm-btn.processing:hover {\\n opacity: 1;\\n}\\n.pm-footer {\\n display: flex;\\n flex-direction: row;\\n justify-content: center;\\n align-items: center;\\n gap: 8px;\\n margin-top: 12px;\\n font-size: 12px;\\n line-height: 15px;\\n}\\n.pm-footer-text {\\n color: var(--pm-text-muted, #808080);\\n}\\n.pm-footer-link {\\n color: #0098ea;\\n cursor: pointer;\\n text-decoration: none;\\n}\\n.pm-footer-link:hover {\\n text-decoration: underline;\\n}\\n.pm-desktop-overlay {\\n position: fixed;\\n top: 0;\\n left: 0;\\n right: 0;\\n bottom: 0;\\n background: rgba(0, 0, 0, 0.44);\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n z-index: 1000;\\n animation: fadeIn 0.2s ease-out;\\n}\\n.pm-desktop-modal {\\n width: 100%;\\n max-width: 414px;\\n margin: 16px;\\n background: var(--pm-bg, #ffffff);\\n border-radius: 32px;\\n box-shadow: 0px 4px 24px rgba(0, 0, 0, 0.25);\\n overflow: hidden;\\n position: relative;\\n animation: scaleIn 0.2s ease-out;\\n}\\n@media (prefers-color-scheme: dark) {\\n .pm-desktop-modal {\\n box-shadow: 0px 4px 32px rgba(0, 0, 0, 0.5);\\n }\\n}\\n@keyframes scaleIn {\\n from {\\n transform: scale(0.95);\\n opacity: 0;\\n }\\n to {\\n transform: scale(1);\\n opacity: 1;\\n }\\n}\\n.pm-iframe-container {\\n width: 100%;\\n height: 100%;\\n min-height: 400px;\\n display: flex;\\n flex-direction: column;\\n background: var(--pm-iframe-bg, #f9f9f9);\\n position: relative;\\n}\\n.pm-iframe-container iframe {\\n flex: 1;\\n width: 100%;\\n height: 100%;\\n border: none;\\n display: block;\\n}\\n.pm-menu-dropdown {\\n position: absolute;\\n top: 100%;\\n right: 0;\\n background: var(--pm-bg, #ffffff);\\n border: 1px solid var(--pm-border, rgba(0, 0, 0, 0.1));\\n border-radius: 8px;\\n box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.15);\\n z-index: 1001;\\n min-width: 150px;\\n overflow: hidden;\\n}\\n@media (prefers-color-scheme: dark) {\\n .pm-menu-dropdown {\\n box-shadow: 0px 4px 16px rgba(0, 0, 0, 0.4);\\n }\\n}\\n.pm-menu-item {\\n padding: 10px 16px;\\n font-size: 14px;\\n cursor: pointer;\\n color: var(--pm-text, #000000);\\n}\\n.pm-menu-item:hover {\\n background: var(--pm-hover-bg, #f9f9f9);\\n}\\n.pm-menu-item.danger {\\n color: #e74c3c;\\n}\\n.pm-menu-item.disabled {\\n cursor: default;\\n color: var(--pm-text-muted, #8c8c8c);\\n}\\n.pm-menu-item.disabled:hover {\\n background: transparent;\\n}\\n.icon-moonpay {\\n background: #7d00ff;\\n color: white;\\n}\\n.icon-onramper {\\n background: #000000;\\n color: white;\\n}\\n.icon-transak {\\n background:\\n linear-gradient(\\n 120deg,\\n #348bed 22.91%,\\n #2b80e8 36.09%,\\n #1461db 60.25%,\\n #0e57d7 66.11%);\\n color: white;\\n}\\n.icon-mercurio {\\n background: #000000;\\n color: white;\\n}\\n.pm-success-container {\\n display: flex;\\n flex-direction: column;\\n align-items: center;\\n justify-content: center;\\n padding: 60px 24px;\\n text-align: center;\\n}\\n.pm-success-icon {\\n width: 64px;\\n height: 64px;\\n background: #0098ea;\\n border-radius: 50%;\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n margin-bottom: 20px;\\n color: white;\\n}\\n.pm-success-icon svg {\\n width: 32px;\\n height: 32px;\\n}\\n.pm-success-title {\\n font-size: 24px;\\n font-weight: 600;\\n color: var(--pm-text, #000000);\\n margin: 0 0 8px;\\n}\\n.pm-success-text {\\n font-size: 16px;\\n color: var(--pm-text-secondary, #666666);\\n margin: 0;\\n}\\n.pm-error-container {\\n display: flex;\\n flex-direction: column;\\n align-items: center;\\n justify-content: center;\\n padding: 60px 24px;\\n text-align: center;\\n}\\n.pm-error-icon {\\n width: 64px;\\n height: 64px;\\n background: #e74c3c;\\n border-radius: 50%;\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n margin-bottom: 20px;\\n color: white;\\n}\\n.pm-error-icon svg {\\n width: 32px;\\n height: 32px;\\n}\\n.pm-error-title {\\n font-size: 24px;\\n font-weight: 600;\\n color: var(--pm-text, #000000);\\n margin: 0 0 8px;\\n}\\n.pm-error-text {\\n font-size: 16px;\\n color: var(--pm-text-secondary, #666666);\\n margin: 0 0 24px;\\n max-width: 300px;\\n}\\n.pm-error-actions {\\n display: flex;\\n flex-direction: column;\\n gap: 12px;\\n width: 100%;\\n max-width: 280px;\\n}\\n.pm-error-inline {\\n display: flex;\\n flex-direction: column;\\n align-items: center;\\n justify-content: center;\\n height: 100%;\\n padding: 40px 20px;\\n text-align: center;\\n color: #e74c3c;\\n}\\n.pm-error-inline svg {\\n width: 48px;\\n height: 48px;\\n margin-bottom: 16px;\\n}\\n.pm-error-inline p {\\n margin: 0 0 20px;\\n color: var(--pm-text-secondary, #666666);\\n font-size: 14px;\\n}\\n.pm-btn-outline {\\n background: transparent;\\n color: #0098ea;\\n border: 1px solid #0098ea;\\n}\\n.pm-btn-outline:hover {\\n background: rgba(0, 152, 234, 0.08);\\n}\\n.pm-loading-container {\\n display: flex;\\n flex-direction: column;\\n align-items: center;\\n justify-content: center;\\n height: 100%;\\n color: var(--pm-text-muted, #6b7280);\\n}\\n.pm-loading-container p {\\n margin: 16px 0 0;\\n font-size: 14px;\\n}\\n.pm-loading-overlay {\\n position: absolute;\\n top: 0;\\n left: 0;\\n right: 0;\\n bottom: 0;\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n background: var(--pm-iframe-bg, #f9f9f9);\\n z-index: 1;\\n}\\n.pm-spinner {\\n width: 32px;\\n height: 32px;\\n border: 3px solid var(--pm-spinner-track, #e5e7eb);\\n border-top-color: #0098ea;\\n border-radius: 50%;\\n animation: pm-spin 0.8s linear infinite;\\n}\\n.pm-btn-spinner {\\n width: 16px;\\n height: 16px;\\n border: 2px solid rgba(122, 122, 122, 0.3);\\n border-top-color: #7a7a7a;\\n border-radius: 50%;\\n animation: pm-spin 0.8s linear infinite;\\n}\\n@keyframes pm-spin {\\n to {\\n transform: rotate(360deg);\\n }\\n}\\n.pm-retry-link {\\n font-family: \\\"Inter\\\", sans-serif;\\n font-style: normal;\\n font-weight: 400;\\n font-size: 12px;\\n line-height: 15px;\\n text-align: center;\\n color: #004062;\\n margin-top: 0px;\\n}\\n.pm-retry-action {\\n cursor: pointer;\\n text-decoration: underline;\\n color: #0098ea;\\n}\\n@media (prefers-color-scheme: dark) {\\n .pm-header-left span {\\n color: #ffffff !important;\\n }\\n .pm-close-btn svg rect {\\n fill: rgba(255, 255, 255, 0.1) !important;\\n }\\n .pm-close-btn svg path {\\n stroke: #ffffff !important;\\n }\\n .pm-retry-link {\\n color: var(--pm-text-muted, #8c8c8c) !important;\\n }\\n .pm-btn.processing {\\n background: rgba(255, 255, 255, 0.1) !important;\\n color: var(--pm-text-muted, #8c8c8c) !important;\\n }\\n}\\n\")","import * as React from 'react';\nimport { createMoonpayTransfer, checkMoonpayAvailability } from '@ton-pay/api';\nimport type {\n CreateMoonpayTransferParams,\n MoonpayGeoResult,\n MoonpayAmountLimits,\n} from '@ton-pay/api';\nimport type { UseMoonPayIframeOptions } from '../types';\n\nexport function useMoonPayIframe({\n apiKey,\n chain = 'mainnet',\n}: UseMoonPayIframeOptions) {\n const [loading, setLoading] = React.useState(false);\n const [error, setError] = React.useState<string | null>(null);\n const [link, setLink] = React.useState<string | null>(null);\n const [geoResult, setGeoResult] = React.useState<MoonpayGeoResult | null>(\n null,\n );\n const [limits, setLimits] = React.useState<MoonpayAmountLimits | null>(null);\n\n const checkAvailability = React.useCallback(\n async (\n amount: number,\n asset: string,\n ipAddress: string,\n ): Promise<boolean> => {\n if (!apiKey) return false;\n\n setLoading(true);\n setError(null);\n\n try {\n const availability = await checkMoonpayAvailability(\n { asset, ipAddress },\n { apiKey, chain },\n );\n setGeoResult(availability.geo);\n setLimits(availability.limits);\n\n if (!availability.geo.isBuyAllowed) {\n return false;\n }\n\n const limitsData = availability.limits;\n\n if (!limitsData?.quoteCurrency) {\n return false;\n }\n\n const { minBuyAmount, maxBuyAmount } = limitsData.quoteCurrency;\n\n if (amount < minBuyAmount || amount > maxBuyAmount) {\n return false;\n }\n\n return true;\n } catch {\n return false;\n } finally {\n setLoading(false);\n }\n },\n [apiKey, chain],\n );\n\n const fetchOnRampLink = React.useCallback(\n async (params: CreateMoonpayTransferParams): Promise<string> => {\n if (!apiKey) throw new Error('API Key is required');\n\n setLoading(true);\n setError(null);\n\n try {\n const response = await createMoonpayTransfer(params, { apiKey, chain });\n setLink(response.link);\n return response.link;\n } catch (e) {\n const msg =\n e instanceof Error ? e.message : 'Failed to generate OnRamp link';\n setError(msg);\n throw new Error(msg);\n } finally {\n setLoading(false);\n }\n },\n [apiKey, chain],\n );\n\n return {\n loading,\n error,\n link,\n fetchOnRampLink,\n checkAvailability,\n geoResult,\n limits,\n };\n}\n","export const BASE_URL = 'https://pay.ton.org';\nexport const TESTNET_BASE_URL = 'https://dev.pay.ton.org';\n","import type { Chain } from '../types/chain';\nimport { BASE_URL, TESTNET_BASE_URL } from './const';\n\nexport const getBaseUrl = (chain?: Chain) => {\n // only for testing. do not use in production. do not use in docs\n // @ts-ignore\n if (\n typeof process !== 'undefined' &&\n process.env &&\n process.env.TONPAY_BASE_URL\n ) {\n // @ts-ignore\n return process.env.TONPAY_BASE_URL;\n }\n if (!chain || chain === 'mainnet') {\n return BASE_URL;\n }\n return TESTNET_BASE_URL;\n};\n","import { getBaseUrl } from '../common/get-base-url';\nimport type { APIOptions } from '../types/api-options';\nimport type {\n CreateMoonpayTransferParams,\n CreateMoonpayTransferResponse,\n} from '../types/create-moonpay-transfer';\n\n/**\n * Creates a MoonPay payment link for buying crypto\n * @param params - the parameters for the MoonPay transfer\n * @param options - the options for the transfer (requires API key)\n * @returns the payment link, geo restrictions, and amount limits\n */\nexport const createMoonpayTransfer = async (\n params: CreateMoonpayTransferParams,\n options: APIOptions,\n): Promise<CreateMoonpayTransferResponse> => {\n if (!options?.apiKey) {\n throw new Error('API key is required for MoonPay transfers');\n }\n\n const baseUrl = getBaseUrl(options.chain);\n const headers = {\n 'Content-Type': 'application/json',\n 'x-api-key': options.apiKey,\n };\n\n const response = await fetch(\n `${baseUrl}/api/merchant/v1/create-moonpay-transfer`,\n {\n method: 'POST',\n body: JSON.stringify(params),\n headers,\n },\n );\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to create MoonPay transfer: ${errorText}`, {\n cause: response.statusText,\n });\n }\n\n const data = await response.json();\n\n if (data.link && data.link.startsWith('/')) {\n data.link = `${baseUrl}${data.link}`;\n }\n\n return data;\n};\n","import { getBaseUrl } from '../common/get-base-url';\nimport type { APIOptions } from '../types/api-options';\nimport type {\n CheckMoonpayAvailabilityParams,\n CheckMoonpayAvailabilityResponse,\n} from '../types/check-moonpay-availability';\n\nexport const checkMoonpayAvailability = async (\n params: CheckMoonpayAvailabilityParams,\n options?: APIOptions,\n): Promise<CheckMoonpayAvailabilityResponse> => {\n const baseUrl = getBaseUrl(options?.chain);\n const headers = {\n 'Content-Type': 'application/json',\n ...(options?.apiKey ? { 'x-api-key': options.apiKey } : {}),\n };\n\n const response = await fetch(`${baseUrl}/api/external/moonpay/check`, {\n method: 'POST',\n body: JSON.stringify(params),\n headers,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to check MoonPay availability: ${errorText}`, {\n cause: response.statusText,\n });\n }\n\n return response.json();\n};\n","import * as CryptoJS from 'crypto-js';\n\n/**\n * Verifies the HMAC-SHA256 signature of a payload\n * @param payload - Raw JSON string or object to verify\n * @param signature - The signature from X-TON Pay-Signature header\n * @param apiSecret - Your TON Pay webhook API secret\n * @returns true if signature is valid, false otherwise\n *\n * @example\n * ```typescript\n * import { verifySignature } from \"@ton-pay/api\";\n *\n * // With raw string\n * app.post(\"/webhook\", (req, res) => {\n * const signature = req.headers[\"x-tonpay-signature\"] as string;\n * const payload = JSON.stringify(req.body);\n *\n * if (!verifySignature(payload, signature, YOUR_API_SECRET)) {\n * return res.status(401).json({ error: \"Invalid signature\" });\n * }\n *\n * res.status(200).json({ received: true });\n * });\n *\n * // With object (will be stringified automatically)\n * app.post(\"/webhook\", (req, res) => {\n * const signature = req.headers[\"x-tonpay-signature\"] as string;\n *\n * if (!verifySignature(req.body, signature, YOUR_API_SECRET)) {\n * return res.status(401).json({ error: \"Invalid signature\" });\n * }\n *\n * res.status(200).json({ received: true });\n * });\n * ```\n */\nexport function verifySignature(\n payload: string | object,\n signature: string,\n apiSecret: string,\n): boolean {\n const payloadString =\n typeof payload === 'string' ? payload : JSON.stringify(payload);\n\n const hmac = CryptoJS.HmacSHA256(payloadString, apiSecret);\n const expectedSignature = `sha256=${hmac.toString(CryptoJS.enc.Hex)}`;\n\n return signature === expectedSignature;\n}\n","import type { TonPayButtonPresetConfig, TonPayPreset } from '../../types';\n\nexport const PRESETS: Record<TonPayPreset, TonPayButtonPresetConfig> = {\n default: {\n bgColor: '#0098EA',\n textColor: '#FFFFFF',\n },\n gradient: {\n bgColor: 'linear-gradient(91.69deg, #2A82EB 8.9%, #0355CF 158.29%)',\n textColor: '#FFFFFF',\n },\n};\n\nexport const buttonStyles = `\n@keyframes tp-pulse{0%{opacity:1;transform:scale(1)}50%{opacity:.5;transform:scale(1.02)}100%{opacity:1;transform:scale(1)}}\n@keyframes tp-fade-in{from{opacity:0;transform:translateY(-4px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}\n@keyframes tp-spin{to{transform:rotate(360deg)}}\n\n.tp-wrap{display:inline-flex;flex-direction:column;position:relative;width:var(--tp-width,300px);max-width:100%;--tp-menu-bg:#ffffff;--tp-menu-text:#111827;--tp-menu-muted:#6b7280;--tp-menu-hover:rgba(0,0,0,.06);--tp-menu-border:rgba(0,0,0,.08);--tp-menu-shadow:0 8px 24px rgba(0,0,0,.12)}\n@media(prefers-color-scheme:dark){.tp-wrap{--tp-menu-bg:#1C2633;--tp-menu-text:#F9FAFB;--tp-menu-muted:#9CA3AF;--tp-menu-hover:rgba(255,255,255,.08);--tp-menu-border:rgba(255,255,255,.1);--tp-menu-shadow:0 8px 32px rgba(0,0,0,.4)}}\n.tp-btn-container{display:flex;flex-direction:row;width:100%}\n.tp-btn{display:flex;flex-direction:column;justify-content:center;align-items:center;padding:13px 10px;gap:10px;flex:1;min-height:var(--tp-height,44px);background:var(--tp-bg,#0098EA);color:var(--tp-text,#fff);border:none;border-radius:var(--tp-radius,8px) 0 0 var(--tp-radius,8px);cursor:pointer;transition:filter .12s ease,transform .12s ease;font-family:var(--tp-font,inherit);font-style:normal;font-weight:500;font-size:20px;line-height:25px;text-align:center;position:relative}\n.tp-btn.with-menu{padding-left:calc(10px + (var(--tp-height,44px))/2)}\n.tp-btn.no-menu{border-radius:var(--tp-radius,8px)}\n.tp-btn-content{display:flex;flex-direction:row;align-items:center;padding:0;gap:5px;white-space:nowrap;margin-top:-4px}\n.tp-btn:hover:not(:disabled){filter:brightness(0.92)}\n.tp-btn:active:not(:disabled){filter:brightness(0.85);transform:translateY(1px)}\n.tp-btn:disabled{cursor:not-allowed;opacity:.85}\n.tp-btn.loading{animation:none}\n.tp-btn.processing{background:#E6E6E6;color:#7A7A7A;cursor:default;opacity:1}\n.tp-btn.processing .tp-spinner{border-color:rgba(122,122,122,0.3);border-top-color:#7A7A7A}\n\n.tp-retry-text{font-weight:400;font-size:12px;line-height:15px;text-align:center;color:#004062;margin-top:8px}\n.tp-retry-link{cursor:pointer;text-decoration:underline;color:#0098EA}\n\n.tp-arrow{display:flex;align-items:center;justify-content:center;padding:13px 10px;min-width:calc(var(--tp-height,44px));min-height:var(--tp-height,44px);background:var(--tp-bg,#0098EA);color:var(--tp-text,#fff);border:none;border-left:1px solid rgba(255,255,255,.2);border-radius:0 var(--tp-radius,8px) var(--tp-radius,8px) 0;cursor:pointer;transition:filter .12s ease,transform .12s ease;font-size:14px}\n.tp-arrow:hover:not(:disabled){filter:brightness(0.92)}\n.tp-arrow:active:not(:disabled){filter:brightness(0.85);transform:translateY(1px)}\n.tp-arrow:disabled{cursor:not-allowed;opacity:.85;transition:none;filter:none;transform:none}\n\n.tp-menu{position:absolute;right:0;top:calc(100% + 8px);width:256px;background:var(--tp-menu-bg);color:var(--tp-menu-text);border:1px solid var(--tp-menu-border);border-radius:var(--tp-menu-radius,16px);padding:8px;box-shadow:var(--tp-menu-shadow);z-index:1000;animation:tp-fade-in .15s ease}\n.tp-menu-arrow{position:absolute;top:-8px;right:20px;width:0;height:0;border-style:solid;border-width:0 8px 8px 8px;border-color:transparent transparent var(--tp-menu-bg) transparent;filter:drop-shadow(0 -1px 1px rgba(0,0,0,.08))}\n.tp-menu-address{padding:.5rem .75rem;font-size:.85rem;color:var(--tp-menu-muted);cursor:default;user-select:text}\n.tp-menu-item{display:flex;align-items:center;gap:8px;width:100%;height:40px;padding-left:12px;padding-right:12px;border:none;background:transparent;text-align:left;cursor:pointer;font-size:15px;font-weight:590;color:var(--tp-menu-text);transition:background-color .15s ease,transform .1s ease-in-out;border-radius:8px;margin:2px}\n.tp-menu-item:hover:not(:disabled){background:var(--tp-menu-hover)}\n.tp-menu-item:active{transform:scale(0.96)}\n.tp-menu-item.danger{color:#e74c3c}\n.tp-menu-item.danger:hover:not(:disabled){background:rgba(231,76,60,.12);color:#c0392b}\n.tp-menu-item:disabled{cursor:default;opacity:1;color:var(--tp-menu-muted)}\n.tp-menu-item:disabled:hover{background:transparent}\n.tp-menu-icon{width:24px;height:24px;display:flex;align-items:center;justify-content:center;color:currentColor}\n.tp-menu-item:disabled .tp-menu-icon{opacity:.5}\n\n.tp-spinner{border:2px solid rgba(255,255,255,.35);border-top-color:var(--tp-text,#fff);border-radius:50%;width:18px;height:18px;animation:tp-spin .6s linear infinite}\n`;\n\nlet stylesInjected = false;\n\nexport function injectStyles(): void {\n if (typeof document === 'undefined' || stylesInjected) return;\n const style = document.createElement('style');\n style.id = 'tonpay-button-styles';\n style.textContent = buttonStyles;\n document.head.appendChild(style);\n stylesInjected = true;\n}\n","import * as React from 'react';\nimport {\n useTonAddress,\n useTonConnectModal,\n useTonConnectUI,\n} from '@tonconnect/ui-react';\nimport type { GetMessageFn, PayInfo } from '../types';\n\nconst WALLET_CONNECTION_TIMEOUT = 5 * 60 * 1000;\nconst TX_VALID_DURATION = 5 * 60;\n\nexport function useTonPay() {\n const address = useTonAddress(true);\n const modal = useTonConnectModal();\n const [tonConnectUI] = useTonConnectUI();\n\n const waitForWalletConnection = React.useCallback((): Promise<string> => {\n return new Promise((resolve, reject) => {\n if (address) {\n resolve(address);\n return;\n }\n\n modal.open();\n\n const unsubscribe = tonConnectUI.onStatusChange((wallet) => {\n if (wallet?.account) {\n unsubscribe();\n unsubscribeModal();\n resolve(wallet.account.address);\n }\n });\n\n const unsubscribeModal = tonConnectUI.onModalStateChange((state) => {\n if (state.status === 'closed') {\n unsubscribe();\n unsubscribeModal();\n reject(new Error('Wallet connection modal closed'));\n }\n });\n\n setTimeout(() => {\n unsubscribe();\n unsubscribeModal();\n reject(new Error('Wallet connection timeout'));\n }, WALLET_CONNECTION_TIMEOUT);\n });\n }, [address, modal, tonConnectUI]);\n\n const pay = React.useCallback(\n async <T extends object = object>(\n getMessage: GetMessageFn<T>,\n options?: { onRequestSent?: (redirectToWallet: () => void) => void },\n ): Promise<PayInfo<T>> => {\n const walletAddress = await waitForWalletConnection();\n const validUntil = Math.floor(Date.now() / 1e3) + TX_VALID_DURATION;\n const messageResult = await getMessage(walletAddress);\n\n const txResult = await tonConnectUI.sendTransaction(\n {\n messages: [messageResult.message],\n validUntil,\n from: walletAddress,\n },\n {\n onRequestSent: options?.onRequestSent,\n },\n );\n\n return { ...messageResult, txResult };\n },\n [waitForWalletConnection, tonConnectUI],\n );\n\n return { pay, address };\n}\n"]}