Skip to main content

Stripe Payments

Custom payment and donation experiences built into Django websites, supporting one-time and recurring contributions while Stripe securely handles payment processing.

  • Django
  • JavaScript
  • Python
  • Stripe
  • Webhooks

Overview

This project integrated Stripe with a Django website to provide a custom donation experience supporting both one-time and recurring contributions. The goal was to keep the donation experience integrated with the website, providing greater control over the user experience and surrounding application logic while relying on Stripe for the specialized infrastructure required to securely process payments.

The integration combines Django's server-side application logic with Stripe's browser-based payment components and APIs. Django manages the donation workflow, creates and tracks the appropriate Stripe resources, and responds to payment events, while sensitive payment details are transmitted directly to Stripe rather than passing through the application.

The result is a custom donation system that can be adapted to the needs of the organization and website without requiring the application itself to become a payment processor.

The Problem

Hosted payment and donation services can provide a quick way to begin accepting contributions, but they can also separate the payment process from the rest of the website and limit control over the donor experience, application logic, and data collected alongside a donation.

A custom integration provides greater control, but payment processing introduces requirements that ordinary web forms do not have. A donation may be immediate or recurring, additional authentication may be required by the card issuer, payment processing can continue after the initial browser request, and the final outcome of a transaction cannot always be determined from the page the donor sees.

The challenge was therefore to build a donation experience that could remain closely integrated with the Django application while delegating sensitive payment handling and the complexities of the payment network to Stripe.

Payment Architecture

The integration deliberately separates the responsibilities of the website, browser, and Stripe.

Django manages the donation workflow and communicates with Stripe's API to create the resources required for a transaction. Stripe's client-side components securely collect payment information in the donor's browser, allowing sensitive card details to be transmitted to Stripe without being submitted to the Django application.

Stripe then communicates payment events back to Django through webhooks, allowing the application to respond to changes independently of the donor's browser session.

This division of responsibility allows the website to control the user experience and business logic while Stripe handles the sensitive payment infrastructure.

System architecture Creates payment/subscription workflow Displays payment experience Secure payment details Webhook events Django Application Donor Browser Stripe
  • Django Application to Stripe : Creates payment/subscription workflow
  • Django Application to Donor Browser : Displays payment experience
  • Donor Browser to Stripe : Secure payment details
  • Stripe to Django Application : Webhook events

One-Time and Recurring Donations

The donation system supports both one-time contributions and recurring donations, while presenting them through a consistent website interface.

Behind that interface, the two payment types have different requirements. One-time donations can be processed as individual payments, while recurring donations require establishing reusable payment credentials and creating an ongoing billing relationship with Stripe.

The integration uses Stripe's PaymentIntent and SetupIntent workflows where appropriate to manage these different payment paths and support modern card authentication requirements such as Strong Customer Authentication.

Keeping these differences inside the integration layer allows the donor-facing experience to remain comparatively simple. Donors choose how much and how often they want to contribute; the application determines the appropriate payment workflow behind the scenes.

Payment Lifecycle and Webhooks

A payment is not always completed during a single HTTP request. Transactions may require additional authentication, succeed asynchronously, fail after initially being submitted, or change state after the donor has left the website.

For that reason, the application does not treat a browser redirect or success page as the authoritative record of a completed payment.

Stripe webhooks provide a separate server-to-server channel for communicating payment and subscription events to Django. The application validates and processes those events so its own records can reflect the state reported by Stripe.

This architecture is particularly important for recurring donations, where activity continues long after the original browser session. Future payments, failures, and other subscription events can occur without the donor actively interacting with the website.

Webhooks therefore make payment processing an ongoing application workflow rather than something dependent on a single form submission.

Security

The integration is designed to minimize the amount of sensitive payment information handled by the Django application.

Card details are collected using Stripe's client-side payment components and transmitted directly to Stripe. The Django application coordinates the transaction but does not need to receive or store raw card numbers or security codes.

Stripe API credentials remain on the server and are not exposed to client-side code. Webhook requests are also verified before they are processed so that incoming payment events can be confirmed as originating from Stripe.

The Django application provides an additional security boundary around the surrounding donation workflow. Standard Django protections, HTTPS, secure configuration, dependency maintenance, server-side validation, and controlled access to application data complement the payment-specific protections provided by Stripe.

The result is deliberately divided: Stripe is responsible for securely handling payment credentials and payment processing, while Django is responsible for the application's donation logic and the non-sensitive information it needs to retain.

Attribution and Analytics

A donation often needs to be understood in the context of the campaign or communication that generated it, not simply as an isolated payment.

The integration captures UTM campaign information associated with a donor's visit and preserves that attribution alongside the donation workflow. This makes it possible to connect completed donations with the campaigns, sources, or communications that directed donors to the website.

Because attribution is captured as part of the application rather than added after payment processing, campaign information can remain associated with the transaction as it moves through the Stripe workflow.

This provides the organization with more useful information about how its fundraising efforts perform while keeping the donor-facing payment experience straightforward.

Reuse and Customization

The integration separates reusable Stripe payment logic from the content and presentation of an individual website. This allows the same underlying approach to be adapted to different donation forms, branding, suggested contribution amounts, campaign requirements, and surrounding content.

Because the payment experience is part of the Django application, it can also participate in functionality beyond the transaction itself. Validation, attribution, application data, confirmation messaging, analytics, and other website-specific requirements can be incorporated without requiring those concerns to be handled by an external hosted donation page.

At the same time, the integration maintains a clear boundary around payment processing. Customization happens primarily in the website and application layers, while Stripe continues to provide the underlying payment infrastructure.

This balance provides control over the experience without attempting to recreate the specialized payment-processing capabilities that Stripe already provides.