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