react-native-iap 14.7.2 → 14.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt +32 -13
  2. package/ios/HybridRnIap.swift +13 -0
  3. package/lib/module/hooks/useIAP.js +18 -8
  4. package/lib/module/hooks/useIAP.js.map +1 -1
  5. package/lib/module/index.js +19 -8
  6. package/lib/module/index.js.map +1 -1
  7. package/lib/module/types.js +31 -0
  8. package/lib/module/types.js.map +1 -1
  9. package/lib/module/utils/type-bridge.js +3 -1
  10. package/lib/module/utils/type-bridge.js.map +1 -1
  11. package/lib/typescript/src/hooks/useIAP.d.ts +2 -0
  12. package/lib/typescript/src/hooks/useIAP.d.ts.map +1 -1
  13. package/lib/typescript/src/index.d.ts.map +1 -1
  14. package/lib/typescript/src/specs/RnIap.nitro.d.ts +48 -6
  15. package/lib/typescript/src/specs/RnIap.nitro.d.ts.map +1 -1
  16. package/lib/typescript/src/types.d.ts +127 -12
  17. package/lib/typescript/src/types.d.ts.map +1 -1
  18. package/lib/typescript/src/utils/type-bridge.d.ts.map +1 -1
  19. package/nitrogen/generated/android/c++/JHybridRnIapSpec.cpp +8 -0
  20. package/nitrogen/generated/android/c++/JNitroAvailablePurchasesAndroidOptions.hpp +7 -3
  21. package/nitrogen/generated/android/c++/JNitroAvailablePurchasesIosOptions.hpp +5 -1
  22. package/nitrogen/generated/android/c++/JNitroProduct.hpp +7 -3
  23. package/nitrogen/generated/android/c++/JNitroPurchaseRequest.hpp +4 -0
  24. package/nitrogen/generated/android/c++/JNitroRequestPurchaseAndroid.hpp +21 -17
  25. package/nitrogen/generated/android/c++/JNitroRequestPurchaseIos.hpp +19 -3
  26. package/nitrogen/generated/android/c++/JPromotionalOfferJwsInputIOS.hpp +61 -0
  27. package/nitrogen/generated/android/c++/JWinBackOfferInputIOS.hpp +57 -0
  28. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroAvailablePurchasesAndroidOptions.kt +6 -3
  29. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroAvailablePurchasesIosOptions.kt +5 -2
  30. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroProduct.kt +6 -3
  31. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroRequestPurchaseAndroid.kt +9 -6
  32. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroRequestPurchaseIos.kt +12 -3
  33. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/PromotionalOfferJwsInputIOS.kt +39 -0
  34. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/WinBackOfferInputIOS.kt +36 -0
  35. package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Bridge.hpp +36 -0
  36. package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Umbrella.hpp +6 -0
  37. package/nitrogen/generated/ios/c++/HybridRnIapSpecSwift.hpp +6 -0
  38. package/nitrogen/generated/ios/swift/NitroAvailablePurchasesAndroidOptions.swift +31 -1
  39. package/nitrogen/generated/ios/swift/NitroAvailablePurchasesIosOptions.swift +31 -1
  40. package/nitrogen/generated/ios/swift/NitroProduct.swift +31 -1
  41. package/nitrogen/generated/ios/swift/NitroRequestPurchaseAndroid.swift +50 -20
  42. package/nitrogen/generated/ios/swift/NitroRequestPurchaseIos.swift +77 -1
  43. package/nitrogen/generated/ios/swift/PromotionalOfferJwsInputIOS.swift +46 -0
  44. package/nitrogen/generated/ios/swift/WinBackOfferInputIOS.swift +35 -0
  45. package/nitrogen/generated/shared/c++/NitroAvailablePurchasesAndroidOptions.hpp +6 -2
  46. package/nitrogen/generated/shared/c++/NitroAvailablePurchasesIosOptions.hpp +5 -1
  47. package/nitrogen/generated/shared/c++/NitroProduct.hpp +6 -2
  48. package/nitrogen/generated/shared/c++/NitroRequestPurchaseAndroid.hpp +21 -17
  49. package/nitrogen/generated/shared/c++/NitroRequestPurchaseIos.hpp +20 -3
  50. package/nitrogen/generated/shared/c++/PromotionalOfferJwsInputIOS.hpp +79 -0
  51. package/nitrogen/generated/shared/c++/WinBackOfferInputIOS.hpp +75 -0
  52. package/openiap-versions.json +4 -4
  53. package/package.json +1 -1
  54. package/src/hooks/useIAP.ts +22 -8
  55. package/src/index.ts +20 -12
  56. package/src/specs/RnIap.nitro.ts +49 -5
  57. package/src/types.ts +131 -12
  58. package/src/utils/type-bridge.ts +2 -0
