From 1d863d79d379d086089efbca2f9f337afbb3993e Mon Sep 17 00:00:00 2001 From: Maximilien Cuony Date: Sat, 11 Jan 2014 19:42:23 +0100 Subject: [PATCH] IPN --- client/httpd.py | 9 +++++ client/libs/polybanking.py | 19 ++++++++++ server/app/settings.py | 9 +++++ server/app/settingsLocal.py.dist | 2 ++ server/data/pip-reqs.txt | 3 ++ server/paiements/tasks.py | 35 +++++++++++++++++++ .../paiements/transactions/list.html | 2 ++ .../paiements/transactions/show.html | 5 ++- server/paiements/urls.py | 2 ++ server/paiements/views.py | 27 +++++++++++--- 10 files changed, 107 insertions(+), 6 deletions(-) create mode 100644 server/paiements/tasks.py diff --git a/client/httpd.py b/client/httpd.py index bf1f26d..68487d5 100644 --- a/client/httpd.py +++ b/client/httpd.py @@ -26,10 +26,19 @@ def start(): return render_template('start.html', result=result, url=url) + @app.route('/back') def back(): return render_template('back.html', result='ok' in request.args) + +@app.route('/ipn', methods=['POST']) +def ipn(): + + print api.check_ipn(request.form) + + return '' + if __name__ == "__main__": app.run(debug=True) diff --git a/client/libs/polybanking.py b/client/libs/polybanking.py index 0a95d66..275bea9 100644 --- a/client/libs/polybanking.py +++ b/client/libs/polybanking.py @@ -1,5 +1,6 @@ import requests import hashlib +import datetime class PolyBanking(): @@ -45,3 +46,21 @@ def new_transation(self, amount, reference, extra_data=''): return (result['status'], result['url']) except: return ('ERROR', '') + + def check_ipn(self, post_data): + """Check if IPN data is valid. Return (is_ok, message, reference, status, status_good, last_update)""" + + data = {} + + #check sign + for key in post_data: + if key != 'sign': + data[key] = post_data[key] + + if post_data['sign'] != self.compute_sign(self.keyIPN, data): + return (False, 'SIGN', None, None, None, None) + + if post_data['config'] != self.config_id: + return (False, 'CONFIG', None, None, None, None) + + return (True, '', data['reference'], data['postfinance_status'], data['postfinance_status_good'] == 'True', datetime.datetime.strptime(data['last_update'][:-6], '%Y-%m-%d %H:%M:%S')) diff --git a/server/app/settings.py b/server/app/settings.py index 13d25a1..6fa7c9f 100644 --- a/server/app/settings.py +++ b/server/app/settings.py @@ -114,6 +114,7 @@ 'south', 'bootstrap3', + 'djcelery', 'main', 'users', @@ -161,6 +162,14 @@ TEQUILA_AUTOCREATE = True # Auto create users ? TEQUILA_FAILURE = '/' # Where to redirect user if there is a problem +import djcelery +djcelery.setup_loader() + +DATE_FORMAT = 'Y/d/m' +TIME_FORMAT = 'H:i:s' + +DATETIME_FORMAT = DATE_FORMAT + ' ' + TIME_FORMAT + try: from settingsLocal import * except ImportError: diff --git a/server/app/settingsLocal.py.dist b/server/app/settingsLocal.py.dist index 47880a5..0079811 100644 --- a/server/app/settingsLocal.py.dist +++ b/server/app/settingsLocal.py.dist @@ -30,3 +30,5 @@ PSPID_TEST = '' PSPID_PROD = '' CURRENCY = 'CHF' + +BROKER_URL = '' diff --git a/server/data/pip-reqs.txt b/server/data/pip-reqs.txt index b3a1e51..697a2df 100644 --- a/server/data/pip-reqs.txt +++ b/server/data/pip-reqs.txt @@ -1,2 +1,5 @@ south django-bootstrap3 +requests +celery +django-celery diff --git a/server/paiements/tasks.py b/server/paiements/tasks.py new file mode 100644 index 0000000..0ed0467 --- /dev/null +++ b/server/paiements/tasks.py @@ -0,0 +1,35 @@ +from celery import task + +import requests +from paiements.models import Transaction, TransactionLog + +from django.utils.timezone import now + +from libs.utils import compute_sign + + +@task(ignore_result=True) +def send_ipn(transactionPk): + """Send IPN about a transaction""" + + transaction = Transaction.objects.get(pk=transactionPk) + + if not transaction.config.active or not transaction.config.admin_enable: + return + + data = {'config': str(transaction.config.pk), 'reference': transaction.reference, 'postfinance_status': transaction.postfinance_status, 'postfinance_status_good': str(transaction.postfinance_status_good()), 'last_update': str(transaction.last_postfinance_ipn_date)} + data['sign'] = compute_sign(transaction.config.key_ipn, data) + + try: + error = requests.post(transaction.config.url_ipn, data=data).status_code != 200 + except: + error = True + + if not error: + transaction.last_ipn_date = now() + transaction.ipn_needed = False + transaction.save(update_fields=['last_ipn_date', 'ipn_needed']) + + TransactionLog(transaction=transaction, log_type='ipnsuccess', extra_data='').save() + else: + TransactionLog(transaction=transaction, log_type='ipnfailled', extra_data='').save() diff --git a/server/paiements/templates/paiements/transactions/list.html b/server/paiements/templates/paiements/transactions/list.html index f1f722e..0490fa1 100644 --- a/server/paiements/templates/paiements/transactions/list.html +++ b/server/paiements/templates/paiements/transactions/list.html @@ -44,6 +44,7 @@

