dj-stripe 2.11.0 (2026-06-26)

IMPORTANT: Migrations have been reset. You MUST upgrade to dj-stripe 2.10.x before upgrading to dj-stripe 2.11.x.

Version support

  • Support for Django versions older than 5.2 has been dropped.
  • The minimum supported Stripe SDK version is now 15.0.1.
  • Migrations have been fully reset. You MUST upgrade to dj-stripe 2.10.x before upgrading to dj-stripe 2.11.x.
  • The default Stripe API version is now 2026-05-27.dahlia (was 2020-08-27).

Breaking Changes

  • InvoiceOrLineItemForeignKey has been removed (deprecated in 2.10)
  • Remove PlanTiersMode and PlanUsageType enums
  • Remove Customer.retry_unpaid_invoices
  • The Plan model has been removed. The Stripe Plans API was deprecated in favour of the Prices API years ago; use the Price model instead. This also removes the InvoiceItem.plan and SubscriptionItem.plan foreign keys (use .price), the subscription_plan argument of Invoice.upcoming(), the plan argument of Subscription.update(), and support for {"plan": ...} items in Customer.subscribe() (use {"price": ...}).
  • Legacy customer payment sources have been removed. The Card and BankAccount models now only represent external accounts on Connect accounts; their customer foreign key — and the Customer.legacy_cards and Customer.bank_account relations — are gone, along with the LegacySourceMixin class (renamed to ExternalAccountMixin) and Customer._sync_cards(). Connect external-account support is unchanged. Use PaymentMethod for customer payment instruments.
  • PaymentsContextMixin and SubscriptionMixin now expose prices and is_prices_plural in the template context (sourced from Price) instead of plans and is_plans_plural.
  • The djstripe_update_invoiceitem_ids management command has been removed. It was a one-time migration for the pre-2019-12-03 invoice item id format.

Deprecations

  • The Source model is now officially deprecated and emits a DeprecationWarning when synced. The Stripe Sources API has been deprecated in favour of the PaymentMethods API; use the PaymentMethod model instead. Source will be removed in a future release.

New features

  • Add the AccountV2 model for the Stripe Accounts v2 / Organizations API (/v2/core/accounts). It is served by Stripe's service-based v2 Core API, so its api_* methods route through StripeClient(...).v2.core.accounts (retrieve/list use include, close replaces delete, and the RFC 3339 created timestamp is parsed to a datetime). It is synced by djstripe_sync_models like other models. v2 thin-event webhooks are not yet handled.
  • Add ProductFeature model and automatic sync of product features
  • Add currency_options to Price.expand_fields
  • Stripe API keys may now be configured entirely via Django settings / environment variables, without having to store them in the database. The djstripe_sync_models command now syncs the keys defined in settings in addition to those in the database, so environment-variable-only and admin-less deployments work out of the box (#2100, #2055).
  • Customer.create() and Customer.get_or_create() now accept a metadata dict and arbitrary keyword arguments, which are forwarded to the Stripe Customer create call (saving a round-trip versus creating and then updating). The SUBSCRIBER_CUSTOMER_KEY metadata is still always set to the subscriber pk and takes precedence, so the customer-to-subscriber link cannot be broken by caller-supplied metadata (#795).
  • WebhookEndpoint URLs entered in the admin are now validated, with a hint pointing at the stripe_listen command for local development.

Bugfixes

  • Webhook processing errors now persist their WebhookEventTrigger record (with the exception and traceback) even when ATOMIC_REQUESTS is enabled. The webhook view is now exempt from ATOMIC_REQUESTS, and from_request wraps event processing in its own transaction, so a failure rolls back the partial event sync while still committing the error record before re-raising for Django's logging (#833).
  • Customer.valid_subscriptions now also excludes incomplete subscriptions (in addition to canceled and incomplete_expired). An incomplete subscription's initial payment has not succeeded yet — eg. one created with payment_behavior="default_incomplete" — so the customer is not paying for it. This also affects the Customer.subscription shortcut, which is built on valid_subscriptions (#1721).
  • Fix djstripe_sync_models aborting with "You don't have any API Keys in the database" when keys are configured only via settings / environment variables. The command now falls back to the settings keys, and --api-keys accepts keys that are not stored in the database (#2100).
  • Raise a clearer error when no Stripe API key is configured anywhere, instead of the cryptic InvalidStripeAPIKey: Invalid API key: '' (#2055).
  • Add support for the Stripe subscription status paused via SubscriptionStatus.paused.
  • Fix Django 5 template validation by replacing a parent-relative admin template path in webhook endpoint admin views.
  • Fix Connect account webhook sync failures when account branding points to platform-owned file uploads by retrying file retrieval in platform context.
  • Fix djstripe_sync_models Customer failures caused by sending unsupported id parameters to Stripe list_sources requests.
  • Restore compatibility with stripe-python 14 and 15. In stripe 15, StripeObject stopped inheriting from dict and lost the mapping-style methods (get, items, keys, values, pop, setdefault) that dj-stripe relies on across its sync paths. A small compatibility shim restores them on stripe >=15 and is a no-op on earlier versions.
  • Fix Charge._calculate_refund_amount mixing dollars and cents, which caused negative results on subsequent partial refunds.
  • Fix Customer.purge() crashing with AttributeError: property 'default_source' has no setter after default_source was refactored into a read-only property over stripe_data.
  • Stop hard-deleting SubscriptionItem rows when items are removed from a Subscription upstream, and gracefully return None when a referenced SubscriptionItem is no longer retrievable from Stripe. Together these keep invoice.* webhooks from crashing on invoices that reference SubscriptionItem ids removed by a SubscriptionSchedule phase change or a mid-period item removal (#2025, #2105). Note: subscription.items.all() will now include items that were removed upstream.
  • Fix FieldError: Cannot resolve keyword 'status' into field and similar errors raised by SubscriptionManager, TransferManager, and ChargeManager methods. The 2.10 refactor moved fields like status, start_date, canceled_at, paid, refunded, etc. from concrete columns into stripe_data, but the manager methods kept filtering on them as if they were still columns. The manager methods now use stripe_data__<key> JSON-path lookups, with Cast() for numeric aggregations (#2197).
  • Fix update_customer_helper looking up the subscriber by id instead of pk, which broke linking customers to subscribers on custom user models whose primary key is not named id (#2203).
  • Fix the stripe_listen management command generating a forward URL with a double slash (e.g. .../stripe//webhook/<uuid>) by no longer rebuilding the URL via string replacement (#2195).
  • Fix IntegrityError crash (duplicate key value violates unique constraint "djstripe_event_stripe_id_key") when Stripe delivers the same webhook event more than once and two deliveries race past the existence check. Event.process now treats the duplicate as already-processed and returns the existing event instead of crashing (#1239).
  • Mark ProcessWebhookView with login_not_required so incoming Stripe webhooks are no longer redirected to the login page when Django's LoginRequiredMiddleware is enabled (#2137).
  • Strip a trailing port number from the client address parsed out of the X-Forwarded-For header when recording a WebhookEventTrigger. Some proxies (eg. Azure) append :port to the address, which is not a valid inet value and caused DataError: invalid input syntax for type inet on PostgreSQL. IPv6 addresses (bare or bracketed) are handled correctly (#1843).
  • Fix syncing WebhookEndpoint objects that were created outside dj-stripe (eg. directly on the Stripe dashboard). These have no djstripe_uuid in their metadata, which was written straight onto the unique, non-null djstripe_uuid field and aborted the sync with a misleading WebhookEndpoint matching query does not exist. dj-stripe now keeps the auto-generated uuid when none is present in the metadata (#2146).
  • Stop webhooks from crashing when a platform-account event references an object that only exists on a connected account (eg. a destination charge's application_fee, or that ApplicationFee's charge). These are not retrievable from the platform, so the foreign key is now skipped gracefully instead of raising No such charge / No such application fee (#2010).
  • Stop payment_method.detached webhooks and djstripe_sync_models from crashing on detached legacy sources. A detached src_… object is wrapped as a payment method but 404s on the payment_methods endpoint with "A source must be attached to a customer to be used as a payment_method". The detach webhook now syncs from the event payload instead of re-retrieving, and the foreign-key sync path skips the unretrievable payment method gracefully (#1068).
  • Stop *.created / *.updated webhooks from crashing when the object was deleted on Stripe's side before the webhook was processed (eg. an object created and deleted in quick succession with the webhook retried in between). The retrieve now 404s gracefully and the event is skipped instead of raising No such … (#1218).
  • Fix Transfer.during() and Charge.during() computing month boundaries inconsistently; the start and end of the selected month are now derived in UTC.

Other changes

  • The handling of "the referenced Stripe object is gone" errors during sync is now centralised in djstripe._stripe_errors.object_is_absent, replacing the per-object-type message-substring branches that had accumulated across the sync and webhook code paths. As a side effect, the foreign-key sync path now skips any object that 404s as resource_missing, not just the handful of types that were previously hardcoded.
  • djstripe_sync_models now exits with a non-zero status code when one or more models fail to sync, so failures are detectable in CI and scripts.
  • The API key admin now displays a redacted secret instead of the plaintext key.