Make Kassa communicate with Dusken.
This commit is contained in:
1 parent
f400d7a295
commit
031a400855
21 files changed
+14731
-7321
No files matched your search
+4
-2
@@ -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)
|
||||
@@ -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),
|
||||
),
|
||||
]
|
||||
@@ -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)
|
||||
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
Reference in new issue
Block a user