expo-iap 2.8.3 → 2.8.4
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
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## [2.8.4] - 2025-08-31
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- Fixed iOS 18.4 properties build failure on Xcode 16.3 and below by adding Swift 6.1 compiler guard
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Android: Enabled automatic service reconnection (Android Billing Client v8 feature) and simplified connection logic (#178)
|
|
12
|
+
|
|
3
13
|
## [2.8.3] - 2025-08-27
|
|
4
14
|
|
|
5
15
|
### Fixed
|
|
@@ -166,7 +166,7 @@ class ExpoIapModule :
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
if (skuList.isEmpty()) {
|
|
169
|
-
promise.reject(
|
|
169
|
+
promise.reject(IapErrorCode.E_EMPTY_SKU_LIST, "The SKU list is empty.", null)
|
|
170
170
|
return@ensureConnection
|
|
171
171
|
}
|
|
172
172
|
|
|
@@ -575,7 +575,9 @@ class ExpoIapModule :
|
|
|
575
575
|
promise: Promise,
|
|
576
576
|
callback: (billingClient: BillingClient) -> Unit,
|
|
577
577
|
) {
|
|
578
|
-
if
|
|
578
|
+
// With auto-reconnection enabled, we only need to check if billing client exists
|
|
579
|
+
// The service will automatically reconnect when needed
|
|
580
|
+
if (billingClientCache != null) {
|
|
579
581
|
callback(billingClientCache!!)
|
|
580
582
|
return
|
|
581
583
|
}
|
|
@@ -605,6 +607,7 @@ class ExpoIapModule :
|
|
|
605
607
|
.newBuilder(context)
|
|
606
608
|
.setListener(this)
|
|
607
609
|
.enablePendingPurchases(PendingPurchasesParams.newBuilder().enableOneTimeProducts().build())
|
|
610
|
+
.enableAutoServiceReconnection() // Automatically handle service disconnections
|
|
608
611
|
.build()
|
|
609
612
|
|
|
610
613
|
billingClientCache?.startConnection(
|
|
@@ -13,22 +13,6 @@ object PromiseUtils {
|
|
|
13
13
|
private val promises = HashMap<String, MutableList<Promise>>()
|
|
14
14
|
|
|
15
15
|
const val TAG = "PromiseUtils"
|
|
16
|
-
const val E_ACTIVITY_UNAVAILABLE = "E_ACTIVITY_UNAVAILABLE"
|
|
17
|
-
const val E_UNKNOWN = "E_UNKNOWN"
|
|
18
|
-
const val E_NOT_PREPARED = "E_NOT_PREPARED"
|
|
19
|
-
const val E_ALREADY_PREPARED = "E_ALREADY_PREPARED"
|
|
20
|
-
const val E_PENDING = "E_PENDING"
|
|
21
|
-
const val E_NOT_ENDED = "E_NOT_ENDED"
|
|
22
|
-
const val E_USER_CANCELLED = "E_USER_CANCELLED"
|
|
23
|
-
const val E_ITEM_UNAVAILABLE = "E_ITEM_UNAVAILABLE"
|
|
24
|
-
const val E_NETWORK_ERROR = "E_NETWORK_ERROR"
|
|
25
|
-
const val E_SERVICE_ERROR = "E_SERVICE_ERROR"
|
|
26
|
-
const val E_ALREADY_OWNED = "E_ALREADY_OWNED"
|
|
27
|
-
const val E_REMOTE_ERROR = "E_REMOTE_ERROR"
|
|
28
|
-
const val E_USER_ERROR = "E_USER_ERROR"
|
|
29
|
-
const val E_DEVELOPER_ERROR = "E_DEVELOPER_ERROR"
|
|
30
|
-
const val E_BILLING_RESPONSE_JSON_PARSE_ERROR = "E_BILLING_RESPONSE_JSON_PARSE_ERROR"
|
|
31
|
-
const val E_CONNECTION_CLOSED = "E_CONNECTION_CLOSED"
|
|
32
16
|
|
|
33
17
|
fun addPromiseForKey(
|
|
34
18
|
key: String,
|
|
@@ -49,7 +33,7 @@ object PromiseUtils {
|
|
|
49
33
|
|
|
50
34
|
fun rejectAllPendingPromises() {
|
|
51
35
|
promises.flatMap { it.value }.forEach { promise ->
|
|
52
|
-
promise.safeReject(E_CONNECTION_CLOSED, "Connection has been closed", null)
|
|
36
|
+
promise.safeReject(IapErrorCode.E_CONNECTION_CLOSED, "Connection has been closed", null)
|
|
53
37
|
}
|
|
54
38
|
promises.clear()
|
|
55
39
|
}
|
|
@@ -117,19 +101,19 @@ object PlayUtils {
|
|
|
117
101
|
when (responseCode) {
|
|
118
102
|
BillingClient.BillingResponseCode.FEATURE_NOT_SUPPORTED -> {
|
|
119
103
|
BillingResponse(
|
|
120
|
-
|
|
104
|
+
IapErrorCode.E_SERVICE_ERROR,
|
|
121
105
|
"This feature is not available on your device.",
|
|
122
106
|
)
|
|
123
107
|
}
|
|
124
108
|
BillingClient.BillingResponseCode.SERVICE_DISCONNECTED -> {
|
|
125
109
|
BillingResponse(
|
|
126
|
-
|
|
110
|
+
IapErrorCode.E_NETWORK_ERROR,
|
|
127
111
|
"The service is disconnected (check your internet connection.)",
|
|
128
112
|
)
|
|
129
113
|
}
|
|
130
114
|
BillingClient.BillingResponseCode.NETWORK_ERROR -> {
|
|
131
115
|
BillingResponse(
|
|
132
|
-
|
|
116
|
+
IapErrorCode.E_NETWORK_ERROR,
|
|
133
117
|
"You have a problem with network connection.",
|
|
134
118
|
)
|
|
135
119
|
}
|
|
@@ -141,49 +125,49 @@ object PlayUtils {
|
|
|
141
125
|
}
|
|
142
126
|
BillingClient.BillingResponseCode.USER_CANCELED -> {
|
|
143
127
|
BillingResponse(
|
|
144
|
-
|
|
128
|
+
IapErrorCode.E_USER_CANCELLED,
|
|
145
129
|
"Payment is cancelled.",
|
|
146
130
|
)
|
|
147
131
|
}
|
|
148
132
|
BillingClient.BillingResponseCode.SERVICE_UNAVAILABLE -> {
|
|
149
133
|
BillingResponse(
|
|
150
|
-
|
|
134
|
+
IapErrorCode.E_SERVICE_ERROR,
|
|
151
135
|
"The service is unreachable. This may be your internet connection, or the Play Store may be down.",
|
|
152
136
|
)
|
|
153
137
|
}
|
|
154
138
|
BillingClient.BillingResponseCode.BILLING_UNAVAILABLE -> {
|
|
155
139
|
BillingResponse(
|
|
156
|
-
|
|
140
|
+
IapErrorCode.E_SERVICE_ERROR,
|
|
157
141
|
"Billing is unavailable. This may be a problem with your device, or the Play Store may be down.",
|
|
158
142
|
)
|
|
159
143
|
}
|
|
160
144
|
BillingClient.BillingResponseCode.ITEM_UNAVAILABLE -> {
|
|
161
145
|
BillingResponse(
|
|
162
|
-
|
|
146
|
+
IapErrorCode.E_ITEM_UNAVAILABLE,
|
|
163
147
|
"That item is unavailable.",
|
|
164
148
|
)
|
|
165
149
|
}
|
|
166
150
|
BillingClient.BillingResponseCode.DEVELOPER_ERROR -> {
|
|
167
151
|
BillingResponse(
|
|
168
|
-
|
|
152
|
+
IapErrorCode.E_DEVELOPER_ERROR,
|
|
169
153
|
"Google is indicating that we have some issue connecting to payment.",
|
|
170
154
|
)
|
|
171
155
|
}
|
|
172
156
|
BillingClient.BillingResponseCode.ERROR -> {
|
|
173
157
|
BillingResponse(
|
|
174
|
-
|
|
158
|
+
IapErrorCode.E_UNKNOWN,
|
|
175
159
|
"An unknown or unexpected error has occurred. Please try again later.",
|
|
176
160
|
)
|
|
177
161
|
}
|
|
178
162
|
BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED -> {
|
|
179
163
|
BillingResponse(
|
|
180
|
-
|
|
164
|
+
IapErrorCode.E_ALREADY_OWNED,
|
|
181
165
|
"You already own this item.",
|
|
182
166
|
)
|
|
183
167
|
}
|
|
184
168
|
else -> {
|
|
185
169
|
BillingResponse(
|
|
186
|
-
|
|
170
|
+
IapErrorCode.E_UNKNOWN,
|
|
187
171
|
"Purchase failed with code: $responseCode",
|
|
188
172
|
)
|
|
189
173
|
}
|
|
@@ -5,18 +5,47 @@ package expo.modules.iap
|
|
|
5
5
|
* Single source of truth for all error codes used across the module
|
|
6
6
|
*/
|
|
7
7
|
object IapErrorCode {
|
|
8
|
-
//
|
|
8
|
+
// Connection and initialization errors
|
|
9
9
|
const val E_NOT_PREPARED = "E_NOT_PREPARED"
|
|
10
10
|
const val E_INIT_CONNECTION = "E_INIT_CONNECTION"
|
|
11
|
+
const val E_SERVICE_DISCONNECTED = "E_SERVICE_DISCONNECTED"
|
|
12
|
+
const val E_ALREADY_PREPARED = "E_ALREADY_PREPARED"
|
|
13
|
+
const val E_CONNECTION_CLOSED = "E_CONNECTION_CLOSED"
|
|
14
|
+
|
|
15
|
+
// Product and purchase errors
|
|
11
16
|
const val E_QUERY_PRODUCT = "E_QUERY_PRODUCT"
|
|
12
|
-
const val E_UNKNOWN = "E_UNKNOWN"
|
|
13
|
-
const val E_SKU_OFFER_MISMATCH = "E_SKU_OFFER_MISMATCH"
|
|
14
17
|
const val E_SKU_NOT_FOUND = "E_SKU_NOT_FOUND"
|
|
18
|
+
const val E_SKU_OFFER_MISMATCH = "E_SKU_OFFER_MISMATCH"
|
|
19
|
+
const val E_PURCHASE_ERROR = "E_PURCHASE_ERROR"
|
|
15
20
|
const val E_USER_CANCELLED = "E_USER_CANCELLED"
|
|
21
|
+
const val E_PENDING = "E_PENDING"
|
|
22
|
+
|
|
23
|
+
// Service and developer errors
|
|
24
|
+
const val E_SERVICE_ERROR = "E_SERVICE_ERROR"
|
|
16
25
|
const val E_DEVELOPER_ERROR = "E_DEVELOPER_ERROR"
|
|
17
26
|
const val E_ITEM_UNAVAILABLE = "E_ITEM_UNAVAILABLE"
|
|
18
|
-
const val
|
|
19
|
-
const val
|
|
27
|
+
const val E_ALREADY_OWNED = "E_ALREADY_OWNED"
|
|
28
|
+
const val E_ITEM_NOT_OWNED = "E_ITEM_NOT_OWNED"
|
|
29
|
+
|
|
30
|
+
// Network and billing errors
|
|
31
|
+
const val E_NETWORK_ERROR = "E_NETWORK_ERROR"
|
|
32
|
+
const val E_BILLING_UNAVAILABLE = "E_BILLING_UNAVAILABLE"
|
|
33
|
+
const val E_FEATURE_NOT_SUPPORTED = "E_FEATURE_NOT_SUPPORTED"
|
|
34
|
+
const val E_BILLING_RESPONSE_JSON_PARSE_ERROR = "E_BILLING_RESPONSE_JSON_PARSE_ERROR"
|
|
35
|
+
|
|
36
|
+
// Activity and UI errors
|
|
37
|
+
const val E_ACTIVITY_UNAVAILABLE = "E_ACTIVITY_UNAVAILABLE"
|
|
38
|
+
|
|
39
|
+
// User and remote errors
|
|
40
|
+
const val E_USER_ERROR = "E_USER_ERROR"
|
|
41
|
+
const val E_REMOTE_ERROR = "E_REMOTE_ERROR"
|
|
42
|
+
const val E_NOT_ENDED = "E_NOT_ENDED"
|
|
43
|
+
|
|
44
|
+
// Unknown error
|
|
45
|
+
const val E_UNKNOWN = "E_UNKNOWN"
|
|
46
|
+
|
|
47
|
+
// Empty SKU list error
|
|
48
|
+
const val E_EMPTY_SKU_LIST = "E_EMPTY_SKU_LIST"
|
|
20
49
|
|
|
21
50
|
// Cached map for Constants export - initialized once at class loading time
|
|
22
51
|
// Using constants as keys to avoid duplication and ensure type safety
|
|
@@ -31,7 +60,22 @@ object IapErrorCode {
|
|
|
31
60
|
E_DEVELOPER_ERROR to E_DEVELOPER_ERROR,
|
|
32
61
|
E_ITEM_UNAVAILABLE to E_ITEM_UNAVAILABLE,
|
|
33
62
|
E_SERVICE_ERROR to E_SERVICE_ERROR,
|
|
34
|
-
E_PURCHASE_ERROR to E_PURCHASE_ERROR
|
|
63
|
+
E_PURCHASE_ERROR to E_PURCHASE_ERROR,
|
|
64
|
+
E_ACTIVITY_UNAVAILABLE to E_ACTIVITY_UNAVAILABLE,
|
|
65
|
+
E_ALREADY_PREPARED to E_ALREADY_PREPARED,
|
|
66
|
+
E_PENDING to E_PENDING,
|
|
67
|
+
E_NOT_ENDED to E_NOT_ENDED,
|
|
68
|
+
E_NETWORK_ERROR to E_NETWORK_ERROR,
|
|
69
|
+
E_ALREADY_OWNED to E_ALREADY_OWNED,
|
|
70
|
+
E_REMOTE_ERROR to E_REMOTE_ERROR,
|
|
71
|
+
E_USER_ERROR to E_USER_ERROR,
|
|
72
|
+
E_BILLING_RESPONSE_JSON_PARSE_ERROR to E_BILLING_RESPONSE_JSON_PARSE_ERROR,
|
|
73
|
+
E_CONNECTION_CLOSED to E_CONNECTION_CLOSED,
|
|
74
|
+
E_SERVICE_DISCONNECTED to E_SERVICE_DISCONNECTED,
|
|
75
|
+
E_BILLING_UNAVAILABLE to E_BILLING_UNAVAILABLE,
|
|
76
|
+
E_FEATURE_NOT_SUPPORTED to E_FEATURE_NOT_SUPPORTED,
|
|
77
|
+
E_ITEM_NOT_OWNED to E_ITEM_NOT_OWNED,
|
|
78
|
+
E_EMPTY_SKU_LIST to E_EMPTY_SKU_LIST
|
|
35
79
|
)
|
|
36
80
|
|
|
37
81
|
// Return cached map reference - no new allocations on repeated calls
|
|
@@ -47,9 +91,8 @@ object IapEvent {
|
|
|
47
91
|
}
|
|
48
92
|
|
|
49
93
|
/**
|
|
50
|
-
* Other IAP-related constants
|
|
94
|
+
* Other IAP-related constants (Promise keys, etc.)
|
|
51
95
|
*/
|
|
52
96
|
object IapConstants {
|
|
53
|
-
const val EMPTY_SKU_LIST = "EMPTY_SKU_LIST"
|
|
54
97
|
const val PROMISE_BUY_ITEM = "PROMISE_BUY_ITEM"
|
|
55
98
|
}
|
package/ios/ExpoIapModule.swift
CHANGED
|
@@ -385,10 +385,14 @@ public class ExpoIapModule: Module {
|
|
|
385
385
|
"preorderDate": appTransaction.preorderDate.map { $0.timeIntervalSince1970 * 1000 }
|
|
386
386
|
]
|
|
387
387
|
|
|
388
|
+
// iOS 18.4+ properties - only compile with Xcode 16.4+ (Swift 6.1+)
|
|
389
|
+
// This prevents build failures on Xcode 16.3 and below
|
|
390
|
+
#if swift(>=6.1)
|
|
388
391
|
if #available(iOS 18.4, *) {
|
|
389
392
|
result["appTransactionId"] = appTransaction.appTransactionID
|
|
390
393
|
result["originalPlatform"] = appTransaction.originalPlatform.rawValue
|
|
391
394
|
}
|
|
395
|
+
#endif
|
|
392
396
|
|
|
393
397
|
return result
|
|
394
398
|
#else
|