Source code for web_payments.django.urls
'''
This module is responsible for automatic processing of provider callback
data (asynchronous transaction updates).
'''
import logging
import simplejson as json
import xmltodict
from django.http import Http404, HttpResponseRedirect, HttpResponseServerError, HttpResponse
from django.shortcuts import get_object_or_404
from django.views.decorators.csrf import csrf_exempt
from django.db.transaction import atomic
try:
from django.urls import re_path as url
except ImportError:
from django.conf.urls import url
from . import get_payment_model
from .. import HttpRequest, RedirectNeeded, provider_factory
def _process_data(request, payment, provider):
try:
# request should have some abstraction
# redefine django request as namedtuple
# parse most used content types correct
if request.method == "GET":
content = None
elif request.content_type in ("application/json", "application/hal+json"):
content = json.loads(request.body, use_decimal=True)
elif request.content_type == "application/x-www-form-urlencoded":
content = request.POST
elif request.content_type in ('application/xml', 'application/hal+xml', 'text/xml'):
# I cannot allow people to handle xml themselves
# You need good security know-how to handle it
content = xmltodict.parse(request.body)
else:
content = request.body
reqparsed = HttpRequest(request.method, request.GET, content, request.content_type)
ret = provider.process_data(payment, reqparsed)
if ret in (True, False, None):
status = 200
if ret is None:
logging.error("process_data returned None, reached end of function without returning")
status = 500
elif not ret:
status = 404
return HttpResponse(status=status)
else:
content, type = ret
return HttpResponse(content, type)
except RedirectNeeded as exc:
return HttpResponseRedirect(exc.args[0])
except Exception as exc:
# for some providers this faces to the banking solution
# log here for beeing visible
logging.exception(exc)
# could contain sensitive data so don't return any information
# just log
return HttpResponseServerError()
[docs]@csrf_exempt
@atomic
def process_data(request, token, provider=None):
'''
Calls process_data of an appropriate provider.
Raises Http404 if variant does not exist.
'''
Payment = get_payment_model()
payment = get_object_or_404(Payment, token=token)
if not provider:
try:
provider = payment.provider
except ValueError:
raise Http404('No such provider')
return _process_data(request, payment, provider)
[docs]@csrf_exempt
@atomic
def static_callback(request, variant):
Payment = get_payment_model()
filtered_v = Payment.list_providers(name=variant)
if len(filtered_v) != 1:
raise Http404('No such provider/or not unique')
else:
try:
provider = provider_factory(filtered_v[0])
except ValueError:
raise Http404('No such provider')
token = provider.get_token_from_request(request=request, payment=None)
if not token:
raise Http404('Invalid response')
return process_data(request, token, provider)
urlpatterns = [
url(r'^process/(?P<token>[0-9a-z]{8}-[0-9a-z]{4}-'
'[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12})/$', process_data,
name='process_payment'),
url(r'^process/(?P<variant>[a-z-]+)/$', static_callback,
name='static_process_payment')
]