Skip to content
This repository was archived by the owner on Aug 4, 2026. It is now read-only.

Commit 1c268eb

Browse files
hyochanclaude
andcommitted
fix(hook): handle initConnection errors with onError callback
- Wrap initConnection in try/catch to prevent unhandled exceptions - Call invokeOnError when initConnection fails - Clean up listeners on error - Add tests for initConnection error handling Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 5568f48 commit 1c268eb

2 files changed

Lines changed: 82 additions & 6 deletions

File tree

src/__tests__/hooks/useIAP.test.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,5 +285,68 @@ describe('hooks/useIAP (renderer)', () => {
285285
expect(onError).toHaveBeenCalledWith(expect.any(Error));
286286
expect(onError.mock.calls[0][0].message).toBe(stringError);
287287
});
288+
289+
it('calls onError when initConnection fails', async () => {
290+
const initError = new Error('Failed to initialize connection');
291+
jest.spyOn(IAP, 'initConnection').mockRejectedValueOnce(initError);
292+
293+
const onError = jest.fn();
294+
const Harness = () => {
295+
useIAP({onError});
296+
return null;
297+
};
298+
299+
await act(async () => {
300+
TestRenderer.create(React.createElement(Harness));
301+
});
302+
303+
// Wait for initConnection to be called and fail
304+
await act(async () => {});
305+
306+
expect(onError).toHaveBeenCalledWith(initError);
307+
});
308+
309+
it('does not throw unhandled exception when initConnection fails with onError', async () => {
310+
const initError = new Error('Store unavailable');
311+
jest.spyOn(IAP, 'initConnection').mockRejectedValueOnce(initError);
312+
313+
const onError = jest.fn();
314+
const Harness = () => {
315+
useIAP({onError});
316+
return null;
317+
};
318+
319+
// This should not throw an unhandled promise rejection
320+
await act(async () => {
321+
TestRenderer.create(React.createElement(Harness));
322+
});
323+
324+
await act(async () => {});
325+
326+
// onError should be called, error should be handled gracefully
327+
expect(onError).toHaveBeenCalledWith(initError);
328+
});
329+
330+
it('handles initConnection failure without onError callback', async () => {
331+
const initError = new Error('Connection failed');
332+
const initConnectionSpy = jest
333+
.spyOn(IAP, 'initConnection')
334+
.mockRejectedValueOnce(initError);
335+
336+
// No onError callback - should not throw unhandled exception
337+
const Harness = () => {
338+
useIAP();
339+
return null;
340+
};
341+
342+
await act(async () => {
343+
TestRenderer.create(React.createElement(Harness));
344+
});
345+
346+
await act(async () => {});
347+
348+
// Test passes if no unhandled exception is thrown
349+
expect(initConnectionSpy).toHaveBeenCalled();
350+
});
288351
});
289352
});

src/hooks/useIAP.ts

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -440,15 +440,28 @@ export function useIAP(options?: UseIapOptions): UseIap {
440440
}
441441
}
442442

443-
const result = await initConnection(config);
444-
setConnected(result);
445-
if (!result) {
446-
// Clean up some listeners but leave purchaseError for potential retries
443+
try {
444+
const result = await initConnection(config);
445+
setConnected(result);
446+
if (!result) {
447+
// Clean up some listeners but leave purchaseError for potential retries
448+
subscriptionsRef.current.purchaseUpdate?.remove();
449+
subscriptionsRef.current.purchaseUpdate = undefined;
450+
}
451+
} catch (error) {
452+
RnIapConsole.error('initConnection failed:', error);
453+
invokeOnError(error);
454+
// Clean up listeners on error
447455
subscriptionsRef.current.purchaseUpdate?.remove();
456+
subscriptionsRef.current.promotedProductIOS?.remove();
448457
subscriptionsRef.current.purchaseUpdate = undefined;
449-
return;
458+
subscriptionsRef.current.promotedProductIOS = undefined;
450459
}
451-
}, [getActiveSubscriptionsInternal, getAvailablePurchasesInternal]);
460+
}, [
461+
getActiveSubscriptionsInternal,
462+
getAvailablePurchasesInternal,
463+
invokeOnError,
464+
]);
452465

453466
useEffect(() => {
454467
initIapWithSubscriptions();

0 commit comments

Comments
 (0)