link up purchase button to paypal (bug 684408)

This commit is contained in:
Andy McKay 2011-09-09 10:21:30 -07:00
Родитель aabbd06a4e
Коммит b0af79630f
9 изменённых файлов: 113 добавлений и 36 удалений

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

@ -219,9 +219,10 @@ class InstallButton(object):
url = urlparams(roadblock, eula='', version=self.version.version)
if self.can_be_purchased:
# TODO(gkobes): Use the actual price!
# TODO(andym): make this faster and less naff
# L10n: {0} is a price
text = _('Purchase for {0}').format('$123.45')
text = _('Purchase for {0}').format(self.addon
.premium.price.price)
return text, url, os

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

@ -9,8 +9,10 @@
data-developers="{{ addon.meet_the_dev_url() }}"
data-name="{{ addon.name }}"
{{ b.attrs()|xmlattr }}
{% if addon.can_be_purchased() %}
data-cost="$123.45" {# TODO(gkoberger): Change This! #}
{% if waffle.switch('marketplace') and addon.can_be_purchased() %}
data-purchase="{{ url('addons.purchase', addon.slug) }}?"
{# TODO(andym): make this faster and less naff #}
data-cost="{{ addon.premium.price.price }}"
data-login-url="{{ url('users.login_modal') }}"
{% endif %}
{% if compat %}

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

@ -28,6 +28,7 @@ from addons import cron
from addons.models import (Addon, AddonDependency, AddonUpsell, AddonUser,
Charity, Category)
from files.models import File
from market.models import AddonPremium
from paypal.tests import other_error
from stats.models import Contribution
from translations.helpers import truncate
@ -214,6 +215,48 @@ class TestContributeEmbedded(amo.tests.TestCase):
assert not json.loads(res.content)['paykey']
class TestPurchaseEmbedded(amo.tests.TestCase):
fixtures = ['base/apps', 'base/addon_592', 'prices']
def setUp(self):
waffle.models.Switch.objects.create(name='marketplace', active=True)
self.addon = Addon.objects.get(pk=592)
self.addon.update(premium_type=amo.ADDON_PREMIUM,
status=amo.STATUS_PUBLIC)
AddonPremium.objects.create(addon=self.addon, price_id=1)
self.purchase_url = reverse('addons.purchase', args=[self.addon.slug])
def test_premium_only(self):
self.addon.update(premium_type=amo.ADDON_FREE)
eq_(self.client.get(self.purchase_url).status_code, 404)
@patch('paypal.get_paykey')
def test_redirect(self, get_paykey):
get_paykey.return_value = 'some-pay-key'
res = self.client.get(self.purchase_url)
assert 'some-pay-key' in res['Location']
@patch('paypal.get_paykey')
def test_ajax(self, get_paykey):
get_paykey.return_value = 'some-pay-key'
res = self.client.get_ajax(self.purchase_url)
assert json.loads(res.content)['paykey'] == 'some-pay-key'
@patch('paypal.get_paykey')
def test_paykey_amount(self, get_paykey):
# Test the amount the paykey for is the price.
get_paykey.return_value = 'some-pay-key'
self.client.get_ajax(self.purchase_url)
# wtf? Can we get any more [0]'s there?
eq_(get_paykey.call_args_list[0][0][0]['amount'], Decimal('0.99'))
@patch('paypal.get_paykey')
def test_paykey_error(self, get_paykey):
get_paykey.side_effect = Exception('woah')
res = self.client.get_ajax(self.purchase_url)
assert json.loads(res.content)['error'].startswith('There was an')
class TestDeveloperPages(amo.tests.TestCase):
fixtures = ['base/apps', 'base/addon_3615', 'base/addon_592',
'base/users', 'addons/eula+contrib-addon',

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

@ -30,6 +30,7 @@ detail_patterns = patterns('',
url('^contribute/(?P<status>cancel|complete)$', views.paypal_result,
name='addons.paypal'),
url('^purchase/$', views.purchase, name='addons.purchase'),
url('^about$', lambda r, addon_id: redirect('addons.installed',
addon_id, permanent=True),

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

@ -10,7 +10,6 @@ from django.conf import settings
from django.db.models import Q
from django.shortcuts import get_list_or_404, get_object_or_404, redirect
from django.utils.translation import trans_real as translation
from django.utils import http as urllib
from django.views.decorators.cache import cache_page, cache_control
from django.views.decorators.vary import vary_on_headers
@ -28,7 +27,6 @@ from amo import messages
from amo.decorators import login_required
from amo.forms import AbuseForm
from amo.utils import sorted_groupby, randslice
from amo.helpers import absolutify
from amo.models import manual_order
from amo import urlresolvers
from amo.urlresolvers import reverse
@ -39,7 +37,7 @@ import paypal
from reviews.forms import ReviewForm
from reviews.models import Review, GroupedRating
from sharing.views import share as share_redirect
from stats.models import GlobalStat, Contribution
from stats.models import Contribution
from translations.query import order_by_translation
from translations.helpers import truncate
from versions.models import Version
@ -462,6 +460,41 @@ def developers(request, addon, page):
'version': version})
# TODO(andym): remove this once we figure out how to process for
# anonymous users. For now we are concentrating on logged in users.
@login_required
@addon_view
def purchase(request, addon):
if not waffle.switch_is_active('marketplace'):
raise http.Http404
if (not addon.is_premium() or not addon.premium):
raise http.Http404
amount = addon.premium.price.price
uuid_ = hashlib.md5(str(uuid.uuid4())).hexdigest()
contrib_for = _(u'Purchase of {0}').format(jinja2.escape(addon.name))
paykey, error = '', ''
try:
paykey = paypal.get_paykey(dict(uuid=uuid_, slug=addon.slug,
amount=amount, memo=contrib_for,
email=addon.paypal_id,
ip=request.META.get('REMOTE_ADDR')))
except Exception:
log.error('Error getting paykey, purchase of addon: %s' % addon.pk,
exc_info=True)
error = _('There was an error communicating with PayPal.')
url = '%s?paykey=%s' % (settings.PAYPAL_FLOW_URL, paykey)
if request.GET.get('result_type') == 'json' or request.is_ajax():
return http.HttpResponse(json.dumps({'url': url,
'paykey': paykey,
'error': error}),
content_type='application/json')
return http.HttpResponseRedirect(url)
@addon_view
def contribute(request, addon):
contrib_type = request.GET.get('type', 'suggested')
@ -476,7 +509,6 @@ def contribute(request, addon):
amount = settings.DEFAULT_SUGGESTED_CONTRIBUTION
contribution_uuid = hashlib.md5(str(uuid.uuid4())).hexdigest()
uuid_qs = urllib.urlencode({'uuid': contribution_uuid})
if addon.charity:
name, paypal_id = addon.charity.name, addon.charity.paypal
@ -484,26 +516,16 @@ def contribute(request, addon):
name, paypal_id = addon.name, addon.paypal_id
contrib_for = _(u'Contribution for {0}').format(jinja2.escape(name))
paykey, nice_error = None, None
paykey, error = '', ''
try:
paykey = paypal.get_paykey({
'return_url': absolutify('%s?%s' % (reverse('addons.paypal',
args=[addon.slug, 'complete']),
uuid_qs)),
'cancel_url': absolutify('%s?%s' % (reverse('addons.paypal',
args=[addon.slug, 'cancel']),
uuid_qs)),
'uuid': contribution_uuid,
'amount': str(amount),
'email': paypal_id,
'ip': request.META.get('REMOTE_ADDR'),
'memo': contrib_for})
except paypal.AuthError, error:
paypal_log.error('Authentication error: %s' % error)
nice_error = _('There was a problem communicating with Paypal.')
except Exception, error:
paypal_log.error('Error: %s' % error)
nice_error = _('There was a problem with that contribution.')
paykey = paypal.get_paykey(dict(uuid=contribution_uuid,
slug=addon.slug, amount=amount,
email=paypal_id, memo=contrib_for,
ip=request.META.get('REMOTE_ADDR')))
except Exception:
log.error('Error getting paykey, contribution for addon: %s'
% addon.pk, exc_info=True)
error = _('There was an error communicating with PayPal.')
if paykey:
contrib = Contribution(addon_id=addon.id,
@ -527,7 +549,7 @@ def contribute(request, addon):
# not have a paykey and the JS can cope appropriately.
return http.HttpResponse(json.dumps({'url': url,
'paykey': paykey,
'error': nice_error}),
'error': error}),
content_type='application/json')
return http.HttpResponseRedirect(url)

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

@ -29,20 +29,23 @@ paypal_log = commonware.log.getLogger('z.paypal')
def get_paykey(data):
"""
Gets a paykey from Paypal. Need to pass in the following in data:
return_url and cancel_url: where user goes back to (required)
slug: addon, will form urls for where user goes back to (required)
email: who the money is going to (required)
amount: the amount of money (required)
ip: ip address of end user (required)
uuid: contribution_uuid (required)
memo: any nice message
"""
complete = reverse('addons.paypal', args=[data['slug'], 'complete'])
cancel = reverse('addons.paypal', args=[data['slug'], 'cancel'])
uuid_qs = urllib.urlencode({'uuid': data['uuid']})
paypal_data = {
'actionType': 'PAY',
'requestEnvelope.errorLanguage': 'US',
'currencyCode': 'USD',
'cancelUrl': data['cancel_url'],
'returnUrl': data['return_url'],
'cancelUrl': absolutify('%s?%s' % (cancel, uuid_qs)),
'returnUrl': absolutify('%s?%s' % (complete, uuid_qs)),
'receiverList.receiver(0).email': data['email'],
'receiverList.receiver(0).amount': data['amount'],
'receiverList.receiver(0).invoiceID': 'mozilla-%s' % data['uuid'],
@ -55,7 +58,12 @@ def get_paykey(data):
paypal_data['memo'] = data['memo']
with statsd.timer('paypal.paykey.retrieval'):
response = _call(settings.PAYPAL_PAY_URL, paypal_data, ip=data['ip'])
try:
response = _call(settings.PAYPAL_PAY_URL, paypal_data,
ip=data['ip'])
except AuthError, error:
paypal_log.error('Authentication error: %s' % error)
raise
return response['payKey']

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

@ -29,8 +29,7 @@ other_error = ('error(0).errorId=520001'
class TestPayPal(amo.tests.TestCase):
def setUp(self):
self.data = {'return_url': absolutify(reverse('home')),
'cancel_url': absolutify(reverse('home')),
self.data = {'slug': 'xx',
'amount': 10,
'email': 'someone@somewhere.com',
'uuid': time.time(),

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

@ -377,7 +377,8 @@ jQuery.fn.addPaypal = function(html, allowClick) {
}));
}
paypal.append($("<button>", {'class': 'button prominent paypal',
'html': gettext('Pay <small>with</small> Pay<em>Pal</em>')}));
'href': $install.attr('data-purchase'),
'html': gettext('Pay <small>with</small> Pay<em>Pal</em>')}));
paypal.append($('<p>', {'text': gettext('Complete your purchase with PayPal. No account necessary.')}));
return true;
}

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

@ -2,7 +2,7 @@ $(document).ready(function() {
$("#contribute-why").popup("#contribute-more-info", {
pointTo: "#contribute-more-info"
});
$('div.contribute a.suggested-amount').live('click', function(event) {
$('div.contribute a.suggested-amount,button.paypal').live('click', function(event) {
var el = this;
$(el).addClass('ajax-loading');
$.ajax({