Question

Issues with In-App Purchases and Sandbox Testing


Badge

Context:We are currently facing issues with our in-app purchases that have not yet been approved and are still in the "Ready to Submit" stage in App Store Connect. We are currently using the getOfferings method from the React Native IAP library, react-native-purchases

Current Setup and Observations:

  1. StoreKit Configuration Testing:
    • When testing with the StoreKit configuration on the Simulator, transactions go through successfully and are visible in RevenueCat. This indicates that our setup and integration with RevenueCat are functioning correctly in the simulator environment.
  2. App Store Connect Sandbox Testing:
    • When testing using the App Store Connect Sandbox, by navigating to Settings > App Store > Sandbox Account > Test Transactions, the subscriptions appear in the test accounts as expected.
    • However, these sandbox transactions do not show up in the RevenueCat Recent Transactions. This is a critical issue for us, as we need to ensure that transactions are correctly logged and visible in RevenueCat for testing and verification purposes.
  3. Development Build Testing (TestFlight):
    • When we build a development version of the app, there are no available products, even though they are visible in the simulator. This discrepancy is causing significant confusion and hindering our testing process.
    • We have filled out all the necessary forms for tax, banking, and Paid Apps in App Store Connect. Additionally, we have disabled StoreKit testing, but the issue persists.

Questions:

  1. Approval Requirement for Sandbox Testing:
    • Do the in-app purchases need to be fully approved (status: "Approved" or "Ready for Sale") before they will show up correctly in the Sandbox environment and be logged in RevenueCat?
  2. Product Availability in Development Builds:
    • Do the in-app products need to be approved in App Store Connect for them to be available in TestFlight and development builds? Currently, they do not appear despite being configured correctly.

Any guidance or assistance on these issues would be greatly appreciated. We are trying to ensure a smooth testing process and need to confirm whether the approval status of in-app purchases is affecting their visibility and functionality in sandbox and development environments


This post has been closed for comments

2 replies

Userlevel 2
Badge +4

Hi @adi-tomar, I’m happy to help. In-app purchases don’t have to be in the final approved state - “Ready to submit” will work fine. Regarding sandbox purchases not appearing in RevenueCat, have you ensured that you’ve selected the “Sandbox data” checkbox?

If this is selected, can you share a snippet of your purchase code in the app?

I’m sure you’ve seen this already, but I wanted to share our documentation on sandbox testing for reference.

Badge

Thanks @jeffrey_bunn for your reply! Here’s our code below. We have gone through the documentation multiple times and have been stuck on this issue for a month. This is our first in-app purchase product, so we don’t know if its unapproved status is affecting our code. We have ensured the sandbox data checkbox is enabled. We have used StoreKit testing and knows that the code works and the transaction show up under the sandbox apple Id correctly. We have been repeatedly rejected by Apple’s App Review Team for the same issue and we have never been able to display our products on TestFlight and the App Review still can’t see the purchases either. Below I have included an excerpt code for our subscription screen:

 

const [packages, setPackages] = useState([]);
const [loading, setLoading] = useState(true);
const { theme } = useTheme();
const themeColors = themes[theme];
const styles = SubscriptionScreenStyles(themeColors);
const navigation = useNavigation();

const initializeRevenueCat = async () => {
console.log("Setting log level to DEBUG");
Purchases.setDebugLogsEnabled(true);
console.log("Initializing RevenueCat...", API_KEY);
Purchases.configure({ apiKey: API_KEY });
try {
const uid = auth().currentUser.uid;
const { customerInfo, created } = await Purchases.logIn(uid);
if (created) {
console.log(`New user ${uid} signed in`);
} else {
console.log(`User ${uid} signed in`);
}
console.log("Fetching offerings...");
const fetchedOfferings = await Purchases.getOfferings();
console.log("Offerings fetched:", fetchedOfferings);

if (
fetchedOfferings &&
fetchedOfferings.current &&
fetchedOfferings.current.availablePackages
) {
setPackages(fetchedOfferings.current.availablePackages);
} else {
console.log("No packages available");
}
setLoading(false);
} catch (error) {
console.error("Error initializing RevenueCat", error);
setLoading(false);
}
};

useEffect(() => {
initializeRevenueCat();
}, []);

const makePurchase = async (selectedPackage) => {
console.log("Activating makePurchase for package:", selectedPackage);
try {
const purchaseInfo = await Purchases.purchasePackage(selectedPackage);
console.log("Purchase info:", purchaseInfo);

const activeEntitlements = purchaseInfo.customerInfo.entitlements.active;
console.log("Active entitlements:", activeEntitlements);

if (typeof activeEntitlements["premium"] !== "undefined") {
console.log("You are now subscribed to premium.");
Alert.alert("Success", "You are now subscribed to the premium plan.");
}
} catch (error) {
console.error("Error during purchase:", error);
if (!error.userCancelled) {
Alert.alert("Error", error.message);
} else {
console.log("User cancelled the purchase");
}
}
};

const restorePurchases = async () => {
console.log("Restoring purchases...");
try {
const restoredInfo = await Purchases.restorePurchases();
console.log("Restored info:", restoredInfo);

const activeEntitlements = restoredInfo.entitlements.active;
console.log("Active entitlements after restore:", activeEntitlements);

if (Object.keys(activeEntitlements).length > 0) {
console.log("You have active entitlements.");
Alert.alert("Success", "Your purchases have been restored.");
} else {
Alert.alert("No Purchases", "No previous purchases found to restore.");
}
} catch (error) {
console.error("Error restoring purchases:", error);
Alert.alert("Error", error.message);
}
};
// if (loading) {
// return <LoadingScreen />;
// }