0001-dpark-accept-UTC-datetime-in-payment-notification-41.patch
passerelle/contrib/dpark/models.py | ||
---|---|---|
16 | 16 | |
17 | 17 |
from __future__ import unicode_literals |
18 | 18 | |
19 |
import datetime |
|
19 | 20 |
import base64 |
20 | 21 | |
22 |
import pytz |
|
23 | ||
21 | 24 |
from django.conf import settings |
22 | 25 |
from django.db import models |
23 | 26 |
from django.utils import six, timezone |
... | ... | |
115 | 118 |
if not idate: |
116 | 119 |
return None |
117 | 120 |
try: |
118 |
return timezone.datetime.strptime(idate, '%Y%m%d').date().isoformat()
|
|
121 |
return datetime.datetime.strptime(idate, '%Y%m%d').date().isoformat()
|
|
119 | 122 |
except (ValueError,): |
120 | 123 |
return idate |
121 | 124 | |
122 | 125 | |
126 |
def date_or_datetime_to_local_date(value): |
|
127 |
try: |
|
128 |
dt = datetime.datetime.strptime(value, '%Y-%m-%dT%H:%M:%S') |
|
129 |
except ValueError: |
|
130 |
pass |
|
131 |
else: |
|
132 |
dt = pytz.utc.localize(dt) |
|
133 |
dt = dt.astimezone(pytz.timezone('Europe/Paris')) |
|
134 |
return dt.date() |
|
135 | ||
136 |
try: |
|
137 |
dt = datetime.datetime.strptime(value, '%Y%m%d') |
|
138 |
except ValueError: |
|
139 |
pass |
|
140 |
else: |
|
141 |
return dt.date() |
|
142 | ||
143 |
return None |
|
144 | ||
145 | ||
123 | 146 |
def normalize_reply(reply): |
124 | 147 |
excluded = ('CodeRetour', 'MessageRetour') |
125 | 148 |
serialized_reply = serialize_object(reply) |
... | ... | |
414 | 437 |
'transaction_datetime', 'total_amount', |
415 | 438 |
'application_external_id') |
416 | 439 |
) |
440 |
# We accept a simple date or a datetime using UTC, we convert it to Europe/Paris timezone on exit |
|
441 |
transaction_date = date_or_datetime_to_local_date(data['transaction_datetime']) |
|
442 |
if transaction_date is None: |
|
443 |
raise APIError(_('Invalid value for transaction datetime')) |
|
417 | 444 |
pairings = Pairing.objects.filter(resource=self, |
418 | 445 |
nameid=data['nameid'], |
419 | 446 |
filenumber=data['filenumber']) |
... | ... | |
427 | 454 |
data['application_id'], |
428 | 455 |
data.get('applicaiton_payment_type', 10), |
429 | 456 |
total_amount, |
430 |
data['transaction_datetime'],
|
|
457 |
transaction_date.strftime('%Y%m%d'),
|
|
431 | 458 |
data['transaction_id']) |
432 | 459 |
for pairing in pairings: |
433 | 460 |
pairing.clear_cache() |
setup.py | ||
---|---|---|
110 | 110 |
'pdfrw', |
111 | 111 |
'httplib2', |
112 | 112 |
'xmlschema', |
113 |
'pytz', |
|
113 | 114 |
], |
114 | 115 |
cmdclass={ |
115 | 116 |
'build': build, |
tests/test_dpark.py | ||
---|---|---|
73 | 73 | |
74 | 74 | |
75 | 75 |
class ReplyDataClass(dict): |
76 | ||
77 | 76 |
def __init__(self, **kwargs): |
78 | 77 |
self.__dict__.update(kwargs) |
79 | 78 |
super(ReplyDataClass, self).__init__(**kwargs) |
... | ... | |
110 | 109 | |
111 | 110 | |
112 | 111 |
def get_client(success=True, error_class=None, replydata=None): |
112 |
service = MockedService(success, error_class, replydata) |
|
113 | 113 | |
114 | 114 |
def create_service(binging, operation_endpoint): |
115 |
return MockedService(success, error_class, replydata)
|
|
115 |
return service
|
|
116 | 116 | |
117 |
return mock.Mock(create_service=create_service) |
|
117 |
return mock.Mock(create_service=create_service, service=service)
|
|
118 | 118 | |
119 | 119 | |
120 | 120 |
def test_call_service_error(dpark, app): |
... | ... | |
492 | 492 |
assert data['numerodemande'] == '55555' |
493 | 493 | |
494 | 494 | |
495 |
def test_payment_notification(dpark, app): |
|
496 |
with mock.patch('passerelle.contrib.dpark.models.get_client') as client: |
|
495 |
@pytest.mark.parametrize('transaction_datetime,expected_date', [ |
|
496 |
('20180611', '20180611'), |
|
497 |
# UTC datetime should be converted to Europe/Paris date |
|
498 |
('2018-06-11T23:59:00', '20180612') |
|
499 |
]) |
|
500 |
def test_payment_notification(dpark, app, transaction_datetime, expected_date): |
|
501 |
operation = mock.Mock(name='PLS_NOTIFCB') |
|
502 |
service = mock.Mock(spec=['PLS_NOTIFCB'], PLS_NOTIFCB=operation) |
|
503 |
create_service = mock.Mock(spec=[], return_value=service) |
|
504 |
client = mock.NonCallableMock(spec=['create_service'], create_service=create_service) |
|
505 |
with mock.patch('passerelle.contrib.dpark.models.get_client', return_value=client): |
|
497 | 506 |
nameid = 'abcd' * 8 |
498 | 507 |
filenumber = '1' * 9 |
499 | 508 |
params = { |
500 | 509 |
'nameid': nameid, 'filenumber': filenumber, 'transaction_id': 'I123456789', |
501 |
'transaction_datetime': '2018-06-11T10:23', 'total_amount': '125',
|
|
510 |
'transaction_datetime': transaction_datetime, 'total_amount': '125',
|
|
502 | 511 |
'application_id': '61718', 'application_external_id': 'E-8-N5UTAK6P' |
503 | 512 |
} |
504 | 513 |
url = '/dpark/test/notify-payment/' |
... | ... | |
509 | 518 |
'nameid': nameid, 'firstnames': 'spam eggs', 'lastname': 'bar', |
510 | 519 |
'filenumber': filenumber, 'badgenumber': '2' * 9} |
511 | 520 |
) |
512 |
client.return_value = get_client(replydata={'CodeRetour': '02', 'MessageRetour': u'Dossier inconnu'})
|
|
521 |
operation.return_value = mock.Mock(CodeRetour='02', MessageRetour=u'Dossier inconnu')
|
|
513 | 522 |
resp = app.post_json(url, params=params) |
523 |
assert operation.call_args_list[-1].args[5] == expected_date |
|
514 | 524 |
assert resp.json['err'] == 1 |
515 | 525 |
assert resp.json['err_desc'] == 'Dossier inconnu' |
516 |
client.return_value = get_client(replydata={'CodeRetour': '01'})
|
|
526 |
operation.return_value = mock.Mock(CodeRetour='01')
|
|
517 | 527 |
resp = app.post_json(url, params=params) |
518 | 528 |
assert resp.json['data'] is True |
519 | 529 | |
520 |
- |