218 lines
9.0 KiB
Python
218 lines
9.0 KiB
Python
# combo-plugin-nanterre - Combo Nanterre plugin
|
|
# -*- coding: utf-8 -*-
|
|
# Copyright (C) 2017 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/>.
|
|
|
|
import json
|
|
import logging
|
|
|
|
from django.conf import settings
|
|
from django.contrib import messages
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.core.exceptions import PermissionDenied
|
|
from django.http import HttpResponse, HttpResponseRedirect, Http404
|
|
from django.urls import reverse
|
|
from django.views.decorators.csrf import csrf_exempt
|
|
|
|
from combo.utils import requests, get_templated_url
|
|
|
|
|
|
ERROR_MESSAGE = "Le système de paiement n'est pas disponible, " "veuillez essayer ultérieurement."
|
|
MESSAGE_BY_STATE = {
|
|
'paye': (messages.SUCCESS, "Le paiement a bien été effectué."),
|
|
'abandon': (messages.WARNING, "Le paiement a été annulé."),
|
|
'refus': (messages.WARNING, "Le paiement a été refusé."),
|
|
}
|
|
|
|
|
|
def rsu_post(request, endpoint, payload):
|
|
timeout = getattr(settings, 'NANTERRE_PAYMENT_TIMEOUT', 20)
|
|
url = '[zoo_url]rsu/' + endpoint
|
|
context = {'request': request}
|
|
url = get_templated_url(url, context=context)
|
|
return requests.post(url, json=payload, timeout=timeout).json()
|
|
|
|
|
|
@csrf_exempt
|
|
@login_required
|
|
def saga_transaction(request):
|
|
logger = logging.getLogger('combo_plugin_nanterre.saga_transaction')
|
|
num_factures = request.POST.getlist('num_factures')
|
|
email = request.POST.get('email') or request.user.email
|
|
error_url = request.POST.get('error_url') or '/'
|
|
# nettoye la session à chaque transaction
|
|
if 'saga_retour_synchrone' in request.session:
|
|
del request.session['saga_retour_synchrone']
|
|
|
|
urlretour_asynchrone = request.build_absolute_uri(reverse('nanterre-saga-retour-asynchrone')).rstrip('/')
|
|
urlretour_synchrone = request.build_absolute_uri(reverse('nanterre-saga-retour-synchrone')).rstrip('/')
|
|
|
|
payload = {
|
|
'num_factures': num_factures,
|
|
'email': email,
|
|
'urlretour_asynchrone': urlretour_asynchrone,
|
|
'urlretour_synchrone': urlretour_synchrone,
|
|
}
|
|
try:
|
|
saga = rsu_post(request, 'saga/[user_nameid]/transaction/', payload)
|
|
except:
|
|
logger.exception('[rsu/saga] failed to create transaction ' 'for num_factures=%s', num_factures)
|
|
messages.error(request, ERROR_MESSAGE)
|
|
return HttpResponseRedirect(error_url)
|
|
if not isinstance(saga, dict):
|
|
logger.error(
|
|
'[rsu/saga] failed to create transaction ' 'for num_factures=%s, received bad response=%r',
|
|
num_factures,
|
|
saga,
|
|
)
|
|
messages.error(request, ERROR_MESSAGE)
|
|
return HttpResponseRedirect(error_url)
|
|
if saga.get('errors'):
|
|
logger.warning(
|
|
'[rsu/saga] failed to create transaction ' 'for num_factures=%s, errors=%r',
|
|
num_factures,
|
|
saga['errors'],
|
|
)
|
|
for error in saga['errors']:
|
|
messages.error(request, error)
|
|
return HttpResponseRedirect(error_url)
|
|
if saga.get('err') != 0:
|
|
logger.warning(
|
|
'[rsu/saga] failed to create transaction ' 'for num_factures=%s, unknown error, code=%r',
|
|
num_factures,
|
|
saga['err'],
|
|
)
|
|
messages.error(request, ERROR_MESSAGE)
|
|
return HttpResponseRedirect(error_url)
|
|
if not saga.get('data', {}).get('url'):
|
|
logger.error(
|
|
'[rsu/saga] failed to create transaction ' 'for num_factures=%s, response without url: %r',
|
|
num_factures,
|
|
saga,
|
|
)
|
|
messages.error(request, ERROR_MESSAGE)
|
|
return HttpResponseRedirect(error_url)
|
|
|
|
# finally, response seems good! redirect to payment system URL
|
|
logger.info(
|
|
'[rsu/saga] new transaction created ' 'for num_factures=%s, redirect to %s',
|
|
num_factures,
|
|
saga['data']['url'],
|
|
)
|
|
return HttpResponseRedirect(saga['data']['url'])
|
|
|
|
|
|
@csrf_exempt
|
|
@login_required
|
|
def saga_retour_synchrone(request):
|
|
logger = logging.getLogger('combo_plugin_nanterre.saga_retour_synchrone')
|
|
next_url = getattr(settings, 'NANTERRE_PAYMENT_RESULT_PAGE', '/')
|
|
idop = request.GET.get('idop')
|
|
payload = {'idop': idop}
|
|
try:
|
|
saga = rsu_post(request, 'saga/retour-synchrone/', payload)
|
|
except:
|
|
logger.error('[rsu/saga] retour-synchrone: cannot post idop=%s', idop)
|
|
messages.error(request, ERROR_MESSAGE)
|
|
return HttpResponseRedirect(next_url)
|
|
|
|
# add a result message and redirect
|
|
if isinstance(saga, dict) and saga.get('err') == 0 and saga.get('data', {}).get('etat'):
|
|
etat = saga['data']['etat']
|
|
if etat == 'paye':
|
|
logger.info('[rsu/saga] retour-synchrone: idop=%s etat=%s', idop, etat)
|
|
request.session['saga_retour_synchrone'] = saga['data']
|
|
else:
|
|
# on nettoye la session
|
|
if 'saga_retour_synchrone' in request.session:
|
|
del request.session['saga_retour_synchrone']
|
|
if etat in MESSAGE_BY_STATE:
|
|
logger.info('[rsu/saga] retour-synchrone: idop=%s etat=%s', idop, etat)
|
|
messages.add_message(request, *MESSAGE_BY_STATE[etat])
|
|
else:
|
|
logger.error('[rsu/saga] retour-synchrone: idop=%s ' 'receive unknown etat=%s', idop, etat)
|
|
messages.error(request, ERROR_MESSAGE)
|
|
else:
|
|
logger.error('[rsu/saga] retour-synchrone: idop=%s ' 'receive bad response=%r', idop, saga)
|
|
messages.error(request, ERROR_MESSAGE)
|
|
return HttpResponseRedirect(next_url)
|
|
|
|
|
|
@csrf_exempt
|
|
def saga_retour_asynchrone(request):
|
|
logger = logging.getLogger('combo_plugin_nanterre.saga_retour_asynchrone')
|
|
idop = request.POST.get('idop')
|
|
if not idop:
|
|
logger.error('[rsu/saga] retour-asynchrone: no idop')
|
|
return HttpResponse('{"err": 1}', content_type='application/json')
|
|
payload = {'idop': idop}
|
|
err = 0
|
|
try:
|
|
saga = rsu_post(request, 'saga/retour-asynchrone/', payload)
|
|
except:
|
|
err = 1
|
|
logger.error('[rsu/saga] retour-asynchrone: cannot post idop=%s', idop)
|
|
else:
|
|
if isinstance(saga, dict) and saga.get('err') == 0 and saga.get('data', {}).get('etat'):
|
|
etat = saga['data']['etat']
|
|
if etat in MESSAGE_BY_STATE:
|
|
logger.info('[rsu/saga] retour-asynchrone: idop=%s etat=%s', idop, etat)
|
|
else:
|
|
err = 1
|
|
logger.error('[rsu/saga] retour-asynchrone: idop=%s ' 'receive unknown etat=%s', idop, etat)
|
|
else:
|
|
err = 1
|
|
logger.error('[rsu/saga] retour-asynchrone: idop=%s ' 'receive bad response=%r', idop, saga)
|
|
response = HttpResponse(content_type='application/json')
|
|
response.write(json.dumps({'err': err}))
|
|
return response
|
|
|
|
|
|
@login_required
|
|
def qf_carte_famille(request, qf_id):
|
|
logger = logging.getLogger('combo_plugin_nanterre.qf_carte_famille')
|
|
rsu_id = request.GET.get('rsu_id')
|
|
if rsu_id:
|
|
# rsu_id can be set only by NANTERRE_QF_READER_GROUP members
|
|
# (ie only for RSU agents in RSU backoffice)
|
|
qf_reader_group = getattr(settings, 'NANTERRE_QF_READER_GROUP', None)
|
|
if not qf_reader_group:
|
|
logger.warning('rsu_id present but settings.NANTERRE_QF_READER_GROUP is unset')
|
|
raise PermissionDenied
|
|
if not request.user.groups.filter(name=qf_reader_group).exists():
|
|
logger.warning('rsu_id present but request user is not in NANTERRE_QF_READER_GROUP')
|
|
raise PermissionDenied
|
|
else:
|
|
rsu_id = request.user.saml_identifiers.first().name_id
|
|
url = '[zoo_url]rsu/qf/[rsu_id]/editer-carte/[qf_id]/'
|
|
context = {'request': request, 'rsu_id': rsu_id, 'qf_id': qf_id}
|
|
url = get_templated_url(url, context=context)
|
|
carte = requests.get(url, timeout=20)
|
|
if carte.status_code != 200:
|
|
logger.warning('fail to get PDF on %s, got status %s', url, carte.status_code)
|
|
raise Http404
|
|
content_type = carte.headers.get('Content-Type')
|
|
if content_type == 'application/json':
|
|
logger.warning('fail to get PDF on %s, got JSON: %r', url, carte.content)
|
|
raise Http404
|
|
if content_type != 'application/pdf':
|
|
logger.warning('fail to get PDF on %s, got %s: %r', url, content_type, carte.content[200:])
|
|
raise Http404
|
|
filename = 'carte-famille-%s.pdf' % qf_id
|
|
logger.debug('return %s obtained from %s', filename, url)
|
|
response = HttpResponse(carte.content, content_type=content_type)
|
|
response['Content-Disposition'] = 'attachment; filename="%s"' % filename
|
|
return response
|