Skip to content

Commit ebba430

Browse files
feat: extract payout polling
1 parent 0b19e0c commit ebba430

File tree

2 files changed

+170
-80
lines changed

2 files changed

+170
-80
lines changed

packages/connectkit/src/components/Pages/Confirmation/index.tsx

Lines changed: 11 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import {
1414
getAddressContraction,
1515
getChainExplorerTxUrl,
1616
getOrderDestChainId,
17-
getPayment,
1817
rozoSolana,
1918
rozoStellar,
2019
updatePaymentPayInTxHash,
@@ -31,13 +30,12 @@ import {
3130
ROZO_INVOICE_URL,
3231
} from "../../../constants/rozoConfig";
3332
import { useRozoPay } from "../../../hooks/useDaimoPay";
33+
import { usePayoutPolling } from "../../../hooks/usePayoutPolling";
3434
import { useSupportedChains } from "../../../hooks/useSupportedChains";
3535
import styled from "../../../styles/styled";
3636
import Button from "../../Common/Button";
3737
import PoweredByFooter from "../../Common/PoweredByFooter";
3838

39-
const poolDelay = 1000;
40-
4139
const Confirmation: React.FC = () => {
4240
const {
4341
confirmationMessage,
@@ -56,14 +54,6 @@ const Confirmation: React.FC = () => {
5654

5755
const [isConfirming, setIsConfirming] = useState<boolean>(true);
5856

59-
const [payoutLoading, setPayoutLoading] = useState<boolean>(false);
60-
const [payoutTxHashUrl, setPayoutTxHashUrl] = useState<string | undefined>(
61-
undefined
62-
);
63-
const [payoutTxHash, setPayoutTxHash] = useState<string | undefined>(
64-
undefined
65-
);
66-
6757
// Track if completion events have been sent to prevent duplicate calls
6858
const paymentCompletedSent = React.useRef<string | null>(null);
6959
const payoutCompletedSent = React.useRef<string | null>(null);
@@ -173,75 +163,16 @@ const Confirmation: React.FC = () => {
173163
return undefined;
174164
}, [rozoPaymentId, receiptUrl]);
175165

176-
useEffect(() => {
177-
if (order && done && rozoPaymentId && showProcessingPayout) {
178-
triggerResize();
179-
context.log(
180-
"[CONFIRMATION] Starting payout polling for order:",
181-
order.externalId
182-
);
183-
setPayoutLoading(true);
184-
185-
let isActive = true;
186-
let timeoutId: NodeJS.Timeout;
187-
188-
const pollPayout = async () => {
189-
if (!isActive || !rozoPaymentId) return;
190-
191-
try {
192-
context.log(
193-
"[CONFIRMATION] Polling for payout transaction:",
194-
rozoPaymentId
195-
);
196-
const response = await getPayment(rozoPaymentId, "v2");
197-
context.log("[CONFIRMATION] Payout polling response:", response.data);
198-
199-
if (
200-
isActive &&
201-
response.data &&
202-
response.data.destination.txHash &&
203-
typeof response.data.destination.txHash === "string"
204-
) {
205-
const url = getChainExplorerTxUrl(
206-
Number(response.data.destination.chainId),
207-
response.data.destination.txHash
208-
);
209-
context.log(
210-
"[CONFIRMATION] Found payout transaction:",
211-
response.data.destination.txHash,
212-
"URL:",
213-
url
214-
);
215-
setPayoutTxHash(response.data.destination.txHash);
216-
setPayoutTxHashUrl(url);
217-
setPayoutLoading(false);
218-
triggerResize();
219-
return;
220-
}
221-
222-
// Schedule next poll
223-
if (isActive) {
224-
timeoutId = setTimeout(pollPayout, poolDelay);
225-
}
226-
} catch (error) {
227-
console.error("[CONFIRMATION] Payout polling error:", error);
228-
if (isActive) {
229-
timeoutId = setTimeout(pollPayout, poolDelay);
230-
}
231-
}
232-
};
233-
234-
// Start polling
235-
timeoutId = setTimeout(pollPayout, 0);
236-
237-
return () => {
238-
isActive = false;
239-
if (timeoutId) {
240-
clearTimeout(timeoutId);
241-
}
242-
};
243-
}
244-
}, [txURL, order, done, rozoPaymentId, showProcessingPayout]);
166+
// Use payout polling hook
167+
const { payoutLoading, payoutTxHash, payoutTxHashUrl } = usePayoutPolling({
168+
enabled: showProcessingPayout,
169+
rozoPaymentId,
170+
order,
171+
done,
172+
showProcessingPayout,
173+
log: context.log,
174+
triggerResize,
175+
});
245176

246177
/**
247178
* Sets the payment completed state.
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import {
2+
getChainExplorerTxUrl,
3+
getPayment,
4+
RozoPayHydratedOrderWithOrg,
5+
RozoPayOrder,
6+
RozoPayOrderWithOrg,
7+
} from "@rozoai/intent-common";
8+
import { useEffect, useState } from "react";
9+
import { PayLogFn } from "../provider/PayContext";
10+
11+
const POLL_DELAY = 1000;
12+
13+
export interface UsePayoutPollingOptions {
14+
/** Whether polling is enabled */
15+
enabled: boolean | undefined;
16+
/** The Rozo payment ID to poll for */
17+
rozoPaymentId: string | undefined;
18+
/** The order object (for logging purposes) */
19+
order:
20+
| RozoPayOrder
21+
| RozoPayOrderWithOrg
22+
| RozoPayHydratedOrderWithOrg
23+
| null
24+
| undefined;
25+
/** Whether the payment is done */
26+
done: boolean | undefined;
27+
/** Whether to show processing payout */
28+
showProcessingPayout: boolean | undefined;
29+
/** Logging function */
30+
log: PayLogFn;
31+
/** Function to trigger resize */
32+
triggerResize: () => void;
33+
}
34+
35+
export interface UsePayoutPollingResult {
36+
/** Whether payout is currently loading */
37+
payoutLoading: boolean;
38+
/** The payout transaction hash */
39+
payoutTxHash: string | undefined;
40+
/** The URL to view the payout transaction */
41+
payoutTxHashUrl: string | undefined;
42+
}
43+
44+
/**
45+
* Hook to poll for payout transaction hash.
46+
* Polls the payment API until a payout transaction hash is found.
47+
*
48+
* @param options - Configuration options for payout polling
49+
* @returns Payout polling state and results
50+
*/
51+
export const usePayoutPolling = (
52+
options: UsePayoutPollingOptions
53+
): UsePayoutPollingResult => {
54+
const {
55+
enabled,
56+
rozoPaymentId,
57+
order,
58+
done,
59+
showProcessingPayout,
60+
log,
61+
triggerResize,
62+
} = options;
63+
64+
const [payoutLoading, setPayoutLoading] = useState<boolean>(false);
65+
const [payoutTxHash, setPayoutTxHash] = useState<string | undefined>(
66+
undefined
67+
);
68+
const [payoutTxHashUrl, setPayoutTxHashUrl] = useState<string | undefined>(
69+
undefined
70+
);
71+
72+
useEffect(() => {
73+
// Only start polling if all conditions are met and polling is enabled
74+
if (
75+
!enabled ||
76+
!order ||
77+
!done ||
78+
!rozoPaymentId ||
79+
!showProcessingPayout ||
80+
!("externalId" in order)
81+
) {
82+
return;
83+
}
84+
85+
triggerResize();
86+
log("[CONFIRMATION] Starting payout polling for order:", order.externalId);
87+
setPayoutLoading(true);
88+
89+
let isActive = true;
90+
let timeoutId: NodeJS.Timeout;
91+
92+
const pollPayout = async () => {
93+
if (!isActive || !rozoPaymentId) return;
94+
95+
try {
96+
log("[CONFIRMATION] Polling for payout transaction:", rozoPaymentId);
97+
const response = await getPayment(rozoPaymentId, "v2");
98+
log("[CONFIRMATION] Payout polling response:", response.data);
99+
100+
if (
101+
isActive &&
102+
response.data &&
103+
response.data.destination.txHash &&
104+
typeof response.data.destination.txHash === "string"
105+
) {
106+
const url = getChainExplorerTxUrl(
107+
Number(response.data.destination.chainId),
108+
response.data.destination.txHash
109+
);
110+
log(
111+
"[CONFIRMATION] Found payout transaction:",
112+
response.data.destination.txHash,
113+
"URL:",
114+
url
115+
);
116+
setPayoutTxHash(response.data.destination.txHash);
117+
setPayoutTxHashUrl(url);
118+
setPayoutLoading(false);
119+
triggerResize();
120+
return;
121+
}
122+
123+
// Schedule next poll
124+
if (isActive) {
125+
timeoutId = setTimeout(pollPayout, POLL_DELAY);
126+
}
127+
} catch (error) {
128+
console.error("[CONFIRMATION] Payout polling error:", error);
129+
if (isActive) {
130+
timeoutId = setTimeout(pollPayout, POLL_DELAY);
131+
}
132+
}
133+
};
134+
135+
// Start polling
136+
timeoutId = setTimeout(pollPayout, 0);
137+
138+
return () => {
139+
isActive = false;
140+
if (timeoutId) {
141+
clearTimeout(timeoutId);
142+
}
143+
};
144+
}, [
145+
enabled,
146+
order,
147+
done,
148+
rozoPaymentId,
149+
showProcessingPayout,
150+
log,
151+
triggerResize,
152+
]);
153+
154+
return {
155+
payoutLoading,
156+
payoutTxHash,
157+
payoutTxHashUrl,
158+
};
159+
};

0 commit comments

Comments
 (0)