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

Commit de94e83

Browse files
hyochanclaude
andauthored
fix(useIAP): forward PurchaseOptions in getAvailablePurchases and res… (#3173)
…torePurchases The hook's getAvailablePurchases and restorePurchases methods were hardcoding PurchaseOptions (alsoPublishToEventListenerIOS, onlyIncludeActiveItemsIOS), making it impossible for hook consumers to customize these values. This change adds an optional PurchaseOptions parameter to both methods, defaulting to the previous behavior for backward compatibility. Related to hyochan/expo-iap#329 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **New Features** * Added optional configuration options to purchase retrieval methods for greater control over which purchases are included in results. * New platform-specific settings available: iOS can filter active items and control event listener publishing; Android can include or exclude suspended purchases. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 1bbbbae commit de94e83

3 files changed

Lines changed: 29 additions & 20 deletions

File tree

src/__tests__/index.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -812,10 +812,10 @@ describe('Public API (src/index.ts)', () => {
812812
.mockResolvedValueOnce([nitro('s1')]);
813813
const res = await IAP.getAvailablePurchases();
814814
expect(mockIap.getAvailablePurchases).toHaveBeenNthCalledWith(1, {
815-
android: {type: 'inapp'},
815+
android: {type: 'inapp', includeSuspended: false},
816816
});
817817
expect(mockIap.getAvailablePurchases).toHaveBeenNthCalledWith(2, {
818-
android: {type: 'subs'},
818+
android: {type: 'subs', includeSuspended: false},
819819
});
820820
expect(res.map((p: any) => p.productId).sort()).toEqual(['p1', 's1']);
821821
});

src/hooks/useIAP.ts

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import type {
4040
VerifyPurchaseResult,
4141
VerifyPurchaseWithProviderProps,
4242
VerifyPurchaseWithProviderResult,
43+
PurchaseOptions,
4344
} from '../types';
4445
import type {
4546
ActiveSubscription,
@@ -63,7 +64,7 @@ type UseIap = {
6364
promotedProductIOS?: Product;
6465
activeSubscriptions: ActiveSubscription[];
6566
finishTransaction: (args: MutationFinishTransactionArgs) => Promise<void>;
66-
getAvailablePurchases: (skus?: string[]) => Promise<void>;
67+
getAvailablePurchases: (options?: PurchaseOptions) => Promise<void>;
6768
fetchProducts: (params: {
6869
skus: string[];
6970
type?: ProductQueryType | null;
@@ -83,7 +84,7 @@ type UseIap = {
8384
verifyPurchaseWithProvider: (
8485
options: VerifyPurchaseWithProviderProps,
8586
) => Promise<VerifyPurchaseWithProviderResult>;
86-
restorePurchases: () => Promise<void>;
87+
restorePurchases: (options?: PurchaseOptions) => Promise<void>;
8788
getPromotedProductIOS: () => Promise<Product | null>;
8889
requestPurchaseOnPromotedProductIOS: () => Promise<boolean>;
8990
getActiveSubscriptions: (
@@ -271,11 +272,13 @@ export function useIAP(options?: UseIapOptions): UseIap {
271272
);
272273

273274
const getAvailablePurchasesInternal = useCallback(
274-
async (_skus?: string[]): Promise<void> => {
275+
async (options?: PurchaseOptions): Promise<void> => {
275276
try {
276277
const result = await getAvailablePurchases({
277-
alsoPublishToEventListenerIOS: false,
278-
onlyIncludeActiveItemsIOS: true,
278+
alsoPublishToEventListenerIOS:
279+
options?.alsoPublishToEventListenerIOS ?? false,
280+
onlyIncludeActiveItemsIOS: options?.onlyIncludeActiveItemsIOS ?? true,
281+
includeSuspendedAndroid: options?.includeSuspendedAndroid ?? false,
279282
});
280283
setAvailablePurchases(result);
281284
} catch (error) {
@@ -333,18 +336,21 @@ export function useIAP(options?: UseIapOptions): UseIap {
333336
[],
334337
);
335338

336-
const restorePurchases = useCallback(async (): Promise<void> => {
337-
try {
338-
if (Platform.OS === 'ios') {
339-
await syncIOS();
340-
}
339+
const restorePurchases = useCallback(
340+
async (options?: PurchaseOptions): Promise<void> => {
341+
try {
342+
if (Platform.OS === 'ios') {
343+
await syncIOS();
344+
}
341345

342-
await getAvailablePurchasesInternal();
343-
} catch (error) {
344-
RnIapConsole.warn('Failed to restore purchases:', error);
345-
invokeOnError(error);
346-
}
347-
}, [getAvailablePurchasesInternal, invokeOnError]);
346+
await getAvailablePurchasesInternal(options);
347+
} catch (error) {
348+
RnIapConsole.warn('Failed to restore purchases:', error);
349+
invokeOnError(error);
350+
}
351+
},
352+
[getAvailablePurchasesInternal, invokeOnError],
353+
);
348354

349355
const validateReceipt = useCallback(
350356
async (options: VerifyPurchaseProps): Promise<VerifyPurchaseResult> =>

src/index.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -737,11 +737,14 @@ export const getAvailablePurchases: QueryField<
737737
return validPurchases.map(convertNitroPurchaseToPurchase);
738738
} else if (Platform.OS === 'android') {
739739
// For Android, we need to call twice for inapp and subs
740+
const includeSuspended = Boolean(
741+
options?.includeSuspendedAndroid ?? false,
742+
);
740743
const inappNitroPurchases = await IAP.instance.getAvailablePurchases({
741-
android: {type: 'inapp'},
744+
android: {type: 'inapp', includeSuspended},
742745
});
743746
const subsNitroPurchases = await IAP.instance.getAvailablePurchases({
744-
android: {type: 'subs'},
747+
android: {type: 'subs', includeSuspended},
745748
});
746749

747750
// Validate and convert both sets of purchases

0 commit comments

Comments
 (0)