Django plans payments
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.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.
Release files for django-plans-payments 2.3.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| django_plans_payments-2.3.2.tar.gz | 27.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_plans_payments-2.3.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 51.5 kB
Release files / django_plans_payments-2.3.2.tar.gz
| Download URL | django_plans_payments-2.3.2.tar.gz |
|---|---|
| Size | 27.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9f7aa672aeee35708ccf218a5a4aaa986d557cf5bb1e11e95a285aa1c4726971
|
|
BLAKE2b-256 checksum How to use checksums |
3d7dbc6e16518928e8113f31bc84ff7ff6253b050001f1abd06f9448f05844b1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|
Release files / django_plans_payments-2.3.2-py3-none-any.whl
| Download URL | django_plans_payments-2.3.2-py3-none-any.whl |
|---|---|
| Size | 23.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5072aab79a6339c26daa24bf8045017af525f8b2e23e3d0e3e8fa07a64eee2e9
|
|
BLAKE2b-256 checksum How to use checksums |
8309e78c809c823ef6f18f57016696c0477aeccfee6a1aca8448952a2f534d55
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|