add python3 compatibility (#23720)

This commit is contained in:
Frédéric Péters 2018-03-26 09:56:16 +02:00
parent 31b85ae9fd
commit 887832bc5f
16 changed files with 165 additions and 125 deletions

View File

@ -1,9 +1,10 @@
# -*- coding: utf-8 -*-
import importlib
import logging
from common import (URL, HTML, FORM, RECEIVED, ACCEPTED, PAID, DENIED,
CANCELED, CANCELLED, ERROR, WAITING, ResponseError, force_text)
from .common import (URL, HTML, FORM, RECEIVED, ACCEPTED, PAID, DENIED,
CANCELED, CANCELLED, ERROR, WAITING, ResponseError, force_text)
__all__ = ['Payment', 'URL', 'HTML', 'FORM', 'SIPS',
'SYSTEMPAY', 'SPPLUS', 'TIPI', 'DUMMY', 'get_backend', 'RECEIVED', 'ACCEPTED',
@ -23,7 +24,7 @@ logger = logging.getLogger(__name__)
def get_backend(kind):
'''Resolve a backend name into a module object'''
module = __import__(kind, globals(), locals(), [])
module = importlib.import_module('.' + kind, package='eopayment')
return module.Payment
__BACKENDS = [ DUMMY, SIPS, SIPS2, SYSTEMPAY, SPPLUS, OGONE, PAYBOX, PAYZEN, TIPI ]

View File

@ -2,9 +2,15 @@ import os.path
import os
import random
import logging
import cgi
from datetime import date
import six
if six.PY3:
import html
else:
import cgi
__all__ = ['PaymentCommon', 'URL', 'HTML', 'RANDOM', 'RECEIVED', 'ACCEPTED',
'PAID', 'ERROR', 'WAITING']
@ -29,15 +35,27 @@ ORDERID_TRANSACTION_SEPARATOR = '!'
def force_text(s, encoding='utf-8'):
if isinstance(s, unicode):
if issubclass(type(s), six.text_type):
return s
try:
return unicode(s, encoding)
if not issubclass(type(s), six.string_types):
if six.PY3:
if isinstance(s, bytes):
s = six.text_type(s, encoding)
else:
s = six.text_type(s)
elif hasattr(s, '__unicode__'):
s = six.text_type(s)
else:
s = six.text_type(bytes(s), encoding)
else:
s = s.decode(encoding)
except UnicodeDecodeError:
return unicode(s)
return six.text_type(s, encoding, 'ignore')
return s
def force_byte(s, encoding='utf-8'):
if isinstance(s, str):
if isinstance(s, bytes):
return s
try:
return s.encode(encoding)
@ -148,7 +166,10 @@ class Form(object):
return s
def escape(self, s):
return cgi.escape(force_text(s, self.encoding).encode(self.encoding))
if six.PY3:
return html.escape(force_text(s, self.encoding))
else:
return cgi.escape(force_text(s, self.encoding)).encode(self.encoding)
def __str__(self):
s = '<form method="%s" action="%s">' % (self.method, self.url)

View File

@ -1,21 +1,18 @@
import urllib
import string
import logging
import warnings
def N_(message): return message
try:
from cgi import parse_qs
except ImportError:
from urlparse import parse_qs
from six.moves.urllib.parse import parse_qs, urlencode
from common import PaymentCommon, URL, PaymentResponse, PAID, ERROR, WAITING, ResponseError
from .common import (PaymentCommon, URL, PaymentResponse, PAID, ERROR, WAITING,
ResponseError, force_text)
__all__ = [ 'Payment' ]
SERVICE_URL = 'http://dummy-payment.demo.entrouvert.com/'
ALPHANUM = string.letters + string.digits
ALPHANUM = string.ascii_letters + string.digits
LOGGER = logging.getLogger(__name__)
class Payment(PaymentCommon):
@ -114,14 +111,14 @@ class Payment(PaymentCommon):
}
query.update(dict(name=name, address=address, email=email, phone=phone,
orderid=orderid, info1=info1, info2=info2, info3=info3))
for key in query.keys():
for key in list(query.keys()):
if query[key] is None:
del query[key]
url = '%s?%s' % (SERVICE_URL, urllib.urlencode(query))
url = '%s?%s' % (SERVICE_URL, urlencode(query))
return transaction_id, URL, url
def response(self, query_string, logger=LOGGER, **kwargs):
form = parse_qs(query_string)
form = parse_qs(force_text(query_string))
if not 'transaction_id' in form:
raise ResponseError()
transaction_id = form.get('transaction_id',[''])[0]

View File

@ -1,10 +1,11 @@
# -*- coding: utf-8 -*-
import hashlib
import string
import urlparse
import six
from six.moves.urllib import parse as urlparse
from decimal import Decimal, ROUND_HALF_UP
from common import (PaymentCommon, PaymentResponse, FORM, CANCELLED, PAID,
from .common import (PaymentCommon, PaymentResponse, FORM, CANCELLED, PAID,
ERROR, Form, DENIED, ACCEPTED, ORDERID_TRANSACTION_SEPARATOR,
WAITING, ResponseError, force_byte, force_text)
def N_(message): return message
@ -499,7 +500,7 @@ class Payment(PaymentCommon):
# arrondi comptable francais
amount = amount.quantize(Decimal('1.'), rounding=ROUND_HALF_UP)
params = {
'AMOUNT': unicode(amount),
'AMOUNT': force_text(amount),
'ORDERID': reference,
'PSPID': self.pspid,
'LANGUAGE': language,
@ -517,7 +518,7 @@ class Payment(PaymentCommon):
params['EMAIL'] = email
if description:
params['COM'] = description
for key, value in kwargs.iteritems():
for key, value in kwargs.items():
params[key.upper()] = value
params['SHASIGN'] = self.sha_sign_in(params)
# uniformize all values to UTF-8 string
@ -533,7 +534,10 @@ class Payment(PaymentCommon):
return reference, FORM, form
def response(self, query_string, **kwargs):
params = urlparse.parse_qs(query_string, True)
if six.PY3:
params = urlparse.parse_qs(query_string, True, encoding='iso-8859-1')
else:
params = urlparse.parse_qs(query_string, True)
params = dict((key.upper(), params[key][0]) for key in params)
if not set(params) >= set(['ORDERID', 'PAYID', 'STATUS', 'NCERROR']):
raise ResponseError()

View File

@ -1,5 +1,6 @@
# -*- coding: utf-8
import codecs
from collections import OrderedDict
import datetime
import logging
@ -9,15 +10,18 @@ from decimal import Decimal, ROUND_DOWN
from Crypto.Signature import PKCS1_v1_5
from Crypto.PublicKey import RSA
from Crypto.Hash import SHA
import urlparse
import urllib
import six
from six.moves.urllib import parse as urlparse
from six.moves.urllib import parse as urllib
import base64
from gettext import gettext as _
import string
import warnings
from common import (PaymentCommon, PaymentResponse, FORM, PAID, ERROR, Form,
ORDERID_TRANSACTION_SEPARATOR, ResponseError)
from .common import (PaymentCommon, PaymentResponse, FORM, PAID, ERROR, Form,
ORDERID_TRANSACTION_SEPARATOR, ResponseError, force_text)
__all__ = ['sign', 'Payment']
@ -109,9 +113,10 @@ def sign(data, key):
algo = ALGOS[v]
break
assert algo, 'Missing or invalid PBX_HASH'
tosign = ['%s=%s' % (k, unicode(v).encode('utf-8')) for k, v in data]
tosign = ['%s=%s' % (k, force_text(v)) for k, v in data]
tosign = '&'.join(tosign)
logger.debug('signed string %r', tosign)
tosign = tosign.encode('utf-8')
signature = hmac.new(key, tosign, algo)
return tuple(data) + (('PBX_HMAC', signature.hexdigest().upper()),)
@ -208,22 +213,22 @@ class Payment(PaymentCommon):
def request(self, amount, email, name=None, orderid=None, **kwargs):
d = OrderedDict()
d['PBX_SITE'] = unicode(self.site)
d['PBX_RANG'] = unicode(self.rang).strip()[-2:]
d['PBX_IDENTIFIANT'] = unicode(self.identifiant)
d['PBX_SITE'] = force_text(self.site)
d['PBX_RANG'] = force_text(self.rang).strip()[-2:]
d['PBX_IDENTIFIANT'] = force_text(self.identifiant)
d['PBX_TOTAL'] = (amount * Decimal(100)).to_integral_value(ROUND_DOWN)
d['PBX_DEVISE'] = unicode(self.devise)
d['PBX_DEVISE'] = force_text(self.devise)
transaction_id = kwargs.get('transaction_id') or \
self.transaction_id(12, string.digits, 'paybox', self.site,
self.rang, self.identifiant)
d['PBX_CMD'] = unicode(transaction_id)
d['PBX_CMD'] = force_text(transaction_id)
# prepend order id command reference
if orderid:
d['PBX_CMD'] = orderid + ORDERID_TRANSACTION_SEPARATOR + d['PBX_CMD']
d['PBX_PORTEUR'] = unicode(email)
d['PBX_PORTEUR'] = force_text(email)
d['PBX_RETOUR'] = 'montant:M;reference:R;code_autorisation:A;erreur:E;signature:K'
d['PBX_HASH'] = 'SHA512'
d['PBX_TIME'] = kwargs.get('time') or (unicode(datetime.datetime.utcnow().isoformat('T')).split('.')[0]+'+00:00')
d['PBX_TIME'] = kwargs.get('time') or (force_text(datetime.datetime.utcnow().isoformat('T')).split('.')[0]+'+00:00')
d['PBX_ARCHIVAGE'] = transaction_id
if self.normal_return_url:
d['PBX_EFFECTUE'] = self.normal_return_url
@ -236,16 +241,21 @@ class Payment(PaymentCommon):
"use automatic_return_url", DeprecationWarning)
automatic_return_url = self.callback
if automatic_return_url:
d['PBX_REPONDRE_A'] = unicode(automatic_return_url)
d['PBX_REPONDRE_A'] = force_text(automatic_return_url)
d = d.items()
d = sign(d, self.shared_secret.decode('hex'))
if six.PY3:
shared_secret = codecs.decode(bytes(self.shared_secret, 'ascii'), 'hex')
else:
shared_secret = codecs.decode(bytes(self.shared_secret), 'hex')
d = sign(d, shared_secret)
url = URLS[self.platform]
fields = []
for k, v in d:
fields.append({
'type': u'hidden',
'name': unicode(k),
'value': unicode(v),
'name': force_text(k),
'value': force_text(v),
})
form = Form(url, 'POST', fields, submit_name=None,
submit_value=u'Envoyer', encoding='utf-8')

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
import urlparse
from six.moves.urllib import parse as urlparse
import string
import subprocess
from decimal import Decimal
@ -9,8 +9,8 @@ import os.path
import uuid
import warnings
from common import PaymentCommon, HTML, PaymentResponse, ResponseError
from cb import CB_RESPONSE_CODES
from .common import PaymentCommon, HTML, PaymentResponse, ResponseError
from .cb import CB_RESPONSE_CODES
'''
Payment backend module for the ATOS/SIPS system used by many Frenck banks.
@ -112,7 +112,7 @@ class Payment(PaymentCommon):
if PATHFILE in self.options:
params[PATHFILE] = self.options[PATHFILE]
executable = os.path.join(self.binpath, executable)
args = [executable] + ["%s=%s" % p for p in params.iteritems()]
args = [executable] + ["%s=%s" % p for p in params.items()]
self.logger.debug('executing %s' % args)
result,_ = subprocess.Popen(' '.join(args),
stdout=subprocess.PIPE, shell=True).communicate()

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
import collections
import json
import urlparse
from six.moves.urllib import parse as urlparse
import string
from decimal import Decimal
import uuid
@ -11,8 +11,8 @@ from gettext import gettext as _
import requests
import warnings
from common import (PaymentCommon, FORM, Form, PaymentResponse, PAID, ERROR,
CANCELED, ResponseError)
from .common import (PaymentCommon, FORM, Form, PaymentResponse, PAID, ERROR,
CANCELED, ResponseError, force_text)
__all__ = ['Payment']
@ -139,12 +139,12 @@ class Payment(PaymentCommon):
}
def encode_data(self, data):
return u'|'.join(u'%s=%s' % (unicode(key), unicode(value))
for key, value in data.iteritems())
return u'|'.join(u'%s=%s' % (force_text(key), force_text(value))
for key, value in data.items())
def seal_data(self, data):
s = self.encode_data(data)
s += unicode(self.secret_key)
s += force_text(self.secret_key)
s = s.encode('utf-8')
s = hashlib.sha256(s).hexdigest()
return s
@ -172,13 +172,13 @@ class Payment(PaymentCommon):
info1=None, info2=None, info3=None, next_url=None, **kwargs):
data = self.get_data()
transaction_id = self.transaction_id(10, string.digits, 'sips2', data['merchantId'])
data['transactionReference'] = unicode(transaction_id)
data['orderId'] = orderid or unicode(uuid.uuid4()).replace('-', '')
data['transactionReference'] = force_text(transaction_id)
data['orderId'] = orderid or force_text(uuid.uuid4()).replace('-', '')
if info1:
data['statementReference'] = unicode(info1)
data['statementReference'] = force_text(info1)
else:
data['statementReference'] = data['transactionReference']
data['amount'] = unicode(int(Decimal(amount) * 100))
data['amount'] = force_text(int(Decimal(amount) * 100))
if email:
data['billingContact.email'] = email
if 'captureDay' in kwargs:
@ -215,7 +215,7 @@ class Payment(PaymentCommon):
def decode_data(self, data):
data = data.split('|')
data = [map(unicode, p.split('=', 1)) for p in data]
data = [map(force_text, p.split('=', 1)) for p in data]
return collections.OrderedDict(data)
def check_seal(self, data, seal):
@ -254,9 +254,9 @@ class Payment(PaymentCommon):
for key in sorted(data.keys()):
if key in ('keyVersion', 'sealAlgorithm', 'seal'):
continue
data_to_send.append(unicode(data[key]))
data_to_send.append(force_text(data[key]))
data_to_send_str = u''.join(data_to_send).encode('utf-8')
return hmac.new(unicode(self.secret_key).encode('utf-8'), data_to_send_str, hashlib.sha256).hexdigest()
return hmac.new(force_text(self.secret_key).encode('utf-8'), data_to_send_str, hashlib.sha256).hexdigest()
def perform_cash_management_operation(self, endpoint, data):
data['merchantId'] = self.merchant_id
@ -282,13 +282,13 @@ class Payment(PaymentCommon):
def cancel(self, amount, bank_data, **kwargs):
data = {}
data['operationAmount'] = unicode(int(Decimal(amount) * 100))
data['operationAmount'] = force_text(int(Decimal(amount) * 100))
data['transactionReference'] = bank_data.get('transactionReference')
return self.perform_cash_management_operation('cancel', data)
def validate(self, amount, bank_data, **kwargs):
data = {}
data['operationAmount'] = unicode(int(Decimal(amount) * 100))
data['operationAmount'] = force_text(int(Decimal(amount) * 100))
data['transactionReference'] = bank_data.get('transactionReference')
return self.perform_cash_management_operation('validate', data)

View File

@ -4,8 +4,8 @@ import binascii
from gettext import gettext as _
import hmac
import hashlib
import urlparse
import urllib
from six.moves.urllib import parse as urlparse
from six.moves.urllib import parse as urllib
import string
import datetime as dt
import logging
@ -13,15 +13,15 @@ import re
import warnings
import Crypto.Cipher.DES
from common import (PaymentCommon, URL, PaymentResponse, RECEIVED, ACCEPTED,
PAID, ERROR, ResponseError)
from .common import (PaymentCommon, URL, PaymentResponse, RECEIVED, ACCEPTED,
PAID, ERROR, ResponseError, force_byte)
def N_(message): return message
__all__ = ['Payment']
KEY_DES_KEY = '\x45\x1f\xba\x4f\x4c\x3f\xd4\x97'
IV = '\x30\x78\x30\x62\x2c\x30\x78\x30'
KEY_DES_KEY = b'\x45\x1f\xba\x4f\x4c\x3f\xd4\x97'
IV = b'\x30\x78\x30\x62\x2c\x30\x78\x30'
REFERENCE = 'reference'
ETAT = 'etat'
SPCHECKOK = 'spcheckok'
@ -56,7 +56,7 @@ TEST_STATE = ('99',)
def decrypt_ntkey(ntkey):
key = binascii.unhexlify(ntkey.replace(' ',''))
key = binascii.unhexlify(ntkey.replace(b' ', b''))
return decrypt_key(key)
def decrypt_key(key):
@ -70,7 +70,7 @@ def extract_values(query_string):
k, v = kv.split('=', 1)
if k != 'hmac':
result.append(v)
return ''.join(result)
return force_byte(''.join(result))
def sign_ntkey_query(ntkey, query):
key = decrypt_ntkey(ntkey)
@ -89,7 +89,7 @@ def sign_url_paiement(ntkey, query):
data_to_sign = ''.join(fields)
return hmac.new(key[:20], data_to_sign, hashlib.sha1).hexdigest().upper()
ALPHANUM = string.letters + string.digits
ALPHANUM = string.ascii_letters + string.digits
SERVICE_URL = "https://www.spplus.net/paiement/init.do"
class Payment(PaymentCommon):
@ -178,7 +178,7 @@ class Payment(PaymentCommon):
form = urlparse.parse_qs(query_string)
if not set(form) >= set([REFERENCE, ETAT, REFSFP]):
raise ResponseError()
for key, value in form.iteritems():
for key, value in form.items():
form[key] = value[0]
logger.debug('received query_string %s' % query_string)
logger.debug('parsed as %s' % form)
@ -233,8 +233,8 @@ if __name__ == '__main__':
ntkey = '58 6d fc 9c 34 91 9b 86 3f fd 64 63 c9 13 4a 26 ba 29 74 1e c7 e9 80 79'
if len(sys.argv) == 2:
print sign_url_paiement(ntkey, sys.argv[1])
print sign_ntkey_query(ntkey, sys.argv[1])
print(sign_url_paiement(ntkey, sys.argv[1]))
print(sign_ntkey_query(ntkey, sys.argv[1]))
elif len(sys.argv) > 2:
print sign_url_paiement(sys.argv[1], sys.argv[2])
print sign_ntkey_query(sys.argv[1], sys.argv[2])
print(sign_url_paiement(sys.argv[1], sys.argv[2]))
print(sign_ntkey_query(sys.argv[1], sys.argv[2]))

View File

@ -4,13 +4,13 @@ import datetime as dt
import hashlib
import logging
import string
import urlparse
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, ERROR, FORM, Form,
ResponseError, force_text, force_byte)
from .cb import CB_RESPONSE_CODES
__all__ = ['Payment']
@ -117,20 +117,20 @@ PARAMETERS = [
'ONEY_SANDBOX, PAYPAL, PAYPAL_SB, PAYSAFECARD, '
'VISA')),
# must be SINGLE or MULTI with parameters
Parameter('vads_payment_config', '', 07, default='SINGLE',
Parameter('vads_payment_config', '', 7, default='SINGLE',
choices=('SINGLE', 'MULTI'), needed=True),
Parameter('vads_return_mode', None, 48, default='GET',
choices=('', 'NONE', 'POST', 'GET')),
Parameter('signature', 'an', None, length=40),
Parameter('vads_site_id', 'n', 02, length=8, needed=True,
Parameter('vads_site_id', 'n', 2, length=8, needed=True,
description=_(u'Identifiant de la boutique')),
Parameter('vads_theme_config', 'ans', 32, max_length=255),
Parameter(VADS_TRANS_DATE, 'n', 04, length=14, needed=True,
Parameter(VADS_TRANS_DATE, 'n', 4, length=14, needed=True,
default=isonow),
Parameter('vads_trans_id', 'n', 03, length=6, needed=True),
Parameter('vads_trans_id', 'n', 3, length=6, needed=True),
Parameter('vads_validation_mode', 'n', 5, max_length=1, choices=('', 0, 1),
default=''),
Parameter('vads_version', 'an', 01, default='V2', needed=True,
Parameter('vads_version', 'an', 1, default='V2', needed=True,
choices=('V2',)),
Parameter('vads_url_success', 'ans', 24, max_length=127),
Parameter('vads_url_referral', 'ans', 26, max_length=127),
@ -173,7 +173,7 @@ EXTRA_RESULT_MAP = {
def add_vads(kwargs):
new_vargs = {}
for k, v in kwargs.iteritems():
for k, v in kwargs.items():
if k.startswith('vads_'):
new_vargs[k] = v
else:
@ -276,8 +276,8 @@ class Payment(PaymentCommon):
info2, info3, next_url, kwargs)
# amount unit is cents
amount = '%.0f' % (100 * amount)
kwargs.update(add_vads({'amount': unicode(amount)}))
if amount < 0:
kwargs.update(add_vads({'amount': force_text(amount)}))
if int(amount) < 0:
raise ValueError('amount must be an integer >= 0')
normal_return_url = self.normal_return_url
if next_url:
@ -285,30 +285,30 @@ class Payment(PaymentCommon):
"set normal_return_url in options", DeprecationWarning)
normal_return_url = next_url
if normal_return_url:
kwargs[VADS_URL_RETURN] = unicode(normal_return_url)
kwargs[VADS_URL_RETURN] = force_text(normal_return_url)
if name is not None:
kwargs['vads_cust_name'] = unicode(name)
kwargs['vads_cust_name'] = force_text(name)
if first_name is not None:
kwargs[VADS_CUST_FIRST_NAME] = unicode(first_name)
kwargs[VADS_CUST_FIRST_NAME] = force_text(first_name)
if last_name is not None:
kwargs[VADS_CUST_LAST_NAME] = unicode(last_name)
kwargs[VADS_CUST_LAST_NAME] = force_text(last_name)
if address is not None:
kwargs['vads_cust_address'] = unicode(address)
kwargs['vads_cust_address'] = force_text(address)
if email is not None:
kwargs['vads_cust_email'] = unicode(email)
kwargs['vads_cust_email'] = force_text(email)
if phone is not None:
kwargs['vads_cust_phone'] = unicode(phone)
kwargs['vads_cust_phone'] = force_text(phone)
if info1 is not None:
kwargs['vads_order_info'] = unicode(info1)
kwargs['vads_order_info'] = force_text(info1)
if info2 is not None:
kwargs['vads_order_info2'] = unicode(info2)
kwargs['vads_order_info2'] = force_text(info2)
if info3 is not None:
kwargs['vads_order_info3'] = unicode(info3)
kwargs['vads_order_info3'] = force_text(info3)
if orderid is not None:
# check orderid format first
name = 'vads_order_id'
orderid = unicode(orderid)
orderid = force_text(orderid)
ptype = 'an-'
p = Parameter(name, ptype, 13, max_length=32)
if not p.check_value(orderid):
@ -318,14 +318,14 @@ class Payment(PaymentCommon):
transaction_id = self.transaction_id(6, string.digits, 'systempay',
self.options[VADS_SITE_ID])
kwargs[VADS_TRANS_ID] = unicode(transaction_id)
kwargs[VADS_TRANS_ID] = force_text(transaction_id)
fields = kwargs
for parameter in PARAMETERS:
name = parameter.name
# import default parameters from configuration
if name not in fields \
and name in self.options:
fields[name] = unicode(self.options[name])
fields[name] = force_text(self.options[name])
# import default parameters from module
if name not in fields and parameter.default is not None:
if callable(parameter.default):
@ -333,7 +333,7 @@ class Payment(PaymentCommon):
else:
fields[name] = parameter.default
check_vads(fields)
fields[SIGNATURE] = unicode(self.signature(fields))
fields[SIGNATURE] = force_text(self.signature(fields))
self.logger.debug('%s request contains fields: %s', __name__, fields)
transaction_id = '%s_%s' % (fields[VADS_TRANS_DATE], transaction_id)
self.logger.debug('%s transaction id: %s', __name__, transaction_id)
@ -346,14 +346,14 @@ class Payment(PaymentCommon):
'name': force_text(field_name),
'value': force_text(field_value),
}
for field_name, field_value in fields.iteritems()])
for field_name, field_value in fields.items()])
return transaction_id, FORM, form
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]):
raise ResponseError()
for key, value in fields.iteritems():
for key, value in fields.items():
fields[key] = value[0]
copy = fields.copy()
bank_status = []
@ -417,8 +417,8 @@ class Payment(PaymentCommon):
self.logger.debug('ordered keys %s' % ordered_keys)
ordered_fields = [force_byte(fields[key]) for key in ordered_keys]
secret = getattr(self, 'secret_%s' % fields['vads_ctx_mode'].lower())
signed_data = '+'.join(ordered_fields)
signed_data = '%s+%s' % (signed_data, force_byte(secret))
signed_data = b'+'.join(ordered_fields)
signed_data = b'%s+%s' % (signed_data, force_byte(secret))
self.logger.debug(u'generating signature on «%s»', signed_data)
sign = hashlib.sha1(signed_data).hexdigest()
self.logger.debug(u'signature «%s»', sign)

View File

@ -1,15 +1,15 @@
# -*- coding: utf-8 -*-
from decimal import Decimal, ROUND_DOWN
from common import (PaymentCommon, PaymentResponse, URL, PAID, DENIED,
from .common import (PaymentCommon, PaymentResponse, URL, PAID, DENIED,
CANCELLED, ERROR, ResponseError)
from urllib import urlencode
from urlparse import parse_qs
from six.moves.urllib.parse import urlencode, parse_qs
from gettext import gettext as _
import logging
import warnings
from systempayv2 import isonow
from .systempayv2 import isonow
__all__ = ['Payment']
@ -95,12 +95,12 @@ class Payment(PaymentCommon):
refdet = str(refdet)
if 6 > len(refdet) > 30:
raise ValueError('len(REFDET) < 6 or > 30')
except Exception, e:
except Exception as e:
raise ValueError('REFDET format invalide, %r' % refdet, e)
if objet is not None:
try:
objet = str(objet)
except Exception, e:
except Exception as e:
raise ValueError('OBJET must be a string', e)
if not objet.replace(' ','').isalnum():
raise ValueError('OBJECT must only contains '
@ -113,7 +113,7 @@ class Payment(PaymentCommon):
raise ValueError('no @ in MEL')
if not (6 <= len(mel) <= 80):
raise ValueError('len(MEL) is invalid, must be between 6 and 80')
except Exception, e:
except Exception as e:
raise ValueError('MEL is not a valid email, %r' % mel, e)
saisie = saisie or self.saisie
@ -146,7 +146,7 @@ class Payment(PaymentCommon):
fields = parse_qs(query_string, True)
if not set(fields) >= set(['refdet', 'resultrans']):
raise ResponseError()
for key, value in fields.iteritems():
for key, value in fields.items():
fields[key] = value[0]
refdet = fields.get('refdet')
if refdet is None:
@ -183,11 +183,11 @@ class Payment(PaymentCommon):
if __name__ == '__main__':
p = Payment({'numcli': '12345'})
print p.request(amount=Decimal('123.12'),
print(p.request(amount=Decimal('123.12'),
exer=9999,
refdet=999900000000999999,
objet='tout a fait',
email='info@entrouvert.com',
urlcl='http://example.com/tipi/test',
saisie='T')
print p.response('objet=tout+a+fait&montant=12312&saisie=T&mel=info%40entrouvert.com&numcli=12345&exer=9999&refdet=999900000000999999&resultrans=P')
saisie='T'))
print(p.response('objet=tout+a+fait&montant=12312&saisie=T&mel=info%40entrouvert.com&numcli=12345&exer=9999&refdet=999900000000999999&resultrans=P'))

View File

@ -46,7 +46,7 @@ class TestCommand(distutils.core.Command):
class eo_sdist(sdist):
def run(self):
print "creating VERSION file"
print("creating VERSION file")
if os.path.exists('VERSION'):
os.remove('VERSION')
version = get_version()
@ -54,7 +54,7 @@ class eo_sdist(sdist):
version_file.write(version)
version_file.close()
sdist.run(self)
print "removing VERSION file"
print("removing VERSION file")
if os.path.exists('VERSION'):
os.remove('VERSION')
@ -72,7 +72,7 @@ def get_version():
stderr=subprocess.PIPE)
result = p.communicate()[0]
if p.returncode == 0:
version = result.split()[0][1:]
version = str(result.split()[0][1:])
version = version.replace('-', '.')
return version
return '0.0.0'
@ -83,7 +83,7 @@ setuptools.setup(
license='GPLv3 or later',
description='Common API to use all French online payment credit card '
'processing services',
long_description=file(
long_description=open(
os.path.join(
os.path.dirname(__file__),
'README.txt')).read(),
@ -96,6 +96,7 @@ setuptools.setup(
install_requires=[
'pycrypto >= 2.5',
'requests',
'six',
],
cmdclass={
'sdist': eo_sdist,

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
from unittest import TestCase
import urllib
from six.moves.urllib import parse as urllib
import eopayment
import eopayment.ogone as ogone

View File

@ -1,9 +1,10 @@
# -*- coding: utf-8 -*-
import codecs
from unittest import TestCase
from decimal import Decimal
import base64
import urllib
from six.moves.urllib import parse as urllib
import eopayment.paybox as paybox
import eopayment
@ -19,7 +20,8 @@ BACKEND_PARAMS = {
class PayboxTests(TestCase):
def test_sign(self):
key = '0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF'.decode('hex')
key = b'0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF'
key = codecs.decode(key, 'hex')
d = dict(paybox.sign([
['PBX_SITE', u'12345678'],
['PBX_RANG', u'32'],
@ -97,7 +99,7 @@ UX4D2A/QcMvkEcRVXFx5tQqcE9/JnMqE41TF/ebn7jC/MBxxtPFkUN7+EZoeMN7x
OWzAMDm/xsCWRvvel4GGixgm3aQRUPyTrlm4Ksy32Ya0rNnEDMAvB3dxOn7cp8GR
ZdzrudBlevZXpr6iYwIDAQAB
-----END PUBLIC KEY-----'''
data = 'coin\n'
data = b'coin\n'
sig64 = '''VCt3sgT0ecacmDEWWNVXJ+jGmIPBMApK42tBJV0FlDjpllOGPy8MsAmLW4/QjTtx
z0Dkz0NjxvU+5WzQZh9Uuxr/egRCwV4NMRWqu0zaVVioeBvl4/5CWm4f4/1L9+0m
FBFKOZhgBJnkC+l6+XhT4aYWKaQ4ocmOMV92yjeXTE4='''

View File

@ -2,8 +2,8 @@ from unittest import TestCase
import eopayment.spplus as spplus
class SPPlustTest(TestCase):
ntkey = '58 6d fc 9c 34 91 9b 86 3f ' \
'fd 64 63 c9 13 4a 26 ba 29 74 1e c7 e9 80 79'
ntkey = b'58 6d fc 9c 34 91 9b 86 3f ' \
b'fd 64 63 c9 13 4a 26 ba 29 74 1e c7 e9 80 79'
tests = [('x=coin', 'c04f8266d6ae3ce37551cce996c751be4a95d10a'),
('x=coin&y=toto', 'ef008e02f8dbf5e70e83da416b0b3a345db203de'),

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
import urlparse
from six.moves.urllib import parse as urlparse
from eopayment.systempayv2 import Payment, VADS_CUST_FIRST_NAME, \
VADS_CUST_LAST_NAME, PAID

View File

@ -3,6 +3,10 @@
# test suite on all supported python versions. To use it, "pip install tox"
# and then run "tox" from this directory.
[tox]
envlist = py2,py3
toxworkdir = {env:TMPDIR:/tmp}/tox-{env:USER}/eopayment/
[testenv]
# django.contrib.auth is not tested it does not work with our templates
commands =