scorched earth non ajax pass of advanced payments submission (bug 726205)
This commit is contained in:
Родитель
8eae390ce4
Коммит
b7b6ce59fa
|
@ -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.
|
||||
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
Загрузка…
Ссылка в новой задаче