Merge pull request #904 from davidbgk/891000-status-code-409

Return a different status code for "already purchased" (bug 891000)
This commit is contained in:
Andy McKay 2013-07-23 07:52:44 -07:00
Родитель fa48d8296e a6d0b9238f
Коммит 00789340eb
8 изменённых файлов: 34 добавлений и 34 удалений

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

@ -6,8 +6,8 @@ from django.shortcuts import get_object_or_404
import waffle
from addons.models import Addon
import commonware.log
from addons.models import Addon
log = commonware.log.getLogger('mkt.purchase')
@ -90,17 +90,6 @@ def has_purchased_or_refunded(f):
return wrapper
def has_not_purchased(f):
""" The opposite of has_purchased. """
@functools.wraps(f)
def wrapper(request, addon, *args, **kw):
if addon.is_premium() and addon.has_purchased(request.amo_user):
log.info('Already purchased: %d' % addon.pk)
raise PermissionDenied
return f(request, addon, *args, **kw)
return wrapper
def can_become_premium(f):
"""Check that the addon can become premium."""
@functools.wraps(f)

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

@ -6,8 +6,8 @@ from nose.tools import eq_
from test_utils import RequestFactory
import amo.tests
from addons.models import Addon
from addons import decorators as dec
from addons.models import Addon
class TestAddonView(amo.tests.TestCase):
@ -126,16 +126,3 @@ class TestPremiumDecorators(amo.tests.TestCase):
self.addon.has_purchased.return_value = False
with self.assertRaises(PermissionDenied):
view(self.request, self.addon)
def test_has_not_purchased(self):
view = dec.has_not_purchased(self.func)
self.addon.is_premium.return_value = True
self.addon.has_purchased.return_value = False
eq_(view(self.request, self.addon), True)
def test_has_not_purchased_failure(self):
view = dec.has_not_purchased(self.func)
self.addon.is_premium.return_value = True
self.addon.has_purchased.return_value = True
with self.assertRaises(PermissionDenied):
view(self.request, self.addon)

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

@ -289,8 +289,8 @@ Produces the JWT that is passed to `navigator.mozPay`_.
:status 201: successfully completed.
:status 401: not authenticated.
:status 403: app cannot be purchased. This could be because the app has
already been purchased.
:status 403: app cannot be purchased.
:status 409: app already purchased.
.. _payment-status-label:

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

@ -12,19 +12,20 @@ from django.http import HttpResponseNotFound
import commonware.log
from rest_framework.routers import Route, SimpleRouter
from rest_framework.relations import HyperlinkedRelatedField, SlugRelatedField
from rest_framework.relations import HyperlinkedRelatedField
from rest_framework.viewsets import GenericViewSet
from tastypie import fields, http
from tastypie.bundle import Bundle
from tastypie.exceptions import (ImmediateHttpResponse, NotFound,
UnsupportedFormat)
from tastypie.fields import ToOneField
from tastypie.http import HttpConflict
from tastypie.resources import ModelResource, Resource
from access import acl
from translations.fields import PurifiedField, TranslatedField
from .exceptions import DeserializationError
from .exceptions import AlreadyPurchased, DeserializationError
from .http import HttpTooManyRequests
from .serializers import Serializer
@ -120,6 +121,9 @@ class Marketplace(object):
# Reraise PermissionDenied as 403, otherwise you get 500.
raise http_error(http.HttpForbidden, 'Permission denied.')
except AlreadyPurchased:
raise http_error(HttpConflict, 'Already purchased app.')
def non_form_errors(self, error_list):
"""
Raises passed field errors as an immediate HttpBadRequest response.

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

@ -4,3 +4,7 @@ from tastypie.exceptions import TastypieError
class DeserializationError(TastypieError):
def __init__(self, original=None):
self.original = original
class AlreadyPurchased(Exception):
pass

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

@ -17,6 +17,8 @@ import amo
from amo.helpers import absolutify
from amo.tests import TestCase
from amo.urlresolvers import reverse
from market.models import AddonPurchase
from mkt.api.exceptions import AlreadyPurchased
from stats.models import Contribution
from utils import PurchaseTest
@ -105,6 +107,14 @@ class TestPurchase(PurchaseTest):
args=[self.addon.app_slug, uuid_]))
eq_(data['status'], 'incomplete')
def test_status_for_already_purchased(self):
AddonPurchase.objects.create(addon=self.addon,
user=self.user,
type=amo.CONTRIB_PURCHASE)
with self.assertRaises(AlreadyPurchased):
self.client.post(self.prepare_pay)
def test_pay_status_for_unknown_contrib(self):
data = self.get(reverse('webpay.pay_status',
args=[self.addon.app_slug, '<garbage>']))

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

@ -14,8 +14,7 @@ import bleach
import commonware.log
from tower import ugettext as _
from addons.decorators import (addon_view_factory, can_be_purchased,
has_not_purchased)
from addons.decorators import addon_view_factory, can_be_purchased
import amo
from amo.decorators import json_view, login_required, post_required, write
from amo.helpers import absolutify
@ -23,6 +22,7 @@ from amo.urlresolvers import reverse
from lib.cef_loggers import app_pay_cef
from lib.crypto.webpay import (InvalidSender, parse_from_webpay,
sign_webpay_jwt)
from mkt.api.exceptions import AlreadyPurchased
from mkt.webapps.models import Webapp
from stats.models import ClientData, Contribution
@ -71,9 +71,12 @@ def prepare_pay(request, addon):
@can_be_purchased
@has_not_purchased
def _prepare_pay(request, addon):
"""Prepare a JWT to pass into navigator.pay()"""
if addon.is_premium() and addon.has_purchased(request.amo_user):
log.info('Already purchased: %d' % addon.pk)
raise AlreadyPurchased
amount, currency, uuid_, contrib_for = start_purchase(request, addon)
log.debug('Storing contrib for uuid: %s' % uuid_)
Contribution.objects.create(addon_id=addon.id, amount=amount,

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

@ -52,8 +52,11 @@ class TestPrepare(PurchaseTest, BaseOAuth):
@patch('mkt.webapps.models.Webapp.has_purchased')
def test_already_purchased(self, has_purchased):
has_purchased.return_value = True
self.setup_base()
self.setup_package()
res = self.client.post(self.list_url, data=json.dumps({'app': 337141}))
eq_(res.status_code, 403)
eq_(res.status_code, 409)
eq_(res.content, '{"reason": "Already purchased app."}')
def _post(self):
return self.client.post(self.list_url,