react-native-iap 9.0.0-beta9 → 9.0.0-rc.3

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/README.md CHANGED
@@ -19,9 +19,22 @@
19
19
 
20
20
  Published in [website](https://react-native-iap.dooboolab.com).
21
21
 
22
+ ## Our maintainers
23
+
24
+ Please [fund the project](https://opencollective.com/react-native-iap) if you are willing the maintainers to make the repository sustainable.
25
+
26
+ - [andresesfm](https://github.com/andresesfm)
27
+ - [jeremybarbet](https://github.com/jeremybarbet)
28
+
29
+ > The fund goes to maintainers.
30
+
31
+ ### Supporter
32
+
33
+ - [hyochan](https://github.com/hyochan)
34
+
22
35
  ## Announcement
23
36
 
24
- - Version `9.0.0` is currently in release candidate. The module migrates android sdk to [play billing library v5](https://qonversion.io/blog/google-play-billing-library-5-0) and iOS sdk to [storekit2](https://developer.apple.com/videos/play/wwdc2021/10114). Our core maintainer [andresesfm](https://github.com/andresesfm) is working hard on this. Please [fund the project](https://opencollective.com/react-native-iap) if you wish to support his effort. The fund goes to maintainers. To try the earlier version please use the `next` package.
37
+ - Version `9.0.0` is currently in release candidate. The module migrates android sdk to [play billing library v5](https://qonversion.io/blog/google-play-billing-library-5-0) and iOS sdk to [storekit2](https://developer.apple.com/videos/play/wwdc2021/10114). Our core maintainers [andresesfm](https://github.com/andresesfm) and [jeremybarbet](https://github.com/jeremybarbet) are working hard on this.
25
38
 
26
39
  ```
27
40
  yarn add react-native-iap@next
@@ -69,12 +69,7 @@ class RNIapAmazonModule(reactContext: ReactApplicationContext) :
69
69
 
70
70
  @ReactMethod
71
71
  fun buyItemByType(
72
- type: String?,
73
72
  sku: String?,
74
- purchaseToken: String?,
75
- prorationMode: Int?,
76
- obfuscatedAccountId: String?,
77
- obfuscatedProfileId: String?,
78
73
  promise: Promise
79
74
  ) {
80
75
  DoobooUtils.instance.addPromiseForKey(PROMISE_BUY_ITEM, promise)
@@ -87,7 +87,7 @@ class RNIapModule(
87
87
  }
88
88
 
89
89
  @ReactMethod
90
- fun initConnection(promise: Promise) {
90
+ fun initConnection(promise: Promise) {
91
91
  if (googleApiAvailability.isGooglePlayServicesAvailable(reactContext)
92
92
  != ConnectionResult.SUCCESS
93
93
  ) {
@@ -108,13 +108,13 @@ class RNIapModule(
108
108
  billingClientCache = it
109
109
  it.startConnection(
110
110
  object : BillingClientStateListener {
111
- override fun onBillingSetupFinished(billingResult: BillingResult) {
111
+ override fun onBillingSetupFinished(billingResult: BillingResult) {
112
112
  if (!isValidResult(billingResult, promise)) return
113
113
 
114
114
  promise.safeResolve(true)
115
115
  }
116
116
 
117
- override fun onBillingServiceDisconnected() {
117
+ override fun onBillingServiceDisconnected() {
118
118
  Log.i(TAG, "Billing service disconnected")
119
119
  }
120
120
  }
@@ -123,7 +123,7 @@ class RNIapModule(
123
123
  }
124
124
 
125
125
  @ReactMethod
126
- fun endConnection(promise: Promise) {
126
+ fun endConnection(promise: Promise) {
127
127
  billingClientCache?.endConnection()
128
128
  billingClientCache = null
129
129
  promise.safeResolve(true)
@@ -160,7 +160,7 @@ class RNIapModule(
160
160
  }
161
161
 
162
162
  @ReactMethod
163
- fun flushFailedPurchasesCachedAsPending(promise: Promise) {
163
+ fun flushFailedPurchasesCachedAsPending(promise: Promise) {
164
164
  ensureConnection(
165
165
  promise
166
166
  ) { billingClient ->
@@ -193,7 +193,7 @@ class RNIapModule(
193
193
  }
194
194
 
195
195
  @ReactMethod
196
- fun getItemsByType(type: String, skuArr: ReadableArray, promise: Promise) {
196
+ fun getItemsByType(type: String, skuArr: ReadableArray, promise: Promise) {
197
197
  ensureConnection(
198
198
  promise
199
199
  ) { billingClient ->
@@ -317,37 +317,34 @@ class RNIapModule(
317
317
  ).build()
318
318
  ) { billingResult: BillingResult, purchases: List<Purchase>? ->
319
319
  if (!isValidResult(billingResult, promise)) return@queryPurchasesAsync
320
- if (purchases != null) {
321
- for (i in purchases.indices) {
322
- val purchase = purchases[i]
323
- val item = WritableNativeMap()
324
- item.putString("productId", purchase.products[0])// kept for convenience/backward-compatibility. productIds has the complete list
325
- val products = Arguments.createArray()
326
- purchase.products.forEach { products.pushString(it) }
327
- item.putArray("productIds", products)
328
- item.putString("transactionId", purchase.orderId)
329
- item.putDouble("transactionDate", purchase.purchaseTime.toDouble())
330
- item.putString("transactionReceipt", purchase.originalJson)
331
- item.putString("orderId", purchase.orderId)
332
- item.putString("purchaseToken", purchase.purchaseToken)
333
- item.putString("developerPayloadAndroid", purchase.developerPayload)
334
- item.putString("signatureAndroid", purchase.signature)
335
- item.putInt("purchaseStateAndroid", purchase.purchaseState)
336
- item.putBoolean("isAcknowledgedAndroid", purchase.isAcknowledged)
337
- item.putString("packageNameAndroid", purchase.packageName)
338
- item.putString(
339
- "obfuscatedAccountIdAndroid",
340
- purchase.accountIdentifiers?.obfuscatedAccountId
341
- )
342
- item.putString(
343
- "obfuscatedProfileIdAndroid",
344
- purchase.accountIdentifiers?.obfuscatedProfileId
345
- )
346
- if (type == BillingClient.ProductType.SUBS) {
347
- item.putBoolean("autoRenewingAndroid", purchase.isAutoRenewing)
348
- }
349
- items.pushMap(item)
320
+ purchases?.forEach { purchase ->
321
+ val item = WritableNativeMap()
322
+ item.putString("productId", purchase.products[0]) // kept for convenience/backward-compatibility. productIds has the complete list
323
+ val products = Arguments.createArray()
324
+ purchase.products.forEach { products.pushString(it) }
325
+ item.putArray("productIds", products)
326
+ item.putString("transactionId", purchase.orderId)
327
+ item.putDouble("transactionDate", purchase.purchaseTime.toDouble())
328
+ item.putString("transactionReceipt", purchase.originalJson)
329
+ item.putString("orderId", purchase.orderId)
330
+ item.putString("purchaseToken", purchase.purchaseToken)
331
+ item.putString("developerPayloadAndroid", purchase.developerPayload)
332
+ item.putString("signatureAndroid", purchase.signature)
333
+ item.putInt("purchaseStateAndroid", purchase.purchaseState)
334
+ item.putBoolean("isAcknowledgedAndroid", purchase.isAcknowledged)
335
+ item.putString("packageNameAndroid", purchase.packageName)
336
+ item.putString(
337
+ "obfuscatedAccountIdAndroid",
338
+ purchase.accountIdentifiers?.obfuscatedAccountId
339
+ )
340
+ item.putString(
341
+ "obfuscatedProfileIdAndroid",
342
+ purchase.accountIdentifiers?.obfuscatedProfileId
343
+ )
344
+ if (type == BillingClient.ProductType.SUBS) {
345
+ item.putBoolean("autoRenewingAndroid", purchase.isAutoRenewing)
350
346
  }
347
+ items.pushMap(item)
351
348
  }
352
349
  promise.safeResolve(items)
353
350
  }
@@ -397,7 +394,8 @@ class RNIapModule(
397
394
  prorationMode: Int,
398
395
  obfuscatedAccountId: String?,
399
396
  obfuscatedProfileId: String?,
400
- selectedOfferIndexArr: ReadableArray, // New optional parameter in V5
397
+ offerTokenArr: ReadableArray, // New parameter in V5
398
+ isOfferPersonalized: Boolean, // New parameter in V5
401
399
  promise: Promise
402
400
  ) {
403
401
  val activity = currentActivity
@@ -412,36 +410,42 @@ class RNIapModule(
412
410
  PROMISE_BUY_ITEM,
413
411
  promise
414
412
  )
413
+ if (type == BillingClient.ProductType.SUBS && skuArr.size() != offerTokenArr.size()) {
414
+ val debugMessage =
415
+ "The number of skus (${skuArr.size()}) must match: the number of offerTokens (${offerTokenArr.size()}) for Subscriptions"
416
+ val error = Arguments.createMap()
417
+ error.putString("debugMessage", debugMessage)
418
+ error.putString("code", PROMISE_BUY_ITEM)
419
+ error.putString("message", debugMessage)
420
+ sendEvent(reactContext, "purchase-error", error)
421
+ promise.safeReject(PROMISE_BUY_ITEM, debugMessage)
422
+ return@ensureConnection
423
+ }
415
424
  val productParamsList =
416
- skuArr.toArrayList().map { it.toString() }.mapIndexed{ index,sku ->
417
- val selectedSku: ProductDetails? = skus[sku]
418
- if (selectedSku == null) {
419
- val debugMessage =
420
- "The sku was not found. Please fetch products first by calling getItems"
421
- val error = Arguments.createMap()
422
- error.putString("debugMessage", debugMessage)
423
- error.putString("code", PROMISE_BUY_ITEM)
424
- error.putString("message", debugMessage)
425
- error.putString("productId", sku)
426
- sendEvent(reactContext, "purchase-error", error)
427
- promise.safeReject(PROMISE_BUY_ITEM, debugMessage)
428
- return@ensureConnection
429
- }
430
- var productParams = BillingFlowParams.ProductDetailsParams.newBuilder().setProductDetails(selectedSku)
431
- val selectedOfferIndex = selectedOfferIndexArr.getInt(index)
432
- if (selectedOfferIndex > -1 && (
433
- selectedSku.subscriptionOfferDetails?.size
434
- ?: 0
435
- ) > selectedOfferIndex
436
- ) {
437
- val offerToken =
438
- selectedSku.subscriptionOfferDetails?.get(selectedOfferIndex)?.offerToken
439
- offerToken?.let { productParams = productParams.setOfferToken(offerToken) }
425
+ skuArr.toArrayList().map { it.toString() }.mapIndexed { index, sku ->
426
+ val selectedSku: ProductDetails? = skus[sku]
427
+ if (selectedSku == null) {
428
+ val debugMessage =
429
+ "The sku was not found. Please fetch products first by calling getItems"
430
+ val error = Arguments.createMap()
431
+ error.putString("debugMessage", debugMessage)
432
+ error.putString("code", PROMISE_BUY_ITEM)
433
+ error.putString("message", debugMessage)
434
+ error.putString("productId", sku)
435
+ sendEvent(reactContext, "purchase-error", error)
436
+ promise.safeReject(PROMISE_BUY_ITEM, debugMessage)
437
+ return@ensureConnection
438
+ }
439
+ var productDetailParams = BillingFlowParams.ProductDetailsParams.newBuilder().setProductDetails(selectedSku)
440
+ if (type == BillingClient.ProductType.SUBS) {
441
+ val offerToken = offerTokenArr.getString(index)
442
+ productDetailParams = productDetailParams.setOfferToken(offerToken)
443
+ }
444
+ productDetailParams.build()
440
445
  }
441
- productParams.build()
442
- }
443
446
  val builder = BillingFlowParams.newBuilder()
444
- builder.setProductDetailsParamsList(productParamsList)
447
+ builder.setProductDetailsParamsList(productParamsList).setIsOfferPersonalized(isOfferPersonalized)
448
+
445
449
  val subscriptionUpdateParamsBuilder = SubscriptionUpdateParams.newBuilder()
446
450
  if (purchaseToken != null) {
447
451
  subscriptionUpdateParamsBuilder.setOldPurchaseToken(purchaseToken)
@@ -571,7 +575,7 @@ class RNIapModule(
571
575
  .getBillingResponseData(billingResult.responseCode)
572
576
  map.putString("code", errorData[0])
573
577
  map.putString("message", errorData[1])
574
- map.putString("purchaseToken",purchaseToken)
578
+ map.putString("purchaseToken", purchaseToken)
575
579
  promise.safeResolve(map)
576
580
  }
577
581
  }
@@ -621,7 +625,7 @@ class RNIapModule(
621
625
  accountIdentifiers.obfuscatedProfileId
622
626
  )
623
627
  }
624
- promiseItems.pushMap(item)
628
+ promiseItems.pushMap(item.copy())
625
629
  sendEvent(reactContext, "purchase-updated", item)
626
630
  }
627
631
  DoobooUtils.instance.resolvePromisesForKey(PROMISE_BUY_ITEM, promiseItems)
@@ -4,12 +4,10 @@ import com.android.billingclient.api.BillingClient
4
4
  import com.android.billingclient.api.BillingClientStateListener
5
5
  import com.android.billingclient.api.BillingResult
6
6
  import com.android.billingclient.api.ConsumeResponseListener
7
- import com.android.billingclient.api.ProductDetails
8
7
  import com.android.billingclient.api.ProductDetailsResponseListener
9
8
  import com.android.billingclient.api.Purchase
10
9
  import com.android.billingclient.api.PurchasesResponseListener
11
10
  import com.android.billingclient.api.QueryPurchasesParams
12
- import com.android.billingclient.api.SkuDetailsResponseListener
13
11
  import com.facebook.react.bridge.Arguments
14
12
  import com.facebook.react.bridge.Promise
15
13
  import com.facebook.react.bridge.ReactApplicationContext
@@ -43,7 +43,8 @@ class RNIapIos: RCTEventEmitter, SKRequestDelegate, SKPaymentTransactionObserver
43
43
  private var promotedPayment: SKPayment?
44
44
  private var promotedProduct: SKProduct?
45
45
  private var productsRequest: SKProductsRequest?
46
- private var countPendingTransaction: Int?
46
+ private var countPendingTransaction: Int = 0
47
+ private var hasTransactionObserver = false
47
48
 
48
49
  override init() {
49
50
  promisesByKey = [String: [RNIapIosPromise]]()
@@ -51,17 +52,31 @@ class RNIapIos: RCTEventEmitter, SKRequestDelegate, SKPaymentTransactionObserver
51
52
  myQueue = DispatchQueue(label: "reject")
52
53
  validProducts = [SKProduct]()
53
54
  super.init()
54
- SKPaymentQueue.default().add(self)
55
+ addTransactionObserver()
55
56
  }
56
57
 
57
58
  deinit {
58
- SKPaymentQueue.default().remove(self)
59
+ removeTransactionObserver()
59
60
  }
60
61
 
61
62
  override class func requiresMainQueueSetup() -> Bool {
62
63
  return true
63
64
  }
64
65
 
66
+ func addTransactionObserver() {
67
+ if !hasTransactionObserver {
68
+ hasTransactionObserver = true
69
+ SKPaymentQueue.default().add(self)
70
+ }
71
+ }
72
+
73
+ func removeTransactionObserver() {
74
+ if hasTransactionObserver {
75
+ hasTransactionObserver = false
76
+ SKPaymentQueue.default().remove(self)
77
+ }
78
+ }
79
+
65
80
  func flushUnheardEvents() {
66
81
  paymentQueue(SKPaymentQueue.default(), updatedTransactions: SKPaymentQueue.default().transactions)
67
82
  }
@@ -136,6 +151,7 @@ class RNIapIos: RCTEventEmitter, SKRequestDelegate, SKPaymentTransactionObserver
136
151
  _ resolve: @escaping RCTPromiseResolveBlock = { _ in },
137
152
  reject: @escaping RCTPromiseRejectBlock = { _, _, _ in }
138
153
  ) {
154
+ addTransactionObserver()
139
155
  let canMakePayments = SKPaymentQueue.canMakePayments()
140
156
  resolve(NSNumber(value: canMakePayments))
141
157
  }
@@ -143,7 +159,7 @@ class RNIapIos: RCTEventEmitter, SKRequestDelegate, SKPaymentTransactionObserver
143
159
  _ resolve: @escaping RCTPromiseResolveBlock = { _ in },
144
160
  reject: @escaping RCTPromiseRejectBlock = { _, _, _ in }
145
161
  ) {
146
- SKPaymentQueue.default().remove(self)
162
+ removeTransactionObserver()
147
163
  resolve(nil)
148
164
  }
149
165
  @objc public func getItems(
@@ -300,18 +316,19 @@ class RNIapIos: RCTEventEmitter, SKRequestDelegate, SKPaymentTransactionObserver
300
316
  _ resolve: @escaping RCTPromiseResolveBlock = { _ in },
301
317
  reject: @escaping RCTPromiseRejectBlock = { _, _, _ in }
302
318
  ) {
303
- debugMessage("clear remaining Transactions. Call this before make a new transaction")
304
-
305
319
  let pendingTrans = SKPaymentQueue.default().transactions
306
- let countPendingTransaction = pendingTrans.count
320
+ countPendingTransaction = pendingTrans.count
321
+
322
+ debugMessage("clear remaining Transactions (\(countPendingTransaction)). Call this before make a new transaction")
307
323
 
308
324
  if countPendingTransaction > 0 {
309
325
  addPromise(forKey: "cleaningTransactions", resolve: resolve, reject: reject)
310
326
  for transaction in pendingTrans {
311
327
  SKPaymentQueue.default().finishTransaction(transaction)
312
328
  }
329
+ } else {
330
+ resolve(nil)
313
331
  }
314
- resolve(nil)
315
332
  }
316
333
 
317
334
  @objc public func clearProducts(
@@ -936,18 +953,14 @@ class RNIapIos: RCTEventEmitter, SKRequestDelegate, SKPaymentTransactionObserver
936
953
  }
937
954
 
938
955
  func paymentQueue(_ queue: SKPaymentQueue, removedTransactions transactions: [SKPaymentTransaction]) {
939
- debugMessage("removedTransactions")
956
+ debugMessage("removedTransactions - countPendingTransactions \(countPendingTransaction)")
940
957
 
941
- guard var unwrappedCount = countPendingTransaction else {
942
- return
943
- }
944
-
945
- if unwrappedCount > 0 {
946
- unwrappedCount -= transactions.count
958
+ if countPendingTransaction > 0 {
959
+ countPendingTransaction -= transactions.count
947
960
 
948
- if unwrappedCount == 0 {
961
+ if countPendingTransaction <= 0 {
949
962
  resolvePromises(forKey: "cleaningTransactions", value: nil)
950
- countPendingTransaction = nil
963
+ countPendingTransaction = 0
951
964
  }
952
965
  }
953
966
  }
@@ -81,7 +81,7 @@ function withIAPContext(Component) {
81
81
  return () => {
82
82
  purchaseUpdateSubscription.remove();
83
83
  purchaseErrorSubscription.remove();
84
- promotedProductSubscription.remove();
84
+ promotedProductSubscription === null || promotedProductSubscription === void 0 ? void 0 : promotedProductSubscription.remove();
85
85
  };
86
86
  }, [connected]);
87
87
  return /*#__PURE__*/_react.default.createElement(IAPContext.Provider, {
@@ -1 +1 @@
1
- {"version":3,"names":["IAPContext","React","createContext","useIAPContext","ctx","useContext","Error","withIAPContext","Component","WrapperComponent","props","connected","setConnected","useState","products","setProducts","promotedProductsIOS","setPromotedProductsIOS","subscriptions","setSubscriptions","purchaseHistories","setPurchaseHistories","availablePurchases","setAvailablePurchases","currentPurchase","setCurrentPurchase","currentPurchaseError","setCurrentPurchaseError","initConnectionError","setInitConnectionError","context","useMemo","useEffect","initConnection","then","value","undefined","catch","purchaseUpdateSubscription","purchaseUpdatedListener","purchase","purchaseErrorSubscription","purchaseErrorListener","error","promotedProductSubscription","promotedProductListener","product","getPromotedProductIOS","prevProducts","remove"],"sources":["withIAPContext.tsx"],"sourcesContent":["import React, {useContext, useEffect, useMemo, useState} from 'react';\n\nimport {\n getPromotedProductIOS,\n initConnection,\n promotedProductListener,\n purchaseErrorListener,\n purchaseUpdatedListener,\n} from '../iap';\nimport type {\n InAppPurchase,\n Product,\n Purchase,\n PurchaseError,\n Subscription,\n SubscriptionPurchase,\n} from '../types';\n\ntype IAPContextType = {\n connected: boolean;\n products: Product[];\n promotedProductsIOS: Product[];\n subscriptions: Subscription[];\n purchaseHistories: Purchase[];\n availablePurchases: Purchase[];\n currentPurchase?: Purchase;\n currentPurchaseError?: PurchaseError;\n initConnectionError?: Error;\n setProducts: (products: Product[]) => void;\n setSubscriptions: (subscriptions: Subscription[]) => void;\n setPurchaseHistories: (purchaseHistories: Purchase[]) => void;\n setAvailablePurchases: (availablePurchases: Purchase[]) => void;\n setCurrentPurchase: (currentPurchase: Purchase | undefined) => void;\n setCurrentPurchaseError: (\n currentPurchaseError: PurchaseError | undefined,\n ) => void;\n};\n\n// @ts-ignore\nconst IAPContext = React.createContext<IAPContextType>(null);\n\nexport function useIAPContext(): IAPContextType {\n const ctx = useContext(IAPContext);\n\n if (!ctx) {\n throw new Error('You need wrap your app with withIAPContext HOC');\n }\n\n return ctx;\n}\n\nexport function withIAPContext<T>(Component: React.ComponentType<T>) {\n return function WrapperComponent(props: T) {\n const [connected, setConnected] = useState(false);\n const [products, setProducts] = useState<Product[]>([]);\n\n const [promotedProductsIOS, setPromotedProductsIOS] = useState<Product[]>(\n [],\n );\n const [subscriptions, setSubscriptions] = useState<Subscription[]>([]);\n const [purchaseHistories, setPurchaseHistories] = useState<Purchase[]>([]);\n\n const [availablePurchases, setAvailablePurchases] = useState<Purchase[]>(\n [],\n );\n const [currentPurchase, setCurrentPurchase] = useState<Purchase>();\n\n const [currentPurchaseError, setCurrentPurchaseError] =\n useState<PurchaseError>();\n\n const [initConnectionError, setInitConnectionError] = useState<Error>();\n\n const context = useMemo(\n () => ({\n connected,\n products,\n subscriptions,\n promotedProductsIOS,\n purchaseHistories,\n availablePurchases,\n currentPurchase,\n currentPurchaseError,\n initConnectionError,\n setProducts,\n setSubscriptions,\n setPurchaseHistories,\n setAvailablePurchases,\n setCurrentPurchase,\n setCurrentPurchaseError,\n }),\n [\n connected,\n products,\n subscriptions,\n promotedProductsIOS,\n purchaseHistories,\n availablePurchases,\n currentPurchase,\n currentPurchaseError,\n initConnectionError,\n setProducts,\n setSubscriptions,\n setPurchaseHistories,\n setAvailablePurchases,\n setCurrentPurchase,\n setCurrentPurchaseError,\n ],\n );\n\n useEffect(() => {\n initConnection()\n .then((value) => {\n setInitConnectionError(undefined);\n setConnected(value);\n })\n .catch(setInitConnectionError);\n }, []);\n\n useEffect(() => {\n if (!connected) {\n return;\n }\n\n const purchaseUpdateSubscription = purchaseUpdatedListener(\n async (purchase: InAppPurchase | SubscriptionPurchase) => {\n setCurrentPurchaseError(undefined);\n setCurrentPurchase(purchase);\n },\n );\n\n const purchaseErrorSubscription = purchaseErrorListener(\n (error: PurchaseError) => {\n setCurrentPurchase(undefined);\n setCurrentPurchaseError(error);\n },\n );\n\n const promotedProductSubscription = promotedProductListener(async () => {\n const product = await getPromotedProductIOS();\n\n setPromotedProductsIOS((prevProducts) => [\n ...prevProducts,\n ...(product ? [product] : []),\n ]);\n });\n\n return () => {\n purchaseUpdateSubscription.remove();\n purchaseErrorSubscription.remove();\n promotedProductSubscription.remove();\n };\n }, [connected]);\n\n return (\n <IAPContext.Provider value={context}>\n <Component {...props} />\n </IAPContext.Provider>\n );\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"names":["IAPContext","React","createContext","useIAPContext","ctx","useContext","Error","withIAPContext","Component","WrapperComponent","props","connected","setConnected","useState","products","setProducts","promotedProductsIOS","setPromotedProductsIOS","subscriptions","setSubscriptions","purchaseHistories","setPurchaseHistories","availablePurchases","setAvailablePurchases","currentPurchase","setCurrentPurchase","currentPurchaseError","setCurrentPurchaseError","initConnectionError","setInitConnectionError","context","useMemo","useEffect","initConnection","then","value","undefined","catch","purchaseUpdateSubscription","purchaseUpdatedListener","purchase","purchaseErrorSubscription","purchaseErrorListener","error","promotedProductSubscription","promotedProductListener","product","getPromotedProductIOS","prevProducts","remove"],"sources":["withIAPContext.tsx"],"sourcesContent":["import React, {useContext, useEffect, useMemo, useState} from 'react';\n\nimport {\n getPromotedProductIOS,\n initConnection,\n promotedProductListener,\n purchaseErrorListener,\n purchaseUpdatedListener,\n} from '../iap';\nimport type {\n InAppPurchase,\n Product,\n Purchase,\n PurchaseError,\n Subscription,\n SubscriptionPurchase,\n} from '../types';\n\ntype IAPContextType = {\n connected: boolean;\n products: Product[];\n promotedProductsIOS: Product[];\n subscriptions: Subscription[];\n purchaseHistories: Purchase[];\n availablePurchases: Purchase[];\n currentPurchase?: Purchase;\n currentPurchaseError?: PurchaseError;\n initConnectionError?: Error;\n setProducts: (products: Product[]) => void;\n setSubscriptions: (subscriptions: Subscription[]) => void;\n setPurchaseHistories: (purchaseHistories: Purchase[]) => void;\n setAvailablePurchases: (availablePurchases: Purchase[]) => void;\n setCurrentPurchase: (currentPurchase: Purchase | undefined) => void;\n setCurrentPurchaseError: (\n currentPurchaseError: PurchaseError | undefined,\n ) => void;\n};\n\n// @ts-ignore\nconst IAPContext = React.createContext<IAPContextType>(null);\n\nexport function useIAPContext(): IAPContextType {\n const ctx = useContext(IAPContext);\n\n if (!ctx) {\n throw new Error('You need wrap your app with withIAPContext HOC');\n }\n\n return ctx;\n}\n\nexport function withIAPContext<T>(Component: React.ComponentType<T>) {\n return function WrapperComponent(props: T) {\n const [connected, setConnected] = useState(false);\n const [products, setProducts] = useState<Product[]>([]);\n\n const [promotedProductsIOS, setPromotedProductsIOS] = useState<Product[]>(\n [],\n );\n const [subscriptions, setSubscriptions] = useState<Subscription[]>([]);\n const [purchaseHistories, setPurchaseHistories] = useState<Purchase[]>([]);\n\n const [availablePurchases, setAvailablePurchases] = useState<Purchase[]>(\n [],\n );\n const [currentPurchase, setCurrentPurchase] = useState<Purchase>();\n\n const [currentPurchaseError, setCurrentPurchaseError] =\n useState<PurchaseError>();\n\n const [initConnectionError, setInitConnectionError] = useState<Error>();\n\n const context = useMemo(\n () => ({\n connected,\n products,\n subscriptions,\n promotedProductsIOS,\n purchaseHistories,\n availablePurchases,\n currentPurchase,\n currentPurchaseError,\n initConnectionError,\n setProducts,\n setSubscriptions,\n setPurchaseHistories,\n setAvailablePurchases,\n setCurrentPurchase,\n setCurrentPurchaseError,\n }),\n [\n connected,\n products,\n subscriptions,\n promotedProductsIOS,\n purchaseHistories,\n availablePurchases,\n currentPurchase,\n currentPurchaseError,\n initConnectionError,\n setProducts,\n setSubscriptions,\n setPurchaseHistories,\n setAvailablePurchases,\n setCurrentPurchase,\n setCurrentPurchaseError,\n ],\n );\n\n useEffect(() => {\n initConnection()\n .then((value) => {\n setInitConnectionError(undefined);\n setConnected(value);\n })\n .catch(setInitConnectionError);\n }, []);\n\n useEffect(() => {\n if (!connected) {\n return;\n }\n\n const purchaseUpdateSubscription = purchaseUpdatedListener(\n async (purchase: InAppPurchase | SubscriptionPurchase) => {\n setCurrentPurchaseError(undefined);\n setCurrentPurchase(purchase);\n },\n );\n\n const purchaseErrorSubscription = purchaseErrorListener(\n (error: PurchaseError) => {\n setCurrentPurchase(undefined);\n setCurrentPurchaseError(error);\n },\n );\n\n const promotedProductSubscription = promotedProductListener(async () => {\n const product = await getPromotedProductIOS();\n\n setPromotedProductsIOS((prevProducts) => [\n ...prevProducts,\n ...(product ? [product] : []),\n ]);\n });\n\n return () => {\n purchaseUpdateSubscription.remove();\n purchaseErrorSubscription.remove();\n promotedProductSubscription?.remove();\n };\n }, [connected]);\n\n return (\n <IAPContext.Provider value={context}>\n <Component {...props} />\n </IAPContext.Provider>\n );\n };\n}\n"],"mappings":"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"}
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.validateReceiptIos = exports.validateReceiptAndroid = exports.validateReceiptAmazon = exports.setAndroidNativeModule = exports.requestSubscription = exports.requestPurchaseWithQuantityIOS = exports.requestPurchaseWithOfferIOS = exports.requestPurchase = exports.purchaseUpdatedListener = exports.purchaseErrorListener = exports.promotedProductListener = exports.presentCodeRedemptionSheetIOS = exports.initConnection = exports.getSubscriptions = exports.getPurchaseHistory = exports.getPromotedProductIOS = exports.getProducts = exports.getPendingPurchasesIOS = exports.getInstallSourceAndroid = exports.getAvailablePurchases = exports.flushFailedPurchasesCachedAsPendingAndroid = exports.finishTransaction = exports.endConnection = exports.deepLinkToSubscriptionsAndroid = exports.clearTransactionIOS = exports.clearProductsIOS = exports.buyPromotedProductIOS = exports.acknowledgePurchaseAndroid = void 0;
6
+ exports.validateReceiptIos = exports.validateReceiptAndroid = exports.validateReceiptAmazon = exports.setAndroidNativeModule = exports.requestSubscription = exports.requestPurchaseWithQuantityIOS = exports.requestPurchaseWithOfferIOS = exports.requestPurchase = exports.purchaseUpdatedListener = exports.purchaseErrorListener = exports.promotedProductListener = exports.presentCodeRedemptionSheetIOS = exports.initConnection = exports.getSubscriptions = exports.getReceiptIOS = exports.getPurchaseHistory = exports.getPromotedProductIOS = exports.getProducts = exports.getPendingPurchasesIOS = exports.getInstallSourceAndroid = exports.getAvailablePurchases = exports.flushFailedPurchasesCachedAsPendingAndroid = exports.finishTransaction = exports.endConnection = exports.deepLinkToSubscriptionsAndroid = exports.clearTransactionIOS = exports.clearProductsIOS = exports.buyPromotedProductIOS = exports.acknowledgePurchaseAndroid = exports.IapError = void 0;
7
7
 
8
8
  var _reactNative = require("react-native");
9
9
 
@@ -17,9 +17,23 @@ const {
17
17
  RNIapAmazonModule
18
18
  } = _reactNative.NativeModules;
19
19
  const isAndroid = _reactNative.Platform.OS === 'android';
20
+ const isAmazon = isAndroid && !!RNIapAmazonModule;
21
+ const isIos = _reactNative.Platform.OS === 'ios';
20
22
  const ANDROID_ITEM_TYPE_SUBSCRIPTION = 'subs';
21
23
  const ANDROID_ITEM_TYPE_IAP = 'inapp';
22
24
 
25
+ class IapError {
26
+ constructor(code, message) {
27
+ this.code = code;
28
+ this.message = message;
29
+ this.code = code;
30
+ this.message = message;
31
+ }
32
+
33
+ }
34
+
35
+ exports.IapError = IapError;
36
+
23
37
  const getInstallSourceAndroid = () => {
24
38
  return RNIapModule ? _types.InstallSourceAndroid.GOOGLE_PLAY : _types.InstallSourceAndroid.AMAZON;
25
39
  };
@@ -207,6 +221,8 @@ const getAvailablePurchases = () => (_reactNative.Platform.select({
207
221
  * @param {boolean} [andDangerouslyFinishTransactionAutomaticallyIOS] You should set this to false and call finishTransaction manually when you have delivered the purchased goods to the user. It defaults to true to provide backwards compatibility. Will default to false in version 4.0.0.
208
222
  * @param {string} [obfuscatedAccountIdAndroid] Specifies an optional obfuscated string that is uniquely associated with the user's account in your app.
209
223
  * @param {string} [obfuscatedProfileIdAndroid] Specifies an optional obfuscated string that is uniquely associated with the user's profile in your app.
224
+ * @param {string[]} [skus] Product Ids to purchase. Note that this is only for Android. iOS only uses a single SKU. If not provided, it'll default to using [sku] for backward-compatibility
225
+ * @param {boolean} [isOfferPersonalized] Defaults to false, Only for Android V5
210
226
  * @returns {Promise<InAppPurchase>}
211
227
  */
212
228
 
@@ -219,7 +235,11 @@ const requestPurchase = _ref => {
219
235
  andDangerouslyFinishTransactionAutomaticallyIOS = false,
220
236
  obfuscatedAccountIdAndroid,
221
237
  obfuscatedProfileIdAndroid,
222
- applicationUsername
238
+ applicationUsername,
239
+ skus,
240
+ // Android Billing V5
241
+ isOfferPersonalized = undefined // Android Billing V5
242
+
223
243
  } = _ref;
224
244
  return (_reactNative.Platform.select({
225
245
  ios: async () => {
@@ -230,7 +250,7 @@ const requestPurchase = _ref => {
230
250
  return getIosModule().buyProduct(sku, andDangerouslyFinishTransactionAutomaticallyIOS, applicationUsername);
231
251
  },
232
252
  android: async () => {
233
- return getAndroidModule().buyItemByType(ANDROID_ITEM_TYPE_IAP, [sku], null, -1, obfuscatedAccountIdAndroid, obfuscatedProfileIdAndroid, undefined);
253
+ return getAndroidModule().buyItemByType(ANDROID_ITEM_TYPE_IAP, skus !== null && skus !== void 0 && skus.length ? skus : [sku], null, -1, obfuscatedAccountIdAndroid, obfuscatedProfileIdAndroid, undefined, isOfferPersonalized ?? false);
234
254
  }
235
255
  }) || Promise.resolve)();
236
256
  };
@@ -243,7 +263,7 @@ const requestPurchase = _ref => {
243
263
  * @param {ProrationModesAndroid} [prorationModeAndroid] UNKNOWN_SUBSCRIPTION_UPGRADE_DOWNGRADE_POLICY, IMMEDIATE_WITH_TIME_PRORATION, IMMEDIATE_AND_CHARGE_PRORATED_PRICE, IMMEDIATE_WITHOUT_PRORATION, DEFERRED
244
264
  * @param {string} [obfuscatedAccountIdAndroid] Specifies an optional obfuscated string that is uniquely associated with the user's account in your app.
245
265
  * @param {string} [obfuscatedProfileIdAndroid] Specifies an optional obfuscated string that is uniquely associated with the user's profile in your app.
246
- * @param {string} [selectedOfferIndex] Selected Offer index from the list returned by get products
266
+ * @param {SubscriptionOffers[]} [subscriptionOffers] Array of SubscriptionOffers. Every sku must be paired with a corresponding offerToken
247
267
  * @returns {Promise<SubscriptionPurchase | null>} Promise resolves to null when using proratioModesAndroid=DEFERRED, and to a SubscriptionPurchase otherwise
248
268
  */
249
269
 
@@ -258,9 +278,9 @@ const requestSubscription = _ref2 => {
258
278
  prorationModeAndroid = -1,
259
279
  obfuscatedAccountIdAndroid,
260
280
  obfuscatedProfileIdAndroid,
261
- selectedOfferIndexArr = undefined,
281
+ subscriptionOffers = undefined,
262
282
  // Android Billing V5
263
- skus = undefined,
283
+ isOfferPersonalized = undefined,
264
284
  // Android Billing V5
265
285
  applicationUsername
266
286
  } = _ref2;
@@ -273,7 +293,16 @@ const requestSubscription = _ref2 => {
273
293
  return getIosModule().buyProduct(sku, andDangerouslyFinishTransactionAutomaticallyIOS, applicationUsername);
274
294
  },
275
295
  android: async () => {
276
- return getAndroidModule().buyItemByType(ANDROID_ITEM_TYPE_SUBSCRIPTION, sku ? [sku] : skus, purchaseTokenAndroid, prorationModeAndroid, obfuscatedAccountIdAndroid, obfuscatedProfileIdAndroid, selectedOfferIndexArr);
296
+ if (isAmazon) {
297
+ return RNIapAmazonModule.buyItemByType(sku);
298
+ } else {
299
+ if (!(subscriptionOffers !== null && subscriptionOffers !== void 0 && subscriptionOffers.length)) {
300
+ Promise.reject('subscriptionOffers are required for Google Play Subscriptions');
301
+ return;
302
+ }
303
+
304
+ return RNIapModule.buyItemByType(ANDROID_ITEM_TYPE_SUBSCRIPTION, subscriptionOffers === null || subscriptionOffers === void 0 ? void 0 : subscriptionOffers.map(so => so.sku), purchaseTokenAndroid, prorationModeAndroid, obfuscatedAccountIdAndroid, obfuscatedProfileIdAndroid, subscriptionOffers === null || subscriptionOffers === void 0 ? void 0 : subscriptionOffers.map(so => so.offerToken), isOfferPersonalized ?? false);
305
+ }
277
306
  }
278
307
  }) || Promise.resolve)();
279
308
  };
@@ -560,7 +589,13 @@ const purchaseErrorListener = listener => new _reactNative.NativeEventEmitter(ge
560
589
 
561
590
  exports.purchaseErrorListener = purchaseErrorListener;
562
591
 
563
- const promotedProductListener = listener => new _reactNative.NativeEventEmitter(getIosModule()).addListener('iap-promoted-product', listener);
592
+ const promotedProductListener = listener => {
593
+ if (isIos) {
594
+ return new _reactNative.NativeEventEmitter(getIosModule()).addListener('iap-promoted-product', listener);
595
+ }
596
+
597
+ return null;
598
+ };
564
599
  /**
565
600
  * Get the current receipt base64 encoded in IOS.
566
601
  * @param {forceRefresh?:boolean}
@@ -571,13 +606,23 @@ const promotedProductListener = listener => new _reactNative.NativeEventEmitter(
571
606
  exports.promotedProductListener = promotedProductListener;
572
607
 
573
608
  const getPendingPurchasesIOS = async () => getIosModule().getPendingTransactions();
609
+ /**
610
+ * Get the current receipt base64 encoded in IOS.
611
+ * @param {forceRefresh?:boolean}
612
+ * @returns {Promise<string>}
613
+ */
614
+
615
+
616
+ exports.getPendingPurchasesIOS = getPendingPurchasesIOS;
617
+
618
+ const getReceiptIOS = async forceRefresh => getIosModule().requestReceipt(forceRefresh ?? false);
574
619
  /**
575
620
  * Launches a modal to register the redeem offer code in IOS.
576
621
  * @returns {Promise<null>}
577
622
  */
578
623
 
579
624
 
580
- exports.getPendingPurchasesIOS = getPendingPurchasesIOS;
625
+ exports.getReceiptIOS = getReceiptIOS;
581
626
 
582
627
  const presentCodeRedemptionSheetIOS = async () => getIosModule().presentCodeRedemptionSheet();
583
628