expo-iap 3.1.1 → 3.1.3
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/android/src/main/java/expo/modules/iap/ExpoIapHelper.kt +171 -0
- package/android/src/main/java/expo/modules/iap/ExpoIapLog.kt +4 -0
- package/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +50 -114
- package/build/types.d.ts +34 -34
- package/build/types.js +34 -34
- package/build/types.js.map +1 -1
- package/build/utils/errorMapping.d.ts +7 -8
- package/build/utils/errorMapping.d.ts.map +1 -1
- package/build/utils/errorMapping.js +83 -53
- package/build/utils/errorMapping.js.map +1 -1
- package/coverage/clover.xml +506 -0
- package/coverage/coverage-final.json +6 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +161 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +196 -0
- package/coverage/lcov-report/src/helpers/index.html +116 -0
- package/coverage/lcov-report/src/helpers/subscription.ts.html +496 -0
- package/coverage/lcov-report/src/index.html +116 -0
- package/coverage/lcov-report/src/index.ts.html +1993 -0
- package/coverage/lcov-report/src/modules/android.ts.html +550 -0
- package/coverage/lcov-report/src/modules/index.html +131 -0
- package/coverage/lcov-report/src/modules/ios.ts.html +1222 -0
- package/coverage/lcov-report/src/utils/errorMapping.ts.html +1126 -0
- package/coverage/lcov-report/src/utils/index.html +116 -0
- package/coverage/lcov.info +959 -0
- package/ios/ExpoIapHelper.swift +101 -6
- package/ios/ExpoIapModule.swift +29 -97
- package/jest.config.js +0 -1
- package/openiap-versions.json +2 -2
- package/package.json +1 -1
- package/src/types.ts +34 -34
- package/src/utils/errorMapping.ts +101 -82
- package/build/utils/constants.d.ts +0 -6
- package/build/utils/constants.d.ts.map +0 -1
- package/build/utils/constants.js +0 -19
- package/build/utils/constants.js.map +0 -1
- package/src/utils/constants.ts +0 -23
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
package expo.modules.iap
|
|
2
|
+
|
|
3
|
+
import android.util.Log
|
|
4
|
+
import dev.hyo.openiap.AndroidSubscriptionOfferInput
|
|
5
|
+
import dev.hyo.openiap.OpenIapModule
|
|
6
|
+
import dev.hyo.openiap.ProductQueryType
|
|
7
|
+
import expo.modules.kotlin.Promise
|
|
8
|
+
import expo.modules.kotlin.modules.Module
|
|
9
|
+
import kotlinx.coroutines.CoroutineScope
|
|
10
|
+
import kotlinx.coroutines.launch
|
|
11
|
+
import java.util.Locale
|
|
12
|
+
import java.util.concurrent.ConcurrentLinkedQueue
|
|
13
|
+
|
|
14
|
+
object ExpoIapHelper {
|
|
15
|
+
private const val TAG = "ExpoIapHelper"
|
|
16
|
+
private const val MAX_BUFFERED_EVENTS = 200
|
|
17
|
+
|
|
18
|
+
fun emitOrQueue(
|
|
19
|
+
module: Module,
|
|
20
|
+
scope: CoroutineScope,
|
|
21
|
+
connectionReady: java.util.concurrent.atomic.AtomicBoolean,
|
|
22
|
+
pendingEvents: ConcurrentLinkedQueue<Pair<String, Map<String, Any?>>>,
|
|
23
|
+
name: String,
|
|
24
|
+
payload: Map<String, Any?>,
|
|
25
|
+
) {
|
|
26
|
+
if (connectionReady.get()) {
|
|
27
|
+
// Ensure event emission occurs on the main dispatcher
|
|
28
|
+
scope.launch { module.sendEvent(name, payload) }
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
// Bound the buffer to prevent unbounded growth if init stalls
|
|
32
|
+
if (pendingEvents.size >= MAX_BUFFERED_EVENTS) {
|
|
33
|
+
pendingEvents.poll()
|
|
34
|
+
ExpoIapLog.warning("pendingEvents overflow; dropping oldest")
|
|
35
|
+
}
|
|
36
|
+
pendingEvents.add(name to payload)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
fun parseProductQueryType(rawType: String?): ProductQueryType {
|
|
40
|
+
val normalized =
|
|
41
|
+
rawType
|
|
42
|
+
?.trim()
|
|
43
|
+
?.lowercase(Locale.US)
|
|
44
|
+
?.replace("-", "")
|
|
45
|
+
?.replace("_", "")
|
|
46
|
+
|
|
47
|
+
return when (normalized) {
|
|
48
|
+
"subs" -> ProductQueryType.Subs
|
|
49
|
+
"all" -> ProductQueryType.All
|
|
50
|
+
else -> ProductQueryType.InApp
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
fun parseRequestPurchaseParams(params: Map<String, Any?>): RequestPurchaseParams {
|
|
55
|
+
val type = params["type"] as? String
|
|
56
|
+
val skus: List<String> =
|
|
57
|
+
(params["skus"] as? List<*>)?.filterIsInstance<String>()
|
|
58
|
+
?: (params["skuArr"] as? List<*>)?.filterIsInstance<String>()
|
|
59
|
+
?: emptyList()
|
|
60
|
+
val obfuscatedAccountId =
|
|
61
|
+
(params["obfuscatedAccountIdAndroid"] ?: params["obfuscatedAccountId"]) as? String
|
|
62
|
+
val obfuscatedProfileId =
|
|
63
|
+
(params["obfuscatedProfileIdAndroid"] ?: params["obfuscatedProfileId"]) as? String
|
|
64
|
+
val isOfferPersonalized = params["isOfferPersonalized"] as? Boolean ?: false
|
|
65
|
+
val offerTokenArr =
|
|
66
|
+
(params["offerTokenArr"] as? List<*>)?.filterIsInstance<String>() ?: emptyList()
|
|
67
|
+
val explicitSubscriptionOffers =
|
|
68
|
+
(params["subscriptionOffers"] as? List<*>)?.mapNotNull { rawOffer ->
|
|
69
|
+
val offerMap = rawOffer as? Map<*, *> ?: return@mapNotNull null
|
|
70
|
+
val sku = offerMap["sku"] as? String
|
|
71
|
+
val offerToken = offerMap["offerToken"] as? String
|
|
72
|
+
if (sku.isNullOrEmpty() || offerToken.isNullOrEmpty()) {
|
|
73
|
+
null
|
|
74
|
+
} else {
|
|
75
|
+
AndroidSubscriptionOfferInput(offerToken = offerToken, sku = sku)
|
|
76
|
+
}
|
|
77
|
+
} ?: emptyList()
|
|
78
|
+
val purchaseToken =
|
|
79
|
+
(params["purchaseTokenAndroid"] ?: params["purchaseToken"]) as? String
|
|
80
|
+
val replacementMode =
|
|
81
|
+
(params["replacementModeAndroid"] ?: params["replacementMode"]) as? Number
|
|
82
|
+
|
|
83
|
+
return RequestPurchaseParams(
|
|
84
|
+
type = type,
|
|
85
|
+
skus = skus,
|
|
86
|
+
obfuscatedAccountId = obfuscatedAccountId,
|
|
87
|
+
obfuscatedProfileId = obfuscatedProfileId,
|
|
88
|
+
isOfferPersonalized = isOfferPersonalized,
|
|
89
|
+
offerTokenArr = offerTokenArr,
|
|
90
|
+
explicitSubscriptionOffers = explicitSubscriptionOffers,
|
|
91
|
+
purchaseToken = purchaseToken,
|
|
92
|
+
replacementMode = replacementMode,
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
data class RequestPurchaseParams(
|
|
97
|
+
val type: String?,
|
|
98
|
+
val skus: List<String>,
|
|
99
|
+
val obfuscatedAccountId: String?,
|
|
100
|
+
val obfuscatedProfileId: String?,
|
|
101
|
+
val isOfferPersonalized: Boolean,
|
|
102
|
+
val offerTokenArr: List<String>,
|
|
103
|
+
val explicitSubscriptionOffers: List<AndroidSubscriptionOfferInput>,
|
|
104
|
+
val purchaseToken: String?,
|
|
105
|
+
val replacementMode: Number?,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
fun addPurchasePromise(promise: Promise) {
|
|
109
|
+
PromiseUtils.addPromiseForKey(PromiseUtils.PROMISE_BUY_ITEM, promise)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
fun resolvePurchasePromises(purchases: List<Map<String, Any?>>) {
|
|
113
|
+
PromiseUtils.resolvePromisesForKey(
|
|
114
|
+
PromiseUtils.PROMISE_BUY_ITEM,
|
|
115
|
+
purchases,
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
fun rejectPurchasePromises(
|
|
120
|
+
code: String,
|
|
121
|
+
message: String?,
|
|
122
|
+
error: Exception?,
|
|
123
|
+
) {
|
|
124
|
+
PromiseUtils.rejectPromisesForKey(
|
|
125
|
+
PromiseUtils.PROMISE_BUY_ITEM,
|
|
126
|
+
code,
|
|
127
|
+
message,
|
|
128
|
+
error,
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
fun setupListeners(
|
|
133
|
+
openIap: OpenIapModule,
|
|
134
|
+
module: Module,
|
|
135
|
+
scope: CoroutineScope,
|
|
136
|
+
connectionReady: java.util.concurrent.atomic.AtomicBoolean,
|
|
137
|
+
pendingEvents: ConcurrentLinkedQueue<Pair<String, Map<String, Any?>>>,
|
|
138
|
+
eventPurchaseUpdated: String,
|
|
139
|
+
eventPurchaseError: String,
|
|
140
|
+
) {
|
|
141
|
+
openIap.addPurchaseUpdateListener { p ->
|
|
142
|
+
runCatching {
|
|
143
|
+
emitOrQueue(
|
|
144
|
+
module,
|
|
145
|
+
scope,
|
|
146
|
+
connectionReady,
|
|
147
|
+
pendingEvents,
|
|
148
|
+
eventPurchaseUpdated,
|
|
149
|
+
p.toJson(),
|
|
150
|
+
)
|
|
151
|
+
}.onFailure { android.util.Log.e(TAG, "Failed to buffer/send PURCHASE_UPDATED", it) }
|
|
152
|
+
}
|
|
153
|
+
openIap.addPurchaseErrorListener { e ->
|
|
154
|
+
runCatching {
|
|
155
|
+
emitOrQueue(
|
|
156
|
+
module,
|
|
157
|
+
scope,
|
|
158
|
+
connectionReady,
|
|
159
|
+
pendingEvents,
|
|
160
|
+
eventPurchaseError,
|
|
161
|
+
e.toJSON(),
|
|
162
|
+
)
|
|
163
|
+
}.onFailure { android.util.Log.e(TAG, "Failed to buffer/send PURCHASE_ERROR", it) }
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
fun cleanupListeners(openIap: OpenIapModule) {
|
|
168
|
+
// Android doesn't have explicit listeners to clean up
|
|
169
|
+
// This function is kept for API compatibility with iOS
|
|
170
|
+
}
|
|
171
|
+
}
|
|
@@ -28,7 +28,6 @@ import kotlinx.coroutines.Job
|
|
|
28
28
|
import kotlinx.coroutines.launch
|
|
29
29
|
import kotlinx.coroutines.sync.Mutex
|
|
30
30
|
import kotlinx.coroutines.sync.withLock
|
|
31
|
-
import java.util.Locale
|
|
32
31
|
import java.util.concurrent.ConcurrentLinkedQueue
|
|
33
32
|
import java.util.concurrent.atomic.AtomicBoolean
|
|
34
33
|
|
|
@@ -53,38 +52,6 @@ class ExpoIapModule : Module() {
|
|
|
53
52
|
private val connectionReady = AtomicBoolean(false)
|
|
54
53
|
private val connectionMutex = Mutex()
|
|
55
54
|
|
|
56
|
-
private fun emitOrQueue(
|
|
57
|
-
name: String,
|
|
58
|
-
payload: Map<String, Any?>,
|
|
59
|
-
) {
|
|
60
|
-
if (connectionReady.get()) {
|
|
61
|
-
// Ensure event emission occurs on the main dispatcher
|
|
62
|
-
scope.launch { sendEvent(name, payload) }
|
|
63
|
-
return
|
|
64
|
-
}
|
|
65
|
-
// Bound the buffer to prevent unbounded growth if init stalls
|
|
66
|
-
if (pendingEvents.size >= MAX_BUFFERED_EVENTS) {
|
|
67
|
-
pendingEvents.poll()
|
|
68
|
-
Log.w(TAG, "pendingEvents overflow; dropping oldest")
|
|
69
|
-
}
|
|
70
|
-
pendingEvents.add(name to payload)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
private fun parseProductQueryType(rawType: String?): ProductQueryType {
|
|
74
|
-
val normalized =
|
|
75
|
-
rawType
|
|
76
|
-
?.trim()
|
|
77
|
-
?.lowercase(Locale.US)
|
|
78
|
-
?.replace("-", "")
|
|
79
|
-
?.replace("_", "")
|
|
80
|
-
|
|
81
|
-
return when (normalized) {
|
|
82
|
-
"subs" -> ProductQueryType.Subs
|
|
83
|
-
"all" -> ProductQueryType.All
|
|
84
|
-
else -> ProductQueryType.InApp
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
55
|
override fun definition() =
|
|
89
56
|
ModuleDefinition {
|
|
90
57
|
Name("ExpoIap")
|
|
@@ -101,7 +68,6 @@ class ExpoIapModule : Module() {
|
|
|
101
68
|
connectionMutex.withLock {
|
|
102
69
|
try {
|
|
103
70
|
// Activity may be unavailable in headless/background scenarios.
|
|
104
|
-
// Attempt to set it, but do not fail init if missing.
|
|
105
71
|
runCatching { openIap.setActivity(currentActivity) }
|
|
106
72
|
.onFailure { Log.w(TAG, "initConnection: Activity missing; proceeding headless", it) }
|
|
107
73
|
|
|
@@ -115,15 +81,15 @@ class ExpoIapModule : Module() {
|
|
|
115
81
|
// Attach listeners early to avoid races during init
|
|
116
82
|
if (!listenersAttached) {
|
|
117
83
|
listenersAttached = true
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
84
|
+
ExpoIapHelper.setupListeners(
|
|
85
|
+
openIap,
|
|
86
|
+
this@ExpoIapModule,
|
|
87
|
+
scope,
|
|
88
|
+
connectionReady,
|
|
89
|
+
pendingEvents,
|
|
90
|
+
EVENT_PURCHASE_UPDATED,
|
|
91
|
+
EVENT_PURCHASE_ERROR,
|
|
92
|
+
)
|
|
127
93
|
}
|
|
128
94
|
|
|
129
95
|
val ok = openIap.initConnection()
|
|
@@ -131,10 +97,7 @@ class ExpoIapModule : Module() {
|
|
|
131
97
|
if (!ok) {
|
|
132
98
|
// Clear any buffered events from a failed init
|
|
133
99
|
pendingEvents.clear()
|
|
134
|
-
ExpoIapLog.failure(
|
|
135
|
-
"initConnection",
|
|
136
|
-
IllegalStateException("Failed to initialize connection"),
|
|
137
|
-
)
|
|
100
|
+
ExpoIapLog.failure("initConnection", IllegalStateException("Failed to initialize connection"))
|
|
138
101
|
promise.reject(OpenIapError.InitConnection.CODE, "Failed to initialize connection", null)
|
|
139
102
|
return@withLock
|
|
140
103
|
}
|
|
@@ -163,9 +126,11 @@ class ExpoIapModule : Module() {
|
|
|
163
126
|
scope.launch {
|
|
164
127
|
connectionMutex.withLock {
|
|
165
128
|
runCatching { openIap.endConnection() }
|
|
129
|
+
ExpoIapHelper.cleanupListeners(openIap)
|
|
166
130
|
// Reset connection state and clear any buffered events
|
|
167
131
|
connectionReady.set(false)
|
|
168
132
|
pendingEvents.clear()
|
|
133
|
+
listenersAttached = false
|
|
169
134
|
ExpoIapLog.result("endConnection", true)
|
|
170
135
|
promise.resolve(true)
|
|
171
136
|
}
|
|
@@ -179,7 +144,7 @@ class ExpoIapModule : Module() {
|
|
|
179
144
|
)
|
|
180
145
|
scope.launch {
|
|
181
146
|
try {
|
|
182
|
-
val queryType = parseProductQueryType(type)
|
|
147
|
+
val queryType = ExpoIapHelper.parseProductQueryType(type)
|
|
183
148
|
val request = ProductRequest(skuArr.toList(), queryType)
|
|
184
149
|
val result = openIap.fetchProducts(request)
|
|
185
150
|
val payload =
|
|
@@ -216,18 +181,10 @@ class ExpoIapModule : Module() {
|
|
|
216
181
|
AsyncFunction("deepLinkToSubscriptionsAndroid") { params: Map<String, Any?>, promise: Promise ->
|
|
217
182
|
val sku = (params["sku"] ?: params["skuAndroid"]) as? String
|
|
218
183
|
val packageName = (params["packageName"] ?: params["packageNameAndroid"]) as? String
|
|
219
|
-
ExpoIapLog.payload(
|
|
220
|
-
"deepLinkToSubscriptionsAndroid",
|
|
221
|
-
mapOf("sku" to sku, "packageName" to packageName),
|
|
222
|
-
)
|
|
184
|
+
ExpoIapLog.payload("deepLinkToSubscriptionsAndroid", mapOf("sku" to sku, "packageName" to packageName))
|
|
223
185
|
scope.launch {
|
|
224
186
|
try {
|
|
225
|
-
openIap.deepLinkToSubscriptions(
|
|
226
|
-
DeepLinkOptions(
|
|
227
|
-
packageNameAndroid = packageName,
|
|
228
|
-
skuAndroid = sku,
|
|
229
|
-
),
|
|
230
|
-
)
|
|
187
|
+
openIap.deepLinkToSubscriptions(DeepLinkOptions(packageNameAndroid = packageName, skuAndroid = sku))
|
|
231
188
|
ExpoIapLog.result("deepLinkToSubscriptionsAndroid", true)
|
|
232
189
|
promise.resolve(null)
|
|
233
190
|
} catch (e: Exception) {
|
|
@@ -254,43 +211,17 @@ class ExpoIapModule : Module() {
|
|
|
254
211
|
|
|
255
212
|
AsyncFunction("requestPurchase") { params: Map<String, Any?>, promise: Promise ->
|
|
256
213
|
ExpoIapLog.payload("requestPurchaseAndroid", params)
|
|
257
|
-
val
|
|
258
|
-
val skus: List<String> =
|
|
259
|
-
(params["skus"] as? List<*>)?.filterIsInstance<String>()
|
|
260
|
-
?: (params["skuArr"] as? List<*>)?.filterIsInstance<String>()
|
|
261
|
-
?: emptyList()
|
|
262
|
-
val obfuscatedAccountId =
|
|
263
|
-
(params["obfuscatedAccountIdAndroid"] ?: params["obfuscatedAccountId"]) as? String
|
|
264
|
-
val obfuscatedProfileId =
|
|
265
|
-
(params["obfuscatedProfileIdAndroid"] ?: params["obfuscatedProfileId"]) as? String
|
|
266
|
-
val isOfferPersonalized = params["isOfferPersonalized"] as? Boolean ?: false
|
|
267
|
-
val offerTokenArr =
|
|
268
|
-
(params["offerTokenArr"] as? List<*>)?.filterIsInstance<String>() ?: emptyList()
|
|
269
|
-
val explicitSubscriptionOffers =
|
|
270
|
-
(params["subscriptionOffers"] as? List<*>)?.mapNotNull { rawOffer ->
|
|
271
|
-
val offerMap = rawOffer as? Map<*, *> ?: return@mapNotNull null
|
|
272
|
-
val sku = offerMap["sku"] as? String
|
|
273
|
-
val offerToken = offerMap["offerToken"] as? String
|
|
274
|
-
if (sku.isNullOrEmpty() || offerToken.isNullOrEmpty()) {
|
|
275
|
-
null
|
|
276
|
-
} else {
|
|
277
|
-
AndroidSubscriptionOfferInput(offerToken = offerToken, sku = sku)
|
|
278
|
-
}
|
|
279
|
-
} ?: emptyList()
|
|
280
|
-
val purchaseToken =
|
|
281
|
-
(params["purchaseTokenAndroid"] ?: params["purchaseToken"]) as? String
|
|
282
|
-
val replacementMode =
|
|
283
|
-
(params["replacementModeAndroid"] ?: params["replacementMode"]) as? Number
|
|
214
|
+
val parsedParams = ExpoIapHelper.parseRequestPurchaseParams(params)
|
|
284
215
|
|
|
285
216
|
val productType =
|
|
286
|
-
when (parseProductQueryType(type)) {
|
|
217
|
+
when (ExpoIapHelper.parseProductQueryType(parsedParams.type)) {
|
|
287
218
|
ProductQueryType.Subs -> ProductQueryType.Subs
|
|
288
219
|
else -> ProductQueryType.InApp
|
|
289
220
|
}
|
|
290
221
|
|
|
291
222
|
val fallbackOffers =
|
|
292
|
-
if (explicitSubscriptionOffers.isEmpty() && offerTokenArr.isNotEmpty()) {
|
|
293
|
-
skus.zip(offerTokenArr).mapNotNull { (sku, token) ->
|
|
223
|
+
if (parsedParams.explicitSubscriptionOffers.isEmpty() && parsedParams.offerTokenArr.isNotEmpty()) {
|
|
224
|
+
parsedParams.skus.zip(parsedParams.offerTokenArr).mapNotNull { (sku, token) ->
|
|
294
225
|
if (token.isNotEmpty()) {
|
|
295
226
|
AndroidSubscriptionOfferInput(offerToken = token, sku = sku)
|
|
296
227
|
} else {
|
|
@@ -302,7 +233,7 @@ class ExpoIapModule : Module() {
|
|
|
302
233
|
}
|
|
303
234
|
|
|
304
235
|
val subscriptionOffers =
|
|
305
|
-
(explicitSubscriptionOffers.ifEmpty { fallbackOffers })
|
|
236
|
+
(parsedParams.explicitSubscriptionOffers.ifEmpty { fallbackOffers })
|
|
306
237
|
.takeIf { it.isNotEmpty() }
|
|
307
238
|
|
|
308
239
|
val requestProps =
|
|
@@ -310,12 +241,12 @@ class ExpoIapModule : Module() {
|
|
|
310
241
|
ProductQueryType.Subs -> {
|
|
311
242
|
val android =
|
|
312
243
|
RequestSubscriptionAndroidProps(
|
|
313
|
-
isOfferPersonalized = isOfferPersonalized,
|
|
314
|
-
obfuscatedAccountIdAndroid = obfuscatedAccountId,
|
|
315
|
-
obfuscatedProfileIdAndroid = obfuscatedProfileId,
|
|
316
|
-
purchaseTokenAndroid = purchaseToken,
|
|
317
|
-
replacementModeAndroid = replacementMode?.toInt(),
|
|
318
|
-
skus = skus,
|
|
244
|
+
isOfferPersonalized = parsedParams.isOfferPersonalized,
|
|
245
|
+
obfuscatedAccountIdAndroid = parsedParams.obfuscatedAccountId,
|
|
246
|
+
obfuscatedProfileIdAndroid = parsedParams.obfuscatedProfileId,
|
|
247
|
+
purchaseTokenAndroid = parsedParams.purchaseToken,
|
|
248
|
+
replacementModeAndroid = parsedParams.replacementMode?.toInt(),
|
|
249
|
+
skus = parsedParams.skus,
|
|
319
250
|
subscriptionOffers = subscriptionOffers,
|
|
320
251
|
)
|
|
321
252
|
RequestPurchaseProps(
|
|
@@ -330,10 +261,10 @@ class ExpoIapModule : Module() {
|
|
|
330
261
|
else -> {
|
|
331
262
|
val android =
|
|
332
263
|
RequestPurchaseAndroidProps(
|
|
333
|
-
isOfferPersonalized = isOfferPersonalized,
|
|
334
|
-
obfuscatedAccountIdAndroid = obfuscatedAccountId,
|
|
335
|
-
obfuscatedProfileIdAndroid = obfuscatedProfileId,
|
|
336
|
-
skus = skus,
|
|
264
|
+
isOfferPersonalized = parsedParams.isOfferPersonalized,
|
|
265
|
+
obfuscatedAccountIdAndroid = parsedParams.obfuscatedAccountId,
|
|
266
|
+
obfuscatedProfileIdAndroid = parsedParams.obfuscatedProfileId,
|
|
267
|
+
skus = parsedParams.skus,
|
|
337
268
|
)
|
|
338
269
|
RequestPurchaseProps(
|
|
339
270
|
request =
|
|
@@ -345,7 +276,7 @@ class ExpoIapModule : Module() {
|
|
|
345
276
|
}
|
|
346
277
|
}
|
|
347
278
|
|
|
348
|
-
|
|
279
|
+
ExpoIapHelper.addPurchasePromise(promise)
|
|
349
280
|
scope.launch {
|
|
350
281
|
try {
|
|
351
282
|
openIap.setActivity(currentActivity)
|
|
@@ -360,10 +291,7 @@ class ExpoIapModule : Module() {
|
|
|
360
291
|
"requestPurchaseAndroid",
|
|
361
292
|
purchases.map { it.toJson() },
|
|
362
293
|
)
|
|
363
|
-
|
|
364
|
-
PromiseUtils.PROMISE_BUY_ITEM,
|
|
365
|
-
purchases.map { it.toJson() },
|
|
366
|
-
)
|
|
294
|
+
ExpoIapHelper.resolvePurchasePromises(purchases.map { it.toJson() })
|
|
367
295
|
} catch (e: Exception) {
|
|
368
296
|
ExpoIapLog.failure("requestPurchaseAndroid", e)
|
|
369
297
|
val errorMap =
|
|
@@ -372,16 +300,23 @@ class ExpoIapModule : Module() {
|
|
|
372
300
|
"message" to (e.message ?: "Purchase failed"),
|
|
373
301
|
"platform" to "android",
|
|
374
302
|
)
|
|
375
|
-
runCatching {
|
|
376
|
-
.
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
303
|
+
runCatching {
|
|
304
|
+
ExpoIapHelper.emitOrQueue(
|
|
305
|
+
this@ExpoIapModule,
|
|
306
|
+
scope,
|
|
307
|
+
connectionReady,
|
|
308
|
+
pendingEvents,
|
|
309
|
+
EVENT_PURCHASE_ERROR,
|
|
310
|
+
errorMap,
|
|
311
|
+
)
|
|
312
|
+
}.onFailure { ex ->
|
|
313
|
+
Log.e(
|
|
314
|
+
TAG,
|
|
315
|
+
"Failed to send PURCHASE_ERROR event (requestPurchase)",
|
|
316
|
+
ex,
|
|
317
|
+
)
|
|
318
|
+
}
|
|
319
|
+
ExpoIapHelper.rejectPurchasePromises(
|
|
385
320
|
OpenIapError.PurchaseFailed.CODE,
|
|
386
321
|
e.message,
|
|
387
322
|
null,
|
|
@@ -422,6 +357,7 @@ class ExpoIapModule : Module() {
|
|
|
422
357
|
}
|
|
423
358
|
|
|
424
359
|
OnDestroy {
|
|
360
|
+
ExpoIapHelper.cleanupListeners(openIap)
|
|
425
361
|
job.cancel()
|
|
426
362
|
}
|
|
427
363
|
}
|
package/build/types.d.ts
CHANGED
|
@@ -77,40 +77,40 @@ export interface EntitlementIOS {
|
|
|
77
77
|
transactionId: string;
|
|
78
78
|
}
|
|
79
79
|
export declare enum ErrorCode {
|
|
80
|
-
ActivityUnavailable = "
|
|
81
|
-
AlreadyOwned = "
|
|
82
|
-
AlreadyPrepared = "
|
|
83
|
-
BillingResponseJsonParseError = "
|
|
84
|
-
BillingUnavailable = "
|
|
85
|
-
ConnectionClosed = "
|
|
86
|
-
DeferredPayment = "
|
|
87
|
-
DeveloperError = "
|
|
88
|
-
EmptySkuList = "
|
|
89
|
-
FeatureNotSupported = "
|
|
90
|
-
IapNotAvailable = "
|
|
91
|
-
InitConnection = "
|
|
92
|
-
Interrupted = "
|
|
93
|
-
ItemNotOwned = "
|
|
94
|
-
ItemUnavailable = "
|
|
95
|
-
NetworkError = "
|
|
96
|
-
NotEnded = "
|
|
97
|
-
NotPrepared = "
|
|
98
|
-
Pending = "
|
|
99
|
-
PurchaseError = "
|
|
100
|
-
QueryProduct = "
|
|
101
|
-
ReceiptFailed = "
|
|
102
|
-
ReceiptFinished = "
|
|
103
|
-
ReceiptFinishedFailed = "
|
|
104
|
-
RemoteError = "
|
|
105
|
-
ServiceDisconnected = "
|
|
106
|
-
ServiceError = "
|
|
107
|
-
SkuNotFound = "
|
|
108
|
-
SkuOfferMismatch = "
|
|
109
|
-
SyncError = "
|
|
110
|
-
TransactionValidationFailed = "
|
|
111
|
-
Unknown = "
|
|
112
|
-
UserCancelled = "
|
|
113
|
-
UserError = "
|
|
80
|
+
ActivityUnavailable = "activity-unavailable",
|
|
81
|
+
AlreadyOwned = "already-owned",
|
|
82
|
+
AlreadyPrepared = "already-prepared",
|
|
83
|
+
BillingResponseJsonParseError = "billing-response-json-parse-error",
|
|
84
|
+
BillingUnavailable = "billing-unavailable",
|
|
85
|
+
ConnectionClosed = "connection-closed",
|
|
86
|
+
DeferredPayment = "deferred-payment",
|
|
87
|
+
DeveloperError = "developer-error",
|
|
88
|
+
EmptySkuList = "empty-sku-list",
|
|
89
|
+
FeatureNotSupported = "feature-not-supported",
|
|
90
|
+
IapNotAvailable = "iap-not-available",
|
|
91
|
+
InitConnection = "init-connection",
|
|
92
|
+
Interrupted = "interrupted",
|
|
93
|
+
ItemNotOwned = "item-not-owned",
|
|
94
|
+
ItemUnavailable = "item-unavailable",
|
|
95
|
+
NetworkError = "network-error",
|
|
96
|
+
NotEnded = "not-ended",
|
|
97
|
+
NotPrepared = "not-prepared",
|
|
98
|
+
Pending = "pending",
|
|
99
|
+
PurchaseError = "purchase-error",
|
|
100
|
+
QueryProduct = "query-product",
|
|
101
|
+
ReceiptFailed = "receipt-failed",
|
|
102
|
+
ReceiptFinished = "receipt-finished",
|
|
103
|
+
ReceiptFinishedFailed = "receipt-finished-failed",
|
|
104
|
+
RemoteError = "remote-error",
|
|
105
|
+
ServiceDisconnected = "service-disconnected",
|
|
106
|
+
ServiceError = "service-error",
|
|
107
|
+
SkuNotFound = "sku-not-found",
|
|
108
|
+
SkuOfferMismatch = "sku-offer-mismatch",
|
|
109
|
+
SyncError = "sync-error",
|
|
110
|
+
TransactionValidationFailed = "transaction-validation-failed",
|
|
111
|
+
Unknown = "unknown",
|
|
112
|
+
UserCancelled = "user-cancelled",
|
|
113
|
+
UserError = "user-error"
|
|
114
114
|
}
|
|
115
115
|
export type FetchProductsResult = Product[] | ProductSubscription[] | null;
|
|
116
116
|
export type IapEvent = 'purchase-updated' | 'purchase-error' | 'promoted-product-ios';
|
package/build/types.js
CHANGED
|
@@ -4,40 +4,40 @@
|
|
|
4
4
|
// ============================================================================
|
|
5
5
|
export var ErrorCode;
|
|
6
6
|
(function (ErrorCode) {
|
|
7
|
-
ErrorCode["ActivityUnavailable"] = "
|
|
8
|
-
ErrorCode["AlreadyOwned"] = "
|
|
9
|
-
ErrorCode["AlreadyPrepared"] = "
|
|
10
|
-
ErrorCode["BillingResponseJsonParseError"] = "
|
|
11
|
-
ErrorCode["BillingUnavailable"] = "
|
|
12
|
-
ErrorCode["ConnectionClosed"] = "
|
|
13
|
-
ErrorCode["DeferredPayment"] = "
|
|
14
|
-
ErrorCode["DeveloperError"] = "
|
|
15
|
-
ErrorCode["EmptySkuList"] = "
|
|
16
|
-
ErrorCode["FeatureNotSupported"] = "
|
|
17
|
-
ErrorCode["IapNotAvailable"] = "
|
|
18
|
-
ErrorCode["InitConnection"] = "
|
|
19
|
-
ErrorCode["Interrupted"] = "
|
|
20
|
-
ErrorCode["ItemNotOwned"] = "
|
|
21
|
-
ErrorCode["ItemUnavailable"] = "
|
|
22
|
-
ErrorCode["NetworkError"] = "
|
|
23
|
-
ErrorCode["NotEnded"] = "
|
|
24
|
-
ErrorCode["NotPrepared"] = "
|
|
25
|
-
ErrorCode["Pending"] = "
|
|
26
|
-
ErrorCode["PurchaseError"] = "
|
|
27
|
-
ErrorCode["QueryProduct"] = "
|
|
28
|
-
ErrorCode["ReceiptFailed"] = "
|
|
29
|
-
ErrorCode["ReceiptFinished"] = "
|
|
30
|
-
ErrorCode["ReceiptFinishedFailed"] = "
|
|
31
|
-
ErrorCode["RemoteError"] = "
|
|
32
|
-
ErrorCode["ServiceDisconnected"] = "
|
|
33
|
-
ErrorCode["ServiceError"] = "
|
|
34
|
-
ErrorCode["SkuNotFound"] = "
|
|
35
|
-
ErrorCode["SkuOfferMismatch"] = "
|
|
36
|
-
ErrorCode["SyncError"] = "
|
|
37
|
-
ErrorCode["TransactionValidationFailed"] = "
|
|
38
|
-
ErrorCode["Unknown"] = "
|
|
39
|
-
ErrorCode["UserCancelled"] = "
|
|
40
|
-
ErrorCode["UserError"] = "
|
|
7
|
+
ErrorCode["ActivityUnavailable"] = "activity-unavailable";
|
|
8
|
+
ErrorCode["AlreadyOwned"] = "already-owned";
|
|
9
|
+
ErrorCode["AlreadyPrepared"] = "already-prepared";
|
|
10
|
+
ErrorCode["BillingResponseJsonParseError"] = "billing-response-json-parse-error";
|
|
11
|
+
ErrorCode["BillingUnavailable"] = "billing-unavailable";
|
|
12
|
+
ErrorCode["ConnectionClosed"] = "connection-closed";
|
|
13
|
+
ErrorCode["DeferredPayment"] = "deferred-payment";
|
|
14
|
+
ErrorCode["DeveloperError"] = "developer-error";
|
|
15
|
+
ErrorCode["EmptySkuList"] = "empty-sku-list";
|
|
16
|
+
ErrorCode["FeatureNotSupported"] = "feature-not-supported";
|
|
17
|
+
ErrorCode["IapNotAvailable"] = "iap-not-available";
|
|
18
|
+
ErrorCode["InitConnection"] = "init-connection";
|
|
19
|
+
ErrorCode["Interrupted"] = "interrupted";
|
|
20
|
+
ErrorCode["ItemNotOwned"] = "item-not-owned";
|
|
21
|
+
ErrorCode["ItemUnavailable"] = "item-unavailable";
|
|
22
|
+
ErrorCode["NetworkError"] = "network-error";
|
|
23
|
+
ErrorCode["NotEnded"] = "not-ended";
|
|
24
|
+
ErrorCode["NotPrepared"] = "not-prepared";
|
|
25
|
+
ErrorCode["Pending"] = "pending";
|
|
26
|
+
ErrorCode["PurchaseError"] = "purchase-error";
|
|
27
|
+
ErrorCode["QueryProduct"] = "query-product";
|
|
28
|
+
ErrorCode["ReceiptFailed"] = "receipt-failed";
|
|
29
|
+
ErrorCode["ReceiptFinished"] = "receipt-finished";
|
|
30
|
+
ErrorCode["ReceiptFinishedFailed"] = "receipt-finished-failed";
|
|
31
|
+
ErrorCode["RemoteError"] = "remote-error";
|
|
32
|
+
ErrorCode["ServiceDisconnected"] = "service-disconnected";
|
|
33
|
+
ErrorCode["ServiceError"] = "service-error";
|
|
34
|
+
ErrorCode["SkuNotFound"] = "sku-not-found";
|
|
35
|
+
ErrorCode["SkuOfferMismatch"] = "sku-offer-mismatch";
|
|
36
|
+
ErrorCode["SyncError"] = "sync-error";
|
|
37
|
+
ErrorCode["TransactionValidationFailed"] = "transaction-validation-failed";
|
|
38
|
+
ErrorCode["Unknown"] = "unknown";
|
|
39
|
+
ErrorCode["UserCancelled"] = "user-cancelled";
|
|
40
|
+
ErrorCode["UserError"] = "user-error";
|
|
41
41
|
})(ErrorCode || (ErrorCode = {}));
|
|
42
42
|
// -- End subscription helper types
|
|
43
43
|
//# sourceMappingURL=types.js.map
|