diff --git a/client/.gitignore b/client/.gitignore new file mode 100644 index 0000000..f85c6b1 --- /dev/null +++ b/client/.gitignore @@ -0,0 +1 @@ +config.py \ No newline at end of file diff --git a/client/config.py.dist b/client/config.py.dist new file mode 100644 index 0000000..e69de29 diff --git a/client/httpd.py b/client/httpd.py new file mode 100644 index 0000000..bf1f26d --- /dev/null +++ b/client/httpd.py @@ -0,0 +1,35 @@ +from flask import Flask +from flask import render_template +from flask import request + +import config +from libs.polybanking import PolyBanking + +import uuid + +api = PolyBanking(config.POLYBANKING_SERVER, config.CONFIG_ID, config.KEY_REQUESTS, config.KEY_IPN, config.KEY_API) + +app = Flask(__name__) + + +@app.route("/") +def home(): + """Display the home page""" + return render_template('home.html') + + +@app.route('/start') +def start(): + """Start a new paiement""" + + (result, url) = api.new_transation(request.args.get('amount', ''), str(uuid.uuid4())) + + return render_template('start.html', result=result, url=url) + +@app.route('/back') +def back(): + + return render_template('back.html', result='ok' in request.args) + +if __name__ == "__main__": + app.run(debug=True) diff --git a/client/libs/__init__.py b/client/libs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/client/libs/polybanking.py b/client/libs/polybanking.py new file mode 100644 index 0000000..0a95d66 --- /dev/null +++ b/client/libs/polybanking.py @@ -0,0 +1,47 @@ +import requests +import hashlib + + +class PolyBanking(): + """Api for polybanking accesses""" + + def __init__(self, server, config_id, keyRequests, keyIPN, keyAPI): + self.server = server + self.config_id = config_id + self.keyRequests = keyRequests + self.keyIPN = keyIPN + self.keyAPI = keyAPI + + def compute_sign(self, secret, data): + """Compute the signature for a dict""" + + def escape_chars(s): + """Remove = and ; from a string""" + return s.replace(';', '!!').replace('=', '??') + + h = hashlib.sha512() + + for key, value in sorted(data.iteritems(), key=lambda (k, v): k): + h.update(escape_chars(key)) + h.update('=') + h.update(escape_chars(value)) + h.update(';') + h.update(secret) + h.update(';') + + return h.hexdigest() + + def new_transation(self, amount, reference, extra_data=''): + """Start a new transation, with the specified amount and reference. The reference must be unique. + Return (Status, the URL where the user should be redirected or None) + Status can be 'OK', 'KEY_ERROR', 'CONFIG_ERROR', 'AMOUNT_ERROR', 'REFERENCE_ERROR', 'ERROR'""" + + data = {'amount': amount, 'reference': reference, 'extra_data': extra_data, 'config_id': self.config_id} + + data['sign'] = self.compute_sign(self.keyRequests, data) + + try: + result = requests.post(self.server + '/paiements/start/', data=data).json() + return (result['status'], result['url']) + except: + return ('ERROR', '') diff --git a/client/templates/back.html b/client/templates/back.html new file mode 100644 index 0000000..6e8ebf8 --- /dev/null +++ b/client/templates/back.html @@ -0,0 +1,3 @@ +

PolyBanking Test/Demo client

+ +We're back. {% if result %}Status seem ok{% else %}Status seem err{%endif %} \ No newline at end of file diff --git a/client/templates/home.html b/client/templates/home.html new file mode 100644 index 0000000..bf34b5f --- /dev/null +++ b/client/templates/home.html @@ -0,0 +1,4 @@ +

PolyBanking Test/Demo client

+ +New payement, 100.-
+New payement, 10'000.-
\ No newline at end of file diff --git a/client/templates/start.html b/client/templates/start.html new file mode 100644 index 0000000..baceb99 --- /dev/null +++ b/client/templates/start.html @@ -0,0 +1,7 @@ +

PolyBanking Test/Demo client

+ +Result is {{result}}. + +User should go to {{url}}

