scorched earth non ajax pass of advanced payments submission (bug 726205)

This commit is contained in:
Andy McKay 2012-02-22 10:51:00 -08:00
Родитель 8eae390ce4
Коммит b7b6ce59fa
13 изменённых файлов: 346 добавлений и 34 удалений

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

@ -0,0 +1,3 @@
INSERT INTO waffle_flag_mkt
(name, everyone, percent, superusers, staff, authenticated, rollout, note) VALUES
('advanced-payments-submission', 0, NULL, 0, 0, 0, 0, 'A more advanced payments submission');

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

@ -9,7 +9,8 @@ from access import acl
from addons.decorators import addon_view
def dev_required(owner_for_post=False, allow_editors=False, webapp=False):
def dev_required(owner_for_post=False, allow_editors=False, webapp=False,
skip_submit_check=False):
"""Requires user to be add-on owner or admin.
When allow_editors is True, an editor can view the page.
@ -35,16 +36,17 @@ def dev_required(owner_for_post=False, allow_editors=False, webapp=False):
# Ignore disabled so they can view their add-on.
elif acl.check_addon_ownership(request, addon, viewer=True,
ignore_disabled=True):
try:
# If it didn't go through the app submission
# checklist. Don't die. This will be useful for
# creating apps with an API later.
step = addon.appsubmissionchecklist.get_next()
except ObjectDoesNotExist:
step = None
# Redirect to the submit flow if they're not done.
if not getattr(f, 'submitting', False) and step:
return _resume(addon, step)
if not skip_submit_check:
try:
# If it didn't go through the app submission
# checklist. Don't die. This will be useful for
# creating apps with an API later.
step = addon.appsubmissionchecklist.get_next()
except ObjectDoesNotExist:
step = None
# Redirect to the submit flow if they're not done.
if not getattr(f, 'submitting', False) and step:
return _resume(addon, step)
return fun()
return http.HttpResponseForbidden()
return wrapper

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

@ -32,6 +32,7 @@ from applications.models import Application, AppVersion
from files.models import File, FileUpload, Platform
from files.utils import parse_addon, VERSION_RE
from market.models import AddonPremium, Price, AddonPaymentData
from mkt.site.forms import AddonChoiceField, APP_UPSELL_CHOICES
from payments.models import InappConfig
from translations.widgets import (TransInput, TransTextarea,
TranslationTextarea, TranslationTextInput)
@ -981,21 +982,6 @@ class NewManifestForm(happyforms.Form):
return manifest
UPSELL_CHOICES = (
(0, _("I don't have a free add-on to associate.")),
(1, _('This is a premium upgrade to:')),
)
APP_UPSELL_CHOICES = (
(0, _("I don't have a free app to associate.")),
(1, _('This is a premium upgrade to:')),
)
class AddonChoiceField(forms.ModelChoiceField):
def label_from_instance(self, obj):
return obj.name
class PremiumForm(happyforms.Form):
"""
The premium details for an addon, which is unfortunately
@ -1009,7 +995,7 @@ class PremiumForm(happyforms.Form):
empty_label=None,
required=False)
do_upsell = forms.TypedChoiceField(coerce=lambda x: bool(int(x)),
choices=UPSELL_CHOICES,
choices=APP_UPSELL_CHOICES,
widget=forms.RadioSelect(),
required=False)
free = AddonChoiceField(queryset=Addon.objects.none(),

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

@ -44,6 +44,7 @@ from mkt.developers.forms import (CheckCompatibilityForm, InappConfigForm,
AppFormBasic, AppFormDetails)
from mkt.developers.models import ActivityLog, SubmitStep
from mkt.developers import perf
from mkt.submit.decorators import submit_step
from editors.helpers import get_position
from files.models import File, FileUpload, Platform
from files.utils import parse_addon
@ -295,7 +296,7 @@ def paypal_setup_bounce(request, addon_id, addon, webapp):
if not addon.paypal_id:
messages.error(request, 'We need a PayPal email before continuing.')
return redirect(addon.get_dev_url('paypal_setup'))
paypal_url = paypal.get_permission_url(addon, 'payments',
paypal_url = paypal.get_permission_url(addon, 'management',
['REFUND',
'ACCESS_BASIC_PERSONAL_DATA',
'ACCESS_ADVANCED_PERSONAL_DATA'])
@ -320,15 +321,28 @@ def paypal_setup_confirm(request, addon_id, addon, webapp):
@write
@dev_required(webapp=True)
@dev_required(webapp=True, skip_submit_check=True)
def acquire_refund_permission(request, addon_id, addon, webapp=False):
"""This is the callback from Paypal."""
# Set up our redirects.
if request.GET.get('dest', '') == 'submission':
on_good = reverse('submit.app.payments.confirm',
args=[addon.app_slug])
on_error = reverse('submit.app.payments.paypal',
args=[addon.app_slug])
show_good_msgs = False
else:
# The management pages are the default.
on_good = addon.get_dev_url('paypal_setup_confirm')
on_error = addon.get_dev_url('paypal_setup_bounce')
show_good_msgs = True
if 'request_token' not in request.GET:
paypal_log.debug('User did not approve permissions for'
' addon: %s' % addon_id)
messages.error(request, 'You will need to accept the permissions '
'to continue.')
return redirect(addon.get_dev_url('paypal_setup_bounce'))
return redirect(on_error)
paypal_log.debug('User approved permissions for addon: %s' % addon_id)
@ -366,9 +380,10 @@ def acquire_refund_permission(request, addon_id, addon, webapp=False):
paypal_log.debug('AddonPremium saved with token: %s' % addonpremium.pk)
amo.log(amo.LOG.EDIT_PROPERTIES, addon)
messages.success(request, 'Please confirm the data we '
'received from PayPal.')
return redirect(addon.get_dev_url('paypal_setup_confirm'))
if show_good_msgs:
messages.success(request, 'Please confirm the data we '
'received from PayPal.')
return redirect(on_good)
# End of new paypal stuff.

15
mkt/site/forms.py Normal file
Просмотреть файл

@ -0,0 +1,15 @@
from django import forms
from tower import ugettext as _
APP_UPSELL_CHOICES = (
(0, _("I don't have a free app to associate.")),
(1, _('This is a premium upgrade to:')),
)
class AddonChoiceField(forms.ModelChoiceField):
def label_from_instance(self, obj):
return obj.name

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

@ -9,6 +9,8 @@ import amo
from addons.forms import AddonFormBasic
from addons.models import Addon
from files.models import FileUpload
from market.models import Price
from mkt.site.forms import AddonChoiceField, APP_UPSELL_CHOICES
from translations.widgets import TransInput, TransTextarea
from translations.fields import TransField
from users.models import UserProfile
@ -52,6 +54,23 @@ class PremiumTypeForm(happyforms.Form):
widget=forms.RadioSelect())
class UpsellForm(happyforms.Form):
price = forms.ModelChoiceField(queryset=Price.objects.active(),
label=_('App price'),
empty_label=None,
required=True)
do_upsell = forms.TypedChoiceField(coerce=lambda x: bool(int(x)),
choices=APP_UPSELL_CHOICES,
widget=forms.RadioSelect(),
required=False)
free = AddonChoiceField(queryset=Addon.objects.none(),
required=False,
empty_label='')
text = forms.CharField(widget=forms.Textarea(), required=False)
class AppDetailsBasicForm(AddonFormBasic):
"""Form for "Details" submission step."""
name = TransField(max_length=128,

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

@ -0,0 +1,26 @@
{% extends 'developers/base_impala.html' %}
{% set title = _('App Payments') %}
{% block title %}{{ hub_page_title(title) }}{% endblock %}
{% block content %}
{{ hub_breadcrumbs(items=[(None, _('Submit App'))]) }}
<h1>{{ _('Submit an App') }}</h1>
{{ progress(request, addon=addon, step=step) }}
<section id="submit-payments" class="primary">
<h2>App Payments</h2>
<div class="brform island swagger c">
<p>{{ loc('In order to process refunds you approve through the Marketplace
as well as automatic refunds we need you to give permission to us
in PayPal. We also request permission to read your account information
from PayPal.') }}</p>
<footer class="listing-footer">
<a href="{{ paypal_url }}" class="button">Set up permissions</a>
</footer>
</div>
</section>
{% endblock %}

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

@ -0,0 +1,30 @@
{% extends 'developers/base_impala.html' %}
{% set title = _('App Payments') %}
{% block title %}{{ hub_page_title(title) }}{% endblock %}
{% block content %}
{{ hub_breadcrumbs(items=[(None, _('Submit App'))]) }}
<h1>{{ _('Submit an App') }}</h1>
{{ progress(request, addon=addon, step=step) }}
<section id="submit-payments" class="primary">
<h2>App Payments</h2>
<form method="post" id="upload-webapp">
<div class="brform island swagger c">
{{ csrf() }}
<p>{{ loc("Your PayPal is all set up! The last thing we need is
your real name and contact information.") }}</p>
<p>
{{ form.as_p() }}
</p>
</div>
<button class="prominent continue c" type="submit">
{{ _('Continue') }}
</button>
</form>
</section>
{% endblock %}

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

@ -0,0 +1,29 @@
{% extends 'developers/base_impala.html' %}
{% set title = _('App Payments') %}
{% block title %}{{ hub_page_title(title) }}{% endblock %}
{% block content %}
{{ hub_breadcrumbs(items=[(None, _('Submit App'))]) }}
<h1>{{ _('Submit an App') }}</h1>
{{ progress(request, addon=addon, step=step) }}
<section id="submit-payments" class="primary">
<h2>App Payments</h2>
<form method="post" id="upload-webapp">
<div class="brform island swagger c">
{{ csrf() }}
<p>{{ loc("Next, we'll set up your PayPal account so you can
receive payments.") }}</p>
<p>
{{ form.as_p() }}
</p>
</div>
<button class="prominent continue c" type="submit">
{{ _('Continue') }}
</button>
</form>
</section>
{% endblock %}

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

@ -0,0 +1,30 @@
{% extends 'developers/base_impala.html' %}
{% set title = _('App Payments') %}
{% block title %}{{ hub_page_title(title) }}{% endblock %}
{% block content %}
{{ hub_breadcrumbs(items=[(None, _('Submit App'))]) }}
<h1>{{ _('Submit an App') }}</h1>
{{ progress(request, addon=addon, step=step) }}
<section id="submit-payments" class="primary">
<h2>App Payments</h2>
<form method="post" id="upload-webapp">
<div class="brform island swagger c">
{{ csrf() }}
<p>{{ loc("To sell your app in the Marketplace you'll first need
to set your price. Select a price tier for your add-on
below. Price tiers are based on US Dollars and are
fixed for all support countries.") }} </p>
<p>
{{ form.as_p() }}
</p>
</div>
<button class="prominent continue c" type="submit">
{{ _('Continue') }}
</button>
</form>
</section>
{% endblock %}

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

@ -5,6 +5,7 @@ from django.conf import settings
import mock
from nose.tools import eq_
from nose import SkipTest
from pyquery import PyQuery as pq
import waffle
@ -15,6 +16,7 @@ from addons.models import (Addon, AddonCategory, AddonDeviceType, AddonUser,
Category, DeviceType)
from addons.utils import ReverseNameLookup
from files.tests.test_models import UploadTest as BaseUploadTest
from market.models import Price
import mkt
from mkt.submit.models import AppSubmissionChecklist
from translations.models import Translation
@ -523,6 +525,86 @@ class TestPayments(TestSubmit):
eq_(self.get_webapp().premium_type, amo.ADDON_PREMIUM)
class TestPaymentsAdvanced(TestSubmit):
fixtures = ['base/users', 'webapps/337141-steamcube']
def setUp(self):
super(TestPaymentsAdvanced, self).setUp()
AppSubmissionChecklist.objects.all().delete() # TODO fix this.
self.webapp = self.get_webapp()
self.url = self.get_url('payments')
self.price = Price.objects.create(price='1.00')
flag = (waffle.models.Flag
.objects.create(name='advanced-payments-submission'))
flag.everyone = True
flag.save()
self._step()
def get_url(self, url):
return reverse('submit.app.%s' % url, args=[self.webapp.app_slug])
def get_webapp(self):
return Webapp.objects.get(id=337141)
def _step(self):
self.user.update(read_dev_agreement=True)
self.cl = AppSubmissionChecklist.objects.create(addon=self.webapp,
terms=True, manifest=True, details=True)
AddonUser.objects.create(addon=self.webapp, user=self.user)
def test_anonymous(self):
self._test_anonymous()
def test_valid(self):
res = self.client.post(self.get_url('payments'),
{'premium_type': amo.ADDON_FREE})
eq_(res.status_code, 302)
self.assertRedirects(res, self.get_url('done'))
def test_premium(self):
for type_ in [amo.ADDON_PREMIUM, amo.ADDON_PREMIUM_INAPP]:
res = self.client.post(self.get_url('payments'),
{'premium_type': type_})
eq_(res.status_code, 302)
self.assertRedirects(res, self.get_url('payments.upsell'))
def test_free_inapp(self):
res = self.client.post(self.get_url('payments'),
{'premium_type': amo.ADDON_FREE_INAPP})
eq_(res.status_code, 302)
self.assertRedirects(res, self.get_url('payments.paypal'))
def test_upsell(self):
self.webapp.update(premium_type=amo.ADDON_PREMIUM)
res = self.client.post(self.get_url('payments.upsell'),
{'price': self.price.pk})
eq_(res.status_code, 302)
self.assertRedirects(res, self.get_url('payments.paypal'))
def test_bad_upsell(self):
# some tests for when it goes wrong
raise SkipTest
def test_paypal(self):
self.webapp.update(premium_type=amo.ADDON_PREMIUM)
res = self.client.post(self.get_url('payments.paypal'),
{'business_account': 'yes', 'email': 'foo'})
eq_(res.status_code, 200)
def test_bad_paypal(self):
# some tests for when it goes wrong
raise SkipTest
def test_acquire(self):
raise SkipTest
def test_confirm(self):
raise SkipTest
# and this is where it all breaks down, need to figure out
# how to test this new flow.
class TestDone(TestSubmit):
fixtures = ['base/users', 'webapps/337141-steamcube']

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

@ -12,6 +12,14 @@ submit_apps_patterns = patterns('',
url('^details/%s$' % APP_SLUG, views.details, name='submit.app.details'),
url('^payments/%s$' % APP_SLUG, views.payments,
name='submit.app.payments'),
url('^payments/upsell/%s$' % APP_SLUG, views.payments_upsell,
name='submit.app.payments.upsell'),
url('^payments/paypal/%s$' % APP_SLUG, views.payments_paypal,
name='submit.app.payments.paypal'),
url('^payments/bounce/%s$' % APP_SLUG, views.payments_bounce,
name='submit.app.payments.bounce'),
url('^payments/confirm/%s$' % APP_SLUG, views.payments_confirm,
name='submit.app.payments.confirm'),
url('^done/%s$' % APP_SLUG, views.done, name='submit.app.done'),
)

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

@ -1,15 +1,19 @@
from django.shortcuts import redirect
import jingo
import waffle
import amo
from amo.decorators import login_required
from addons.forms import CategoryFormSet, DeviceTypeForm
from addons.models import Addon, AddonUser
from market.models import AddonPaymentData
from mkt.developers import tasks
from mkt.developers.decorators import dev_required
from mkt.developers.forms import PaypalSetupForm, PaypalPaymentData
from mkt.submit.forms import AppDetailsBasicForm
from mkt.submit.models import AppSubmissionChecklist
import paypal
from files.models import Platform
from users.models import UserProfile
from . import forms
@ -118,8 +122,14 @@ def details(request, addon_id, addon):
def payments(request, addon_id, addon):
form = forms.PremiumTypeForm(request.POST or None)
if request.POST and form.is_valid():
# Save this to the addon, eg:
addon.update(premium_type=form.cleaned_data['premium_type'])
if waffle.flag_is_active(request, 'advanced-payments-submission'):
if addon.premium_type in amo.ADDON_PREMIUMS:
return redirect('submit.app.payments.upsell', addon.app_slug)
if addon.premium_type == amo.ADDON_FREE_INAPP:
return redirect('submit.app.payments.paypal', addon.app_slug)
AppSubmissionChecklist.objects.get(addon=addon).update(payments=True)
return redirect('submit.app.done', addon.app_slug)
return jingo.render(request, 'submit/payments.html', {
@ -129,6 +139,63 @@ def payments(request, addon_id, addon):
})
@dev_required
@submit_step('payments')
def payments_upsell(request, addon_id, addon):
form = forms.UpsellForm(request.POST or None)
if request.POST and form.is_valid():
return redirect('submit.app.payments.paypal', addon.app_slug)
return jingo.render(request, 'submit/payments-upsell.html', {
'step': 'payments',
'addon': addon,
'form': form
})
@dev_required
@submit_step('payments')
def payments_paypal(request, addon_id, addon):
form = PaypalSetupForm(request.POST or None)
if request.POST and form.is_valid():
return redirect('submit.app.payments.bounce', addon.app_slug)
return jingo.render(request, 'submit/payments-paypal.html', {
'step': 'payments',
'addon': addon,
'form': form
})
@dev_required
@submit_step('payments')
def payments_bounce(request, addon_id, addon):
paypal_url = paypal.get_permission_url(addon, 'submission',
['REFUND',
'ACCESS_BASIC_PERSONAL_DATA',
'ACCESS_ADVANCED_PERSONAL_DATA'])
return jingo.render(request, 'submit/payments-bounce.html', {
'step': 'payments',
'paypal_url': paypal_url,
'addon': addon
})
@dev_required
@submit_step('payments')
def payments_confirm(request, addon_id, addon):
adp, created = AddonPaymentData.objects.safer_get_or_create(addon=addon)
form = PaypalPaymentData(request.POST or None, instance=adp)
if request.method == 'POST' and form.is_valid():
adp.update(**form.cleaned_data)
AppSubmissionChecklist.objects.get(addon=addon).update(payments=True)
return redirect('submit.app.done', addon.app_slug)
return jingo.render(request, 'submit/payments-confirm.html', {
'step': 'payments',
'addon': addon,
'form': form
})
@dev_required
def done(request, addon_id, addon):
# No submit step forced on this page, we don't really care.