Source code for web_payments.forms


import datetime

from wtforms import Form, validators, ValidationError
from wtforms import StringField, DateField
from wtforms.utils import WebobInputWrapper

from .translation import translation
_ = translation.gettext_lazy

from .utils import get_credit_card_issuer, DictInputWrapper

__all__ = ["DateValidator", "CreditCardNumberValidator", "PaymentForm", "CreditCardPaymentForm", "CreditCardPaymentFormWithName"]

[docs]class DateValidator(object): def __init__(self, message=None): if not message: message = _('Please enter a valid date.') self.message = message def __call__(self, form, field): data = field.data if isinstance(data, str): data = datetime.datetime.strptime(data, '%Y-%m').date() if not data or data < datetime.date.today(): raise ValidationError(self.message)
[docs]class CreditCardNumberValidator(object): def __init__(self, message=None): if not message: message = _('Please enter a valid card number.') self.message = message def __call__(self, form, field): if not self.cart_number_checksum_validation(field.data): raise ValidationError(self.message)
[docs] @staticmethod def cart_number_checksum_validation(number): digits = [] even = False if not number.isdigit(): return False for digit in reversed(number): digit = ord(digit) - ord('0') if even: digit *= 2 if digit >= 10: digit = digit % 10 + digit // 10 digits.append(digit) even = not even return sum(digits) % 10 == 0 if digits else False
[docs]class PaymentForm(Form): ''' Payment form When displaying the form remember to use *action* and *method*. use always formdata except for defaults. formdata of is different to Wtforms as it supports dicts ''' method = 'post' action = '' provider = None payment = None
[docs] class Meta:
[docs] def wrap_formdata(self, form, formdata): """ work around wtform implementation """ if formdata is not None and not hasattr(formdata, 'getlist'): if hasattr(formdata, 'getall'): return WebobInputWrapper(formdata) elif hasattr(formdata, '__getitem__'): # wtform lacks this return DictInputWrapper(formdata) else: raise TypeError("formdata should be a (multi)dict-type wrapper that supports the 'getlist' method") return formdata
def __init__(self, *, provider=None, payment=None, **kwargs): kwargs["obj"] = payment super().__init__(**kwargs) self.provider = provider self.payment = payment if provider and payment: self.action = provider.get_action(payment)
[docs]class CreditCardPaymentForm(PaymentForm): # which credit card types are accepted? VALID_TYPES = None number = StringField(label=_('Credit Card Number'), validators=[validators.InputRequired(), validators.Length(max=32), CreditCardNumberValidator()], render_kw={'autocomplete': 'cc-number'}) expiration = DateField(label=_('Expiration date (MM/YYYY):'), validators=[validators.InputRequired(_('Enter a valid expiration date.')), DateValidator()], format='%m/%Y', render_kw={'autocomplete': 'cc-exp', 'pattern': '[0-9]{2}/[0-9]{4}'}) cvv2 = StringField( label=_('CVV2 Security Number'), validators=[validators.InputRequired(_('Enter a valid security number.')), validators.Regexp('^[0-9]{3,4}$', message=_('Enter a valid security number.'))], description=_( 'Last three digits located on the back of your card.' ' For American Express the four digits found on the front side.'), render_kw={'autocomplete': 'cc-csc'}) def __init__(self, *, valid_types=None, **kwargs): self.VALID_TYPES = valid_types super().__init__(**kwargs)
[docs] def validate_number(self, field): if self.VALID_TYPES and get_credit_card_issuer(field.data)[0] not in self.VALID_TYPES: raise ValidationError( _('We accept only %(valid_types)s') % {"valid_types": ", ".join(self.VALID_TYPES)})
[docs]class CreditCardPaymentFormWithName(CreditCardPaymentForm): name = StringField(label=_('Name on Credit Card'), validators=[validators.Length(max=128)], render_kw={'autocomplete': 'cc-name'})