0002-lingo-do-not-retry-notify-on-4xx-error-48393.patch
combo/apps/lingo/models.py | ||
---|---|---|
253 | 253 |
return [] |
254 | 254 |
return [] |
255 | 255 | |
256 |
def get_invoice(self, user, invoice_id, log_errors=True): |
|
256 |
def get_invoice(self, user, invoice_id, log_errors=True, raise_4xx=False):
|
|
257 | 257 |
if not self.is_remote(): |
258 | 258 |
return self.basketitem_set.get(pk=invoice_id) |
259 | 259 |
url = self.webservice_url + '/invoice/%s/' % invoice_id |
260 | 260 |
response = requests.get(url, user=user, remote_service='auto', cache_duration=0, log_errors=log_errors) |
261 |
if raise_4xx and 400 <= response.status_code < 500: |
|
262 |
raise ObjectDoesNotExist() |
|
261 | 263 |
if response.status_code == 404: |
262 | 264 |
raise ObjectDoesNotExist() |
263 | 265 |
response.raise_for_status() |
... | ... | |
286 | 288 |
'transaction_date': transaction_date.strftime('%Y-%m-%dT%H:%M:%S') |
287 | 289 |
} |
288 | 290 |
headers = {'content-type': 'application/json'} |
289 |
return requests.post(url, remote_service='auto', |
|
290 |
data=json.dumps(data), headers=headers).json() |
|
291 |
try: |
|
292 |
response = requests.post( |
|
293 |
url, remote_service='auto', |
|
294 |
data=json.dumps(data), headers=headers) |
|
295 |
if 400 <= response.status_code < 500: |
|
296 |
raise ObjectDoesNotExist() |
|
297 |
response.raise_for_status() |
|
298 |
except RequestException as e: |
|
299 |
raise RemoteInvoiceException from e |
|
300 |
try: |
|
301 |
resp = response.json() |
|
302 |
except ValueError as e: |
|
303 |
raise RemoteInvoiceException from e |
|
304 |
if resp.get('err'): |
|
305 |
raise RemoteInvoiceException |
|
306 |
return resp |
|
291 | 307 | |
292 | 308 |
def as_api_dict(self): |
293 | 309 |
return {'id': self.slug, |
... | ... | |
620 | 636 |
to_be_paid_remote_items = [] |
621 | 637 |
for item_id in items.split(','): |
622 | 638 |
try: |
623 |
remote_item = regie.get_invoice(user=self.user, invoice_id=item_id) |
|
639 |
remote_item = regie.get_invoice(user=self.user, invoice_id=item_id, raise_4xx=True)
|
|
624 | 640 |
regie.pay_invoice(item_id, self.order_id, self.bank_transaction_date or self.end_date) |
641 |
except ObjectDoesNotExist: |
|
642 |
# 4xx error |
|
643 |
logger.error( |
|
644 |
'unable to retrieve or pay remote item %s from transaction %s, ignore it', |
|
645 |
item_id, self) |
|
646 |
except (RequestException, RemoteInvoiceException): |
|
647 |
# 5xx, err or requests error |
|
648 |
to_be_paid_remote_items.append(item_id) |
|
649 |
logger.warning( |
|
650 |
'unable to notify payment for remote item %s from transaction %s, retry later', |
|
651 |
item_id, self) |
|
625 | 652 |
except Exception: |
653 |
# unknown error |
|
626 | 654 |
to_be_paid_remote_items.append(item_id) |
627 |
logger.exception(u'unable to notify payment for remote item %s from transaction %s', |
|
628 |
item_id, self) |
|
655 |
logger.exception( |
|
656 |
'unable to notify payment for remote item %s from transaction %s', |
|
657 |
item_id, self) |
|
629 | 658 |
else: |
630 | 659 |
logger.info(u'notified payment for remote item %s from transaction %s', |
631 | 660 |
item_id, self) |
tests/test_lingo_payment.py | ||
---|---|---|
8 | 8 |
import uuid |
9 | 9 | |
10 | 10 |
from mellon.models import UserSAMLIdentifier |
11 |
from requests.exceptions import ConnectionError |
|
12 |
from requests.models import Response |
|
11 | 13 | |
12 | 14 |
from django.apps import apps |
13 | 15 |
from django.contrib.auth.models import User |
... | ... | |
1073 | 1075 |
assert transaction.to_be_paid_remote_items == '42,35' |
1074 | 1076 | |
1075 | 1077 | |
1078 |
@mock.patch('combo.utils.requests_wrapper.RequestsSession.request') |
|
1079 |
def test_transaction_retry_failure(mock_request, remote_regie): |
|
1080 |
transaction = Transaction.objects.create( |
|
1081 |
status=eopayment.PAID, regie=remote_regie, |
|
1082 |
end_date=timezone.now(), |
|
1083 |
to_be_paid_remote_items='42,35') |
|
1084 | ||
1085 |
appconfig = apps.get_app_config('lingo') |
|
1086 | ||
1087 |
mock_json_item = mock.Mock(status_code=200) |
|
1088 |
mock_json_item.json.return_value = {'err': 0, 'data': { |
|
1089 |
'created': '2020-11-23', 'pay_limit_date': '2021-11-23', |
|
1090 |
'total_amount': '123.45', 'amount': '123.45'}} |
|
1091 | ||
1092 |
mock_json_paid = mock.Mock(status_code=200) |
|
1093 |
mock_json_paid.json.return_value = {'err': 0} |
|
1094 | ||
1095 |
mock_5xx = mock.Mock(status_code=500) |
|
1096 | ||
1097 |
mock_4xx = mock.Mock(status_code=400) |
|
1098 | ||
1099 |
mock_err = mock.Mock(status_code=200) |
|
1100 |
mock_err.json.return_url = {'err': 1} |
|
1101 | ||
1102 |
# error on get invoice |
|
1103 |
mock_request.side_effect = [ |
|
1104 |
ConnectionError('where is my hostname?'), # get invoice 42 |
|
1105 |
mock_json_item, # get invoice 35 |
|
1106 |
mock_json_paid, # pay invoice 35 |
|
1107 |
] |
|
1108 |
appconfig.update_transactions() |
|
1109 |
transaction.refresh_from_db() |
|
1110 |
assert transaction.to_be_paid_remote_items == '42' # retry for first one |
|
1111 | ||
1112 |
# error on pay invoice |
|
1113 |
Transaction.objects.update(to_be_paid_remote_items='42,35') |
|
1114 |
mock_request.side_effect = [ |
|
1115 |
mock_json_item, # get invoice 42 |
|
1116 |
ConnectionError('where is my hostname?'), # pay invoice 42 |
|
1117 |
mock_json_item, # get invoice 35 |
|
1118 |
mock_json_paid, # pay invoice 35 |
|
1119 |
] |
|
1120 |
appconfig.update_transactions() |
|
1121 |
transaction.refresh_from_db() |
|
1122 |
assert transaction.to_be_paid_remote_items == '42' # retry for first one |
|
1123 | ||
1124 |
# unknown error on get invoice |
|
1125 |
Transaction.objects.update(to_be_paid_remote_items='42,35') |
|
1126 |
mock_request.side_effect = [ |
|
1127 |
Exception, # get invoice 42 |
|
1128 |
mock_json_item, # get invoice 35 |
|
1129 |
mock_json_paid, # pay invoice 35 |
|
1130 |
] |
|
1131 |
appconfig.update_transactions() |
|
1132 |
transaction.refresh_from_db() |
|
1133 |
assert transaction.to_be_paid_remote_items == '42' # retry for first one |
|
1134 | ||
1135 |
# unknown error on pay invoice |
|
1136 |
Transaction.objects.update(to_be_paid_remote_items='42,35') |
|
1137 |
mock_request.side_effect = [ |
|
1138 |
mock_json_item, # get invoice 42 |
|
1139 |
Exception, # pay invoice 42 |
|
1140 |
mock_json_item, # get invoice 35 |
|
1141 |
mock_json_paid, # pay invoice 35 |
|
1142 |
] |
|
1143 |
appconfig.update_transactions() |
|
1144 |
transaction.refresh_from_db() |
|
1145 |
assert transaction.to_be_paid_remote_items == '42' # retry for first one |
|
1146 | ||
1147 |
# 5xx on get invoice |
|
1148 |
Transaction.objects.update(to_be_paid_remote_items='42,35') |
|
1149 |
mock_request.side_effect = [ |
|
1150 |
mock_5xx, # get invoice 42 |
|
1151 |
mock_json_item, # get invoice 35 |
|
1152 |
mock_json_paid, # pay invoice 35 |
|
1153 |
] |
|
1154 |
appconfig.update_transactions() |
|
1155 |
transaction.refresh_from_db() |
|
1156 |
assert transaction.to_be_paid_remote_items == '42' # retry for first one |
|
1157 | ||
1158 |
# 5xx on pay invoice |
|
1159 |
Transaction.objects.update(to_be_paid_remote_items='42,35') |
|
1160 |
mock_request.side_effect = [ |
|
1161 |
mock_json_item, # get invoice 42 |
|
1162 |
mock_5xx, # pay invoice 42 |
|
1163 |
mock_json_item, # get invoice 35 |
|
1164 |
mock_json_paid, # pay invoice 35 |
|
1165 |
] |
|
1166 |
appconfig.update_transactions() |
|
1167 |
transaction.refresh_from_db() |
|
1168 |
assert transaction.to_be_paid_remote_items == '42' # retry for first one |
|
1169 | ||
1170 |
# err on get invoice |
|
1171 |
Transaction.objects.update(to_be_paid_remote_items='42,35') |
|
1172 |
mock_request.side_effect = [ |
|
1173 |
mock_err, # get invoice 42 |
|
1174 |
mock_json_item, # get invoice 35 |
|
1175 |
mock_json_paid, # pay invoice 35 |
|
1176 |
] |
|
1177 |
appconfig.update_transactions() |
|
1178 |
transaction.refresh_from_db() |
|
1179 |
assert transaction.to_be_paid_remote_items == '42' # retry for first one |
|
1180 | ||
1181 |
# err on pay invoice |
|
1182 |
Transaction.objects.update(to_be_paid_remote_items='42,35') |
|
1183 |
mock_request.side_effect = [ |
|
1184 |
mock_json_item, # get invoice 42 |
|
1185 |
mock_err, # pay invoice 42 |
|
1186 |
mock_json_item, # get invoice 35 |
|
1187 |
mock_json_paid, # pay invoice 35 |
|
1188 |
] |
|
1189 |
appconfig.update_transactions() |
|
1190 |
transaction.refresh_from_db() |
|
1191 |
assert transaction.to_be_paid_remote_items == '42' # retry for first one |
|
1192 | ||
1193 |
# 4xx on get invoice |
|
1194 |
Transaction.objects.update(to_be_paid_remote_items='42,35') |
|
1195 |
mock_request.side_effect = [ |
|
1196 |
mock_4xx, # get invoice 42 |
|
1197 |
mock_5xx, # get invoice 35 |
|
1198 |
] |
|
1199 |
appconfig.update_transactions() |
|
1200 |
transaction.refresh_from_db() |
|
1201 |
assert transaction.to_be_paid_remote_items == '35' # retry for the second only |
|
1202 | ||
1203 |
# 4xx on pay invoice |
|
1204 |
Transaction.objects.update(to_be_paid_remote_items='42,35') |
|
1205 |
mock_request.side_effect = [ |
|
1206 |
mock_json_item, # get invoice 42 |
|
1207 |
mock_4xx, # get invoice 35 |
|
1208 |
mock_5xx, # pay invoice 35 |
|
1209 |
] |
|
1210 |
appconfig.update_transactions() |
|
1211 |
transaction.refresh_from_db() |
|
1212 |
assert transaction.to_be_paid_remote_items == '35' # retry for the second only |
|
1213 | ||
1214 | ||
1076 | 1215 |
def test_transaction_validate(app, key, regie, user): |
1077 | 1216 |
t1 = Transaction(regie=regie, bank_data={'bank': 'data'}, amount=12, |
1078 | 1217 |
status=eopayment.PAID) |
tests/test_lingo_remote_regie.py | ||
---|---|---|
315 | 315 |
invoices = copy.deepcopy(INVOICES) |
316 | 316 |
invoices[0]['amount'] = '100.00' |
317 | 317 |
invoices[0]['amount_paid'] = '23.45' |
318 |
mock_json = mock.Mock() |
|
318 |
mock_json = mock.Mock(status_code=200)
|
|
319 | 319 |
mock_json.json.return_value = {'err': 0, 'data': invoices[0]} |
320 | 320 |
mock_get.return_value = mock_json |
321 | 321 |
mock_pay_invoice.return_value = mock.Mock(status_code=200) |
... | ... | |
325 | 325 |
assert 'Amount already paid: <span class="amount">23.45€</span>' in resp.text |
326 | 326 | |
327 | 327 |
# invoice without amount_paid |
328 |
mock_json = mock.Mock() |
|
328 |
mock_json = mock.Mock(status_code=200)
|
|
329 | 329 |
mock_json.json.return_value = {'err': 0, 'data': INVOICES[0]} |
330 | 330 |
mock_get.return_value = mock_json |
331 | 331 |
mock_pay_invoice.return_value = mock.Mock(status_code=200) |
... | ... | |
491 | 491 |
page.save() |
492 | 492 |
assert remote_regie.is_remote() |
493 | 493 |
encrypt_id = aes_hex_encrypt(settings.SECRET_KEY, force_bytes('F201601')) |
494 |
mock_json = mock.Mock() |
|
494 |
mock_json = mock.Mock(status_code=200)
|
|
495 | 495 |
mock_json.json.return_value = {'err': 0, 'data': INVOICES[0]} |
496 | 496 |
mock_get.return_value = mock_json |
497 | 497 |
mock_pay_invoice.return_value = mock.Mock(status_code=200) |
498 |
- |