diff options
author | Patryk Zawadzki <patrys@room-303.com> | 2017-02-23 18:24:58 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-02-23 18:24:58 +0100 |
commit | 35c368141bcb7dd9ff436b282bce42094a519083 (patch) | |
tree | a417552d555c38e9395d60d6f7725eff68565418 | |
parent | b33b33960d100b00e35fa18e3acad4228049064b (diff) | |
parent | ca3a957421e135e5a0487c64a84b1c8aa54c85a7 (diff) | |
download | django-payments-taler-35c368141bcb7dd9ff436b282bce42094a519083.tar.gz django-payments-taler-35c368141bcb7dd9ff436b282bce42094a519083.tar.bz2 django-payments-taler-35c368141bcb7dd9ff436b282bce42094a519083.zip |
Merge pull request #124 from artursmet/improve-stripe-js
Fix stripejs input names
-rw-r--r-- | .travis.yml | 9 | ||||
-rw-r--r-- | payments/fields.py | 12 | ||||
-rw-r--r-- | payments/static/js/payments/stripe.js | 10 | ||||
-rw-r--r-- | payments/stripe/forms.py | 29 | ||||
-rw-r--r-- | payments/stripe/test_stripe.py | 14 | ||||
-rw-r--r-- | payments/utils.py | 14 | ||||
-rw-r--r-- | payments/widgets.py | 38 | ||||
-rw-r--r-- | tox.ini | 4 |
8 files changed, 107 insertions, 23 deletions
diff --git a/.travis.yml b/.travis.yml index 51921a0..d1a4a70 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,19 +6,22 @@ script: - tox env: - TOXENV=py27-django18 - - TOXENV=py27-django19 - TOXENV=py27-django110 + - TOXENV=py27-django111 - TOXENV=py27-django_master - TOXENV=py34-django18 - - TOXENV=py34-django19 - TOXENV=py34-django110 + - TOXENV=py34-django111 - TOXENV=py34-django_master - TOXENV=py35-django18 - - TOXENV=py35-django19 - TOXENV=py35-django110 + - TOXENV=py35-django111 - TOXENV=py35-django_master matrix: allow_failures: + - env: TOXENV=py27-django111 + - env: TOXENV=py34-django111 + - env: TOXENV=py35-django111 - env: TOXENV=py27-django_master - env: TOXENV=py34-django_master - env: TOXENV=py35-django_master diff --git a/payments/fields.py b/payments/fields.py index 8ebd8be..1c1290a 100644 --- a/payments/fields.py +++ b/payments/fields.py @@ -8,6 +8,7 @@ from django.core import validators from django.utils.translation import ugettext_lazy as _ from .core import get_credit_card_issuer +from .utils import get_month_choices, get_year_choices from .widgets import CreditCardExpiryWidget, CreditCardNumberWidget @@ -60,11 +61,8 @@ class CreditCardNumberField(forms.CharField): return sum(digits) % 10 == 0 if digits else False -class CreditCardExpiryField(forms.MultiValueField): - EXP_MONTH = [(str(x), '%02d' % (x,)) for x in range(1, 13)] - EXP_YEAR = [(str(x), str(x)) for x in range(date.today().year, - date.today().year + 15)] +class CreditCardExpiryField(forms.MultiValueField): default_error_messages = { 'invalid_month': 'Enter a valid month.', @@ -77,13 +75,13 @@ class CreditCardExpiryField(forms.MultiValueField): fields = ( forms.ChoiceField( - choices=[('', _('Month'))] + self.EXP_MONTH, + choices=get_month_choices(), error_messages={'invalid': errors['invalid_month']}, widget=forms.Select( attrs={'autocomplete': 'cc-exp-month', 'required': 'required'})), forms.ChoiceField( - choices=[('', _('Year'))] + self.EXP_YEAR, + choices=get_year_choices(), error_messages={'invalid': errors['invalid_year']}, widget=forms.Select( attrs={'autocomplete': 'cc-exp-year', @@ -96,7 +94,7 @@ class CreditCardExpiryField(forms.MultiValueField): def clean(self, value): exp = super(CreditCardExpiryField, self).clean(value) - if date.today() > exp: + if exp and date.today() > exp: raise forms.ValidationError( "The expiration date you entered is in the past.") return exp diff --git a/payments/static/js/payments/stripe.js b/payments/static/js/payments/stripe.js index a17a8e4..feca99c 100644 --- a/payments/static/js/payments/stripe.js +++ b/payments/static/js/payments/stripe.js @@ -7,11 +7,11 @@ document.addEventListener('DOMContentLoaded', function () { var button = this.querySelector('[type=submit]'); button.disabled = true; Stripe.card.createToken({ - name: this.elements['name'].value, - number: this.elements['number'].value, - cvc: this.elements['cvv2'].value, - exp_month: this.elements['expiration_0'].value, - exp_year: this.elements['expiration_1'].value, + name: this.elements.id_name.value, + number: this.elements.id_number.value, + cvc: this.elements.id_cvv2.value, + exp_month: this.elements.id_expiration_0.value, + exp_year: this.elements.id_expiration_1.value, address_line1: stripeInput.attributes['data-address-line1'].value, address_line2: stripeInput.attributes['data-address-line2'].value, address_city: stripeInput.attributes['data-address-city'].value, diff --git a/payments/stripe/forms.py b/payments/stripe/forms.py index e3863b9..0c83657 100644 --- a/payments/stripe/forms.py +++ b/payments/stripe/forms.py @@ -1,13 +1,15 @@ from __future__ import unicode_literals +import stripe from django import forms from django.utils.translation import ugettext as _ -import stripe -from .widgets import StripeCheckoutWidget, StripeWidget -from .. import RedirectNeeded +from .. import FraudStatus, PaymentStatus, RedirectNeeded from ..forms import PaymentForm as BasePaymentForm, CreditCardPaymentFormWithName -from ..import FraudStatus, PaymentStatus +from ..utils import get_month_choices, get_year_choices +from ..widgets import ( + SensitiveTextInput, SensitiveSelect, CreditCardExpiryWidget) +from .widgets import StripeCheckoutWidget, StripeWidget class StripeFormMixin(object): @@ -87,3 +89,22 @@ class PaymentForm(StripeFormMixin, CreditCardPaymentFormWithName): stripe_attrs['data-address-state'] = self.payment.billing_country_area stripe_attrs['data-address-zip'] = self.payment.billing_postcode stripe_attrs['data-address-country'] = self.payment.billing_country_code + widget_map = { + 'name': SensitiveTextInput( + attrs={'autocomplete': 'cc-name', 'required': 'required'}), + 'cvv2': SensitiveTextInput(attrs={'autocomplete': 'cc-csc'}), + 'number': SensitiveTextInput( + attrs={'autocomplete': 'cc-number', 'required': 'required'}), + 'expiration': CreditCardExpiryWidget( + widgets=[ + SensitiveSelect( + attrs={'autocomplete': 'cc-exp-month', + 'required': 'required'}, + choices=get_month_choices()), + SensitiveSelect( + attrs={'autocomplete': 'cc-exp-year', + 'required': 'required'}, + choices=get_year_choices())])} + for field_name, widget in widget_map.items(): + self.fields[field_name].widget = widget + self.fields[field_name].required = False diff --git a/payments/stripe/test_stripe.py b/payments/stripe/test_stripe.py index 676bd54..dd08ccf 100644 --- a/payments/stripe/test_stripe.py +++ b/payments/stripe/test_stripe.py @@ -6,7 +6,7 @@ from mock import patch, Mock from unittest import TestCase import stripe -from . import StripeProvider +from . import StripeProvider, StripeCardProvider from .. import FraudStatus, PaymentStatus, RedirectNeeded @@ -168,3 +168,15 @@ class TestStripeProvider(TestCase): form = provider.get_form(payment, data=data) msg = 'This payment has already been processed.' self.assertEqual(form.errors['__all__'][0], msg) + + def test_form_doesnt_have_name_attributes_on_fields(self): + payment = Payment() + store_name = 'Test store' + provider = StripeCardProvider( + name=store_name, + secret_key=SECRET_KEY, public_key=PUBLIC_KEY) + form = provider.get_form(payment) + sensitive_fields = ['name', 'cvv2', 'expiration', 'number'] + for field_name in sensitive_fields: + field = form[field_name] + self.assertTrue('name=' not in str(field)) diff --git a/payments/utils.py b/payments/utils.py new file mode 100644 index 0000000..2d78659 --- /dev/null +++ b/payments/utils.py @@ -0,0 +1,14 @@ +from datetime import date + +from django.utils.translation import ugettext_lazy as _ + + +def get_month_choices(): + month_choices = [(str(x), '%02d' % (x,)) for x in range(1, 13)] + return [('', _('Month'))] + month_choices + + +def get_year_choices(): + year_choices = [(str(x), str(x)) for x in range( + date.today().year, date.today().year + 15)] + return [('', _('Year'))] + year_choices diff --git a/payments/widgets.py b/payments/widgets.py index e135675..a843cbe 100644 --- a/payments/widgets.py +++ b/payments/widgets.py @@ -1,7 +1,12 @@ import re +from django import VERSION as DJANGO_VERSION +from django.forms.utils import flatatt +from django.forms.widgets import TextInput, MultiWidget, Select from django.template.loader import render_to_string -from django.forms.widgets import TextInput, MultiWidget +from django.utils.encoding import force_text +from django.utils.html import format_html +from django.utils.safestring import mark_safe class CreditCardNumberWidget(TextInput): @@ -32,3 +37,34 @@ class CreditCardExpiryWidget(MultiWidget): def format_output(self, rendered_widgets): ctx = {'month': rendered_widgets[0], 'year': rendered_widgets[1]} return render_to_string('payments/credit_card_expiry_widget.html', ctx) + + +class SensitiveTextInput(TextInput): + + def render(self, name, value, attrs=None): + # Explicitly skip parent implementation and exclude + # 'name' from attrs + if value is None: + value = '' + final_attrs = self.build_attrs(attrs, type=self.input_type) + if value != '': + # Only add the 'value' attribute if a value is non-empty. + final_attrs['value'] = force_text(self.format_value(value)) + return format_html('<input{} />', flatatt(final_attrs)) + + +class SensitiveSelect(Select): + + def render(self, name, value, attrs=None): + if value is None: + value = '' + final_attrs = self.build_attrs(attrs) + output = [format_html('<select{}>', flatatt(final_attrs))] + if DJANGO_VERSION <= (1, 10, 0): + options = self.render_options([], [value]) + else: + options = self.render_options([value]) + if options: + output.append(options) + output.append('</select>') + return mark_safe('\n'.join(output)) @@ -1,13 +1,13 @@ [tox] -envlist = py{27,34,35}-django{18,19,_master},py{27,34}-django14 +envlist = py{27,34,35}-django{18,110,111,_master} [testenv] usedevelop=True deps= coverage django18: django>=1.8,<1.9a0 - django19: django>=1.9a1,<1.10a0 django110: django>=1.10,<1.11a0 + django111: django>=1.11a1,<1.12 django_master: https://github.com/django/django/archive/master.tar.gz mock pytest |