UNPKG

@coin-voyage/paykit

Version:

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

198 lines (197 loc) 7.5 kB
import { useAccount } from "@coin-voyage/crypto/hooks"; import { zPayOrder } from "@coin-voyage/shared/schemas"; import { ChainType, PayOrderMode, } from "@coin-voyage/shared/types"; import { useResolveSuiNSName } from "@mysten/dapp-kit"; import { useCallback, useRef, useState } from "react"; import { mainnet } from "viem/chains"; import { useEnsName } from "wagmi"; import { useBackendApi } from "../components/contexts/api"; import { ROUTE } from "../types/routes"; import { usePayOrderQuotes } from "./usePayOrderQuotes"; import { usePayToAddress } from "./usePayToAddress"; import { usePayWithCard } from "./usePayWithCard"; import { usePayFromWallet } from "./usePayWithToken"; export function usePaymentState({ payOrder, setPayOrder, setRoute, log, }) { const api = useBackendApi(); const [connectorChainType, setConnectorChainType] = useState(); const [selectedWallet, setSelectedWallet] = useState(); const latestRequestedIdRef = useRef(null); const inFlightPayOrderIdsRef = useRef(new Set()); const { account: senderAccount } = useAccount({ selectedWallet, chainType: connectorChainType, }); const { data: suiEnsName } = useResolveSuiNSName(senderAccount.address, { enabled: !!senderAccount.address && senderAccount.chainType === ChainType.SUI && senderAccount.isConnected, }); const { data: evmEnsName } = useEnsName({ chainId: mainnet.id, address: senderAccount.address, query: { enabled: !!senderAccount.address && senderAccount.chainType === ChainType.EVM && senderAccount.isConnected, }, }); const payOrderQuotes = usePayOrderQuotes({ payOrder, address: senderAccount.address, chainType: connectorChainType, }); const [selectedCurrencyOption, setSelectedCurrencyOption] = useState(); const [paymentMethod, setPaymentMethod] = useState(); const [payToAddressChainId, setPayToAddressChainId] = useState(); const [payToAddressCurrency, setPayToAddressCurrency] = useState(); const { payFromWallet } = usePayFromWallet({ senderAddr: senderAccount.address, payOrder, setPayOrder, chainType: connectorChainType, log, }); const { payToAddress } = usePayToAddress({ payOrder, setPayOrder, log, }); const { payWithCard } = usePayWithCard({ payOrder, setPayOrder, log, }); const fetchPayOrder = useCallback(async (id) => { const { data: order, error } = await api.getPayOrder(id); if (!order || error) { log(`[CHECKOUT] No order found for ${id}: ${JSON.stringify(error)}`); return undefined; } return order; }, [api, log]); const refreshOrder = useCallback(async () => { if (!payOrder?.id) return; const order = await fetchPayOrder(payOrder.id); if (order) setPayOrder(order); }, [payOrder, fetchPayOrder, setPayOrder]); const setPayOrderId = useCallback(async (payOrderId) => { if (!payOrderId) return; if (payOrder?.id === payOrderId) return; if (inFlightPayOrderIdsRef.current.has(payOrderId)) return; latestRequestedIdRef.current = payOrderId; inFlightPayOrderIdsRef.current.add(payOrderId); try { const order = await fetchPayOrder(payOrderId); if (order && latestRequestedIdRef.current === payOrderId) { setPayOrder(order); } return order; } finally { inFlightPayOrderIdsRef.current.delete(payOrderId); } }, [fetchPayOrder, payOrder?.id, setPayOrder]); const copyDepositPayOrder = useCallback(async () => { if (!payOrder?.id) { log(`No payOrder to copy`); return; } if (payOrder.mode !== PayOrderMode.DEPOSIT) { log(`Cannot recreate SALE order`); return; } const asset = payOrder.fulfillment.asset; const { data, error } = await api.createDepositPayOrder({ intent: { asset, amount: { ...(asset ? { token_amount: payOrder.fulfillment.amount.ui_amount } : {}), ...(payOrder.fulfillment.fiat ? { fiat: { amount: payOrder.fulfillment.amount.value_usd, unit: payOrder.fulfillment.fiat, }, } : {}), }, receiving_address: payOrder.fulfillment.receiving_address, }, metadata: payOrder.metadata, }); if (!data || error) { log(`[CHECKOUT] Error creating payOrder: ${JSON.stringify(error)}`); return; } await setPayOrderId(data.id); }, [api, payOrder, log, setPayOrderId]); const createDepositPayOrder = useCallback(async (params, onError) => { try { const result = zPayOrder.safeParse(params); if (result.error) { onError?.(`[CREATE DEPOSIT]: ${result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join(", ")}`); return; } const { data: payOrder, error } = await api.createDepositPayOrder(params); if (!payOrder || error) { const errorMessage = error?.message || "Unable to create payment order"; onError?.(errorMessage); log(`[CREATE DEPOSIT] Error creating payOrder: ${JSON.stringify(error)}`); return; } setPayOrder(payOrder); return payOrder; } catch (e) { if (e instanceof Error) onError?.(e.message); else onError?.("Unknown error"); } }, [api, setPayOrder, log]); const clearUserSelection = useCallback(() => { setSelectedCurrencyOption(undefined); setConnectorChainType(undefined); setSelectedWallet(undefined); setRoute(ROUTE.SELECT_METHOD); }, [setRoute]); const resetPaymentState = useCallback(() => { latestRequestedIdRef.current = null; inFlightPayOrderIdsRef.current.clear(); setPayOrder(undefined); setPaymentMethod(undefined); setSelectedCurrencyOption(undefined); setConnectorChainType(undefined); setSelectedWallet(undefined); setPayToAddressChainId(undefined); setPayToAddressCurrency(undefined); setRoute(ROUTE.SELECT_METHOD); }, [setPayOrder, setRoute]); return { setPayId: setPayOrderId, createDepositPayOrder, copyDepositPayOrder, clearUserSelection, resetPaymentState, payOrder, paymentMethod, setPaymentMethod, connectorChainType, setConnectorChainType, selectedWallet, setSelectedWallet, selectedCurrencyOption, setSelectedCurrencyOption, payOrderQuotes, payFromWallet, payWithCard, payToAddress, refreshOrder, senderEnsName: evmEnsName ?? suiEnsName ?? undefined, payToAddressChainId, setPayToAddressChainId, payToAddressCurrency, setPayToAddressCurrency, }; }