Skip to main content

Django plans payments

https://badge.fury.io/py/django-plans-payments.svg https://github.com/PetrDlouhy/django-plans-payments/actions/workflows/main.yml/badge.svg https://codecov.io/gh/PetrDlouhy/django-plans-payments/branch/master/graph/badge.svg

Almost automatic integration between django-plans and django-payments. This will add payment buttons to the order page and automatically confirm the Order after the payment. Optionally, it can return the corresponding order when a payment is refunded.

Documentation

The full documentation is at https://django-plans-payments.readthedocs.io.

Quickstart

Install and configure django-plans and django-payments apps. Capture mode is not yet supported, so PAYMENT_VARINANTS with 'capture': False will not get confirmed.

Install Django plans payments:

pip install django-plans-payments

Add it to your INSTALLED_APPS, before the plans:

INSTALLED_APPS = (
    ...
    'related_admin',
    'plans_payments',
    'plans',
    ...
)

Add Django plans_payments to the URL patterns:

urlpatterns = [
    ...
    url(r'^plans-payments', include('plans_payments.urls')),
    ...
]

Set django-plans settings and set model to:

PAYMENT_MODEL = 'plans_payments.Payment'

Charge-attempt idempotency

CreatePaymentView refuses to fire duplicate live charge attempts: a request while a previous payment is still in flight (or just captured) redirects to that payment instead of creating a twin, and a request right after a decline waits out a short cooldown, because banks read rapid-fire retries as fraud. Both windows look across all the user’s orders and are configurable:

PLANS_PAYMENTS_JOIN_IN_FLIGHT_SECONDS = 180  # 0 disables joining
PLANS_PAYMENTS_DECLINE_COOLDOWN_SECONDS = 60  # 0 disables the cooldown

Customer IP address

Customer IP address is stored in Payment model and used for some payment providers (i.e. PayU). For security reasons django-plans-payments does acquire the IP only from request REMOTE_ADDR parameter. If you are behind proxy, you will need to setup some mechanism to populate this variable from HTTP_X_FORWARDED_FOR parameter. The suggested solution is to use django-httpforwardedfor or django-xff application for that.

Running Tests

Does the code actually work?

source <YOURVIRTUALENV>/bin/activate
(myenv) $ pip install tox
(myenv) $ tox

Credits

Tools used in rendering this package:

History

2.5.0 (2026-10-09)

  • System check plans_payments.W001 warns when the installed django-payments has no Payment.autocomplete_with_wallet(). Released django-payments lacks it, so automatic renewals failed with an AttributeError on the day they were due.

  • An automatic renewal that needs 3-D Secure is logged instead of printed to stdout, without the member’s payment link.

  • The package’s long description is declared as reStructuredText.

  • A confirmed payment never removes the subscription. 2.4.0 removed it when the payment’s variant differed from the subscription’s provider, so a subscriber who bought a one-off period or an upgrade with another payment method (a payu card payment against a payu-recurring subscription, a PayPal one-off) lost the renewal. The subscription now changes only when a confirmed payment stores a new renew token. A payment with another variant also no longer marks the stored token verified, since it did not use it.

  • Payment.replace_renew_token (and the matching create_payment_object argument) lets a payment collect new payment details for an existing subscription. get_renew_token() and get_renew_data() return None for it, so the provider asks for a card (with 3-D Secure) instead of charging the stored token, and the token it receives is kept in Payment.new_renew_token and written to the subscription only when the payment is confirmed. Before, a member whose stored card kept failing could only retry that card, and a declined new card would have replaced the stored one, leaving the subscription without a usable token. Both columns are nullable, so code without them can still insert payments during a deploy.

2.4.0 (2026-10-08)

  • A failed payment cancels its order based on the stored order row, locked, and writes only status. It used the order in memory, which can be stale, and saved it whole, so an order completed meanwhile became CANCELED and lost its completion date.

  • A subscription with another payment provider is removed when the new payment is confirmed, not when it is created. create_payment_object deleted it as soon as a payment with another method was started, so a buyer who tried another method and gave up lost a working subscription.

  • Tested on Django 4.2-6.1 with Python 3.10-3.14, each Django version on the Python versions it supports (added Django 6.0/6.1 and Python 3.14). The tests run against the django-payments fork commit BlenderKit runs in production instead of the work-in-progress model-payu branch.

  • PLANS_PAYMENTS_COMPLETE_ORDER_AFTER_COMMIT (default False) completes the order after the confirmed payment commits instead of inside the same transaction. A failing complete_order() no longer rolls back the confirmation of a payment the provider has already captured (a PayPal checkout left the buyer charged with the payment still waiting).

2.3.2 (2026-09-15)

  • CreatePaymentView’s in-flight join and decline cooldown consider only payments of the requested variant. A checkout page that pre-creates a card payment for an embedded widget on page load no longer captures a click on another method (every “Pay with PayPal” click within the join window was redirected to the card payment), and a buyer whose card was just declined can switch to another method without waiting out the cooldown.

2.3.1 (2026-09-07)

  • Payment.set_renew_token on a plan-change order (an order without a pricing) stores only the token-related values on the existing RecurringUserPlan instead of re-arming it from the order, which left the renewal with pricing=None and the one-off difference as its amount.

2.3.0 (2026-08-19)

  • make CreatePaymentView idempotent: an attempt while a previous payment is in flight joins it instead of creating a twin, and an attempt right after a decline waits out a short cooldown (banks read rapid-fire retries as fraud). Configurable via PLANS_PAYMENTS_JOIN_IN_FLIGHT_SECONDS (default 180) and PLANS_PAYMENTS_DECLINE_COOLDOWN_SECONDS (default 60); 0 disables.

  • fix create_payment_object crashing with BillingInfo.DoesNotExist for users without saved billing info – billing fields fall back to empty strings.

  • fix renew_accounts charging automatic renewals for free plans: an armed RecurringUserPlan surviving a switch to a free plan took the customer’s money and extended nothing. The renewal is skipped with a warning instead.

