-
Notifications
You must be signed in to change notification settings - Fork 185
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
The purchase works but (sometimes) invalid/no response is returned? #202
Comments
In the same scenario, it returns us an error telling that it could not contact the app store. Even if the user has the popup telling him that he already purchased the item. |
I have the very same issue and the problem is that I have absolutely no idea of how to debug it any further. |
We are seeing the same issue. We are seeing |
We have been getting this issue where users reported that the payment went through fine, yet the call back never happened or an error happened. Seems like this issue to me. Can anyone update if they found a solution for this? |
We ended un implementing a workaround that worked: When the purchaseProduct method returns an error, immediately call the restorePurchase method and you'll get the right receipt. |
But, isn't this buggy. If the purchaseproduct actually returns an error, then what would happen? |
@cuttlas is a bad idea, because in the apple documentation its recommended don't restore purchases automatically.
|
Of course it is not an ideal solution but it is the ONLY solution we've found so far. If we do not restore purchases automatically, about 20% of responses to successful payments fail, making it a horrible UX and hard to track, since we lose the data of the payment, having no way to know which accounts have paid. So, how do you solve this issue? In what % do you get invalid/no responses during successful purchases? |
20% is a big number @mezod . Is this an issue with Apple's IAP payment system or with this framework. If this was something wrong with the IAP and the number would be 20%, then I guess the world would be on fire by now. What is your opinion on this? |
Indeed it is a horrible number. We've got over 400 payments and at least 60-70 experienced issues. People writing to us like "hey, I paid, here's the proof that Apple says I paid, but your app won't grant me access". Like the worst email you can ever get !_! I don't know where's the problem, but I know of Apple developers not using this lib or even not using react-native having this issue (not at the same level though). So either most of our users have bad connectivity, or Apple fails to respond 20% of the time... We don't have this problem with Android. I'm surprised there's no documentation on the issue or more people complaining about it. It's a very sensible issue. |
@mezod Can you please point me to a resource which substantiates this :- "I don't know where's the problem, but I know of Apple developers not using this lib or even not using react-native having this issue ". I was trying to look online but could not find anything like this on Stackoverflow or other places. If you have found something online, please share. |
I don't have a link, unfortunately. |
This is not an issue with this lib. When a users initiates a purchase 2 things can happen:
For 1, purchase will go through and a transaction with state For 2, Apple will see that user cannot complete the purchase. A transaction with state You will be receiving both the You can see what the error codes mean here: #15 (comment) You need to catch Use can use my fork here. My fork adds optional promise support and helper methods. To install: You need to do something like this: const buy = async (productId) => {
await IAU.loadProducts([productId])
let purchase
try {
purchase = await IAU.purchaseProduct(productId)
} catch (error) {
if (error && error.code === 'ESKERRORDOMAIN2') {
purchase = await getPurchaseUntilFound(productId)
}
if (!purchase) {
throw error
}
}
if (purchase) {
return purchase
}
throw new Error('Purchase failed.')
}
const getPurchaseUntilFound = async (productId) => {
try {
// Wait in case purchase transaction hasn't been added to queue yet
// Sometimes it is already in the queue and sometimes it takes more time.
await new Promise(res => setTimeout(res, 2000))
const purchases = await IAU.getPurchaseTransactions()
const purchase = purchases.find(
item => item.productIdentifier === productId
)
if (purchase) return purchase
// Keep looking until found - add a break point so it doesn't keep running
return getPurchaseUntilFound(productId)
} catch (error) {
console.log(error)
}
} |
@superandrew213 thanks for explanation! In my case we are seeing: Also i think Apple throws
Edit: i saw your PR |
Same Question... Here... If the user cancels the payment we get the ESKERRORDOMAIN2. So, in that case as well, it will keep waiting and search for the next successful transaction, which would not be present. @superandrew213 @vafada @vafada So how did you fix it? Seems like it should ESKERRORDOMAIN0 but not sure |
@sonicvision yes it's weird that Apple triggers During the purchase process show a loader to the user. If they cancel the purchase and trigger Not the best UX, but majority won't cancel and most imprtantly purchase completes successfully. |
I've looked at your branch and we are probably going to use your fork to deal with this issue. Why is the only thing that does is prevent invoking the StoreKit's Does calling |
You don't need to call You just need to call |
Here's my pseudo code now, can you comment on it? let shouldClearCurrentTransaction = true;
try {
await InAppUtils.shouldFinishTransactions(false);
const response = await InAppUtils.purchaseProductForUser(productId, userId);
const receiptData = await InAppUtils.receiptData();
// send receiptData to backend
} catch (error) {
// user might be in StoreKit flow: https://forums.developer.apple.com/thread/6431#14831
// Lets give StoreKit some time (10 seconds) to put the success transaction in queue
// loop 5 times and sleep for 2 seconds per iteration
let purchaseInStoreKitFlow = false;
for (let i = 0; i < 5; i++) {
await new Promise(res => setTimeout(res, 2000))
// InAppUtils.getPurchaseTransactions finishes the transactions so we dont need to
// manually do it
shouldClearCurrentTransaction = false;
const purchases = await InAppUtils.getPurchaseTransactions();
const purchase = purchases.find(item => item.productIdentifier === productId);
if (purchase) {
purchaseInStoreKitFlow = true;
const receiptData = await InAppUtils.receiptData();
// send receiptData to backend
// get out of the loop
break;
}
}
if (!purchaseInStoreKitFlow) {
// Real error and not a StoreKit flow.
if (error.code !== 'ESKERRORDOMAIN2') {
// alert user
}
throw error;
}
} finally {
await InAppUtils.clearCompletedTransactions();
if (shouldClearCurrentTransaction) {
await InAppUtils.finishCurrentTransaction();
}
await InAppUtils.shouldFinishTransactions(true);
} Notes:
without that, I will be receiving both the
Does all logic make sense? |
Looks ok. I would try/catch what you have in your catch block. Make sure normal purchases go through without any issues. Then release and see if it lowers your complains. |
I am glad to report that I implemented the code recommended by @superandrew213 and now we have started seeing that the code is saving customers who paid but did not get upgraded. Instead it upgrades them. Though, the only error code we see is ESKERRORDOMAIN0, which is also expected, as this is an unknown error. |
We're seeing the same issue.
|
Yup.. this has brought down our errors of this kind a lot. I have put a tracking in our app, and I see that it is surely saving some users. |
Observed a similar issue with sandbox account with RN 0.60.5
Edit |
It's unrelated to 6.0.2 @unmec, it's not 10/10 times reproducible. That's the worst part about it. It happens a lot, but not for everyone. |
I'm getting this and it's causing a lot of angry customers, especially in the restore purchase flow. I will likely switch to https://github.com/dooboolab/react-native-iap. They describe redesigning the library to not use a promise or callback flow, and instead use an event pattern. I believe this is what is resulting in some of the issues we're seeing here, not properly handing events in the transaction queue. |
We are using this library to handle in app purchases in a production app and approximately 1 every 10 users that try to purchase a subscription report that the payment fails. However, if they try to purchase it again, the AppStore responds that they've already purchased that product (which we've checked in the AppStore console that it's true). So, apparently the purchaseProduct function always works, but it randomly doesn't return or returns an invalid response/error. Is this happening to anybody? Any solution?
This is the code we are using:
Unfortunately I cannot confirm if in those cases the callback is never called, or is called with an error/invalid response (even though the purchase was successful). We'll try to log that and I'll update this issue with more info.
Thanks
The text was updated successfully, but these errors were encountered: