Skip to content

Commit 192fa23

Browse files
committed
Add a way for sponsors to pay without asking for logos etc.
1 parent 98805b1 commit 192fa23

File tree

5 files changed

+171
-4
lines changed

5 files changed

+171
-4
lines changed

readthedocs/donate/forms.py

+44
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,47 @@ def save(self, commit=True):
8686
supporter.user = self.user
8787
supporter.save()
8888
return supporter
89+
90+
91+
class EthicalAdForm(StripeResourceMixin, StripeModelForm):
92+
93+
"""Payment form for ethical ads
94+
95+
This extends the basic payment form, giving fields for credit card number,
96+
expiry, and CVV. The proper Knockout data bindings are established on
97+
:py:class:`StripeModelForm`
98+
"""
99+
100+
class Meta:
101+
model = Supporter
102+
fields = (
103+
'last_4_digits',
104+
'name',
105+
'email',
106+
'dollars',
107+
)
108+
help_texts = {
109+
'email': _('Your email is used so we can send you a receipt'),
110+
}
111+
widgets = {
112+
'dollars': forms.HiddenInput(attrs={
113+
'data-bind': 'value: dollars'
114+
}),
115+
'last_4_digits': forms.TextInput(attrs={
116+
'data-bind': 'valueInit: card_digits, value: card_digits'
117+
}),
118+
}
119+
120+
last_4_digits = forms.CharField(widget=forms.HiddenInput(), required=True)
121+
name = forms.CharField(required=True)
122+
email = forms.CharField(required=True)
123+
124+
def validate_stripe(self):
125+
"""Call stripe for payment (not ideal here) and clean up logo < $200"""
126+
stripe.Charge.create(
127+
amount=int(self.cleaned_data['dollars']) * 100,
128+
currency='usd',
129+
source=self.cleaned_data['stripe_token'],
130+
description='Read the Docs Ethical Ads',
131+
receipt_email=self.cleaned_data['email']
132+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{% extends "base.html" %}
2+
3+
{% load i18n %}
4+
{% load static %}
5+
6+
{% block title %}{% trans "Sustainability" %}{% endblock %}
7+
8+
{% block content %}
9+
<h2>Thanks for your support</h2>
10+
11+
<p>
12+
We appreciate your contribution greatly, thank you for showing your support!
13+
Your help will go a long ways towards making the service more sustainable.
14+
</p>
15+
{% endblock %}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
{% extends "base.html" %}
2+
3+
{% load i18n %}
4+
{% load static %}
5+
6+
{% block title %}{% trans "Pay for your ad" %}{% endblock %}
7+
8+
{% block extra_links %}
9+
<link rel="stylesheet" href="{% static 'payments/css/form.css' %}" />
10+
{% endblock %}
11+
12+
{% block extra_scripts %}
13+
<script src="https://js.stripe.com/v2/" type="text/javascript"></script>
14+
<script type="text/javascript" src="{% static 'vendor/knockout.js' %}"></script>
15+
<script type="text/javascript" src="{% static 'donate/js/donate.js' %}"></script>
16+
<script type="text/javascript">
17+
var donate_views = require('donate/donate');
18+
$(document).ready(function () {
19+
var key;
20+
//<![CDATA[
21+
key = '{{ stripe_publishable }}';
22+
//]]>
23+
24+
var view = donate_views.DonateView.init({
25+
key: key,
26+
form: $('form#donate-payment')
27+
});
28+
});
29+
</script>
30+
{% endblock %}
31+
32+
33+
{% block content %}
34+
<h2>Pay for your Sponsorship</h2>
35+
36+
<p>
37+
This form can be used to pay for your sponsorship of Read the Docs.
38+
Thanks for helping make Open Source more sustainable!
39+
</p>
40+
41+
<form action="" method="post" id="donate-payment" class="payment">
42+
{% csrf_token %}
43+
44+
{{ form.non_field_errors }}
45+
46+
{% for field in form.fields_with_cc_group %}
47+
{% if field.is_cc_group %}
48+
<div class="subscription-card">
49+
{% for groupfield in field.fields %}
50+
{% include 'core/ko_form_field.html' with field=groupfield %}
51+
{% endfor %}
52+
</div>
53+
{% elif field.name == 'dollars' %}
54+
{{ field.errors }}
55+
<p>
56+
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
57+
<input
58+
type="hidden"
59+
name="{{ field.name }}"
60+
id="{{ field.id_for_label }}"
61+
data-bind="value: dollars" />
62+
<input
63+
type="number"
64+
data-bind="textInput: dollars_input, visible: dollars_select() == 'custom'"
65+
value="50"
66+
id="id_dollars_input"
67+
style="display: none;" />
68+
<select data-bind="value: dollars_select, visible: dollars_select() != 'custom'">
69+
<option value="custom">{% trans "Custom amount" %}</option>
70+
<option value="2000">$2,000</option>
71+
<option value="3000">$3,000</option>
72+
<option value="5000" selected>$5,000</option>
73+
<option value="10000">$10,000</option>
74+
</select>
75+
{% if field.help_text %}
76+
<span class="helptext">{{ field.help_text }}</span>
77+
{% endif %}
78+
</p>
79+
{% else %}
80+
{% include 'core/ko_form_field.html' with field=field %}
81+
{% endif %}
82+
{% endfor %}
83+
84+
{% trans "Pay" as form_submit_text %}
85+
<input type="submit" value="{{ form_submit_text }}" data-bind="click: process_form" />
86+
<p>
87+
We do not store your credit card details,
88+
payment is processed directly through <a href="https://stripe.com">Stripe</a>.
89+
</p>
90+
</form>
91+
{% endblock %}

readthedocs/donate/urls.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
from django.conf.urls import url, include
22

3-
from .views import DonateCreateView
4-
from .views import DonateListView
5-
from .views import DonateSuccessView
3+
from .views import DonateCreateView, DonateListView, DonateSuccessView
4+
from .views import PayAdsView, PaySuccess
65
from .views import click_proxy, view_proxy
76

87

98
urlpatterns = [
109
url(r'^$', DonateListView.as_view(), name='donate'),
10+
url(r'^pay/$', PayAdsView.as_view(), name='pay_ads'),
11+
url(r'^pay/success/$', PaySuccess.as_view(), name='pay_ads'),
1112
url(r'^contribute/$', DonateCreateView.as_view(), name='donate_add'),
1213
url(r'^contribute/thanks$', DonateSuccessView.as_view(), name='donate_success'),
1314
url(r'^view/(?P<promo_id>\d+)/(?P<hash>.+)/$', view_proxy, name='donate_view_proxy'),

readthedocs/donate/views.py

+17-1
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,28 @@
1515
from readthedocs.projects.models import Project
1616

1717
from .models import Supporter, SupporterPromo, CLICKS, VIEWS
18-
from .forms import SupporterForm
18+
from .forms import SupporterForm, EthicalAdForm
1919
from .mixins import DonateProgressMixin
2020

2121
log = logging.getLogger(__name__)
2222

2323

24+
class PayAdsView(StripeMixin, CreateView):
25+
26+
"""Create a donation locally and in Stripe"""
27+
28+
form_class = EthicalAdForm
29+
success_message = _('Your payment has been received')
30+
template_name = 'donate/ethicalads.html'
31+
32+
def get_success_url(self):
33+
return reverse('pay_success')
34+
35+
36+
class PaySuccess(TemplateView):
37+
template_name = 'donate/ethicalads-success.html'
38+
39+
2440
class DonateCreateView(StripeMixin, CreateView):
2541

2642
"""Create a donation locally and in Stripe"""

0 commit comments

Comments
 (0)