UNPKG

@coin-voyage/paykit

Version:

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

83 lines (82 loc) 3.51 kB
import { PayOrderMode, PayOrderStatus, } from "@coin-voyage/shared/types"; import { useEffect, useRef } from "react"; const COMPLETED_STATES = [PayOrderStatus.COMPLETED, PayOrderStatus.REFUNDED]; const STARTED_STATES = [ ...COMPLETED_STATES, PayOrderStatus.PARTIAL_PAYMENT, PayOrderStatus.AWAITING_CONFIRMATION, PayOrderStatus.OPTIMISTIC_CONFIRMED, PayOrderStatus.EXECUTING_ORDER, ]; /** * Handles payment lifecycle events of an order, such as started, completed, and bounced. * @param {PayOrder | undefined} order The pay order to monitor. * @param {PaymentLifecycleHandlers} handlers Handlers for payment lifecycle events. * @param {PaymentLifecycleOptions} options Lifecycle behavior flags. * @returns */ export function usePaymentLifecycle(order, handlers, options = {}) { const sentStart = useRef(false); const sentComplete = useRef(false); const currentOrderId = useRef(undefined); const { onPaymentStarted, onPaymentCompleted, onPaymentBounced } = handlers; const { optimisticConfirmation = true } = options; const orderId = order?.id; const orderStatus = order?.status; const orderMetadata = order?.metadata; const payment = order?.payment; const allowOptimisticCompletion = optimisticConfirmation && order?.mode === PayOrderMode.SALE; const isStarted = !!orderStatus && STARTED_STATES.includes(orderStatus); const isFinalized = !!orderStatus && (COMPLETED_STATES.includes(orderStatus) || (allowOptimisticCompletion && (orderStatus === PayOrderStatus.OPTIMISTIC_CONFIRMED || orderStatus === PayOrderStatus.EXECUTING_ORDER))); useEffect(() => { if (!orderId) return; if (currentOrderId.current === orderId) return; currentOrderId.current = orderId; sentStart.current = false; sentComplete.current = false; }, [orderId]); useEffect(() => { if (sentStart.current || !orderId || !payment || !orderStatus || !isStarted) return; sentStart.current = true; onPaymentStarted?.({ type: "payorder_confirming", payorder_id: orderId, status: orderStatus, metadata: orderMetadata, payment_data: payment, }); }, [isStarted, onPaymentStarted, orderId, orderMetadata, orderStatus, payment]); useEffect(() => { if (sentComplete.current || !orderId || !payment || !orderStatus || !isFinalized) return; sentComplete.current = true; if (orderStatus === PayOrderStatus.REFUNDED) { onPaymentBounced?.({ type: "payorder_refunded", payorder_id: orderId, status: orderStatus, metadata: orderMetadata, payment_data: payment, refund_address: payment.refund_address ?? "", refund_tx_hash: payment.refund_tx_hash ?? "", }); return; } onPaymentCompleted?.({ type: "payorder_completed", payorder_id: orderId, status: orderStatus, metadata: orderMetadata, payment_data: payment, source_tx_hash: payment.source_tx_hash ?? "", destination_tx_hash: payment.destination_tx_hash ?? "", }); }, [isFinalized, onPaymentBounced, onPaymentCompleted, orderId, orderMetadata, orderStatus, payment]); return { isStarted, isFinalized, order }; }