expo-iap 4.7.2 → 5.0.0
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 +5 -80
- package/build/index.d.ts +1 -19
- package/build/index.d.ts.map +1 -1
- package/build/index.js +14 -89
- 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 +10 -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 +3 -102
- package/build/modules/ios.js.map +1 -1
- package/build/types.d.ts +76 -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 +7 -50
- package/build/useIAP.js.map +1 -1
- 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/vega-adapter.d.ts +1 -1
- package/build/vega-adapter.d.ts.map +1 -1
- package/build/vega-adapter.js +8 -28
- package/build/vega-adapter.js.map +1 -1
- package/ios/ExpoIapHelper.swift +19 -55
- package/ios/ExpoIapLog.swift +0 -11
- package/ios/ExpoIapModule.swift +12 -46
- 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 +12 -93
- package/src/index.ts +13 -125
- package/src/modules/android.ts +0 -152
- package/src/modules/ios.ts +4 -122
- package/src/types.ts +77 -295
- package/src/useIAP.ts +9 -108
- package/src/utils/errorMapping.ts +31 -7
- package/src/vega-adapter.ts +13 -34
- 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
|
|
@@ -203,15 +202,15 @@ class ExpoIapModule : Module() {
|
|
|
203
202
|
}
|
|
204
203
|
}
|
|
205
204
|
|
|
206
|
-
AsyncFunction("fetchProducts") { type: String,
|
|
205
|
+
AsyncFunction("fetchProducts") { type: String, skus: Array<String>, promise: Promise ->
|
|
207
206
|
ExpoIapLog.payload(
|
|
208
207
|
"fetchProducts",
|
|
209
|
-
mapOf("type" to type, "skus" to
|
|
208
|
+
mapOf("type" to type, "skus" to skus.toList()),
|
|
210
209
|
)
|
|
211
210
|
scope.launch {
|
|
212
211
|
try {
|
|
213
212
|
val queryType = ExpoIapHelper.parseProductQueryType(type)
|
|
214
|
-
val request = ProductRequest(
|
|
213
|
+
val request = ProductRequest(skus.toList(), queryType)
|
|
215
214
|
val result = openIap.fetchProducts(request)
|
|
216
215
|
val payload =
|
|
217
216
|
when (result) {
|
|
@@ -232,7 +231,7 @@ class ExpoIapModule : Module() {
|
|
|
232
231
|
"message" to "Failed to query products",
|
|
233
232
|
"debugMessage" to e.message,
|
|
234
233
|
"platform" to "android",
|
|
235
|
-
"productIds" to
|
|
234
|
+
"productIds" to skus.toList(),
|
|
236
235
|
"productType" to type,
|
|
237
236
|
)
|
|
238
237
|
}
|
|
@@ -339,22 +338,8 @@ class ExpoIapModule : Module() {
|
|
|
339
338
|
else -> ProductQueryType.InApp
|
|
340
339
|
}
|
|
341
340
|
|
|
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
341
|
val subscriptionOffers =
|
|
356
|
-
|
|
357
|
-
.takeIf { it.isNotEmpty() }
|
|
342
|
+
parsedParams.explicitSubscriptionOffers.takeIf { it.isNotEmpty() }
|
|
358
343
|
|
|
359
344
|
val requestProps =
|
|
360
345
|
when (productType) {
|
|
@@ -367,7 +352,6 @@ class ExpoIapModule : Module() {
|
|
|
367
352
|
developerBillingOption = parsedParams.developerBillingOption,
|
|
368
353
|
originalExternalTransactionId = parsedParams.originalExternalTransactionId,
|
|
369
354
|
purchaseToken = parsedParams.purchaseToken,
|
|
370
|
-
replacementMode = parsedParams.replacementMode?.toInt(),
|
|
371
355
|
skus = parsedParams.skus,
|
|
372
356
|
subscriptionOffers = subscriptionOffers,
|
|
373
357
|
subscriptionProductReplacementParams = parsedParams.subscriptionProductReplacementParams,
|
|
@@ -486,65 +470,6 @@ class ExpoIapModule : Module() {
|
|
|
486
470
|
}
|
|
487
471
|
}
|
|
488
472
|
|
|
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
473
|
@Suppress("UNCHECKED_CAST")
|
|
549
474
|
AsyncFunction("verifyPurchase") { params: Map<String, Any?>, promise: Promise ->
|
|
550
475
|
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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"}
|
package/build/index.js
CHANGED
|
@@ -3,10 +3,9 @@ import { Platform } from 'react-native';
|
|
|
3
3
|
// Internal modules
|
|
4
4
|
import ExpoIapModule, { getNativeModule } from './ExpoIapModule';
|
|
5
5
|
import { isVegaOS } from './vega';
|
|
6
|
-
import { isProductIOS,
|
|
7
|
-
import { isProductAndroid,
|
|
6
|
+
import { isProductIOS, deepLinkToSubscriptionsIOS, syncIOS } from './modules/ios';
|
|
7
|
+
import { isProductAndroid, deepLinkToSubscriptionsAndroid, } from './modules/android';
|
|
8
8
|
import { ExpoIapConsole } from './utils/debug';
|
|
9
|
-
import { warnLegacyOnce } from './utils/deprecation';
|
|
10
9
|
import { ErrorCode } from './types';
|
|
11
10
|
import { createPurchaseError, createPurchaseErrorFromNativeException, OPENIAP_ERROR_ENVELOPE_PREFIX, } from './utils/errorMapping';
|
|
12
11
|
// Export all types
|
|
@@ -118,10 +117,7 @@ const configurePurchaseUpdatedListenerOptionsIOS = (dedupeTransactionIOS) => {
|
|
|
118
117
|
});
|
|
119
118
|
};
|
|
120
119
|
const normalizeProductType = (type) => {
|
|
121
|
-
if (type === '
|
|
122
|
-
warnLegacyOnce('product-type.inapp', "'inapp' product type is deprecated and will be removed in expo-iap 5.0.0. Use 'in-app' instead.");
|
|
123
|
-
}
|
|
124
|
-
if (!type || type === 'inapp' || type === 'in-app') {
|
|
120
|
+
if (!type || type === 'in-app') {
|
|
125
121
|
return {
|
|
126
122
|
canonical: 'in-app',
|
|
127
123
|
native: 'in-app',
|
|
@@ -141,18 +137,6 @@ const normalizeProductType = (type) => {
|
|
|
141
137
|
}
|
|
142
138
|
throw new Error(`Unsupported product type: ${type}`);
|
|
143
139
|
};
|
|
144
|
-
const normalizePurchasePlatform = (purchase) => {
|
|
145
|
-
const platform = purchase.platform;
|
|
146
|
-
if (typeof platform !== 'string') {
|
|
147
|
-
return purchase;
|
|
148
|
-
}
|
|
149
|
-
const lowered = platform.toLowerCase();
|
|
150
|
-
if (lowered === platform || (lowered !== 'ios' && lowered !== 'android')) {
|
|
151
|
-
return purchase;
|
|
152
|
-
}
|
|
153
|
-
return { ...purchase, platform: lowered };
|
|
154
|
-
};
|
|
155
|
-
const normalizePurchaseArray = (purchases) => purchases.map((purchase) => normalizePurchasePlatform(purchase));
|
|
156
140
|
const isAndroidStoreRuntime = () => {
|
|
157
141
|
return Platform.OS === 'android' || isVegaOS();
|
|
158
142
|
};
|
|
@@ -165,7 +149,7 @@ export const purchaseUpdatedListener = (listener, options) => {
|
|
|
165
149
|
};
|
|
166
150
|
let listenerDedupeGenerationIOS = purchaseUpdatedDedupeGenerationIOS;
|
|
167
151
|
const wrappedListener = (event) => {
|
|
168
|
-
const normalized =
|
|
152
|
+
const normalized = event;
|
|
169
153
|
if (Platform.OS === 'ios') {
|
|
170
154
|
if (listenerDedupeGenerationIOS !== purchaseUpdatedDedupeGenerationIOS) {
|
|
171
155
|
clearPurchaseUpdatedDedupeHistoryIOS(listenerDedupeHistoryIOS);
|
|
@@ -364,10 +348,8 @@ export const developerProvidedBillingListenerAndroid = (listener) => {
|
|
|
364
348
|
* ```
|
|
365
349
|
*/
|
|
366
350
|
export const subscriptionBillingIssueListener = (listener) => {
|
|
367
|
-
// Mirror purchaseUpdatedListener's platform normalization so consumers get
|
|
368
|
-
// a consistent payload regardless of native casing.
|
|
369
351
|
const wrappedListener = (event) => {
|
|
370
|
-
listener(
|
|
352
|
+
listener(event);
|
|
371
353
|
};
|
|
372
354
|
return emitter.addListener(OpenIapEvent.SubscriptionBillingIssue, wrappedListener);
|
|
373
355
|
};
|
|
@@ -549,7 +531,7 @@ export const getAvailablePurchases = async (options) => {
|
|
|
549
531
|
};
|
|
550
532
|
if (isVegaOS()) {
|
|
551
533
|
const purchases = await ExpoIapModule.getAvailableItems(normalizedOptions);
|
|
552
|
-
return
|
|
534
|
+
return purchases;
|
|
553
535
|
}
|
|
554
536
|
let purchases;
|
|
555
537
|
if (Platform.OS === 'ios') {
|
|
@@ -561,7 +543,7 @@ export const getAvailablePurchases = async (options) => {
|
|
|
561
543
|
else {
|
|
562
544
|
throw unsupportedPlatformError();
|
|
563
545
|
}
|
|
564
|
-
return
|
|
546
|
+
return purchases;
|
|
565
547
|
};
|
|
566
548
|
/**
|
|
567
549
|
* Get all active subscriptions with detailed information.
|
|
@@ -665,23 +647,9 @@ export const getStorefront = async () => {
|
|
|
665
647
|
};
|
|
666
648
|
function normalizeRequestProps(request, platform) {
|
|
667
649
|
if (platform === 'ios') {
|
|
668
|
-
|
|
669
|
-
return request.apple;
|
|
670
|
-
}
|
|
671
|
-
if (Object.prototype.hasOwnProperty.call(request, 'ios')) {
|
|
672
|
-
warnLegacyOnce('request-purchase.ios', '`request.ios` is deprecated and will be removed in expo-iap 5.0.0. Use `request.apple` instead.');
|
|
673
|
-
return request.ios;
|
|
674
|
-
}
|
|
675
|
-
return undefined;
|
|
650
|
+
return request.apple;
|
|
676
651
|
}
|
|
677
|
-
|
|
678
|
-
return request.google;
|
|
679
|
-
}
|
|
680
|
-
if (Object.prototype.hasOwnProperty.call(request, 'android')) {
|
|
681
|
-
warnLegacyOnce('request-purchase.android', '`request.android` is deprecated and will be removed in expo-iap 5.0.0. Use `request.google` instead.');
|
|
682
|
-
return request.android;
|
|
683
|
-
}
|
|
684
|
-
return undefined;
|
|
652
|
+
return request.google;
|
|
685
653
|
}
|
|
686
654
|
/**
|
|
687
655
|
* Initiate a purchase or subscription flow. The result is delivered through
|
|
@@ -733,7 +701,6 @@ export const requestPurchase = async (args) => {
|
|
|
733
701
|
const payload = {
|
|
734
702
|
type: canonical === 'in-app' ? 'in-app' : 'subs',
|
|
735
703
|
request: { apple: normalizedRequest },
|
|
736
|
-
useAlternativeBilling: args.useAlternativeBilling,
|
|
737
704
|
};
|
|
738
705
|
const purchase = (await invokeNativeWithPurchaseError(() => ExpoIapModule.requestPurchase(payload), 'ios', {
|
|
739
706
|
code: ErrorCode.PurchaseError,
|
|
@@ -743,10 +710,10 @@ export const requestPurchase = async (args) => {
|
|
|
743
710
|
productType: canonical,
|
|
744
711
|
}));
|
|
745
712
|
if (Array.isArray(purchase)) {
|
|
746
|
-
return
|
|
713
|
+
return purchase;
|
|
747
714
|
}
|
|
748
715
|
if (purchase) {
|
|
749
|
-
return
|
|
716
|
+
return purchase;
|
|
750
717
|
}
|
|
751
718
|
return canonical === 'subs' ? [] : null;
|
|
752
719
|
}
|
|
@@ -770,7 +737,6 @@ export const requestPurchase = async (args) => {
|
|
|
770
737
|
type: native,
|
|
771
738
|
skus,
|
|
772
739
|
purchaseToken: undefined,
|
|
773
|
-
replacementMode: -1,
|
|
774
740
|
obfuscatedAccountId: obfuscatedAccountId,
|
|
775
741
|
obfuscatedProfileId: obfuscatedProfileId,
|
|
776
742
|
offerToken: offerToken,
|
|
@@ -783,7 +749,7 @@ export const requestPurchase = async (args) => {
|
|
|
783
749
|
productIds: skus,
|
|
784
750
|
productType: canonical,
|
|
785
751
|
}));
|
|
786
|
-
return
|
|
752
|
+
return result;
|
|
787
753
|
}
|
|
788
754
|
if (canonical === 'subs') {
|
|
789
755
|
const normalizedRequest = normalizeRequestProps(request, 'android');
|
|
@@ -799,16 +765,14 @@ export const requestPurchase = async (args) => {
|
|
|
799
765
|
' })\n\n' +
|
|
800
766
|
'See: https://openiap.dev/docs/apis/request-purchase');
|
|
801
767
|
}
|
|
802
|
-
const { skus, obfuscatedAccountId, obfuscatedProfileId, isOfferPersonalized, subscriptionOffers,
|
|
768
|
+
const { skus, obfuscatedAccountId, obfuscatedProfileId, isOfferPersonalized, subscriptionOffers, purchaseToken: purchaseTokenInput, originalExternalTransactionId, developerBillingOption, subscriptionProductReplacementParams, } = normalizedRequest;
|
|
803
769
|
const normalizedOffers = subscriptionOffers ?? [];
|
|
804
|
-
const replacementMode = replacementModeInput ?? -1;
|
|
805
770
|
const purchaseToken = purchaseTokenInput ?? undefined;
|
|
806
771
|
const result = (await invokeNativeWithPurchaseError(() => ExpoIapModule.requestPurchase({
|
|
807
772
|
type: native,
|
|
808
773
|
skus,
|
|
809
774
|
purchaseToken,
|
|
810
775
|
originalExternalTransactionId: originalExternalTransactionId ?? undefined,
|
|
811
|
-
replacementMode,
|
|
812
776
|
obfuscatedAccountId: obfuscatedAccountId,
|
|
813
777
|
obfuscatedProfileId: obfuscatedProfileId,
|
|
814
778
|
subscriptionOffers: normalizedOffers,
|
|
@@ -822,7 +786,7 @@ export const requestPurchase = async (args) => {
|
|
|
822
786
|
productIds: skus,
|
|
823
787
|
productType: canonical,
|
|
824
788
|
}));
|
|
825
|
-
return
|
|
789
|
+
return result;
|
|
826
790
|
}
|
|
827
791
|
throw new Error("Invalid request for Android: Expected a valid request object with 'skus' array.");
|
|
828
792
|
}
|
|
@@ -934,45 +898,6 @@ export const deepLinkToSubscriptions = async (options) => {
|
|
|
934
898
|
}
|
|
935
899
|
throw unsupportedPlatformError();
|
|
936
900
|
};
|
|
937
|
-
/**
|
|
938
|
-
* Internal receipt validation function (NOT RECOMMENDED for production use)
|
|
939
|
-
*
|
|
940
|
-
* WARNING: This function performs client-side validation which is NOT secure.
|
|
941
|
-
* For production apps, always validate receipts on your secure server:
|
|
942
|
-
* - iOS: Send receipt data to Apple's verification endpoint from your server
|
|
943
|
-
* - Android: Use Google Play Developer API with service account credentials
|
|
944
|
-
*
|
|
945
|
-
* @deprecated Use verifyPurchase instead. This function will be removed in
|
|
946
|
-
* expo-iap 5.0.0.
|
|
947
|
-
*
|
|
948
|
-
* @see {@link https://openiap.dev/docs/apis/validate-receipt}
|
|
949
|
-
*/
|
|
950
|
-
export const validateReceipt = async (options) => {
|
|
951
|
-
const { apple, google } = options;
|
|
952
|
-
if (Platform.OS === 'ios') {
|
|
953
|
-
if (!apple?.sku) {
|
|
954
|
-
throw new Error('iOS validation requires apple.sku');
|
|
955
|
-
}
|
|
956
|
-
return validateReceiptIOS({ apple: { sku: apple.sku } });
|
|
957
|
-
}
|
|
958
|
-
if (Platform.OS === 'android') {
|
|
959
|
-
if (!google ||
|
|
960
|
-
!google.sku ||
|
|
961
|
-
!google.packageName ||
|
|
962
|
-
!google.purchaseToken ||
|
|
963
|
-
!google.accessToken) {
|
|
964
|
-
throw new Error('Android validation requires google.sku, google.packageName, google.purchaseToken, and google.accessToken');
|
|
965
|
-
}
|
|
966
|
-
return validateReceiptAndroid({
|
|
967
|
-
packageName: google.packageName,
|
|
968
|
-
productId: google.sku,
|
|
969
|
-
productToken: google.purchaseToken,
|
|
970
|
-
accessToken: google.accessToken,
|
|
971
|
-
isSub: google.isSub ?? undefined,
|
|
972
|
-
});
|
|
973
|
-
}
|
|
974
|
-
throw unsupportedPlatformError();
|
|
975
|
-
};
|
|
976
901
|
/**
|
|
977
902
|
* Verify purchase with the configured providers
|
|
978
903
|
*
|