expo-iap 2.2.7 → 2.2.8-rc.2

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
@@ -1,84 +1,117 @@
1
1
  # Expo IAP Documentation
2
2
 
3
- > **Key Feature**: `expo-iap` works seamlessly with Expo's managed workflow—no bare setup or native code required! This is a major improvement over `react-native-iap`, making it ideal for small teams using Expo SDK.
3
+ > **Key Feature**: `expo-iap` works seamlessly with Expo's managed workflow—no native code required! This is a major improvement over `react-native-iap`, making it ideal for small teams using Expo SDK.
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 native setup, `expo-iap` integrates seamlessly into Expo's [managed workflow](https://docs.expo.dev/archive/managed-vs-bare)—no ejecting needed! However, youll need a [development client](https://docs.expo.dev/development/introduction/) instead of Expo Go for full functionality. Starting from version 2.2.8, 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 workflows and React Native CLI 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 (Managed Workflow Only)
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
+ For managed workflows, 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 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 Native Code Required—Use a Development Client!**
36
+ > Unlike `react-native-iap`, `expo-iap` works in Expo’s managed workflow without native modifications. However, you’ll need a [development client](https://docs.expo.dev/development/introduction/) instead of Expo Go.
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`)
51
+ - Listening for updates/errors (`purchaseUpdatedListener`, `purchaseErrorListener`)
52
+
53
+ ## React Native CLI Projects
54
+
55
+ For React Native CLI environments, you’ll need to manually configure native settings that the Expo config plugin handles automatically in managed workflows. Follow these steps:
56
+
57
+ ### Prerequisites
58
+
59
+ Ensure the Expo package is installed:
42
60
 
43
61
  ```bash
44
- npm install expo-iap
62
+ npx install-expo-modules@latest
45
63
  ```
46
64
 
47
- ### Configure for iOS
65
+ Then configure it as described in [Installing Expo Modules](https://docs.expo.dev/bare/installing-expo-modules/).
48
66
 
49
- Run `npx pod-install`. Set `deploymentTarget` to `15.0` or higher (StoreKit 2 requirement):
67
+ ### iOS Configuration
50
68
 
51
- ```json
52
- "ios": {
53
- "deploymentTarget": "15.0"
54
- }
69
+ Run `npx pod-install` to install native dependencies. Update your `ios/Podfile` to set the deployment target to `15.0` or higher (required for StoreKit 2):
70
+
71
+ ```ruby
72
+ platform :ios, '15.0'
55
73
  ```
56
74
 
57
- ### Configure for Android
75
+ ### Android Configuration
58
76
 
59
- No additional configuration is needed—Google Play Billing is handled internally.
77
+ Manually apply the following changes:
60
78
 
61
- ### Configure with Expo Config Plugin
79
+ 1. **Update `android/build.gradle`**
80
+ Add the `supportLibVersion` to the `ext` block:
62
81
 
63
- Add 'expo-iap' to the plugins array in your app.json or app.config.js file:
82
+ ```gradle
83
+ buildscript {
84
+ ext {
85
+ supportLibVersion = "28.0.0" // Add this line
86
+ // Other existing ext properties...
87
+ }
88
+ // ...
89
+ }
90
+ ```
64
91
 
65
- ```json
66
- {
67
- "expo": {
68
- "plugins": ["expo-iap"]
69
- }
70
- }
71
- ```
92
+ If there’s no `ext` block, append it at the end.
93
+
94
+ 2. **Update `android/app/src/main/AndroidManifest.xml`**
95
+ Add the BILLING permission:
96
+
97
+ ```xml
98
+ <manifest ...>
99
+ <uses-permission android:name="com.android.vending.BILLING" />
100
+ <!-- Other manifest content... -->
101
+ </manifest>
102
+ ```
72
103
 
73
- This plugin automatically sets up the necessary Android configuration, including adding the BILLING permission to your AndroidManifest.xml.
104
+ ## Current State & Feedback
105
+
106
+ 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
107
 
75
108
  ## IAP Types
76
109
 
77
- `expo-iap` supports the following In-App Purchase types, aligned with platform-specific APIs (Google Play Billing for Android, StoreKit2 for iOS).
110
+ `expo-iap` supports the following In-App Purchase types, aligned with platform-specific APIs (Google Play Billing for Android, StoreKit 2 for iOS).
78
111
 
79
112
  ### Consumable
80
113
 
81
- - **Description**: Items that are consumed after purchase and can be repurchased (e.g., in-game currency, boosts).
114
+ - **Description**: Items consumed after purchase and repurchasable (e.g., in-game currency, boosts).
82
115
  - **Behavior**: Requires acknowledgment to enable repurchasing.
83
116
  - **Platforms**: Supported on Android and iOS.
84
117
 
@@ -123,7 +156,6 @@ This section outlines the properties of products supported by `expo-iap`.
123
156
  ### iOS-Only Product Types
124
157
 
125
158
  - **`ProductIos`**
126
- - `displayPrice: string`: Formatted price.
127
159
  - `isFamilyShareable: boolean`: Family sharing support.
128
160
  - `jsonRepresentation: string`: StoreKit 2 JSON data.
129
161
  - `subscription: SubscriptionInfo`: Subscription details.
@@ -147,57 +179,38 @@ This section describes purchase properties in `expo-iap`.
147
179
  ### Android-Only Purchase Types
148
180
 
149
181
  - **`ProductPurchase`**:
150
-
151
- - Adds the following properties specific to in-app product purchases:
152
- - **`ids`**: `string[]` - A list of product IDs associated with the purchase (for multi-item purchases).
153
- - **`dataAndroid`**: `string` - The raw purchase data from Google Play (e.g., JSON payload).
154
- - **`signatureAndroid`**: `string` - The cryptographic signature from Google Play to verify the purchase's authenticity.
155
- - **`purchaseStateAndroid`**: `number` - The state of the purchase (e.g., 0 = purchased, 1 = canceled, 2 = pending).
156
-
182
+ - `ids?: string[]`: List of product IDs (multi-item purchases).
183
+ - `dataAndroid?: string`: Raw purchase data from Google Play.
184
+ - `signatureAndroid?: string`: Cryptographic signature.
185
+ - `purchaseStateAndroid?: number`: Purchase state (e.g., 0 = purchased).
157
186
  - **`SubscriptionPurchase`**:
158
-
159
- - Extends the base properties and includes:
160
- - **`autoRenewingAndroid`**: `boolean` - Indicates whether the subscription automatically renews (true) or not (false).
161
-
162
- - **`purchaseTokenAndroid`**:
163
- - **Description**: A unique identifier provided by Google Play for each purchase, used to track, verify, and manage the transaction. For example, it is required to "consume" a consumable product (e.g., in-game coins) or query purchase details via the Google Play Developer API.
187
+ - `autoRenewingAndroid?: boolean`: Indicates auto-renewal status.
188
+ - **`purchaseTokenAndroid?`**: Unique identifier for tracking/verifying purchases.
164
189
 
165
190
  ### iOS-Only Purchase Types
166
191
 
167
192
  - **`ProductPurchase`**:
168
-
169
- - Extends the base purchase properties with iOS-specific fields:
170
- - **`quantityIos`**: `number` - The quantity of the product purchased (e.g., how many units of an item were bought).
171
- - **`expirationDateIos`**: `number?` - The expiration date of the purchase as a Unix timestamp (in milliseconds), if applicable (optional, may be null for non-expiring products).
172
- - **`subscriptionGroupIdIos`**: `string?` - The identifier of the subscription group this product belongs to, used for managing related subscriptions in the App Store (optional, may be null for non-subscription products).
173
-
193
+ - `quantityIos?: number`: Quantity purchased.
194
+ - `expirationDateIos?: number`: Expiration timestamp (optional).
195
+ - `subscriptionGroupIdIos?: string`: Subscription group ID (optional).
174
196
  - **`SubscriptionPurchase`**:
175
- - Extends the base purchase properties with iOS-specific subscription handling:
176
- - Includes all fields from `ProductPurchase` where applicable, plus additional subscription-specific logic.
177
- - May include fields like:
178
- - **`expirationDateIos`**: `number?` - The date and time when the subscription expires, represented as a Unix timestamp (in milliseconds), unless auto-renewed.
179
- - **`autoRenewingIos`**: `boolean` - Indicates whether the subscription is set to automatically renew (true) or not (false).
180
- - Handles subscription-specific features such as renewals, grace periods, and App Store receipt validation.
197
+ - Extends `ProductPurchase` with subscription-specific fields like `expirationDateIos`.
181
198
 
182
199
  ## Implementation Notes
183
200
 
184
201
  ### Platform-Uniform Purchase Handling
185
202
 
186
- Transactions are mapped directly to `Purchase` or `SubscriptionPurchase` with platform-specific fields (e.g., `expirationDateIos`, `purchaseStateAndroid`).
187
-
188
- ### Status
189
-
190
- This module is under development—expect occasional bugs (e.g., Android acknowledgment issues). Test thoroughly and consider contributing fixes!
203
+ Transactions map to `Purchase` or `SubscriptionPurchase` with platform-specific fields (e.g., `expirationDateIos`, `purchaseStateAndroid`).
191
204
 
192
205
  > **Sample Code**: See [example/App.tsx](https://github.com/hyochan/expo-iap/blob/main/example/App.tsx).
193
206
 
194
207
  ## Implementation
195
208
 
196
- Below is a simple example of fetching products and making a purchase with `expo-iap` in a managed workflow:
209
+ Below is a simple example of fetching products and making a purchase with `expo-iap` in a managed workflow, updated to use the new `requestPurchase` signature:
197
210
 
198
211
  ```tsx
199
- import {useEffect, useState} from 'react';
200
- import {Button, Text, View} from 'react-native';
212
+ import { useEffect, useState } from 'react';
213
+ import { Button, Text, View } from 'react-native';
201
214
  import {
202
215
  initConnection,
203
216
  endConnection,
@@ -216,16 +229,15 @@ export default function SimpleIAP() {
216
229
  const setupIAP = async () => {
217
230
  if (await initConnection()) {
218
231
  setIsConnected(true);
219
- const products = await getProducts(['my.consumable.item']); // Replace with your product SKU
232
+ const products = await getProducts(['my.consumable.item']); // Replace with your SKU
220
233
  if (products.length > 0) setProduct(products[0]);
221
234
  }
222
235
  };
223
236
  setupIAP();
224
237
 
225
- // Handle purchase updates
226
238
  const purchaseListener = purchaseUpdatedListener(async (purchase) => {
227
239
  if (purchase) {
228
- await finishTransaction({purchase, isConsumable: true});
240
+ await finishTransaction({ purchase, isConsumable: true });
229
241
  alert('Purchase completed!');
230
242
  }
231
243
  });
@@ -239,11 +251,13 @@ export default function SimpleIAP() {
239
251
  // Trigger a purchase
240
252
  const buyItem = async () => {
241
253
  if (!product) return;
242
- await requestPurchase({skus: [product.id]});
254
+ await requestPurchase({
255
+ request: { skus: [product.id] }, // Android expects 'skus'; iOS would use 'sku'
256
+ });
243
257
  };
244
258
 
245
259
  return (
246
- <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
260
+ <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
247
261
  <Text>{isConnected ? 'Connected' : 'Connecting...'}</Text>
248
262
  {product ? (
249
263
  <>
@@ -260,10 +274,10 @@ export default function SimpleIAP() {
260
274
 
261
275
  ## Using useIAP Hook
262
276
 
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.
277
+ The `useIAP` hook simplifies managing in-app purchases. Below is an example updated to use the new `requestPurchase` signature:
264
278
 
265
279
  ```tsx
266
- import {useEffect, useState} from 'react';
280
+ import { useEffect, useState } from 'react';
267
281
  import {
268
282
  SafeAreaView,
269
283
  ScrollView,
@@ -275,34 +289,15 @@ import {
275
289
  InteractionManager,
276
290
  Alert,
277
291
  } 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
292
+ import { useIAP } from 'expo-iap';
293
+ import type { ProductPurchase, SubscriptionProduct } from 'expo-iap';
285
294
 
286
295
  // Define SKUs
287
- const productSkus = [
288
- 'cpk.points.1000',
289
- 'cpk.points.5000',
290
- 'cpk.points.10000',
291
- 'cpk.points.30000',
292
- ];
293
-
294
- const subscriptionSkus = [
295
- 'cpk.membership.monthly.bronze',
296
- 'cpk.membership.monthly.silver',
297
- ];
296
+ const productSkus = ['cpk.points.1000', 'cpk.points.5000', 'cpk.points.10000', 'cpk.points.30000'];
297
+ const subscriptionSkus = ['cpk.membership.monthly.bronze', 'cpk.membership.monthly.silver'];
298
298
 
299
299
  // Define operations
300
- const operations = [
301
- 'initConnection',
302
- 'getProducts',
303
- 'getSubscriptions',
304
- 'endConnection',
305
- ];
300
+ const operations = ['getProducts', 'getSubscriptions'] as const;
306
301
  type Operation = (typeof operations)[number];
307
302
 
308
303
  export default function IAPWithHook() {
@@ -316,38 +311,24 @@ export default function IAPWithHook() {
316
311
  getSubscriptions,
317
312
  finishTransaction,
318
313
  requestPurchase,
319
- requestSubscription,
320
314
  } = useIAP();
321
315
 
322
- const [isConnected, setIsConnected] = useState(connected);
316
+ const [isReady, setIsReady] = useState(false);
323
317
 
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
- };
318
+ // Fetch products and subscriptions only when connected
319
+ useEffect(() => {
320
+ if (!connected) return;
321
+
322
+ const initializeIAP = async () => {
323
+ try {
324
+ await Promise.all([getProducts(productSkus), getSubscriptions(subscriptionSkus)]);
325
+ setIsReady(true);
326
+ } catch (error) {
327
+ console.error('Error initializing IAP:', error);
328
+ }
329
+ };
330
+ initializeIAP();
331
+ }, [connected, getProducts, getSubscriptions]);
351
332
 
352
333
  // Handle purchase updates and errors
353
334
  useEffect(() => {
@@ -356,32 +337,59 @@ export default function IAPWithHook() {
356
337
  try {
357
338
  await finishTransaction({
358
339
  purchase: currentPurchase,
359
- isConsumable: true,
340
+ isConsumable: currentPurchase.productType === 'inapp',
360
341
  });
361
- Alert.alert('Purchase updated', JSON.stringify(currentPurchase));
342
+ Alert.alert('Purchase Successful', JSON.stringify(currentPurchase));
362
343
  } catch (error) {
363
344
  console.error('Error finishing transaction:', error);
345
+ Alert.alert('Transaction Error', String(error));
364
346
  }
365
347
  });
366
348
  }
367
349
 
368
350
  if (currentPurchaseError) {
369
351
  InteractionManager.runAfterInteractions(() => {
370
- Alert.alert('Purchase error', JSON.stringify(currentPurchaseError));
352
+ Alert.alert('Purchase Error', JSON.stringify(currentPurchaseError));
371
353
  });
372
354
  }
373
355
  }, [currentPurchase, currentPurchaseError, finishTransaction]);
374
356
 
357
+ // Handle operation buttons
358
+ const handleOperation = async (operation: Operation) => {
359
+ if (!connected) {
360
+ Alert.alert('Not Connected', 'Please wait for IAP to connect.');
361
+ return;
362
+ }
363
+
364
+ try {
365
+ switch (operation) {
366
+ case 'getProducts':
367
+ await getProducts(productSkus);
368
+ break;
369
+ case 'getSubscriptions':
370
+ await getSubscriptions(subscriptionSkus);
371
+ break;
372
+ }
373
+ } catch (error) {
374
+ console.error(`Error in ${operation}:`, error);
375
+ }
376
+ };
377
+
378
+ if (!connected) {
379
+ return (
380
+ <SafeAreaView style={styles.container}>
381
+ <Text style={styles.title}>Connecting to IAP...</Text>
382
+ </SafeAreaView>
383
+ );
384
+ }
385
+
375
386
  return (
376
387
  <SafeAreaView style={styles.container}>
377
388
  <Text style={styles.title}>Expo IAP with useIAP Hook</Text>
378
389
  <View style={styles.buttons}>
379
390
  <ScrollView contentContainerStyle={styles.buttonsWrapper} horizontal>
380
391
  {operations.map((operation) => (
381
- <Pressable
382
- key={operation}
383
- onPress={() => handleOperation(operation)}
384
- >
392
+ <Pressable key={operation} onPress={() => handleOperation(operation)}>
385
393
  <View style={styles.buttonView}>
386
394
  <Text>{operation}</Text>
387
395
  </View>
@@ -390,82 +398,60 @@ export default function IAPWithHook() {
390
398
  </ScrollView>
391
399
  </View>
392
400
  <View style={styles.content}>
393
- {!isConnected ? (
394
- <Text>Not connected</Text>
401
+ {!isReady ? (
402
+ <Text>Loading...</Text>
395
403
  ) : (
396
- <View style={{gap: 12}}>
397
- <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
- })}
427
-
428
- <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
- })}
404
+ <View style={{ gap: 12 }}>
405
+ <Text style={{ fontSize: 20 }}>Products</Text>
406
+ {products.map((item) => (
407
+ <View key={item.id} style={{ gap: 12 }}>
408
+ <Text>
409
+ {item.title} -{' '}
410
+ {item.platform === 'android'
411
+ ? item.oneTimePurchaseOfferDetails?.formattedPrice
412
+ : item.displayPrice}
413
+ </Text>
414
+ <Button
415
+ title="Buy"
416
+ onPress={() =>
417
+ requestPurchase({
418
+ request: item.platform === 'android' ? { skus: [item.id] } : { sku: item.id },
419
+ })
420
+ }
421
+ />
422
+ </View>
423
+ ))}
424
+
425
+ <Text style={{ fontSize: 20 }}>Subscriptions</Text>
426
+ {subscriptions.map((item) => (
427
+ <View key={item.id} style={{ gap: 12 }}>
428
+ <Text>
429
+ {item.title || item.displayName} -{' '}
430
+ {item.platform === 'android' && item.subscriptionOfferDetails
431
+ ? item.subscriptionOfferDetails[0]?.pricingPhases.pricingPhaseList[0].formattedPrice
432
+ : item.displayPrice}
433
+ </Text>
434
+ <Button
435
+ title="Subscribe"
436
+ onPress={() =>
437
+ requestPurchase({
438
+ request:
439
+ item.platform === 'android'
440
+ ? {
441
+ skus: [item.id],
442
+ subscriptionOffers:
443
+ item.subscriptionOfferDetails?.map((offer) => ({
444
+ sku: item.id,
445
+ offerToken: offer.offerToken,
446
+ })) || [],
447
+ }
448
+ : { sku: item.id },
449
+ type: 'subs',
450
+ })
451
+ }
452
+ />
453
+ </View>
454
+ ))}
469
455
  </View>
470
456
  )}
471
457
  </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.2",
4
4
  "description": "In App Purchase module in Expo",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",