Metadata-Version: 2.4
Name: dj-paypal-checkout
Version: 0.1.0
Summary: A modern, REST-first PayPal integration for Django: Orders v2 checkout, refunds and verified webhooks.
Author-email: Otto <opensource@otto.srl>
License-Expression: MIT
Project-URL: Homepage, https://github.com/otto-torino/dj-paypal-checkout
Project-URL: Repository, https://github.com/otto-torino/dj-paypal-checkout
Project-URL: Documentation, https://dj-paypal-checkout.readthedocs.io
Project-URL: Bug Tracker, https://github.com/otto-torino/dj-paypal-checkout/issues
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Framework :: Django :: 5.2
Classifier: Framework :: Django :: 6.0
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Office/Business :: Financial
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: Django>=5.2
Requires-Dist: httpx>=0.27
Provides-Extra: crypto
Requires-Dist: cryptography>=42.0; extra == "crypto"
Dynamic: license-file

# dj-paypal-checkout

[![CI](https://github.com/otto-torino/dj-paypal-checkout/actions/workflows/ci.yml/badge.svg)](https://github.com/otto-torino/dj-paypal-checkout/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/otto-torino/dj-paypal-checkout/branch/main/graph/badge.svg)](https://codecov.io/gh/otto-torino/dj-paypal-checkout)
[![Documentation](https://readthedocs.org/projects/dj-paypal-checkout/badge/?version=latest)](https://dj-paypal-checkout.readthedocs.io/)
![Django 5.2 | 6.0](https://img.shields.io/badge/Django-5.2%20%7C%206.0-092E20?logo=django&logoColor=white)
![Python 3.11+](https://img.shields.io/badge/Python-3.11%2B-3776AB?logo=python&logoColor=white)
![PayPal REST](https://img.shields.io/badge/PayPal-Orders%20v2-003087?logo=paypal&logoColor=white)

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](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.
- **`Decimal` end to end**, with currency-correct scale (never float).
- **One config entry point**: a single `PAYPAL` settings dict, read only by
  `paypal_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:

```bash
export PAYPAL_CLIENT_ID=...      # sandbox REST app credentials
export PAYPAL_CLIENT_SECRET=...
./run_demo.sh                    # http://127.0.0.1:8000/
```

## Development

```bash
# 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](LICENSE).
