279 lines
11 KiB
Python
279 lines
11 KiB
Python
from django.views.decorators.csrf import csrf_exempt
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.views.generic import DetailView, ListView
|
|
from django.http import HttpResponse, HttpResponseForbidden
|
|
from django.template import RequestContext
|
|
from django.core.urlresolvers import reverse
|
|
from django.shortcuts import redirect
|
|
from django.core.exceptions import ObjectDoesNotExist
|
|
|
|
from . import app_settings
|
|
|
|
import datetime
|
|
import eopayment
|
|
|
|
from .models import Regie, TransactionEvent
|
|
from cmsplugin_blurp.renderers.data_source import Data
|
|
|
|
import logging
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class LoginRequiredMixin(object):
|
|
@classmethod
|
|
def as_view(cls, **initkwargs):
|
|
view = super(LoginRequiredMixin, cls).as_view(**initkwargs)
|
|
return login_required(view)
|
|
|
|
|
|
class InvoiceView(DetailView):
|
|
model = Regie
|
|
http_method_names = [u'get']
|
|
|
|
def get_template_names(self):
|
|
return ('facturier/%s.html' % self.object.slug,
|
|
'facturier/%s.html' % self.object.service)
|
|
|
|
def get_object_and_invoice(self, **kwargs):
|
|
self.object = self.get_object()
|
|
self.invoice = self.object.get_invoice(kwargs['id'],
|
|
kwargs['hash'], self.request)
|
|
|
|
def get_context_data(self):
|
|
context = super(InvoiceView, self).get_context_data()
|
|
context['invoice'] = self.invoice
|
|
return context
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
self.get_object_and_invoice(**kwargs)
|
|
context = self.get_context_data()
|
|
return self.render_to_response(context)
|
|
|
|
|
|
class InvoiceDownloadView(LoginRequiredMixin, InvoiceView):
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
self.get_object_and_invoice(**kwargs)
|
|
# if invoice can't be downloaded, stop here
|
|
if not self.invoice.get('download_url'):
|
|
return HttpResponseForbidden()
|
|
context = self.get_context_data()
|
|
filename = app_settings.download_filename.format(**context)
|
|
mimetype = app_settings.download_filename_mimetype
|
|
return HttpResponse(open(filename, 'rb'), mimetype=mimetype)
|
|
|
|
|
|
class TransactionView(DetailView):
|
|
model = Regie
|
|
template_name = 'facturier/transaction.html'
|
|
http_method_names = [u'get', u'post']
|
|
|
|
def create_event(self, transaction_id, invoice_id, status,
|
|
response=False, message=None, details={}):
|
|
address=self.request.META.get('HTTP_X_REAL_IP') or \
|
|
self.request.META.get('HTTP_X_FORWARDED_FOR') or \
|
|
self.request.META.get('REMOTE_ADDR')
|
|
details['message'] = message
|
|
TransactionEvent.objects.create(invoice_id=invoice_id,
|
|
regie=self.object,
|
|
transaction_id=transaction_id,
|
|
address=address,
|
|
response=response,
|
|
status=status,
|
|
details=details)
|
|
|
|
def create_transaction(self, options, invoice, context, message=None):
|
|
p = eopayment.Payment(kind=self.object.service,
|
|
options=options)
|
|
transaction_id, kind, redirect_url = p.request(**invoice)
|
|
assert kind == eopayment.URL
|
|
self.create_event(transaction_id, context['invoice_id'], 'CREATED',
|
|
message=message)
|
|
return transaction_id, redirect_url
|
|
|
|
def handle_transaction(self, invoice, context):
|
|
"""
|
|
checks if a transaction exists, otherwise creates a new transaction.
|
|
"""
|
|
service_options = dict([(option.name, option.value) \
|
|
for option in self.object.serviceoption_set.all()])
|
|
try:
|
|
event = TransactionEvent.objects.filter(regie=self.object,
|
|
invoice_id=context['invoice_id']).latest()
|
|
except ObjectDoesNotExist:
|
|
# this is a new invoice : create a new transaction
|
|
transaction_id, redirect_url = self.create_transaction(service_options,
|
|
invoice, context, message='new')
|
|
context.update({'redirect_url': redirect_url})
|
|
else:
|
|
if event.status == 'PAID':
|
|
# a online payment took place = the user can't pay again
|
|
context['paid'] = True
|
|
context['paid_date'] = event.date
|
|
elif event.status == 'CREATED' and 'force' in self.request.GET:
|
|
# the user want to force a new transaction : cancel the current
|
|
# one and create a new one
|
|
self.create_event(event.transaction_id, context['invoice_id'],
|
|
status='CANCELED',
|
|
message='forced by user')
|
|
transaction_id, redirect_url = self.create_transaction(service_options,
|
|
invoice, context,
|
|
message='forced by user, replace %s' % event.transaction_id)
|
|
context.update({'redirect_url': redirect_url})
|
|
elif event.status == 'CREATED':
|
|
# there is a current transaction: propose to "force" a new transaction
|
|
context['force_new_transaction_allowed'] = True
|
|
context['last_transaction_date'] = event.date
|
|
else:
|
|
# the last transaction is closed (cancel, error, test, etc.):
|
|
# create a new one
|
|
transaction_id, redirect_url = self.create_transaction(service_options,
|
|
invoice, context,
|
|
message='recreated after %s of %s' % (event.status, event.transaction_id))
|
|
context.update({'redirect_url': redirect_url})
|
|
|
|
return context
|
|
|
|
def invoice_rest_request(self, data_source_url, context):
|
|
data = Data(data_source_url, RequestContext(self.request, context),
|
|
limit=None, refresh=None)
|
|
return data.update_content().get('data').get('invoice')
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(TransactionView, self).get_context_data(**kwargs)
|
|
invoice_id = self.kwargs['id']
|
|
invoice_hash = self.kwargs['hash']
|
|
slug = self.object.slug # (self.object is a "Regie")
|
|
|
|
# create an invoice :
|
|
# 1. create a base from request and regie options
|
|
invoice = {}
|
|
if hasattr(self.request.user, 'email'):
|
|
invoice['email'] = self.request.user.email
|
|
request_options = dict([(option.name, option.value) \
|
|
for option in self.object.requestoption_set.all()])
|
|
invoice.update(request_options)
|
|
|
|
# get invoice from the regie (webservice)
|
|
context.update({'invoice_id': invoice_id,
|
|
'invoice_hash': invoice_hash,
|
|
'slug': slug,
|
|
'paid': False,})
|
|
invoice_data = self.invoice_rest_request(self.object.get_url, context)
|
|
invoice.update(invoice_data)
|
|
|
|
# invoice can be overriden by request.GET parameters
|
|
if self.request.GET:
|
|
for k, v in self.request.GET.iteritems():
|
|
if k in invoice:
|
|
invoice[k] = v
|
|
|
|
# invoice is paid : stop now
|
|
if invoice.get('paid'):
|
|
context['paid'] = True
|
|
return context
|
|
|
|
next_url = reverse('transaction', args=(slug, invoice_id, invoice_hash))
|
|
if request_options.get('next_url_base'):
|
|
invoice['next_url'] = str('%s%s' % (request_options['next_url_base'] ,next_url))
|
|
else:
|
|
invoice['next_url'] = self.request.build_absolute_uri(next_url)
|
|
context.update({'details': invoice})
|
|
|
|
self.handle_transaction(invoice, context)
|
|
logger.debug('payment request: context %s' % context)
|
|
return context
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
self.object = self.get_object()
|
|
context = self.get_context_data(object=self.object)
|
|
if context.get('redirect_url'):
|
|
return redirect(context['redirect_url'], permanent=False)
|
|
return self.render_to_response(context)
|
|
|
|
# don't use CSRF protection (for post)
|
|
@csrf_exempt
|
|
def dispatch(self, *args, **kwargs):
|
|
return super(TransactionView, self).dispatch(*args, **kwargs)
|
|
|
|
def post(self, request, *args, **kwargs):
|
|
logger.debug('payment response: path %s' % request.path)
|
|
|
|
query_string = str(request.META['QUERY_STRING'])
|
|
if not query_string:
|
|
if request.META['CONTENT_TYPE'] == 'application/x-www-form-urlencoded':
|
|
query_string = str(request.body)
|
|
logger.debug('payment response: query_string %s', query_string)
|
|
|
|
self.object = self.get_object()
|
|
service_options = dict([(option.name, option.value) \
|
|
for option in self.object.serviceoption_set.all()])
|
|
|
|
invoice_id = self.kwargs['id']
|
|
invoice_hash = self.kwargs['hash']
|
|
logger.debug('payment response: invoice %s', invoice_id)
|
|
|
|
try:
|
|
event = TransactionEvent.objects.filter(regie=self.object,
|
|
invoice_id=invoice_id).latest()
|
|
logger.debug('payment response: found transaction %s', event.transaction_id)
|
|
except ObjectDoesNotExist:
|
|
logger.warn('payment response: no transaction for invoice %s', invoice_id)
|
|
return HttpResponse(status=404) # HTTP Not Found
|
|
if event.status != 'CREATED':
|
|
logger.warn('payment response: transaction status != CREATED')
|
|
return HttpResponse(status=410) # HTTP Gone
|
|
|
|
backend = eopayment.Payment(kind=self.object.service, options=service_options)
|
|
response = backend.response(query_string)
|
|
logger.debug('payment response: response %s', response)
|
|
|
|
if response.result == eopayment.common.PAID:
|
|
status = 'PAID'
|
|
else:
|
|
status = 'ERROR'
|
|
if response.test:
|
|
status = 'TEST_%s' % status
|
|
context = {
|
|
'invoice_id': invoice_id,
|
|
'invoice_hash': invoice_hash,
|
|
'status': status
|
|
}
|
|
|
|
logger.debug('payment response: close transaction %s' % event.transaction_id)
|
|
self.create_event(event.transaction_id, invoice_id, status,
|
|
response=True, message='response', details=response.__dict__)
|
|
logger.debug('payment response: update invoice PAID %s -- context %s' %
|
|
(self.object.update_url, context))
|
|
self.invoice_rest_request(self.object.update_url, context)
|
|
|
|
# ok !
|
|
logger.debug('payment response: OK invoice %s', invoice_id)
|
|
return HttpResponse(status=200)
|
|
|
|
|
|
class TransactionResponseListView(ListView):
|
|
content_type = 'text/csv'
|
|
|
|
def get_regie(self):
|
|
self.regie = Regie.objects.filter(slug=self.kwargs['regie']).get()
|
|
|
|
def get_template_names(self):
|
|
return ('facturier/response-%s.csv' % self.regie.slug,
|
|
'facturier/response-%s.csv' % self.regie.service)
|
|
|
|
def get_queryset(self):
|
|
days = int(self.kwargs.get('days', 5))
|
|
day_start = datetime.datetime.now() - datetime.timedelta(days)
|
|
return TransactionEvent.objects.filter(regie=self.regie,
|
|
response=True,
|
|
date__gte=day_start)
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
if request.GET.get('key') != app_settings.responses_csv_key:
|
|
return HttpResponseForbidden('forbidden')
|
|
self.get_regie()
|
|
return super(TransactionResponseListView, self).\
|
|
get(request, *args, **kwargs)
|
|
|