@@ -104,6 +104,8 @@ type UseIap = {
104
104
  export interface UseIapOptions {
105
105
  onPurchaseSuccess?: (purchase: Purchase) => void;
106
106
  onPurchaseError?: (error: PurchaseError) => void;
107
+ /** Callback for non-purchase errors (fetchProducts, getAvailablePurchases, etc.) */
108
+ onError?: (error: Error) => void;
107
109
  onPromotedProductIOS?: (product: Product) => void;
108
110
  onUserChoiceBillingAndroid?: (details: UserChoiceBillingDetails) => void;
109
111
  /**
@@ -180,6 +182,15 @@ export function useIAP(options?: UseIapOptions): UseIap {
180
182
  subscriptionsRefState.current = subscriptions;
181
183
  }, [subscriptions]);
182
184
 
185
+ // Helper function to invoke onError callback
186
+ const invokeOnError = useCallback((error: unknown) => {
187
+ if (optionsRef.current?.onError) {
188
+ optionsRef.current.onError(
189
+ error instanceof Error ? error : new Error(String(error)),
190
+ );
191
+ }
192
+ }, []);
193
+
183
194
  const fetchProductsInternal = useCallback(
184
195
  async (params: {
185
196
  skus: string[];
@@ -253,9 +264,10 @@ export function useIAP(options?: UseIapOptions): UseIap {
253
264
  );
254
265
  } catch (error) {
255
266
  RnIapConsole.error('Error fetching products:', error);
267
+ invokeOnError(error);
256
268
  }
257
269
  },
258
- [mergeWithDuplicateCheck],
270
+ [mergeWithDuplicateCheck, invokeOnError],
259
271
  );
260
272
 
261
273
  const getAvailablePurchasesInternal = useCallback(
@@ -268,9 +280,10 @@ export function useIAP(options?: UseIapOptions): UseIap {
268
280
  setAvailablePurchases(result);
269
281
  } catch (error) {
270
282
  RnIapConsole.error('Error fetching available purchases:', error);
283
+ invokeOnError(error);
271
284
  }
272
285
  },
273
- [],
286
+ [invokeOnError],
274
287
  );
275
288
 
276
289
  const getActiveSubscriptionsInternal = useCallback(
@@ -281,12 +294,11 @@ export function useIAP(options?: UseIapOptions): UseIap {
281
294
  return result;
282
295
  } catch (error) {
283
296
  RnIapConsole.error('Error getting active subscriptions:', error);
284
- // Don't clear existing activeSubscriptions on error - preserve current state
285
- // This prevents the UI from showing empty state when there are temporary network issues
297
+ invokeOnError(error);
286
298
  return [];
287
299
  }
288
300
  },
289
- [],
301
+ [invokeOnError],
290
302
  );
291
303
 
292
304
  const hasActiveSubscriptionsInternal = useCallback(
@@ -295,10 +307,11 @@ export function useIAP(options?: UseIapOptions): UseIap {
295
307
  return await hasActiveSubscriptions(subscriptionIds);
296
308
  } catch (error) {
297
309
  RnIapConsole.error('Error checking active subscriptions:', error);
310
+ invokeOnError(error);
298
311
  return false;
299
312
  }
300
313
  },
301
- [],
314
+ [invokeOnError],
302
315
  );
303
316
 
304
317
  const finishTransaction = useCallback(
@@ -469,8 +482,9 @@ export function useIAP(options?: UseIapOptions): UseIap {
469
482
  try {
470
483
  await restorePurchasesTopLevel();
471
484
  await getAvailablePurchasesInternal();
472
- } catch (e) {
473
- RnIapConsole.warn('Failed to restore purchases:', e);
485
+ } catch (error) {
486
+ RnIapConsole.warn('Failed to restore purchases:', error);
487
+ invokeOnError(error);
474
488
  }
475
489
  },
476
490
  getPromotedProductIOS,
package/src/index.ts CHANGED
@@ -1241,27 +1241,35 @@ export const requestPurchase: MutationField<'requestPurchase'> = async (
1241
1241
  skus: androidRequest.skus,
1242
1242
  };
1243
1243
 
1244
- if (androidRequest.obfuscatedAccountIdAndroid) {
1245
- androidPayload.obfuscatedAccountIdAndroid =
1246
- androidRequest.obfuscatedAccountIdAndroid;
1244
+ if (androidRequest.obfuscatedAccountId) {
1245
+ androidPayload.obfuscatedAccountId = androidRequest.obfuscatedAccountId;
1247
1246
  }
1248
- if (androidRequest.obfuscatedProfileIdAndroid) {
1249
- androidPayload.obfuscatedProfileIdAndroid =
1250
- androidRequest.obfuscatedProfileIdAndroid;
1247
+ if (androidRequest.obfuscatedProfileId) {
1248
+ androidPayload.obfuscatedProfileId = androidRequest.obfuscatedProfileId;
1251
1249
  }
1252
1250
  if (androidRequest.isOfferPersonalized != null) {
1253
1251
  androidPayload.isOfferPersonalized = androidRequest.isOfferPersonalized;
1254
1252
  }
1255
1253
 
1254
+ // One-time purchase offerToken (Android 7.0+)
1255
+ if (!isSubs) {
1256
+ const purchaseRequest = androidRequest as RequestPurchaseAndroidProps;
1257
+ if (purchaseRequest.offerToken) {
1258
+ androidPayload.offerToken = purchaseRequest.offerToken;
1259
+ }
1260
+ }
1261
+
1256
1262
  if (isSubs) {
1257
1263
  const subsRequest = androidRequest as RequestSubscriptionAndroidProps;
1258
- if (subsRequest.purchaseTokenAndroid) {
1259
- androidPayload.purchaseTokenAndroid =
1260
- subsRequest.purchaseTokenAndroid;
1264
+ if (subsRequest.purchaseToken) {
1265
+ androidPayload.purchaseToken = subsRequest.purchaseToken;
1266
+ }
1267
+ if (subsRequest.replacementMode != null) {
1268
+ androidPayload.replacementMode = subsRequest.replacementMode;
1261
1269
  }
1262
- if (subsRequest.replacementModeAndroid != null) {
1263
- androidPayload.replacementModeAndroid =
1264
- subsRequest.replacementModeAndroid;
1270
+ if (subsRequest.subscriptionProductReplacementParams) {
1271
+ androidPayload.subscriptionProductReplacementParams =
1272
+ subsRequest.subscriptionProductReplacementParams;
1265
1273
  }
1266
1274
  androidPayload.subscriptionOffers = (
1267
1275
  subsRequest.subscriptionOffers ?? []
@@ -12,6 +12,7 @@ import type {
12
12
  ExternalPurchaseNoticeResultIOS,
13
13
  MutationFinishTransactionArgs,
14
14
  ProductCommon,
15
+ PromotionalOfferJwsInputIOS,
15
16
  PurchaseCommon,
16
17
  PurchaseOptions,
17
18
  VerifyPurchaseAppleOptions,
@@ -24,6 +25,7 @@ import type {
24
25
  UserChoiceBillingDetails,
25
26
  PaymentModeIOS,
26
27
  SubscriptionProductReplacementParamsAndroid,
28
+ WinBackOfferInputIOS,
27
29
  } from '../types';
28
30
 
29
31
  // Nitro-compatible enum types (Nitro doesn't support inline string unions from types.ts)
@@ -118,20 +120,47 @@ export interface NitroRequestPurchaseIos {
118
120
  * @platform iOS
119
121
  */
120
122
  advancedCommerceData?: RequestPurchaseIosProps['advancedCommerceData'];
123
+ /**
124
+ * Override introductory offer eligibility (iOS 15+, WWDC 2025).
125
+ * Set to true to indicate the user is eligible for introductory offer,
126
+ * or false to indicate they are not. When nil, the system determines eligibility.
127
+ * Back-deployed to iOS 15.
128
+ * @platform iOS
129
+ */
130
+ introductoryOfferEligibility?: boolean | null;
131
+ /**
132
+ * JWS promotional offer (iOS 15+, WWDC 2025).
133
+ * New signature format using compact JWS string for promotional offers.
134
+ * Back-deployed to iOS 15.
135
+ * @platform iOS
136
+ */
137
+ promotionalOfferJWS?: PromotionalOfferJwsInputIOS | null;
138
+ /**
139
+ * Win-back offer to apply (iOS 18+).
140
+ * Used to re-engage churned subscribers with a discount or free trial.
141
+ * @platform iOS
142
+ */
143
+ winBackOffer?: WinBackOfferInputIOS | null;
121
144
  }
122
145
 
123
146
  export interface NitroRequestPurchaseAndroid {
124
147
  skus: RequestSubscriptionAndroidProps['skus'];
125
- obfuscatedAccountIdAndroid?: RequestSubscriptionAndroidProps['obfuscatedAccountIdAndroid'];
126
- obfuscatedProfileIdAndroid?: RequestSubscriptionAndroidProps['obfuscatedProfileIdAndroid'];
148
+ obfuscatedAccountId?: RequestSubscriptionAndroidProps['obfuscatedAccountId'];
149
+ obfuscatedProfileId?: RequestSubscriptionAndroidProps['obfuscatedProfileId'];
127
150
  isOfferPersonalized?: RequestSubscriptionAndroidProps['isOfferPersonalized'];
151
+ /**
152
+ * Offer token for one-time purchase discounts (7.0+).
153
+ * Pass the offerToken from oneTimePurchaseOfferDetailsAndroid or discountOffers
154
+ * to apply a discount offer to the purchase.
155
+ */
156
+ offerToken?: string | null;
128
157
  subscriptionOffers?: AndroidSubscriptionOfferInput[] | null;
129
158
  /** @deprecated Use subscriptionProductReplacementParams instead for item-level replacement (8.1.0+) */
130
- replacementModeAndroid?: RequestSubscriptionAndroidProps['replacementModeAndroid'];
131
- purchaseTokenAndroid?: RequestSubscriptionAndroidProps['purchaseTokenAndroid'];
159
+ replacementMode?: RequestSubscriptionAndroidProps['replacementMode'];
160
+ purchaseToken?: RequestSubscriptionAndroidProps['purchaseToken'];
132
161
  /**
133
162
  * Product-level replacement parameters (8.1.0+)
134
- * Use this instead of replacementModeAndroid for item-level replacement
163
+ * Use this instead of replacementMode for item-level replacement
135
164
  */
136
165
  subscriptionProductReplacementParams?: SubscriptionProductReplacementParamsAndroid | null;
137
166
  }
@@ -161,6 +190,13 @@ type NitroAvailablePurchasesAndroidType = 'inapp' | 'subs';
161
190
 
162
191
  export interface NitroAvailablePurchasesAndroidOptions {
163
192
  type?: NitroAvailablePurchasesAndroidType;
193
+ /**
194
+ * Include suspended subscriptions in the result (Android 8.1+).
195
+ * Suspended subscriptions have isSuspendedAndroid=true and should NOT be granted entitlements.
196
+ * Users should be directed to the subscription center to resolve payment issues.
197
+ * Default: false (only active subscriptions are returned)
198
+ */
199
+ includeSuspended?: boolean | null;
164
200
  }
165
201
 
166
202
  export interface NitroAvailablePurchasesOptions {
@@ -543,6 +579,14 @@ export interface NitroProduct {
543
579
  freeTrialPeriodAndroid?: string | null;
544
580
  subscriptionOfferDetailsAndroid?: string | null;
545
581
  oneTimePurchaseOfferDetailsAndroid?: NitroOneTimePurchaseOfferDetail[] | null;
582
+ /**
583
+ * Product-level status code indicating fetch result (Android 8.0+)
584
+ * OK = product fetched successfully
585
+ * NOT_FOUND = SKU doesn't exist
586
+ * NO_OFFERS_AVAILABLE = user not eligible for any offers
587
+ * Available in Google Play Billing Library 8.0.0+
588
+ */
589
+ productStatusAndroid?: string | null;
546
590
  }
547
591
 
548
592
  // ╔══════════════════════════════════════════════════════════════════════════╗
package/src/types.ts CHANGED
@@ -99,6 +99,22 @@ export interface BillingProgramReportingDetailsAndroid {
99
99
  externalTransactionToken: string;
100
100
  }
101
101
 
102
+ /**
103
+ * Extended billing result with sub-response code (Android)
104
+ * Available in Google Play Billing Library 8.0.0+
105
+ */
106
+ export interface BillingResultAndroid {
107
+ /** Debug message from the billing library */
108
+ debugMessage?: (string | null);
109
+ /** The response code from the billing operation */
110
+ responseCode: number;
111
+ /**
112
+ * Sub-response code for more granular error information (8.0+).
113
+ * Provides additional context when responseCode indicates an error.
114
+ */
115
+ subResponseCode?: (SubResponseCodeAndroid | null);
116
+ }
117
+
102
118
  export interface DeepLinkOptions {
103
119
  /** Android package name to target (required on Android) */
104
120
  packageNameAndroid?: (string | null);
@@ -525,7 +541,7 @@ export interface Mutation {
525
541
  * promoted products can be purchased directly via the standard purchase flow.
526
542
  * @deprecated Use promotedProductListenerIOS + requestPurchase instead
527
543
  */
528
- requestPurchaseOnPromotedProductIOS: boolean;
544
+ requestPurchaseOnPromotedProductIOS: Promise<boolean>;
529
545
  /** Restore completed purchases across platforms */
530
546
  restorePurchases: Promise<void>;
531
547
  /**
@@ -664,6 +680,14 @@ export interface ProductAndroid extends ProductCommon {
664
680
  oneTimePurchaseOfferDetailsAndroid?: (ProductAndroidOneTimePurchaseOfferDetail[] | null);
665
681
  platform: 'android';
666
682
  price?: (number | null);
683
+ /**
684
+ * Product-level status code indicating fetch result (Android 8.0+)
685
+ * OK = product fetched successfully
686
+ * NOT_FOUND = SKU doesn't exist
687
+ * NO_OFFERS_AVAILABLE = user not eligible for any offers
688
+ * Available in Google Play Billing Library 8.0.0+
689
+ */
690
+ productStatusAndroid?: (ProductStatusAndroid | null);
667
691
  /**
668
692
  * @deprecated Use subscriptionOffers instead for cross-platform compatibility.
669
693
  * @deprecated Use subscriptionOffers instead
@@ -769,6 +793,14 @@ export interface ProductRequest {
769
793
  type?: (ProductQueryType | null);
770
794
  }
771
795
 
796
+ /**
797
+ * Status code for individual products returned from queryProductDetailsAsync (Android)
798
+ * Prior to 8.0, products that couldn't be fetched were simply not returned.
799
+ * With 8.0+, these products are returned with a status code explaining why.
800
+ * Available in Google Play Billing Library 8.0.0+
801
+ */
802
+ export type ProductStatusAndroid = 'ok' | 'not-found' | 'no-offers-available' | 'unknown';
803
+
772
804
  export type ProductSubscription = ProductSubscriptionAndroid | ProductSubscriptionIOS;
773
805
 
774
806
  export interface ProductSubscriptionAndroid extends ProductCommon {
@@ -794,6 +826,14 @@ export interface ProductSubscriptionAndroid extends ProductCommon {
794
826
  oneTimePurchaseOfferDetailsAndroid?: (ProductAndroidOneTimePurchaseOfferDetail[] | null);
795
827
  platform: 'android';
796
828
  price?: (number | null);
829
+ /**
830
+ * Product-level status code indicating fetch result (Android 8.0+)
831
+ * OK = product fetched successfully
832
+ * NOT_FOUND = SKU doesn't exist
833
+ * NO_OFFERS_AVAILABLE = user not eligible for any offers
834
+ * Available in Google Play Billing Library 8.0.0+
835
+ */
836
+ productStatusAndroid?: (ProductStatusAndroid | null);
797
837
  /**
798
838
  * @deprecated Use subscriptionOffers instead for cross-platform compatibility.
799
839
  * @deprecated Use subscriptionOffers instead
@@ -866,6 +906,23 @@ export type ProductType = 'in-app' | 'subs';
866
906
 
867
907
  export type ProductTypeIOS = 'consumable' | 'non-consumable' | 'auto-renewable-subscription' | 'non-renewing-subscription';
868
908
 
909
+ /**
910
+ * JWS promotional offer input for iOS 15+ (StoreKit 2, WWDC 2025).
911
+ * New signature format using compact JWS string for promotional offers.
912
+ * This provides a simpler alternative to the legacy signature-based promotional offers.
913
+ * Back-deployed to iOS 15.
914
+ */
915
+ export interface PromotionalOfferJwsInputIOS {
916
+ /**
917
+ * Compact JWS string signed by your server.
918
+ * The JWS should contain the promotional offer signature data.
919
+ * Format: header.payload.signature (base64url encoded)
920
+ */
921
+ jws: string;
922
+ /** The promotional offer identifier from App Store Connect */
923
+ offerId: string;
924
+ }
925
+
869
926
  export type Purchase = PurchaseAndroid | PurchaseIOS;
870
927
 
871
928
  export interface PurchaseAndroid extends PurchaseCommon {
@@ -980,6 +1037,13 @@ export interface PurchaseOfferIOS {
980
1037
  export interface PurchaseOptions {
981
1038
  /** Also emit results through the iOS event listeners */
982
1039
  alsoPublishToEventListenerIOS?: (boolean | null);
1040
+ /**
1041
+ * Include suspended subscriptions in the result (Android 8.1+).
1042
+ * Suspended subscriptions have isSuspendedAndroid=true and should NOT be granted entitlements.
1043
+ * Users should be directed to the subscription center to resolve payment issues.
1044
+ * Default: false (only active subscriptions are returned)
1045
+ */
1046
+ includeSuspendedAndroid?: (boolean | null);
983
1047
  /** Limit to currently active items on iOS */
984
1048
  onlyIncludeActiveItemsIOS?: (boolean | null);
985
1049
  }
@@ -1130,12 +1194,21 @@ export interface RequestPurchaseAndroidProps {
1130
1194
  * Google Play Billing and the developer's external payment option.
1131
1195
  */
1132
1196
  developerBillingOption?: (DeveloperBillingOptionParamsAndroid | null);
1133
- /** Personalized offer flag */
1197
+ /**
1198
+ * Personalized offer flag.
1199
+ * When true, indicates the price was customized for this user.
1200
+ */
1134
1201
  isOfferPersonalized?: (boolean | null);
1135
1202
  /** Obfuscated account ID */
1136
- obfuscatedAccountIdAndroid?: (string | null);
1203
+ obfuscatedAccountId?: (string | null);
1137
1204
  /** Obfuscated profile ID */
1138
- obfuscatedProfileIdAndroid?: (string | null);
1205
+ obfuscatedProfileId?: (string | null);
1206
+ /**
1207
+ * Offer token for one-time purchase discounts (7.0+).
1208
+ * Pass the offerToken from oneTimePurchaseOfferDetailsAndroid or discountOffers
1209
+ * to apply a discount offer to the purchase.
1210
+ */
1211
+ offerToken?: (string | null);
1139
1212
  /** List of product SKUs */
1140
1213
  skus: string[];
1141
1214
  }
@@ -1156,7 +1229,10 @@ export interface RequestPurchaseIosProps {
1156
1229
  quantity?: (number | null);
1157
1230
  /** Product SKU */
1158
1231
  sku: string;
1159
- /** Discount offer to apply */
1232
+ /**
1233
+ * Promotional offer to apply (subscriptions only, ignored for one-time purchases).
1234
+ * iOS only supports promotional offers for auto-renewable subscriptions.
1235
+ */
1160
1236
  withOffer?: (DiscountOfferInputIOS | null);
1161
1237
  }
1162
1238
 
@@ -1204,26 +1280,29 @@ export interface RequestSubscriptionAndroidProps {
1204
1280
  * Google Play Billing and the developer's external payment option.
1205
1281
  */
1206
1282
  developerBillingOption?: (DeveloperBillingOptionParamsAndroid | null);
1207
- /** Personalized offer flag */
1283
+ /**
1284
+ * Personalized offer flag.
1285
+ * When true, indicates the price was customized for this user.
1286
+ */
1208
1287
  isOfferPersonalized?: (boolean | null);
1209
1288
  /** Obfuscated account ID */
1210
- obfuscatedAccountIdAndroid?: (string | null);
1289
+ obfuscatedAccountId?: (string | null);
1211
1290
  /** Obfuscated profile ID */
1212
- obfuscatedProfileIdAndroid?: (string | null);
1291
+ obfuscatedProfileId?: (string | null);
1213
1292
  /** Purchase token for upgrades/downgrades */
1214
- purchaseTokenAndroid?: (string | null);
1293
+ purchaseToken?: (string | null);
1215
1294
  /**
1216
1295
  * Replacement mode for subscription changes
1217
1296
  * @deprecated Use subscriptionProductReplacementParams instead for item-level replacement (8.1.0+)
1218
1297
  */
1219
- replacementModeAndroid?: (number | null);
1298
+ replacementMode?: (number | null);
1220
1299
  /** List of subscription SKUs */
1221
1300
  skus: string[];
1222
1301
  /** Subscription offers */
1223
1302
  subscriptionOffers?: (AndroidSubscriptionOfferInput[] | null);
1224
1303
  /**
1225
1304
  * Product-level replacement parameters (8.1.0+)
1226
- * Use this instead of replacementModeAndroid for item-level replacement
1305
+ * Use this instead of replacementMode for item-level replacement
1227
1306
  */
1228
1307
  subscriptionProductReplacementParams?: (SubscriptionProductReplacementParamsAndroid | null);
1229
1308
  }
@@ -1238,8 +1317,32 @@ export interface RequestSubscriptionIosProps {
1238
1317
  advancedCommerceData?: (string | null);
1239
1318
  andDangerouslyFinishTransactionAutomatically?: (boolean | null);
1240
1319
  appAccountToken?: (string | null);
1320
+ /**
1321
+ * Override introductory offer eligibility (iOS 15+, WWDC 2025).
1322
+ * Set to true to indicate the user is eligible for introductory offer,
1323
+ * or false to indicate they are not. When nil, the system determines eligibility.
1324
+ * Back-deployed to iOS 15.
1325
+ */
1326
+ introductoryOfferEligibility?: (boolean | null);
1327
+ /**
1328
+ * JWS promotional offer (iOS 15+, WWDC 2025).
1329
+ * New signature format using compact JWS string for promotional offers.
1330
+ * Back-deployed to iOS 15.
1331
+ */
1332
+ promotionalOfferJWS?: (PromotionalOfferJwsInputIOS | null);
1241
1333
  quantity?: (number | null);
1242
1334
  sku: string;
1335
+ /**
1336
+ * Win-back offer to apply (iOS 18+)
1337
+ * Used to re-engage churned subscribers with a discount or free trial.
1338
+ * The offer is available when the customer is eligible and can be discovered
1339
+ * via StoreKit Message (automatic) or subscription offer APIs.
1340
+ */
1341
+ winBackOffer?: (WinBackOfferInputIOS | null);
1342
+ /**
1343
+ * Promotional offer to apply for subscription purchases.
1344
+ * Requires server-signed offer with nonce, timestamp, keyId, and signature.
1345
+ */
1243
1346
  withOffer?: (DiscountOfferInputIOS | null);
1244
1347
  }
1245
1348
 
@@ -1295,6 +1398,12 @@ export interface RequestVerifyPurchaseWithIapkitResult {
1295
1398
  store: IapStore;
1296
1399
  }
1297
1400
 
1401
+ /**
1402
+ * Sub-response codes for more granular purchase error information (Android)
1403
+ * Available in Google Play Billing Library 8.0.0+
1404
+ */
1405
+ export type SubResponseCodeAndroid = 'no-applicable-sub-response-code' | 'payment-declined-due-to-insufficient-funds' | 'user-ineligible';
1406
+
1298
1407
  export interface Subscription {
1299
1408
  /**
1300
1409
  * Fires when a user selects developer billing in the External Payments flow (Android only)
@@ -1415,7 +1524,7 @@ export interface SubscriptionOfferIOS {
1415
1524
  type: SubscriptionOfferTypeIOS;
1416
1525
  }
1417
1526
 
1418
- export type SubscriptionOfferTypeIOS = 'introductory' | 'promotional';
1527
+ export type SubscriptionOfferTypeIOS = 'introductory' | 'promotional' | 'win-back';
1419
1528
 
1420
1529
  /** Subscription period value combining unit and count. */
1421
1530
  export interface SubscriptionPeriod {
@@ -1615,6 +1724,16 @@ export interface VerifyPurchaseWithProviderResult {
1615
1724
 
1616
1725
  export type VoidResult = void;
1617
1726
 
1727
+ /**
1728
+ * Win-back offer input for iOS 18+ (StoreKit 2)
1729
+ * Win-back offers are used to re-engage churned subscribers.
1730
+ * The offer is automatically presented via StoreKit Message when eligible,
1731
+ * or can be applied programmatically during purchase.
1732
+ */
1733
+ export interface WinBackOfferInputIOS {
1734
+ /** The win-back offer ID from App Store Connect */
1735
+ offerId: string;
1736
+ }
1618
1737
  // -- Query helper types (auto-generated)
1619
1738
  export type QueryArgsMap = {
1620
1739
  canPresentExternalPurchaseNoticeIOS: never;
@@ -312,6 +312,8 @@ export function convertNitroProductToProduct(
312
312
  subscriptionOfferDetailsAndroid: parseSubscriptionOffers(
313
313
  nitroProduct.subscriptionOfferDetailsAndroid,
314
314
  ),
315
+ // Product status (Billing Library 8.0+, OpenIAP 1.3.14+)
316
+ productStatusAndroid: nitroProduct.productStatusAndroid ?? null,
315
317
  };
316
318
 
317
319
  // Parse standardized subscriptionOffers (cross-platform, OpenIAP 1.3.10+)