cope nicer with paypal errors (bug 726814)

This commit is contained in:
Andy McKay 2012-03-07 15:36:41 -08:00
Родитель 833c48ef4e
Коммит 1c93bd6375
5 изменённых файлов: 126 добавлений и 1 удалений

19
apps/paypal/decorators.py Normal file
Просмотреть файл

@ -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

Просмотреть файл

@ -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)

Просмотреть файл

@ -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.

Просмотреть файл

@ -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 %}
<section class="primary">
<header>
<h1>Oops!</h1>
</header>
<div class="island hero prose">
<p class="paypal-error-message">
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.
</p>
{% if submission and addon %}
<form method="post" action="{{ url('submit.app.payments.paypal', addon.app_slug) }}">
{{ csrf() }}
<input type="hidden" name="business_account" value="later" />
<p>Setting up PayPal is required if you'd like to sell your app on
the marketplace.</p>
<p>
<input type="submit" class="button" value="Setup PayPal later"> or
<a href="{{ url('submit.app.payments.bounce', addon.app_slug) }}">try again</a>.
</p>
{% else %}
<p>You can try refreshing the page, or try again later.</p>
{% endif %}
</div>
</section>
{% endblock %}
{# Hide "Internal Server Error" message. #}
{% block outer_content %}{% endblock %}

Просмотреть файл

@ -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