Files @ 1064a12981d7
Branch filter:

Location: website/conservancy/supporters/views.py - annotation

bsturmfels
Bring Stripe sustainer form into "Become a sustainer" page
2ff551147c69
26a6928a2046
2ff551147c69
a74244efb44e
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
b7e2ce2a4d44
531a97a3c9af
26a6928a2046
26a6928a2046
6fe13959a3b3
26a6928a2046
b7e2ce2a4d44
2ff551147c69
15ce31eedbb3
4fa0f8343c20
4fa0f8343c20
4fa0f8343c20
4fa0f8343c20
4fa0f8343c20
501b4b05fc91
4fa0f8343c20
6fe13959a3b3
6fe13959a3b3
6fe13959a3b3
6fe13959a3b3
2ff551147c69
2ff551147c69
2ff551147c69
2ff551147c69
2ff551147c69
2ff551147c69
2ff551147c69
2ff551147c69
2ff551147c69
2ff551147c69
2ff551147c69
2ff551147c69
2ff551147c69
2ff551147c69
2ff551147c69
2ff551147c69
2ff551147c69
2ff551147c69
26a6928a2046
26a6928a2046
3fe83d14667f
ce4ae22fa59e
a74244efb44e
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
ce4ae22fa59e
ce4ae22fa59e
3fe83d14667f
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
ce4ae22fa59e
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
1064a12981d7
1064a12981d7
1064a12981d7
1064a12981d7
1064a12981d7
1064a12981d7
1064a12981d7
1064a12981d7
1064a12981d7
1064a12981d7
1064a12981d7
1064a12981d7
1064a12981d7
1064a12981d7
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
ce4ae22fa59e
3fe83d14667f
3fe83d14667f
ce4ae22fa59e
ce4ae22fa59e
26a6928a2046
3fe83d14667f
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
a74244efb44e
a74244efb44e
a74244efb44e
a74244efb44e
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
a74244efb44e
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
a74244efb44e
a74244efb44e
a74244efb44e
a74244efb44e
a74244efb44e
a74244efb44e
a74244efb44e
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
a74244efb44e
a74244efb44e
a74244efb44e
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
26a6928a2046
from datetime import datetime
import logging

from django.conf import settings
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: str, base_url: str):
    # https://docs.stripe.com/payments/accept-a-payment
    # https://docs.stripe.com/api/checkout/sessions
    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': recurring} 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):
    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()}'
            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_stripe2.html', {'form': form})


stripe.api_key = settings.STRIPE_API_KEY
if stripe.api_key == '':
    logger.warning('Missing STRIPE_API_KEY')


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', 'invoice'],
    )

    # 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()
            if checkout_session['payment_intent']:
                # Payments get a payment intent directly
                order.payment_id = checkout_session['payment_intent']
            else:
                # Subscriptions go get a payment intent generated on the invoice
                order.payment_id = checkout_session['invoice']['payment_intent']
            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 = settings.STRIPE_ENDPOINT_SECRET
    if endpoint_secret == '':
        logger.warning('Missing STRIPE_ENDPOINT_SECRET')

    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)