expo-iap 3.1.35 → 3.1.36
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/CLAUDE.md +2 -3
- package/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +13 -6
- package/coverage/clover.xml +2 -2
- package/coverage/lcov-report/index.html +1 -1
- package/coverage/lcov-report/src/index.html +1 -1
- package/coverage/lcov-report/src/index.ts.html +1 -1
- package/coverage/lcov-report/src/modules/android.ts.html +1 -1
- package/coverage/lcov-report/src/modules/index.html +1 -1
- package/coverage/lcov-report/src/modules/ios.ts.html +1 -1
- package/coverage/lcov-report/src/utils/debug.ts.html +1 -1
- package/coverage/lcov-report/src/utils/errorMapping.ts.html +1 -1
- package/coverage/lcov-report/src/utils/index.html +1 -1
- package/ios/ExpoIapHelper.swift +18 -0
- package/ios/ExpoIapModule.swift +9 -8
- package/package.json +1 -1
package/CLAUDE.md
CHANGED
|
@@ -93,10 +93,9 @@ The library follows the OpenIAP type specifications with platform-specific exten
|
|
|
93
93
|
### Hook API Semantics (useIAP)
|
|
94
94
|
|
|
95
95
|
- Inside the `useIAP` hook, most methods return `Promise<void>` and update internal state. Do not design examples or implementations that expect data from these methods.
|
|
96
|
-
- Examples: `fetchProducts`, `requestPurchase`, `getAvailablePurchases`.
|
|
97
|
-
- After calling, consume state from the hook: `products`, `subscriptions`, `availablePurchases`, etc.
|
|
96
|
+
- Examples: `fetchProducts`, `requestPurchase`, `getAvailablePurchases`, `getActiveSubscriptions`.
|
|
97
|
+
- After calling, consume state from the hook: `products`, `subscriptions`, `availablePurchases`, `activeSubscriptions`, etc.
|
|
98
98
|
- Defined exceptions that DO return values in the hook:
|
|
99
|
-
- `getActiveSubscriptions(subscriptionIds?) => Promise<ActiveSubscription[]>` (also updates `activeSubscriptions` state)
|
|
100
99
|
- `hasActiveSubscriptions(subscriptionIds?) => Promise<boolean>`
|
|
101
100
|
- The root (index) API is value-returning and can be awaited to receive data directly. Use root API when not using React state.
|
|
102
101
|
|
|
@@ -307,12 +307,19 @@ class ExpoIapModule : Module() {
|
|
|
307
307
|
ExpoIapHelper.resolvePurchasePromises(purchases.map { it.toJson() })
|
|
308
308
|
} catch (e: Exception) {
|
|
309
309
|
ExpoIapLog.failure("requestPurchaseAndroid", e)
|
|
310
|
+
// Try to use toJSON() if available (OpenIAP PurchaseError), otherwise create a generic error map
|
|
310
311
|
val errorMap =
|
|
311
|
-
|
|
312
|
-
"
|
|
313
|
-
"
|
|
314
|
-
|
|
315
|
-
|
|
312
|
+
runCatching {
|
|
313
|
+
@Suppress("UNCHECKED_CAST")
|
|
314
|
+
e.javaClass.getMethod("toJSON").invoke(e) as Map<String, Any?>
|
|
315
|
+
}.getOrElse {
|
|
316
|
+
mapOf(
|
|
317
|
+
"code" to OpenIapError.PurchaseFailed.CODE,
|
|
318
|
+
"message" to (e.message ?: "Purchase failed"),
|
|
319
|
+
"platform" to "android",
|
|
320
|
+
)
|
|
321
|
+
}
|
|
322
|
+
val errorCode = errorMap["code"] as? String ?: OpenIapError.PurchaseFailed.CODE
|
|
316
323
|
runCatching {
|
|
317
324
|
ExpoIapHelper.emitOrQueue(
|
|
318
325
|
this@ExpoIapModule,
|
|
@@ -330,7 +337,7 @@ class ExpoIapModule : Module() {
|
|
|
330
337
|
)
|
|
331
338
|
}
|
|
332
339
|
ExpoIapHelper.rejectPurchasePromises(
|
|
333
|
-
|
|
340
|
+
errorCode,
|
|
334
341
|
e.message,
|
|
335
342
|
null,
|
|
336
343
|
)
|
package/coverage/clover.xml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<coverage generated="
|
|
3
|
-
<project timestamp="
|
|
2
|
+
<coverage generated="1764088678733" clover="3.2.0">
|
|
3
|
+
<project timestamp="1764088678733" name="All files">
|
|
4
4
|
<metrics statements="457" coveredstatements="429" conditionals="251" coveredconditionals="217" methods="95" coveredmethods="75" elements="803" coveredelements="721" complexity="0" loc="457" ncloc="457" packages="3" files="5" classes="5"/>
|
|
5
5
|
<package name="src">
|
|
6
6
|
<metrics statements="196" coveredstatements="190" conditionals="99" coveredconditionals="89" methods="41" coveredmethods="32"/>
|
|
@@ -131,7 +131,7 @@
|
|
|
131
131
|
<div class='footer quiet pad2 space-top1 center small'>
|
|
132
132
|
Code coverage generated by
|
|
133
133
|
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
134
|
-
at 2025-11-
|
|
134
|
+
at 2025-11-25T16:37:58.715Z
|
|
135
135
|
</div>
|
|
136
136
|
<script src="prettify.js"></script>
|
|
137
137
|
<script>
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
<div class='footer quiet pad2 space-top1 center small'>
|
|
102
102
|
Code coverage generated by
|
|
103
103
|
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
104
|
-
at 2025-11-
|
|
104
|
+
at 2025-11-25T16:37:58.715Z
|
|
105
105
|
</div>
|
|
106
106
|
<script src="../prettify.js"></script>
|
|
107
107
|
<script>
|
|
@@ -2335,7 +2335,7 @@ export {<span class="fstat-no" title="function not covered" >ExpoIapConsole}</sp
|
|
|
2335
2335
|
<div class='footer quiet pad2 space-top1 center small'>
|
|
2336
2336
|
Code coverage generated by
|
|
2337
2337
|
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
2338
|
-
at 2025-11-
|
|
2338
|
+
at 2025-11-25T16:37:58.715Z
|
|
2339
2339
|
</div>
|
|
2340
2340
|
<script src="../prettify.js"></script>
|
|
2341
2341
|
<script>
|
|
@@ -814,7 +814,7 @@ export const createAlternativeBillingTokenAndroid: MutationField<
|
|
|
814
814
|
<div class='footer quiet pad2 space-top1 center small'>
|
|
815
815
|
Code coverage generated by
|
|
816
816
|
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
817
|
-
at 2025-11-
|
|
817
|
+
at 2025-11-25T16:37:58.715Z
|
|
818
818
|
</div>
|
|
819
819
|
<script src="../../prettify.js"></script>
|
|
820
820
|
<script>
|
|
@@ -116,7 +116,7 @@
|
|
|
116
116
|
<div class='footer quiet pad2 space-top1 center small'>
|
|
117
117
|
Code coverage generated by
|
|
118
118
|
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
119
|
-
at 2025-11-
|
|
119
|
+
at 2025-11-25T16:37:58.715Z
|
|
120
120
|
</div>
|
|
121
121
|
<script src="../../prettify.js"></script>
|
|
122
122
|
<script>
|
|
@@ -1267,7 +1267,7 @@ export const presentExternalPurchaseLinkIOS: MutationField<
|
|
|
1267
1267
|
<div class='footer quiet pad2 space-top1 center small'>
|
|
1268
1268
|
Code coverage generated by
|
|
1269
1269
|
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
1270
|
-
at 2025-11-
|
|
1270
|
+
at 2025-11-25T16:37:58.715Z
|
|
1271
1271
|
</div>
|
|
1272
1272
|
<script src="../../prettify.js"></script>
|
|
1273
1273
|
<script>
|
|
@@ -268,7 +268,7 @@ export const ExpoIapConsole = createConsole();
|
|
|
268
268
|
<div class='footer quiet pad2 space-top1 center small'>
|
|
269
269
|
Code coverage generated by
|
|
270
270
|
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
271
|
-
at 2025-11-
|
|
271
|
+
at 2025-11-25T16:37:58.715Z
|
|
272
272
|
</div>
|
|
273
273
|
<script src="../../prettify.js"></script>
|
|
274
274
|
<script>
|
|
@@ -1111,7 +1111,7 @@ export function getUserFriendlyErrorMessage(error: ErrorLike): string {
|
|
|
1111
1111
|
<div class='footer quiet pad2 space-top1 center small'>
|
|
1112
1112
|
Code coverage generated by
|
|
1113
1113
|
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
1114
|
-
at 2025-11-
|
|
1114
|
+
at 2025-11-25T16:37:58.715Z
|
|
1115
1115
|
</div>
|
|
1116
1116
|
<script src="../../prettify.js"></script>
|
|
1117
1117
|
<script>
|
|
@@ -116,7 +116,7 @@
|
|
|
116
116
|
<div class='footer quiet pad2 space-top1 center small'>
|
|
117
117
|
Code coverage generated by
|
|
118
118
|
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
119
|
-
at 2025-11-
|
|
119
|
+
at 2025-11-25T16:37:58.715Z
|
|
120
120
|
</div>
|
|
121
121
|
<script src="../../prettify.js"></script>
|
|
122
122
|
<script>
|
package/ios/ExpoIapHelper.swift
CHANGED
|
@@ -1,6 +1,24 @@
|
|
|
1
|
+
import ExpoModulesCore
|
|
1
2
|
import Foundation
|
|
2
3
|
import OpenIAP
|
|
3
4
|
|
|
5
|
+
/// Exception wrapper for PurchaseError that preserves OpenIAP error codes
|
|
6
|
+
/// This ensures consistent error format between try-catch and onPurchaseError callback
|
|
7
|
+
class IapException: GenericException<(code: String, message: String, productId: String?)> {
|
|
8
|
+
override var code: String { param.code }
|
|
9
|
+
override var reason: String { param.message }
|
|
10
|
+
|
|
11
|
+
var productId: String? { param.productId }
|
|
12
|
+
|
|
13
|
+
static func from(_ error: PurchaseError) -> IapException {
|
|
14
|
+
let payload = OpenIapSerialization.encode(error)
|
|
15
|
+
let code = payload["code"] as? String ?? "unknown"
|
|
16
|
+
let message = payload["message"] as? String ?? error.localizedDescription
|
|
17
|
+
let productId = payload["productId"] as? String
|
|
18
|
+
return IapException((code: code, message: message, productId: productId))
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
4
22
|
enum ExpoIapHelper {
|
|
5
23
|
private static var listeners: [Subscription] = []
|
|
6
24
|
|
package/ios/ExpoIapModule.swift
CHANGED
|
@@ -86,10 +86,11 @@ public final class ExpoIapModule: Module {
|
|
|
86
86
|
}
|
|
87
87
|
} catch let error as PurchaseError {
|
|
88
88
|
ExpoIapLog.failure("requestPurchase", error: error)
|
|
89
|
-
throw error
|
|
89
|
+
throw IapException.from(error)
|
|
90
90
|
} catch {
|
|
91
91
|
ExpoIapLog.failure("requestPurchase", error: error)
|
|
92
|
-
throw
|
|
92
|
+
throw IapException.from(
|
|
93
|
+
PurchaseError.make(code: .purchaseError, message: error.localizedDescription))
|
|
93
94
|
}
|
|
94
95
|
}
|
|
95
96
|
|
|
@@ -198,10 +199,10 @@ public final class ExpoIapModule: Module {
|
|
|
198
199
|
return sanitized
|
|
199
200
|
} catch let error as PurchaseError {
|
|
200
201
|
ExpoIapLog.failure("validateReceiptIOS", error: error)
|
|
201
|
-
throw error
|
|
202
|
+
throw IapException.from(error)
|
|
202
203
|
} catch {
|
|
203
204
|
ExpoIapLog.failure("validateReceiptIOS", error: error)
|
|
204
|
-
throw PurchaseError.make(code: .receiptFailed)
|
|
205
|
+
throw IapException.from(PurchaseError.make(code: .receiptFailed))
|
|
205
206
|
}
|
|
206
207
|
}
|
|
207
208
|
|
|
@@ -312,10 +313,10 @@ public final class ExpoIapModule: Module {
|
|
|
312
313
|
return nil
|
|
313
314
|
} catch let error as PurchaseError {
|
|
314
315
|
ExpoIapLog.failure("currentEntitlementIOS", error: error)
|
|
315
|
-
throw error
|
|
316
|
+
throw IapException.from(error)
|
|
316
317
|
} catch {
|
|
317
318
|
ExpoIapLog.failure("currentEntitlementIOS", error: error)
|
|
318
|
-
throw PurchaseError.make(code: .skuNotFound, productId: sku)
|
|
319
|
+
throw IapException.from(PurchaseError.make(code: .skuNotFound, productId: sku))
|
|
319
320
|
}
|
|
320
321
|
}
|
|
321
322
|
|
|
@@ -332,10 +333,10 @@ public final class ExpoIapModule: Module {
|
|
|
332
333
|
return nil
|
|
333
334
|
} catch let error as PurchaseError {
|
|
334
335
|
ExpoIapLog.failure("latestTransactionIOS", error: error)
|
|
335
|
-
throw error
|
|
336
|
+
throw IapException.from(error)
|
|
336
337
|
} catch {
|
|
337
338
|
ExpoIapLog.failure("latestTransactionIOS", error: error)
|
|
338
|
-
throw PurchaseError.make(code: .skuNotFound, productId: sku)
|
|
339
|
+
throw IapException.from(PurchaseError.make(code: .skuNotFound, productId: sku))
|
|
339
340
|
}
|
|
340
341
|
}
|
|
341
342
|
|