Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions libraries/expo-iap/src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -872,7 +872,8 @@ describe('Public API (index.ts)', () => {
request: {
apple: {
sku: 'com.example.subscription.monthly',
introductoryOfferEligibility: true,
billingPlanType: 'monthly',
compactJWS: 'intro-eligibility-jws',
promotionalOfferJWS: {
offerId: 'promo-offer',
jws: 'compact-jws',
Expand All @@ -890,7 +891,8 @@ describe('Public API (index.ts)', () => {
request: {
ios: {
sku: 'com.example.subscription.monthly',
introductoryOfferEligibility: true,
billingPlanType: 'monthly',
compactJWS: 'intro-eligibility-jws',
promotionalOfferJWS: {
offerId: 'promo-offer',
jws: 'compact-jws',
Expand Down
69 changes: 64 additions & 5 deletions libraries/expo-iap/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -985,6 +985,11 @@ export interface ProductIOS extends ProductCommon {
jsonRepresentationIOS: string;
platform: 'ios';
price?: (number | null);
/**
* iOS 26.4+ subscription pricing terms, including billing plan metadata for
* monthly subscriptions with a 12-month commitment.
*/
pricingTermsIOS?: (SubscriptionPricingTermsIOS[] | null);
/**
* @deprecated Use subscriptionOffers instead for cross-platform compatibility.
* @deprecated Use subscriptionOffers instead
Expand Down Expand Up @@ -1108,6 +1113,11 @@ export interface ProductSubscriptionIOS extends ProductCommon {
jsonRepresentationIOS: string;
platform: 'ios';
price?: (number | null);
/**
* iOS 26.4+ subscription pricing terms, including billing plan metadata for
* monthly subscriptions with a 12-month commitment.
*/
pricingTermsIOS?: (SubscriptionPricingTermsIOS[] | null);
/** App Store subscription group identifier for intro-offer eligibility checks. */
subscriptionGroupIdIOS?: (string | null);
/**
Expand Down Expand Up @@ -1234,6 +1244,10 @@ export interface PurchaseIOS extends PurchaseCommon {
advancedCommerceInfoIOS?: (AdvancedCommerceInfoIOS | null);
appAccountToken?: (string | null);
appBundleIdIOS?: (string | null);
/** iOS 26.4+ billing plan selected for this transaction. */
billingPlanTypeIOS?: (SubscriptionBillingPlanTypeIOS | null);
/** iOS 26.4+ progress information for monthly subscriptions with a 12-month commitment. */
commitmentInfoIOS?: (TransactionCommitmentInfoIOS | null);
countryCodeIOS?: (string | null);
currencyCodeIOS?: (string | null);
currencySymbolIOS?: (string | null);
Expand Down Expand Up @@ -1454,12 +1468,25 @@ export interface RefundResultIOS {
status: string;
}

export interface RenewalCommitmentInfoIOS {
commitmentAutoRenewProductId: string;
commitmentAutoRenewStatus: boolean;
commitmentRenewalBillingPlanType: SubscriptionBillingPlanTypeIOS;
commitmentRenewalDate: number;
commitmentRenewalPrice: number;
}

/**
* Subscription renewal information from Product.SubscriptionInfo.RenewalInfo
* https://developer.apple.com/documentation/storekit/product/subscriptioninfo/renewalinfo
*/
export interface RenewalInfoIOS {
autoRenewPreference?: (string | null);
/**
* iOS 26.4+ renewal commitment metadata for monthly subscriptions with a
* 12-month commitment.
*/
commitmentInfo?: (RenewalCommitmentInfoIOS | null);
/**
* When subscription expires due to cancellation/billing issue
* Possible values: "VOLUNTARY", "BILLING_ERROR", "DID_NOT_AGREE_TO_PRICE_INCREASE", "PRODUCT_NOT_AVAILABLE", "UNKNOWN"
Expand All @@ -1486,6 +1513,8 @@ export interface RenewalInfoIOS {
* Possible values: "AGREED", "PENDING", null (no price increase)
*/
priceIncreaseStatus?: (string | null);
/** iOS 26.4+ billing plan that will renew after the current period. */
renewalBillingPlanType?: (SubscriptionBillingPlanTypeIOS | null);
/**
* Expected renewal date (milliseconds since epoch)
* For active subscriptions, when the next renewal/charge will occur
Expand Down Expand Up @@ -1646,12 +1675,17 @@ export interface RequestSubscriptionIosProps {
andDangerouslyFinishTransactionAutomatically?: (boolean | null);
appAccountToken?: (string | null);
/**
* Override introductory offer eligibility (iOS 15+, WWDC 2025).
* Set to true to indicate the user is eligible for introductory offer,
* or false to indicate they are not. When nil, the system determines eligibility.
* Back-deployed to iOS 15.
* Billing plan to use when purchasing an annual subscription that offers
* monthly billing with a 12-month commitment (iOS 26.4+).
*/
billingPlanType?: (SubscriptionBillingPlanTypeIOS | null);
/**
* Compact JWS string for overriding introductory offer eligibility
* (iOS 15+, WWDC 2025). When nil, the system determines eligibility.
* Generate the JWS on your server and pass it to StoreKit's
* introductoryOfferEligibility(compactJWS:) purchase option.
*/
introductoryOfferEligibility?: (boolean | null);
compactJWS?: (string | null);
/**
* JWS promotional offer (iOS 15+, WWDC 2025).
* New signature format using compact JWS string for promotional offers.
Expand Down Expand Up @@ -1777,8 +1811,17 @@ export interface Subscription {

export type SubscriptionPurchaseUpdatedArgs = (PurchaseUpdatedListenerOptions | null) | undefined;

export type SubscriptionBillingPlanTypeIOS = 'unknown' | 'monthly' | 'up-front';

export interface SubscriptionCommitmentInfoIOS {
displayPrice: string;
period: SubscriptionPeriodValueIOS;
price: number;
}

export interface SubscriptionInfoIOS {
introductoryOffer?: (SubscriptionOfferIOS | null);
pricingTerms?: (SubscriptionPricingTermsIOS[] | null);
promotionalOffers?: (SubscriptionOfferIOS[] | null);
subscriptionGroupId: string;
subscriptionPeriod: SubscriptionPeriodValueIOS;
Expand Down Expand Up @@ -1900,6 +1943,15 @@ export interface SubscriptionPeriodValueIOS {
value: number;
}

export interface SubscriptionPricingTermsIOS {
billingDisplayPrice: string;
billingPeriod: SubscriptionPeriodValueIOS;
billingPlanType: SubscriptionBillingPlanTypeIOS;
billingPrice: number;
commitmentInfo: SubscriptionCommitmentInfoIOS;
subscriptionOffers?: (SubscriptionOffer[] | null);
}

/**
* Product-level subscription replacement parameters (Android)
* Used with setSubscriptionProductReplacementParams in BillingFlowParams.ProductDetailsParams
Expand All @@ -1926,6 +1978,13 @@ export interface SubscriptionStatusIOS {
state: string;
}

export interface TransactionCommitmentInfoIOS {
billingPeriodNumber: number;
commitmentExpiresDate: number;
commitmentPrice: number;
totalBillingPeriods: number;
}

/**
* User Choice Billing event details (Android)
* Fired when a user selects alternative billing in the User Choice Billing dialog
Expand Down
29 changes: 26 additions & 3 deletions libraries/flutter_inapp_purchase/lib/builders.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,21 @@ class RequestPurchaseIosBuilder {
DiscountOfferInputIOS? withOffer;
String? advancedCommerceData;

/// Win-back offer to apply when this builder is used with subscriptions
/// (iOS 18+)
WinBackOfferInputIOS? winBackOffer;

/// JWS promotional offer when this builder is used with subscriptions
/// (iOS 15+, WWDC 2025)
PromotionalOfferJWSInputIOS? promotionalOfferJWS;

/// Billing plan for annual subscriptions with monthly commitment (iOS 26.4+)
SubscriptionBillingPlanTypeIOS? billingPlanType;

/// Compact JWS for overriding introductory offer eligibility when this
/// builder is used with subscriptions (iOS 15+)
String? compactJWS;

RequestPurchaseIosBuilder();

RequestPurchaseIosProps build() {
Expand Down Expand Up @@ -39,8 +54,11 @@ class RequestSubscriptionIosBuilder {
/// JWS promotional offer (iOS 15+, WWDC 2025)
PromotionalOfferJWSInputIOS? promotionalOfferJWS;

/// Override introductory offer eligibility (iOS 15+)
bool? introductoryOfferEligibility;
/// Billing plan for annual subscriptions with monthly commitment (iOS 26.4+)
SubscriptionBillingPlanTypeIOS? billingPlanType;

/// Compact JWS for overriding introductory offer eligibility (iOS 15+)
String? compactJWS;

RequestSubscriptionIosBuilder();

Expand All @@ -53,9 +71,10 @@ class RequestSubscriptionIosBuilder {
quantity: quantity,
withOffer: withOffer,
advancedCommerceData: advancedCommerceData,
billingPlanType: billingPlanType,
winBackOffer: winBackOffer,
promotionalOfferJWS: promotionalOfferJWS,
introductoryOfferEligibility: introductoryOfferEligibility,
compactJWS: compactJWS,
);
}
}
Expand Down Expand Up @@ -174,6 +193,10 @@ class RequestPurchaseBuilder {
quantity: iosProps.quantity,
withOffer: iosProps.withOffer,
advancedCommerceData: iosProps.advancedCommerceData,
billingPlanType: ios.billingPlanType,
winBackOffer: ios.winBackOffer,
promotionalOfferJWS: ios.promotionalOfferJWS,
compactJWS: ios.compactJWS,
);

final androidSub = androidProps == null
Expand Down
Loading
Loading