2.2.0 (2026-07-23)

  • add Payment.invalidate_renew_token() - payment providers call it when the gateway reports the stored recurring token as permanently dead (e.g. PayU INVALID_TOKEN). The token is marked unverified so renewal tasks stop selecting the account and get_renew_token() returns None; the new plans_payments.signals.renew_token_invalidated signal lets host apps prompt the user to update their payment method.

  • add a pre-commit config (black/isort/flake8 at line length 119 plus hygiene hooks) and run it in CI, so local and CI lint cannot drift.

2.1.0 (2026-07-21)

  • Don’t disable token_verified on an unsuccessful renewal attempt, so a transient payment failure no longer permanently disarms automatic renewals

  • Add get_renew_data() and pass provider metadata (e.g. Stripe customer_id) through set_renew_token(**kwargs)

  • Add migration for the new django-payments PaymentStatus.CANCELLED (choices-only, database no-op)

  • Tests: drop the unused pytz dependency, satisfy current black

2.0.2 (2025-05-29)

  • fix the release

2.0.1 (2025-05-29)

  • fix the release

2.0.0 (2025-05-29)

  • Implement the wallet logic

  • Drop support for Python 3.8, Django 4.1

  • Add support for Django 5.2

  • don’t obscure Exceptions by catching them

1.5.0 (2025-02-28)

  • Drop support for Python 3.7

  • Add support for Python 3.13, Django 5.1

  • fix the wrong assumption that Payment.extra_data["response"] contains a "transactions" key

1.4.1 (2024-04-24)

  • do not check whether a confirmed payment of a completed order is left anymore

1.4.0 (2024-04-15)

  • migrate to RecurringUserPlan.renewal_triggered_by

  • add renewal_triggered_by parameter to Payment.set_renew_token

  • deprecate automatic_renewal parameter of Payment.set_renew_token; use renewal_triggered_by parameter instead

  • deprecate None value of renewal_triggered_by parameter of Payment.set_renew_token; set an AbstractRecurringUserPlan.RENEWAL_TRIGGERED_BY instead

1.3.1 (2024-04-15)

  • fix typo in payment description

1.3.0 (2024-04-12)

  • add optional returning orders when payments are refunded

1.2.2 (2023-12-20)

  • add change_reason for django-simple-history

1.2.1 (2023-12-19)

  • specify sender=Payment for change_payment_status receiver

1.2.0 (2023-10-16)

  • bugfix release (fix prevoius bad release)

1.1.3 (2023-10-15)

  • add some indexes to Payment model

1.1.2 (2023-03-29)

  • reword Payment description to ommit word “Subscribtion” which might raise warnings for banks/card providers

1.1.1 (2023-01-27)

  • correction release, include wheel update, correctly rebase to master

1.1.0 (2023-01-27)

  • Fix transaction fee double counting

1.0.1 (2022-12-09)

  • Fix migrations

1.0.0 (2022-12-08)

  • Recurring payments functionality

0.2.0 (2018-08-05)

  • Payment process without capturing should work

  • Automatic buttons generation

0.1.0 (2018-07-23)

  • First release on PyPI.

Metadata

Release files for django-plans-payments 2.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for django-plans-payments 2.5.0
File Size Uploaded
django_plans_payments-2.5.0.tar.gz 32.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for django-plans-payments 2.5.0
File Interpreter ABI Platform
django_plans_payments-2.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 59.6 kB

Release files / django_plans_payments-2.5.0.tar.gz

Download URL django_plans_payments-2.5.0.tar.gz
Size 32.8 kB
Tags Source
SHA-256 checksum
How to use checksums
b15b0a7b6f9de97921dc45f97f56ef3c8fa039e3524e70057fc61f5b8f189e50
BLAKE2b-256 checksum
How to use checksums
aec00e34886089800e657a701d77b172c6da08c2bd82ff237ae276767e5ebfa2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.6

Release files / django_plans_payments-2.5.0-py3-none-any.whl

Download URL django_plans_payments-2.5.0-py3-none-any.whl
Size 26.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
42e8b63a63581a9c45e8915a08eb1a96ef045e61804455563afaf3889218d1e9
BLAKE2b-256 checksum
How to use checksums
26f62c89bfacf75479ecd7ac3b7181f46c3ba6790a304f5b765279d4999db606
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.6

Release history Release notifications | RSS feed

This release

2.5.0 This release

2 release files

2.4.0

2 release files

2.3.2

2 release files

2.3.1

2 release files

2.3.0

2 release files

2.2.0

2 release files

2.1.0

2 release files

2.0.2

2 release files

2.0.0

2 release files

1.5.0

2 release files

1.4.1

2 release files

1.4.0

2 release files

1.3.1

2 release files

1.3.0

2 release files

1.2.2

2 release files

1.2.1

2 release files

1.2.0

2 release files

1.1.3

2 release files

1.1.2

1 release file

1.1.1

1 release file

1.1.0

1 release file

1.0.1

1 release file

1.0.0

1 release file

0.5.8

1 release file

0.5.7

1 release file

0.5.6

1 release file

0.5.5

1 release file

0.5.4

1 release file

0.5.3

1 release file

0.5.2

1 release file

0.5.1

1 release file

0.5.0

1 release file

0.4.1

1 release file

0.4.0

1 release file

0.3.0

1 release file

0.2.0

1 release file

0.1.0

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page