UNPKG

@coin-voyage/paykit

Version:

Seamless crypto payments. Onboard users from any chain, any coin into your app with one click.

199 lines 8.19 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { useAccount, useConnectCallback } from "@coin-voyage/crypto/hooks"; import { PayOrderMode, PayOrderStatus } from "@coin-voyage/shared/common"; import { Buffer } from "buffer"; import { useCallback, useContext, useEffect, useRef, useState } from "react"; import { ThemeProvider as StyledThemeProvider } from "styled-components"; import { WagmiContext } from "wagmi"; import { PayContext } from "../components/contexts/pay/index"; import { Web3ContextProvider } from "../components/contexts/web3/index"; import { PayModal } from "../components/pay-modal/index"; import { useThemeFont } from "../hooks/useGoogleFont"; import { usePaymentState } from "../hooks/usePaymentState"; import defaultTheme from "../styles/defaultTheme"; import { ROUTES } from "../types/routes"; import { ApiProvider } from "./api-provider"; const defaultOptions = { language: "en-US", hideTooltips: false, hideQuestionMarkCTA: false, hideNoWalletCTA: false, walletConnectCTA: "link", hideRecentBadge: false, embedGoogleFonts: false, walletConnectName: undefined, disclaimer: null, bufferPolyfill: true, overlayBlur: undefined, }; export const PayKitProvider = ({ apiKey, environment = "production", ...props }) => { const wagmiContext = useContext(WagmiContext); if (!wagmiContext) { throw new Error("PayKitProvider must be used within a WagmiContext"); } return (_jsx(ApiProvider, { apiKey: apiKey, environment: environment, children: _jsx(PayKitProviderInternal, { ...props }) })); }; function PayKitProviderInternal({ theme = "auto", mode = "auto", customTheme, options, onConnect, onConnectValidation, onDisconnect, debugMode = false, children }) { const [allowedWallets, setAllowedWallets] = useState(!!onConnectValidation ? [] : null); useConnectCallback({ onConnect, onDisconnect, onConnectValidation, setAllowedWallets, }); const opts = Object.assign({}, defaultOptions, options); if (typeof window !== "undefined" && opts.bufferPolyfill) { // Buffer Polyfill, needed for bundlers that don't provide Node polyfills (e.g CRA, Vite, etc.) window.Buffer = window.Buffer ?? Buffer; } const [ckTheme, setTheme] = useState(theme); const [ckMode, setMode] = useState(mode); const [ckCustomTheme, setCustomTheme] = useState(customTheme ?? {}); const [ckLang, setLang] = useState("en-US"); const [open, setOpenState] = useState(false); const [lockPayParams, setLockPayParams] = useState(false); const [paymentCompleted, setPaymentCompleted] = useState(false); const [route, setRoute] = useState(ROUTES.SELECT_METHOD); const [modalOptions, setModalOptions] = useState(); const [errorMessage, setErrorMessage] = useState(""); const [resize, onResize] = useState(0); const onOpenRef = useRef(); const onCloseRef = useRef(); const setOnOpen = useCallback((fn) => { onOpenRef.current = fn; }, []); const setOnClose = useCallback((fn) => { onCloseRef.current = fn; }, []); // Include Google Font that is needed for a themes if (opts.embedGoogleFonts) { useThemeFont(theme); } const setOpen = useCallback((open) => { setOpenState(open); // Locking pay params the first time the modal is opened to // prevent the payOrder from changing while the modal is open. if (open) { setLockPayParams(true); } // Reset payment state on close if resetOnSuccess is true if (!open && paymentCompleted && modalOptions?.resetOnSuccess) { setPaymentCompleted(false); setLockPayParams(false); paymentState.resetPayOrder(); } // Run the onOpen and onClose callbacks if (open) onOpenRef.current?.(); else onCloseRef.current?.(); }, // We don't have good caching on paymentState, so don't include it as a dep // eslint-disable-next-line react-hooks/exhaustive-deps [modalOptions?.resetOnSuccess, paymentCompleted]); // Callback when a payOrder is successfully completed (regardless of whether // the final call succeeded or bounced) const onSuccess = useCallback(() => { if (modalOptions?.closeOnSuccess) { setTimeout(() => setOpen(false), 1000); } setPaymentCompleted(true); }, [modalOptions?.closeOnSuccess, setOpen, setPaymentCompleted]); // Other Configuration useEffect(() => setTheme(theme), [theme]); useEffect(() => setLang(opts.language || "en-US"), [opts.language]); useEffect(() => setErrorMessage(null), [route, open]); const { account } = useAccount(); const log = debugMode ? console.log : () => { }; // PaymentState is a second, inner context object containing a PayOrder // plus all associated status and callbacks. In order for useContext() and // downstream hooks like usePayStatus() to work correctly, we must set // set refresh context when payment status changes; done via setPayOrder. const [payOrder, setPayOrder] = useState(); const paymentState = usePaymentState({ payOrder, setPayOrder, setRoute, log }); // When a payment has started, poll for updates. Do this regardless // of whether the modal is still being displayed for usePayStatus(). useEffect(() => { if (!payOrder?.status) return; let intervalMs = 0; if (payOrder.status === PayOrderStatus.AWAITING_PAYMENT) { intervalMs = 5000; } else if ([PayOrderStatus.AWAITING_CONFIRMATION, PayOrderStatus.OPTIMISTIC_CONFIRMED].includes(payOrder.status)) { intervalMs = 2500; } else if (payOrder?.status === PayOrderStatus.EXECUTING_ORDER) { // Poll faster for deposits, sales completion is not/less relevant for users intervalMs = payOrder.mode === PayOrderMode.DEPOSIT ? 1000 : 2500; } else { return; } const timeout = setTimeout(paymentState.refreshOrder, intervalMs); return () => { clearTimeout(timeout); }; }, [payOrder]); const showPayment = (modalOptions) => { setModalOptions(modalOptions); setOpen(true); const isConnected = account.isConnected; if (isConnected) { paymentState.setConnectorChainType(account.chainType); } const payOrder = paymentState.payOrder; if (payOrder?.status === PayOrderStatus.COMPLETED) { setRoute(ROUTES.CONFIRMATION); } else if (payOrder?.status === PayOrderStatus.AWAITING_PAYMENT && payOrder.payment?.src) { paymentState.setSelectedTokenOption(payOrder.payment.src); setRoute(ROUTES.PAY_WITH_TOKEN); } else { setRoute(isConnected ? ROUTES.SELECT_TOKEN : ROUTES.SELECT_METHOD); } }; const value = { theme: ckTheme, setTheme, mode: ckMode, setMode, customTheme: ckCustomTheme, setCustomTheme, lang: ckLang, setLang, setOnOpen, setOnClose, open, setOpen, onSuccess, route, setRoute, // Other configuration options: opts, errorMessage, debugMode, log, displayError: (message, code) => { setErrorMessage(message); console.log("---------COINVOYAGE DEBUG---------"); if (code) console.table(code); console.log("---------/COINVOYAGE DEBUG---------"); }, resize, triggerResize: () => onResize((prev) => prev + 1), // Pay context showPayment, paymentState, allowedWallets, }; return (_jsx(PayContext.Provider, { value: value, children: _jsx(Web3ContextProvider, { enabled: open, children: _jsxs(StyledThemeProvider, { theme: defaultTheme, children: [children, _jsx(PayModal, {})] }) }) })); } //# sourceMappingURL=paykit-provider.js.map