Debugging & Logging
Enable verbose logging to see internal operations, warnings, and debug information during development.
TL;DR
- Logging is disabled by default in production
- Enable with
OpenIapLog.enable(true) - Android basePlanId limitation: Use client-side tracking or backend validation
Enable Logging#
Logging is disabled by default in production. Enable it only during development.
// Enable logging for debug builds only
#if DEBUG
OpenIapLog.enable(true)
#endif
// Or enable unconditionally
OpenIapLog.enable(true)
// Disable logging
OpenIapLog.enable(false)Android basePlanId Limitation#
Root Cause
Google Play Billing API's Purchase object does NOT include basePlanId information. When a subscription group has multiple base plans (weekly, monthly, yearly), there is no way to determine which specific plan was purchased from the client-side Purchase object.
What Works Correctly
productId- Subscription group IDpurchaseToken- Purchase tokenisActive- Subscription active statustransactionId- Transaction ID
What May Be Incorrect
currentPlanId/basePlanIdAndroid- May return first plan instead of purchased plan
Solutions#
1. Client-side Tracking (Recommended for most apps)
// Track basePlanId yourself during the purchase flow
// 1. Store basePlanId BEFORE calling requestPurchase
let purchasedBasePlanId: string | null = null;
const handlePurchase = async (basePlanId: string) => {
// Use subscriptionOffers (cross-platform standardized type)
const offers = product.subscriptionOffers ?? [];
const offer = offers.find(
(candidate) =>
candidate.basePlanIdAndroid === basePlanId &&
candidate.id === candidate.basePlanIdAndroid,
);
if (!offer?.offerTokenAndroid) {
throw new Error(`Base plan '${basePlanId}' is unavailable`);
}
// Store it before purchase
purchasedBasePlanId = basePlanId;
await requestPurchase({
request: {
google: {
skus: [subscriptionGroupId],
subscriptionOffers: [
{ sku: subscriptionGroupId, offerToken: offer.offerTokenAndroid },
],
},
},
type: 'subs',
});
};
// 2. Use YOUR tracked value in onPurchaseSuccess
onPurchaseSuccess: (purchase) => {
// DON'T rely on purchase.currentPlanId - it may be wrong!
const actualBasePlanId = purchasedBasePlanId;
// Save to your backend; hook callback promises are not observed.
void saveToBackend({
purchaseToken: purchase.purchaseToken,
basePlanId: actualBasePlanId, // Use YOUR tracked value
productId: purchase.productId,
}).catch((error) => {
console.error('Failed to save purchase metadata', error);
});
}2. IAPKit Backend Validation (Recommended)

Use verifyPurchaseWithProvider with IAPKit to get accurate basePlanId from Google Play Developer API. The response includes offerDetails.basePlanId:
import { verifyPurchaseWithProvider } from 'expo-iap';
const result = await verifyPurchaseWithProvider({
provider: 'iapkit',
iapkit: {
apiKey: 'openiap-kit_pk_<your-publishable-key>',
google: { purchaseToken: purchase.purchaseToken },
},
});
// Access basePlanId from the response
const basePlanId = result.iapkit?.google?.lineItems?.[0]?.offerDetails?.basePlanId;
console.log('Actual basePlanId:', basePlanId);3. Single Base Plan Per Subscription Group
If your subscription group has only one base plan, the basePlanId will always be accurate. This is the simplest solution if your product design allows it.
Common Warnings#
When logging is enabled, you may see these warnings about specific scenarios:
| Warning | Meaning | Action |
|---|---|---|
Multiple offers found | Multiple base plans exist for subscription | Use client-side tracking or backend validation for accurate basePlanId |
Connection not initialized | IAP operation called before initConnection() | Call initConnection() first |
Transaction not finished | Purchase completed but finishTransaction not called | Call finishTransaction() after verification |