PayzenService.py 6.63 KB
Newer Older
1 2 3 4
import zope
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions, PropertySheet, interfaces
from Products.ERP5Type.XMLObject import XMLObject
Łukasz Nowak's avatar
Łukasz Nowak committed
5 6
from Products.ERP5Type.Document import newTempDocument
import hashlib
7
import datetime
8
from zLOG import LOG, WARNING
9

Łukasz Nowak's avatar
Łukasz Nowak committed
10 11 12 13 14 15 16 17
try:
  import suds
except ImportError:
  class PayzenSOAP:
    pass
else:
  import time
  class PayzenSOAP:
Łukasz Nowak's avatar
Łukasz Nowak committed
18
    """SOAP communication
19

Łukasz Nowak's avatar
Łukasz Nowak committed
20 21
    Methods are returning list of:
      * parsed response
22
      * signature check (True or False)
Łukasz Nowak's avatar
Łukasz Nowak committed
23 24
      * sent XML
      * received XML
25

Łukasz Nowak's avatar
Łukasz Nowak committed
26 27
    SOAP protocol is assumed as untrusted and dangerous, users of those methods
    are encouraged to log such messages for future debugging."""
28 29 30 31 32 33 34 35 36 37 38
    def _check_transcationInfoSignature(self, data):
      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',
Łukasz Nowak's avatar
Łukasz Nowak committed
39
        'vadsXID', 'vadsCAVVAlgorithm', 'vadsCAVV', 'vadsSignatureValid',
40 41 42 43
        'directoryServer', 'authMode', 'markAmount', 'markDevise', 'markDate',
        'markNb', 'markResult', 'markCVV2_CVC2', 'authAmount', 'authDevise',
        'authDate', 'authNb', 'authResult', 'authCVV2_CVC2', 'warrantlyResult',
        'captureDate', 'captureNumber', 'rapprochementStatut', 'refoundAmount',
44
        'refundDevise', 'litige', 'timestamp']
45 46
      signature = ''
      for k in received_sorted_keys:
47 48 49 50
        try:
          v = getattr(data, k)
        except AttributeError:
          pass
51
        else:
52 53 54
          if k in ['transmissionDate', 'presentationDate', 'cardExpirationDate',
            'markDate', 'authDate', 'captureDate']:
            # crazyiness again
55 56 57 58
            if isinstance(v, datetime.datetime):
              v = v.strftime('%Y%m%d')
            else:
              v = time.strftime('%Y%m%d', time.strptime(str(v), '%Y-%m-%d %H:%M:%S'))
59 60
          v = str(v)
          signature += v + '+'
61 62 63 64
      signature += self.getServicePassword()
      signature = hashlib.sha1(signature).hexdigest()
      return signature == data.signature

Łukasz Nowak's avatar
Łukasz Nowak committed
65
    def soap_getInfo(self, transmissionDate, transactionId):
Łukasz Nowak's avatar
Łukasz Nowak committed
66
      """Returns getInfo
67

Łukasz Nowak's avatar
Łukasz Nowak committed
68 69
      transmissionDate is "raw" date in format YYYYMMDD, without any marks
      transactionId is id of transaction for this date"""
Łukasz Nowak's avatar
Łukasz Nowak committed
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
      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,
      )
      date = time.strptime(transmissionDate, '%Y%m%d')
      signature = ''
      for k in sorted_keys:
        if k == 'transmissionDate':
          # craziness: date format in signature is different then in sent message
          v = time.strftime('%Y%m%d', date)
          kw['transmissionDate'] = time.strftime('%Y-%m-%dT%H:%M:%S', date)
        else:
          v = kw[k]
        signature += str(v) + '+'
      signature += self.getServicePassword()
      kw['wsSignature'] = hashlib.sha1(signature).hexdigest()
Łukasz Nowak's avatar
Łukasz Nowak committed
91
      data = client.service.getInfo(**kw)
92 93 94 95 96
      data_kw = dict(data)
      for k in data_kw.keys():
        v = data_kw[k]
        if not isinstance(v, str):
          data_kw[k] = str(v)
97 98 99 100 101 102 103 104
      try:
        signature = self._check_transcationInfoSignature(data)
      except Exception:
        LOG('PayzenService', WARNING, 'Issue during signature calculation:',
          error=True)
        signature = False
      return [data_kw, signature, str(client.last_sent()),
        str(client.last_received())]
Łukasz Nowak's avatar
Łukasz Nowak committed
105 106

class PayzenService(XMLObject, PayzenSOAP):
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
  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

Łukasz Nowak's avatar
Łukasz Nowak committed
125 126 127 128 129 130 131 132 133
  def _getSignature(self, field_list):
    field_list.sort()
    signature = ''
    for k, v in field_list:
      signature += v + '+'
    signature += self.getServicePassword()
    return hashlib.sha1(signature).hexdigest()

  def _getFieldList(self, payzen_dict):
Łukasz Nowak's avatar
Łukasz Nowak committed
134 135 136
    field_list = [
      ('vads_action_mode', self.getPayzenVadsActionMode()),
      ('vads_ctx_mode', self.getPayzenVadsCtxMode()),
Łukasz Nowak's avatar
Łukasz Nowak committed
137
      ('vads_contrib', 'ERP5'),
Łukasz Nowak's avatar
Łukasz Nowak committed
138 139 140 141 142 143
      ('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
Łukasz Nowak's avatar
Łukasz Nowak committed
144 145 146
    for k,v in payzen_dict.iteritems():
      field_list.append((k, v))
    signature = self._getSignature(field_list)
Łukasz Nowak's avatar
Łukasz Nowak committed
147
    field_list.append(('signature', signature))
Łukasz Nowak's avatar
Łukasz Nowak committed
148 149 150 151 152 153
    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')
Łukasz Nowak's avatar
Łukasz Nowak committed
154 155 156
    temp_document.edit(
      link_url_string=self.getLinkUrlString(),
      title='title',
Łukasz Nowak's avatar
Łukasz Nowak committed
157
      field_list=self._getFieldList(payzen_dict),
Łukasz Nowak's avatar
Łukasz Nowak committed
158 159 160 161
      # append the rest of transmitted parameters page template
      **kw
    )
    return getattr(temp_document, page_template)()
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176

  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)