Changeset - 1064a12981d7
[Not reviewed]
master
0 4 0
Ben Sturmfels (bsturmfels) - 22 hours ago 2024-10-18 00:46:01
ben@sturm.com.au
Bring Stripe sustainer form into "Become a sustainer" page
4 files changed with 129 insertions and 41 deletions:
0 comments (0 inline, 0 general)
conservancy/supporters/templates/supporters/sustainers_stripe.html
Show inline comments
...
 
@@ -22,31 +22,133 @@
 
     background: #224c57;
 
   }
 
   progress::-webkit-progress-bar {
 
     background: #ddd;
 
   }
 
   progress::-webkit-progress-value {
 
     background: #224c57;
 
   }
 
   .btn:active {
 
     transform: scale(1.05);
 
   }
 
  </style>
 

	
 
  <script defer src="{% static "js/vendor/alpine-3.14.1.js" %}"></script>
 
{% endblock %}
 

	
 
{% block content %}
 
  <h1 class="lh-title tc mt4 mb0">Become a Sustainer Now</h1>
 
  <div class="breadcrumbs">
 
    <p><a href="/">Home</a> / <a href="/about">Who We Are</a></p>
 
  </div>
 

	
 
  <h1 class="lh-title tc mt2 mb0">Become a Sustainer Now</h1>
 
  <p class="measure-wide center tc">Sustainers help us do our work in a strategic, long-term way.</p>
 

	
 
  <div class="f5 measure-wide center tc mv4">
 
  <p class="mt3 mb0"><em>“If software freedom is important to you, I can't think of a
 
    more effective way to use your money than to support Conservancy.”</em></p>
 
  <p class="tr">— <strong>Made Up Person</strong></p>
 
  </div>
 

	
 
  <div id="sustainer-grid" class="mv4">
 
    <div>
 
        <noscript>
 
          <p><strong>Hey there!</strong> We greatly respect you visiting our site <strong>without JavaScript</strong>. We make significant efforts to ensure our site works without JavaScript and where necessary to use only free software JavaScript.</p>
 
          <p><strong>Unfortunately, our payment providers Stripe and PayPal do not work without JavaScript enabled</strong>. We also accept payment by <a href="#">paper check</a> and <a href="#">wire transfer</a> or otherwise please <a href="#">get in touch</a> and we'll try to work something out. Thanks for your support!</p>
 
        </noscript>
 

	
 

	
 
    <form id="sustainer" method="post" action="."
 
          x-data="{
 
                    tshirt_size: '',
 
                    tshirt_required: function () { return this.tshirt_size !== '' },
 
                    recurring: '',
 
                  }">
 
      {% csrf_token %}
 
      <fieldset class="bg-black-05 pa3 br3 center" style="max-width: 24rem; border: 1px solid #ccc">
 
        <legend class="b f5">Become a Sustainer</legend>
 
      {{ form.errors }}
 
      <div class="mb2"><label>
 
        <label class="mr1"><input type="radio" name="recurring" value="" checked x-model="recurring"> Annual</label>
 
        <label class="mr1"><input type="radio" name="recurring" value="month" x-model="recurring"> Monthly</label>
 
        <label><input type="radio" name="recurring" value="year" x-model="recurring"> Annual Renew</label>
 
      </label></div>
 
      <div class="mb2">
 
        <label>Amount<br>
 
          <span class="mt1" x-show="recurring === ''">$ {{ form.amount }}</span>
 
          <span class="mt1" x-show="recurring === 'month'">$ {{ form.amount_monthly }}</span>
 
          <span class="mt1" x-show="recurring === 'year'">$ {{ form.amount }}</span>
 
        </label>
 
      </div>
 
      <div class="mb2"><label>Name
 
        <span class="db mt1">{{ form.name }}</span>
 
      </label></div>
 
      <div class="mb2"><label>Email
 
        <span class="db mt1">{{ form.email }}</span>
 
      </label>
 
      <p class="f7 black-60 mt1">To send your receipt</p>
 
      </div>
 
      <div class="mv3"><label class="lh-title">{{ form.acknowledge_publicly }} Acknowledge me on the public <a href="">list of sustainers</a></label></div>
 
      <div class="mv3"><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="mv3">
 
        <label>T-shirt:
 
          <!-- Form field has an x-model attribute in forms.py. -->
 
          <span class="db mt1">{{ form.tshirt_size }}</span>
 
        </label>
 
        <p class="f7 black-60 mt1">Sizing:
 
          <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></p>
 

	
 
                <figure>
 
          <img src="/static/img/tshirt-2023.png" alt="Software Freedom Conservancy T-shirt" width="200px">
 
        </figure>
 

	
 

	
 
      </div>
 
      <!-- Using Alpine.js to show/hide the address based on T-shirt choice. -->
 
      <template x-if="tshirt_required">
 
        <div id="address">
 
          <div class="mb2">
 
            <label>Postal address</label>
 
          </div>
 
        <div class="mb2"><label>Street
 
          <span class="db mt1">{{ form.street }}</span>
 
        </label></div>
 
        <div class="mb2"><label>City
 
          <span class="db mt1">{{ form.city }}</span>
 
        </label></div>
 
        <div class="mb2"><label>State/Region
 
          <span class="db mt1">{{ form.state }}</span>
 
        </label></div>
 
        <div class="mb2"><label>Zip/Postal
 
          <span class="db mt1">{{ form.zip_code }}</span>
 
        </label></div>
 
        <div class="mb2"><label>Country
 
          <span class="db mt1">{{ form.country }}</span>
 
        </label></div>
 
        </div>
 
      </template>
 

	
 
      <div class="mt3"><button type="submit" class="pointer btn f5 pv2" style="width: 100%; font-weight: bold; color: white; background-color: var(--orange); border-radius: 0.5rem; border: none; border-bottom: 2px solid rgba(0,0,0,0.1);">Become a Sustainer by<br>Credit Card or <abbr title="US Bank Direct Debit">ACH</button></div>
 
      </fieldset>
 
    </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>
 

	
 
        <div>
 
        <a href="{% url "sustainers" %}">
 
          <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>
 
        </a>
 
      </div>
 
    </div>
 
    <div style="grid-row: 1 / span 2">
 
      <video controls poster="https://sfconservancy.org/videos/sfc-introduction-video_poster.jpg" class="mb3">
 
        <source src="https://sfconservancy.org/videos/sfc-introduction_1080p.mp4">
 
        <track src="/docs/sfc-introduction-vtt-captions.txt" kind="subtitles" srclang="en" label="English">
 
        <a href="https://sfconservancy.org/videos/sfc-introduction_1080p.mp4"><img src="https://sfconservancy.org/videos/sfc-introduction-video_poster.jpg" alt="Software Freedom Conservancy introduction video"></a><br>
 
        <a href="https://youtu.be/yCCxMfW0LTM">(watch on Youtube)</a>
 
      </video>
 

	
 
      <h3>The wide range of work we engage in is supported by people like you.</h3>
 

	
 
      <p>We are so proud that we're funded by individuals and stay unbeholden to corporate interests and pressures. We stand up for developers, consumers and those who have been historically excluded. We work to make technology truly fair for all. </p>
 

	
...
 
@@ -250,44 +352,15 @@ for the PLC and contributors to get together to plan and work on technical
 
challenges. The first back in-person <b>Selenium</b> <a
 
href="https://seleniumconf.com/">conference</a> was in Chicago this past
 
may</a>. Attendance from over 10 countries, it was an incredible reunion for
 
the project contributors and users to get together. The <b>Git</b>
 
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
 
code forges and what the new contributor experience is like. A great mix of
 
code related and process related talks. The <a href="https://reproducible-builds.org/">Reproducible Builds</a>
 
annual summit was hosted in Hamburg featuring incredible
 
technical talks, project planning and continues to build the momentum and
 
reach for reproducibility. </p>
 
</details>
 

	
 

	
 
      <h3>Our sustainers</h3>
 

	
 
      <p>Anonymous (100 people), Aurimas Fišeras, Kat Walsh, Richard Wheeler, Karl Ove Hufthammer, Mark Wielaard, Karl Fogel, Richard Fontana, Richard L. Schmeidler, Karen Sandler, Russ Allbery, Christine Webber, Jeremy Allison, J.B. Nicholson, Michael Dexter, Tom Marble, Johannes Krampf, Michael Linksvayer, Jack Hill, Stefano Zacchiroli, Daniel Callahan, Ben Cotton, in memory of Marina Zhurakhinskaya, Jim Radford, Tyng-Ruey Chuang, Francois Marier, Henri Sivonen, Keith Packard, Monica Neumann, Michal Nazarewicz, Bdale Garbee, David Neary, Alexander Bokovoy, Andrew Isaacson, Brian Hart, James Pannacciulli, Sasa Poznanovic, David Batley, David Crossland, Steve Sprang, Bob Murphy, Mark Galassi, James R. Garrison, Bluebird Interactive, David Quist, Patrick Masson, Neil McGovern, Lenore Ramm Hill, Paul Logston, David Arnold, Benjamin Pfaff, Timothy Howes, Britta Gustafson, Wookey, Michael Gulick, Tanu Kaskinen, Jeffrey Layton, Raphaël Hertzog, Will Thompson, Matteo Settenvini, Kevin Krammer, Elana Hashman, Richard Schultz, Charkov Alexey, Donald Craig, Michael Catanzaro, Olav Reinert, Stephen Kitt, Barry Fishman, Luigi Toscano, Steve McIntyre, Cornelia Huck, Jonathan McDowell, Emmanuel Seyman, Mike Crowe, Alexandre Julliard, Ross Vandegrift, Ian Jackson, Alexander Reichle-Schmehl, Sang Engineering, Preston Maness, John Hagemeister, Julien Cristau, Rebecca Sobol, John Hughes, Peter Link, Solomon Peachy, Riccardo Ghetta, Stefano Rivera, Julian Gilbey, Srivats P, JRS System Solutions, Eric Dorland, Matija Nalis, Brett Smith, Dmitry Borodaenko, Johannes Berg, Howard Shih, Nigel Whillier, Peter Maydell, Lars Wirzenius, Stephanie Feingold, Kevin Adler, Matthew Vernon, Stefan Seefeld, scrye.com, Robert Horn, Andreas Bombe, Michael Kuhn, Stephen Waite, Philip Cohn-Cort, Stuart Smith, Michel Machado, Joseph Thompson, Joan Sandler, Sage Ross, Peter Levy, Daniel Gillmor, James Carter, Wilson E. Alvarez, Michael Andreen, Aaron Puchert, Andrew Eikum, Vladimir Michl, Gregory Grossmeier, Josh Triplett, James Blair, Felix Gruber, Claire Connelly, Antoine Amarilli, Kenneth J. Pronovici, Igalia S. L., Karl-Johan Karlsson, David Gibson, Tom Callaway, Steven A. Ovadia, Gerard Ryan, James Garrett, William Norris, Luke Faraone, Christian Gelinek, Chris Neugebauer, David Potter, Paul Fenwick, George Bojanov, Jondale Stratton, Kiatikun Luangkesorn, hmk, Yu Tomita, Jure Varlec, Antonin Houska, Chad Henderson, Adam Batkin, Marc Jeanmougin, Mike Dowling, Nicholas George, Leif Lindholm, Diane Trout, Daniel Walls, Donald Anderson, Darrick Wong, Greg Price, Martin Krafft, Tony Sebro, Matthew Treinish, Jason Baker, Kathy Giori, Brennen Bearnes, Olly Betts, Steven Adger, John Maloney, Gargi Sharma, Andrew Janke, Andy Kittner, Holger Levsen, Jacopo Corbetta, Andy Balaam, Justin W. Flory, Albert Chae, Elias Rudberg, Gene Hightower, Asumu Takikawa, John-Isaac Greant, Ulrich Czekalla, Bob Proulx, Nick Alcock, Geoffrey Knauth, Luke Shumaker, Stephen Hinton, Philip McGrath, Anjandev Momi, Meisam Tabriz, Alex Dryden, Thomas Schwinge, Julia Kreger, nicholas Bishop, Rachel Wonnacott, Benjamin Kraus, David Witten, Pontus Ullgren, Brendan Horan, Alex Karle, Michael Pennisi, Dave Jansen, Kit Aultman, Jason Prince, Frank Eigler, Keyhan Vakil, Daniel Whiting, tam phan, Jon Stumpf, Anna Philips, Anthony Symkowick, Drew Fustini, Anthony Mirabella, Eric Perko, Simon Michael, Rod Nayfield, Joerg Jaspert, Lieven Govaerts, David Harris, BRUST, Alexander Couzens, Amisha Singla, Athul Iddya, kyle Davis, Trace Pearson, Paul Williams, Peter Murray, anne fonteyn</p>
 
    </div>
 

	
 
    <div>
 
      <progress min="0" max="100" value="84.5" class="w-100">84.5%</progress>
 
      <div class="mv3">
 
        <div class="f2 b">$15,558</div>
 
        <div class="f5 b black-50">Remaining of the $100,000 goal</div>
 
      </div>
 
      <div class="mv3">
 
        <div class="f2 b">15 days</div>
 
        <div class="f5 b black-50">Remaining</div>
 
      </div>
 
      <div class="mt4">
 
        <a href="{% url "stripe2" %}">
 
          <button type="submit" class="pointer btn" style="height: 40px; width: 100%; font-size: 18px; font-weight: bold; color: white; background-color: var(--orange); border-radius: 0.5rem; border: none; border-bottom: 2px solid rgba(0,0,0,0.1);">Become a Sustainer!</button>
 
        </a>
 
      </div>
 

	
 
      <div class="mt3">
 
        <figure>
 
          <img src="/static/img/tshirt-2023.png" alt="Software Freedom Conservancy T-shirt">
 
          <figcaption class="tc black-70" style="margin-top: -20px">Our new Sustainer T-shirt</figcaption>
 
        </figure>
 
      </div>
 
    </div>
 
  </div>
 
