Debugging & Logging

Enable verbose logging to see internal operations, warnings, and debug information during development.

TL;DR

Enable Logging#

Logging is disabled by default in production. Enable it only during development.

swift
// 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 ID
  • purchaseToken - Purchase token
  • isActive - Subscription active status
  • transactionId - 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)

ts
// 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)

IAPKit Banner

Use verifyPurchaseWithProvider with IAPKit to get accurate basePlanId from Google Play Developer API. The response includes offerDetails.basePlanId:

ts
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:

WarningMeaningAction
Multiple offers foundMultiple base plans exist for subscriptionUse client-side tracking or backend validation for accurate basePlanId
Connection not initializedIAP operation called before initConnection()Call initConnection() first
Transaction not finishedPurchase completed but finishTransaction not calledCall finishTransaction() after verification

Native References#