summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEmmanuel Cazenave <ecazenave@entrouvert.com>2018-10-12 13:35:23 (GMT)
committerEmmanuel Cazenave <ecazenave@entrouvert.com>2018-11-12 14:18:10 (GMT)
commit980ab967ba9eb3ddbb67f10b0cc574c4a12bdb47 (patch)
treea71290ca48bb82c0c597df9034d4de0496ec059a
parentd383315b34639421d6ef29dcdb603c41f07dde28 (diff)
downloadeopayment-980ab967ba9eb3ddbb67f10b0cc574c4a12bdb47.zip
eopayment-980ab967ba9eb3ddbb67f10b0cc574c4a12bdb47.tar.gz
eopayment-980ab967ba9eb3ddbb67f10b0cc574c4a12bdb47.tar.bz2
allow arbitrary date for deferred payment (#26992)
-rw-r--r--eopayment/__init__.py32
-rw-r--r--eopayment/paybox.py5
-rw-r--r--eopayment/sips2.py4
-rw-r--r--eopayment/systempayv2.py8
-rw-r--r--tests/test_base_payment.py66
-rw-r--r--tests/test_paybox.py31
-rw-r--r--tests/test_sips2.py5
-rw-r--r--tests/test_systempayv2.py49
-rw-r--r--tox.ini1
9 files changed, 195 insertions, 6 deletions
diff --git a/eopayment/__init__.py b/eopayment/__init__.py
index fbd0e55..f2f1239 100644
--- a/eopayment/__init__.py
+++ b/eopayment/__init__.py
@@ -1,7 +1,10 @@
# -*- coding: utf-8 -*-
+import datetime
import importlib
import logging
+import pytz
+
from .common import (URL, HTML, FORM, RECEIVED, ACCEPTED, PAID, DENIED,
CANCELED, CANCELLED, ERROR, WAITING, ResponseError, force_text)
@@ -102,7 +105,34 @@ class Payment(object):
- the third is the URL or the HTML form to contact the payment
server, which must be sent to the customer browser.
'''
- logger.debug(u'%r' % kwargs)
+ logger.debug(u'%r' % kwargs)
+
+ if 'capture_date' in kwargs:
+ capture_date = kwargs.pop('capture_date')
+
+ delay_param = False
+ for parameter in self.backend.description['parameters']:
+ if parameter['name'] == 'capture_day':
+ delay_param = True
+ break
+
+ if not delay_param:
+ raise ValueError('capture_date is not supported by the backend.')
+
+ if not isinstance(capture_date, datetime.date):
+ raise ValueError('capture_date should be a datetime.date object.')
+
+ # backend timezone should come from some backend configuration
+ backend_tz = pytz.timezone('Europe/Paris')
+ utc_tz = pytz.timezone('Etc/UTC')
+ backend_trans_date = utc_tz.localize(
+ datetime.datetime.utcnow()).astimezone(backend_tz)
+ capture_day = (capture_date - backend_trans_date.date()).days
+ if capture_day <= 0:
+ raise ValueError("capture_date needs to be superior to the transaction date.")
+
+ kwargs['capture_day'] = capture_day
+
for param in kwargs:
# encode all input params to unicode
kwargs[param] = force_text(kwargs[param])
diff --git a/eopayment/paybox.py b/eopayment/paybox.py
index ab77f83..4c34731 100644
--- a/eopayment/paybox.py
+++ b/eopayment/paybox.py
@@ -279,8 +279,9 @@ class Payment(PaymentCommon):
warnings.warn("callback option is deprecated, "
"use automatic_return_url", DeprecationWarning)
automatic_return_url = self.callback
- if self.capture_day:
- d['PBX_DIFF'] = self.capture_day.zfill(2)
+ capture_day = capture_day = kwargs.get('capture_day', self.capture_day)
+ if capture_day:
+ d['PBX_DIFF'] = capture_day.zfill(2)
d['PBX_AUTOSEULE'] = PAYMENT_MODES[self.capture_mode]
if automatic_return_url:
d['PBX_REPONDRE_A'] = force_text(automatic_return_url)
diff --git a/eopayment/sips2.py b/eopayment/sips2.py
index fe4de5a..440546c 100644
--- a/eopayment/sips2.py
+++ b/eopayment/sips2.py
@@ -181,8 +181,8 @@ class Payment(PaymentCommon):
data['amount'] = force_text(int(Decimal(amount) * 100))
if email:
data['billingContact.email'] = email
- if 'captureDay' in kwargs:
- data['captureDay'] == kwargs.get('captureDay')
+ if 'capture_day' in kwargs:
+ data['captureDay'] = kwargs.get('capture_day')
normal_return_url = self.normal_return_url
if next_url and not normal_return_url:
warnings.warn("passing next_url to request() is deprecated, "
diff --git a/eopayment/systempayv2.py b/eopayment/systempayv2.py
index 201cb2c..31731fd 100644
--- a/eopayment/systempayv2.py
+++ b/eopayment/systempayv2.py
@@ -82,6 +82,9 @@ PARAMETERS = [
choices=('SILENT', 'INTERACTIVE')),
Parameter('vads_amount', 'n', 9, max_length=12, needed=True),
Parameter('vads_capture_delay', 'n', 6, max_length=3, default=''),
+ # Same as 'vads_capture_delay' but matches other backend naming for
+ # deferred payment
+ Parameter('capture_day', 'n', 6, max_length=3, default=''),
Parameter('vads_contrib', 'ans', 31, max_length=255, default='eopayment'),
# defaut currency = EURO, norme ISO4217
Parameter('vads_currency', 'n', 10, length=3, default='978', needed=True),
@@ -248,7 +251,7 @@ class Payment(PaymentCommon):
for name in ('vads_ctx_mode', VADS_SITE_ID, 'vads_order_info',
'vads_order_info2', 'vads_order_info3',
- 'vads_payment_cards', 'vads_payment_config'):
+ 'vads_payment_cards', 'vads_payment_config', 'capture_day'):
parameter = PARAMETER_MAP[name]
x = {'name': name,
'caption': parameter.description or name,
@@ -332,6 +335,9 @@ class Payment(PaymentCommon):
fields[name] = parameter.default()
else:
fields[name] = parameter.default
+ capture_day = fields.pop('capture_day')
+ if capture_day:
+ fields['vads_capture_delay'] = capture_day
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..7ea551e
--- /dev/null
+++ b/tests/test_base_payment.py
@@ -0,0 +1,66 @@
+from datetime import date, datetime, timedelta
+
+import mock
+import pytest
+
+
+def do_mock_backend(monkeypatch):
+
+ class MockBackend(object):
+
+ request = mock.Mock()
+
+ description = {
+ 'parameters': [
+ {
+ 'name': 'capture_day',
+ }
+ ]
+ }
+
+ def get_backend(*args, **kwargs):
+ def backend(*args, **kwargs):
+ return MockBackend
+ return backend
+
+ import eopayment
+ monkeypatch.setattr(eopayment, 'get_backend', get_backend)
+ return MockBackend, eopayment.Payment('kind', None)
+
+
+def test_deferred_payment(monkeypatch):
+ mock_backend, payment = do_mock_backend(monkeypatch)
+
+ capture_date = (datetime.now().date() + timedelta(days=3))
+ payment.request(amount=12.2, capture_date=capture_date)
+ mock_backend.request.assert_called_with(12.2, **{'capture_day': u'3'})
+
+ # capture date can't be inferior to the transaction date
+ capture_date = (datetime.now().date() - timedelta(days=3))
+ with pytest.raises(
+ ValueError, match='capture_date needs to be superior to the transaction date.'):
+ payment.request(amount=12.2, capture_date=capture_date)
+
+ # capture date should be a date object
+ capture_date = 'not a date'
+ with pytest.raises(ValueError, match='capture_date should be a datetime.date object.'):
+ payment.request(amount=12.2, capture_date=capture_date)
+
+ # using capture date on a backend that does not support it raise an error
+ capture_date = (datetime.now().date() + timedelta(days=3))
+ mock_backend.description['parameters'] = []
+ with pytest.raises(ValueError, match='capture_date is not supported by the backend.'):
+ payment.request(amount=12.2, capture_date=capture_date)
+
+
+def test_paris_timezone(freezer, monkeypatch):
+ freezer.move_to('2018-10-02 23:50:00')
+ _, payment = do_mock_backend(monkeypatch)
+ capture_date = date(year=2018, month=10, day=3)
+
+ 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 it is already 2018-10-03
+ # so 2018-10-03 for capture_date is invalid
+ payment.request(amount=12.2, capture_date=capture_date)
diff --git a/tests/test_paybox.py b/tests/test_paybox.py
index 7045026..9f38090 100644
--- a/tests/test_paybox.py
+++ b/tests/test_paybox.py
@@ -105,6 +105,37 @@ class PayboxTests(TestCase):
self.assertIn('PBX_DIFF', form_params)
self.assertEqual(form_params['PBX_DIFF'], '07')
+ # capture_day can be used as a request argument
+ params = BACKEND_PARAMS.copy()
+ backend = eopayment.Payment('paybox', params)
+ transaction_id, kind, what = backend.request(
+ Decimal(amount), email=email, orderid=order_id,
+ transaction_id=transaction, time=time, capture_day=2)
+ root = ET.fromstring(str(what))
+
+ form_params = dict(((
+ node.attrib['name'], node.attrib['value']) for node in root
+ if node.attrib['type'] == 'hidden'))
+ self.assertIn('PBX_DIFF', form_params)
+ self.assertEqual(form_params['PBX_DIFF'], '02')
+
+ # capture_day passed as a request argument
+ # overrides capture_day from backend params
+ params = BACKEND_PARAMS.copy()
+ params['capture_day'] = '7'
+ backend = eopayment.Payment('paybox', params)
+ transaction_id, kind, what = backend.request(
+ Decimal(amount), email=email, orderid=order_id,
+ transaction_id=transaction, time=time, capture_day=2)
+ root = ET.fromstring(str(what))
+
+ form_params = dict(((
+ node.attrib['name'], node.attrib['value']) for node in root
+ if node.attrib['type'] == 'hidden'))
+ self.assertIn('PBX_DIFF', form_params)
+ self.assertEqual(form_params['PBX_DIFF'], '02')
+
+
def test_request_with_authorization_only(self):
params = BACKEND_PARAMS.copy()
time = '2018-08-21T10:26:32+02:00'
diff --git a/tests/test_sips2.py b/tests/test_sips2.py
index 65e63f1..563c75b 100644
--- a/tests/test_sips2.py
+++ b/tests/test_sips2.py
@@ -19,6 +19,11 @@ def test_build_request():
data = [f for f in form.fields if f['name'] == 'Data']
assert 'statementReference=foobar' in data[0]['value']
+ transaction, f, form = backend.request(amount=u'12', info1='foobar', capture_day=u'1')
+ data = [f for f in form.fields if f['name'] == 'Data']
+ assert 'captureDay=1' in data[0]['value']
+
+
def test_options():
payment = eopayment.Payment('sips2', {'capture_mode': u'VALIDATION'})
assert payment.backend.get_data()['captureMode'] == 'VALIDATION'
diff --git a/tests/test_systempayv2.py b/tests/test_systempayv2.py
index bf00b6d..1df7282 100644
--- a/tests/test_systempayv2.py
+++ b/tests/test_systempayv2.py
@@ -1,8 +1,10 @@
# -*- coding: utf-8 -*-
+from datetime import datetime, timedelta
import pytest
from six.moves.urllib import parse as urlparse
+import eopayment
from eopayment.systempayv2 import Payment, VADS_CUST_FIRST_NAME, \
VADS_CUST_LAST_NAME, PAID
from eopayment import ResponseError
@@ -14,6 +16,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 +58,43 @@ 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'
+ }
+
+ # default vads_capture_delay used
+ params = default_params.copy()
+ params['vads_capture_delay'] = 1
+
+ backend = eopayment.Payment('systempayv2', params)
+ data = default_data.copy()
+ transaction_id, f, form = backend.request(**data)
+ assert get_field(form, 'vads_capture_delay')['value'] == '1'
+
+ # vads_capture_delay can used in request and
+ # override default vads_capture_delay
+ params = default_params.copy()
+ params['vads_capture_delay'] = 1
+ p = eopayment.Payment('systempayv2', params)
+ data = default_data.copy()
+ data['vads_capture_delay'] = '3'
+ transaction_id, f, form = p.request(**data)
+ assert get_field(form, 'vads_capture_delay')['value'] == '3'
+
+ # capture_date can be used for deferred_payment
+ params = default_params.copy()
+ params['vads_capture_delay'] = 1
+ p = eopayment.Payment('systempayv2', params)
+ data = default_data.copy()
+ data['capture_date'] = (datetime.now().date() + timedelta(days=4))
+ transaction_id, f, form = p.request(**data)
+ assert get_field(form, 'vads_capture_delay')['value'] == '4'
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