diff --git a/corporate/lib/stripe.py b/corporate/lib/stripe.py index 09be7b36d6..bc8a18fd75 100644 --- a/corporate/lib/stripe.py +++ b/corporate/lib/stripe.py @@ -866,6 +866,7 @@ class BillingSession(ABC): plan_tier: int, billing_schedule: int, charge_automatically: bool, + invoice_period: Dict[str, int], license_management: Optional[str] = None, ) -> stripe.Invoice: plan_name = CustomerPlan.name_from_tier(plan_tier) @@ -887,6 +888,7 @@ class BillingSession(ABC): customer=customer.stripe_customer_id, description=plan_name, discountable=False, + period=invoice_period, **price_args, ) @@ -903,6 +905,7 @@ class BillingSession(ABC): description=f"${cents_to_dollar_string(customer.flat_discount)}/month new customer discount", # Negative value to apply discount. amount=(-1 * discount), + period=invoice_period, ) if charge_automatically: @@ -1162,6 +1165,7 @@ class BillingSession(ABC): metadata["billing_schedule"], charge_automatically=charge_automatically, license_management=metadata["license_management"], + invoice_period=metadata["invoice_period"], ) assert stripe_invoice.id is not None invoice = Invoice.objects.create( @@ -1595,14 +1599,34 @@ class BillingSession(ABC): "type": "upgrade", "plan_tier": plan_tier, } + discount_for_plan = customer.get_discount_for_plan_tier(plan_tier) + ( + invoice_period_start, + _, + invoice_period_end, + price_per_license, + ) = compute_plan_parameters( + plan_tier, + billing_schedule, + discount_for_plan, + # TODO: Use the correct value for free_trial when we switch behaviour to send invoice + # at the start of free trial. + False, + None, + not isinstance(self, RealmBillingSession), + ) if fixed_price_plan_offer is None: - discount_for_plan = customer.get_discount_for_plan_tier(plan_tier) - price_per_license = get_price_per_license( - plan_tier, billing_schedule, discount_for_plan - ) general_metadata["price_per_license"] = price_per_license else: general_metadata["fixed_price"] = fixed_price_plan_offer.fixed_price + invoice_period_end = add_months( + invoice_period_start, CustomerPlan.FIXED_PRICE_PLAN_DURATION_MONTHS + ) + + general_metadata["invoice_period"] = { + "start": datetime_to_timestamp(invoice_period_start), + "end": datetime_to_timestamp(invoice_period_end), + } updated_metadata = self.update_data_for_checkout_session_and_invoice_payment( general_metadata ) @@ -1778,9 +1802,10 @@ class BillingSession(ABC): # Manual license management is not available for fixed price plan. assert automanage_licenses is True plan_params["fixed_price"] = fixed_price_plan_offer.fixed_price - plan_params["end_date"] = add_months( + period_end = add_months( billing_cycle_anchor, CustomerPlan.FIXED_PRICE_PLAN_DURATION_MONTHS ) + plan_params["end_date"] = period_end fixed_price_plan_offer.status = CustomerPlanOffer.PROCESSED fixed_price_plan_offer.save(update_fields=["status"]) @@ -1841,6 +1866,10 @@ class BillingSession(ABC): plan_tier=plan.tier, billing_schedule=billing_schedule, charge_automatically=False, + invoice_period={ + "start": datetime_to_timestamp(billing_cycle_anchor), + "end": datetime_to_timestamp(period_end), + }, ) def do_upgrade(self, upgrade_request: UpgradeRequest) -> Dict[str, Any]: @@ -2834,6 +2863,7 @@ class BillingSession(ABC): invoiced_through_id = plan.invoiced_through.id invoice_item_created = False + invoice_period = None for ledger_entry in LicenseLedger.objects.filter( plan=plan, id__gt=invoiced_through_id, event_time__lte=event_time ).order_by("id"): @@ -2887,17 +2917,18 @@ class BillingSession(ABC): plan.invoicing_status = CustomerPlan.INVOICING_STATUS_STARTED plan.save(update_fields=["invoicing_status", "invoiced_through"]) assert plan.customer.stripe_customer_id is not None + invoice_period = { + "start": datetime_to_timestamp(ledger_entry.event_time), + "end": datetime_to_timestamp( + get_plan_renewal_or_end_date(plan, ledger_entry.event_time) + ), + } stripe.InvoiceItem.create( currency="usd", customer=plan.customer.stripe_customer_id, description=description, discountable=False, - period={ - "start": datetime_to_timestamp(ledger_entry.event_time), - "end": datetime_to_timestamp( - get_plan_renewal_or_end_date(plan, ledger_entry.event_time) - ), - }, + period=invoice_period, idempotency_key=get_idempotency_key(ledger_entry), **price_args, ) @@ -2923,6 +2954,7 @@ class BillingSession(ABC): description=f"${cents_to_dollar_string(flat_discount)}/month new customer discount", # Negative value to apply discount. amount=(-1 * discount), + period=invoice_period, ) if plan.charge_automatically: diff --git a/corporate/tests/stripe_fixtures/upgrade_by_card--Charge.list.1.json b/corporate/tests/stripe_fixtures/upgrade_by_card--Charge.list.1.json index 483485f6e8..62344ffb94 100644 Binary files a/corporate/tests/stripe_fixtures/upgrade_by_card--Charge.list.1.json and b/corporate/tests/stripe_fixtures/upgrade_by_card--Charge.list.1.json differ diff --git a/corporate/tests/stripe_fixtures/upgrade_by_card--Customer.modify.1.json b/corporate/tests/stripe_fixtures/upgrade_by_card--Customer.modify.1.json index 96d5fc9c55..80a4faa869 100644 Binary files a/corporate/tests/stripe_fixtures/upgrade_by_card--Customer.modify.1.json and b/corporate/tests/stripe_fixtures/upgrade_by_card--Customer.modify.1.json differ diff --git a/corporate/tests/stripe_fixtures/upgrade_by_card--Customer.retrieve.2.json b/corporate/tests/stripe_fixtures/upgrade_by_card--Customer.retrieve.2.json index 90578bd434..b8da7e4b33 100644 Binary files a/corporate/tests/stripe_fixtures/upgrade_by_card--Customer.retrieve.2.json and b/corporate/tests/stripe_fixtures/upgrade_by_card--Customer.retrieve.2.json differ diff --git a/corporate/tests/stripe_fixtures/upgrade_by_card--Customer.retrieve.3.json b/corporate/tests/stripe_fixtures/upgrade_by_card--Customer.retrieve.3.json index 90578bd434..b8da7e4b33 100644 Binary files a/corporate/tests/stripe_fixtures/upgrade_by_card--Customer.retrieve.3.json and b/corporate/tests/stripe_fixtures/upgrade_by_card--Customer.retrieve.3.json differ diff --git a/corporate/tests/stripe_fixtures/upgrade_by_card--Customer.retrieve.4.json b/corporate/tests/stripe_fixtures/upgrade_by_card--Customer.retrieve.4.json index 90578bd434..b8da7e4b33 100644 Binary files a/corporate/tests/stripe_fixtures/upgrade_by_card--Customer.retrieve.4.json and b/corporate/tests/stripe_fixtures/upgrade_by_card--Customer.retrieve.4.json differ diff --git a/corporate/tests/stripe_fixtures/upgrade_by_card--Customer.retrieve.5.json b/corporate/tests/stripe_fixtures/upgrade_by_card--Customer.retrieve.5.json index 90578bd434..b8da7e4b33 100644 Binary files a/corporate/tests/stripe_fixtures/upgrade_by_card--Customer.retrieve.5.json and b/corporate/tests/stripe_fixtures/upgrade_by_card--Customer.retrieve.5.json differ diff --git a/corporate/tests/stripe_fixtures/upgrade_by_card--Customer.retrieve.6.json b/corporate/tests/stripe_fixtures/upgrade_by_card--Customer.retrieve.6.json index c8acc5c682..5ee08275ce 100644 Binary files a/corporate/tests/stripe_fixtures/upgrade_by_card--Customer.retrieve.6.json and b/corporate/tests/stripe_fixtures/upgrade_by_card--Customer.retrieve.6.json differ diff --git a/corporate/tests/stripe_fixtures/upgrade_by_card--Event.list.1.json b/corporate/tests/stripe_fixtures/upgrade_by_card--Event.list.1.json index 4e30bdb774..9deef4277b 100644 Binary files a/corporate/tests/stripe_fixtures/upgrade_by_card--Event.list.1.json and b/corporate/tests/stripe_fixtures/upgrade_by_card--Event.list.1.json differ diff --git a/corporate/tests/stripe_fixtures/upgrade_by_card--Event.list.2.json b/corporate/tests/stripe_fixtures/upgrade_by_card--Event.list.2.json index 6dfe31a3a7..ffd3f203b6 100644 Binary files a/corporate/tests/stripe_fixtures/upgrade_by_card--Event.list.2.json and b/corporate/tests/stripe_fixtures/upgrade_by_card--Event.list.2.json differ diff --git a/corporate/tests/stripe_fixtures/upgrade_by_card--Event.list.3.json b/corporate/tests/stripe_fixtures/upgrade_by_card--Event.list.3.json index 4f5c4e2719..404f29097d 100644 Binary files a/corporate/tests/stripe_fixtures/upgrade_by_card--Event.list.3.json and b/corporate/tests/stripe_fixtures/upgrade_by_card--Event.list.3.json differ diff --git a/corporate/tests/stripe_fixtures/upgrade_by_card--Event.list.4.json b/corporate/tests/stripe_fixtures/upgrade_by_card--Event.list.4.json index d24cf7ec17..142ae867fb 100644 Binary files a/corporate/tests/stripe_fixtures/upgrade_by_card--Event.list.4.json and b/corporate/tests/stripe_fixtures/upgrade_by_card--Event.list.4.json differ diff --git a/corporate/tests/stripe_fixtures/upgrade_by_card--Invoice.create.1.json b/corporate/tests/stripe_fixtures/upgrade_by_card--Invoice.create.1.json index fb8ade5770..550b9fcb07 100644 Binary files a/corporate/tests/stripe_fixtures/upgrade_by_card--Invoice.create.1.json and b/corporate/tests/stripe_fixtures/upgrade_by_card--Invoice.create.1.json differ diff --git a/corporate/tests/stripe_fixtures/upgrade_by_card--Invoice.finalize_invoice.1.json b/corporate/tests/stripe_fixtures/upgrade_by_card--Invoice.finalize_invoice.1.json index ac07016c3e..7955ee253e 100644 Binary files a/corporate/tests/stripe_fixtures/upgrade_by_card--Invoice.finalize_invoice.1.json and b/corporate/tests/stripe_fixtures/upgrade_by_card--Invoice.finalize_invoice.1.json differ diff --git a/corporate/tests/stripe_fixtures/upgrade_by_card--Invoice.list.2.json b/corporate/tests/stripe_fixtures/upgrade_by_card--Invoice.list.2.json index 22a8d955c4..54368a60f1 100644 Binary files a/corporate/tests/stripe_fixtures/upgrade_by_card--Invoice.list.2.json and b/corporate/tests/stripe_fixtures/upgrade_by_card--Invoice.list.2.json differ diff --git a/corporate/tests/stripe_fixtures/upgrade_by_card--Invoice.pay.1.json b/corporate/tests/stripe_fixtures/upgrade_by_card--Invoice.pay.1.json index c30d61fe72..a3feb53be9 100644 Binary files a/corporate/tests/stripe_fixtures/upgrade_by_card--Invoice.pay.1.json and b/corporate/tests/stripe_fixtures/upgrade_by_card--Invoice.pay.1.json differ diff --git a/corporate/tests/stripe_fixtures/upgrade_by_card--InvoiceItem.create.1.json b/corporate/tests/stripe_fixtures/upgrade_by_card--InvoiceItem.create.1.json index 38644b98ed..bbe2ae869b 100644 Binary files a/corporate/tests/stripe_fixtures/upgrade_by_card--InvoiceItem.create.1.json and b/corporate/tests/stripe_fixtures/upgrade_by_card--InvoiceItem.create.1.json differ diff --git a/corporate/tests/stripe_fixtures/upgrade_by_card--SetupIntent.create.1.json b/corporate/tests/stripe_fixtures/upgrade_by_card--SetupIntent.create.1.json index 30672ba9b1..b748d0663d 100644 Binary files a/corporate/tests/stripe_fixtures/upgrade_by_card--SetupIntent.create.1.json and b/corporate/tests/stripe_fixtures/upgrade_by_card--SetupIntent.create.1.json differ diff --git a/corporate/tests/stripe_fixtures/upgrade_by_card--SetupIntent.list.1.json b/corporate/tests/stripe_fixtures/upgrade_by_card--SetupIntent.list.1.json index e8a95b5e04..0bf4c68aa9 100644 Binary files a/corporate/tests/stripe_fixtures/upgrade_by_card--SetupIntent.list.1.json and b/corporate/tests/stripe_fixtures/upgrade_by_card--SetupIntent.list.1.json differ diff --git a/corporate/tests/stripe_fixtures/upgrade_by_card--SetupIntent.retrieve.1.json b/corporate/tests/stripe_fixtures/upgrade_by_card--SetupIntent.retrieve.1.json index 30672ba9b1..b748d0663d 100644 Binary files a/corporate/tests/stripe_fixtures/upgrade_by_card--SetupIntent.retrieve.1.json and b/corporate/tests/stripe_fixtures/upgrade_by_card--SetupIntent.retrieve.1.json differ diff --git a/corporate/tests/stripe_fixtures/upgrade_by_card--checkout.Session.create.1.json b/corporate/tests/stripe_fixtures/upgrade_by_card--checkout.Session.create.1.json index a532314f22..0936636555 100644 Binary files a/corporate/tests/stripe_fixtures/upgrade_by_card--checkout.Session.create.1.json and b/corporate/tests/stripe_fixtures/upgrade_by_card--checkout.Session.create.1.json differ diff --git a/corporate/tests/stripe_fixtures/upgrade_by_card--checkout.Session.list.1.json b/corporate/tests/stripe_fixtures/upgrade_by_card--checkout.Session.list.1.json index 150a47b63c..9a83b1c505 100644 Binary files a/corporate/tests/stripe_fixtures/upgrade_by_card--checkout.Session.list.1.json and b/corporate/tests/stripe_fixtures/upgrade_by_card--checkout.Session.list.1.json differ diff --git a/corporate/tests/stripe_fixtures/upgrade_by_invoice--Event.list.1.json b/corporate/tests/stripe_fixtures/upgrade_by_invoice--Event.list.1.json index 041fccf746..9b546ad56e 100644 Binary files a/corporate/tests/stripe_fixtures/upgrade_by_invoice--Event.list.1.json and b/corporate/tests/stripe_fixtures/upgrade_by_invoice--Event.list.1.json differ diff --git a/corporate/tests/stripe_fixtures/upgrade_by_invoice--Event.list.2.json b/corporate/tests/stripe_fixtures/upgrade_by_invoice--Event.list.2.json index ee4362c10c..14f29eac24 100644 Binary files a/corporate/tests/stripe_fixtures/upgrade_by_invoice--Event.list.2.json and b/corporate/tests/stripe_fixtures/upgrade_by_invoice--Event.list.2.json differ diff --git a/corporate/tests/stripe_fixtures/upgrade_by_invoice--Event.list.3.json b/corporate/tests/stripe_fixtures/upgrade_by_invoice--Event.list.3.json index e22ecc4c73..e9fcaf5706 100644 Binary files a/corporate/tests/stripe_fixtures/upgrade_by_invoice--Event.list.3.json and b/corporate/tests/stripe_fixtures/upgrade_by_invoice--Event.list.3.json differ diff --git a/corporate/tests/stripe_fixtures/upgrade_by_invoice--Invoice.create.1.json b/corporate/tests/stripe_fixtures/upgrade_by_invoice--Invoice.create.1.json index a3709fcc03..2c62012cb8 100644 Binary files a/corporate/tests/stripe_fixtures/upgrade_by_invoice--Invoice.create.1.json and b/corporate/tests/stripe_fixtures/upgrade_by_invoice--Invoice.create.1.json differ diff --git a/corporate/tests/stripe_fixtures/upgrade_by_invoice--Invoice.finalize_invoice.1.json b/corporate/tests/stripe_fixtures/upgrade_by_invoice--Invoice.finalize_invoice.1.json index 2598f9e58d..9839256ca0 100644 Binary files a/corporate/tests/stripe_fixtures/upgrade_by_invoice--Invoice.finalize_invoice.1.json and b/corporate/tests/stripe_fixtures/upgrade_by_invoice--Invoice.finalize_invoice.1.json differ diff --git a/corporate/tests/stripe_fixtures/upgrade_by_invoice--Invoice.list.1.json b/corporate/tests/stripe_fixtures/upgrade_by_invoice--Invoice.list.1.json index 7d7143dcfb..83c684e965 100644 Binary files a/corporate/tests/stripe_fixtures/upgrade_by_invoice--Invoice.list.1.json and b/corporate/tests/stripe_fixtures/upgrade_by_invoice--Invoice.list.1.json differ diff --git a/corporate/tests/stripe_fixtures/upgrade_by_invoice--Invoice.pay.1.json b/corporate/tests/stripe_fixtures/upgrade_by_invoice--Invoice.pay.1.json index e07ab6b1de..ebde93816d 100644 Binary files a/corporate/tests/stripe_fixtures/upgrade_by_invoice--Invoice.pay.1.json and b/corporate/tests/stripe_fixtures/upgrade_by_invoice--Invoice.pay.1.json differ diff --git a/corporate/tests/stripe_fixtures/upgrade_by_invoice--InvoiceItem.create.1.json b/corporate/tests/stripe_fixtures/upgrade_by_invoice--InvoiceItem.create.1.json index 322aec25aa..bea0dd3c75 100644 Binary files a/corporate/tests/stripe_fixtures/upgrade_by_invoice--InvoiceItem.create.1.json and b/corporate/tests/stripe_fixtures/upgrade_by_invoice--InvoiceItem.create.1.json differ diff --git a/corporate/tests/test_stripe.py b/corporate/tests/test_stripe.py index 0bf9c42e43..16ecc665e8 100644 --- a/corporate/tests/test_stripe.py +++ b/corporate/tests/test_stripe.py @@ -884,6 +884,10 @@ class StripeTest(StripeTestCase): "plan": None, "proration": False, "quantity": self.seat_count, + "period": { + "start": datetime_to_timestamp(self.now), + "end": datetime_to_timestamp(add_months(self.now, 12)), + }, } for key, value in line_item_params.items(): self.assertEqual(item0.get(key), value) @@ -1022,6 +1026,10 @@ class StripeTest(StripeTestCase): "plan": None, "proration": False, "quantity": 123, + "period": { + "start": datetime_to_timestamp(self.now), + "end": datetime_to_timestamp(add_months(self.now, 12)), + }, } for key, value in line_item_params.items(): self.assertEqual(item.get(key), value)