paystack-django 1.2.0__tar.gz → 2.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- paystack_django-2.0.1/CHANGELOG.md +75 -0
- paystack_django-2.0.1/PKG-INFO +642 -0
- paystack_django-2.0.1/README.md +583 -0
- paystack_django-2.0.1/djpaystack/__init__.py +30 -0
- paystack_django-2.0.1/djpaystack/admin.py +77 -0
- {paystack_django-1.2.0 → paystack_django-2.0.1}/djpaystack/api/__init__.py +47 -42
- paystack_django-2.0.1/djpaystack/api/apple_pay.py +24 -0
- paystack_django-2.0.1/djpaystack/api/base.py +120 -0
- paystack_django-2.0.1/djpaystack/api/bulk_charges.py +50 -0
- paystack_django-2.0.1/djpaystack/api/charge.py +75 -0
- {paystack_django-1.2.0 → paystack_django-2.0.1}/djpaystack/api/customers.py +118 -59
- paystack_django-2.0.1/djpaystack/api/dedicated_accounts.py +113 -0
- paystack_django-2.0.1/djpaystack/api/direct_debit.py +39 -0
- paystack_django-2.0.1/djpaystack/api/disputes.py +104 -0
- {paystack_django-1.2.0 → paystack_django-2.0.1}/djpaystack/api/integration.py +5 -4
- paystack_django-2.0.1/djpaystack/api/miscellaneous.py +45 -0
- paystack_django-2.0.1/djpaystack/api/order.py +75 -0
- paystack_django-2.0.1/djpaystack/api/pages.py +67 -0
- paystack_django-2.0.1/djpaystack/api/payment_requests.py +112 -0
- paystack_django-2.0.1/djpaystack/api/plans.py +72 -0
- paystack_django-2.0.1/djpaystack/api/products.py +67 -0
- paystack_django-2.0.1/djpaystack/api/refunds.py +56 -0
- paystack_django-2.0.1/djpaystack/api/settlements.py +35 -0
- {paystack_django-1.2.0 → paystack_django-2.0.1}/djpaystack/api/splits.py +29 -18
- paystack_django-2.0.1/djpaystack/api/storefront.py +80 -0
- paystack_django-2.0.1/djpaystack/api/subaccounts.py +79 -0
- paystack_django-2.0.1/djpaystack/api/subscriptions.py +53 -0
- {paystack_django-1.2.0 → paystack_django-2.0.1}/djpaystack/api/terminal.py +17 -14
- {paystack_django-1.2.0 → paystack_django-2.0.1}/djpaystack/api/transactions.py +48 -53
- paystack_django-2.0.1/djpaystack/api/transfer_control.py +42 -0
- paystack_django-2.0.1/djpaystack/api/transfer_recipients.py +62 -0
- paystack_django-2.0.1/djpaystack/api/transfers.py +81 -0
- paystack_django-2.0.1/djpaystack/api/verification.py +38 -0
- paystack_django-2.0.1/djpaystack/api/virtual_terminal.py +81 -0
- paystack_django-2.0.1/djpaystack/apps.py +14 -0
- {paystack_django-1.2.0 → paystack_django-2.0.1}/djpaystack/client.py +74 -60
- paystack_django-2.0.1/djpaystack/decorators.py +34 -0
- {paystack_django-1.2.0 → paystack_django-2.0.1}/djpaystack/dev/__init__.py +4 -4
- {paystack_django-1.2.0 → paystack_django-2.0.1}/djpaystack/dev/ngrok_tunnel.py +18 -26
- paystack_django-2.0.1/djpaystack/dev/webhook_tester.py +179 -0
- {paystack_django-1.2.0 → paystack_django-2.0.1}/djpaystack/exceptions.py +5 -0
- paystack_django-2.0.1/djpaystack/management/commands/cleanup_paystack_logs.py +88 -0
- {paystack_django-1.2.0 → paystack_django-2.0.1}/djpaystack/management/commands/list_webhook_events.py +27 -24
- paystack_django-2.0.1/djpaystack/management/commands/start_webhook_tunnel.py +86 -0
- paystack_django-2.0.1/djpaystack/management/commands/sync_paystack_data.py +85 -0
- paystack_django-2.0.1/djpaystack/management/commands/test_webhook.py +104 -0
- paystack_django-2.0.1/djpaystack/management/commands/verify_paystack_config.py +32 -0
- {paystack_django-1.2.0 → paystack_django-2.0.1}/djpaystack/middleware.py +5 -6
- paystack_django-2.0.1/djpaystack/migrations/0002_alter_paystacktransfer_status.py +29 -0
- {paystack_django-1.2.0 → paystack_django-2.0.1}/djpaystack/models.py +46 -47
- paystack_django-2.0.1/djpaystack/settings.py +91 -0
- paystack_django-2.0.1/djpaystack/signals.py +20 -0
- paystack_django-2.0.1/djpaystack/tests/conftest.py +65 -0
- paystack_django-2.0.1/djpaystack/tests/settings.py +25 -0
- paystack_django-2.0.1/djpaystack/tests/test_api_compliance.py +192 -0
- {paystack_django-1.2.0 → paystack_django-2.0.1}/djpaystack/tests/test_client.py +39 -42
- paystack_django-2.0.1/djpaystack/tests/test_customers.py +55 -0
- paystack_django-2.0.1/djpaystack/tests/test_models.py +45 -0
- paystack_django-2.0.1/djpaystack/tests/test_new_endpoints.py +324 -0
- paystack_django-2.0.1/djpaystack/tests/test_production_hardening.py +179 -0
- paystack_django-2.0.1/djpaystack/tests/test_security_remediation.py +122 -0
- {paystack_django-1.2.0 → paystack_django-2.0.1}/djpaystack/tests/test_transactions.py +40 -46
- {paystack_django-1.2.0 → paystack_django-2.0.1}/djpaystack/tests/test_utils.py +12 -12
- paystack_django-2.0.1/djpaystack/tests/test_webhooks.py +135 -0
- {paystack_django-1.2.0 → paystack_django-2.0.1}/djpaystack/utils.py +12 -17
- paystack_django-2.0.1/djpaystack/views.py +1 -0
- paystack_django-2.0.1/djpaystack/webhooks/events.py +135 -0
- paystack_django-2.0.1/djpaystack/webhooks/handlers.py +438 -0
- paystack_django-2.0.1/djpaystack/webhooks/urls.py +9 -0
- paystack_django-2.0.1/djpaystack/webhooks/views.py +116 -0
- paystack_django-2.0.1/paystack_django.egg-info/PKG-INFO +642 -0
- {paystack_django-1.2.0 → paystack_django-2.0.1}/paystack_django.egg-info/SOURCES.txt +7 -11
- {paystack_django-1.2.0 → paystack_django-2.0.1}/paystack_django.egg-info/requires.txt +1 -2
- {paystack_django-1.2.0 → paystack_django-2.0.1}/pyproject.toml +9 -10
- paystack_django-1.2.0/CHANGELOG.md +0 -155
- paystack_django-1.2.0/PKG-INFO +0 -409
- paystack_django-1.2.0/README.md +0 -344
- paystack_django-1.2.0/djpaystack/__init__.py +0 -26
- paystack_django-1.2.0/djpaystack/admin.py +0 -69
- paystack_django-1.2.0/djpaystack/api/apple_pay.py +0 -22
- paystack_django-1.2.0/djpaystack/api/base.py +0 -97
- paystack_django-1.2.0/djpaystack/api/bulk_charges.py +0 -39
- paystack_django-1.2.0/djpaystack/api/charge.py +0 -96
- paystack_django-1.2.0/djpaystack/api/dedicated_accounts.py +0 -157
- paystack_django-1.2.0/djpaystack/api/direct_debit.py +0 -104
- paystack_django-1.2.0/djpaystack/api/disputes.py +0 -64
- paystack_django-1.2.0/djpaystack/api/miscellaneous.py +0 -34
- paystack_django-1.2.0/djpaystack/api/pages.py +0 -42
- paystack_django-1.2.0/djpaystack/api/payment_requests.py +0 -68
- paystack_django-1.2.0/djpaystack/api/plans.py +0 -42
- paystack_django-1.2.0/djpaystack/api/products.py +0 -35
- paystack_django-1.2.0/djpaystack/api/refunds.py +0 -82
- paystack_django-1.2.0/djpaystack/api/settlements.py +0 -20
- paystack_django-1.2.0/djpaystack/api/subaccounts.py +0 -46
- paystack_django-1.2.0/djpaystack/api/subscriptions.py +0 -42
- paystack_django-1.2.0/djpaystack/api/transfer_control.py +0 -32
- paystack_django-1.2.0/djpaystack/api/transfer_recipients.py +0 -41
- paystack_django-1.2.0/djpaystack/api/transfers.py +0 -42
- paystack_django-1.2.0/djpaystack/api/verification.py +0 -26
- paystack_django-1.2.0/djpaystack/api/virtual_terminal.py +0 -11
- paystack_django-1.2.0/djpaystack/apps.py +0 -39
- paystack_django-1.2.0/djpaystack/decorators.py +0 -32
- paystack_django-1.2.0/djpaystack/dev/webhook_tester.py +0 -193
- paystack_django-1.2.0/djpaystack/management/commands/cleanup_paystack_logs.py +0 -97
- paystack_django-1.2.0/djpaystack/management/commands/paystack_listen.py +0 -219
- paystack_django-1.2.0/djpaystack/management/commands/paystack_webhook_event.py +0 -422
- paystack_django-1.2.0/djpaystack/management/commands/start_webhook_tunnel.py +0 -95
- paystack_django-1.2.0/djpaystack/management/commands/sync_paystack_data.py +0 -80
- paystack_django-1.2.0/djpaystack/management/commands/test_webhook.py +0 -106
- paystack_django-1.2.0/djpaystack/management/commands/verify_paystack_config.py +0 -35
- paystack_django-1.2.0/djpaystack/migrations/0002_remove_redundant_indexes.py +0 -21
- paystack_django-1.2.0/djpaystack/settings.py +0 -70
- paystack_django-1.2.0/djpaystack/signals.py +0 -43
- paystack_django-1.2.0/djpaystack/tests/conftest.py +0 -86
- paystack_django-1.2.0/djpaystack/tests/settings.py +0 -45
- paystack_django-1.2.0/djpaystack/tests/test_admin.py +0 -85
- paystack_django-1.2.0/djpaystack/tests/test_customers.py +0 -59
- paystack_django-1.2.0/djpaystack/tests/test_decorators.py +0 -81
- paystack_django-1.2.0/djpaystack/tests/test_middleware.py +0 -40
- paystack_django-1.2.0/djpaystack/tests/test_models.py +0 -49
- paystack_django-1.2.0/djpaystack/tests/test_settings.py +0 -94
- paystack_django-1.2.0/djpaystack/tests/test_views.py +0 -93
- paystack_django-1.2.0/djpaystack/tests/test_webhook_handlers.py +0 -304
- paystack_django-1.2.0/djpaystack/tests/test_webhook_security.py +0 -72
- paystack_django-1.2.0/djpaystack/tests/test_webhooks.py +0 -174
- paystack_django-1.2.0/djpaystack/views.py +0 -57
- paystack_django-1.2.0/djpaystack/webhooks/events.py +0 -126
- paystack_django-1.2.0/djpaystack/webhooks/handlers.py +0 -642
- paystack_django-1.2.0/djpaystack/webhooks/urls.py +0 -8
- paystack_django-1.2.0/djpaystack/webhooks/views.py +0 -104
- paystack_django-1.2.0/paystack_django.egg-info/PKG-INFO +0 -409
- paystack_django-1.2.0/setup.py +0 -72
- {paystack_django-1.2.0 → paystack_django-2.0.1}/CONTRIBUTING.md +0 -0
- {paystack_django-1.2.0 → paystack_django-2.0.1}/LICENSE +0 -0
- {paystack_django-1.2.0 → paystack_django-2.0.1}/MANIFEST.in +0 -0
- {paystack_django-1.2.0 → paystack_django-2.0.1}/djpaystack/management/__init__.py +0 -0
- {paystack_django-1.2.0 → paystack_django-2.0.1}/djpaystack/management/commands/__init__.py +0 -0
- {paystack_django-1.2.0 → paystack_django-2.0.1}/djpaystack/migrations/0001_initial.py +0 -0
- {paystack_django-1.2.0 → paystack_django-2.0.1}/djpaystack/migrations/__init__.py +0 -0
- {paystack_django-1.2.0 → paystack_django-2.0.1}/djpaystack/py.typed +0 -0
- {paystack_django-1.2.0 → paystack_django-2.0.1}/djpaystack/tests/__init__.py +0 -0
- {paystack_django-1.2.0 → paystack_django-2.0.1}/djpaystack/webhooks/__init__.py +0 -0
- {paystack_django-1.2.0 → paystack_django-2.0.1}/paystack_django.egg-info/dependency_links.txt +0 -0
- {paystack_django-1.2.0 → paystack_django-2.0.1}/paystack_django.egg-info/top_level.txt +0 -0
- {paystack_django-1.2.0 → paystack_django-2.0.1}/setup.cfg +0 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [2.0.0] - 2026-06-13
|
|
9
|
+
|
|
10
|
+
This is a major release: it completes Paystack API coverage and includes
|
|
11
|
+
behavioural **breaking changes** (see the *Breaking changes* section below).
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- Clients for the full Paystack API surface, including **Virtual Terminal**,
|
|
16
|
+
**Direct Debit**, **Orders**, and **Storefronts**.
|
|
17
|
+
- Customer authorization and direct-debit onboarding: `initialize_authorization`,
|
|
18
|
+
`verify_authorization`, `initialize_direct_debit`,
|
|
19
|
+
`directdebit_activation_charge`, and `fetch_mandate_authorizations`.
|
|
20
|
+
- Additional endpoints: product `delete`, refund `retry_with_customer_details`,
|
|
21
|
+
transfer `export`, dedicated-account `assign`, and balance-ledger pagination.
|
|
22
|
+
- Lazy `iter_all()` iterators on `transactions` and `customers` to stream every
|
|
23
|
+
record across pages with bounded memory.
|
|
24
|
+
- `WEBHOOK_SIGNATURE_REQUIRED` setting (defaults to `True`).
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- **`WEBHOOK_SECRET` now defaults to `SECRET_KEY`.** Paystack signs webhooks with
|
|
29
|
+
the account secret key, so `WEBHOOK_SECRET` is an optional override.
|
|
30
|
+
- Added a `User-Agent: paystack-django/<version>` header to all requests.
|
|
31
|
+
- HTTP 401/403 responses now raise `PaystackAuthenticationError`.
|
|
32
|
+
- Webhook datetime fields (`paid_at`, `next_payment_date`, `transferred_at`) are
|
|
33
|
+
parsed into real `datetime` objects.
|
|
34
|
+
- `sync_paystack_data` honours `--days` and iterates lazily.
|
|
35
|
+
- Consolidated build configuration onto `pyproject.toml`; removed `setup.py` and
|
|
36
|
+
`setup.cfg`. Removed the unused `python-decouple` dependency and raised the
|
|
37
|
+
`requests` floor to `>=2.32.0`.
|
|
38
|
+
- The package is fully formatted with black and isort, and CI now enforces
|
|
39
|
+
linting, formatting, typing, coverage, and dependency/security scanning.
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
|
|
43
|
+
- Webhook signature verification now **fails closed** when no signing key can be
|
|
44
|
+
resolved (previously it accepted unverified webhooks).
|
|
45
|
+
- Non-idempotent requests (`POST`/`PUT`) are no longer automatically retried,
|
|
46
|
+
preventing duplicate charges/transfers on transient errors.
|
|
47
|
+
- Webhook deduplication is now race-safe and database-backed, so concurrent or
|
|
48
|
+
redelivered webhooks are processed exactly once across multiple workers.
|
|
49
|
+
- List endpoints send the correct `from`/`to` date filters (were ignored).
|
|
50
|
+
- Dispute webhook events use their correct `charge.dispute.*` names, so dispute
|
|
51
|
+
handlers and signals fire.
|
|
52
|
+
- Bulk-charge `pause()`/`resume()` use the correct request method and path.
|
|
53
|
+
- `charge.submit_address()` sends `zip_code` (was `zipcode`).
|
|
54
|
+
- `transfers.list()` filters by `recipient` (with `customer` kept as a
|
|
55
|
+
deprecated alias).
|
|
56
|
+
- Transfer reversals are recorded with status `reversed` (was `failed`).
|
|
57
|
+
- `list()` no longer eagerly loads every page into memory; it returns a single
|
|
58
|
+
page (use `iter_all()` to stream all records).
|
|
59
|
+
|
|
60
|
+
### Breaking changes
|
|
61
|
+
|
|
62
|
+
- Webhooks are rejected when no `SECRET_KEY`/`WEBHOOK_SECRET` is configured.
|
|
63
|
+
- `POST`/`PUT` requests are not auto-retried; retry writes yourself with a stable
|
|
64
|
+
`reference`.
|
|
65
|
+
- `list()` returns a single page instead of all pages; use `iter_all()`.
|
|
66
|
+
- `setup.py`/`setup.cfg` removed in favour of `pyproject.toml`.
|
|
67
|
+
|
|
68
|
+
See the documentation for migration guidance.
|
|
69
|
+
|
|
70
|
+
## [1.0.0] - 2024-02-13
|
|
71
|
+
|
|
72
|
+
### Added
|
|
73
|
+
|
|
74
|
+
- Initial release: Django integration for the Paystack payment gateway with
|
|
75
|
+
API clients, models, webhook handling, signals, and management commands.
|
|
@@ -0,0 +1,642 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: paystack-django
|
|
3
|
+
Version: 2.0.1
|
|
4
|
+
Summary: A comprehensive Django integration for Paystack Payment Gateway
|
|
5
|
+
Author-email: Humming Byte <dev@hummingbyte.org>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/HummingByteDev/paystack-django
|
|
8
|
+
Project-URL: Documentation, https://django-paystack.readthedocs.io
|
|
9
|
+
Project-URL: Repository, https://github.com/HummingByteDev/paystack-django.git
|
|
10
|
+
Project-URL: Bug Tracker, https://github.com/HummingByteDev/paystack-django/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/HummingByteDev/paystack-django/blob/main/CHANGELOG.md
|
|
12
|
+
Keywords: django,paystack,payment,payment-gateway,nigerian-payment
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Environment :: Web Environment
|
|
15
|
+
Classifier: Framework :: Django
|
|
16
|
+
Classifier: Framework :: Django :: 3.2
|
|
17
|
+
Classifier: Framework :: Django :: 4.0
|
|
18
|
+
Classifier: Framework :: Django :: 4.1
|
|
19
|
+
Classifier: Framework :: Django :: 4.2
|
|
20
|
+
Classifier: Framework :: Django :: 5.0
|
|
21
|
+
Classifier: Framework :: Django :: 5.2
|
|
22
|
+
Classifier: Framework :: Django :: 6.0
|
|
23
|
+
Classifier: Intended Audience :: Developers
|
|
24
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
25
|
+
Classifier: Natural Language :: English
|
|
26
|
+
Classifier: Operating System :: OS Independent
|
|
27
|
+
Classifier: Programming Language :: Python
|
|
28
|
+
Classifier: Programming Language :: Python :: 3
|
|
29
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
30
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
31
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
32
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
33
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
34
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
35
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
36
|
+
Requires-Python: >=3.8
|
|
37
|
+
Description-Content-Type: text/markdown
|
|
38
|
+
License-File: LICENSE
|
|
39
|
+
Requires-Dist: Django>=3.2
|
|
40
|
+
Requires-Dist: requests>=2.32.0
|
|
41
|
+
Provides-Extra: dev
|
|
42
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
43
|
+
Requires-Dist: pytest-django>=4.5; extra == "dev"
|
|
44
|
+
Requires-Dist: pytest-cov>=3.0; extra == "dev"
|
|
45
|
+
Requires-Dist: black>=22.0; extra == "dev"
|
|
46
|
+
Requires-Dist: flake8>=4.0; extra == "dev"
|
|
47
|
+
Requires-Dist: isort>=5.10; extra == "dev"
|
|
48
|
+
Requires-Dist: mypy>=0.950; extra == "dev"
|
|
49
|
+
Requires-Dist: django-stubs>=1.12.0; extra == "dev"
|
|
50
|
+
Requires-Dist: types-requests>=2.25.0; extra == "dev"
|
|
51
|
+
Requires-Dist: tox>=3.24; extra == "dev"
|
|
52
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
53
|
+
Requires-Dist: bandit>=1.7.0; extra == "dev"
|
|
54
|
+
Provides-Extra: docs
|
|
55
|
+
Requires-Dist: sphinx>=4.5; extra == "docs"
|
|
56
|
+
Requires-Dist: sphinx-rtd-theme>=1.0; extra == "docs"
|
|
57
|
+
Requires-Dist: sphinx-autodoc-typehints>=1.18; extra == "docs"
|
|
58
|
+
Dynamic: license-file
|
|
59
|
+
|
|
60
|
+
# paystack-django
|
|
61
|
+
|
|
62
|
+
A comprehensive Django integration for the **Paystack Payment Gateway**. This package provides a complete, production-ready solution for integrating Paystack payments into your Django applications.
|
|
63
|
+
|
|
64
|
+
[](https://badge.fury.io/py/paystack-django)
|
|
65
|
+
[](https://www.djangoproject.com)
|
|
66
|
+
[](https://www.python.org)
|
|
67
|
+
[](https://opensource.org/licenses/MIT)
|
|
68
|
+
|
|
69
|
+
> **New in 2.0** — full coverage of the Paystack API (including Virtual Terminal,
|
|
70
|
+
> Direct Debit, Orders and Storefronts), memory-safe pagination with lazy
|
|
71
|
+
> `iter_all()` iterators, race-safe webhook deduplication, and a fail-closed
|
|
72
|
+
> webhook verification model. See the [CHANGELOG](CHANGELOG.md) for the full
|
|
73
|
+
> list, including **breaking changes**.
|
|
74
|
+
|
|
75
|
+
## Features
|
|
76
|
+
|
|
77
|
+
- **Full Paystack API Coverage** - Django-native clients for every Paystack API category
|
|
78
|
+
- **Django Models** - Pre-built models for transactions, customers, plans, subscriptions, transfers, and webhook events
|
|
79
|
+
- **Webhook Support** - Built-in webhook handling, HMAC-SHA512 signature verification (fails closed), and race-safe deduplication
|
|
80
|
+
- **Signal Support** - Django signals for payment, transfer, refund, subscription, and dispute events
|
|
81
|
+
- **Memory-safe Pagination** - Single-page `list()` plus lazy `iter_all()` iterators
|
|
82
|
+
- **Type Hints** - Typed public interface with a shipped `py.typed` marker
|
|
83
|
+
- **Comprehensive Documentation** - Detailed docs and examples
|
|
84
|
+
|
|
85
|
+
## Supported Services
|
|
86
|
+
|
|
87
|
+
The package provides Django-native clients for the following Paystack APIs.
|
|
88
|
+
"Full" means every endpoint in that category is implemented.
|
|
89
|
+
|
|
90
|
+
| Category | Status |
|
|
91
|
+
|---|---|
|
|
92
|
+
| Transactions | Full |
|
|
93
|
+
| Transaction Splits | Full |
|
|
94
|
+
| Customers (incl. authorization & direct-debit onboarding) | Full |
|
|
95
|
+
| Plans / Subscriptions | Full |
|
|
96
|
+
| Products | Full |
|
|
97
|
+
| Payment Pages / Payment Requests | Full |
|
|
98
|
+
| Transfers / Transfer Recipients / Transfer Control | Full |
|
|
99
|
+
| Refunds | Full |
|
|
100
|
+
| Disputes | Full |
|
|
101
|
+
| Subaccounts | Full |
|
|
102
|
+
| Dedicated Virtual Accounts | Full |
|
|
103
|
+
| Terminal | Full |
|
|
104
|
+
| Virtual Terminal | Full |
|
|
105
|
+
| Direct Debit | Full |
|
|
106
|
+
| Bulk Charges | Full |
|
|
107
|
+
| Charge | Full |
|
|
108
|
+
| Verification (Bank) | Full |
|
|
109
|
+
| Settlements | Full |
|
|
110
|
+
| Integration | Full |
|
|
111
|
+
| Apple Pay | Full |
|
|
112
|
+
| Orders | Full |
|
|
113
|
+
| Storefronts | Full |
|
|
114
|
+
| Miscellaneous (banks, countries, states) | Full |
|
|
115
|
+
|
|
116
|
+
> Some list endpoints currently support page-based pagination; cursor-based
|
|
117
|
+
> pagination is on the roadmap. See the parity matrix for per-endpoint detail.
|
|
118
|
+
|
|
119
|
+
## Installation
|
|
120
|
+
|
|
121
|
+
Install using pip:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
pip install paystack-django
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Or install from source:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
git clone https://github.com/HummingByteDev/paystack-django.git
|
|
131
|
+
cd django-paystack
|
|
132
|
+
pip install -e .
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Quick Start
|
|
136
|
+
|
|
137
|
+
### 1. Add to Django Settings
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
# settings.py
|
|
141
|
+
|
|
142
|
+
INSTALLED_APPS = [
|
|
143
|
+
# ...
|
|
144
|
+
'djpaystack',
|
|
145
|
+
]
|
|
146
|
+
|
|
147
|
+
PAYSTACK = {
|
|
148
|
+
'SECRET_KEY': 'sk_live_your_secret_key_here',
|
|
149
|
+
'PUBLIC_KEY': 'pk_live_your_public_key_here',
|
|
150
|
+
'ENVIRONMENT': 'production', # or 'test'
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
> Paystack signs webhooks with your account **secret key**, so `WEBHOOK_SECRET`
|
|
155
|
+
> is optional and defaults to `SECRET_KEY`. Webhooks are **rejected** if no
|
|
156
|
+
> signing key can be resolved (fail closed).
|
|
157
|
+
|
|
158
|
+
### 2. Create PaystackClient Instance
|
|
159
|
+
|
|
160
|
+
```python
|
|
161
|
+
from djpaystack import PaystackClient
|
|
162
|
+
|
|
163
|
+
client = PaystackClient()
|
|
164
|
+
|
|
165
|
+
# Initialize a transaction
|
|
166
|
+
response = client.transactions.initialize(
|
|
167
|
+
email='customer@example.com',
|
|
168
|
+
amount=50000, # in kobo (500 NGN)
|
|
169
|
+
reference='unique-reference-123'
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
authorization_url = response['data']['authorization_url']
|
|
173
|
+
print(f"Redirect user to: {authorization_url}")
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### 3. Verify Transaction
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
# After user completes payment
|
|
180
|
+
verified = client.transactions.verify(reference='unique-reference-123')
|
|
181
|
+
|
|
182
|
+
if verified['data']['status'] == 'success':
|
|
183
|
+
print("Payment successful!")
|
|
184
|
+
# Update your database
|
|
185
|
+
else:
|
|
186
|
+
print("Payment failed!")
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### 4. Set Up Webhooks
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
# urls.py
|
|
193
|
+
from django.urls import include, path
|
|
194
|
+
|
|
195
|
+
urlpatterns = [
|
|
196
|
+
# Exposes the webhook endpoint at /paystack/webhook/
|
|
197
|
+
path('paystack/', include('djpaystack.webhooks.urls')),
|
|
198
|
+
]
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Then set the webhook URL (e.g. `https://yoursite.com/paystack/webhook/`) in your
|
|
202
|
+
Paystack dashboard.
|
|
203
|
+
|
|
204
|
+
## Configuration
|
|
205
|
+
|
|
206
|
+
Complete configuration options available in `PAYSTACK` setting:
|
|
207
|
+
|
|
208
|
+
```python
|
|
209
|
+
PAYSTACK = {
|
|
210
|
+
# Required
|
|
211
|
+
'SECRET_KEY': 'your-secret-key', # Required
|
|
212
|
+
'PUBLIC_KEY': 'your-public-key', # Required
|
|
213
|
+
|
|
214
|
+
# Optional
|
|
215
|
+
'BASE_URL': 'https://api.paystack.co', # API base URL
|
|
216
|
+
# Paystack signs webhooks with your SECRET_KEY. Leave WEBHOOK_SECRET unset
|
|
217
|
+
# to use SECRET_KEY automatically; only set it to override.
|
|
218
|
+
'WEBHOOK_SECRET': None,
|
|
219
|
+
'WEBHOOK_SIGNATURE_REQUIRED': True, # reject unsigned webhooks (fail closed)
|
|
220
|
+
'CALLBACK_URL': 'https://yoursite.com/callback/', # Callback URL
|
|
221
|
+
'ENVIRONMENT': 'production', # 'production' or 'test'
|
|
222
|
+
'TIMEOUT': 30, # Request timeout in seconds
|
|
223
|
+
'MAX_RETRIES': 3, # Number of retries
|
|
224
|
+
'VERIFY_SSL': True, # Verify SSL certificates
|
|
225
|
+
'CURRENCY': 'NGN', # Default currency
|
|
226
|
+
'AUTO_VERIFY_TRANSACTIONS': True, # Auto-verify on webhook
|
|
227
|
+
'CACHE_TIMEOUT': 300, # Cache timeout in seconds
|
|
228
|
+
'LOG_REQUESTS': False, # Log API requests
|
|
229
|
+
'LOG_RESPONSES': False, # Log API responses
|
|
230
|
+
'ENABLE_SIGNALS': True, # Enable Django signals
|
|
231
|
+
'ENABLE_MODELS': True, # Enable Django models
|
|
232
|
+
'ALLOWED_WEBHOOK_IPS': [], # Allowed webhook IPs (empty = all)
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Usage Examples
|
|
237
|
+
|
|
238
|
+
### Transactions
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
from djpaystack import PaystackClient
|
|
242
|
+
|
|
243
|
+
client = PaystackClient()
|
|
244
|
+
|
|
245
|
+
# Initialize transaction
|
|
246
|
+
response = client.transactions.initialize(
|
|
247
|
+
email='user@example.com',
|
|
248
|
+
amount=100000,
|
|
249
|
+
reference='unique-ref-001',
|
|
250
|
+
metadata={'order_id': 123}
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
# Verify transaction
|
|
254
|
+
response = client.transactions.verify(reference='unique-ref-001')
|
|
255
|
+
|
|
256
|
+
# List transactions (one page; use iter_all() to stream everything)
|
|
257
|
+
response = client.transactions.list(page=1, per_page=10)
|
|
258
|
+
|
|
259
|
+
# Fetch transaction
|
|
260
|
+
response = client.transactions.fetch(id_or_reference=123456)
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Customers
|
|
264
|
+
|
|
265
|
+
```python
|
|
266
|
+
# Create customer
|
|
267
|
+
response = client.customers.create(
|
|
268
|
+
email='customer@example.com',
|
|
269
|
+
first_name='John',
|
|
270
|
+
last_name='Doe',
|
|
271
|
+
phone='1234567890'
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
# List customers
|
|
275
|
+
response = client.customers.list(page=1, per_page=50)
|
|
276
|
+
|
|
277
|
+
# Fetch customer
|
|
278
|
+
response = client.customers.fetch(email_or_code='CUS_xxxxx')
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Subscriptions
|
|
282
|
+
|
|
283
|
+
```python
|
|
284
|
+
# Create subscription
|
|
285
|
+
response = client.subscriptions.create(
|
|
286
|
+
customer='CUS_xxxxx',
|
|
287
|
+
plan='PLN_xxxxx',
|
|
288
|
+
authorization='AUTH_xxxxx'
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
# Enable subscription
|
|
292
|
+
response = client.subscriptions.enable(
|
|
293
|
+
code='SUB_xxxxx',
|
|
294
|
+
token='tok_xxxxx'
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
# Disable subscription
|
|
298
|
+
response = client.subscriptions.disable(code='SUB_xxxxx')
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### Plans
|
|
302
|
+
|
|
303
|
+
```python
|
|
304
|
+
# Create plan
|
|
305
|
+
response = client.plans.create(
|
|
306
|
+
name='Monthly Plan',
|
|
307
|
+
amount=500000, # 5000 NGN
|
|
308
|
+
interval='monthly',
|
|
309
|
+
description='Premium monthly subscription'
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
# List plans
|
|
313
|
+
response = client.plans.list(page=1)
|
|
314
|
+
|
|
315
|
+
# Fetch plan
|
|
316
|
+
response = client.plans.fetch(id_or_code='PLN_xxxxx')
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Transfers
|
|
320
|
+
|
|
321
|
+
```python
|
|
322
|
+
# Create transfer recipient
|
|
323
|
+
response = client.transfer_recipients.create(
|
|
324
|
+
type='nuban',
|
|
325
|
+
name='John Doe',
|
|
326
|
+
account_number='0000000000',
|
|
327
|
+
bank_code='001'
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
# Initiate transfer
|
|
331
|
+
response = client.transfers.initiate(
|
|
332
|
+
source='balance',
|
|
333
|
+
amount=50000,
|
|
334
|
+
recipient='RCP_xxxxx',
|
|
335
|
+
reference='transfer-001'
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
# Finalize transfer
|
|
339
|
+
response = client.transfers.finalize(transfer_code='TRF_xxxxx', otp='123456')
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### Refunds
|
|
343
|
+
|
|
344
|
+
```python
|
|
345
|
+
# Create refund
|
|
346
|
+
response = client.refunds.create(
|
|
347
|
+
transaction='123456'
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
# List refunds
|
|
351
|
+
response = client.refunds.list(page=1)
|
|
352
|
+
|
|
353
|
+
# Fetch refund
|
|
354
|
+
response = client.refunds.fetch(reference='123456')
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Orders, Storefronts & Virtual Terminal (new in 2.0)
|
|
358
|
+
|
|
359
|
+
```python
|
|
360
|
+
# Create a virtual terminal
|
|
361
|
+
client.virtual_terminal.create(
|
|
362
|
+
name='In-store till',
|
|
363
|
+
destinations=[{'target': '+2348000000000', 'name': 'Sales'}],
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
# Create a storefront and publish it
|
|
367
|
+
sf = client.storefront.create(name='My Shop', slug='my-shop', currency='NGN')
|
|
368
|
+
client.storefront.publish(sf['data']['id'])
|
|
369
|
+
|
|
370
|
+
# Customer direct-debit onboarding
|
|
371
|
+
init = client.customers.initialize_authorization(
|
|
372
|
+
email='customer@example.com', channel='direct_debit',
|
|
373
|
+
)
|
|
374
|
+
client.customers.verify_authorization(init['data']['reference'])
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
## Database Models
|
|
378
|
+
|
|
379
|
+
The package includes Django models for persistence:
|
|
380
|
+
|
|
381
|
+
```python
|
|
382
|
+
from djpaystack.models import (
|
|
383
|
+
PaystackTransaction,
|
|
384
|
+
PaystackCustomer,
|
|
385
|
+
PaystackPlan,
|
|
386
|
+
PaystackSubscription,
|
|
387
|
+
PaystackTransfer,
|
|
388
|
+
PaystackWebhookEvent,
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
# Query transactions
|
|
392
|
+
transactions = PaystackTransaction.objects.filter(status='success')
|
|
393
|
+
|
|
394
|
+
# Transactions by customer
|
|
395
|
+
customer_transactions = PaystackTransaction.objects.filter(
|
|
396
|
+
customer_email='user@example.com'
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
# Inspect stored webhook events
|
|
400
|
+
events = PaystackWebhookEvent.objects.filter(event_type='charge.success')
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
> Persistence is controlled by the `ENABLE_MODELS` setting (default `True`).
|
|
404
|
+
> Webhook handlers populate these models automatically.
|
|
405
|
+
|
|
406
|
+
## Webhooks
|
|
407
|
+
|
|
408
|
+
Handle Paystack webhooks automatically:
|
|
409
|
+
|
|
410
|
+
```python
|
|
411
|
+
# Webhook signals are dispatched automatically as events arrive
|
|
412
|
+
from django.dispatch import receiver
|
|
413
|
+
|
|
414
|
+
from djpaystack.signals import (
|
|
415
|
+
paystack_payment_successful,
|
|
416
|
+
paystack_payment_failed,
|
|
417
|
+
paystack_transfer_successful,
|
|
418
|
+
paystack_refund_processed,
|
|
419
|
+
paystack_dispute_created,
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
@receiver(paystack_payment_successful)
|
|
423
|
+
def on_payment_success(sender, transaction_data, **kwargs):
|
|
424
|
+
print(f"Payment successful: {transaction_data['reference']}")
|
|
425
|
+
# Update your application
|
|
426
|
+
|
|
427
|
+
@receiver(paystack_payment_failed)
|
|
428
|
+
def on_payment_failed(sender, transaction_data, **kwargs):
|
|
429
|
+
print(f"Payment failed: {transaction_data['reference']}")
|
|
430
|
+
# Handle failed payment
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
Available signals: `paystack_payment_successful`, `paystack_payment_failed`,
|
|
434
|
+
`paystack_subscription_created`, `paystack_subscription_cancelled`,
|
|
435
|
+
`paystack_transfer_successful`, `paystack_transfer_failed`,
|
|
436
|
+
`paystack_refund_processed`, `paystack_dispute_created`,
|
|
437
|
+
`paystack_dispute_resolved`. Each receiver is called with a keyword argument
|
|
438
|
+
carrying the event payload (e.g. `transaction_data`, `transfer_data`,
|
|
439
|
+
`refund_data`, `dispute_data`).
|
|
440
|
+
|
|
441
|
+
## Testing
|
|
442
|
+
|
|
443
|
+
Run the test suite:
|
|
444
|
+
|
|
445
|
+
```bash
|
|
446
|
+
pip install -e ".[dev]"
|
|
447
|
+
pytest
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
With coverage:
|
|
451
|
+
|
|
452
|
+
```bash
|
|
453
|
+
pytest --cov=djpaystack
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
Run tests across Python versions:
|
|
457
|
+
|
|
458
|
+
```bash
|
|
459
|
+
tox
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
## Django Compatibility
|
|
463
|
+
|
|
464
|
+
The 2.x line is tested against the current and LTS Django releases:
|
|
465
|
+
|
|
466
|
+
| Package Version | Django 4.2 (LTS) | Django 5.2 (LTS) | Django 6.0 |
|
|
467
|
+
| --------------- | ---------------- | ---------------- | ---------- |
|
|
468
|
+
| 2.0.x | ✅ | ✅ | ✅ |
|
|
469
|
+
|
|
470
|
+
## Python Compatibility
|
|
471
|
+
|
|
472
|
+
- Python 3.8
|
|
473
|
+
- Python 3.9
|
|
474
|
+
- Python 3.10
|
|
475
|
+
- Python 3.11
|
|
476
|
+
- Python 3.12
|
|
477
|
+
- Python 3.13
|
|
478
|
+
|
|
479
|
+
## Environment Variables
|
|
480
|
+
|
|
481
|
+
You can also configure using environment variables:
|
|
482
|
+
|
|
483
|
+
```bash
|
|
484
|
+
PAYSTACK_SECRET_KEY=sk_live_xxx
|
|
485
|
+
PAYSTACK_PUBLIC_KEY=pk_live_xxx
|
|
486
|
+
# Webhooks are signed with your secret key; use the same sk_... value here.
|
|
487
|
+
PAYSTACK_WEBHOOK_SECRET=sk_live_xxx
|
|
488
|
+
PAYSTACK_ENVIRONMENT=production
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
Load them however you prefer — for example with the standard library:
|
|
492
|
+
|
|
493
|
+
```python
|
|
494
|
+
import os
|
|
495
|
+
|
|
496
|
+
PAYSTACK = {
|
|
497
|
+
'SECRET_KEY': os.environ['PAYSTACK_SECRET_KEY'],
|
|
498
|
+
'PUBLIC_KEY': os.environ['PAYSTACK_PUBLIC_KEY'],
|
|
499
|
+
'ENVIRONMENT': os.environ.get('PAYSTACK_ENVIRONMENT', 'test'),
|
|
500
|
+
}
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
> `python-decouple` is **not** a dependency of this package. If you prefer
|
|
504
|
+
> `decouple.config(...)`, install it in your own project.
|
|
505
|
+
|
|
506
|
+
## Error Handling
|
|
507
|
+
|
|
508
|
+
The package provides specific exception classes:
|
|
509
|
+
|
|
510
|
+
```python
|
|
511
|
+
from djpaystack.exceptions import (
|
|
512
|
+
PaystackError,
|
|
513
|
+
PaystackAPIError,
|
|
514
|
+
PaystackValidationError,
|
|
515
|
+
PaystackAuthenticationError,
|
|
516
|
+
PaystackNetworkError,
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
try:
|
|
520
|
+
client.transactions.verify(reference='ref-123')
|
|
521
|
+
except PaystackAuthenticationError:
|
|
522
|
+
print("Invalid API credentials")
|
|
523
|
+
except PaystackNetworkError:
|
|
524
|
+
print("Network error occurred")
|
|
525
|
+
except PaystackAPIError as e:
|
|
526
|
+
print(f"API error: {e}")
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
## Pagination
|
|
530
|
+
|
|
531
|
+
`list()` returns a **single page** (the first by default) and preserves
|
|
532
|
+
Paystack's `meta` block, so you control how much you fetch:
|
|
533
|
+
|
|
534
|
+
```python
|
|
535
|
+
response = client.transactions.list(
|
|
536
|
+
page=1,
|
|
537
|
+
per_page=50,
|
|
538
|
+
from_date='2024-01-01',
|
|
539
|
+
to_date='2024-12-31',
|
|
540
|
+
status='success',
|
|
541
|
+
)
|
|
542
|
+
|
|
543
|
+
transactions = response['data'] # this page's records
|
|
544
|
+
meta = response['meta'] # {'page', 'pageCount', 'total', ...}
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
To stream **every** record across all pages without loading them all into
|
|
548
|
+
memory, use the lazy iterator:
|
|
549
|
+
|
|
550
|
+
```python
|
|
551
|
+
for txn in client.transactions.iter_all(status='success', from_date='2024-01-01'):
|
|
552
|
+
process(txn) # one record at a time; pages fetched on demand
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
> **Upgrading from 1.x?** Previously `list()` eagerly fetched *all* pages.
|
|
556
|
+
> It now returns one page — switch full scans to `iter_all()`. See the
|
|
557
|
+
> [CHANGELOG](CHANGELOG.md) for the full list of breaking changes.
|
|
558
|
+
|
|
559
|
+
## Logging
|
|
560
|
+
|
|
561
|
+
Enable logging to debug API interactions:
|
|
562
|
+
|
|
563
|
+
```python
|
|
564
|
+
import logging
|
|
565
|
+
|
|
566
|
+
# In settings.py
|
|
567
|
+
PAYSTACK = {
|
|
568
|
+
'LOG_REQUESTS': True,
|
|
569
|
+
'LOG_RESPONSES': True,
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
# Configure logging
|
|
573
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
574
|
+
logger = logging.getLogger('djpaystack')
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
## Security
|
|
578
|
+
|
|
579
|
+
### Environment Variables
|
|
580
|
+
|
|
581
|
+
Never hardcode secrets — load them from the environment:
|
|
582
|
+
|
|
583
|
+
```python
|
|
584
|
+
import os
|
|
585
|
+
|
|
586
|
+
PAYSTACK = {
|
|
587
|
+
'SECRET_KEY': os.environ['PAYSTACK_SECRET_KEY'],
|
|
588
|
+
'PUBLIC_KEY': os.environ['PAYSTACK_PUBLIC_KEY'],
|
|
589
|
+
}
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
### Webhook Verification
|
|
593
|
+
|
|
594
|
+
The built-in `PaystackWebhookView` verifies the HMAC-SHA512 signature on every
|
|
595
|
+
request and **rejects** anything it cannot verify (fail closed), so you normally
|
|
596
|
+
don't need to verify manually. If you handle webhooks yourself, use the helper:
|
|
597
|
+
|
|
598
|
+
```python
|
|
599
|
+
from djpaystack.utils import verify_webhook_signature
|
|
600
|
+
|
|
601
|
+
is_valid = verify_webhook_signature(
|
|
602
|
+
request.body, # payload (bytes)
|
|
603
|
+
request.headers.get('X-Paystack-Signature', ''), # signature
|
|
604
|
+
settings.PAYSTACK['SECRET_KEY'], # secret (the signing key)
|
|
605
|
+
)
|
|
606
|
+
|
|
607
|
+
if not is_valid:
|
|
608
|
+
return JsonResponse({'status': 'invalid'}, status=403)
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
## Contributing
|
|
612
|
+
|
|
613
|
+
We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details.
|
|
614
|
+
|
|
615
|
+
## Support
|
|
616
|
+
|
|
617
|
+
- 📚 [Full Documentation](https://paystack-django.readthedocs.io/)
|
|
618
|
+
- 🐛 [Report Issues](https://github.com/HummingByteDev/paystack-django/issues)
|
|
619
|
+
- 💬 [Discussions](https://github.com/HummingByteDev/paystack-django/discussions)
|
|
620
|
+
- 📧 [Email Support](mailto:dev@hummingbyte.org)
|
|
621
|
+
|
|
622
|
+
## Changelog
|
|
623
|
+
|
|
624
|
+
See [CHANGELOG.md](CHANGELOG.md) for detailed release notes.
|
|
625
|
+
|
|
626
|
+
## License
|
|
627
|
+
|
|
628
|
+
This project is licensed under the MIT License - see [LICENSE](LICENSE) file for details.
|
|
629
|
+
|
|
630
|
+
## Acknowledgments
|
|
631
|
+
|
|
632
|
+
- [Paystack](https://paystack.com) for the excellent payment gateway
|
|
633
|
+
- Django community for the amazing framework
|
|
634
|
+
- All contributors and users of this package
|
|
635
|
+
|
|
636
|
+
## Disclaimer
|
|
637
|
+
|
|
638
|
+
This package is not affiliated with or endorsed by Paystack. It is maintained by Humming Byte as a community contribution.
|
|
639
|
+
|
|
640
|
+
---
|
|
641
|
+
|
|
642
|
+
**Made with ❤️ by [Humming Byte](https://hummingbyte.org)**
|