Skip to main content

Webhooks for syncing Stripe payment data to your Django database.

Project description

Stripe Webhooks for Django

codecov

django-stripe-hooks is a Django application that keeps your local database in sync with Stripe by consuming webhook events. Instead of making manual API calls every time you need Stripe data, this library automatically maintains local, up-to-date copies of Stripe objects in your database.

Incoming webhooks are signature-verified and routed to update_or_create calls on the corresponding Django model. Your database reflects Stripe's authoritative state; you query it like any other Django model.

The local models are read-only mirrors of Stripe. Always create or modify Stripe objects through the Stripe SDK (or Dashboard) — the corresponding local model will be updated automatically when the webhook arrives. If you need the local instance immediately (e.g. in the same request context, without waiting for a webhook), you can call Model.objects.from_stripe() directly on the return value of any SDK method:

stripe_customer = stripe_client.v1.customers.create(params={...})
customer = Customer.objects.from_stripe(stripe_customer)  # synced immediately

This library facilitates Stripe integration. Always test thoroughly in Stripe's test mode before deploying to production.


Supported Models

Stripe Object Django Model
Product Product
Price Price
Coupon Coupon
PromotionCode PromotionCode
Discount Discount
Customer Customer
PaymentMethod PaymentMethod
PaymentIntent PaymentIntent
Subscription Subscription
SubscriptionItem SubscriptionItem
Invoice Invoice
InvoiceLineItem InvoiceLineItem
InvoicePayment InvoicePayment
BalanceTransaction BalanceTransaction
Charge Charge
Refund Refund

Installation and Configuration

1. Install the package

pip install django-stripe-hooks

2. Add to INSTALLED_APPS

INSTALLED_APPS = [
  # ...
  'django_stripe_hooks',
]

3. Add Stripe credentials to settings.py

Retrieve your API keys from the Stripe Dashboard.

STRIPE_PUBLIC_KEY = "pk_live_..."
STRIPE_SECRET_KEY = "sk_live_..."
STRIPE_WEBHOOK_SECRET_KEY = "whsec_..."  # filled in after step 5

Security: Never commit API keys to version control. Use environment variables or a secrets manager.

4. Include the webhook URL

In your project's urls.py:

from django.urls import path, include

urlpatterns = [
  path('stripe/', include('django_stripe_hooks.urls')),
  # ...
]

This exposes https://yourdomain.com/stripe/webhooks/ as the webhook endpoint.

If you prefer to use a custom URL (e.g. because you are subclassing StripeWebhooks), see the Custom URL section below.

5. Run migrations

python manage.py migrate

6. Run the setup management command

python manage.py setup_stripe

This interactive command will allow you to create, update, and manage your Stripe webhook endpoint directly from the command line. After creation, the command prints two values to add to your settings.py:

STRIPE_WEBHOOK_ENDPOINT_ID = "we_..."    # used to update the endpoint later
STRIPE_WEBHOOK_SECRET_KEY  = "whsec_..." # used to verify incoming signatures

Important: STRIPE_WEBHOOK_SECRET_KEY is only returned once by Stripe. Copy it immediately.

Re-run python manage.py setup_stripe any time you need to update the endpoint URL or event subscriptions.

7. Run the import management command (optional)

If you have existing Stripe data that you want to import into your local database, run:

python manage.py import_stripe_data

API Version

This package pins the Stripe Python SDK to version 14.x, which uses API version 2026-02-25.clover. The setup_stripe management command creates webhook endpoints using the SDK's API version automatically — no manual version selection in the Stripe Dashboard is required.


Author Hooks

To react to specific webhook events (e.g. sending a welcome email when a subscription is created), subclass StripeWebhooks and add a method named after the event type with dots replaced by underscores:

from django_stripe_hooks.views import StripeWebhooks

class MyStripeWebhooks(StripeWebhooks):

  def customer_subscription_created(self) -> None:
    subscription = self.django_obj
    send_mail(
      subject="Thanks for subscribing!",
      message="...",
      from_email="noreply@example.com",
      recipient_list=[subscription.customer.email],
    )

  def invoice_paid(self) -> None:
    # self.event    — the raw Stripe Event object
    # self.stripe_obj — the fetched Stripe object (Invoice in this case)
    # self.django_obj — the corresponding Django model instance
    ...

Hook methods are called after the local database has been updated, so self.django_obj always reflects the latest state.

Hook methods may optionally return an HttpResponse. If they return None, the default 200 OK response is used.

