question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

getPurchaseHistory trigger lots of restored, clearTransactionIOS takes long time to clear it, subscription request no response during this

See original GitHub issue

Version of react-native-iap

5.2.14 & 6.02

Version of react-native

0.63.4

Platforms you faced the error (IOS or Android or both?)

IOS

Expected behavior

no Restored action & purchase show fast

Actual behavior

getPurchaseHistory() trigger Restored and the subscription button work only after cleaning all the restored,

Tested environment (Emulator? Real Device?)

Real Device

Steps to reproduce the behavior

useEffect(() => {
		IAP.initConnection().catch(() => {
			console.log("error connect to store")
		})
		.then(async () => {
			if (Platform.OS === 'ios') {
			 await IAP.clearTransactionIOS();
			}
			console.log("connected to store...")
			await IAP.getSubscriptions(items).catch((e) => {
				console.log(e);
			}).then((res) => {
				setProducts(res);
			})
		})
		// getPurchaseHistory block
		.then(async () => {
			await IAP.getPurchaseHistory().catch(() => {
				setChecking(false);
				setPurchased(false);
			}).then(async (res: any) => {
				if (res != null) {
					const receiptInfo = res[res.length - 1];
					if (receiptInfo != null) {
						const receipt = receiptInfo.transactionReceipt;
						console.log(res.length); // it equal to the number of restored on xcode
						await IAP.clearTransactionIOS();
						if (receipt) {
							await validate(receipt);
						}
					}
				}
			});
		})
		// declare listener block
		.then(async () => {
			setChecking(false); // without checking history
			console.log("declare purchaseUpdatedListener");
			purchaseUpdatedListener = IAP.purchaseUpdatedListener(async (purchase: InAppPurchase | SubscriptionPurchase) => {
				try {
					console.log("update listener")
					const receipt = purchase.transactionReceipt;
					if (receipt) {
						console.log(receipt);
						if (validateFlag) { // flag prevent listener run suddenly
							await validate(receipt);
						} else {
							console.log("block validate")
						}
						await IAP.finishTransaction(purchase);
						await IAP.clearTransactionIOS();
					}
				} catch (error) {

				}
			})
		})
		return () => {
			if (purchaseUpdatedListener) {
				purchaseUpdatedListener.remove();
				purchaseUpdatedListener = null;
			}
			if (purchaseErrorListener) {
				purchaseErrorListener.remove();
				purchaseErrorListener = null;
			}
		}
	}, [])

I found that when i use getPurchaseHistory(), the subscription button is broken When i look at the debug log of Xcode, I see lots of Restored action occurs after getPurchaseHistory

2021-04-04 17:10:03.761129+0800 MyApp[7767:3203077] Restored
2021-04-04 17:10:03.761269+0800 MyApp[7767:3203077] Restored
2021-04-04 17:10:03.761373+0800 MyApp[7767:3203077] Restored
...
2021-04-04 17:10:03.781085+0800 MyApp[7767:3203077] Restored
2021-04-04 17:10:03.781211+0800 MyApp[7767:3203077] Restored
2021-04-04 17:10:03.781295+0800 MyApp[7767:3203077] Restored

total 151 Restored within 1 second, this number is the length of respond of getPurchaseHistory() so I add await IAP.clearTransactionIOS() after using getPurchaseHistory(), but it takes long time to kill all the Restored, 151 is the length of respond of getPurchaseHistory() during this process, nothing happen in clicking subscription button

2021-04-04 17:10:03.918169+0800 MyApp[7767:3203371] [javascript] 151
  ***  clear remaining Transactions. Call this before make a new transaction   

2021-04-04 17:10:05.282095+0800 MyApp[7767:3203077] removedTransactions
2021-04-04 17:10:06.709204+0800 MyApp[7767:3203077] removedTransactions
2021-04-04 17:10:08.161100+0800 MyApp[7767:3203077] removedTransactions
...
2021-04-04 17:16:13.535573+0800 MyApp[7767:3203077] removedTransactions
2021-04-04 17:16:14.756614+0800 MyApp[7767:3203077] removedTransactions
2021-04-04 17:16:15.816927+0800 MyApp[7767:3203077] removedTransactions

