From e7e7cc565b6654deefe8e9cdbd2d3b4537296037 Mon Sep 17 00:00:00 2001 From: Emmanuel Cazenave Date: Fri, 12 Oct 2018 15:35:23 +0200 Subject: [PATCH 2/2] systempayv2: allow arbitrary date for deferred payment (#26992) --- eopayment/__init__.py | 5 ++- eopayment/systempayv2.py | 24 +++++++++-- tests/test_base_payment.py | 18 +++++++++ tests/test_systempayv2.py | 81 ++++++++++++++++++++++++++++++++++++++ tox.ini | 1 + 5 files changed, 124 insertions(+), 5 deletions(-) create mode 100644 tests/test_base_payment.py diff --git a/eopayment/__init__.py b/eopayment/__init__.py index fbd0e55..efc0ae9 100644 --- a/eopayment/__init__.py +++ b/eopayment/__init__.py @@ -104,8 +104,9 @@ class Payment(object): ''' logger.debug(u'%r' % kwargs) for param in kwargs: - # encode all input params to unicode - kwargs[param] = force_text(kwargs[param]) + if param != 'capture_date': + # encode all input params to unicode + kwargs[param] = force_text(kwargs[param]) return self.backend.request(amount, **kwargs) def response(self, query_string, **kwargs): diff --git a/eopayment/systempayv2.py b/eopayment/systempayv2.py index 201cb2c..044200d 100644 --- a/eopayment/systempayv2.py +++ b/eopayment/systempayv2.py @@ -4,6 +4,7 @@ import datetime as dt import hashlib import logging import string +import pytz from six.moves.urllib import parse as urlparse import warnings from gettext import gettext as _ @@ -266,14 +267,14 @@ class Payment(PaymentCommon): def request(self, amount, name=None, first_name=None, last_name=None, address=None, email=None, phone=None, orderid=None, info1=None, - info2=None, info3=None, next_url=None, **kwargs): + info2=None, info3=None, next_url=None, capture_date=None, **kwargs): ''' Create the URL string to send a request to SystemPay ''' self.logger.debug('%s amount %s name %s address %s email %s phone %s ' - 'next_url %s info1 %s info2 %s info3 %s kwargs: %s', + 'next_url %s info1 %s info2 %s info3 %s capture_date %s kwargs: %s', __name__, amount, name, address, email, phone, info1, - info2, info3, next_url, kwargs) + info2, info3, next_url, capture_date, kwargs) # amount unit is cents amount = '%.0f' % (100 * amount) kwargs.update(add_vads({'amount': force_text(amount)})) @@ -332,6 +333,23 @@ class Payment(PaymentCommon): fields[name] = parameter.default() else: fields[name] = parameter.default + + if capture_date: + if not isinstance(capture_date, dt.date): + raise ValueError('capture_date should be a date object.') + + paris_tz = pytz.timezone('Europe/Paris') + utc_tz = pytz.timezone('Etc/UTC') + paris_trans_date = utc_tz.localize( + dt.datetime.strptime(fields[VADS_TRANS_DATE], "%Y%m%d%H%M%S") + ).astimezone(paris_tz) + vads_capture_delay = ( + capture_date - paris_trans_date.date() + ).days + if vads_capture_delay <= 0: + raise ValueError("capture_date needs to be superior to the transaction date") + fields['vads_capture_delay'] = force_text(vads_capture_delay) + check_vads(fields) fields[SIGNATURE] = force_text(self.signature(fields)) self.logger.debug('%s request contains fields: %s', __name__, fields) diff --git a/tests/test_base_payment.py b/tests/test_base_payment.py new file mode 100644 index 0000000..1fa118f --- /dev/null +++ b/tests/test_base_payment.py @@ -0,0 +1,18 @@ +from datetime import datetime +import mock + + +def test_cature_date_not_forced_to_unicode(monkeypatch): + mock_backend = mock.Mock() + + def get_backend(*args, **kwargs): + def backend(*args, **kwargs): + return mock_backend + return backend + + import eopayment + monkeypatch.setattr(eopayment, 'get_backend', get_backend) + now = datetime.now() + payement = eopayment.Payment('kind', None) + payement.request(12, capture_date=now) + mock_backend.request.assert_called_with(12, capture_date=now) diff --git a/tests/test_systempayv2.py b/tests/test_systempayv2.py index bf00b6d..71def58 100644 --- a/tests/test_systempayv2.py +++ b/tests/test_systempayv2.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +from datetime import datetime, date, timedelta import pytest from six.moves.urllib import parse as urlparse @@ -14,6 +15,13 @@ PARAMS = { 'vads_trans_date': u'20090501193530', } + +def get_field(form, field_name): + for field in form.fields: + if field['name'] == field_name: + return field + + def test_systempayv2(): p = Payment(PARAMS) data = {'amount': 15.24, 'orderid': '654321', @@ -49,3 +57,76 @@ def test_systempayv2(): # bad response with pytest.raises(ResponseError, match='missing signature, vads_ctx_mode or vads_auth_result'): p.response('foo=bar') + + +def test_systempayv2_deferred_payment(): + default_params = { + 'secret_test': u'1122334455667788', + 'vads_site_id': u'12345678', + 'vads_ctx_mode': u'TEST', + } + default_data = { + 'amount': 15.24, 'orderid': '654321', 'first_name': u'John', + 'last_name': u'Doe' + } + p = Payment(default_params.copy()) + data = default_data.copy() + data['capture_date'] = (datetime.now().date() + timedelta(days=3)) + transaction_id, f, form = p.request(**data) + assert get_field(form, 'vads_capture_delay')['value'] == '3' + + # default vads_capture_delay used if no capture date + params = default_params.copy() + params['vads_capture_delay'] = 1 + p = Payment(params) + transaction_id, f, form = p.request(**default_data.copy()) + assert get_field(form, 'vads_capture_delay')['value'] == '1' + + # capture date override vads_capture_delay + params = default_params.copy() + params['vads_capture_delay'] = 1 + p = Payment(params) + data = default_data.copy() + data['capture_date'] = (datetime.now().date() + timedelta(days=3)) + transaction_id, f, form = p.request(**data) + assert get_field(form, 'vads_capture_delay')['value'] == '3' + + # capture date can't be inferior to the transaction date + params = default_params.copy() + params['vads_trans_date'] = (datetime.utcnow() + timedelta(days=3)).strftime("%Y%m%d%H%M%S") + p = Payment(params) + data = default_data.copy() + data['capture_date'] = (datetime.now().date()) + + with pytest.raises( + ValueError, match='capture_date needs to be superior to the transaction date'): + transaction_id, f, form = p.request(**data) + + # capture date should have the right format + p = Payment(params) + data = default_data.copy() + data['capture_date'] = 'not a date' + with pytest.raises( + ValueError, match='capture_date should be a date object.'): + transaction_id, f, form = p.request(**data) + + +@pytest.mark.freeze_time('2018-10-02 23:50:00') +def test_paris_timezone(): + params = { + 'secret_test': u'1122334455667788', + 'vads_site_id': u'12345678', + 'vads_ctx_mode': u'TEST', + } + data = { + 'amount': 15.24, 'orderid': '654321', 'first_name': u'John', + 'last_name': u'Doe', 'capture_date': date(year=2018, month=10, day=3), + } + + p = Payment(params) + with pytest.raises( + ValueError, match='capture_date needs to be superior to the transaction date'): + # utcnow will return 2018-10-02 23:50:00, + # converted to Europe/Paris is already 2018-10-03 ... + # so 2018-10-03 for capture_date is invalid + transaction_id, f, form = p.request(**data) diff --git a/tox.ini b/tox.ini index 676a49b..622788c 100644 --- a/tox.ini +++ b/tox.ini @@ -16,5 +16,6 @@ commands = usedevelop = True deps = coverage pytest + pytest-freezegun py2: pytest-cov mock -- 2.19.0