stripe: Prepare to switch to stripe inline annotations.

https://github.com/stripe/stripe-python/wiki/Inline-type-annotations

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg
2023-11-14 12:48:14 -08:00
committed by Tim Abbott
parent 2c9c105bbb
commit 7a4ca3135d
5 changed files with 97 additions and 64 deletions

View File

@@ -8,7 +8,7 @@ from datetime import datetime, timedelta
from decimal import Decimal
from enum import Enum
from functools import wraps
from typing import Any, Callable, Dict, Generator, Optional, Tuple, TypeVar, Union
from typing import Any, Callable, Dict, Generator, Optional, Tuple, TypedDict, TypeVar, Union
import stripe
from django.conf import settings
@@ -335,6 +335,7 @@ def catch_stripe_errors(func: Callable[ParamT, ReturnT]) -> Callable[ParamT, Ret
# https://stripe.com/docs/api/ruby#error_handling suggests there are additional fields, and
# https://stripe.com/docs/error-codes gives a more detailed set of error codes
except stripe.error.StripeError as e:
assert isinstance(e.json_body, dict)
err = e.json_body.get("error", {})
if isinstance(e, stripe.error.CardError):
billing_logger.info(
@@ -1170,8 +1171,10 @@ class RealmBillingSession(BillingSession):
def stripe_customer_has_credit_card_as_default_payment_method(
stripe_customer: stripe.Customer,
) -> bool:
assert stripe_customer.invoice_settings is not None
if not stripe_customer.invoice_settings.default_payment_method:
return False
assert isinstance(stripe_customer.invoice_settings.default_payment_method, stripe.PaymentMethod)
return stripe_customer.invoice_settings.default_payment_method.type == "card"
@@ -1361,6 +1364,12 @@ def get_plan_renewal_or_end_date(plan: CustomerPlan, event_time: datetime) -> da
return billing_period_end
class PriceArgs(TypedDict, total=False):
amount: int
unit_amount: int
quantity: int
def invoice_plan(plan: CustomerPlan, event_time: datetime) -> None:
if plan.invoicing_status == CustomerPlan.STARTED:
raise NotImplementedError("Plan with invoicing_status==STARTED needs manual resolution.")
@@ -1386,7 +1395,7 @@ def invoice_plan(plan: CustomerPlan, event_time: datetime) -> None:
for ledger_entry in LicenseLedger.objects.filter(
plan=plan, id__gt=invoiced_through_id, event_time__lte=event_time
).order_by("id"):
price_args: Dict[str, int] = {}
price_args: PriceArgs = {}
if ledger_entry.is_renewal:
if plan.fixed_price is not None:
price_args = {"amount": plan.fixed_price}
@@ -1536,6 +1545,7 @@ def void_all_open_invoices(realm: Realm) -> int:
voided_invoices_count = 0
for invoice in invoices:
if invoice.status == "open":
assert invoice.id is not None
stripe.Invoice.void_invoice(invoice.id)
voided_invoices_count += 1
return voided_invoices_count

View File

@@ -69,13 +69,16 @@ def handle_checkout_session_completed_event(
session.status = Session.COMPLETED
session.save()
assert isinstance(stripe_session.setup_intent, str)
stripe_setup_intent = stripe.SetupIntent.retrieve(stripe_session.setup_intent)
assert session.customer.realm is not None
assert stripe_session.metadata is not None
user_id = stripe_session.metadata.get("user_id")
assert user_id is not None
user = get_active_user_profile_by_id_in_realm(user_id, session.customer.realm)
user = get_active_user_profile_by_id_in_realm(int(user_id), session.customer.realm)
billing_session = RealmBillingSession(user)
payment_method = stripe_setup_intent.payment_method
assert isinstance(payment_method, (str, type(None)))
if session.type in [
Session.UPGRADE_FROM_BILLING_PAGE,
@@ -124,7 +127,10 @@ def handle_payment_intent_succeeded_event(
user = get_active_user_profile_by_id_in_realm(user_id, payment_intent.customer.realm)
description = ""
charge: stripe.Charge
for charge in stripe_payment_intent.charges:
assert charge.payment_method_details is not None
assert charge.payment_method_details.card is not None
description = f"Payment (Card ending in {charge.payment_method_details.card.last4})"
break
@@ -163,6 +169,7 @@ def handle_payment_intent_succeeded_event(
def handle_payment_intent_payment_failed_event(
stripe_payment_intent: stripe.PaymentIntent, payment_intent: PaymentIntent
) -> None:
assert stripe_payment_intent.last_payment_error is not None
payment_intent.status = PaymentIntent.get_status_integer_from_status_text(
stripe_payment_intent.status
)

View File

@@ -162,6 +162,7 @@ def generate_and_save_stripe_fixture(
stripe_object = mocked_function(*args, **kwargs)
except stripe.error.StripeError as e:
with open(fixture_path, "w") as f:
assert e.headers is not None
error_dict = {**vars(e), "headers": dict(e.headers)}
f.write(
json.dumps(error_dict, indent=2, separators=(",", ": "), sort_keys=True) + "\n"
@@ -485,7 +486,7 @@ class StripeTestCase(ZulipTestCase):
payment_method: str,
stripe_session: Optional[stripe.checkout.Session] = None,
) -> None:
[checkout_setup_intent] = stripe.SetupIntent.list(limit=1)
[checkout_setup_intent] = iter(stripe.SetupIntent.list(limit=1))
stripe_setup_intent = stripe.SetupIntent.create(
payment_method=payment_method,
confirm=True,
@@ -495,7 +496,7 @@ class StripeTestCase(ZulipTestCase):
usage=checkout_setup_intent.usage,
)
if stripe_session is None:
[stripe_session] = stripe.checkout.Session.list(limit=1)
[stripe_session] = iter(stripe.checkout.Session.list(limit=1))
stripe_session_dict = stripe_session.to_dict_recursive()
stripe_session_dict["setup_intent"] = stripe_setup_intent.id
@@ -528,7 +529,7 @@ class StripeTestCase(ZulipTestCase):
most_recent_event = events_old_to_new[-1]
def send_last_stripe_webhook_event(self) -> None:
[last_event] = stripe.Event.list(limit=1)
[last_event] = iter(stripe.Event.list(limit=1))
self.send_stripe_webhook_event(last_event)
def upgrade(
@@ -570,7 +571,7 @@ class StripeTestCase(ZulipTestCase):
del params[key]
if talk_to_stripe:
[last_event] = stripe.Event.list(limit=1)
[last_event] = iter(stripe.Event.list(limit=1))
upgrade_json_response = self.client_post("/json/billing/upgrade", params)
@@ -719,6 +720,7 @@ class StripeTest(StripeTestCase):
self.assertEqual(stripe_customer.description, "zulip (Zulip Dev)")
self.assertEqual(stripe_customer.discount, None)
self.assertEqual(stripe_customer.email, user.delivery_email)
assert stripe_customer.metadata is not None
metadata_dict = dict(stripe_customer.metadata)
self.assertEqual(metadata_dict["realm_str"], "zulip")
try:
@@ -727,7 +729,7 @@ class StripeTest(StripeTestCase):
raise AssertionError("realm_id is not a number")
# Check Charges in Stripe
[charge] = stripe.Charge.list(customer=stripe_customer.id)
[charge] = iter(stripe.Charge.list(customer=stripe_customer.id))
self.assertEqual(charge.amount, 8000 * self.seat_count)
# TODO: fix Decimal
self.assertEqual(
@@ -736,7 +738,7 @@ class StripeTest(StripeTestCase):
self.assertEqual(charge.receipt_email, user.delivery_email)
self.assertEqual(charge.statement_descriptor, "Zulip Cloud Standard")
# Check Invoices in Stripe
[invoice] = stripe.Invoice.list(customer=stripe_customer.id)
[invoice] = iter(stripe.Invoice.list(customer=stripe_customer.id))
self.assertIsNotNone(invoice.status_transitions.finalized_at)
invoice_params = {
# auto_advance is False because the invoice has been paid
@@ -751,7 +753,7 @@ class StripeTest(StripeTestCase):
for key, value in invoice_params.items():
self.assertEqual(invoice.get(key), value)
# Check Line Items on Stripe Invoice
[item0, item1] = invoice.lines
[item0, item1] = iter(invoice.lines)
line_item_params = {
"amount": 8000 * self.seat_count,
"description": "Zulip Cloud Standard",
@@ -883,7 +885,7 @@ class StripeTest(StripeTestCase):
# Check Charges in Stripe
self.assertFalse(stripe.Charge.list(customer=stripe_customer.id))
# Check Invoices in Stripe
[invoice] = stripe.Invoice.list(customer=stripe_customer.id)
[invoice] = iter(stripe.Invoice.list(customer=stripe_customer.id))
self.assertIsNotNone(invoice.due_date)
self.assertIsNotNone(invoice.status_transitions.finalized_at)
invoice_params = {
@@ -899,7 +901,7 @@ class StripeTest(StripeTestCase):
for key, value in invoice_params.items():
self.assertEqual(invoice.get(key), value)
# Check Line Items on Stripe Invoice
[item] = invoice.lines
[item] = iter(invoice.lines)
line_item_params = {
"amount": 8000 * 123,
"description": "Zulip Cloud Standard",
@@ -1024,6 +1026,7 @@ class StripeTest(StripeTestCase):
self.assertEqual(stripe_customer.description, "zulip (Zulip Dev)")
self.assertEqual(stripe_customer.discount, None)
self.assertEqual(stripe_customer.email, user.delivery_email)
assert stripe_customer.metadata is not None
metadata_dict = dict(stripe_customer.metadata)
self.assertEqual(metadata_dict["realm_str"], "zulip")
try:
@@ -1142,7 +1145,7 @@ class StripeTest(StripeTestCase):
self.assertEqual(customer_plan.status, CustomerPlan.ACTIVE)
self.assertEqual(customer_plan.next_invoice_date, add_months(free_trial_end_date, 1))
self.assertEqual(realm.plan_type, Realm.PLAN_TYPE_STANDARD)
[invoice] = stripe.Invoice.list(customer=stripe_customer.id)
[invoice] = iter(stripe.Invoice.list(customer=stripe_customer.id))
invoice_params = {
"amount_due": 15 * 80 * 100,
"amount_paid": 0,
@@ -1157,7 +1160,7 @@ class StripeTest(StripeTestCase):
}
for key, value in invoice_params.items():
self.assertEqual(invoice.get(key), value)
[invoice_item] = invoice.get("lines")
[invoice_item] = iter(invoice.lines)
invoice_item_params = {
"amount": 15 * 80 * 100,
"description": "Zulip Cloud Standard - renewal",
@@ -1174,7 +1177,7 @@ class StripeTest(StripeTestCase):
self.assertEqual(invoice_item[key], value)
invoice_plans_as_needed(add_months(free_trial_end_date, 1))
[invoice] = stripe.Invoice.list(customer=stripe_customer.id)
[invoice] = iter(stripe.Invoice.list(customer=stripe_customer.id))
with patch("corporate.lib.stripe.get_latest_seat_count", return_value=19):
update_license_ledger_if_needed(realm, add_months(free_trial_end_date, 10))
@@ -1185,14 +1188,14 @@ class StripeTest(StripeTestCase):
(19, 19),
)
invoice_plans_as_needed(add_months(free_trial_end_date, 10))
[invoice0, invoice1] = stripe.Invoice.list(customer=stripe_customer.id)
[invoice0, invoice1] = iter(stripe.Invoice.list(customer=stripe_customer.id))
invoice_params = {
"amount_due": 5172,
"auto_advance": True,
"collection_method": "charge_automatically",
"customer_email": "hamlet@zulip.com",
}
[invoice_item] = invoice0.get("lines")
[invoice_item] = iter(invoice0.lines)
invoice_item_params = {
"amount": 5172,
"description": "Additional license (Jan 2, 2013 - Mar 2, 2013)",
@@ -1205,7 +1208,7 @@ class StripeTest(StripeTestCase):
}
invoice_plans_as_needed(add_months(free_trial_end_date, 12))
[invoice0, invoice1, invoice2] = stripe.Invoice.list(customer=stripe_customer.id)
[invoice0, invoice1, invoice2] = iter(stripe.Invoice.list(customer=stripe_customer.id))
# Check /billing/ has correct information for fixed price customers.
plan.fixed_price = 127
@@ -1250,6 +1253,7 @@ class StripeTest(StripeTestCase):
self.assertEqual(stripe_customer.description, "zulip (Zulip Dev)")
self.assertEqual(stripe_customer.discount, None)
self.assertEqual(stripe_customer.email, user.delivery_email)
assert stripe_customer.metadata is not None
metadata_dict = dict(stripe_customer.metadata)
self.assertEqual(metadata_dict["realm_str"], "zulip")
try:
@@ -1305,6 +1309,7 @@ class StripeTest(StripeTestCase):
)
self.assertEqual(stripe_customer.discount, None)
self.assertEqual(stripe_customer.email, user.delivery_email)
assert stripe_customer.metadata is not None
metadata_dict = dict(stripe_customer.metadata)
self.assertEqual(metadata_dict["realm_str"], "zulip")
try:
@@ -1395,7 +1400,7 @@ class StripeTest(StripeTestCase):
self.assertEqual(customer_plan.status, CustomerPlan.ACTIVE)
self.assertEqual(customer_plan.next_invoice_date, add_months(free_trial_end_date, 12))
self.assertEqual(realm.plan_type, Realm.PLAN_TYPE_STANDARD)
[invoice] = stripe.Invoice.list(customer=stripe_customer.id)
[invoice] = iter(stripe.Invoice.list(customer=stripe_customer.id))
invoice_params = {
"amount_due": 123 * 80 * 100,
"amount_paid": 0,
@@ -1410,7 +1415,7 @@ class StripeTest(StripeTestCase):
}
for key, value in invoice_params.items():
self.assertEqual(invoice.get(key), value)
[invoice_item] = invoice.get("lines")
[invoice_item] = iter(invoice.lines)
invoice_item_params = {
"amount": 123 * 80 * 100,
"description": "Zulip Cloud Standard - renewal",
@@ -1427,13 +1432,13 @@ class StripeTest(StripeTestCase):
self.assertEqual(invoice_item[key], value)
invoice_plans_as_needed(add_months(free_trial_end_date, 1))
[invoice] = stripe.Invoice.list(customer=stripe_customer.id)
[invoice] = iter(stripe.Invoice.list(customer=stripe_customer.id))
invoice_plans_as_needed(add_months(free_trial_end_date, 10))
[invoice] = stripe.Invoice.list(customer=stripe_customer.id)
[invoice] = iter(stripe.Invoice.list(customer=stripe_customer.id))
invoice_plans_as_needed(add_months(free_trial_end_date, 12))
[invoice0, invoice1] = stripe.Invoice.list(customer=stripe_customer.id)
[invoice0, invoice1] = iter(stripe.Invoice.list(customer=stripe_customer.id))
@mock_stripe(tested_timestamp_fields=["created"])
def test_upgrade_by_card_with_outdated_seat_count(self, *mocks: Mock) -> None:
@@ -1450,10 +1455,10 @@ class StripeTest(StripeTestCase):
assert customer is not None
stripe_customer_id: str = assert_is_not_none(customer.stripe_customer_id)
# Check that the Charge used the old quantity, not new_seat_count
[charge] = stripe.Charge.list(customer=stripe_customer_id)
[charge] = iter(stripe.Charge.list(customer=stripe_customer_id))
self.assertEqual(8000 * self.seat_count, charge.amount)
# Check that the invoice has a credit for the old amount and a charge for the new one
[stripe_invoice] = stripe.Invoice.list(customer=stripe_customer_id)
[stripe_invoice] = iter(stripe.Invoice.list(customer=stripe_customer_id))
self.assertEqual(
[8000 * new_seat_count, -8000 * self.seat_count],
[item.amount for item in stripe_invoice.lines],
@@ -1515,7 +1520,7 @@ class StripeTest(StripeTestCase):
self.assertFalse(CustomerPlan.objects.exists())
# Check that we created a Customer in stripe, a failed Charge, and no Invoices or Invoice Items
self.assertTrue(stripe_get_customer(stripe_customer_id))
[charge] = stripe.Charge.list(customer=stripe_customer_id)
[charge] = iter(stripe.Charge.list(customer=stripe_customer_id))
self.assertEqual(charge.failure_code, "card_declined")
self.assertFalse(stripe.Invoice.list(customer=stripe_customer_id))
self.assertFalse(stripe.InvoiceItem.list(customer=stripe_customer_id))
@@ -1537,7 +1542,7 @@ class StripeTest(StripeTestCase):
self.assertEqual(response.status_code, 302)
self.assertEqual("/upgrade/", response["Location"])
[last_event] = stripe.Event.list(limit=1)
[last_event] = iter(stripe.Event.list(limit=1))
retry_payment_intent_json_response = self.client_post(
"/json/billing/session/start_retry_payment_intent_session",
{
@@ -1601,9 +1606,9 @@ class StripeTest(StripeTestCase):
self.assertEqual(ledger_entry.licenses, self.seat_count)
self.assertEqual(ledger_entry.licenses_at_next_renewal, self.seat_count)
# Check the Charges and Invoices in Stripe
[charge0, charge1] = stripe.Charge.list(customer=stripe_customer_id)
[charge0, charge1] = iter(stripe.Charge.list(customer=stripe_customer_id))
self.assertEqual(8000 * self.seat_count, charge0.amount)
[stripe_invoice] = stripe.Invoice.list(customer=stripe_customer_id)
[stripe_invoice] = iter(stripe.Invoice.list(customer=stripe_customer_id))
self.assertEqual(
[8000 * self.seat_count, -8000 * self.seat_count],
[item.amount for item in stripe_invoice.lines],
@@ -1683,7 +1688,7 @@ class StripeTest(StripeTestCase):
self.assertFalse(CustomerPlan.objects.exists())
# Check that we created a Customer in stripe, a failed Charge, and no Invoices or Invoice Items
self.assertTrue(stripe_get_customer(stripe_customer_id))
[charge] = stripe.Charge.list(customer=stripe_customer_id)
[charge] = iter(stripe.Charge.list(customer=stripe_customer_id))
self.assertEqual(charge.failure_code, "card_declined")
# TODO: figure out what these actually are
self.assertFalse(stripe.Invoice.list(customer=stripe_customer_id))
@@ -1738,9 +1743,9 @@ class StripeTest(StripeTestCase):
self.assertEqual(ledger_entry.licenses, 23)
self.assertEqual(ledger_entry.licenses_at_next_renewal, 23)
# Check the Charges and Invoices in Stripe
[charge0, charge1] = stripe.Charge.list(customer=stripe_customer_id)
[charge0, charge1] = iter(stripe.Charge.list(customer=stripe_customer_id))
self.assertEqual(8000 * 23, charge0.amount)
[stripe_invoice] = stripe.Invoice.list(customer=stripe_customer_id)
[stripe_invoice] = iter(stripe.Invoice.list(customer=stripe_customer_id))
self.assertEqual([8000 * 23, -8000 * 23], [item.amount for item in stripe_invoice.lines])
# Check that we correctly populated RealmAuditLog
audit_log_entries = list(
@@ -1793,8 +1798,8 @@ class StripeTest(StripeTestCase):
"license_management": "automatic",
},
)
[hamlet_stripe_session] = stripe.checkout.Session.list(limit=1)
[hamlet_payment_intent] = stripe.PaymentIntent.list(limit=1)
[hamlet_stripe_session] = iter(stripe.checkout.Session.list(limit=1))
[hamlet_payment_intent] = iter(stripe.PaymentIntent.list(limit=1))
self.login_user(othello)
self.upgrade()
@@ -1868,7 +1873,7 @@ class StripeTest(StripeTestCase):
customer = get_customer_by_realm(get_realm("zulip"))
assert customer is not None
assert customer.stripe_customer_id is not None
[invoice, _] = stripe.Invoice.list(customer=customer.stripe_customer_id)
[invoice, _] = iter(stripe.Invoice.list(customer=customer.stripe_customer_id))
self.assertEqual(invoice.total, -1 * charged_amount)
stripe_customer = stripe.Customer.retrieve(customer.stripe_customer_id)
self.assertEqual(stripe_customer.balance, -1 * charged_amount)
@@ -2640,6 +2645,9 @@ class StripeTest(StripeTestCase):
with patch("corporate.views.billing_page.timezone_now", return_value=self.now):
mock_customer = Mock(email=user.delivery_email)
mock_customer.invoice_settings.default_payment_method = Mock(
spec=stripe.PaymentMethod, type=Mock()
)
with patch(
"corporate.views.billing_page.stripe_get_customer", return_value=mock_customer
):
@@ -2812,9 +2820,11 @@ class StripeTest(StripeTestCase):
self.assertEqual(monthly_plan.next_invoice_date, None)
assert customer.stripe_customer_id
[invoice0, invoice1, invoice2] = stripe.Invoice.list(customer=customer.stripe_customer_id)
[invoice0, invoice1, invoice2] = iter(
stripe.Invoice.list(customer=customer.stripe_customer_id)
)
[invoice_item0, invoice_item1] = invoice0.get("lines")
[invoice_item0, invoice_item1] = iter(invoice0.lines)
annual_plan_invoice_item_params = {
"amount": 5 * 80 * 100,
"description": "Additional license (Feb 2, 2012 - Feb 2, 2013)",
@@ -2845,7 +2855,7 @@ class StripeTest(StripeTestCase):
for key, value in annual_plan_invoice_item_params.items():
self.assertEqual(invoice_item1[key], value)
[monthly_plan_invoice_item] = invoice1.get("lines")
[monthly_plan_invoice_item] = iter(invoice1.lines)
monthly_plan_invoice_item_params = {
"amount": 14 * 8 * 100,
"description": "Additional license (Jan 2, 2012 - Feb 2, 2012)",
@@ -2865,11 +2875,11 @@ class StripeTest(StripeTestCase):
update_license_ledger_if_needed(user.realm, add_months(self.next_month, 1))
invoice_plans_as_needed(add_months(self.next_month, 1))
[invoice0, invoice1, invoice2, invoice3] = stripe.Invoice.list(
customer=customer.stripe_customer_id
[invoice0, invoice1, invoice2, invoice3] = iter(
stripe.Invoice.list(customer=customer.stripe_customer_id)
)
[monthly_plan_invoice_item] = invoice0.get("lines")
[monthly_plan_invoice_item] = iter(invoice0.lines)
monthly_plan_invoice_item_params = {
"amount": 5 * 7366,
"description": "Additional license (Mar 2, 2012 - Feb 2, 2013)",
@@ -2886,11 +2896,11 @@ class StripeTest(StripeTestCase):
self.assertEqual(monthly_plan_invoice_item[key], value)
invoice_plans_as_needed(add_months(self.now, 13))
[invoice0, invoice1, invoice2, invoice3, invoice4] = stripe.Invoice.list(
customer=customer.stripe_customer_id
[invoice0, invoice1, invoice2, invoice3, invoice4] = iter(
stripe.Invoice.list(customer=customer.stripe_customer_id)
)
[invoice_item] = invoice0.get("lines")
[invoice_item] = iter(invoice0.lines)
annual_plan_invoice_item_params = {
"amount": 30 * 80 * 100,
"description": "Zulip Cloud Standard - renewal",
@@ -2977,9 +2987,9 @@ class StripeTest(StripeTestCase):
self.assertEqual(annual_plan.invoicing_status, CustomerPlan.DONE)
assert customer.stripe_customer_id
[invoice0, invoice1] = stripe.Invoice.list(customer=customer.stripe_customer_id)
[invoice0, invoice1] = iter(stripe.Invoice.list(customer=customer.stripe_customer_id))
[invoice_item] = invoice0.get("lines")
[invoice_item] = iter(invoice0.lines)
annual_plan_invoice_item_params = {
"amount": num_licenses * 80 * 100,
"description": "Zulip Cloud Standard - renewal",
@@ -3001,9 +3011,11 @@ class StripeTest(StripeTestCase):
invoice_plans_as_needed(add_months(self.now, 13))
[invoice0, invoice1, invoice2] = stripe.Invoice.list(customer=customer.stripe_customer_id)
[invoice0, invoice1, invoice2] = iter(
stripe.Invoice.list(customer=customer.stripe_customer_id)
)
[invoice_item] = invoice0.get("lines")
[invoice_item] = iter(invoice0.lines)
annual_plan_invoice_item_params = {
"amount": num_licenses * 80 * 100,
"description": "Zulip Cloud Standard - renewal",
@@ -3231,7 +3243,7 @@ class StripeTest(StripeTestCase):
stripe_customer = stripe_get_customer(
assert_is_not_none(Customer.objects.get(realm=user.realm).stripe_customer_id)
)
[invoice, _] = stripe.Invoice.list(customer=stripe_customer.id)
[invoice, _] = iter(stripe.Invoice.list(customer=stripe_customer.id))
invoice_params = {
"amount_due": 8000 * 150 + 8000 * 50,
"amount_paid": 0,
@@ -3244,7 +3256,7 @@ class StripeTest(StripeTestCase):
}
for key, value in invoice_params.items():
self.assertEqual(invoice.get(key), value)
[renewal_item, extra_license_item] = invoice.lines
[renewal_item, extra_license_item] = iter(invoice.lines)
line_item_params = {
"amount": 8000 * 150,
"description": "Zulip Cloud Standard - renewal",
@@ -3281,7 +3293,7 @@ class StripeTest(StripeTestCase):
stripe_customer = stripe_get_customer(
assert_is_not_none(Customer.objects.get(realm=user.realm).stripe_customer_id)
)
[invoice, _, _] = stripe.Invoice.list(customer=stripe_customer.id)
[invoice, _, _] = iter(stripe.Invoice.list(customer=stripe_customer.id))
invoice_params = {
"amount_due": 8000 * 120,
"amount_paid": 0,
@@ -3294,7 +3306,7 @@ class StripeTest(StripeTestCase):
}
for key, value in invoice_params.items():
self.assertEqual(invoice.get(key), value)
[renewal_item] = invoice.lines
[renewal_item] = iter(invoice.lines)
line_item_params = {
"amount": 8000 * 120,
"description": "Zulip Cloud Standard - renewal",
@@ -3818,7 +3830,7 @@ class StripeTest(StripeTestCase):
# customer's balance.
stripe_customer_id = customer.stripe_customer_id
assert stripe_customer_id is not None
_, cb_txn = stripe.Customer.list_balance_transactions(stripe_customer_id)
_, cb_txn = iter(stripe.Customer.list_balance_transactions(stripe_customer_id))
self.assertEqual(cb_txn.amount, -7200)
self.assertEqual(
cb_txn.description,
@@ -3828,7 +3840,7 @@ class StripeTest(StripeTestCase):
# The customer now only pays the difference 14400 - 7200 = 7200 = $72,
# since the unused proration is for the whole month.
(invoice,) = stripe.Invoice.list(customer=stripe_customer_id)
(invoice,) = iter(stripe.Invoice.list(customer=stripe_customer_id))
self.assertEqual(invoice.amount_due, 7200)
@mock_stripe()
@@ -4736,9 +4748,9 @@ class InvoiceTest(StripeTestCase):
invoice_plan(plan, self.now + timedelta(days=400))
stripe_cutomer_id = plan.customer.stripe_customer_id
assert stripe_cutomer_id is not None
[invoice0, invoice1] = stripe.Invoice.list(customer=stripe_cutomer_id)
[invoice0, invoice1] = iter(stripe.Invoice.list(customer=stripe_cutomer_id))
self.assertIsNotNone(invoice0.status_transitions.finalized_at)
[item0, item1, item2] = invoice0.lines
[item0, item1, item2] = iter(invoice0.lines)
line_item_params = {
"amount": int(8000 * (1 - ((400 - 366) / 365)) + 0.5),
"description": "Additional license (Feb 5, 2013 - Jan 2, 2014)",
@@ -4791,9 +4803,9 @@ class InvoiceTest(StripeTestCase):
invoice_plan(plan, self.next_year)
stripe_customer_id = plan.customer.stripe_customer_id
assert stripe_customer_id is not None
[invoice0, invoice1] = stripe.Invoice.list(customer=stripe_customer_id)
[invoice0, invoice1] = iter(stripe.Invoice.list(customer=stripe_customer_id))
self.assertEqual(invoice0.collection_method, "send_invoice")
[item] = invoice0.lines
[item] = iter(invoice0.lines)
line_item_params = {
"amount": 100,
"description": "Zulip Cloud Standard - renewal",
@@ -4920,11 +4932,11 @@ class TestSupportBillingHelpers(StripeTestCase):
self.upgrade()
customer = Customer.objects.first()
assert customer is not None
[charge] = stripe.Charge.list(customer=customer.stripe_customer_id)
[charge] = iter(stripe.Charge.list(customer=customer.stripe_customer_id))
self.assertEqual(1200 * self.seat_count, charge.amount)
stripe_customer_id = customer.stripe_customer_id
assert stripe_customer_id is not None
[invoice] = stripe.Invoice.list(customer=stripe_customer_id)
[invoice] = iter(stripe.Invoice.list(customer=stripe_customer_id))
self.assertEqual(
[1200 * self.seat_count, -1200 * self.seat_count],
[item.amount for item in invoice.lines],
@@ -4938,11 +4950,11 @@ class TestSupportBillingHelpers(StripeTestCase):
attach_discount_to_realm(user.realm, Decimal(25), acting_user=support_admin)
with patch("corporate.lib.stripe.timezone_now", return_value=self.now):
self.upgrade(license_management="automatic", billing_modality="charge_automatically")
[charge, _] = stripe.Charge.list(customer=customer.stripe_customer_id)
[charge, _] = iter(stripe.Charge.list(customer=customer.stripe_customer_id))
self.assertEqual(6000 * self.seat_count, charge.amount)
stripe_customer_id = customer.stripe_customer_id
assert stripe_customer_id is not None
[invoice, _] = stripe.Invoice.list(customer=stripe_customer_id)
[invoice, _] = iter(stripe.Invoice.list(customer=stripe_customer_id))
self.assertEqual(
[6000 * self.seat_count, -6000 * self.seat_count],
[item.amount for item in invoice.lines],
@@ -4958,7 +4970,7 @@ class TestSupportBillingHelpers(StripeTestCase):
invoice_plans_as_needed(self.next_year + timedelta(days=10))
stripe_customer_id = customer.stripe_customer_id
assert stripe_customer_id is not None
[invoice, _, _] = stripe.Invoice.list(customer=stripe_customer_id)
[invoice, _, _] = iter(stripe.Invoice.list(customer=stripe_customer_id))
self.assertEqual([4000 * self.seat_count], [item.amount for item in invoice.lines])
realm_audit_log = RealmAuditLog.objects.filter(
event_type=RealmAuditLog.REALM_DISCOUNT_CHANGED

View File

@@ -51,11 +51,14 @@ CARD_CAPITALIZATION = {
# Should only be called if the customer is being charged automatically
def payment_method_string(stripe_customer: stripe.Customer) -> str:
assert stripe_customer.invoice_settings is not None
default_payment_method = stripe_customer.invoice_settings.default_payment_method
if default_payment_method is None:
return _("No payment method on file.")
assert isinstance(default_payment_method, stripe.PaymentMethod)
if default_payment_method.type == "card":
assert default_payment_method.card is not None
brand_name = default_payment_method.card.brand
if brand_name in CARD_CAPITALIZATION:
brand_name = CARD_CAPITALIZATION[default_payment_method.card.brand]

View File

@@ -6,6 +6,7 @@ from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.http import HttpRequest, HttpResponse
from django.views.decorators.csrf import csrf_exempt
from stripe.webhook import Webhook
from corporate.lib.stripe import STRIPE_API_VERSION
from corporate.lib.stripe_event_handler import (
@@ -27,7 +28,7 @@ def stripe_webhook(request: HttpRequest) -> HttpResponse:
): # nocoverage: We can't verify the signature in test suite since we fetch the events
# from Stripe events API and manually post to the webhook endpoint.
try:
stripe_event = stripe.Webhook.construct_event(
stripe_event = Webhook.construct_event(
request.body,
request.headers["Stripe-Signature"],
stripe_webhook_endpoint_secret,