@@ -121,6 +121,9 @@ gentype.ProductCommon parseProductFromNative(
121121 subscriptionInfoIOS: _parseSubscriptionInfoIOS (
122122 json['subscriptionInfoIOS' ] ?? json['subscription' ],
123123 ),
124+ subscriptionOffers: _parseSubscriptionOffersIOS (
125+ json['subscriptionOffers' ],
126+ ),
124127 subscriptionPeriodNumberIOS:
125128 json['subscriptionPeriodNumberIOS' ]? .toString (),
126129 subscriptionPeriodUnitIOS: _parseSubscriptionPeriod (
@@ -635,6 +638,103 @@ gentype.SubscriptionInfoIOS? _parseSubscriptionInfoIOS(dynamic value) {
635638 return null ;
636639}
637640
641+ /// Parse standardized SubscriptionOffer list from iOS native data.
642+ List <gentype.SubscriptionOffer >? _parseSubscriptionOffersIOS (dynamic json) {
643+ if (json == null ) return null ;
644+ if (json is ! List ) return null ;
645+
646+ final offers = < gentype.SubscriptionOffer > [];
647+ for (final item in json) {
648+ final map = normalizeDynamicMap (item);
649+ if (map == null ) continue ;
650+
651+ // Parse payment mode
652+ gentype.PaymentMode ? paymentMode;
653+ final paymentModeRaw = map['paymentMode' ]? .toString ().toUpperCase ();
654+ if (paymentModeRaw != null ) {
655+ try {
656+ paymentMode = gentype.PaymentMode .fromJson (paymentModeRaw);
657+ } catch (_) {
658+ // Fallback mapping
659+ switch (paymentModeRaw) {
660+ case 'FREE_TRIAL' :
661+ case 'FREETRIAL' :
662+ paymentMode = gentype.PaymentMode .FreeTrial ;
663+ break ;
664+ case 'PAY_UP_FRONT' :
665+ case 'PAYUPFRONT' :
666+ paymentMode = gentype.PaymentMode .PayUpFront ;
667+ break ;
668+ case 'PAY_AS_YOU_GO' :
669+ case 'PAYASYOUGO' :
670+ paymentMode = gentype.PaymentMode .PayAsYouGo ;
671+ break ;
672+ }
673+ }
674+ }
675+
676+ // Parse offer type
677+ gentype.DiscountOfferType type = gentype.DiscountOfferType .Introductory ;
678+ final typeRaw = map['type' ]? .toString ().toUpperCase ();
679+ if (typeRaw != null ) {
680+ try {
681+ type = gentype.DiscountOfferType .fromJson (typeRaw);
682+ } catch (_) {
683+ switch (typeRaw) {
684+ case 'PROMOTIONAL' :
685+ case 'WIN_BACK' :
686+ case 'WINBACK' :
687+ case 'CODE' :
688+ type = gentype.DiscountOfferType .Promotional ;
689+ break ;
690+ case 'ONE_TIME' :
691+ case 'ONETIME' :
692+ type = gentype.DiscountOfferType .OneTime ;
693+ break ;
694+ }
695+ }
696+ }
697+
698+ // Parse period
699+ gentype.SubscriptionPeriod ? period;
700+ final periodMap = normalizeDynamicMap (map['period' ]);
701+ if (periodMap != null ) {
702+ final unitRaw = periodMap['unit' ]? .toString ().toUpperCase ();
703+ final value = (periodMap['value' ] as num ? )? .toInt () ?? 1 ;
704+ gentype.SubscriptionPeriodUnit ? unit;
705+ if (unitRaw != null ) {
706+ try {
707+ unit = gentype.SubscriptionPeriodUnit .fromJson (unitRaw);
708+ } catch (_) {
709+ // ignore
710+ }
711+ }
712+ if (unit != null ) {
713+ period = gentype.SubscriptionPeriod (unit: unit, value: value);
714+ }
715+ }
716+
717+ offers.add (gentype.SubscriptionOffer (
718+ id: map['id' ]? .toString () ?? '' ,
719+ displayPrice: map['displayPrice' ]? .toString () ?? '' ,
720+ price: (map['price' ] as num ? )? .toDouble () ?? 0 ,
721+ currency: map['currency' ]? .toString (),
722+ type: type,
723+ paymentMode: paymentMode,
724+ period: period,
725+ periodCount: (map['periodCount' ] as num ? )? .toInt (),
726+ keyIdentifierIOS: map['keyIdentifierIOS' ]? .toString (),
727+ nonceIOS: map['nonceIOS' ]? .toString (),
728+ signatureIOS: map['signatureIOS' ]? .toString (),
729+ timestampIOS: (map['timestampIOS' ] as num ? )? .toDouble (),
730+ numberOfPeriodsIOS: (map['numberOfPeriodsIOS' ] as num ? )? .toInt (),
731+ localizedPriceIOS: map['localizedPriceIOS' ]? .toString (),
732+ ));
733+ }
734+
735+ return offers.isEmpty ? null : offers;
736+ }
737+
638738/// Parse standardized SubscriptionOffer list from subscription offer details.
639739/// Converts legacy subscriptionOfferDetailsAndroid to cross-platform SubscriptionOffer.
640740List <gentype.SubscriptionOffer > _parseSubscriptionOffers (
0 commit comments