expo-iap 2.2.7-rc.1 → 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
30
 
31
- This automatically configures Android BILLING permissions and iOS setup.
31
+ This plugin automatically configures Android BILLING permissions and iOS setup, making it plug-and-play for managed workflows with a development client.
32
32
 
33
- 3. **Run Your App** Use `expo run:android` or `expo run:ios` to build and test.
33
+ ## Managed Expo Projects
34
34
 
35
- > **Example**: See a working sample at [example/App.tsx](https://github.com/hyochan/expo-iap/blob/main/example/App.tsx).
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.
36
37
 
37
- ## Installation in Bare React Native Projects
38
+ 1. **Run Your App**
39
+ After adding the package and configuring the plugin, build a development client and test with:
38
40
 
39
- For bare projects, ensure the [`expo` package is installed and configured](https://docs.expo.dev/bare/installing-expo-modules/).
41
+ ```bash
42
+ expo run:android
43
+ expo run:ios
44
+ ```
40
45
 
41
- ### Add the Package
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`)
42
52
 
43
- ```bash
44
- npm install expo-iap
45
- ```
53
+ ## Bare React Native Projects
46
54
 
47
- ### Configure for iOS
55
+ Ensure the [`expo` package is installed and configured](https://docs.expo.dev/bare/installing-expo-modules/) before proceeding.
56
+
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.
@@ -147,6 +146,7 @@ This section describes purchase properties in `expo-iap`.
147
146
  ### Android-Only Purchase Types
148
147
 
149
148
  - **`ProductPurchase`**:
149
+
150
150
  - Adds the following properties specific to in-app product purchases:
151
151
  - **`ids`**: `string[]` - A list of product IDs associated with the purchase (for multi-item purchases).
152
152
  - **`dataAndroid`**: `string` - The raw purchase data from Google Play (e.g., JSON payload).
@@ -154,6 +154,7 @@ This section describes purchase properties in `expo-iap`.
154
154
  - **`purchaseStateAndroid`**: `number` - The state of the purchase (e.g., 0 = purchased, 1 = canceled, 2 = pending).
155
155
 
156
156
  - **`SubscriptionPurchase`**:
157
+
157
158
  - Extends the base properties and includes:
158
159
  - **`autoRenewingAndroid`**: `boolean` - Indicates whether the subscription automatically renews (true) or not (false).
159
160
 
@@ -163,6 +164,7 @@ This section describes purchase properties in `expo-iap`.
163
164
  ### iOS-Only Purchase Types
164
165
 
165
166
  - **`ProductPurchase`**:
167
+
166
168
  - Extends the base purchase properties with iOS-specific fields:
167
169
  - **`quantityIos`**: `number` - The quantity of the product purchased (e.g., how many units of an item were bought).
168
170
  - **`expirationDateIos`**: `number?` - The expiration date of the purchase as a Unix timestamp (in milliseconds), if applicable (optional, may be null for non-expiring products).
@@ -182,10 +184,6 @@ This section describes purchase properties in `expo-iap`.
182
184
 
183
185
  Transactions are mapped directly to `Purchase` or `SubscriptionPurchase` with platform-specific fields (e.g., `expirationDateIos`, `purchaseStateAndroid`).
184
186
 
185
- ### Status
186
-
187
- This module is under development—expect occasional bugs (e.g., Android acknowledgment issues). Test thoroughly and consider contributing fixes!
188
-
189
187
  > **Sample Code**: See [example/App.tsx](https://github.com/hyochan/expo-iap/blob/main/example/App.tsx).
190
188
 
191
189
  ## Implementation
@@ -257,7 +255,7 @@ export default function SimpleIAP() {
257
255
 
258
256
  ## Using useIAP Hook
259
257
 
260
- 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`).
261
259
 
262
260
  ```tsx
263
261
  import {useEffect, useState} from 'react';
@@ -272,13 +270,8 @@ import {
272
270
  InteractionManager,
273
271
  Alert,
274
272
  } from 'react-native';
275
- import {useIap} from 'expo-iap';
276
- import type {
277
- PurchaseError,
278
- ProductPurchase,
279
- SubscriptionProduct,
280
- } from 'expo-iap';
281
- import {RequestSubscriptionAndroidProps} from './types/ExpoIapAndroid.types'; // Adjust path as needed
273
+ import {useIAP} from 'expo-iap';
274
+ import type {ProductPurchase, SubscriptionProduct} from 'expo-iap';
282
275
 
283
276
  // Define SKUs
284
277
  const productSkus = [
@@ -294,12 +287,7 @@ const subscriptionSkus = [
294
287
  ];
295
288
 
296
289
  // Define operations
297
- const operations = [
298
- 'initConnection',
299
- 'getProducts',
300
- 'getSubscriptions',
301
- 'endConnection',
302
- ];
290
+ const operations = ['getProducts', 'getSubscriptions'] as const;
303
291
  type Operation = (typeof operations)[number];
304
292
 
305
293
  export default function IAPWithHook() {
@@ -316,35 +304,25 @@ export default function IAPWithHook() {
316
304
  requestSubscription,
317
305
  } = useIAP();
318
306
 
319
- const [isConnected, setIsConnected] = useState(connected);
307
+ const [isReady, setIsReady] = useState(false);
320
308
 
321
- // Handle operations
322
- const handleOperation = async (operation: Operation) => {
323
- switch (operation) {
324
- case 'initConnection':
325
- setIsConnected(true); // Connection is handled by useIAP hook
326
- break;
327
- case 'endConnection':
328
- setIsConnected(false); // Cleanup is handled by useIAP hook
329
- break;
330
- case 'getProducts':
331
- try {
332
- await getProducts(productSkus);
333
- } catch (error) {
334
- console.error('Error fetching products:', error);
335
- }
336
- break;
337
- case 'getSubscriptions':
338
- try {
339
- await getSubscriptions(subscriptionSkus);
340
- } catch (error) {
341
- console.error('Error fetching subscriptions:', error);
342
- }
343
- break;
344
- default:
345
- console.log('Unknown operation');
346
- }
347
- };
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]);
348
326
 
349
327
  // Handle purchase updates and errors
350
328
  useEffect(() => {
@@ -353,22 +331,52 @@ export default function IAPWithHook() {
353
331
  try {
354
332
  await finishTransaction({
355
333
  purchase: currentPurchase,
356
- isConsumable: true,
334
+ isConsumable: currentPurchase.productType === 'inapp', // Consumable for in-app products
357
335
  });
358
- Alert.alert('Purchase updated', JSON.stringify(currentPurchase));
336
+ Alert.alert('Purchase Successful', JSON.stringify(currentPurchase));
359
337
  } catch (error) {
360
338
  console.error('Error finishing transaction:', error);
339
+ Alert.alert('Transaction Error', String(error));
361
340
  }
362
341
  });
363
342
  }
364
343
 
365
344
  if (currentPurchaseError) {
366
345
  InteractionManager.runAfterInteractions(() => {
367
- Alert.alert('Purchase error', JSON.stringify(currentPurchaseError));
346
+ Alert.alert('Purchase Error', JSON.stringify(currentPurchaseError));
368
347
  });
369
348
  }
370
349
  }, [currentPurchase, currentPurchaseError, finishTransaction]);
371
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
+
372
380
  return (
373
381
  <SafeAreaView style={styles.container}>
374
382
  <Text style={styles.title}>Expo IAP with useIAP Hook</Text>
@@ -387,82 +395,61 @@ export default function IAPWithHook() {
387
395
  </ScrollView>
388
396
  </View>
389
397
  <View style={styles.content}>
390
- {!isConnected ? (
391
- <Text>Not connected</Text>
398
+ {!isReady ? (
399
+ <Text>Loading...</Text>
392
400
  ) : (
393
401
  <View style={{gap: 12}}>
394
402
  <Text style={{fontSize: 20}}>Products</Text>
395
- {products.map((item) => {
396
- if (item.platform === 'android') {
397
- return (
398
- <View key={item.title} style={{gap: 12}}>
399
- <Text>
400
- {item.title} -{' '}
401
- {item.oneTimePurchaseOfferDetails?.formattedPrice}
402
- </Text>
403
- <Button
404
- title="Buy"
405
- onPress={() => requestPurchase({skus: [item.id]})}
406
- />
407
- </View>
408
- );
409
- }
410
- if (item.platform === 'ios') {
411
- return (
412
- <View key={item.id} style={{gap: 12}}>
413
- <Text>
414
- {item.title} - {item.displayPrice}
415
- </Text>
416
- <Button
417
- title="Buy"
418
- onPress={() => requestPurchase({sku: item.id})}
419
- />
420
- </View>
421
- );
422
- }
423
- })}
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
+ ))}
424
423
 
425
424
  <Text style={{fontSize: 20}}>Subscriptions</Text>
426
- {subscriptions.map((item) => {
427
- if (item.platform === 'android') {
428
- return item.subscriptionOfferDetails?.map((offer) => (
429
- <View key={offer.offerId} style={{gap: 12}}>
430
- <Text>
431
- {item.title} -{' '}
432
- {offer.pricingPhases.pricingPhaseList
433
- .map((ppl) => ppl.billingPeriod)
434
- .join(',')}
435
- </Text>
436
- <Button
437
- title="Subscribe"
438
- onPress={() =>
439
- requestSubscription({
440
- skus: [item.id],
441
- ...(offer.offerToken && {
442
- subscriptionOffers: [
443
- {sku: item.id, offerToken: offer.offerToken},
444
- ],
445
- }),
446
- } as RequestSubscriptionAndroidProps)
447
- }
448
- />
449
- </View>
450
- ));
451
- }
452
- if (item.platform === 'ios') {
453
- return (
454
- <View key={item.id} style={{gap: 12}}>
455
- <Text>
456
- {item.displayName} - {item.displayPrice}
457
- </Text>
458
- <Button
459
- title="Subscribe"
460
- onPress={() => requestSubscription({sku: item.id})}
461
- />
462
- </View>
463
- );
464
- }
465
- })}
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
+ ))}
466
453
  </View>
467
454
  )}
468
455
  </View>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-iap",
3
- "version": "2.2.7-rc.1",
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 = {