2015-02-08 15:14:58 +01:00
|
|
|
# lingo - basket and payment system
|
|
|
|
# Copyright (C) 2015 Entr'ouvert
|
|
|
|
#
|
|
|
|
# This program is free software: you can redistribute it and/or modify it
|
|
|
|
# under the terms of the GNU Affero General Public License as published
|
|
|
|
# by the Free Software Foundation, either version 3 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU Affero General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2015-02-08 11:32:29 +01:00
|
|
|
|
2015-02-08 16:04:03 +01:00
|
|
|
from decimal import Decimal
|
2015-02-08 15:14:58 +01:00
|
|
|
import json
|
|
|
|
|
2015-02-08 16:04:03 +01:00
|
|
|
from django.contrib.auth.models import User
|
2015-03-06 10:47:34 +01:00
|
|
|
from django.core.urlresolvers import reverse
|
2015-03-05 17:02:52 +01:00
|
|
|
from django.http import HttpResponse, HttpResponseRedirect
|
2015-03-06 10:47:34 +01:00
|
|
|
from django.template.response import TemplateResponse
|
2015-03-07 14:01:24 +01:00
|
|
|
from django.utils import timezone
|
2015-02-08 16:04:03 +01:00
|
|
|
from django.views.decorators.csrf import csrf_exempt
|
|
|
|
from django.views.generic import View, ListView
|
2015-02-08 15:14:58 +01:00
|
|
|
|
2015-03-05 17:02:52 +01:00
|
|
|
import eopayment
|
|
|
|
|
2015-05-12 17:34:48 +02:00
|
|
|
try:
|
|
|
|
from mellon.models import UserSAMLIdentifier
|
|
|
|
except ImportError:
|
|
|
|
UserSAMLIdentifier = None
|
|
|
|
|
2015-03-05 17:02:52 +01:00
|
|
|
from .models import Regie, BasketItem, Transaction
|
2015-02-08 15:14:58 +01:00
|
|
|
|
|
|
|
class RegiesApiView(ListView):
|
|
|
|
model = Regie
|
|
|
|
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
|
|
response = HttpResponse(content_type='application/json')
|
|
|
|
data = {'data': [x.as_api_dict() for x in self.get_queryset()]}
|
|
|
|
json_str = json.dumps(data)
|
|
|
|
if 'jsonpCallback' in request.GET:
|
|
|
|
json_str = '%s(%s);' % (request.GET['jsonpCallback'], json_str)
|
|
|
|
response.write(json_str)
|
|
|
|
return response
|
2015-02-08 16:04:03 +01:00
|
|
|
|
|
|
|
|
|
|
|
class AddBasketItemApiView(View):
|
|
|
|
http_method_names = ['post', 'options']
|
|
|
|
|
|
|
|
@csrf_exempt
|
|
|
|
def dispatch(self, *args, **kwargs):
|
|
|
|
return super(AddBasketItemApiView, self).dispatch(*args, **kwargs)
|
|
|
|
|
|
|
|
def post(self, request, *args, **kwargs):
|
2015-03-05 16:02:32 +01:00
|
|
|
# XXX: check request signature
|
|
|
|
|
|
|
|
request_body = json.loads(self.request.body)
|
|
|
|
|
2015-02-08 16:04:03 +01:00
|
|
|
item = BasketItem()
|
2015-06-05 17:05:30 +02:00
|
|
|
item.amount = sum([Decimal(x) for x in request.GET.getlist('amount')])
|
2015-02-08 16:04:03 +01:00
|
|
|
|
2015-03-05 16:02:32 +01:00
|
|
|
try:
|
|
|
|
if request.GET.get('NameId'):
|
2015-05-12 17:34:48 +02:00
|
|
|
if UserSAMLIdentifier is None:
|
|
|
|
raise Exception('missing mellon?')
|
|
|
|
try:
|
|
|
|
user = UserSAMLIdentifier.objects.get(name_id=request.GET.get('NameId')).user
|
|
|
|
except UserSAMLIdentifier.DoesNotExist:
|
|
|
|
raise Exception('unknown name id')
|
2015-03-05 16:02:32 +01:00
|
|
|
elif request.GET.get('email'):
|
|
|
|
user = User.objects.get(email=request.GET.get('email'))
|
|
|
|
else:
|
|
|
|
raise Exception('no user specified')
|
|
|
|
except User.DoesNotExist:
|
|
|
|
raise Exception('unknown user')
|
|
|
|
|
|
|
|
item.user = user
|
|
|
|
if request.GET.get('regie_id'):
|
|
|
|
item.regie = Regie.objects.get(id=request.GET.get('regie_id'))
|
|
|
|
else:
|
|
|
|
# if there's no regie specified, use the first one we get from the
|
|
|
|
# database...
|
|
|
|
item.regie = Regie.objects.all()[0]
|
|
|
|
|
|
|
|
item.subject = request_body.get('display_name')
|
|
|
|
item.source_url = request_body.get('url')
|
2015-02-08 16:04:03 +01:00
|
|
|
|
|
|
|
item.save()
|
2015-03-05 16:02:32 +01:00
|
|
|
|
2015-02-08 16:04:03 +01:00
|
|
|
response = HttpResponse(content_type='application/json')
|
|
|
|
response.write(json.dumps({'result': 'success'}))
|
|
|
|
return response
|
2015-03-05 17:02:52 +01:00
|
|
|
|
|
|
|
class PayView(View):
|
|
|
|
def post(self, request, *args, **kwargs):
|
|
|
|
items = BasketItem.objects.filter(id__in=request.POST.getlist('item'))
|
|
|
|
# XXX: check all items are going to the same regie
|
|
|
|
|
|
|
|
transaction = Transaction()
|
2015-03-07 14:02:13 +01:00
|
|
|
transaction.user = request.user
|
2015-03-05 17:02:52 +01:00
|
|
|
transaction.save()
|
|
|
|
transaction.items = items
|
2015-04-21 16:40:46 +02:00
|
|
|
transaction.status = 0
|
2015-03-05 17:02:52 +01:00
|
|
|
transaction.save()
|
|
|
|
|
|
|
|
total_amount = sum([x.amount for x in items])
|
|
|
|
|
|
|
|
regie = items[0].regie
|
|
|
|
|
|
|
|
payment = eopayment.Payment(regie.service, regie.service_options)
|
2015-03-06 10:47:34 +01:00
|
|
|
return_url = request.build_absolute_uri(
|
|
|
|
reverse('lingo-callback', kwargs={'regie_pk': regie.id}))
|
2015-03-05 17:02:52 +01:00
|
|
|
(order_id, kind, data) = payment.request(total_amount,
|
2015-08-11 11:42:28 +02:00
|
|
|
email=request.user.email,
|
2015-03-06 10:47:34 +01:00
|
|
|
next_url=request.build_absolute_uri('/'),
|
|
|
|
accepturl=return_url,
|
|
|
|
declineurl=return_url,
|
|
|
|
exceptionurl=return_url,
|
|
|
|
cancelurl=return_url)
|
2015-03-05 17:02:52 +01:00
|
|
|
transaction.order_id = order_id
|
|
|
|
transaction.save()
|
|
|
|
|
|
|
|
# XXX: mark basket items as being processed (?)
|
|
|
|
|
|
|
|
if kind == eopayment.URL:
|
|
|
|
return HttpResponseRedirect(data)
|
2015-03-06 10:47:34 +01:00
|
|
|
elif kind == eopayment.FORM:
|
|
|
|
return TemplateResponse(request, 'lingo/payment_form.html', {'form': data})
|
2015-03-05 17:02:52 +01:00
|
|
|
|
|
|
|
raise NotImplementedError()
|
2015-03-06 10:47:34 +01:00
|
|
|
|
|
|
|
|
|
|
|
class CallbackView(View):
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
|
|
regie = Regie.objects.get(id=kwargs.get('regie_pk'))
|
|
|
|
payment = eopayment.Payment(regie.service, regie.service_options)
|
|
|
|
payment_response = payment.response(request.environ['QUERY_STRING'])
|
2015-04-21 16:40:46 +02:00
|
|
|
if not payment_response.result == eopayment.CANCELLED:
|
|
|
|
# cancellation are not signed...
|
|
|
|
assert payment_response.signed is True
|
|
|
|
|
2015-03-06 10:47:34 +01:00
|
|
|
transaction = Transaction.objects.get(order_id=payment_response.order_id)
|
2015-04-21 16:40:46 +02:00
|
|
|
transaction.status = payment_response.result
|
2015-03-06 10:47:34 +01:00
|
|
|
transaction.bank_data = payment_response.bank_data
|
2015-03-07 14:01:24 +01:00
|
|
|
transaction.end_date = timezone.now()
|
2015-03-06 10:47:34 +01:00
|
|
|
transaction.save()
|
2015-04-21 16:40:46 +02:00
|
|
|
|
|
|
|
if payment_response.result != eopayment.PAID:
|
|
|
|
return HttpResponseRedirect('/')
|
|
|
|
|
2015-03-06 10:47:34 +01:00
|
|
|
for item in transaction.items.all():
|
|
|
|
item.payment_date = transaction.end_date
|
|
|
|
item.save()
|
2015-03-07 14:00:17 +01:00
|
|
|
try:
|
|
|
|
item.notify()
|
|
|
|
except:
|
|
|
|
# ignore errors, it will be retried later on if it fails
|
|
|
|
pass
|
2015-03-06 10:47:34 +01:00
|
|
|
return HttpResponseRedirect('/')
|
2015-09-03 16:03:44 +02:00
|
|
|
|
|
|
|
|
|
|
|
class ItemDownloadView(View):
|
|
|
|
http_method_names = [u'get']
|
|
|
|
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
|
|
regie = Regie.objects.get(pk=kwargs['regie_id'])
|
|
|
|
data = regie.download_item(request, kwargs['item_id'])
|
|
|
|
r = HttpResponse(data, content_type='application/pdf')
|
|
|
|
r['Content-Disposition'] = 'attachment; filename="%(item_id)s.pdf"' % kwargs
|
|
|
|
return r
|