Skip to main content

10 posts tagged with "Android"

Android platform specific features

View All Tags

3.4.4 - Android Input Type Field Naming Simplification

· 2 min read
Hyo
Expo IAP Maintainer

This release simplifies field naming in Android input types (RequestPurchaseAndroidProps and RequestSubscriptionAndroidProps). Since these types are already Android-specific, their fields no longer need the Android suffix.

Breaking Changes

Simplified Field Names in Android Input Types

Fields inside platform-specific input types no longer require the platform suffix. The parent type name already indicates the platform context.

Why this change?

When you write google: { offerToken: "..." }, the google key already tells you this is Android-specific. Adding Android suffix to fields inside is redundant:

// Redundant - we know it's Android from the parent
google: { offerTokenAndroid: "..." }

// Cleaner - parent context is sufficient
google: { offerToken: "..." }

Migration Guide

Old Name (v3.4.3)New Name (v3.4.4)
offerTokenAndroidofferToken
isOfferPersonalizedAndroidisOfferPersonalized
obfuscatedAccountIdAndroidobfuscatedAccountId
obfuscatedProfileIdAndroidobfuscatedProfileId
purchaseTokenAndroidpurchaseToken
replacementModeAndroidreplacementMode
developerBillingOptionAndroiddeveloperBillingOption

Before (v3.4.3):

await requestPurchase({
request: {
google: {
skus: ['product_id'],
offerTokenAndroid: discountOffer.offerTokenAndroid,
isOfferPersonalizedAndroid: true,
obfuscatedAccountIdAndroid: 'user_123',
},
},
type: 'in-app',
});

After (v3.4.4):

await requestPurchase({
request: {
google: {
skus: ['product_id'],
offerToken: discountOffer.offerTokenAndroid, // Note: response field keeps suffix
isOfferPersonalized: true,
obfuscatedAccountId: 'user_123',
},
},
type: 'in-app',
});

Important: Response Types Keep Suffixes

The suffix removal only applies to input types (request parameters). Response types still use suffixes because they're cross-platform:

// Response fields KEEP the Android suffix
const product = products[0] as ProductAndroid;
const discountOffer = product.discountOffers?.[0];

// These response fields still have Android suffix
console.log(discountOffer?.offerTokenAndroid); // ✓ Keep suffix
console.log(discountOffer?.percentageDiscountAndroid); // ✓ Keep suffix

// But input fields don't need it anymore
await requestPurchase({
request: {
google: {
skus: [product.id],
offerToken: discountOffer?.offerTokenAndroid, // Input: no suffix
},
},
type: 'in-app',
});

One-Time Purchase Discount Offers (Android 7.0+)

This release also adds support for discount offers on one-time (in-app) purchases:

import {fetchProducts, requestPurchase} from 'expo-iap';
import type {ProductAndroid} from 'expo-iap';

// 1. Fetch products with discount offers
const products = await fetchProducts({
skus: ['premium_upgrade'],
type: 'in-app',
});

const product = products[0] as ProductAndroid;

// 2. Get the discount offer
const discountOffer = product.discountOffers?.[0];

// 3. Purchase with the discount
if (discountOffer?.offerTokenAndroid) {
await requestPurchase({
request: {
google: {
skus: [product.id],
offerToken: discountOffer.offerTokenAndroid, // Use simplified input field
},
},
type: 'in-app',
});
}

Naming Convention Summary

Field LocationSuffix Required?Example
Inside RequestPurchaseAndroidPropsNOofferToken
Inside RequestSubscriptionAndroidPropsNOpurchaseToken
Cross-platform response typeYESDiscountOffer.offerTokenAndroid

Closes

  • #307 - Support offerToken for in-app products (one-time purchases)

OpenIAP Versions

PackageVersion
openiap-gql1.3.15
openiap-google1.3.26
openiap-apple1.3.13

For detailed changes, see the OpenIAP Release Notes.

v3.4.3 - Win-Back Offers & Advanced Billing Features

· 3 min read
Hyo
Expo IAP Maintainer

This release syncs with OpenIAP v1.3.14, introducing iOS 18+ win-back offers, JWS promotional offers, Android 8.0+ product status codes, and important type cleanup.

New Features

Win-Back Offers (iOS 18+)

Win-back offers allow you to re-engage churned subscribers with special discounts or free trials.

import {requestPurchase} from 'expo-iap';

// Apply a win-back offer during subscription purchase
await requestPurchase({
request: {
apple: {
sku: 'premium_monthly',
winBackOffer: {
offerId: 'winback_50_off', // iOS 18+
},
},
},
type: 'subs',
});

Win-back offers are automatically presented via StoreKit Message when eligible, or can be applied programmatically.

JWS Promotional Offers (iOS 15+)

A new simplified format for promotional offers using compact JWS strings, back-deployed to iOS 15.

await requestPurchase({
request: {
apple: {
sku: 'premium_yearly',
promotionalOfferJWS: {
offerId: 'promo_20_off',
jws: 'eyJhbGciOiJFUzI1NiI...', // Server-signed JWS
},
},
},
type: 'subs',
});

Introductory Offer Eligibility Override (iOS 15+)

Override system-determined introductory offer eligibility.

await requestPurchase({
request: {
apple: {
sku: 'premium_monthly',
introductoryOfferEligibility: true, // Force eligible
},
},
type: 'subs',
});

Product Status Codes (Android 8.0+)

Get detailed feedback on why products couldn't be fetched.

import {fetchProducts} from 'expo-iap';
import type {ProductAndroid} from 'expo-iap';

const result = await fetchProducts({
skus: ['product_1', 'product_2'],
type: 'in-app',
});

