mirror of https://github.com/zulip/zulip.git
billing: Update stripe error handling code.
This commit is contained in:
parent
aab977dbf8
commit
e804c563a8
|
@ -1,8 +1,22 @@
|
||||||
|
from typing import Any, Dict
|
||||||
|
|
||||||
|
# List of StripeError's from https://stripe.com/docs/api/python#error_handling
|
||||||
|
|
||||||
class StripeError(Exception):
|
class StripeError(Exception):
|
||||||
http_status: str
|
http_status: str
|
||||||
|
json_body: Dict[str, Any]
|
||||||
|
|
||||||
class CardError(Exception):
|
class CardError(StripeError):
|
||||||
http_status: str
|
...
|
||||||
|
|
||||||
class InvalidRequestError(Exception):
|
class RateLimitError(StripeError):
|
||||||
|
...
|
||||||
|
|
||||||
|
class InvalidRequestError(StripeError):
|
||||||
|
...
|
||||||
|
|
||||||
|
class AuthenticationError(StripeError):
|
||||||
|
...
|
||||||
|
|
||||||
|
class APIConnectionError(StripeError):
|
||||||
...
|
...
|
||||||
|
|
|
@ -73,6 +73,12 @@ class BillingError(Exception):
|
||||||
self.description = description
|
self.description = description
|
||||||
self.message = message
|
self.message = message
|
||||||
|
|
||||||
|
class StripeCardError(BillingError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class StripeConnectionError(BillingError):
|
||||||
|
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:
|
||||||
|
@ -85,12 +91,22 @@ def catch_stripe_errors(func: CallableT) -> CallableT:
|
||||||
"Plan objects not created. Please run ./manage.py setup_stripe")
|
"Plan objects not created. Please run ./manage.py setup_stripe")
|
||||||
try:
|
try:
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
|
# See https://stripe.com/docs/api/python#error_handling, though
|
||||||
|
# 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:
|
except stripe.error.StripeError as e:
|
||||||
billing_logger.error("Stripe error: %d %s", e.http_status, e.__class__.__name__)
|
err = e.json_body.get('error', {})
|
||||||
|
billing_logger.error("Stripe error: %s %s %s %s" % (
|
||||||
|
e.http_status, err.get('type'), err.get('code'), err.get('param')))
|
||||||
if isinstance(e, stripe.error.CardError):
|
if isinstance(e, stripe.error.CardError):
|
||||||
raise BillingError('card error', e.json_body.get('error', {}).get('message'))
|
# TODO: Look into i18n for this
|
||||||
else:
|
raise StripeCardError('card error', err.get('message'))
|
||||||
raise BillingError('other stripe error', BillingError.CONTACT_SUPPORT)
|
if isinstance(e, stripe.error.RateLimitError) or \
|
||||||
|
isinstance(e, stripe.error.APIConnectionError): # nocoverage TODO
|
||||||
|
raise StripeConnectionError(
|
||||||
|
'stripe connection error',
|
||||||
|
_("Something went wrong. Please wait a few seconds and try again."))
|
||||||
|
raise BillingError('other stripe error', BillingError.CONTACT_SUPPORT)
|
||||||
return wrapped # type: ignore # https://github.com/python/mypy/issues/1927
|
return wrapped # type: ignore # https://github.com/python/mypy/issues/1927
|
||||||
|
|
||||||
@catch_stripe_errors
|
@catch_stripe_errors
|
||||||
|
|
|
@ -69,10 +69,11 @@ class StripeTest(ZulipTestCase):
|
||||||
return match.group(1) if match else None
|
return match.group(1) if match else None
|
||||||
|
|
||||||
@mock.patch("zilencer.lib.stripe.billing_logger.error")
|
@mock.patch("zilencer.lib.stripe.billing_logger.error")
|
||||||
def test_errors(self, mock_billing_logger_error: mock.Mock) -> None:
|
def test_catch_stripe_errors(self, mock_billing_logger_error: mock.Mock) -> None:
|
||||||
@catch_stripe_errors
|
@catch_stripe_errors
|
||||||
def raise_invalid_request_error() -> None:
|
def raise_invalid_request_error() -> None:
|
||||||
raise stripe.error.InvalidRequestError("Request req_oJU621i6H6X4Ez: No such token: x", None)
|
raise stripe.error.InvalidRequestError(
|
||||||
|
"Request req_oJU621i6H6X4Ez: No such token: x", None, json_body={})
|
||||||
with self.assertRaises(BillingError) as context:
|
with self.assertRaises(BillingError) as context:
|
||||||
raise_invalid_request_error()
|
raise_invalid_request_error()
|
||||||
self.assertEqual('other stripe error', context.exception.description)
|
self.assertEqual('other stripe error', context.exception.description)
|
||||||
|
@ -84,7 +85,7 @@ class StripeTest(ZulipTestCase):
|
||||||
json_body = {"error": {"message": error_message}}
|
json_body = {"error": {"message": error_message}}
|
||||||
raise stripe.error.CardError(error_message, "number", "invalid_number",
|
raise stripe.error.CardError(error_message, "number", "invalid_number",
|
||||||
json_body=json_body)
|
json_body=json_body)
|
||||||
with self.assertRaises(BillingError) as context:
|
with self.assertRaises(StripeCardError) as context:
|
||||||
raise_card_error()
|
raise_card_error()
|
||||||
self.assertIn('not a valid credit card', context.exception.message)
|
self.assertIn('not a valid credit card', context.exception.message)
|
||||||
self.assertEqual('card error', context.exception.description)
|
self.assertEqual('card error', context.exception.description)
|
||||||
|
|
Loading…
Reference in New Issue