3.4.7 - ExternalPurchaseCustomLink API (iOS 18.1+)
Expo IAP 3.4.7 adds support for Apple's ExternalPurchaseCustomLink API (iOS 18.1+) for apps using custom external purchase links.
iOS platform specific features
View All TagsExpo IAP 3.4.7 adds support for Apple's ExternalPurchaseCustomLink API (iOS 18.1+) for apps using custom external purchase links.
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.
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.
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',
});
Override system-determined introductory offer eligibility.
await requestPurchase({
request: {
apple: {
sku: 'premium_monthly',
introductoryOfferEligibility: true, // Force eligible
},
},
type: 'subs',
});
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;
}
}
});
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.
More granular error information for purchase failures.
// SubResponseCodeAndroid provides additional context:
// - 'no-applicable-sub-response-code'
// - 'payment-declined-due-to-insufficient-funds'
// - 'user-ineligible'
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-onlyintroductoryOfferEligibility - Introductory eligibility is subscription-onlyThese 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'.
| Type | Platform | Description |
|---|---|---|
WinBackOfferInputIOS | iOS 18+ | Win-back offer configuration |
PromotionalOfferJwsInputIOS | iOS 15+ | JWS promotional offer input |
ProductStatusAndroid | Android 8.0+ | Product fetch status codes |
SubResponseCodeAndroid | Android 8.0+ | Granular purchase error codes |
BillingResultAndroid | Android 8.0+ | Extended billing result with sub-response |
SubscriptionOfferTypeIOS now includes 'win-back' typeRequestSubscriptionIosProps now supports:
winBackOfferpromotionalOfferJWSintroductoryOfferEligibilitywithOffer (promotional offer)PurchaseOptions now supports includeSuspendedAndroidProductAndroid and ProductSubscriptionAndroid now include productStatusAndroid| Package | Version |
|---|---|
| openiap-gql | 1.3.14 |
| openiap-google | 1.3.25 |
| openiap-apple | 1.3.13 |
For detailed changes, see the OpenIAP Release Notes.
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.
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.
Expo IAP 3.0.0 is a major release that fully integrates OpenIAP’s Android and iOS modules. Expo IAP is no longer just a framework bridge — it now leverages a centralized OpenIAP core for shared logic across platforms and SDKs. The result is a leaner, more maintainable codebase with faster iteration and greater stability.
We're excited to announce 2.9.0, featuring full integration with OpenIAP Apple that reduces our Swift codebase by ~1,180 lines (73% reduction)!
We're excited to announce the release of expo-iap 2.6.0! This release brings significant improvements to iOS functionality, better TypeScript support, and enhanced developer experience.