summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatryk Zawadzki <patrys@room-303.com>2017-02-23 18:24:58 +0100
committerGitHub <noreply@github.com>2017-02-23 18:24:58 +0100
commit35c368141bcb7dd9ff436b282bce42094a519083 (patch)
treea417552d555c38e9395d60d6f7725eff68565418
parentb33b33960d100b00e35fa18e3acad4228049064b (diff)
parentca3a957421e135e5a0487c64a84b1c8aa54c85a7 (diff)
downloaddjango-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.yml9
-rw-r--r--payments/fields.py12
-rw-r--r--payments/static/js/payments/stripe.js10
-rw-r--r--payments/stripe/forms.py29
-rw-r--r--payments/stripe/test_stripe.py14
-rw-r--r--payments/utils.py14
-rw-r--r--payments/widgets.py38
-rw-r--r--tox.ini4
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))
diff --git a/tox.ini b/tox.ini
index dc2a786..000c84e 100644
--- a/tox.ini
+++ b/tox.ini
@@ -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