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.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.4.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.4.0
File Size Uploaded
django_plans_payments-2.4.0.tar.gz 29.6 kB Details

Built distribution (wheel)

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

Total release size: 54.0 kB

Release files / django_plans_payments-2.4.0.tar.gz

Download URL django_plans_payments-2.4.0.tar.gz
Size 29.6 kB
Tags Source
SHA-256 checksum
How to use checksums
8da50cda69f8b3a691188e32ffdf65b4542533f34b19abeacd83eddcfd6cc8d1
BLAKE2b-256 checksum
How to use checksums
dc01337688658c3a631781883396c379cbe7f04f3d8b26eb93d1eaffba485577
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.4.0-py3-none-any.whl

Download URL django_plans_payments-2.4.0-py3-none-any.whl
Size 24.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8aa81b3ead712fc63269716f0072bcd2775eac7aa4c9a5721d59a5d83f83eafd
BLAKE2b-256 checksum
How to use checksums
19f3a7ed9de10eea9ff9d00b32682fc9d8debfa252231a7ec7f3afe500ae487c
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

2.5.0

2 release files

This release

2.4.0 This release

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