# 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 . 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.core.urlresolvers import reverse from django.http import HttpResponse, HttpResponseRedirect, Http404 from django.template import RequestContext 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 = RequestContext(request, {'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 '/' 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.error('[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 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.GET.get('idop') 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 = RequestContext(request, {'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