Files @ ce4ae22fa59e
Branch filter:

Location: website/conservancy/supporters/views.py

bsturmfels
Add prototype monthly recurring payment via Stripe
from datetime import datetime
import logging

from django.http import HttpResponse
from django.shortcuts import render, redirect
from django.utils import timezone
import stripe

from .. import ParameterValidator
from . import forms
from .models import Supporter, SustainerOrder

logger = logging.getLogger(__name__)

def sustainers(request):
    with ParameterValidator(request.GET, 'upgrade_id') as validator:
        try:
            amount_param = float(request.GET['upgrade'])
        except (KeyError, ValueError):
            validator.fail()
        else:
            validator.validate('{:.2f}'.format(amount_param))
    partial_amount = amount_param if validator.valid else 0
    context = {
        'partial_amount': partial_amount,
        'minimum_amount': 120 - partial_amount,
    }
    return render(request, "supporters/sustainers.html", context)


def sponsors(request):
    """Conservancy Sponsors Page view

    Performs object queries necessary to render the sponsors page.
    """
    supporters = Supporter.objects.all().filter(display_until_date__gte=datetime.now())
    supporters_count = len(supporters)
    anonymous_count  = len(supporters.filter(display_name='Anonymous'))
    supporters = supporters.exclude(display_name='Anonymous').order_by('ledger_entity_id')
    c = {
        'supporters' : supporters,
        'supporters_count' : supporters_count,
        'anonymous_count' : anonymous_count
    }
    return render(request, "supporters/sponsors.html", c)


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
    try:
        checkout_session = stripe.checkout.Session.create(
            client_reference_id=str(reference_id),
            line_items=[
                {
                    'price_data': {
                        'currency': 'usd',
                        'product_data': {'name': 'Contribution'},
                        'unit_amount': amount * 100,  # in cents
                        # https://docs.stripe.com/products-prices/pricing-models#variable-pricing
                        'recurring': {'interval': 'month'} if recurring else None,
                    },
                    'quantity': 1,
                },
            ],
            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', {})


def sustainers_stripe2(request):
    if request.method == 'POST':
        form = forms.SustainerForm(request.POST)
        if form.is_valid():
            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, order.monthly_recurring, base_url)
            return redirect(stripe_checkout_url)
    else:
        form = forms.SustainerForm()
    return render(request, 'supporters/sustainers_stripe2.html', {'form': form})


stripe.api_key = 'sk_test_zaAqrpHmpkXnHQfAs4UWkE3d'

def fulfill_checkout(session_id):
    print("Fulfilling Checkout Session", session_id)

    # TODO: Make this function safe to run multiple times,
    # even concurrently, with the same session ID

    # TODO: Make sure fulfillment hasn't already been
    # peformed for this Checkout Session

    # Retrieve the Checkout Session from the API with line_items expanded
    checkout_session = stripe.checkout.Session.retrieve(
        session_id,
        expand=['line_items'],
    )

    # Check the Checkout Session's payment_status property
    # to determine if fulfillment should be peformed
    if checkout_session.payment_status != 'unpaid':
        # TODO: Perform fulfillment of the line items

        # TODO: Record/save fulfillment status for this
        # Checkout Session
        logger.info(f'Session ID {session_id} PAID!')
        try:
            order = SustainerOrder.objects.get(id=checkout_session['client_reference_id'], paid_time=None)
            order.paid_time=timezone.now()
            order.save()
            logger.info(f'Marked sustainer order {order.id} (order.email) as paid')
        except SustainerOrder.DoesNotExist:
            logger.info('No action')


def success(request):
    fulfill_checkout(request.GET['session_id'])
    return render(request, 'supporters/stripe_success.html', {})


def webhook(request):
    payload = request.body
    sig_header = request.META['HTTP_STRIPE_SIGNATURE']
    event = None

    # From webhook dashboard
    endpoint_secret = 'whsec_lLy9pqxAAHdl4fwiC0cFg1KwR6y4CvOH'

    try:
        event = stripe.Webhook.construct_event(
            payload, sig_header, endpoint_secret
        )
    except ValueError:
        # Invalid payload
        return HttpResponse(status=400)
    except stripe.error.SignatureVerificationError:
        # Invalid signature
        return HttpResponse(status=400)

    if (
            event['type'] == 'checkout.session.completed'
            or event['type'] == 'checkout.session.async_payment_succeeded'
    ):
        fulfill_checkout(event['data']['object']['id'])

    return HttpResponse(status=200)