Add a new PaymentResponse to abstract the return value of the response method
This commit is contained in:
parent
e9e3f0cc63
commit
8dee7ee341
|
@ -28,20 +28,23 @@ class PaymentResponse(object):
|
|||
a response transmitted directly from the bank to the merchant website,
|
||||
you usually have to confirm good reception of the message by returning a
|
||||
properly formatted response, this is it.
|
||||
bank_error_status -- if result is False, it contains the reason
|
||||
bank_status -- if result is False, it contains the reason
|
||||
order_id -- the id given by the merchant in the payment request
|
||||
transaction_id -- the id assigned by the bank to this transaction, it
|
||||
could be the one sent by the merchant in the request, but it is usually
|
||||
an identifier internal to the bank.
|
||||
'''
|
||||
|
||||
def __init__(self, result=None, signed_result=None, bank_data=dict(),
|
||||
return_content=None, bank_error_status='', transaction_id=''):
|
||||
return_content=None, bank_status='', transaction_id='',
|
||||
order_id=''):
|
||||
self.result = result
|
||||
self.signed_result = signed_result
|
||||
self.bank_data = bank_data
|
||||
self.return_content = return_content
|
||||
self.bank_error_status = bank_error_status
|
||||
self.bank_status = bank_status
|
||||
self.transaction_id = transaction_id
|
||||
self.order_id = order_id
|
||||
|
||||
class PaymentCommon(object):
|
||||
PATH = '/tmp'
|
||||
|
|
|
@ -5,7 +5,7 @@ try:
|
|||
except:
|
||||
from urlparse import parse_qs
|
||||
|
||||
from common import PaymentCommon, URL
|
||||
from common import PaymentCommon, URL, PaymentResponse
|
||||
|
||||
__all__ = [ 'Payment' ]
|
||||
|
||||
|
@ -38,20 +38,31 @@ class Payment(PaymentCommon):
|
|||
'dummy_service_url': {
|
||||
'caption': 'URL of the dummy payment service',
|
||||
'default': SERVICE_URL,
|
||||
'type': string,
|
||||
},
|
||||
'direct_notification_url': {
|
||||
'caption': 'direct notification url',
|
||||
'type': string,
|
||||
},
|
||||
'origin': {
|
||||
'caption': 'name of the requesting service, '
|
||||
'to present in the user interface'
|
||||
'to present in the user interface',
|
||||
'type': string,
|
||||
|
||||
},
|
||||
'siret': {
|
||||
'caption': 'dummy siret parameter',
|
||||
'type': string,
|
||||
},
|
||||
'next_url': {
|
||||
'caption': 'Return URL for the user',
|
||||
}
|
||||
'type': string,
|
||||
},
|
||||
'consider_all_response_signed': {
|
||||
'caption': '',
|
||||
'type': bool,
|
||||
'default': False,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,7 +91,15 @@ class Payment(PaymentCommon):
|
|||
content = 'signature ok'
|
||||
else:
|
||||
content = None
|
||||
return 'ok' in form and transaction_id and True, transaction_id, form, content
|
||||
|
||||
response = PaymentResponse(result='ok' in form,
|
||||
signed_result='ok' in form and 'signed' in form,
|
||||
bank_data=form,
|
||||
return_content=content,
|
||||
order_id=transaction_id,
|
||||
transaction_id=transaction_id,
|
||||
bank_status=form.get('reason'))
|
||||
return response
|
||||
|
||||
if __name__ == '__main__':
|
||||
options = {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import urlparse
|
||||
import string
|
||||
import subprocess
|
||||
|
@ -7,7 +8,7 @@ import os
|
|||
import os.path
|
||||
import uuid
|
||||
|
||||
from common import PaymentCommon, HTML
|
||||
from common import PaymentCommon, HTML, PaymentResponse
|
||||
|
||||
'''
|
||||
Payment backend module for the ATOS/SIPS system used by many Frenck banks.
|
||||
|
@ -61,6 +62,63 @@ DEFAULT_PARAMS = { 'merchant_id': '014213245611111',
|
|||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CB_BANK_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',
|
||||
}
|
||||
|
||||
AMEX_BANK_RESPONSE_CODE = {
|
||||
'00': 'Transaction approuvée ou traitée avec succès',
|
||||
'02': 'Dépassement de plafond',
|
||||
'04': 'Conserver la carte',
|
||||
'05': 'Ne pas honorer',
|
||||
'97': 'Échéance de la temporisation de surveillance globale',
|
||||
}
|
||||
|
||||
FINAREF_BANK_RESPONSE_CODE = {
|
||||
'00': 'Transaction approuvée',
|
||||
'03': 'Commerçant inconnu - Identifiant de commerçant incorrect',
|
||||
'05': 'Compte / Porteur avec statut bloqué ou invalide',
|
||||
'11': 'Compte / porteur inconnu',
|
||||
'16': 'Provision insuffisante',
|
||||
'20': 'Commerçant invalide - Code monnaie incorrect - Opération commerciale inconnue - Opération commerciale invalide',
|
||||
'80': 'Transaction approuvée avec dépassement',
|
||||
'81': 'Transaction approuvée avec augmentation capital',
|
||||
'82': 'Transaction approuvée NPAI',
|
||||
'83': 'Compte / porteur invalide',
|
||||
}
|
||||
|
||||
class Payment(PaymentCommon):
|
||||
def __init__(self, options):
|
||||
self.options = options
|
||||
|
@ -106,6 +164,16 @@ class Payment(PaymentCommon):
|
|||
result = self.execute('response', params)
|
||||
d = dict(zip(RESPONSE_PARAMS, result))
|
||||
# The reference identifier for the payment is the authorisation_id
|
||||
d[self.BANK_ID] = result.get(AUTHORISATION_ID, '')
|
||||
d[self.BANK_ID] = d.get(AUTHORISATION_ID)
|
||||
LOGGER.debug('response contains fields %s' % d)
|
||||
return result.get(RESPONSE_CODE) == '00', form.get(ORDER_ID), d, None
|
||||
response_result = d.get(RESPONSE_CODE) == '00'
|
||||
response_code_msg = CB_BANK_RESPONSE_CODES.get(d.get(RESPONSE_CODE))
|
||||
response = PaymentResponse(
|
||||
result=response_result,
|
||||
signed_result=response_result,
|
||||
bank_data=d,
|
||||
order_id=d.get(ORDER_ID),
|
||||
transaction_id=d.get(AUTHORISATION_ID),
|
||||
bank_status=response_code_msg)
|
||||
|
||||
return None
|
||||
|
|
|
@ -10,7 +10,7 @@ import logging
|
|||
import re
|
||||
|
||||
import Crypto.Cipher.DES
|
||||
from common import PaymentCommon, URL
|
||||
from common import PaymentCommon, URL, PaymentResponse
|
||||
|
||||
__all__ = ['Payment']
|
||||
|
||||
|
@ -122,21 +122,35 @@ next_url=%s' % (montant, email, next_url))
|
|||
LOGGER.debug('received query_string %s' % query_string)
|
||||
LOGGER.debug('parsed as %s' % form)
|
||||
reference = form.get(REFERENCE)
|
||||
if not 'hmac' in form:
|
||||
return form.get('etat') == 1, reference, form, None
|
||||
else:
|
||||
bank_status = ''
|
||||
signed_result = None
|
||||
result = form.get('etat') == 1
|
||||
form[self.BANK_ID] = form.get(REFSFP)
|
||||
if 'hmac' in form:
|
||||
try:
|
||||
signed_data, signature = query_string.rsplit('&', 1)
|
||||
_, hmac = signature.split('=', 1)
|
||||
LOGGER.debug('got signature %s' % hmac)
|
||||
computed_hmac = sign_ntkey_query(self.cle, signed_data)
|
||||
LOGGER.debug('computed signature %s' % hmac)
|
||||
result = hmac==computed_hmac \
|
||||
result = signed_result = hmac==computed_hmac \
|
||||
and form.get(ETAT) == ETAT_PAIEMENT_ACCEPTE
|
||||
form[self.BANK_ID] = form.get(REFSFP, '')
|
||||
return result, reference, form, SPCHECKOK
|
||||
if not signed_result:
|
||||
result = False
|
||||
bank_status = 'invalid signature'
|
||||
except ValueError:
|
||||
return False, reference, form, SPCHECKOK
|
||||
result = signed_result = False
|
||||
|
||||
response = PaymentResponse(
|
||||
result=result,
|
||||
signed_result=signed_result,
|
||||
bank_data=form,
|
||||
order_id=reference,
|
||||
transaction_id=form[self.BANK_ID],
|
||||
bank_status=bank_status,
|
||||
return_content=SPCHECKOK if 'hmac' in form else None)
|
||||
return response
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
|
|
|
@ -8,7 +8,7 @@ import urlparse
|
|||
import urllib
|
||||
from decimal import Decimal
|
||||
|
||||
from common import PaymentCommon, URL
|
||||
from common import PaymentCommon, URL, PaymentResponse
|
||||
|
||||
__all__ = ['Payment']
|
||||
|
||||
|
@ -17,6 +17,11 @@ LOGGER = logging.getLogger(__name__)
|
|||
SERVICE_URL = '???'
|
||||
VADS_TRANS_DATE = 'vads_trans_date'
|
||||
VADS_AUTH_NUMBER = 'vads_auth_number'
|
||||
VADS_AUTH_RESULT = 'vads_auth_result'
|
||||
VADS_RESULT = 'vads_result'
|
||||
VADS_EXTRA_RESULT = 'vads_extra_result'
|
||||
SIGNATURE = 'signature'
|
||||
VADS_TRANS_ID = 'vads_trans_id'
|
||||
|
||||
def isonow():
|
||||
return dt.datetime.now() \
|
||||
|
@ -232,7 +237,7 @@ parameters received: %s' % (name, kwargs))
|
|||
raise TypeError('%s value %s is not of the type %s' % (
|
||||
name, fields[name],
|
||||
parameter.ptype))
|
||||
fields['signature'] = self.signature(fields)
|
||||
fields[SIGNATURE] = self.signature(fields)
|
||||
url = '%s?%s' % (SERVICE_URL, urllib.urlencode(fields))
|
||||
transaction_id = '%s_%s' % (fields[VADS_TRANS_DATE], transaction_id)
|
||||
return transaction_id, URL, fields
|
||||
|
@ -240,36 +245,52 @@ parameters received: %s' % (name, kwargs))
|
|||
def response(self, query_string):
|
||||
fields = urlparse.parse_qs(query_string)
|
||||
copy = fields.copy()
|
||||
if 'vads_auth_result' in fields:
|
||||
v = copy['vads_auth_result']
|
||||
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
|
||||
if 'vads_result' in copy:
|
||||
v = copy['vads_result']
|
||||
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
|
||||
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 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
|
||||
fields['vads_extra_result'] = s
|
||||
copy[VADS_EXTRA_RESULT] = s
|
||||
bank_status.append(copy[VADS_EXTRA_RESULT])
|
||||
elif v in ('05', '00'):
|
||||
v = fields['vads_extra_result']
|
||||
fields['vads_extra_result'] = '%s: %s' % (v,
|
||||
v = fields[VADS_EXTRA_RESULT]
|
||||
copy[VADS_EXTRA_RESULT] = '%s: %s' % (v,
|
||||
EXTRA_RESULT_MAP.get(v, 'Code inconnu'))
|
||||
bank_status.append(copy[VADS_EXTRA_RESULT])
|
||||
LOGGER.debug('checking systempay response on:')
|
||||
for key in sorted(fields.keys):
|
||||
LOGGER.debug(' %s: %s' % (key, copy[key]))
|
||||
signature = self.signature(fields)
|
||||
result = signature == fields['signature']
|
||||
signature_result = signature == fields[SIGNATURE]
|
||||
if not signature_result:
|
||||
bank_status.append('invalid signature')
|
||||
result = fields[VADS_AUTH_RESULT] == '00'
|
||||
signed_result = signature_result and result
|
||||
LOGGER.debug('signature check result: %s' % result)
|
||||
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
|
||||
copy[self.BANK_ID] = copy.get(copy[VADS_AUTH_NUMBER], '')
|
||||
return result, transaction_id, copy, None
|
||||
copy[self.BANK_ID] = copy.get(VADS_AUTH_NUMBER, '')
|
||||
response = PaymentResponse(
|
||||
result=result,
|
||||
signed_result=signed_result,
|
||||
bankd_data=copy,
|
||||
order_id=transaction_id,
|
||||
transaction_id=copy.get(VADS_AUTH_NUMBER),
|
||||
bank_status=' - '.join(bank_status))
|
||||
return response
|
||||
|
||||
def signature(self, fields):
|
||||
LOGGER.debug('got fields %s to sign' % fields )
|
||||
|
|
Loading…
Reference in New Issue