Skip to content

Coalesce implementations of Stripe payment forms #1761

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Oct 22, 2015
Merged
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@ whoosh_index
xml_output
public_cnames
public_symlinks
.rope_project/
2 changes: 1 addition & 1 deletion bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"readthedocs-client": "https://github.com/agjohnson/readthedocs-client-js.git",
"sphinx-rtd-theme": "https://github.com/snide/sphinx-rtd-theme.git#0.1.9",
"knockout": "~3.3.0",
"jquery.payment": "~1.2.3",
"jquery.payment": "~1.3.0",
"jquery-migrate": "~1.2.1",
"jquery-ui": "1.8.23"
},
Expand Down
32 changes: 16 additions & 16 deletions media/css/core.css
Original file line number Diff line number Diff line change
Expand Up @@ -836,27 +836,27 @@ ul.donate-supporters.donate-supporters-large div.supporter-name {
font-size: .9em;
}

div#payment-form div.cc-type {
height: 23px;
margin: 3px 0px 10px;
background: url('/static/donate/img/creditcard.png');
background-repeat: no-repeat;
}
div#payment-form input#cc-number.visa + div.cc-type {
background-position: 0px -23px;
/* Gold */
div.gold-subscription p.subscription-detail,
div.gold-subscription p.subscription-projects {
margin: 0em;
}
div#payment-form input#cc-number.mastercard + div.cc-type {
background-position: 0px -46px;

div.gold-subscription p.subscription-detail label {
display: inline-block;
}
div#payment-form input#cc-number.amex + div.cc-type {
background-position: 0px -69px;

div.gold-subscription p.subscription-detail-card > span {
font-family: monospace;
}
div#payment-form input#cc-number.discover + div.cc-type {
background-position: 0px -92px;

div.gold-subscription > form {
display: inline-block;
}

div#payment-form input#cc-expiry { width: 150px; }
div#payment-form input#cc-cvv { width: 100px; }
div.gold-subscription > form button {
margin: 1em .3em 1.5em 0em;
}

/* Form Wizards */
div.actions.wizard-actions button.action-primary,
Expand Down
23 changes: 9 additions & 14 deletions readthedocs/core/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,9 @@
Common mixin classes for views
"""

from django.conf import settings

from vanilla import ListView


class StripeMixin(object):

"""Adds Stripe publishable key to the context data"""

def get_context_data(self, **kwargs):
context = super(StripeMixin, self).get_context_data(**kwargs)
context.update({
'publishable': settings.STRIPE_PUBLISHABLE,
})
return context
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator


class ListViewWithForm(ListView):
Expand All @@ -27,3 +15,10 @@ def get_context_data(self, **kwargs):
context = super(ListViewWithForm, self).get_context_data(**kwargs)
context['form'] = self.get_form(data=None, files=None)
return context


class LoginRequiredMixin(object):

@method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(LoginRequiredMixin, self).dispatch(*args, **kwargs)
99 changes: 0 additions & 99 deletions readthedocs/core/static-src/core/js/payment.js

This file was deleted.

22 changes: 22 additions & 0 deletions readthedocs/core/templates/core/ko_form_field.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{% if field.is_hidden %}
{{ field }}
{% else %}
<!-- Begin: {{ field.name }} -->
{{ field.errors }}
{% if 'data-bind' in field.field.widget.attrs %}
<ul
class="errorlist"
data-bind="visible: error_{{ field.name }}"
style="display: none;">
<li data-bind="text: error_{{ field.name }}"></li>
</ul>
{% endif %}
<p>
<label for="{{ field.id_for_label }}">{{ field.label }}:</label>
{{ field }}
{% if field.help_text %}
<span class="helptext">{{ field.help_text }}</span>
{% endif %}
</p>
<!-- End: {{ field.name }} -->
{% endif %}
50 changes: 27 additions & 23 deletions readthedocs/donate/forms.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,32 @@
"""Forms for RTD donations"""

import logging

from django import forms
from django.conf import settings
from django.utils.translation import ugettext_lazy as _

import stripe
from readthedocs.payments.forms import StripeModelForm, StripeResourceMixin
from readthedocs.payments.utils import stripe

from .models import Supporter

log = logging.getLogger(__name__)


class SupporterForm(forms.ModelForm):
class SupporterForm(StripeResourceMixin, StripeModelForm):

"""Donation support sign up form