Note: If the object type is not implemented by this library (e.g. a KeyError during model resolution), the hook is still called but self.stripe_obj and self.django_obj will not be set. Guard accordingly if you handle unimplemented event types.

Custom URL

If you use a subclassed view, remove the django_stripe_hooks.urls include and register your view directly:

# urls.py
from django.urls import path
from myapp.views import MyStripeWebhooks

urlpatterns = [
  path('payments/webhooks/', MyStripeWebhooks.as_view(), name='stripe_webhooks'),
]

Keep the name='stripe_webhooks' so the setup_stripe management command can resolve the path automatically. If you use a different name, the command will prompt you to enter the path manually.


Webhook Delivery Caveats

Stripe delivers webhooks asynchronously and without guaranteed ordering. A few consequences to be aware of:

  • Events may arrive out of order. For example, an invoice.paid event may arrive before the customer.created event for the same customer. The library handles this with db_constraint=False on all foreign keys — Stripe is the authoritative source, and referential integrity is enforced at the application level rather than the database level.

  • Events may be retried. If your endpoint returns a non-2xx response, Stripe will retry the event. The library's update_or_create logic is idempotent, so retries are safe.

  • Events may be delayed. In practice, most webhooks arrive within a few seconds, but network conditions and Stripe processing can delay delivery by minutes. Do not rely on webhooks for time-critical operations where you need an immediate response.

  • Not all objects generate webhooks. SubscriptionItem, InvoiceLineItem, InvoicePayment, and BalanceTransaction do not have their own Stripe webhook events. They are populated as nested objects within parent webhook payloads (e.g. a customer.subscription.updated event includes subscription items).


Testing

To install dev dependencies and set up the test environment:

git clone git@github.com:geoffrey-eisenbarth/django-stripe-hooks.git
cd django-stripe-hooks
pip install poetry
poetry install --with dev

The integration test suite requires the Stripe CLI to forward webhook events to your local server.

1. Install the Stripe CLI

curl -L https://github.com/stripe/stripe-cli/releases/download/v1.39.0/stripe_1.39.0_linux_x86_64.tar.gz | sudo tar -xz -C /usr/local/bin stripe
stripe --version

2. Configure test credentials

Copy .env.example to .env and fill in your Stripe test-mode keys:

STRIPE_PUBLIC_KEY=pk_test_...
STRIPE_SECRET_KEY=sk_test_...

STRIPE_WEBHOOK_SECRET_KEY is set automatically by the test suite via the Stripe CLI tunnel.

3. Run tests

poetry run pytest -s

To generate a coverage report:

poetry run pytest --cov --cov-branch --cov-report=xml

Troubleshooting

  • OSError: [Errno 98] Address already in use — run pkill stripe to clear any hanging CLI processes.
  • Detailed webhook forwarding logs are written to tests/stripe_cli.log.

License

Distributed under the MIT license.

Support

Open an issue for bugs or questions.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_stripe_hooks-0.2.0.dev2.tar.gz (32.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_stripe_hooks-0.2.0.dev2-py3-none-any.whl (34.0 kB view details)

Uploaded Python 3

File details

Details for the file django_stripe_hooks-0.2.0.dev2.tar.gz.

File metadata

  • Download URL: django_stripe_hooks-0.2.0.dev2.tar.gz
  • Upload date:
  • Size: 32.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.3.3 CPython/3.14.3 Linux/6.17.0-1008-azure

File hashes

Hashes for django_stripe_hooks-0.2.0.dev2.tar.gz
Algorithm Hash digest
SHA256 dbe43292600865a761697e28b95d228d045845388ce68b02251b92c310c36eb3
MD5 11356b2b4f406d0488e3f0746576bad2
BLAKE2b-256 ae93ab9e7ae28b1ac05b4eeedb48f0c869d6949b9a971acc880cf36f0a52574c

See more details on using hashes here.

File details

Details for the file django_stripe_hooks-0.2.0.dev2-py3-none-any.whl.

File metadata

File hashes

Hashes for django_stripe_hooks-0.2.0.dev2-py3-none-any.whl
Algorithm Hash digest
SHA256 84afeeec06d8039491f3790e2b0b4d6c15e1b2c6f2bf892e9d31159a8a463973
MD5 aefb54a6089f57fd19104c5502d4f8d8
BLAKE2b-256 da1f1b828b09c5063ade67b431b1120811e1e3886be4a787da18e2e059de97f8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page