add `price` and `payment_account` as settable fields on webapps. (bug 869557) (bug 869559)

This commit is contained in:
Allen Short 2013-05-16 00:22:57 -07:00
Родитель b539b43b09
Коммит 57eb235d45
6 изменённых файлов: 196 добавлений и 5 удалений

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

@ -180,10 +180,15 @@ App
refer to Android mobile and tablet. As opposed to Firefox OS.
:param required premium_type: One of `free`, `premium`,
`free-inapp`, `premium-inapp`, or `other`.
:param optional price: The price for your app as a string, for example
"0.10". Required for `premium` or `premium-inapp` apps.
:param optional payment_account: The path for the
:ref:`payment account <payment-account-label>` resource you want to
associate with this app.
**Response**
:status 201: successfully updated.
:status 202: successfully updated.
.. http:delete:: /api/v1/apps/app/(int:id)/

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

@ -7,6 +7,8 @@ Payments
This API is specific to setting up and processing payments for an app in the
Marketplace.
.. _payment-account-label:
Configuring payment accounts
============================

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

@ -25,6 +25,7 @@ from amo.utils import no_translation
from constants.applications import DEVICE_TYPES
from files.models import FileUpload, Platform
from lib.metrics import record_action
from market.models import AddonPremium, Price
from mkt.api.authentication import (OptionalOAuthAuthentication,
OAuthAuthentication)
@ -38,6 +39,7 @@ from mkt.api.http import HttpLegallyUnavailable
from mkt.carriers import get_carrier_id, CARRIERS, CARRIER_MAP
from mkt.developers import tasks
from mkt.developers.forms import NewManifestForm, PreviewForm
from mkt.developers.models import AddonPaymentAccount
from mkt.regions import get_region_id, get_region, REGIONS_DICT
from mkt.submit.forms import AppDetailsBasicForm
from mkt.webapps.utils import app_to_dict
@ -109,14 +111,17 @@ class ValidationResource(CORSResource, MarketplaceModelResource):
class AppResource(CORSResource, MarketplaceModelResource):
payment_account = fields.ToOneField('mkt.developers.api.AccountResource',
'app_payment_account', null=True)
premium_type = fields.IntegerField(null=True)
previews = fields.ToManyField('mkt.api.resources.PreviewResource',
'previews', readonly=True)
premium_type = fields.IntegerField()
class Meta(MarketplaceModelResource.Meta):
queryset = Webapp.objects.all() # Gets overriden in dispatch.
fields = ['categories', 'description', 'device_types', 'homepage',
'id', 'name', 'premium_type', 'privacy_policy',
'id', 'name', 'payment_account', 'premium_type',
'privacy_policy',
'status', 'summary', 'support_email', 'support_url']
list_allowed_methods = ['get', 'post']
detail_allowed_methods = ['get', 'put', 'delete']
@ -241,7 +246,8 @@ class AppResource(CORSResource, MarketplaceModelResource):
data['app_slug'] = data.get('app_slug', obj.app_slug)
data.update(self.formset(data))
data.update(self.devices(data))
self.hydrate_premium_type(bundle)
self.update_premium_type(bundle)
self.update_payment_account(bundle)
forms = [AppDetailsBasicForm(data, instance=obj, request=request),
DeviceTypeForm(data, addon=obj),
@ -258,6 +264,45 @@ class AppResource(CORSResource, MarketplaceModelResource):
return bundle
def update_premium_type(self, bundle):
self.hydrate_premium_type(bundle)
if bundle.obj.premium_type != amo.ADDON_FREE:
ap = AddonPremium.objects.safer_get_or_create(addon=bundle.obj)[0]
else:
ap = None
if bundle.obj.premium_type in amo.ADDON_PREMIUMS:
if not bundle.data.get('price') or not Price.objects.filter(
price=bundle.data['price']).exists():
tiers = ', '.join('"%s"' % p.get_price()
for p in Price.objects.exclude(price="0.00"))
raise fields.ApiFieldError(
'Premium app specified without a valid price. price can be'
' one of %s.' % (tiers,))
else:
ap.price = Price.objects.get(price=bundle.data['price'])
ap.save()
else:
if ap:
ap.price = Price.objects.get(price='0.00')
ap.save()
def update_payment_account(self, bundle):
if 'payment_account' in bundle.data:
if bundle.obj.premium_type == amo.ADDON_FREE:
raise fields.ApiFieldError(
'Free apps cannot have payment accounts.')
acct = self.fields['payment_account'].hydrate(bundle).obj
try:
log.info('[1@%s] Deleting app payment account' % bundle.obj.pk)
AddonPaymentAccount.objects.get(addon=bundle.obj).delete()
except AddonPaymentAccount.DoesNotExist:
pass
log.info('[1@%s] Creating new app payment account' % bundle.obj.pk)
AddonPaymentAccount.create(
provider='bango', addon=bundle.obj,
payment_account=acct)
def dehydrate(self, bundle):
obj = bundle.obj
user = getattr(bundle.request, 'user', None)

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

@ -11,11 +11,15 @@ from addons.models import (Addon, AddonDeviceType, AddonUpsell,
AddonUser, Category, Flag, Preview)
from amo.tests import AMOPaths, app_factory
from files.models import FileUpload
from market.models import Price, AddonPremium
from users.models import UserProfile
from mkt.api.tests.test_oauth import BaseOAuth
from mkt.api.tests.test_oauth import BaseOAuth, get_absolute_url
from mkt.api.base import get_url, list_url
from mkt.constants import APP_IMAGE_SIZES, carriers, regions
from mkt.developers.models import (AddonPaymentAccount, PaymentAccount,
SolitudeSeller)
from mkt.developers.tests.test_api import payment_data
from mkt.site.fixtures import fixture
from mkt.webapps.models import ContentRating, ImageAsset, Webapp
from reviews.models import Review
@ -533,6 +537,130 @@ class TestAppCreateHandler(CreateHandler, AMOPaths):
assert '12345' in self.get_error(res)['device_types'][0], (
self.get_error(res))
def test_put_price(self):
app = self.create_app()
data = self.base_data()
Price.objects.create(price='1.07')
data['premium_type'] = 'premium'
data['price'] = "1.07"
res = self.client.put(self.get_url, data=json.dumps(data))
eq_(res.status_code, 202)
eq_(str(app.addonpremium.price.get_price()), "1.07")
def test_put_premium_inapp(self):
app = self.create_app()
data = self.base_data()
Price.objects.create(price='1.07')
data['premium_type'] = 'premium-inapp'
data['price'] = "1.07"
res = self.client.put(self.get_url, data=json.dumps(data))
eq_(res.status_code, 202)
eq_(str(app.addonpremium.price.get_price()), "1.07")
eq_(Webapp.objects.get(pk=app.pk).premium_type,
amo.ADDON_PREMIUM_INAPP)
def test_put_bad_price(self):
self.create_app()
data = self.base_data()
Price.objects.create(price='1.07')
Price.objects.create(price='3.14')
data['premium_type'] = 'premium'
data['price'] = "2.03"
res = self.client.put(self.get_url, data=json.dumps(data))
eq_(res.status_code, 400)
eq_(res.content,
'Premium app specified without a valid price. price can be one of '
'"1.07", "3.14".')
def test_put_no_price(self):
self.create_app()
data = self.base_data()
Price.objects.create(price='1.07')
Price.objects.create(price='3.14')
data['premium_type'] = 'premium'
res = self.client.put(self.get_url, data=json.dumps(data))
eq_(res.status_code, 400)
eq_(res.content,
'Premium app specified without a valid price. price can be one of '
'"1.07", "3.14".')
def test_put_free_inapp(self):
app = self.create_app()
data = self.base_data()
Price.objects.create(price='0.00')
Price.objects.create(price='3.14')
data['premium_type'] = 'free-inapp'
res = self.client.put(self.get_url, data=json.dumps(data))
eq_(res.status_code, 202)
eq_(str(app.addonpremium.get_price()), "0.00")
@patch('mkt.developers.models.client')
def test_get_payment_account(self, client):
client.api.bango.package().get.return_value = {"full": payment_data}
app = self.create_app()
app.premium_type = amo.ADDON_PREMIUM
app.save()
seller = SolitudeSeller.objects.create(user=self.profile, uuid='uid')
acct = PaymentAccount.objects.create(
user=self.profile, solitude_seller=seller, agreed_tos=True,
seller_uri='uri', uri='uri', bango_package_id=123)
AddonPaymentAccount.objects.create(
addon=app, payment_account=acct, set_price=1,
product_uri="/path/to/app/")
p = Price.objects.create(price='1.07')
AddonPremium.objects.create(addon=app, price=p)
acct_url = get_absolute_url(get_url('account', acct.pk),
'payments', False)
res = self.client.get(self.get_url)
eq_(res.status_code, 200)
data = json.loads(res.content)
eq_(data['payment_account'], acct_url)
eq_(data['price'], '1.07')
@patch('mkt.developers.models.client')
def test_put_payment_account(self, client):
client.api.bango.package().get.return_value = {"full": payment_data}
app = self.create_app()
data = self.base_data()
seller = SolitudeSeller.objects.create(user=self.profile, uuid='uid')
acct = PaymentAccount.objects.create(
user=self.profile, solitude_seller=seller, agreed_tos=True,
seller_uri='uri', uri='uri', bango_package_id=123)
Price.objects.create(price='1.07')
data['price'] = "1.07"
data['premium_type'] = 'premium'
data['payment_account'] = get_absolute_url(get_url('account', acct.pk),
'payments', False)
res = self.client.put(self.get_url, data=json.dumps(data))
eq_(res.status_code, 202)
eq_(app.app_payment_account.payment_account.pk, acct.pk)
@patch('mkt.developers.models.client')
def test_put_payment_account_on_free(self, client):
client.api.bango.package().get.return_value = {"full": payment_data}
self.create_app()
data = self.base_data()
seller = SolitudeSeller.objects.create(user=self.profile, uuid='uid')
acct = PaymentAccount.objects.create(
user=self.profile, solitude_seller=seller, agreed_tos=True,
seller_uri='uri', uri='uri', bango_package_id=123)
data['payment_account'] = get_absolute_url(get_url('account', acct.pk),
'payments', False)
res = self.client.put(self.get_url, data=json.dumps(data))
eq_(res.status_code, 400)
def test_put_bogus_payment_account(self):
app = self.create_app()
data = self.base_data()
Price.objects.create(price='1.07')
data['price'] = "1.07"
data['premium_type'] = 'premium'
data['payment_account'] = get_absolute_url(get_url('account', 999),
'payments', False)
res = self.client.put(self.get_url, data=json.dumps(data))
eq_(res.status_code, 400)
assert not hasattr(app, 'app_payment_account')
def test_put_not_mine(self):
obj = self.create_app()
obj.authors.clear()

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

@ -119,6 +119,10 @@ class AccountTests(BaseOAuth):
pkg['resource_uri'] = '/api/v1/payments/account/%s/' % self.account.pk
eq_(data, pkg)
def test_only_get_by_owner(self, client):
r = self.anon.get(get_url('account', self.account.pk))
eq_(r.status_code, 401)
def test_put(self, client):
addr = 'b@b.com'
newpkg = package_data.copy()

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

@ -40,6 +40,9 @@ def app_to_dict(app, currency=None, user=None):
"""Return app data as dict for API."""
# Sad circular import issues.
from mkt.api.resources import PreviewResource
from mkt.developers.api import AccountResource
from mkt.developers.models import AddonPaymentAccount
cv = app.current_version
version_data = {
@ -75,6 +78,10 @@ def app_to_dict(app, currency=None, user=None):
}
if app.premium:
q = AddonPaymentAccount.objects.filter(addon=app)
if len(q) > 0 and q[0].payment_account:
data['payment_account'] = AccountResource().get_resource_uri(
q[0].payment_account)
try:
data['price'] = app.get_price(currency)
data['price_locale'] = app.get_price_locale(currency)