billing: Update stripe error handling code.

This commit is contained in:
Rishi Gupta 2018-08-06 17:07:26 -04:00
parent aab977dbf8
commit e804c563a8
3 changed files with 42 additions and 11 deletions

View File

@ -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):
http_status: str
json_body: Dict[str, Any]
class CardError(Exception):
http_status: str
class InvalidRequestError(Exception):
class CardError(StripeError):
...
class RateLimitError(StripeError):
...
class InvalidRequestError(StripeError):
...
class AuthenticationError(StripeError):
...
class APIConnectionError(StripeError):
...

View File

@ -73,6 +73,12 @@ class BillingError(Exception):
self.description = description
self.message = message
class StripeCardError(BillingError):
pass
class StripeConnectionError(BillingError):
pass
def catch_stripe_errors(func: CallableT) -> CallableT:
@wraps(func)
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")
try:
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:
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):
raise BillingError('card error', e.json_body.get('error', {}).get('message'))
else:
raise BillingError('other stripe error', BillingError.CONTACT_SUPPORT)
# TODO: Look into i18n for this
raise StripeCardError('card error', err.get('message'))
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
@catch_stripe_errors

View File

@ -69,10 +69,11 @@ class StripeTest(ZulipTestCase):
return match.group(1) if match else None
@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
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:
raise_invalid_request_error()
self.assertEqual('other stripe error', context.exception.description)
@ -84,7 +85,7 @@ class StripeTest(ZulipTestCase):
json_body = {"error": {"message": error_message}}
raise stripe.error.CardError(error_message, "number", "invalid_number",
json_body=json_body)
with self.assertRaises(BillingError) as context:
with self.assertRaises(StripeCardError) as context:
raise_card_error()
self.assertIn('not a valid credit card', context.exception.message)
self.assertEqual('card error', context.exception.description)