diff --git a/corporate/lib/stripe.py b/corporate/lib/stripe.py index 407920411e..6910d3ec1c 100644 --- a/corporate/lib/stripe.py +++ b/corporate/lib/stripe.py @@ -68,6 +68,20 @@ STRIPE_API_VERSION = "2020-08-27" stripe.api_version = STRIPE_API_VERSION +# This function imitates the behavior of the format_money in billing/helpers.ts +def format_money(cents: float) -> str: + # allow for small floating point errors + cents = math.ceil(cents - 0.001) + if cents % 100 == 0: + precision = 0 + else: + precision = 2 + + dollars = cents / 100 + # Format the number as a string with the correct number of decimal places + return f"{dollars:.{precision}f}" + + def get_latest_seat_count(realm: Realm) -> int: return get_seat_count(realm, extra_non_guests_count=0, extra_guests_count=0) diff --git a/corporate/tests/stripe_fixtures/free_trial_upgrade_by_card--Customer.retrieve.4.json b/corporate/tests/stripe_fixtures/free_trial_upgrade_by_card--Customer.retrieve.4.json new file mode 100644 index 0000000000..87ad38f284 Binary files /dev/null and b/corporate/tests/stripe_fixtures/free_trial_upgrade_by_card--Customer.retrieve.4.json differ diff --git a/corporate/tests/test_stripe.py b/corporate/tests/test_stripe.py index 49a6aff7a5..4e507ab80e 100644 --- a/corporate/tests/test_stripe.py +++ b/corporate/tests/test_stripe.py @@ -1208,6 +1208,16 @@ class StripeTest(StripeTestCase): invoice_plans_as_needed(add_months(free_trial_end_date, 12)) [invoice0, invoice1, invoice2] = stripe.Invoice.list(customer=stripe_customer.id) + # Check /billing/ has correct information for fixed price customers. + plan.fixed_price = 127 + plan.price_per_license = None + plan.save(update_fields=["fixed_price", "price_per_license"]) + with patch("corporate.views.billing_page.timezone_now", return_value=self.now): + response = self.client_get("/billing/") + self.assert_in_success_response(["$1.27"], response) + # Don't show price breakdown + self.assert_not_in_success_response(["{self.seat_count} x"], response) + @mock_stripe(tested_timestamp_fields=["created"]) def test_free_trial_upgrade_by_card_from_onboarding_page(self, *mocks: Mock) -> None: user = self.example_user("hamlet") diff --git a/corporate/views/billing_page.py b/corporate/views/billing_page.py index 1f851e24fe..fd5550beda 100644 --- a/corporate/views/billing_page.py +++ b/corporate/views/billing_page.py @@ -14,6 +14,7 @@ from corporate.lib.stripe import ( do_change_plan_status, downgrade_at_the_end_of_billing_cycle, downgrade_now_without_creating_additional_invoices, + format_money, get_latest_seat_count, make_end_of_cycle_updates_if_needed, renewal_amount, @@ -194,15 +195,13 @@ def billing_home( ) billing_frequency = CustomerPlan.BILLING_SCHEDULES[plan.billing_schedule] - price_per_license_int = plan.price_per_license - if price_per_license_int is not None and billing_frequency == "Annual": - price_per_license_int = int(price_per_license_int / 12) - price_per_license = ( - cents_to_dollar_string(price_per_license_int) - if price_per_license_int is not None - else None - ) + if plan.price_per_license is None: + price_per_license = "" + elif billing_frequency == "Annual": + price_per_license = format_money(plan.price_per_license / 12) + else: + price_per_license = format_money(plan.price_per_license) context.update( plan_name=plan.name, diff --git a/web/src/billing/helpers.ts b/web/src/billing/helpers.ts index d435635d06..fc216f90e6 100644 --- a/web/src/billing/helpers.ts +++ b/web/src/billing/helpers.ts @@ -89,6 +89,7 @@ export function create_ajax_request( }); } +// This function imitates the behavior of the format_money in views/billing_page.py export function format_money(cents: number): string { // allow for small floating point errors cents = Math.ceil(cents - 0.001);