Project

General

Profile

0001-lingo-add-support-for-async-payment-notification-in-.patch

Frédéric Péters, 17 Aug 2017 09:20 AM

Download (5.69 KB)

View differences:

Subject: [PATCH] lingo: add support for async payment notification in case of
 errors (#6638)

 .../lingo/management/commands/notify_payments.py   | 11 +++--
 combo/apps/lingo/views.py                          |  6 +--
 tests/test_lingo_payment.py                        | 53 ++++++++++++++++++++++
 3 files changed, 64 insertions(+), 6 deletions(-)
combo/apps/lingo/management/commands/notify_payments.py
17 17
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 18

  
19 19
import logging
20
import datetime
20 21

  
21 22
from django.core.management.base import BaseCommand
23
from django.utils import timezone
22 24

  
23 25
from combo.apps.lingo.models import BasketItem
24 26

  
......
27 29

  
28 30
    def handle(self, *args, **kwargs):
29 31
        logger = logging.getLogger(__name__)
30
        for item in BasketItem.objects.filter(payment_date__isnull=False,
31
                notification_date__isnull=True):
32
        now = timezone.now()
33
        for item in BasketItem.objects.filter(
34
                notification_date__isnull=True,
35
                cancellation_date__isnull=True,
36
                payment_date__lt=now-datetime.timedelta(minutes=5)):
32 37
            try:
33 38
                item.notify_payment()
34 39
            except:
35
                logger.exception('error notifying payment for basket item %s', item.id)
40
                logger.exception('error in async notification for basket item %s', item.id)
combo/apps/lingo/views.py
436 436
            item.save()
437 437
            try:
438 438
                item.notify_payment()
439
            except RuntimeError:
440
                # ignore errors, it should be retried later on if it fails
441
                pass
439
            except:
440
                # ignore errors, it will be retried later on if it fails
441
                logger.exception('error in sync notification for basket item %s', item.id)
442 442
        regie.compute_extra_fees(user=transaction.user)
443 443
        if transaction.remote_items:
444 444
            transaction.first_notify_remote_items_of_payments()
tests/test_lingo_payment.py
21 21
from combo.apps.lingo.models import (Regie, BasketItem, Transaction,
22 22
        TransactionOperation, RemoteItem, EXPIRED, LingoBasketCell)
23 23
from combo.apps.lingo.management.commands.update_transactions import Command as UpdateTransactionsCommand
24
from combo.apps.lingo.management.commands.notify_payments import Command as NotifyPaymentsCommand
24 25
from combo.utils import sign_url
25 26

  
26 27
pytestmark = pytest.mark.django_db
......
573 574
    resp = client.get(callback_url, data)
574 575
    assert resp.status_code == 200
575 576
    assert Transaction.objects.get(order_id=transaction_id).status == 3
577

  
578
def test_payment_callback_error(regie, user):
579
    item = BasketItem.objects.create(user=user, regie=regie,
580
                        subject='test_item', amount='10.5',
581
                        source_url='http://example.org/testitem/')
582
    login()
583
    resp = client.post(reverse('lingo-pay'), {'regie': regie.pk})
584
    assert resp.status_code == 302
585
    location = resp.get('location')
586
    parsed = urlparse.urlparse(location)
587
    qs = urlparse.parse_qs(parsed.query)
588
    transaction_id = qs['transaction_id'][0]
589
    data = {'transaction_id': transaction_id, 'signed': True,
590
            'amount': qs['amount'][0], 'ok': True}
591
    assert data['amount'] == '10.50'
592

  
593
    # call callback with GET
594
    callback_url = reverse('lingo-callback', kwargs={'regie_pk': regie.id})
595
    with mock.patch('combo.utils.RequestsSession.request') as request:
596
        mock_response = mock.Mock()
597
        def kaboom():
598
            raise Exception('kaboom')
599
        mock_response.status_code = 500
600
        mock_response.raise_for_status = kaboom
601
        request.return_value = mock_response
602
        get_resp = client.get(callback_url, data)
603
        url = request.call_args[0][1]
604
        assert url.startswith('http://example.org/testitem/jump/trigger/paid')
605
    assert get_resp.status_code == 200
606
    assert Transaction.objects.get(order_id=transaction_id).status == 3
607
    assert BasketItem.objects.get(id=item.id).payment_date
608
    assert not BasketItem.objects.get(id=item.id).notification_date
609

  
610
    # too soon
611
    NotifyPaymentsCommand().handle()
612
    assert BasketItem.objects.get(id=item.id).payment_date
613
    assert not BasketItem.objects.get(id=item.id).notification_date
614

  
615
    # fake delay
616
    basket_item = BasketItem.objects.get(id=item.id)
617
    basket_item.payment_date = timezone.now() - timedelta(hours=1)
618
    basket_item.save()
619

  
620
    with mock.patch('combo.utils.RequestsSession.request') as request:
621
        mock_response = mock.Mock()
622
        mock_response.status_code = 200
623
        request.return_value = mock_response
624
        NotifyPaymentsCommand().handle()
625
        url = request.call_args[0][1]
626
        assert url.startswith('http://example.org/testitem/jump/trigger/paid')
627
    assert BasketItem.objects.get(id=item.id).payment_date
628
    assert BasketItem.objects.get(id=item.id).notification_date
576
-