Handle error cases better for external services
This commit is contained in:
parent
09a1c4d2b5
commit
99d75bede7
5 changed files with 54 additions and 20 deletions
|
@ -4,7 +4,7 @@ from flask import request
|
||||||
from app import billing
|
from app import billing
|
||||||
from endpoints.api import (resource, nickname, ApiResource, validate_json_request, log_action,
|
from endpoints.api import (resource, nickname, ApiResource, validate_json_request, log_action,
|
||||||
related_user_resource, internal_only, Unauthorized, NotFound,
|
related_user_resource, internal_only, Unauthorized, NotFound,
|
||||||
require_user_admin, show_if, hide_if)
|
require_user_admin, show_if, hide_if, abort)
|
||||||
from endpoints.api.subscribe import subscribe, subscription_view
|
from endpoints.api.subscribe import subscribe, subscription_view
|
||||||
from auth.permissions import AdministerOrganizationPermission
|
from auth.permissions import AdministerOrganizationPermission
|
||||||
from auth.auth_context import get_authenticated_user
|
from auth.auth_context import get_authenticated_user
|
||||||
|
@ -23,7 +23,11 @@ def get_card(user):
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.stripe_id:
|
if user.stripe_id:
|
||||||
|
try:
|
||||||
cus = billing.Customer.retrieve(user.stripe_id)
|
cus = billing.Customer.retrieve(user.stripe_id)
|
||||||
|
except stripe.APIConnectionError as e:
|
||||||
|
abort(503, message='Cannot contact Stripe')
|
||||||
|
|
||||||
if cus and cus.default_card:
|
if cus and cus.default_card:
|
||||||
# Find the default card.
|
# Find the default card.
|
||||||
default_card = None
|
default_card = None
|
||||||
|
@ -46,7 +50,11 @@ def get_card(user):
|
||||||
|
|
||||||
def set_card(user, token):
|
def set_card(user, token):
|
||||||
if user.stripe_id:
|
if user.stripe_id:
|
||||||
|
try:
|
||||||
cus = billing.Customer.retrieve(user.stripe_id)
|
cus = billing.Customer.retrieve(user.stripe_id)
|
||||||
|
except stripe.APIConnectionError as e:
|
||||||
|
abort(503, message='Cannot contact Stripe')
|
||||||
|
|
||||||
if cus:
|
if cus:
|
||||||
try:
|
try:
|
||||||
cus.card = token
|
cus.card = token
|
||||||
|
@ -55,6 +63,8 @@ def set_card(user, token):
|
||||||
return carderror_response(exc)
|
return carderror_response(exc)
|
||||||
except stripe.InvalidRequestError as exc:
|
except stripe.InvalidRequestError as exc:
|
||||||
return carderror_response(exc)
|
return carderror_response(exc)
|
||||||
|
except stripe.APIConnectionError as e:
|
||||||
|
return carderror_response(e)
|
||||||
|
|
||||||
return get_card(user)
|
return get_card(user)
|
||||||
|
|
||||||
|
@ -75,7 +85,11 @@ def get_invoices(customer_id):
|
||||||
'plan': i.lines.data[0].plan.id if i.lines.data[0].plan else None
|
'plan': i.lines.data[0].plan.id if i.lines.data[0].plan else None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
invoices = billing.Invoice.all(customer=customer_id, count=12)
|
invoices = billing.Invoice.all(customer=customer_id, count=12)
|
||||||
|
except stripe.APIConnectionError as e:
|
||||||
|
abort(503, message='Cannot contact Stripe')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'invoices': [invoice_view(i) for i in invoices.data]
|
'invoices': [invoice_view(i) for i in invoices.data]
|
||||||
}
|
}
|
||||||
|
@ -228,7 +242,10 @@ class UserPlan(ApiResource):
|
||||||
private_repos = model.get_private_repo_count(user.username)
|
private_repos = model.get_private_repo_count(user.username)
|
||||||
|
|
||||||
if user.stripe_id:
|
if user.stripe_id:
|
||||||
|
try:
|
||||||
cus = billing.Customer.retrieve(user.stripe_id)
|
cus = billing.Customer.retrieve(user.stripe_id)
|
||||||
|
except stripe.APIConnectionError as e:
|
||||||
|
abort(503, message='Cannot contact Stripe')
|
||||||
|
|
||||||
if cus.subscription:
|
if cus.subscription:
|
||||||
return subscription_view(cus.subscription, private_repos)
|
return subscription_view(cus.subscription, private_repos)
|
||||||
|
@ -291,7 +308,10 @@ class OrganizationPlan(ApiResource):
|
||||||
private_repos = model.get_private_repo_count(orgname)
|
private_repos = model.get_private_repo_count(orgname)
|
||||||
organization = model.get_organization(orgname)
|
organization = model.get_organization(orgname)
|
||||||
if organization.stripe_id:
|
if organization.stripe_id:
|
||||||
|
try:
|
||||||
cus = billing.Customer.retrieve(organization.stripe_id)
|
cus = billing.Customer.retrieve(organization.stripe_id)
|
||||||
|
except stripe.APIConnectionError as e:
|
||||||
|
abort(503, message='Cannot contact Stripe')
|
||||||
|
|
||||||
if cus.subscription:
|
if cus.subscription:
|
||||||
return subscription_view(cus.subscription, private_repos)
|
return subscription_view(cus.subscription, private_repos)
|
||||||
|
|
|
@ -15,6 +15,9 @@ logger = logging.getLogger(__name__)
|
||||||
def carderror_response(exc):
|
def carderror_response(exc):
|
||||||
return {'carderror': exc.message}, 402
|
return {'carderror': exc.message}, 402
|
||||||
|
|
||||||
|
def connection_response(exc):
|
||||||
|
return {'message': 'Could not contact Stripe. Please try again.'}, 503
|
||||||
|
|
||||||
|
|
||||||
def subscription_view(stripe_subscription, used_repos):
|
def subscription_view(stripe_subscription, used_repos):
|
||||||
view = {
|
view = {
|
||||||
|
@ -74,19 +77,29 @@ def subscribe(user, plan, token, require_business_plan):
|
||||||
log_action('account_change_plan', user.username, {'plan': plan})
|
log_action('account_change_plan', user.username, {'plan': plan})
|
||||||
except stripe.CardError as e:
|
except stripe.CardError as e:
|
||||||
return carderror_response(e)
|
return carderror_response(e)
|
||||||
|
except stripe.APIConnectionError as e:
|
||||||
|
return connection_response(e)
|
||||||
|
|
||||||
response_json = subscription_view(cus.subscription, private_repos)
|
response_json = subscription_view(cus.subscription, private_repos)
|
||||||
status_code = 201
|
status_code = 201
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Change the plan
|
# Change the plan
|
||||||
|
try:
|
||||||
cus = billing.Customer.retrieve(user.stripe_id)
|
cus = billing.Customer.retrieve(user.stripe_id)
|
||||||
|
except stripe.APIConnectionError as e:
|
||||||
|
return connection_response(e)
|
||||||
|
|
||||||
if plan_found['price'] == 0:
|
if plan_found['price'] == 0:
|
||||||
if cus.subscription is not None:
|
if cus.subscription is not None:
|
||||||
# We only have to cancel the subscription if they actually have one
|
# We only have to cancel the subscription if they actually have one
|
||||||
|
try:
|
||||||
cus.cancel_subscription()
|
cus.cancel_subscription()
|
||||||
cus.save()
|
cus.save()
|
||||||
|
except stripe.APIConnectionError as e:
|
||||||
|
return connection_response(e)
|
||||||
|
|
||||||
|
|
||||||
check_repository_usage(user, plan_found)
|
check_repository_usage(user, plan_found)
|
||||||
log_action('account_change_plan', user.username, {'plan': plan})
|
log_action('account_change_plan', user.username, {'plan': plan})
|
||||||
|
|
||||||
|
@ -101,6 +114,8 @@ def subscribe(user, plan, token, require_business_plan):
|
||||||
cus.save()
|
cus.save()
|
||||||
except stripe.CardError as e:
|
except stripe.CardError as e:
|
||||||
return carderror_response(e)
|
return carderror_response(e)
|
||||||
|
except stripe.APIConnectionError as e:
|
||||||
|
return connection_response(e)
|
||||||
|
|
||||||
response_json = subscription_view(cus.subscription, private_repos)
|
response_json = subscription_view(cus.subscription, private_repos)
|
||||||
check_repository_usage(user, plan_found)
|
check_repository_usage(user, plan_found)
|
||||||
|
|
|
@ -5570,8 +5570,8 @@ quayApp.run(['$location', '$rootScope', 'Restangular', 'UserService', 'PlanServi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Features.BILLING && response.status == 402) {
|
if (response.status == 503) {
|
||||||
$('#overlicenseModal').modal({});
|
$('#cannotContactService').modal({});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,23 +35,18 @@
|
||||||
</div><!-- /.modal-dialog -->
|
</div><!-- /.modal-dialog -->
|
||||||
</div><!-- /.modal -->
|
</div><!-- /.modal -->
|
||||||
|
|
||||||
{% if not has_billing %}
|
|
||||||
<!-- Modal message dialog -->
|
<!-- Modal message dialog -->
|
||||||
<div class="modal fade" id="overlicenseModal" data-backdrop="static">
|
<div class="modal fade" id="cannotContactService" data-backdrop="static">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h4 class="modal-title">Cannot create user</h4>
|
<h4 class="modal-title">Cannot Contact External Service</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
A new user cannot be created as this organization has reached its licensed seat count. Please contact your administrator.
|
A connection to an external service has failed. Please reload the page to try again.
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<a href="javascript:void(0)" class="btn btn-primary" data-dismiss="modal" onclick="location = '/signin'">Sign In</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div><!-- /.modal-content -->
|
</div><!-- /.modal-content -->
|
||||||
</div><!-- /.modal-dialog -->
|
</div><!-- /.modal-dialog -->
|
||||||
</div><!-- /.modal -->
|
</div><!-- /.modal -->
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -30,7 +30,11 @@ class SendToMixpanel(Process):
|
||||||
while True:
|
while True:
|
||||||
mp_request = self._mp_queue.get()
|
mp_request = self._mp_queue.get()
|
||||||
logger.debug('Got queued mixpanel reqeust.')
|
logger.debug('Got queued mixpanel reqeust.')
|
||||||
|
try:
|
||||||
self._consumer.send(*json.loads(mp_request))
|
self._consumer.send(*json.loads(mp_request))
|
||||||
|
except:
|
||||||
|
# Make sure we don't crash if Mixpanel request fails.
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class FakeMixpanel(object):
|
class FakeMixpanel(object):
|
||||||
|
|
Reference in a new issue