summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Dauvergne <bdauvergne@entrouvert.com>2020-10-11 07:43:07 (GMT)
committerBenjamin Dauvergne <bdauvergne@entrouvert.com>2020-10-15 19:05:36 (GMT)
commit7fa4e3564124ecfadec9717bc4ed231961686bac (patch)
tree34d7fec1c5bf22d89ef72d871881206b6f43c499
parentb1ebd698b3f206d4d389c5a979ebd88fe38e0419 (diff)
downloadeopayment-7fa4e3564124ecfadec9717bc4ed231961686bac.zip
eopayment-7fa4e3564124ecfadec9717bc4ed231961686bac.tar.gz
eopayment-7fa4e3564124ecfadec9717bc4ed231961686bac.tar.bz2
systempayv2: map DENIED and CANCELLED result codes (#17065)
-rw-r--r--eopayment/cb.py78
-rw-r--r--eopayment/systempayv2.py142
-rw-r--r--tests/test_systempayv2.py10
3 files changed, 129 insertions, 101 deletions
diff --git a/eopayment/cb.py b/eopayment/cb.py
index ccd7f69..6ab828d 100644
--- a/eopayment/cb.py
+++ b/eopayment/cb.py
@@ -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
diff --git a/eopayment/systempayv2.py b/eopayment/systempayv2.py
index 5c6582a..ed5da47 100644
--- a/eopayment/systempayv2.py
+++ b/eopayment/systempayv2.py
@@ -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
diff --git a/tests/test_systempayv2.py b/tests/test_systempayv2.py
index e796360..3b64f60 100644
--- a/tests/test_systempayv2.py
+++ b/tests/test_systempayv2.py
@@ -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