allow arbitrary date for deferred payment (#26992)
This commit is contained in:
parent
d383315b34
commit
980ab967ba
|
@ -1,7 +1,10 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import datetime
|
||||||
import importlib
|
import importlib
|
||||||
import logging
|
import logging
|
||||||
|
import pytz
|
||||||
|
|
||||||
|
|
||||||
from .common import (URL, HTML, FORM, RECEIVED, ACCEPTED, PAID, DENIED,
|
from .common import (URL, HTML, FORM, RECEIVED, ACCEPTED, PAID, DENIED,
|
||||||
CANCELED, CANCELLED, ERROR, WAITING, ResponseError, force_text)
|
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
|
- the third is the URL or the HTML form to contact the payment
|
||||||
server, which must be sent to the customer browser.
|
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:
|
for param in kwargs:
|
||||||
# encode all input params to unicode
|
# encode all input params to unicode
|
||||||
kwargs[param] = force_text(kwargs[param])
|
kwargs[param] = force_text(kwargs[param])
|
||||||
|
|
|
@ -279,8 +279,9 @@ class Payment(PaymentCommon):
|
||||||
warnings.warn("callback option is deprecated, "
|
warnings.warn("callback option is deprecated, "
|
||||||
"use automatic_return_url", DeprecationWarning)
|
"use automatic_return_url", DeprecationWarning)
|
||||||
automatic_return_url = self.callback
|
automatic_return_url = self.callback
|
||||||
if self.capture_day:
|
capture_day = capture_day = kwargs.get('capture_day', self.capture_day)
|
||||||
d['PBX_DIFF'] = self.capture_day.zfill(2)
|
if capture_day:
|
||||||
|
d['PBX_DIFF'] = capture_day.zfill(2)
|
||||||
d['PBX_AUTOSEULE'] = PAYMENT_MODES[self.capture_mode]
|
d['PBX_AUTOSEULE'] = PAYMENT_MODES[self.capture_mode]
|
||||||
if automatic_return_url:
|
if automatic_return_url:
|
||||||
d['PBX_REPONDRE_A'] = force_text(automatic_return_url)
|
d['PBX_REPONDRE_A'] = force_text(automatic_return_url)
|
||||||
|
|
|
@ -181,8 +181,8 @@ class Payment(PaymentCommon):
|
||||||
data['amount'] = force_text(int(Decimal(amount) * 100))
|
data['amount'] = force_text(int(Decimal(amount) * 100))
|
||||||
if email:
|
if email:
|
||||||
data['billingContact.email'] = email
|
data['billingContact.email'] = email
|
||||||
if 'captureDay' in kwargs:
|
if 'capture_day' in kwargs:
|
||||||
data['captureDay'] == kwargs.get('captureDay')
|
data['captureDay'] = kwargs.get('capture_day')
|
||||||
normal_return_url = self.normal_return_url
|
normal_return_url = self.normal_return_url
|
||||||
if next_url and not normal_return_url:
|
if next_url and not normal_return_url:
|
||||||
warnings.warn("passing next_url to request() is deprecated, "
|
warnings.warn("passing next_url to request() is deprecated, "
|
||||||
|
|
|
@ -82,6 +82,9 @@ PARAMETERS = [
|
||||||
choices=('SILENT', 'INTERACTIVE')),
|
choices=('SILENT', 'INTERACTIVE')),
|
||||||
Parameter('vads_amount', 'n', 9, max_length=12, needed=True),
|
Parameter('vads_amount', 'n', 9, max_length=12, needed=True),
|
||||||
Parameter('vads_capture_delay', 'n', 6, max_length=3, default=''),
|
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'),
|
Parameter('vads_contrib', 'ans', 31, max_length=255, default='eopayment'),
|
||||||
# defaut currency = EURO, norme ISO4217
|
# defaut currency = EURO, norme ISO4217
|
||||||
Parameter('vads_currency', 'n', 10, length=3, default='978', needed=True),
|
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',
|
for name in ('vads_ctx_mode', VADS_SITE_ID, 'vads_order_info',
|
||||||
'vads_order_info2', 'vads_order_info3',
|
'vads_order_info2', 'vads_order_info3',
|
||||||
'vads_payment_cards', 'vads_payment_config'):
|
'vads_payment_cards', 'vads_payment_config', 'capture_day'):
|
||||||
parameter = PARAMETER_MAP[name]
|
parameter = PARAMETER_MAP[name]
|
||||||
x = {'name': name,
|
x = {'name': name,
|
||||||
'caption': parameter.description or name,
|
'caption': parameter.description or name,
|
||||||
|
@ -332,6 +335,9 @@ class Payment(PaymentCommon):
|
||||||
fields[name] = parameter.default()
|
fields[name] = parameter.default()
|
||||||
else:
|
else:
|
||||||
fields[name] = parameter.default
|
fields[name] = parameter.default
|
||||||
|
capture_day = fields.pop('capture_day')
|
||||||
|
if capture_day:
|
||||||
|
fields['vads_capture_delay'] = capture_day
|
||||||
check_vads(fields)
|
check_vads(fields)
|
||||||
fields[SIGNATURE] = force_text(self.signature(fields))
|
fields[SIGNATURE] = force_text(self.signature(fields))
|
||||||
self.logger.debug('%s request contains fields: %s', __name__, fields)
|
self.logger.debug('%s request contains fields: %s', __name__, fields)
|
||||||
|
|
|
@ -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)
|
|
@ -105,6 +105,37 @@ class PayboxTests(TestCase):
|
||||||
self.assertIn('PBX_DIFF', form_params)
|
self.assertIn('PBX_DIFF', form_params)
|
||||||
self.assertEqual(form_params['PBX_DIFF'], '07')
|
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):
|
def test_request_with_authorization_only(self):
|
||||||
params = BACKEND_PARAMS.copy()
|
params = BACKEND_PARAMS.copy()
|
||||||
time = '2018-08-21T10:26:32+02:00'
|
time = '2018-08-21T10:26:32+02:00'
|
||||||
|
|
|
@ -19,6 +19,11 @@ def test_build_request():
|
||||||
data = [f for f in form.fields if f['name'] == 'Data']
|
data = [f for f in form.fields if f['name'] == 'Data']
|
||||||
assert 'statementReference=foobar' in data[0]['value']
|
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():
|
def test_options():
|
||||||
payment = eopayment.Payment('sips2', {'capture_mode': u'VALIDATION'})
|
payment = eopayment.Payment('sips2', {'capture_mode': u'VALIDATION'})
|
||||||
assert payment.backend.get_data()['captureMode'] == 'VALIDATION'
|
assert payment.backend.get_data()['captureMode'] == 'VALIDATION'
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from six.moves.urllib import parse as urlparse
|
from six.moves.urllib import parse as urlparse
|
||||||
|
|
||||||
|
import eopayment
|
||||||
from eopayment.systempayv2 import Payment, VADS_CUST_FIRST_NAME, \
|
from eopayment.systempayv2 import Payment, VADS_CUST_FIRST_NAME, \
|
||||||
VADS_CUST_LAST_NAME, PAID
|
VADS_CUST_LAST_NAME, PAID
|
||||||
from eopayment import ResponseError
|
from eopayment import ResponseError
|
||||||
|
@ -14,6 +16,13 @@ PARAMS = {
|
||||||
'vads_trans_date': u'20090501193530',
|
'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():
|
def test_systempayv2():
|
||||||
p = Payment(PARAMS)
|
p = Payment(PARAMS)
|
||||||
data = {'amount': 15.24, 'orderid': '654321',
|
data = {'amount': 15.24, 'orderid': '654321',
|
||||||
|
@ -49,3 +58,43 @@ def test_systempayv2():
|
||||||
# bad response
|
# bad response
|
||||||
with pytest.raises(ResponseError, match='missing signature, vads_ctx_mode or vads_auth_result'):
|
with pytest.raises(ResponseError, match='missing signature, vads_ctx_mode or vads_auth_result'):
|
||||||
p.response('foo=bar')
|
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'
|
||||||
|
|
Loading…
Reference in New Issue