Projet

Général

Profil

0001-systempayv2-map-DENIED-and-CANCELLED-result-codes-17.patch

Benjamin Dauvergne, 12 octobre 2020 14:02

Télécharger (15,5 ko)

Voir les différences:

Subject: [PATCH] systempayv2: map DENIED and CANCELLED result codes (#17065)

 eopayment/cb.py           |  78 ++++++++++++---------
 eopayment/systempayv2.py  | 142 +++++++++++++++++++++-----------------
 tests/test_systempayv2.py |  10 ++-
 3 files changed, 129 insertions(+), 101 deletions(-)
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 locaux'},
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_result in ('00', '05', '30') and vads_extra_result_message:
443
            message += ' ' + vads_extra_result_message
444
        if vads_auth_result_message:
445
            message += ' ' + vads_auth_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
-