import zope from AccessControl import ClassSecurityInfo from Products.ERP5Type import Permissions, PropertySheet, interfaces from Products.ERP5Type.XMLObject import XMLObject from Products.ERP5Type.Document import newTempDocument import hashlib from zLOG import LOG, WARNING import datetime import os import time present = False tz = None if 'TZ' in os.environ: present = True tz = os.environ['TZ'] os.environ['TZ'] = 'UTC' time.tzset() try: import suds except ImportError: class PayzenSOAP: pass else: def setUTCTimeZone(fn): def wrapped(*args, **kwargs): present = False tz = None if 'TZ' in os.environ: present = True tz = os.environ['TZ'] os.environ['TZ'] = 'UTC' time.tzset() try: return fn(*args, **kwargs) finally: if present: os.environ['TZ'] = tz else: del(os.environ['TZ']) time.tzset() return wrapped class PayzenSOAP: """SOAP communication Methods are returning list of: * parsed response * signature check (True or False) * sent XML * received XML SOAP protocol is assumed as untrusted and dangerous, users of those methods are encouraged to log such messages for future debugging.""" def _check_transactionInfoSignature(self, data): """Checks transactionInfo signature Can raise. """ received_sorted_keys = ['errorCode', 'extendedErrorCode', 'transactionStatus', 'shopId', 'paymentMethod', 'contractNumber', 'orderId', 'orderInfo', 'orderInfo2', 'orderInfo3', 'transmissionDate', 'transactionId', 'sequenceNb', 'amount', 'initialAmount', 'devise', 'cvAmount', 'cvDevise', 'presentationDate', 'type', 'multiplePaiement', 'ctxMode', 'cardNumber', 'cardNetwork', 'cardType', 'cardCountry', 'cardExpirationDate', 'customerId', 'customerTitle', 'customerName', 'customerPhone', 'customerMail', 'customerAddress', 'customerZipCode', 'customerCity', 'customerCountry', 'customerLanguage', 'customerIP', 'transactionCondition', 'vadsEnrolled', 'vadsStatus', 'vadsECI', 'vadsXID', 'vadsCAVVAlgorithm', 'vadsCAVV', 'vadsSignatureValid', 'directoryServer', 'authMode', 'markAmount', 'markDevise', 'markDate', 'markNb', 'markResult', 'markCVV2_CVC2', 'authAmount', 'authDevise', 'authDate', 'authNb', 'authResult', 'authCVV2_CVC2', 'warrantlyResult', 'captureDate', 'captureNumber', 'rapprochementStatut', 'refoundAmount', 'refundDevise', 'litige', 'timestamp'] signature = self._getSignature(data, received_sorted_keys) return signature == data.signature @setUTCTimeZone def soap_getInfo(self, transmissionDate, transactionId): """Returns getInfo as dict, booelan, string, string transmissionDate is "raw" date in format YYYYMMDD, without any marks transactionId is id of transaction for this date As soon as communication happeneded does not raise. """ client = suds.client.Client(self.wsdl_link.getUrlString()) sorted_keys = ['shopId', 'transmissionDate', 'transactionId', 'sequenceNb', 'ctxMode'] kw = dict( transactionId=transactionId, ctxMode=self.getPayzenVadsCtxMode(), shopId=self.getServiceUsername(), sequenceNb=1, transmissionDate=transmissionDate, ) kw['wsSignature'] = self._getSignature(kw, sorted_keys) data = client.service.getInfo(**kw) # Note: Code shall not raise since now, as communication begin and caller # will have to log sent/received messages. try: data_kw = dict(data) for k in data_kw.keys(): v = data_kw[k] if not isinstance(v, str): data_kw[k] = str(v) except Exception: data_kw = {} signature = False LOG('PayzenService', WARNING, 'Issue during processing data_kw:', error=True) else: try: signature = self._check_transactionInfoSignature(data) except Exception: LOG('PayzenService', WARNING, 'Issue during signature calculation:', error=True) signature = False try: last_sent = str(client.last_sent()) except Exception: LOG('PayzenService', WARNING, 'Issue during converting last_sent to string:', error=True) signature = False try: last_received = str(client.last_received()) except Exception: LOG('PayzenService', WARNING, 'Issue during converting last_received to string:', error=True) signature = False return [data_kw, signature, last_sent, last_received] @setUTCTimeZone def soap_duplicate(self, transmissionDate, transactionId, presentationDate, newTransactionId, amount, devise, orderId='', orderInfo='', orderInfo2='', orderInfo3='', validationMode=0, comment=''): # prepare with passed parameters kw = dict(transmissionDate=transmissionDate, transactionId=transactionId, presentationDate=presentationDate, newTransactionId=newTransactionId, amount=amount, devise=devise, orderId=orderId, orderInfo=orderInfo, orderInfo2=orderInfo2, orderInfo3=orderInfo3, validationMode=validationMode, comment=comment) signature_sorted_key_list= ['shopId', 'transmissionDate', 'transactionId', 'sequenceNb', 'ctxMode', 'orderId', 'orderInfo', 'orderInfo2', 'orderInfo3', 'amount', 'devise', 'newTransactionId', 'presentationDate', 'validationMode', 'comment'] kw.update( ctxMode=self.getPayzenVadsCtxMode(), shopId=self.getServiceUsername(), sequenceNb=1, ) kw['wsSignature'] = self._getSignature(kw, signature_sorted_key_list) # Note: Code shall not raise since now, as communication begin and caller # will have to log sent/received messages. client = suds.client.Client(self.wsdl_link.getUrlString()) data = client.service.duplicate(**kw) # Note: Code shall not raise since now, as communication begin and caller # will have to log sent/received messages. try: data_kw = dict(data) for k in data_kw.keys(): v = data_kw[k] if not isinstance(v, str): data_kw[k] = str(v) except Exception: data_kw = {} signature = False LOG('PayzenService', WARNING, 'Issue during processing data_kw:', error=True) else: try: signature = self._check_transactionInfoSignature(data) except Exception: LOG('PayzenService', WARNING, 'Issue during signature calculation:', error=True) signature = False try: last_sent = str(client.last_sent()) except Exception: LOG('PayzenService', WARNING, 'Issue during converting last_sent to string:', error=True) signature = False try: last_received = str(client.last_received()) except Exception: LOG('PayzenService', WARNING, 'Issue during converting last_received to string:', error=True) signature = False return [data_kw, signature, last_sent, last_received] @setUTCTimeZone def soap_cancel(self, transmissionDate, transactionId, comment=''): # prepare with passed parameters kw = dict(transmissionDate=transmissionDate, transactionId=transactionId, comment=comment) signature_sorted_key_list= ['shopId', 'transmissionDate', 'transactionId', 'sequenceNb', 'ctxMode', 'comment'] kw.update( ctxMode=self.getPayzenVadsCtxMode(), shopId=self.getServiceUsername(), sequenceNb=1, ) kw['wsSignature'] = self._getSignature(kw, signature_sorted_key_list) # Note: Code shall not raise since now, as communication begin and caller # will have to log sent/received messages. client = suds.client.Client(self.wsdl_link.getUrlString()) data = client.service.cancel(**kw) # Note: Code shall not raise since now, as communication begin and caller # will have to log sent/received messages. try: data_kw = dict(data) for k in data_kw.keys(): v = data_kw[k] if not isinstance(v, str): data_kw[k] = str(v) except Exception: data_kw = {} signature = False LOG('PayzenService', WARNING, 'Issue during processing data_kw:', error=True) else: try: signature = self._check_transactionInfoSignature(data) except Exception: LOG('PayzenService', WARNING, 'Issue during signature calculation:', error=True) signature = False try: last_sent = str(client.last_sent()) except Exception: LOG('PayzenService', WARNING, 'Issue during converting last_sent to string:', error=True) signature = False try: last_received = str(client.last_received()) except Exception: LOG('PayzenService', WARNING, 'Issue during converting last_received to string:', error=True) signature = False return [data_kw, signature, last_sent, last_received] finally: if present: os.environ['TZ'] = tz else: del(os.environ['TZ']) time.tzset() class PayzenService(XMLObject, PayzenSOAP): meta_type = 'Payzen Service' portal_type = 'Payzen Service' zope.interface.implements(interfaces.IPaymentService) # Declarative security security = ClassSecurityInfo() security.declareObjectProtected(Permissions.AccessContentsInformation) # Declarative properties property_sheets = ( PropertySheet.Base , PropertySheet.XMLObject , PropertySheet.Reference ) def initialize(self, REQUEST=None, **kw): """See Payment Service Interface Documentation""" pass def _getSignature(self, ob, sorted_key_list): """Calculates signature from ob ob can be dict or getattr capable object in case if ob is a dict all .strftime callable values are converted to datetime soapish format """ if isinstance(ob, dict): isdict = True else: isdict = False signature = '' for k in sorted_key_list: if isdict: v = ob[k] else: v = getattr(ob, k, None) if v is None: # empty or not transmitted -- add as empty v = '' elif isinstance(v, datetime.datetime): # for sure date v = v.strftime('%Y%m%d') # import ipdb ; ipdb.set_trace() else: # anything else cast to string v = str(v) signature += v + '+' signature += self.getServicePassword() return hashlib.sha1(signature).hexdigest() def _getFieldList(self, payzen_dict): payzen_dict.update( vads_action_mode=self.getPayzenVadsActionMode(), vads_ctx_mode=self.getPayzenVadsCtxMode(), vads_contrib='ERP5', vads_page_action=self.getPayzenVadsPageAction(), vads_payment_config='SINGLE', vads_site_id=self.getServiceUsername(), vads_version=self.getPayzenVadsVersion() ) # fetch all prepared vads_ values and remove them from dict signature = self._getSignature(payzen_dict, sorted(payzen_dict.keys())) payzen_dict['signature'] = signature field_list = [] for k,v in payzen_dict.iteritems(): field_list.append((k, v)) return field_list def navigate(self, page_template, payzen_dict, REQUEST=None, **kw): """Returns configured template used to do the payment""" self.Base_checkConsistency() temp_document = newTempDocument(self, 'id') temp_document.edit( link_url_string=self.getLinkUrlString(), title='title', field_list=self._getFieldList(payzen_dict), # append the rest of transmitted parameters page template **kw ) return getattr(temp_document, page_template)() def notifySuccess(self, REQUEST=None, **kw): """See Payment Service Interface Documentation""" raise NotImplementedError return self._getTypeBasedMethod("acceptPayment")(**kw) def notifyFail(self, REQUEST=None, **kw): """See Payment Service Interface Documentation""" raise NotImplementedError return self._getTypeBasedMethod("failInPayment")(**kw) def notifyCancel(self, REQUEST=None, **kw): """See Payment Service Interface Documentation""" raise NotImplementedError return self._getTypeBasedMethod("abortPayment")(**kw)