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.1.0, the first release. 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. Not yet: subscriptions, Vault, Card Fields.
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:
| 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 (standalone buttons, Card Fields) |
| Subscriptions | Subscriptions v1 + plans/products catalog (after 0.1.0) |
| 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:
export PAYPAL_CLIENT_ID=... # sandbox REST app credentials
export PAYPAL_CLIENT_SECRET=...
./run_demo.sh # http://127.0.0.1:8000/
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.1.0.tar.gz.
File metadata
- Download URL: dj_paypal_checkout-0.1.0.tar.gz
- Upload date:
- Size: 74.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0ddc3dce8d0e9bc6662aadf49b9511b927b5d704be7024ce1eeabd96390b3f9e
|
|
| MD5 |
24219f45b3bb8e4ab31faf5768a072f8
|
|
| BLAKE2b-256 |
28cbd53494d7fc0f832e456d630cbfa6b0e06dd12f0a7fb7db67a994e04fc01d
|
Provenance
The following attestation bundles were made for dj_paypal_checkout-0.1.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.1.0.tar.gz -
Subject digest:
0ddc3dce8d0e9bc6662aadf49b9511b927b5d704be7024ce1eeabd96390b3f9e - Sigstore transparency entry: 2272266126
- Sigstore integration time:
-
Permalink:
otto-torino/dj-paypal-checkout@2ee822adee3c0f6f844bf999af87dfd5205bda83 -
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@2ee822adee3c0f6f844bf999af87dfd5205bda83 -
Trigger Event:
push
-
Statement type:
File details
Details for the file dj_paypal_checkout-0.1.0-py3-none-any.whl.
File metadata
- Download URL: dj_paypal_checkout-0.1.0-py3-none-any.whl
- Upload date:
- Size: 51.9 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 |
8a202b8dfea59b88e6dd8472dadf4931aae099bb362bb4f9637483164efe9bbf
|
|
| MD5 |
ac74612e53f3797b6fdb6244165904fb
|
|
| BLAKE2b-256 |
6b321811ea413cef8d5685c9a67ed8ca71510d5f9a1ab1701d67b74c735511a0
|
Provenance
The following attestation bundles were made for dj_paypal_checkout-0.1.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.1.0-py3-none-any.whl -
Subject digest:
8a202b8dfea59b88e6dd8472dadf4931aae099bb362bb4f9637483164efe9bbf - Sigstore transparency entry: 2272266157
- Sigstore integration time:
-
Permalink:
otto-torino/dj-paypal-checkout@2ee822adee3c0f6f844bf999af87dfd5205bda83 -
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@2ee822adee3c0f6f844bf999af87dfd5205bda83 -
Trigger Event:
push
-
Statement type: