supporters: Consolidate alternative payment options

This commit is contained in:
Ben Sturmfels 2024-10-22 23:50:02 +11:00
parent 82f8fbb758
commit bb89d69ef1
Signed by: bsturmfels
GPG key ID: 023C05E2C9C068F0
5 changed files with 56 additions and 26 deletions

View file

@ -33,6 +33,9 @@ class SustainerForm(forms.ModelForm):
template_name = 'supporters/sustainer_form.html' template_name = 'supporters/sustainer_form.html'
MONTH_OPTIONS = [12, 25, 50, 100]
YEAR_OPTIONS = [128, 250, 500, 1000]
class Meta: class Meta:
model = SustainerOrder model = SustainerOrder
fields = [ fields = [
@ -63,14 +66,14 @@ class SustainerForm(forms.ModelForm):
self.renderer = SustainerFormRenderer() self.renderer = SustainerFormRenderer()
self.fields['recurring'].label = '' self.fields['recurring'].label = ''
self.fields['amount'].initial = self.YEAR_OPTIONS[0]
# So we can write to this field easily from Alpine JS. # So we can write to this field easily from Alpine JS.
self.fields['amount'].widget.attrs['x-ref'] = 'amount' self.fields['amount'].widget.attrs['x-ref'] = 'amount'
self.fields['amount'].widget.attrs['style'] = 'width: 5rem' self.fields['amount'].widget.attrs['style'] = 'width: 5rem'
self.fields['email'].help_text = 'For your payment receipt' self.fields['email'].help_text = 'For your payment receipt'
self.fields['tshirt_size'].help_text = mark_safe("""Sizing chart: <a href="https://sfconservancy.org/videos/women-2017-to-2020-t-shirt-sizing.jpg" target="_blank" class="black-60">Women's</a>, <a href="https://sfconservancy.org/videos/men-2017-to-2020-t-shirt-sizing.jpg" target="_blank" class="black-60">Men's</a>""") self.fields['tshirt_size'].help_text = mark_safe("""Sizing chart: <a href="/videos/women-2017-to-2020-t-shirt-sizing.jpg" target="_blank" class="black-60">Women's</a>, <a href="/videos/men-2017-to-2020-t-shirt-sizing.jpg" target="_blank" class="black-60">Men's</a>""")
self.fields['tshirt_size'].widget.attrs['x-model'] = 'tshirt_size' self.fields['tshirt_size'].widget.attrs['x-model'] = 'tshirt_size'
def clean(self): def clean(self):
super().clean() super().clean()
recurring = self.cleaned_data.get('recurring', '') recurring = self.cleaned_data.get('recurring', '')

View file

@ -6,6 +6,6 @@
{% if field.label %}{{ field.label_tag }}{% endif %} {% if field.label %}{{ field.label_tag }}{% endif %}
{% endif %} {% endif %}
{{ field.errors }} {{ field.errors }}
<span class="db mt1">{{ field }}</span> <div class="mt1">{{ field }}</div>
<div class="f7 black-60 mt1">{{ field.help_text }}</div> <div class="f7 black-60 mt1">{{ field.help_text }}</div>
{% if field.use_fieldset %}</fieldset>{% endif %} {% if field.use_fieldset %}</fieldset>{% endif %}

View file

@ -5,6 +5,9 @@
{% block head %} {% block head %}
{{ block.super }} {{ block.super }}
{% include "opengraph_partial.html" with url="/sustainer/" title="Support Conservancy!" description="Software freedom is critical to many of today&rsquo;s most pressing social issues, but it&rsquo;s only effective when FOSS is for everyone. Support Conservancy today to help make that happen!" %}
{% include "opengraph_urllist_partial.html" with property='image' urls='' fallback='/static/img/conservancy-logo.png' %}
{{ form.media }}
<style> <style>
@media screen and (min-width: 40em) { @media screen and (min-width: 40em) {
#sustainer-grid-wrapper { #sustainer-grid-wrapper {
@ -41,9 +44,9 @@
<div style="padding: 1rem; border: 2px solid #0f0; margin-bottom: 1.5rem"> <div style="padding: 1rem; border: 2px solid #0f0; margin-bottom: 1.5rem">
<p><marquee><strong>Hey there!</strong></marquee> Thanks for visiting our site <strong>without JavaScript</strong>! We respect your choice and make a significant effort to ensure our site works without JavaScript or, where necessary, to use only free software JavaScript.</p> <p><marquee><strong>Hey there!</strong></marquee> Thanks for visiting our site <strong>without JavaScript</strong>! We respect your choice and make a significant effort to ensure our site works without JavaScript or, where necessary, to use only free software JavaScript.</p>
<p>The bad news is that <strong>our credit card/ACH payment services</strong> Stripe and PayPal <strong>don't work without JavaScript</strong>. We also don't currently have the resources to handle PCI compliant credit-card processing directly.</p> <p>The bad news is that our credit card/ACH payment services <strong>Stripe and PayPal don't work without JavaScript</strong>. We also don't currently have the resources to handle PCI compliant credit-card processing directly.</p>
<p>We'd still love to have you as a Sustainer though, and will gladly accept your payment by <a href="#">paper check</a> and <a href="#">wire transfer</a>. If those aren't feasible, please <a href="#">get in touch</a> and we'll try to work something out. Thanks for your support!</p> <p>We'd still love to have you as a Sustainer though, and will gladly accept your payment by <a href="#wire-transfer">wire transfer</a> or <a href="#paper-check">paper check</a> . If those aren't feasible, please <a href="mailto:donate@sfconservancy.org">get in touch</a> and we'll try to work something out. Thanks for your support!</p>
<img src="{% static 'img/dancing-banana.gif' %}" alt="Dancing Banana"> <img src="{% static 'img/dancing-banana.gif' %}" alt="Dancing Banana">
</div> </div>
@ -56,14 +59,13 @@
amount: parseInt('{{ form.amount.value|escapejs }}'), amount: parseInt('{{ form.amount.value|escapejs }}'),
amount_option: '{{ form.amount_option.value|escapejs }}', amount_option: '{{ form.amount_option.value|escapejs }}',
amount_options: function() { amount_options: function() {
const MONTH_OPTIONS = [12, 25, 50, 100]; let month_options = {{ form.MONTH_OPTIONS|escapejs }};
const YEAR_OPTIONS = [128, 250, 500, 1000]; let year_options = {{ form.YEAR_OPTIONS|escapejs }};
return this.recurring === 'month' ? MONTH_OPTIONS : YEAR_OPTIONS; return this.recurring === 'month' ? month_options : year_options;
}, },
tshirt_size: '{{ form.tshirt_size.value|escapejs }}', tshirt_size: '{{ form.tshirt_size.value|escapejs }}',
}"> }">
{% csrf_token %} {% csrf_token %}
{{ form.media }}
<fieldset class="bg-black-05 pa3 br3 center" style="border: 1px solid #ccc"> <fieldset class="bg-black-05 pa3 br3 center" style="border: 1px solid #ccc">
<legend class="b f5">Become a Sustainer</legend> <legend class="b f5">Become a Sustainer</legend>
{{ form.non_field_errors }} {{ form.non_field_errors }}
@ -74,13 +76,16 @@
<template x-for="m in amount_options"> <template x-for="m in amount_options">
<label> <label>
<input type="radio" name="amount_option" x-bind:value="m" x-on:change="$refs.amount.value = $event.currentTarget.value" x-model="amount_option" required> <input type="radio" name="amount_option" x-bind:value="m" x-on:change="$refs.amount.value = $event.currentTarget.value" x-model="amount_option" required>
<span>$<span x-text="m">12</span></span> <span>$<span x-text="m.toLocaleString()"></span></span>
</label>
</template>
<!-- Hide if no JS -->
<template x-if="true">
<label>
<input type="radio" name="amount_option" value="other" x-on:change="$refs.amount.value = ''" x-model="amount_option" required>
<span>Other</span>
</label> </label>
</template> </template>
<label>
<input type="radio" name="amount_option" value="other" x-on:change="$refs.amount.value = ''" x-model="amount_option" required>
<span>Other</span>
</label>
</div> </div>
<div class="mt2" x-show="amount_option === 'other'"> <div class="mt2" x-show="amount_option === 'other'">
{{ form.amount.as_field_group }} {{ form.amount.as_field_group }}
@ -91,14 +96,14 @@
<div class="mt2">{{ form.email.as_field_group }}</div> <div class="mt2">{{ form.email.as_field_group }}</div>
<div class="mt3"><label class="lh-title">{{ form.acknowledge_publicly }} Acknowledge me on the public <a href="">list of sustainers</a></label></div> <div class="mt3"><label class="lh-title">{{ form.acknowledge_publicly }} Acknowledge me on the <a href="/sponsors#sustainers" target="_blank">list of sustainers</a></label></div>
<div class="mt3"><label class="lh-title">{{ form.add_to_mailing_list }} Add me to the low-traffic <a href="https://lists.sfconservancy.org/pipermail/announce/">announcements</a> email list</label></div> <div class="mt3"><label class="lh-title">{{ form.add_to_mailing_list }} Add me to the <a href="https://lists.sfconservancy.org/pipermail/announce/">announcements</a> email list</label></div>
<div class="mt3"> <div class="mt3">
{{ form.tshirt_size.as_field_group }} {{ form.tshirt_size.as_field_group }}
<figure class="mt2"> <figure class="mt2">
<img src="/static/img/tshirt-2023.png" alt="Software Freedom Conservancy T-shirt" width="200px"> <img src="/static/img/tshirt-2023.png" alt="Software Freedom Conservancy T-shirt" width="200">
</figure> </figure>
</div> </div>
@ -117,13 +122,28 @@
</fieldset> </fieldset>
</form> </form>
<p class="f7 mv3">Credit card and ACH payments are processed with Stripe. We also accept payment by <a href="#">paper check</a> and <a href="#">wire transfer</a> and PayPal. Our sustainer program has a minimum of $120 USD per year, but we also accept <a href="#">donations of smaller amounts</a>.</p> <p class="f7 mt3">Credit card and ACH payments are processed with Stripe. We also accept payment by paper check and wire transfer and PayPal (see below). Our sustainer program has a minimum of $120 USD per year, but we also accept <a href="/donate/">donations of smaller amounts</a>.</p>
<div class="mb4"> <details id="wire-transfer">
<a href="{% url "sustainers" %}"> <summary class="f6">Wire Transfer</summary>
<button type="submit" class="pointer btn f5" style="height: 40px; width: 100%; font-weight: bold; color: white; background-color: var(--orange); opacity: 75%; border-radius: 0.5rem; border: none; border-bottom: 2px solid rgba(0,0,0,0.1);">Become a Sustainer by PayPal</button> <p>Contact <a href="mailto:donate@sfconservancy.org">Conservancy
</a> by email</a> for wire transfer instructions. Include currency &amp; country.</p>
</div> </details>
<details id="paper-check">
<summary class="f6">Paper Check</summary>
<p>Send a paper check to:</p>
<p>Software Freedom Conservancy, Inc.<br>
137 MONTAGUE ST STE 380<br>
BROOKLYN, NY 11201-3548 &nbsp; USA</p>
<p>Please write <q>SUSTAINER</q>, T-shirt size, if you are renewing, and if
you want public acknowledgment in memo line.</p>
</details>
<details id="paypal">
<summary class="f6">PayPal</summary>
<p>Please visit our <a href="{% url 'sustainer_paypal' %}">Become a Sustainer by PayPal</a> page.</p>
</details>
</section> </section>
<section style="grid-row: 1 / span 2"> <section style="grid-row: 1 / span 2">
@ -164,7 +184,7 @@
<h2 class="">2024 in Review</h2> <h2 class="">2024 in Review</h2>
<details id="YearInReview"> <details id="YearInReview">
<summary>Our Year in Review</summary> <summary>Overview</summary>
<p>This has been a big year for Software Freedom Conservancy in our tireless <p>This has been a big year for Software Freedom Conservancy in our tireless
efforts to promote ethical technology, increase diversity and inclusion in efforts to promote ethical technology, increase diversity and inclusion in
@ -177,7 +197,7 @@ Right to Repair movement. We hosted our first large conference, <a href="https:/
and while we finalize details for next year, we hope to see you there to join and while we finalize details for next year, we hope to see you there to join
us in community!</p> us in community!</p>
<div class="picture-small right"> <img src="https://nextcloud.sfconservancy.org/apps/files_sharing/publicpreview/pnZYsi2CkjscLwc?file=/&fileId=24825&x=1366&y=768&a=true&etag=f4341a40f90786b0356201c21278ee23" alt="SFC lawyers posing outside at the courthouse“ " /></a> <div class="picture-small right"> <img src="https://nextcloud.sfconservancy.org/apps/files_sharing/publicpreview/pnZYsi2CkjscLwc?file=/&fileId=24825&x=1366&y=768&a=true&etag=f4341a40f90786b0356201c21278ee23" alt="SFC lawyers posing outside at the courthouse“ " />
<p>SFC lawyers after recent Vizio case- CC BY-SA 4.0</p></div> <p>SFC lawyers after recent Vizio case- CC BY-SA 4.0</p></div>
<p>Our <a href="https://vizio.sfconservancy.org">lawsuit against Vizio</a>— the first <p>Our <a href="https://vizio.sfconservancy.org">lawsuit against Vizio</a>— the first
@ -337,7 +357,7 @@ very exciting developments for the project. Creation of a new <a href="https://i
for the PLC and contributors to get together to plan and work on technical for the PLC and contributors to get together to plan and work on technical
challenges. The first back in-person <b>Selenium</b> <a challenges. The first back in-person <b>Selenium</b> <a
href="https://seleniumconf.com/">conference</a> was in Chicago this past href="https://seleniumconf.com/">conference</a> was in Chicago this past
may</a>. Attendance from over 10 countries, it was an incredible reunion for may. Attendance from over 10 countries, it was an incredible reunion for
the project contributors and users to get together. The <b>Git</b> the project contributors and users to get together. The <b>Git</b>
contributor summit was held online this year in September. Topics ranged from contributor summit was held online this year in September. Topics ranged from
ideas of new library support to how to better support for scaling with large ideas of new library support to how to better support for scaling with large
@ -348,4 +368,5 @@ technical talks, project planning and continues to build the momentum and
reach for reproducibility. </p> reach for reproducibility. </p>
</details> </details>
</section> </section>
</div>
{% endblock %} {% endblock %}

View file

@ -9,5 +9,7 @@ urlpatterns = [
path('banners/', TemplateView.as_view(template_name='supporters/banners.html')), path('banners/', TemplateView.as_view(template_name='supporters/banners.html')),
path('success/', views.success), path('success/', views.success),
path('webhook/', views.webhook), path('webhook/', views.webhook),
# TODO
path('stripe/', views.sustainers_stripe), path('stripe/', views.sustainers_stripe),
path('paypal/', views.sustainers_paypal, name='sustainer_paypal'),
] ]

View file

@ -75,6 +75,10 @@ def create_checkout_session(reference_id, email: str, amount: int, recurring: st
return checkout_session.url return checkout_session.url
def sustainers_paypal(request):
return render(request, 'supporters/sustainers_paypal.html')
def sustainers_stripe(request): def sustainers_stripe(request):
if request.method == 'POST': if request.method == 'POST':
form = forms.SustainerForm(request.POST) form = forms.SustainerForm(request.POST)