Make Kassa communicate with Dusken.

This commit is contained in:
ponas committed 2017-07-27 15:52:14 +02:00
1 parent f400d7a295
commit 031a400855
21 files changed
+14731 -7321

No files matched your search

+4 -2
View File
@@ -3,8 +3,10 @@ from django.contrib import admin
class KassaEventAdmin(admin.ModelAdmin):
list_display = ['id', 'event', 'card_number', 'user_phone_number', 'user_inside_id', 'created']
list_display = ['id', 'event', 'card_number', 'user_phone_number', 'user_dusken_id',
'user_inside_id', 'created', 'transaction_id']
list_filter = ['event']
search_fields = ['card_number', 'user_phone_number', 'user_inside_id']
search_fields = ['card_number', 'user_phone_number', 'user_dusken_id',
'user_inside_id', 'transaction_id']
admin.site.register(KassaEvent, KassaEventAdmin)
+1
View File
@@ -14,3 +14,4 @@ class AddCardForm(forms.Form):
card_number = forms.IntegerField(widget=forms.NumberInput(attrs={'autofocus': True}))
phone_number = PhoneNumberField(widget=forms.TextInput(attrs={'type': 'tel'}))
user_id = forms.IntegerField(widget=forms.HiddenInput(), required=False)
order_uuid = forms.IntegerField(widget=forms.HiddenInput(), required=False)
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.3 on 2017-07-22 12:40
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('kassa', '0006_auto_20150829_1811'),
]
operations = [
migrations.AddField(
model_name='kassaevent',
name='transaction_id',
field=models.UUIDField(blank=True, null=True),
),
]
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.3 on 2017-07-22 12:43
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('kassa', '0007_kassaevent_transaction_id'),
]
operations = [
migrations.AddField(
model_name='kassaevent',
name='user_dusken_id',
field=models.IntegerField(blank=True, null=True),
),
]
+2
View File
@@ -21,7 +21,9 @@ class KassaEvent(models.Model):
)
event = models.CharField(max_length=255, choices=EVENT_CHOICES)
card_number = models.IntegerField(null=True, blank=True)
user_dusken_id = models.IntegerField(null=True, blank=True)
user_inside_id = models.IntegerField(null=True, blank=True)
user_phone_number = models.CharField(max_length=100, null=True, blank=True)
transaction_id = models.UUIDField(null=True, blank=True)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
+1 -1
View File
@@ -18,7 +18,7 @@
{{ add_card_form|bootstrap }}
<div class="register-card-form--selected-user-section">
<label>{% trans "Member" %}</label>
<div class="register-card-form--selected-user-wrap"></div>
<div class="register-card-form--selected-entity-wrap"></div>
</div>
<button id="register-submit-btn" type="submit" class="btn btn-primary btn-lg" disabled>{% trans "Register card" %}</button>
+6 -5
View File
@@ -1,12 +1,13 @@
from django.conf.urls import url
from apps.kassa.views import register, inside_user_api, inside_card_api, check_phone_number, \
register_card_and_membership, stats_card_sales, renew_membership
from apps.kassa.views import (register, user_search, check_card,
check_phone_number, register_card_and_membership,
stats_card_sales, renew_membership)
urlpatterns = [
url(r'^$', register, name='register'),
url(r'^inside/user/$', inside_user_api, name='inside-user-api'),
url(r'^check-phonenumber/$', check_phone_number, name='check-phone-number'),
url(r'^inside/card/$', inside_card_api, name='inside-card-number-api'),
url(r'^user-search/$', user_search, name='user-search'),
url(r'^check-phone-number/$', check_phone_number, name='check-phone-number'),
url(r'^check-card/$', check_card, name='check-card'),
url(r'^register-card-membership/$', register_card_and_membership, name='register-card-and-membership'),
url(r'^renew-membership/$', renew_membership, name='renew-membership'),
url(r'^stats/card-sales/$', stats_card_sales, name='stats-card-sales')
+68 -36
View File
@@ -3,6 +3,13 @@ from django.conf import settings
import datetime
import phonenumbers
import requests
import logging
logger = logging.getLogger(__name__)
dusken_auth = {
'Authorization': 'Token {}'.format(settings.DUSKEN_API_KEY)
}
def tekstmelding_new_membership_card(card_number, phone_number):
@@ -12,6 +19,10 @@ def tekstmelding_new_membership_card(card_number, phone_number):
'action': 'new_card_no_user',
}
if not settings.TEKSTMELDING_ENABLED:
logger.info('Tekstmelding disabled. Payload would be: ', payload)
return
url = '{}kassa/new-membership-card'.format(settings.TEKSTMELDING_API_URL)
response = requests.post(
url,
@@ -23,48 +34,69 @@ def tekstmelding_new_membership_card(card_number, phone_number):
return response.json()
def inside_get_card(card_number):
url = '{}card.php'.format(settings.INSIDE_API_URL)
params = {
'apikey': settings.INSIDE_API_KEY,
'card_number': card_number
}
return requests.get(url, params=params)
def get_card(card_number):
url = '{}cards/{}/'.format(settings.DUSKEN_API_URL, card_number)
return requests.get(url, headers=dusken_auth)
def inside_update_card(card_number, user_id, phone_number, action, membership_trial=None):
url = '{}card.php'.format(settings.INSIDE_API_URL)
def get_order(order_uuid):
url = '{}orders/{}/'.format(settings.DUSKEN_API_URL, order_uuid)
return requests.get(url, headers=dusken_auth)
def get_latest_order_by_card(card_number):
url = '{}orders/'.format(settings.DUSKEN_API_URL)
payload = {'card_number': card_number}
orders = requests.get(url, params=payload, headers=dusken_auth).json()
return orders.get('results')[0]
def get_latest_order_by_phone(phone_number):
url = '{}orders/'.format(settings.DUSKEN_API_URL)
payload = {'phone_number': phone_number}
orders = requests.get(url, params=payload, headers=dusken_auth).json()
if orders.get('count'):
return orders.get('results')[0]
return None
def get_user(user_id):
url = '{}users/{}/'.format(settings.DUSKEN_API_URL, user_id)
return requests.get(url, headers=dusken_auth)
def get_user_by_phone(phone_number):
url = '{}users/'.format(settings.DUSKEN_API_URL)
payload = {'phone_number': phone_number}
users = requests.get(url, params=payload, headers=dusken_auth).json()
if users.get('count') == 1:
return users.get('results')[0]
return None
def update_card(card_number, user_id=None, order_uuid=None):
assert not (user_id and order_uuid)
url = '{}kassa/card/'.format(settings.DUSKEN_API_URL)
payload = {
'card_number': card_number,
'user_id': user_id,
'member_card': card_number,
'user': user_id,
'order': order_uuid,
}
return requests.patch(url, json=payload, headers=dusken_auth)
def update_membership(user=None, phone_number=None, card_number=None, membership_type=None,
transaction_id=None):
assert membership_type in ('standard', 'trial')
url = '{}kassa/membership/'.format(settings.DUSKEN_API_URL)
payload = {
'user': user,
'phone_number': phone_number,
'action': action # new_card_membership, update_card, add_or_renew, sms_card_notify
'member_card': card_number,
'membership_type': membership_type,
'transaction_id': transaction_id,
}
if membership_trial is not None:
payload.update({'membership_trial': membership_trial})
return requests.post(
url,
json=payload,
params={'apikey': settings.INSIDE_API_KEY},
headers=dict(content_type='application/json')
)
def inside_update_membership(user_id, purchased=None, membership_trial=None):
url = '{}membership.php'.format(settings.INSIDE_API_URL)
payload = {
'apikey': settings.INSIDE_API_KEY,
'user_id': user_id,
'purchased': purchased,
'source': 'kassa'
}
if membership_trial is not None:
payload.update({'membership_trial': membership_trial})
return requests.post(url, json=payload, headers=dict(content_type='application/json'))
return requests.post(url, json=payload, headers=dusken_auth)
def format_phone_number(number):
+84 -70
View File
@@ -6,14 +6,16 @@ from django.shortcuts import render
from django.utils import timezone
from itertools import groupby
import json
import uuid
import logging
import phonenumbers
import requests
from apps.kassa.models import KassaEvent
from apps.kassa.forms import AddCardForm, SearchUserForm
from apps.kassa.utils import tekstmelding_new_membership_card, inside_update_card, inside_get_card, \
inside_update_membership, format_phone_number, is_autumn
from apps.kassa.utils import tekstmelding_new_membership_card, update_card, get_card, \
get_order, get_latest_order_by_card, get_latest_order_by_phone, get_user, get_user_by_phone, \
dusken_auth, update_membership, format_phone_number, is_autumn
logger = logging.getLogger(__name__)
@@ -29,18 +31,17 @@ def register(request):
@login_required
def inside_user_api(request):
def user_search(request):
payload = {
'apikey': settings.INSIDE_API_KEY,
'q': request.GET.get('q', '')
'search': request.GET.get('search', '')
}
url = '{}user.php'.format(settings.INSIDE_API_URL)
data = requests.get(url, params=payload).json()
url = '{}users/'.format(settings.DUSKEN_API_URL)
data = requests.get(url, params=payload, headers=dusken_auth).json()
return JsonResponse(data)
@login_required()
@login_required
def check_phone_number(request):
number = request.GET.get('phone_number', '').strip()
@@ -56,115 +57,128 @@ def check_phone_number(request):
return JsonResponse({'error': str(e).replace('(1) ', '')})
number = phonenumbers.format_number(p, phonenumbers.PhoneNumberFormat.E164)
# Inside lookup existing user
payload = {
'apikey': settings.INSIDE_API_KEY,
'q': number
}
url = '{}phonenumber.php'.format(settings.INSIDE_API_URL)
inside_data = requests.get(url, params=payload).json()
# Tekstmelding lookup pending memberships
payload = {
'api_key': settings.TEKSTMELDING_API_KEY,
'number': number
}
url = '{}kassa/pending-membership'.format(settings.TEKSTMELDING_API_URL)
tekstmelding_data = requests.get(url, params=payload).json()
user = get_user_by_phone(number)
order = get_latest_order_by_phone(number)
return JsonResponse({
'tekstmelding': tekstmelding_data,
'inside': inside_data
'user': user,
'order': order
})
@login_required
def inside_card_api(request):
def check_card(request):
if request.method != 'GET':
return JsonResponse({'error': 'Only method GET supported'})
response = inside_get_card(request.GET.get('card_number'))
return JsonResponse(response.json(), status=response.status_code)
card_number = request.GET.get('card_number')
response = get_card(card_number)
if response.status_code != 200:
return JsonResponse(response.json(), status=response.status_code)
card = response.json()
if card.get('user'):
card['user'] = get_user(card['user']).json()
card['order'] = None
if card.get('orders'):
card['order'] = get_latest_order_by_card(card_number)
card.pop('orders')
return JsonResponse(card, status=response.status_code)
@login_required()
@login_required
def register_card_and_membership(request):
if request.method != 'POST':
return JsonResponse({'error': 'Only method POST supported'})
post_data = json.loads(request.body.decode('utf-8'))
action = post_data.get('action') # new_card_membership, update_card, add_or_renew, sms_card_notify
# new_card_membership, update_card, add_or_renew, sms_card_notify
# TODO: multiple actions (to allow renewal only)
action = post_data.get('action')
user_id = post_data.get('user_id')
order_uuid = post_data.get('order_uuid')
card_number = post_data.get('card_number')
phone_number = post_data.get('phone_number')
membership_trial = post_data.get('membership_trial')
membership_type = 'trial' if membership_trial else 'standard'
response = inside_update_card(
card_number,
user_id,
post_data.get('phone_number'),
action,
membership_trial
)
if response.status_code != 200:
return JsonResponse(response.json(), status=response.status_code)
# Update card number on user or order
if action in ('update_card', 'sms_card_notify'):
if action == 'update_card' and user_id:
response = update_card(card_number, user_id=user_id)
elif action == 'sms_card_notify' and order_uuid:
response = update_card(card_number, order_uuid=order_uuid)
else:
assert user_id or order_uuid
if response.status_code != 200:
return JsonResponse(response.json(), status=response.status_code)
event_data = {
'event': KassaEvent.UPDATE_CARD,
'user_phone_number': format_phone_number(post_data.get('phone_number')),
'card_number': card_number
}
if user_id:
event_data.update({'user_inside_id': user_id})
event_data = {
'event': KassaEvent.UPDATE_CARD,
'user_phone_number': phone_number,
'card_number': card_number,
'user_dusken_id': user_id,
}
logger.debug(event_data)
KassaEvent.objects.create(**event_data)
logger.debug(event_data)
KassaEvent.objects.create(**event_data)
card_update_response = response.json()
# Add initial or renew membership for existing user or order
if action in ('add_or_renew', 'new_card_membership'):
transaction_id = uuid.uuid4()
response = update_membership(
user=user_id,
phone_number=phone_number,
card_number=card_number,
membership_type=membership_type,
transaction_id=str(transaction_id))
KassaEvent.objects.create(
event=KassaEvent.ADD_OR_RENEW if membership_type != 'trial' else KassaEvent.MEMBERSHIP_TRIAL,
user_dusken_id=user_id,
card_number=card_number,
user_phone_number=phone_number,
transaction_id=transaction_id
)
# Send activation notification (link) to user by SMS
if action == 'new_card_membership' or action == 'sms_card_notify':
card = card_update_response['card']
if action in ('new_card_membership', 'sms_card_notify'):
event = KassaEvent.NEW_CARD_MEMBERSHIP
if action == 'sms_card_notify':
event = KassaEvent.SMS_CARD_NOTIFY
elif event == KassaEvent.NEW_CARD_MEMBERSHIP and membership_trial is not None:
event = KassaEvent.MEMBERSHIP_TRIAL
# FIXME: could be async
tekstmelding_new_membership_card(card_number=card['card_number'], phone_number=card['owner_phone_number'])
tekstmelding_new_membership_card(card_number=card_number,
phone_number=phone_number)
KassaEvent.objects.create(
event=event,
card_number=card['card_number'],
user_phone_number=card['owner_phone_number']
)
# Add initial or renew membership for existing user
elif action == 'add_or_renew':
response = inside_update_membership(user_id, post_data.get('purchased'), membership_trial=membership_trial)
KassaEvent.objects.create(
event=KassaEvent.ADD_OR_RENEW if membership_trial is None else KassaEvent.MEMBERSHIP_TRIAL,
user_inside_id=user_id
card_number=card_number,
user_phone_number=phone_number
)
return JsonResponse(response.json(), status=response.status_code)
@login_required()
@login_required
def renew_membership(request):
if request.method != 'POST':
return JsonResponse({'error': 'Only method POST supported'})
post_data = json.loads(request.body.decode('utf-8'))
user_id = post_data.get('user_id')
phone_number = post_data.get('phone_number')
card_number = post_data.get('card_number')
membership_trial = post_data.get('membership_trial')
membership_type = 'trial' if membership_trial else 'standard'
transaction_id = str(uuid.uuid4())
response = inside_update_membership(user_id, purchased=post_data.get('purchased'), membership_trial=membership_trial)
response = update_membership(
user=user_id,
phone_number=phone_number,
card_number=card_number,
membership_type=membership_type,
transaction_id=transaction_id)
KassaEvent.objects.create(
event=KassaEvent.RENEW_ONLY if membership_trial is None else KassaEvent.MEMBERSHIP_TRIAL,
user_inside_id=user_id
user_dusken_id=user_id,
transaction_id=transaction_id
)
return JsonResponse(response.json(), status=response.status_code)