Skip to content

Commit 952c98e

Browse files
committed
fix: resolve CI build failures and add missing APIs
- Add missing requestProducts() function for unified product fetching - Add deprecation warnings to getProducts() and getSubscriptions() - Change getStorefrontIOS() to show warning instead of throwing error on non-iOS platforms - Add requestProducts to useIap hook for consistency - Fix documentation build issues (broken anchors, missing tags, truncation marker) - Update blog post link to correct path
1 parent bca1999 commit 952c98e

File tree

5 files changed

+150
-2
lines changed

5 files changed

+150
-2
lines changed

docs/blog/2025-07-22-v2-6-3-release.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ tags: [release, ios, apptransaction]
99

1010
We're excited to announce the release of expo-iap version 2.6.3, which includes critical fixes for iOS AppTransaction functionality.
1111

12+
<!-- truncate -->
13+
1214
## What's New
1315

1416
### Complete AppTransaction Properties

docs/blog/2025-07-22-v2-7-0-release.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ bun add expo-iap@2.7.0
325325

326326
## 📚 Documentation
327327

328-
- Check our [updated documentation](/docs/latest/guides/purchases)
328+
- Check our [updated documentation](/docs/guides/purchases)
329329
- View [complete examples](https://github.com/hyochan/expo-iap/tree/main/example)
330330
- Join our [community discussions](https://github.com/hyochan/expo-iap/discussions)
331331

docs/blog/tags.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,33 @@ breaking-changes:
3232
label: Breaking Changes
3333
permalink: /breaking-changes
3434
description: Updates that require code changes
35+
36+
release:
37+
label: Release
38+
permalink: /release
39+
description: New version releases and updates
40+
41+
api:
42+
label: API
43+
permalink: /api
44+
description: API updates and changes
45+
46+
android:
47+
label: Android
48+
permalink: /android
49+
description: Android platform specific features
50+
51+
google-play-billing:
52+
label: Google Play Billing
53+
permalink: /google-play-billing
54+
description: Google Play Billing Library updates
55+
56+
apptransaction:
57+
label: App Transaction
58+
permalink: /apptransaction
59+
description: App transaction related features
60+
61+
migration:
62+
label: Migration
63+
permalink: /migration
64+
description: Migration guides and updates

src/index.ts

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ export function initConnection() {
8181
}
8282

8383
export const getProducts = async (skus: string[]): Promise<Product[]> => {
84+
console.warn(
85+
'`getProducts` is deprecated. Use `requestProducts({ skus, type: \'inapp\' })` instead. This function will be removed in version 3.0.0.',
86+
);
8487
if (!skus?.length) {
8588
return Promise.reject(new Error('"skus" is required'));
8689
}
@@ -112,6 +115,9 @@ export const getProducts = async (skus: string[]): Promise<Product[]> => {
112115
export const getSubscriptions = async (
113116
skus: string[],
114117
): Promise<SubscriptionProduct[]> => {
118+
console.warn(
119+
'`getSubscriptions` is deprecated. Use `requestProducts({ skus, type: \'subs\' })` instead. This function will be removed in version 3.0.0.',
120+
);
115121
if (!skus?.length) {
116122
return Promise.reject(new Error('"skus" is required'));
117123
}
@@ -151,6 +157,78 @@ export async function endConnection(): Promise<boolean> {
151157
return ExpoIapModule.endConnection();
152158
}
153159

160+
/**
161+
* Request products with unified API (v2.7.0+)
162+
*
163+
* @param params - Product request configuration
164+
* @param params.skus - Array of product SKUs to fetch
165+
* @param params.type - Type of products: 'inapp' for regular products (default) or 'subs' for subscriptions
166+
*
167+
* @example
168+
* ```typescript
169+
* // Regular products
170+
* const products = await requestProducts({
171+
* skus: ['product1', 'product2'],
172+
* type: 'inapp'
173+
* });
174+
*
175+
* // Subscriptions
176+
* const subscriptions = await requestProducts({
177+
* skus: ['sub1', 'sub2'],
178+
* type: 'subs'
179+
* });
180+
* ```
181+
*/
182+
export const requestProducts = async ({
183+
skus,
184+
type = 'inapp',
185+
}: {
186+
skus: string[];
187+
type?: 'inapp' | 'subs';
188+
}): Promise<Product[] | SubscriptionProduct[]> => {
189+
if (!skus?.length) {
190+
throw new Error('No SKUs provided');
191+
}
192+
193+
if (Platform.OS === 'ios') {
194+
const rawItems = await ExpoIapModule.getItems(skus);
195+
const filteredItems = rawItems.filter((item: unknown) => {
196+
if (!isProductIos(item)) return false;
197+
return (
198+
typeof item === 'object' &&
199+
item !== null &&
200+
'id' in item &&
201+
typeof item.id === 'string' &&
202+
skus.includes(item.id)
203+
);
204+
});
205+
206+
return type === 'inapp'
207+
? (filteredItems as Product[])
208+
: (filteredItems as SubscriptionProduct[]);
209+
}
210+
211+
if (Platform.OS === 'android') {
212+
const items = await ExpoIapModule.getItemsByType(type, skus);
213+
const filteredItems = items.filter((item: unknown) => {
214+
if (!isProductAndroid(item)) return false;
215+
return (
216+
typeof item === 'object' &&
217+
item !== null &&
218+
'id' in item &&
219+
typeof item.id === 'string' &&
220+
skus.includes(item.id)
221+
);
222+
});
223+
224+
return type === 'inapp'
225+
? (filteredItems as Product[])
226+
: (filteredItems as SubscriptionProduct[]);
227+
}
228+
229+
throw new Error('Unsupported platform');
230+
};
231+
154232
/**
155233
* @deprecated Use `getPurchaseHistories` instead. This function will be removed in version 3.0.0.
156234
*/
@@ -529,7 +607,8 @@ export const finishTransaction = ({
529607
*/
530608
export const getStorefrontIOS = (): Promise<string> => {
531609
if (Platform.OS !== 'ios') {
532-
throw new Error('getStorefrontIOS: This method is only available on iOS');
610+
console.warn('getStorefrontIOS: This method is only available on iOS');
611+
return Promise.resolve('');
533612
}
534613
return ExpoIapModule.getStorefront();
535614
};

src/useIap.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
finishTransaction as finishTransactionInternal,
1616
getSubscriptions,
1717
requestPurchase as requestPurchaseInternal,
18+
requestProducts,
1819
} from './';
1920
import {syncIOS, validateReceiptIOS} from './modules/ios';
2021
import {validateReceiptAndroid} from './modules/android';
@@ -54,6 +55,10 @@ type UseIap = {
5455
getPurchaseHistories: (skus: string[]) => Promise<void>;
5556
getProducts: (skus: string[]) => Promise<void>;
5657
getSubscriptions: (skus: string[]) => Promise<void>;
58+
requestProducts: (params: {
59+
skus: string[];
60+
type?: 'inapp' | 'subs';
61+
}) => Promise<void>;
5762
requestPurchase: (params: {
5863
request: RequestPurchaseProps | RequestSubscriptionProps;
5964
type?: 'inapp' | 'subs';
@@ -179,6 +184,37 @@ export function useIAP(options?: UseIAPOptions): UseIap {
179184
[mergeWithDuplicateCheck],
180185
);
181186

187+
const requestProductsInternal = useCallback(
188+
async (params: {
189+
skus: string[];
190+
type?: 'inapp' | 'subs';
191+
}): Promise<void> => {
192+
try {
193+
const result = await requestProducts(params);
194+
if (params.type === 'subs') {
195+
setSubscriptions((prevSubscriptions) =>
196+
mergeWithDuplicateCheck(
197+
prevSubscriptions,
198+
result as SubscriptionProduct[],
199+
(subscription) => subscription.id,
200+
),
201+
);
202+
} else {
203+
setProducts((prevProducts) =>
204+
mergeWithDuplicateCheck(
205+
prevProducts,
206+
result as Product[],
207+
(product) => product.id,
208+
),
209+
);
210+
}
211+
} catch (error) {
212+
console.error('Error fetching products:', error);
213+
}
214+
},
215+
[mergeWithDuplicateCheck],
216+
);
217+
182218
const getAvailablePurchasesInternal = useCallback(async (): Promise<void> => {
183219
try {
184220
const result = await getAvailablePurchases();
@@ -389,6 +425,7 @@ export function useIAP(options?: UseIAPOptions): UseIap {
389425
getPurchaseHistories: getPurchaseHistoriesInternal,
390426
getProducts: getProductsInternal,
391427
getSubscriptions: getSubscriptionsInternal,
428+
requestProducts: requestProductsInternal,
392429
requestPurchase: requestPurchaseWithReset,
393430
validateReceipt,
394431
restorePurchases,

0 commit comments

Comments
 (0)