Adding initial Epay integration - far from done.

This commit is contained in:
Víðir Valberg Guðmundsson 2016-05-10 17:55:54 +02:00
parent 9617582b2a
commit bbaf3ea964
11 changed files with 210 additions and 11 deletions

View file

@ -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')

View file

@ -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

View file

@ -8,12 +8,14 @@ class TicketAdmin(admin.ModelAdmin):
list_display = [
'user',
'ticket_type',
'payment_method',
'paid',
]
list_filter = [
'paid',
'ticket_type',
'payment_method',
]

View file

@ -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()
)

View 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),
),
]

View file

@ -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()

View 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 %}

View file

@ -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

View file

@ -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(),

View file

@ -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')