Adding initial Epay integration - far from done.
This commit is contained in:
parent
9617582b2a
commit
bbaf3ea964
|
@ -1,5 +1,8 @@
|
|||
import os
|
||||
|
||||
import environ
|
||||
env = environ.Env()
|
||||
environ.Env.read_env()
|
||||
|
||||
def local_dir(entry):
|
||||
return os.path.join(
|
||||
|
@ -91,3 +94,6 @@ BOOTSTRAP3 = {
|
|||
'jquery_url': '/static/js/jquery.min.js',
|
||||
'javascript_url': '/static/js/bootstrap.min.js'
|
||||
}
|
||||
|
||||
EPAY_MERCHANT_NUMBER = env('EPAY_MERCHANT_NUMBER')
|
||||
EPAY_MD5_SECRET = env('EPAY_MD5_SECRET')
|
||||
|
|
|
@ -7,3 +7,5 @@ EMAIL_HOST_USER='mymailuser'
|
|||
EMAIL_HOST_PASSWORD='mymailpassword'
|
||||
EMAIL_USE_TLS=True
|
||||
EMAIL_FROM='noreply@example.com'
|
||||
EPAY_MERCHANT_NUMBER=something
|
||||
EPAY_MD5_SECRET=something
|
||||
|
|
|
@ -8,12 +8,14 @@ class TicketAdmin(admin.ModelAdmin):
|
|||
list_display = [
|
||||
'user',
|
||||
'ticket_type',
|
||||
'payment_method',
|
||||
'paid',
|
||||
]
|
||||
|
||||
list_filter = [
|
||||
'paid',
|
||||
'ticket_type',
|
||||
'payment_method',
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -8,9 +8,14 @@ class TicketForm(forms.ModelForm):
|
|||
model = Ticket
|
||||
fields = [
|
||||
'ticket_type',
|
||||
'payment_method',
|
||||
]
|
||||
widgets = {
|
||||
'payment_method': forms.RadioSelect()
|
||||
}
|
||||
|
||||
ticket_type = forms.ModelChoiceField(
|
||||
queryset=TicketType.objects.available()
|
||||
)
|
||||
|
||||
|
||||
|
|
20
tickets/migrations/0004_ticket_payment_method.py
Normal file
20
tickets/migrations/0004_ticket_payment_method.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.5 on 2016-05-08 11:21
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('tickets', '0003_auto_20160506_2016'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='ticket',
|
||||
name='payment_method',
|
||||
field=models.CharField(choices=[('credit_card', 'Credit card'), ('altcoin', 'Altcoin'), ('bank_transfer', 'Bank transfer')], default='credit_card', max_length=50),
|
||||
),
|
||||
]
|
|
@ -1,7 +1,8 @@
|
|||
from django.db import models
|
||||
from django.contrib.postgres.fields import DateTimeRangeField
|
||||
from django.contrib.postgres.fields import DateTimeRangeField, JSONField
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils import timezone
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
|
||||
from bornhack.utils import CreatedUpdatedModel, UUIDModel
|
||||
|
||||
|
@ -31,12 +32,33 @@ class Ticket(CreatedUpdatedModel, UUIDModel):
|
|||
verbose_name=_('Ticket type'),
|
||||
)
|
||||
|
||||
CREDIT_CARD = 'credit_card'
|
||||
ALTCOIN = 'altcoin'
|
||||
BANK_TRANSFER = 'bank_transfer'
|
||||
|
||||
PAYMENT_METHODS = [
|
||||
(CREDIT_CARD, 'Credit card'),
|
||||
(ALTCOIN, 'Altcoin'),
|
||||
(BANK_TRANSFER, 'Bank transfer'),
|
||||
]
|
||||
|
||||
payment_method = models.CharField(
|
||||
max_length=50,
|
||||
choices=PAYMENT_METHODS,
|
||||
default=CREDIT_CARD
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return '{} ({})'.format(
|
||||
self.ticket_type.name,
|
||||
self.ticket_type.camp
|
||||
)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse_lazy('ticket:detail', kwargs={
|
||||
'pk': self.pk
|
||||
})
|
||||
|
||||
|
||||
class TicketType(CreatedUpdatedModel, UUIDModel):
|
||||
class Meta:
|
||||
|
@ -74,3 +96,20 @@ class TicketType(CreatedUpdatedModel, UUIDModel):
|
|||
def is_available(self):
|
||||
now = timezone.now()
|
||||
return now in self.available_in
|
||||
|
||||
|
||||
class EpayCallback(CreatedUpdatedModel, UUIDModel):
|
||||
class Meta:
|
||||
verbose_name = 'Epay Callback'
|
||||
verbose_name_plural = 'Epay Callbacks'
|
||||
payload = JSONField()
|
||||
|
||||
|
||||
class EpayPayment(CreatedUpdatedModel, UUIDModel):
|
||||
class Meta:
|
||||
verbose_name = 'Epay Payment'
|
||||
verbose_name_plural = 'Epay Payments'
|
||||
|
||||
ticket = models.OneToOneField('tickets.Ticket')
|
||||
callback = models.ForeignKey('tickets.EpayCallback')
|
||||
txnid = models.IntegerField()
|
||||
|
|
27
tickets/templates/tickets/epay_form.html
Normal file
27
tickets/templates/tickets/epay_form.html
Normal file
|
@ -0,0 +1,27 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<article>
|
||||
<script charset="UTF-8" src="https://ssl.ditonlinebetalingssystem.dk/integration/ewindow/paymentwindow.js" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
paymentwindow = new PaymentWindow({
|
||||
'merchantnumber': "{{ merchant_number }}",
|
||||
'description': "{{ description }}",
|
||||
'instantcapture': 1,
|
||||
'ownreceipt': 1,
|
||||
'amount': "{{ amount }}",
|
||||
'currency': "DKK",
|
||||
'orderid': "{{ order_id }}",
|
||||
'accepturl': "{{ accept_url }}",
|
||||
'hash': "{{ epay_hash }}",
|
||||
});
|
||||
</script>
|
||||
<input onclick="javascript: paymentwindow.open()" type="button" value="Go to payment">
|
||||
<script type="text/javascript">
|
||||
$( document ).ready(function() {
|
||||
paymentwindow.open();
|
||||
});
|
||||
</script>
|
||||
</article>
|
||||
{% endblock content %}
|
||||
|
|
@ -36,7 +36,9 @@ Here you can see the different ticket types, their prices and availability.
|
|||
{% endif %}
|
||||
<td>
|
||||
{% if ticket_type.is_available %}
|
||||
<a href="{% url 'tickets:buy' %}?ticket_type={{ ticket_type.pk }}">Buy</a>
|
||||
<a href="{% url 'tickets:order' %}?ticket_type={{ ticket_type.pk }}">
|
||||
Order
|
||||
</a>
|
||||
{% else %}
|
||||
N/A
|
||||
{% endif %}
|
||||
|
@ -63,7 +65,7 @@ Here you can see the different ticket types, their prices and availability.
|
|||
{% endfor %}
|
||||
</table>
|
||||
|
||||
<a href="{% url 'tickets:buy' %}" class="btn btn-success">Buy tickets</a>
|
||||
<a href="{% url 'tickets:order' %}" class="btn btn-success">Order tickets</a>
|
||||
|
||||
{% else %}
|
||||
<a href="{% url 'account_signup' %}?next={% url 'tickets:index' %}">Sign up</a> or
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
from django.conf.urls import url
|
||||
|
||||
from .views import BuyTicketView, TicketIndexView, TicketDetailView
|
||||
from .views import (
|
||||
TicketIndexView,
|
||||
TicketOrderView,
|
||||
TicketDetailView,
|
||||
EpayView,
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
url(r'buy/$', BuyTicketView.as_view(), name='buy'),
|
||||
url(r'order/$', TicketOrderView.as_view(), name='order'),
|
||||
url(r'pay/credit_card/(?P<ticket_id>[a-zA-Z0-9\-]+)/$', EpayView.as_view(), name='epay_form'),
|
||||
url(
|
||||
r'detail/(?P<pk>[a-zA-Z0-9\-]+)/$',
|
||||
TicketDetailView.as_view(),
|
||||
|
|
102
tickets/views.py
102
tickets/views.py
|
@ -1,12 +1,15 @@
|
|||
import hashlib
|
||||
|
||||
from django.http import HttpResponseRedirect, Http404
|
||||
from django.shortcuts import render
|
||||
from django.views.generic import CreateView, TemplateView, DetailView
|
||||
from django.views.generic import CreateView, TemplateView, DetailView, View
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.http import HttpResponse
|
||||
|
||||
from camps.models import Camp
|
||||
|
||||
from .models import Ticket, TicketType
|
||||
from .models import Ticket, TicketType, EpayPayment, EpayCallback
|
||||
from .forms import TicketForm
|
||||
|
||||
|
||||
|
@ -33,13 +36,13 @@ class TicketDetailView(LoginRequiredMixin, CampTicketSaleCheck, DetailView):
|
|||
context_object_name = 'ticket'
|
||||
|
||||
|
||||
class BuyTicketView(LoginRequiredMixin, CampTicketSaleCheck, CreateView):
|
||||
class TicketOrderView(LoginRequiredMixin, CampTicketSaleCheck, CreateView):
|
||||
model = Ticket
|
||||
template_name = "tickets/buy.html"
|
||||
template_name = "tickets/order.html"
|
||||
form_class = TicketForm
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super(BuyTicketView, self).get_form_kwargs()
|
||||
kwargs = super(TicketOrderView, self).get_form_kwargs()
|
||||
ticket_type = self.request.GET.get('ticket_type', None)
|
||||
if ticket_type:
|
||||
kwargs['initial'] = {
|
||||
|
@ -52,9 +55,96 @@ class BuyTicketView(LoginRequiredMixin, CampTicketSaleCheck, CreateView):
|
|||
instance = form.save(commit=False)
|
||||
instance.user = self.request.user
|
||||
instance.save()
|
||||
|
||||
if instance.payment_method == Ticket.ALTCOIN:
|
||||
return HttpResponse('Altcoin')
|
||||
|
||||
if instance.payment_method == Ticket.CREDIT_CARD:
|
||||
return HttpResponseRedirect(
|
||||
reverse_lazy('tickets:epay_form', kwargs={
|
||||
'ticket_id': str(instance.pk)
|
||||
})
|
||||
)
|
||||
|
||||
return HttpResponseRedirect(
|
||||
reverse_lazy('tickets:detail', kwargs={
|
||||
'pk': str(instance.pk)
|
||||
})
|
||||
)
|
||||
|
||||
|
||||
class EpayView(TemplateView):
|
||||
template_name = 'tickets/epay_form.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ticket = Ticket.objects.get(pk=kwargs).select_related('ticket_type')
|
||||
accept_url = ticket.get_absolute_url()
|
||||
amount = ticket.ticket_type.price * 100
|
||||
order_id = str(ticket.pk)
|
||||
description = str(ticket.user.pk)
|
||||
|
||||
hashstring = (
|
||||
'{merchantnumber}{description}11{amount}DKK'
|
||||
'{order_id}{accept_url}{md5_secret}'
|
||||
).format(
|
||||
settings.EPAY_MERCHANT_NUMBER,
|
||||
description,
|
||||
str(amount),
|
||||
str(order_id),
|
||||
accept_url,
|
||||
settings.EPAY_MD5_SECRET,
|
||||
)
|
||||
epay_hash = hashlib.md5(hashstring).hexdigest()
|
||||
|
||||
context = super(EpayView, self).get_context_data(**kwargs)
|
||||
context['merchant_number'] = settings.EPAY_MERCHANT_NUMBER
|
||||
context['description'] = description
|
||||
context['order_id'] = order_id
|
||||
context['accept_url'] = accept_url
|
||||
context['amount'] = amount
|
||||
context['epay_hash'] = epay_hash
|
||||
return context
|
||||
|
||||
|
||||
class EpayCallbackView(View):
|
||||
|
||||
def get(self, request, **kwargs):
|
||||
|
||||
callback = EpayCallback.objects.create(
|
||||
payload=request.GET
|
||||
)
|
||||
|
||||
if 'orderid' in request.GET:
|
||||
ticket = Ticket.objects.get(pk=request.GET.get('order_id'))
|
||||
query = dict(
|
||||
map(
|
||||
lambda x: tuple(x.split('=')),
|
||||
request.META['QUERY_STRING'].split('&')
|
||||
)
|
||||
)
|
||||
|
||||
hashstring = (
|
||||
'{merchantnumber}{description}11{amount}DKK'
|
||||
'{order_id}{accept_url}{md5_secret}'
|
||||
).format(
|
||||
query.get('merchantnumber'),
|
||||
query.get('description'),
|
||||
query.get('amount'),
|
||||
query.get('order_id'),
|
||||
query.get('accept_url'),
|
||||
settings.EPAY_MD5_SECRET,
|
||||
)
|
||||
epay_hash = hashlib.md5(hashstring).hexdigest()
|
||||
|
||||
if not epay_hash == request.GET['hash']:
|
||||
return HttpResponse(status=400)
|
||||
|
||||
EpayPayment.objects.create(
|
||||
ticket=ticket,
|
||||
callback=callback,
|
||||
txnid=request.GET['txnid'],
|
||||
)
|
||||
else:
|
||||
return HttpResponse(status=400)
|
||||
|
||||
return HttpResponse('OK')
|
||||
|
|
Loading…
Reference in a new issue