@@ -316,17 +316,33 @@ def get_schedule(self, owner: Owner):
316
316
return stripe .SubscriptionSchedule .retrieve (subscription_schedule_id )
317
317
318
318
@_log_stripe_error
319
- def modify_subscription (self , owner , desired_plan ):
319
+ def modify_subscription (self , owner : Owner , desired_plan : dict ):
320
+ desired_plan_info = Plan .objects .filter (name = desired_plan ["value" ]).first ()
321
+ if not desired_plan_info :
322
+ log .error (
323
+ f"Plan { desired_plan ['value' ]} not found" ,
324
+ extra = dict (owner_id = owner .ownerid ),
325
+ )
326
+ return
327
+
320
328
subscription = stripe .Subscription .retrieve (owner .stripe_subscription_id )
321
- proration_behavior = self ._get_proration_params (owner , desired_plan )
329
+ proration_behavior = self ._get_proration_params (
330
+ owner ,
331
+ desired_plan_info = desired_plan_info ,
332
+ desired_quantity = desired_plan ["quantity" ],
333
+ )
322
334
subscription_schedule_id = subscription .schedule
323
335
324
336
# proration_behavior indicates whether we immediately invoice a user or not. We only immediately
325
337
# invoice a user if the user increases the number of seats or if the plan changes from monthly to yearly.
326
338
# An increase in seats and/or plan implies the user is upgrading, hence 'is_upgrading' is a consequence
327
339
# of proration_behavior providing an invoice, in this case, != "none"
328
340
# TODO: change this to "self._is_upgrading_seats(owner, desired_plan) or self._is_extending_term(owner, desired_plan)"
329
- is_upgrading = True if proration_behavior != "none" else False
341
+ is_upgrading = (
342
+ True
343
+ if proration_behavior != "none" and desired_plan_info .stripe_id
344
+ else False
345
+ )
330
346
331
347
# Divide logic bw immediate updates and scheduled updates
332
348
# Immediate updates: when user upgrades seats or plan
@@ -335,15 +351,6 @@ def modify_subscription(self, owner, desired_plan):
335
351
# Scheduled updates: when the user decreases seats or plan
336
352
# If the user is not in a schedule, create a schedule
337
353
# If the user is in a schedule, update the existing schedule
338
-
339
- plan = Plan .objects .filter (name = desired_plan ["value" ]).first ()
340
- if not plan or not plan .stripe_id :
341
- log .error (
342
- f"Plan { desired_plan ['value' ]} not found" ,
343
- extra = dict (owner_id = owner .ownerid ),
344
- )
345
- return
346
-
347
354
if is_upgrading :
348
355
if subscription_schedule_id :
349
356
log .info (
@@ -360,7 +367,7 @@ def modify_subscription(self, owner, desired_plan):
360
367
items = [
361
368
{
362
369
"id" : subscription ["items" ]["data" ][0 ]["id" ],
363
- "plan" : plan .stripe_id ,
370
+ "plan" : desired_plan_info .stripe_id ,
364
371
"quantity" : desired_plan ["quantity" ],
365
372
}
366
373
],
@@ -404,7 +411,11 @@ def modify_subscription(self, owner, desired_plan):
404
411
)
405
412
406
413
def _modify_subscription_schedule (
407
- self , owner : Owner , subscription , subscription_schedule_id , desired_plan
414
+ self ,
415
+ owner : Owner ,
416
+ subscription : stripe .Subscription ,
417
+ subscription_schedule_id : str ,
418
+ desired_plan : dict ,
408
419
):
409
420
current_subscription_start_date = subscription ["current_period_start" ]
410
421
current_subscription_end_date = subscription ["current_period_end" ]
@@ -453,20 +464,18 @@ def _modify_subscription_schedule(
453
464
metadata = self ._get_checkout_session_and_subscription_metadata (owner ),
454
465
)
455
466
456
- def _is_upgrading_seats (self , owner : Owner , desired_plan : dict ) -> bool :
467
+ def _is_upgrading_seats (self , owner : Owner , desired_quantity : int ) -> bool :
457
468
"""
458
469
Returns `True` if purchasing more seats.
459
470
"""
460
- return bool (
461
- owner .plan_user_count and owner .plan_user_count < desired_plan ["quantity" ]
462
- )
471
+ return bool (owner .plan_user_count and owner .plan_user_count < desired_quantity )
463
472
464
- def _is_extending_term (self , owner : Owner , desired_plan : dict ) -> bool :
473
+ def _is_extending_term (
474
+ self , current_plan_info : Plan , desired_plan_info : Plan
475
+ ) -> bool :
465
476
"""
466
477
Returns `True` if switching from monthly to yearly plan.
467
478
"""
468
- current_plan_info = Plan .objects .get (name = owner .plan )
469
- desired_plan_info = Plan .objects .get (name = desired_plan ["value" ])
470
479
471
480
return bool (
472
481
current_plan_info
@@ -475,23 +484,24 @@ def _is_extending_term(self, owner: Owner, desired_plan: dict) -> bool:
475
484
and desired_plan_info .billing_rate == PlanBillingRate .YEARLY .value
476
485
)
477
486
478
- def _is_similar_plan (self , owner : Owner , desired_plan : dict ) -> bool :
487
+ def _is_similar_plan (
488
+ self ,
489
+ owner : Owner ,
490
+ current_plan_info : Plan ,
491
+ desired_plan_info : Plan ,
492
+ desired_quantity : int ,
493
+ ) -> bool :
479
494
"""
480
495
Returns `True` if switching to a plan with similar term and seats.
481
496
"""
482
- current_plan_info = Plan .objects .select_related ("tier" ).get (name = owner .plan )
483
- desired_plan_info = Plan .objects .select_related ("tier" ).get (
484
- name = desired_plan ["value" ]
485
- )
486
-
487
497
is_same_term = (
488
498
current_plan_info
489
499
and desired_plan_info
490
500
and current_plan_info .billing_rate == desired_plan_info .billing_rate
491
501
)
492
502
493
503
is_same_seats = (
494
- owner .plan_user_count and owner .plan_user_count == desired_plan [ "quantity" ]
504
+ owner .plan_user_count and owner .plan_user_count == desired_quantity
495
505
)
496
506
# If from PRO to TEAM, then not a similar plan
497
507
if (
@@ -508,25 +518,35 @@ def _is_similar_plan(self, owner: Owner, desired_plan: dict) -> bool:
508
518
509
519
return bool (is_same_term and is_same_seats )
510
520
511
- def _get_proration_params (self , owner : Owner , desired_plan : dict ) -> str :
521
+ def _get_proration_params (
522
+ self , owner : Owner , desired_plan_info : Plan , desired_quantity : int
523
+ ) -> str :
524
+ current_plan_info = Plan .objects .select_related ("tier" ).get (name = owner .plan )
512
525
if (
513
- self ._is_upgrading_seats (owner , desired_plan )
514
- or self ._is_extending_term (owner , desired_plan )
515
- or self ._is_similar_plan (owner , desired_plan )
526
+ self ._is_upgrading_seats (owner = owner , desired_quantity = desired_quantity )
527
+ or self ._is_extending_term (
528
+ current_plan_info = current_plan_info , desired_plan_info = desired_plan_info
529
+ )
530
+ or self ._is_similar_plan (
531
+ owner = owner ,
532
+ current_plan_info = current_plan_info ,
533
+ desired_plan_info = desired_plan_info ,
534
+ desired_quantity = desired_quantity ,
535
+ )
516
536
):
517
537
return "always_invoice"
518
538
else :
519
539
return "none"
520
540
521
- def _get_success_and_cancel_url (self , owner ):
541
+ def _get_success_and_cancel_url (self , owner : Owner ):
522
542
short_services = {"github" : "gh" , "bitbucket" : "bb" , "gitlab" : "gl" }
523
543
base_path = f"/plan/{ short_services [owner .service ]} /{ owner .username } "
524
544
success_url = f"{ settings .CODECOV_DASHBOARD_URL } { base_path } ?success"
525
545
cancel_url = f"{ settings .CODECOV_DASHBOARD_URL } { base_path } ?cancel"
526
546
return success_url , cancel_url
527
547
528
548
@_log_stripe_error
529
- def create_checkout_session (self , owner : Owner , desired_plan ):
549
+ def create_checkout_session (self , owner : Owner , desired_plan : dict ):
530
550
success_url , cancel_url = self ._get_success_and_cancel_url (owner )
531
551
log .info (
532
552
"Creating Stripe Checkout Session for owner" ,
0 commit comments