react-native-iap 14.7.1 → 14.7.3

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 +151 -7
  2. package/ios/HybridRnIap.swift +13 -0
  3. package/ios/RnIapHelper.swift +24 -0
  4. package/lib/module/types.js +94 -1
  5. package/lib/module/types.js.map +1 -1
  6. package/lib/module/utils/type-bridge.js +52 -1
  7. package/lib/module/utils/type-bridge.js.map +1 -1
  8. package/lib/typescript/src/specs/RnIap.nitro.d.ts +47 -1
  9. package/lib/typescript/src/specs/RnIap.nitro.d.ts.map +1 -1
  10. package/lib/typescript/src/types.d.ts +350 -4
  11. package/lib/typescript/src/types.d.ts.map +1 -1
  12. package/lib/typescript/src/utils/type-bridge.d.ts.map +1 -1
  13. package/nitrogen/generated/android/c++/JHybridRnIapSpec.cpp +16 -0
  14. package/nitrogen/generated/android/c++/JNitroAvailablePurchasesAndroidOptions.hpp +7 -3
  15. package/nitrogen/generated/android/c++/JNitroAvailablePurchasesIosOptions.hpp +5 -1
  16. package/nitrogen/generated/android/c++/JNitroProduct.hpp +15 -3
  17. package/nitrogen/generated/android/c++/JNitroPurchaseRequest.hpp +8 -0
  18. package/nitrogen/generated/android/c++/JNitroRequestPurchaseAndroid.hpp +11 -3
  19. package/nitrogen/generated/android/c++/JNitroRequestPurchaseIos.hpp +19 -3
  20. package/nitrogen/generated/android/c++/JPromotionalOfferJwsInputIOS.hpp +61 -0
  21. package/nitrogen/generated/android/c++/JSubscriptionProductReplacementParamsAndroid.hpp +63 -0
  22. package/nitrogen/generated/android/c++/JSubscriptionReplacementModeAndroid.hpp +74 -0
  23. package/nitrogen/generated/android/c++/JWinBackOfferInputIOS.hpp +57 -0
  24. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroAvailablePurchasesAndroidOptions.kt +6 -3
  25. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroAvailablePurchasesIosOptions.kt +5 -2
  26. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroProduct.kt +12 -3
  27. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroRequestPurchaseAndroid.kt +6 -3
  28. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroRequestPurchaseIos.kt +12 -3
  29. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/PromotionalOfferJwsInputIOS.kt +39 -0
  30. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/SubscriptionProductReplacementParamsAndroid.kt +39 -0
  31. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/SubscriptionReplacementModeAndroid.kt +26 -0
  32. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/WinBackOfferInputIOS.kt +36 -0
  33. package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Bridge.hpp +57 -0
  34. package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Umbrella.hpp +12 -0
  35. package/nitrogen/generated/ios/c++/HybridRnIapSpecSwift.hpp +12 -0
  36. package/nitrogen/generated/ios/swift/NitroAvailablePurchasesAndroidOptions.swift +31 -1
  37. package/nitrogen/generated/ios/swift/NitroAvailablePurchasesIosOptions.swift +31 -1
  38. package/nitrogen/generated/ios/swift/NitroProduct.swift +91 -1
  39. package/nitrogen/generated/ios/swift/NitroRequestPurchaseAndroid.swift +24 -1
  40. package/nitrogen/generated/ios/swift/NitroRequestPurchaseIos.swift +77 -1
  41. package/nitrogen/generated/ios/swift/PromotionalOfferJwsInputIOS.swift +46 -0
  42. package/nitrogen/generated/ios/swift/SubscriptionProductReplacementParamsAndroid.swift +46 -0
  43. package/nitrogen/generated/ios/swift/SubscriptionReplacementModeAndroid.swift +60 -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 +14 -2
  48. package/nitrogen/generated/shared/c++/NitroRequestPurchaseAndroid.hpp +9 -2
  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++/SubscriptionProductReplacementParamsAndroid.hpp +81 -0
  52. package/nitrogen/generated/shared/c++/SubscriptionReplacementModeAndroid.hpp +96 -0
  53. package/nitrogen/generated/shared/c++/WinBackOfferInputIOS.hpp +75 -0
  54. package/openiap-versions.json +4 -4
  55. package/package.json +1 -1
  56. package/src/specs/RnIap.nitro.ts +50 -0
  57. package/src/types.ts +360 -4
  58. package/src/utils/type-bridge.ts +55 -0
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);
@@ -169,6 +185,11 @@ export interface DiscountDisplayInfoAndroid {
169
185
  percentageDiscount?: (number | null);
170
186
  }
