expo-iap 3.0.6 → 3.0.7
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.
- package/CHANGELOG.md +1 -310
- package/android/build.gradle +1 -1
- package/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +35 -0
- package/build/index.d.ts +10 -3
- package/build/index.d.ts.map +1 -1
- package/build/index.js +20 -14
- package/build/index.js.map +1 -1
- package/package.json +1 -1
- package/plugin/build/withIAP.js +3 -3
- package/plugin/src/withIAP.ts +3 -3
- package/src/index.ts +88 -29
package/CHANGELOG.md
CHANGED
|
@@ -1,312 +1,3 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
- Hook: Remove the transient `currentPurchase` / `currentPurchaseError` state from `useIAP` so consumers rely exclusively on the `onPurchaseSuccess` and `onPurchaseError` callbacks for post-purchase handling. This is a light breaking tweak for hook callers, but keeps the root API untouched.
|
|
6
|
-
|
|
7
|
-
## 3.0.5 - 2025-09-17
|
|
8
|
-
|
|
9
|
-
- Types: Normalize OpenIAP literal unions to `'in-app'`, `'ios'`, and `'android'`, update `useIAP` helpers to the new `PurchaseRequestInput`, and refresh docs/examples/tests to the lowercase schema tokens.
|
|
10
|
-
- Tooling: Regenerate `src/types.ts` with `openiap-gql` 1.0.2, add lint/format ignores for the generated file, and document the type update workflow.
|
|
11
|
-
- Native: Upgrade the Android fallback/config plugin to [openiap-google 1.1.10](https://github.com/hyodotdev/openiap-google/releases/tag/1.1.10), bump the iOS pod to [openiap 1.1.12](https://github.com/hyodotdev/openiap-apple/releases/tag/1.1.12), adopt PascalCase error codes, and wire the new request parameter models through the bridges and config plugin.
|
|
12
|
-
|
|
13
|
-
## 3.0.4 - 2025-09-16
|
|
14
|
-
|
|
15
|
-
- Types: Regenerate the OpenIAP schema with the canonical PascalCase names (`ProductIOS`, `PurchaseIOS`, etc.) and align docs/tests/examples with the new exports.
|
|
16
|
-
- Errors: Promote `PurchaseError` to extend `Error`, tighten typings for platform error input/output, and ensure Android acknowledgement resolves a `VoidResult {success}` object.
|
|
17
|
-
- Docs: Refresh iOS setup examples to use platform-specific request shapes, fix legacy `ErrorCode` references in versioned guides, and trim example helpers to the updated API surface.
|
|
18
|
-
- Build: Adopt [openiap-gql 1.0.0](https://github.com/hyodotdev/openiap-gql/releases/tag/1.0.0) for the transport layer to stay aligned with the GraphQL contract shipped across the ecosystem.
|
|
19
|
-
|
|
20
|
-
## 3.0.3 - 2025-09-14
|
|
21
|
-
|
|
22
|
-
- Types: Align Expo IAP surface with [react-native-iap #3006](https://github.com/hyochan/react-native-iap/pull/3006) by renaming subscription aliases, adding StoreKit product enums, and exposing optional purchase metadata (quantity, purchaseState, isAutoRenewing).
|
|
23
|
-
- Errors: Switch JS helpers and docs to camelCase `ErrorCode` members and tighten error inspection utilities to avoid `any` usage.
|
|
24
|
-
|
|
25
|
-
## 3.0.2 - 2025-09-13
|
|
26
|
-
|
|
27
|
-
- iOS: Fix build error “cannot convert value of type '[[String : Any?]]'” in Expo bridge by returning non‑optional dictionaries and removing double‑serialization in `showManageSubscriptionsIOS` (Fixes #202).
|
|
28
|
-
- Examples: Make `requestPurchase` fire‑and‑forget with guards for test/mock envs to avoid spurious errors.
|
|
29
|
-
- Hook: Dedupe purchase success events across re‑mounts to avoid duplicate logs in dev.
|
|
30
|
-
|
|
31
|
-
## 3.0.1 - 2025-09-13
|
|
32
|
-
|
|
33
|
-
- Android: Ensure `openiap-google:1.1.0` is added inside `dependencies {}` and replace/dedupe existing entries. In local dev, remove the Maven line and rely on `project(':openiap-google')`. Library fallback bumped to 1.1.0.
|
|
34
|
-
- iOS: Honor `enableLocalDev: false` even when `localPath` is set. Ensure CocoaPods CDN in Podfile and remove any stale local `pod 'openiap', :path => ...` lines.
|
|
35
|
-
- Misc: Drop legacy Billing/GMS cleanup patterns, simplify prebuild logs, and add a short release blog post.
|
|
36
|
-
|
|
37
|
-
## 3.0.0 - 2025-09-13
|
|
38
|
-
|
|
39
|
-
Breaking changes:
|
|
40
|
-
|
|
41
|
-
- Remove legacy APIs: `getProducts`, `getSubscriptions`, `requestProducts`, `requestSubscription`, `getPurchaseHistory`/`getPurchaseHistories`, and non‑suffixed iOS aliases
|
|
42
|
-
- `showManageSubscriptionsIOS()` now returns `Promise<Purchase[]>` (was `boolean`)
|
|
43
|
-
- `getAvailablePurchases()` options now only accept iOS keys: `alsoPublishToEventListenerIOS`, `onlyIncludeActiveItemsIOS`
|
|
44
|
-
|
|
45
|
-
Features:
|
|
46
|
-
|
|
47
|
-
- Unify tokens via `purchaseToken` on `Purchase` (iOS JWS + Android token)
|
|
48
|
-
- Add `getReceiptDataIOS` alias for iOS receipt parity
|
|
49
|
-
|
|
50
|
-
Refactor:
|
|
51
|
-
|
|
52
|
-
- Remove deprecated comments/tests/mocks; simplify subscription dedup to `id`
|
|
53
|
-
- Clean up hook surface and examples to current API
|
|
54
|
-
|
|
55
|
-
Fixes:
|
|
56
|
-
|
|
57
|
-
- iOS parity and guards across native bridge and TS layer
|
|
58
|
-
|
|
59
|
-
Docs:
|
|
60
|
-
|
|
61
|
-
- Add v3 migration and update API pages to remove legacy helpers
|
|
62
|
-
|
|
63
|
-
Tests:
|
|
64
|
-
|
|
65
|
-
- Remove legacy specs; all root and example suites pass
|
|
66
|
-
|
|
67
|
-
## [2.9.7] - 2025-09-12
|
|
68
|
-
|
|
69
|
-
### Changed
|
|
70
|
-
|
|
71
|
-
- Android: remove `ensureConnection` wrapper in favor of `BillingClient` auto-reconnect and a simpler `getBillingClientOrReject` precheck
|
|
72
|
-
- Android: also verify `BillingClient.isReady` before proceeding to avoid sporadic failures
|
|
73
|
-
- Android: drop deprecated product fields from mapping (`displayName`, `name`, `oneTimePurchaseOfferDetails`, `subscriptionOfferDetails`) in favor of `...Android` suffixed fields
|
|
74
|
-
- iOS: add `ensureConnection()` guard to all public async APIs; fix main-actor state updates and minor warnings
|
|
75
|
-
|
|
76
|
-
### Fixed
|
|
77
|
-
|
|
78
|
-
- Android: fix stray brace that prematurely closed `ModuleDefinition` and ktlint trailing spaces
|
|
79
|
-
|
|
80
|
-
## [2.9.6] - 2025-09-11
|
|
81
|
-
|
|
82
|
-
### Fixed
|
|
83
|
-
|
|
84
|
-
- Metro bundling error when importing the hook: fix "Unable to resolve '../../..' from node_modules/expo-iap/build/useIAP.js" by changing an ambiguous `import '.'` to an explicit `import './index'` inside `useIAP`. This prevents Metro from walking up to the app root and trying to resolve `expo-router/entry`.
|
|
85
|
-
|
|
86
|
-
### Notes
|
|
87
|
-
|
|
88
|
-
- No runtime behavior changes; this is a bundling path fix only.
|
|
89
|
-
- If you cannot upgrade immediately, temporary workaround: patch `node_modules/expo-iap/build/useIAP.js` to replace `from '.'` with `from './index'`, then clear cache (`npx expo start -c`).
|
|
90
|
-
|
|
91
|
-
## [2.9.5] - 2025-09-10
|
|
92
|
-
|
|
93
|
-
### Changed
|
|
94
|
-
|
|
95
|
-
- iOS: Podspec pins `openiap` to `1.1.9` to prevent CocoaPods from resolving unintended versions and to stabilize builds.
|
|
96
|
-
|
|
97
|
-
### Deprecated
|
|
98
|
-
|
|
99
|
-
- Deprecated `2.9.0`–`2.9.4` due to unpinned iOS dependency behavior in the wild. Please upgrade to `2.9.5`.
|
|
100
|
-
|
|
101
|
-
### Notes
|
|
102
|
-
|
|
103
|
-
- If you encounter CocoaPods CDN issues, try `pod install --repo-update`. The config plugin already hardens Podfile sources, but network flakiness can still require a repo update.
|
|
104
|
-
|
|
105
|
-
## [2.9.3] - 2025-09-10
|
|
106
|
-
|
|
107
|
-
### Fixed
|
|
108
|
-
|
|
109
|
-
- iOS: replace thrown `OpenIapError.*` and nonexistent types with `OpenIapFailure` cases to resolve build errors and align with error mapping
|
|
110
|
-
- iOS: remove explicit `OpenIapErrorEvent` annotation in Swift listener (type is inferred from OpenIAP API)
|
|
111
|
-
|
|
112
|
-
### Notes
|
|
113
|
-
|
|
114
|
-
- If your CocoaPods CDN is flaky, `pod install --repo-update` or temporarily pin `openiap` in your app Podfile.
|
|
115
|
-
|
|
116
|
-
## [2.9.1] - 2025-09-09
|
|
117
|
-
|
|
118
|
-
### Added
|
|
119
|
-
|
|
120
|
-
- Expanded error codes and mappings (Android parity) with friendly messages in utilities
|
|
121
|
-
- New object-style `PurchaseError` constructor and guideline in docs (legacy positional args remain supported)
|
|
122
|
-
- Docs site can build for GitHub Pages via dynamic `baseUrl` switch
|
|
123
|
-
- Example iOS: pin `openiap` to `1.1.7` via Git to avoid CocoaPods index lag
|
|
124
|
-
|
|
125
|
-
### Changed
|
|
126
|
-
|
|
127
|
-
- iOS: `getPromotedProductIOS` now returns a fully serialized Product (fetch by SKU then serialize)
|
|
128
|
-
- iOS: `subscriptionStatusIOS` returns `{ state, renewalInfo? }` where `renewalInfo.willAutoRenew` is a real boolean and `autoRenewPreference` is preserved
|
|
129
|
-
- Hook: register purchase error listener early and ignore `E_INIT_CONNECTION` until connected
|
|
130
|
-
|
|
131
|
-
## [2.9.0] - 2025-09-05
|
|
132
|
-
|
|
133
|
-
### Added
|
|
134
|
-
|
|
135
|
-
- iOS: Integrated OpenIAP Apple v1.1.6
|
|
136
|
-
- Updated types to match OpenIAP v1.1.0 specification
|
|
137
|
-
- Enhanced error handling with `PurchaseError` type and native error code mapping
|
|
138
|
-
- New type system: `ProductRequest`, `RequestPurchaseProps`, `ReceiptValidationProps`
|
|
139
|
-
- Improved receipt validation with `ReceiptValidationResult`
|
|
140
|
-
- Root-level type re-exports to avoid deep imports (e.g., `ProductAndroid`, `ProductIOS`, `PaymentDiscount`)
|
|
141
|
-
|
|
142
|
-
### Changed
|
|
143
|
-
|
|
144
|
-
- Updated serializers for purchases/products to follow OpenIAP structure
|
|
145
|
-
- Updated listener setup to use new OpenIAP methods (`purchaseUpdatedListener`, `purchaseErrorListener`)
|
|
146
|
-
- Added unified `removeAllListeners()` for cleanup
|
|
147
|
-
- `showManageSubscriptionsIOS()` now returns updated subscriptions array (not boolean)
|
|
148
|
-
|
|
149
|
-
### Fixed
|
|
150
|
-
|
|
151
|
-
- Fixed duplicate purchase success alerts
|
|
152
|
-
- Fixed restore purchase alerts on screen entry
|
|
153
|
-
- Improved purchase validation logic
|
|
154
|
-
|
|
155
|
-
### Note
|
|
156
|
-
|
|
157
|
-
- Android native module integration with OpenIAP Android is planned for v3.0.0
|
|
158
|
-
|
|
159
|
-
## [2.8.8] - 2025-09-05
|
|
160
|
-
|
|
161
|
-
### Added
|
|
162
|
-
|
|
163
|
-
- Enhanced `ActiveSubscription` interface with backend validation fields:
|
|
164
|
-
- `transactionId` - Transaction identifier for backend validation
|
|
165
|
-
- `purchaseToken` - JWT token (iOS) or purchase token (Android) for backend validation
|
|
166
|
-
- `transactionDate` - Transaction timestamp
|
|
167
|
-
- Return subscription changes from `showManageSubscriptionsIOS()` as Promise data
|
|
168
|
-
|
|
169
|
-
### Fixed
|
|
170
|
-
|
|
171
|
-
- Fixed iOS `getAvailablePurchases({ onlyIncludeActiveItemsIOS: true })` returning expired subscriptions
|
|
172
|
-
- Now correctly uses `Transaction.currentEntitlements` for better performance and accuracy
|
|
173
|
-
- Fixed subscription status matching to specific SKU in `showManageSubscriptionsIOS()`
|
|
174
|
-
- Prevents picking wrong status when multiple statuses exist in a subscription group
|
|
175
|
-
|
|
176
|
-
### Changed
|
|
177
|
-
|
|
178
|
-
- Removed unnecessary event sending from `getAvailableItems()` - events are only sent from `requestPurchase()`
|
|
179
|
-
- Removed polling logic from subscription status monitoring for cleaner code
|
|
180
|
-
- Updated to comply with OpenIAP v1.1.1 specification
|
|
181
|
-
|
|
182
|
-
## [2.8.7] - 2025-09-03
|
|
183
|
-
|
|
184
|
-
### Added
|
|
185
|
-
|
|
186
|
-
- `fetchProducts` function following OpenIAP terminology (replaces `requestProducts`)
|
|
187
|
-
|
|
188
|
-
### Deprecated
|
|
189
|
-
|
|
190
|
-
- `requestProducts` - Use `fetchProducts` instead (will be removed in v3.0.0)
|
|
191
|
-
|
|
192
|
-
### Changed
|
|
193
|
-
|
|
194
|
-
- Internal useIAP hook now uses `fetchProducts`
|
|
195
|
-
- Updated documentation and deprecation messages
|
|
196
|
-
|
|
197
|
-
## [2.8.6]
|
|
198
|
-
|
|
199
|
-
### Changed
|
|
200
|
-
|
|
201
|
-
- **BREAKING NAMING CONVENTION**: Added platform-specific suffixes to native functions for clarity
|
|
202
|
-
- iOS functions now use `IOS` suffix (e.g., `getPromotedProductIOS`, `clearTransactionIOS`)
|
|
203
|
-
- Android functions now use `Android` suffix (e.g., `acknowledgePurchaseAndroid`, `consumeProductAndroid`)
|
|
204
|
-
- Common cross-platform functions remain without suffix (`requestProducts`, `requestPurchase`)
|
|
205
|
-
- Renamed `buyPromotedProductIOS` to `requestPurchaseOnPromotedProductIOS` for consistency
|
|
206
|
-
|
|
207
|
-
### Added
|
|
208
|
-
|
|
209
|
-
- Added `getPendingTransactionsIOS` function for iOS
|
|
210
|
-
- Added `clearTransactionIOS` function for iOS
|
|
211
|
-
|
|
212
|
-
### Deprecated
|
|
213
|
-
|
|
214
|
-
- `getPurchaseHistories` - Use `getAvailablePurchases` instead (will be removed in v2.9.0)
|
|
215
|
-
- `buyPromotedProductIOS` - Use `requestPurchaseOnPromotedProductIOS` instead (will be removed in v2.9.0)
|
|
216
|
-
- `disable` function - No longer needed, observer management is automatic (will be removed in v2.9.0)
|
|
217
|
-
|
|
218
|
-
## [2.8.5] - 2025-09-03
|
|
219
|
-
|
|
220
|
-
### Fixed
|
|
221
|
-
|
|
222
|
-
- Fixed Android `finishTransaction` null error by adding fallback to `purchaseTokenAndroid` (#180)
|
|
223
|
-
|
|
224
|
-
## [2.8.4] - 2025-08-31
|
|
225
|
-
|
|
226
|
-
### Fixed
|
|
227
|
-
|
|
228
|
-
- Fixed iOS 18.4 properties build failure on Xcode 16.3 and below by adding Swift 6.1 compiler guard
|
|
229
|
-
|
|
230
|
-
### Changed
|
|
231
|
-
|
|
232
|
-
- Android: Enabled automatic service reconnection (Android Billing Client v8 feature) and simplified connection logic (#178)
|
|
233
|
-
|
|
234
|
-
## [2.8.3] - 2025-08-27
|
|
235
|
-
|
|
236
|
-
### Fixed
|
|
237
|
-
|
|
238
|
-
- Fixed TypeScript type issues
|
|
239
|
-
- Added critical warning about iOS platform version in podspec (#169)
|
|
240
|
-
|
|
241
|
-
## [2.8.2] - 2025-08-26
|
|
242
|
-
|
|
243
|
-
### Added
|
|
244
|
-
|
|
245
|
-
- Added `platform` field to all IAP types for improved runtime type discrimination
|
|
246
|
-
- Consolidated `Purchase` types and deprecated legacy type aliases for consistency
|
|
247
|
-
|
|
248
|
-
### Changed
|
|
249
|
-
|
|
250
|
-
- Refactored and consolidated Purchase types to follow OpenIAP specification
|
|
251
|
-
- Improved type consistency across iOS and Android platforms
|
|
252
|
-
|
|
253
|
-
### Deprecated
|
|
254
|
-
|
|
255
|
-
**Note**: The following deprecated type aliases will be removed in v2.9.0:
|
|
256
|
-
|
|
257
|
-
- `ProductPurchase` (use `Purchase` instead)
|
|
258
|
-
- `SubscriptionPurchase` (use `Purchase` instead)
|
|
259
|
-
|
|
260
|
-
## [2.8.1] - 2025-08-19
|
|
261
|
-
|
|
262
|
-
### Added
|
|
263
|
-
|
|
264
|
-
- Added `debugDescription?: string` field to `ProductCommon` for debugging purposes
|
|
265
|
-
- Added `platform?: string` field to `ProductCommon` and `PurchaseCommon` for platform identification
|
|
266
|
-
- Added `platform: "ios"` to iOS-specific types (`ProductIOS`, `ProductSubscriptionIOS`, `PurchaseIOS`)
|
|
267
|
-
- Added `platform: "android"` to Android-specific types (`ProductAndroid`, `ProductSubscriptionAndroid`, `PurchaseAndroid`)
|
|
268
|
-
- Added `ids?: string[]` field to `PurchaseCommon` (moved from Android-specific types)
|
|
269
|
-
|
|
270
|
-
### Changed
|
|
271
|
-
|
|
272
|
-
- Moved common fields from platform-specific types to shared Common types
|
|
273
|
-
- Updated iOS native code to populate missing subscription fields:
|
|
274
|
-
- `introductoryPriceAsAmountIOS`
|
|
275
|
-
- `introductoryPricePaymentModeIOS`
|
|
276
|
-
- `introductoryPriceNumberOfPeriodsIOS`
|
|
277
|
-
- `introductoryPriceSubscriptionPeriodIOS`
|
|
278
|
-
- `subscriptionPeriodNumberIOS`
|
|
279
|
-
- `subscriptionPeriodUnitIOS`
|
|
280
|
-
- Updated Android native code to use common `ids` field instead of platform-specific `idsAndroid`
|
|
281
|
-
|
|
282
|
-
### Fixed
|
|
283
|
-
|
|
284
|
-
- Fixed type mismatches between Product and Purchase types across iOS and Android platforms
|
|
285
|
-
- Resolved missing field mappings in iOS native subscription data extraction
|
|
286
|
-
- Improved type consistency for cross-platform compatibility
|
|
287
|
-
|
|
288
|
-
### Deprecated
|
|
289
|
-
|
|
290
|
-
**Note**: No breaking changes in this release. The following deprecated fields will be removed in v2.9.0:
|
|
291
|
-
|
|
292
|
-
- Android: `idsAndroid` (use common `ids` field instead)
|
|
293
|
-
- Android: `name`, `oneTimePurchaseOfferDetails`, `subscriptionOfferDetails` (use fields with `Android` suffix)
|
|
294
|
-
- iOS: `displayName`, `isFamilyShareable`, `jsonRepresentation`, `subscription` (use fields with `IOS` suffix)
|
|
295
|
-
- iOS: `discounts`, `introductoryPrice` (use fields with `IOS` suffix)
|
|
296
|
-
|
|
297
|
-
## [2.8.0] - 2025-08-18
|
|
298
|
-
|
|
299
|
-
### Breaking Changes
|
|
300
|
-
|
|
301
|
-
- **iOS Field Naming Convention**: All iOS-related field names ending with "Ios" have been renamed to end with "IOS" to follow the convention that acronyms at the end of field names should be uppercase.
|
|
302
|
-
|
|
303
|
-
**Migration Guide**: See the full migration guide at [hyochan.github.io/expo-iap/blog/v2-8-0-migration-guide](https://hyochan.github.io/expo-iap/blog/v2-8-0-migration-guide)
|
|
304
|
-
|
|
305
|
-
Affected fields:
|
|
306
|
-
|
|
307
|
-
- `quantityIos` → `quantityIOS`
|
|
308
|
-
- `expirationDateIos` → `expirationDateIOS`
|
|
309
|
-
- `environmentIos` → `environmentIOS`
|
|
310
|
-
- And all other iOS-suffixed fields
|
|
311
|
-
|
|
312
|
-
For older versions, checkout [Release Notes](https://github.com/hyochan/expo-iap/releases)
|
|
3
|
+
[Check release notes](https://github.com/hyochan/expo-iap/releases)
|
package/android/build.gradle
CHANGED
|
@@ -58,6 +58,6 @@ dependencies {
|
|
|
58
58
|
implementation project(":openiap-google")
|
|
59
59
|
} else {
|
|
60
60
|
// Fallback to published artifact when local project isn't linked
|
|
61
|
-
implementation "io.github.hyochan.openiap:openiap-google:1.1.
|
|
61
|
+
implementation "io.github.hyochan.openiap:openiap-google:1.1.11"
|
|
62
62
|
}
|
|
63
63
|
}
|
|
@@ -7,6 +7,7 @@ import dev.hyo.openiap.OpenIapModule
|
|
|
7
7
|
import dev.hyo.openiap.models.DeepLinkOptions
|
|
8
8
|
import dev.hyo.openiap.models.ProductRequest
|
|
9
9
|
import dev.hyo.openiap.models.RequestPurchaseParams
|
|
10
|
+
import dev.hyo.openiap.models.RequestSubscriptionAndroidProps
|
|
10
11
|
import expo.modules.kotlin.Promise
|
|
11
12
|
import expo.modules.kotlin.exception.Exceptions
|
|
12
13
|
import expo.modules.kotlin.modules.Module
|
|
@@ -196,12 +197,45 @@ class ExpoIapModule : Module() {
|
|
|
196
197
|
val obfuscatedProfileId =
|
|
197
198
|
(params["obfuscatedProfileIdAndroid"] ?: params["obfuscatedProfileId"]) as? String
|
|
198
199
|
val isOfferPersonalized = params["isOfferPersonalized"] as? Boolean ?: false
|
|
200
|
+
val offerTokenArr =
|
|
201
|
+
(params["offerTokenArr"] as? List<*>)?.filterIsInstance<String>() ?: emptyList()
|
|
202
|
+
val subscriptionOffersParam =
|
|
203
|
+
(params["subscriptionOffers"] as? List<*>)?.mapNotNull { rawOffer ->
|
|
204
|
+
val offerMap = rawOffer as? Map<*, *> ?: return@mapNotNull null
|
|
205
|
+
val sku = offerMap["sku"] as? String
|
|
206
|
+
val offerToken = offerMap["offerToken"] as? String
|
|
207
|
+
if (sku.isNullOrEmpty() || offerToken.isNullOrEmpty()) {
|
|
208
|
+
null
|
|
209
|
+
} else {
|
|
210
|
+
RequestSubscriptionAndroidProps.SubscriptionOffer(sku = sku, offerToken = offerToken)
|
|
211
|
+
}
|
|
212
|
+
} ?: emptyList()
|
|
199
213
|
|
|
200
214
|
PromiseUtils.addPromiseForKey(PromiseUtils.PROMISE_BUY_ITEM, promise)
|
|
201
215
|
scope.launch {
|
|
202
216
|
try {
|
|
203
217
|
openIap.setActivity(currentActivity)
|
|
204
218
|
val reqType = ProductRequest.ProductRequestType.fromString(type)
|
|
219
|
+
val subscriptionOffers =
|
|
220
|
+
if (reqType == ProductRequest.ProductRequestType.Subs) {
|
|
221
|
+
when {
|
|
222
|
+
subscriptionOffersParam.isNotEmpty() -> subscriptionOffersParam
|
|
223
|
+
offerTokenArr.isNotEmpty() ->
|
|
224
|
+
skus.zip(offerTokenArr).mapNotNull { (sku, token) ->
|
|
225
|
+
if (token.isNotEmpty()) {
|
|
226
|
+
RequestSubscriptionAndroidProps.SubscriptionOffer(
|
|
227
|
+
sku = sku,
|
|
228
|
+
offerToken = token,
|
|
229
|
+
)
|
|
230
|
+
} else {
|
|
231
|
+
null
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
else -> emptyList()
|
|
235
|
+
}
|
|
236
|
+
} else {
|
|
237
|
+
emptyList()
|
|
238
|
+
}
|
|
205
239
|
val result =
|
|
206
240
|
openIap.requestPurchase(
|
|
207
241
|
RequestPurchaseParams(
|
|
@@ -209,6 +243,7 @@ class ExpoIapModule : Module() {
|
|
|
209
243
|
obfuscatedAccountIdAndroid = obfuscatedAccountId,
|
|
210
244
|
obfuscatedProfileIdAndroid = obfuscatedProfileId,
|
|
211
245
|
isOfferPersonalized = isOfferPersonalized,
|
|
246
|
+
subscriptionOffers = subscriptionOffers,
|
|
212
247
|
),
|
|
213
248
|
reqType,
|
|
214
249
|
)
|
package/build/index.d.ts
CHANGED
|
@@ -12,12 +12,19 @@ export declare enum OpenIapEvent {
|
|
|
12
12
|
PromotedProductIOS = "promoted-product-ios"
|
|
13
13
|
}
|
|
14
14
|
export declare function setValueAsync(value: string): any;
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
type ExpoIapEventPayloads = {
|
|
16
|
+
[OpenIapEvent.PurchaseUpdated]: Purchase;
|
|
17
|
+
[OpenIapEvent.PurchaseError]: PurchaseError;
|
|
18
|
+
[OpenIapEvent.PromotedProductIOS]: Product;
|
|
19
|
+
};
|
|
20
|
+
type ExpoIapEventListener<E extends OpenIapEvent> = (payload: ExpoIapEventPayloads[E]) => void;
|
|
21
|
+
type ExpoIapEmitter = {
|
|
22
|
+
addListener<E extends OpenIapEvent>(eventName: E, listener: ExpoIapEventListener<E>): {
|
|
17
23
|
remove: () => void;
|
|
18
24
|
};
|
|
19
|
-
removeListener
|
|
25
|
+
removeListener<E extends OpenIapEvent>(eventName: E, listener: ExpoIapEventListener<E>): void;
|
|
20
26
|
};
|
|
27
|
+
export declare const emitter: ExpoIapEmitter;
|
|
21
28
|
/**
|
|
22
29
|
* TODO(v3.1.0): Remove legacy 'inapp' alias once downstream apps migrate to 'in-app'.
|
|
23
30
|
*/
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAmBA,OAAO,EACL,OAAO,EACP,QAAQ,EAER,oBAAoB,EACpB,+BAA+B,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/build/index.js
CHANGED
|
@@ -27,7 +27,8 @@ export function setValueAsync(value) {
|
|
|
27
27
|
return ExpoIapModule.setValueAsync(value);
|
|
28
28
|
}
|
|
29
29
|
// Ensure the emitter has proper EventEmitter interface
|
|
30
|
-
export const emitter = (ExpoIapModule ||
|
|
30
|
+
export const emitter = (ExpoIapModule ||
|
|
31
|
+
NativeModulesProxy.ExpoIap);
|
|
31
32
|
const normalizeProductType = (type) => {
|
|
32
33
|
if (type === 'inapp') {
|
|
33
34
|
console.warn("expo-iap: 'inapp' product type is deprecated and will be removed in v3.1.0. Use 'in-app' instead.");
|
|
@@ -204,13 +205,10 @@ const offerToRecordIOS = (offer) => {
|
|
|
204
205
|
timestamp: offer.timestamp.toString(),
|
|
205
206
|
};
|
|
206
207
|
};
|
|
207
|
-
|
|
208
|
-
* Helper to normalize request props to platform-specific format
|
|
209
|
-
*/
|
|
210
|
-
const normalizeRequestProps = (request, platform) => {
|
|
208
|
+
function normalizeRequestProps(request, platform) {
|
|
211
209
|
// Platform-specific format - directly return the appropriate platform data
|
|
212
210
|
return platform === 'ios' ? request.ios : request.android;
|
|
213
|
-
}
|
|
211
|
+
}
|
|
214
212
|
/**
|
|
215
213
|
* Request a purchase for products or subscriptions.
|
|
216
214
|
*
|
|
@@ -253,7 +251,7 @@ export const requestPurchase = (requestObj) => {
|
|
|
253
251
|
}
|
|
254
252
|
const { sku, andDangerouslyFinishTransactionAutomatically = false, appAccountToken, quantity, withOffer, } = normalizedRequest;
|
|
255
253
|
return (async () => {
|
|
256
|
-
const offer = offerToRecordIOS(withOffer);
|
|
254
|
+
const offer = offerToRecordIOS(withOffer ?? undefined);
|
|
257
255
|
const purchase = await ExpoIapModule.requestPurchase({
|
|
258
256
|
sku,
|
|
259
257
|
andDangerouslyFinishTransactionAutomatically,
|
|
@@ -265,11 +263,11 @@ export const requestPurchase = (requestObj) => {
|
|
|
265
263
|
})();
|
|
266
264
|
}
|
|
267
265
|
if (Platform.OS === 'android') {
|
|
268
|
-
const normalizedRequest = normalizeRequestProps(request, 'android');
|
|
269
|
-
if (!normalizedRequest?.skus?.length) {
|
|
270
|
-
throw new Error('Invalid request for Android. The `skus` property is required and must be a non-empty array.');
|
|
271
|
-
}
|
|
272
266
|
if (isInAppPurchase) {
|
|
267
|
+
const normalizedRequest = normalizeRequestProps(request, 'android');
|
|
268
|
+
if (!normalizedRequest?.skus?.length) {
|
|
269
|
+
throw new Error('Invalid request for Android. The `skus` property is required and must be a non-empty array.');
|
|
270
|
+
}
|
|
273
271
|
const { skus, obfuscatedAccountIdAndroid, obfuscatedProfileIdAndroid, isOfferPersonalized, } = normalizedRequest;
|
|
274
272
|
return (async () => {
|
|
275
273
|
return ExpoIapModule.requestPurchase({
|
|
@@ -285,16 +283,24 @@ export const requestPurchase = (requestObj) => {
|
|
|
285
283
|
})();
|
|
286
284
|
}
|
|
287
285
|
if (canonical === 'subs') {
|
|
288
|
-
const
|
|
286
|
+
const normalizedRequest = normalizeRequestProps(request, 'android');
|
|
287
|
+
if (!normalizedRequest?.skus?.length) {
|
|
288
|
+
throw new Error('Invalid request for Android. The `skus` property is required and must be a non-empty array.');
|
|
289
|
+
}
|
|
290
|
+
const { skus, obfuscatedAccountIdAndroid, obfuscatedProfileIdAndroid, isOfferPersonalized, subscriptionOffers, replacementModeAndroid, purchaseTokenAndroid, } = normalizedRequest;
|
|
291
|
+
const normalizedOffers = subscriptionOffers ?? [];
|
|
292
|
+
const replacementMode = replacementModeAndroid ?? -1;
|
|
293
|
+
const purchaseToken = purchaseTokenAndroid ?? undefined;
|
|
289
294
|
return (async () => {
|
|
290
295
|
return ExpoIapModule.requestPurchase({
|
|
291
296
|
type: native,
|
|
292
297
|
skuArr: skus,
|
|
293
298
|
purchaseToken,
|
|
294
|
-
replacementMode
|
|
299
|
+
replacementMode,
|
|
295
300
|
obfuscatedAccountId: obfuscatedAccountIdAndroid,
|
|
296
301
|
obfuscatedProfileId: obfuscatedProfileIdAndroid,
|
|
297
|
-
offerTokenArr:
|
|
302
|
+
offerTokenArr: normalizedOffers.map((so) => so.offerToken),
|
|
303
|
+
subscriptionOffers: normalizedOffers,
|
|
298
304
|
isOfferPersonalized: isOfferPersonalized ?? false,
|
|
299
305
|
});
|
|
300
306
|
})();
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["// External dependencies\nimport {NativeModulesProxy} from 'expo-modules-core';\nimport {Platform} from 'react-native';\n\n// Internal modules\nimport ExpoIapModule from './ExpoIapModule';\nimport {\n isProductIOS,\n validateReceiptIOS,\n deepLinkToSubscriptionsIOS,\n syncIOS,\n} from './modules/ios';\nimport {\n isProductAndroid,\n validateReceiptAndroid,\n deepLinkToSubscriptionsAndroid,\n} from './modules/android';\n\n// Types\nimport {\n Product,\n Purchase,\n ErrorCode,\n RequestPurchaseProps,\n RequestPurchasePropsByPlatforms,\n RequestSubscriptionPropsByPlatforms,\n ProductSubscription,\n PurchaseAndroid,\n DiscountOfferInputIOS,\n VoidResult,\n ReceiptValidationResult,\n} from './types';\nimport {PurchaseError} from './purchase-error';\n\n// Export all types\nexport * from './types';\nexport {ErrorCodeUtils, ErrorCodeMapping} from './purchase-error';\nexport * from './modules/android';\nexport * from './modules/ios';\n\n// Export subscription helpers\nexport {\n getActiveSubscriptions,\n hasActiveSubscriptions,\n} from './helpers/subscription';\n\n// Get the native constant value\nexport const PI = ExpoIapModule.PI;\n\nexport enum OpenIapEvent {\n PurchaseUpdated = 'purchase-updated',\n PurchaseError = 'purchase-error',\n PromotedProductIOS = 'promoted-product-ios',\n}\n\nexport function setValueAsync(value: string) {\n return ExpoIapModule.setValueAsync(value);\n}\n\n// Ensure the emitter has proper EventEmitter interface\nexport const emitter = (ExpoIapModule || NativeModulesProxy.ExpoIap) as {\n addListener: (\n eventName: string,\n listener: (...args: any[]) => void,\n ) => {remove: () => void};\n removeListener: (\n eventName: string,\n listener: (...args: any[]) => void,\n ) => void;\n};\n\n/**\n * TODO(v3.1.0): Remove legacy 'inapp' alias once downstream apps migrate to 'in-app'.\n */\nexport type ProductTypeInput = 'inapp' | 'in-app' | 'subs';\nexport type InAppTypeInput = Exclude<ProductTypeInput, 'subs'>;\n\ntype PurchaseRequestInApp = {\n request: RequestPurchasePropsByPlatforms;\n type?: InAppTypeInput;\n};\n\ntype PurchaseRequestSubscription = {\n request: RequestSubscriptionPropsByPlatforms;\n type: 'subs';\n};\n\nexport type PurchaseRequestInput =\n | PurchaseRequestInApp\n | PurchaseRequestSubscription;\n\nexport type PurchaseRequest =\n | {\n request: RequestPurchaseProps;\n type?: InAppTypeInput;\n }\n | {\n request: RequestSubscriptionPropsByPlatforms;\n type: 'subs';\n };\n\nconst normalizeProductType = (type?: ProductTypeInput) => {\n if (type === 'inapp') {\n console.warn(\n \"expo-iap: 'inapp' product type is deprecated and will be removed in v3.1.0. Use 'in-app' instead.\",\n );\n }\n\n if (!type || type === 'inapp' || type === 'in-app') {\n return {\n canonical: 'in-app' as const,\n native: 'inapp' as const,\n };\n }\n if (type === 'subs') {\n return {\n canonical: 'subs' as const,\n native: 'subs' as const,\n };\n }\n throw new Error(`Unsupported product type: ${type}`);\n};\n\nexport const purchaseUpdatedListener = (\n listener: (event: Purchase) => void,\n) => {\n console.log('[JS] Registering purchaseUpdatedListener');\n const wrappedListener = (event: Purchase) => {\n console.log('[JS] purchaseUpdatedListener fired:', event);\n listener(event);\n };\n const emitterSubscription = emitter.addListener(\n OpenIapEvent.PurchaseUpdated,\n wrappedListener,\n );\n console.log('[JS] purchaseUpdatedListener registered successfully');\n return emitterSubscription;\n};\n\nexport const purchaseErrorListener = (\n listener: (error: PurchaseError) => void,\n) => {\n console.log('[JS] Registering purchaseErrorListener');\n const wrappedListener = (error: PurchaseError) => {\n console.log('[JS] purchaseErrorListener fired:', error);\n listener(error);\n };\n const emitterSubscription = emitter.addListener(\n OpenIapEvent.PurchaseError,\n wrappedListener,\n );\n console.log('[JS] purchaseErrorListener registered successfully');\n return emitterSubscription;\n};\n\n/**\n * iOS-only listener for App Store promoted product events.\n * This fires when a user taps on a promoted product in the App Store.\n *\n * @param listener - Callback function that receives the promoted product details\n * @returns EventSubscription that can be used to unsubscribe\n *\n * @example\n * ```typescript\n * const subscription = promotedProductListenerIOS((product) => {\n * console.log('Promoted product:', product);\n * // Handle the promoted product\n * });\n *\n * // Later, clean up\n * subscription.remove();\n * ```\n *\n * @platform iOS\n */\nexport const promotedProductListenerIOS = (\n listener: (product: Product) => void,\n) => {\n if (Platform.OS !== 'ios') {\n console.warn(\n 'promotedProductListenerIOS: This listener is only available on iOS',\n );\n return {remove: () => {}};\n }\n return emitter.addListener(OpenIapEvent.PromotedProductIOS, listener);\n};\n\nexport function initConnection(): Promise<boolean> {\n const result = ExpoIapModule.initConnection();\n return Promise.resolve(result);\n}\n\nexport async function endConnection(): Promise<boolean> {\n return ExpoIapModule.endConnection();\n}\n\n/**\n * Fetch products with unified API (v2.7.0+)\n *\n * @param params - Product fetch configuration\n * @param params.skus - Array of product SKUs to fetch\n * @param params.type - Type of products: 'in-app' for regular products (default) or 'subs' for subscriptions\n *\n * @example\n * ```typescript\n * // Regular products\n * const products = await fetchProducts({\n * skus: ['product1', 'product2'],\n * type: 'in-app'\n * });\n *\n * // Subscriptions\n * const subscriptions = await fetchProducts({\n * skus: ['sub1', 'sub2'],\n * type: 'subs'\n * });\n * ```\n */\nexport const fetchProducts = async ({\n skus,\n type,\n}: {\n skus: string[];\n type?: ProductTypeInput;\n}): Promise<Product[] | ProductSubscription[]> => {\n if (!skus?.length) {\n throw new PurchaseError({\n message: 'No SKUs provided',\n code: ErrorCode.EmptySkuList,\n });\n }\n\n const {canonical, native} = normalizeProductType(type);\n\n if (Platform.OS === 'ios') {\n const rawItems = await ExpoIapModule.fetchProducts({skus, type: native});\n\n const filteredItems = rawItems.filter((item: unknown) => {\n if (!isProductIOS(item)) {\n return false;\n }\n const isValid =\n typeof item === 'object' &&\n item !== null &&\n 'id' in item &&\n typeof item.id === 'string' &&\n skus.includes(item.id);\n return isValid;\n });\n\n return canonical === 'in-app'\n ? (filteredItems as Product[])\n : (filteredItems as ProductSubscription[]);\n }\n\n if (Platform.OS === 'android') {\n const items = await ExpoIapModule.fetchProducts(native, skus);\n const filteredItems = items.filter((item: unknown) => {\n if (!isProductAndroid(item)) return false;\n return (\n typeof item === 'object' &&\n item !== null &&\n 'id' in item &&\n typeof item.id === 'string' &&\n skus.includes(item.id)\n );\n });\n\n return canonical === 'in-app'\n ? (filteredItems as Product[])\n : (filteredItems as ProductSubscription[]);\n }\n\n throw new Error('Unsupported platform');\n};\n\nexport const getAvailablePurchases = ({\n alsoPublishToEventListenerIOS = false,\n onlyIncludeActiveItemsIOS = true,\n}: {\n alsoPublishToEventListenerIOS?: boolean;\n onlyIncludeActiveItemsIOS?: boolean;\n} = {}): Promise<Purchase[]> =>\n (\n Platform.select({\n ios: () =>\n ExpoIapModule.getAvailableItems(\n alsoPublishToEventListenerIOS,\n onlyIncludeActiveItemsIOS,\n ),\n android: () => ExpoIapModule.getAvailableItems(),\n }) || (() => Promise.resolve([]))\n )();\n\n/**\n * Restore completed transactions (cross-platform behavior)\n *\n * - iOS: perform a lightweight sync to refresh transactions and ignore sync errors,\n * then fetch available purchases to surface restored items to the app.\n * - Android: simply fetch available purchases (restoration happens via query).\n *\n * This helper returns the restored/available purchases so callers can update UI/state.\n *\n * @param options.alsoPublishToEventListenerIOS - iOS only: whether to also publish to the event listener\n * @param options.onlyIncludeActiveItemsIOS - iOS only: whether to only include active items\n * @returns Promise resolving to the list of available/restored purchases\n */\nexport const restorePurchases = async (\n options: {\n alsoPublishToEventListenerIOS?: boolean;\n onlyIncludeActiveItemsIOS?: boolean;\n } = {},\n): Promise<Purchase[]> => {\n if (Platform.OS === 'ios') {\n // Perform best-effort sync on iOS and ignore sync errors to avoid blocking restore flow\n await syncIOS().catch(() => undefined);\n }\n\n // Then, fetch available purchases for both platforms\n const purchases = await getAvailablePurchases({\n alsoPublishToEventListenerIOS:\n options.alsoPublishToEventListenerIOS ?? false,\n onlyIncludeActiveItemsIOS: options.onlyIncludeActiveItemsIOS ?? true,\n });\n\n return purchases;\n};\n\nconst offerToRecordIOS = (\n offer: DiscountOfferInputIOS | undefined,\n): Record<keyof DiscountOfferInputIOS, string> | undefined => {\n if (!offer) return undefined;\n return {\n identifier: offer.identifier,\n keyIdentifier: offer.keyIdentifier,\n nonce: offer.nonce,\n signature: offer.signature,\n timestamp: offer.timestamp.toString(),\n };\n};\n\n/**\n * Helper to normalize request props to platform-specific format\n */\nconst normalizeRequestProps = (\n request:\n | RequestPurchasePropsByPlatforms\n | RequestSubscriptionPropsByPlatforms,\n platform: 'ios' | 'android',\n): any => {\n // Platform-specific format - directly return the appropriate platform data\n return platform === 'ios' ? request.ios : request.android;\n};\n\n/**\n * Request a purchase for products or subscriptions.\n *\n * @param requestObj - Purchase request configuration\n * @param requestObj.request - Platform-specific purchase parameters\n * @param requestObj.type - Type of purchase: 'in-app' for products (default) or 'subs' for subscriptions\n *\n * @example\n * ```typescript\n * // Product purchase\n * await requestPurchase({\n * request: {\n * ios: { sku: productId },\n * android: { skus: [productId] }\n * },\n * type: 'in-app'\n * });\n *\n * // Subscription purchase\n * await requestPurchase({\n * request: {\n * ios: { sku: subscriptionId },\n * android: {\n * skus: [subscriptionId],\n * subscriptionOffers: [{ sku: subscriptionId, offerToken: 'token' }]\n * }\n * },\n * type: 'subs'\n * });\n * ```\n */\nexport const requestPurchase = (\n requestObj: PurchaseRequestInput,\n): Promise<Purchase | Purchase[] | void> => {\n const {request, type} = requestObj;\n const {canonical, native} = normalizeProductType(type);\n const isInAppPurchase = canonical === 'in-app';\n\n if (Platform.OS === 'ios') {\n const normalizedRequest = normalizeRequestProps(request, 'ios');\n\n if (!normalizedRequest?.sku) {\n throw new Error(\n 'Invalid request for iOS. The `sku` property is required and must be a string.',\n );\n }\n\n const {\n sku,\n andDangerouslyFinishTransactionAutomatically = false,\n appAccountToken,\n quantity,\n withOffer,\n } = normalizedRequest;\n\n return (async () => {\n const offer = offerToRecordIOS(withOffer);\n const purchase = await ExpoIapModule.requestPurchase({\n sku,\n andDangerouslyFinishTransactionAutomatically,\n appAccountToken,\n quantity,\n withOffer: offer,\n });\n\n return purchase as Purchase;\n })();\n }\n\n if (Platform.OS === 'android') {\n const normalizedRequest = normalizeRequestProps(request, 'android');\n\n if (!normalizedRequest?.skus?.length) {\n throw new Error(\n 'Invalid request for Android. The `skus` property is required and must be a non-empty array.',\n );\n }\n\n if (isInAppPurchase) {\n const {\n skus,\n obfuscatedAccountIdAndroid,\n obfuscatedProfileIdAndroid,\n isOfferPersonalized,\n } = normalizedRequest;\n\n return (async () => {\n return ExpoIapModule.requestPurchase({\n type: native,\n skuArr: skus,\n purchaseToken: undefined,\n replacementMode: -1,\n obfuscatedAccountId: obfuscatedAccountIdAndroid,\n obfuscatedProfileId: obfuscatedProfileIdAndroid,\n offerTokenArr: [],\n isOfferPersonalized: isOfferPersonalized ?? false,\n }) as Promise<Purchase[]>;\n })();\n }\n\n if (canonical === 'subs') {\n const {\n skus,\n obfuscatedAccountIdAndroid,\n obfuscatedProfileIdAndroid,\n isOfferPersonalized,\n subscriptionOffers = [],\n replacementModeAndroid = -1,\n purchaseToken,\n } = normalizedRequest;\n\n return (async () => {\n return ExpoIapModule.requestPurchase({\n type: native,\n skuArr: skus,\n purchaseToken,\n replacementMode: replacementModeAndroid,\n obfuscatedAccountId: obfuscatedAccountIdAndroid,\n obfuscatedProfileId: obfuscatedProfileIdAndroid,\n offerTokenArr: subscriptionOffers.map((so: any) => so.offerToken),\n isOfferPersonalized: isOfferPersonalized ?? false,\n }) as Promise<Purchase[]>;\n })();\n }\n\n throw new Error(\n \"Invalid request for Android: Expected a valid request object with 'skus' array.\",\n );\n }\n\n return Promise.resolve(); // Fallback for unsupported platforms\n};\n\nexport const finishTransaction = ({\n purchase,\n isConsumable = false,\n}: {\n purchase: Purchase;\n isConsumable?: boolean;\n}): Promise<VoidResult | boolean> => {\n return (\n Platform.select({\n ios: async () => {\n const transactionId = purchase.id;\n if (!transactionId) {\n return Promise.reject(\n new Error('purchase.id required to finish iOS transaction'),\n );\n }\n await ExpoIapModule.finishTransaction(transactionId);\n return Promise.resolve(true);\n },\n android: async () => {\n const androidPurchase = purchase as PurchaseAndroid;\n\n // Use purchaseToken if available, fallback to purchaseTokenAndroid for backward compatibility\n const token = androidPurchase.purchaseToken;\n\n if (!token) {\n return Promise.reject(\n new PurchaseError({\n message: 'Purchase token is required to finish transaction',\n code: ErrorCode.DeveloperError,\n productId: androidPurchase.productId,\n platform: 'android',\n }),\n );\n }\n\n if (isConsumable) {\n return ExpoIapModule.consumePurchaseAndroid(token);\n }\n\n return ExpoIapModule.acknowledgePurchaseAndroid(token);\n },\n }) || (() => Promise.reject(new Error('Unsupported Platform')))\n )();\n};\n\n/**\n * Retrieves the current storefront information from iOS App Store\n *\n * @returns Promise resolving to the storefront country code\n * @throws Error if called on non-iOS platform\n *\n * @example\n * ```typescript\n * const storefront = await getStorefrontIOS();\n * console.log(storefront); // 'US'\n * ```\n *\n * @platform iOS\n */\nexport const getStorefrontIOS = (): Promise<string> => {\n if (Platform.OS !== 'ios') {\n console.warn('getStorefrontIOS: This method is only available on iOS');\n return Promise.resolve('');\n }\n return ExpoIapModule.getStorefrontIOS();\n};\n\n/**\n * Gets the storefront country code from the underlying native store.\n * Returns a two-letter country code such as 'US', 'KR', or empty string on failure.\n *\n * @platform ios\n * @platform android\n */\nexport const getStorefront = (): Promise<string> => {\n // Cross-platform storefront\n if (Platform.OS === 'android') {\n if (typeof (ExpoIapModule as any).getStorefrontAndroid === 'function') {\n return (ExpoIapModule as any).getStorefrontAndroid();\n }\n return Promise.resolve('');\n }\n return getStorefrontIOS();\n};\n\n/**\n * Internal receipt validation function (NOT RECOMMENDED for production use)\n *\n * WARNING: This function performs client-side validation which is NOT secure.\n * For production apps, always validate receipts on your secure server:\n * - iOS: Send receipt data to Apple's verification endpoint from your server\n * - Android: Use Google Play Developer API with service account credentials\n */\nexport const validateReceipt = async (\n sku: string,\n androidOptions?: {\n packageName: string;\n productToken: string;\n accessToken: string;\n isSub?: boolean;\n },\n): Promise<ReceiptValidationResult> => {\n if (Platform.OS === 'ios') {\n return await validateReceiptIOS(sku);\n } else if (Platform.OS === 'android') {\n if (\n !androidOptions ||\n !androidOptions.packageName ||\n !androidOptions.productToken ||\n !androidOptions.accessToken\n ) {\n throw new Error(\n 'Android validation requires packageName, productToken, and accessToken',\n );\n }\n return await validateReceiptAndroid({\n packageName: androidOptions.packageName,\n productId: sku,\n productToken: androidOptions.productToken,\n accessToken: androidOptions.accessToken,\n isSub: androidOptions.isSub,\n });\n } else {\n throw new Error('Platform not supported');\n }\n};\n\n/**\n * Deeplinks to native interface that allows users to manage their subscriptions\n * @param options.skuAndroid - Required for Android to locate specific subscription (ignored on iOS)\n * @param options.packageNameAndroid - Required for Android to identify your app (ignored on iOS)\n *\n * @returns Promise that resolves when the deep link is successfully opened\n *\n * @throws {Error} When called on unsupported platform or when required Android parameters are missing\n *\n * @example\n * import { deepLinkToSubscriptions } from 'expo-iap';\n *\n * // Works on both iOS and Android\n * await deepLinkToSubscriptions({\n * skuAndroid: 'your_subscription_sku',\n * packageNameAndroid: 'com.example.app'\n * });\n */\nexport const deepLinkToSubscriptions = (options: {\n skuAndroid?: string;\n packageNameAndroid?: string;\n}): Promise<void> => {\n if (Platform.OS === 'ios') {\n return deepLinkToSubscriptionsIOS();\n }\n\n if (Platform.OS === 'android') {\n return deepLinkToSubscriptionsAndroid({\n sku: options?.skuAndroid,\n packageName: options?.packageNameAndroid,\n });\n }\n\n return Promise.reject(new Error(`Unsupported platform: ${Platform.OS}`));\n};\n\nexport * from './useIAP';\nexport * from './utils/errorMapping';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["// External dependencies\nimport {NativeModulesProxy} from 'expo-modules-core';\nimport {Platform} from 'react-native';\n\n// Internal modules\nimport ExpoIapModule from './ExpoIapModule';\nimport {\n isProductIOS,\n validateReceiptIOS,\n deepLinkToSubscriptionsIOS,\n syncIOS,\n} from './modules/ios';\nimport {\n isProductAndroid,\n validateReceiptAndroid,\n deepLinkToSubscriptionsAndroid,\n} from './modules/android';\n\n// Types\nimport {\n Product,\n Purchase,\n ErrorCode,\n RequestPurchaseProps,\n RequestPurchasePropsByPlatforms,\n RequestPurchaseAndroidProps,\n RequestPurchaseIosProps,\n RequestSubscriptionPropsByPlatforms,\n RequestSubscriptionAndroidProps,\n RequestSubscriptionIosProps,\n ProductSubscription,\n PurchaseAndroid,\n DiscountOfferInputIOS,\n VoidResult,\n ReceiptValidationResult,\n AndroidSubscriptionOfferInput,\n} from './types';\nimport {PurchaseError} from './purchase-error';\n\n// Export all types\nexport * from './types';\nexport {ErrorCodeUtils, ErrorCodeMapping} from './purchase-error';\nexport * from './modules/android';\nexport * from './modules/ios';\n\n// Export subscription helpers\nexport {\n getActiveSubscriptions,\n hasActiveSubscriptions,\n} from './helpers/subscription';\n\n// Get the native constant value\nexport const PI = ExpoIapModule.PI;\n\nexport enum OpenIapEvent {\n PurchaseUpdated = 'purchase-updated',\n PurchaseError = 'purchase-error',\n PromotedProductIOS = 'promoted-product-ios',\n}\n\nexport function setValueAsync(value: string) {\n return ExpoIapModule.setValueAsync(value);\n}\n\ntype ExpoIapEventPayloads = {\n [OpenIapEvent.PurchaseUpdated]: Purchase;\n [OpenIapEvent.PurchaseError]: PurchaseError;\n [OpenIapEvent.PromotedProductIOS]: Product;\n};\n\ntype ExpoIapEventListener<E extends OpenIapEvent> = (\n payload: ExpoIapEventPayloads[E],\n) => void;\n\ntype ExpoIapEmitter = {\n addListener<E extends OpenIapEvent>(\n eventName: E,\n listener: ExpoIapEventListener<E>,\n ): {remove: () => void};\n removeListener<E extends OpenIapEvent>(\n eventName: E,\n listener: ExpoIapEventListener<E>,\n ): void;\n};\n\n// Ensure the emitter has proper EventEmitter interface\nexport const emitter = (ExpoIapModule ||\n NativeModulesProxy.ExpoIap) as ExpoIapEmitter;\n\n/**\n * TODO(v3.1.0): Remove legacy 'inapp' alias once downstream apps migrate to 'in-app'.\n */\nexport type ProductTypeInput = 'inapp' | 'in-app' | 'subs';\nexport type InAppTypeInput = Exclude<ProductTypeInput, 'subs'>;\n\ntype PurchaseRequestInApp = {\n request: RequestPurchasePropsByPlatforms;\n type?: InAppTypeInput;\n};\n\ntype PurchaseRequestSubscription = {\n request: RequestSubscriptionPropsByPlatforms;\n type: 'subs';\n};\n\nexport type PurchaseRequestInput =\n | PurchaseRequestInApp\n | PurchaseRequestSubscription;\n\nexport type PurchaseRequest =\n | {\n request: RequestPurchaseProps;\n type?: InAppTypeInput;\n }\n | {\n request: RequestSubscriptionPropsByPlatforms;\n type: 'subs';\n };\n\nconst normalizeProductType = (type?: ProductTypeInput) => {\n if (type === 'inapp') {\n console.warn(\n \"expo-iap: 'inapp' product type is deprecated and will be removed in v3.1.0. Use 'in-app' instead.\",\n );\n }\n\n if (!type || type === 'inapp' || type === 'in-app') {\n return {\n canonical: 'in-app' as const,\n native: 'inapp' as const,\n };\n }\n if (type === 'subs') {\n return {\n canonical: 'subs' as const,\n native: 'subs' as const,\n };\n }\n throw new Error(`Unsupported product type: ${type}`);\n};\n\nexport const purchaseUpdatedListener = (\n listener: (event: Purchase) => void,\n) => {\n console.log('[JS] Registering purchaseUpdatedListener');\n const wrappedListener = (event: Purchase) => {\n console.log('[JS] purchaseUpdatedListener fired:', event);\n listener(event);\n };\n const emitterSubscription = emitter.addListener(\n OpenIapEvent.PurchaseUpdated,\n wrappedListener,\n );\n console.log('[JS] purchaseUpdatedListener registered successfully');\n return emitterSubscription;\n};\n\nexport const purchaseErrorListener = (\n listener: (error: PurchaseError) => void,\n) => {\n console.log('[JS] Registering purchaseErrorListener');\n const wrappedListener = (error: PurchaseError) => {\n console.log('[JS] purchaseErrorListener fired:', error);\n listener(error);\n };\n const emitterSubscription = emitter.addListener(\n OpenIapEvent.PurchaseError,\n wrappedListener,\n );\n console.log('[JS] purchaseErrorListener registered successfully');\n return emitterSubscription;\n};\n\n/**\n * iOS-only listener for App Store promoted product events.\n * This fires when a user taps on a promoted product in the App Store.\n *\n * @param listener - Callback function that receives the promoted product details\n * @returns EventSubscription that can be used to unsubscribe\n *\n * @example\n * ```typescript\n * const subscription = promotedProductListenerIOS((product) => {\n * console.log('Promoted product:', product);\n * // Handle the promoted product\n * });\n *\n * // Later, clean up\n * subscription.remove();\n * ```\n *\n * @platform iOS\n */\nexport const promotedProductListenerIOS = (\n listener: (product: Product) => void,\n) => {\n if (Platform.OS !== 'ios') {\n console.warn(\n 'promotedProductListenerIOS: This listener is only available on iOS',\n );\n return {remove: () => {}};\n }\n return emitter.addListener(OpenIapEvent.PromotedProductIOS, listener);\n};\n\nexport function initConnection(): Promise<boolean> {\n const result = ExpoIapModule.initConnection();\n return Promise.resolve(result);\n}\n\nexport async function endConnection(): Promise<boolean> {\n return ExpoIapModule.endConnection();\n}\n\n/**\n * Fetch products with unified API (v2.7.0+)\n *\n * @param params - Product fetch configuration\n * @param params.skus - Array of product SKUs to fetch\n * @param params.type - Type of products: 'in-app' for regular products (default) or 'subs' for subscriptions\n *\n * @example\n * ```typescript\n * // Regular products\n * const products = await fetchProducts({\n * skus: ['product1', 'product2'],\n * type: 'in-app'\n * });\n *\n * // Subscriptions\n * const subscriptions = await fetchProducts({\n * skus: ['sub1', 'sub2'],\n * type: 'subs'\n * });\n * ```\n */\nexport const fetchProducts = async ({\n skus,\n type,\n}: {\n skus: string[];\n type?: ProductTypeInput;\n}): Promise<Product[] | ProductSubscription[]> => {\n if (!skus?.length) {\n throw new PurchaseError({\n message: 'No SKUs provided',\n code: ErrorCode.EmptySkuList,\n });\n }\n\n const {canonical, native} = normalizeProductType(type);\n\n if (Platform.OS === 'ios') {\n const rawItems = await ExpoIapModule.fetchProducts({skus, type: native});\n\n const filteredItems = rawItems.filter((item: unknown) => {\n if (!isProductIOS(item)) {\n return false;\n }\n const isValid =\n typeof item === 'object' &&\n item !== null &&\n 'id' in item &&\n typeof item.id === 'string' &&\n skus.includes(item.id);\n return isValid;\n });\n\n return canonical === 'in-app'\n ? (filteredItems as Product[])\n : (filteredItems as ProductSubscription[]);\n }\n\n if (Platform.OS === 'android') {\n const items = await ExpoIapModule.fetchProducts(native, skus);\n const filteredItems = items.filter((item: unknown) => {\n if (!isProductAndroid(item)) return false;\n return (\n typeof item === 'object' &&\n item !== null &&\n 'id' in item &&\n typeof item.id === 'string' &&\n skus.includes(item.id)\n );\n });\n\n return canonical === 'in-app'\n ? (filteredItems as Product[])\n : (filteredItems as ProductSubscription[]);\n }\n\n throw new Error('Unsupported platform');\n};\n\nexport const getAvailablePurchases = ({\n alsoPublishToEventListenerIOS = false,\n onlyIncludeActiveItemsIOS = true,\n}: {\n alsoPublishToEventListenerIOS?: boolean;\n onlyIncludeActiveItemsIOS?: boolean;\n} = {}): Promise<Purchase[]> =>\n (\n Platform.select({\n ios: () =>\n ExpoIapModule.getAvailableItems(\n alsoPublishToEventListenerIOS,\n onlyIncludeActiveItemsIOS,\n ),\n android: () => ExpoIapModule.getAvailableItems(),\n }) || (() => Promise.resolve([]))\n )();\n\n/**\n * Restore completed transactions (cross-platform behavior)\n *\n * - iOS: perform a lightweight sync to refresh transactions and ignore sync errors,\n * then fetch available purchases to surface restored items to the app.\n * - Android: simply fetch available purchases (restoration happens via query).\n *\n * This helper returns the restored/available purchases so callers can update UI/state.\n *\n * @param options.alsoPublishToEventListenerIOS - iOS only: whether to also publish to the event listener\n * @param options.onlyIncludeActiveItemsIOS - iOS only: whether to only include active items\n * @returns Promise resolving to the list of available/restored purchases\n */\nexport const restorePurchases = async (\n options: {\n alsoPublishToEventListenerIOS?: boolean;\n onlyIncludeActiveItemsIOS?: boolean;\n } = {},\n): Promise<Purchase[]> => {\n if (Platform.OS === 'ios') {\n // Perform best-effort sync on iOS and ignore sync errors to avoid blocking restore flow\n await syncIOS().catch(() => undefined);\n }\n\n // Then, fetch available purchases for both platforms\n const purchases = await getAvailablePurchases({\n alsoPublishToEventListenerIOS:\n options.alsoPublishToEventListenerIOS ?? false,\n onlyIncludeActiveItemsIOS: options.onlyIncludeActiveItemsIOS ?? true,\n });\n\n return purchases;\n};\n\nconst offerToRecordIOS = (\n offer: DiscountOfferInputIOS | undefined,\n): Record<keyof DiscountOfferInputIOS, string> | undefined => {\n if (!offer) return undefined;\n return {\n identifier: offer.identifier,\n keyIdentifier: offer.keyIdentifier,\n nonce: offer.nonce,\n signature: offer.signature,\n timestamp: offer.timestamp.toString(),\n };\n};\n\n/**\n * Helper to normalize request props to platform-specific format\n */\nfunction normalizeRequestProps(\n request: RequestPurchasePropsByPlatforms,\n platform: 'ios',\n): RequestPurchaseIosProps | null | undefined;\nfunction normalizeRequestProps(\n request: RequestPurchasePropsByPlatforms,\n platform: 'android',\n): RequestPurchaseAndroidProps | null | undefined;\nfunction normalizeRequestProps(\n request: RequestSubscriptionPropsByPlatforms,\n platform: 'ios',\n): RequestSubscriptionIosProps | null | undefined;\nfunction normalizeRequestProps(\n request: RequestSubscriptionPropsByPlatforms,\n platform: 'android',\n): RequestSubscriptionAndroidProps | null | undefined;\nfunction normalizeRequestProps(\n request:\n | RequestPurchasePropsByPlatforms\n | RequestSubscriptionPropsByPlatforms,\n platform: 'ios' | 'android',\n) {\n // Platform-specific format - directly return the appropriate platform data\n return platform === 'ios' ? request.ios : request.android;\n}\n\n/**\n * Request a purchase for products or subscriptions.\n *\n * @param requestObj - Purchase request configuration\n * @param requestObj.request - Platform-specific purchase parameters\n * @param requestObj.type - Type of purchase: 'in-app' for products (default) or 'subs' for subscriptions\n *\n * @example\n * ```typescript\n * // Product purchase\n * await requestPurchase({\n * request: {\n * ios: { sku: productId },\n * android: { skus: [productId] }\n * },\n * type: 'in-app'\n * });\n *\n * // Subscription purchase\n * await requestPurchase({\n * request: {\n * ios: { sku: subscriptionId },\n * android: {\n * skus: [subscriptionId],\n * subscriptionOffers: [{ sku: subscriptionId, offerToken: 'token' }]\n * }\n * },\n * type: 'subs'\n * });\n * ```\n */\nexport const requestPurchase = (\n requestObj: PurchaseRequestInput,\n): Promise<Purchase | Purchase[] | void> => {\n const {request, type} = requestObj;\n const {canonical, native} = normalizeProductType(type);\n const isInAppPurchase = canonical === 'in-app';\n\n if (Platform.OS === 'ios') {\n const normalizedRequest = normalizeRequestProps(request, 'ios');\n\n if (!normalizedRequest?.sku) {\n throw new Error(\n 'Invalid request for iOS. The `sku` property is required and must be a string.',\n );\n }\n\n const {\n sku,\n andDangerouslyFinishTransactionAutomatically = false,\n appAccountToken,\n quantity,\n withOffer,\n } = normalizedRequest;\n\n return (async () => {\n const offer = offerToRecordIOS(withOffer ?? undefined);\n const purchase = await ExpoIapModule.requestPurchase({\n sku,\n andDangerouslyFinishTransactionAutomatically,\n appAccountToken,\n quantity,\n withOffer: offer,\n });\n\n return purchase as Purchase;\n })();\n }\n\n if (Platform.OS === 'android') {\n if (isInAppPurchase) {\n const normalizedRequest: RequestPurchaseAndroidProps | null | undefined =\n normalizeRequestProps(\n request as RequestPurchasePropsByPlatforms,\n 'android',\n );\n\n if (!normalizedRequest?.skus?.length) {\n throw new Error(\n 'Invalid request for Android. The `skus` property is required and must be a non-empty array.',\n );\n }\n\n const {\n skus,\n obfuscatedAccountIdAndroid,\n obfuscatedProfileIdAndroid,\n isOfferPersonalized,\n } = normalizedRequest;\n\n return (async () => {\n return ExpoIapModule.requestPurchase({\n type: native,\n skuArr: skus,\n purchaseToken: undefined,\n replacementMode: -1,\n obfuscatedAccountId: obfuscatedAccountIdAndroid,\n obfuscatedProfileId: obfuscatedProfileIdAndroid,\n offerTokenArr: [],\n isOfferPersonalized: isOfferPersonalized ?? false,\n }) as Promise<Purchase[]>;\n })();\n }\n\n if (canonical === 'subs') {\n const normalizedRequest:\n | RequestSubscriptionAndroidProps\n | null\n | undefined = normalizeRequestProps(\n request as RequestSubscriptionPropsByPlatforms,\n 'android',\n );\n\n if (!normalizedRequest?.skus?.length) {\n throw new Error(\n 'Invalid request for Android. The `skus` property is required and must be a non-empty array.',\n );\n }\n\n const {\n skus,\n obfuscatedAccountIdAndroid,\n obfuscatedProfileIdAndroid,\n isOfferPersonalized,\n subscriptionOffers,\n replacementModeAndroid,\n purchaseTokenAndroid,\n } = normalizedRequest;\n\n const normalizedOffers = subscriptionOffers ?? [];\n const replacementMode = replacementModeAndroid ?? -1;\n const purchaseToken = purchaseTokenAndroid ?? undefined;\n\n return (async () => {\n return ExpoIapModule.requestPurchase({\n type: native,\n skuArr: skus,\n purchaseToken,\n replacementMode,\n obfuscatedAccountId: obfuscatedAccountIdAndroid,\n obfuscatedProfileId: obfuscatedProfileIdAndroid,\n offerTokenArr: normalizedOffers.map(\n (so: AndroidSubscriptionOfferInput) => so.offerToken,\n ),\n subscriptionOffers: normalizedOffers,\n isOfferPersonalized: isOfferPersonalized ?? false,\n }) as Promise<Purchase[]>;\n })();\n }\n\n throw new Error(\n \"Invalid request for Android: Expected a valid request object with 'skus' array.\",\n );\n }\n\n return Promise.resolve(); // Fallback for unsupported platforms\n};\n\nexport const finishTransaction = ({\n purchase,\n isConsumable = false,\n}: {\n purchase: Purchase;\n isConsumable?: boolean;\n}): Promise<VoidResult | boolean> => {\n return (\n Platform.select({\n ios: async () => {\n const transactionId = purchase.id;\n if (!transactionId) {\n return Promise.reject(\n new Error('purchase.id required to finish iOS transaction'),\n );\n }\n await ExpoIapModule.finishTransaction(transactionId);\n return Promise.resolve(true);\n },\n android: async () => {\n const androidPurchase = purchase as PurchaseAndroid;\n\n // Use purchaseToken if available, fallback to purchaseTokenAndroid for backward compatibility\n const token = androidPurchase.purchaseToken;\n\n if (!token) {\n return Promise.reject(\n new PurchaseError({\n message: 'Purchase token is required to finish transaction',\n code: ErrorCode.DeveloperError,\n productId: androidPurchase.productId,\n platform: 'android',\n }),\n );\n }\n\n if (isConsumable) {\n return ExpoIapModule.consumePurchaseAndroid(token);\n }\n\n return ExpoIapModule.acknowledgePurchaseAndroid(token);\n },\n }) || (() => Promise.reject(new Error('Unsupported Platform')))\n )();\n};\n\n/**\n * Retrieves the current storefront information from iOS App Store\n *\n * @returns Promise resolving to the storefront country code\n * @throws Error if called on non-iOS platform\n *\n * @example\n * ```typescript\n * const storefront = await getStorefrontIOS();\n * console.log(storefront); // 'US'\n * ```\n *\n * @platform iOS\n */\nexport const getStorefrontIOS = (): Promise<string> => {\n if (Platform.OS !== 'ios') {\n console.warn('getStorefrontIOS: This method is only available on iOS');\n return Promise.resolve('');\n }\n return ExpoIapModule.getStorefrontIOS();\n};\n\n/**\n * Gets the storefront country code from the underlying native store.\n * Returns a two-letter country code such as 'US', 'KR', or empty string on failure.\n *\n * @platform ios\n * @platform android\n */\nexport const getStorefront = (): Promise<string> => {\n // Cross-platform storefront\n if (Platform.OS === 'android') {\n if (typeof ExpoIapModule.getStorefrontAndroid === 'function') {\n return ExpoIapModule.getStorefrontAndroid();\n }\n return Promise.resolve('');\n }\n return getStorefrontIOS();\n};\n\n/**\n * Internal receipt validation function (NOT RECOMMENDED for production use)\n *\n * WARNING: This function performs client-side validation which is NOT secure.\n * For production apps, always validate receipts on your secure server:\n * - iOS: Send receipt data to Apple's verification endpoint from your server\n * - Android: Use Google Play Developer API with service account credentials\n */\nexport const validateReceipt = async (\n sku: string,\n androidOptions?: {\n packageName: string;\n productToken: string;\n accessToken: string;\n isSub?: boolean;\n },\n): Promise<ReceiptValidationResult> => {\n if (Platform.OS === 'ios') {\n return await validateReceiptIOS(sku);\n } else if (Platform.OS === 'android') {\n if (\n !androidOptions ||\n !androidOptions.packageName ||\n !androidOptions.productToken ||\n !androidOptions.accessToken\n ) {\n throw new Error(\n 'Android validation requires packageName, productToken, and accessToken',\n );\n }\n return await validateReceiptAndroid({\n packageName: androidOptions.packageName,\n productId: sku,\n productToken: androidOptions.productToken,\n accessToken: androidOptions.accessToken,\n isSub: androidOptions.isSub,\n });\n } else {\n throw new Error('Platform not supported');\n }\n};\n\n/**\n * Deeplinks to native interface that allows users to manage their subscriptions\n * @param options.skuAndroid - Required for Android to locate specific subscription (ignored on iOS)\n * @param options.packageNameAndroid - Required for Android to identify your app (ignored on iOS)\n *\n * @returns Promise that resolves when the deep link is successfully opened\n *\n * @throws {Error} When called on unsupported platform or when required Android parameters are missing\n *\n * @example\n * import { deepLinkToSubscriptions } from 'expo-iap';\n *\n * // Works on both iOS and Android\n * await deepLinkToSubscriptions({\n * skuAndroid: 'your_subscription_sku',\n * packageNameAndroid: 'com.example.app'\n * });\n */\nexport const deepLinkToSubscriptions = (options: {\n skuAndroid?: string;\n packageNameAndroid?: string;\n}): Promise<void> => {\n if (Platform.OS === 'ios') {\n return deepLinkToSubscriptionsIOS();\n }\n\n if (Platform.OS === 'android') {\n return deepLinkToSubscriptionsAndroid({\n sku: options?.skuAndroid,\n packageName: options?.packageNameAndroid,\n });\n }\n\n return Promise.reject(new Error(`Unsupported platform: ${Platform.OS}`));\n};\n\nexport * from './useIAP';\nexport * from './utils/errorMapping';\n"]}
|
package/package.json
CHANGED
package/plugin/build/withIAP.js
CHANGED
|
@@ -69,7 +69,7 @@ const modifyAppBuildGradle = (gradle, language) => {
|
|
|
69
69
|
const impl = (ga, v) => language === 'kotlin'
|
|
70
70
|
? ` implementation("${ga}:${v}")`
|
|
71
71
|
: ` implementation "${ga}:${v}"`;
|
|
72
|
-
const openiapDep = impl('io.github.hyochan.openiap:openiap-google', '1.1.
|
|
72
|
+
const openiapDep = impl('io.github.hyochan.openiap:openiap-google', '1.1.11');
|
|
73
73
|
// Remove any existing openiap-google lines (any version, groovy/kotlin, implementation/api)
|
|
74
74
|
const openiapAnyLine = /^\s*(?:implementation|api)\s*\(?\s*["']io\.github\.hyochan\.openiap:openiap-google:[^"']+["']\s*\)?\s*$/gm;
|
|
75
75
|
const hadExisting = openiapAnyLine.test(modified);
|
|
@@ -81,8 +81,8 @@ const modifyAppBuildGradle = (gradle, language) => {
|
|
|
81
81
|
// Insert just after the opening `dependencies {` line
|
|
82
82
|
modified = addLineToGradle(modified, /dependencies\s*{/, openiapDep, 1);
|
|
83
83
|
logOnce(hadExisting
|
|
84
|
-
? '🛠️ expo-iap: Replaced OpenIAP dependency with 1.1.
|
|
85
|
-
: '🛠️ expo-iap: Added OpenIAP dependency (1.1.
|
|
84
|
+
? '🛠️ expo-iap: Replaced OpenIAP dependency with 1.1.11'
|
|
85
|
+
: '🛠️ expo-iap: Added OpenIAP dependency (1.1.11) to build.gradle');
|
|
86
86
|
}
|
|
87
87
|
return modified;
|
|
88
88
|
};
|
package/plugin/src/withIAP.ts
CHANGED
|
@@ -54,7 +54,7 @@ const modifyAppBuildGradle = (
|
|
|
54
54
|
language === 'kotlin'
|
|
55
55
|
? ` implementation("${ga}:${v}")`
|
|
56
56
|
: ` implementation "${ga}:${v}"`;
|
|
57
|
-
const openiapDep = impl('io.github.hyochan.openiap:openiap-google', '1.1.
|
|
57
|
+
const openiapDep = impl('io.github.hyochan.openiap:openiap-google', '1.1.11');
|
|
58
58
|
|
|
59
59
|
// Remove any existing openiap-google lines (any version, groovy/kotlin, implementation/api)
|
|
60
60
|
const openiapAnyLine =
|
|
@@ -74,8 +74,8 @@ const modifyAppBuildGradle = (
|
|
|
74
74
|
modified = addLineToGradle(modified, /dependencies\s*{/, openiapDep, 1);
|
|
75
75
|
logOnce(
|
|
76
76
|
hadExisting
|
|
77
|
-
? '🛠️ expo-iap: Replaced OpenIAP dependency with 1.1.
|
|
78
|
-
: '🛠️ expo-iap: Added OpenIAP dependency (1.1.
|
|
77
|
+
? '🛠️ expo-iap: Replaced OpenIAP dependency with 1.1.11'
|
|
78
|
+
: '🛠️ expo-iap: Added OpenIAP dependency (1.1.11) to build.gradle',
|
|
79
79
|
);
|
|
80
80
|
}
|
|
81
81
|
|
package/src/index.ts
CHANGED
|
@@ -23,12 +23,17 @@ import {
|
|
|
23
23
|
ErrorCode,
|
|
24
24
|
RequestPurchaseProps,
|
|
25
25
|
RequestPurchasePropsByPlatforms,
|
|
26
|
+
RequestPurchaseAndroidProps,
|
|
27
|
+
RequestPurchaseIosProps,
|
|
26
28
|
RequestSubscriptionPropsByPlatforms,
|
|
29
|
+
RequestSubscriptionAndroidProps,
|
|
30
|
+
RequestSubscriptionIosProps,
|
|
27
31
|
ProductSubscription,
|
|
28
32
|
PurchaseAndroid,
|
|
29
33
|
DiscountOfferInputIOS,
|
|
30
34
|
VoidResult,
|
|
31
35
|
ReceiptValidationResult,
|
|
36
|
+
AndroidSubscriptionOfferInput,
|
|
32
37
|
} from './types';
|
|
33
38
|
import {PurchaseError} from './purchase-error';
|
|
34
39
|
|
|
@@ -57,18 +62,31 @@ export function setValueAsync(value: string) {
|
|
|
57
62
|
return ExpoIapModule.setValueAsync(value);
|
|
58
63
|
}
|
|
59
64
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
65
|
+
type ExpoIapEventPayloads = {
|
|
66
|
+
[OpenIapEvent.PurchaseUpdated]: Purchase;
|
|
67
|
+
[OpenIapEvent.PurchaseError]: PurchaseError;
|
|
68
|
+
[OpenIapEvent.PromotedProductIOS]: Product;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
type ExpoIapEventListener<E extends OpenIapEvent> = (
|
|
72
|
+
payload: ExpoIapEventPayloads[E],
|
|
73
|
+
) => void;
|
|
74
|
+
|
|
75
|
+
type ExpoIapEmitter = {
|
|
76
|
+
addListener<E extends OpenIapEvent>(
|
|
77
|
+
eventName: E,
|
|
78
|
+
listener: ExpoIapEventListener<E>,
|
|
79
|
+
): {remove: () => void};
|
|
80
|
+
removeListener<E extends OpenIapEvent>(
|
|
81
|
+
eventName: E,
|
|
82
|
+
listener: ExpoIapEventListener<E>,
|
|
83
|
+
): void;
|
|
70
84
|
};
|
|
71
85
|
|
|
86
|
+
// Ensure the emitter has proper EventEmitter interface
|
|
87
|
+
export const emitter = (ExpoIapModule ||
|
|
88
|
+
NativeModulesProxy.ExpoIap) as ExpoIapEmitter;
|
|
89
|
+
|
|
72
90
|
/**
|
|
73
91
|
* TODO(v3.1.0): Remove legacy 'inapp' alias once downstream apps migrate to 'in-app'.
|
|
74
92
|
*/
|
|
@@ -342,15 +360,31 @@ const offerToRecordIOS = (
|
|
|
342
360
|
/**
|
|
343
361
|
* Helper to normalize request props to platform-specific format
|
|
344
362
|
*/
|
|
345
|
-
|
|
363
|
+
function normalizeRequestProps(
|
|
364
|
+
request: RequestPurchasePropsByPlatforms,
|
|
365
|
+
platform: 'ios',
|
|
366
|
+
): RequestPurchaseIosProps | null | undefined;
|
|
367
|
+
function normalizeRequestProps(
|
|
368
|
+
request: RequestPurchasePropsByPlatforms,
|
|
369
|
+
platform: 'android',
|
|
370
|
+
): RequestPurchaseAndroidProps | null | undefined;
|
|
371
|
+
function normalizeRequestProps(
|
|
372
|
+
request: RequestSubscriptionPropsByPlatforms,
|
|
373
|
+
platform: 'ios',
|
|
374
|
+
): RequestSubscriptionIosProps | null | undefined;
|
|
375
|
+
function normalizeRequestProps(
|
|
376
|
+
request: RequestSubscriptionPropsByPlatforms,
|
|
377
|
+
platform: 'android',
|
|
378
|
+
): RequestSubscriptionAndroidProps | null | undefined;
|
|
379
|
+
function normalizeRequestProps(
|
|
346
380
|
request:
|
|
347
381
|
| RequestPurchasePropsByPlatforms
|
|
348
382
|
| RequestSubscriptionPropsByPlatforms,
|
|
349
383
|
platform: 'ios' | 'android',
|
|
350
|
-
)
|
|
384
|
+
) {
|
|
351
385
|
// Platform-specific format - directly return the appropriate platform data
|
|
352
386
|
return platform === 'ios' ? request.ios : request.android;
|
|
353
|
-
}
|
|
387
|
+
}
|
|
354
388
|
|
|
355
389
|
/**
|
|
356
390
|
* Request a purchase for products or subscriptions.
|
|
@@ -408,7 +442,7 @@ export const requestPurchase = (
|
|
|
408
442
|
} = normalizedRequest;
|
|
409
443
|
|
|
410
444
|
return (async () => {
|
|
411
|
-
const offer = offerToRecordIOS(withOffer);
|
|
445
|
+
const offer = offerToRecordIOS(withOffer ?? undefined);
|
|
412
446
|
const purchase = await ExpoIapModule.requestPurchase({
|
|
413
447
|
sku,
|
|
414
448
|
andDangerouslyFinishTransactionAutomatically,
|
|
@@ -422,15 +456,19 @@ export const requestPurchase = (
|
|
|
422
456
|
}
|
|
423
457
|
|
|
424
458
|
if (Platform.OS === 'android') {
|
|
425
|
-
const normalizedRequest = normalizeRequestProps(request, 'android');
|
|
426
|
-
|
|
427
|
-
if (!normalizedRequest?.skus?.length) {
|
|
428
|
-
throw new Error(
|
|
429
|
-
'Invalid request for Android. The `skus` property is required and must be a non-empty array.',
|
|
430
|
-
);
|
|
431
|
-
}
|
|
432
|
-
|
|
433
459
|
if (isInAppPurchase) {
|
|
460
|
+
const normalizedRequest: RequestPurchaseAndroidProps | null | undefined =
|
|
461
|
+
normalizeRequestProps(
|
|
462
|
+
request as RequestPurchasePropsByPlatforms,
|
|
463
|
+
'android',
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
if (!normalizedRequest?.skus?.length) {
|
|
467
|
+
throw new Error(
|
|
468
|
+
'Invalid request for Android. The `skus` property is required and must be a non-empty array.',
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
|
|
434
472
|
const {
|
|
435
473
|
skus,
|
|
436
474
|
obfuscatedAccountIdAndroid,
|
|
@@ -453,25 +491,46 @@ export const requestPurchase = (
|
|
|
453
491
|
}
|
|
454
492
|
|
|
455
493
|
if (canonical === 'subs') {
|
|
494
|
+
const normalizedRequest:
|
|
495
|
+
| RequestSubscriptionAndroidProps
|
|
496
|
+
| null
|
|
497
|
+
| undefined = normalizeRequestProps(
|
|
498
|
+
request as RequestSubscriptionPropsByPlatforms,
|
|
499
|
+
'android',
|
|
500
|
+
);
|
|
501
|
+
|
|
502
|
+
if (!normalizedRequest?.skus?.length) {
|
|
503
|
+
throw new Error(
|
|
504
|
+
'Invalid request for Android. The `skus` property is required and must be a non-empty array.',
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
|
|
456
508
|
const {
|
|
457
509
|
skus,
|
|
458
510
|
obfuscatedAccountIdAndroid,
|
|
459
511
|
obfuscatedProfileIdAndroid,
|
|
460
512
|
isOfferPersonalized,
|
|
461
|
-
subscriptionOffers
|
|
462
|
-
replacementModeAndroid
|
|
463
|
-
|
|
513
|
+
subscriptionOffers,
|
|
514
|
+
replacementModeAndroid,
|
|
515
|
+
purchaseTokenAndroid,
|
|
464
516
|
} = normalizedRequest;
|
|
465
517
|
|
|
518
|
+
const normalizedOffers = subscriptionOffers ?? [];
|
|
519
|
+
const replacementMode = replacementModeAndroid ?? -1;
|
|
520
|
+
const purchaseToken = purchaseTokenAndroid ?? undefined;
|
|
521
|
+
|
|
466
522
|
return (async () => {
|
|
467
523
|
return ExpoIapModule.requestPurchase({
|
|
468
524
|
type: native,
|
|
469
525
|
skuArr: skus,
|
|
470
526
|
purchaseToken,
|
|
471
|
-
replacementMode
|
|
527
|
+
replacementMode,
|
|
472
528
|
obfuscatedAccountId: obfuscatedAccountIdAndroid,
|
|
473
529
|
obfuscatedProfileId: obfuscatedProfileIdAndroid,
|
|
474
|
-
offerTokenArr:
|
|
530
|
+
offerTokenArr: normalizedOffers.map(
|
|
531
|
+
(so: AndroidSubscriptionOfferInput) => so.offerToken,
|
|
532
|
+
),
|
|
533
|
+
subscriptionOffers: normalizedOffers,
|
|
475
534
|
isOfferPersonalized: isOfferPersonalized ?? false,
|
|
476
535
|
}) as Promise<Purchase[]>;
|
|
477
536
|
})();
|
|
@@ -563,8 +622,8 @@ export const getStorefrontIOS = (): Promise<string> => {
|
|
|
563
622
|
export const getStorefront = (): Promise<string> => {
|
|
564
623
|
// Cross-platform storefront
|
|
565
624
|
if (Platform.OS === 'android') {
|
|
566
|
-
if (typeof
|
|
567
|
-
return
|
|
625
|
+
if (typeof ExpoIapModule.getStorefrontAndroid === 'function') {
|
|
626
|
+
return ExpoIapModule.getStorefrontAndroid();
|
|
568
627
|
}
|
|
569
628
|
return Promise.resolve('');
|
|
570
629
|
}
|