From 1c93bd637526a031c3823e2779c66264cc9d526e Mon Sep 17 00:00:00 2001 From: Andy McKay Date: Wed, 7 Mar 2012 15:36:41 -0800 Subject: [PATCH] cope nicer with paypal errors (bug 726814) --- apps/paypal/decorators.py | 19 ++++++++++++ apps/paypal/tests/test_views.py | 39 ++++++++++++++++++++++++- mkt/developers/views.py | 2 ++ mkt/site/templates/site/500_paypal.html | 35 ++++++++++++++++++++++ mkt/submit/tests/test_views.py | 32 ++++++++++++++++++++ 5 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 apps/paypal/decorators.py create mode 100644 mkt/site/templates/site/500_paypal.html diff --git a/apps/paypal/decorators.py b/apps/paypal/decorators.py new file mode 100644 index 0000000000..5fbf7de2e1 --- /dev/null +++ b/apps/paypal/decorators.py @@ -0,0 +1,19 @@ +import jingo +import paypal + + +def handle_paypal_error(fn): + """Wraps the view so that if a paypal error occurs, you show + a more menaningful error message. May or may not make sense for + all views, so providing as a decorator.""" + def wrapper(request, *args, **kw): + try: + return fn(request, *args, **kw) + except paypal.PaypalError: + # This is specific handling for the submission step. + dest = request.GET.get('dest') + return jingo.render(request, 'site/500_paypal.html', + {'submission': dest == 'submission', + 'addon': kw.get('addon', None)}, + status=500) + return wrapper diff --git a/apps/paypal/tests/test_views.py b/apps/paypal/tests/test_views.py index 255400ae95..913edbb6b7 100644 --- a/apps/paypal/tests/test_views.py +++ b/apps/paypal/tests/test_views.py @@ -9,13 +9,17 @@ from django.core import mail from mock import patch, Mock from nose.tools import eq_ +from pyquery import PyQuery as pq +from test_utils import RequestFactory import amo.tests from amo.urlresolvers import reverse from addons.models import Addon from stats.models import SubscriptionEvent, Contribution from users.models import UserProfile -from paypal import views +from paypal import views, PaypalError +from paypal.decorators import handle_paypal_error + URL_ENCODED = 'application/x-www-form-urlencoded' @@ -344,3 +348,36 @@ class TestEmbeddedPaymentsPaypal(amo.tests.TestCase): def test_email(self, urlopen): self.reversal(urlopen) eq_(len(mail.outbox), 1) + + +class TestDecorators(amo.tests.TestCase): + + def setUp(self): + self.func = Mock() + self.get_request('/') + + def get_request(self, url): + self.request = RequestFactory().get(url) + + @patch('jingo.render') + def test_caught(self, render): + self.func.side_effect = PaypalError + view = handle_paypal_error(self.func) + view(self.request) + eq_(render.call_args[1]['status'], 500) + eq_(render.call_args[0][1], 'site/500_paypal.html') + + def test_not_caught(self): + self.func.side_effect = ZeroDivisionError + view = handle_paypal_error(self.func) + self.assertRaises(ZeroDivisionError, view, self.request) + + @patch('jingo.render') + def test_submission(self, render): + self.get_request('/?dest=submission') + self.func.side_effect = PaypalError + view = handle_paypal_error(self.func) + addon = Addon.objects.create(type=amo.ADDON_WEBAPP) + view(self.request, addon=addon) + eq_(render.call_args[0][2]['submission'], True) + eq_(render.call_args[0][2]['addon'], addon) diff --git a/mkt/developers/views.py b/mkt/developers/views.py index c95196dceb..fe85d807b2 100644 --- a/mkt/developers/views.py +++ b/mkt/developers/views.py @@ -47,6 +47,7 @@ from files.utils import parse_addon from market.models import AddonPremium, Refund, AddonPaymentData from payments.models import InappConfig from paypal.check import Check +from paypal.decorators import handle_paypal_error import paypal from search.views import BaseAjaxSearch from stats.models import Contribution @@ -317,6 +318,7 @@ def paypal_setup_confirm(request, addon_id, addon, webapp, source='paypal'): @write @dev_required(webapp=True, skip_submit_check=True) +@handle_paypal_error def acquire_refund_permission(request, addon_id, addon, webapp=False): """This is the callback from Paypal.""" # Set up our redirects. diff --git a/mkt/site/templates/site/500_paypal.html b/mkt/site/templates/site/500_paypal.html new file mode 100644 index 0000000000..2a896af594 --- /dev/null +++ b/mkt/site/templates/site/500_paypal.html @@ -0,0 +1,35 @@ +{% extends 'base_modal.html' if request.is_ajax() else 'developers/base_impala.html' %} + +{% block title %}{{ hub_page_title(_('Oops')) }}{% endblock %} + +{# TODO(apps): Finalize copy. #} + +{% block content %} +
+
+

Oops!

+
+
+

+ We tried to communicate with PayPal to complete that request, but + we got an error back. It could be on our end or it might be on the other. +

+ {% if submission and addon %} +
+ {{ csrf() }} + +

Setting up PayPal is required if you'd like to sell your app on + the marketplace.

+

+ or + try again. +

+ {% else %} +

You can try refreshing the page, or try again later.

+ {% endif %} +
+
+{% endblock %} + +{# Hide "Internal Server Error" message. #} +{% block outer_content %}{% endblock %} diff --git a/mkt/submit/tests/test_views.py b/mkt/submit/tests/test_views.py index 921880e239..8bc1be8cdf 100644 --- a/mkt/submit/tests/test_views.py +++ b/mkt/submit/tests/test_views.py @@ -10,6 +10,7 @@ from pyquery import PyQuery as pq import waffle import amo +from amo.helpers import urlparams import amo.tests from amo.tests import close_to_now, formset, initial from amo.tests.test_helpers import get_image_path @@ -23,6 +24,7 @@ from files.tests.test_models import UploadTest as BaseUploadTest from market.models import Price import mkt from mkt.submit.models import AppSubmissionChecklist +import paypal from translations.models import Translation from users.models import UserProfile from webapps.models import Webapp @@ -816,6 +818,36 @@ class TestPayments(TestSubmit): eq_(res.status_code, 302) self.assertRedirects(res, self.get_url('done')) + def get_acquire_url(self): + url = self.webapp.get_dev_url('acquire_refund_permission') + return urlparams(url, dest='submission', request_token='foo', + verification_code='foo') + + @mock.patch('paypal.get_permissions_token') + @mock.patch('paypal.get_personal_data') + def test_bounce_result_works(self, get_personal_data, + get_permissions_token): + self.webapp.update(premium_type=amo.ADDON_PREMIUM) + get_permissions_token.return_value = 'foo' + get_personal_data.return_value = {} + res = self.client.get(self.get_acquire_url()) + eq_(res.status_code, 302) + self.assertRedirects(res, self.get_url('payments.confirm')) + + @mock.patch('paypal.get_permissions_token') + def test_bounce_result_fails(self, get_permissions_token): + self.webapp.update(premium_type=amo.ADDON_PREMIUM) + get_permissions_token.side_effect = paypal.PaypalError + res = self.client.get(self.get_acquire_url()) + eq_(res.status_code, 500) + self.assertTemplateUsed(res, 'site/500_paypal.html') + + doc = pq(res.content) + eq_(doc('div.prose form a').attr('href'), + self.get_url('payments.bounce')) + eq_(doc('div.prose form').attr('action'), + self.get_url('payments.paypal')) + def test_bad_paypal(self): # some tests for when it goes wrong raise SkipTest