{% trans "Amount" %} {% trans "Postfinance status" %} {% trans "Internal status" %} + {% trans "IPN Needed ?" %} {% trans "Date" %} @@ -55,6 +56,7 @@

{{elem.amount_chf|floatformat:"2"}} CHF {{elem.get_postfinance_status_display}} {{elem.get_internal_status_display}} + {{elem.ipn_needed|yesno}} {{elem.creation_date|date}} {{elem.creation_date|time}} ({{elem.creation_date|timesince}}) {% trans "Logs" %} diff --git a/server/paiements/templates/paiements/transactions/show.html b/server/paiements/templates/paiements/transactions/show.html index 7e0629d..1fe6ef5 100644 --- a/server/paiements/templates/paiements/transactions/show.html +++ b/server/paiements/templates/paiements/transactions/show.html @@ -62,7 +62,10 @@

{% trans "Details of a transaction" %}

-

{{object.ipn_needed|yesno}}

+

+ {{object.ipn_needed|yesno}} + {% trans "Force send" %} +

diff --git a/server/paiements/urls.py b/server/paiements/urls.py index e0d610d..3115d33 100644 --- a/server/paiements/urls.py +++ b/server/paiements/urls.py @@ -13,5 +13,7 @@ url(r'^transactions/list$', 'transactions_list'), url(r'^transactions/(?P[0-9]+)/$', 'transactions_show'), url(r'^transactions/(?P[0-9]+)/logs$', 'transactions_show_logs'), + url(r'^transactions/(?P[0-9]+)/ipn$', 'transactions_send_ipn'), + ) diff --git a/server/paiements/views.py b/server/paiements/views.py index 08188b6..8a6d73a 100644 --- a/server/paiements/views.py +++ b/server/paiements/views.py @@ -23,6 +23,7 @@ from configs.models import Config from paiements.models import Transaction, TransactionLog +from paiements.tasks import send_ipn from libs import utils from django.utils.timezone import now @@ -107,7 +108,7 @@ def go(request, pk): t.internal_status = 'fw' t.last_userforwarded_date = now() - t.save() + t.save(update_fields=['internal_status', 'last_userforwarded_date']) return render_to_response('paiements/go.html', {'fields': fields, 'urlDest': urlDest}, context_instance=RequestContext(request)) @@ -116,8 +117,6 @@ def go(request, pk): def ipn(request): """Call by Postfinance website about status""" - print request.POST - # Get transaction pk orderId = request.POST.get('orderID') @@ -154,10 +153,13 @@ def ipn(request): t.internal_status = 'fb' t.last_postfinance_ipn_date = now() t.postfinance_status = request.POST.get('STATUS') - t.save() + t.ipn_needed = True + t.save(update_fields=['internal_status', 'last_postfinance_ipn_date', 'postfinance_status', 'ipn_needed', 'postfinance_id']) TransactionLog(transaction=t, log_type='postfinanceStatus', extra_data=request.POST.get('STATUS')).save() + send_ipn.delay(t.pk) + return HttpResponse('') @@ -198,7 +200,7 @@ def return_from_postfinance(request): t.internal_status = 'fb' t.last_user_back_from_postfinance_date = now() - t.save() + t.save(update_fields=['internal_status', 'last_user_back_from_postfinance_date', 'postfinance_id']) TransactionLog(transaction=t, log_type='userBackFromPostfinance', extra_data=request.META['REMOTE_ADDR']).save() @@ -268,3 +270,18 @@ def transactions_show(request, pk): raise Http404 return render_to_response('paiements/transactions/show.html', {'object': object}, context_instance=RequestContext(request)) + + +@login_required +def transactions_send_ipn(request, pk): + + object = get_object_or_404(Transaction, pk=pk) + + if not request.user.is_superuser and not request.user in object.allowed_users: + raise Http404 + + send_ipn.delay(object.pk) + + messages.success(request, _('IPN has been queued !')) + + return redirect('paiements.views.transactions_show', object.pk)