171
187
 
188
+ /**
189
+ * Discount information returned from the store.
190
+ * @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
191
+ * @see https://openiap.dev/docs/types#subscription-offer
192
+ */
172
193
  export interface DiscountIOS {
173
194
  identifier: string;
174
195
  localizedPrice?: (string | null);
@@ -180,6 +201,79 @@ export interface DiscountIOS {
180
201
  type: string;
181
202
  }
182
203
 
204
+ /**
205
+ * Standardized one-time product discount offer.
206
+ * Provides a unified interface for one-time purchase discounts across platforms.
207
+ *
208
+ * Currently supported on Android (Google Play Billing 7.0+).
209
+ * iOS does not support one-time purchase discounts in the same way.
210
+ *
211
+ * @see https://openiap.dev/docs/features/discount
212
+ */
213
+ export interface DiscountOffer {
214
+ /** Currency code (ISO 4217, e.g., "USD") */
215
+ currency: string;
216
+ /**
217
+ * [Android] Fixed discount amount in micro-units.
218
+ * Only present for fixed amount discounts.
219
+ */
220
+ discountAmountMicrosAndroid?: (string | null);
221
+ /** Formatted display price string (e.g., "$4.99") */
222
+ displayPrice: string;
223
+ /** [Android] Formatted discount amount string (e.g., "$5.00 OFF"). */
224
+ formattedDiscountAmountAndroid?: (string | null);
225
+ /**
226
+ * [Android] Original full price in micro-units before discount.
227
+ * Divide by 1,000,000 to get the actual price.
228
+ * Use for displaying strikethrough original price.
229
+ */
230
+ fullPriceMicrosAndroid?: (string | null);
231
+ /**
232
+ * Unique identifier for the offer.
233
+ * - iOS: Not applicable (one-time discounts not supported)
234
+ * - Android: offerId from ProductAndroidOneTimePurchaseOfferDetail
235
+ */
236
+ id?: (string | null);
237
+ /**
238
+ * [Android] Limited quantity information.
239
+ * Contains maximumQuantity and remainingQuantity.
240
+ */
241
+ limitedQuantityInfoAndroid?: (LimitedQuantityInfoAndroid | null);
242
+ /** [Android] List of tags associated with this offer. */
243
+ offerTagsAndroid?: (string[] | null);
244
+ /**
245
+ * [Android] Offer token required for purchase.
246
+ * Must be passed to requestPurchase() when purchasing with this offer.
247
+ */
248
+ offerTokenAndroid?: (string | null);
249
+ /**
250
+ * [Android] Percentage discount (e.g., 33 for 33% off).
251
+ * Only present for percentage-based discounts.
252
+ */
253
+ percentageDiscountAndroid?: (number | null);
254
+ /**
255
+ * [Android] Pre-order details if this is a pre-order offer.
256
+ * Available in Google Play Billing Library 8.1.0+
257
+ */
258
+ preorderDetailsAndroid?: (PreorderDetailsAndroid | null);
259
+ /** Numeric price value */
260
+ price: number;
261
+ /** [Android] Rental details if this is a rental offer. */
262
+ rentalDetailsAndroid?: (RentalDetailsAndroid | null);
263
+ /** Type of discount offer */
264
+ type: DiscountOfferType;
265
+ /**
266
+ * [Android] Valid time window for the offer.
267
+ * Contains startTimeMillis and endTimeMillis.
268
+ */
269
+ validTimeWindowAndroid?: (ValidTimeWindowAndroid | null);
270
+ }
271
+
272
+ /**
273
+ * iOS DiscountOffer (output type).
274
+ * @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
275
+ * @see https://openiap.dev/docs/types#subscription-offer
276
+ */
183
277
  export interface DiscountOfferIOS {
184
278
  /** Discount identifier */
185
279
  identifier: string;
@@ -206,6 +300,12 @@ export interface DiscountOfferInputIOS {
206
300
  timestamp: number;
207
301
  }
208
302
 
303
+ /**
304
+ * Discount offer type enumeration.
305
+ * Categorizes the type of discount or promotional offer.
306
+ */
307
+ export type DiscountOfferType = 'introductory' | 'promotional' | 'one-time';
308
+
209
309
  export interface EntitlementIOS {
210
310
  jsonRepresentation: string;
211
311
  sku: string;
@@ -441,7 +541,7 @@ export interface Mutation {
441
541
  * promoted products can be purchased directly via the standard purchase flow.
442
542
  * @deprecated Use promotedProductListenerIOS + requestPurchase instead
443
543
  */
444
- requestPurchaseOnPromotedProductIOS: boolean;
544
+ requestPurchaseOnPromotedProductIOS: Promise<boolean>;
445
545
  /** Restore completed purchases across platforms */
446
546
  restorePurchases: Promise<void>;
447
547
  /**
@@ -517,6 +617,12 @@ export type MutationVerifyPurchaseArgs = VerifyPurchaseProps;
517
617
 
518
618
  export type MutationVerifyPurchaseWithProviderArgs = VerifyPurchaseWithProviderProps;
519
619
 
620
+ /**
621
+ * Payment mode for subscription offers.
622
+ * Determines how the user pays during the offer period.
623
+ */
624
+ export type PaymentMode = 'free-trial' | 'pay-as-you-go' | 'pay-up-front' | 'unknown';
625
+
520
626
  export type PaymentModeIOS = 'empty' | 'free-trial' | 'pay-as-you-go' | 'pay-up-front';
521
627
 
522
628
  /**
@@ -555,6 +661,12 @@ export interface ProductAndroid extends ProductCommon {
555
661
  currency: string;
556
662
  debugDescription?: (string | null);
557
663
  description: string;
664
+ /**
665
+ * Standardized discount offers for one-time products.
666
+ * Cross-platform type with Android-specific fields using suffix.
667
+ * @see https://openiap.dev/docs/types#discount-offer
668
+ */
669
+ discountOffers?: (DiscountOffer[] | null);
558
670
  displayName?: (string | null);
559
671
  displayPrice: string;
560
672
  id: string;
@@ -562,18 +674,40 @@ export interface ProductAndroid extends ProductCommon {
562
674
  /**
563
675
  * One-time purchase offer details including discounts (Android)
564
676
  * Returns all eligible offers. Available in Google Play Billing Library 7.0+
677
+ * @deprecated Use discountOffers instead for cross-platform compatibility.
678
+ * @deprecated Use discountOffers instead
565
679
  */
566
680
  oneTimePurchaseOfferDetailsAndroid?: (ProductAndroidOneTimePurchaseOfferDetail[] | null);
567
681
  platform: 'android';
568
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);
691
+ /**
692
+ * @deprecated Use subscriptionOffers instead for cross-platform compatibility.
693
+ * @deprecated Use subscriptionOffers instead
694
+ */
569
695
  subscriptionOfferDetailsAndroid?: (ProductSubscriptionAndroidOfferDetails[] | null);
696
+ /**
697
+ * Standardized subscription offers.
698
+ * Cross-platform type with Android-specific fields using suffix.
699
+ * @see https://openiap.dev/docs/types#subscription-offer
700
+ */
701
+ subscriptionOffers?: (SubscriptionOffer[] | null);
570
702
  title: string;
571
703
  type: 'in-app';
572
704
  }
573
705
 
574
706
  /**
575
- * One-time purchase offer details (Android)
707
+ * One-time purchase offer details (Android).
576
708
  * Available in Google Play Billing Library 7.0+
709
+ * @deprecated Use the standardized DiscountOffer type instead for cross-platform compatibility.
710
+ * @see https://openiap.dev/docs/types#discount-offer
577
711
  */
578
712
  export interface ProductAndroidOneTimePurchaseOfferDetail {
579
713
  /**
@@ -633,7 +767,18 @@ export interface ProductIOS extends ProductCommon {
633
767
  jsonRepresentationIOS: string;
634
768
  platform: 'ios';
635
769
  price?: (number | null);
770
+ /**
771
+ * @deprecated Use subscriptionOffers instead for cross-platform compatibility.
772
+ * @deprecated Use subscriptionOffers instead
773
+ */
636
774
  subscriptionInfoIOS?: (SubscriptionInfoIOS | null);
775
+ /**
776
+ * Standardized subscription offers.
777
+ * Cross-platform type with iOS-specific fields using suffix.
778
+ * Note: iOS does not support one-time product discounts.
779
+ * @see https://openiap.dev/docs/types#subscription-offer
780
+ */
781
+ subscriptionOffers?: (SubscriptionOffer[] | null);
637
782
  title: string;
638
783
  type: 'in-app';
639
784
  typeIOS: ProductTypeIOS;
@@ -648,12 +793,26 @@ export interface ProductRequest {
648
793
  type?: (ProductQueryType | null);
649
794
  }
650
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
+
651
804
  export type ProductSubscription = ProductSubscriptionAndroid | ProductSubscriptionIOS;
652
805
 
653
806
  export interface ProductSubscriptionAndroid extends ProductCommon {
654
807
  currency: string;
655
808
  debugDescription?: (string | null);
656
809
  description: string;
810
+ /**
811
+ * Standardized discount offers for one-time products.
812
+ * Cross-platform type with Android-specific fields using suffix.
813
+ * @see https://openiap.dev/docs/types#discount-offer
814
+ */
815
+ discountOffers?: (DiscountOffer[] | null);
657
816
  displayName?: (string | null);
658
817
  displayPrice: string;
659
818
  id: string;
@@ -661,15 +820,40 @@ export interface ProductSubscriptionAndroid extends ProductCommon {
661
820
  /**
662
821
  * One-time purchase offer details including discounts (Android)
663
822
  * Returns all eligible offers. Available in Google Play Billing Library 7.0+
823
+ * @deprecated Use discountOffers instead for cross-platform compatibility.
824
+ * @deprecated Use discountOffers instead
664
825
  */
665
826
  oneTimePurchaseOfferDetailsAndroid?: (ProductAndroidOneTimePurchaseOfferDetail[] | null);
666
827
  platform: 'android';
667
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);
837
+ /**
838
+ * @deprecated Use subscriptionOffers instead for cross-platform compatibility.
839
+ * @deprecated Use subscriptionOffers instead
840
+ */
668
841
  subscriptionOfferDetailsAndroid: ProductSubscriptionAndroidOfferDetails[];
842
+ /**
843
+ * Standardized subscription offers.
844
+ * Cross-platform type with Android-specific fields using suffix.
845
+ * @see https://openiap.dev/docs/types#subscription-offer
846
+ */
847
+ subscriptionOffers: SubscriptionOffer[];
669
848
  title: string;
670
849
  type: 'subs';
671
850
  }
672
851
 
852
+ /**
853
+ * Subscription offer details (Android).
854
+ * @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
855
+ * @see https://openiap.dev/docs/types#subscription-offer
856
+ */
673
857
  export interface ProductSubscriptionAndroidOfferDetails {
674
858
  basePlanId: string;
675
859
  offerId?: (string | null);
@@ -682,6 +866,10 @@ export interface ProductSubscriptionIOS extends ProductCommon {
682
866
  currency: string;
683
867
  debugDescription?: (string | null);
684
868
  description: string;
869
+ /**
870
+ * @deprecated Use subscriptionOffers instead for cross-platform compatibility.
871
+ * @deprecated Use subscriptionOffers instead
872
+ */
685
873
  discountsIOS?: (DiscountIOS[] | null);
686
874
  displayName?: (string | null);
687
875
  displayNameIOS: string;
@@ -696,7 +884,17 @@ export interface ProductSubscriptionIOS extends ProductCommon {
696
884
  jsonRepresentationIOS: string;
697
885
  platform: 'ios';
698
886
  price?: (number | null);
887
+ /**
888
+ * @deprecated Use subscriptionOffers instead for cross-platform compatibility.
889
+ * @deprecated Use subscriptionOffers instead
890
+ */
699
891
  subscriptionInfoIOS?: (SubscriptionInfoIOS | null);
892
+ /**
893
+ * Standardized subscription offers.
894
+ * Cross-platform type with iOS-specific fields using suffix.
895
+ * @see https://openiap.dev/docs/types#subscription-offer
896
+ */
897
+ subscriptionOffers?: (SubscriptionOffer[] | null);
700
898
  subscriptionPeriodNumberIOS?: (string | null);
701
899
  subscriptionPeriodUnitIOS?: (SubscriptionPeriodIOS | null);
702
900
  title: string;
@@ -708,6 +906,23 @@ export type ProductType = 'in-app' | 'subs';
708
906
 
709
907
  export type ProductTypeIOS = 'consumable' | 'non-consumable' | 'auto-renewable-subscription' | 'non-renewing-subscription';
710
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
+
711
926
  export type Purchase = PurchaseAndroid | PurchaseIOS;
712
927
 
713
928
  export interface PurchaseAndroid extends PurchaseCommon {
@@ -822,6 +1037,13 @@ export interface PurchaseOfferIOS {
822
1037
  export interface PurchaseOptions {
823
1038
  /** Also emit results through the iOS event listeners */
824
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);
825
1047
  /** Limit to currently active items on iOS */
826
1048
  onlyIncludeActiveItemsIOS?: (boolean | null);
827
1049
  }
@@ -998,7 +1220,10 @@ export interface RequestPurchaseIosProps {
998
1220
  quantity?: (number | null);
999
1221
  /** Product SKU */
1000
1222
  sku: string;
1001
- /** Discount offer to apply */
1223
+ /**
1224
+ * Promotional offer to apply (subscriptions only, ignored for one-time purchases).
1225
+ * iOS only supports promotional offers for auto-renewable subscriptions.
1226
+ */
1002
1227
  withOffer?: (DiscountOfferInputIOS | null);
1003
1228
  }
1004
1229
 
@@ -1080,8 +1305,32 @@ export interface RequestSubscriptionIosProps {
1080
1305
  advancedCommerceData?: (string | null);
1081
1306
  andDangerouslyFinishTransactionAutomatically?: (boolean | null);
1082
1307
  appAccountToken?: (string | null);
1308
+ /**
1309
+ * Override introductory offer eligibility (iOS 15+, WWDC 2025).
1310
+ * Set to true to indicate the user is eligible for introductory offer,
1311
+ * or false to indicate they are not. When nil, the system determines eligibility.
1312
+ * Back-deployed to iOS 15.
1313
+ */
1314
+ introductoryOfferEligibility?: (boolean | null);
1315
+ /**
1316
+ * JWS promotional offer (iOS 15+, WWDC 2025).
1317
+ * New signature format using compact JWS string for promotional offers.
1318
+ * Back-deployed to iOS 15.
1319
+ */
1320
+ promotionalOfferJWS?: (PromotionalOfferJwsInputIOS | null);
1083
1321
  quantity?: (number | null);
1084
1322
  sku: string;
1323
+ /**
1324
+ * Win-back offer to apply (iOS 18+)
1325
+ * Used to re-engage churned subscribers with a discount or free trial.
1326
+ * The offer is available when the customer is eligible and can be discovered
1327
+ * via StoreKit Message (automatic) or subscription offer APIs.
1328
+ */
1329
+ winBackOffer?: (WinBackOfferInputIOS | null);
1330
+ /**
1331
+ * Promotional offer to apply for subscription purchases.
1332
+ * Requires server-signed offer with nonce, timestamp, keyId, and signature.
1333
+ */
1085
1334
  withOffer?: (DiscountOfferInputIOS | null);
1086
1335
  }
1087
1336
 
@@ -1137,6 +1386,12 @@ export interface RequestVerifyPurchaseWithIapkitResult {
1137
1386
  store: IapStore;
1138
1387
  }
1139
1388
 
1389
+ /**
1390
+ * Sub-response codes for more granular purchase error information (Android)
1391
+ * Available in Google Play Billing Library 8.0.0+
1392
+ */
1393
+ export type SubResponseCodeAndroid = 'no-applicable-sub-response-code' | 'payment-declined-due-to-insufficient-funds' | 'user-ineligible';
1394
+
1140
1395
  export interface Subscription {
1141
1396
  /**
1142
1397
  * Fires when a user selects developer billing in the External Payments flow (Android only)
@@ -1167,6 +1422,86 @@ export interface SubscriptionInfoIOS {
1167
1422
  subscriptionPeriod: SubscriptionPeriodValueIOS;
1168
1423
  }
1169
1424
 
1425
+ /**
1426
+ * Standardized subscription discount/promotional offer.
1427
+ * Provides a unified interface for subscription offers across iOS and Android.
1428
+ *
1429
+ * Both platforms support subscription offers with different implementations:
1430
+ * - iOS: Introductory offers, promotional offers with server-side signatures
1431
+ * - Android: Offer tokens with pricing phases
1432
+ *
1433
+ * @see https://openiap.dev/docs/types/ios#discount-offer
1434
+ * @see https://openiap.dev/docs/types/android#subscription-offer
1435
+ */
1436
+ export interface SubscriptionOffer {
1437
+ /**
1438
+ * [Android] Base plan identifier.
1439
+ * Identifies which base plan this offer belongs to.
1440
+ */
1441
+ basePlanIdAndroid?: (string | null);
1442
+ /** Currency code (ISO 4217, e.g., "USD") */
1443
+ currency?: (string | null);
1444
+ /** Formatted display price string (e.g., "$9.99/month") */
1445
+ displayPrice: string;
1446
+ /**
1447
+ * Unique identifier for the offer.
1448
+ * - iOS: Discount identifier from App Store Connect
1449
+ * - Android: offerId from ProductSubscriptionAndroidOfferDetails
1450
+ */
1451
+ id: string;
1452
+ /**
1453
+ * [iOS] Key identifier for signature validation.
1454
+ * Used with server-side signature generation for promotional offers.
1455
+ */
1456
+ keyIdentifierIOS?: (string | null);
1457
+ /** [iOS] Localized price string. */
1458
+ localizedPriceIOS?: (string | null);
1459
+ /**
1460
+ * [iOS] Cryptographic nonce (UUID) for signature validation.
1461
+ * Must be generated server-side for each purchase attempt.
1462
+ */
1463
+ nonceIOS?: (string | null);
1464
+ /** [iOS] Number of billing periods for this discount. */
1465
+ numberOfPeriodsIOS?: (number | null);
1466
+ /** [Android] List of tags associated with this offer. */
1467
+ offerTagsAndroid?: (string[] | null);
1468
+ /**
1469
+ * [Android] Offer token required for purchase.
1470
+ * Must be passed to requestPurchase() when purchasing with this offer.
1471
+ */
1472
+ offerTokenAndroid?: (string | null);
1473
+ /** Payment mode during the offer period */
1474
+ paymentMode?: (PaymentMode | null);
1475
+ /** Subscription period for this offer */
1476
+ period?: (SubscriptionPeriod | null);
1477
+ /** Number of periods the offer applies */
1478
+ periodCount?: (number | null);
1479
+ /** Numeric price value */
1480
+ price: number;
1481
+ /**
1482
+ * [Android] Pricing phases for this subscription offer.
1483
+ * Contains detailed pricing information for each phase (trial, intro, regular).
1484
+ */
1485
+ pricingPhasesAndroid?: (PricingPhasesAndroid | null);
1486
+ /**
1487
+ * [iOS] Server-generated signature for promotional offer validation.
1488
+ * Required when applying promotional offers on iOS.
1489
+ */
1490
+ signatureIOS?: (string | null);
1491
+ /**
1492
+ * [iOS] Timestamp when the signature was generated.
1493
+ * Used for signature validation.
1494
+ */
1495
+ timestampIOS?: (number | null);
1496
+ /** Type of subscription offer (Introductory or Promotional) */
1497
+ type: DiscountOfferType;
1498
+ }
1499
+
1500
+ /**
1501
+ * iOS subscription offer details.
1502
+ * @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
1503
+ * @see https://openiap.dev/docs/types#subscription-offer
1504
+ */
1170
1505
  export interface SubscriptionOfferIOS {
1171
1506
  displayPrice: string;
1172
1507
  id: string;
@@ -1177,10 +1512,21 @@ export interface SubscriptionOfferIOS {
1177
1512
  type: SubscriptionOfferTypeIOS;
1178
1513
  }
1179
1514
 
1180
- export type SubscriptionOfferTypeIOS = 'introductory' | 'promotional';
1515
+ export type SubscriptionOfferTypeIOS = 'introductory' | 'promotional' | 'win-back';
1516
+
1517
+ /** Subscription period value combining unit and count. */
1518
+ export interface SubscriptionPeriod {
1519
+ /** The period unit (day, week, month, year) */
1520
+ unit: SubscriptionPeriodUnit;
1521
+ /** The number of units (e.g., 1 for monthly, 3 for quarterly) */
1522
+ value: number;
1523
+ }
1181
1524
 
1182
1525
  export type SubscriptionPeriodIOS = 'day' | 'week' | 'month' | 'year' | 'empty';
1183
1526
 
1527
+ /** Subscription period unit for cross-platform use. */
1528
+ export type SubscriptionPeriodUnit = 'day' | 'week' | 'month' | 'year' | 'unknown';
1529
+
1184
1530
  export interface SubscriptionPeriodValueIOS {
1185
1531
  unit: SubscriptionPeriodIOS;
1186
1532
  value: number;
@@ -1366,6 +1712,16 @@ export interface VerifyPurchaseWithProviderResult {
1366
1712
 
1367
1713
  export type VoidResult = void;
1368
1714
 
1715
+ /**
1716
+ * Win-back offer input for iOS 18+ (StoreKit 2)
1717
+ * Win-back offers are used to re-engage churned subscribers.
1718
+ * The offer is automatically presented via StoreKit Message when eligible,
1719
+ * or can be applied programmatically during purchase.
1720
+ */
1721
+ export interface WinBackOfferInputIOS {
1722
+ /** The win-back offer ID from App Store Connect */
1723
+ offerId: string;
1724
+ }
1369
1725
  // -- Query helper types (auto-generated)
1370
1726
  export type QueryArgsMap = {
1371
1727
  canPresentExternalPurchaseNoticeIOS: never;
@@ -277,6 +277,30 @@ export function convertNitroProductToProduct(
277
277
  iosProduct.discountsIOS = null;
278
278
  }
279
279
 
280
+ // Parse standardized subscriptionOffers (cross-platform, OpenIAP 1.3.10+)
281
+ if (nitroProduct.subscriptionOffers) {
282
+ try {
283
+ iosProduct.subscriptionOffers = JSON.parse(
284
+ nitroProduct.subscriptionOffers,
285
+ );
286
+ } catch {
287
+ iosProduct.subscriptionOffers = null;
288
+ }
289
+ } else {
290
+ iosProduct.subscriptionOffers = null;
291
+ }
292
+
293
+ // Parse standardized discountOffers (cross-platform, OpenIAP 1.3.10+)
294
+ if (nitroProduct.discountOffers) {
295
+ try {
296
+ iosProduct.discountOffers = JSON.parse(nitroProduct.discountOffers);
297
+ } catch {
298
+ iosProduct.discountOffers = null;
299
+ }
300
+ } else {
301
+ iosProduct.discountOffers = null;
302
+ }
303
+
280
304
  return iosProduct as Product;
281
305
  }
282
306
 
@@ -288,12 +312,43 @@ export function convertNitroProductToProduct(
288
312
  subscriptionOfferDetailsAndroid: parseSubscriptionOffers(
289
313
  nitroProduct.subscriptionOfferDetailsAndroid,
290
314
  ),
315
+ // Product status (Billing Library 8.0+, OpenIAP 1.3.14+)
316
+ productStatusAndroid: nitroProduct.productStatusAndroid ?? null,
291
317
  };
292
318
 
319
+ // Parse standardized subscriptionOffers (cross-platform, OpenIAP 1.3.10+)
320
+ if (nitroProduct.subscriptionOffers) {
321
+ try {
322
+ androidProduct.subscriptionOffers = JSON.parse(
323
+ nitroProduct.subscriptionOffers,
324
+ );
325
+ } catch {
326
+ androidProduct.subscriptionOffers = null;
327
+ }
328
+ } else {
329
+ androidProduct.subscriptionOffers = null;
330
+ }
331
+
332
+ // Parse standardized discountOffers (cross-platform, OpenIAP 1.3.10+)
333
+ if (nitroProduct.discountOffers) {
334
+ try {
335
+ androidProduct.discountOffers = JSON.parse(nitroProduct.discountOffers);
336
+ } catch {
337
+ androidProduct.discountOffers = null;
338
+ }
339
+ } else {
340
+ androidProduct.discountOffers = null;
341
+ }
342
+
293
343
  if (type === PRODUCT_TYPE_SUBS) {
344
+ // Ensure subscriptionOfferDetailsAndroid is always an array for subscriptions
294
345
  if (!Array.isArray(androidProduct.subscriptionOfferDetailsAndroid)) {
295
346
  androidProduct.subscriptionOfferDetailsAndroid = [];
296
347
  }
348
+ // Ensure subscriptionOffers is always an array for subscriptions (non-nullable in ProductSubscriptionAndroid)
349
+ if (!Array.isArray(androidProduct.subscriptionOffers)) {
350
+ androidProduct.subscriptionOffers = [];
351
+ }
297
352
  }
298
353
 
299
354
  return androidProduct as Product;