expo-iap 2.2.7 → 2.2.8-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"useIap.js","sourceRoot":"","sources":["../src/useIap.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n endConnection,\n initConnection,\n purchaseErrorListener,\n purchaseUpdatedListener,\n transactionUpdatedIos,\n getProducts,\n getAvailablePurchases,\n getPurchaseHistory,\n finishTransaction as finishTransactionInternal,\n getSubscriptions,\n} from './';\nimport {useCallback, useEffect, useState, useRef} from 'react';\nimport {\n Product,\n ProductPurchase,\n Purchase,\n PurchaseError,\n PurchaseResult,\n SubscriptionProduct,\n SubscriptionPurchase,\n} from './ExpoIap.types';\nimport {TransactionEvent} from './modules/ios';\nimport {Subscription} from 'expo-modules-core';\nimport {Platform} from 'react-native';\n\ntype IAP_STATUS = {\n connected: boolean;\n products: Product[];\n promotedProductsIOS: ProductPurchase[];\n subscriptions: SubscriptionProduct[];\n purchaseHistories: ProductPurchase[];\n availablePurchases: ProductPurchase[];\n currentPurchase?: ProductPurchase;\n currentPurchaseError?: PurchaseError;\n finishTransaction: ({\n purchase,\n isConsumable,\n }: {\n purchase: Purchase;\n isConsumable?: boolean;\n }) => Promise<string | boolean | PurchaseResult | void>;\n getAvailablePurchases: () => Promise<void>;\n getPurchaseHistories: () => Promise<void>;\n getProducts: (skus: string[]) => Promise<void>;\n getSubscriptions: (skus: string[]) => Promise<void>;\n};\n\nexport function useIAP(): IAP_STATUS {\n const [connected, setConnected] = useState<boolean>(false);\n const [products, setProducts] = useState<Product[]>([]);\n const [promotedProductsIOS, setPromotedProductsIOS] = useState<\n ProductPurchase[]\n >([]);\n const [subscriptions, setSubscriptions] = useState<SubscriptionProduct[]>([]);\n const [purchaseHistories, setPurchaseHistories] = useState<ProductPurchase[]>(\n [],\n );\n const [availablePurchases, setAvailablePurchases] = useState<\n ProductPurchase[]\n >([]);\n const [currentPurchase, setCurrentPurchase] = useState<ProductPurchase>();\n const [currentPurchaseError, setCurrentPurchaseError] =\n useState<PurchaseError>();\n\n // 구독을 훅 인스턴스별로 관리하기 위한 ref\n const subscriptionsRef = useRef<{\n purchaseUpdate?: Subscription;\n purchaseError?: Subscription;\n promotedProductsIos?: Subscription;\n }>({});\n\n const requestProducts = useCallback(async (skus: string[]): Promise<void> => {\n setProducts(await getProducts(skus));\n }, []);\n\n const requestSubscriptions = useCallback(\n async (skus: string[]): Promise<void> => {\n setSubscriptions(await getSubscriptions(skus));\n },\n [],\n );\n\n const requestAvailablePurchases = useCallback(async (): Promise<void> => {\n setAvailablePurchases(await getAvailablePurchases());\n }, []);\n\n const requestPurchaseHistories = useCallback(async (): Promise<void> => {\n setPurchaseHistories(await getPurchaseHistory());\n }, []);\n\n const finishTransaction = useCallback(\n async ({\n purchase,\n isConsumable,\n }: {\n purchase: ProductPurchase;\n isConsumable?: boolean;\n }): Promise<string | boolean | PurchaseResult | void> => {\n try {\n return await finishTransactionInternal({\n purchase,\n isConsumable,\n });\n } catch (err) {\n throw err;\n } finally {\n if (purchase.id === currentPurchase?.id) {\n setCurrentPurchase(undefined);\n }\n if (purchase.id === currentPurchaseError?.productId) {\n setCurrentPurchaseError(undefined);\n }\n }\n },\n [currentPurchase?.id, currentPurchaseError?.productId],\n );\n\n const initIapWithSubscriptions = useCallback(async (): Promise<void> => {\n const result = await initConnection();\n setConnected(result);\n\n if (result) {\n subscriptionsRef.current.purchaseUpdate = purchaseUpdatedListener(\n async (purchase: Purchase | SubscriptionPurchase) => {\n setCurrentPurchaseError(undefined);\n setCurrentPurchase(purchase);\n },\n );\n\n subscriptionsRef.current.purchaseError = purchaseErrorListener(\n (error: PurchaseError) => {\n setCurrentPurchase(undefined);\n setCurrentPurchaseError(error);\n },\n );\n\n if (Platform.OS === 'ios') {\n subscriptionsRef.current.promotedProductsIos = transactionUpdatedIos(\n (event: TransactionEvent) => {\n setPromotedProductsIOS((prevProducts) =>\n event.transaction\n ? [...prevProducts, event.transaction]\n : prevProducts,\n );\n },\n );\n }\n }\n }, []);\n\n useEffect(() => {\n initIapWithSubscriptions();\n const currentSubscriptions = subscriptionsRef.current;\n\n return () => {\n currentSubscriptions.purchaseUpdate?.remove();\n currentSubscriptions.purchaseError?.remove();\n currentSubscriptions.promotedProductsIos?.remove();\n endConnection();\n setConnected(false);\n };\n }, [initIapWithSubscriptions]);\n\n return {\n connected,\n products,\n promotedProductsIOS,\n subscriptions,\n purchaseHistories,\n finishTransaction,\n availablePurchases,\n currentPurchase,\n currentPurchaseError,\n getProducts: requestProducts,\n getSubscriptions: requestSubscriptions,\n getAvailablePurchases: requestAvailablePurchases,\n getPurchaseHistories: requestPurchaseHistories,\n };\n}\n"]}
1
+ {"version":3,"file":"useIap.js","sourceRoot":"","sources":["../src/useIap.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n endConnection,\n initConnection,\n purchaseErrorListener,\n purchaseUpdatedListener,\n transactionUpdatedIos,\n getProducts,\n getAvailablePurchases,\n getPurchaseHistory,\n finishTransaction as finishTransactionInternal,\n getSubscriptions,\n requestPurchase as requestPurchaseInternal,\n} from './';\nimport {useCallback, useEffect, useState, useRef} from 'react';\nimport {\n Product,\n ProductPurchase,\n Purchase,\n PurchaseError,\n PurchaseResult,\n SubscriptionProduct,\n SubscriptionPurchase,\n} from './ExpoIap.types';\nimport {TransactionEvent} from './modules/ios';\nimport {Subscription} from 'expo-modules-core';\nimport {Platform} from 'react-native';\n\ntype IAP_STATUS = {\n connected: boolean;\n products: Product[];\n promotedProductsIOS: ProductPurchase[];\n subscriptions: SubscriptionProduct[];\n purchaseHistories: ProductPurchase[];\n availablePurchases: ProductPurchase[];\n currentPurchase?: ProductPurchase;\n currentPurchaseError?: PurchaseError;\n finishTransaction: ({\n purchase,\n isConsumable,\n }: {\n purchase: Purchase;\n isConsumable?: boolean;\n }) => Promise<string | boolean | PurchaseResult | void>;\n getAvailablePurchases: (skus: string[]) => Promise<void>;\n getPurchaseHistories: (skus: string[]) => Promise<void>;\n getProducts: (skus: string[]) => Promise<void>;\n getSubscriptions: (skus: string[]) => Promise<void>;\n requestPurchase: typeof requestPurchaseInternal;\n};\n\nexport function useIAP(): IAP_STATUS {\n const [connected, setConnected] = useState<boolean>(false);\n const [products, setProducts] = useState<Product[]>([]);\n const [promotedProductsIOS, setPromotedProductsIOS] = useState<\n ProductPurchase[]\n >([]);\n const [subscriptions, setSubscriptions] = useState<SubscriptionProduct[]>([]);\n const [purchaseHistories, setPurchaseHistories] = useState<ProductPurchase[]>(\n [],\n );\n const [availablePurchases, setAvailablePurchases] = useState<\n ProductPurchase[]\n >([]);\n const [currentPurchase, setCurrentPurchase] = useState<ProductPurchase>();\n const [currentPurchaseError, setCurrentPurchaseError] =\n useState<PurchaseError>();\n\n const subscriptionsRef = useRef<{\n purchaseUpdate?: Subscription;\n purchaseError?: Subscription;\n promotedProductsIos?: Subscription;\n }>({});\n\n const getProductsInternal = useCallback(\n async (skus: string[]): Promise<void> => {\n setProducts(await getProducts(skus));\n },\n [],\n );\n\n const getSubscriptionsInternal = useCallback(\n async (skus: string[]): Promise<void> => {\n setSubscriptions(await getSubscriptions(skus));\n },\n [],\n );\n\n const getAvailablePurchasesInternal = useCallback(async (): Promise<void> => {\n setAvailablePurchases(await getAvailablePurchases());\n }, []);\n\n const getPurchaseHistoriesInternal = useCallback(async (): Promise<void> => {\n setPurchaseHistories(await getPurchaseHistory());\n }, []);\n\n const finishTransaction = useCallback(\n async ({\n purchase,\n isConsumable,\n }: {\n purchase: ProductPurchase;\n isConsumable?: boolean;\n }): Promise<string | boolean | PurchaseResult | void> => {\n try {\n return await finishTransactionInternal({\n purchase,\n isConsumable,\n });\n } catch (err) {\n throw err;\n } finally {\n if (purchase.id === currentPurchase?.id) {\n setCurrentPurchase(undefined);\n }\n if (purchase.id === currentPurchaseError?.productId) {\n setCurrentPurchaseError(undefined);\n }\n }\n },\n [currentPurchase?.id, currentPurchaseError?.productId],\n );\n\n const initIapWithSubscriptions = useCallback(async (): Promise<void> => {\n const result = await initConnection();\n setConnected(result);\n\n if (result) {\n subscriptionsRef.current.purchaseUpdate = purchaseUpdatedListener(\n async (purchase: Purchase | SubscriptionPurchase) => {\n setCurrentPurchaseError(undefined);\n setCurrentPurchase(purchase);\n },\n );\n\n subscriptionsRef.current.purchaseError = purchaseErrorListener(\n (error: PurchaseError) => {\n setCurrentPurchase(undefined);\n setCurrentPurchaseError(error);\n },\n );\n\n if (Platform.OS === 'ios') {\n subscriptionsRef.current.promotedProductsIos = transactionUpdatedIos(\n (event: TransactionEvent) => {\n setPromotedProductsIOS((prevProducts) =>\n event.transaction\n ? [...prevProducts, event.transaction]\n : prevProducts,\n );\n },\n );\n }\n }\n }, []);\n\n useEffect(() => {\n initIapWithSubscriptions();\n const currentSubscriptions = subscriptionsRef.current;\n\n return () => {\n currentSubscriptions.purchaseUpdate?.remove();\n currentSubscriptions.purchaseError?.remove();\n currentSubscriptions.promotedProductsIos?.remove();\n endConnection();\n setConnected(false);\n };\n }, [initIapWithSubscriptions]);\n\n return {\n connected,\n products,\n promotedProductsIOS,\n subscriptions,\n purchaseHistories,\n finishTransaction,\n availablePurchases,\n currentPurchase,\n currentPurchaseError,\n getAvailablePurchases: getAvailablePurchasesInternal,\n getPurchaseHistories: getPurchaseHistoriesInternal,\n getProducts: getProductsInternal,\n getSubscriptions: getSubscriptionsInternal,\n requestPurchase: requestPurchaseInternal,\n };\n}\n"]}
package/iap.md CHANGED
@@ -4,47 +4,57 @@
4
4
 
