expo-iap 4.7.2 → 5.0.1
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 +5 -6
- package/android/src/main/java/expo/modules/iap/ExpoIapHelper.kt +12 -87
- package/android/src/main/java/expo/modules/iap/ExpoIapLog.kt +0 -14
- package/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +48 -96
- package/build/index.d.ts +1 -19
- package/build/index.d.ts.map +1 -1
- package/build/index.js +19 -97
- package/build/index.js.map +1 -1
- package/build/index.kepler.d.ts +1 -42
- package/build/index.kepler.d.ts.map +1 -1
- package/build/index.kepler.js +11 -65
- package/build/index.kepler.js.map +1 -1
- package/build/modules/android.d.ts +1 -104
- package/build/modules/android.d.ts.map +1 -1
- package/build/modules/android.js +0 -124
- package/build/modules/android.js.map +1 -1
- package/build/modules/ios.d.ts +2 -57
- package/build/modules/ios.d.ts.map +1 -1
- package/build/modules/ios.js +7 -105
- package/build/modules/ios.js.map +1 -1
- package/build/types.d.ts +87 -284
- package/build/types.d.ts.map +1 -1
- package/build/types.js +0 -6
- package/build/types.js.map +1 -1
- package/build/useIAP.d.ts +1 -34
- package/build/useIAP.d.ts.map +1 -1
- package/build/useIAP.js +12 -54
- package/build/useIAP.js.map +1 -1
- package/build/utils/availablePurchases.d.ts +8 -0
- package/build/utils/availablePurchases.d.ts.map +1 -0
- package/build/utils/availablePurchases.js +74 -0
- package/build/utils/availablePurchases.js.map +1 -0
- package/build/utils/errorMapping.d.ts.map +1 -1
- package/build/utils/errorMapping.js +25 -7
- package/build/utils/errorMapping.js.map +1 -1
- package/build/utils/restorePurchases.d.ts +9 -0
- package/build/utils/restorePurchases.d.ts.map +1 -0
- package/build/utils/restorePurchases.js +29 -0
- package/build/utils/restorePurchases.js.map +1 -0
- package/build/vega-adapter.d.ts +1 -1
- package/build/vega-adapter.d.ts.map +1 -1
- package/build/vega-adapter.js +20 -34
- package/build/vega-adapter.js.map +1 -1
- package/ios/ExpoIapHelper.swift +46 -55
- package/ios/ExpoIapLog.swift +0 -11
- package/ios/ExpoIapModule.swift +26 -52
- package/ios/onside/OnsideIapModule.swift +1 -19
- package/openiap-versions.json +3 -3
- package/package.json +1 -1
- package/plugin/build/withIAP.d.ts +2 -14
- package/plugin/build/withIAP.js +6 -67
- package/plugin/src/expoConfig.augmentation.d.ts +6 -33
- package/plugin/src/withIAP.ts +8 -132
- package/src/index.kepler.ts +13 -92
- package/src/index.ts +21 -135
- package/src/modules/android.ts +0 -152
- package/src/modules/ios.ts +8 -125
- package/src/types.ts +88 -295
- package/src/useIAP.ts +14 -112
- package/src/utils/availablePurchases.ts +105 -0
- package/src/utils/errorMapping.ts +31 -7
- package/src/utils/restorePurchases.ts +31 -0
- package/src/vega-adapter.ts +37 -37
- package/build/utils/deprecation.d.ts +0 -3
- package/build/utils/deprecation.d.ts.map +0 -1
- package/build/utils/deprecation.js +0 -13
- package/build/utils/deprecation.js.map +0 -1
- package/src/utils/deprecation.ts +0 -16
package/CLAUDE.md
CHANGED
|
@@ -51,18 +51,17 @@ Before committing any changes:
|
|
|
51
51
|
|
|
52
52
|
#### Function Naming
|
|
53
53
|
|
|
54
|
-
- Functions that only operate on one platform must carry the suffix: `nameIOS` or `nameAndroid` (e.g. `
|
|
54
|
+
- Functions that only operate on one platform must carry the suffix: `nameIOS` or `nameAndroid` (e.g. `getReceiptDataIOS`, `deepLinkToSubscriptionsAndroid`).
|
|
55
55
|
- Cross-platform helpers should expose a single name and branch internally via `Platform.select` or equivalent.
|
|
56
56
|
|
|
57
57
|
#### Field Naming
|
|
58
58
|
|
|
59
|
-
- **iOS-related fields**: Use `IOS` suffix (e.g., `displayNameIOS`, `
|
|
59
|
+
- **iOS-related fields**: Use `IOS` suffix (e.g., `displayNameIOS`, `subscriptionGroupIdIOS`, `introductoryPriceIOS`)
|
|
60
60
|
- **Exception**: When an acronym appears at the end of a field name, use uppercase (e.g., `quantityIOS`, `appBundleIdIOS`, not `quantityIos`)
|
|
61
61
|
- Platform-specific fields: `currencyCodeIOS`, `currencySymbolIOS`, `countryCodeIOS`
|
|
62
|
-
- Product fields: `isFamilyShareableIOS`, `jsonRepresentationIOS`, `
|
|
62
|
+
- Product fields: `isFamilyShareableIOS`, `jsonRepresentationIOS`, `subscriptionGroupIdIOS`
|
|
63
63
|
- **Android-related fields**: Use `Android` suffix (e.g., `nameAndroid`)
|
|
64
|
-
- Platform-specific fields: `
|
|
65
|
-
- In **Android-specific types** (e.g., `ProductSubscriptionAndroidOfferDetails`), keep `pricingPhases` without suffix for consistency with Google Play Billing
|
|
64
|
+
- Platform-specific fields: `productStatusAndroid`, `basePlanIdAndroid`
|
|
66
65
|
- In **cross-platform types** (e.g., `SubscriptionOffer`, `DiscountOffer`), use `pricingPhasesAndroid` suffix to distinguish from common fields
|
|
67
66
|
- **Common fields**: Fields shared across platforms go in Common types (e.g., `ids`, `platform`, `debugDescription`)
|
|
68
67
|
- Use these for data that exists on both platforms without platform-specific variations
|
|
@@ -71,7 +70,7 @@ Before committing any changes:
|
|
|
71
70
|
|
|
72
71
|
- **iOS types**: Use `IOS` suffix (e.g., `PurchaseIOS`, `ProductIOS`)
|
|
73
72
|
- **Android types**: Use descriptive prefixes to identify subtypes:
|
|
74
|
-
- ✅ Good: `
|
|
73
|
+
- ✅ Good: `BillingProgramAvailabilityResultAndroid`, `SubscriptionPeriodIOS`, `PurchaseAndroidState`
|
|
75
74
|
- ❌ Avoid: `OneTimePurchaseOfferDetails`, `SubscriptionOfferAndroid`, `PurchaseStateAndroid`
|
|
76
75
|
- **General IAP types**: Use `Iap` prefix (e.g., `IapPurchase`, not `IAPPurchase`)
|
|
77
76
|
|
|
@@ -64,76 +64,35 @@ object ExpoIapHelper {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
fun parseProductQueryType(rawType: String?): ProductQueryType {
|
|
67
|
-
val normalized =
|
|
68
|
-
rawType
|
|
69
|
-
?.trim()
|
|
70
|
-
?.lowercase(Locale.US)
|
|
71
|
-
?.replace("-", "")
|
|
72
|
-
?.replace("_", "")
|
|
73
|
-
|
|
67
|
+
val normalized = rawType?.trim()?.lowercase(Locale.US)
|
|
74
68
|
return when (normalized) {
|
|
69
|
+
null, "", "in-app" -> ProductQueryType.InApp
|
|
75
70
|
"subs" -> ProductQueryType.Subs
|
|
76
71
|
"all" -> ProductQueryType.All
|
|
77
|
-
else ->
|
|
72
|
+
else -> throw IllegalArgumentException(
|
|
73
|
+
"Unsupported product type: $rawType. Use in-app, subs, or all.",
|
|
74
|
+
)
|
|
78
75
|
}
|
|
79
76
|
}
|
|
80
77
|
|
|
81
78
|
internal fun parseDeepLinkSubscriptionParams(params: Map<String, Any?>): DeepLinkSubscriptionParams {
|
|
82
|
-
val hasCanonicalSku = params.containsKey("skuAndroid")
|
|
83
|
-
val hasCanonicalPackageName = params.containsKey("packageNameAndroid")
|
|
84
|
-
val legacySku = params["sku"] as? String
|
|
85
|
-
val legacyPackageName = params["packageName"] as? String
|
|
86
|
-
|
|
87
|
-
if (!hasCanonicalSku && legacySku != null) {
|
|
88
|
-
ExpoIapLog.deprecation(
|
|
89
|
-
"deep-link.sku",
|
|
90
|
-
"`sku` is deprecated and will be removed in expo-iap 5.0.0. Use `skuAndroid` instead.",
|
|
91
|
-
)
|
|
92
|
-
}
|
|
93
|
-
if (!hasCanonicalPackageName && legacyPackageName != null) {
|
|
94
|
-
ExpoIapLog.deprecation(
|
|
95
|
-
"deep-link.packageName",
|
|
96
|
-
"`packageName` is deprecated and will be removed in expo-iap 5.0.0. Use `packageNameAndroid` instead.",
|
|
97
|
-
)
|
|
98
|
-
}
|
|
99
|
-
|
|
100
79
|
return DeepLinkSubscriptionParams(
|
|
101
|
-
sku =
|
|
102
|
-
|
|
103
|
-
params["skuAndroid"] as? String
|
|
104
|
-
} else {
|
|
105
|
-
legacySku
|
|
106
|
-
},
|
|
107
|
-
packageName =
|
|
108
|
-
if (hasCanonicalPackageName) {
|
|
109
|
-
params["packageNameAndroid"] as? String
|
|
110
|
-
} else {
|
|
111
|
-
legacyPackageName
|
|
112
|
-
},
|
|
80
|
+
sku = params["skuAndroid"] as? String,
|
|
81
|
+
packageName = params["packageNameAndroid"] as? String,
|
|
113
82
|
)
|
|
114
83
|
}
|
|
115
84
|
|
|
116
85
|
fun parseRequestPurchaseParams(params: Map<String, Any?>): RequestPurchaseParams {
|
|
117
|
-
//
|
|
118
|
-
//
|
|
119
|
-
// extract and flatten it before parsing.
|
|
86
|
+
// If the params contain a canonical request.google envelope, flatten it
|
|
87
|
+
// before parsing the native request.
|
|
120
88
|
val effective: Map<String, Any?> =
|
|
121
89
|
run {
|
|
122
90
|
val request = params["request"] as? Map<*, *>
|
|
123
91
|
if (request != null) {
|
|
124
|
-
val
|
|
125
|
-
val canonicalGoogle = request["google"] as? Map<*, *>
|
|
126
|
-
val legacyAndroid = request["android"] as? Map<*, *>
|
|
127
|
-
if (!hasCanonicalGoogle && legacyAndroid != null) {
|
|
128
|
-
ExpoIapLog.deprecation(
|
|
129
|
-
"request-purchase.android",
|
|
130
|
-
"`request.android` is deprecated and will be removed in expo-iap 5.0.0. Use `request.google` instead.",
|
|
131
|
-
)
|
|
132
|
-
}
|
|
133
|
-
val nested = if (hasCanonicalGoogle) canonicalGoogle else legacyAndroid
|
|
92
|
+
val nested = request["google"] as? Map<*, *>
|
|
134
93
|
if (nested != null) {
|
|
135
94
|
val flat = mutableMapOf<String, Any?>()
|
|
136
|
-
// Carry over top-level fields
|
|
95
|
+
// Carry over top-level fields such as the purchase type.
|
|
137
96
|
for ((k, v) in params) {
|
|
138
97
|
if (k != "request") flat[k] = v
|
|
139
98
|
}
|
|
@@ -151,33 +110,10 @@ object ExpoIapHelper {
|
|
|
151
110
|
}
|
|
152
111
|
|
|
153
112
|
val type = effective["type"] as? String
|
|
154
|
-
val
|
|
155
|
-
val canonicalSkus = (effective["skus"] as? List<*>)?.filterIsInstance<String>()
|
|
156
|
-
val legacySkuArr = (effective["skuArr"] as? List<*>)?.filterIsInstance<String>()
|
|
157
|
-
if (!hasCanonicalSkus && legacySkuArr != null) {
|
|
158
|
-
ExpoIapLog.deprecation(
|
|
159
|
-
"request-purchase.skuArr",
|
|
160
|
-
"`skuArr` is deprecated and will be removed in expo-iap 5.0.0. Use `skus` instead.",
|
|
161
|
-
)
|
|
162
|
-
}
|
|
163
|
-
val skus: List<String> =
|
|
164
|
-
if (hasCanonicalSkus) {
|
|
165
|
-
canonicalSkus ?: emptyList()
|
|
166
|
-
} else {
|
|
167
|
-
legacySkuArr ?: emptyList()
|
|
168
|
-
}
|
|
113
|
+
val skus = (effective["skus"] as? List<*>)?.filterIsInstance<String>() ?: emptyList()
|
|
169
114
|
val obfuscatedAccountId = effective["obfuscatedAccountId"] as? String
|
|
170
115
|
val obfuscatedProfileId = effective["obfuscatedProfileId"] as? String
|
|
171
116
|
val isOfferPersonalized = effective["isOfferPersonalized"] as? Boolean ?: false
|
|
172
|
-
val hasCanonicalSubscriptionOffers = effective.containsKey("subscriptionOffers")
|
|
173
|
-
val legacyOfferTokenArr =
|
|
174
|
-
(effective["offerTokenArr"] as? List<*>)?.filterIsInstance<String>() ?: emptyList()
|
|
175
|
-
val offerTokenArr =
|
|
176
|
-
if (hasCanonicalSubscriptionOffers) {
|
|
177
|
-
emptyList()
|
|
178
|
-
} else {
|
|
179
|
-
legacyOfferTokenArr
|
|
180
|
-
}
|
|
181
117
|
val explicitSubscriptionOffers =
|
|
182
118
|
(effective["subscriptionOffers"] as? List<*>)?.mapNotNull { rawOffer ->
|
|
183
119
|
val offerMap = rawOffer as? Map<*, *> ?: return@mapNotNull null
|
|
@@ -189,15 +125,8 @@ object ExpoIapHelper {
|
|
|
189
125
|
AndroidSubscriptionOfferInput(offerToken = offerToken, sku = sku)
|
|
190
126
|
}
|
|
191
127
|
} ?: emptyList()
|
|
192
|
-
if (!hasCanonicalSubscriptionOffers && offerTokenArr.isNotEmpty()) {
|
|
193
|
-
ExpoIapLog.deprecation(
|
|
194
|
-
"request-purchase.offerTokenArr",
|
|
195
|
-
"`offerTokenArr` is deprecated and will be removed in expo-iap 5.0.0. Use `subscriptionOffers` instead.",
|
|
196
|
-
)
|
|
197
|
-
}
|
|
198
128
|
val purchaseToken = effective["purchaseToken"] as? String
|
|
199
129
|
val originalExternalTransactionId = effective["originalExternalTransactionId"] as? String
|
|
200
|
-
val replacementMode = effective["replacementMode"] as? Number
|
|
201
130
|
val developerBillingOption =
|
|
202
131
|
(effective["developerBillingOption"] as? Map<*, *>)?.let { optionMap ->
|
|
203
132
|
val json =
|
|
@@ -230,12 +159,10 @@ object ExpoIapHelper {
|
|
|
230
159
|
obfuscatedProfileId = obfuscatedProfileId,
|
|
231
160
|
isOfferPersonalized = isOfferPersonalized,
|
|
232
161
|
offerToken = offerToken,
|
|
233
|
-
offerTokenArr = offerTokenArr,
|
|
234
162
|
explicitSubscriptionOffers = explicitSubscriptionOffers,
|
|
235
163
|
developerBillingOption = developerBillingOption,
|
|
236
164
|
originalExternalTransactionId = originalExternalTransactionId,
|
|
237
165
|
purchaseToken = purchaseToken,
|
|
238
|
-
replacementMode = replacementMode,
|
|
239
166
|
subscriptionProductReplacementParams = subscriptionProductReplacementParams,
|
|
240
167
|
)
|
|
241
168
|
}
|
|
@@ -259,12 +186,10 @@ object ExpoIapHelper {
|
|
|
259
186
|
val isOfferPersonalized: Boolean,
|
|
260
187
|
/** Offer token for one-time purchase discounts (Android 8.0+) */
|
|
261
188
|
val offerToken: String?,
|
|
262
|
-
val offerTokenArr: List<String>,
|
|
263
189
|
val explicitSubscriptionOffers: List<AndroidSubscriptionOfferInput>,
|
|
264
190
|
val developerBillingOption: DeveloperBillingOptionParamsAndroid?,
|
|
265
191
|
val originalExternalTransactionId: String?,
|
|
266
192
|
val purchaseToken: String?,
|
|
267
|
-
val replacementMode: Number?,
|
|
268
193
|
val subscriptionProductReplacementParams: SubscriptionProductReplacementParamsAndroid?,
|
|
269
194
|
)
|
|
270
195
|
|
|
@@ -3,14 +3,10 @@ package expo.modules.iap
|
|
|
3
3
|
import android.util.Log
|
|
4
4
|
import org.json.JSONArray
|
|
5
5
|
import org.json.JSONObject
|
|
6
|
-
import java.util.Collections
|
|
7
6
|
import java.util.Locale
|
|
8
|
-
import java.util.concurrent.ConcurrentHashMap
|
|
9
7
|
|
|
10
8
|
internal object ExpoIapLog {
|
|
11
9
|
private const val TAG = "ExpoIap"
|
|
12
|
-
private val emittedDeprecations: MutableSet<String> =
|
|
13
|
-
Collections.newSetFromMap(ConcurrentHashMap())
|
|
14
10
|
private val SENSITIVE_KEY_FRAGMENTS =
|
|
15
11
|
setOf(
|
|
16
12
|
"token",
|
|
@@ -57,16 +53,6 @@ internal object ExpoIapLog {
|
|
|
57
53
|
Log.w(TAG, message)
|
|
58
54
|
}
|
|
59
55
|
|
|
60
|
-
fun deprecation(
|
|
61
|
-
key: String,
|
|
62
|
-
message: String,
|
|
63
|
-
) {
|
|
64
|
-
if (!emittedDeprecations.add(key)) {
|
|
65
|
-
return
|
|
66
|
-
}
|
|
67
|
-
Log.w(TAG, message)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
56
|
fun debug(message: String) {
|
|
71
57
|
if (BuildConfig.DEBUG || Log.isLoggable(TAG, Log.DEBUG)) {
|
|
72
58
|
Log.d(TAG, message)
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
package expo.modules.iap
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
|
-
import dev.hyo.openiap.AndroidSubscriptionOfferInput
|
|
5
4
|
import dev.hyo.openiap.DeepLinkOptions
|
|
6
5
|
import dev.hyo.openiap.FetchProductsResultAll
|
|
7
6
|
import dev.hyo.openiap.FetchProductsResultProducts
|
|
@@ -28,6 +27,7 @@ import expo.modules.kotlin.Promise
|
|
|
28
27
|
import expo.modules.kotlin.exception.Exceptions
|
|
29
28
|
import expo.modules.kotlin.modules.Module
|
|
30
29
|
import expo.modules.kotlin.modules.ModuleDefinition
|
|
30
|
+
import kotlinx.coroutines.CancellationException
|
|
31
31
|
import kotlinx.coroutines.CoroutineScope
|
|
32
32
|
import kotlinx.coroutines.Dispatchers
|
|
33
33
|
import kotlinx.coroutines.Job
|
|
@@ -59,6 +59,20 @@ internal suspend fun endExpoConnectionWithCleanup(
|
|
|
59
59
|
internal fun endConnectionErrorCode(error: Exception): String =
|
|
60
60
|
(error as? OpenIapError)?.code ?: OpenIapError.ServiceDisconnected.CODE
|
|
61
61
|
|
|
62
|
+
internal fun deliverPurchaseRequestFailure(
|
|
63
|
+
reachedOpenIapRequest: Boolean,
|
|
64
|
+
isCancellation: Boolean = false,
|
|
65
|
+
errorCode: String,
|
|
66
|
+
errorEnvelope: String,
|
|
67
|
+
emitLocalError: () -> Unit,
|
|
68
|
+
rejectPendingPromises: (String, String) -> Unit,
|
|
69
|
+
) {
|
|
70
|
+
if (!reachedOpenIapRequest && !isCancellation) {
|
|
71
|
+
emitLocalError()
|
|
72
|
+
}
|
|
73
|
+
rejectPendingPromises(errorCode, errorEnvelope)
|
|
74
|
+
}
|
|
75
|
+
|
|
62
76
|
class ExpoIapModule : Module() {
|
|
63
77
|
companion object {
|
|
64
78
|
const val TAG = "ExpoIapModule"
|
|
@@ -203,15 +217,15 @@ class ExpoIapModule : Module() {
|
|
|
203
217
|
}
|
|
204
218
|
}
|
|
205
219
|
|
|
206
|
-
AsyncFunction("fetchProducts") { type: String,
|
|
220
|
+
AsyncFunction("fetchProducts") { type: String, skus: Array<String>, promise: Promise ->
|
|
207
221
|
ExpoIapLog.payload(
|
|
208
222
|
"fetchProducts",
|
|
209
|
-
mapOf("type" to type, "skus" to
|
|
223
|
+
mapOf("type" to type, "skus" to skus.toList()),
|
|
210
224
|
)
|
|
211
225
|
scope.launch {
|
|
212
226
|
try {
|
|
213
227
|
val queryType = ExpoIapHelper.parseProductQueryType(type)
|
|
214
|
-
val request = ProductRequest(
|
|
228
|
+
val request = ProductRequest(skus.toList(), queryType)
|
|
215
229
|
val result = openIap.fetchProducts(request)
|
|
216
230
|
val payload =
|
|
217
231
|
when (result) {
|
|
@@ -232,7 +246,7 @@ class ExpoIapModule : Module() {
|
|
|
232
246
|
"message" to "Failed to query products",
|
|
233
247
|
"debugMessage" to e.message,
|
|
234
248
|
"platform" to "android",
|
|
235
|
-
"productIds" to
|
|
249
|
+
"productIds" to skus.toList(),
|
|
236
250
|
"productType" to type,
|
|
237
251
|
)
|
|
238
252
|
}
|
|
@@ -339,22 +353,8 @@ class ExpoIapModule : Module() {
|
|
|
339
353
|
else -> ProductQueryType.InApp
|
|
340
354
|
}
|
|
341
355
|
|
|
342
|
-
val fallbackOffers =
|
|
343
|
-
if (parsedParams.explicitSubscriptionOffers.isEmpty() && parsedParams.offerTokenArr.isNotEmpty()) {
|
|
344
|
-
parsedParams.skus.zip(parsedParams.offerTokenArr).mapNotNull { (sku, token) ->
|
|
345
|
-
if (token.isNotEmpty()) {
|
|
346
|
-
AndroidSubscriptionOfferInput(offerToken = token, sku = sku)
|
|
347
|
-
} else {
|
|
348
|
-
null
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
} else {
|
|
352
|
-
emptyList()
|
|
353
|
-
}
|
|
354
|
-
|
|
355
356
|
val subscriptionOffers =
|
|
356
|
-
|
|
357
|
-
.takeIf { it.isNotEmpty() }
|
|
357
|
+
parsedParams.explicitSubscriptionOffers.takeIf { it.isNotEmpty() }
|
|
358
358
|
|
|
359
359
|
val requestProps =
|
|
360
360
|
when (productType) {
|
|
@@ -367,7 +367,6 @@ class ExpoIapModule : Module() {
|
|
|
367
367
|
developerBillingOption = parsedParams.developerBillingOption,
|
|
368
368
|
originalExternalTransactionId = parsedParams.originalExternalTransactionId,
|
|
369
369
|
purchaseToken = parsedParams.purchaseToken,
|
|
370
|
-
replacementMode = parsedParams.replacementMode?.toInt(),
|
|
371
370
|
skus = parsedParams.skus,
|
|
372
371
|
subscriptionOffers = subscriptionOffers,
|
|
373
372
|
subscriptionProductReplacementParams = parsedParams.subscriptionProductReplacementParams,
|
|
@@ -403,9 +402,11 @@ class ExpoIapModule : Module() {
|
|
|
403
402
|
|
|
404
403
|
ExpoIapHelper.addPurchasePromise(promise)
|
|
405
404
|
scope.launch {
|
|
405
|
+
var reachedOpenIapRequest = false
|
|
406
406
|
try {
|
|
407
407
|
val activity = currentActivity
|
|
408
408
|
openIap.setActivity(activity)
|
|
409
|
+
reachedOpenIapRequest = true
|
|
409
410
|
val result = openIap.requestPurchase(requestProps)
|
|
410
411
|
val purchases =
|
|
411
412
|
when (result) {
|
|
@@ -431,23 +432,33 @@ class ExpoIapModule : Module() {
|
|
|
431
432
|
)
|
|
432
433
|
}
|
|
433
434
|
val errorCode = errorMap["code"] as? String ?: OpenIapError.PurchaseFailed.CODE
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
435
|
+
val errorEnvelope = ExpoIapHelper.serializeErrorEnvelope(errorMap)
|
|
436
|
+
deliverPurchaseRequestFailure(
|
|
437
|
+
reachedOpenIapRequest = reachedOpenIapRequest,
|
|
438
|
+
isCancellation = e is CancellationException,
|
|
439
|
+
errorCode = errorCode,
|
|
440
|
+
errorEnvelope = errorEnvelope,
|
|
441
|
+
emitLocalError = {
|
|
442
|
+
runCatching {
|
|
443
|
+
ExpoIapHelper.emitOrQueue(
|
|
444
|
+
this@ExpoIapModule,
|
|
445
|
+
scope,
|
|
446
|
+
connectionReady,
|
|
447
|
+
pendingEvents,
|
|
448
|
+
EVENT_PURCHASE_ERROR,
|
|
449
|
+
errorMap,
|
|
450
|
+
)
|
|
451
|
+
}.onFailure { ex ->
|
|
452
|
+
ExpoIapLog.failure("send PURCHASE_ERROR event requestPurchase", ex)
|
|
453
|
+
}
|
|
454
|
+
},
|
|
455
|
+
rejectPendingPromises = { code, message ->
|
|
456
|
+
ExpoIapHelper.rejectPurchasePromises(code, message, null)
|
|
457
|
+
},
|
|
450
458
|
)
|
|
459
|
+
if (e is CancellationException) {
|
|
460
|
+
throw e
|
|
461
|
+
}
|
|
451
462
|
}
|
|
452
463
|
}
|
|
453
464
|
}
|
|
@@ -486,65 +497,6 @@ class ExpoIapModule : Module() {
|
|
|
486
497
|
}
|
|
487
498
|
}
|
|
488
499
|
|
|
489
|
-
@Suppress("DEPRECATION")
|
|
490
|
-
AsyncFunction("checkAlternativeBillingAvailabilityAndroid") { promise: Promise ->
|
|
491
|
-
ExpoIapLog.payload("checkAlternativeBillingAvailabilityAndroid", null)
|
|
492
|
-
scope.launch {
|
|
493
|
-
try {
|
|
494
|
-
val isAvailable = openIap.checkAlternativeBillingAvailability()
|
|
495
|
-
ExpoIapLog.result("checkAlternativeBillingAvailabilityAndroid", isAvailable)
|
|
496
|
-
promise.resolve(isAvailable)
|
|
497
|
-
} catch (e: Exception) {
|
|
498
|
-
ExpoIapLog.failure("checkAlternativeBillingAvailabilityAndroid", e)
|
|
499
|
-
promise.reject(OpenIapError.ServiceUnavailable.CODE, e.message, null)
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
@Suppress("DEPRECATION")
|
|
505
|
-
AsyncFunction("showAlternativeBillingDialogAndroid") { promise: Promise ->
|
|
506
|
-
ExpoIapLog.payload("showAlternativeBillingDialogAndroid", null)
|
|
507
|
-
scope.launch {
|
|
508
|
-
try {
|
|
509
|
-
val activity =
|
|
510
|
-
runCatching { currentActivity }
|
|
511
|
-
.onFailure {
|
|
512
|
-
ExpoIapLog.failure("showAlternativeBillingDialogAndroid activity", it)
|
|
513
|
-
}.getOrNull() ?: run {
|
|
514
|
-
promise.reject(OpenIapError.ServiceUnavailable.CODE, "Activity not available", null)
|
|
515
|
-
return@launch
|
|
516
|
-
}
|
|
517
|
-
openIap.setActivity(activity)
|
|
518
|
-
val userAccepted = openIap.showAlternativeBillingInformationDialog(activity)
|
|
519
|
-
ExpoIapLog.result("showAlternativeBillingDialogAndroid", userAccepted)
|
|
520
|
-
promise.resolve(userAccepted)
|
|
521
|
-
} catch (e: Exception) {
|
|
522
|
-
ExpoIapLog.failure("showAlternativeBillingDialogAndroid", e)
|
|
523
|
-
promise.reject(OpenIapError.ServiceUnavailable.CODE, e.message, null)
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
@Suppress("DEPRECATION")
|
|
529
|
-
AsyncFunction("createAlternativeBillingTokenAndroid") { sku: String?, promise: Promise ->
|
|
530
|
-
ExpoIapLog.payload("createAlternativeBillingTokenAndroid", mapOf("sku" to sku))
|
|
531
|
-
scope.launch {
|
|
532
|
-
try {
|
|
533
|
-
// Note: OpenIapModule.createAlternativeBillingReportingToken() doesn't accept sku parameter
|
|
534
|
-
// The sku parameter is ignored for now - may be used in future versions
|
|
535
|
-
val token = openIap.createAlternativeBillingReportingToken()
|
|
536
|
-
ExpoIapLog.result(
|
|
537
|
-
"createAlternativeBillingTokenAndroid",
|
|
538
|
-
if (token.isNullOrBlank()) "<empty>" else "<token>",
|
|
539
|
-
)
|
|
540
|
-
promise.resolve(token)
|
|
541
|
-
} catch (e: Exception) {
|
|
542
|
-
ExpoIapLog.failure("createAlternativeBillingTokenAndroid", e)
|
|
543
|
-
promise.reject(OpenIapError.ServiceUnavailable.CODE, e.message, null)
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
|
|
548
500
|
@Suppress("UNCHECKED_CAST")
|
|
549
501
|
AsyncFunction("verifyPurchase") { params: Map<String, Any?>, promise: Promise ->
|
|
550
502
|
ExpoIapLog.payload("verifyPurchase", params)
|
package/build/index.d.ts
CHANGED
|
@@ -41,11 +41,7 @@ type ExpoIapEmitter = {
|
|
|
41
41
|
removeListener<E extends OpenIapEvent>(eventName: E, listener: ExpoIapEventListener<E>): void;
|
|
42
42
|
};
|
|
43
43
|
export declare const emitter: ExpoIapEmitter;
|
|
44
|
-
|
|
45
|
-
* Accepts the legacy 'inapp' alias for backward compatibility. The alias is
|
|
46
|
-
* deprecated and scheduled for removal in expo-iap 5.0.0 — use 'in-app'.
|
|
47
|
-
*/
|
|
48
|
-
export type ProductTypeInput = ProductQueryType | 'inapp';
|
|
44
|
+
export type ProductTypeInput = ProductQueryType;
|
|
49
45
|
export declare const purchaseUpdatedListener: (listener: (event: Purchase) => void, options?: PurchaseUpdatedListenerOptions | null) => {
|
|
50
46
|
remove: () => void;
|
|
51
47
|
};
|
|
@@ -373,20 +369,6 @@ export declare const restorePurchases: MutationField<'restorePurchases'>;
|
|
|
373
369
|
* @see {@link https://openiap.dev/docs/apis/deep-link-to-subscriptions}
|
|
374
370
|
*/
|
|
375
371
|
export declare const deepLinkToSubscriptions: MutationField<'deepLinkToSubscriptions'>;
|
|
376
|
-
/**
|
|
377
|
-
* Internal receipt validation function (NOT RECOMMENDED for production use)
|
|
378
|
-
*
|
|
379
|
-
* WARNING: This function performs client-side validation which is NOT secure.
|
|
380
|
-
* For production apps, always validate receipts on your secure server:
|
|
381
|
-
* - iOS: Send receipt data to Apple's verification endpoint from your server
|
|
382
|
-
* - Android: Use Google Play Developer API with service account credentials
|
|
383
|
-
*
|
|
384
|
-
* @deprecated Use verifyPurchase instead. This function will be removed in
|
|
385
|
-
* expo-iap 5.0.0.
|
|
386
|
-
*
|
|
387
|
-
* @see {@link https://openiap.dev/docs/apis/validate-receipt}
|
|
388
|
-
*/
|
|
389
|
-
export declare const validateReceipt: MutationField<'validateReceipt'>;
|
|
390
372
|
/**
|
|
391
373
|
* Verify purchase with the configured providers
|
|
392
374
|
*
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|