I receive a popup saying, "You already own this item."
Please use the Discussion board if you want to get some help. Please use issues to report bugs.
Description
I am facing an issue with consumable product purchases. When a user purchases an item and kills the app before receiving a response, the payment is completed, but the data is not retrieved from the available purchases and purchase history.
Expected Behavior
get response from purchase history and finish the transection
Screenshots
See the attachment
Environment:
- react-native-iap:^12.15.2
- react-native:0.70.3
- Platforms (iOS, Android): Real device
To Reproduce Steps to reproduce the behavior:
- When a user clicks on the "Buy Item" button with UPI.
- it opens the UPI app. After entering the UPI PIN.
- the payment is successful.
- and I immediately close both the payment app and my app.
- After some time, I reopen the app and do not get the purchase info from the purchase history and available purchases.
- When I attempt the payment again, I receive a popup saying, "You already own this item."
This is my code:
import { Platform } from "react-native"; import Auth from "../Auth"; import { clearTransactionIOS, finishTransaction, flushFailedPurchasesCachedAsPendingAndroid, getAvailablePurchases, getPurchaseHistory, requestPurchase } from "react-native-iap"; import sendEventToWeb from "../SendEventToWeb/SendEventToWeb";
const IAP_function = async (productId, info) => { let data = JSON.parse(info) const params = Platform.select({ ios: { sku: productId, andDangerouslyFinishTransactionAutomaticallyIOS: false }, android: { skus: [productId] } }); try { const currentPurchase = await requestPurchase(params); if (currentPurchase) { var dictJSON1 = { ...currentPurchase, ...data, action: "inapppurchase_response", device_type: Platform.OS == 'ios' ? '2' : '1', packageName: 'com.my.app', gotResponseFrom: 'Normal Flow' }; console.log("Normal Flow ==>", dictJSON1); sendEventToWeb(dictJSON1); }
} catch (e) {
handlePurchaseError(e, productId);
}
};
const handlePurchaseError = async (error, productId) => { let data = await Auth.getItem(productId) data = data != null ? JSON.parse(data) : { isCancelled: true } var dictJSON1 = { ...data, action: "inapppurchase_response", device_type: Platform.OS == 'ios' ? '2' : '1', packageName: 'com.my.app', ...error, }; sendEventToWeb(dictJSON1); await Auth.removeItem(productId) };
const ClearTransections = async (purchase) => { try { if (purchase?.transactionId || purchase?.purchaseToken || Platform.OS == 'ios') { console.log("finishTransaction purchase -=>> ", purchase) await finishTransaction({ purchase: purchase, isConsumable: true }); await Auth.removeItem(purchase.productId) flushTransactions() } else if (purchase["0"]) { console.log("finishTransaction purchase[0] -=>> ", purchase["0"]) await finishTransaction({ purchase: purchase["0"], isConsumable: true }); await Auth.removeItem(purchase["0"].productId) flushTransactions() } else if (purchase == null) { availablePurchases() } } catch (e) { console.log("Clear Transections Failure ==>", e); } }
const flushTransactions = async () => { if (Platform.OS === "android") { await flushFailedPurchasesCachedAsPendingAndroid(); } else { await clearTransactionIOS(); } };
const fetchPurchaseHistory = async () => { try { const purchaseHistory = await getPurchaseHistory(); if (purchaseHistory.length > 0) { purchaseHistory?.map(async item => { let data = await Auth.getItem(item.productId) data = data != null ? JSON.parse(data) : { isCancelled: true } var dictJSON1 = { ...item, ...data, action: "inapppurchase_response", device_type: Platform.OS == 'ios' ? '2' : '1', packageName: 'com.my.app', gotResponseFrom: 'From Purchase History' }; if (!data.isCancelled) { console.log("fetchPurchaseHistory==>", dictJSON1); sendEventToWeb(dictJSON1); } }) } } catch (error) { console.error("Error fetching purchase history: ", error); } };
const availablePurchases = async (call = 0) => { try { const purchaseHistory = await getAvailablePurchases(); if (purchaseHistory.length > 0) { purchaseHistory?.map(async item => { let data = await Auth.getItem(item.productId) data = data != null ? JSON.parse(data) : { isCancelled: true } var dictJSON1 = { ...item, ...data, action: "inapppurchase_response", device_type: Platform.OS == 'ios' ? '2' : '1', packageName: 'com.my.app', gotResponseFrom: 'From available Purchases' }; if (!data.isCancelled) { console.log("availablePurchases==>", dictJSON1); sendEventToWeb(dictJSON1); } }) } else { console.log("No pending transactions found."); if (call == 1) { fetchPurchaseHistory() } else { flushTransactions() } } } catch (error) { console.error("Error fetching purchase history: ", error); } };
I've already had this issue. My app crashed once after buying the product and before finishing the transaction. The fact that the transaction wasn't properly finished kept the item as an already owned non-consumable.
Hope it helps
@cuchillitos How did you solve this problem?
@RouberR , it’s been a while since I worked on this, but I think the solution might be in handling the last transaction. Here’s what I did as far as I can remember: 1. First, I retrieved the available purchases using getAvailablePurchases(). 2. Then, I took the most recent transaction (from the list of availablePurchases) and finished it using finishTransaction.
For example :
const availablePurchases = await RNIap.getAvailablePurchases();
if (availablePurchases && availablePurchases.length > 0) {
const lastPurchase = availablePurchases[0];
await RNIap.finishTransaction({
purchase: lastPurchase,
isConsumable: true,
});
}
I hope it helps !
@cuchillitos Yes, thank you, I did it in a similar way and everything worked.
I’m closing all issues reported in versions below 14, as the library now supports the new architecture with NitroModules and has been completely revamped.