add/remove multiple payment providers on manage payments page, paypal flow done (bug 776665)
This commit is contained in:
Родитель
519de78b9e
Коммит
d5ff95aef2
|
@ -1,9 +1,10 @@
|
|||
import hashlib
|
||||
import logging
|
||||
import uuid
|
||||
from tower import ugettext as _
|
||||
|
||||
import paypal
|
||||
from amo.helpers import loc
|
||||
|
||||
|
||||
log = logging.getLogger('z.paypal')
|
||||
|
||||
|
@ -47,21 +48,21 @@ class Check(object):
|
|||
"""Check that the paypal id is good."""
|
||||
test_id = 'id'
|
||||
if not self.paypal_id:
|
||||
self.failure(test_id, loc('No PayPal id provided.'))
|
||||
self.failure(test_id, _('No PayPal ID provided.'))
|
||||
return
|
||||
|
||||
valid, msg = paypal.check_paypal_id(self.paypal_id)
|
||||
if not valid:
|
||||
self.failure(test_id, loc('You do not seem to have'
|
||||
' a PayPal account.'))
|
||||
self.failure(test_id, _('Please enter a valid email.'))
|
||||
|
||||
else:
|
||||
self.pass_(test_id)
|
||||
|
||||
def check_refund(self):
|
||||
"""Check that we have the refund permission."""
|
||||
test_id = 'refund'
|
||||
msg = loc('You have not setup permissions for us to check this '
|
||||
'paypal account.')
|
||||
msg = _('You have not setup permissions for us to check this '
|
||||
'PayPal account.')
|
||||
if not self.addon:
|
||||
# If there's no addon there's not even any point checking.
|
||||
return
|
||||
|
@ -82,7 +83,7 @@ class Check(object):
|
|||
status = paypal.check_permission(token, ['REFUND'])
|
||||
if not status:
|
||||
self.state['permissions'] = False
|
||||
self.failure(test_id, loc('No permission to do refunds.'))
|
||||
self.failure(test_id, _('No permission to do refunds.'))
|
||||
else:
|
||||
self.pass_(test_id)
|
||||
except paypal.PaypalError:
|
||||
|
@ -112,8 +113,8 @@ class Check(object):
|
|||
'email': self.paypal_id})
|
||||
log.info('Get paykey passed in %s' % currency)
|
||||
except paypal.PaypalError:
|
||||
msg = loc('Failed to make a test transaction '
|
||||
'in %s.' % (currency))
|
||||
msg = _('Failed to make a test transaction '
|
||||
'in %s.' % (currency))
|
||||
self.failure(test_id, msg)
|
||||
log.info('Get paykey returned an error'
|
||||
'in %s' % currency, exc_info=True)
|
||||
|
|
|
@ -113,7 +113,7 @@
|
|||
}
|
||||
> ul > li {
|
||||
display: inline-block;
|
||||
&:not(:last-child):after {
|
||||
&:not(:first-child):before {
|
||||
background-color: @note-gray;
|
||||
border-radius: 5em;
|
||||
content: "";
|
||||
|
|
|
@ -38,6 +38,36 @@ button, .button {
|
|||
}
|
||||
}
|
||||
|
||||
.button, button {
|
||||
&.paypal,
|
||||
&.bluevia {
|
||||
font: bold italic 24px/26px Verdana, @sans-stack;
|
||||
padding: 22px 40px 21px;
|
||||
min-width: 175px;
|
||||
}
|
||||
&.paypal {
|
||||
.gradient-two-color(#F8EAC4, #EEC546);
|
||||
color: rgb(51, 70, 118);
|
||||
text-shadow: 1px 1px #EDE8BF;
|
||||
small {
|
||||
font-size: 0.7em;
|
||||
}
|
||||
em {
|
||||
color: rgb(90, 120, 168);
|
||||
}
|
||||
&.disabled em {
|
||||
color: #919497;
|
||||
}
|
||||
}
|
||||
&.bluevia {
|
||||
background: #def url(../../img/mkt/bluevia.png) 50% -20% no-repeat;
|
||||
text-indent: -9999px;
|
||||
&:hover {
|
||||
background-color: fade(#def, 70%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
display: inline-block;
|
||||
&.add { // Green
|
||||
|
@ -82,23 +112,6 @@ button, .button {
|
|||
&.premium {
|
||||
.gradient-two-color(#E3C17F, #D6913D);
|
||||
}
|
||||
&.paypal {
|
||||
.gradient-two-color(#F8EAC4, #EEC546);
|
||||
color: rgb(51, 70, 118);
|
||||
text-shadow: 1px 1px #EDE8BF;
|
||||
font-style: italic;
|
||||
font-family: Verdana, @sans-stack;
|
||||
background-color: #EEC546;
|
||||
small {
|
||||
font-size: 0.7em;
|
||||
}
|
||||
em {
|
||||
color: rgb(90, 120, 168);
|
||||
}
|
||||
&.disabled em {
|
||||
color: #919497;
|
||||
}
|
||||
}
|
||||
&.contribute { // Blue
|
||||
&.prominent b {
|
||||
background: url(../../img/impala/button-icons.png) no-repeat;
|
||||
|
|
|
@ -437,6 +437,9 @@ button.loading-submit:after,
|
|||
a {
|
||||
margin-left: 4px;
|
||||
}
|
||||
button a {
|
||||
color: @white;
|
||||
}
|
||||
}
|
||||
|
||||
.html-rtl, .login, .modal {
|
||||
|
|
|
@ -331,7 +331,6 @@
|
|||
a {
|
||||
color: @note-gray;
|
||||
&.more-actions {
|
||||
display: block;
|
||||
position: relative;
|
||||
&:after {
|
||||
border-color: @note-gray transparent transparent;
|
||||
|
@ -362,7 +361,7 @@
|
|||
}
|
||||
> ul > li {
|
||||
display: inline-block;
|
||||
&:not(:last-child):after {
|
||||
&:not(:first-child):before {
|
||||
background-color: @note-gray;
|
||||
border-radius: 5em;
|
||||
content: "";
|
||||
|
|
|
@ -4,8 +4,153 @@
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
.inactive {
|
||||
color: @light-gray;
|
||||
cursor: default;
|
||||
&:hover {
|
||||
color: @light-gray;
|
||||
cursor: default;
|
||||
text-decoration: none;
|
||||
}
|
||||
span.req, #payments-payment-account p, .status-fail {
|
||||
color: @light-gray !important;
|
||||
}
|
||||
}
|
||||
|
||||
.payment-account-actions {
|
||||
display: block;
|
||||
float: right;
|
||||
font-size: 11px;
|
||||
margin: -10px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.payments {
|
||||
#premium-type {
|
||||
float: left;
|
||||
width: 35%;
|
||||
}
|
||||
.divider {
|
||||
width: 5%;
|
||||
}
|
||||
.premium-detail {
|
||||
border-left: 1px dotted @light-gray;
|
||||
float: right;
|
||||
padding-left: 10px;
|
||||
margin-bottom: 12px;
|
||||
width: 60%;
|
||||
}
|
||||
#payments-price-level {
|
||||
.extra {
|
||||
margin: 13px 0 5px;
|
||||
}
|
||||
}
|
||||
#payments-currencies {
|
||||
ul li label, li {
|
||||
display: inline;
|
||||
}
|
||||
ul li label {
|
||||
font-weight: normal;
|
||||
margin-right: 4px;
|
||||
}
|
||||
input {
|
||||
margin-right: -2px;
|
||||
}
|
||||
}
|
||||
#payments-payment-account {
|
||||
.extra, #payment-label {
|
||||
display: inline;
|
||||
}
|
||||
ul label {
|
||||
font-weight: normal;
|
||||
}
|
||||
p {
|
||||
color: @red;
|
||||
font-style: italic;
|
||||
margin-top: 5px;
|
||||
}
|
||||
#paypal-id-verify, .loading-submit {
|
||||
display: inline;
|
||||
}
|
||||
.check-icon {
|
||||
&:before {
|
||||
.border-radius(8px);
|
||||
color: @white;
|
||||
display: inline-block;
|
||||
font-weight: normal;
|
||||
height: 16px;
|
||||
line-height: 14px;
|
||||
margin: 0 10px 0 2px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
width: 16px;
|
||||
}
|
||||
}
|
||||
.check-icon.inactive {
|
||||
&:before {
|
||||
background-color: @light-gray !important;
|
||||
}
|
||||
}
|
||||
#check-passed:before {
|
||||
background-color: @green;
|
||||
content: "\2714"; /* checkmark */
|
||||
}
|
||||
#check-failed:before {
|
||||
background-color: @red;
|
||||
content: "\00D7"; /* mult symbol */
|
||||
font-weight: bold;
|
||||
}
|
||||
.item-actions {
|
||||
margin: 2px 0 5px;
|
||||
.setup-bounce {
|
||||
display: none;
|
||||
}
|
||||
ul {
|
||||
font-size: 11px;
|
||||
line-height: 13px;
|
||||
}
|
||||
li:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
#paypal-errors {
|
||||
display: none;
|
||||
p {
|
||||
display: inline;
|
||||
font-style: normal;
|
||||
}
|
||||
li.status-fail {
|
||||
font-style: italic;
|
||||
line-height: 1.1;
|
||||
strong {
|
||||
color: lighten(#D81916, 5%);
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
.payment-option {
|
||||
.transition(~'.3s all');
|
||||
&.deleting {
|
||||
background: fade(@red, 40%);
|
||||
}
|
||||
&.deleted {
|
||||
border: 0;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
#payments-upsell {
|
||||
.note {
|
||||
margin-top: 5px;
|
||||
}
|
||||
#upsell-form {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.listing-footer {
|
||||
clear: both;
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
.button-wrapper {
|
||||
|
@ -38,3 +183,68 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* overlays */
|
||||
.overlay {
|
||||
.close {
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
background-position: -25px 0;
|
||||
background-color: @red;
|
||||
}
|
||||
}
|
||||
h2 {
|
||||
text-align: left;
|
||||
font-size: 18px;
|
||||
font-weight: 400;
|
||||
}
|
||||
.brform, input {
|
||||
margin-top: 10px;
|
||||
}
|
||||
form {
|
||||
margin: 0;
|
||||
}
|
||||
a {
|
||||
display: block;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.listing-footer {
|
||||
clear:both;
|
||||
margin: 13px -10px -10px;
|
||||
padding-bottom: 46px;
|
||||
}
|
||||
}
|
||||
section.choose-payment-account {
|
||||
text-align: center;
|
||||
div {
|
||||
display: inline-block;
|
||||
padding: 10px;
|
||||
button {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
.or {
|
||||
color: @note-gray;
|
||||
font: italic 24px/75px Georgia, serif;
|
||||
margin: 0 1em;
|
||||
vertical-align: top;
|
||||
}
|
||||
}
|
||||
section.add-paypal-email {
|
||||
max-width: 400px;
|
||||
.email {
|
||||
width: 250px;
|
||||
}
|
||||
input.error {
|
||||
margin-bottom: 10px;
|
||||
.box-shadow(0 1px 1px 2px @red inset);
|
||||
}
|
||||
.email-error {
|
||||
color: @red;
|
||||
display: none;
|
||||
}
|
||||
.redirect-info {
|
||||
margin-top: 10px;
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 1.6 KiB |
|
@ -28,48 +28,183 @@ exports.email_setup = function() {
|
|||
|
||||
// This is the setup payments form.
|
||||
exports.payment_setup = function() {
|
||||
|
||||
// Set up account modal.
|
||||
var newPaymentAccount = function(e) {
|
||||
var overlay = makeOrGetOverlay('choose-payment-account');
|
||||
overlay.html($('#choose-payment-account-template').html());
|
||||
handlePaymentOverlay(overlay);
|
||||
|
||||
// PayPal chosen.
|
||||
$('button.paypal').click(_pd(function(e) {
|
||||
$('.overlay').remove();
|
||||
var overlay = makeOrGetOverlay('add-paypal-email');
|
||||
overlay.html($('#add-paypal-email-template').html());
|
||||
handlePaymentOverlay(overlay);
|
||||
|
||||
// Email entered and submitted.
|
||||
var emailRe = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
var continue_button = $('.continue');
|
||||
continue_button.click(function(e) {
|
||||
// Validate email.
|
||||
var postData = {'email': $('.email').val()};
|
||||
// Setup the PayPal in the backend.
|
||||
if (emailRe.test(postData.email)) {
|
||||
$.post(continue_button.data('paypal-url'), postData, function(data) {
|
||||
// Request permissions for refunds by redirecting to PayPal.
|
||||
if (data.valid) {
|
||||
window.location.replace(data.paypal_url);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$('.email').addClass('error');
|
||||
$('.email-error').show();
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
$('button.bluevia').click(_pd(function(e) {
|
||||
alert('TODO: bluevia onboarding');
|
||||
}));
|
||||
};
|
||||
|
||||
// Remove PayPal account from app.
|
||||
var paypalRemove = _pd(function(e) {
|
||||
var success = $('.success h2');
|
||||
|
||||
var $remove = $('.paypal-actions #remove-account');
|
||||
var resp = $.get($remove.data('remove-url'));
|
||||
if (!resp.success) {
|
||||
if (success.length) {
|
||||
success.text(gettext('There was an error removing the Paypal account.'));
|
||||
} else {
|
||||
$('#page').prepend($('<section class="full notification-box">' +
|
||||
'<div class="success"><h2>' +
|
||||
gettext('There was an error removing the PayPal account.') +
|
||||
'</h2></div></section>'));
|
||||
}
|
||||
}
|
||||
|
||||
var $paypal = $('#paypal');
|
||||
$paypal.addClass('deleting');
|
||||
setTimeout(function() {
|
||||
$paypal.addClass('deleted');
|
||||
|
||||
// If already existing Django message, replace message.
|
||||
if (success.length) {
|
||||
success.text(gettext('PayPal account successfully removed from app.'));
|
||||
} else {
|
||||
$('#page').prepend($('<section class="full notification-box">' +
|
||||
'<div class="success"><h2>' +
|
||||
gettext('PayPal account successfully removed from app.') +
|
||||
'</h2></div></section>'));
|
||||
}
|
||||
}, 500);
|
||||
});
|
||||
|
||||
var update_forms = function(value) {
|
||||
var fields = [
|
||||
// Free
|
||||
[[], ['payments-support-type', 'payments-price-level',
|
||||
'payments-upsell']],
|
||||
[[], ['premium-detail']],
|
||||
// Premium
|
||||
[['payments-support-type', 'payments-price-level',
|
||||
'payments-upsell'], []],
|
||||
[['premium-detail'], []],
|
||||
// Premium with in-app
|
||||
[['payments-support-type', 'payments-price-level',
|
||||
'payments-upsell'], []],
|
||||
[['premium-detail'], []],
|
||||
// Free with in-app
|
||||
[['payments-support-type'],
|
||||
['payments-price-level', 'payments-upsell']],
|
||||
[['payments-support-type'], ['premium-detail']],
|
||||
// Premium but other.
|
||||
[[], ['payments-support-type', 'payments-price-level',
|
||||
'payments-upsell']],
|
||||
[[], ['premium-detail']]
|
||||
];
|
||||
$.each(fields[value][0], function() { $('#' + this).show(); });
|
||||
$.each(fields[value][1], function() { $('#' + this).hide(); });
|
||||
|
||||
// To disable all elements within premium form.
|
||||
var elements = ['', 'a', 'textarea', 'select', 'input', 'label', 'span', 'p', 'li'];
|
||||
function getPremiumElements(id) {
|
||||
var selector = $(id);
|
||||
$.each(elements, function(index, element) {
|
||||
selector = selector.add('#' + id + ' ' + element);
|
||||
});
|
||||
return selector;
|
||||
}
|
||||
|
||||
$.each(fields[value][1], function() {
|
||||
var $inactive = getPremiumElements(this);
|
||||
$inactive.addClass('inactive').unbind('click')
|
||||
.attr('disabled', 'disabled')
|
||||
.attr('href', 'javascript:void(0)');
|
||||
});
|
||||
$.each(fields[value][0], function() {
|
||||
var $active = getPremiumElements(this);
|
||||
$active.removeClass('inactive').removeAttr('disabled');
|
||||
|
||||
// Re-enable links.
|
||||
$.each($('#' + this + ' a'), function(index, link) {
|
||||
var $link = $(link);
|
||||
$link.attr('href', $link.data('url'));
|
||||
});
|
||||
|
||||
$('.payment-account-actions').on('click', _pd(newPaymentAccount));
|
||||
$('#remove-account').on('click', _pd(paypalRemove));
|
||||
});
|
||||
};
|
||||
|
||||
// Hide or show upsell form.
|
||||
var $freeSelect = $('#id_free');
|
||||
var $freeText = $('#upsell-form');
|
||||
$freeSelect.change(function() {
|
||||
if ($freeSelect.val()) {
|
||||
$freeText.show();
|
||||
} else {
|
||||
$freeText.hide();
|
||||
}
|
||||
}).trigger('change');
|
||||
|
||||
if ($('section.payments input[name=premium_type]').length) {
|
||||
update_forms($('section.payments input[name=premium_type]:checked').val());
|
||||
$('section.payments input[name=premium_type]').click(function(e) {
|
||||
update_forms($(this).val());
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function handlePaymentOverlay(overlay) {
|
||||
overlay.addClass('show');
|
||||
overlay.on('click', '.close', _pd(function() {
|
||||
overlay.remove();
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
exports.check_with_paypal = function() {
|
||||
// Looks for errors with PayPal account.
|
||||
var $paypal_verify = $('#paypal-id-verify'),
|
||||
target = '.paypal-fail';
|
||||
if ($paypal_verify.length) {
|
||||
$.get($paypal_verify.attr('data-url'), function(d) {
|
||||
$paypal_verify.find('p').eq(0).hide();
|
||||
target = d.valid ? '.paypal-pass' : '.paypal-fail';
|
||||
$paypal_verify.find(target).show();
|
||||
$paypal_verify.find(target).show().css('display', 'inline');
|
||||
|
||||
var $paypalErrors = $('#paypal-errors');
|
||||
if (d.message.length) {
|
||||
$paypalErrors.show();
|
||||
$('.item-actions .setup-bounce').css('display', 'inline');
|
||||
|
||||
// Load the paypal link into the anchor tag.
|
||||
var setupBounceLink = $('#setup-bounce-link');
|
||||
var paypalUrl = setupBounceLink.data('paypal-url');
|
||||
$.post(paypalUrl, { email: $('#paypal-id').text() }, function(data) {
|
||||
setupBounceLink.attr('href', data.paypal_url);
|
||||
});
|
||||
}
|
||||
$.each(d.message, function(index, value) {
|
||||
$paypal_verify.find('ul').append(format('<li class="status-fail"><strong>{0}</strong></li>', value));
|
||||
})
|
||||
$paypalErrors.find('ul').append(format('<li class="status-fail"><strong>{0}</strong></li>', value));
|
||||
});
|
||||
|
||||
// Make error messages inactive when initial premium type is
|
||||
// free.
|
||||
if ($.inArray($('section.payments input[name=premium_type]:checked').val(), [0, 3, 4])) {
|
||||
$('.status-fail').addClass('inactive');
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -83,4 +218,3 @@ $(document).ready(function() {
|
|||
dev_paypal.payment_setup();
|
||||
dev_paypal.check_with_paypal();
|
||||
});
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ CSS = {
|
|||
'css/devreg/buttons.less',
|
||||
|
||||
# Popups, Modals, Tooltips.
|
||||
'css/mkt/overlay.less',
|
||||
'css/devreg/devhub-popups.less',
|
||||
'css/mkt/device.less',
|
||||
'css/devreg/tooltips.less',
|
||||
|
@ -167,6 +168,7 @@ JS = {
|
|||
'js/zamboni/storage.js',
|
||||
'js/zamboni/tabs.js',
|
||||
'js/impala/serializers.js',
|
||||
'js/mkt/utils.js',
|
||||
|
||||
# jQuery UI.
|
||||
'js/lib/jquery-ui/jquery.ui.core.js',
|
||||
|
|
|
@ -13,7 +13,6 @@ import waffle
|
|||
from quieter_formset.formset import BaseFormSet, BaseModelFormSet
|
||||
from tower import ugettext as _, ugettext_lazy as _lazy, ungettext as ngettext
|
||||
|
||||
|
||||
import amo
|
||||
import addons.forms
|
||||
import paypal
|
||||
|
@ -25,8 +24,6 @@ from addons.widgets import CategoriesSelectMultiple
|
|||
from amo.utils import raise_required, remove_icons
|
||||
from lib.video import tasks as vtasks
|
||||
from market.models import AddonPremium, Price, PriceCurrency
|
||||
from mkt.constants.ratingsbodies import (RATINGS_BY_NAME, ALL_RATINGS,
|
||||
RATINGS_BODIES)
|
||||
from translations.fields import TransField
|
||||
from translations.forms import TranslationFormMixin
|
||||
from translations.models import Translation
|
||||
|
@ -34,9 +31,11 @@ from translations.widgets import TransInput, TransTextarea
|
|||
|
||||
import mkt
|
||||
from mkt.constants import APP_IMAGE_SIZES
|
||||
from mkt.constants.ratingsbodies import (RATINGS_BY_NAME, ALL_RATINGS,
|
||||
RATINGS_BODIES)
|
||||
from mkt.inapp_pay.models import InappConfig
|
||||
from mkt.reviewers.models import RereviewQueue
|
||||
from mkt.site.forms import AddonChoiceField, APP_UPSELL_CHOICES
|
||||
from mkt.site.forms import AddonChoiceField
|
||||
from mkt.webapps.models import (AddonExcludedRegion, ContentRating, ImageAsset,
|
||||
Webapp)
|
||||
|
||||
|
@ -483,12 +482,12 @@ class PremiumForm(happyforms.Form):
|
|||
label=_lazy(u'App Price'),
|
||||
empty_label=None,
|
||||
required=False)
|
||||
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='')
|
||||
label=_lazy(u'This is a paid upgrade of'),
|
||||
empty_label=_lazy(u'Not an upgrade'))
|
||||
currencies = forms.MultipleChoiceField(
|
||||
widget=forms.CheckboxSelectMultiple,
|
||||
required=False, label=_lazy(u'Supported Non-USD Currencies'))
|
||||
|
||||
def __init__(self, *args, **kw):
|
||||
self.extra = kw.pop('extra')
|
||||
|
@ -496,7 +495,6 @@ class PremiumForm(happyforms.Form):
|
|||
self.addon = self.extra['addon']
|
||||
kw['initial'] = {
|
||||
'premium_type': self.addon.premium_type,
|
||||
'do_upsell': 0,
|
||||
}
|
||||
if self.addon.premium:
|
||||
kw['initial']['price'] = self.addon.premium.price
|
||||
|
@ -505,18 +503,30 @@ class PremiumForm(happyforms.Form):
|
|||
if upsell:
|
||||
kw['initial'].update({
|
||||
'free': upsell.free,
|
||||
'do_upsell': 1,
|
||||
})
|
||||
|
||||
if waffle.switch_is_active('currencies'):
|
||||
currencies = self.addon.premium.currencies
|
||||
kw['initial']['currencies'] = currencies
|
||||
|
||||
super(PremiumForm, self).__init__(*args, **kw)
|
||||
|
||||
if waffle.switch_is_active('currencies'):
|
||||
choices = (PriceCurrency.objects.values_list('currency', flat=True)
|
||||
.distinct())
|
||||
self.fields['currencies'].choices = [(k, k)
|
||||
for k in choices if k]
|
||||
|
||||
if self.addon.is_webapp():
|
||||
self.fields['premium_type'].label = _lazy(u'Will your app '
|
||||
'use payments?')
|
||||
self.fields['price'].label = _(u'App Price')
|
||||
self.fields['do_upsell'].choices = APP_UPSELL_CHOICES
|
||||
|
||||
self.fields['free'].queryset = (self.extra['amo_user'].addons
|
||||
.exclude(pk=self.addon.pk)
|
||||
.filter(premium_type__in=amo.ADDON_FREES,
|
||||
status__in=amo.VALID_STATUSES,
|
||||
type=self.addon.type))
|
||||
.exclude(pk=self.addon.pk)
|
||||
.filter(premium_type__in=amo.ADDON_FREES,
|
||||
status__in=amo.VALID_STATUSES,
|
||||
type=self.addon.type))
|
||||
if (not self.initial.get('price') and
|
||||
len(list(self.fields['price'].choices)) > 1):
|
||||
# Tier 0 (Free) should not be the default selection.
|
||||
|
@ -534,9 +544,6 @@ class PremiumForm(happyforms.Form):
|
|||
return self.cleaned_data['price']
|
||||
|
||||
def clean_free(self):
|
||||
if (self.cleaned_data['do_upsell']
|
||||
and not self.cleaned_data['free']):
|
||||
raise_required()
|
||||
return self.cleaned_data['free']
|
||||
|
||||
def save(self):
|
||||
|
@ -549,7 +556,7 @@ class PremiumForm(happyforms.Form):
|
|||
premium.save()
|
||||
|
||||
upsell = self.addon.upsold
|
||||
if self.cleaned_data['do_upsell'] and self.cleaned_data['free']:
|
||||
if self.cleaned_data['free']:
|
||||
|
||||
# Check if this app was already a premium version for another app.
|
||||
if upsell and upsell.free != self.cleaned_data['free']:
|
||||
|
@ -559,7 +566,7 @@ class PremiumForm(happyforms.Form):
|
|||
upsell = AddonUpsell(premium=self.addon)
|
||||
upsell.free = self.cleaned_data['free']
|
||||
upsell.save()
|
||||
elif not self.cleaned_data['do_upsell'] and upsell:
|
||||
elif not self.cleaned_data['free'] and upsell:
|
||||
upsell.delete()
|
||||
|
||||
# Check for free -> paid for already public apps.
|
||||
|
@ -573,6 +580,11 @@ class PremiumForm(happyforms.Form):
|
|||
RereviewQueue.flag(self.addon, amo.LOG.REREVIEW_FREE_TO_PAID)
|
||||
|
||||
self.addon.premium_type = premium_type
|
||||
|
||||
if waffle.switch_is_active('currencies'):
|
||||
currencies = self.cleaned_data['currencies']
|
||||
self.addon.premium.update(currencies=currencies)
|
||||
|
||||
self.addon.save()
|
||||
|
||||
# If they checked later in the wizard and then decided they want
|
||||
|
@ -655,20 +667,12 @@ class AppFormBasic(addons.forms.AddonFormBase):
|
|||
|
||||
|
||||
class PaypalSetupForm(happyforms.Form):
|
||||
business_account = forms.ChoiceField(widget=forms.RadioSelect, choices=[],
|
||||
label=_lazy(u'Do you already have a PayPal Premier '
|
||||
'or Business account?'))
|
||||
email = forms.EmailField(required=False,
|
||||
label=_lazy(u'PayPal email address'))
|
||||
|
||||
def __init__(self, *args, **kw):
|
||||
super(PaypalSetupForm, self).__init__(*args, **kw)
|
||||
self.fields['business_account'].choices = (('yes', _lazy(u'Yes')),
|
||||
('no', _lazy(u'No')))
|
||||
|
||||
def clean(self):
|
||||
data = self.cleaned_data
|
||||
if data.get('business_account') == 'yes' and not data.get('email'):
|
||||
if not data.get('email'):
|
||||
msg = _(u'The PayPal email is required.')
|
||||
self._errors['email'] = self.error_class([msg])
|
||||
|
||||
|
@ -781,19 +785,6 @@ class AppFormSupport(addons.forms.AddonFormBase):
|
|||
return super(AppFormSupport, self).save(commit)
|
||||
|
||||
|
||||
class CurrencyForm(happyforms.Form):
|
||||
currencies = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple,
|
||||
required=False,
|
||||
label=_lazy(u'Other currencies'))
|
||||
|
||||
def __init__(self, *args, **kw):
|
||||
super(CurrencyForm, self).__init__(*args, **kw)
|
||||
choices = (PriceCurrency.objects.values_list('currency', flat=True)
|
||||
.distinct())
|
||||
self.fields['currencies'].choices = [(k, amo.PAYPAL_CURRENCIES[k])
|
||||
for k in choices if k]
|
||||
|
||||
|
||||
class AppAppealForm(happyforms.Form):
|
||||
"""
|
||||
If a developer's app is rejected he can make changes and request
|
||||
|
|
|
@ -62,10 +62,6 @@
|
|||
(addon.get_dev_url('refunds'), _('Manage Refunds'))
|
||||
) %}
|
||||
{% endif %}
|
||||
{% if addon.needs_paypal() %}
|
||||
{% do manage_urls.insert(1,
|
||||
(addon.get_dev_url('paypal_setup'), _('Manage PayPal'))) %}
|
||||
{% endif %}
|
||||
{% if addon.is_webapp() and addon.premium_type in amo.ADDON_INAPPS
|
||||
and waffle.switch('in-app-payments') %}
|
||||
{% do manage_urls.insert(1,
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
check_addon_ownership(request, addon, support=True) %}
|
||||
{% do urls.insert(4, (addon.get_dev_url('refunds'), _('Manage Refunds'))) %}
|
||||
{% endif %}
|
||||
{% if addon.needs_paypal() %}
|
||||
{% do urls.insert(4, (addon.get_dev_url('paypal_setup'), _('Manage PayPal'))) %}
|
||||
{% endif %}
|
||||
{% if addon.is_webapp() and addon.premium_type in amo.ADDON_INAPPS
|
||||
and waffle.switch('in-app-payments') %}
|
||||
{% do urls.insert(4,
|
||||
|
@ -39,7 +36,10 @@
|
|||
<div class="island" id="edit-addon-nav">
|
||||
<ul class="refinements">
|
||||
{% for url, title in urls %}
|
||||
<li{% if request.path.startswith(url) %} class="selected"{% endif %}>
|
||||
{# Hook up PayPal urls under payment urls #}
|
||||
<li{% if request.path.startswith(url)
|
||||
or (request.path.startswith(addon.get_dev_url('paypal_setup'))
|
||||
and url == addon.get_dev_url('payments')) %} class="selected"{% endif %}>
|
||||
<a href="{{ url }}">{{ title }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
<script type="text/template" id="choose-payment-account-template">
|
||||
<section class="choose-payment-account">
|
||||
<h2>{{ _('Select Payment Provider') }}</h2>
|
||||
<a class="close">{{ _('close') }}</a>
|
||||
<div id="choose" class="brform c paypal-inline">
|
||||
<div>
|
||||
<form>
|
||||
<button type="submit" class="bluevia prominent">BlueVia</button>
|
||||
</form>
|
||||
<a href="https://bluevia.com/" target="_blank">{{ _('Learn More') }}</a>
|
||||
</div>
|
||||
<div class="or">{{ _('or') }}</div>
|
||||
<div>
|
||||
<form>
|
||||
<button type="submit" class="paypal prominent">Pay<em>Pal</em></button>
|
||||
</form>
|
||||
<a href="https://paypal.com/" target="_blank">{{ _('Learn More') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</script>
|
|
@ -0,0 +1,23 @@
|
|||
<script type="text/template" id="add-paypal-email-template">
|
||||
<section class="add-paypal-email">
|
||||
<h2>{{ _('Add PayPal Account') }}</h2>
|
||||
<a class="close">{{ _('close') }}</a>
|
||||
<div class="brform c paypal-inline">
|
||||
<label>{{ _('What is the email for your PayPal account?') }}</label>
|
||||
<input type="text" name="email" class="email">
|
||||
</div>
|
||||
<span class="email-error">{{ _('Please enter a valid Paypal email.') }}</span>
|
||||
<a href="{{ paypal_create_url }}" target="_blank">{{ _('No Premier or Business PayPal account? Sign up.') }}</a>
|
||||
<p class='redirect-info'>
|
||||
{{ _('On continue, log in to PayPal to give Marketplace
|
||||
permissions to process refunds and access your PayPal account
|
||||
information.') }}
|
||||
</p>
|
||||
<div class="listing-footer">
|
||||
<button type="submit" class="continue prominent"
|
||||
data-paypal-url="{{ addon.get_dev_url('paypal_setup') }}">
|
||||
{{ _('Continue') }}
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</script>
|
|
@ -0,0 +1,8 @@
|
|||
{% from 'developers/includes/macros.html' import required %}
|
||||
<div class="brform" id="payments-currencies">
|
||||
<div class="extra">
|
||||
</div>
|
||||
<label for="id_price">{{ form.currencies.label }}</label>
|
||||
{{ form.currencies.errors }}
|
||||
<div class="indent">{{ form.currencies }}</div>
|
||||
</div>
|
|
@ -0,0 +1,62 @@
|
|||
{% from 'developers/includes/macros.html' import required %}
|
||||
<div class="brform" id="payments-payment-account">
|
||||
<label id="payment-label" for="id_payment_account">{{ _('Use Payment Account') }} {{ required() }}</label>
|
||||
<div class="extra">
|
||||
<a href="#" class="payment-account-actions" data-action="add">{{ _('Add Account') }}</a>
|
||||
</div>
|
||||
|
||||
{% if addon.paypal_id %}
|
||||
<div id="paypal" class="indent payment-option">
|
||||
<label for="paypal-account">PayPal</label>
|
||||
<span id="paypal-id">{{ addon.paypal_id }}</span>
|
||||
<div id="paypal-id-verify" data-url="{{ addon.get_dev_url('paypal_setup_check') }}">
|
||||
<p class="loading-submit"></p>
|
||||
<div class="paypal-pass js-hidden">
|
||||
<span id="check-passed" class="check-icon"></span>
|
||||
</div>
|
||||
<div class="paypal-fail js-hidden">
|
||||
<span id="check-failed" class="check-icon"></span>
|
||||
</div>
|
||||
|
||||
<div class="item-actions paypal-actions">
|
||||
<ul>
|
||||
<li>
|
||||
<a id="remove-account"
|
||||
data-remove-url="{{ addon.get_dev_url('paypal_remove') }}">
|
||||
{{ _('Remove Account') }}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ addon.get_dev_url('paypal_setup_details')}}"
|
||||
data-url="{{ addon.get_dev_url('paypal_setup_details') }}">
|
||||
{{ _('Edit Contact Details') }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="setup-bounce">
|
||||
<a id="setup-bounce-link"
|
||||
data-paypal-url="{{ addon.get_dev_url('paypal_setup') }}">
|
||||
{{ _('Set Up Permissions') }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="paypal-errors">
|
||||
<p><strong>
|
||||
{{ _('We found errors on your PayPal account') }}:
|
||||
</strong></p>
|
||||
<ul></ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="bluevia" class="indent payment-option">
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
<p>{{ _('You have not yet setup any payment providers. You will not be able to
|
||||
receive payments until you do so.') }}</p>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
|
@ -1,14 +1,15 @@
|
|||
{% from 'developers/includes/macros.html' import required %}
|
||||
<div class="brform" id="payments-price-level">
|
||||
<label for="id_price">{{ form.price.label }} {{ required() }}</label>
|
||||
<div class="extra">
|
||||
<p>
|
||||
{% trans doc_url='https://developer.mozilla.org/en/Apps/Marketplace_Payments#Price_tiers' %}
|
||||
Select a price tier below. Price tiers are based on US Dollars and are fixed for all supported countries.
|
||||
For more information <a href="{{ doc_url }}">view our pricing matrix</a>.
|
||||
To sell your app in the Marketplace, you'll first need to set its price
|
||||
tier below. Price tiers are based on US Dollars and are fixed for all supported
|
||||
currencies. <a href="{{ doc_url }}">Learn more about price tiers</a>.
|
||||
{% endtrans %}
|
||||
</p>
|
||||
</div>
|
||||
<label for="id_price">{{ form.price.label }} {{ required() }}</label>
|
||||
{{ form.price.errors }}
|
||||
<div>{{ form.price }}</div>
|
||||
</div>
|
||||
|
|
|
@ -1,24 +1,21 @@
|
|||
{% from 'developers/includes/macros.html' import some_html_tip %}
|
||||
<div class="brform c" id="payments-upsell">
|
||||
{% if form.fields['free'].queryset.count() %}
|
||||
{{ form.do_upsell.errors }}
|
||||
{{ form.do_upsell }}
|
||||
<div class="indent">
|
||||
{{ form.free.errors }}
|
||||
<p>{{ form.free }}</p>
|
||||
<p class="extra">
|
||||
{% trans %}
|
||||
Pitch your premium app over the free version. This will appear
|
||||
on the free app's details page.
|
||||
{% endtrans %}
|
||||
</p>
|
||||
</div>
|
||||
{{ form.free.errors }}
|
||||
<label>{{ form.free.label }} {{ required() }}</label>
|
||||
{{ form.free }}
|
||||
<p class="note">
|
||||
{% trans %}
|
||||
Linking this app will promote your premium app next to the free
|
||||
version.
|
||||
{% endtrans %}
|
||||
</p>
|
||||
{% else %}
|
||||
<label>{{ _('App upsell') if webapp else _('Add-on upsell') }}</label>
|
||||
<label>{{ _('App Upsell') }}</label>
|
||||
<div class="extra">
|
||||
{% trans %}
|
||||
If you have a free app, you can pitch your premium app
|
||||
on that free app. Currently you have no free apps.
|
||||
If you have a free app, you can link and promote your premium app
|
||||
next to the free version here.
|
||||
{% endtrans %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
{% block content %}
|
||||
<header>
|
||||
{{ hub_breadcrumbs(addon, items=[(addon.get_dev_url('paypal_setup'),
|
||||
_('Manage PayPal')),
|
||||
{{ hub_breadcrumbs(addon, items=[(addon.get_dev_url('payments'),
|
||||
_('Manage Payments')),
|
||||
(None, title)]) }}
|
||||
<h1>{{ title }}</h1>
|
||||
</header>
|
||||
|
@ -71,10 +71,10 @@
|
|||
</div>
|
||||
</fieldset>
|
||||
<footer class="listing-footer">
|
||||
<form action="{{ addon.get_dev_url('payments') }}" method="get">
|
||||
<button type="submit">{{ _('Cancel') }}</button>
|
||||
</form>
|
||||
<button type="submit">{{ button }}</button>
|
||||
{% trans cancel=addon.get_dev_url('paypal_setup') %}
|
||||
or <a href="{{ cancel }}">Cancel</a>
|
||||
{% endtrans %}
|
||||
</footer>
|
||||
</form>
|
||||
</section>
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
{% extends 'developers/base_impala.html' %}
|
||||
|
||||
{% set title = _('Request Permission') %}}
|
||||
{% block title %}{{ hub_page_title(title, addon) }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<header>
|
||||
{{ hub_breadcrumbs(addon, items=[(addon.get_dev_url('paypal_setup'),
|
||||
_('Manage PayPal')),
|
||||
(None, title)]) }}
|
||||
<h1>{{ title }}</h1>
|
||||
</header>
|
||||
<section class="primary payments island devhub-form" role="main">
|
||||
<p>{{ _('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>
|
||||
</section>
|
||||
{% include 'developers/includes/addons_edit_nav.html' %}
|
||||
{% endblock %}
|
|
@ -46,8 +46,7 @@
|
|||
<form method="post">
|
||||
{{ csrf() }}
|
||||
<div class="brform paypal-inline paypal devhub-form">
|
||||
{{ form_field(paypal_form.business_account) }}
|
||||
{{ form_field(paypal_form.email) }}
|
||||
|
||||
</div>
|
||||
<footer class="listing-footer">
|
||||
<button type="submit" name="form" value="paypal">{{ _('Change') }}</button>
|
||||
|
|
|
@ -19,29 +19,35 @@
|
|||
<section class="primary payments island devhub-form" role="main">
|
||||
<form method="post" action="{{ addon.get_dev_url('payments') }}">
|
||||
{{ csrf() }}
|
||||
<div class="brform" id="payments-premium-type">
|
||||
<div class="brform" id="premium-type">
|
||||
<label for="id_premium_type">
|
||||
{{ form.premium_type.label }} {{ required() }}
|
||||
</label>
|
||||
<div class="extra">
|
||||
{{ _('How your app will displayed on the Marketplace.') }}
|
||||
</div>
|
||||
{{ form.premium_type.errors }}
|
||||
<div class="choice">{{ form.premium_type }}</div>
|
||||
<p>
|
||||
{% trans payments_url='https://developer.mozilla.org/en/Apps/Marketplace_Payments',
|
||||
receipts_url='https://developer.mozilla.org/en/Apps/Validating_a_receipt' %}
|
||||
Learn about <a href="{{ payments_url }}" target="_blank">different payment types</a>.
|
||||
Learn about <a href="{{ receipts_url }}" target="_blank">validating purchase receipts in your app</a>.
|
||||
Learn about <a href="{{ receipts_url }}" target="_blank">validating purchase receipts</a>.
|
||||
{% endtrans %}
|
||||
</p>
|
||||
</div>
|
||||
{% include 'developers/payments/includes/tier.html' %}
|
||||
{% include 'developers/payments/includes/upsell.html' %}
|
||||
<div class="divider"></div>
|
||||
<div id="premium-detail" class="premium-detail">
|
||||
{% include 'developers/payments/includes/tier.html' %}
|
||||
{% if waffle.switch('currencies') %}
|
||||
{% include 'developers/payments/includes/currencies.html' %}
|
||||
{% endif %}
|
||||
{% include 'developers/payments/includes/payment_account.html' %}
|
||||
{% include 'developers/payments/includes/upsell.html' %}
|
||||
</div>
|
||||
<footer class="listing-footer">
|
||||
<button type="submit">{{ _('Save Changes') }}</button>
|
||||
</footer>
|
||||
</form>
|
||||
</section>
|
||||
{% include 'developers/includes/addons_edit_nav.html' %}
|
||||
{% include 'developers/payments/includes/add_payment_account_choose.html' %}
|
||||
{% include 'developers/payments/includes/add_payment_account_paypal_email.html' %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -72,24 +72,16 @@ class TestPreviewForm(amo.tests.TestCase):
|
|||
|
||||
class TestPaypalSetupForm(amo.tests.TestCase):
|
||||
|
||||
def test_email_not_required(self):
|
||||
data = {'business_account': 'no',
|
||||
'email': ''}
|
||||
assert forms.PaypalSetupForm(data=data).is_valid()
|
||||
|
||||
def test_email_required(self):
|
||||
data = {'business_account': 'yes',
|
||||
'email': ''}
|
||||
data = {'email': ''}
|
||||
assert not forms.PaypalSetupForm(data=data).is_valid()
|
||||
|
||||
def test_email_gotten(self):
|
||||
data = {'business_account': 'yes',
|
||||
'email': 'foo@bar.com'}
|
||||
data = {'email': 'foo@bar.com'}
|
||||
assert forms.PaypalSetupForm(data=data).is_valid()
|
||||
|
||||
def test_email_malformed(self):
|
||||
data = {'business_account': 'yes',
|
||||
'email': 'foo'}
|
||||
data = {'email': 'foo'}
|
||||
assert not forms.PaypalSetupForm(data=data).is_valid()
|
||||
|
||||
|
||||
|
@ -233,7 +225,6 @@ class TestRegionForm(amo.tests.WebappTestCase):
|
|||
eq_(form.initial['regions'], regions)
|
||||
eq_(form.initial['other_regions'], False)
|
||||
|
||||
|
||||
def test_worldwide_only(self):
|
||||
form = forms.RegionForm(data={'other_regions': 'on'}, **self.kwargs)
|
||||
assert form.is_valid()
|
||||
|
|
|
@ -200,7 +200,6 @@ class TestAppDashboard(AppHubTest):
|
|||
expected = [
|
||||
('Manage Status', app.get_dev_url('versions')),
|
||||
('Manage In-App Payments', app.get_dev_url('in_app_config')),
|
||||
('Manage PayPal', app.get_dev_url('paypal_setup')),
|
||||
('Manage Refunds', app.get_dev_url('refunds')),
|
||||
]
|
||||
eq_(doc('.status-link').length, 0)
|
||||
|
@ -416,6 +415,9 @@ class TestPaymentsProfile(amo.tests.TestCase):
|
|||
class MarketplaceMixin(object):
|
||||
|
||||
def setUp(self):
|
||||
waffle.models.Switch.objects.get_or_create(name='currencies',
|
||||
active=True)
|
||||
|
||||
self.addon = Addon.objects.get(id=337141)
|
||||
self.addon.update(status=amo.STATUS_NOMINATED,
|
||||
highest_status=amo.STATUS_NOMINATED)
|
||||
|
@ -445,6 +447,10 @@ class MarketplaceMixin(object):
|
|||
self.addon.update(premium_type=amo.ADDON_PREMIUM,
|
||||
paypal_id='a@a.com')
|
||||
|
||||
session = self.client.session
|
||||
session['unconfirmed_paypal_id'] = self.addon.paypal_id
|
||||
session.save()
|
||||
|
||||
|
||||
# Mock out verifying the paypal id has refund permissions with paypal and
|
||||
# that the account exists on paypal.
|
||||
|
@ -452,15 +458,13 @@ class MarketplaceMixin(object):
|
|||
@mock.patch('mkt.developers.forms.PremiumForm.clean',
|
||||
new=lambda x: x.cleaned_data)
|
||||
class TestMarketplace(MarketplaceMixin, amo.tests.TestCase):
|
||||
fixtures = ['base/users', 'webapps/337141-steamcube']
|
||||
fixtures = ['base/users', 'webapps/337141-steamcube', 'market/prices']
|
||||
|
||||
def get_data(self, **kw):
|
||||
data = {
|
||||
'price': self.price.pk,
|
||||
'free': self.other_addon.pk,
|
||||
'do_upsell': 1,
|
||||
'premium_type': amo.ADDON_PREMIUM,
|
||||
'support_email': 'c@c.com',
|
||||
}
|
||||
data.update(kw)
|
||||
return data
|
||||
|
@ -473,15 +477,29 @@ class TestMarketplace(MarketplaceMixin, amo.tests.TestCase):
|
|||
|
||||
def test_set(self):
|
||||
self.setup_premium()
|
||||
res = self.client.post(self.url, data={
|
||||
'support_email': 'c@c.com',
|
||||
'price': self.price_two.pk,
|
||||
'premium_type': amo.ADDON_PREMIUM
|
||||
})
|
||||
res = self.client.post(self.url, data=self.get_data(price=self.
|
||||
price_two.pk))
|
||||
eq_(res.status_code, 302)
|
||||
self.addon = Addon.objects.get(pk=self.addon.pk)
|
||||
eq_(self.addon.addonpremium.price, self.price_two)
|
||||
|
||||
def test_set_currency(self):
|
||||
self.setup_premium()
|
||||
res = self.client.post(self.url,
|
||||
data=self.get_data(currencies=['EUR', 'BRL']))
|
||||
eq_(res.status_code, 302)
|
||||
self.addon = Addon.objects.get(pk=self.addon.pk)
|
||||
eq_(self.addon.premium.currencies, ['EUR', 'BRL'])
|
||||
|
||||
def test_set_currency_fail(self):
|
||||
self.setup_premium()
|
||||
res = self.client.post(self.url,
|
||||
data=self.get_data(currencies=['EUR', 'LOL']))
|
||||
eq_(res.status_code, 200)
|
||||
self.assertFormError(res, 'form', 'currencies',
|
||||
[u'Select a valid choice. '
|
||||
'LOL is not one of the available choices.'])
|
||||
|
||||
def test_set_upsell(self):
|
||||
self.setup_premium()
|
||||
res = self.client.post(self.url, data=self.get_data())
|
||||
|
@ -513,7 +531,7 @@ class TestMarketplace(MarketplaceMixin, amo.tests.TestCase):
|
|||
upsell = AddonUpsell.objects.create(free=self.other_addon,
|
||||
premium=self.addon)
|
||||
eq_(self.addon._upsell_to.all()[0], upsell)
|
||||
self.client.post(self.url, data=self.get_data(do_upsell=0))
|
||||
self.client.post(self.url, data=self.get_data(free=''))
|
||||
eq_(len(self.addon._upsell_to.all()), 0)
|
||||
|
||||
def test_replace_upsell(self):
|
||||
|
@ -542,23 +560,26 @@ class TestMarketplace(MarketplaceMixin, amo.tests.TestCase):
|
|||
@mock.patch('paypal.get_permissions_token', lambda x, y: x.upper())
|
||||
@mock.patch('paypal.get_personal_data', lambda x: {'email': 'a@a.com'})
|
||||
def test_permissions_token(self):
|
||||
|
||||
self.setup_premium()
|
||||
eq_(self.addon.premium.paypal_permissions_token, '')
|
||||
url = self.addon.get_dev_url('acquire_refund_permission')
|
||||
self.client.get(urlparams(url, request_token='foo',
|
||||
verification_code='bar'))
|
||||
self.addon = Addon.objects.get(pk=self.addon.pk)
|
||||
eq_(self.addon.premium.paypal_permissions_token, 'FOO')
|
||||
eq_(self.addon.premium.paypal_permissions_token.lower(), 'foo')
|
||||
|
||||
@mock.patch('paypal.get_permissions_token', lambda x, y: x.upper())
|
||||
@mock.patch('paypal.get_personal_data', lambda x: {})
|
||||
def test_permissions_token_different_email(self):
|
||||
# With different email, the new token overwrites the old one, and the
|
||||
# paypal_id stays the same.
|
||||
self.setup_premium()
|
||||
url = self.addon.get_dev_url('acquire_refund_permission')
|
||||
self.client.get(urlparams(url, request_token='foo',
|
||||
verification_code='bar'))
|
||||
self.addon = Addon.objects.get(pk=self.addon.pk)
|
||||
eq_(self.addon.premium.paypal_permissions_token, '')
|
||||
eq_(self.addon.premium.paypal_permissions_token.lower(), 'foo')
|
||||
|
||||
@mock.patch('mkt.developers.views.client')
|
||||
def test_permissions_token_solitude(self, client):
|
||||
|
@ -580,7 +601,7 @@ class TestMarketplace(MarketplaceMixin, amo.tests.TestCase):
|
|||
res = self.client.get(urlparams(url, request_token='foo',
|
||||
verification_code='bar'))
|
||||
self.assertRedirects(res,
|
||||
self.addon.get_dev_url('paypal_setup_bounce'))
|
||||
self.addon.get_dev_url('payments'))
|
||||
|
||||
|
||||
class TestIssueRefund(amo.tests.TestCase):
|
||||
|
|
|
@ -241,7 +241,7 @@ class TestDeveloperRoleAccess(amo.tests.TestCase):
|
|||
eq_(res.status_code, 403)
|
||||
|
||||
def test_urls(self):
|
||||
urls = ['owner', 'payments', 'paypal_setup']
|
||||
urls = ['owner', 'payments']
|
||||
for url in urls:
|
||||
self._check_it(self.webapp.get_dev_url(url))
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import json
|
||||
|
||||
import mock
|
||||
from nose.tools import eq_
|
||||
from pyquery import PyQuery as pq
|
||||
|
@ -105,11 +107,6 @@ class TestPaypal(amo.tests.TestCase):
|
|||
def get_webapp(self):
|
||||
return Addon.objects.get(pk=337141)
|
||||
|
||||
def test_not_premium(self):
|
||||
self.webapp.update(premium_type=amo.ADDON_FREE)
|
||||
res = self.client.get(self.url)
|
||||
eq_(res.status_code, 302)
|
||||
|
||||
def test_partial_submit(self):
|
||||
from mkt.submit.models import AppSubmissionChecklist
|
||||
AppSubmissionChecklist.objects.create(addon=self.webapp)
|
||||
|
@ -117,53 +114,21 @@ class TestPaypal(amo.tests.TestCase):
|
|||
res = self.client.get(self.url, follow=True)
|
||||
self.assertRedirects(res, reverse('submit.app.terms'))
|
||||
|
||||
@mock.patch('mkt.developers.views.waffle.switch_is_active')
|
||||
def test_currencies_fails(self, switch_is_active):
|
||||
switch_is_active.return_value = True
|
||||
def test_paypal_setup_json(self):
|
||||
self.webapp.update(premium_type=amo.ADDON_PREMIUM)
|
||||
res = self.client.post(self.url, {'currencies': ['GBP', 'EUR'],
|
||||
'form': 'currency'})
|
||||
eq_(res.status_code, 200)
|
||||
self.assertFormError(res, 'currency_form', 'currencies',
|
||||
[u'Select a valid choice. '
|
||||
'GBP is not one of the available choices.'])
|
||||
|
||||
@mock.patch('mkt.developers.views.waffle.switch_is_active')
|
||||
def test_currencies_passes(self, switch_is_active):
|
||||
switch_is_active.return_value = True
|
||||
self.webapp.update(premium_type=amo.ADDON_PREMIUM)
|
||||
res = self.client.post(self.url, {'currencies': ['EUR', 'BRL'],
|
||||
'form': 'currency'})
|
||||
eq_(res.status_code, 302)
|
||||
eq_(self.webapp.premium.currencies, ['EUR', 'BRL'])
|
||||
|
||||
def test_later(self):
|
||||
self.webapp.update(premium_type=amo.ADDON_PREMIUM)
|
||||
res = self.client.post(self.url, {'email': 'a@a.com',
|
||||
'business_account': 'yes',
|
||||
'form': 'paypal'})
|
||||
self.assertRedirects(res,
|
||||
self.webapp.get_dev_url('paypal_setup_bounce'))
|
||||
|
||||
@mock.patch('mkt.developers.views.client')
|
||||
@mock.patch('mkt.developers.views.waffle.flag_is_active')
|
||||
def test_later_solitude(self, flag_is_active, client):
|
||||
flag_is_active.return_value = True
|
||||
self.webapp.update(premium_type=amo.ADDON_PREMIUM)
|
||||
self.client.post(self.url, {'email': 'a@a.com', 'form': 'paypal',
|
||||
'business_account': 'yes'})
|
||||
eq_(client.create_seller_paypal.call_args[0][0], self.webapp)
|
||||
eq_(client.patch_seller_paypal.call_args[1]['data']['paypal_id'],
|
||||
'a@a.com')
|
||||
res = json.loads(self.client.post(self.url, {'email':
|
||||
'a@a.com'}).content)
|
||||
eq_(res['valid'], True)
|
||||
eq_('paypal_url' in res, True)
|
||||
eq_(len(res['message']), 0)
|
||||
|
||||
@mock.patch('mkt.developers.views.client')
|
||||
def test_bounce_solitude(self, client):
|
||||
self.create_flag(name='solitude-payments')
|
||||
url = 'http://foo.com'
|
||||
client.post_permission_url.return_value = {'token': url}
|
||||
self.webapp.update(premium_type=amo.ADDON_PREMIUM, paypal_id='a@.com')
|
||||
res = self.client.post(self.webapp.get_dev_url('paypal_setup_bounce'))
|
||||
eq_(pq(res.content)('section.primary a.button').attr('href'), url)
|
||||
url = self.webapp.get_dev_url('payments')
|
||||
eq_(pq(res.content)('section.primary form')[1].action, url)
|
||||
|
||||
|
||||
class TestPaypalResponse(amo.tests.TestCase):
|
||||
|
@ -175,6 +140,10 @@ class TestPaypalResponse(amo.tests.TestCase):
|
|||
self.webapp.update(status=amo.STATUS_NULL)
|
||||
self.client.login(username='admin@mozilla.com', password='password')
|
||||
|
||||
session = self.client.session
|
||||
session['unconfirmed_paypal_id'] = 'bob@dog.com'
|
||||
session.save()
|
||||
|
||||
def get_webapp(self):
|
||||
return Addon.objects.get(pk=337141)
|
||||
|
||||
|
|
|
@ -13,16 +13,19 @@ def paypal_patterns(prefix):
|
|||
return patterns('',
|
||||
url('^$', views.paypal_setup,
|
||||
name='mkt.developers.%s.paypal_setup' % prefix),
|
||||
url('^bounce$', views.paypal_setup_confirm,
|
||||
name='mkt.developers.%s.paypal_setup_bounce' % prefix,
|
||||
kwargs={'source': 'paypal'}),
|
||||
url('^confirm$', views.paypal_setup_confirm,
|
||||
name='mkt.developers.%s.paypal_setup_confirm' % prefix,
|
||||
kwargs={'source': 'paypal'}),
|
||||
url('^details$', views.paypal_setup_confirm,
|
||||
name='mkt.developers.%s.paypal_setup_details' % prefix,
|
||||
kwargs={'source': 'developers'}),
|
||||
url('^bounce$', views.paypal_setup_bounce,
|
||||
name='mkt.developers.%s.paypal_setup_bounce' % prefix),
|
||||
url('^check$', views.paypal_setup_check,
|
||||
name='mkt.developers.%s.paypal_setup_check' % prefix),
|
||||
url('^remove$', views.paypal_remove,
|
||||
name='mkt.developers.%s.paypal_remove' % prefix),
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ from versions.models import Version
|
|||
from mkt.constants import APP_IMAGE_SIZES
|
||||
from mkt.developers.decorators import dev_required
|
||||
from mkt.developers.forms import (AppFormBasic, AppFormDetails, AppFormMedia,
|
||||
AppFormSupport, CategoryForm, CurrencyForm,
|
||||
AppFormSupport, CategoryForm,
|
||||
ImageAssetFormSet, InappConfigForm,
|
||||
PaypalSetupForm, PreviewFormSet, RegionForm,
|
||||
trap_duplicate)
|
||||
|
@ -281,71 +281,29 @@ def payments(request, addon_id, addon, webapp=False):
|
|||
return _premium(request, addon_id, addon, webapp)
|
||||
|
||||
|
||||
@dev_required(owner_for_post=True, webapp=True)
|
||||
def paypal_setup(request, addon_id, addon, webapp):
|
||||
if addon.premium_type == amo.ADDON_FREE:
|
||||
messages.error(request, 'Your app does not use payments.')
|
||||
return redirect(addon.get_dev_url('payments'))
|
||||
|
||||
paypal_form = PaypalSetupForm(request.POST or None)
|
||||
currency_form = CurrencyForm(request.POST or None,
|
||||
initial={'currencies': addon.premium.currencies
|
||||
if addon.premium else {}})
|
||||
|
||||
context = {'addon': addon, 'paypal_form': paypal_form,
|
||||
'currency_form': currency_form}
|
||||
|
||||
if request.POST.get('form') == 'paypal' and paypal_form.is_valid():
|
||||
existing = paypal_form.cleaned_data['business_account']
|
||||
if existing != 'yes':
|
||||
# Go create an account.
|
||||
# TODO: this will either become the API or something some better
|
||||
# URL for the future.
|
||||
return redirect(settings.PAYPAL_CGI_URL)
|
||||
else:
|
||||
# Go setup your details on paypal.
|
||||
# TODO(solitude): we will remove this.
|
||||
addon.update(paypal_id=paypal_form.cleaned_data['email'])
|
||||
|
||||
if waffle.flag_is_active(request, 'solitude-payments'):
|
||||
obj = client.create_seller_paypal(addon)
|
||||
client.patch_seller_paypal(pk=obj['resource_pk'],
|
||||
data={'paypal_id': paypal_form.cleaned_data['email']})
|
||||
|
||||
if addon.premium and addon.premium.paypal_permissions_token:
|
||||
addon.premium.update(paypal_permissions_token='')
|
||||
return redirect(addon.get_dev_url('paypal_setup_bounce'))
|
||||
|
||||
if (waffle.switch_is_active('currencies') and
|
||||
request.POST.get('form') == 'currency' and currency_form.is_valid()):
|
||||
currencies = currency_form.cleaned_data['currencies']
|
||||
addon.premium.update(currencies=currencies)
|
||||
messages.success(request, _('Currencies updated.'))
|
||||
return redirect(addon.get_dev_url('paypal_setup'))
|
||||
|
||||
return jingo.render(request, 'developers/payments/paypal-setup.html',
|
||||
context)
|
||||
|
||||
|
||||
@json_view
|
||||
@dev_required(owner_for_post=True, webapp=True)
|
||||
def paypal_setup_check(request, addon_id, addon, webapp):
|
||||
if waffle.flag_is_active(request, 'solitude-payments'):
|
||||
data = client.post_account_check(data={'seller': addon})
|
||||
return {'valid': data['passed'], 'message': data['errors']}
|
||||
else:
|
||||
if not addon.paypal_id:
|
||||
return {'valid': False, 'message': ['No PayPal email.']}
|
||||
def paypal_setup(request, addon_id, addon, webapp):
|
||||
paypal_form = PaypalSetupForm(request.POST or None)
|
||||
|
||||
check = Check(addon=addon)
|
||||
check.all()
|
||||
return {'valid': check.passed, 'message': check.errors}
|
||||
if paypal_form.is_valid():
|
||||
# Don't save the paypal_id into the addon until we set up permissions
|
||||
# and confirm it as good.
|
||||
request.session['unconfirmed_paypal_id'] = (paypal_form
|
||||
.cleaned_data['email'])
|
||||
|
||||
# Go setup your details on paypal.
|
||||
paypal_url = get_paypal_bounce_url(request, str(addon_id), addon,
|
||||
webapp, json_view=True)
|
||||
|
||||
return {'valid': True, 'message': [], 'paypal_url': paypal_url}
|
||||
|
||||
return {'valid': False, 'message': [_('Form not valid')]}
|
||||
|
||||
|
||||
@dev_required(owner_for_post=True, webapp=True)
|
||||
def paypal_setup_bounce(request, addon_id, addon, webapp):
|
||||
if not addon.paypal_id:
|
||||
messages.error(request, 'We need a PayPal email before continuing.')
|
||||
def get_paypal_bounce_url(request, addon_id, addon, webapp, json_view=False):
|
||||
if not addon.paypal_id and not json_view:
|
||||
messages.error(request, _('We need a PayPal email before continuing.'))
|
||||
return redirect(addon.get_dev_url('paypal_setup'))
|
||||
|
||||
dest = 'developers'
|
||||
|
@ -359,57 +317,7 @@ def paypal_setup_bounce(request, addon_id, addon, webapp):
|
|||
# TODO(solitude): remove this.
|
||||
else:
|
||||
paypal_url = paypal.get_permission_url(addon, dest, perms)
|
||||
|
||||
return jingo.render(request,
|
||||
'developers/payments/paypal-details-request.html',
|
||||
{'paypal_url': paypal_url, 'addon': addon})
|
||||
|
||||
|
||||
@dev_required(owner_for_post=True, webapp=True)
|
||||
def paypal_setup_confirm(request, addon_id, addon, webapp, source='paypal'):
|
||||
# If you bounce through paypal as you do permissions changes set the
|
||||
# source to paypal.
|
||||
if source == 'paypal':
|
||||
msg = _('PayPal set up complete.')
|
||||
title = _('Confirm Details')
|
||||
button = _('Continue')
|
||||
# If you just hit this page from the Manage Paypal, show some less
|
||||
# wizardy stuff.
|
||||
else:
|
||||
msg = _('Changes saved.')
|
||||
title = _('Contact Details')
|
||||
button = _('Save')
|
||||
|
||||
data = {}
|
||||
if waffle.flag_is_active(request, 'solitude-payments'):
|
||||
data = client.get_seller_paypal_if_exists(addon) or {}
|
||||
|
||||
# TODO(solitude): remove this bit.
|
||||
# If it's not in solitude, use the local version
|
||||
adp, created = (AddonPaymentData.objects
|
||||
.safer_get_or_create(addon=addon))
|
||||
if not data:
|
||||
data = model_to_dict(adp)
|
||||
|
||||
form = forms.PaypalPaymentData(request.POST or data)
|
||||
if request.method == 'POST' and form.is_valid():
|
||||
if waffle.flag_is_active(request, 'solitude-payments'):
|
||||
# TODO(solitude): when the migration of data is completed, we
|
||||
# will be able to remove this.
|
||||
pk = client.create_seller_for_pay(addon)
|
||||
client.patch_seller_paypal(pk=pk, data=form.cleaned_data)
|
||||
|
||||
# TODO(solitude): remove this.
|
||||
adp.update(**form.cleaned_data)
|
||||
messages.success(request, msg)
|
||||
if source == 'paypal' and addon.is_incomplete() and addon.paypal_id:
|
||||
addon.mark_done()
|
||||
return redirect(addon.get_dev_url('paypal_setup'))
|
||||
|
||||
return jingo.render(request,
|
||||
'developers/payments/paypal-details-confirm.html',
|
||||
{'addon': addon, 'button': button, 'form': form,
|
||||
'title': title})
|
||||
return paypal_url
|
||||
|
||||
|
||||
@write
|
||||
|
@ -420,12 +328,13 @@ def acquire_refund_permission(request, addon_id, addon, webapp=False):
|
|||
# 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])
|
||||
on_error = reverse('submit.app.payments',
|
||||
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')
|
||||
on_error = addon.get_dev_url('payments')
|
||||
show_good_msgs = True
|
||||
|
||||
if 'request_token' not in request.GET:
|
||||
|
@ -455,18 +364,6 @@ def acquire_refund_permission(request, addon_id, addon, webapp=False):
|
|||
request.GET['verification_code'])
|
||||
data = paypal.get_personal_data(token)
|
||||
|
||||
# TODO(solitude): remove this.
|
||||
email = data.get('email')
|
||||
# If the email from paypal is different, something has gone wrong.
|
||||
if email != addon.paypal_id:
|
||||
paypal_log.debug('Addon paypal_id and personal data differ: '
|
||||
'%s vs %s' % (email, addon.paypal_id))
|
||||
messages.warning(request, _('The email returned by Paypal, '
|
||||
'did not match the PayPal email you '
|
||||
'entered. Please login using %s.')
|
||||
% email)
|
||||
return redirect(on_error)
|
||||
|
||||
# TODO(solitude): remove this. Sadly because the permissions tokens
|
||||
# are never being traversed back we have a disconnect between what is
|
||||
# happening in solitude and here and this will not easily survive flipping
|
||||
|
@ -478,6 +375,22 @@ def acquire_refund_permission(request, addon_id, addon, webapp=False):
|
|||
.safer_get_or_create(addon=addon))
|
||||
addonpremium.update(paypal_permissions_token=token)
|
||||
|
||||
# TODO(solitude): remove this.
|
||||
# Do this after setting permissions token since the previous permissions
|
||||
# token becomes invalid after making a call to get_permissions_token.
|
||||
email = data.get('email')
|
||||
paypal_id = request.session['unconfirmed_paypal_id']
|
||||
# If the email from paypal is different, something has gone wrong.
|
||||
if email != paypal_id:
|
||||
paypal_log.debug('Addon paypal_id and personal data differ: '
|
||||
'%s vs %s' %
|
||||
(email, paypal_id))
|
||||
messages.warning(request, _('The email returned by PayPal '
|
||||
'did not match the PayPal email you '
|
||||
'entered. Please log in using %s.')
|
||||
% paypal_id)
|
||||
return redirect(on_error)
|
||||
|
||||
# Finally update the data returned from PayPal for this addon.
|
||||
paypal_log.debug('Updating personal data for: %s' % addon_id)
|
||||
# TODO(solitude): delete this, as the data was pulled through solitude
|
||||
|
@ -499,6 +412,95 @@ def acquire_refund_permission(request, addon_id, addon, webapp=False):
|
|||
# End of new paypal stuff.
|
||||
|
||||
|
||||
@dev_required(owner_for_post=True, webapp=True)
|
||||
def paypal_setup_confirm(request, addon_id, addon, webapp, source='paypal'):
|
||||
# If you bounce through paypal as you do permissions changes set the
|
||||
# source to paypal.
|
||||
if source == 'paypal':
|
||||
msg = _('PayPal setup complete.')
|
||||
title = _('Confirm Details')
|
||||
button = _('Continue')
|
||||
# If you just hit this page from the Manage Paypal, show some less
|
||||
# wizardy stuff.
|
||||
else:
|
||||
msg = _('Changes saved.')
|
||||
title = _('Contact Details')
|
||||
button = _('Save')
|
||||
|
||||
data = {}
|
||||
if waffle.flag_is_active(request, 'solitude-payments'):
|
||||
data = client.get_seller_paypal_if_exists(addon) or {}
|
||||
|
||||
# TODO(solitude): remove this bit.
|
||||
# If it's not in solitude, use the local version
|
||||
adp, created = (AddonPaymentData.objects
|
||||
.safer_get_or_create(addon=addon))
|
||||
if not data:
|
||||
data = model_to_dict(adp)
|
||||
|
||||
form = forms.PaypalPaymentData(request.POST or data)
|
||||
if request.method == 'POST' and form.is_valid():
|
||||
|
||||
# TODO(solitude): we will remove this.
|
||||
# Everything is finally set up so now save the paypal_id and token
|
||||
# to the addon.
|
||||
addon.update(paypal_id=request.session['unconfirmed_paypal_id'])
|
||||
if waffle.flag_is_active(request, 'solitude-payments'):
|
||||
obj = client.create_seller_paypal(addon)
|
||||
client.patch_seller_paypal(pk=obj['resource_pk'],
|
||||
data={'paypal_id': addon.paypal_id})
|
||||
|
||||
if waffle.flag_is_active(request, 'solitude-payments'):
|
||||
# TODO(solitude): when the migration of data is completed, we
|
||||
# will be able to remove this.
|
||||
pk = client.create_seller_for_pay(addon)
|
||||
client.patch_seller_paypal(pk=pk, data=form.cleaned_data)
|
||||
|
||||
# TODO(solitude): remove this.
|
||||
adp.update(**form.cleaned_data)
|
||||
|
||||
messages.success(request, msg)
|
||||
if source == 'paypal' and addon.is_incomplete() and addon.paypal_id:
|
||||
addon.mark_done()
|
||||
return redirect(addon.get_dev_url('payments'))
|
||||
|
||||
return jingo.render(request,
|
||||
'developers/payments/paypal-details-confirm.html',
|
||||
{'addon': addon, 'button': button, 'form': form,
|
||||
'title': title})
|
||||
|
||||
|
||||
@json_view
|
||||
@dev_required(owner_for_post=True, webapp=True)
|
||||
def paypal_setup_check(request, addon_id, addon, webapp):
|
||||
if waffle.flag_is_active(request, 'solitude-payments'):
|
||||
data = client.post_account_check(data={'seller': addon})
|
||||
return {'valid': data['passed'], 'message': data['errors']}
|
||||
else:
|
||||
if not addon.paypal_id:
|
||||
return {'valid': False, 'message': [_('No PayPal email.')]}
|
||||
|
||||
check = Check(addon=addon)
|
||||
check.all()
|
||||
return {'valid': check.passed, 'message': check.errors}
|
||||
|
||||
|
||||
@json_view
|
||||
@dev_required(owner_for_post=True, webapp=True)
|
||||
def paypal_remove(request, addon_id, addon, webapp):
|
||||
"""
|
||||
Unregisters PayPal account from app.
|
||||
"""
|
||||
try:
|
||||
addon.update(paypal_id='')
|
||||
addonpremium, created = (AddonPremium.objects
|
||||
.safer_get_or_create(addon=addon))
|
||||
addonpremium.update(paypal_permissions_token='')
|
||||
except Exception as e:
|
||||
return {'success': False, 'error': [e]}
|
||||
return {'success': True, 'error': []}
|
||||
|
||||
|
||||
@waffle_switch('in-app-payments')
|
||||
@dev_required(owner_for_post=True, webapp=True)
|
||||
@transaction.commit_on_success
|
||||
|
@ -583,8 +585,10 @@ def _premium(request, addon_id, addon, webapp=False):
|
|||
premium_form.save()
|
||||
messages.success(request, _('Changes successfully saved.'))
|
||||
return redirect(addon.get_dev_url('payments'))
|
||||
|
||||
return jingo.render(request, 'developers/payments/premium.html',
|
||||
dict(addon=addon, webapp=webapp, premium=addon.premium,
|
||||
paypal_create_url=settings.PAYPAL_CGI_URL,
|
||||
form=premium_form))
|
||||
|
||||
|
||||
|
|
|
@ -1,15 +1,8 @@
|
|||
from django import forms
|
||||
|
||||
import amo
|
||||
from tower import ugettext as _
|
||||
|
||||
|
||||
APP_UPSELL_CHOICES = (
|
||||
(0, _("I don't have a free app to associate.")),
|
||||
(1, _('This is a premium upgrade.')),
|
||||
)
|
||||
|
||||
|
||||
APP_PUBLIC_CHOICES = (
|
||||
(0, _('As soon as it is approved.')),
|
||||
(1, _('Not until I manually make it public.')),
|
||||
|
|
|
@ -17,10 +17,8 @@ from market.models import AddonPremium, Price
|
|||
from translations.widgets import TransInput, TransTextarea
|
||||
from translations.fields import TransField
|
||||
|
||||
from mkt.developers.forms import (PaypalSetupForm as OriginalPaypalSetupForm,
|
||||
verify_app_domain)
|
||||
from mkt.site.forms import (AddonChoiceField, APP_UPSELL_CHOICES,
|
||||
APP_PUBLIC_CHOICES)
|
||||
from mkt.developers.forms import verify_app_domain
|
||||
from mkt.site.forms import AddonChoiceField, APP_PUBLIC_CHOICES
|
||||
|
||||
|
||||
class DevAgreementForm(happyforms.Form):
|
||||
|
@ -63,18 +61,29 @@ class NewWebappForm(happyforms.Form):
|
|||
_(u'Version %s already exists') % ver)
|
||||
else:
|
||||
# Throw an error if this is a dupe.
|
||||
verify_app_domain(upload.name) # JS sets manifest as `upload.name`.
|
||||
# (JS sets manifest as `upload.name`.)
|
||||
verify_app_domain(upload.name)
|
||||
return upload
|
||||
|
||||
|
||||
class PaypalSetupForm(OriginalPaypalSetupForm):
|
||||
class PaypalSetupForm(happyforms.Form):
|
||||
business_account = forms.ChoiceField(widget=forms.RadioSelect, choices=[],
|
||||
label=_(u'Do you already have a PayPal Premier or Business account?'))
|
||||
email = forms.EmailField(required=False, label=_(u'PayPal email address'))
|
||||
|
||||
def __init__(self, *args, **kw):
|
||||
super(PaypalSetupForm, self).__init__(*args, **kw)
|
||||
self.fields['business_account'].choices = (
|
||||
('yes', _lazy(u'Yes')),
|
||||
('no', _lazy(u'No')),
|
||||
('later', _lazy(u"I'll link my PayPal account later.")))
|
||||
self.fields['business_account'].choices = (('yes', _lazy('Yes')),
|
||||
('no', _lazy('No')),
|
||||
('later', _lazy(u"I'll link my PayPal account later.")))
|
||||
|
||||
def clean(self):
|
||||
data = self.cleaned_data
|
||||
if data.get('business_account') == 'yes' and not data.get('email'):
|
||||
msg = _(u'The PayPal email is required.')
|
||||
self._errors['email'] = self.error_class([msg])
|
||||
|
||||
return data
|
||||
|
||||
|
||||
class PremiumTypeForm(happyforms.Form):
|
||||
|
@ -95,11 +104,6 @@ class UpsellForm(happyforms.Form):
|
|||
'made available for sale?'),
|
||||
coerce=int,
|
||||
required=False)
|
||||
do_upsell = forms.TypedChoiceField(coerce=lambda x: bool(int(x)),
|
||||
choices=APP_UPSELL_CHOICES,
|
||||
widget=forms.RadioSelect(),
|
||||
label=_lazy(u'Upsell this app'),
|
||||
required=False)
|
||||
free = AddonChoiceField(queryset=Addon.objects.none(),
|
||||
required=False,
|
||||
empty_label='',
|
||||
|
|
|
@ -1067,6 +1067,10 @@ class TestPayments(TestSubmit):
|
|||
get_permissions_token):
|
||||
self.webapp.update(premium_type=amo.ADDON_PREMIUM,
|
||||
paypal_id='a@a.com')
|
||||
session = self.client.session
|
||||
session['unconfirmed_paypal_id'] = self.webapp.paypal_id
|
||||
session.save()
|
||||
|
||||
get_permissions_token.return_value = 'foo'
|
||||
get_personal_data.return_value = {'email': 'a@a.com'}
|
||||
res = self.client.get(self.get_acquire_url())
|
||||
|
@ -1100,10 +1104,14 @@ class TestPayments(TestSubmit):
|
|||
get_permissions_token):
|
||||
self.webapp.update(premium_type=amo.ADDON_PREMIUM,
|
||||
paypal_id='b@b.com')
|
||||
session = self.client.session
|
||||
session['unconfirmed_paypal_id'] = self.webapp.paypal_id
|
||||
session.save()
|
||||
|
||||
get_permissions_token.return_value = 'foo'
|
||||
get_personal_data.return_value = {'email': 'a@a.com'}
|
||||
res = self.client.get(self.get_acquire_url())
|
||||
self.assert3xx(res, self.get_url('payments.paypal'))
|
||||
self.assert3xx(res, self.get_url('payments'))
|
||||
|
||||
@mock.patch('paypal.get_permissions_token')
|
||||
def test_bounce_result_fails_paypal_error(self, get_permissions_token):
|
||||
|
|
Загрузка…
Ссылка в новой задаче