mirror of https://github.com/zulip/zulip.git
billing: Raise exceptions instead of returning errors in upgrade flow.
This commit is contained in:
parent
8643c681f0
commit
e06957bef5
|
@ -67,6 +67,9 @@ def unsign_string(signed_string: str, salt: str) -> str:
|
||||||
class StripeError(JsonableError):
|
class StripeError(JsonableError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class BillingError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
def catch_stripe_errors(func: CallableT) -> CallableT:
|
def catch_stripe_errors(func: CallableT) -> CallableT:
|
||||||
@wraps(func)
|
@wraps(func)
|
||||||
def wrapped(*args: Any, **kwargs: Any) -> Any:
|
def wrapped(*args: Any, **kwargs: Any) -> Any:
|
||||||
|
@ -147,7 +150,7 @@ def do_subscribe_customer_to_plan(stripe_customer: stripe.Customer, stripe_plan_
|
||||||
billing_logger.error("Stripe customer %s trying to subscribe to %s, "
|
billing_logger.error("Stripe customer %s trying to subscribe to %s, "
|
||||||
"but has an active subscription" % (stripe_customer.id, stripe_plan_id))
|
"but has an active subscription" % (stripe_customer.id, stripe_plan_id))
|
||||||
# TODO: Change to an error sent to the frontend
|
# TODO: Change to an error sent to the frontend
|
||||||
raise AssertionError("Customer already has an active subscription.")
|
raise BillingError("Your organization has an existing active subscription.")
|
||||||
stripe_subscription = stripe.Subscription.create(
|
stripe_subscription = stripe.Subscription.create(
|
||||||
customer=stripe_customer.id,
|
customer=stripe_customer.id,
|
||||||
billing='charge_automatically',
|
billing='charge_automatically',
|
||||||
|
@ -180,17 +183,17 @@ def do_subscribe_customer_to_plan(stripe_customer: stripe.Customer, stripe_plan_
|
||||||
extra_data=ujson.dumps({'quantity': current_seat_count}))
|
extra_data=ujson.dumps({'quantity': current_seat_count}))
|
||||||
|
|
||||||
def process_initial_upgrade(user: UserProfile, plan: str, signed_seat_count: str,
|
def process_initial_upgrade(user: UserProfile, plan: str, signed_seat_count: str,
|
||||||
salt: str, stripe_token: str) -> Optional[str]:
|
salt: str, stripe_token: str) -> None:
|
||||||
if plan not in [Plan.CLOUD_ANNUAL, Plan.CLOUD_MONTHLY]:
|
if plan not in [Plan.CLOUD_ANNUAL, Plan.CLOUD_MONTHLY]:
|
||||||
billing_logger.warning("Tampered plan during realm upgrade. user: %s, realm: %s (%s)."
|
billing_logger.warning("Tampered plan during realm upgrade. user: %s, realm: %s (%s)."
|
||||||
% (user.id, user.realm.id, user.realm.string_id))
|
% (user.id, user.realm.id, user.realm.string_id))
|
||||||
return "Something went wrong. Please contact support@zulipchat.com"
|
raise BillingError("Something went wrong. Please contact support@zulipchat.com")
|
||||||
try:
|
try:
|
||||||
seat_count = int(unsign_string(signed_seat_count, salt))
|
seat_count = int(unsign_string(signed_seat_count, salt))
|
||||||
except signing.BadSignature:
|
except signing.BadSignature:
|
||||||
billing_logger.warning("Tampered seat count during realm upgrade. user: %s, realm: %s (%s)."
|
billing_logger.warning("Tampered seat count during realm upgrade. user: %s, realm: %s (%s)."
|
||||||
% (user.id, user.realm.id, user.realm.string_id))
|
% (user.id, user.realm.id, user.realm.string_id))
|
||||||
return "Something went wrong. Please contact support@zulipchat.com"
|
raise BillingError("Something went wrong. Please contact support@zulipchat.com")
|
||||||
|
|
||||||
stripe_customer = do_create_customer_with_payment_source(user, stripe_token)
|
stripe_customer = do_create_customer_with_payment_source(user, stripe_token)
|
||||||
do_subscribe_customer_to_plan(
|
do_subscribe_customer_to_plan(
|
||||||
|
@ -200,5 +203,3 @@ def process_initial_upgrade(user: UserProfile, plan: str, signed_seat_count: str
|
||||||
# TODO: billing address details are passed to us in the request;
|
# TODO: billing address details are passed to us in the request;
|
||||||
# use that to calculate taxes.
|
# use that to calculate taxes.
|
||||||
tax_percent=0)
|
tax_percent=0)
|
||||||
# TODO: check for errors and raise/send to frontend
|
|
||||||
return None
|
|
||||||
|
|
|
@ -14,7 +14,8 @@ from zerver.lib.timestamp import timestamp_to_datetime
|
||||||
from zerver.models import Realm, UserProfile, get_realm, RealmAuditLog
|
from zerver.models import Realm, UserProfile, get_realm, RealmAuditLog
|
||||||
from zilencer.lib.stripe import StripeError, catch_stripe_errors, \
|
from zilencer.lib.stripe import StripeError, catch_stripe_errors, \
|
||||||
do_create_customer_with_payment_source, do_subscribe_customer_to_plan, \
|
do_create_customer_with_payment_source, do_subscribe_customer_to_plan, \
|
||||||
get_seat_count, extract_current_subscription, sign_string, unsign_string
|
get_seat_count, extract_current_subscription, sign_string, unsign_string, \
|
||||||
|
BillingError
|
||||||
from zilencer.models import Customer, Plan
|
from zilencer.models import Customer, Plan
|
||||||
|
|
||||||
fixture_data_file = open(os.path.join(os.path.dirname(__file__), 'stripe_fixtures.json'), 'r')
|
fixture_data_file = open(os.path.join(os.path.dirname(__file__), 'stripe_fixtures.json'), 'r')
|
||||||
|
@ -264,7 +265,7 @@ class StripeTest(ZulipTestCase):
|
||||||
|
|
||||||
@mock.patch("stripe.Customer.retrieve", side_effect=mock_customer_with_active_subscription)
|
@mock.patch("stripe.Customer.retrieve", side_effect=mock_customer_with_active_subscription)
|
||||||
def test_subscribe_customer_to_second_plan(self, mock_customer_with_active_subscription: mock.Mock) -> None:
|
def test_subscribe_customer_to_second_plan(self, mock_customer_with_active_subscription: mock.Mock) -> None:
|
||||||
with self.assertRaisesRegex(AssertionError, "Customer already has an active subscription."):
|
with self.assertRaisesRegex(BillingError, "Your organization has an existing active subscription."):
|
||||||
do_subscribe_customer_to_plan(stripe.Customer.retrieve(), # type: ignore # Mocked out function call
|
do_subscribe_customer_to_plan(stripe.Customer.retrieve(), # type: ignore # Mocked out function call
|
||||||
self.stripe_plan_id, self.quantity, 0)
|
self.stripe_plan_id, self.quantity, 0)
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,8 @@ from zerver.models import UserProfile, Realm
|
||||||
from zerver.views.push_notifications import validate_token
|
from zerver.views.push_notifications import validate_token
|
||||||
from zilencer.lib.stripe import STRIPE_PUBLISHABLE_KEY, StripeError, \
|
from zilencer.lib.stripe import STRIPE_PUBLISHABLE_KEY, StripeError, \
|
||||||
get_stripe_customer, get_upcoming_invoice, get_seat_count, \
|
get_stripe_customer, get_upcoming_invoice, get_seat_count, \
|
||||||
extract_current_subscription, process_initial_upgrade, sign_string
|
extract_current_subscription, process_initial_upgrade, sign_string, \
|
||||||
|
BillingError
|
||||||
from zilencer.models import RemotePushDeviceToken, RemoteZulipServer, \
|
from zilencer.models import RemotePushDeviceToken, RemoteZulipServer, \
|
||||||
Customer, Plan
|
Customer, Plan
|
||||||
|
|
||||||
|
@ -169,10 +170,14 @@ def initial_upgrade(request: HttpRequest) -> HttpResponse:
|
||||||
return HttpResponseRedirect(reverse('zilencer.views.billing_home'))
|
return HttpResponseRedirect(reverse('zilencer.views.billing_home'))
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
error_message = process_initial_upgrade(user, request.POST['plan'],
|
try:
|
||||||
request.POST['signed_seat_count'],
|
process_initial_upgrade(user, request.POST['plan'], request.POST['signed_seat_count'],
|
||||||
request.POST['salt'], request.POST['stripeToken']) or ""
|
request.POST['salt'], request.POST['stripeToken'])
|
||||||
if not error_message:
|
except (BillingError, StripeError) as e:
|
||||||
|
error_message = str(e)
|
||||||
|
except Exception:
|
||||||
|
error_message = "Something went wrong. Please contact support@zulipchat.com."
|
||||||
|
else:
|
||||||
return HttpResponseRedirect(reverse('zilencer.views.billing_home'))
|
return HttpResponseRedirect(reverse('zilencer.views.billing_home'))
|
||||||
|
|
||||||
seat_count = get_seat_count(user.realm)
|
seat_count = get_seat_count(user.realm)
|
||||||
|
|
Loading…
Reference in New Issue