mirror of https://github.com/zulip/zulip.git
stripe: Move error handling into stripe.py too.
This completes the separation of our logic for managing Stripe customers from the view code for the billing page. As we add more features to our Customer model and to our Stripe integration, we might further separate those two things; but for now they're nearly synonymous and there's no problem in them being mixed together.
This commit is contained in:
parent
0b81762350
commit
f9b12952f8
|
@ -1,12 +1,13 @@
|
||||||
|
from functools import wraps
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
from typing import Any, Callable, TypeVar
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
import stripe
|
import stripe
|
||||||
from stripe.error import CardError, RateLimitError, InvalidRequestError, \
|
|
||||||
AuthenticationError, APIConnectionError, StripeError
|
|
||||||
|
|
||||||
|
from zerver.lib.exceptions import JsonableError
|
||||||
from zerver.lib.logging_util import log_to_file
|
from zerver.lib.logging_util import log_to_file
|
||||||
from zerver.models import Realm, UserProfile
|
from zerver.models import Realm, UserProfile
|
||||||
from zilencer.models import Customer
|
from zilencer.models import Customer
|
||||||
|
@ -24,6 +25,35 @@ billing_logger = logging.getLogger('zilencer.stripe')
|
||||||
log_to_file(billing_logger, BILLING_LOG_PATH)
|
log_to_file(billing_logger, BILLING_LOG_PATH)
|
||||||
log_to_file(logging.getLogger('stripe'), BILLING_LOG_PATH)
|
log_to_file(logging.getLogger('stripe'), BILLING_LOG_PATH)
|
||||||
|
|
||||||
|
CallableT = TypeVar('CallableT', bound=Callable[..., Any])
|
||||||
|
|
||||||
|
class StripeError(JsonableError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def catch_stripe_errors(func: CallableT) -> CallableT:
|
||||||
|
@wraps(func)
|
||||||
|
def wrapped(*args: Any, **kwargs: Any) -> Any:
|
||||||
|
if STRIPE_PUBLISHABLE_KEY is None:
|
||||||
|
# Dev-only message; no translation needed.
|
||||||
|
raise StripeError(
|
||||||
|
"Missing Stripe config. In dev, add to zproject/dev-secrets.conf .")
|
||||||
|
try:
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
except stripe.error.StripeError as e:
|
||||||
|
billing_logger.error("Stripe error: %d %s",
|
||||||
|
e.http_status, e.__class__.__name__)
|
||||||
|
if isinstance(e, stripe.error.CardError):
|
||||||
|
raise StripeError(e.json_body.get('error', {}).get('message'))
|
||||||
|
else:
|
||||||
|
raise StripeError(
|
||||||
|
_("Something went wrong. Please try again or email us at %s.")
|
||||||
|
% (settings.ZULIP_ADMINISTRATOR,))
|
||||||
|
except Exception as e:
|
||||||
|
billing_logger.exception("Uncaught error in Stripe integration")
|
||||||
|
raise
|
||||||
|
return wrapped # type: ignore # https://github.com/python/mypy/issues/1927
|
||||||
|
|
||||||
|
@catch_stripe_errors
|
||||||
def count_stripe_cards(realm: Realm) -> int:
|
def count_stripe_cards(realm: Realm) -> int:
|
||||||
try:
|
try:
|
||||||
customer_obj = Customer.objects.get(realm=realm)
|
customer_obj = Customer.objects.get(realm=realm)
|
||||||
|
@ -32,6 +62,7 @@ def count_stripe_cards(realm: Realm) -> int:
|
||||||
except Customer.DoesNotExist:
|
except Customer.DoesNotExist:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
@catch_stripe_errors
|
||||||
def save_stripe_token(user: UserProfile, token: str) -> int:
|
def save_stripe_token(user: UserProfile, token: str) -> int:
|
||||||
"""Returns total number of cards."""
|
"""Returns total number of cards."""
|
||||||
# The card metadata doesn't show up in Dashboard but can be accessed
|
# The card metadata doesn't show up in Dashboard but can be accessed
|
||||||
|
|
|
@ -19,7 +19,7 @@ from zerver.lib.validator import check_int
|
||||||
from zerver.models import UserProfile, Realm
|
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, count_stripe_cards, \
|
from zilencer.lib.stripe import STRIPE_PUBLISHABLE_KEY, count_stripe_cards, \
|
||||||
save_stripe_token, CardError, StripeError, billing_logger
|
save_stripe_token, StripeError
|
||||||
from zilencer.models import RemotePushDeviceToken, RemoteZulipServer
|
from zilencer.models import RemotePushDeviceToken, RemoteZulipServer
|
||||||
|
|
||||||
def validate_entity(entity: Union[UserProfile, RemoteZulipServer]) -> None:
|
def validate_entity(entity: Union[UserProfile, RemoteZulipServer]) -> None:
|
||||||
|
@ -112,16 +112,11 @@ def add_payment_method(request: HttpRequest) -> HttpResponse:
|
||||||
"email": user.email,
|
"email": user.email,
|
||||||
} # type: Dict[str, Any]
|
} # type: Dict[str, Any]
|
||||||
|
|
||||||
def render_error(message: str) -> HttpResponse:
|
|
||||||
ctx["error_message"] = message
|
|
||||||
return render(request, 'zilencer/billing.html', context=ctx)
|
|
||||||
|
|
||||||
if not user.is_realm_admin:
|
if not user.is_realm_admin:
|
||||||
return render_error(_("You should be an administrator of the organization %s to view this page.")
|
ctx["error_message"] = (
|
||||||
|
_("You should be an administrator of the organization %s to view this page.")
|
||||||
% (user.realm.name,))
|
% (user.realm.name,))
|
||||||
if STRIPE_PUBLISHABLE_KEY is None:
|
return render(request, 'zilencer/billing.html', context=ctx)
|
||||||
# Dev-only message; no translation needed.
|
|
||||||
return render_error("Missing Stripe config. In dev, add to zproject/dev-secrets.conf .")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
|
@ -133,12 +128,5 @@ def add_payment_method(request: HttpRequest) -> HttpResponse:
|
||||||
ctx["payment_method_added"] = True
|
ctx["payment_method_added"] = True
|
||||||
return render(request, 'zilencer/billing.html', context=ctx)
|
return render(request, 'zilencer/billing.html', context=ctx)
|
||||||
except StripeError as e:
|
except StripeError as e:
|
||||||
billing_logger.error("Stripe error: %d %s", e.http_status, e.__class__.__name__)
|
ctx["error_message"] = e.msg
|
||||||
if isinstance(e, CardError):
|
return render(request, 'zilencer/billing.html', context=ctx)
|
||||||
return render_error(e.json_body.get('error', {}).get('message'))
|
|
||||||
else:
|
|
||||||
return render_error(_("Something went wrong. Please try again or email us at %s.")
|
|
||||||
% (settings.ZULIP_ADMINISTRATOR,))
|
|
||||||
except Exception as e:
|
|
||||||
billing_logger.exception("Uncaught error in billing")
|
|
||||||
raise
|
|
||||||
|
|
Loading…
Reference in New Issue