misc: style, PEP8
This commit is contained in:
parent
d0bdc988ac
commit
768f65f72f
|
@ -1,18 +1,35 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# eopayment - online payment library
|
||||
# Copyright (C) 2011-2020 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import datetime
|
||||
import importlib
|
||||
import logging
|
||||
import pytz
|
||||
|
||||
from .common import ( # noqa: F401
|
||||
URL, HTML, FORM, RECEIVED, ACCEPTED, PAID, DENIED,
|
||||
CANCELED, CANCELLED, ERROR, WAITING, force_text,
|
||||
ResponseError,
|
||||
)
|
||||
|
||||
from .common import (URL, HTML, FORM, RECEIVED, ACCEPTED, PAID, DENIED,
|
||||
CANCELED, CANCELLED, ERROR, WAITING, ResponseError, force_text,
|
||||
PaymentException)
|
||||
|
||||
__all__ = ['Payment', 'URL', 'HTML', 'FORM', 'SIPS',
|
||||
'SYSTEMPAY', 'SPPLUS', 'TIPI', 'DUMMY', 'get_backend', 'RECEIVED', 'ACCEPTED',
|
||||
'PAID', 'DENIED', 'CANCELED', 'CANCELLED', 'ERROR', 'WAITING', 'get_backends', 'PAYFIP_WS']
|
||||
__all__ = ['Payment', 'URL', 'HTML', 'FORM', 'SIPS', 'SYSTEMPAY', 'SPPLUS',
|
||||
'TIPI', 'DUMMY', 'get_backend', 'RECEIVED', 'ACCEPTED', 'PAID',
|
||||
'DENIED', 'CANCELED', 'CANCELLED', 'ERROR', 'WAITING',
|
||||
'get_backends', 'PAYFIP_WS']
|
||||
|
||||
SIPS = 'sips'
|
||||
SIPS2 = 'sips2'
|
||||
|
@ -27,12 +44,15 @@ PAYFIP_WS = 'payfip_ws'
|
|||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_backend(kind):
|
||||
'''Resolve a backend name into a module object'''
|
||||
module = importlib.import_module('.' + kind, package='eopayment')
|
||||
return module.Payment
|
||||
|
||||
__BACKENDS = [ DUMMY, SIPS, SIPS2, SYSTEMPAY, SPPLUS, OGONE, PAYBOX, PAYZEN, TIPI, PAYFIP_WS ]
|
||||
__BACKENDS = [DUMMY, SIPS, SIPS2, SYSTEMPAY, SPPLUS, OGONE, PAYBOX, PAYZEN,
|
||||
TIPI, PAYFIP_WS]
|
||||
|
||||
|
||||
def get_backends():
|
||||
'''Return a dictionnary mapping existing eopayment backends name to their
|
||||
|
@ -44,6 +64,7 @@ def get_backends():
|
|||
'''
|
||||
return dict((backend, get_backend(backend)) for backend in __BACKENDS)
|
||||
|
||||
|
||||
class Payment(object):
|
||||
'''
|
||||
Interface to credit card online payment servers of French banks. The
|
||||
|
|
|
@ -1,4 +1,19 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# eopayment - online payment library
|
||||
# Copyright (C) 2011-2020 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
'''Responses codes emitted by EMV Card or 'Carte Bleu' in France'''
|
||||
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
# eopayment - online payment library
|
||||
# Copyright (C) 2011-2020 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os.path
|
||||
import os
|
||||
import random
|
||||
|
@ -54,6 +70,7 @@ def force_text(s, encoding='utf-8'):
|
|||
return six.text_type(s, encoding, 'ignore')
|
||||
return s
|
||||
|
||||
|
||||
def force_byte(s, encoding='utf-8'):
|
||||
if isinstance(s, bytes):
|
||||
return s
|
||||
|
@ -144,7 +161,7 @@ class PaymentCommon(object):
|
|||
try:
|
||||
fd = os.open(os.path.join(self.PATH, name),
|
||||
os.O_CREAT | os.O_EXCL)
|
||||
except:
|
||||
except Exception:
|
||||
raise
|
||||
else:
|
||||
os.close(fd)
|
||||
|
|
|
@ -1,20 +1,46 @@
|
|||
# eopayment - online payment library
|
||||
# Copyright (C) 2011-2020 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import string
|
||||
import logging
|
||||
import warnings
|
||||
|
||||
def N_(message): return message
|
||||
|
||||
from six.moves.urllib.parse import parse_qs, urlencode
|
||||
|
||||
from .common import (PaymentCommon, URL, PaymentResponse, PAID, ERROR, WAITING,
|
||||
ResponseError, force_text)
|
||||
from .common import (
|
||||
PaymentCommon,
|
||||
PaymentResponse,
|
||||
ResponseError,
|
||||
URL,
|
||||
PAID, ERROR, WAITING,
|
||||
force_text
|
||||
)
|
||||
|
||||
__all__ = ['Payment']
|
||||
|
||||
|
||||
def N_(message):
|
||||
return message
|
||||
|
||||
__all__ = [ 'Payment' ]
|
||||
|
||||
SERVICE_URL = 'http://dummy-payment.demo.entrouvert.com/'
|
||||
ALPHANUM = string.ascii_letters + string.digits
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Payment(PaymentCommon):
|
||||
'''
|
||||
Dummy implementation of the payment interface.
|
||||
|
@ -49,34 +75,41 @@ class Payment(PaymentCommon):
|
|||
'caption': N_('Automatic return URL'),
|
||||
'required': False,
|
||||
},
|
||||
{ 'name': 'dummy_service_url',
|
||||
{
|
||||
'name': 'dummy_service_url',
|
||||
'caption': 'URL of the dummy payment service',
|
||||
'default': SERVICE_URL,
|
||||
'type': str,
|
||||
},
|
||||
{ 'name': 'origin',
|
||||
{
|
||||
'name': 'origin',
|
||||
'caption': 'name of the requesting service, '
|
||||
'to present in the user interface',
|
||||
'type': str,
|
||||
|
||||
},
|
||||
{ 'name': 'siret',
|
||||
{
|
||||
'name': 'siret',
|
||||
'caption': 'dummy siret parameter',
|
||||
'type': str,
|
||||
},
|
||||
{ 'name': 'consider_all_response_signed',
|
||||
'caption': 'All response will be considered as signed '
|
||||
{
|
||||
'name': 'consider_all_response_signed',
|
||||
'caption': (
|
||||
'All response will be considered as signed '
|
||||
'(to test payment locally for example, as you '
|
||||
'cannot received the signed callback)',
|
||||
'cannot received the signed callback)'
|
||||
),
|
||||
'type': bool,
|
||||
'default': False,
|
||||
},
|
||||
{ 'name': 'direct_notification_url',
|
||||
{
|
||||
'name': 'direct_notification_url',
|
||||
'caption': 'direct notification url (replaced by automatic_return_url)',
|
||||
'type': str,
|
||||
'deprecated': True,
|
||||
},
|
||||
{ 'name': 'next_url (replaced by normal_return_url)',
|
||||
{
|
||||
'name': 'next_url (replaced by normal_return_url)',
|
||||
'caption': 'Return URL for the user',
|
||||
'type': str,
|
||||
'deprecated': True,
|
||||
|
@ -85,8 +118,10 @@ class Payment(PaymentCommon):
|
|||
}
|
||||
|
||||
def request(self, amount, name=None, address=None, email=None, phone=None,
|
||||
orderid=None, info1=None, info2=None, info3=None, next_url=None, **kwargs):
|
||||
self.logger.debug('%s amount %s name %s address %s email %s phone %s'
|
||||
orderid=None, info1=None, info2=None, info3=None,
|
||||
next_url=None, **kwargs):
|
||||
self.logger.debug(
|
||||
'%s amount %s name %s address %s email %s phone %s'
|
||||
' next_url %s info1 %s info2 %s info3 %s kwargs: %s',
|
||||
__name__, amount, name, address, email, phone, info1, info2, info3, next_url, kwargs)
|
||||
transaction_id = self.transaction_id(30, ALPHANUM, 'dummy', self.siret)
|
||||
|
@ -109,7 +144,8 @@ class Payment(PaymentCommon):
|
|||
'direct_notification_url': automatic_return_url or '',
|
||||
'origin': self.origin
|
||||
}
|
||||
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))
|
||||
for key in list(query.keys()):
|
||||
if query[key] is None:
|
||||
|
@ -119,9 +155,9 @@ class Payment(PaymentCommon):
|
|||
|
||||
def response(self, query_string, logger=LOGGER, **kwargs):
|
||||
form = parse_qs(force_text(query_string))
|
||||
if not 'transaction_id' in form:
|
||||
if 'transaction_id' not in form:
|
||||
raise ResponseError('missing transaction_id')
|
||||
transaction_id = form.get('transaction_id',[''])[0]
|
||||
transaction_id = form.get('transaction_id', [''])[0]
|
||||
form[self.BANK_ID] = transaction_id
|
||||
|
||||
signed = 'signed' in form
|
||||
|
@ -134,7 +170,8 @@ class Payment(PaymentCommon):
|
|||
if 'waiting' in form:
|
||||
result = WAITING
|
||||
|
||||
response = PaymentResponse(result=result,
|
||||
response = PaymentResponse(
|
||||
result=result,
|
||||
signed=signed,
|
||||
bank_data=form,
|
||||
return_content=content,
|
||||
|
|
|
@ -1,14 +1,35 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# eopayment - online payment library
|
||||
# Copyright (C) 2011-2020 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import hashlib
|
||||
import string
|
||||
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
|
||||
WAITING, ResponseError, force_byte, force_text
|
||||
)
|
||||
|
||||
|
||||
def N_(message):
|
||||
return message
|
||||
|
||||
ENVIRONMENT_TEST = 'TEST'
|
||||
ENVIRONMENT_TEST_URL = 'https://secure.ogone.com/ncol/test/orderstandard.asp'
|
||||
|
@ -409,6 +430,7 @@ TRXDATE
|
|||
VC
|
||||
""".split()
|
||||
|
||||
|
||||
class Payment(PaymentCommon):
|
||||
# See http://payment-services.ingenico.com/fr/fr/ogone/support/guides/integration%20guides/e-commerce
|
||||
description = {
|
||||
|
@ -425,33 +447,40 @@ class Payment(PaymentCommon):
|
|||
'caption': N_('Automatic return URL (ignored, must be set in Ogone backoffice)'),
|
||||
'required': False,
|
||||
},
|
||||
{'name': 'environment',
|
||||
{
|
||||
'name': 'environment',
|
||||
'default': ENVIRONMENT_TEST,
|
||||
'caption': N_(u'Environnement'),
|
||||
'choices': ENVIRONMENT,
|
||||
},
|
||||
{'name': 'pspid',
|
||||
{
|
||||
'name': 'pspid',
|
||||
'caption': N_(u"Nom d'affiliation dans le système"),
|
||||
'required': True,
|
||||
},
|
||||
{'name': 'language',
|
||||
{
|
||||
'name': 'language',
|
||||
'caption': N_(u'Langage'),
|
||||
'default': 'fr_FR',
|
||||
'choices': (('fr_FR', N_('français')),),
|
||||
},
|
||||
{'name': 'hash_algorithm',
|
||||
{
|
||||
'name': 'hash_algorithm',
|
||||
'caption': N_(u'Algorithme de hachage'),
|
||||
'default': 'sha1',
|
||||
},
|
||||
{'name': 'sha_in',
|
||||
{
|
||||
'name': 'sha_in',
|
||||
'caption': N_(u'Clé SHA-IN'),
|
||||
'required': True,
|
||||
},
|
||||
{'name': 'sha_out',
|
||||
{
|
||||
'name': 'sha_out',
|
||||
'caption': N_(u'Clé SHA-OUT'),
|
||||
'required': True,
|
||||
},
|
||||
{'name': 'currency',
|
||||
{
|
||||
'name': 'currency',
|
||||
'caption': N_(u'Monnaie'),
|
||||
'default': 'EUR',
|
||||
'choices': ('EUR',),
|
||||
|
@ -493,7 +522,11 @@ class Payment(PaymentCommon):
|
|||
if orderid:
|
||||
if len(orderid) > 24:
|
||||
raise ValueError('orderid length exceeds 25 characters')
|
||||
reference = orderid + ORDERID_TRANSACTION_SEPARATOR + self.transaction_id(29-len(orderid), string.digits + string.ascii_letters)
|
||||
reference = (
|
||||
orderid
|
||||
+ ORDERID_TRANSACTION_SEPARATOR
|
||||
+ self.transaction_id(29 - len(orderid),
|
||||
string.digits + string.ascii_letters))
|
||||
language = language or self.language
|
||||
# convertir en centimes
|
||||
amount = Decimal(amount) * 100
|
||||
|
|
|
@ -1,4 +1,19 @@
|
|||
# -*- coding: utf-8
|
||||
# eopayment - online payment library
|
||||
# Copyright (C) 2011-2020 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import codecs
|
||||
from collections import OrderedDict
|
||||
|
@ -289,7 +304,9 @@ class Payment(PaymentCommon):
|
|||
'signature:K'
|
||||
)
|
||||
d['PBX_HASH'] = 'SHA512'
|
||||
d['PBX_TIME'] = kwargs.get('time') or (force_text(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
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# eopayment - online payment library
|
||||
# Copyright (C) 2011-2019 Entr'ouvert
|
||||
# Copyright (C) 2011-2020 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
|
|
|
@ -1,9 +1,26 @@
|
|||
# eopayment - online payment library
|
||||
# Copyright (C) 2011-2020 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from copy import deepcopy
|
||||
|
||||
from . import systempayv2
|
||||
|
||||
__all__ = ['Payment']
|
||||
|
||||
|
||||
class Payment(systempayv2.Payment):
|
||||
service_url = 'https://secure.payzen.eu/vads-payment/'
|
||||
|
||||
|
|
|
@ -1,4 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# eopayment - online payment library
|
||||
# Copyright (C) 2011-2020 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from six.moves.urllib import parse as urlparse
|
||||
import string
|
||||
import subprocess
|
||||
|
@ -34,23 +50,25 @@ __all__ = ['Payment']
|
|||
BINPATH = 'binpath'
|
||||
PATHFILE = 'pathfile'
|
||||
AUTHORISATION_ID = 'authorisation_id'
|
||||
REQUEST_VALID_PARAMS = ['merchant_id', 'merchant_country', 'amount',
|
||||
'currency_code', 'pathfile', 'normal_return_url', 'cancel_return_url',
|
||||
'automatic_response_url', 'language', 'payment_means', 'header_flag',
|
||||
'capture_day', 'capture_mode', 'bgcolor', 'block_align', 'block_order',
|
||||
'textcolor', 'receipt_complement', 'caddie', 'customer_id',
|
||||
'customer_email', 'customer_ip_address', 'data', 'return_context',
|
||||
'target', 'order_id']
|
||||
REQUEST_VALID_PARAMS = [
|
||||
'merchant_id', 'merchant_country', 'amount', 'currency_code', 'pathfile',
|
||||
'normal_return_url', 'cancel_return_url', 'automatic_response_url',
|
||||
'language', 'payment_means', 'header_flag', 'capture_day', 'capture_mode',
|
||||
'bgcolor', 'block_align', 'block_order', 'textcolor', 'receipt_complement',
|
||||
'caddie', 'customer_id', 'customer_email', 'customer_ip_address', 'data',
|
||||
'return_context', 'target', 'order_id',
|
||||
]
|
||||
|
||||
RESPONSE_PARAMS = ['code', 'error', 'merchant_id', 'merchant_country',
|
||||
'amount', 'transaction_id', 'payment_means', 'transmission_date',
|
||||
'payment_time', 'payment_date', 'response_code', 'payment_certificate',
|
||||
AUTHORISATION_ID, 'currency_code', 'card_number', 'cvv_flag',
|
||||
'cvv_response_code', 'bank_response_code', 'complementary_code',
|
||||
'complementary_info', 'return_context', 'caddie', 'receipt_complement',
|
||||
'merchant_language', 'language', 'customer_id', 'order_id',
|
||||
'customer_email', 'customer_ip_address', 'capture_day', 'capture_mode',
|
||||
'data', ]
|
||||
RESPONSE_PARAMS = [
|
||||
'code', 'error', 'merchant_id', 'merchant_country', 'amount',
|
||||
'transaction_id', 'payment_means', 'transmission_date', 'payment_time',
|
||||
'payment_date', 'response_code', 'payment_certificate', AUTHORISATION_ID,
|
||||
'currency_code', 'card_number', 'cvv_flag', 'cvv_response_code',
|
||||
'bank_response_code', 'complementary_code', 'complementary_info',
|
||||
'return_context', 'caddie', 'receipt_complement', 'merchant_language',
|
||||
'language', 'customer_id', 'order_id', 'customer_email',
|
||||
'customer_ip_address', 'capture_day', 'capture_mode', 'data',
|
||||
]
|
||||
|
||||
DATA = 'DATA'
|
||||
PARAMS = 'params'
|
||||
|
@ -69,34 +87,35 @@ LOGGER = logging.getLogger(__name__)
|
|||
CB_BANK_RESPONSE_CODES = CB_RESPONSE_CODES
|
||||
|
||||
AMEX_BANK_RESPONSE_CODE = {
|
||||
'00': 'Transaction approuvée ou traitée avec succès',
|
||||
'02': 'Dépassement de plafond',
|
||||
'04': 'Conserver la carte',
|
||||
'05': 'Ne pas honorer',
|
||||
'97': 'Échéance de la temporisation de surveillance globale',
|
||||
'00': 'Transaction approuvée ou traitée avec succès',
|
||||
'02': 'Dépassement de plafond',
|
||||
'04': 'Conserver la carte',
|
||||
'05': 'Ne pas honorer',
|
||||
'97': 'Échéance de la temporisation de surveillance globale',
|
||||
}
|
||||
|
||||
FINAREF_BANK_RESPONSE_CODE = {
|
||||
'00': 'Transaction approuvée',
|
||||
'03': 'Commerçant inconnu - Identifiant de commerçant incorrect',
|
||||
'05': 'Compte / Porteur avec statut bloqué ou invalide',
|
||||
'11': 'Compte / porteur inconnu',
|
||||
'16': 'Provision insuffisante',
|
||||
'20': 'Commerçant invalide - Code monnaie incorrect - ' + \
|
||||
'Opération commerciale inconnue - Opération commerciale invalide',
|
||||
'80': 'Transaction approuvée avec dépassement',
|
||||
'81': 'Transaction approuvée avec augmentation capital',
|
||||
'82': 'Transaction approuvée NPAI',
|
||||
'83': 'Compte / porteur invalide',
|
||||
'00': 'Transaction approuvée',
|
||||
'03': 'Commerçant inconnu - Identifiant de commerçant incorrect',
|
||||
'05': 'Compte / Porteur avec statut bloqué ou invalide',
|
||||
'11': 'Compte / porteur inconnu',
|
||||
'16': 'Provision insuffisante',
|
||||
'20': (
|
||||
'Commerçant invalide - Code monnaie incorrect - '
|
||||
'Opération commerciale inconnue - Opération commerciale invalide'
|
||||
),
|
||||
'80': 'Transaction approuvée avec dépassement',
|
||||
'81': 'Transaction approuvée avec augmentation capital',
|
||||
'82': 'Transaction approuvée NPAI',
|
||||
'83': 'Compte / porteur invalide',
|
||||
}
|
||||
|
||||
|
||||
class Payment(PaymentCommon):
|
||||
description = {
|
||||
'caption': 'SIPS',
|
||||
'parameters': [{
|
||||
'name': 'merchand_id',
|
||||
},
|
||||
'parameters': [
|
||||
{'name': 'merchand_id'},
|
||||
{'name': 'merchant_country', },
|
||||
{'name': 'currency_code', }
|
||||
],
|
||||
|
@ -114,8 +133,11 @@ class Payment(PaymentCommon):
|
|||
executable = os.path.join(self.binpath, executable)
|
||||
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()
|
||||
result, _ = subprocess.Popen(
|
||||
' '.join(args),
|
||||
stdout=subprocess.PIPE,
|
||||
shell=True
|
||||
).communicate()
|
||||
try:
|
||||
if result[0] == '!':
|
||||
result = result[1:]
|
||||
|
@ -133,11 +155,11 @@ class Payment(PaymentCommon):
|
|||
params.update(self.options)
|
||||
return params
|
||||
|
||||
def request(self, amount, name=None, address=None, email=None, phone=None, orderid=None,
|
||||
info1=None, info2=None, info3=None, next_url=None, **kwargs):
|
||||
def request(self, amount, name=None, address=None, email=None, phone=None,
|
||||
orderid=None, info1=None, info2=None, info3=None,
|
||||
next_url=None, **kwargs):
|
||||
params = self.get_request_params()
|
||||
transaction_id = self.transaction_id(6, string.digits, 'sips',
|
||||
params[MERCHANT_ID])
|
||||
transaction_id = self.transaction_id(6, string.digits, 'sips', params[MERCHANT_ID])
|
||||
params[TRANSACTION_ID] = transaction_id
|
||||
params[ORDER_ID] = orderid or str(uuid.uuid4())
|
||||
params[ORDER_ID] = params[ORDER_ID].replace('-', '')
|
||||
|
@ -159,7 +181,7 @@ class Payment(PaymentCommon):
|
|||
|
||||
def response(self, query_string, **kwargs):
|
||||
form = urlparse.parse_qs(query_string)
|
||||
if not DATA in form:
|
||||
if DATA not in form:
|
||||
raise ResponseError('missing %s' % DATA)
|
||||
params = {'message': form[DATA][0]}
|
||||
result = self.execute('response', params)
|
||||
|
|
|
@ -1,4 +1,19 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# eopayment - online payment library
|
||||
# Copyright (C) 2011-2020 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import datetime
|
||||
from decimal import Decimal
|
||||
|
|
|
@ -1,7 +1,22 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# eopayment - online payment library
|
||||
# Copyright (C) 2011-2020 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from decimal import Decimal
|
||||
import binascii
|
||||
from gettext import gettext as _
|
||||
import hmac
|
||||
import hashlib
|
||||
from six.moves.urllib import parse as urlparse
|
||||
|
@ -13,10 +28,14 @@ import re
|
|||
import warnings
|
||||
|
||||
import Crypto.Cipher.DES
|
||||
from .common import (PaymentCommon, URL, PaymentResponse, RECEIVED, ACCEPTED,
|
||||
PAID, ERROR, ResponseError, force_byte)
|
||||
from .common import (
|
||||
PaymentCommon, URL, PaymentResponse, RECEIVED, ACCEPTED,
|
||||
PAID, ERROR, ResponseError, force_byte
|
||||
)
|
||||
|
||||
def N_(message): return message
|
||||
|
||||
def N_(message):
|
||||
return message
|
||||
|
||||
__all__ = ['Payment']
|
||||
|
||||
|
@ -39,7 +58,7 @@ SPPLUS_RESPONSE_CODES = {
|
|||
'8': 'Chèque encaissé',
|
||||
'10': 'Paiement terminé',
|
||||
'11': 'Echéance du paiement annulée par le commerçant',
|
||||
'12': 'Abandon de l\’internaute',
|
||||
'12': 'Abandon de l’internaute',
|
||||
'15': 'Remboursement enregistré',
|
||||
'16': 'Remboursement annulé',
|
||||
'17': 'Remboursement accepté',
|
||||
|
@ -59,10 +78,12 @@ def decrypt_ntkey(ntkey):
|
|||
key = binascii.unhexlify(ntkey.replace(b' ', b''))
|
||||
return decrypt_key(key)
|
||||
|
||||
|
||||
def decrypt_key(key):
|
||||
CIPHER = Crypto.Cipher.DES.new(KEY_DES_KEY, Crypto.Cipher.DES.MODE_CBC, IV)
|
||||
return CIPHER.decrypt(key)
|
||||
|
||||
|
||||
def extract_values(query_string):
|
||||
kvs = query_string.split('&')
|
||||
result = []
|
||||
|
@ -72,26 +93,30 @@ def extract_values(query_string):
|
|||
result.append(v)
|
||||
return force_byte(''.join(result))
|
||||
|
||||
|
||||
def sign_ntkey_query(ntkey, query):
|
||||
key = decrypt_ntkey(ntkey)
|
||||
data_to_sign = extract_values(query)
|
||||
return hmac.new(key[:20], data_to_sign, hashlib.sha1).hexdigest().upper()
|
||||
|
||||
PAIEMENT_FIELDS = [ 'siret', REFERENCE, 'langue', 'devise', 'montant',
|
||||
'taxe', 'validite' ]
|
||||
PAIEMENT_FIELDS = [
|
||||
'siret', REFERENCE, 'langue', 'devise', 'montant', 'taxe', 'validite'
|
||||
]
|
||||
|
||||
|
||||
def sign_url_paiement(ntkey, query):
|
||||
if '?' in query:
|
||||
query = query[query.index('?')+1:]
|
||||
query = query[query.index('?') + 1:]
|
||||
key = decrypt_ntkey(ntkey)
|
||||
data = urlparse.parse_qs(query, True)
|
||||
fields = [data.get(field,[''])[0] for field in PAIEMENT_FIELDS]
|
||||
fields = [data.get(field, [''])[0] for field in PAIEMENT_FIELDS]
|
||||
data_to_sign = ''.join(fields)
|
||||
return hmac.new(key[:20], data_to_sign, hashlib.sha1).hexdigest().upper()
|
||||
|
||||
ALPHANUM = string.ascii_letters + string.digits
|
||||
SERVICE_URL = "https://www.spplus.net/paiement/init.do"
|
||||
|
||||
|
||||
class Payment(PaymentCommon):
|
||||
description = {
|
||||
'caption': "SPPlus payment service of French bank Caisse d'epargne",
|
||||
|
@ -107,30 +132,40 @@ class Payment(PaymentCommon):
|
|||
'caption': N_('Automatic return URL'),
|
||||
'required': False,
|
||||
},
|
||||
{ 'name': 'cle',
|
||||
{
|
||||
'name': 'cle',
|
||||
'caption': 'Secret key, a 40 digits hexadecimal number',
|
||||
'regexp': re.compile('^ *((?:[a-fA-F0-9] *){40}) *$')
|
||||
},
|
||||
{ 'name': 'siret',
|
||||
'caption': 'Siret of the entreprise augmented with the '
|
||||
'site number, example: 00000000000001-01',
|
||||
'regexp': re.compile('^ *(\d{14}-\d{2}) *$')
|
||||
{
|
||||
'name': 'siret',
|
||||
'caption': (
|
||||
'Siret of the entreprise augmented with the '
|
||||
'site number, example: 00000000000001-01'
|
||||
),
|
||||
'regexp': re.compile(r'^ *(\d{14}-\d{2}) *$')
|
||||
},
|
||||
{ 'name': 'langue',
|
||||
{
|
||||
'name': 'langue',
|
||||
'caption': 'Language of the customers',
|
||||
'default': 'FR',
|
||||
},
|
||||
{ 'name': 'taxe',
|
||||
{
|
||||
'name': 'taxe',
|
||||
'caption': 'Taxes',
|
||||
'default': '0.00'
|
||||
},
|
||||
{ 'name': 'modalite',
|
||||
{
|
||||
'name': 'modalite',
|
||||
'caption': '1x, 2x, 3x, xx, nx (if multiple separated by "/")',
|
||||
'default': '1x',
|
||||
},
|
||||
{ 'name': 'moyen',
|
||||
'caption': 'AUR, AMX, CBS, CGA, '
|
||||
'CHK, DIN, PRE (if multiple separate by "/")',
|
||||
{
|
||||
'name': 'moyen',
|
||||
'caption': (
|
||||
'AUR, AMX, CBS, CGA, '
|
||||
'CHK, DIN, PRE (if multiple separate by "/")'
|
||||
),
|
||||
'default': 'CBS',
|
||||
},
|
||||
]
|
||||
|
@ -138,13 +173,14 @@ class Payment(PaymentCommon):
|
|||
devise = '978'
|
||||
|
||||
def request(self, amount, name=None, address=None, email=None, phone=None,
|
||||
orderid=None, info1=None, info2=None, info3=None, next_url=None,
|
||||
logger=LOGGER, **kwargs):
|
||||
orderid=None, info1=None, info2=None, info3=None,
|
||||
next_url=None, logger=LOGGER, **kwargs):
|
||||
logger.debug('requesting spplus payment with montant %s email=%s' % (amount, email))
|
||||
reference = self.transaction_id(20, ALPHANUM, 'spplus', self.siret)
|
||||
validite = dt.date.today()+dt.timedelta(days=1)
|
||||
validite = validite.strftime('%d/%m/%Y')
|
||||
fields = { 'siret': self.siret,
|
||||
fields = {
|
||||
'siret': self.siret,
|
||||
'devise': self.devise,
|
||||
'langue': self.langue,
|
||||
'taxe': self.taxe,
|
||||
|
@ -153,7 +189,8 @@ class Payment(PaymentCommon):
|
|||
'validite': validite,
|
||||
'version': '1',
|
||||
'modalite': self.modalite,
|
||||
'moyen': self.moyen }
|
||||
'moyen': self.moyen,
|
||||
}
|
||||
if email:
|
||||
fields['email'] = email
|
||||
normal_return_url = self.normal_return_url
|
||||
|
@ -162,15 +199,14 @@ class Payment(PaymentCommon):
|
|||
"set normal_return_url in options", DeprecationWarning)
|
||||
normal_return_url = next_url
|
||||
if normal_return_url:
|
||||
if (not normal_return_url.startswith('http://') \
|
||||
and not normal_return_url.startswith('https://')) \
|
||||
or '?' in normal_return_url:
|
||||
if ((not normal_return_url.startswith('http://')
|
||||
and not normal_return_url.startswith('https://'))
|
||||
or '?' in normal_return_url):
|
||||
raise ValueError('normal_return_url must be an absolute URL without parameters')
|
||||
fields['urlretour'] = normal_return_url
|
||||
logger.debug('sending fields %s' % fields)
|
||||
query = urllib.urlencode(fields)
|
||||
url = '%s?%s&hmac=%s' % (SERVICE_URL, query, sign_url_paiement(self.cle,
|
||||
query))
|
||||
url = '%s?%s&hmac=%s' % (SERVICE_URL, query, sign_url_paiement(self.cle, query))
|
||||
logger.debug('full url %s' % url)
|
||||
return reference, URL, url
|
||||
|
||||
|
|
|
@ -1,4 +1,19 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# eopayment - online payment library
|
||||
# Copyright (C) 2011-2020 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import pytz
|
||||
import datetime as dt
|
||||
|
@ -286,7 +301,8 @@ class Payment(PaymentCommon):
|
|||
|
||||
def request(self, amount, name=None, first_name=None, last_name=None,
|
||||
address=None, email=None, phone=None, orderid=None, info1=None,
|
||||
info2=None, info3=None, next_url=None, manual_validation=None, **kwargs):
|
||||
info2=None, info3=None, next_url=None, manual_validation=None,
|
||||
**kwargs):
|
||||
'''
|
||||
Create the URL string to send a request to SystemPay
|
||||
'''
|
||||
|
@ -332,8 +348,8 @@ class Payment(PaymentCommon):
|
|||
ptype = 'an-'
|
||||
p = Parameter(name, ptype, 13, max_length=32)
|
||||
if not p.check_value(orderid):
|
||||
raise ValueError('%s value %s is not of the type %s' % (name,
|
||||
orderid, ptype))
|
||||
raise ValueError(
|
||||
'%s value %s is not of the type %s' % (name, orderid, ptype))
|
||||
kwargs[name] = orderid
|
||||
|
||||
transaction_id = self.transaction_id(6, string.digits, 'systempay',
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
# eopayment - online payment library
|
||||
# Copyright (C) 2011-2020 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import re
|
||||
import random
|
||||
from decimal import Decimal, ROUND_DOWN
|
||||
|
|
|
@ -1,13 +1,29 @@
|
|||
# eopayment - online payment library
|
||||
# Copyright (C) 2011-2020 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from datetime import date, datetime, timedelta
|
||||
|
||||
import mock
|
||||
import pytest
|
||||
|
||||
import eopayment
|
||||
|
||||
|
||||
def do_mock_backend(monkeypatch):
|
||||
|
||||
class MockBackend(object):
|
||||
|
||||
request = mock.Mock()
|
||||
|
||||
description = {
|
||||
|
@ -38,7 +54,6 @@ def do_mock_backend(monkeypatch):
|
|||
return MockBackend
|
||||
return backend
|
||||
|
||||
import eopayment
|
||||
monkeypatch.setattr(eopayment, 'get_backend', get_backend)
|
||||
return MockBackend, eopayment.Payment('kind', None)
|
||||
|
||||
|
|
|
@ -1,6 +1,24 @@
|
|||
# eopayment - online payment library
|
||||
# Copyright (C) 2011-2020 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
import eopayment
|
||||
import pytest
|
||||
|
||||
|
||||
def test_dummy():
|
||||
options = {
|
||||
'direct_notification_url': 'http://example.com/direct_notification_url',
|
||||
|
@ -8,17 +26,31 @@ def test_dummy():
|
|||
'origin': 'Mairie de Perpette-les-oies'
|
||||
}
|
||||
p = eopayment.Payment('dummy', 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])
|
||||
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.signed
|
||||
assert r.transaction_id == '6Tfw2e1bPyYnz7CedZqvdHt7T9XX6T'
|
||||
assert r.return_content 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])
|
||||
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.signed
|
||||
assert r.transaction_id == '6Tfw2e1bPyYnz7CedZqvdHt7T9XX6T'
|
||||
assert r.return_content == 'signature ok'
|
||||
|
||||
data = {'foo': 'bar'}
|
||||
with pytest.raises(eopayment.ResponseError, match='missing transaction_id'):
|
||||
p.response('foo=bar')
|
||||
|
|
|
@ -1,6 +1,23 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# eopayment - online payment library
|
||||
# Copyright (C) 2011-2020 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from unittest import TestCase
|
||||
from xml.etree import ElementTree as ET
|
||||
|
||||
from six.moves.urllib import parse as urllib
|
||||
|
||||
import eopayment
|
||||
|
@ -17,17 +34,18 @@ BACKEND_PARAMS = {
|
|||
'automatic_return_url': u'http://example.com/autömatic_réturn_url'
|
||||
}
|
||||
|
||||
class OgoneTests(TestCase):
|
||||
|
||||
class OgoneTests(TestCase):
|
||||
def test_request(self):
|
||||
ogone_backend = eopayment.Payment('ogone', BACKEND_PARAMS)
|
||||
amount = '42.42'
|
||||
order_id = u'my ordér'
|
||||
reference, kind, what = ogone_backend.request(amount=amount,
|
||||
orderid=order_id, email='foo@example.com')
|
||||
reference, kind, what = ogone_backend.request(
|
||||
amount=amount,
|
||||
orderid=order_id,
|
||||
email='foo@example.com')
|
||||
self.assertEqual(len(reference), 30)
|
||||
assert reference.startswith(order_id)
|
||||
from xml.etree import ElementTree as ET
|
||||
root = ET.fromstring(str(what))
|
||||
self.assertEqual(root.tag, 'form')
|
||||
self.assertEqual(root.attrib['method'], 'POST')
|
||||
|
@ -67,27 +85,39 @@ class OgoneTests(TestCase):
|
|||
def test_iso_8859_1_response(self):
|
||||
ogone_backend = eopayment.Payment('ogone', BACKEND_PARAMS)
|
||||
order_id = 'lRXK4Rl1N2yIR3R5z7Kc'
|
||||
backend_response = 'orderID=lRXK4Rl1N2yIR3R5z7Kc¤cy=EUR&amount=7%2E5&PM=CreditCard&ACCEPTANCE=test123&STATUS=9&CARDNO=XXXXXXXXXXXX9999&ED=0118&CN=Miha%EF+Serghe%EF&TRXDATE=10%2F24%2F16&PAYID=3011228911&NCERROR=0&BRAND=MasterCard&IP=80%2E12%2E92%2E47&SHASIGN=435D5E36E1F4B17739C1054FFD204218E65C15AB'
|
||||
backend_response = (
|
||||
'orderID=lRXK4Rl1N2yIR3R5z7Kc¤cy=EUR&amount=7%2E5'
|
||||
'&PM=CreditCard&ACCEPTANCE=test123&STATUS=9'
|
||||
'&CARDNO=XXXXXXXXXXXX9999&ED=0118'
|
||||
'&CN=Miha%EF+Serghe%EF&TRXDATE=10%2F24%2F16'
|
||||
'&PAYID=3011228911&NCERROR=0&BRAND=MasterCard'
|
||||
'&IP=80%2E12%2E92%2E47&SHASIGN=435D5E36E1F4B17739C1054FFD204218E65C15AB'
|
||||
)
|
||||
response = ogone_backend.response(backend_response)
|
||||
assert response.signed
|
||||
self.assertEqual(response.order_id, order_id)
|
||||
|
||||
def test_bad_response(self):
|
||||
ogone_backend = eopayment.Payment('ogone', BACKEND_PARAMS)
|
||||
order_id = 'myorder'
|
||||
data = {'payid': '32100123', 'status': 9, 'ncerror': 0}
|
||||
with self.assertRaisesRegexp(ResponseError, 'missing ORDERID, PAYID, STATUS or NCERROR'):
|
||||
response = ogone_backend.response(urllib.urlencode(data))
|
||||
ogone_backend.response(urllib.urlencode(data))
|
||||
|
||||
def test_bank_transfer_response(self):
|
||||
ogone_backend = eopayment.Payment('ogone', BACKEND_PARAMS)
|
||||
order_id = 'myorder'
|
||||
data = {'orderid': u'myorder', 'status': u'41', 'payid': u'3011229363',
|
||||
'cn': u'User', 'ncerror': u'0',
|
||||
data = {
|
||||
'orderid': u'myorder',
|
||||
'status': u'41',
|
||||
'payid': u'3011229363',
|
||||
'cn': u'User',
|
||||
'ncerror': u'0',
|
||||
'trxdate': u'10/24/16',
|
||||
'brand': 'Bank transfer', 'pm': 'bank transfer',
|
||||
'currency': u'eur', 'amount': u'7.5',
|
||||
'shasign': u'0E35F687ACBEAA6CA769E0ADDBD0863EB6C1678A'}
|
||||
'brand': 'Bank transfer',
|
||||
'pm': 'bank transfer',
|
||||
'currency': u'eur',
|
||||
'amount': u'7.5',
|
||||
'shasign': u'0E35F687ACBEAA6CA769E0ADDBD0863EB6C1678A'
|
||||
}
|
||||
# uniformize to utf-8 first
|
||||
for k in data:
|
||||
data[k] = eopayment.common.force_byte(data[k])
|
||||
|
|
|
@ -1,4 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# eopayment - online payment library
|
||||
# Copyright (C) 2011-2020 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
import codecs
|
||||
from unittest import TestCase
|
||||
|
@ -16,13 +32,22 @@ BACKEND_PARAMS = {
|
|||
'site': u'12345678',
|
||||
'rang': u'001',
|
||||
'identifiant': u'12345678',
|
||||
'shared_secret': u'0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF',
|
||||
'shared_secret': (
|
||||
u'0123456789ABCDEF0123456789ABCDEF01234'
|
||||
u'56789ABCDEF0123456789ABCDEF0123456789'
|
||||
u'ABCDEF0123456789ABCDEF0123456789ABCDE'
|
||||
u'F0123456789ABCDEF'
|
||||
),
|
||||
'callback': u'http://example.com/callback',
|
||||
}
|
||||
|
||||
|
||||
class PayboxTests(TestCase):
|
||||
def test_sign(self):
|
||||
key = b'0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF'
|
||||
key = (b'0123456789ABCDEF0123456789ABCDEF0123456789'
|
||||
b'ABCDEF0123456789ABCDEF0123456789ABCDEF0123'
|
||||
b'456789ABCDEF0123456789ABCDEF0123456789ABCD'
|
||||
b'EF')
|
||||
key = codecs.decode(key, 'hex')
|
||||
d = dict(paybox.sign([
|
||||
['PBX_SITE', u'12345678'],
|
||||
|
@ -35,9 +60,13 @@ class PayboxTests(TestCase):
|
|||
['PBX_RETOUR', u'Mt:M;Ref:R;Auto:A;Erreur:E'],
|
||||
['PBX_HASH', u'SHA512'],
|
||||
['PBX_TIME', u'2015-06-08T16:21:16+02:00'],
|
||||
],
|
||||
key))
|
||||
result = '7E74D8E9A0DBB65AAE51C5C50C2668FD98FC99AEDF18244BB1935F602B6C2E953B61FD84364F34FDB88B049901C0A07F6040AF446BBF5589113F48A733D551D4'
|
||||
], key))
|
||||
result = (
|
||||
'7E74D8E9A0DBB65AAE51C5C50C2668FD98FC99AED'
|
||||
'F18244BB1935F602B6C2E953B61FD84364F34FDB8'
|
||||
'8B049901C0A07F6040AF446BBF5589113F48A733D'
|
||||
'551D4'
|
||||
)
|
||||
self.assertIn('PBX_HMAC', d)
|
||||
self.assertEqual(d['PBX_HMAC'], result)
|
||||
|
||||
|
@ -61,14 +90,23 @@ class PayboxTests(TestCase):
|
|||
'PBX_RANG': '01',
|
||||
'PBX_SITE': '12345678',
|
||||
'PBX_IDENTIFIANT': '12345678',
|
||||
'PBX_RETOUR': 'montant:M;reference:R;code_autorisation:A;erreur:E;numero_appel:T;numero_transaction:S;date_transaction:W;heure_transaction:Q;signature:K',
|
||||
'PBX_RETOUR': (
|
||||
'montant:M;reference:R;code_autorisation:A;'
|
||||
'erreur:E;numero_appel:T;numero_transaction:S;'
|
||||
'date_transaction:W;heure_transaction:Q;signature:K'
|
||||
),
|
||||
'PBX_TIME': time,
|
||||
'PBX_PORTEUR': email,
|
||||
'PBX_CMD': order_id + eopayment.common.ORDERID_TRANSACTION_SEPARATOR + transaction,
|
||||
'PBX_TOTAL': amount.replace('.', ''),
|
||||
'PBX_DEVISE': '978',
|
||||
'PBX_HASH': 'SHA512',
|
||||
'PBX_HMAC': 'CE29AB421D9FF5E22B52A0F0D31BB881E6D3040B7A0B390AC3F335292A75D2389253A3ED6B3E430A90D30088F6AC29F792B484A2ECFC36A1B73771796A5FD15C',
|
||||
'PBX_HMAC': (
|
||||
'CE29AB421D9FF5E22B52A0F0D31BB881E6D'
|
||||
'3040B7A0B390AC3F335292A75D2389253A3'
|
||||
'ED6B3E430A90D30088F6AC29F792B484A2E'
|
||||
'CFC36A1B73771796A5FD15C'
|
||||
),
|
||||
'PBX_ARCHIVAGE': '1234',
|
||||
'PBX_REPONDRE_A': 'http://example.com/callback',
|
||||
'PBX_AUTOSEULE': 'N'
|
||||
|
@ -101,7 +139,8 @@ class PayboxTests(TestCase):
|
|||
transaction_id=transaction, time=time)
|
||||
root = ET.fromstring(str(what))
|
||||
|
||||
form_params = dict(((node.attrib['name'], node.attrib['value']) for node in root if node.attrib['type'] == 'hidden'))
|
||||
form_params = dict(
|
||||
((node.attrib['name'], node.attrib['value']) for node in root if node.attrib['type'] == 'hidden'))
|
||||
self.assertIn('PBX_DIFF', form_params)
|
||||
self.assertEqual(form_params['PBX_DIFF'], '07')
|
||||
|
||||
|
@ -135,7 +174,6 @@ class PayboxTests(TestCase):
|
|||
self.assertIn('PBX_DIFF', form_params)
|
||||
self.assertEqual(form_params['PBX_DIFF'], '02')
|
||||
|
||||
|
||||
def test_request_with_authorization_only(self):
|
||||
params = BACKEND_PARAMS.copy()
|
||||
time = '2018-08-21T10:26:32+02:00'
|
||||
|
@ -151,7 +189,8 @@ class PayboxTests(TestCase):
|
|||
transaction_id=transaction, time=time)
|
||||
root = ET.fromstring(str(what))
|
||||
|
||||
form_params = dict(((node.attrib['name'], node.attrib['value']) for node in root if node.attrib['type'] == 'hidden'))
|
||||
form_params = dict(
|
||||
((node.attrib['name'], node.attrib['value']) for node in root if node.attrib['type'] == 'hidden'))
|
||||
self.assertEqual(form_params['PBX_AUTOSEULE'], 'O')
|
||||
|
||||
def test_response(self):
|
||||
|
@ -180,11 +219,16 @@ class PayboxTests(TestCase):
|
|||
params = BACKEND_PARAMS.copy()
|
||||
params['cle'] = 'cancelling_key'
|
||||
backend = eopayment.Payment('paybox', params)
|
||||
bank_data = {'numero_transaction': ['13957441'],
|
||||
bank_data = {
|
||||
'numero_transaction': ['13957441'],
|
||||
'numero_appel': ['30310733'],
|
||||
'reference': ['830657461681']
|
||||
'reference': ['830657461681'],
|
||||
}
|
||||
backend_raw_response = u"""NUMTRANS=0013989865&NUMAPPEL=0030378572&NUMQUESTION=0013989862&SITE=1999888&RANG=32&AUTORISATION=XXXXXX&CODEREPONSE=00000&COMMENTAIRE=Demande traitée avec succès&REFABONNE=&PORTEUR="""
|
||||
backend_raw_response = (
|
||||
u'NUMTRANS=0013989865&NUMAPPEL=0030378572&NUMQUESTION=0013989862'
|
||||
u'&SITE=1999888&RANG=32&AUTORISATION=XXXXXX&CODEREPONSE=00000'
|
||||
u'&COMMENTAIRE=Demande traitée avec succès&REFABONNE=&PORTEUR='
|
||||
)
|
||||
backend_expected_response = {"CODEREPONSE": "00000",
|
||||
"RANG": "32",
|
||||
"AUTORISATION": "XXXXXX",
|
||||
|
@ -206,7 +250,8 @@ class PayboxTests(TestCase):
|
|||
assert 'DATEQ' in params_sent
|
||||
# don't care about its value
|
||||
params_sent.pop('DATEQ')
|
||||
expected_params = {'CLE': 'cancelling_key',
|
||||
expected_params = {
|
||||
'CLE': 'cancelling_key',
|
||||
'VERSION': '00103',
|
||||
'TYPE': operation_code,
|
||||
'MONTANT': Decimal('1000'),
|
||||
|
@ -233,26 +278,33 @@ class PayboxTests(TestCase):
|
|||
error_response = u"""CODEREPONSE=00015&COMMENTAIRE=PAYBOX : Transaction non trouvée"""
|
||||
response = mock.Mock(status_code=200, text=error_response)
|
||||
requests_post.return_value = response
|
||||
self.assertRaisesRegexp(eopayment.ResponseError, 'Transaction non trouvée', getattr(backend, operation_name),
|
||||
Decimal('10'), bank_data)
|
||||
|
||||
self.assertRaisesRegexp(
|
||||
eopayment.ResponseError,
|
||||
'Transaction non trouvée',
|
||||
getattr(backend, operation_name),
|
||||
Decimal('10'),
|
||||
bank_data)
|
||||
|
||||
def test_validate_payment(self):
|
||||
params = BACKEND_PARAMS.copy()
|
||||
params['cle'] = 'cancelling_key'
|
||||
backend = eopayment.Payment('paybox', params)
|
||||
bank_data = {'numero_transaction': ['13957441'],
|
||||
bank_data = {
|
||||
'numero_transaction': ['13957441'],
|
||||
'numero_appel': ['30310733'],
|
||||
'reference': ['830657461681']
|
||||
}
|
||||
backend_raw_response = u"""NUMTRANS=0013989865&NUMAPPEL=0030378572&NUMQUESTION=0013989862&SITE=1999888&RANG=32&AUTORISATION=XXXXXX&CODEREPONSE=00000&COMMENTAIRE=Demande traitée avec succès&REFABONNE=&PORTEUR="""
|
||||
backend_raw_response = (
|
||||
u'NUMTRANS=0013989865&NUMAPPEL=0030378572&NUMQUESTION=0013989862'
|
||||
u'&SITE=1999888&RANG=32&AUTORISATION=XXXXXX&CODEREPONSE=00000'
|
||||
u'&COMMENTAIRE=Demande traitée avec succès&REFABONNE=&PORTEUR='
|
||||
)
|
||||
|
||||
with mock.patch('eopayment.paybox.requests.post') as requests_post:
|
||||
response = mock.Mock(status_code=200, text=backend_raw_response)
|
||||
requests_post.return_value = response
|
||||
backend.validate(Decimal(12), bank_data)
|
||||
|
||||
|
||||
def test_rsa_signature_validation(self):
|
||||
pkey = '''-----BEGIN PUBLIC KEY-----
|
||||
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDUgYufHuheMztK1LhQSG6xsOzb
|
||||
|
@ -277,8 +329,11 @@ FBFKOZhgBJnkC+l6+XhT4aYWKaQ4ocmOMV92yjeXTE4='''
|
|||
backend = eopayment.Payment('paybox', params)
|
||||
|
||||
transaction_id, kind, what = backend.request(
|
||||
Decimal(amount), email=email, orderid=order_id,
|
||||
transaction_id=transaction, time=time)
|
||||
Decimal(amount),
|
||||
email=email,
|
||||
orderid=order_id,
|
||||
transaction_id=transaction,
|
||||
time=time)
|
||||
root = ET.fromstring(str(what))
|
||||
form_params = dict((
|
||||
(node.attrib['name'], node.attrib['value']) for node in root
|
||||
|
@ -287,8 +342,12 @@ FBFKOZhgBJnkC+l6+XhT4aYWKaQ4ocmOMV92yjeXTE4='''
|
|||
self.assertEqual(form_params['PBX_AUTOSEULE'], 'N')
|
||||
|
||||
transaction_id, kind, what = backend.request(
|
||||
Decimal(amount), email=email, orderid=order_id,
|
||||
transaction_id=transaction, time=time, manual_validation=True)
|
||||
Decimal(amount),
|
||||
email=email,
|
||||
orderid=order_id,
|
||||
transaction_id=transaction,
|
||||
time=time,
|
||||
manual_validation=True)
|
||||
root = ET.fromstring(str(what))
|
||||
form_params = dict((
|
||||
(node.attrib['name'], node.attrib['value']) for node in root
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# coding: utf-8
|
||||
#
|
||||
# eopayment - online payment library
|
||||
# Copyright (C) 2011-2019 Entr'ouvert
|
||||
# Copyright (C) 2011-2020 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
|
|
|
@ -1,4 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# eopayment - online payment library
|
||||
# Copyright (C) 2011-2020 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
import eopayment
|
||||
import pytest
|
||||
|
@ -9,7 +25,7 @@ def test_build_request():
|
|||
transaction, f, form = backend.request(amount=u'12', last_name=u'Foo',
|
||||
first_name=u'Félix000000')
|
||||
data = [f for f in form.fields if f['name'] == 'Data']
|
||||
assert not u'lix000000' in data[0]['value']
|
||||
assert u'lix000000' not in data[0]['value']
|
||||
|
||||
transaction, f, form = backend.request(amount=u'12')
|
||||
data = [f for f in form.fields if f['name'] == 'Data']
|
||||
|
@ -29,19 +45,20 @@ def test_options():
|
|||
assert payment.backend.get_data()['captureMode'] == 'VALIDATION'
|
||||
|
||||
payment = eopayment.Payment('sips2', {})
|
||||
assert not 'captureDay' in payment.backend.get_data()
|
||||
assert 'captureDay' not in payment.backend.get_data()
|
||||
|
||||
payment = eopayment.Payment('sips2', {'capture_day': '10'})
|
||||
assert 'captureDay' in payment.backend.get_data()
|
||||
|
||||
|
||||
def test_parse_response():
|
||||
qs = '''Data=captureDay%3D0%7CcaptureMode%3DAUTHOR_CAPTURE%7CcurrencyCode%3D978%7CmerchantId%3D002001000000001%7CorderChannel%3DINTERNET%7CresponseCode%3D00%7CtransactionDateTime%3D2016-02-01T17%3A44%3A20%2B01%3A00%7CtransactionReference%3D668930%7CkeyVersion%3D1%7CacquirerResponseCode%3D00%7Camount%3D1200%7CauthorisationId%3D12345%7CcardCSCResultCode%3D4E%7CpanExpiryDate%3D201605%7CpaymentMeanBrand%3DMASTERCARD%7CpaymentMeanType%3DCARD%7CcustomerIpAddress%3D82.244.203.243%7CmaskedPan%3D5100%23%23%23%23%23%23%23%23%23%23%23%2300%7CorderId%3Dd4903de7027f4d56ac01634fd7ab9526%7CholderAuthentRelegation%3DN%7CholderAuthentStatus%3D3D_ERROR%7CtransactionOrigin%3DINTERNET%7CpaymentPattern%3DONE_SHOT&Seal=6ca3247765a19b45d25ad54ef4076483e7d55583166bd5ac9c64357aac097602&InterfaceVersion=HP_2.0&Encode='''
|
||||
qs = '''Data=captureDay%3D0%7CcaptureMode%3DAUTHOR_CAPTURE%7CcurrencyCode%3D978%7CmerchantId%3D002001000000001%7CorderChannel%3DINTERNET%7CresponseCode%3D00%7CtransactionDateTime%3D2016-02-01T17%3A44%3A20%2B01%3A00%7CtransactionReference%3D668930%7CkeyVersion%3D1%7CacquirerResponseCode%3D00%7Camount%3D1200%7CauthorisationId%3D12345%7CcardCSCResultCode%3D4E%7CpanExpiryDate%3D201605%7CpaymentMeanBrand%3DMASTERCARD%7CpaymentMeanType%3DCARD%7CcustomerIpAddress%3D82.244.203.243%7CmaskedPan%3D5100%23%23%23%23%23%23%23%23%23%23%23%2300%7CorderId%3Dd4903de7027f4d56ac01634fd7ab9526%7CholderAuthentRelegation%3DN%7CholderAuthentStatus%3D3D_ERROR%7CtransactionOrigin%3DINTERNET%7CpaymentPattern%3DONE_SHOT&Seal=6ca3247765a19b45d25ad54ef4076483e7d55583166bd5ac9c64357aac097602&InterfaceVersion=HP_2.0&Encode=''' # noqa: E501
|
||||
backend = eopayment.Payment('sips2', {})
|
||||
response = backend.response(qs)
|
||||
assert response.signed
|
||||
assert response.transaction_date is None
|
||||
|
||||
qs = '''Data=captureDay%3D0%7CcaptureMode%3DAUTHOR_CAPTURE%7CcurrencyCode%3D978%7CmerchantId%3D002001000000001%7CorderChannel%3DINTERNET%7CresponseCode%3D00%7CtransactionDateTime%3D2016-02-01T17%3A44%3A20%2B01%3A00%7CtransactionReference%3D668930%7CkeyVersion%3D1%7CacquirerResponseCode%3D00%7Camount%3D1200%7CauthorisationId%3D12345%7CcardCSCResultCode%3D4E%7CpanExpiryDate%3D201605%7CpaymentMeanBrand%3DMASTERCARD%7CpaymentMeanType%3DCARD%7CcustomerIpAddress%3D82.244.203.243%7CmaskedPan%3D5100%23%23%23%23%23%23%23%23%23%23%23%2300%7CorderId%3Dd4903de7027f4d56ac01634fd7ab9526%7CholderAuthentRelegation%3DN%7CholderAuthentStatus%3D3D_ERROR%7CtransactionOrigin%3DINTERNET%7CpaymentPattern%3DONE_SHOT%7CtransactionDateTime%3D2020-01-01%2001:01:01&Seal=6ca3247765a19b45d25ad54ef4076483e7d55583166bd5ac9c64357aac097602&InterfaceVersion=HP_2.0&Encode='''
|
||||
qs = '''Data=captureDay%3D0%7CcaptureMode%3DAUTHOR_CAPTURE%7CcurrencyCode%3D978%7CmerchantId%3D002001000000001%7CorderChannel%3DINTERNET%7CresponseCode%3D00%7CtransactionDateTime%3D2016-02-01T17%3A44%3A20%2B01%3A00%7CtransactionReference%3D668930%7CkeyVersion%3D1%7CacquirerResponseCode%3D00%7Camount%3D1200%7CauthorisationId%3D12345%7CcardCSCResultCode%3D4E%7CpanExpiryDate%3D201605%7CpaymentMeanBrand%3DMASTERCARD%7CpaymentMeanType%3DCARD%7CcustomerIpAddress%3D82.244.203.243%7CmaskedPan%3D5100%23%23%23%23%23%23%23%23%23%23%23%2300%7CorderId%3Dd4903de7027f4d56ac01634fd7ab9526%7CholderAuthentRelegation%3DN%7CholderAuthentStatus%3D3D_ERROR%7CtransactionOrigin%3DINTERNET%7CpaymentPattern%3DONE_SHOT%7CtransactionDateTime%3D2020-01-01%2001:01:01&Seal=6ca3247765a19b45d25ad54ef4076483e7d55583166bd5ac9c64357aac097602&InterfaceVersion=HP_2.0&Encode=''' # noqa: E501
|
||||
response = backend.response(qs)
|
||||
assert not response.signed
|
||||
assert response.transaction_date.isoformat() == '2020-01-01T01:01:01+01:00'
|
||||
|
|
|
@ -1,14 +1,36 @@
|
|||
# eopayment - online payment library
|
||||
# Copyright (C) 2011-2020 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from unittest import TestCase
|
||||
import eopayment.spplus as spplus
|
||||
from eopayment import ResponseError
|
||||
|
||||
|
||||
class SPPlustTest(TestCase):
|
||||
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'),
|
||||
('x=wdwd%20%3Fdfgfdgd&z=343&hmac=04233b78bb5aff332d920d4e89394f505ec58a2a', '04233b78bb5aff332d920d4e89394f505ec58a2a')]
|
||||
tests = [
|
||||
('x=coin',
|
||||
'c04f8266d6ae3ce37551cce996c751be4a95d10a'),
|
||||
('x=coin&y=toto',
|
||||
'ef008e02f8dbf5e70e83da416b0b3a345db203de'),
|
||||
('x=wdwd%20%3Fdfgfdgd&z=343&hmac=04233b78bb5aff332d920d4e89394f505ec58a2a',
|
||||
'04233b78bb5aff332d920d4e89394f505ec58a2a')
|
||||
]
|
||||
|
||||
def test_spplus(self):
|
||||
payment = spplus.Payment({'cle': self.ntkey, 'siret': '00000000000001-01'})
|
||||
|
|
|
@ -1,4 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# eopayment - online payment library
|
||||
# Copyright (C) 2011-2020 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import pytest
|
||||
|
@ -25,14 +42,18 @@ def get_field(form, field_name):
|
|||
|
||||
def test_systempayv2():
|
||||
p = Payment(PARAMS)
|
||||
data = {'amount': 15.24, 'orderid': '654321',
|
||||
data = {
|
||||
'amount': 15.24,
|
||||
'orderid': '654321',
|
||||
'first_name': u'Jean Michél',
|
||||
'last_name': u'Mihaï'
|
||||
}
|
||||
qs = 'vads_version=V2&vads_page_action=PAYMENT&vads_action_mode=INTERACTIV' \
|
||||
'E&vads_payment_config=SINGLE&vads_site_id=12345678&vads_ctx_mode=TES' \
|
||||
'T&vads_trans_id=654321&vads_trans_date=20090501193530&vads_amount=15' \
|
||||
qs = (
|
||||
'vads_version=V2&vads_page_action=PAYMENT&vads_action_mode=INTERACTIV'
|
||||
'E&vads_payment_config=SINGLE&vads_site_id=12345678&vads_ctx_mode=TES'
|
||||
'T&vads_trans_id=654321&vads_trans_date=20090501193530&vads_amount=15'
|
||||
'24&vads_currency=978&vads_cust_first_name=Jean+Mich%C3%A9l&vads_cust_last_name=Mihaï'
|
||||
)
|
||||
qs = urlparse.parse_qs(qs)
|
||||
for key in qs.keys():
|
||||
qs[key] = qs[key][0]
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
# eopayment - online payment library
|
||||
# Copyright (C) 2011-2020 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import datetime
|
||||
from decimal import Decimal
|
||||
from six.moves.urllib.parse import urlparse, parse_qs
|
||||
|
|
Loading…
Reference in New Issue