From 1b98c01f6347cf27dc3fec319c40acf4548b5520 Mon Sep 17 00:00:00 2001 From: Thomas Steen Rasmussen Date: Mon, 3 Apr 2017 18:00:25 +0200 Subject: [PATCH] improved error handling, always save callback body, various fixes --- .../migrations/0039_auto_20170403_1752.py | 26 +++++++++++++++ src/shop/models.py | 3 +- src/shop/views.py | 32 +++++++++++++------ 3 files changed, 51 insertions(+), 10 deletions(-) create mode 100644 src/shop/migrations/0039_auto_20170403_1752.py diff --git a/src/shop/migrations/0039_auto_20170403_1752.py b/src/shop/migrations/0039_auto_20170403_1752.py new file mode 100644 index 00000000..9be91371 --- /dev/null +++ b/src/shop/migrations/0039_auto_20170403_1752.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.5 on 2017-04-03 15:52 +from __future__ import unicode_literals + +import django.contrib.postgres.fields.jsonb +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('shop', '0038_auto_20170323_2021'), + ] + + operations = [ + migrations.AddField( + model_name='coinifyapicallback', + name='body', + field=models.TextField(default=''), + ), + migrations.AlterField( + model_name='coinifyapicallback', + name='payload', + field=django.contrib.postgres.fields.jsonb.JSONField(null=True), + ), + ] diff --git a/src/shop/models.py b/src/shop/models.py index 167c67ab..86fe0f95 100644 --- a/src/shop/models.py +++ b/src/shop/models.py @@ -410,7 +410,8 @@ class CoinifyAPIInvoice(CreatedUpdatedModel): class CoinifyAPICallback(CreatedUpdatedModel): headers = JSONField() - payload = JSONField() + payload = JSONField(null=True) + body = models.TextField(default='') order = models.ForeignKey('shop.Order') valid = models.BooleanField(default=False) diff --git a/src/shop/views.py b/src/shop/views.py index baff7117..67ac9978 100644 --- a/src/shop/views.py +++ b/src/shop/views.py @@ -537,7 +537,7 @@ class CoinifyRedirectView(LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsureUn # this coinifyinvoice expired, delete it logger.warning("deleting expired coinifyinvoice id %s" % order.coinifyapiinvoice.invoicejson['id']) order.coinifyapiinvoice.delete() - order = self.get_object() + order.refresh_from_db() # create a new coinify invoice if needed if not hasattr(order, 'coinifyapiinvoice'): @@ -601,38 +601,52 @@ class CoinifyCallbackView(SingleObjectMixin, View): if key[:5] == 'HTTP_': headerdict[key[5:]] = value + # parse json + try: + parsed = json.loads(str(request.body)) + except JSONDecodeError: + parsed = None + # save callback to db callbackobject = CoinifyAPICallback.objects.create( headers=headerdict, - payload=json.loads(str(request.body)), + body=request.body, + payload=parsed, order=self.get_object() ) + + # do we have a json body? + if not parsed: + # no, return an error + logger.error("unable to parse JSON body in callback for order %s" % callbackobject.order.id) + return HttpResponseBadRequest('unable to parse json') + + # attemt to validate the callbackc if sdk.validate_callback(request.body, signature): # mark callback as valid in db callbackobject.valid=True callbackobject.save() - # parse json - callbackjson = json.loads(str(request.body)) - if callbackjson['event'] == 'invoice_state_change' or callbackjson['event'] == 'invoice_manual_resend': + if callbackobject.payload['event'] == 'invoice_state_change' or callbackobject.payload['event'] == 'invoice_manual_resend': # find coinify invoice in db try: - coinifyinvoice = CoinifyAPIInvoice.objects.get(invoicejson__id=callbackjson['data']['id']) + coinifyinvoice = CoinifyAPIInvoice.objects.get(invoicejson__id=callbackobject.payload['data']['id']) except CoinifyAPIInvoice.DoesNotExist: - logger.error("unable to find CoinifyAPIInvoice with id %s" % callbackjson['data']['id']) + logger.error("unable to find CoinifyAPIInvoice with id %s" % callbackobject.payload['data']['id']) return HttpResponseBadRequest('bad coinifyinvoice id') # save new coinifyinvoice payload - coinifyinvoice.invoicejson = callbackjson['data'] + coinifyinvoice.invoicejson = callbackobject.payload['data'] coinifyinvoice.save() # so, is the order paid in full now? - if callbackjson['data']['state'] == 'complete': + if callbackobject.payload['data']['state'] == 'complete': coinifyinvoice.order.mark_as_paid() # return 200 OK return HttpResponse('OK') else: + logger.error("unsupported callback event %s" % callbackobject.payload['event']) return HttpResponseBadRequest('unsupported event') else: logger.error("invalid coinify callback detected")