+ +Home \ No newline at end of file diff --git a/server/app/settings.py b/server/app/settings.py index f02cf8b..13d25a1 100644 --- a/server/app/settings.py +++ b/server/app/settings.py @@ -118,6 +118,7 @@ 'main', 'users', 'configs', + 'paiements', ) SESSION_SERIALIZER = 'django.contrib.sessions.serializers.JSONSerializer' diff --git a/server/app/settingsLocal.py.dist b/server/app/settingsLocal.py.dist index 20dba43..47880a5 100644 --- a/server/app/settingsLocal.py.dist +++ b/server/app/settingsLocal.py.dist @@ -14,3 +14,19 @@ DATABASES = { # Make this unique, and don't share it with anybody. SECRET_KEY = '' + +SHA_IN_TEST = '' +SHA_OUT_TEST = '' + +SHA_IN_PROD = '' +SHA_OUT_PROD = '' + +EXTERNAL_BASE_URL = '' + +POSTFINANCE_TEST_URL = 'https://e-payment.postfinance.ch/ncol/test/orderstandard.asp' +POSTFINANCE_PROD_URL = 'https://e-payment.postfinance.ch/ncol/prod/orderstandard.asp' + +PSPID_TEST = '' +PSPID_PROD = '' + +CURRENCY = 'CHF' diff --git a/server/app/urls.py b/server/app/urls.py index 9b276a1..067ba23 100644 --- a/server/app/urls.py +++ b/server/app/urls.py @@ -23,6 +23,7 @@ url(r'', include('main.urls')), url(r'^users/', include('users.urls')), url(r'^configs/', include('configs.urls')), + url(r'^paiements/', include('paiements.urls')), (r'^' + settings.MEDIA_URL[1:] + '(?P.*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT}), # In prod, use apache ! (r'^' + settings.STATIC_URL[1:] + '(?P.*)$', 'django.views.static.serve', {'document_root': settings.STATIC_ROOT}), # In prod, use apache ! diff --git a/server/configs/models.py b/server/configs/models.py index d2c3340..fe65dfb 100644 --- a/server/configs/models.py +++ b/server/configs/models.py @@ -1,5 +1,6 @@ from django.db import models from django.contrib.auth.models import User +from django.utils.html import escape import uuid import datetime @@ -39,7 +40,7 @@ def __unicode__(self): if bonus: bonus = ' (' + bonus + ')' - return self.name + bonus + return escape(self.name) + bonus def build_user_list(self): """Return a list of user in text format""" diff --git a/server/configs/templates/configs/configs/show.html b/server/configs/templates/configs/configs/show.html index bc19199..f39c8fa 100644 --- a/server/configs/templates/configs/configs/show.html +++ b/server/configs/templates/configs/configs/show.html @@ -76,6 +76,11 @@

{% trans "Details of a config" %}

{{object.admin_enable|yesno}}

+
+ +

{{object.pk}}

+
+

diff --git a/server/configs/urls.py b/server/configs/urls.py index 85d0b78..c827e54 100644 --- a/server/configs/urls.py +++ b/server/configs/urls.py @@ -6,14 +6,14 @@ 'configs.views', url(r'^$', 'list'), - url(r'^(?P[0-9]?)/show/$', 'show'), - url(r'^(?P[0-9]?)/edit/$', 'edit'), - url(r'^(?P[0-9]?)/logs/$', 'show_logs'), + url(r'^(?P[0-9]+)/show/$', 'show'), + url(r'^(?P[0-9]+)/edit/$', 'edit'), + url(r'^(?P[0-9]+)/logs/$', 'show_logs'), - #url(r'^(?P[0-9]?)/delete/$', 'delete'), + #url(r'^(?P[0-9]+)/delete/$', 'delete'), - url(r'^(?P[0-9]?)/keys/ipn/new/$', 'new_ipn_key'), - url(r'^(?P[0-9]?)/keys/requests/new/$', 'new_requests_key'), - url(r'^(?P[0-9]?)/keys/api/new/$', 'new_api_key'), + url(r'^(?P[0-9]+)/keys/ipn/new/$', 'new_ipn_key'), + url(r'^(?P[0-9]+)/keys/requests/new/$', 'new_requests_key'), + url(r'^(?P[0-9]+)/keys/api/new/$', 'new_api_key'), ) diff --git a/server/configs/views.py b/server/configs/views.py index 789d9f6..5f9ceec 100644 --- a/server/configs/views.py +++ b/server/configs/views.py @@ -71,7 +71,7 @@ def edit(request, pk): messages.success(request, 'The config has been saved.') - return redirect(reverse('configs.views.list')) + return redirect('configs.views.list') else: form = ConfigForm(request.user, instance=object) @@ -122,7 +122,7 @@ def new_ipn_key(request, pk): messages.success(request, 'A new IPN key has been generated !') - return redirect(reverse('configs.views.show', args=(pk,))) + return redirect('configs.views.show', pk=pk) @login_required @@ -141,7 +141,7 @@ def new_requests_key(request, pk): messages.success(request, 'A new requests key has been generated !') - return redirect(reverse('configs.views.show', args=(pk,))) + return redirect('configs.views.show', pk=pk) @login_required @@ -160,7 +160,7 @@ def new_api_key(request, pk): messages.success(request, 'A new api key has been generated !') - return redirect(reverse('configs.views.show', args=(pk,))) + return redirect('configs.views.show', pk=pk) @login_required diff --git a/server/libs/__init__.py b/server/libs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/server/libs/postfinance.py b/server/libs/postfinance.py new file mode 100644 index 0000000..dddbbc3 --- /dev/null +++ b/server/libs/postfinance.py @@ -0,0 +1,47 @@ +import requests +import hashlib +from django.conf import settings + + +class PostFinance(): + """Api for postfinance""" + + def __init__(self, SHA_IN, SHA_OUT, PSPID, testMode=False): + self.SHA_IN = SHA_IN + self.SHA_OUT = SHA_OUT + self.PSPID = PSPID + self.testMode = testMode + + def computeSign(self, secret, data): + """Compute a SHA signature following PostFinance's protocol""" + + h = hashlib.sha512() + + for key, value in sorted(data.iteritems(), key=lambda (k, v): k): + h.update(key) + h.update('=') + h.update(value) + h.update(secret) + + return h.hexdigest() + + def computeOutSign(self, data): + """Compute a SHA signature following PostFinance's protocol using OUT key""" + return self.computeSign(self.SHA_OUT, data) + + def computeInSign(self, data): + """Compute a SHA signature following PostFinance's protocol using IN key""" + return self.computeSign(self.SHA_IN, data) + + def getPspIp(self): + """Return the pspId""" + return self.PSPID + + +def buildPostFinance(testMode=False): + """Return a postfinance object with correct parameters""" + + if testMode: + return PostFinance(settings.SHA_IN_TEST, settings.SHA_OUT_TEST, settings.PSPID_TEST, True) + else: + return PostFinance(settings.SHA_IN_PROD, settings.SHA_OUT_PROD, settings.PSPID_PROD, False) diff --git a/server/libs/utils.py b/server/libs/utils.py new file mode 100644 index 0000000..300182a --- /dev/null +++ b/server/libs/utils.py @@ -0,0 +1,21 @@ +import hashlib + + +def compute_sign(secret, data): + """Compute the signature for a dict""" + + def escape_chars(s): + """Remove = and ; from a string""" + return s.replace(';', '!!').replace('=', '??') + + h = hashlib.sha512() + + for key, value in sorted(data.iteritems(), key=lambda (k, v): k): + h.update(escape_chars(key)) + h.update('=') + h.update(escape_chars(value)) + h.update(';') + h.update(secret) + h.update(';') + + return h.hexdigest() diff --git a/server/main/models.py b/server/main/models.py index 71a8362..e69de29 100644 --- a/server/main/models.py +++ b/server/main/models.py @@ -1,3 +0,0 @@ -from django.db import models - -# Create your models here. diff --git a/server/paiements/migrations/0001_initial.py b/server/paiements/migrations/0001_initial.py new file mode 100644 index 0000000..bf2c5f1 --- /dev/null +++ b/server/paiements/migrations/0001_initial.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + pass + + def backwards(self, orm): + pass + + models = { + + } + + complete_apps = ['paiements'] \ No newline at end of file diff --git a/server/paiements/migrations/0002_initial.py b/server/paiements/migrations/0002_initial.py new file mode 100644 index 0000000..878eda9 --- /dev/null +++ b/server/paiements/migrations/0002_initial.py @@ -0,0 +1,128 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'Transaction' + db.create_table(u'paiements_transaction', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('config', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['configs.Config'])), + ('reference', self.gf('django.db.models.fields.CharField')(max_length=255)), + ('extra_data', self.gf('django.db.models.fields.TextField')(blank=True)), + ('amount', self.gf('django.db.models.fields.IntegerField')()), + ('postfinance_id', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)), + ('postfinance_status', self.gf('django.db.models.fields.CharField')(default='??', max_length=2)), + ('internal_status', self.gf('django.db.models.fields.CharField')(default='cr', max_length=2)), + ('ipn_needed', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('creation_date', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), + ('last_userforwarded_date', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)), + ('last_user_back_from_postfinance_date', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)), + ('last_postfinance_ipn_date', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)), + ('last_ipn_date', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)), + )) + db.send_create_signal(u'paiements', ['Transaction']) + + # Adding model 'TransctionLog' + db.create_table(u'paiements_transctionlog', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('transaction', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['paiements.Transaction'])), + ('when', self.gf('django.db.models.fields.DateTimeField')()), + ('extra_data', self.gf('django.db.models.fields.TextField')()), + ('log_type', self.gf('django.db.models.fields.CharField')(max_length=64)), + )) + db.send_create_signal(u'paiements', ['TransctionLog']) + + + def backwards(self, orm): + # Deleting model 'Transaction' + db.delete_table(u'paiements_transaction') + + # Deleting model 'TransctionLog' + db.delete_table(u'paiements_transctionlog') + + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'configs.config': { + 'Meta': {'object_name': 'Config'}, + 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'admin_enable': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'allowed_users': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.User']", 'symmetrical': 'False', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key_api': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'key_ipn': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'key_request': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'test_mode': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'url_back_err': ('django.db.models.fields.URLField', [], {'max_length': '200'}), + 'url_back_ok': ('django.db.models.fields.URLField', [], {'max_length': '200'}), + 'url_ipn': ('django.db.models.fields.URLField', [], {'max_length': '200'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'paiements.transaction': { + 'Meta': {'object_name': 'Transaction'}, + 'amount': ('django.db.models.fields.IntegerField', [], {}), + 'config': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['configs.Config']"}), + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'extra_data': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'internal_status': ('django.db.models.fields.CharField', [], {'default': "'cr'", 'max_length': '2'}), + 'ipn_needed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_ipn_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'last_postfinance_ipn_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'last_user_back_from_postfinance_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'last_userforwarded_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'postfinance_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'postfinance_status': ('django.db.models.fields.CharField', [], {'default': "'??'", 'max_length': '2'}), + 'reference': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + u'paiements.transctionlog': { + 'Meta': {'object_name': 'TransctionLog'}, + 'extra_data': ('django.db.models.fields.TextField', [], {}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'log_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'transaction': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['paiements.Transaction']"}), + 'when': ('django.db.models.fields.DateTimeField', [], {}) + } + } + + complete_apps = ['paiements'] \ No newline at end of file diff --git a/server/paiements/migrations/0003_auto__chg_field_transaction_extra_data.py b/server/paiements/migrations/0003_auto__chg_field_transaction_extra_data.py new file mode 100644 index 0000000..b059e9d --- /dev/null +++ b/server/paiements/migrations/0003_auto__chg_field_transaction_extra_data.py @@ -0,0 +1,99 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Changing field 'Transaction.extra_data' + db.alter_column(u'paiements_transaction', 'extra_data', self.gf('django.db.models.fields.TextField')(null=True)) + + def backwards(self, orm): + + # Changing field 'Transaction.extra_data' + db.alter_column(u'paiements_transaction', 'extra_data', self.gf('django.db.models.fields.TextField')(default='')) + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'configs.config': { + 'Meta': {'object_name': 'Config'}, + 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'admin_enable': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'allowed_users': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.User']", 'symmetrical': 'False', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key_api': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'key_ipn': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'key_request': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'test_mode': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'url_back_err': ('django.db.models.fields.URLField', [], {'max_length': '200'}), + 'url_back_ok': ('django.db.models.fields.URLField', [], {'max_length': '200'}), + 'url_ipn': ('django.db.models.fields.URLField', [], {'max_length': '200'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'paiements.transaction': { + 'Meta': {'object_name': 'Transaction'}, + 'amount': ('django.db.models.fields.IntegerField', [], {}), + 'config': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['configs.Config']"}), + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'extra_data': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'internal_status': ('django.db.models.fields.CharField', [], {'default': "'cr'", 'max_length': '2'}), + 'ipn_needed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_ipn_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'last_postfinance_ipn_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'last_user_back_from_postfinance_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'last_userforwarded_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'postfinance_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'postfinance_status': ('django.db.models.fields.CharField', [], {'default': "'??'", 'max_length': '2'}), + 'reference': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + u'paiements.transctionlog': { + 'Meta': {'object_name': 'TransctionLog'}, + 'extra_data': ('django.db.models.fields.TextField', [], {}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'log_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'transaction': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['paiements.Transaction']"}), + 'when': ('django.db.models.fields.DateTimeField', [], {}) + } + } + + complete_apps = ['paiements'] \ No newline at end of file diff --git a/server/paiements/migrations/0004_auto__del_transctionlog__add_transactionlog.py b/server/paiements/migrations/0004_auto__del_transctionlog__add_transactionlog.py new file mode 100644 index 0000000..0aa5029 --- /dev/null +++ b/server/paiements/migrations/0004_auto__del_transctionlog__add_transactionlog.py @@ -0,0 +1,119 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Deleting model 'TransctionLog' + db.delete_table(u'paiements_transctionlog') + + # Adding model 'TransactionLog' + db.create_table(u'paiements_transactionlog', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('transaction', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['paiements.Transaction'])), + ('when', self.gf('django.db.models.fields.DateTimeField')()), + ('extra_data', self.gf('django.db.models.fields.TextField')()), + ('log_type', self.gf('django.db.models.fields.CharField')(max_length=64)), + )) + db.send_create_signal(u'paiements', ['TransactionLog']) + + + def backwards(self, orm): + # Adding model 'TransctionLog' + db.create_table(u'paiements_transctionlog', ( + ('transaction', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['paiements.Transaction'])), + ('when', self.gf('django.db.models.fields.DateTimeField')()), + ('log_type', self.gf('django.db.models.fields.CharField')(max_length=64)), + ('extra_data', self.gf('django.db.models.fields.TextField')()), + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + )) + db.send_create_signal(u'paiements', ['TransctionLog']) + + # Deleting model 'TransactionLog' + db.delete_table(u'paiements_transactionlog') + + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'configs.config': { + 'Meta': {'object_name': 'Config'}, + 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'admin_enable': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'allowed_users': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.User']", 'symmetrical': 'False', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key_api': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'key_ipn': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'key_request': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'test_mode': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'url_back_err': ('django.db.models.fields.URLField', [], {'max_length': '200'}), + 'url_back_ok': ('django.db.models.fields.URLField', [], {'max_length': '200'}), + 'url_ipn': ('django.db.models.fields.URLField', [], {'max_length': '200'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'paiements.transaction': { + 'Meta': {'object_name': 'Transaction'}, + 'amount': ('django.db.models.fields.IntegerField', [], {}), + 'config': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['configs.Config']"}), + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'extra_data': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'internal_status': ('django.db.models.fields.CharField', [], {'default': "'cr'", 'max_length': '2'}), + 'ipn_needed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_ipn_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'last_postfinance_ipn_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'last_user_back_from_postfinance_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'last_userforwarded_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'postfinance_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'postfinance_status': ('django.db.models.fields.CharField', [], {'default': "'??'", 'max_length': '2'}), + 'reference': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + u'paiements.transactionlog': { + 'Meta': {'object_name': 'TransactionLog'}, + 'extra_data': ('django.db.models.fields.TextField', [], {}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'log_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'transaction': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['paiements.Transaction']"}), + 'when': ('django.db.models.fields.DateTimeField', [], {}) + } + } + + complete_apps = ['paiements'] \ No newline at end of file diff --git a/server/paiements/migrations/__init__.py b/server/paiements/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/server/paiements/models.py b/server/paiements/models.py index 71a8362..6dc77d2 100644 --- a/server/paiements/models.py +++ b/server/paiements/models.py @@ -1,3 +1,112 @@ from django.db import models -# Create your models here. +from configs.models import Config + + +class Transaction(models.Model): + """Represent one transation""" + + config = models.ForeignKey(Config) + + reference = models.CharField(max_length=255) + extra_data = models.TextField(blank=True, null=True) + + amount = models.IntegerField() + + postfinance_id = models.CharField(max_length=255, blank=True, null=True) + + POSTFINANCE_STATUS = ( + + ('??', 'Unknow'), + ('0', 'Invalid or incomplete'), + ('1', 'Cancelled by customer'), + ('2', 'Authorisation declined'), + ('4', 'Order stored'), + ('40', 'Stored waiting external result'), + ('41', 'Waiting for client payment'), + ('5', 'Authorised'), + ('50', 'Authorized waiting external result'), + ('51', 'Authorisation waiting'), + ('52', 'Authorisation not known'), + ('55', 'Standby'), + ('56', 'OK with scheduled payments'), + ('57', 'Not OK with scheduled payments'), + ('59', 'Authoris. to be requested manually'), + ('6', 'Authorised and cancelled'), + ('61', 'Author. deletion waiting'), + ('62', 'Author. deletion uncertain'), + ('63', 'Author. deletion refused'), + ('64', 'Authorised and cancelled'), + ('7', 'Payment deleted'), + ('71', 'Payment deletion pending'), + ('72', 'Payment deletion uncertain'), + ('73', 'Payment deletion refused'), + ('74', 'Payment deleted'), + ('75', 'Deletion handled by merchant'), + ('8', 'Refund'), + ('81', 'Refund pending'), + ('82', 'Refund uncertain'), + ('83', 'Refund refused'), + ('84', 'Refund'), + ('85', 'Refund handled by merchant'), + ('9', 'Payment requested'), + ('91', 'Payment processing'), + ('92', 'Payment uncertain'), + ('93', 'Payment refused'), + ('94', 'Refund declined by the acquirer'), + ('95', 'Payment handled by merchant'), + ('96', 'Refund reversed'), + ('99', 'Being processed'), + + ) + + postfinance_status = models.CharField(max_length=2, choices=POSTFINANCE_STATUS, default='??') + + INTERNAL_STATUS = ( + ('cr', 'Transation created'), + ('fw', 'User forwarded to PostFinance'), + ('fb', 'Feedback from PostFinance'), + ) + + internal_status = models.CharField(max_length=2, choices=INTERNAL_STATUS, default='cr') + + ipn_needed = models.BooleanField(default=False) + + creation_date = models.DateTimeField(auto_now_add=True) + last_userforwarded_date = models.DateTimeField(blank=True, null=True) + last_user_back_from_postfinance_date = models.DateTimeField(blank=True, null=True) + last_postfinance_ipn_date = models.DateTimeField(blank=True, null=True) + last_ipn_date = models.DateTimeField(blank=True, null=True) + + def amount_chf(self): + """Return the amount in CHF""" + return self.amount / 100.0 + + def postfinance_status_good(self): + """Return true if the status of the transaction is good (valid)""" + return self.postfinance_status in ('5', '9') + + def internal_status_good(self): + """Return true if the internal status of the transaction if good (user back from postfinance)""" + return self.internal_status == 'fb' + + +class TransactionLog(models.Model): + """A transaction log""" + + transaction = models.ForeignKey(Transaction) + when = models.DateTimeField(auto_now_add=True) + extra_data = models.TextField() + + LOG_TYPE = ( + ('created', 'Transaction created'), + ('userForwarded', 'User forwarded'), + ('userBackFromPostfinance', 'User back from postfinance'), + ('postfinanceId', 'Postfinance ID set'), + ('postfinanceStatus', 'Postfinance status changed'), + ('ipnfailled', 'IPN Failled'), + ('ipnsuccess', 'IPN Success'), + + ) + + log_type = models.CharField(max_length=64, choices=LOG_TYPE) diff --git a/server/paiements/templates/paiements/go.html b/server/paiements/templates/paiements/go.html new file mode 100644 index 0000000..2249132 --- /dev/null +++ b/server/paiements/templates/paiements/go.html @@ -0,0 +1,26 @@ +{% load i18n %} + + + PolyBanking + + + + +

PolyBanking

+ + {% trans "You are being redirect to the PostFinance's website. Please wait :)" %} + +
+ {% for key,value in fields.items %} + + {% endfor %} + + +
+ + + + + \ No newline at end of file diff --git a/server/paiements/templates/paiements/transactions/list.html b/server/paiements/templates/paiements/transactions/list.html new file mode 100644 index 0000000..e76f59a --- /dev/null +++ b/server/paiements/templates/paiements/transactions/list.html @@ -0,0 +1,85 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block title %}{{block.super}} :: {% trans "Transactions" %} :: {% trans "List" %}{% endblock %} + +{% block content %} + + + +