total 151 removedTransactions in 6 minutes, even i close the app and rebuild it, next time i open the app, it’s still there needed to be killed, so I also add await IAP.clearTransactionIOS() at the beginning just after the initConnection. sometimes after killing all the Restored, the purchase dialog show… i believe it’s the last time i press the purchase button but nothing happen, now it show after killing all the Restored

if I change the order of getPurchaseHistory block and declare listener block, the restored will trigger the listener, listener will runs 151 times, so you may see I’ve added the flag there to prevent my backend from explosion, but now this order is okay . just need to wait, one time i close the app before it kill all the restore, 4 hours later the purchase dialog show

and I am not sure if this bug makes purchase so slow, sometimes after the killing, purchase button work perfectly, very fast. but sometimes i need to wait, or click couple times

without getPurchaseHistory block, everything works perfectly, the subscription reaction is fast every time, but i need to get the receipt to know the expiry and change the UI logic

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:7

github_iconTop GitHub Comments

1reaction
PazuC10commented, Jun 8, 2021

@PazuC10 how can getReceiptIOS would work if I have multiple purchases available?

After decoding the receipt at backend, the object shows all the item you’ve bought on object[‘receipt’][‘in_app’] source: responseBody.Receipt.In_app | Apple Developer Documentation

if you want to know the latest subscriptions, you can look at Object[‘data’][‘latest_reciept_into’] then you can do the logic if the item(s) not expire

1reaction
basemanabulsicommented, Apr 4, 2021

I am facing the same issue, it takes long time on real IOS device, I have 44 purchases takes a lot of time to trigger

async  isSubscriptionActive() {
        if (Platform.OS === 'ios') {
          await RNIap.clearProductsIOS()
          let x = false
          x = await RNIap.getPurchaseHistory() // This method freeze and takes alot of time 
            .then(async purchases => {
              if(purchases.length  == 0 ){
                return false
              }
             // Other app logic 
        })
    }
}

I don’t know if this is the same as your case also

After many trails I figured out that when we call RNIap.getPurchaseHistory() then call other methods like requestSubscription() takes alot of time. I don’t know what is the issue with RNIap.getPurchaseHistory() but there is a real issue in it.

Issue: After digging on it for days I found there is a bug in SDK or IOS 14 when we use RNIap.getPurchaseHistory() After calling it the other methods takes very long time

Solution: I changed the method with this workaround

 if (Platform.OS === 'ios') {
          const lastPurchase = await RNIap.getReceiptIOS();
          if(!lastPurchase) return false

          const receipt = await RNIap.validateReceiptIos({
            'receipt-data': lastPurchase,
            'password': ''
          }, true)

          const renewalHistory = receipt?.latest_receipt_info 
          
          let result = false;
          renewalHistory.forEach((singleReceipt)=>{
            if(singleReceipt.expires_date_ms > Date.now()) {
              result =  { startDate: singleReceipt.purchase_date.split(" ")[0] , planId: singleReceipt.product_id }
            }
          })
          return result;
        }

  • For security reasons call this method RNIap.validateReceiptIos() through your backend because it has a password and credentials, I set it there in the snippet just so u can understand the process.

If you want to know more you can find the source of Receipt here Verify Receipt

Read more comments on GitHub >

github_iconTop Results From Across the Web

RNIap.getPurchaseHistory() always returns an empty array on ...
finshTransaction more time to finish before requesting the purchase history to make sure the latest purchases are included.
Read more >
react-native-iap - npm
Start using react-native-iap in your project by running `npm i ... clearProductsIOS, void, void, Clear all products, subscriptions in ios.
Read more >
@marf95/react-native-iap NPM | npm.io
This react-native module will help you access the In-app purchases capabilities of your phone on the Android , iOS platforms and the Amazon...
Read more >
In App Purchases and Subscriptions in React Native
After more than a year of using react-native-iap as my React Nativ e in-app purchase solution, I recently decided to migrate to ...
Read more >
modules - React Native IAP - dooboolab
No -op on iOS. ... Should only be called in response to the iap-promoted-product event. ... Clear Transaction (iOS only) Finish remaining transactions....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found