I’m engaged on an iOS app utilizing React Native IAP to fetch subscription information from App Retailer Join.
After I run the app on an actual system utilizing Xcode and a sandbox account, the getOfferings() operate doesn’t return any subscription information. Nevertheless, once I use a StoreKit Configuration file, the subscriptions are fetched efficiently.
Setup Particulars:
Library: react-native-iap (newest model)
Operate Used: getOfferings()
Testing Surroundings: Actual iOS system with a Sandbox account
Paid Agreements: Signed
Subscription Standing: “Able to Submit”
What I’ve Tried:
✅ Verified that the sandbox account is accurately logged into the App Retailer.
✅ Restarted the system and reinstalled the app.
✅ Ensured that in-app purchases are enabled in App Retailer Join.
✅ Checked if the subscriptions are correctly arrange beneath “In-App Purchases” in App Retailer Join.
✅ Confirmed that react-native-iap is accurately configured within the app.
Supply code for fetching subscriptions
import { useEffect, useRef, useState } from "react";
import * as Iap from "react-native-iap";
import { useIAP, validateReceiptIos, getReceiptIOS } from "react-native-iap";
import { SECTIONS, SUBSCRIPTION_DATA_MAPPER } from "@/constants/constants";
import { useSnackBar } from "@/contexts/SnackBarContext";
import { GetActiveSubscriptionPlan } from "@/companies/apiServices";
import { getCurrentSubscriptionPlan } from "@/utils/helper";
const iosPaymentService = () => {
const [subscriptions, setSubscriptions] = useState();
const [iapLoading, setIapLoading] = useState(false);
const [activeSection, setActiveSection] = useState();
const [loading, setLoading] = useState(true);
const [subscriptionLoading, setSubscriptionLoading] = useState(false);
const [showLoadingModal, setShowLoadingModal] = useState(false);
const [currentPlan, setCurrentPlan] = useState({
currentPlan: "free",
choice: -1,
platform: "",
worth: 0,
index: -1,
});
const [showAlert, setShowAlert] = useState(false);
const [sections, setSections] = useState();
const { showSnackBar } = useSnackBar();
const [paymentLoading, setPaymentLoading] = useState(false);
const map = SUBSCRIPTION_DATA_MAPPER();
const [selectedIndex, setSelectedIndex] = useState(1);
const {
linked,
subscriptions: subs,
getSubscriptions,
currentPurchase,
finishTransaction,
purchaseHistory,
getPurchaseHistory,
requestSubscription
} = useIAP();
const getSections = (availableSubscriptions: Iap.Subscription[]) => {
const groupedSubscriptions = availableSubscriptions.cut back((acc, sub) => {
const baseProductId = sub.productId.change(/_(month-to-month|quarterly)$/, '');
if (!acc[baseProductId]) {
acc[baseProductId] = {
subscription: sub,
information: {
title: sub.title,
index: Object.keys(acc).size + 1,
content material: {
perks: [sub.description],
costs: {
forex: {
month-to-month: '',
quarterly: '',
},
cash: {
month-to-month: 0,
quarterly: 0,
}
}
}
}
};
}
if (sub.subscriptionPeriodNumberIOS === '1') {
acc[baseProductId].information.content material.costs.forex.month-to-month = sub.localizedPrice;
} else if (sub.subscriptionPeriodNumberIOS === '3') {
acc[baseProductId].information.content material.costs.forex.quarterly = sub.localizedPrice;
}
return acc;
}, {});
const listing = Object.values(groupedSubscriptions);
listing.unshift({
subscription: null,
information: SECTIONS[0].information,
});
// console.log("Listing II:", listing);
setSections(listing.kind((a, b) => a.information.index - b.information.index));
};
const initIAP = async () => {
if (iapLoading) {
return;
}
setIapLoading(true);
strive {
await Iap.initConnection();
const skus = [
"com.chatreal.subscription.premium_pro_quarterly",
"com.chatreal.subscription.premium_pro_monthly",
"com.chatreal.subscription.premium_quarterly",
"com.chatreal.subscription.premium_monthly"
];
console.log("isConnneted", linked);
const availableSubscriptions = await Iap.getSubscriptions({ skus });
console.log("Out there subscriptions:", JSON.stringify(availableSubscriptions, null, 3));
setSubscriptions(availableSubscriptions);
getSections(availableSubscriptions);
} catch (err) {
console.warn("Error fetching subscriptions:", err);
console.log("Error particulars:", JSON.stringify(err, null, 2));
showSnackBar(`Did not load subscriptions: $`);
} lastly {
setIapLoading(false);
}
};
const purchaseSubscription = async (sku: string) => {
console.log("SKU in IOS: ", sku);
setPaymentLoading(true);
strive {
if (!sku) {
showSnackBar("Subscription Unavailable for the time being");
return;
}
const request: Iap.RequestSubscriptionIOS = {
sku,
andDangerouslyFinishTransactionAutomaticallyIOS: false, // Advisable to maintain false for validation
appAccountToken: undefined, // Elective: Add person account token if wanted
amount: 1
};
console.log("Request:", request);
const buy = await Iap.requestSubscription(request);
console.log("PURCHASE::", buy);
console.log("PURCHASE STRN::", JSON.stringify(buy, null, 2));
if (buy) {
// Validate the receipt (if required)
const receipt = buy.transactionReceipt;
// const temp = decodeTransactionReceipt(receipt);
// console.log("Temp:", temp);
if (receipt) {
setShowLoadingModal(false); // Shut the loading modal
setSubscriptionLoading(false); // Mark subscription as full
} else {
console.warn("Receipt validation failed");
showSnackBar("Subscription validation failed");
}
} else {
console.warn("No Buy discovered");
showSnackBar("No Buy discovered for the subscription");
}
} catch (err) {
console.warn("Error buying subscription:", err);
showSnackBar("Buy failed");
} lastly {
console.log("Lastly block");
setPaymentLoading(false);
}
};
const decodeTransactionReceipt = (receipt: string) => {
strive {
const decoded = atob(receipt); // Decode Base64 string
console.log("Decoded Receipt:", decoded);
return decoded;
} catch (error) {
console.error("Error decoding receipt:", error);
return null;
}
};
const getCurrentSubscription = async () => {
strive {
setLoading(true);
const response = await GetActiveSubscriptionPlan();
if (!response) return;
const currentPlan = getCurrentSubscriptionPlan(response?.subscription);
setCurrentPlan(currentPlan ? { ...currentPlan, platform: response?.platform } : undefined);
} catch (e) {
showSnackBar("Error fetching present subscription", 2000);
} lastly {
setLoading(false);
}
};
const initPaymentService = async () => {
await initIAP();
getCurrentSubscription();
};
return {
initPaymentService,
paymentLoading,
subscriptions,
showAlert,
subscriptionLoading,
setSubscriptionLoading,
setShowAlert,
sections,
activeSection,
showLoadingModal,
setShowLoadingModal,
setActiveSection,
currentPlan,
loading,
setLoading,
setCurrentPlan,
selectedIndex,
setSelectedIndex,
purchaseSubscription,
};
};
export default iosPaymentService;
That is what I get when working it utilizing xcode and sandbox testing on actual system:
'Out there subscriptions:', '[]'
That is what I get when working it utilizing xcode and storekit config file on simulator and even actual system:
Out there subscriptions: [
{
"introductoryPrice": "",
"title": "Premium",
"introductoryPriceSubscriptionPeriodIOS": "",
"introductoryPriceAsAmountIOS": "",
"productId": "com.chatreal.subscription.premium_monthly",
"subscriptionPeriodUnitIOS": "MONTH",
"introductoryPricePaymentModeIOS": "",
"currency": "USD",
"price": "4.99",
"countryCode": "USA",
"subscriptionPeriodNumberIOS": "1",
"type": "subs",
"localizedPrice": "$4.99",
"introductoryPriceNumberOfPeriodsIOS": "",
"discounts": [],
"description": "Chatreal AI Premium Subscription",
"platform": "ios"
},
{
"description": "Chatreal AI Premium Professional Subscription",
"subscriptionPeriodNumberIOS": "1",
"countryCode": "USA",
"introductoryPriceNumberOfPeriodsIOS": "",
"reductions": [],
"subscriptionPeriodUnitIOS": "MONTH",
"productId": "com.chatreal.subscription.premium_pro_monthly",
"title": "Premium Professional",
"introductoryPriceSubscriptionPeriodIOS": "",
"introductoryPrice": "",
"localizedPrice": "$9.99",
"sort": "subs",
"forex": "USD",
"introductoryPricePaymentModeIOS": "",
"worth": "9.99",
"introductoryPriceAsAmountIOS": "",
"platform": "ios"
},
{
"introductoryPriceAsAmountIOS": "",
"subscriptionPeriodUnitIOS": "MONTH",
"productId": "com.chatreal.subscription.premium_pro_quarterly",
"reductions": [],
"introductoryPriceNumberOfPeriodsIOS": "",
"description": "Chatreal AI Premium Professional Subscription",
"introductoryPrice": "",
"localizedPrice": "$24.99",
"sort": "subs",
"worth": "24.99",
"introductoryPricePaymentModeIOS": "",
"forex": "USD",
"subscriptionPeriodNumberIOS": "3",
"countryCode": "USA",
"introductoryPriceSubscriptionPeriodIOS": "",
"title": "Premium Professional",
"platform": "ios"
},
{
"introductoryPriceNumberOfPeriodsIOS": "",
"reductions": [],
"introductoryPriceSubscriptionPeriodIOS": "",
"title": "Premium",
"introductoryPrice": "",
"countryCode": "USA",
"subscriptionPeriodNumberIOS": "3",
"introductoryPricePaymentModeIOS": "",
"worth": "12.99",
"forex": "USD",
"description": "Chatreal AI Premium Subscription",
"productId": "com.chatreal.subscription.premium_quarterly",
"subscriptionPeriodUnitIOS": "MONTH",
"introductoryPriceAsAmountIOS": "",
"sort": "subs",
"localizedPrice": "$12.99",
"platform": "ios"
}
]