{% trans "Configs" %}

+ + + +
+
+
+

+ {% if configPk == 'all' %} + {% trans "List of all transactions" %} + {% else %} + {% trans "List of transactions" %} ({{config|safe}}) + {% endif %} +

+
+
+ + + + + + + + + + + + + {% for elem in list %} + + + + + + + + + {% endfor %} + + +
{% trans "Reference" %}{% trans "Amount" %}{% trans "Postfinance status" %}{% trans "Internal status" %}{% trans "Date" %}
{{elem.reference}}{{elem.amount_chf|floatformat:"2"}} CHF{{elem.get_postfinance_status_display}}{{elem.get_internal_status_display}}{{elem.creation_date|date}} {{elem.creation_date|time}} ({{elem.creation_date|timesince}}) + {% trans "Logs" %} +
+ + {% if user.is_superuser or user.is_staff %} + + {% endif %} + +
+
+ + + +
+ + + +{% endblock %} \ No newline at end of file diff --git a/server/paiements/urls.py b/server/paiements/urls.py new file mode 100644 index 0000000..be157fb --- /dev/null +++ b/server/paiements/urls.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- + +from django.conf.urls import patterns, url + +urlpatterns = patterns( + 'paiements.views', + + url(r'^start/$', 'start'), + url(r'^go/(?P[0-9]+)$', 'go'), + url(r'^ipn$', 'ipn'), + url(r'^return$', 'return_from_postfinance'), + + url(r'^transactions/list$', 'transactions_list'), + +) diff --git a/server/paiements/views.py b/server/paiements/views.py index 60f00ef..ed7f2e5 100644 --- a/server/paiements/views.py +++ b/server/paiements/views.py @@ -1 +1,239 @@ -# Create your views here. +# -*- coding: utf-8 -*- + +from django.shortcuts import get_object_or_404, render_to_response, redirect +from django.template import RequestContext +from django.core.context_processors import csrf +from django.views.decorators.csrf import csrf_exempt +from django.http import Http404, HttpResponse, HttpResponseForbidden, HttpResponseNotFound +from django.utils.encoding import smart_str +from django.conf import settings +from django.contrib.admin.views.decorators import staff_member_required +from django.contrib.auth.decorators import login_required, user_passes_test +from django.http import HttpResponseRedirect +from django.db import connections +from django.core.paginator import InvalidPage, EmptyPage, Paginator +from django.core.cache import cache +from django.core.urlresolvers import reverse +from django.contrib import messages + +import json +import uuid +from libs.postfinance import buildPostFinance + +from configs.models import Config +from paiements.models import Transaction, TransactionLog +from libs import utils + +from django.utils.timezone import now + + +@csrf_exempt +def start(request): + """Start a new request""" + + def build_error(status): + """Build an error response""" + return HttpResponse(json.dumps({'status': status, 'url': ''})) + + # Get config + try: + config = Config.objects.get(pk=request.POST.get('config_id'), active=True, admin_enable=True) + except Config.DoesNotExist: + return build_error('CONFIG_ERROR') + + # Check signature + data = {} + + for key in ('amount', 'reference', 'extra_data', 'config_id'): + data[key] = request.POST.get(key, '') + + if utils.compute_sign(config.key_request, data) != request.POST.get('sign'): + return build_error('KEY_ERROR') + + # Check if amount > 0 + try: + amount = int(request.POST.get('amount')) + except: + return build_error('AMOUNT_ERROR') + + if amount <= 0: + return build_error('AMOUNT_ERROR') + + # Check if reference is unique + reference = request.POST.get('reference') + + if config.transaction_set.filter(reference=reference).exists(): + return build_error('REFERENCE_ERROR') + + # Create transaction + extra_data = request.POST.get('extraData', '') + + t = Transaction(config=config, reference=reference, extra_data=extra_data, amount=amount) + t.save() + + # Log it + TransactionLog(transaction=t, log_type='created').save() + + return HttpResponse(json.dumps({'status': 'OK', 'url': settings.EXTERNAL_BASE_URL + reverse('paiements.views.go', args=(t.pk,))})) + + +def go(request, pk): + """Redirect the user to the postfinance website""" + + # Get transaction + t = get_object_or_404(Transaction, pk=pk, config__active=True, config__admin_enable=True) + + if t.internal_status == 'fb': + raise Http404 + + postFinance = buildPostFinance(t.config.test_mode) + + fields = { + 'AMOUNT': str(t.amount), + 'ORDERID': 'polybanking-' + str(t.pk), + 'PSPID': postFinance.getPspIp(), + 'CURRENCY': 'CHF', + } + + fields['SHASIGN'] = postFinance.computeInSign(fields) + + if t.config.test_mode: + urlDest = settings.POSTFINANCE_TEST_URL + else: + urlDest = settings.POSTFINANCE_PROD_URL + + TransactionLog(transaction=t, log_type='userForwarded', extra_data=request.META['REMOTE_ADDR']).save() + + t.internal_status = 'fw' + t.last_userforwarded_date = now() + t.save() + + return render_to_response('paiements/go.html', {'fields': fields, 'urlDest': urlDest}, context_instance=RequestContext(request)) + + +@csrf_exempt +def ipn(request): + """Call by Postfinance website about status""" + + print request.POST + + # Get transaction pk + orderId = request.POST.get('orderID') + + (who, pk) = orderId.split('-', 2) + + if who != 'polybanking': + raise Http404 + + # Get transaction + t = get_object_or_404(Transaction, pk=pk, config__active=True, config__admin_enable=True) + + if t.internal_status == 'cr': + raise Http404 + + postFinance = buildPostFinance(t.config.test_mode) + + # Check sign + args = {} + + for a in request.POST: + if a != 'SHASIGN': + val = request.POST.get(a) + if val: + args[a.upper()] = val + + if request.POST.get('SHASIGN').upper() != postFinance.computeOutSign(args).upper(): + raise Http404 + + # Let's catch the ID + if not t.postfinance_id: + t.postfinance_id = request.POST.get('PAYID') + TransactionLog(transaction=t, log_type='postfinanceId', extra_data=request.META['REMOTE_ADDR']).save() + + t.internal_status = 'fb' + t.last_postfinance_ipn_date = now() + t.postfinance_status = request.POST.get('STATUS') + t.save() + + TransactionLog(transaction=t, log_type='postfinanceStatus', extra_data=request.POST.get('STATUS')).save() + + return HttpResponse('') + + +@csrf_exempt +def return_from_postfinance(request): + """Client is returnting from postfinance""" + + # Get transaction pk + orderId = request.GET.get('orderID') + + (who, pk) = orderId.split('-', 2) + + if who != 'polybanking': + raise Http404 + + # Get transaction + t = get_object_or_404(Transaction, pk=pk, config__active=True, config__admin_enable=True) + + if t.internal_status == 'cr': + raise Http404 + + postFinance = buildPostFinance(t.config.test_mode) + + # Check sign + args = {} + + for a in request.GET: + if a != 'SHASIGN': + args[a.upper()] = request.GET.get(a) + + if request.GET.get('SHASIGN').upper() != postFinance.computeOutSign(args).upper(): + raise Http404 + + # Let's catch the ID + if not t.postfinance_id: + t.postfinance_id = request.GET.get('PAYID') + TransactionLog(transaction=t, log_type='postfinanceId', extra_data=request.META['REMOTE_ADDR']).save() + + t.internal_status = 'fb' + t.last_user_back_from_postfinance_date = now() + t.save() + + TransactionLog(transaction=t, log_type='userBackFromPostfinance', extra_data=request.META['REMOTE_ADDR']).save() + + # 9 or 5 are good signs + if request.GET.get('STATUS') in ['9', '5']: + return HttpResponseRedirect(t.config.url_back_ok) + else: + return HttpResponseRedirect(t.config.url_back_err) + + +@login_required +def transactions_list(request): + """Show the transactions list""" + + configPk = request.GET.get('configPk', 'NothingSelected') + + if request.user.is_superuser: + available_configs = Config.objects + else: + available_configs = Config.objects.filter(allowed_users=request.user) + + available_configs = available_configs.order_by('name').all() + + if configPk == 'NothingSelected': + if request.user.is_superuser: + configPk = 'all' + elif len(available_configs) > 0: + configPk = available_configs[0] + + if configPk != 'all' or not request.user.is_superuser: + config = get_object_or_404(Config, pk=configPk) + transactions = Transaction.objects.filter(config=config) + else: + transactions = Transaction.objects + config = None + + transactions = transactions.order_by('-creation_date').all() + + return render_to_response('paiements/transactions/list.html', {'list': transactions, 'available_configs': available_configs, 'configPk': configPk, 'config': config}, context_instance=RequestContext(request)) diff --git a/server/templates/base.html b/server/templates/base.html index 3d5e613..f675e24 100644 --- a/server/templates/base.html +++ b/server/templates/base.html @@ -51,7 +51,8 @@