result.forEach((product) => {
const androidProduct = product as ProductAndroid;
if (androidProduct.productStatusAndroid) {
switch (androidProduct.productStatusAndroid) {
case 'ok':
// Product available
break;
case 'not-found':
// SKU doesn't exist in Play Console
break;
case 'no-offers-available':
// User not eligible for any offers
break;
}
}
});

Suspended Subscriptions Support (Android 8.1+)

Include suspended subscriptions when fetching available purchases. This feature required native code updates to pass the option through to the OpenIAP SDK.

import {getAvailablePurchases} from 'expo-iap';

const purchases = await getAvailablePurchases({
includeSuspendedAndroid: true, // Include suspended subs
});

// Check if subscription is suspended
purchases.forEach((purchase) => {
if (purchase.isSuspendedAndroid) {
// Direct user to resolve payment issues
// Do NOT grant entitlements for suspended subscriptions
}
});

Important: Suspended subscriptions should NOT be granted entitlements. Users should be directed to the Play Store subscription center to resolve payment issues.

Sub-Response Codes (Android 8.0+)

More granular error information for purchase failures.

// SubResponseCodeAndroid provides additional context:
// - 'no-applicable-sub-response-code'
// - 'payment-declined-due-to-insufficient-funds'
// - 'user-ineligible'

Type Cleanup

Subscription-Only Fields Removed from RequestPurchaseIosProps

The following fields have been removed from RequestPurchaseIosProps because they only apply to subscription purchases:

  • winBackOffer - Win-back offers are subscription-only (iOS 18+)
  • promotionalOfferJWS - JWS promotional offers are subscription-only
  • introductoryOfferEligibility - Introductory eligibility is subscription-only

These fields remain available in RequestSubscriptionIosProps where they belong.

Migration: If you were incorrectly using these fields with one-time purchases, move them to subscription purchases with type: 'subs'.

New Types

TypePlatformDescription
WinBackOfferInputIOSiOS 18+Win-back offer configuration
PromotionalOfferJwsInputIOSiOS 15+JWS promotional offer input
ProductStatusAndroidAndroid 8.0+Product fetch status codes
SubResponseCodeAndroidAndroid 8.0+Granular purchase error codes
BillingResultAndroidAndroid 8.0+Extended billing result with sub-response

Updated Types

  • SubscriptionOfferTypeIOS now includes 'win-back' type
  • RequestSubscriptionIosProps now supports:
    • winBackOffer
    • promotionalOfferJWS
    • introductoryOfferEligibility
    • withOffer (promotional offer)
  • PurchaseOptions now supports includeSuspendedAndroid
  • ProductAndroid and ProductSubscriptionAndroid now include productStatusAndroid

OpenIAP Versions

PackageVersion
openiap-gql1.3.14
openiap-google1.3.25
openiap-apple1.3.13

For detailed changes, see the OpenIAP Release Notes.

3.4.0 - PurchaseState Cleanup & API Consolidation

· One min read
Hyo
Expo IAP Maintainer

This release reflects OpenIAP v1.3.11 updates, simplifying the PurchaseState enum and consolidating the Android billing API.

Breaking Changes

  • PurchaseState: Removed failed, restored, deferred (now only pending, purchased, unknown)
  • AlternativeBillingModeAndroid: Deprecated in favor of BillingProgramAndroid
  • useAlternativeBilling: Deprecated (only logged debug info, had no effect on purchase flow)

Migration Guide

Before (Deprecated)After (Recommended)
alternativeBillingModeAndroid: 'user-choice'enableBillingProgramAndroid: 'user-choice-billing'
alternativeBillingModeAndroid: 'alternative-only'enableBillingProgramAndroid: 'external-offer'
// Before
const {connected} = useIAP({
alternativeBillingModeAndroid: 'user-choice',
});

// After
const {connected} = useIAP({
enableBillingProgramAndroid: 'user-choice-billing',
});

OpenIAP Versions

PackageVersion
openiap-gql1.3.11
openiap-google1.3.21
openiap-apple1.3.9

For detailed changes, see the OpenIAP Release Notes.

3.1.22 - Horizon OS Support

· 2 min read
Hyo
Expo IAP Maintainer
Ram N
Software Engineer at Meta

Horizon OS Support

Expo IAP 3.1.22 introduces Horizon OS support for Meta Quest devices, enabling developers to implement in-app purchases in VR applications using the same familiar API.

This release integrates Meta's Platform SDK for in-app purchases on Horizon OS, while maintaining the unified OpenIAP interface across iOS, Android, and now Horizon OS.

👉 View the 3.1.22 release

3.1.9 - Alternative Billing Support

· 5 min read
Hyo
Expo IAP Maintainer

Expo IAP 3.1.9 introduces Alternative Billing support for both iOS and Android platforms, enabling developers to offer external payment options in compliance with App Store and Google Play requirements.

This release integrates StoreKit External Purchase APIs (iOS 16.0+) and Google Play Alternative Billing APIs, providing a unified interface for alternative payment flows across platforms.

👉 View the 3.1.9 release

3.1.0 - Full OpenIAP Ecosystem Adoption

· 4 min read
Hyo
Expo IAP Maintainer

Expo IAP 3.1.0 graduates the project into the full OpenIAP ecosystem. The release ships with three dedicated native stacks:

From 3.1.0 onward, Expo IAP stays in lockstep with these modules: Apple v1.2.2, Google v1.2.6, and GQL v1.0.8. That shared version alignment gives Expo IAP stable native compatibility and a unified type system straight from the OpenIAP schema.

👉 View the 3.1.0 release