react-native-iap icon indicating copy to clipboard operation
react-native-iap copied to clipboard

I receive a popup saying, "You already own this item."

Open Rohit-vinfotech opened this issue 1 year ago • 4 comments

Please use the Discussion board if you want to get some help. Please use issues to report bugs.

1726828222363

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:

  1. When a user clicks on the "Buy Item" button with UPI.
  2. it opens the UPI app. After entering the UPI PIN.
  3. the payment is successful.
  4. and I immediately close both the payment app and my app.
  5. After some time, I reopen the app and do not get the purchase info from the purchase history and available purchases.
  6. 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); } };

Rohit-vinfotech avatar Sep 20 '24 11:09 Rohit-vinfotech

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 avatar Sep 26 '24 14:09 cuchillitos

@cuchillitos How did you solve this problem?

RouberR avatar Sep 27 '24 23:09 RouberR

@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 avatar Oct 04 '24 13:10 cuchillitos

@cuchillitos Yes, thank you, I did it in a similar way and everything worked.

RouberR avatar Oct 04 '24 18:10 RouberR

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.

hyochan avatar Sep 30 '25 18:09 hyochan