0001-systempayv2-map-DENIED-and-CANCELLED-result-codes-17.patch
eopayment/cb.py | ||
---|---|---|
17 | 17 | |
18 | 18 |
'''Responses codes emitted by EMV Card or 'Carte Bleu' in France''' |
19 | 19 | |
20 |
from . import PAID, CANCELLED, ERROR, DENIED |
|
21 | ||
22 | ||
20 | 23 |
CB_RESPONSE_CODES = { |
21 |
'00': 'Transaction approuvée ou traitée avec succès', |
|
22 |
'02': 'Contacter l\'émetteur de carte', |
|
23 |
'03': 'Accepteur invalide', |
|
24 |
'04': 'Conserver la carte', |
|
25 |
'05': 'Ne pas honorer', |
|
26 |
'07': 'Conserver la carte, conditions spéciales', |
|
27 |
'08': 'Approuver après identification', |
|
28 |
'12': 'Transaction invalide', |
|
29 |
'13': 'Montant invalide', |
|
30 |
'14': 'Numéro de porteur invalide', |
|
31 |
'15': 'Emetteur de carte inconnu', |
|
32 |
'30': 'Erreur de format', |
|
33 |
'31': 'Identifiant de l\'organisme acquéreur inconnu', |
|
34 |
'33': 'Date de validité de la carte dépassée', |
|
35 |
'34': 'Suspicion de fraude', |
|
36 |
'41': 'Carte perdue', |
|
37 |
'43': 'Carte volée', |
|
38 |
'51': 'Provision insuffisante ou crédit dépassé', |
|
39 |
'54': 'Date de validité de la carte dépassée', |
|
40 |
'56': 'Carte absente du fichier', |
|
41 |
'57': 'Transaction non permise à ce porteur', |
|
42 |
'58': 'Transaction interdite au terminal', |
|
43 |
'59': 'Suspicion de fraude', |
|
44 |
'60': 'L\'accepteur de carte doit contacter l\'acquéreur', |
|
45 |
'61': 'Dépasse la limite du montant de retrait', |
|
46 |
'63': 'Règles de sécurité non respectées', |
|
47 |
'68': 'Réponse non parvenue ou reçue trop tard', |
|
48 |
'90': 'Arrêt momentané du système', |
|
49 |
'91': 'Emetteur de cartes inaccessible', |
|
50 |
'96': 'Mauvais fonctionnement du système', |
|
51 |
'97': 'Échéance de la temporisation de surveillance globale', |
|
52 |
'98': 'Serveur indisponible routage réseau demandé à nouveau', |
|
53 |
'99': 'Incident domaine initiateur', |
|
24 |
'00': {'message': 'Transaction approuvée ou traitée avec succès', 'result': PAID}, |
|
25 |
'02': {'message': 'Contacter l\'émetteur de carte'}, |
|
26 |
'03': {'message': 'Accepteur invalide'}, |
|
27 |
'04': {'message': 'Conserver la carte'}, |
|
28 |
'05': {'message': 'Ne pas honorer', 'result': DENIED}, |
|
29 |
'07': {'message': 'Conserver la carte, conditions spéciales'}, |
|
30 |
'08': {'message': 'Approuver après identification'}, |
|
31 |
'12': {'message': 'Transaction invalide'}, |
|
32 |
'13': {'message': 'Montant invalide'}, |
|
33 |
'14': {'message': 'Numéro de porteur invalide'}, |
|
34 |
'15': {'message': 'Emetteur de carte inconnu'}, |
|
35 |
'17': {'message': 'Annulation par l\'acheteur', 'result': CANCELLED}, |
|
36 |
'30': {'message': 'Erreur de format'}, |
|
37 |
'31': {'message': 'Identifiant de l\'organisme acquéreur inconnu'}, |
|
38 |
'33': {'message': 'Date de validité de la carte dépassée'}, |
|
39 |
'34': {'message': 'Suspicion de fraude'}, |
|
40 |
'41': {'message': 'Carte perdue'}, |
|
41 |
'43': {'message': 'Carte volée'}, |
|
42 |
'51': {'message': 'Provision insuffisante ou crédit dépassé'}, |
|
43 |
'54': {'message': 'Date de validité de la carte dépassée'}, |
|
44 |
'56': {'message': 'Carte absente du fichier'}, |
|
45 |
'57': {'message': 'Transaction non permise à ce porteur'}, |
|
46 |
'58': {'message': 'Transaction interdite au terminal'}, |
|
47 |
'59': {'message': 'Suspicion de fraude'}, |
|
48 |
'60': {'message': 'L\'accepteur de carte doit contacter l\'acquéreur'}, |
|
49 |
'61': {'message': 'Dépasse la limite du montant de retrait'}, |
|
50 |
'63': {'message': 'Règles de sécurité non respectées'}, |
|
51 |
'68': {'message': 'Réponse non parvenue ou reçue trop tard'}, |
|
52 |
'90': {'message': 'Arrêt momentané du système'}, |
|
53 |
'91': {'message': 'Emetteur de cartes inaccessible'}, |
|
54 |
'96': {'message': 'Mauvais fonctionnement du système'}, |
|
55 |
'97': {'message': 'Échéance de la temporisation de surveillance globale'}, |
|
56 |
'98': {'message': 'Serveur indisponible routage réseau demandé à nouveau'}, |
|
57 |
'99': {'message': 'Incident domaine initiateur'}, |
|
54 | 58 |
} |
59 | ||
60 | ||
61 |
def translate_cb_error_code(error_code): |
|
62 |
'Returns message, eopayment_error_code' |
|
63 | ||
64 |
if error_code in CB_RESPONSE_CODES: |
|
65 |
return CB_RESPONSE_CODES[error_code]['message'], CB_RESPONSE_CODES[error_code].get('result', ERROR) |
|
66 |
return None, None |
eopayment/systempayv2.py | ||
---|---|---|
25 | 25 |
import warnings |
26 | 26 |
from gettext import gettext as _ |
27 | 27 | |
28 |
from .common import (PaymentCommon, PaymentResponse, PAID, ERROR, FORM, Form,
|
|
29 |
ResponseError, force_text, force_byte) |
|
30 |
from .cb import CB_RESPONSE_CODES
|
|
28 |
from .common import (PaymentCommon, PaymentResponse, PAID, DENIED, CANCELLED,
|
|
29 |
ERROR, FORM, Form, ResponseError, force_text, force_byte)
|
|
30 |
from .cb import translate_cb_error_code
|
|
31 | 31 | |
32 | 32 |
__all__ = ['Payment'] |
33 | 33 | |
... | ... | |
175 | 175 |
PARAMETER_MAP = dict(((parameter.name, |
176 | 176 |
parameter) for parameter in PARAMETERS)) |
177 | 177 | |
178 |
AUTH_RESULT_MAP = CB_RESPONSE_CODES |
|
179 | ||
180 |
RESULT_MAP = { |
|
181 |
'00': 'paiement réalisé avec succés', |
|
182 |
'02': 'le commerçant doit contacter la banque du porteur', |
|
183 |
'05': 'paiement refusé', |
|
184 |
'17': 'annulation client', |
|
185 |
'30': 'erreur de format', |
|
186 |
'96': 'erreur technique lors du paiement' |
|
187 |
} |
|
188 | ||
189 |
EXTRA_RESULT_MAP = { |
|
190 |
'': "Pas de contrôle effectué", |
|
191 |
'00': "Tous les contrôles se sont déroulés avec succés", |
|
192 |
'02': "La carte a dépassé l'encours autorisé", |
|
193 |
'03': "La carte appartient à la liste grise du commerçant", |
|
194 |
'04': "Le pays d'émission de la carte appartient à la liste grise du " |
|
195 |
"commerçant ou le pays d'émission de la carte n'appartient pas à la " |
|
196 |
"liste blanche du commerçant", |
|
197 |
'05': "L'addresse IP appartient à la liste grise du commerçant", |
|
198 |
'99': "Problème technique recontré par le serveur lors du traitement " |
|
199 |
"d'un des contrôles locaux", |
|
200 |
} |
|
201 | ||
202 | ||
203 | 178 |
def add_vads(kwargs): |
204 | 179 |
new_vargs = {} |
205 | 180 |
for k, v in kwargs.items(): |
... | ... | |
406 | 381 |
for field_name, field_value in fields.items()]) |
407 | 382 |
return transaction_id, FORM, form |
408 | 383 | |
384 |
RESULT_MAP = { |
|
385 |
'00': {'message': 'Paiement réalisé avec succés.', 'result': PAID}, |
|
386 |
'02': {'message': 'Le commerçant doit contacter la banque du porteur.'}, |
|
387 |
'05': {'message': 'Paiement refusé.', 'result': DENIED}, |
|
388 |
'17': {'message': 'Annulation client.', 'result': CANCELLED}, |
|
389 |
'30': {'message': 'Erreur de format.'}, |
|
390 |
'96': {'message': 'Erreur technique lors du paiement.'}, |
|
391 |
} |
|
392 | ||
393 |
EXTRA_RESULT_MAP = { |
|
394 |
'': {'message': 'Pas de contrôle effectué.'}, |
|
395 |
'00': {'message': 'Tous les contrôles se sont déroulés avec succés.'}, |
|
396 |
'02': {'message': 'La carte a dépassé l\'encours autorisé.'}, |
|
397 |
'03': {'message': 'La carte appartient à la liste grise du commerçant.'}, |
|
398 |
'04': {'messaǵe': 'Le pays d\'émission de la carte appartient à la liste grise du ' |
|
399 |
'commerçant ou le pays d\'émission de la carte n\'appartient pas à la ' |
|
400 |
'liste blanche du commerçant.'}, |
|
401 |
'05': {'message': 'L’adresse IP appartient à la liste grise du marchand.'}, |
|
402 |
'06': {'message': 'Le code bin appartient à la liste grise du marchand.'}, |
|
403 |
'07': {'message': 'Détection d’une e-carte bleue.'}, |
|
404 |
'08': {'message': 'Détection d’une carte commerciale nationale.'}, |
|
405 |
'09': {'message': 'Détection d’une carte commerciale étrangère.'}, |
|
406 |
'14': {'message': 'Détection d’une carte à autorisation systématique.'}, |
|
407 |
'30': {'message': 'Le pays de l’adresse IP appartient à la liste grise.'}, |
|
408 |
'99': {'message': 'Problème technique recontré par le serveur lors du traitement ' |
|
409 |
'd\'un des contrôles locauxi.'}, |
|
410 |
} |
|
411 | ||
412 |
@classmethod |
|
413 |
def make_eopayment_result(cls, fields): |
|
414 |
# https://paiement.systempay.fr/doc/fr-FR/payment-file/oneclick-payment/vads-result.html |
|
415 |
# https://paiement.systempay.fr/doc/fr-FR/payment-file/oneclick-payment/vads-auth-result.html |
|
416 |
# https://paiement.systempay.fr/doc/fr-FR/payment-file/oneclick-payment/vads-extra-result.html |
|
417 |
vads_result = fields.get(VADS_RESULT) |
|
418 |
vads_auth_result = fields.get(VADS_AUTH_RESULT) |
|
419 |
vads_extra_result = fields.get(VADS_EXTRA_RESULT) |
|
420 | ||
421 |
# map to human messages and update return |
|
422 |
vads_result_message = cls.RESULT_MAP.get(vads_result, {}).get('message') |
|
423 |
if vads_result_message: |
|
424 |
fields[VADS_RESULT + '_message'] = vads_result_message |
|
425 | ||
426 |
vads_extra_result_message = cls.EXTRA_RESULT_MAP.get(vads_extra_result, {}).get('message') |
|
427 |
if vads_extra_result_message: |
|
428 |
fields[VADS_EXTRA_RESULT + '_message'] = vads_extra_result_message |
|
429 | ||
430 |
vads_auth_result_message, auth_eopayment_result = translate_cb_error_code(vads_auth_result) |
|
431 |
if vads_auth_result_message: |
|
432 |
fields[VADS_AUTH_RESULT + '_message'] = vads_auth_result_message |
|
433 | ||
434 |
# now build eopayment resume |
|
435 |
if vads_result is None: |
|
436 |
return ERROR, 'absence de champ vads_result' |
|
437 |
if vads_result_message is None: |
|
438 |
return ERROR, 'valeur vads_result inconnue' |
|
439 | ||
440 |
result = cls.RESULT_MAP[vads_result].get('result', ERROR) |
|
441 |
message = vads_result_message |
|
442 |
if vads_auth_result_message and (vads_result != '00' or vads_result != vads_auth_result): |
|
443 |
message += ' ' + vads_auth_result_message |
|
444 |
if vads_result in ('00', '05', '30') and vads_extra_result_message and vads_extra_result != '': |
|
445 |
message += ' ' + vads_extra_result_message |
|
446 |
if result == ERROR and auth_eopayment_result not in (PAID, ERROR): |
|
447 |
result = auth_eopayment_result |
|
448 |
return result, message |
|
449 | ||
409 | 450 |
def response(self, query_string, **kwargs): |
410 | 451 |
fields = urlparse.parse_qs(query_string, True) |
411 | 452 |
if not set(fields) >= set([SIGNATURE, VADS_CTX_MODE, VADS_AUTH_RESULT]): |
... | ... | |
414 | 455 |
for key, value in fields.items(): |
415 | 456 |
fields[key] = value[0] |
416 | 457 |
copy = fields.copy() |
417 |
bank_status = [] |
|
418 |
if VADS_AUTH_RESULT in fields: |
|
419 |
v = copy[VADS_AUTH_RESULT] |
|
420 |
ctx = (v, AUTH_RESULT_MAP.get(v, 'Code inconnu')) |
|
421 |
copy[VADS_AUTH_RESULT] = '%s: %s' % ctx |
|
422 |
bank_status.append(copy[VADS_AUTH_RESULT]) |
|
423 |
if VADS_RESULT in copy: |
|
424 |
v = copy[VADS_RESULT] |
|
425 |
ctx = (v, RESULT_MAP.get(v, 'Code inconnu')) |
|
426 |
copy[VADS_RESULT] = '%s: %s' % ctx |
|
427 |
bank_status.append(copy[VADS_RESULT]) |
|
428 |
if v == '30': |
|
429 |
if VADS_EXTRA_RESULT in fields: |
|
430 |
v = fields[VADS_EXTRA_RESULT] |
|
431 |
if v.isdigit(): |
|
432 |
for parameter in PARAMETERS: |
|
433 |
if int(v) == parameter.code: |
|
434 |
s = 'erreur dans le champ %s' % parameter.name |
|
435 |
copy[VADS_EXTRA_RESULT] = s |
|
436 |
bank_status.append(copy[VADS_EXTRA_RESULT]) |
|
437 |
elif v in ('05', '00'): |
|
438 |
if VADS_EXTRA_RESULT in fields: |
|
439 |
v = fields[VADS_EXTRA_RESULT] |
|
440 |
extra_result_name = EXTRA_RESULT_MAP.get(v, 'Code inconnu') |
|
441 |
copy[VADS_EXTRA_RESULT] = '%s: %s' % (v, extra_result_name) |
|
442 |
bank_status.append(copy[VADS_EXTRA_RESULT]) |
|
443 |
self.logger.debug('checking systempay response on:') |
|
444 |
for key in sorted(fields.keys()): |
|
445 |
self.logger.debug(' %s: %s' % (key, copy[key])) |
|
446 | 458 |
signature = self.signature(fields) |
459 |
result, message = self.make_eopayment_result(copy) |
|
460 |
self.logger.debug('checking systempay response on: %r', copy) |
|
447 | 461 |
signature_result = signature == fields[SIGNATURE] |
448 |
self.logger.debug('signature check: %s <!> %s', signature, |
|
449 |
fields[SIGNATURE]) |
|
450 | 462 |
if not signature_result: |
451 |
bank_status.append('invalid signature') |
|
463 |
self.logger.debug('signature check: %s <!> %s', signature, |
|
464 |
fields[SIGNATURE]) |
|
465 | ||
466 |
if not signature_result: |
|
467 |
message += ' signature invalide.' |
|
452 | 468 | |
453 |
if fields[VADS_AUTH_RESULT] == '00': |
|
454 |
result = PAID |
|
455 |
else: |
|
456 |
result = ERROR |
|
457 | 469 |
test = fields[VADS_CTX_MODE] == 'TEST' |
458 | 470 |
transaction_id = '%s_%s' % (copy[VADS_TRANS_DATE], copy[VADS_TRANS_ID]) |
459 | 471 |
# the VADS_AUTH_NUMBER is the number to match payment in bank logs |
... | ... | |
467 | 479 |
bank_data=copy, |
468 | 480 |
order_id=transaction_id, |
469 | 481 |
transaction_id=copy.get(VADS_AUTH_NUMBER), |
470 |
bank_status=' - '.join(bank_status),
|
|
482 |
bank_status=message,
|
|
471 | 483 |
transaction_date=transaction_date, |
472 | 484 |
test=test) |
473 | 485 |
return response |
tests/test_systempayv2.py | ||
---|---|---|
41 | 41 |
return field |
42 | 42 | |
43 | 43 | |
44 |
def test_systempayv2(): |
|
44 |
def test_systempayv2(caplog): |
|
45 |
caplog.set_level(0) |
|
45 | 46 |
p = Payment(PARAMS) |
46 | 47 |
data = { |
47 | 48 |
'amount': 15.24, |
... | ... | |
68 | 69 | |
69 | 70 |
response_qs = 'vads_amount=1042&vads_auth_mode=FULL&vads_auth_number=3feadf' \ |
70 | 71 |
'&vads_auth_result=00&vads_capture_delay=0&vads_card_brand=CB' \ |
72 |
'&vads_result=00' \ |
|
71 | 73 |
'&vads_card_number=497010XXXXXX0000' \ |
72 | 74 |
'&vads_payment_certificate=582ba2b725057618706d7a06e9e59acdbf69ff53' \ |
73 | 75 |
'&vads_ctx_mode=TEST&vads_currency=978&vads_effective_amount=1042' \ |
74 | 76 |
'&vads_site_id=70168983&vads_trans_date=20161013101355' \ |
75 | 77 |
'&vads_trans_id=226787&vads_trans_uuid=4b5053b3b1fe4b02a07753e7a' \ |
76 | 78 |
'&vads_effective_creation_date=20200330162530' \ |
77 |
'&signature=faca0ef814d55a860996e28f84de9a9b29ddeca2'
|
|
79 |
'&signature=c17fab393f94dc027dc029510c85d5fc46c4710f'
|
|
78 | 80 |
response = p.response(response_qs) |
79 | 81 |
assert response.result == PAID |
80 | 82 |
assert response.signed |
... | ... | |
85 | 87 |
p = Payment(PARAMS) |
86 | 88 |
assert p.signature(qs) == 'aHrJ7IzSGFa4pcYA8kh99+M/xBzoQ4Odnu3f4BUrpIA=' |
87 | 89 |
response_qs = 'vads_amount=1042&vads_auth_mode=FULL&vads_auth_number=3feadf' \ |
90 |
'&vads_result=00' \ |
|
88 | 91 |
'&vads_auth_result=00&vads_capture_delay=0&vads_card_brand=CB' \ |
89 | 92 |
'&vads_card_number=497010XXXXXX0000' \ |
90 | 93 |
'&vads_payment_certificate=582ba2b725057618706d7a06e9e59acdbf69ff53' \ |
... | ... | |
92 | 95 |
'&vads_site_id=70168983&vads_trans_date=20161013101355' \ |
93 | 96 |
'&vads_trans_id=226787&vads_trans_uuid=4b5053b3b1fe4b02a07753e7a' \ |
94 | 97 |
'&vads_effective_creation_date=20200330162530' \ |
95 |
'&signature=PeU30M6ilqwhligBAIMQIR3yqxWFGZHJ8Hwtb%2B3IrOM%3D'
|
|
98 |
'&signature=Wbz3bP6R6wDvAwb2HnSiH9%2FiUUoRVCxK7mdLtCMz8Xw%3D'
|
|
96 | 99 |
response = p.response(response_qs) |
100 |
assert response.result == PAID |
|
97 | 101 |
assert response.signed |
98 | 102 | |
99 | 103 |
# bad response |
100 |
- |