common: add method for checking amount value (#39377)

This commit is contained in:
Valentin Deniaud 2020-03-04 11:12:22 +01:00
parent 74a42e542d
commit 4eff1c29f3
7 changed files with 23 additions and 36 deletions

View File

@ -19,6 +19,7 @@ import os
import random import random
import logging import logging
from datetime import date from datetime import date
from decimal import ROUND_DOWN, Decimal
import six import six
@ -167,6 +168,19 @@ class PaymentCommon(object):
os.close(fd) os.close(fd)
return id return id
@staticmethod
def clean_amount(amount, min_amount=0, max_amount=None):
try:
amount = Decimal(amount)
except ValueError:
raise ValueError('invalid amount %s: it must be a decimal integer with two digits '
'at most after the decimal point', amount)
if int(amount) < min_amount or (max_amount and int(amount) > max_amount):
raise ValueError('amount %s is not in range [%s, %s]' % (amount, min_amount, max_amount))
amount *= Decimal('100') # convert to cents
amount = amount.to_integral_value(ROUND_DOWN)
return str(amount)
class Form(object): class Form(object):
def __init__(self, url, method, fields, encoding='utf-8', def __init__(self, url, method, fields, encoding='utf-8',

View File

@ -25,7 +25,6 @@ import requests
import pytz import pytz
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
@ -287,7 +286,7 @@ class Payment(PaymentCommon):
d['PBX_SITE'] = force_text(self.site) d['PBX_SITE'] = force_text(self.site)
d['PBX_RANG'] = force_text(self.rang).strip()[-2:] d['PBX_RANG'] = force_text(self.rang).strip()[-2:]
d['PBX_IDENTIFIANT'] = force_text(self.identifiant) d['PBX_IDENTIFIANT'] = force_text(self.identifiant)
d['PBX_TOTAL'] = (amount * Decimal(100)).to_integral_value(ROUND_DOWN) d['PBX_TOTAL'] = self.clean_amount(amount)
d['PBX_DEVISE'] = force_text(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,
@ -410,7 +409,7 @@ class Payment(PaymentCommon):
'RANG': self.rang.strip(), 'RANG': self.rang.strip(),
'CLE': force_text(self.cle), 'CLE': force_text(self.cle),
'NUMQUESTION': bank_data['numero_transaction'][0].zfill(10), 'NUMQUESTION': bank_data['numero_transaction'][0].zfill(10),
'MONTANT': (amount * Decimal(100)).to_integral_value(ROUND_DOWN), 'MONTANT': self.clean_amount(amount),
'DEVISE': force_text(self.devise), 'DEVISE': force_text(self.devise),
'NUMTRANS': bank_data['numero_transaction'][0], # paybox transaction number 'NUMTRANS': bank_data['numero_transaction'][0], # paybox transaction number
'NUMAPPEL': bank_data['numero_appel'][0], 'NUMAPPEL': bank_data['numero_appel'][0],

View File

@ -191,20 +191,7 @@ class Payment(PaymentCommon):
return '%s%010d' % (isonow(), random.randint(1, 1000000000)) return '%s%010d' % (isonow(), random.randint(1, 1000000000))
def request(self, amount, email, **kwargs): def request(self, amount, email, **kwargs):
try: montant = self.clean_amount(amount, max_amount=100000)
montant = Decimal(amount)
# MONTANT must be sent as centimes
montant = montant * Decimal('100')
montant = montant.to_integral_value(ROUND_DOWN)
if not (Decimal('0') < montant <= Decimal('10000000')):
raise ValueError('MONTANT > 100000 euros or < 0')
montant = str(montant)
except ValueError:
raise ValueError(
'MONTANT invalid format, must be '
'a decimal integer with less than 4 digits '
'before and 2 digits after the decimal point '
', here it is %s' % repr(amount))
numcli = self.numcli numcli = self.numcli
urlnotif = self.automatic_return_url urlnotif = self.automatic_return_url

View File

@ -18,7 +18,6 @@
from six.moves.urllib import parse as urlparse from six.moves.urllib import parse as urlparse
import string import string
import subprocess import subprocess
from decimal import Decimal
import logging import logging
import os import os
import os.path import os.path
@ -163,7 +162,7 @@ class Payment(PaymentCommon):
params[TRANSACTION_ID] = transaction_id params[TRANSACTION_ID] = transaction_id
params[ORDER_ID] = orderid or str(uuid.uuid4()) params[ORDER_ID] = orderid or str(uuid.uuid4())
params[ORDER_ID] = params[ORDER_ID].replace('-', '') params[ORDER_ID] = params[ORDER_ID].replace('-', '')
params['amount'] = str(int(Decimal(amount) * 100)) params['amount'] = self.clean_amount(amount)
if email: if email:
params['customer_email'] = email params['customer_email'] = email
normal_return_url = self.normal_return_url normal_return_url = self.normal_return_url

View File

@ -205,7 +205,7 @@ class Payment(PaymentCommon):
data['statementReference'] = force_text(info1) data['statementReference'] = force_text(info1)
else: else:
data['statementReference'] = data['transactionReference'] data['statementReference'] = data['transactionReference']
data['amount'] = force_text(int(Decimal(amount) * 100)) data['amount'] = self.clean_amount(amount)
if email: if email:
data['billingContact.email'] = email data['billingContact.email'] = email
if 'capture_day' in kwargs: if 'capture_day' in kwargs:
@ -323,13 +323,13 @@ class Payment(PaymentCommon):
def cancel(self, amount, bank_data, **kwargs): def cancel(self, amount, bank_data, **kwargs):
data = {} data = {}
data['operationAmount'] = force_text(int(Decimal(amount) * 100)) data['operationAmount'] = self.clean_amount(amount)
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'] = force_text(int(Decimal(amount) * 100)) data['operationAmount'] = self.clean_amount(amount)
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)

View File

@ -16,7 +16,6 @@
import re import re
import random import random
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)
@ -90,18 +89,7 @@ class Payment(PaymentCommon):
def request(self, amount, next_url=None, exer=None, orderid=None, def request(self, amount, next_url=None, exer=None, orderid=None,
refdet=None, objet=None, email=None, saisie=None, **kwargs): refdet=None, objet=None, email=None, saisie=None, **kwargs):
try: montant = self.clean_amount(amount, max_amount=9999.99)
montant = Decimal(amount)
if Decimal('0') > montant > Decimal('9999.99'):
raise ValueError('MONTANT > 9999.99 euros')
montant = montant * Decimal('100')
montant = montant.to_integral_value(ROUND_DOWN)
except ValueError:
raise ValueError(
'MONTANT invalid format, must be '
'a decimal integer with less than 4 digits '
'before and 2 digits after the decimal point '
', here it is %s' % repr(amount))
automatic_return_url = self.automatic_return_url automatic_return_url = self.automatic_return_url
if next_url and not automatic_return_url: if next_url and not automatic_return_url:

View File

@ -260,7 +260,7 @@ class PayboxTests(TestCase):
'CLE': 'cancelling_key', 'CLE': 'cancelling_key',
'VERSION': '00103', 'VERSION': '00103',
'TYPE': operation_code, 'TYPE': operation_code,
'MONTANT': Decimal('1000'), 'MONTANT': '1000',
'NUMAPPEL': '30310733', 'NUMAPPEL': '30310733',
'NUMTRANS': '13957441', 'NUMTRANS': '13957441',
'NUMQUESTION': '0013957441', 'NUMQUESTION': '0013957441',