react-native-iap 12.14.1 → 12.14.2-rc.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.
@@ -6,7 +6,7 @@ RNIap_buildToolsVersion=33.0.0
6
6
  RNIap_ndkversion=23.1.7779620
7
7
  RNIap_playServicesVersion=18.1.0
8
8
  RNIap_amazonSdkVersion=3.0.4
9
- RNIap_playBillingSdkVersion=6.1.0
9
+ RNIap_playBillingSdkVersion=7.0.0
10
10
 
11
11
  android.useAndroidX=true
12
12
  android.enableJetifier=true
@@ -6,7 +6,10 @@ import com.amazon.device.iap.model.FulfillmentResult
6
6
  import com.amazon.device.iap.model.RequestId
7
7
 
8
8
  interface PurchasingServiceProxy {
9
- fun registerListener(var0: Context?, var1: PurchasingListener?)
9
+ fun registerListener(
10
+ var0: Context?,
11
+ var1: PurchasingListener?,
12
+ )
10
13
 
11
14
  fun getUserData(): RequestId
12
15
 
@@ -16,5 +19,8 @@ interface PurchasingServiceProxy {
16
19
 
17
20
  fun getPurchaseUpdates(var0: Boolean): RequestId
18
21
 
19
- fun notifyFulfillment(var0: String?, var1: FulfillmentResult?)
22
+ fun notifyFulfillment(
23
+ var0: String?,
24
+ var1: FulfillmentResult?,
25
+ )
20
26
  }
@@ -7,27 +7,21 @@ import com.amazon.device.iap.model.FulfillmentResult
7
7
  import com.amazon.device.iap.model.RequestId
8
8
 
9
9
  class PurchasingServiceProxyAmazonImpl : PurchasingServiceProxy {
10
- override fun registerListener(var0: Context?, var1: PurchasingListener?) {
11
- return PurchasingService.registerListener(var0, var1)
12
- }
10
+ override fun registerListener(
11
+ var0: Context?,
12
+ var1: PurchasingListener?,
13
+ ) = PurchasingService.registerListener(var0, var1)
13
14
 
14
- override fun getUserData(): RequestId {
15
- return PurchasingService.getUserData()
16
- }
15
+ override fun getUserData(): RequestId = PurchasingService.getUserData()
17
16
 
18
- override fun purchase(var0: String?): RequestId {
19
- return PurchasingService.purchase(var0)
20
- }
17
+ override fun purchase(var0: String?): RequestId = PurchasingService.purchase(var0)
21
18
 
22
- override fun getProductData(var0: Set<String?>?): RequestId {
23
- return PurchasingService.getProductData(var0)
24
- }
19
+ override fun getProductData(var0: Set<String?>?): RequestId = PurchasingService.getProductData(var0)
25
20
 
26
- override fun getPurchaseUpdates(var0: Boolean): RequestId {
27
- return PurchasingService.getPurchaseUpdates(var0)
28
- }
21
+ override fun getPurchaseUpdates(var0: Boolean): RequestId = PurchasingService.getPurchaseUpdates(var0)
29
22
 
30
- override fun notifyFulfillment(var0: String?, var1: FulfillmentResult?) {
31
- return PurchasingService.notifyFulfillment(var0, var1)
32
- }
23
+ override fun notifyFulfillment(
24
+ var0: String?,
25
+ var1: FulfillmentResult?,
26
+ ) = PurchasingService.notifyFulfillment(var0, var1)
33
27
  }
@@ -22,7 +22,6 @@ class RNIapAmazonListener(
22
22
  var eventSender: EventSender?,
23
23
  var purchasingService: PurchasingServiceProxy?,
24
24
  ) : PurchasingListener {
25
-
26
25
  override fun onProductDataResponse(response: ProductDataResponse) {
27
26
  when (response.requestStatus) {
28
27
  ProductDataResponse.RequestStatus.SUCCESSFUL -> {
@@ -170,7 +169,10 @@ class RNIapAmazonListener(
170
169
  }
171
170
  }
172
171
 
173
- private fun receiptToMap(userData: UserData, receipt: Receipt): WritableMap {
172
+ private fun receiptToMap(
173
+ userData: UserData,
174
+ receipt: Receipt,
175
+ ): WritableMap {
174
176
  val item = Arguments.createMap()
175
177
  item.putString("productId", receipt.sku)
176
178
  item.putDouble("transactionDate", receipt.purchaseDate.time.toDouble())
@@ -22,28 +22,33 @@ class RNIapAmazonModule(
22
22
  private val reactContext: ReactApplicationContext,
23
23
  private val purchasingService: PurchasingServiceProxy = PurchasingServiceProxyAmazonImpl(),
24
24
  private var eventSender: EventSender? = null,
25
- ) :
26
- ReactContextBaseJavaModule(reactContext) {
27
- override fun getName(): String {
28
- return TAG
29
- }
25
+ ) : ReactContextBaseJavaModule(reactContext) {
26
+ override fun getName(): String = TAG
30
27
 
31
28
  @ReactMethod
32
29
  fun initConnection(promise: Promise) {
33
30
  if (RNIapActivityListener.amazonListener == null) {
34
- promise.safeReject(PromiseUtils.E_DEVELOPER_ERROR, Exception("RNIapActivityListener is not registered in your MainActivity.onCreate"))
31
+ promise.safeReject(
32
+ PromiseUtils.E_DEVELOPER_ERROR,
33
+ Exception("RNIapActivityListener is not registered in your MainActivity.onCreate"),
34
+ )
35
35
  return
36
36
  }
37
37
  if (eventSender == null) {
38
- eventSender = object : EventSender {
39
- private val rctDeviceEventEmitter = reactContext
40
- .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
41
-
42
- override fun sendEvent(eventName: String, params: WritableMap?) {
43
- rctDeviceEventEmitter
44
- .emit(eventName, params)
38
+ eventSender =
39
+ object : EventSender {
40
+ private val rctDeviceEventEmitter =
41
+ reactContext
42
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
43
+
44
+ override fun sendEvent(
45
+ eventName: String,
46
+ params: WritableMap?,
47
+ ) {
48
+ rctDeviceEventEmitter
49
+ .emit(eventName, params)
50
+ }
45
51
  }
46
- }
47
52
  }
48
53
  RNIapActivityListener.amazonListener?.eventSender = eventSender
49
54
  RNIapActivityListener.amazonListener?.purchasingService = purchasingService
@@ -109,7 +114,11 @@ class RNIapAmazonModule(
109
114
  }
110
115
 
111
116
  @ReactMethod
112
- fun getItemsByType(type: String?, skuArr: ReadableArray, promise: Promise) {
117
+ fun getItemsByType(
118
+ type: String?,
119
+ skuArr: ReadableArray,
120
+ promise: Promise,
121
+ ) {
113
122
  val productSkus: MutableSet<String> = HashSet()
114
123
  var ii = 0
115
124
  val skuSize = skuArr.size()
@@ -171,7 +180,10 @@ class RNIapAmazonModule(
171
180
  * From https://amazon.developer.forums.answerhub.com/questions/175720/how-to-open-store-subscription-screen-directly-use.html?childToView=179402#answer-179402
172
181
  */
173
182
  @ReactMethod
174
- fun deepLinkToSubscriptions(isAmazonDevice: Boolean, promise: Promise) {
183
+ fun deepLinkToSubscriptions(
184
+ isAmazonDevice: Boolean,
185
+ promise: Promise,
186
+ ) {
175
187
  if (isAmazonDevice) {
176
188
  val intent =
177
189
  Intent("android.intent.action.VIEW", Uri.parse("amzn://apps/library/subscriptions"))
@@ -210,22 +222,26 @@ class RNIapAmazonModule(
210
222
 
211
223
  const val TAG = "RNIapAmazonModule"
212
224
  }
225
+
213
226
  init {
214
- val lifecycleEventListener: LifecycleEventListener = object : LifecycleEventListener {
215
- /**
216
- * From https://developer.amazon.com/docs/in-app-purchasing/iap-implement-iap.html#getpurchaseupdates-responses
217
- * We should fetch updates on resume
218
- */
219
- override fun onHostResume() {
220
- if (RNIapActivityListener.hasListener) {
221
- purchasingService.getUserData()
222
- purchasingService.getPurchaseUpdates(false)
227
+ val lifecycleEventListener: LifecycleEventListener =
228
+ object : LifecycleEventListener {
229
+ /**
230
+ * From https://developer.amazon.com/docs/in-app-purchasing/iap-implement-iap.html#getpurchaseupdates-responses
231
+ * We should fetch updates on resume
232
+ */
233
+ override fun onHostResume() {
234
+ if (RNIapActivityListener.hasListener) {
235
+ purchasingService.getUserData()
236
+ purchasingService.getPurchaseUpdates(false)
237
+ }
238
+ }
239
+
240
+ override fun onHostPause() {}
241
+
242
+ override fun onHostDestroy() {
223
243
  }
224
244
  }
225
- override fun onHostPause() {}
226
- override fun onHostDestroy() {
227
- }
228
- }
229
245
  reactContext.addLifecycleEventListener(lifecycleEventListener)
230
246
  }
231
247
  }
@@ -7,10 +7,7 @@ import com.facebook.react.uimanager.ViewManager
7
7
  import java.util.ArrayList
8
8
 
9
9
  class RNIapPackage : ReactPackage {
10
-
11
- override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
12
- return emptyList()
13
- }
10
+ override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> = emptyList()
14
11
 
15
12
  override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
16
13
  val modules: MutableList<NativeModule> = ArrayList()
@@ -6,11 +6,18 @@ import java.util.HashMap
6
6
 
7
7
  object PromiseUtils {
8
8
  private val promises = HashMap<String, MutableList<Promise>>()
9
- fun addPromiseForKey(key: String, promise: Promise) {
9
+
10
+ fun addPromiseForKey(
11
+ key: String,
12
+ promise: Promise,
13
+ ) {
10
14
  promises.getOrPut(key) { mutableListOf() }.add(promise)
11
15
  }
12
16
 
13
- fun resolvePromisesForKey(key: String, value: Any?) {
17
+ fun resolvePromisesForKey(
18
+ key: String,
19
+ value: Any?,
20
+ ) {
14
21
  promises[key]?.forEach { promise ->
15
22
  promise.safeResolve(value)
16
23
  }
@@ -21,12 +21,21 @@ fun Promise.safeResolve(value: Any?) {
21
21
 
22
22
  fun Promise.safeReject(message: String) = this.safeReject(message, null, null)
23
23
 
24
- fun Promise.safeReject(code: String?, message: String?) = this.safeReject(code, message, null)
25
-
26
- fun Promise.safeReject(code: String?, throwable: Throwable?) =
27
- this.safeReject(code, null, throwable)
28
-
29
- fun Promise.safeReject(code: String?, message: String?, throwable: Throwable?) {
24
+ fun Promise.safeReject(
25
+ code: String?,
26
+ message: String?,
27
+ ) = this.safeReject(code, message, null)
28
+
29
+ fun Promise.safeReject(
30
+ code: String?,
31
+ throwable: Throwable?,
32
+ ) = this.safeReject(code, null, throwable)
33
+
34
+ fun Promise.safeReject(
35
+ code: String?,
36
+ message: String?,
37
+ throwable: Throwable?,
38
+ ) {
30
39
  try {
31
40
  this.reject(code, message, throwable)
32
41
  } catch (oce: ObjectAlreadyConsumedException) {
@@ -4,88 +4,104 @@ import android.util.Log
4
4
  import com.android.billingclient.api.BillingClient
5
5
  import com.facebook.react.bridge.Promise
6
6
 
7
- data class BillingResponse(val code: String, val message: String)
7
+ data class BillingResponse(
8
+ val code: String,
9
+ val message: String,
10
+ )
8
11
 
9
12
  object PlayUtils {
10
- fun rejectPromiseWithBillingError(promise: Promise, responseCode: Int) {
13
+ fun rejectPromiseWithBillingError(
14
+ promise: Promise,
15
+ responseCode: Int,
16
+ ) {
11
17
  val errorData = getBillingResponseData(responseCode)
12
18
  promise.safeReject(errorData.code, errorData.message)
13
19
  }
14
20
 
15
21
  fun getBillingResponseData(responseCode: Int): BillingResponse {
16
- val errorData = when (responseCode) {
17
- BillingClient.BillingResponseCode.FEATURE_NOT_SUPPORTED -> {
18
- BillingResponse(
19
- PromiseUtils.E_SERVICE_ERROR,
20
- "This feature is not available on your device.",
21
- )
22
+ val errorData =
23
+ when (responseCode) {
24
+ BillingClient.BillingResponseCode.FEATURE_NOT_SUPPORTED -> {
25
+ BillingResponse(
26
+ PromiseUtils.E_SERVICE_ERROR,
27
+ "This feature is not available on your device.",
28
+ )
29
+ }
30
+ BillingClient.BillingResponseCode.SERVICE_DISCONNECTED -> {
31
+ BillingResponse(
32
+ PromiseUtils.E_NETWORK_ERROR,
33
+ "The service is disconnected (check your internet connection.)",
34
+ )
35
+ }
36
+ BillingClient.BillingResponseCode.NETWORK_ERROR -> {
37
+ BillingResponse(
38
+ PromiseUtils.E_NETWORK_ERROR,
39
+ "You have problem with network connection.",
40
+ )
41
+ }
42
+ BillingClient.BillingResponseCode.OK -> {
43
+ BillingResponse(
44
+ "OK",
45
+ "",
46
+ )
47
+ }
48
+ BillingClient.BillingResponseCode.USER_CANCELED -> {
49
+ BillingResponse(
50
+ PromiseUtils.E_USER_CANCELLED,
51
+ "Payment is Cancelled.",
52
+ )
53
+ }
54
+ BillingClient.BillingResponseCode.SERVICE_UNAVAILABLE -> {
55
+ BillingResponse(
56
+ PromiseUtils.E_SERVICE_ERROR,
57
+ "The service is unreachable. This may be your internet connection, or the Play Store may be down.",
58
+ )
59
+ }
60
+ BillingClient.BillingResponseCode.BILLING_UNAVAILABLE -> {
61
+ BillingResponse(
62
+ PromiseUtils.E_SERVICE_ERROR,
63
+ "Billing is unavailable. This may be a problem with your device, or the Play Store may be down.",
64
+ )
65
+ }
66
+ BillingClient.BillingResponseCode.ITEM_UNAVAILABLE -> {
67
+ BillingResponse(
68
+ PromiseUtils.E_ITEM_UNAVAILABLE,
69
+ "That item is unavailable.",
70
+ )
71
+ }
72
+ BillingClient.BillingResponseCode.DEVELOPER_ERROR -> {
73
+ BillingResponse(
74
+ PromiseUtils.E_DEVELOPER_ERROR,
75
+ "Google is indicating that we have some issue connecting to payment.",
76
+ )
77
+ }
78
+ BillingClient.BillingResponseCode.ERROR -> {
79
+ BillingResponse(
80
+ PromiseUtils.E_UNKNOWN,
81
+ "An unknown or unexpected error has occurred. Please try again later.",
82
+ )
83
+ }
84
+ BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED -> {
85
+ BillingResponse(
86
+ PromiseUtils.E_ALREADY_OWNED,
87
+ "You already own this item.",
88
+ )
89
+ }
90
+ else -> {
91
+ BillingResponse(
92
+ PromiseUtils.E_UNKNOWN,
93
+ "Purchase failed with code: $responseCode",
94
+ )
95
+ }
22
96
  }
23
- BillingClient.BillingResponseCode.SERVICE_DISCONNECTED -> {
24
- BillingResponse(
25
- PromiseUtils.E_NETWORK_ERROR,
26
- "The service is disconnected (check your internet connection.)",
27
- )
28
- }
29
- BillingClient.BillingResponseCode.OK -> {
30
- BillingResponse(
31
- "OK",
32
- "",
33
- )
34
- }
35
- BillingClient.BillingResponseCode.USER_CANCELED -> {
36
- BillingResponse(
37
- PromiseUtils.E_USER_CANCELLED,
38
- "Payment is Cancelled.",
39
- )
40
- }
41
- BillingClient.BillingResponseCode.SERVICE_UNAVAILABLE -> {
42
- BillingResponse(
43
- PromiseUtils.E_SERVICE_ERROR,
44
- "The service is unreachable. This may be your internet connection, or the Play Store may be down.",
45
- )
46
- }
47
- BillingClient.BillingResponseCode.BILLING_UNAVAILABLE -> {
48
- BillingResponse(
49
- PromiseUtils.E_SERVICE_ERROR,
50
- "Billing is unavailable. This may be a problem with your device, or the Play Store may be down.",
51
- )
52
- }
53
- BillingClient.BillingResponseCode.ITEM_UNAVAILABLE -> {
54
- BillingResponse(
55
- PromiseUtils.E_ITEM_UNAVAILABLE,
56
- "That item is unavailable.",
57
- )
58
- }
59
- BillingClient.BillingResponseCode.DEVELOPER_ERROR -> {
60
- BillingResponse(
61
- PromiseUtils.E_DEVELOPER_ERROR,
62
- "Google is indicating that we have some issue connecting to payment.",
63
- )
64
- }
65
- BillingClient.BillingResponseCode.ERROR -> {
66
- BillingResponse(
67
- PromiseUtils.E_UNKNOWN,
68
- "An unknown or unexpected error has occurred. Please try again later.",
69
- )
70
- }
71
- BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED -> {
72
- BillingResponse(
73
- PromiseUtils.E_ALREADY_OWNED,
74
- "You already own this item.",
75
- )
76
- }
77
- else -> {
78
- BillingResponse(
79
- PromiseUtils.E_UNKNOWN,
80
- "Purchase failed with code: $responseCode",
81
- )
82
- }
83
- }
84
97
  Log.e(TAG, "Error Code : $responseCode")
85
98
  return errorData
86
99
  }
87
100
 
88
- fun rejectPromisesWithBillingError(key: String, responseCode: Int) {
101
+ fun rejectPromisesWithBillingError(
102
+ key: String,
103
+ responseCode: Int,
104
+ ) {
89
105
  val errorData = getBillingResponseData(responseCode)
90
106
  PromiseUtils.rejectPromisesForKey(key, errorData.code, errorData.message, null)
91
107
  }
@@ -34,21 +34,18 @@ import com.facebook.react.module.annotations.ReactModule
34
34
  import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter
35
35
  import com.google.android.gms.common.ConnectionResult
36
36
  import com.google.android.gms.common.GoogleApiAvailability
37
- import java.util.ArrayList
37
+
38
38
  @ReactModule(name = RNIapModule.TAG)
39
39
  class RNIapModule(
40
40
  private val reactContext: ReactApplicationContext,
41
41
  private val builder: BillingClient.Builder = BillingClient.newBuilder(reactContext).enablePendingPurchases(),
42
42
  private val googleApiAvailability: GoogleApiAvailability = GoogleApiAvailability.getInstance(),
43
- ) :
44
- ReactContextBaseJavaModule(reactContext),
43
+ ) : ReactContextBaseJavaModule(reactContext),
45
44
  PurchasesUpdatedListener {
46
-
47
45
  private var billingClientCache: BillingClient? = null
48
46
  private val skus: MutableMap<String, ProductDetails> = mutableMapOf()
49
- override fun getName(): String {
50
- return TAG
51
- }
47
+
48
+ override fun getName(): String = TAG
52
49
 
53
50
  fun ensureConnection(
54
51
  promise: Promise,
@@ -59,68 +56,73 @@ class RNIapModule(
59
56
  callback(billingClient)
60
57
  return
61
58
  } else {
62
- val nested = PromiseImpl(
63
- {
64
- if (it.isNotEmpty() && it[0] is Boolean && it[0] as Boolean) {
65
- val connectedBillingClient = billingClientCache
66
- if (connectedBillingClient?.isReady == true) {
67
- callback(connectedBillingClient)
59
+ val nested =
60
+ PromiseImpl(
61
+ {
62
+ if (it.isNotEmpty() && it[0] is Boolean && it[0] as Boolean) {
63
+ val connectedBillingClient = billingClientCache
64
+ if (connectedBillingClient?.isReady == true) {
65
+ callback(connectedBillingClient)
66
+ } else {
67
+ promise.safeReject(PromiseUtils.E_NOT_PREPARED, "Unable to auto-initialize connection")
68
+ }
68
69
  } else {
69
- promise.safeReject(PromiseUtils.E_NOT_PREPARED, "Unable to auto-initialize connection")
70
+ promise.safeReject(PromiseUtils.E_UNKNOWN, "ensureConnection - incorrect parameter in resolve")
71
+ Log.i(TAG, "Incorrect parameter in resolve")
72
+ }
73
+ },
74
+ {
75
+ var errorCode: String? = null
76
+ var errorMessage: String? = null
77
+ if (it.size > 1 && it[0] is String && it[1] is String) {
78
+ errorCode = it[0] as String
79
+ errorMessage = it[1] as String
80
+ } else if (it.isNotEmpty() && it[0] is WritableNativeMap) {
81
+ val errorMap = it[0] as WritableNativeMap
82
+ errorCode = errorMap.getString("code")
83
+ errorMessage = errorMap.getString("message")
70
84
  }
71
- } else {
72
- promise.safeReject(PromiseUtils.E_UNKNOWN, "ensureConnection - incorrect parameter in resolve")
73
- Log.i(TAG, "Incorrect parameter in resolve")
74
- }
75
- },
76
- {
77
- var errorCode: String? = null
78
- var errorMessage: String? = null
79
- if (it.size > 1 && it[0] is String && it[1] is String) {
80
- errorCode = it[0] as String
81
- errorMessage = it[1] as String
82
- } else if (it.isNotEmpty() && it[0] is WritableNativeMap) {
83
- val errorMap = it[0] as WritableNativeMap
84
- errorCode = errorMap.getString("code")
85
- errorMessage = errorMap.getString("message")
86
- }
87
85
 
88
- if (errorCode is String && errorMessage is String) {
89
- promise.safeReject(
90
- errorCode,
91
- errorMessage,
92
- )
93
- } else {
94
- promise.safeReject(PromiseUtils.E_UNKNOWN, "ensureConnection - incorrect parameter in reject")
95
- Log.i(TAG, "Incorrect parameters in reject")
96
- }
97
- },
98
- )
86
+ if (errorCode is String && errorMessage is String) {
87
+ promise.safeReject(
88
+ errorCode,
89
+ errorMessage,
90
+ )
91
+ } else {
92
+ promise.safeReject(PromiseUtils.E_UNKNOWN, "ensureConnection - incorrect parameter in reject")
93
+ Log.i(TAG, "Incorrect parameters in reject")
94
+ }
95
+ },
96
+ )
99
97
  initConnection(nested)
100
98
  }
101
99
  }
102
100
 
103
101
  @ReactMethod
104
- fun isFeatureSupported(feature: String, promise: Promise) {
102
+ fun isFeatureSupported(
103
+ feature: String,
104
+ promise: Promise,
105
+ ) {
105
106
  ensureConnection(
106
107
  promise,
107
108
  ) { billingClient ->
108
- val f = when (feature) {
109
- "IN_APP_MESSAGING" ->
110
- BillingClient.FeatureType.IN_APP_MESSAGING
111
- "PRICE_CHANGE_CONFIRMATION" ->
112
- BillingClient.FeatureType.PRICE_CHANGE_CONFIRMATION
113
- "PRODUCT_DETAILS" ->
114
- BillingClient.FeatureType.PRODUCT_DETAILS
115
- "SUBSCRIPTIONS" ->
116
- BillingClient.FeatureType.SUBSCRIPTIONS
117
- "SUBSCRIPTIONS_UPDATE" ->
118
- BillingClient.FeatureType.SUBSCRIPTIONS_UPDATE
119
- else -> {
120
- promise.safeReject("Invalid Feature name")
121
- return@ensureConnection
109
+ val f =
110
+ when (feature) {
111
+ "IN_APP_MESSAGING" ->
112
+ BillingClient.FeatureType.IN_APP_MESSAGING
113
+ "PRICE_CHANGE_CONFIRMATION" ->
114
+ BillingClient.FeatureType.PRICE_CHANGE_CONFIRMATION
115
+ "PRODUCT_DETAILS" ->
116
+ BillingClient.FeatureType.PRODUCT_DETAILS
117
+ "SUBSCRIPTIONS" ->
118
+ BillingClient.FeatureType.SUBSCRIPTIONS
119
+ "SUBSCRIPTIONS_UPDATE" ->
120
+ BillingClient.FeatureType.SUBSCRIPTIONS_UPDATE
121
+ else -> {
122
+ promise.safeReject("Invalid Feature name")
123
+ return@ensureConnection
124
+ }
122
125
  }
123
- }
124
126
  promise.safeResolve(billingClient.isFeatureSupported(f))
125
127
  }
126
128
  }
@@ -180,7 +182,9 @@ class RNIapModule(
180
182
  promise,
181
183
  ) { billingClient ->
182
184
  val consumeParams =
183
- ConsumeParams.newBuilder().setPurchaseToken(purchase.purchaseToken)
185
+ ConsumeParams
186
+ .newBuilder()
187
+ .setPurchaseToken(purchase.purchaseToken)
184
188
  .build()
185
189
  val listener =
186
190
  ConsumeResponseListener { billingResult: BillingResult, outToken: String? ->
@@ -205,9 +209,11 @@ class RNIapModule(
205
209
  promise,
206
210
  ) { billingClient ->
207
211
  billingClient.queryPurchasesAsync(
208
- QueryPurchasesParams.newBuilder().setProductType(
209
- BillingClient.ProductType.INAPP,
210
- ).build(),
212
+ QueryPurchasesParams
213
+ .newBuilder()
214
+ .setProductType(
215
+ BillingClient.ProductType.INAPP,
216
+ ).build(),
211
217
  ) { billingResult: BillingResult, list: List<Purchase>? ->
212
218
  if (!isValidResult(billingResult, promise)) return@queryPurchasesAsync
213
219
  if (list == null) {
@@ -233,25 +239,39 @@ class RNIapModule(
233
239
  }
234
240
 
235
241
  @ReactMethod
236
- fun getItemsByType(type: String, skuArr: ReadableArray, promise: Promise) {
237
- ensureConnection(
238
- promise,
239
- ) { billingClient ->
240
- val skuList = ArrayList<QueryProductDetailsParams.Product>()
242
+ fun getItemsByType(
243
+ type: String,
244
+ skuArr: ReadableArray,
245
+ promise: Promise,
246
+ ) {
247
+ ensureConnection(promise) { billingClient ->
248
+ val skuList = mutableListOf<QueryProductDetailsParams.Product>()
241
249
  for (i in 0 until skuArr.size()) {
242
250
  if (skuArr.getType(i) == ReadableType.String) {
243
- skuArr.getString(i)?.let { sku -> // null check for older versions of RN
251
+ skuArr.getString(i)?.let { sku ->
244
252
  skuList.add(
245
- QueryProductDetailsParams.Product.newBuilder().setProductId(sku)
246
- .setProductType(type).build(),
253
+ QueryProductDetailsParams.Product
254
+ .newBuilder()
255
+ .setProductId(sku)
256
+ .setProductType(type)
257
+ .build(),
247
258
  )
248
259
  }
249
260
  }
250
261
  }
251
- val params = QueryProductDetailsParams.newBuilder().setProductList(skuList)
252
- billingClient.queryProductDetailsAsync(
253
- params.build(),
254
- ) { billingResult: BillingResult, skuDetailsList: List<ProductDetails> ->
262
+
263
+ if (skuList.isEmpty()) {
264
+ promise.safeReject("EMPTY_SKU_LIST", "The SKU list is empty.")
265
+ return@ensureConnection
266
+ }
267
+
268
+ val params =
269
+ QueryProductDetailsParams
270
+ .newBuilder()
271
+ .setProductList(skuList)
272
+ .build()
273
+
274
+ billingClient.queryProductDetailsAsync(params) { billingResult, skuDetailsList ->
255
275
  if (!isValidResult(billingResult, promise)) return@queryProductDetailsAsync
256
276
 
257
277
  val items = Arguments.createArray()
@@ -264,68 +284,51 @@ class RNIapModule(
264
284
  item.putString("description", skuDetails.description)
265
285
  item.putString("productType", skuDetails.productType)
266
286
  item.putString("name", skuDetails.name)
267
- val oneTimePurchaseOfferDetails = Arguments.createMap()
287
+
268
288
  skuDetails.oneTimePurchaseOfferDetails?.let {
269
- oneTimePurchaseOfferDetails.putString(
270
- "priceCurrencyCode",
271
- it.priceCurrencyCode,
272
- )
273
- oneTimePurchaseOfferDetails.putString("formattedPrice", it.formattedPrice)
274
- oneTimePurchaseOfferDetails.putString(
275
- "priceAmountMicros",
276
- it.priceAmountMicros.toString(),
277
- )
289
+ val oneTimePurchaseOfferDetails =
290
+ Arguments.createMap().apply {
291
+ putString("priceCurrencyCode", it.priceCurrencyCode)
292
+ putString("formattedPrice", it.formattedPrice)
293
+ putString("priceAmountMicros", it.priceAmountMicros.toString())
294
+ }
278
295
  item.putMap("oneTimePurchaseOfferDetails", oneTimePurchaseOfferDetails)
279
296
  }
297
+
280
298
  skuDetails.subscriptionOfferDetails?.let {
281
299
  val subscriptionOfferDetails = Arguments.createArray()
282
300
  it.forEach { subscriptionOfferDetailsItem ->
283
- val offerDetails = Arguments.createMap()
284
- offerDetails.putString(
285
- "basePlanId",
286
- subscriptionOfferDetailsItem.basePlanId,
287
- )
288
- offerDetails.putString(
289
- "offerId",
290
- subscriptionOfferDetailsItem.offerId,
291
- )
292
- offerDetails.putString(
293
- "offerToken",
294
- subscriptionOfferDetailsItem.offerToken,
295
- )
296
- val offerTags = Arguments.createArray()
297
- subscriptionOfferDetailsItem.offerTags.forEach { offerTag ->
298
- offerTags.pushString(offerTag)
299
- }
300
- offerDetails.putArray("offerTags", offerTags)
301
-
302
- val pricingPhasesList = Arguments.createArray()
303
- subscriptionOfferDetailsItem.pricingPhases.pricingPhaseList.forEach { pricingPhaseItem ->
304
- val pricingPhase = Arguments.createMap()
305
- pricingPhase.putString(
306
- "formattedPrice",
307
- pricingPhaseItem.formattedPrice,
308
- )
309
- pricingPhase.putString(
310
- "priceCurrencyCode",
311
- pricingPhaseItem.priceCurrencyCode,
312
- )
313
- pricingPhase.putString("billingPeriod", pricingPhaseItem.billingPeriod)
314
- pricingPhase.putInt(
315
- "billingCycleCount",
316
- pricingPhaseItem.billingCycleCount,
317
- )
318
- pricingPhase.putString(
319
- "priceAmountMicros",
320
- pricingPhaseItem.priceAmountMicros.toString(),
321
- )
322
- pricingPhase.putInt("recurrenceMode", pricingPhaseItem.recurrenceMode)
323
-
324
- pricingPhasesList.pushMap(pricingPhase)
325
- }
326
- val pricingPhases = Arguments.createMap()
327
- pricingPhases.putArray("pricingPhaseList", pricingPhasesList)
328
- offerDetails.putMap("pricingPhases", pricingPhases)
301
+ val offerDetails =
302
+ Arguments.createMap().apply {
303
+ putString("basePlanId", subscriptionOfferDetailsItem.basePlanId)
304
+ putString("offerId", subscriptionOfferDetailsItem.offerId)
305
+ putString("offerToken", subscriptionOfferDetailsItem.offerToken)
306
+
307
+ val offerTags = Arguments.createArray()
308
+ subscriptionOfferDetailsItem.offerTags.forEach { offerTag ->
309
+ offerTags.pushString(offerTag)
310
+ }
311
+ putArray("offerTags", offerTags)
312
+
313
+ val pricingPhasesList = Arguments.createArray()
314
+ subscriptionOfferDetailsItem.pricingPhases.pricingPhaseList.forEach { pricingPhaseItem ->
315
+ val pricingPhase =
316
+ Arguments.createMap().apply {
317
+ putString("formattedPrice", pricingPhaseItem.formattedPrice)
318
+ putString("priceCurrencyCode", pricingPhaseItem.priceCurrencyCode)
319
+ putString("billingPeriod", pricingPhaseItem.billingPeriod)
320
+ putInt("billingCycleCount", pricingPhaseItem.billingCycleCount)
321
+ putString("priceAmountMicros", pricingPhaseItem.priceAmountMicros.toString())
322
+ putInt("recurrenceMode", pricingPhaseItem.recurrenceMode)
323
+ }
324
+ pricingPhasesList.pushMap(pricingPhase)
325
+ }
326
+ val pricingPhases =
327
+ Arguments.createMap().apply {
328
+ putArray("pricingPhaseList", pricingPhasesList)
329
+ }
330
+ putMap("pricingPhases", pricingPhases)
331
+ }
329
332
  subscriptionOfferDetails.pushMap(offerDetails)
330
333
  }
331
334
  item.putArray("subscriptionOfferDetails", subscriptionOfferDetails)
@@ -353,15 +356,20 @@ class RNIapModule(
353
356
  }
354
357
 
355
358
  @ReactMethod
356
- fun getAvailableItemsByType(type: String, promise: Promise) {
359
+ fun getAvailableItemsByType(
360
+ type: String,
361
+ promise: Promise,
362
+ ) {
357
363
  ensureConnection(
358
364
  promise,
359
365
  ) { billingClient ->
360
366
  val items = WritableNativeArray()
361
367
  billingClient.queryPurchasesAsync(
362
- QueryPurchasesParams.newBuilder().setProductType(
363
- if (type == "subs") BillingClient.ProductType.SUBS else BillingClient.ProductType.INAPP,
364
- ).build(),
368
+ QueryPurchasesParams
369
+ .newBuilder()
370
+ .setProductType(
371
+ if (type == "subs") BillingClient.ProductType.SUBS else BillingClient.ProductType.INAPP,
372
+ ).build(),
365
373
  ) { billingResult: BillingResult, purchases: List<Purchase>? ->
366
374
  if (!isValidResult(billingResult, promise)) return@queryPurchasesAsync
367
375
  purchases?.forEach { purchase ->
@@ -399,16 +407,20 @@ class RNIapModule(
399
407
  }
400
408
 
401
409
  @ReactMethod
402
- fun getPurchaseHistoryByType(type: String, promise: Promise) {
410
+ fun getPurchaseHistoryByType(
411
+ type: String,
412
+ promise: Promise,
413
+ ) {
403
414
  ensureConnection(
404
415
  promise,
405
416
  ) { billingClient ->
406
417
  billingClient.queryPurchaseHistoryAsync(
407
- QueryPurchaseHistoryParams.newBuilder().setProductType(
408
- if (type == "subs") BillingClient.ProductType.SUBS else BillingClient.ProductType.INAPP,
409
- ).build(),
410
- ) {
411
- billingResult: BillingResult, purchaseHistoryRecordList: MutableList<PurchaseHistoryRecord>? ->
418
+ QueryPurchaseHistoryParams
419
+ .newBuilder()
420
+ .setProductType(
421
+ if (type == "subs") BillingClient.ProductType.SUBS else BillingClient.ProductType.INAPP,
422
+ ).build(),
423
+ ) { billingResult: BillingResult, purchaseHistoryRecordList: MutableList<PurchaseHistoryRecord>? ->
412
424
 
413
425
  if (!isValidResult(billingResult, promise)) return@queryPurchaseHistoryAsync
414
426
 
@@ -438,7 +450,7 @@ class RNIapModule(
438
450
  type: String,
439
451
  skuArr: ReadableArray,
440
452
  purchaseToken: String?,
441
- prorationMode: Int,
453
+ replacementMode: Int,
442
454
  obfuscatedAccountId: String?,
443
455
  obfuscatedProfileId: String?,
444
456
  offerTokenArr: ReadableArray, // New parameter in V5
@@ -485,7 +497,8 @@ class RNIapModule(
485
497
  }
486
498
  var productDetailParams = BillingFlowParams.ProductDetailsParams.newBuilder().setProductDetails(selectedSku)
487
499
  if (type == BillingClient.ProductType.SUBS) {
488
- offerTokenArr.getString(index)?.let { offerToken -> // null check for older versions of RN
500
+ offerTokenArr.getString(index)?.let { offerToken ->
501
+ // null check for older versions of RN
489
502
  productDetailParams = productDetailParams.setOfferToken(offerToken)
490
503
  }
491
504
  }
@@ -497,6 +510,23 @@ class RNIapModule(
497
510
  val subscriptionUpdateParamsBuilder = SubscriptionUpdateParams.newBuilder()
498
511
  if (purchaseToken != null) {
499
512
  subscriptionUpdateParamsBuilder.setOldPurchaseToken(purchaseToken)
513
+
514
+ if (type == BillingClient.ProductType.SUBS && replacementMode != -1) {
515
+ val replacementMode =
516
+ when (replacementMode) {
517
+ BillingFlowParams.SubscriptionUpdateParams.ReplacementMode.CHARGE_PRORATED_PRICE -> BillingFlowParams.SubscriptionUpdateParams.ReplacementMode.CHARGE_PRORATED_PRICE
518
+ BillingFlowParams.SubscriptionUpdateParams.ReplacementMode.WITHOUT_PRORATION -> BillingFlowParams.SubscriptionUpdateParams.ReplacementMode.WITHOUT_PRORATION
519
+ BillingFlowParams.SubscriptionUpdateParams.ReplacementMode.DEFERRED -> BillingFlowParams.SubscriptionUpdateParams.ReplacementMode.DEFERRED
520
+ BillingFlowParams.SubscriptionUpdateParams.ReplacementMode.WITH_TIME_PRORATION -> BillingFlowParams.SubscriptionUpdateParams.ReplacementMode.WITH_TIME_PRORATION
521
+ BillingFlowParams.SubscriptionUpdateParams.ReplacementMode.CHARGE_FULL_PRICE -> BillingFlowParams.SubscriptionUpdateParams.ReplacementMode.CHARGE_FULL_PRICE
522
+ else -> BillingFlowParams.SubscriptionUpdateParams.ReplacementMode.UNKNOWN_REPLACEMENT_MODE
523
+ }
524
+ subscriptionUpdateParamsBuilder.setSubscriptionReplacementMode(replacementMode)
525
+ }
526
+ if (purchaseToken != null) {
527
+ val subscriptionUpdateParams = subscriptionUpdateParamsBuilder.build()
528
+ builder.setSubscriptionUpdateParams(subscriptionUpdateParams)
529
+ }
500
530
  }
501
531
  if (obfuscatedAccountId != null) {
502
532
  builder.setObfuscatedAccountId(obfuscatedAccountId)
@@ -504,60 +534,7 @@ class RNIapModule(
504
534
  if (obfuscatedProfileId != null) {
505
535
  builder.setObfuscatedProfileId(obfuscatedProfileId)
506
536
  }
507
- if (prorationMode != -1) {
508
- if (prorationMode
509
- == BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_PRORATED_PRICE
510
- ) {
511
- subscriptionUpdateParamsBuilder.setReplaceProrationMode(
512
- BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_PRORATED_PRICE,
513
- )
514
- if (type != BillingClient.ProductType.SUBS) {
515
- val debugMessage =
516
- (
517
- "IMMEDIATE_AND_CHARGE_PRORATED_PRICE for proration mode only works in" +
518
- " subscription purchase."
519
- )
520
- val error = Arguments.createMap()
521
- error.putString("debugMessage", debugMessage)
522
- error.putString("code", PROMISE_BUY_ITEM)
523
- error.putString("message", debugMessage)
524
- error.putArray("productIds", skuArr)
525
- sendEvent(reactContext, "purchase-error", error)
526
- promise.safeReject(PROMISE_BUY_ITEM, debugMessage)
527
- return@ensureConnection
528
- }
529
- } else if (prorationMode
530
- == BillingFlowParams.ProrationMode.IMMEDIATE_WITHOUT_PRORATION
531
- ) {
532
- subscriptionUpdateParamsBuilder.setReplaceProrationMode(
533
- BillingFlowParams.ProrationMode.IMMEDIATE_WITHOUT_PRORATION,
534
- )
535
- } else if (prorationMode == BillingFlowParams.ProrationMode.DEFERRED) {
536
- subscriptionUpdateParamsBuilder.setReplaceProrationMode(
537
- BillingFlowParams.ProrationMode.DEFERRED,
538
- )
539
- } else if (prorationMode
540
- == BillingFlowParams.ProrationMode.IMMEDIATE_WITH_TIME_PRORATION
541
- ) {
542
- subscriptionUpdateParamsBuilder.setReplaceProrationMode(
543
- BillingFlowParams.ProrationMode.IMMEDIATE_WITHOUT_PRORATION,
544
- )
545
- } else if (prorationMode
546
- == BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_FULL_PRICE
547
- ) {
548
- subscriptionUpdateParamsBuilder.setReplaceProrationMode(
549
- BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_FULL_PRICE,
550
- )
551
- } else {
552
- subscriptionUpdateParamsBuilder.setReplaceProrationMode(
553
- BillingFlowParams.ProrationMode.UNKNOWN_SUBSCRIPTION_UPGRADE_DOWNGRADE_POLICY,
554
- )
555
- }
556
- }
557
- if (purchaseToken != null) {
558
- val subscriptionUpdateParams = subscriptionUpdateParamsBuilder.build()
559
- builder.setSubscriptionUpdateParams(subscriptionUpdateParams)
560
- }
537
+
561
538
  val flowParams = builder.build()
562
539
  val billingResultCode = billingClient.launchBillingFlow(activity, flowParams).responseCode
563
540
  if (billingResultCode != BillingClient.BillingResponseCode.OK) {
@@ -577,9 +554,11 @@ class RNIapModule(
577
554
  promise,
578
555
  ) { billingClient ->
579
556
  val acknowledgePurchaseParams =
580
- AcknowledgePurchaseParams.newBuilder().setPurchaseToken(
581
- token,
582
- ).build()
557
+ AcknowledgePurchaseParams
558
+ .newBuilder()
559
+ .setPurchaseToken(
560
+ token,
561
+ ).build()
583
562
  billingClient.acknowledgePurchase(
584
563
  acknowledgePurchaseParams,
585
564
  ) { billingResult: BillingResult ->
@@ -623,7 +602,10 @@ class RNIapModule(
623
602
  }
624
603
  }
625
604
 
626
- override fun onPurchasesUpdated(billingResult: BillingResult, purchases: List<Purchase>?) {
605
+ override fun onPurchasesUpdated(
606
+ billingResult: BillingResult,
607
+ purchases: List<Purchase>?,
608
+ ) {
627
609
  val responseCode = billingResult.responseCode
628
610
  if (responseCode != BillingClient.BillingResponseCode.OK) {
629
611
  val error = Arguments.createMap()
@@ -691,9 +673,11 @@ class RNIapModule(
691
673
  val types = arrayOf(BillingClient.ProductType.INAPP, BillingClient.ProductType.SUBS)
692
674
  for (type in types) {
693
675
  billingClient.queryPurchasesAsync(
694
- QueryPurchasesParams.newBuilder().setProductType(
695
- type,
696
- ).build(),
676
+ QueryPurchasesParams
677
+ .newBuilder()
678
+ .setProductType(
679
+ type,
680
+ ).build(),
697
681
  ) { billingResult: BillingResult, list: List<Purchase> ->
698
682
  if (!isValidResult(billingResult, promise)) return@queryPurchasesAsync
699
683
 
@@ -739,14 +723,17 @@ class RNIapModule(
739
723
  }
740
724
 
741
725
  init {
742
- val lifecycleEventListener: LifecycleEventListener = object : LifecycleEventListener {
743
- override fun onHostResume() {}
744
- override fun onHostPause() {}
745
- override fun onHostDestroy() {
746
- billingClientCache?.endConnection()
747
- billingClientCache = null
726
+ val lifecycleEventListener: LifecycleEventListener =
727
+ object : LifecycleEventListener {
728
+ override fun onHostResume() {}
729
+
730
+ override fun onHostPause() {}
731
+
732
+ override fun onHostDestroy() {
733
+ billingClientCache?.endConnection()
734
+ billingClientCache = null
735
+ }
748
736
  }
749
- }
750
737
  reactContext.addLifecycleEventListener(lifecycleEventListener)
751
738
  }
752
739
  }
@@ -7,10 +7,7 @@ import com.facebook.react.uimanager.ViewManager
7
7
  import java.util.ArrayList
8
8
 
9
9
  class RNIapPackage : ReactPackage {
10
-
11
- override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
12
- return emptyList()
13
- }
10
+ override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> = emptyList()
14
11
 
15
12
  override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
16
13
  val modules: MutableList<NativeModule> = ArrayList()
@@ -26,7 +26,6 @@ import org.junit.Test
26
26
  import java.util.*
27
27
 
28
28
  class RNIapAmazonModuleTest {
29
-
30
29
  @MockK
31
30
  lateinit var context: ReactApplicationContext
32
31
 
@@ -77,33 +76,38 @@ class RNIapAmazonModuleTest {
77
76
 
78
77
  @Test
79
78
  fun `Purchase Item`() {
80
- val purchaseResponse = mockk<PurchaseResponse>() {
81
- every { requestId } returns RequestId.fromString("0")
82
-
83
- every { requestStatus } returns PurchaseResponse.RequestStatus.SUCCESSFUL
84
- val mReceipt = mockk<Receipt>(relaxed = true) {
85
- every { sku } returns "mySku"
86
- every { purchaseDate } returns Date()
87
- every { receiptId } returns "rId"
88
- }
89
- every { receipt } returns mReceipt
90
- val mUserData = mockk<UserData>(relaxed = true) {
91
- every { userId } returns "uid1"
79
+ val purchaseResponse =
80
+ mockk<PurchaseResponse> {
81
+ every { requestId } returns RequestId.fromString("0")
82
+
83
+ every { requestStatus } returns PurchaseResponse.RequestStatus.SUCCESSFUL
84
+ val mReceipt =
85
+ mockk<Receipt>(relaxed = true) {
86
+ every { sku } returns "mySku"
87
+ every { purchaseDate } returns Date()
88
+ every { receiptId } returns "rId"
89
+ }
90
+ every { receipt } returns mReceipt
91
+ val mUserData =
92
+ mockk<UserData>(relaxed = true) {
93
+ every { userId } returns "uid1"
94
+ }
95
+ every { userData } returns mUserData
92
96
  }
93
- every { userData } returns mUserData
94
- }
95
97
 
96
98
  every { eventSender.sendEvent(any(), any()) } just Runs
97
99
 
98
100
  every { purchasingServiceProxy.purchase(any()) } answers {
99
101
  listener.onPurchaseResponse(
100
102
  purchaseResponse,
101
- ); RequestId.fromString("0")
103
+ )
104
+ RequestId.fromString("0")
102
105
  }
103
106
 
104
- val itemsMap = mockk<WritableMap>(relaxed = true) {
105
- every { getString("productId") } returns "mySku"
106
- }
107
+ val itemsMap =
108
+ mockk<WritableMap>(relaxed = true) {
109
+ every { getString("productId") } returns "mySku"
110
+ }
107
111
  mockkStatic(Arguments::class)
108
112
 
109
113
  every { Arguments.createMap() } returns itemsMap
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-iap",
3
- "version": "12.14.1",
3
+ "version": "12.14.2-rc.1",
4
4
  "description": "React Native In App Purchase Module.",
5
5
  "repository": "https://github.com/dooboolab-community/react-native-iap",
6
6
  "author": "hyochan <dooboolab@gmail.com> (https://github.com/hyochan)",
@@ -71,6 +71,7 @@
71
71
  "lint:ci": "yarn lint:tsc && yarn lint:eslint -f ./node_modules/@firmnav/eslint-github-actions-formatter/dist/formatter.js && yarn lint:prettier",
72
72
  "lint:prettier": "prettier --write \"**/*.{md,js,jsx,ts,tsx}\"",
73
73
  "lint:swift": "swiftlint lint --fix --format --path ios/*.swift --config .swiftlint.yml",
74
+ "lint:kotlin": "ktlint --format",
74
75
  "format": "git ls-files -m | xargs yarn prettier --write --ignore-unknown --no-error-on-unmatched-pattern",
75
76
  "bootstrap": "yarn example && yarn && yarn example pods",
76
77
  "gen:doc": "typedoc",