dj-paypal-checkout
A modern, REST-first PayPal integration for Django: Orders v2 checkout, refunds and verified webhooks, with models, signals and admin.
Status: 0.3.0. One-off payments are covered end to end: configuration, OAuth2 auth with token caching, sync/async HTTP clients, amount handling, models with persisted idempotency keys, the Orders v2 create/authorize/capture flows, refunds and voids, verified webhooks, a reconciliation command, signals, a read-only admin and a runnable demo. Subscriptions add the products/plans catalog, create/revise and lifecycle operations, verified lifecycle/payment webhooks and recurring-payment records. Payment Method Tokens v3 adds setup/payment tokens, verified Vault webhooks and local audit records. Browser-side Card Fields still requires merchant enablement and application UI.
It has not been run against live PayPal traffic yet, and the API may still change on minor versions before 1.0. See PROGRESS.md.
Why another PayPal library?
The established django-paypal package is built on Payments Standard with
IPN/PDT, i.e. PayPal's Classic stack. PayPal now recommends webhooks for all
new integrations and IPN is not fired by newer payment products. Meanwhile
PayPal's own paypal-server-sdk is sync-only and ships neither webhook
signature verification nor the subscription plans/products catalog.
This library targets the current REST APIs and fills those gaps:
| Area | API / implementation |
|---|---|
| Checkout | Orders v2 (create → approve → capture) |
| Captures/refunds | Payments v2, with a local guard against over-refunding |
| Notifications | Webhooks with RSA-SHA256 signature verification — no IPN |
| Client side | JS SDK v6 loader; checkout UI remains application policy |
| Subscriptions | Subscriptions v1 + plans/products catalog and lifecycle webhooks |
| Saved methods | Payment Method Tokens v3 (setup tokens → permanent vault tokens) |
| Async | sync and async client, same surface |
Design principles
- The server owns the amount. It is computed from your own order; the browser only ever receives a PayPal order id.
- Webhooks are the source of truth for money having moved, and handlers are idempotent — PayPal retries, and events can arrive more than once.
- Writes are idempotent, via
PayPal-Request-Id, so a retry cannot double-charge. Decimalend to end, with currency-correct scale (never float).- One config entry point: a single
PAYPALsettings dict, read only bypaypal_checkout.config. - The DB is a local cache of PayPal state — concrete models plus a generic FK to your own order object, so admin, audit and re-sync work out of the box.
Requirements
- Python 3.11+
- Django 5.2 LTS or 6.0
Try it
example/ is a runnable sandbox checkout — two endpoints, signals, and a
read-only admin:
cp example/.env.example example/.env
# Edit example/.env with the client id and secret of a PayPal sandbox REST app.
./run_demo.sh # http://127.0.0.1:8000/
run_demo.sh loads example/.env automatically. The file is ignored by Git
and must never contain live credentials. PAYPAL_WEBHOOK_ID is optional for
the synchronous checkout and is only needed to test verified webhook delivery.
The demo also sets Django's SECURE_CROSS_ORIGIN_OPENER_POLICY to
"same-origin-allow-popups", as required for the cross-origin PayPal popup to
communicate with its opener.
Development
# Run the test suite (custom runner, uses tests/test_settings.py)
python tests/runtests.py
# Coverage (what CI runs; fails below 100% via .coveragerc)
coverage run tests/runtests.py && coverage report -m
# Docs the way CI and Read the Docs build them (warnings are errors)
sphinx-build -W --keep-going -b html docs/source docs/build/html
Invoke tasks are available too: invoke test, invoke coverage,
invoke docs, invoke clean.
License
MIT — see LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dj_paypal_checkout-0.3.0.tar.gz.
File metadata
- Download URL: dj_paypal_checkout-0.3.0.tar.gz
- Upload date:
- Size: 97.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e56c0e1e7eac37f6ad48c3a450c573f81a7614ac033d172bdeb17eb9439573d1
|
|
| MD5 |
805ddefc7d5281c97594dc6938ef86d9
|
|
| BLAKE2b-256 |
4873971b34b6be2a0e27629e9494582151ed2ee9d2dee3e7f80ffe326e77ef74
|
Provenance
The following attestation bundles were made for dj_paypal_checkout-0.3.0.tar.gz:
Publisher:
publish.yml on otto-torino/dj-paypal-checkout
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dj_paypal_checkout-0.3.0.tar.gz -
Subject digest:
e56c0e1e7eac37f6ad48c3a450c573f81a7614ac033d172bdeb17eb9439573d1 - Sigstore transparency entry: 2280887634
- Sigstore integration time:
-
Permalink:
otto-torino/dj-paypal-checkout@97cccbf5c9d1c2226ef4c0cd9de6c234d3bc6e8d -
Branch / Tag:
refs/heads/main - Owner: https://github.com/otto-torino
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@97cccbf5c9d1c2226ef4c0cd9de6c234d3bc6e8d -
Trigger Event:
push
-
Statement type:
File details
Details for the file dj_paypal_checkout-0.3.0-py3-none-any.whl.
File metadata
- Download URL: dj_paypal_checkout-0.3.0-py3-none-any.whl
- Upload date:
- Size: 67.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
310ce6fc3ad8fa5124950e11556c4dd08bba544a8cfe28af5e93f6acb424af41
|
|
| MD5 |
0513926784ea23658396121963301033
|
|
| BLAKE2b-256 |
690391e9d4531c93b585ba4a7c2d7cca3cb8b906e34c70a6edfd88b996118770
|
Provenance
The following attestation bundles were made for dj_paypal_checkout-0.3.0-py3-none-any.whl:
Publisher:
publish.yml on otto-torino/dj-paypal-checkout
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dj_paypal_checkout-0.3.0-py3-none-any.whl -
Subject digest:
310ce6fc3ad8fa5124950e11556c4dd08bba544a8cfe28af5e93f6acb424af41 - Sigstore transparency entry: 2280887649
- Sigstore integration time:
-
Permalink:
otto-torino/dj-paypal-checkout@97cccbf5c9d1c2226ef4c0cd9de6c234d3bc6e8d -
Branch / Tag:
refs/heads/main - Owner: https://github.com/otto-torino
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@97cccbf5c9d1c2226ef4c0cd9de6c234d3bc6e8d -
Trigger Event:
push
-
Statement type: