from functools import partial from django import forms from django.core.urlresolvers import reverse from django.core.exceptions import ValidationError from django.forms import widgets from django.utils import timezone from django_countries import countries from django_countries.fields import LazyTypedChoiceField from django_countries.widgets import CountrySelectWidget class NoRenderWidget(forms.widgets.HiddenInput): def render(self, name, value, attrs=None): return "" def secure_striped(widget): ''' Calls stripe() with secure=True. ''' return striped(widget, True) def striped(WidgetClass, secure=False): ''' Takes a given widget and overrides the render method to be suitable for stripe.js. Arguments: widget: The widget class secure: if True, only the `data-stripe` attribute will be set. Name will be set to None. ''' class StripedWidget(WidgetClass): def render(self, name, value, attrs=None): if not attrs: attrs = {} attrs["data-stripe"] = name if secure: name = "" return super(StripedWidget, self).render( name, value, attrs=attrs ) return StripedWidget class CreditCardForm(forms.Form): def _media(self): js = ( 'https://js.stripe.com/v2/', reverse("registripe_pubkey"), ) return forms.Media(js=js) media = property(_media) number = forms.CharField( required=False, label="Credit card Number", help_text="Your credit card number, with or without spaces.", max_length=255, widget=secure_striped(widgets.TextInput)(), ) exp_month = forms.IntegerField( required=False, label="Card expiry month", min_value=1, max_value=12, widget=secure_striped(widgets.TextInput)(), ) exp_year = forms.IntegerField( required=False, label="Card expiry year", help_text="The expiry year for your card in 4-digit form", min_value=lambda: timezone.now().year, widget=secure_striped(widgets.TextInput)(), ) cvc = forms.CharField( required=False, min_length=3, max_length=4, widget=secure_striped(widgets.TextInput)(), ) stripe_token = forms.CharField( max_length=255, #required=True, widget=NoRenderWidget(), ) name = forms.CharField( required=True, label="Cardholder name", help_text="The cardholder's name, as it appears on the credit card", max_length=255, widget=striped(widgets.TextInput), ) address_line1 = forms.CharField( required=True, label="Cardholder account address, line 1", max_length=255, widget=striped(widgets.TextInput), ) address_line2 = forms.CharField( required=False, label="Cardholder account address, line 2", max_length=255, widget=striped(widgets.TextInput), ) address_city = forms.CharField( required=True, label="Cardholder account city", max_length=255, widget=striped(widgets.TextInput), ) address_state = forms.CharField( required=True, max_length=255, label="Cardholder account state or province", widget=striped(widgets.TextInput), ) address_zip = forms.CharField( required=True, max_length=255, label="Cardholder account postal code", widget=striped(widgets.TextInput), ) address_country = LazyTypedChoiceField( label="Cardholder account country", choices=countries, widget=striped(CountrySelectWidget), ) '''{ From stripe.js details: Card details: The first argument to createToken is a JavaScript object containing credit card data entered by the user. It should contain the following required members: number: card number as a string without any separators (e.g., "4242424242424242") exp_month: two digit number representing the card's expiration month (e.g., 12) exp_year: two or four digit number representing the card's expiration year (e.g., 2017) (The expiration date can also be passed as a single string.) cvc: optional, but we highly recommend you provide it to help prevent fraud. This is the card's security code, as a string (e.g., "123"). The following fields are entirely optional and cannot result in a token creation failure: name: cardholder name address_line1: billing address line 1 address_line2: billing address line 2 address_city: billing address city address_state: billing address state address_zip: billing postal code as a string (e.g., "94301") address_country: billing address country } '''