{% endblock %}
conservancy/supporters/templates/supporters/sustainers_stripe2.html
Show inline comments
...
 
@@ -30,33 +30,35 @@
 
        <span class="db mt1">{{ form.name }}</span>
 
      </label></div>
 
      <div class="mb2"><label>Email
 
        <span class="db mt1">{{ form.email }}</span>
 
      </label>
 
      <p class="f7 black-60 mt1">To send your receipt</p>
 
      </div>
 
      <div class="mb2"><label>
 
        <label class="mr1"><input type="radio" name="recurring" value="" checked x-model="recurring"> Once</label>
 
        <label class="mr1"><input type="radio" name="recurring" value="month" x-model="recurring"> Monthly</label>
 
        <label><input type="radio" name="recurring" value="year" x-model="recurring"> Annual</label>
 
      </label></div>
 
      <div class="mb2" x-show="recurring === ''"><label>Amount
 
        <span class="db mt1">$ {{ form.amount }}</span>
 
      </label></div>
 
      <div class="mb2" x-show="recurring === 'month'"><label>Amount
 
          <span class="db mt1">$ {{ form.amount_monthly }}</span>
 
      </label></div>
 
      <div class="mb2" x-show="recurring === 'year'"><label>Amount
 
          <span class="db mt1">$ {{ form.amount }}</span>
 
      </label></div>
 
      <div class="mb2">
 
        <label>Amount<br>
 
          <span class="mt1" x-show="recurring === ''">$ {{ form.amount }}</span>
 
          <span class="mt1" x-show="recurring === 'month'">$ {{ form.amount_monthly }}</span>
 
          <span class="mt1" x-show="recurring === 'year'">$ {{ form.amount }}</span>
 
        </label>
 
        <button type="button" class="f7 pa1 lh-solid o-40" title="Multiply my impact">10x me</button>
 
      </div>
 
      <p class="f7 black-60 mt1">Giving is known to increase happiness, but are
 
        you sure?
 
        <a href="#">Undo</a></p>
 
      <div class="mv3"><label class="lh-title">{{ form.acknowledge_publicly }} Acknowledge me on the public <a href="">list of sustainers</a></label></div>
 
      <div class="mv3"><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="mv3">
 
        <label>T-shirt:
 
          <!-- Form field has an x-model attribute in forms.py. -->
 
          <span class="db mt1">{{ form.tshirt_size }}</span>
 
        </label>
 
        <p class="f7 black-60 mt1">Sizing:
 
          <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></p>
 
      </div>
 
      <!-- Using Alpine.js to show/hide the address based on T-shirt choice. -->
conservancy/supporters/urls.py
Show inline comments
 
from django.urls import path
 
from django.views.generic import TemplateView
 

	
 
from . import views
 

	
 
urlpatterns = [
 
    path('', views.sustainers),
 
    path('', views.sustainers, name='sustainers'),
 
    path('banner/', TemplateView.as_view(template_name='supporters/banners.html')),
 
    path('banners/', TemplateView.as_view(template_name='supporters/banners.html')),
 
    path('success/', views.success),
 
    path('webhook/', views.webhook),
 
    path('stripe/', views.sustainers_stripe),
 
    path('stripe2/', views.sustainers_stripe2, name='stripe2'),
 
]
conservancy/supporters/views.py
Show inline comments
...
 
@@ -67,25 +67,38 @@ def create_checkout_session(reference_id, email: str, amount: int, recurring: st
 
            ],
 
            customer_email=email,
 
            mode='subscription' if recurring else 'payment',
 
            success_url=YOUR_DOMAIN + '/sustainer/success/?session_id={CHECKOUT_SESSION_ID}',
 
            cancel_url=YOUR_DOMAIN + '/sustainer/stripe/',
 
        )
 
    except Exception as e:
 
        return str(e)
 
    return checkout_session.url
 

	
 

	
 
def sustainers_stripe(request):
 
    return render(request, 'supporters/sustainers_stripe.html', {})
 
    if request.method == 'POST':
 
        form = forms.SustainerForm(request.POST)
 
        if form.is_valid():
 
            order = form.save(commit=False)
 
            order.recurring = form.data['recurring']
 
            if order.recurring == 'month':
 
                order.amount = form.cleaned_data['amount_monthly']
 
            order.save()
 
            base_url = f'{request.scheme}://{request.get_host()}'
 
            stripe_checkout_url = create_checkout_session(order.id, order.email, order.amount, order.recurring, base_url)
 
            return redirect(stripe_checkout_url)
 
    else:
 
        form = forms.SustainerForm()
 
    return render(request, 'supporters/sustainers_stripe.html', {'form': form})
 

	
 

	
 
def sustainers_stripe2(request):
 
    if request.method == 'POST':
 
        form = forms.SustainerForm(request.POST)
 
        if form.is_valid():
 
            order = form.save(commit=False)
 
            order.recurring = form.data['recurring']
 
            if order.recurring == 'month':
 
                order.amount = form.cleaned_data['amount_monthly']
 
            order.save()
 
            base_url = f'{request.scheme}://{request.get_host()}'
0 comments (0 inline, 0 general)