@@ -172,6 +172,19 @@ public async Task Release(string customerId, string subscriptionId)
172172 return null ;
173173 }
174174
175+ // Detect callers who fetched the subscription without expanding "discounts".
176+ // Stripe.NET deserializes the unexpanded ID-array form as a list of null entries,
177+ // which would silently drop pre-existing discounts from Phase 2 (the bug PM-35909 fixed).
178+ if ( subscription . Discounts is { Count : > 0 } && subscription . Discounts . Any ( d => d == null ) )
179+ {
180+ logger . LogError (
181+ "Subscription ({SubscriptionId}) was loaded without expanding 'discounts'; " +
182+ "{Count} pre-existing discount(s) would be silently dropped from Phase 2. " +
183+ "Caller must include \" discounts\" in the Stripe Expand list." ,
184+ subscription . Id , subscription . DiscountIds ? . Count ?? 0 ) ;
185+ return null ;
186+ }
187+
175188 try
176189 {
177190 SubscriberId subscriberId = subscription ;
@@ -190,7 +203,7 @@ public async Task Release(string customerId, string subscriptionId)
190203 catch ( Exception ex )
191204 {
192205 logger . LogError ( ex ,
193- "Failed to resolve subscriber type for subscription ({SubscriptionId}), cannot determine price migration path" ,
206+ "Failed to resolve Phase 2 options for subscription ({SubscriptionId}), cannot determine price migration path" ,
194207 subscription . Id ) ;
195208 return null ;
196209 }
@@ -244,12 +257,21 @@ public async Task Release(string customerId, string subscriptionId)
244257 return null ;
245258 }
246259
260+ var discounts = subscription . Discounts ?
261+ . Select ( d => new SubscriptionSchedulePhaseDiscountOptions { Coupon = d . Coupon . Id } )
262+ . ToList ( ) ?? [ ] ;
263+
264+ discounts . Add ( new SubscriptionSchedulePhaseDiscountOptions
265+ {
266+ Coupon = CouponIDs . Milestone2SubscriptionDiscount
267+ } ) ;
268+
247269 return new SubscriptionSchedulePhaseOptions
248270 {
249271 StartDate = startDate ,
250272 EndDate = startDate . Value . AddYears ( 1 ) ,
251273 Items = items ,
252- Discounts = [ new ( ) { Coupon = CouponIDs . Milestone2SubscriptionDiscount } ] ,
274+ Discounts = discounts ,
253275 ProrationBehavior = ProrationBehavior . None
254276 } ;
255277 }
@@ -291,12 +313,17 @@ public async Task Release(string customerId, string subscriptionId)
291313 } ) ;
292314 }
293315
294- var discounts = oldPlan . Type == PlanType . FamiliesAnnually2019
295- ? new List < SubscriptionSchedulePhaseDiscountOptions >
316+ var discounts = subscription . Discounts ?
317+ . Select ( d => new SubscriptionSchedulePhaseDiscountOptions { Coupon = d . Coupon . Id } )
318+ . ToList ( ) ?? [ ] ;
319+
320+ if ( oldPlan . Type == PlanType . FamiliesAnnually2019 )
321+ {
322+ discounts . Add ( new SubscriptionSchedulePhaseDiscountOptions
296323 {
297- new ( ) { Coupon = CouponIDs . Milestone3SubscriptionDiscount }
298- }
299- : null ;
324+ Coupon = CouponIDs . Milestone3SubscriptionDiscount
325+ } ) ;
326+ }
300327
301328 var startDate = subscription . GetCurrentPeriodEnd ( ) ;
302329 if ( startDate == null )
@@ -312,7 +339,7 @@ public async Task Release(string customerId, string subscriptionId)
312339 StartDate = startDate ,
313340 EndDate = startDate . Value . AddYears ( 1 ) ,
314341 Items = items ,
315- Discounts = discounts ,
342+ Discounts = discounts . Count > 0 ? discounts : null ,
316343 ProrationBehavior = ProrationBehavior . None
317344 } ;
318345 }
0 commit comments