diff --git a/android/gradle.properties b/android/gradle.properties index b2d076b75..e139f155a 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -6,7 +6,7 @@ RNIap_buildToolsVersion=33.0.0 RNIap_ndkversion=23.1.7779620 RNIap_playServicesVersion=18.1.0 RNIap_amazonSdkVersion=3.0.4 -RNIap_playBillingSdkVersion=6.1.0 +RNIap_playBillingSdkVersion=7.0.0 android.useAndroidX=true android.enableJetifier=true diff --git a/android/src/amazon/java/com/dooboolab/rniap/PurchasingServiceProxy.kt b/android/src/amazon/java/com/dooboolab/rniap/PurchasingServiceProxy.kt index d177b3242..59c1c9575 100644 --- a/android/src/amazon/java/com/dooboolab/rniap/PurchasingServiceProxy.kt +++ b/android/src/amazon/java/com/dooboolab/rniap/PurchasingServiceProxy.kt @@ -6,7 +6,10 @@ import com.amazon.device.iap.model.FulfillmentResult import com.amazon.device.iap.model.RequestId interface PurchasingServiceProxy { - fun registerListener(var0: Context?, var1: PurchasingListener?) + fun registerListener( + var0: Context?, + var1: PurchasingListener?, + ) fun getUserData(): RequestId @@ -16,5 +19,8 @@ interface PurchasingServiceProxy { fun getPurchaseUpdates(var0: Boolean): RequestId - fun notifyFulfillment(var0: String?, var1: FulfillmentResult?) + fun notifyFulfillment( + var0: String?, + var1: FulfillmentResult?, + ) } diff --git a/android/src/amazon/java/com/dooboolab/rniap/PurchasingServiceProxyAmazonImpl.kt b/android/src/amazon/java/com/dooboolab/rniap/PurchasingServiceProxyAmazonImpl.kt index fe9a16ad5..372eba637 100644 --- a/android/src/amazon/java/com/dooboolab/rniap/PurchasingServiceProxyAmazonImpl.kt +++ b/android/src/amazon/java/com/dooboolab/rniap/PurchasingServiceProxyAmazonImpl.kt @@ -7,27 +7,21 @@ import com.amazon.device.iap.model.FulfillmentResult import com.amazon.device.iap.model.RequestId class PurchasingServiceProxyAmazonImpl : PurchasingServiceProxy { - override fun registerListener(var0: Context?, var1: PurchasingListener?) { - return PurchasingService.registerListener(var0, var1) - } + override fun registerListener( + var0: Context?, + var1: PurchasingListener?, + ) = PurchasingService.registerListener(var0, var1) - override fun getUserData(): RequestId { - return PurchasingService.getUserData() - } + override fun getUserData(): RequestId = PurchasingService.getUserData() - override fun purchase(var0: String?): RequestId { - return PurchasingService.purchase(var0) - } + override fun purchase(var0: String?): RequestId = PurchasingService.purchase(var0) - override fun getProductData(var0: Set?): RequestId { - return PurchasingService.getProductData(var0) - } + override fun getProductData(var0: Set?): RequestId = PurchasingService.getProductData(var0) - override fun getPurchaseUpdates(var0: Boolean): RequestId { - return PurchasingService.getPurchaseUpdates(var0) - } + override fun getPurchaseUpdates(var0: Boolean): RequestId = PurchasingService.getPurchaseUpdates(var0) - override fun notifyFulfillment(var0: String?, var1: FulfillmentResult?) { - return PurchasingService.notifyFulfillment(var0, var1) - } + override fun notifyFulfillment( + var0: String?, + var1: FulfillmentResult?, + ) = PurchasingService.notifyFulfillment(var0, var1) } diff --git a/android/src/amazon/java/com/dooboolab/rniap/RNIapAmazonListener.kt b/android/src/amazon/java/com/dooboolab/rniap/RNIapAmazonListener.kt index a782b5dfe..26a9a12a1 100644 --- a/android/src/amazon/java/com/dooboolab/rniap/RNIapAmazonListener.kt +++ b/android/src/amazon/java/com/dooboolab/rniap/RNIapAmazonListener.kt @@ -22,7 +22,6 @@ class RNIapAmazonListener( var eventSender: EventSender?, var purchasingService: PurchasingServiceProxy?, ) : PurchasingListener { - override fun onProductDataResponse(response: ProductDataResponse) { when (response.requestStatus) { ProductDataResponse.RequestStatus.SUCCESSFUL -> { @@ -170,7 +169,10 @@ class RNIapAmazonListener( } } - private fun receiptToMap(userData: UserData, receipt: Receipt): WritableMap { + private fun receiptToMap( + userData: UserData, + receipt: Receipt, + ): WritableMap { val item = Arguments.createMap() item.putString("productId", receipt.sku) item.putDouble("transactionDate", receipt.purchaseDate.time.toDouble()) diff --git a/android/src/amazon/java/com/dooboolab/rniap/RNIapAmazonModule.kt b/android/src/amazon/java/com/dooboolab/rniap/RNIapAmazonModule.kt index 0513d35a1..c157ff10c 100644 --- a/android/src/amazon/java/com/dooboolab/rniap/RNIapAmazonModule.kt +++ b/android/src/amazon/java/com/dooboolab/rniap/RNIapAmazonModule.kt @@ -22,28 +22,33 @@ class RNIapAmazonModule( private val reactContext: ReactApplicationContext, private val purchasingService: PurchasingServiceProxy = PurchasingServiceProxyAmazonImpl(), private var eventSender: EventSender? = null, -) : - ReactContextBaseJavaModule(reactContext) { - override fun getName(): String { - return TAG - } +) : ReactContextBaseJavaModule(reactContext) { + override fun getName(): String = TAG @ReactMethod fun initConnection(promise: Promise) { if (RNIapActivityListener.amazonListener == null) { - promise.safeReject(PromiseUtils.E_DEVELOPER_ERROR, Exception("RNIapActivityListener is not registered in your MainActivity.onCreate")) + promise.safeReject( + PromiseUtils.E_DEVELOPER_ERROR, + Exception("RNIapActivityListener is not registered in your MainActivity.onCreate"), + ) return } if (eventSender == null) { - eventSender = object : EventSender { - private val rctDeviceEventEmitter = reactContext - .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) - - override fun sendEvent(eventName: String, params: WritableMap?) { - rctDeviceEventEmitter - .emit(eventName, params) + eventSender = + object : EventSender { + private val rctDeviceEventEmitter = + reactContext + .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) + + override fun sendEvent( + eventName: String, + params: WritableMap?, + ) { + rctDeviceEventEmitter + .emit(eventName, params) + } } - } } RNIapActivityListener.amazonListener?.eventSender = eventSender RNIapActivityListener.amazonListener?.purchasingService = purchasingService @@ -109,7 +114,11 @@ class RNIapAmazonModule( } @ReactMethod - fun getItemsByType(type: String?, skuArr: ReadableArray, promise: Promise) { + fun getItemsByType( + type: String?, + skuArr: ReadableArray, + promise: Promise, + ) { val productSkus: MutableSet = HashSet() var ii = 0 val skuSize = skuArr.size() @@ -171,7 +180,10 @@ class RNIapAmazonModule( * From https://amazon.developer.forums.answerhub.com/questions/175720/how-to-open-store-subscription-screen-directly-use.html?childToView=179402#answer-179402 */ @ReactMethod - fun deepLinkToSubscriptions(isAmazonDevice: Boolean, promise: Promise) { + fun deepLinkToSubscriptions( + isAmazonDevice: Boolean, + promise: Promise, + ) { if (isAmazonDevice) { val intent = Intent("android.intent.action.VIEW", Uri.parse("amzn://apps/library/subscriptions")) @@ -210,22 +222,26 @@ class RNIapAmazonModule( const val TAG = "RNIapAmazonModule" } + init { - val lifecycleEventListener: LifecycleEventListener = object : LifecycleEventListener { - /** - * From https://developer.amazon.com/docs/in-app-purchasing/iap-implement-iap.html#getpurchaseupdates-responses - * We should fetch updates on resume - */ - override fun onHostResume() { - if (RNIapActivityListener.hasListener) { - purchasingService.getUserData() - purchasingService.getPurchaseUpdates(false) + val lifecycleEventListener: LifecycleEventListener = + object : LifecycleEventListener { + /** + * From https://developer.amazon.com/docs/in-app-purchasing/iap-implement-iap.html#getpurchaseupdates-responses + * We should fetch updates on resume + */ + override fun onHostResume() { + if (RNIapActivityListener.hasListener) { + purchasingService.getUserData() + purchasingService.getPurchaseUpdates(false) + } + } + + override fun onHostPause() {} + + override fun onHostDestroy() { } } - override fun onHostPause() {} - override fun onHostDestroy() { - } - } reactContext.addLifecycleEventListener(lifecycleEventListener) } } diff --git a/android/src/amazon/java/com/dooboolab/rniap/RNIapPackage.kt b/android/src/amazon/java/com/dooboolab/rniap/RNIapPackage.kt index 778bee49f..c68e8d3d0 100644 --- a/android/src/amazon/java/com/dooboolab/rniap/RNIapPackage.kt +++ b/android/src/amazon/java/com/dooboolab/rniap/RNIapPackage.kt @@ -7,10 +7,7 @@ import com.facebook.react.uimanager.ViewManager import java.util.ArrayList class RNIapPackage : ReactPackage { - - override fun createViewManagers(reactContext: ReactApplicationContext): List> { - return emptyList() - } + override fun createViewManagers(reactContext: ReactApplicationContext): List> = emptyList() override fun createNativeModules(reactContext: ReactApplicationContext): List { val modules: MutableList = ArrayList() diff --git a/android/src/main/java/com/dooboolab/rniap/PromiseUtils.kt b/android/src/main/java/com/dooboolab/rniap/PromiseUtils.kt index 853dac253..e87326d75 100644 --- a/android/src/main/java/com/dooboolab/rniap/PromiseUtils.kt +++ b/android/src/main/java/com/dooboolab/rniap/PromiseUtils.kt @@ -6,11 +6,18 @@ import java.util.HashMap object PromiseUtils { private val promises = HashMap>() - fun addPromiseForKey(key: String, promise: Promise) { + + fun addPromiseForKey( + key: String, + promise: Promise, + ) { promises.getOrPut(key) { mutableListOf() }.add(promise) } - fun resolvePromisesForKey(key: String, value: Any?) { + fun resolvePromisesForKey( + key: String, + value: Any?, + ) { promises[key]?.forEach { promise -> promise.safeResolve(value) } diff --git a/android/src/main/java/com/dooboolab/rniap/PromiseUtlis.kt b/android/src/main/java/com/dooboolab/rniap/PromiseUtlis.kt index 6176cdebd..c1bc5afde 100644 --- a/android/src/main/java/com/dooboolab/rniap/PromiseUtlis.kt +++ b/android/src/main/java/com/dooboolab/rniap/PromiseUtlis.kt @@ -21,12 +21,21 @@ fun Promise.safeResolve(value: Any?) { fun Promise.safeReject(message: String) = this.safeReject(message, null, null) -fun Promise.safeReject(code: String?, message: String?) = this.safeReject(code, message, null) - -fun Promise.safeReject(code: String?, throwable: Throwable?) = - this.safeReject(code, null, throwable) - -fun Promise.safeReject(code: String?, message: String?, throwable: Throwable?) { +fun Promise.safeReject( + code: String?, + message: String?, +) = this.safeReject(code, message, null) + +fun Promise.safeReject( + code: String?, + throwable: Throwable?, +) = this.safeReject(code, null, throwable) + +fun Promise.safeReject( + code: String?, + message: String?, + throwable: Throwable?, +) { try { this.reject(code, message, throwable) } catch (oce: ObjectAlreadyConsumedException) { diff --git a/android/src/play/java/com/dooboolab/rniap/PlayUtils.kt b/android/src/play/java/com/dooboolab/rniap/PlayUtils.kt index 0e6b3b115..0dd2931ee 100644 --- a/android/src/play/java/com/dooboolab/rniap/PlayUtils.kt +++ b/android/src/play/java/com/dooboolab/rniap/PlayUtils.kt @@ -4,88 +4,104 @@ import android.util.Log import com.android.billingclient.api.BillingClient import com.facebook.react.bridge.Promise -data class BillingResponse(val code: String, val message: String) +data class BillingResponse( + val code: String, + val message: String, +) object PlayUtils { - fun rejectPromiseWithBillingError(promise: Promise, responseCode: Int) { + fun rejectPromiseWithBillingError( + promise: Promise, + responseCode: Int, + ) { val errorData = getBillingResponseData(responseCode) promise.safeReject(errorData.code, errorData.message) } fun getBillingResponseData(responseCode: Int): BillingResponse { - val errorData = when (responseCode) { - BillingClient.BillingResponseCode.FEATURE_NOT_SUPPORTED -> { - BillingResponse( - PromiseUtils.E_SERVICE_ERROR, - "This feature is not available on your device.", - ) + val errorData = + when (responseCode) { + BillingClient.BillingResponseCode.FEATURE_NOT_SUPPORTED -> { + BillingResponse( + PromiseUtils.E_SERVICE_ERROR, + "This feature is not available on your device.", + ) + } + BillingClient.BillingResponseCode.SERVICE_DISCONNECTED -> { + BillingResponse( + PromiseUtils.E_NETWORK_ERROR, + "The service is disconnected (check your internet connection.)", + ) + } + BillingClient.BillingResponseCode.NETWORK_ERROR -> { + BillingResponse( + PromiseUtils.E_NETWORK_ERROR, + "You have problem with network connection.", + ) + } + BillingClient.BillingResponseCode.OK -> { + BillingResponse( + "OK", + "", + ) + } + BillingClient.BillingResponseCode.USER_CANCELED -> { + BillingResponse( + PromiseUtils.E_USER_CANCELLED, + "Payment is Cancelled.", + ) + } + BillingClient.BillingResponseCode.SERVICE_UNAVAILABLE -> { + BillingResponse( + PromiseUtils.E_SERVICE_ERROR, + "The service is unreachable. This may be your internet connection, or the Play Store may be down.", + ) + } + BillingClient.BillingResponseCode.BILLING_UNAVAILABLE -> { + BillingResponse( + PromiseUtils.E_SERVICE_ERROR, + "Billing is unavailable. This may be a problem with your device, or the Play Store may be down.", + ) + } + BillingClient.BillingResponseCode.ITEM_UNAVAILABLE -> { + BillingResponse( + PromiseUtils.E_ITEM_UNAVAILABLE, + "That item is unavailable.", + ) + } + BillingClient.BillingResponseCode.DEVELOPER_ERROR -> { + BillingResponse( + PromiseUtils.E_DEVELOPER_ERROR, + "Google is indicating that we have some issue connecting to payment.", + ) + } + BillingClient.BillingResponseCode.ERROR -> { + BillingResponse( + PromiseUtils.E_UNKNOWN, + "An unknown or unexpected error has occurred. Please try again later.", + ) + } + BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED -> { + BillingResponse( + PromiseUtils.E_ALREADY_OWNED, + "You already own this item.", + ) + } + else -> { + BillingResponse( + PromiseUtils.E_UNKNOWN, + "Purchase failed with code: $responseCode", + ) + } } - BillingClient.BillingResponseCode.SERVICE_DISCONNECTED -> { - BillingResponse( - PromiseUtils.E_NETWORK_ERROR, - "The service is disconnected (check your internet connection.)", - ) - } - BillingClient.BillingResponseCode.OK -> { - BillingResponse( - "OK", - "", - ) - } - BillingClient.BillingResponseCode.USER_CANCELED -> { - BillingResponse( - PromiseUtils.E_USER_CANCELLED, - "Payment is Cancelled.", - ) - } - BillingClient.BillingResponseCode.SERVICE_UNAVAILABLE -> { - BillingResponse( - PromiseUtils.E_SERVICE_ERROR, - "The service is unreachable. This may be your internet connection, or the Play Store may be down.", - ) - } - BillingClient.BillingResponseCode.BILLING_UNAVAILABLE -> { - BillingResponse( - PromiseUtils.E_SERVICE_ERROR, - "Billing is unavailable. This may be a problem with your device, or the Play Store may be down.", - ) - } - BillingClient.BillingResponseCode.ITEM_UNAVAILABLE -> { - BillingResponse( - PromiseUtils.E_ITEM_UNAVAILABLE, - "That item is unavailable.", - ) - } - BillingClient.BillingResponseCode.DEVELOPER_ERROR -> { - BillingResponse( - PromiseUtils.E_DEVELOPER_ERROR, - "Google is indicating that we have some issue connecting to payment.", - ) - } - BillingClient.BillingResponseCode.ERROR -> { - BillingResponse( - PromiseUtils.E_UNKNOWN, - "An unknown or unexpected error has occurred. Please try again later.", - ) - } - BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED -> { - BillingResponse( - PromiseUtils.E_ALREADY_OWNED, - "You already own this item.", - ) - } - else -> { - BillingResponse( - PromiseUtils.E_UNKNOWN, - "Purchase failed with code: $responseCode", - ) - } - } Log.e(TAG, "Error Code : $responseCode") return errorData } - fun rejectPromisesWithBillingError(key: String, responseCode: Int) { + fun rejectPromisesWithBillingError( + key: String, + responseCode: Int, + ) { val errorData = getBillingResponseData(responseCode) PromiseUtils.rejectPromisesForKey(key, errorData.code, errorData.message, null) } diff --git a/android/src/play/java/com/dooboolab/rniap/RNIapModule.kt b/android/src/play/java/com/dooboolab/rniap/RNIapModule.kt index 2d1a3b4fa..e68412aec 100644 --- a/android/src/play/java/com/dooboolab/rniap/RNIapModule.kt +++ b/android/src/play/java/com/dooboolab/rniap/RNIapModule.kt @@ -34,21 +34,18 @@ import com.facebook.react.module.annotations.ReactModule import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter import com.google.android.gms.common.ConnectionResult import com.google.android.gms.common.GoogleApiAvailability -import java.util.ArrayList + @ReactModule(name = RNIapModule.TAG) class RNIapModule( private val reactContext: ReactApplicationContext, private val builder: BillingClient.Builder = BillingClient.newBuilder(reactContext).enablePendingPurchases(), private val googleApiAvailability: GoogleApiAvailability = GoogleApiAvailability.getInstance(), -) : - ReactContextBaseJavaModule(reactContext), +) : ReactContextBaseJavaModule(reactContext), PurchasesUpdatedListener { - private var billingClientCache: BillingClient? = null private val skus: MutableMap = mutableMapOf() - override fun getName(): String { - return TAG - } + + override fun getName(): String = TAG fun ensureConnection( promise: Promise, @@ -59,68 +56,73 @@ class RNIapModule( callback(billingClient) return } else { - val nested = PromiseImpl( - { - if (it.isNotEmpty() && it[0] is Boolean && it[0] as Boolean) { - val connectedBillingClient = billingClientCache - if (connectedBillingClient?.isReady == true) { - callback(connectedBillingClient) + val nested = + PromiseImpl( + { + if (it.isNotEmpty() && it[0] is Boolean && it[0] as Boolean) { + val connectedBillingClient = billingClientCache + if (connectedBillingClient?.isReady == true) { + callback(connectedBillingClient) + } else { + promise.safeReject(PromiseUtils.E_NOT_PREPARED, "Unable to auto-initialize connection") + } } else { - promise.safeReject(PromiseUtils.E_NOT_PREPARED, "Unable to auto-initialize connection") + promise.safeReject(PromiseUtils.E_UNKNOWN, "ensureConnection - incorrect parameter in resolve") + Log.i(TAG, "Incorrect parameter in resolve") + } + }, + { + var errorCode: String? = null + var errorMessage: String? = null + if (it.size > 1 && it[0] is String && it[1] is String) { + errorCode = it[0] as String + errorMessage = it[1] as String + } else if (it.isNotEmpty() && it[0] is WritableNativeMap) { + val errorMap = it[0] as WritableNativeMap + errorCode = errorMap.getString("code") + errorMessage = errorMap.getString("message") } - } else { - promise.safeReject(PromiseUtils.E_UNKNOWN, "ensureConnection - incorrect parameter in resolve") - Log.i(TAG, "Incorrect parameter in resolve") - } - }, - { - var errorCode: String? = null - var errorMessage: String? = null - if (it.size > 1 && it[0] is String && it[1] is String) { - errorCode = it[0] as String - errorMessage = it[1] as String - } else if (it.isNotEmpty() && it[0] is WritableNativeMap) { - val errorMap = it[0] as WritableNativeMap - errorCode = errorMap.getString("code") - errorMessage = errorMap.getString("message") - } - if (errorCode is String && errorMessage is String) { - promise.safeReject( - errorCode, - errorMessage, - ) - } else { - promise.safeReject(PromiseUtils.E_UNKNOWN, "ensureConnection - incorrect parameter in reject") - Log.i(TAG, "Incorrect parameters in reject") - } - }, - ) + if (errorCode is String && errorMessage is String) { + promise.safeReject( + errorCode, + errorMessage, + ) + } else { + promise.safeReject(PromiseUtils.E_UNKNOWN, "ensureConnection - incorrect parameter in reject") + Log.i(TAG, "Incorrect parameters in reject") + } + }, + ) initConnection(nested) } } @ReactMethod - fun isFeatureSupported(feature: String, promise: Promise) { + fun isFeatureSupported( + feature: String, + promise: Promise, + ) { ensureConnection( promise, ) { billingClient -> - val f = when (feature) { - "IN_APP_MESSAGING" -> - BillingClient.FeatureType.IN_APP_MESSAGING - "PRICE_CHANGE_CONFIRMATION" -> - BillingClient.FeatureType.PRICE_CHANGE_CONFIRMATION - "PRODUCT_DETAILS" -> - BillingClient.FeatureType.PRODUCT_DETAILS - "SUBSCRIPTIONS" -> - BillingClient.FeatureType.SUBSCRIPTIONS - "SUBSCRIPTIONS_UPDATE" -> - BillingClient.FeatureType.SUBSCRIPTIONS_UPDATE - else -> { - promise.safeReject("Invalid Feature name") - return@ensureConnection + val f = + when (feature) { + "IN_APP_MESSAGING" -> + BillingClient.FeatureType.IN_APP_MESSAGING + "PRICE_CHANGE_CONFIRMATION" -> + BillingClient.FeatureType.PRICE_CHANGE_CONFIRMATION + "PRODUCT_DETAILS" -> + BillingClient.FeatureType.PRODUCT_DETAILS + "SUBSCRIPTIONS" -> + BillingClient.FeatureType.SUBSCRIPTIONS + "SUBSCRIPTIONS_UPDATE" -> + BillingClient.FeatureType.SUBSCRIPTIONS_UPDATE + else -> { + promise.safeReject("Invalid Feature name") + return@ensureConnection + } } - } promise.safeResolve(billingClient.isFeatureSupported(f)) } } @@ -180,7 +182,9 @@ class RNIapModule( promise, ) { billingClient -> val consumeParams = - ConsumeParams.newBuilder().setPurchaseToken(purchase.purchaseToken) + ConsumeParams + .newBuilder() + .setPurchaseToken(purchase.purchaseToken) .build() val listener = ConsumeResponseListener { billingResult: BillingResult, outToken: String? -> @@ -205,9 +209,11 @@ class RNIapModule( promise, ) { billingClient -> billingClient.queryPurchasesAsync( - QueryPurchasesParams.newBuilder().setProductType( - BillingClient.ProductType.INAPP, - ).build(), + QueryPurchasesParams + .newBuilder() + .setProductType( + BillingClient.ProductType.INAPP, + ).build(), ) { billingResult: BillingResult, list: List? -> if (!isValidResult(billingResult, promise)) return@queryPurchasesAsync if (list == null) { @@ -233,25 +239,39 @@ class RNIapModule( } @ReactMethod - fun getItemsByType(type: String, skuArr: ReadableArray, promise: Promise) { - ensureConnection( - promise, - ) { billingClient -> - val skuList = ArrayList() + fun getItemsByType( + type: String, + skuArr: ReadableArray, + promise: Promise, + ) { + ensureConnection(promise) { billingClient -> + val skuList = mutableListOf() for (i in 0 until skuArr.size()) { if (skuArr.getType(i) == ReadableType.String) { - skuArr.getString(i)?.let { sku -> // null check for older versions of RN + skuArr.getString(i)?.let { sku -> skuList.add( - QueryProductDetailsParams.Product.newBuilder().setProductId(sku) - .setProductType(type).build(), + QueryProductDetailsParams.Product + .newBuilder() + .setProductId(sku) + .setProductType(type) + .build(), ) } } } - val params = QueryProductDetailsParams.newBuilder().setProductList(skuList) - billingClient.queryProductDetailsAsync( - params.build(), - ) { billingResult: BillingResult, skuDetailsList: List -> + + if (skuList.isEmpty()) { + promise.safeReject("EMPTY_SKU_LIST", "The SKU list is empty.") + return@ensureConnection + } + + val params = + QueryProductDetailsParams + .newBuilder() + .setProductList(skuList) + .build() + + billingClient.queryProductDetailsAsync(params) { billingResult, skuDetailsList -> if (!isValidResult(billingResult, promise)) return@queryProductDetailsAsync val items = Arguments.createArray() @@ -264,68 +284,51 @@ class RNIapModule( item.putString("description", skuDetails.description) item.putString("productType", skuDetails.productType) item.putString("name", skuDetails.name) - val oneTimePurchaseOfferDetails = Arguments.createMap() + skuDetails.oneTimePurchaseOfferDetails?.let { - oneTimePurchaseOfferDetails.putString( - "priceCurrencyCode", - it.priceCurrencyCode, - ) - oneTimePurchaseOfferDetails.putString("formattedPrice", it.formattedPrice) - oneTimePurchaseOfferDetails.putString( - "priceAmountMicros", - it.priceAmountMicros.toString(), - ) + val oneTimePurchaseOfferDetails = + Arguments.createMap().apply { + putString("priceCurrencyCode", it.priceCurrencyCode) + putString("formattedPrice", it.formattedPrice) + putString("priceAmountMicros", it.priceAmountMicros.toString()) + } item.putMap("oneTimePurchaseOfferDetails", oneTimePurchaseOfferDetails) } + skuDetails.subscriptionOfferDetails?.let { val subscriptionOfferDetails = Arguments.createArray() it.forEach { subscriptionOfferDetailsItem -> - val offerDetails = Arguments.createMap() - offerDetails.putString( - "basePlanId", - subscriptionOfferDetailsItem.basePlanId, - ) - offerDetails.putString( - "offerId", - subscriptionOfferDetailsItem.offerId, - ) - offerDetails.putString( - "offerToken", - subscriptionOfferDetailsItem.offerToken, - ) - val offerTags = Arguments.createArray() - subscriptionOfferDetailsItem.offerTags.forEach { offerTag -> - offerTags.pushString(offerTag) - } - offerDetails.putArray("offerTags", offerTags) - - val pricingPhasesList = Arguments.createArray() - subscriptionOfferDetailsItem.pricingPhases.pricingPhaseList.forEach { pricingPhaseItem -> - val pricingPhase = Arguments.createMap() - pricingPhase.putString( - "formattedPrice", - pricingPhaseItem.formattedPrice, - ) - pricingPhase.putString( - "priceCurrencyCode", - pricingPhaseItem.priceCurrencyCode, - ) - pricingPhase.putString("billingPeriod", pricingPhaseItem.billingPeriod) - pricingPhase.putInt( - "billingCycleCount", - pricingPhaseItem.billingCycleCount, - ) - pricingPhase.putString( - "priceAmountMicros", - pricingPhaseItem.priceAmountMicros.toString(), - ) - pricingPhase.putInt("recurrenceMode", pricingPhaseItem.recurrenceMode) - - pricingPhasesList.pushMap(pricingPhase) - } - val pricingPhases = Arguments.createMap() - pricingPhases.putArray("pricingPhaseList", pricingPhasesList) - offerDetails.putMap("pricingPhases", pricingPhases) + val offerDetails = + Arguments.createMap().apply { + putString("basePlanId", subscriptionOfferDetailsItem.basePlanId) + putString("offerId", subscriptionOfferDetailsItem.offerId) + putString("offerToken", subscriptionOfferDetailsItem.offerToken) + + val offerTags = Arguments.createArray() + subscriptionOfferDetailsItem.offerTags.forEach { offerTag -> + offerTags.pushString(offerTag) + } + putArray("offerTags", offerTags) + + val pricingPhasesList = Arguments.createArray() + subscriptionOfferDetailsItem.pricingPhases.pricingPhaseList.forEach { pricingPhaseItem -> + val pricingPhase = + Arguments.createMap().apply { + putString("formattedPrice", pricingPhaseItem.formattedPrice) + putString("priceCurrencyCode", pricingPhaseItem.priceCurrencyCode) + putString("billingPeriod", pricingPhaseItem.billingPeriod) + putInt("billingCycleCount", pricingPhaseItem.billingCycleCount) + putString("priceAmountMicros", pricingPhaseItem.priceAmountMicros.toString()) + putInt("recurrenceMode", pricingPhaseItem.recurrenceMode) + } + pricingPhasesList.pushMap(pricingPhase) + } + val pricingPhases = + Arguments.createMap().apply { + putArray("pricingPhaseList", pricingPhasesList) + } + putMap("pricingPhases", pricingPhases) + } subscriptionOfferDetails.pushMap(offerDetails) } item.putArray("subscriptionOfferDetails", subscriptionOfferDetails) @@ -353,15 +356,20 @@ class RNIapModule( } @ReactMethod - fun getAvailableItemsByType(type: String, promise: Promise) { + fun getAvailableItemsByType( + type: String, + promise: Promise, + ) { ensureConnection( promise, ) { billingClient -> val items = WritableNativeArray() billingClient.queryPurchasesAsync( - QueryPurchasesParams.newBuilder().setProductType( - if (type == "subs") BillingClient.ProductType.SUBS else BillingClient.ProductType.INAPP, - ).build(), + QueryPurchasesParams + .newBuilder() + .setProductType( + if (type == "subs") BillingClient.ProductType.SUBS else BillingClient.ProductType.INAPP, + ).build(), ) { billingResult: BillingResult, purchases: List? -> if (!isValidResult(billingResult, promise)) return@queryPurchasesAsync purchases?.forEach { purchase -> @@ -399,16 +407,20 @@ class RNIapModule( } @ReactMethod - fun getPurchaseHistoryByType(type: String, promise: Promise) { + fun getPurchaseHistoryByType( + type: String, + promise: Promise, + ) { ensureConnection( promise, ) { billingClient -> billingClient.queryPurchaseHistoryAsync( - QueryPurchaseHistoryParams.newBuilder().setProductType( - if (type == "subs") BillingClient.ProductType.SUBS else BillingClient.ProductType.INAPP, - ).build(), - ) { - billingResult: BillingResult, purchaseHistoryRecordList: MutableList? -> + QueryPurchaseHistoryParams + .newBuilder() + .setProductType( + if (type == "subs") BillingClient.ProductType.SUBS else BillingClient.ProductType.INAPP, + ).build(), + ) { billingResult: BillingResult, purchaseHistoryRecordList: MutableList? -> if (!isValidResult(billingResult, promise)) return@queryPurchaseHistoryAsync @@ -438,7 +450,7 @@ class RNIapModule( type: String, skuArr: ReadableArray, purchaseToken: String?, - prorationMode: Int, + replacementMode: Int, obfuscatedAccountId: String?, obfuscatedProfileId: String?, offerTokenArr: ReadableArray, // New parameter in V5 @@ -485,7 +497,8 @@ class RNIapModule( } var productDetailParams = BillingFlowParams.ProductDetailsParams.newBuilder().setProductDetails(selectedSku) if (type == BillingClient.ProductType.SUBS) { - offerTokenArr.getString(index)?.let { offerToken -> // null check for older versions of RN + offerTokenArr.getString(index)?.let { offerToken -> + // null check for older versions of RN productDetailParams = productDetailParams.setOfferToken(offerToken) } } @@ -497,6 +510,23 @@ class RNIapModule( val subscriptionUpdateParamsBuilder = SubscriptionUpdateParams.newBuilder() if (purchaseToken != null) { subscriptionUpdateParamsBuilder.setOldPurchaseToken(purchaseToken) + + if (type == BillingClient.ProductType.SUBS && replacementMode != -1) { + val replacementMode = + when (replacementMode) { + BillingFlowParams.SubscriptionUpdateParams.ReplacementMode.CHARGE_PRORATED_PRICE -> BillingFlowParams.SubscriptionUpdateParams.ReplacementMode.CHARGE_PRORATED_PRICE + BillingFlowParams.SubscriptionUpdateParams.ReplacementMode.WITHOUT_PRORATION -> BillingFlowParams.SubscriptionUpdateParams.ReplacementMode.WITHOUT_PRORATION + BillingFlowParams.SubscriptionUpdateParams.ReplacementMode.DEFERRED -> BillingFlowParams.SubscriptionUpdateParams.ReplacementMode.DEFERRED + BillingFlowParams.SubscriptionUpdateParams.ReplacementMode.WITH_TIME_PRORATION -> BillingFlowParams.SubscriptionUpdateParams.ReplacementMode.WITH_TIME_PRORATION + BillingFlowParams.SubscriptionUpdateParams.ReplacementMode.CHARGE_FULL_PRICE -> BillingFlowParams.SubscriptionUpdateParams.ReplacementMode.CHARGE_FULL_PRICE + else -> BillingFlowParams.SubscriptionUpdateParams.ReplacementMode.UNKNOWN_REPLACEMENT_MODE + } + subscriptionUpdateParamsBuilder.setSubscriptionReplacementMode(replacementMode) + } + if (purchaseToken != null) { + val subscriptionUpdateParams = subscriptionUpdateParamsBuilder.build() + builder.setSubscriptionUpdateParams(subscriptionUpdateParams) + } } if (obfuscatedAccountId != null) { builder.setObfuscatedAccountId(obfuscatedAccountId) @@ -504,60 +534,7 @@ class RNIapModule( if (obfuscatedProfileId != null) { builder.setObfuscatedProfileId(obfuscatedProfileId) } - if (prorationMode != -1) { - if (prorationMode - == BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_PRORATED_PRICE - ) { - subscriptionUpdateParamsBuilder.setReplaceProrationMode( - BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_PRORATED_PRICE, - ) - if (type != BillingClient.ProductType.SUBS) { - val debugMessage = - ( - "IMMEDIATE_AND_CHARGE_PRORATED_PRICE for proration mode only works in" + - " subscription purchase." - ) - val error = Arguments.createMap() - error.putString("debugMessage", debugMessage) - error.putString("code", PROMISE_BUY_ITEM) - error.putString("message", debugMessage) - error.putArray("productIds", skuArr) - sendEvent(reactContext, "purchase-error", error) - promise.safeReject(PROMISE_BUY_ITEM, debugMessage) - return@ensureConnection - } - } else if (prorationMode - == BillingFlowParams.ProrationMode.IMMEDIATE_WITHOUT_PRORATION - ) { - subscriptionUpdateParamsBuilder.setReplaceProrationMode( - BillingFlowParams.ProrationMode.IMMEDIATE_WITHOUT_PRORATION, - ) - } else if (prorationMode == BillingFlowParams.ProrationMode.DEFERRED) { - subscriptionUpdateParamsBuilder.setReplaceProrationMode( - BillingFlowParams.ProrationMode.DEFERRED, - ) - } else if (prorationMode - == BillingFlowParams.ProrationMode.IMMEDIATE_WITH_TIME_PRORATION - ) { - subscriptionUpdateParamsBuilder.setReplaceProrationMode( - BillingFlowParams.ProrationMode.IMMEDIATE_WITHOUT_PRORATION, - ) - } else if (prorationMode - == BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_FULL_PRICE - ) { - subscriptionUpdateParamsBuilder.setReplaceProrationMode( - BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_FULL_PRICE, - ) - } else { - subscriptionUpdateParamsBuilder.setReplaceProrationMode( - BillingFlowParams.ProrationMode.UNKNOWN_SUBSCRIPTION_UPGRADE_DOWNGRADE_POLICY, - ) - } - } - if (purchaseToken != null) { - val subscriptionUpdateParams = subscriptionUpdateParamsBuilder.build() - builder.setSubscriptionUpdateParams(subscriptionUpdateParams) - } + val flowParams = builder.build() val billingResultCode = billingClient.launchBillingFlow(activity, flowParams).responseCode if (billingResultCode != BillingClient.BillingResponseCode.OK) { @@ -577,9 +554,11 @@ class RNIapModule( promise, ) { billingClient -> val acknowledgePurchaseParams = - AcknowledgePurchaseParams.newBuilder().setPurchaseToken( - token, - ).build() + AcknowledgePurchaseParams + .newBuilder() + .setPurchaseToken( + token, + ).build() billingClient.acknowledgePurchase( acknowledgePurchaseParams, ) { billingResult: BillingResult -> @@ -623,7 +602,10 @@ class RNIapModule( } } - override fun onPurchasesUpdated(billingResult: BillingResult, purchases: List?) { + override fun onPurchasesUpdated( + billingResult: BillingResult, + purchases: List?, + ) { val responseCode = billingResult.responseCode if (responseCode != BillingClient.BillingResponseCode.OK) { val error = Arguments.createMap() @@ -691,9 +673,11 @@ class RNIapModule( val types = arrayOf(BillingClient.ProductType.INAPP, BillingClient.ProductType.SUBS) for (type in types) { billingClient.queryPurchasesAsync( - QueryPurchasesParams.newBuilder().setProductType( - type, - ).build(), + QueryPurchasesParams + .newBuilder() + .setProductType( + type, + ).build(), ) { billingResult: BillingResult, list: List -> if (!isValidResult(billingResult, promise)) return@queryPurchasesAsync @@ -739,14 +723,17 @@ class RNIapModule( } init { - val lifecycleEventListener: LifecycleEventListener = object : LifecycleEventListener { - override fun onHostResume() {} - override fun onHostPause() {} - override fun onHostDestroy() { - billingClientCache?.endConnection() - billingClientCache = null + val lifecycleEventListener: LifecycleEventListener = + object : LifecycleEventListener { + override fun onHostResume() {} + + override fun onHostPause() {} + + override fun onHostDestroy() { + billingClientCache?.endConnection() + billingClientCache = null + } } - } reactContext.addLifecycleEventListener(lifecycleEventListener) } } diff --git a/android/src/play/java/com/dooboolab/rniap/RNIapPackage.kt b/android/src/play/java/com/dooboolab/rniap/RNIapPackage.kt index 5e3604f3f..140f9fd28 100644 --- a/android/src/play/java/com/dooboolab/rniap/RNIapPackage.kt +++ b/android/src/play/java/com/dooboolab/rniap/RNIapPackage.kt @@ -7,10 +7,7 @@ import com.facebook.react.uimanager.ViewManager import java.util.ArrayList class RNIapPackage : ReactPackage { - - override fun createViewManagers(reactContext: ReactApplicationContext): List> { - return emptyList() - } + override fun createViewManagers(reactContext: ReactApplicationContext): List> = emptyList() override fun createNativeModules(reactContext: ReactApplicationContext): List { val modules: MutableList = ArrayList() diff --git a/android/src/testAmazon/java/com/dooboolab/rniap/RNIapAmazonModuleTest.kt b/android/src/testAmazon/java/com/dooboolab/rniap/RNIapAmazonModuleTest.kt index e19f91f41..d05f4ab98 100644 --- a/android/src/testAmazon/java/com/dooboolab/rniap/RNIapAmazonModuleTest.kt +++ b/android/src/testAmazon/java/com/dooboolab/rniap/RNIapAmazonModuleTest.kt @@ -26,7 +26,6 @@ import org.junit.Test import java.util.* class RNIapAmazonModuleTest { - @MockK lateinit var context: ReactApplicationContext @@ -77,33 +76,38 @@ class RNIapAmazonModuleTest { @Test fun `Purchase Item`() { - val purchaseResponse = mockk() { - every { requestId } returns RequestId.fromString("0") - - every { requestStatus } returns PurchaseResponse.RequestStatus.SUCCESSFUL - val mReceipt = mockk(relaxed = true) { - every { sku } returns "mySku" - every { purchaseDate } returns Date() - every { receiptId } returns "rId" - } - every { receipt } returns mReceipt - val mUserData = mockk(relaxed = true) { - every { userId } returns "uid1" + val purchaseResponse = + mockk { + every { requestId } returns RequestId.fromString("0") + + every { requestStatus } returns PurchaseResponse.RequestStatus.SUCCESSFUL + val mReceipt = + mockk(relaxed = true) { + every { sku } returns "mySku" + every { purchaseDate } returns Date() + every { receiptId } returns "rId" + } + every { receipt } returns mReceipt + val mUserData = + mockk(relaxed = true) { + every { userId } returns "uid1" + } + every { userData } returns mUserData } - every { userData } returns mUserData - } every { eventSender.sendEvent(any(), any()) } just Runs every { purchasingServiceProxy.purchase(any()) } answers { listener.onPurchaseResponse( purchaseResponse, - ); RequestId.fromString("0") + ) + RequestId.fromString("0") } - val itemsMap = mockk(relaxed = true) { - every { getString("productId") } returns "mySku" - } + val itemsMap = + mockk(relaxed = true) { + every { getString("productId") } returns "mySku" + } mockkStatic(Arguments::class) every { Arguments.createMap() } returns itemsMap diff --git a/android/src/testPlay/java/com/dooboolab/rniap/RNIapModuleTest.kt b/android/src/testPlay/java/com/dooboolab/rniap/RNIapModuleTest.kt index 622f51144..89796e532 100644 --- a/android/src/testPlay/java/com/dooboolab/rniap/RNIapModuleTest.kt +++ b/android/src/testPlay/java/com/dooboolab/rniap/RNIapModuleTest.kt @@ -32,7 +32,6 @@ import org.junit.Before import org.junit.Test class RNIapModuleTest { - @MockK lateinit var context: ReactApplicationContext @@ -86,7 +85,9 @@ class RNIapModuleTest { val listener = slot() every { billingClient.startConnection(capture(listener)) } answers { listener.captured.onBillingSetupFinished( - BillingResult.newBuilder().setResponseCode(BillingClient.BillingResponseCode.OK) + BillingResult + .newBuilder() + .setResponseCode(BillingClient.BillingResponseCode.OK) .build(), ) } @@ -104,7 +105,9 @@ class RNIapModuleTest { val listener = slot() every { billingClient.startConnection(capture(listener)) } answers { listener.captured.onBillingSetupFinished( - BillingResult.newBuilder().setResponseCode(BillingClient.BillingResponseCode.ERROR) + BillingResult + .newBuilder() + .setResponseCode(BillingClient.BillingResponseCode.ERROR) .build(), ) } @@ -167,8 +170,10 @@ class RNIapModuleTest { val consumeListener = slot() every { billingClient.consumeAsync(any(), capture(consumeListener)) } answers { consumeListener.captured.onConsumeResponse( - BillingResult.newBuilder() - .setResponseCode(BillingClient.BillingResponseCode.ITEM_NOT_OWNED).build(), + BillingResult + .newBuilder() + .setResponseCode(BillingClient.BillingResponseCode.ITEM_NOT_OWNED) + .build(), "", ) } @@ -193,7 +198,9 @@ class RNIapModuleTest { val listener = slot() every { billingClient.startConnection(capture(listener)) } answers { listener.captured.onBillingSetupFinished( - BillingResult.newBuilder().setResponseCode(BillingClient.BillingResponseCode.OK) + BillingResult + .newBuilder() + .setResponseCode(BillingClient.BillingResponseCode.OK) .build(), ) } @@ -221,50 +228,56 @@ class RNIapModuleTest { every { productType } returns "sub" every { name } returns "name of product" - every { oneTimePurchaseOfferDetails } returns mockk { - every { priceCurrencyCode } returns "my code" - every { formattedPrice } returns "$20.00" - every { priceAmountMicros } returns 20000 - } - every { subscriptionOfferDetails } returns listOf( + every { oneTimePurchaseOfferDetails } returns mockk { - every { offerToken } returns "sToken" - every { basePlanId } returns "basePlanId" - every { offerId } returns "offerId" - every { offerTags } returns listOf("offerTag1", "offerTag2") - every { pricingPhases } returns mockk { - every { pricingPhaseList } returns listOf( + every { priceCurrencyCode } returns "my code" + every { formattedPrice } returns "$20.00" + every { priceAmountMicros } returns 20000 + } + every { subscriptionOfferDetails } returns + listOf( + mockk { + every { offerToken } returns "sToken" + every { basePlanId } returns "basePlanId" + every { offerId } returns "offerId" + every { offerTags } returns listOf("offerTag1", "offerTag2") + every { pricingPhases } returns mockk { - every { formattedPrice } returns "$13.0" - every { priceCurrencyCode } returns "USD" - every { billingPeriod } returns "1 week" - every { billingCycleCount } returns 1 - every { priceAmountMicros } returns 13000 - every { recurrenceMode } returns 2 - }, - ) - } - }, - ) + every { pricingPhaseList } returns + listOf( + mockk { + every { formattedPrice } returns "$13.0" + every { priceCurrencyCode } returns "USD" + every { billingPeriod } returns "1 week" + every { billingCycleCount } returns 1 + every { priceAmountMicros } returns 13000 + every { recurrenceMode } returns 2 + }, + ) + } + }, + ) }, ), ) } - val skus = mockk() { - every { size() } returns 1 - every { getString(0) } returns "sku0" - every { getType(0) } returns ReadableType.String - } + val skus = + mockk { + every { size() } returns 1 + every { getString(0) } returns "sku0" + every { getType(0) } returns ReadableType.String + } mockkStatic(Arguments::class) val itemsMap = mockk(relaxed = true) var itemsSize = 0 - val itemsArr = mockk { - every { pushString(any()) } just runs - every { pushMap(any()) } answers { - itemsSize++ + val itemsArr = + mockk { + every { pushString(any()) } just runs + every { pushMap(any()) } answers { + itemsSize++ + } } - } every { Arguments.createMap() } returns itemsMap every { Arguments.createArray() } returns itemsArr diff --git a/package.json b/package.json index 24bc5b437..47403e5f0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-iap", - "version": "12.14.1", + "version": "12.14.2-rc.1", "description": "React Native In App Purchase Module.", "repository": "https://github.com/dooboolab-community/react-native-iap", "author": "hyochan (https://github.com/hyochan)", @@ -71,6 +71,7 @@ "lint:ci": "yarn lint:tsc && yarn lint:eslint -f ./node_modules/@firmnav/eslint-github-actions-formatter/dist/formatter.js && yarn lint:prettier", "lint:prettier": "prettier --write \"**/*.{md,js,jsx,ts,tsx}\"", "lint:swift": "swiftlint lint --fix --format --path ios/*.swift --config .swiftlint.yml", + "lint:kotlin": "ktlint --format", "format": "git ls-files -m | xargs yarn prettier --write --ignore-unknown --no-error-on-unmatched-pattern", "bootstrap": "yarn example && yarn && yarn example pods", "gen:doc": "typedoc", diff --git a/plugin/build/withIAP.d.ts b/plugin/build/withIAP.d.ts index ed91aeab3..f37335a96 100644 --- a/plugin/build/withIAP.d.ts +++ b/plugin/build/withIAP.d.ts @@ -1,12 +1,9 @@ -import {ConfigPlugin} from 'expo/config-plugins'; +import { ConfigPlugin } from 'expo/config-plugins'; type PaymentProvider = 'Amazon AppStore' | 'both' | 'Play Store'; -export declare const modifyAppBuildGradle: ( - buildGradle: string, - paymentProvider: PaymentProvider, -) => string; +export declare const modifyAppBuildGradle: (buildGradle: string, paymentProvider: PaymentProvider) => string; export declare const modifyProjectBuildGradle: (buildGradle: string) => string; interface Props { - paymentProvider?: PaymentProvider; + paymentProvider?: PaymentProvider; } declare const _default: ConfigPlugin; export default _default; diff --git a/plugin/build/withIAP.js b/plugin/build/withIAP.js index 677e89836..afb79112c 100644 --- a/plugin/build/withIAP.js +++ b/plugin/build/withIAP.js @@ -1,16 +1,16 @@ -'use strict'; -Object.defineProperty(exports, '__esModule', {value: true}); +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); exports.modifyProjectBuildGradle = exports.modifyAppBuildGradle = void 0; -const config_plugins_1 = require('expo/config-plugins'); -const config_plugins_2 = require('expo/config-plugins'); +const config_plugins_1 = require("expo/config-plugins"); +const config_plugins_2 = require("expo/config-plugins"); const pkg = require('../../package.json'); const hasPaymentProviderProperValue = (paymentProvider) => { - return ['Amazon AppStore', 'Play Store', 'both'].includes(paymentProvider); + return ['Amazon AppStore', 'Play Store', 'both'].includes(paymentProvider); }; const linesToAdd = { - ['Amazon AppStore']: `missingDimensionStrategy "store", "amazon"`, - ['Play Store']: `missingDimensionStrategy "store", "play"`, - ['both']: `flavorDimensions "appstore" + ['Amazon AppStore']: `missingDimensionStrategy "store", "amazon"`, + ['Play Store']: `missingDimensionStrategy "store", "play"`, + ['both']: `flavorDimensions "appstore" productFlavors { googlePlay { @@ -25,83 +25,59 @@ productFlavors { }`, }; const addToBuildGradle = (newLine, anchor, offset, buildGradle) => { - const lines = buildGradle.split('\n'); - const lineIndex = lines.findIndex((line) => line.match(anchor)); - // add after given line - lines.splice(lineIndex + offset, 0, newLine); - return lines.join('\n'); + const lines = buildGradle.split('\n'); + const lineIndex = lines.findIndex((line) => line.match(anchor)); + // add after given line + lines.splice(lineIndex + offset, 0, newLine); + return lines.join('\n'); }; const modifyAppBuildGradle = (buildGradle, paymentProvider) => { - if (paymentProvider === 'both') { - if (buildGradle.includes(`flavorDimensions "appstore"`)) { - return buildGradle; + if (paymentProvider === 'both') { + if (buildGradle.includes(`flavorDimensions "appstore"`)) { + return buildGradle; + } + return addToBuildGradle(linesToAdd[paymentProvider], 'defaultConfig', -1, buildGradle); } - return addToBuildGradle( - linesToAdd[paymentProvider], - 'defaultConfig', - -1, - buildGradle, - ); - } - const missingDimensionStrategy = linesToAdd[paymentProvider]; - if (buildGradle.includes(missingDimensionStrategy)) { - return buildGradle; - } - return addToBuildGradle( - missingDimensionStrategy, - 'defaultConfig', - 1, - buildGradle, - ); + const missingDimensionStrategy = linesToAdd[paymentProvider]; + if (buildGradle.includes(missingDimensionStrategy)) { + return buildGradle; + } + return addToBuildGradle(missingDimensionStrategy, 'defaultConfig', 1, buildGradle); }; exports.modifyAppBuildGradle = modifyAppBuildGradle; const modifyProjectBuildGradle = (buildGradle) => { - const supportLibVersion = `supportLibVersion = "28.0.0"`; - if (buildGradle.includes(supportLibVersion)) { - return buildGradle; - } - return addToBuildGradle(supportLibVersion, 'ext', 1, buildGradle); + const supportLibVersion = `supportLibVersion = "28.0.0"`; + if (buildGradle.includes(supportLibVersion)) { + return buildGradle; + } + return addToBuildGradle(supportLibVersion, 'ext', 1, buildGradle); }; exports.modifyProjectBuildGradle = modifyProjectBuildGradle; -const withIAPAndroid = (config, {paymentProvider}) => { - // eslint-disable-next-line @typescript-eslint/no-shadow - config = (0, config_plugins_1.withAppBuildGradle)(config, (config) => { - config.modResults.contents = (0, exports.modifyAppBuildGradle)( - config.modResults.contents, - paymentProvider, - ); - return config; - }); - // eslint-disable-next-line @typescript-eslint/no-shadow - config = (0, config_plugins_1.withProjectBuildGradle)(config, (config) => { - config.modResults.contents = (0, exports.modifyProjectBuildGradle)( - config.modResults.contents, - ); +const withIAPAndroid = (config, { paymentProvider }) => { + // eslint-disable-next-line @typescript-eslint/no-shadow + config = (0, config_plugins_1.withAppBuildGradle)(config, (config) => { + config.modResults.contents = (0, exports.modifyAppBuildGradle)(config.modResults.contents, paymentProvider); + return config; + }); + // eslint-disable-next-line @typescript-eslint/no-shadow + config = (0, config_plugins_1.withProjectBuildGradle)(config, (config) => { + config.modResults.contents = (0, exports.modifyProjectBuildGradle)(config.modResults.contents); + return config; + }); return config; - }); - return config; }; const withIAP = (config, props) => { - const paymentProvider = props?.paymentProvider ?? 'Play Store'; - if (!hasPaymentProviderProperValue(paymentProvider)) { - config_plugins_1.WarningAggregator.addWarningAndroid( - 'react-native-iap', - `The payment provider '${paymentProvider}' is not supported. Please update your app.json file with one of the following supported values: 'Play Store', 'Amazon AppStore', or 'both'.`, - ); + const paymentProvider = props?.paymentProvider ?? 'Play Store'; + if (!hasPaymentProviderProperValue(paymentProvider)) { + config_plugins_1.WarningAggregator.addWarningAndroid('react-native-iap', `The payment provider '${paymentProvider}' is not supported. Please update your app.json file with one of the following supported values: 'Play Store', 'Amazon AppStore', or 'both'.`); + return config; + } + try { + config = withIAPAndroid(config, { paymentProvider }); + } + catch (error) { + config_plugins_1.WarningAggregator.addWarningAndroid('react-native-iap', `There was a problem configuring react-native-iap in your native Android project: ${error}`); + } return config; - } - try { - config = withIAPAndroid(config, {paymentProvider}); - } catch (error) { - config_plugins_1.WarningAggregator.addWarningAndroid( - 'react-native-iap', - `There was a problem configuring react-native-iap in your native Android project: ${error}`, - ); - } - return config; }; -exports.default = (0, config_plugins_2.createRunOncePlugin)( - withIAP, - pkg.name, - pkg.version, -); +exports.default = (0, config_plugins_2.createRunOncePlugin)(withIAP, pkg.name, pkg.version);