This extends the basic payment form, giving fields for credit card number,
expiry, and CVV. The proper Knockout data bindings are established on
:py:cls:`StripeModelForm`
"""

class Meta:
model = Supporter
fields = (
'last_4_digits',
'stripe_id',
'name',
'email',
'dollars',
Expand All @@ -43,38 +52,33 @@ class Meta:
}),
'site_url': forms.TextInput(attrs={
'data-bind': 'value: site_url, enable: urls_enabled'
})
}),
'last_4_digits': forms.TextInput(attrs={
'data-bind': 'valueInit: card_digits, value: card_digits'
}),
}

last_4_digits = forms.CharField(widget=forms.HiddenInput(), required=True)
stripe_id = forms.CharField(widget=forms.HiddenInput(), required=True)
name = forms.CharField(required=True)
email = forms.CharField(required=True)

def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user')
super(SupporterForm, self).__init__(*args, **kwargs)

def clean(self):
'''Call stripe for payment (not ideal here) and clean up logo < $200'''
def validate_stripe(self):
"""Call stripe for payment (not ideal here) and clean up logo < $200"""
dollars = self.cleaned_data['dollars']
if dollars < 200:
self.cleaned_data['logo_url'] = None
self.cleaned_data['site_url'] = None
try:
stripe.api_key = settings.STRIPE_SECRET
stripe.Charge.create(
amount=int(self.cleaned_data['dollars']) * 100,
currency='usd',
source=self.cleaned_data['stripe_id'],
description='Read the Docs Sustained Engineering',
receipt_email=self.cleaned_data['email']
)
except stripe.error.CardError, e:
stripe_error = e.json_body['error']
log.error('Credit card error: %s', stripe_error['message'])
raise forms.ValidationError(
_('There was a problem processing your card: %(message)s'),
params=stripe_error)
return self.cleaned_data
stripe.Charge.create(
amount=int(self.cleaned_data['dollars']) * 100,
currency='usd',
source=self.cleaned_data['stripe_token'],
description='Read the Docs Sustained Engineering',
receipt_email=self.cleaned_data['email']
)

def save(self, commit=True):
supporter = super(SupporterForm, self).save(commit)
Expand Down
11 changes: 5 additions & 6 deletions readthedocs/donate/mixins.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
'''
Mixin classes for donation views
'''
"""Mixin classes for donation views"""

from django.db.models import Avg, Sum

from .models import Supporter


class DonateProgressMixin(object):
'''Add donation progress to context data'''

def get_context_data(self):
context = super(DonateProgressMixin, self).get_context_data()
"""Add donation progress to context data"""

def get_context_data(self, **kwargs):
context = super(DonateProgressMixin, self).get_context_data(**kwargs)
sums = (Supporter.objects
.aggregate(dollars=Sum('dollars')))
avgs = (Supporter.objects
Expand Down
10 changes: 8 additions & 2 deletions readthedocs/donate/static-src/donate/js/donate.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
// Donate payment views

var jquery = require('jquery'),
payment = require('../../../../core/static-src/core/js/payment'),
payment = require('readthedocs/payments/static-src/payments/js/base'),
ko = require('knockout');

function DonateView (config) {
var self = this,
config = config || {};

ko.utils.extend(self, new payment.PaymentView(config));
self.constructor.call(self, config);

self.dollars = ko.observable();
self.logo_url = ko.observable();
self.site_url = ko.observable();
self.error_dollars = ko.observable();
self.error_logo_url = ko.observable();
self.error_site_url = ko.observable();

ko.computed(function () {
var input_logo = $('input#id_logo_url').closest('p'),
input_site = $('input#id_site_url').closest('p');
Expand All @@ -32,6 +36,8 @@ function DonateView (config) {
});
}

DonateView.prototype = new payment.PaymentView();

DonateView.init = function (config, obj) {
var view = new DonateView(config),
obj = obj || $('#donate-payment')[0];
Expand Down
Binary file modified readthedocs/donate/static/donate/img/creditcard.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading