Changeset - ce4ae22fa59e
[Not reviewed]
0 5 1
Ben Sturmfels (bsturmfels) - 1 month ago 2024-09-18 05:34:59
ben@sturm.com.au
Add prototype monthly recurring payment via Stripe
6 files changed with 110 insertions and 9 deletions:
0 comments (0 inline, 0 general)
conservancy/supporters/forms.py
Show inline comments
...
 
@@ -5,2 +5,4 @@ from .models import SustainerOrder
 
class SustainerForm(forms.ModelForm):
 
    amount_monthly = forms.IntegerField(initial=12, required=False)
 

	
 
    class Meta:
...
 
@@ -24,2 +26,4 @@ class SustainerForm(forms.ModelForm):
 
        self.fields['amount'].widget.attrs['style'] = 'width: 5rem'
 
        self.fields['amount'].initial = 128
 
        self.fields['amount_monthly'].widget.attrs['style'] = 'width: 5rem'
 
        self.fields['tshirt_size'].widget.attrs['x-model'] = 'tshirt_size'
conservancy/supporters/migrations/0002_sustainerorder_monthly_recurring_and_more.py
Show inline comments
 
new file 100644
 
# Generated by Django 4.2.16 on 2024-09-18 01:27
 

	
 
import django.core.validators
 
from django.db import migrations, models
 

	
 

	
 
class Migration(migrations.Migration):
 

	
 
    dependencies = [
 
        ('supporters', '0001_initial'),
 
    ]
 

	
 
    operations = [
 
        migrations.AddField(
 
            model_name='sustainerorder',
 
            name='monthly_recurring',
 
            field=models.BooleanField(default=False),
 
        ),
 
        migrations.AlterField(
 
            model_name='sustainerorder',
 
            name='amount',
 
            field=models.IntegerField(
 
                validators=[django.core.validators.MinValueValidator(100)]
 
            ),
 
        ),
 
        migrations.AlterField(
 
            model_name='sustainerorder',
 
            name='paid_time',
 
            field=models.DateTimeField(blank=True, null=True),
 
        ),
 
        migrations.AlterField(
 
            model_name='sustainerorder',
 
            name='tshirt_size',
 
            field=models.CharField(
 
                choices=[
 
                    ('', (('None', 'None'),)),
 
                    (
 
                        "Men's",
 
                        (
 
                            ("Men's S", "Men's S"),
 
                            ("Men's M", "Men's M"),
 
                            ("Men's L", "Men's L"),
 
                            ("Men's XL", "Men's XL"),
 
                            ("Men's 2XL", "Men's 2XL"),
 
                        ),
 
                    ),
 
                    (
 
                        "Standard women's",
 
                        (
 
                            ("Standard women's S", "Standard women's S"),
 
                            ("Standard women's M", "Standard women's M"),
 
                            ("Standard women's L", "Standard women's L"),
 
                            ("Standard women's XL", "Standard women's XL"),
 
                            ("Standard women's 2XL", "Standard women's 2XL"),
 
                        ),
 
                    ),
 
                    (
 
                        "Fitted women's",
 
                        (
 
                            ("Fitted women's S", "Fitted women's S"),
 
                            ("Fitted women's M", "Fitted women's M"),
 
                            ("Fitted women's L", "Fitted women's L"),
 
                            ("Fitted women's XL", "Fitted women's XL"),
 
                            ("Fitted women's 2XL", "Fitted women's 2XL"),
 
                        ),
 
                    ),
 
                ],
 
                max_length=50,
 
            ),
 
        ),
 
    ]
conservancy/supporters/models.py
Show inline comments
...
 
@@ -62,3 +62,2 @@ class SustainerOrder(models.Model):
 
    amount = models.IntegerField(
 
        default=128,
 
        validators=[
...
 
@@ -66,2 +65,3 @@ class SustainerOrder(models.Model):
 
        ])
 
    monthly_recurring = models.BooleanField(default=False)
 
    paid_time = models.DateTimeField(null=True, blank=True)
conservancy/supporters/templates/supporters/sustainers_stripe.html
Show inline comments
...
 
@@ -29,2 +29,5 @@
 
   }
 
   .btn:active {
 
     transform: scale(1.05);
 
   }
 
  </style>
...
 
@@ -277,3 +280,3 @@ reach for reproducibility. </p>
 
        <a href="{% url "stripe2" %}">
 
          <button type="submit" class="pointer" 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>
 
          <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>
conservancy/supporters/templates/supporters/sustainers_stripe2.html
Show inline comments
...
 
@@ -8,2 +8,7 @@
 
  <script defer src="{% static "js/vendor/alpine-3.14.1.js" %}"></script>
 
  <style>
 
   .btn:active {
 
     transform: scale(1.05);
 
   }
 
  </style>
 
{% endblock %}
...
 
@@ -19,4 +24,6 @@
 
                    tshirt_required: function () { return this.tshirt_size !== 'None' },
 
                    recurring: 'once',
 
                  }">
 
      {% csrf_token %}
 
      {{ form.errors }}
 
      <div class="mb2"><label>Name
...
 
@@ -29,5 +36,12 @@
 
      </div>
 
      <div class="mb2"><label>Amount
 
      <div class="mb2"><label>
 
        <label class="mr1"><input type="radio" name="recurring" value="once" x-model="recurring"> Once</label>
 
        <label><input type="radio" name="recurring" value="monthly" x-model="recurring"> Monthly</label>
 
      </label></div>
 
      <div class="mb2" x-show="recurring === 'once'"><label>Amount
 
        <span class="db mt1">$ {{ form.amount }}</span>
 
      </label></div>
 
      <div class="mb2" x-show="recurring === 'monthly'"><label>Amount
 
          <span class="db mt1">$ {{ form.amount_monthly }}</span>
 
        </label></div>
 
      <div class="mv3"><label class="lh-title"><input type="checkbox"> Acknowledge me on the public <a href="">list of sustainers</a></label></div>
...
 
@@ -65,3 +79,5 @@
 

	
 
      <div class="mt3"><button type="submit" 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);">Pay via Stripe</button></div>
 
      <div class="mt3"><button type="submit" class="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);">Pay via Stripe</button></div>
 

	
 
      <p class="f7 mt3">If you have concerns or issues paying with Stripe, we also accept payment by <a href="#">paper check</a> and <a href="#">wire transfer</a>.</p>
 
    </form>
conservancy/supporters/views.py
Show inline comments
...
 
@@ -47,3 +47,4 @@ def sponsors(request):
 

	
 
def create_checkout_session(reference_id, email, amount, base_url):
 
def create_checkout_session(reference_id, email: str, amount: int, recurring: bool, base_url: str):
 
    # https://docs.stripe.com/payments/accept-a-payment
 
    YOUR_DOMAIN = base_url
...
 
@@ -57,3 +58,5 @@ def create_checkout_session(reference_id, email, amount, base_url):
 
                        'product_data': {'name': 'Contribution'},
 
                        'unit_amount': amount * 100,
 
                        'unit_amount': amount * 100,  # in cents
 
                        # https://docs.stripe.com/products-prices/pricing-models#variable-pricing
 
                        'recurring': {'interval': 'month'} if recurring else None,
 
                    },
...
 
@@ -63,3 +66,3 @@ def create_checkout_session(reference_id, email, amount, base_url):
 
            customer_email=email,
 
            mode='payment',
 
            mode='subscription' if recurring else 'payment',
 
            success_url=YOUR_DOMAIN + '/sustainer/success/?session_id={CHECKOUT_SESSION_ID}',
...
 
@@ -80,5 +83,9 @@ def sustainers_stripe2(request):
 
        if form.is_valid():
 
            order = form.save()
 
            order = form.save(commit=False)
 
            if form.data['recurring'] == 'monthly':
 
                order.amount = form.cleaned_data['amount_monthly']
 
                order.monthly_recurring = True
 
            order.save()
 
            base_url = f'{request.scheme}://{request.get_host()}'
 
            stripe_checkout_url = create_checkout_session(order.id, order.email, order.amount, base_url)
 
            stripe_checkout_url = create_checkout_session(order.id, order.email, order.amount, order.monthly_recurring, base_url)
 
            return redirect(stripe_checkout_url)
0 comments (0 inline, 0 general)