5
5
  ## Overview
6
6
 
7
- `expo-iap` is an Expo module for handling in-app purchases (IAP) on iOS (StoreKit 2) and Android (Google Play Billing). It supports consumables, non-consumables, and subscriptions. While it’s functional in managed workflows, its still under active development—test thoroughly before deploying to production. For a fully stable alternative, consider RevenueCat.
7
+ `expo-iap` is an Expo module for handling in-app purchases (IAP) on iOS (StoreKit 2) and Android (Google Play Billing). It supports consumables, non-consumables, and subscriptions. Unlike [`react-native-iap`](https://github.com/hyochan/react-native-iap), which requires a bare workflow due to native dependencies, `expo-iap` is designed to work seamlessly in Expo's [managed workflow](https://docs.expo.dev/archive/managed-vs-bare)—no ejecting required! However, youll need to use a [development client](https://docs.expo.dev/development/introduction/) for full functionality. Starting from version 2.2.7, most features of `react-native-iap` have been ported.
8
8
 
9
- ## Installation in Managed Expo Projects
9
+ ## Installation
10
10
 
11
- For [managed](https://docs.expo.dev/archive/managed-vs-bare/) Expo projects, add `expo-iap` and configure it via the Expo config plugin. It’s compatible with Expo SDK 51+ (check the [API documentation](#api-documentation) for official inclusion). If no documentation is available, it’s awaiting a future SDK release.
11
+ `expo-iap` is compatible with [Expo SDK](https://expo.dev) 51+ and supports both managed and bare React Native projects. Official documentation is in progress for SDK inclusion (see [Expo IAP Documentation](link-to-docs)).
12
12
 
13
- ### Steps
13
+ ### Add the Package
14
14
 
15
- 1. **Add the Package**
15
+ ```bash
16
+ npm install expo-iap
17
+ ```
16
18
 
17
- ```bash
18
- npm install expo-iap
19
- ```
19
+ ### Configure with Expo Config Plugin
20
20
 
21
- 2. **Configure with Expo Config Plugin** Add `'expo-iap'` to the `plugins` array in your `app.json` or `app.config.js`:
21
+ Add `'expo-iap'` to the `plugins` array in your `app.json` or `app.config.js`:
22
22
 
23
- ```json
24
- {
25
- "expo": {
26
- "plugins": ["expo-iap"]
27
- }
28
- }
29
- ```
23
+ ```json
24
+ {
25
+ "expo": {
26
+ "plugins": ["expo-iap"]
27
+ }
28
+ }
29
+ ```
30
+
31
+ This plugin automatically configures Android BILLING permissions and iOS setup, making it plug-and-play for managed workflows with a development client.
30
32
 
31
- This automatically configures Android BILLING permissions and iOS setup.
33
+ ## Managed Expo Projects
32
34
 
33
- 3. **Run Your App** Use `expo run:android` or `expo run:ios` to build and test.
35
+ > **No Ejecting Required—Use a Development Client!**
36
+ > Unlike `react-native-iap`, `expo-iap` integrates into Expo’s managed workflow. However, you’ll need to use a [development client](https://docs.expo.dev/development/introduction/) instead of the default Expo Go app for full compatibility.
34
37
 
35
- > **Example**: See a working sample at [example/App.tsx](https://github.com/hyochan/expo-iap/blob/main/example/App.tsx).
38
+ 1. **Run Your App**
39
+ After adding the package and configuring the plugin, build a development client and test with:
36
40
 
37
- ## Installation in Bare React Native Projects
41
+ ```bash
42
+ expo run:android
43
+ expo run:ios
44
+ ```
38
45
 
39
- For bare projects, ensure the [`expo` package is installed and configured](https://docs.expo.dev/bare/installing-expo-modules/).
46
+ 2. **Example**
47
+ Check out a working sample at [example/App.tsx](https://github.com/hyochan/expo-iap/blob/main/example/App.tsx). It demonstrates:
48
+ - Initializing the connection (`initConnection`)
49
+ - Fetching products/subscriptions (`getProducts`, `getSubscriptions`)
50
+ - Handling purchases (`requestPurchase`, `requestSubscription`)
51
+ - Listening for updates/errors (`purchaseUpdatedListener`, `purchaseErrorListener`)
40
52
 
41
- ### Add the Package
53
+ ## Bare React Native Projects
42
54
 
43
- ```bash
44
- npm install expo-iap
45
- ```
55
+ Ensure the [`expo` package is installed and configured](https://docs.expo.dev/bare/installing-expo-modules/) before proceeding.
46
56
 
47
- ### Configure for iOS
57
+ ### Additional Configuration for iOS
48
58
 
49
59
  Run `npx pod-install`. Set `deploymentTarget` to `15.0` or higher (StoreKit 2 requirement):
50
60
 
@@ -54,23 +64,13 @@ Run `npx pod-install`. Set `deploymentTarget` to `15.0` or higher (StoreKit 2 re
54
64
  }
55
65
  ```
56
66
 
57
- ### Configure for Android
58
-
59
- No additional configuration is needed—Google Play Billing is handled internally.
60
-
61
- ### Configure with Expo Config Plugin
67
+ ### Additional Configuration for Android
62
68
 
63
- Add 'expo-iap' to the plugins array in your app.json or app.config.js file:
69
+ No additional configuration is needed—Google Play Billing is handled internally by the plugin.
64
70
 
65
- ```json
66
- {
67
- "expo": {
68
- "plugins": ["expo-iap"]
69
- }
70
- }
71
- ```
71
+ ## Current State & Feedback
72
72
 
73
- This plugin automatically sets up the necessary Android configuration, including adding the BILLING permission to your AndroidManifest.xml.
73
+ Updates are in progress to improve reliability and address remaining edge cases. For production apps, test thoroughly. Contributions (docs, code, or bug reports) are welcome—especially detailed error logs or use cases!
74
74
 
75
75
  ## IAP Types
76
76
 
@@ -123,7 +123,6 @@ This section outlines the properties of products supported by `expo-iap`.
123
123
  ### iOS-Only Product Types
124
124
 
125
125
  - **`ProductIos`**
126
- - `displayPrice: string`: Formatted price.
127
126
  - `isFamilyShareable: boolean`: Family sharing support.
128
127
  - `jsonRepresentation: string`: StoreKit 2 JSON data.
129
128
  - `subscription: SubscriptionInfo`: Subscription details.
@@ -185,10 +184,6 @@ This section describes purchase properties in `expo-iap`.
185
184
 
186
185
  Transactions are mapped directly to `Purchase` or `SubscriptionPurchase` with platform-specific fields (e.g., `expirationDateIos`, `purchaseStateAndroid`).
187
186
 
188
- ### Status
189
-
190
- This module is under development—expect occasional bugs (e.g., Android acknowledgment issues). Test thoroughly and consider contributing fixes!
191
-
192
187
  > **Sample Code**: See [example/App.tsx](https://github.com/hyochan/expo-iap/blob/main/example/App.tsx).
193
188
 
194
189
  ## Implementation
@@ -260,7 +255,7 @@ export default function SimpleIAP() {
260
255
 
261
256
  ## Using useIAP Hook
262
257
 
263
- The `useIAP` hook simplifies managing in-app purchases with `expo-iap`. Below is an example that fetches products and subscriptions, and allows purchasing them, styled similarly to the `expo-iap` example app. It assumes you’ve implemented the custom `useIAP` hook in your project.
258
+ The `useIAP` hook simplifies managing in-app purchases with `expo-iap`. Below is an example that fetches products and subscriptions only when connected, handles purchases, and displays them in a styled UI, similar to the `expo-iap` example app. This assumes you’ve implemented the `useIAP` hook in your project (e.g., as shown in `useIap.ts`).
264
259
 
265
260
  ```tsx
266
261
  import {useEffect, useState} from 'react';
@@ -275,13 +270,8 @@ import {
275
270
  InteractionManager,
276
271
  Alert,
277
272
  } from 'react-native';
278
- import {useIap} from 'expo-iap';
279
- import type {
280
- PurchaseError,
281
- ProductPurchase,
282
- SubscriptionProduct,
283
- } from 'expo-iap';
284
- import {RequestSubscriptionAndroidProps} from './types/ExpoIapAndroid.types'; // Adjust path as needed
273
+ import {useIAP} from 'expo-iap';
274
+ import type {ProductPurchase, SubscriptionProduct} from 'expo-iap';
285
275
 
286
276
  // Define SKUs
287
277
  const productSkus = [
@@ -297,12 +287,7 @@ const subscriptionSkus = [
297
287
  ];
298
288
 
299
289
  // Define operations
300
- const operations = [
301
- 'initConnection',
302
- 'getProducts',
303
- 'getSubscriptions',
304
- 'endConnection',
305
- ];
290
+ const operations = ['getProducts', 'getSubscriptions'] as const;
306
291
  type Operation = (typeof operations)[number];
307
292
 
308
293
  export default function IAPWithHook() {
@@ -319,35 +304,25 @@ export default function IAPWithHook() {
319
304
  requestSubscription,
320
305
  } = useIAP();
321
306
 
322
- const [isConnected, setIsConnected] = useState(connected);
307
+ const [isReady, setIsReady] = useState(false);
323
308
 
324
- // Handle operations
325
- const handleOperation = async (operation: Operation) => {
326
- switch (operation) {
327
- case 'initConnection':
328
- setIsConnected(true); // Connection is handled by useIAP hook
329
- break;
330
- case 'endConnection':
331
- setIsConnected(false); // Cleanup is handled by useIAP hook
332
- break;
333
- case 'getProducts':
334
- try {
335
- await getProducts(productSkus);
336
- } catch (error) {
337
- console.error('Error fetching products:', error);
338
- }
339
- break;
340
- case 'getSubscriptions':
341
- try {
342
- await getSubscriptions(subscriptionSkus);
343
- } catch (error) {
344
- console.error('Error fetching subscriptions:', error);
345
- }
346
- break;
347
- default:
348
- console.log('Unknown operation');
349
- }
350
- };
309
+ // Fetch products and subscriptions only when connected
310
+ useEffect(() => {
311
+ if (!connected) return;
312
+
313
+ const initializeIAP = async () => {
314
+ try {
315
+ await Promise.all([
316
+ getProducts(productSkus),
317
+ getSubscriptions(subscriptionSkus),
318
+ ]);
319
+ setIsReady(true);
320
+ } catch (error) {
321
+ console.error('Error initializing IAP:', error);
322
+ }
323
+ };
324
+ initializeIAP();
325
+ }, [connected, getProducts, getSubscriptions]);
351
326
 
352
327
  // Handle purchase updates and errors
353
328
  useEffect(() => {
@@ -356,22 +331,52 @@ export default function IAPWithHook() {
356
331
  try {
357
332
  await finishTransaction({
358
333
  purchase: currentPurchase,
359
- isConsumable: true,
334
+ isConsumable: currentPurchase.productType === 'inapp', // Consumable for in-app products
360
335
  });
361
- Alert.alert('Purchase updated', JSON.stringify(currentPurchase));
336
+ Alert.alert('Purchase Successful', JSON.stringify(currentPurchase));
362
337
  } catch (error) {
363
338
  console.error('Error finishing transaction:', error);
339
+ Alert.alert('Transaction Error', String(error));
364
340
  }
365
341
  });
366
342
  }
367
343
 
368
344
  if (currentPurchaseError) {
369
345
  InteractionManager.runAfterInteractions(() => {
370
- Alert.alert('Purchase error', JSON.stringify(currentPurchaseError));
346
+ Alert.alert('Purchase Error', JSON.stringify(currentPurchaseError));
371
347
  });
372
348
  }
373
349
  }, [currentPurchase, currentPurchaseError, finishTransaction]);
374
350
 
351
+ // Handle operation buttons
352
+ const handleOperation = async (operation: Operation) => {
353
+ if (!connected) {
354
+ Alert.alert('Not Connected', 'Please wait for IAP to connect.');
355
+ return;
356
+ }
357
+
358
+ try {
359
+ switch (operation) {
360
+ case 'getProducts':
361
+ await getProducts(productSkus);
362
+ break;
363
+ case 'getSubscriptions':
364
+ await getSubscriptions(subscriptionSkus);
365
+ break;
366
+ }
367
+ } catch (error) {
368
+ console.error(`Error in ${operation}:`, error);
369
+ }
370
+ };
371
+
372
+ if (!connected) {
373
+ return (
374
+ <SafeAreaView style={styles.container}>
375
+ <Text style={styles.title}>Connecting to IAP...</Text>
376
+ </SafeAreaView>
377
+ );
378
+ }
379
+
375
380
  return (
376
381
  <SafeAreaView style={styles.container}>
377
382
  <Text style={styles.title}>Expo IAP with useIAP Hook</Text>
@@ -390,82 +395,61 @@ export default function IAPWithHook() {
390
395
  </ScrollView>
391
396
  </View>
392
397
  <View style={styles.content}>
393
- {!isConnected ? (
394
- <Text>Not connected</Text>
398
+ {!isReady ? (
399
+ <Text>Loading...</Text>
395
400
  ) : (
396
401
  <View style={{gap: 12}}>
397
402
  <Text style={{fontSize: 20}}>Products</Text>
398
- {products.map((item) => {
399
- if (item.platform === 'android') {
400
- return (
401
- <View key={item.title} style={{gap: 12}}>
402
- <Text>
403
- {item.title} -{' '}
404
- {item.oneTimePurchaseOfferDetails?.formattedPrice}
405
- </Text>
406
- <Button
407
- title="Buy"
408
- onPress={() => requestPurchase({skus: [item.id]})}
409
- />
410
- </View>
411
- );
412
- }
413
- if (item.platform === 'ios') {
414
- return (
415
- <View key={item.id} style={{gap: 12}}>
416
- <Text>
417
- {item.title} - {item.displayPrice}
418
- </Text>
419
- <Button
420
- title="Buy"
421
- onPress={() => requestPurchase({sku: item.id})}
422
- />
423
- </View>
424
- );
425
- }
426
- })}
403
+ {products.map((item) => (
404
+ <View key={item.id} style={{gap: 12}}>
405
+ <Text>
406
+ {item.title} -{' '}
407
+ {item.platform === 'android'
408
+ ? item.oneTimePurchaseOfferDetails?.formattedPrice
409
+ : item.displayPrice}
410
+ </Text>
411
+ <Button
412
+ title="Buy"
413
+ onPress={() =>
414
+ requestPurchase(
415
+ item.platform === 'android'
416
+ ? {skus: [item.id]}
417
+ : {sku: item.id},
418
+ )
419
+ }
420
+ />
421
+ </View>
422
+ ))}
427
423
 
428
424
  <Text style={{fontSize: 20}}>Subscriptions</Text>
429
- {subscriptions.map((item) => {
430
- if (item.platform === 'android') {
431
- return item.subscriptionOfferDetails?.map((offer) => (
432
- <View key={offer.offerId} style={{gap: 12}}>
433
- <Text>
434
- {item.title} -{' '}
435
- {offer.pricingPhases.pricingPhaseList
436
- .map((ppl) => ppl.billingPeriod)
437
- .join(',')}
438
- </Text>
439
- <Button
440
- title="Subscribe"
441
- onPress={() =>
442
- requestSubscription({
443
- skus: [item.id],
444
- ...(offer.offerToken && {
445
- subscriptionOffers: [
446
- {sku: item.id, offerToken: offer.offerToken},
447
- ],
448
- }),
449
- } as RequestSubscriptionAndroidProps)
450
- }
451
- />
452
- </View>
453
- ));
454
- }
455
- if (item.platform === 'ios') {
456
- return (
457
- <View key={item.id} style={{gap: 12}}>
458
- <Text>
459
- {item.displayName} - {item.displayPrice}
460
- </Text>
461
- <Button
462
- title="Subscribe"
463
- onPress={() => requestSubscription({sku: item.id})}
464
- />
465
- </View>
466
- );
467
- }
468
- })}
425
+ {subscriptions.map((item) => (
426
+ <View key={item.id} style={{gap: 12}}>
427
+ <Text>
428
+ {item.title || item.displayName} -{' '}
429
+ {item.platform === 'android' && item.subscriptionOfferDetails
430
+ ? item.subscriptionOfferDetails[0]?.pricingPhases
431
+ .pricingPhaseList[0].formattedPrice
432
+ : item.displayPrice}
433
+ </Text>
434
+ <Button
435
+ title="Subscribe"
436
+ onPress={() =>
437
+ requestSubscription(
438
+ item.platform === 'android'
439
+ ? {
440
+ skus: [item.id],
441
+ subscriptionOffers:
442
+ item.subscriptionOfferDetails?.map((offer) => ({
443
+ sku: item.id,
444
+ offerToken: offer.offerToken,
445
+ })) || [],
446
+ }
447
+ : {sku: item.id},
448
+ )
449
+ }
450
+ />
451
+ </View>
452
+ ))}
469
453
  </View>
470
454
  )}
471
455
  </View>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-iap",
3
- "version": "2.2.7",
3
+ "version": "2.2.8-rc.1",
4
4
  "description": "In App Purchase module in Expo",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -34,11 +34,7 @@ export type ProductBase = {
34
34
  // Define literal platform types for better type discrimination
35
35
  export type IosPlatform = {platform: 'ios'};
36
36
  export type AndroidPlatform = {platform: 'android'};
37
-
38
- export enum ProductType {
39
- InAppPurchase = 'inapp',
40
- Subscription = 'subs',
41
- }
37
+ export type ProductType = 'inapp' | 'subs';
42
38
 
43
39
  // Common base purchase type
44
40
  export type PurchaseBase = {