We don't only operate on tickets - renaming and refactoring accordingly
This commit is contained in:
parent
b00b3f3156
commit
5cfcf13e7d
|
@ -26,7 +26,7 @@ INSTALLED_APPS = [
|
|||
|
||||
'profiles',
|
||||
'camps',
|
||||
'tickets',
|
||||
'shop',
|
||||
|
||||
'allauth',
|
||||
'allauth.account',
|
||||
|
|
|
@ -37,8 +37,8 @@
|
|||
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
{% if current_camp.ticket_sale_open %}
|
||||
<li><a href="{% url 'tickets:index' %}">Tickets</a></li>
|
||||
{% if current_camp.shop_open %}
|
||||
<li><a href="{% url 'shop:index' %}">Shop</a></li>
|
||||
{% endif %}
|
||||
<li><a href="{% url 'good-to-know' %}">Good to know</a></li>
|
||||
{% if user.is_authenticated %}
|
||||
|
|
|
@ -36,8 +36,8 @@ urlpatterns = [
|
|||
include('profiles.urls', namespace='profiles')
|
||||
),
|
||||
url(
|
||||
r'^tickets/',
|
||||
include('tickets.urls', namespace='tickets')
|
||||
r'^shop/',
|
||||
include('shop.urls', namespace='shop')
|
||||
),
|
||||
url(r'^accounts/', include('allauth.urls')),
|
||||
|
||||
|
|
24
camps/migrations/0005_auto_20160510_2011.py
Normal file
24
camps/migrations/0005_auto_20160510_2011.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.6 on 2016-05-10 20:11
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0004_camp_ticket_sale_open'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='camp',
|
||||
name='ticket_sale_open',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='camp',
|
||||
name='shop_open',
|
||||
field=models.BooleanField(default=False, help_text='Whether the shop is open or not.', verbose_name='Shop open?'),
|
||||
),
|
||||
]
|
|
@ -30,9 +30,9 @@ class Camp(CreatedUpdatedModel, UUIDModel):
|
|||
unique=True,
|
||||
)
|
||||
|
||||
ticket_sale_open = models.BooleanField(
|
||||
verbose_name=_('Ticket sale open?'),
|
||||
help_text=_('Whether tickets are for sale or not.'),
|
||||
shop_open = models.BooleanField(
|
||||
verbose_name=_('Shop open?'),
|
||||
help_text=_('Whether the shop is open or not.'),
|
||||
default=False,
|
||||
)
|
||||
|
||||
|
|
46
shop/admin.py
Normal file
46
shop/admin.py
Normal file
|
@ -0,0 +1,46 @@
|
|||
from django.contrib import admin
|
||||
|
||||
from .models import Order, ProductCategory, Product, OrderProductRelation
|
||||
|
||||
|
||||
@admin.register(ProductCategory)
|
||||
class ProductCategoryAdmin(admin.ModelAdmin):
|
||||
list_display = [
|
||||
'name',
|
||||
]
|
||||
|
||||
|
||||
@admin.register(Product)
|
||||
class ProductAdmin(admin.ModelAdmin):
|
||||
list_display = [
|
||||
'name',
|
||||
'category',
|
||||
'price',
|
||||
'available_in',
|
||||
]
|
||||
|
||||
|
||||
class ProductInline(admin.TabularInline):
|
||||
model = OrderProductRelation
|
||||
|
||||
|
||||
@admin.register(Order)
|
||||
class OrderAdmin(admin.ModelAdmin):
|
||||
list_display = [
|
||||
'user',
|
||||
'camp',
|
||||
'payment_method',
|
||||
'paid',
|
||||
]
|
||||
|
||||
list_filter = [
|
||||
'user',
|
||||
'camp',
|
||||
'payment_method',
|
||||
'paid',
|
||||
]
|
||||
|
||||
exclude = ['products']
|
||||
|
||||
inlines = [ProductInline]
|
||||
|
5
shop/apps.py
Normal file
5
shop/apps.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ShopConfig(AppConfig):
|
||||
name = 'shop'
|
218
shop/epayintegration.py
Normal file
218
shop/epayintegration.py
Normal file
|
@ -0,0 +1,218 @@
|
|||
### epay callback function
|
||||
@require_safe
|
||||
def api_epay_callback(request):
|
||||
### save GET payload
|
||||
callback = EpayCallback.objects.create(
|
||||
json_payload=json.dumps(request.GET),
|
||||
callback_time=timezone.now(),
|
||||
)
|
||||
|
||||
### find order
|
||||
if 'orderid' in request.GET:
|
||||
### find order type
|
||||
if request.GET['orderid'][0] == "M":
|
||||
### find epay order
|
||||
try:
|
||||
order = EpayOrder.objects.get(id=request.GET['orderid'][1:])
|
||||
except EpayOrder.DoesNotExist:
|
||||
print "epay callback - epayorder %s not found" % request.GET['orderid']
|
||||
return HttpResponse("Not OK")
|
||||
|
||||
### check hash here
|
||||
if 'hash' not in request.GET:
|
||||
print "epay callback - missing epay hash"
|
||||
return HttpResponse("Not OK")
|
||||
|
||||
### this does not work sometimes, ordering is off maybe?
|
||||
hashstring = ''
|
||||
qs = request.META['QUERY_STRING']
|
||||
print "querystring is %s" % qs
|
||||
for kv in qs.split("&"):
|
||||
print "hashstring is now %s" % hashstring
|
||||
if kv.split("=")[0] != "hash":
|
||||
hashstring += kv.split("=")[1]
|
||||
print "hashstring is now %s" % hashstring
|
||||
hashstring += settings.EPAY_MD5_SECRET
|
||||
epayhash = hashlib.md5(hashstring).hexdigest()
|
||||
if epayhash != request.GET['hash']:
|
||||
print "epay callback - wrong epay hash"
|
||||
return HttpResponse("Not OK")
|
||||
|
||||
### save callback in epayorder
|
||||
order.epay_callback = callback
|
||||
if 'txnid' in request.GET:
|
||||
order.epay_txnid=request.GET['txnid']
|
||||
if 'amount' in request.GET:
|
||||
order.epay_amount=int(request.GET['amount'])/100
|
||||
if 'fraud' in request.GET:
|
||||
if request.GET['fraud'] == '1':
|
||||
order.epay_fraud=True
|
||||
else:
|
||||
order.epay_fraud=False
|
||||
|
||||
### delay epayorder depending on user level
|
||||
if settings.ACCOUNT_LEVEL_SETTINGS[str(order.user.profile.account_level)]['MOBILEPAY_DELAY_DAYS'] > 0:
|
||||
order.btc_processing_delayed = True
|
||||
|
||||
### all ok?
|
||||
if order.epay_amount == order.xxx_amount and not order.epay_fraud:
|
||||
order.epay_payment_ok=True
|
||||
|
||||
### save
|
||||
order.save()
|
||||
else:
|
||||
print "epay callback - order %s not recognized" % request.GET['orderid']
|
||||
return HttpResponse("Not OK")
|
||||
return HttpResponse("OK")
|
||||
|
||||
|
||||
### epay order function
|
||||
@login_required
|
||||
def epay_order(request):
|
||||
if 'HTTP_X_FORWARDED_FOR' in request.META:
|
||||
ip = request.META['HTTP_X_FORWARDED_FOR']
|
||||
else:
|
||||
ip = request.META['REMOTE_ADDR']
|
||||
|
||||
country = GeoIP().country(ip)["country_code"]
|
||||
if country not in settings.PERMITTED_EPAY_COUNTRIES:
|
||||
return render(request, 'epay_sepa_only.html')
|
||||
|
||||
### get provision percent
|
||||
provision = get_provision_percent(request.user, "epayorder")
|
||||
|
||||
### convert BTC price to DKK
|
||||
btc_usd_price = CurrencyPrice.objects.get(item='BTCUSD').price
|
||||
btc_dkk_price = ConvertCurrency(btc_usd_price, 'USD', 'DKK')
|
||||
|
||||
### add provision
|
||||
btc_dkk_price_with_provision = btc_dkk_price * provision
|
||||
|
||||
### instantiate form
|
||||
form = EpayOrderForm(request.POST or None, request=request, initial={
|
||||
'currency': request.user.profile.preferred_currency,
|
||||
'btc_address': request.user.profile.btc_address,
|
||||
})
|
||||
|
||||
if form.is_valid():
|
||||
### Check exhange rates
|
||||
check_if_exchange_rates_are_too_old()
|
||||
|
||||
### save order
|
||||
try:
|
||||
### save epay order with commit=False
|
||||
epayorder = form.save(commit=False)
|
||||
except Exception as E:
|
||||
print "unable to save epay order with commit=false"
|
||||
return render(request, 'epay_order_fail.html', {
|
||||
'message': _('Unable to save epay order. Please try again, and please contact us if the problem persists.')
|
||||
})
|
||||
|
||||
### set useragent and IP
|
||||
epayorder.useragent = request.META['HTTP_USER_AGENT']
|
||||
epayorder.ip = ip
|
||||
|
||||
### create time
|
||||
epayorder.create_time = timezone.now()
|
||||
|
||||
### set order user
|
||||
epayorder.user = request.user
|
||||
|
||||
### save DKK amount
|
||||
epayorder.dkk_amount = ConvertCurrency(epayorder.xxx_amount, epayorder.currency, 'DKK')
|
||||
|
||||
### set fee
|
||||
epayorder.fee = settings.ACCOUNT_LEVEL_SETTINGS[str(epayorder.user.profile.account_level)]['MOBILEPAY_PROVISION']
|
||||
|
||||
### save epayorder
|
||||
epayorder.save()
|
||||
|
||||
### save btc_address to profile if needed
|
||||
if not request.user.profile.btc_address:
|
||||
request.user.profile.btc_address = epayorder.btc_address
|
||||
request.user.profile.save()
|
||||
|
||||
send_amqp_message("epayorder M%s created by user %s" % (epayorder.id, request.user), "epayorder.create")
|
||||
return HttpResponseRedirect(reverse('epay_epay_form', kwargs={'epayorderid': epayorder.id}))
|
||||
|
||||
### calculate BTC price for the users native currency
|
||||
btc_xxx_price = ConvertCurrency(amount=btc_usd_price, fromcurrency="USD", tocurrency=request.user.profile.preferred_currency or "EUR")
|
||||
|
||||
### add provision
|
||||
btc_xxx_price = btc_xxx_price * provision
|
||||
|
||||
### find out if new orders by this user should be delayed
|
||||
if settings.ACCOUNT_LEVEL_SETTINGS[str(request.user.profile.account_level)]['MOBILEPAY_DELAY_DAYS'] == 0:
|
||||
skipdelay = True
|
||||
else:
|
||||
skipdelay = False
|
||||
|
||||
### delay relevant?
|
||||
if settings.ACCOUNT_LEVEL_SETTINGS[str(request.user.profile.account_level)]['MOBILEPAY_DELAY_DAYS'] > 0:
|
||||
nextlevel_history_delay = settings.ACCOUNT_LEVEL_SETTINGS[str(request.user.profile.account_level+1)]['BUY_HISTORY_DELAY_DAYS']
|
||||
nextlevel_history_amount = settings.ACCOUNT_LEVEL_SETTINGS[str(request.user.profile.account_level+1)]['BUY_HISTORY_MINIMUM']
|
||||
else:
|
||||
nextlevel_history_delay = None
|
||||
nextlevel_history_amount = None
|
||||
|
||||
### render the response
|
||||
return render(request, 'epay_order.html', {
|
||||
'form': form,
|
||||
'btc_xxx_price': round(btc_xxx_price, 2),
|
||||
'currency': request.user.profile.preferred_currency or "EUR",
|
||||
'provision': round((provision-1)*100, 1),
|
||||
'skipdelay': skipdelay,
|
||||
'delaydays': settings.ACCOUNT_LEVEL_SETTINGS[str(request.user.profile.account_level)]['MOBILEPAY_DELAY_DAYS'],
|
||||
'daily_limit': settings.ACCOUNT_LEVEL_SETTINGS[str(request.user.profile.account_level)]['MOBILEPAY_LIMIT_DAY'],
|
||||
'nextlevel_history_delay': nextlevel_history_delay,
|
||||
'nextlevel_history_amount': nextlevel_history_amount,
|
||||
'btc_address': request.user.profile.btc_address,
|
||||
})
|
||||
|
||||
|
||||
### the page which shows the epay epay form
|
||||
@login_required
|
||||
def epay_epay_form(request, epayorderid):
|
||||
epayorder = get_object_or_404(EpayOrder, id=epayorderid, user=request.user, user_cancelled=False, epay_callback__isnull=True)
|
||||
accepturl = request.build_absolute_uri(reverse('epay_thanks', kwargs={'epayorderid': epayorder.id})).replace('http://', 'https://')
|
||||
description = str(request.user.id)
|
||||
hashstring = settings.EPAY_MERCHANT_NUMBER+description+'11'+str(epayorder.xxx_amount*100)+str(epayorder.currency)+'M'+str(epayorder.id)+accepturl+settings.EPAY_MD5_SECRET
|
||||
epayhash = hashlib.md5(hashstring).hexdigest()
|
||||
return render(request, 'epay_form.html', {
|
||||
'orderid': 'M%s' % epayorder.id,
|
||||
'description': description,
|
||||
'merchantnumber': settings.EPAY_MERCHANT_NUMBER,
|
||||
'amount': epayorder.xxx_amount*100,
|
||||
'currency': epayorder.currency,
|
||||
'epayhash': epayhash,
|
||||
'accepturl': accepturl,
|
||||
})
|
||||
|
||||
|
||||
### after epay payment thanks page
|
||||
@login_required
|
||||
def epay_thanks(request, epayorderid):
|
||||
### get order - if it is owned by this user and uncancelled
|
||||
epayorder = get_object_or_404(EpayOrder, id=epayorderid, user=request.user, user_cancelled=False)
|
||||
|
||||
### check for querystring
|
||||
if request.GET:
|
||||
### update order to register thanks page view
|
||||
epayorder.thanks_page_view_time=timezone.now()
|
||||
epayorder.save()
|
||||
|
||||
### redirect to get rid of GET querystring
|
||||
return HttpResponseRedirect(reverse('epay_thanks', kwargs={'epayorderid': epayorder.id}))
|
||||
|
||||
if epayorder.btc_processing_delayed:
|
||||
minamount = ConvertLimit(settings.ACCOUNT_LEVEL_SETTINGS[str(request.user.profile.account_level+1)]['BUY_HISTORY_MINIMUM'], epayorder.currency)
|
||||
delaydays = settings.ACCOUNT_LEVEL_SETTINGS[str(request.user.profile.account_level)]['MOBILEPAY_DELAY_DAYS']
|
||||
else:
|
||||
minamount = None
|
||||
delaydays = None
|
||||
|
||||
return render(request, 'epay_thanks.html', {
|
||||
'order': epayorder,
|
||||
'delaydays': delaydays,
|
||||
'minamount': minamount,
|
||||
})
|
1
shop/forms.py
Normal file
1
shop/forms.py
Normal file
|
@ -0,0 +1 @@
|
|||
from django import forms
|
|
@ -4,7 +4,7 @@ from django.db.models import QuerySet
|
|||
from django.utils import timezone
|
||||
|
||||
|
||||
class TicketTypeQuerySet(QuerySet):
|
||||
class ProductQuerySet(QuerySet):
|
||||
|
||||
def available(self):
|
||||
now = timezone.now()
|
127
shop/migrations/0001_initial.py
Normal file
127
shop/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,127 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.6 on 2016-05-10 20:00
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
import django.contrib.postgres.fields.jsonb
|
||||
import django.contrib.postgres.fields.ranges
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import uuid
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('camps', '0004_camp_ticket_sale_open'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='EpayCallback',
|
||||
fields=[
|
||||
('created', models.DateTimeField(auto_now_add=True)),
|
||||
('updated', models.DateTimeField(auto_now=True)),
|
||||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('payload', django.contrib.postgres.fields.jsonb.JSONField()),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Epay Callback',
|
||||
'verbose_name_plural': 'Epay Callbacks',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='EpayPayment',
|
||||
fields=[
|
||||
('created', models.DateTimeField(auto_now_add=True)),
|
||||
('updated', models.DateTimeField(auto_now=True)),
|
||||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('txnid', models.IntegerField()),
|
||||
('callback', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shop.EpayCallback')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Epay Payment',
|
||||
'verbose_name_plural': 'Epay Payments',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Order',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created', models.DateTimeField(auto_now_add=True)),
|
||||
('updated', models.DateTimeField(auto_now=True)),
|
||||
('paid', models.BooleanField(default=False, help_text='Whether this order has been paid.', verbose_name='Paid?')),
|
||||
('payment_method', models.CharField(choices=[(b'credit_card', b'Credit card'), (b'altcoin', b'Altcoin'), (b'bank_transfer', b'Bank transfer')], default=b'credit_card', max_length=50)),
|
||||
('camp', models.ForeignKey(help_text='The camp this order is for.', on_delete=django.db.models.deletion.CASCADE, to='camps.Camp', verbose_name='Camp')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='OrderProductRelation',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('quantity', models.PositiveIntegerField()),
|
||||
('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shop.Order')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Product',
|
||||
fields=[
|
||||
('created', models.DateTimeField(auto_now_add=True)),
|
||||
('updated', models.DateTimeField(auto_now=True)),
|
||||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('name', models.CharField(max_length=150)),
|
||||
('price', models.IntegerField(help_text='Price of the ticket (in DKK).')),
|
||||
('description', models.TextField()),
|
||||
('available_in', django.contrib.postgres.fields.ranges.DateTimeRangeField(help_text='Which period is this ticket available for purchase? | (Format: YYYY-MM-DD HH:MM) | Only one of start/end is required')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['available_in'],
|
||||
'verbose_name': 'Product',
|
||||
'verbose_name_plural': 'Products',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ProductCategory',
|
||||
fields=[
|
||||
('created', models.DateTimeField(auto_now_add=True)),
|
||||
('updated', models.DateTimeField(auto_now=True)),
|
||||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('name', models.CharField(max_length=150)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Product category',
|
||||
'verbose_name_plural': 'Product categories',
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='product',
|
||||
name='category',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shop.ProductCategory'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='orderproductrelation',
|
||||
name='product',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shop.Product'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='order',
|
||||
name='products',
|
||||
field=models.ManyToManyField(through='shop.OrderProductRelation', to='shop.Product'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='order',
|
||||
name='user',
|
||||
field=models.ForeignKey(help_text='The user this order belongs to.', on_delete=django.db.models.deletion.CASCADE, related_name='orders', to=settings.AUTH_USER_MODEL, verbose_name='User'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='epaypayment',
|
||||
name='order',
|
||||
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='shop.Order'),
|
||||
),
|
||||
]
|
20
shop/migrations/0002_orderproductrelation_handed_out.py
Normal file
20
shop/migrations/0002_orderproductrelation_handed_out.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.6 on 2016-05-10 20:02
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('shop', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='orderproductrelation',
|
||||
name='handed_out',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
|
@ -2,34 +2,36 @@ from django.db import models
|
|||
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
|
||||
|
||||
from .managers import TicketTypeQuerySet
|
||||
from .managers import ProductQuerySet
|
||||
|
||||
|
||||
class Ticket(CreatedUpdatedModel, UUIDModel):
|
||||
class Meta:
|
||||
verbose_name = _('Ticket')
|
||||
verbose_name_plural = _('Tickets')
|
||||
class Order(CreatedUpdatedModel):
|
||||
|
||||
products = models.ManyToManyField(
|
||||
'shop.Product',
|
||||
through='shop.OrderProductRelation'
|
||||
)
|
||||
|
||||
user = models.ForeignKey(
|
||||
'auth.User',
|
||||
verbose_name=_('User'),
|
||||
help_text=_('The user this ticket belongs to.'),
|
||||
related_name='tickets',
|
||||
help_text=_('The user this order belongs to.'),
|
||||
related_name='orders',
|
||||
)
|
||||
|
||||
paid = models.BooleanField(
|
||||
verbose_name=_('Paid?'),
|
||||
help_text=_('Whether the user has paid.'),
|
||||
help_text=_('Whether this order has been paid.'),
|
||||
default=False,
|
||||
)
|
||||
|
||||
ticket_type = models.ForeignKey(
|
||||
'tickets.TicketType',
|
||||
verbose_name=_('Ticket type'),
|
||||
camp = models.ForeignKey(
|
||||
'camps.Camp',
|
||||
verbose_name=_('Camp'),
|
||||
help_text=_('The camp this order is for.'),
|
||||
)
|
||||
|
||||
CREDIT_CARD = 'credit_card'
|
||||
|
@ -48,44 +50,42 @@ class Ticket(CreatedUpdatedModel, UUIDModel):
|
|||
default=CREDIT_CARD
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return '{} ({})'.format(
|
||||
self.ticket_type.name,
|
||||
self.ticket_type.camp
|
||||
)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse_lazy('tickets:detail', kwargs={
|
||||
'pk': self.pk
|
||||
})
|
||||
|
||||
|
||||
class TicketType(CreatedUpdatedModel, UUIDModel):
|
||||
class ProductCategory(CreatedUpdatedModel, UUIDModel):
|
||||
class Meta:
|
||||
verbose_name = _('Ticket Type')
|
||||
verbose_name_plural = _('Ticket Types')
|
||||
ordering = ['available_in']
|
||||
verbose_name = 'Product category'
|
||||
verbose_name_plural = 'Product categories'
|
||||
|
||||
name = models.CharField(max_length=150)
|
||||
|
||||
camp = models.ForeignKey(
|
||||
'camps.Camp',
|
||||
verbose_name=_('Camp'),
|
||||
help_text=_('The camp this ticket type is for.'),
|
||||
)
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Product(CreatedUpdatedModel, UUIDModel):
|
||||
class Meta:
|
||||
verbose_name = 'Product'
|
||||
verbose_name_plural = 'Products'
|
||||
ordering = ['available_in']
|
||||
|
||||
category = models.ForeignKey('shop.ProductCategory')
|
||||
|
||||
name = models.CharField(max_length=150)
|
||||
|
||||
price = models.IntegerField(
|
||||
help_text=_('Price of the ticket (in DKK).')
|
||||
help_text=_('Price of the product (in DKK).')
|
||||
)
|
||||
|
||||
description = models.TextField()
|
||||
|
||||
available_in = DateTimeRangeField(
|
||||
help_text=_(
|
||||
'Which period is this ticket available for purchase? | '
|
||||
'Which period is this product available for purchase? | '
|
||||
'(Format: YYYY-MM-DD HH:MM) | Only one of start/end is required'
|
||||
)
|
||||
)
|
||||
|
||||
objects = TicketTypeQuerySet.as_manager()
|
||||
objects = ProductQuerySet.as_manager()
|
||||
|
||||
def __str__(self):
|
||||
return '{} ({} DKK)'.format(
|
||||
|
@ -98,6 +98,13 @@ class TicketType(CreatedUpdatedModel, UUIDModel):
|
|||
return now in self.available_in
|
||||
|
||||
|
||||
class OrderProductRelation(models.Model):
|
||||
order = models.ForeignKey('shop.Order')
|
||||
product = models.ForeignKey('shop.Product')
|
||||
quantity = models.PositiveIntegerField()
|
||||
handed_out = models.BooleanField(default=False)
|
||||
|
||||
|
||||
class EpayCallback(CreatedUpdatedModel, UUIDModel):
|
||||
class Meta:
|
||||
verbose_name = 'Epay Callback'
|
||||
|
@ -110,6 +117,6 @@ class EpayPayment(CreatedUpdatedModel, UUIDModel):
|
|||
verbose_name = 'Epay Payment'
|
||||
verbose_name_plural = 'Epay Payments'
|
||||
|
||||
ticket = models.OneToOneField('tickets.Ticket')
|
||||
callback = models.ForeignKey('tickets.EpayCallback')
|
||||
order = models.OneToOneField('shop.Order')
|
||||
callback = models.ForeignKey('shop.EpayCallback')
|
||||
txnid = models.IntegerField()
|
|
@ -22,21 +22,21 @@ Here you can see the different ticket types, their prices and availability.
|
|||
|
||||
<tbody>
|
||||
|
||||
{% for ticket_type in ticket_types %}
|
||||
{% for product in tickets %}
|
||||
|
||||
<tr {% if not ticket_type.is_available %}style="color: lightgrey"{%endif%}>
|
||||
<tr {% if not product.is_available %}style="color: lightgrey"{%endif%}>
|
||||
<td>
|
||||
{{ ticket_type.name }}
|
||||
{{ product.name }}
|
||||
<td>
|
||||
{{ ticket_type.price }} DKK
|
||||
{{ product.price }} DKK
|
||||
<td>
|
||||
{{ ticket_type.available_in.lower }}
|
||||
{% if ticket_type.available_in.upper %}
|
||||
- {{ ticket_type.available_in.upper }}
|
||||
{{ product.available_in.lower }}
|
||||
{% if product.available_in.upper %}
|
||||
- {{ product.available_in.upper }}
|
||||
{% endif %}
|
||||
<td>
|
||||
{% if ticket_type.is_available %}
|
||||
<a href="{% url 'tickets:order' %}?ticket_type={{ ticket_type.pk }}">
|
||||
{% if product.is_available %}
|
||||
<a href="">
|
||||
Order
|
||||
</a>
|
||||
{% else %}
|
||||
|
@ -47,7 +47,7 @@ Here you can see the different ticket types, their prices and availability.
|
|||
|
||||
</table>
|
||||
|
||||
|
||||
{% comment %}
|
||||
{% if user.is_authenticated %}
|
||||
<hr />
|
||||
<h3>Your tickets</h3>
|
||||
|
@ -71,5 +71,6 @@ Here you can see the different ticket types, their prices and availability.
|
|||
<a href="{% url 'account_signup' %}?next={% url 'tickets:index' %}">Sign up</a> or
|
||||
<a href="{% url 'account_login' %}?next={% url 'tickets:index' %}">login</a> to buy tickets.
|
||||
{% endif %}
|
||||
{% endcomment %}
|
||||
|
||||
{% endblock %}
|
27
shop/urls.py
Normal file
27
shop/urls.py
Normal file
|
@ -0,0 +1,27 @@
|
|||
from django.conf.urls import url
|
||||
|
||||
from .views import (
|
||||
ShopIndexView,
|
||||
# EpayView,
|
||||
# EpayCallbackView,
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
#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'epay_callback/',
|
||||
#EpayCallbackView,
|
||||
#name='epay_callback'
|
||||
#),
|
||||
#url(
|
||||
#r'detail/(?P<pk>[a-zA-Z0-9\-]+)/$',
|
||||
#TicketDetailView.as_view(),
|
||||
#name='detail'
|
||||
#),
|
||||
url(r'$', ShopIndexView.as_view(), name='index'),
|
||||
]
|
102
shop/views.py
Normal file
102
shop/views.py
Normal file
|
@ -0,0 +1,102 @@
|
|||
import hashlib
|
||||
|
||||
from django.http import HttpResponseRedirect, Http404
|
||||
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 .models import Order, Product, EpayCallback, EpayPayment
|
||||
|
||||
|
||||
class ShopIndexView(TemplateView):
|
||||
template_name = "shop/index.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(ShopIndexView, self).get_context_data(**kwargs)
|
||||
context['tickets'] = Product.objects.filter(category__name='Tickets')
|
||||
return context
|
||||
|
||||
|
||||
class ProductDetailView(LoginRequiredMixin, DetailView):
|
||||
model = Product
|
||||
template_name = 'product/detail.html'
|
||||
context_object_name = 'product'
|
||||
|
||||
|
||||
# class EpayView(TemplateView):
|
||||
# template_name = 'tickets/epay_form.html'
|
||||
#
|
||||
# def get_context_data(self, **kwargs):
|
||||
# ticket = Ticket.objects.get(pk=kwargs.get('ticket_id'))
|
||||
# accept_url = ticket.get_absolute_url()
|
||||
# amount = ticket.ticket_type.price * 100
|
||||
# order_id = str(ticket.pk)
|
||||
# description = str(ticket.user.pk)
|
||||
#
|
||||
# hashstring = (
|
||||
# '{merchant_number}{description}11{amount}DKK'
|
||||
# '{order_id}{accept_url}{md5_secret}'
|
||||
# ).format(
|
||||
# merchant_number=settings.EPAY_MERCHANT_NUMBER,
|
||||
# description=description,
|
||||
# amount=str(amount),
|
||||
# order_id=str(order_id),
|
||||
# accept_url=accept_url,
|
||||
# md5_secret=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 = (
|
||||
# '{merchant_number}{description}11{amount}DKK'
|
||||
# '{order_id}{accept_url}{md5_secret}'
|
||||
# ).format(
|
||||
# merchant_number=query.get('merchantnumber'),
|
||||
# description=query.get('description'),
|
||||
# amount=query.get('amount'),
|
||||
# order_id=query.get('orderid'),
|
||||
# accept_url=query.get('accepturl'),
|
||||
# md5_secret=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')
|
|
@ -1,29 +0,0 @@
|
|||
from django.contrib import admin
|
||||
|
||||
from .models import Ticket, TicketType
|
||||
|
||||
|
||||
@admin.register(Ticket)
|
||||
class TicketAdmin(admin.ModelAdmin):
|
||||
list_display = [
|
||||
'user',
|
||||
'ticket_type',
|
||||
'payment_method',
|
||||
'paid',
|
||||
]
|
||||
|
||||
list_filter = [
|
||||
'paid',
|
||||
'ticket_type',
|
||||
'payment_method',
|
||||
]
|
||||
|
||||
|
||||
@admin.register(TicketType)
|
||||
class TicketTypeAdmin(admin.ModelAdmin):
|
||||
list_display = [
|
||||
'name',
|
||||
'price',
|
||||
'available_in',
|
||||
'camp',
|
||||
]
|
|
@ -1,5 +0,0 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class TicketsConfig(AppConfig):
|
||||
name = 'tickets'
|
|
@ -1,21 +0,0 @@
|
|||
from django import forms
|
||||
from .models import Ticket, TicketType
|
||||
|
||||
|
||||
class TicketForm(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = Ticket
|
||||
fields = [
|
||||
'ticket_type',
|
||||
'payment_method',
|
||||
]
|
||||
widgets = {
|
||||
'payment_method': forms.RadioSelect()
|
||||
}
|
||||
|
||||
ticket_type = forms.ModelChoiceField(
|
||||
queryset=TicketType.objects.available()
|
||||
)
|
||||
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.2 on 2016-04-22 20:19
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
import django.contrib.postgres.fields.ranges
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import uuid
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('camps', '0003_auto_20160422_2019'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Ticket',
|
||||
fields=[
|
||||
('created', models.DateTimeField(auto_now_add=True)),
|
||||
('updated', models.DateTimeField(auto_now=True)),
|
||||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('paid', models.BooleanField(default=False, help_text='Whether the user has paid.', verbose_name='Paid?')),
|
||||
('camp', models.ForeignKey(help_text='The camp this ticket is for.', on_delete=django.db.models.deletion.CASCADE, to='camps.Camp', verbose_name='Camp')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'Tickets',
|
||||
'verbose_name': 'Ticket',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TicketType',
|
||||
fields=[
|
||||
('created', models.DateTimeField(auto_now_add=True)),
|
||||
('updated', models.DateTimeField(auto_now=True)),
|
||||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('name', models.CharField(max_length=100)),
|
||||
('price', models.IntegerField(help_text='Price of the ticket (in DKK).')),
|
||||
('available_in', django.contrib.postgres.fields.ranges.DateTimeRangeField(help_text='Which period is this ticket available for purchase?')),
|
||||
('camp', models.ForeignKey(help_text='The camp this ticket type is for.', on_delete=django.db.models.deletion.CASCADE, to='camps.Camp', verbose_name='Camp')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'Ticket Types',
|
||||
'verbose_name': 'Ticket Type',
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='ticket',
|
||||
name='ticket_type',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tickets.TicketType'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='ticket',
|
||||
name='user',
|
||||
field=models.ForeignKey(help_text='The user this ticket belongs to.', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='User'),
|
||||
),
|
||||
]
|
|
@ -1,36 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.5 on 2016-05-06 16:02
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import django.contrib.postgres.fields.ranges
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('tickets', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='ticket',
|
||||
name='camp',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ticket',
|
||||
name='ticket_type',
|
||||
field=models.ForeignKey(help_text='The type of the ticket.', on_delete=django.db.models.deletion.CASCADE, to='tickets.TicketType', verbose_name='Ticket type'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='tickettype',
|
||||
name='available_in',
|
||||
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(help_text='Which period is this ticket available for purchase? | (Format: YYYY-MM-DD HH:MM) | Only one of start/end is required'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='tickettype',
|
||||
name='name',
|
||||
field=models.CharField(max_length=150),
|
||||
),
|
||||
]
|
|
@ -1,31 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.5 on 2016-05-06 20:16
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('tickets', '0002_auto_20160506_1602'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='tickettype',
|
||||
options={'ordering': ['available_in'], 'verbose_name': 'Ticket Type', 'verbose_name_plural': 'Ticket Types'},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ticket',
|
||||
name='ticket_type',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tickets.TicketType', verbose_name='Ticket type'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ticket',
|
||||
name='user',
|
||||
field=models.ForeignKey(help_text='The user this ticket belongs to.', on_delete=django.db.models.deletion.CASCADE, related_name='tickets', to=settings.AUTH_USER_MODEL, verbose_name='User'),
|
||||
),
|
||||
]
|
|
@ -1,20 +0,0 @@
|
|||
# -*- 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,29 +0,0 @@
|
|||
from django.conf.urls import url
|
||||
|
||||
from .views import (
|
||||
TicketIndexView,
|
||||
TicketOrderView,
|
||||
TicketDetailView,
|
||||
EpayView,
|
||||
EpayCallbackView,
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
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'epay_callback/',
|
||||
EpayCallbackView,
|
||||
name='epay_callback'
|
||||
),
|
||||
url(
|
||||
r'detail/(?P<pk>[a-zA-Z0-9\-]+)/$',
|
||||
TicketDetailView.as_view(),
|
||||
name='detail'
|
||||
),
|
||||
url(r'$', TicketIndexView.as_view(), name='index'),
|
||||
]
|
150
tickets/views.py
150
tickets/views.py
|
@ -1,150 +0,0 @@
|
|||
import hashlib
|
||||
|
||||
from django.http import HttpResponseRedirect, Http404
|
||||
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, EpayPayment, EpayCallback
|
||||
from .forms import TicketForm
|
||||
|
||||
|
||||
class CampTicketSaleCheck(object):
|
||||
def dispatch(self, *args, **kwargs):
|
||||
current_camp = Camp.objects.current()
|
||||
if current_camp and current_camp.ticket_sale_open:
|
||||
return super(CampTicketSaleCheck, self).dispatch(*args, **kwargs)
|
||||
raise Http404()
|
||||
|
||||
|
||||
class TicketIndexView(CampTicketSaleCheck, TemplateView):
|
||||
template_name = "tickets/index.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(TicketIndexView, self).get_context_data(**kwargs)
|
||||
context['ticket_types'] = TicketType.objects.all()
|
||||
return context
|
||||
|
||||
|
||||
class TicketDetailView(LoginRequiredMixin, CampTicketSaleCheck, DetailView):
|
||||
model = Ticket
|
||||
template_name = 'tickets/detail.html'
|
||||
context_object_name = 'ticket'
|
||||
|
||||
|
||||
class TicketOrderView(LoginRequiredMixin, CampTicketSaleCheck, CreateView):
|
||||
model = Ticket
|
||||
template_name = "tickets/order.html"
|
||||
form_class = TicketForm
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super(TicketOrderView, self).get_form_kwargs()
|
||||
ticket_type = self.request.GET.get('ticket_type', None)
|
||||
if ticket_type:
|
||||
kwargs['initial'] = {
|
||||
'ticket_type': ticket_type
|
||||
}
|
||||
|
||||
return kwargs
|
||||
|
||||
def form_valid(self, form):
|
||||
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.get('ticket_id'))
|
||||
accept_url = ticket.get_absolute_url()
|
||||
amount = ticket.ticket_type.price * 100
|
||||
order_id = str(ticket.pk)
|
||||
description = str(ticket.user.pk)
|
||||
|
||||
hashstring = (
|
||||
'{merchant_number}{description}11{amount}DKK'
|
||||
'{order_id}{accept_url}{md5_secret}'
|
||||
).format(
|
||||
merchant_number=settings.EPAY_MERCHANT_NUMBER,
|
||||
description=description,
|
||||
amount=str(amount),
|
||||
order_id=str(order_id),
|
||||
accept_url=accept_url,
|
||||
md5_secret=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 = (
|
||||
'{merchant_number}{description}11{amount}DKK'
|
||||
'{order_id}{accept_url}{md5_secret}'
|
||||
).format(
|
||||
merchant_number=query.get('merchantnumber'),
|
||||
description=query.get('description'),
|
||||
amount=query.get('amount'),
|
||||
order_id=query.get('orderid'),
|
||||
accept_url=query.get('accepturl'),
|
||||
md5_secret=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