Compare commits
24 Commits
main
...
wip/python
Author | SHA1 | Date |
---|---|---|
Frédéric Péters | 3814073d30 | |
Frédéric Péters | fd56510014 | |
Frédéric Péters | bbf699c827 | |
Frédéric Péters | b88a759e10 | |
Frédéric Péters | d313a9cac4 | |
François Poulain | 36f2e38a84 | |
François Poulain | 714bf8023b | |
Frédéric Péters | 7b2f106348 | |
Frédéric Péters | 9e1ead2e7c | |
Frédéric Péters | f99d6eeb91 | |
Frédéric Péters | f3611bcc38 | |
Frédéric Péters | fd6566a4ca | |
Frédéric Péters | c028af586b | |
Frédéric Péters | d009b35ab2 | |
Frédéric Péters | fe718e1159 | |
Frédéric Péters | 79735f6418 | |
Frédéric Péters | f21d662912 | |
Frédéric Péters | 8e74d949b5 | |
Frédéric Péters | 2469de19e2 | |
Frédéric Péters | b4cc0e71d4 | |
Frédéric Péters | fbf3ebf34d | |
Frédéric Péters | 019188f70e | |
Frédéric Péters | 2ee8b2e5fd | |
Frédéric Péters | c2f059e7aa |
|
@ -1,9 +1,10 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import importlib
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from common import (URL, HTML, FORM, RECEIVED, ACCEPTED, PAID, DENIED,
|
from .common import (URL, HTML, FORM, RECEIVED, ACCEPTED, PAID, DENIED,
|
||||||
CANCELED, CANCELLED, ERROR, WAITING, ResponseError, force_text)
|
CANCELED, CANCELLED, ERROR, WAITING, ResponseError, force_text)
|
||||||
|
|
||||||
__all__ = ['Payment', 'URL', 'HTML', 'FORM', 'SIPS',
|
__all__ = ['Payment', 'URL', 'HTML', 'FORM', 'SIPS',
|
||||||
'SYSTEMPAY', 'SPPLUS', 'TIPI', 'DUMMY', 'get_backend', 'RECEIVED', 'ACCEPTED',
|
'SYSTEMPAY', 'SPPLUS', 'TIPI', 'DUMMY', 'get_backend', 'RECEIVED', 'ACCEPTED',
|
||||||
|
@ -23,7 +24,7 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
def get_backend(kind):
|
def get_backend(kind):
|
||||||
'''Resolve a backend name into a module object'''
|
'''Resolve a backend name into a module object'''
|
||||||
module = __import__(kind, globals(), locals(), [])
|
module = importlib.import_module('.' + kind, package='eopayment')
|
||||||
return module.Payment
|
return module.Payment
|
||||||
|
|
||||||
__BACKENDS = [ DUMMY, SIPS, SIPS2, SYSTEMPAY, SPPLUS, OGONE, PAYBOX, PAYZEN, TIPI ]
|
__BACKENDS = [ DUMMY, SIPS, SIPS2, SYSTEMPAY, SPPLUS, OGONE, PAYBOX, PAYZEN, TIPI ]
|
||||||
|
|
|
@ -2,9 +2,15 @@ import os.path
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
import logging
|
import logging
|
||||||
import cgi
|
|
||||||
from datetime import date
|
from datetime import date
|
||||||
|
|
||||||
|
import six
|
||||||
|
|
||||||
|
if six.PY3:
|
||||||
|
import html
|
||||||
|
else:
|
||||||
|
import cgi
|
||||||
|
|
||||||
__all__ = ['PaymentCommon', 'URL', 'HTML', 'RANDOM', 'RECEIVED', 'ACCEPTED',
|
__all__ = ['PaymentCommon', 'URL', 'HTML', 'RANDOM', 'RECEIVED', 'ACCEPTED',
|
||||||
'PAID', 'ERROR', 'WAITING']
|
'PAID', 'ERROR', 'WAITING']
|
||||||
|
|
||||||
|
@ -29,15 +35,27 @@ ORDERID_TRANSACTION_SEPARATOR = '!'
|
||||||
|
|
||||||
|
|
||||||
def force_text(s, encoding='utf-8'):
|
def force_text(s, encoding='utf-8'):
|
||||||
if isinstance(s, unicode):
|
if issubclass(type(s), six.text_type):
|
||||||
return s
|
return s
|
||||||
try:
|
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:
|
except UnicodeDecodeError:
|
||||||
return unicode(s)
|
return six.text_type(s, encoding, 'ignore')
|
||||||
|
return s
|
||||||
|
|
||||||
def force_byte(s, encoding='utf-8'):
|
def force_byte(s, encoding='utf-8'):
|
||||||
if isinstance(s, str):
|
if isinstance(s, bytes):
|
||||||
return s
|
return s
|
||||||
try:
|
try:
|
||||||
return s.encode(encoding)
|
return s.encode(encoding)
|
||||||
|
@ -148,7 +166,10 @@ class Form(object):
|
||||||
return s
|
return s
|
||||||
|
|
||||||
def escape(self, 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):
|
def __str__(self):
|
||||||
s = '<form method="%s" action="%s">' % (self.method, self.url)
|
s = '<form method="%s" action="%s">' % (self.method, self.url)
|
||||||
|
|
|
@ -1,21 +1,18 @@
|
||||||
import urllib
|
|
||||||
import string
|
import string
|
||||||
import logging
|
import logging
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
def N_(message): return message
|
def N_(message): return message
|
||||||
|
|
||||||
try:
|
from six.moves.urllib.parse import parse_qs, urlencode
|
||||||
from cgi import parse_qs
|
|
||||||
except ImportError:
|
|
||||||
from urlparse import parse_qs
|
|
||||||
|
|
||||||
from common import PaymentCommon, URL, PaymentResponse, PAID, ERROR, WAITING, ResponseError
|
from .common import (PaymentCommon, URL, PaymentResponse, PAID, ERROR, WAITING,
|
||||||
|
ResponseError, force_text)
|
||||||
|
|
||||||
__all__ = [ 'Payment' ]
|
__all__ = [ 'Payment' ]
|
||||||
|
|
||||||
SERVICE_URL = 'http://dummy-payment.demo.entrouvert.com/'
|
SERVICE_URL = 'http://dummy-payment.demo.entrouvert.com/'
|
||||||
ALPHANUM = string.letters + string.digits
|
ALPHANUM = string.ascii_letters + string.digits
|
||||||
LOGGER = logging.getLogger(__name__)
|
LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
class Payment(PaymentCommon):
|
class Payment(PaymentCommon):
|
||||||
|
@ -114,14 +111,14 @@ class Payment(PaymentCommon):
|
||||||
}
|
}
|
||||||
query.update(dict(name=name, address=address, email=email, phone=phone,
|
query.update(dict(name=name, address=address, email=email, phone=phone,
|
||||||
orderid=orderid, info1=info1, info2=info2, info3=info3))
|
orderid=orderid, info1=info1, info2=info2, info3=info3))
|
||||||
for key in query.keys():
|
for key in list(query.keys()):
|
||||||
if query[key] is None:
|
if query[key] is None:
|
||||||
del query[key]
|
del query[key]
|
||||||
url = '%s?%s' % (SERVICE_URL, urllib.urlencode(query))
|
url = '%s?%s' % (SERVICE_URL, urlencode(query))
|
||||||
return transaction_id, URL, url
|
return transaction_id, URL, url
|
||||||
|
|
||||||
def response(self, query_string, logger=LOGGER, **kwargs):
|
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:
|
if not 'transaction_id' in form:
|
||||||
raise ResponseError()
|
raise ResponseError()
|
||||||
transaction_id = form.get('transaction_id',[''])[0]
|
transaction_id = form.get('transaction_id',[''])[0]
|
||||||
|
@ -152,21 +149,3 @@ class Payment(PaymentCommon):
|
||||||
|
|
||||||
def cancel(self, amount, bank_data, **kwargs):
|
def cancel(self, amount, bank_data, **kwargs):
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
options = {
|
|
||||||
'direct_notification_url': 'http://example.com/direct_notification_url',
|
|
||||||
'siret': '1234',
|
|
||||||
'origin': 'Mairie de Perpette-les-oies'
|
|
||||||
}
|
|
||||||
p = Payment(options)
|
|
||||||
retour = 'http://example.com/retour?amount=10.0&direct_notification_url=http%3A%2F%2Fexample.com%2Fdirect_notification_url&email=toto%40example.com&transaction_id=6Tfw2e1bPyYnz7CedZqvdHt7T9XX6T&return_url=http%3A%2F%2Fexample.com%2Fretour&nok=1'
|
|
||||||
r = p.response(retour.split('?',1)[1])
|
|
||||||
assert not r[0]
|
|
||||||
assert r[1] == '6Tfw2e1bPyYnz7CedZqvdHt7T9XX6T'
|
|
||||||
assert r[3] is None
|
|
||||||
retour = 'http://example.com/retour?amount=10.0&direct_notification_url=http%3A%2F%2Fexample.com%2Fdirect_notification_url&email=toto%40example.com&transaction_id=6Tfw2e1bPyYnz7CedZqvdHt7T9XX6T&return_url=http%3A%2F%2Fexample.com%2Fretour&ok=1&signed=1'
|
|
||||||
r = p.response(retour.split('?',1)[1])
|
|
||||||
assert r[0]
|
|
||||||
assert r[1] == '6Tfw2e1bPyYnz7CedZqvdHt7T9XX6T'
|
|
||||||
assert r[3] == 'signature ok'
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import hashlib
|
import hashlib
|
||||||
import string
|
import string
|
||||||
import urlparse
|
import six
|
||||||
|
from six.moves.urllib import parse as urlparse
|
||||||
from decimal import Decimal, ROUND_HALF_UP
|
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,
|
ERROR, Form, DENIED, ACCEPTED, ORDERID_TRANSACTION_SEPARATOR,
|
||||||
WAITING, ResponseError, force_byte, force_text)
|
WAITING, ResponseError, force_byte, force_text)
|
||||||
def N_(message): return message
|
def N_(message): return message
|
||||||
|
@ -499,7 +500,7 @@ class Payment(PaymentCommon):
|
||||||
# arrondi comptable francais
|
# arrondi comptable francais
|
||||||
amount = amount.quantize(Decimal('1.'), rounding=ROUND_HALF_UP)
|
amount = amount.quantize(Decimal('1.'), rounding=ROUND_HALF_UP)
|
||||||
params = {
|
params = {
|
||||||
'AMOUNT': unicode(amount),
|
'AMOUNT': force_text(amount),
|
||||||
'ORDERID': reference,
|
'ORDERID': reference,
|
||||||
'PSPID': self.pspid,
|
'PSPID': self.pspid,
|
||||||
'LANGUAGE': language,
|
'LANGUAGE': language,
|
||||||
|
@ -517,7 +518,7 @@ class Payment(PaymentCommon):
|
||||||
params['EMAIL'] = email
|
params['EMAIL'] = email
|
||||||
if description:
|
if description:
|
||||||
params['COM'] = description
|
params['COM'] = description
|
||||||
for key, value in kwargs.iteritems():
|
for key, value in kwargs.items():
|
||||||
params[key.upper()] = value
|
params[key.upper()] = value
|
||||||
params['SHASIGN'] = self.sha_sign_in(params)
|
params['SHASIGN'] = self.sha_sign_in(params)
|
||||||
# uniformize all values to UTF-8 string
|
# uniformize all values to UTF-8 string
|
||||||
|
@ -533,7 +534,10 @@ class Payment(PaymentCommon):
|
||||||
return reference, FORM, form
|
return reference, FORM, form
|
||||||
|
|
||||||
def response(self, query_string, **kwargs):
|
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)
|
params = dict((key.upper(), params[key][0]) for key in params)
|
||||||
if not set(params) >= set(['ORDERID', 'PAYID', 'STATUS', 'NCERROR']):
|
if not set(params) >= set(['ORDERID', 'PAYID', 'STATUS', 'NCERROR']):
|
||||||
raise ResponseError()
|
raise ResponseError()
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
# -*- coding: utf-8
|
# -*- coding: utf-8
|
||||||
|
|
||||||
|
import codecs
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
|
@ -9,15 +10,18 @@ from decimal import Decimal, ROUND_DOWN
|
||||||
from Crypto.Signature import PKCS1_v1_5
|
from Crypto.Signature import PKCS1_v1_5
|
||||||
from Crypto.PublicKey import RSA
|
from Crypto.PublicKey import RSA
|
||||||
from Crypto.Hash import SHA
|
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
|
import base64
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
import string
|
import string
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from common import (PaymentCommon, PaymentResponse, FORM, PAID, ERROR, Form,
|
from .common import (PaymentCommon, PaymentResponse, FORM, PAID, ERROR, Form,
|
||||||
ORDERID_TRANSACTION_SEPARATOR, ResponseError)
|
ORDERID_TRANSACTION_SEPARATOR, ResponseError, force_text)
|
||||||
|
|
||||||
__all__ = ['sign', 'Payment']
|
__all__ = ['sign', 'Payment']
|
||||||
|
|
||||||
|
@ -109,9 +113,10 @@ def sign(data, key):
|
||||||
algo = ALGOS[v]
|
algo = ALGOS[v]
|
||||||
break
|
break
|
||||||
assert algo, 'Missing or invalid PBX_HASH'
|
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)
|
tosign = '&'.join(tosign)
|
||||||
logger.debug('signed string %r', tosign)
|
logger.debug('signed string %r', tosign)
|
||||||
|
tosign = tosign.encode('utf-8')
|
||||||
signature = hmac.new(key, tosign, algo)
|
signature = hmac.new(key, tosign, algo)
|
||||||
return tuple(data) + (('PBX_HMAC', signature.hexdigest().upper()),)
|
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):
|
def request(self, amount, email, name=None, orderid=None, **kwargs):
|
||||||
d = OrderedDict()
|
d = OrderedDict()
|
||||||
d['PBX_SITE'] = unicode(self.site)
|
d['PBX_SITE'] = force_text(self.site)
|
||||||
d['PBX_RANG'] = unicode(self.rang).strip()[-2:]
|
d['PBX_RANG'] = force_text(self.rang).strip()[-2:]
|
||||||
d['PBX_IDENTIFIANT'] = unicode(self.identifiant)
|
d['PBX_IDENTIFIANT'] = force_text(self.identifiant)
|
||||||
d['PBX_TOTAL'] = (amount * Decimal(100)).to_integral_value(ROUND_DOWN)
|
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 \
|
transaction_id = kwargs.get('transaction_id') or \
|
||||||
self.transaction_id(12, string.digits, 'paybox', self.site,
|
self.transaction_id(12, string.digits, 'paybox', self.site,
|
||||||
self.rang, self.identifiant)
|
self.rang, self.identifiant)
|
||||||
d['PBX_CMD'] = unicode(transaction_id)
|
d['PBX_CMD'] = force_text(transaction_id)
|
||||||
# prepend order id command reference
|
# prepend order id command reference
|
||||||
if orderid:
|
if orderid:
|
||||||
d['PBX_CMD'] = orderid + ORDERID_TRANSACTION_SEPARATOR + d['PBX_CMD']
|
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_RETOUR'] = 'montant:M;reference:R;code_autorisation:A;erreur:E;signature:K'
|
||||||
d['PBX_HASH'] = 'SHA512'
|
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
|
d['PBX_ARCHIVAGE'] = transaction_id
|
||||||
if self.normal_return_url:
|
if self.normal_return_url:
|
||||||
d['PBX_EFFECTUE'] = self.normal_return_url
|
d['PBX_EFFECTUE'] = self.normal_return_url
|
||||||
|
@ -236,16 +241,21 @@ class Payment(PaymentCommon):
|
||||||
"use automatic_return_url", DeprecationWarning)
|
"use automatic_return_url", DeprecationWarning)
|
||||||
automatic_return_url = self.callback
|
automatic_return_url = self.callback
|
||||||
if automatic_return_url:
|
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 = 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]
|
url = URLS[self.platform]
|
||||||
fields = []
|
fields = []
|
||||||
for k, v in d:
|
for k, v in d:
|
||||||
fields.append({
|
fields.append({
|
||||||
'type': u'hidden',
|
'type': u'hidden',
|
||||||
'name': unicode(k),
|
'name': force_text(k),
|
||||||
'value': unicode(v),
|
'value': force_text(v),
|
||||||
})
|
})
|
||||||
form = Form(url, 'POST', fields, submit_name=None,
|
form = Form(url, 'POST', fields, submit_name=None,
|
||||||
submit_value=u'Envoyer', encoding='utf-8')
|
submit_value=u'Envoyer', encoding='utf-8')
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import urlparse
|
from six.moves.urllib import parse as urlparse
|
||||||
import string
|
import string
|
||||||
import subprocess
|
import subprocess
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
@ -9,8 +9,8 @@ import os.path
|
||||||
import uuid
|
import uuid
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from common import PaymentCommon, HTML, PaymentResponse, ResponseError
|
from .common import PaymentCommon, HTML, PaymentResponse, ResponseError
|
||||||
from cb import CB_RESPONSE_CODES
|
from .cb import CB_RESPONSE_CODES
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Payment backend module for the ATOS/SIPS system used by many Frenck banks.
|
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:
|
if PATHFILE in self.options:
|
||||||
params[PATHFILE] = self.options[PATHFILE]
|
params[PATHFILE] = self.options[PATHFILE]
|
||||||
executable = os.path.join(self.binpath, executable)
|
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)
|
self.logger.debug('executing %s' % args)
|
||||||
result,_ = subprocess.Popen(' '.join(args),
|
result,_ = subprocess.Popen(' '.join(args),
|
||||||
stdout=subprocess.PIPE, shell=True).communicate()
|
stdout=subprocess.PIPE, shell=True).communicate()
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import collections
|
import collections
|
||||||
import json
|
import json
|
||||||
import urlparse
|
from six.moves.urllib import parse as urlparse
|
||||||
import string
|
import string
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
import uuid
|
import uuid
|
||||||
|
@ -11,8 +11,8 @@ from gettext import gettext as _
|
||||||
import requests
|
import requests
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from common import (PaymentCommon, FORM, Form, PaymentResponse, PAID, ERROR,
|
from .common import (PaymentCommon, FORM, Form, PaymentResponse, PAID, ERROR,
|
||||||
CANCELED, ResponseError)
|
CANCELED, ResponseError, force_text)
|
||||||
|
|
||||||
__all__ = ['Payment']
|
__all__ = ['Payment']
|
||||||
|
|
||||||
|
@ -139,12 +139,12 @@ class Payment(PaymentCommon):
|
||||||
}
|
}
|
||||||
|
|
||||||
def encode_data(self, data):
|
def encode_data(self, data):
|
||||||
return u'|'.join(u'%s=%s' % (unicode(key), unicode(value))
|
return u'|'.join(u'%s=%s' % (force_text(key), force_text(value))
|
||||||
for key, value in data.iteritems())
|
for key, value in data.items())
|
||||||
|
|
||||||
def seal_data(self, data):
|
def seal_data(self, data):
|
||||||
s = self.encode_data(data)
|
s = self.encode_data(data)
|
||||||
s += unicode(self.secret_key)
|
s += force_text(self.secret_key)
|
||||||
s = s.encode('utf-8')
|
s = s.encode('utf-8')
|
||||||
s = hashlib.sha256(s).hexdigest()
|
s = hashlib.sha256(s).hexdigest()
|
||||||
return s
|
return s
|
||||||
|
@ -172,13 +172,13 @@ class Payment(PaymentCommon):
|
||||||
info1=None, info2=None, info3=None, next_url=None, **kwargs):
|
info1=None, info2=None, info3=None, next_url=None, **kwargs):
|
||||||
data = self.get_data()
|
data = self.get_data()
|
||||||
transaction_id = self.transaction_id(10, string.digits, 'sips2', data['merchantId'])
|
transaction_id = self.transaction_id(10, string.digits, 'sips2', data['merchantId'])
|
||||||
data['transactionReference'] = unicode(transaction_id)
|
data['transactionReference'] = force_text(transaction_id)
|
||||||
data['orderId'] = orderid or unicode(uuid.uuid4()).replace('-', '')
|
data['orderId'] = orderid or force_text(uuid.uuid4()).replace('-', '')
|
||||||
if info1:
|
if info1:
|
||||||
data['statementReference'] = unicode(info1)
|
data['statementReference'] = force_text(info1)
|
||||||
else:
|
else:
|
||||||
data['statementReference'] = data['transactionReference']
|
data['statementReference'] = data['transactionReference']
|
||||||
data['amount'] = unicode(int(Decimal(amount) * 100))
|
data['amount'] = force_text(int(Decimal(amount) * 100))
|
||||||
if email:
|
if email:
|
||||||
data['billingContact.email'] = email
|
data['billingContact.email'] = email
|
||||||
if 'captureDay' in kwargs:
|
if 'captureDay' in kwargs:
|
||||||
|
@ -215,7 +215,7 @@ class Payment(PaymentCommon):
|
||||||
|
|
||||||
def decode_data(self, data):
|
def decode_data(self, data):
|
||||||
data = data.split('|')
|
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)
|
return collections.OrderedDict(data)
|
||||||
|
|
||||||
def check_seal(self, data, seal):
|
def check_seal(self, data, seal):
|
||||||
|
@ -254,9 +254,9 @@ class Payment(PaymentCommon):
|
||||||
for key in sorted(data.keys()):
|
for key in sorted(data.keys()):
|
||||||
if key in ('keyVersion', 'sealAlgorithm', 'seal'):
|
if key in ('keyVersion', 'sealAlgorithm', 'seal'):
|
||||||
continue
|
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')
|
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):
|
def perform_cash_management_operation(self, endpoint, data):
|
||||||
data['merchantId'] = self.merchant_id
|
data['merchantId'] = self.merchant_id
|
||||||
|
@ -282,13 +282,13 @@ class Payment(PaymentCommon):
|
||||||
|
|
||||||
def cancel(self, amount, bank_data, **kwargs):
|
def cancel(self, amount, bank_data, **kwargs):
|
||||||
data = {}
|
data = {}
|
||||||
data['operationAmount'] = unicode(int(Decimal(amount) * 100))
|
data['operationAmount'] = force_text(int(Decimal(amount) * 100))
|
||||||
data['transactionReference'] = bank_data.get('transactionReference')
|
data['transactionReference'] = bank_data.get('transactionReference')
|
||||||
return self.perform_cash_management_operation('cancel', data)
|
return self.perform_cash_management_operation('cancel', data)
|
||||||
|
|
||||||
def validate(self, amount, bank_data, **kwargs):
|
def validate(self, amount, bank_data, **kwargs):
|
||||||
data = {}
|
data = {}
|
||||||
data['operationAmount'] = unicode(int(Decimal(amount) * 100))
|
data['operationAmount'] = force_text(int(Decimal(amount) * 100))
|
||||||
data['transactionReference'] = bank_data.get('transactionReference')
|
data['transactionReference'] = bank_data.get('transactionReference')
|
||||||
return self.perform_cash_management_operation('validate', data)
|
return self.perform_cash_management_operation('validate', data)
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,8 @@ import binascii
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
import hmac
|
import hmac
|
||||||
import hashlib
|
import hashlib
|
||||||
import urlparse
|
from six.moves.urllib import parse as urlparse
|
||||||
import urllib
|
from six.moves.urllib import parse as urllib
|
||||||
import string
|
import string
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
import logging
|
import logging
|
||||||
|
@ -13,15 +13,15 @@ import re
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
import Crypto.Cipher.DES
|
import Crypto.Cipher.DES
|
||||||
from common import (PaymentCommon, URL, PaymentResponse, RECEIVED, ACCEPTED,
|
from .common import (PaymentCommon, URL, PaymentResponse, RECEIVED, ACCEPTED,
|
||||||
PAID, ERROR, ResponseError)
|
PAID, ERROR, ResponseError, force_byte)
|
||||||
|
|
||||||
def N_(message): return message
|
def N_(message): return message
|
||||||
|
|
||||||
__all__ = ['Payment']
|
__all__ = ['Payment']
|
||||||
|
|
||||||
KEY_DES_KEY = '\x45\x1f\xba\x4f\x4c\x3f\xd4\x97'
|
KEY_DES_KEY = b'\x45\x1f\xba\x4f\x4c\x3f\xd4\x97'
|
||||||
IV = '\x30\x78\x30\x62\x2c\x30\x78\x30'
|
IV = b'\x30\x78\x30\x62\x2c\x30\x78\x30'
|
||||||
REFERENCE = 'reference'
|
REFERENCE = 'reference'
|
||||||
ETAT = 'etat'
|
ETAT = 'etat'
|
||||||
SPCHECKOK = 'spcheckok'
|
SPCHECKOK = 'spcheckok'
|
||||||
|
@ -56,7 +56,7 @@ TEST_STATE = ('99',)
|
||||||
|
|
||||||
|
|
||||||
def decrypt_ntkey(ntkey):
|
def decrypt_ntkey(ntkey):
|
||||||
key = binascii.unhexlify(ntkey.replace(' ',''))
|
key = binascii.unhexlify(ntkey.replace(b' ', b''))
|
||||||
return decrypt_key(key)
|
return decrypt_key(key)
|
||||||
|
|
||||||
def decrypt_key(key):
|
def decrypt_key(key):
|
||||||
|
@ -70,7 +70,7 @@ def extract_values(query_string):
|
||||||
k, v = kv.split('=', 1)
|
k, v = kv.split('=', 1)
|
||||||
if k != 'hmac':
|
if k != 'hmac':
|
||||||
result.append(v)
|
result.append(v)
|
||||||
return ''.join(result)
|
return force_byte(''.join(result))
|
||||||
|
|
||||||
def sign_ntkey_query(ntkey, query):
|
def sign_ntkey_query(ntkey, query):
|
||||||
key = decrypt_ntkey(ntkey)
|
key = decrypt_ntkey(ntkey)
|
||||||
|
@ -89,7 +89,7 @@ def sign_url_paiement(ntkey, query):
|
||||||
data_to_sign = ''.join(fields)
|
data_to_sign = ''.join(fields)
|
||||||
return hmac.new(key[:20], data_to_sign, hashlib.sha1).hexdigest().upper()
|
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"
|
SERVICE_URL = "https://www.spplus.net/paiement/init.do"
|
||||||
|
|
||||||
class Payment(PaymentCommon):
|
class Payment(PaymentCommon):
|
||||||
|
@ -178,7 +178,7 @@ class Payment(PaymentCommon):
|
||||||
form = urlparse.parse_qs(query_string)
|
form = urlparse.parse_qs(query_string)
|
||||||
if not set(form) >= set([REFERENCE, ETAT, REFSFP]):
|
if not set(form) >= set([REFERENCE, ETAT, REFSFP]):
|
||||||
raise ResponseError()
|
raise ResponseError()
|
||||||
for key, value in form.iteritems():
|
for key, value in form.items():
|
||||||
form[key] = value[0]
|
form[key] = value[0]
|
||||||
logger.debug('received query_string %s' % query_string)
|
logger.debug('received query_string %s' % query_string)
|
||||||
logger.debug('parsed as %s' % form)
|
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'
|
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:
|
if len(sys.argv) == 2:
|
||||||
print sign_url_paiement(ntkey, sys.argv[1])
|
print(sign_url_paiement(ntkey, sys.argv[1]))
|
||||||
print sign_ntkey_query(ntkey, sys.argv[1])
|
print(sign_ntkey_query(ntkey, sys.argv[1]))
|
||||||
elif len(sys.argv) > 2:
|
elif len(sys.argv) > 2:
|
||||||
print sign_url_paiement(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])
|
print(sign_ntkey_query(sys.argv[1], sys.argv[2]))
|
||||||
|
|
|
@ -4,13 +4,13 @@ import datetime as dt
|
||||||
import hashlib
|
import hashlib
|
||||||
import logging
|
import logging
|
||||||
import string
|
import string
|
||||||
import urlparse
|
from six.moves.urllib import parse as urlparse
|
||||||
import warnings
|
import warnings
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
|
|
||||||
from common import (PaymentCommon, PaymentResponse, PAID, ERROR, FORM, Form,
|
from .common import (PaymentCommon, PaymentResponse, PAID, ERROR, FORM, Form,
|
||||||
ResponseError, force_text, force_byte)
|
ResponseError, force_text, force_byte)
|
||||||
from cb import CB_RESPONSE_CODES
|
from .cb import CB_RESPONSE_CODES
|
||||||
|
|
||||||
__all__ = ['Payment']
|
__all__ = ['Payment']
|
||||||
|
|
||||||
|
@ -117,20 +117,20 @@ PARAMETERS = [
|
||||||
'ONEY_SANDBOX, PAYPAL, PAYPAL_SB, PAYSAFECARD, '
|
'ONEY_SANDBOX, PAYPAL, PAYPAL_SB, PAYSAFECARD, '
|
||||||
'VISA')),
|
'VISA')),
|
||||||
# must be SINGLE or MULTI with parameters
|
# 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),
|
choices=('SINGLE', 'MULTI'), needed=True),
|
||||||
Parameter('vads_return_mode', None, 48, default='GET',
|
Parameter('vads_return_mode', None, 48, default='GET',
|
||||||
choices=('', 'NONE', 'POST', 'GET')),
|
choices=('', 'NONE', 'POST', 'GET')),
|
||||||
Parameter('signature', 'an', None, length=40),
|
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')),
|
description=_(u'Identifiant de la boutique')),
|
||||||
Parameter('vads_theme_config', 'ans', 32, max_length=255),
|
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),
|
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),
|
Parameter('vads_validation_mode', 'n', 5, max_length=1, choices=('', 0, 1),
|
||||||
default=''),
|
default=''),
|
||||||
Parameter('vads_version', 'an', 01, default='V2', needed=True,
|
Parameter('vads_version', 'an', 1, default='V2', needed=True,
|
||||||
choices=('V2',)),
|
choices=('V2',)),
|
||||||
Parameter('vads_url_success', 'ans', 24, max_length=127),
|
Parameter('vads_url_success', 'ans', 24, max_length=127),
|
||||||
Parameter('vads_url_referral', 'ans', 26, max_length=127),
|
Parameter('vads_url_referral', 'ans', 26, max_length=127),
|
||||||
|
@ -173,7 +173,7 @@ EXTRA_RESULT_MAP = {
|
||||||
|
|
||||||
def add_vads(kwargs):
|
def add_vads(kwargs):
|
||||||
new_vargs = {}
|
new_vargs = {}
|
||||||
for k, v in kwargs.iteritems():
|
for k, v in kwargs.items():
|
||||||
if k.startswith('vads_'):
|
if k.startswith('vads_'):
|
||||||
new_vargs[k] = v
|
new_vargs[k] = v
|
||||||
else:
|
else:
|
||||||
|
@ -276,8 +276,8 @@ class Payment(PaymentCommon):
|
||||||
info2, info3, next_url, kwargs)
|
info2, info3, next_url, kwargs)
|
||||||
# amount unit is cents
|
# amount unit is cents
|
||||||
amount = '%.0f' % (100 * amount)
|
amount = '%.0f' % (100 * amount)
|
||||||
kwargs.update(add_vads({'amount': unicode(amount)}))
|
kwargs.update(add_vads({'amount': force_text(amount)}))
|
||||||
if amount < 0:
|
if int(amount) < 0:
|
||||||
raise ValueError('amount must be an integer >= 0')
|
raise ValueError('amount must be an integer >= 0')
|
||||||
normal_return_url = self.normal_return_url
|
normal_return_url = self.normal_return_url
|
||||||
if next_url:
|
if next_url:
|
||||||
|
@ -285,30 +285,30 @@ class Payment(PaymentCommon):
|
||||||
"set normal_return_url in options", DeprecationWarning)
|
"set normal_return_url in options", DeprecationWarning)
|
||||||
normal_return_url = next_url
|
normal_return_url = next_url
|
||||||
if normal_return_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:
|
if name is not None:
|
||||||
kwargs['vads_cust_name'] = unicode(name)
|
kwargs['vads_cust_name'] = force_text(name)
|
||||||
if first_name is not None:
|
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:
|
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:
|
if address is not None:
|
||||||
kwargs['vads_cust_address'] = unicode(address)
|
kwargs['vads_cust_address'] = force_text(address)
|
||||||
if email is not None:
|
if email is not None:
|
||||||
kwargs['vads_cust_email'] = unicode(email)
|
kwargs['vads_cust_email'] = force_text(email)
|
||||||
if phone is not None:
|
if phone is not None:
|
||||||
kwargs['vads_cust_phone'] = unicode(phone)
|
kwargs['vads_cust_phone'] = force_text(phone)
|
||||||
if info1 is not None:
|
if info1 is not None:
|
||||||
kwargs['vads_order_info'] = unicode(info1)
|
kwargs['vads_order_info'] = force_text(info1)
|
||||||
if info2 is not None:
|
if info2 is not None:
|
||||||
kwargs['vads_order_info2'] = unicode(info2)
|
kwargs['vads_order_info2'] = force_text(info2)
|
||||||
if info3 is not None:
|
if info3 is not None:
|
||||||
kwargs['vads_order_info3'] = unicode(info3)
|
kwargs['vads_order_info3'] = force_text(info3)
|
||||||
if orderid is not None:
|
if orderid is not None:
|
||||||
# check orderid format first
|
# check orderid format first
|
||||||
name = 'vads_order_id'
|
name = 'vads_order_id'
|
||||||
orderid = unicode(orderid)
|
orderid = force_text(orderid)
|
||||||
ptype = 'an-'
|
ptype = 'an-'
|
||||||
p = Parameter(name, ptype, 13, max_length=32)
|
p = Parameter(name, ptype, 13, max_length=32)
|
||||||
if not p.check_value(orderid):
|
if not p.check_value(orderid):
|
||||||
|
@ -318,14 +318,14 @@ class Payment(PaymentCommon):
|
||||||
|
|
||||||
transaction_id = self.transaction_id(6, string.digits, 'systempay',
|
transaction_id = self.transaction_id(6, string.digits, 'systempay',
|
||||||
self.options[VADS_SITE_ID])
|
self.options[VADS_SITE_ID])
|
||||||
kwargs[VADS_TRANS_ID] = unicode(transaction_id)
|
kwargs[VADS_TRANS_ID] = force_text(transaction_id)
|
||||||
fields = kwargs
|
fields = kwargs
|
||||||
for parameter in PARAMETERS:
|
for parameter in PARAMETERS:
|
||||||
name = parameter.name
|
name = parameter.name
|
||||||
# import default parameters from configuration
|
# import default parameters from configuration
|
||||||
if name not in fields \
|
if name not in fields \
|
||||||
and name in self.options:
|
and name in self.options:
|
||||||
fields[name] = unicode(self.options[name])
|
fields[name] = force_text(self.options[name])
|
||||||
# import default parameters from module
|
# import default parameters from module
|
||||||
if name not in fields and parameter.default is not None:
|
if name not in fields and parameter.default is not None:
|
||||||
if callable(parameter.default):
|
if callable(parameter.default):
|
||||||
|
@ -333,7 +333,7 @@ class Payment(PaymentCommon):
|
||||||
else:
|
else:
|
||||||
fields[name] = parameter.default
|
fields[name] = parameter.default
|
||||||
check_vads(fields)
|
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)
|
self.logger.debug('%s request contains fields: %s', __name__, fields)
|
||||||
transaction_id = '%s_%s' % (fields[VADS_TRANS_DATE], transaction_id)
|
transaction_id = '%s_%s' % (fields[VADS_TRANS_DATE], transaction_id)
|
||||||
self.logger.debug('%s transaction id: %s', __name__, transaction_id)
|
self.logger.debug('%s transaction id: %s', __name__, transaction_id)
|
||||||
|
@ -346,14 +346,14 @@ class Payment(PaymentCommon):
|
||||||
'name': force_text(field_name),
|
'name': force_text(field_name),
|
||||||
'value': force_text(field_value),
|
'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
|
return transaction_id, FORM, form
|
||||||
|
|
||||||
def response(self, query_string, **kwargs):
|
def response(self, query_string, **kwargs):
|
||||||
fields = urlparse.parse_qs(query_string, True)
|
fields = urlparse.parse_qs(query_string, True)
|
||||||
if not set(fields) >= set([SIGNATURE, VADS_CTX_MODE, VADS_AUTH_RESULT]):
|
if not set(fields) >= set([SIGNATURE, VADS_CTX_MODE, VADS_AUTH_RESULT]):
|
||||||
raise ResponseError()
|
raise ResponseError()
|
||||||
for key, value in fields.iteritems():
|
for key, value in fields.items():
|
||||||
fields[key] = value[0]
|
fields[key] = value[0]
|
||||||
copy = fields.copy()
|
copy = fields.copy()
|
||||||
bank_status = []
|
bank_status = []
|
||||||
|
@ -417,8 +417,8 @@ class Payment(PaymentCommon):
|
||||||
self.logger.debug('ordered keys %s' % ordered_keys)
|
self.logger.debug('ordered keys %s' % ordered_keys)
|
||||||
ordered_fields = [force_byte(fields[key]) for key in ordered_keys]
|
ordered_fields = [force_byte(fields[key]) for key in ordered_keys]
|
||||||
secret = getattr(self, 'secret_%s' % fields['vads_ctx_mode'].lower())
|
secret = getattr(self, 'secret_%s' % fields['vads_ctx_mode'].lower())
|
||||||
signed_data = '+'.join(ordered_fields)
|
signed_data = b'+'.join(ordered_fields)
|
||||||
signed_data = '%s+%s' % (signed_data, force_byte(secret))
|
signed_data = b'%s+%s' % (signed_data, force_byte(secret))
|
||||||
self.logger.debug(u'generating signature on «%s»', signed_data)
|
self.logger.debug(u'generating signature on «%s»', signed_data)
|
||||||
sign = hashlib.sha1(signed_data).hexdigest()
|
sign = hashlib.sha1(signed_data).hexdigest()
|
||||||
self.logger.debug(u'signature «%s»', sign)
|
self.logger.debug(u'signature «%s»', sign)
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from decimal import Decimal, ROUND_DOWN
|
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)
|
CANCELLED, ERROR, ResponseError)
|
||||||
from urllib import urlencode
|
from six.moves.urllib.parse import urlencode, parse_qs
|
||||||
from urlparse import parse_qs
|
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
import logging
|
import logging
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from systempayv2 import isonow
|
from .systempayv2 import isonow
|
||||||
|
|
||||||
__all__ = ['Payment']
|
__all__ = ['Payment']
|
||||||
|
|
||||||
|
@ -95,12 +95,12 @@ class Payment(PaymentCommon):
|
||||||
refdet = str(refdet)
|
refdet = str(refdet)
|
||||||
if 6 > len(refdet) > 30:
|
if 6 > len(refdet) > 30:
|
||||||
raise ValueError('len(REFDET) < 6 or > 30')
|
raise ValueError('len(REFDET) < 6 or > 30')
|
||||||
except Exception, e:
|
except Exception as e:
|
||||||
raise ValueError('REFDET format invalide, %r' % refdet, e)
|
raise ValueError('REFDET format invalide, %r' % refdet, e)
|
||||||
if objet is not None:
|
if objet is not None:
|
||||||
try:
|
try:
|
||||||
objet = str(objet)
|
objet = str(objet)
|
||||||
except Exception, e:
|
except Exception as e:
|
||||||
raise ValueError('OBJET must be a string', e)
|
raise ValueError('OBJET must be a string', e)
|
||||||
if not objet.replace(' ','').isalnum():
|
if not objet.replace(' ','').isalnum():
|
||||||
raise ValueError('OBJECT must only contains '
|
raise ValueError('OBJECT must only contains '
|
||||||
|
@ -113,7 +113,7 @@ class Payment(PaymentCommon):
|
||||||
raise ValueError('no @ in MEL')
|
raise ValueError('no @ in MEL')
|
||||||
if not (6 <= len(mel) <= 80):
|
if not (6 <= len(mel) <= 80):
|
||||||
raise ValueError('len(MEL) is invalid, must be between 6 and 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)
|
raise ValueError('MEL is not a valid email, %r' % mel, e)
|
||||||
|
|
||||||
saisie = saisie or self.saisie
|
saisie = saisie or self.saisie
|
||||||
|
@ -146,7 +146,7 @@ class Payment(PaymentCommon):
|
||||||
fields = parse_qs(query_string, True)
|
fields = parse_qs(query_string, True)
|
||||||
if not set(fields) >= set(['refdet', 'resultrans']):
|
if not set(fields) >= set(['refdet', 'resultrans']):
|
||||||
raise ResponseError()
|
raise ResponseError()
|
||||||
for key, value in fields.iteritems():
|
for key, value in fields.items():
|
||||||
fields[key] = value[0]
|
fields[key] = value[0]
|
||||||
refdet = fields.get('refdet')
|
refdet = fields.get('refdet')
|
||||||
if refdet is None:
|
if refdet is None:
|
||||||
|
@ -180,14 +180,3 @@ class Payment(PaymentCommon):
|
||||||
bank_data=fields,
|
bank_data=fields,
|
||||||
transaction_id=transaction_id,
|
transaction_id=transaction_id,
|
||||||
test=test)
|
test=test)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
p = Payment({'numcli': '12345'})
|
|
||||||
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')
|
|
||||||
|
|
9
setup.py
9
setup.py
|
@ -46,7 +46,7 @@ class TestCommand(distutils.core.Command):
|
||||||
class eo_sdist(sdist):
|
class eo_sdist(sdist):
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
print "creating VERSION file"
|
print("creating VERSION file")
|
||||||
if os.path.exists('VERSION'):
|
if os.path.exists('VERSION'):
|
||||||
os.remove('VERSION')
|
os.remove('VERSION')
|
||||||
version = get_version()
|
version = get_version()
|
||||||
|
@ -54,7 +54,7 @@ class eo_sdist(sdist):
|
||||||
version_file.write(version)
|
version_file.write(version)
|
||||||
version_file.close()
|
version_file.close()
|
||||||
sdist.run(self)
|
sdist.run(self)
|
||||||
print "removing VERSION file"
|
print("removing VERSION file")
|
||||||
if os.path.exists('VERSION'):
|
if os.path.exists('VERSION'):
|
||||||
os.remove('VERSION')
|
os.remove('VERSION')
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ def get_version():
|
||||||
stderr=subprocess.PIPE)
|
stderr=subprocess.PIPE)
|
||||||
result = p.communicate()[0]
|
result = p.communicate()[0]
|
||||||
if p.returncode == 0:
|
if p.returncode == 0:
|
||||||
version = result.split()[0][1:]
|
version = str(result.split()[0][1:])
|
||||||
version = version.replace('-', '.')
|
version = version.replace('-', '.')
|
||||||
return version
|
return version
|
||||||
return '0.0.0'
|
return '0.0.0'
|
||||||
|
@ -83,7 +83,7 @@ setuptools.setup(
|
||||||
license='GPLv3 or later',
|
license='GPLv3 or later',
|
||||||
description='Common API to use all French online payment credit card '
|
description='Common API to use all French online payment credit card '
|
||||||
'processing services',
|
'processing services',
|
||||||
long_description=file(
|
long_description=open(
|
||||||
os.path.join(
|
os.path.join(
|
||||||
os.path.dirname(__file__),
|
os.path.dirname(__file__),
|
||||||
'README.txt')).read(),
|
'README.txt')).read(),
|
||||||
|
@ -96,6 +96,7 @@ setuptools.setup(
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'pycrypto >= 2.5',
|
'pycrypto >= 2.5',
|
||||||
'requests',
|
'requests',
|
||||||
|
'six',
|
||||||
],
|
],
|
||||||
cmdclass={
|
cmdclass={
|
||||||
'sdist': eo_sdist,
|
'sdist': eo_sdist,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
import urllib
|
from six.moves.urllib import parse as urllib
|
||||||
|
|
||||||
import eopayment
|
import eopayment
|
||||||
import eopayment.ogone as ogone
|
import eopayment.ogone as ogone
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import codecs
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
import base64
|
import base64
|
||||||
import urllib
|
from six.moves.urllib import parse as urllib
|
||||||
|
|
||||||
import eopayment.paybox as paybox
|
import eopayment.paybox as paybox
|
||||||
import eopayment
|
import eopayment
|
||||||
|
@ -19,7 +20,8 @@ BACKEND_PARAMS = {
|
||||||
|
|
||||||
class PayboxTests(TestCase):
|
class PayboxTests(TestCase):
|
||||||
def test_sign(self):
|
def test_sign(self):
|
||||||
key = '0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF'.decode('hex')
|
key = b'0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF'
|
||||||
|
key = codecs.decode(key, 'hex')
|
||||||
d = dict(paybox.sign([
|
d = dict(paybox.sign([
|
||||||
['PBX_SITE', u'12345678'],
|
['PBX_SITE', u'12345678'],
|
||||||
['PBX_RANG', u'32'],
|
['PBX_RANG', u'32'],
|
||||||
|
@ -97,7 +99,7 @@ UX4D2A/QcMvkEcRVXFx5tQqcE9/JnMqE41TF/ebn7jC/MBxxtPFkUN7+EZoeMN7x
|
||||||
OWzAMDm/xsCWRvvel4GGixgm3aQRUPyTrlm4Ksy32Ya0rNnEDMAvB3dxOn7cp8GR
|
OWzAMDm/xsCWRvvel4GGixgm3aQRUPyTrlm4Ksy32Ya0rNnEDMAvB3dxOn7cp8GR
|
||||||
ZdzrudBlevZXpr6iYwIDAQAB
|
ZdzrudBlevZXpr6iYwIDAQAB
|
||||||
-----END PUBLIC KEY-----'''
|
-----END PUBLIC KEY-----'''
|
||||||
data = 'coin\n'
|
data = b'coin\n'
|
||||||
sig64 = '''VCt3sgT0ecacmDEWWNVXJ+jGmIPBMApK42tBJV0FlDjpllOGPy8MsAmLW4/QjTtx
|
sig64 = '''VCt3sgT0ecacmDEWWNVXJ+jGmIPBMApK42tBJV0FlDjpllOGPy8MsAmLW4/QjTtx
|
||||||
z0Dkz0NjxvU+5WzQZh9Uuxr/egRCwV4NMRWqu0zaVVioeBvl4/5CWm4f4/1L9+0m
|
z0Dkz0NjxvU+5WzQZh9Uuxr/egRCwV4NMRWqu0zaVVioeBvl4/5CWm4f4/1L9+0m
|
||||||
FBFKOZhgBJnkC+l6+XhT4aYWKaQ4ocmOMV92yjeXTE4='''
|
FBFKOZhgBJnkC+l6+XhT4aYWKaQ4ocmOMV92yjeXTE4='''
|
||||||
|
|
|
@ -2,8 +2,8 @@ from unittest import TestCase
|
||||||
import eopayment.spplus as spplus
|
import eopayment.spplus as spplus
|
||||||
|
|
||||||
class SPPlustTest(TestCase):
|
class SPPlustTest(TestCase):
|
||||||
ntkey = '58 6d fc 9c 34 91 9b 86 3f ' \
|
ntkey = b'58 6d fc 9c 34 91 9b 86 3f ' \
|
||||||
'fd 64 63 c9 13 4a 26 ba 29 74 1e c7 e9 80 79'
|
b'fd 64 63 c9 13 4a 26 ba 29 74 1e c7 e9 80 79'
|
||||||
|
|
||||||
tests = [('x=coin', 'c04f8266d6ae3ce37551cce996c751be4a95d10a'),
|
tests = [('x=coin', 'c04f8266d6ae3ce37551cce996c751be4a95d10a'),
|
||||||
('x=coin&y=toto', 'ef008e02f8dbf5e70e83da416b0b3a345db203de'),
|
('x=coin&y=toto', 'ef008e02f8dbf5e70e83da416b0b3a345db203de'),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import urlparse
|
from six.moves.urllib import parse as urlparse
|
||||||
|
|
||||||
from eopayment.systempayv2 import Payment, VADS_CUST_FIRST_NAME, \
|
from eopayment.systempayv2 import Payment, VADS_CUST_FIRST_NAME, \
|
||||||
VADS_CUST_LAST_NAME, PAID
|
VADS_CUST_LAST_NAME, PAID
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
from decimal import Decimal
|
||||||
|
from six.moves.urllib.parse import urlparse, parse_qs
|
||||||
|
import eopayment
|
||||||
|
|
||||||
|
def test_tipi():
|
||||||
|
p = eopayment.Payment('tipi', {'numcli': '12345'})
|
||||||
|
payment_id, kind, url = 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')
|
||||||
|
parsed_qs = parse_qs(urlparse(url).query)
|
||||||
|
assert parsed_qs['objet'][0].startswith('tout a fait ')
|
||||||
|
assert parsed_qs['montant'] == ['12312']
|
||||||
|
assert parsed_qs['saisie'] == ['T']
|
||||||
|
assert parsed_qs['mel'] == ['info@entrouvert.com']
|
||||||
|
assert parsed_qs['numcli'] == ['12345']
|
||||||
|
assert parsed_qs['exer'] == ['9999']
|
||||||
|
assert parsed_qs['refdet'] == ['999900000000999999']
|
||||||
|
|
||||||
|
response = p.response('objet=tout+a+fait&montant=12312&saisie=T&mel=info%40entrouvert.com&numcli=12345&exer=9999&refdet=999900000000999999&resultrans=P')
|
||||||
|
assert response.signed # ...
|
||||||
|
assert response.result == eopayment.PAID
|
4
tox.ini
4
tox.ini
|
@ -3,6 +3,10 @@
|
||||||
# test suite on all supported python versions. To use it, "pip install tox"
|
# test suite on all supported python versions. To use it, "pip install tox"
|
||||||
# and then run "tox" from this directory.
|
# and then run "tox" from this directory.
|
||||||
|
|
||||||
|
[tox]
|
||||||
|
envlist = py2,py3
|
||||||
|
toxworkdir = {env:TMPDIR:/tmp}/tox-{env:USER}/eopayment/
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
# django.contrib.auth is not tested it does not work with our templates
|
# django.contrib.auth is not tested it does not work with our templates
|
||||||
commands =
|
commands =
|
||||||
|
|
Loading…
Reference in New Issue