@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
JavaScript
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 };
}