systempayv2: map DENIED and CANCELLED result codes (#17065)
This commit is contained in:
parent
b1ebd698b3
commit
7fa4e35641
|
@ -17,38 +17,50 @@
|
|||
|
||||
'''Responses codes emitted by EMV Card or 'Carte Bleu' in France'''
|
||||
|
||||
from . import PAID, CANCELLED, ERROR, DENIED
|
||||
|
||||
|
||||
CB_RESPONSE_CODES = {
|
||||
'00': 'Transaction approuvée ou traitée avec succès',
|
||||
'02': 'Contacter l\'émetteur de carte',
|
||||
'03': 'Accepteur invalide',
|
||||
'04': 'Conserver la carte',
|
||||
'05': 'Ne pas honorer',
|
||||
'07': 'Conserver la carte, conditions spéciales',
|
||||
'08': 'Approuver après identification',
|
||||
'12': 'Transaction invalide',
|
||||
'13': 'Montant invalide',
|
||||
'14': 'Numéro de porteur invalide',
|
||||
'15': 'Emetteur de carte inconnu',
|
||||
'30': 'Erreur de format',
|
||||
'31': 'Identifiant de l\'organisme acquéreur inconnu',
|
||||
'33': 'Date de validité de la carte dépassée',
|
||||
'34': 'Suspicion de fraude',
|
||||
'41': 'Carte perdue',
|
||||
'43': 'Carte volée',
|
||||
'51': 'Provision insuffisante ou crédit dépassé',
|
||||
'54': 'Date de validité de la carte dépassée',
|
||||
'56': 'Carte absente du fichier',
|
||||
'57': 'Transaction non permise à ce porteur',
|
||||
'58': 'Transaction interdite au terminal',
|
||||
'59': 'Suspicion de fraude',
|
||||
'60': 'L\'accepteur de carte doit contacter l\'acquéreur',
|
||||
'61': 'Dépasse la limite du montant de retrait',
|
||||
'63': 'Règles de sécurité non respectées',
|
||||
'68': 'Réponse non parvenue ou reçue trop tard',
|
||||
'90': 'Arrêt momentané du système',
|
||||
'91': 'Emetteur de cartes inaccessible',
|
||||
'96': 'Mauvais fonctionnement du système',
|
||||
'97': 'Échéance de la temporisation de surveillance globale',
|
||||
'98': 'Serveur indisponible routage réseau demandé à nouveau',
|
||||
'99': 'Incident domaine initiateur',
|
||||
'00': {'message': 'Transaction approuvée ou traitée avec succès', 'result': PAID},
|
||||
'02': {'message': 'Contacter l\'émetteur de carte'},
|
||||
'03': {'message': 'Accepteur invalide'},
|
||||
'04': {'message': 'Conserver la carte'},
|
||||
'05': {'message': 'Ne pas honorer', 'result': DENIED},
|
||||
'07': {'message': 'Conserver la carte, conditions spéciales'},
|
||||
'08': {'message': 'Approuver après identification'},
|
||||
'12': {'message': 'Transaction invalide'},
|
||||
'13': {'message': 'Montant invalide'},
|
||||
'14': {'message': 'Numéro de porteur invalide'},
|
||||
'15': {'message': 'Emetteur de carte inconnu'},
|
||||
'17': {'message': 'Annulation par l\'acheteur', 'result': CANCELLED},
|
||||
'30': {'message': 'Erreur de format'},
|
||||
'31': {'message': 'Identifiant de l\'organisme acquéreur inconnu'},
|
||||
'33': {'message': 'Date de validité de la carte dépassée'},
|
||||
'34': {'message': 'Suspicion de fraude'},
|
||||
'41': {'message': 'Carte perdue'},
|
||||
'43': {'message': 'Carte volée'},
|
||||
'51': {'message': 'Provision insuffisante ou crédit dépassé'},
|
||||
'54': {'message': 'Date de validité de la carte dépassée'},
|
||||
'56': {'message': 'Carte absente du fichier'},
|
||||
'57': {'message': 'Transaction non permise à ce porteur'},
|
||||
'58': {'message': 'Transaction interdite au terminal'},
|
||||
'59': {'message': 'Suspicion de fraude'},
|
||||
'60': {'message': 'L\'accepteur de carte doit contacter l\'acquéreur'},
|
||||
'61': {'message': 'Dépasse la limite du montant de retrait'},
|
||||
'63': {'message': 'Règles de sécurité non respectées'},
|
||||
'68': {'message': 'Réponse non parvenue ou reçue trop tard'},
|
||||
'90': {'message': 'Arrêt momentané du système'},
|
||||
'91': {'message': 'Emetteur de cartes inaccessible'},
|
||||
'96': {'message': 'Mauvais fonctionnement du système'},
|
||||
'97': {'message': 'Échéance de la temporisation de surveillance globale'},
|
||||
'98': {'message': 'Serveur indisponible routage réseau demandé à nouveau'},
|
||||
'99': {'message': 'Incident domaine initiateur'},
|
||||
}
|
||||
|
||||
|
||||
def translate_cb_error_code(error_code):
|
||||
'Returns message, eopayment_error_code'
|
||||
|
||||
if error_code in CB_RESPONSE_CODES:
|
||||
return CB_RESPONSE_CODES[error_code]['message'], CB_RESPONSE_CODES[error_code].get('result', ERROR)
|
||||
return None, None
|
||||
|
|
|
@ -28,9 +28,9 @@ from six.moves.urllib import parse as urlparse
|
|||
import warnings
|
||||
from gettext import gettext as _
|
||||
|
||||
from .common import (PaymentCommon, PaymentResponse, PAID, ERROR, FORM, Form,
|
||||
ResponseError, force_text, force_byte)
|
||||
from .cb import CB_RESPONSE_CODES
|
||||
from .common import (PaymentCommon, PaymentResponse, PAID, DENIED, CANCELLED,
|
||||
ERROR, FORM, Form, ResponseError, force_text, force_byte)
|
||||
from .cb import translate_cb_error_code
|
||||
|
||||
__all__ = ['Payment']
|
||||
|
||||
|
@ -179,31 +179,6 @@ PARAMETERS = [
|
|||
PARAMETER_MAP = dict(((parameter.name,
|
||||
parameter) for parameter in PARAMETERS))
|
||||
|
||||
AUTH_RESULT_MAP = CB_RESPONSE_CODES
|
||||
|
||||
RESULT_MAP = {
|
||||
'00': 'paiement réalisé avec succés',
|
||||
'02': 'le commerçant doit contacter la banque du porteur',
|
||||
'05': 'paiement refusé',
|
||||
'17': 'annulation client',
|
||||
'30': 'erreur de format',
|
||||
'96': 'erreur technique lors du paiement'
|
||||
}
|
||||
|
||||
EXTRA_RESULT_MAP = {
|
||||
'': "Pas de contrôle effectué",
|
||||
'00': "Tous les contrôles se sont déroulés avec succés",
|
||||
'02': "La carte a dépassé l'encours autorisé",
|
||||
'03': "La carte appartient à la liste grise du commerçant",
|
||||
'04': "Le pays d'émission de la carte appartient à la liste grise du "
|
||||
"commerçant ou le pays d'émission de la carte n'appartient pas à la "
|
||||
"liste blanche du commerçant",
|
||||
'05': "L'addresse IP appartient à la liste grise du commerçant",
|
||||
'99': "Problème technique recontré par le serveur lors du traitement "
|
||||
"d'un des contrôles locaux",
|
||||
}
|
||||
|
||||
|
||||
def add_vads(kwargs):
|
||||
new_vargs = {}
|
||||
for k, v in kwargs.items():
|
||||
|
@ -426,6 +401,72 @@ class Payment(PaymentCommon):
|
|||
for field_name, field_value in fields.items()])
|
||||
return transaction_id, FORM, form
|
||||
|
||||
RESULT_MAP = {
|
||||
'00': {'message': 'Paiement réalisé avec succés.', 'result': PAID},
|
||||
'02': {'message': 'Le commerçant doit contacter la banque du porteur.'},
|
||||
'05': {'message': 'Paiement refusé.', 'result': DENIED},
|
||||
'17': {'message': 'Annulation client.', 'result': CANCELLED},
|
||||
'30': {'message': 'Erreur de format.'},
|
||||
'96': {'message': 'Erreur technique lors du paiement.'},
|
||||
}
|
||||
|
||||
EXTRA_RESULT_MAP = {
|
||||
'': {'message': 'Pas de contrôle effectué.'},
|
||||
'00': {'message': 'Tous les contrôles se sont déroulés avec succés.'},
|
||||
'02': {'message': 'La carte a dépassé l\'encours autorisé.'},
|
||||
'03': {'message': 'La carte appartient à la liste grise du commerçant.'},
|
||||
'04': {'messaǵe': 'Le pays d\'émission de la carte appartient à la liste grise du '
|
||||
'commerçant ou le pays d\'émission de la carte n\'appartient pas à la '
|
||||
'liste blanche du commerçant.'},
|
||||
'05': {'message': 'L’adresse IP appartient à la liste grise du marchand.'},
|
||||
'06': {'message': 'Le code bin appartient à la liste grise du marchand.'},
|
||||
'07': {'message': 'Détection d’une e-carte bleue.'},
|
||||
'08': {'message': 'Détection d’une carte commerciale nationale.'},
|
||||
'09': {'message': 'Détection d’une carte commerciale étrangère.'},
|
||||
'14': {'message': 'Détection d’une carte à autorisation systématique.'},
|
||||
'30': {'message': 'Le pays de l’adresse IP appartient à la liste grise.'},
|
||||
'99': {'message': 'Problème technique recontré par le serveur lors du traitement '
|
||||
'd\'un des contrôles locauxi.'},
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def make_eopayment_result(cls, fields):
|
||||
# https://paiement.systempay.fr/doc/fr-FR/payment-file/oneclick-payment/vads-result.html
|
||||
# https://paiement.systempay.fr/doc/fr-FR/payment-file/oneclick-payment/vads-auth-result.html
|
||||
# https://paiement.systempay.fr/doc/fr-FR/payment-file/oneclick-payment/vads-extra-result.html
|
||||
vads_result = fields.get(VADS_RESULT)
|
||||
vads_auth_result = fields.get(VADS_AUTH_RESULT)
|
||||
vads_extra_result = fields.get(VADS_EXTRA_RESULT)
|
||||
|
||||
# map to human messages and update return
|
||||
vads_result_message = cls.RESULT_MAP.get(vads_result, {}).get('message')
|
||||
if vads_result_message:
|
||||
fields[VADS_RESULT + '_message'] = vads_result_message
|
||||
|
||||
vads_extra_result_message = cls.EXTRA_RESULT_MAP.get(vads_extra_result, {}).get('message')
|
||||
if vads_extra_result_message:
|
||||
fields[VADS_EXTRA_RESULT + '_message'] = vads_extra_result_message
|
||||
|
||||
vads_auth_result_message, auth_eopayment_result = translate_cb_error_code(vads_auth_result)
|
||||
if vads_auth_result_message:
|
||||
fields[VADS_AUTH_RESULT + '_message'] = vads_auth_result_message
|
||||
|
||||
# now build eopayment resume
|
||||
if vads_result is None:
|
||||
return ERROR, 'absence de champ vads_result'
|
||||
if vads_result_message is None:
|
||||
return ERROR, 'valeur vads_result inconnue'
|
||||
|
||||
result = cls.RESULT_MAP[vads_result].get('result', ERROR)
|
||||
message = vads_result_message
|
||||
if vads_auth_result_message and (vads_result != '00' or vads_result != vads_auth_result):
|
||||
message += ' ' + vads_auth_result_message
|
||||
if vads_result in ('00', '05', '30') and vads_extra_result_message and vads_extra_result != '':
|
||||
message += ' ' + vads_extra_result_message
|
||||
if result == ERROR and auth_eopayment_result not in (PAID, ERROR, None):
|
||||
result = auth_eopayment_result
|
||||
return result, message
|
||||
|
||||
def response(self, query_string, **kwargs):
|
||||
fields = urlparse.parse_qs(query_string, True)
|
||||
if not set(fields) >= set([SIGNATURE, VADS_CTX_MODE, VADS_AUTH_RESULT]):
|
||||
|
@ -434,46 +475,17 @@ class Payment(PaymentCommon):
|
|||
for key, value in fields.items():
|
||||
fields[key] = value[0]
|
||||
copy = fields.copy()
|
||||
bank_status = []
|
||||
if VADS_AUTH_RESULT in fields:
|
||||
v = copy[VADS_AUTH_RESULT]
|
||||
ctx = (v, AUTH_RESULT_MAP.get(v, 'Code inconnu'))
|
||||
copy[VADS_AUTH_RESULT] = '%s: %s' % ctx
|
||||
bank_status.append(copy[VADS_AUTH_RESULT])
|
||||
if VADS_RESULT in copy:
|
||||
v = copy[VADS_RESULT]
|
||||
ctx = (v, RESULT_MAP.get(v, 'Code inconnu'))
|
||||
copy[VADS_RESULT] = '%s: %s' % ctx
|
||||
bank_status.append(copy[VADS_RESULT])
|
||||
if v == '30':
|
||||
if VADS_EXTRA_RESULT in fields:
|
||||
v = fields[VADS_EXTRA_RESULT]
|
||||
if v.isdigit():
|
||||
for parameter in PARAMETERS:
|
||||
if int(v) == parameter.code:
|
||||
s = 'erreur dans le champ %s' % parameter.name
|
||||
copy[VADS_EXTRA_RESULT] = s
|
||||
bank_status.append(copy[VADS_EXTRA_RESULT])
|
||||
elif v in ('05', '00'):
|
||||
if VADS_EXTRA_RESULT in fields:
|
||||
v = fields[VADS_EXTRA_RESULT]
|
||||
extra_result_name = EXTRA_RESULT_MAP.get(v, 'Code inconnu')
|
||||
copy[VADS_EXTRA_RESULT] = '%s: %s' % (v, extra_result_name)
|
||||
bank_status.append(copy[VADS_EXTRA_RESULT])
|
||||
self.logger.debug('checking systempay response on:')
|
||||
for key in sorted(fields.keys()):
|
||||
self.logger.debug(' %s: %s' % (key, copy[key]))
|
||||
signature = self.signature(fields)
|
||||
result, message = self.make_eopayment_result(copy)
|
||||
self.logger.debug('checking systempay response on: %r', copy)
|
||||
signature_result = signature == fields[SIGNATURE]
|
||||
self.logger.debug('signature check: %s <!> %s', signature,
|
||||
fields[SIGNATURE])
|
||||
if not signature_result:
|
||||
bank_status.append('invalid signature')
|
||||
self.logger.debug('signature check: %s <!> %s', signature,
|
||||
fields[SIGNATURE])
|
||||
|
||||
if not signature_result:
|
||||
message += ' signature invalide.'
|
||||
|
||||
if fields[VADS_AUTH_RESULT] == '00':
|
||||
result = PAID
|
||||
else:
|
||||
result = ERROR
|
||||
test = fields[VADS_CTX_MODE] == 'TEST'
|
||||
transaction_id = '%s_%s' % (copy[VADS_TRANS_DATE], copy[VADS_TRANS_ID])
|
||||
# the VADS_AUTH_NUMBER is the number to match payment in bank logs
|
||||
|
@ -487,7 +499,7 @@ class Payment(PaymentCommon):
|
|||
bank_data=copy,
|
||||
order_id=transaction_id,
|
||||
transaction_id=copy.get(VADS_AUTH_NUMBER),
|
||||
bank_status=' - '.join(bank_status),
|
||||
bank_status=message,
|
||||
transaction_date=transaction_date,
|
||||
test=test)
|
||||
return response
|
||||
|
|
|
@ -41,7 +41,8 @@ def get_field(form, field_name):
|
|||
return field
|
||||
|
||||
|
||||
def test_systempayv2():
|
||||
def test_systempayv2(caplog):
|
||||
caplog.set_level(0)
|
||||
p = Payment(PARAMS)
|
||||
data = {
|
||||
'amount': 15.24,
|
||||
|
@ -68,13 +69,14 @@ def test_systempayv2():
|
|||
|
||||
response_qs = 'vads_amount=1042&vads_auth_mode=FULL&vads_auth_number=3feadf' \
|
||||
'&vads_auth_result=00&vads_capture_delay=0&vads_card_brand=CB' \
|
||||
'&vads_result=00' \
|
||||
'&vads_card_number=497010XXXXXX0000' \
|
||||
'&vads_payment_certificate=582ba2b725057618706d7a06e9e59acdbf69ff53' \
|
||||
'&vads_ctx_mode=TEST&vads_currency=978&vads_effective_amount=1042' \
|
||||
'&vads_site_id=70168983&vads_trans_date=20161013101355' \
|
||||
'&vads_trans_id=226787&vads_trans_uuid=4b5053b3b1fe4b02a07753e7a' \
|
||||
'&vads_effective_creation_date=20200330162530' \
|
||||
'&signature=faca0ef814d55a860996e28f84de9a9b29ddeca2'
|
||||
'&signature=c17fab393f94dc027dc029510c85d5fc46c4710f'
|
||||
response = p.response(response_qs)
|
||||
assert response.result == PAID
|
||||
assert response.signed
|
||||
|
@ -85,6 +87,7 @@ def test_systempayv2():
|
|||
p = Payment(PARAMS)
|
||||
assert p.signature(qs) == 'aHrJ7IzSGFa4pcYA8kh99+M/xBzoQ4Odnu3f4BUrpIA='
|
||||
response_qs = 'vads_amount=1042&vads_auth_mode=FULL&vads_auth_number=3feadf' \
|
||||
'&vads_result=00' \
|
||||
'&vads_auth_result=00&vads_capture_delay=0&vads_card_brand=CB' \
|
||||
'&vads_card_number=497010XXXXXX0000' \
|
||||
'&vads_payment_certificate=582ba2b725057618706d7a06e9e59acdbf69ff53' \
|
||||
|
@ -92,8 +95,9 @@ def test_systempayv2():
|
|||
'&vads_site_id=70168983&vads_trans_date=20161013101355' \
|
||||
'&vads_trans_id=226787&vads_trans_uuid=4b5053b3b1fe4b02a07753e7a' \
|
||||
'&vads_effective_creation_date=20200330162530' \
|
||||
'&signature=PeU30M6ilqwhligBAIMQIR3yqxWFGZHJ8Hwtb%2B3IrOM%3D'
|
||||
'&signature=Wbz3bP6R6wDvAwb2HnSiH9%2FiUUoRVCxK7mdLtCMz8Xw%3D'
|
||||
response = p.response(response_qs)
|
||||
assert response.result == PAID
|
||||
assert response.signed
|
||||
|
||||
# bad response
|
||||
|
|
Loading…
Reference in New Issue