extensible-django-commerce 1.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.
- extensible_django_commerce-1.0.0/PKG-INFO +276 -0
- extensible_django_commerce-1.0.0/README.md +242 -0
- extensible_django_commerce-1.0.0/pyproject.toml +77 -0
- extensible_django_commerce-1.0.0/setup.cfg +4 -0
- extensible_django_commerce-1.0.0/src/django_commerce/__init__.py +0 -0
- extensible_django_commerce-1.0.0/src/django_commerce/admin.py +1 -0
- extensible_django_commerce-1.0.0/src/django_commerce/apps.py +6 -0
- extensible_django_commerce-1.0.0/src/django_commerce/cart/__init__.py +0 -0
- extensible_django_commerce-1.0.0/src/django_commerce/cart/admin.py +1 -0
- extensible_django_commerce-1.0.0/src/django_commerce/cart/apps.py +6 -0
- extensible_django_commerce-1.0.0/src/django_commerce/cart/migrations/0001_initial.py +53 -0
- extensible_django_commerce-1.0.0/src/django_commerce/cart/migrations/0002_cart_active_cartitem__final_price_cartitem_active_and_more.py +37 -0
- extensible_django_commerce-1.0.0/src/django_commerce/cart/migrations/0003_remove_cart_user.py +17 -0
- extensible_django_commerce-1.0.0/src/django_commerce/cart/migrations/__init__.py +0 -0
- extensible_django_commerce-1.0.0/src/django_commerce/cart/models.py +86 -0
- extensible_django_commerce-1.0.0/src/django_commerce/cart/tests.py +1 -0
- extensible_django_commerce-1.0.0/src/django_commerce/cart/views.py +1 -0
- extensible_django_commerce-1.0.0/src/django_commerce/models.py +2 -0
- extensible_django_commerce-1.0.0/src/django_commerce/offsite_payment_gateway/__init__.py +0 -0
- extensible_django_commerce-1.0.0/src/django_commerce/offsite_payment_gateway/admin.py +1 -0
- extensible_django_commerce-1.0.0/src/django_commerce/offsite_payment_gateway/apps.py +11 -0
- extensible_django_commerce-1.0.0/src/django_commerce/offsite_payment_gateway/migrations/0001_initial.py +41 -0
- extensible_django_commerce-1.0.0/src/django_commerce/offsite_payment_gateway/migrations/__init__.py +0 -0
- extensible_django_commerce-1.0.0/src/django_commerce/offsite_payment_gateway/models.py +7 -0
- extensible_django_commerce-1.0.0/src/django_commerce/offsite_payment_gateway/plugins.py +35 -0
- extensible_django_commerce-1.0.0/src/django_commerce/offsite_payment_gateway/tests.py +1 -0
- extensible_django_commerce-1.0.0/src/django_commerce/offsite_payment_gateway/urls.py +7 -0
- extensible_django_commerce-1.0.0/src/django_commerce/offsite_payment_gateway/views.py +33 -0
- extensible_django_commerce-1.0.0/src/django_commerce/order/__init__.py +0 -0
- extensible_django_commerce-1.0.0/src/django_commerce/order/admin.py +1 -0
- extensible_django_commerce-1.0.0/src/django_commerce/order/apps.py +6 -0
- extensible_django_commerce-1.0.0/src/django_commerce/order/migrations/0001_initial.py +47 -0
- extensible_django_commerce-1.0.0/src/django_commerce/order/migrations/0002_remove_order_cart_items_order_cart_and_more.py +50 -0
- extensible_django_commerce-1.0.0/src/django_commerce/order/migrations/__init__.py +0 -0
- extensible_django_commerce-1.0.0/src/django_commerce/order/models.py +84 -0
- extensible_django_commerce-1.0.0/src/django_commerce/order/signals.py +5 -0
- extensible_django_commerce-1.0.0/src/django_commerce/order/tests.py +1 -0
- extensible_django_commerce-1.0.0/src/django_commerce/order/views.py +1 -0
- extensible_django_commerce-1.0.0/src/django_commerce/payment/__init__.py +0 -0
- extensible_django_commerce-1.0.0/src/django_commerce/payment/admin.py +1 -0
- extensible_django_commerce-1.0.0/src/django_commerce/payment/apps.py +11 -0
- extensible_django_commerce-1.0.0/src/django_commerce/payment/models.py +1 -0
- extensible_django_commerce-1.0.0/src/django_commerce/payment/plugins.py +11 -0
- extensible_django_commerce-1.0.0/src/django_commerce/payment/tests.py +1 -0
- extensible_django_commerce-1.0.0/src/django_commerce/payment/views.py +1 -0
- extensible_django_commerce-1.0.0/src/django_commerce/product/__init__.py +0 -0
- extensible_django_commerce-1.0.0/src/django_commerce/product/admin.py +1 -0
- extensible_django_commerce-1.0.0/src/django_commerce/product/apps.py +6 -0
- extensible_django_commerce-1.0.0/src/django_commerce/product/migrations/0001_initial.py +21 -0
- extensible_django_commerce-1.0.0/src/django_commerce/product/migrations/__init__.py +0 -0
- extensible_django_commerce-1.0.0/src/django_commerce/product/models.py +7 -0
- extensible_django_commerce-1.0.0/src/django_commerce/product/tests.py +1 -0
- extensible_django_commerce-1.0.0/src/django_commerce/product/views.py +1 -0
- extensible_django_commerce-1.0.0/src/django_commerce/tests.py +1 -0
- extensible_django_commerce-1.0.0/src/django_commerce/transaction/__init__.py +0 -0
- extensible_django_commerce-1.0.0/src/django_commerce/transaction/admin.py +1 -0
- extensible_django_commerce-1.0.0/src/django_commerce/transaction/apps.py +6 -0
- extensible_django_commerce-1.0.0/src/django_commerce/transaction/migrations/0001_initial.py +43 -0
- extensible_django_commerce-1.0.0/src/django_commerce/transaction/migrations/0002_remove_transaction_plugin_remove_transaction_result_and_more.py +30 -0
- extensible_django_commerce-1.0.0/src/django_commerce/transaction/migrations/__init__.py +0 -0
- extensible_django_commerce-1.0.0/src/django_commerce/transaction/models.py +19 -0
- extensible_django_commerce-1.0.0/src/django_commerce/transaction/tests.py +1 -0
- extensible_django_commerce-1.0.0/src/django_commerce/transaction/views.py +1 -0
- extensible_django_commerce-1.0.0/src/django_commerce/views.py +1 -0
- extensible_django_commerce-1.0.0/src/extensible_django_commerce.egg-info/PKG-INFO +276 -0
- extensible_django_commerce-1.0.0/src/extensible_django_commerce.egg-info/SOURCES.txt +67 -0
- extensible_django_commerce-1.0.0/src/extensible_django_commerce.egg-info/dependency_links.txt +1 -0
- extensible_django_commerce-1.0.0/src/extensible_django_commerce.egg-info/requires.txt +10 -0
- extensible_django_commerce-1.0.0/src/extensible_django_commerce.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: extensible-django-commerce
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Basic yet highly extensible requirements for commerce in django.
|
|
5
|
+
Author-email: Alireza Tabatabaeian <alireza.tabatabaeian@gmail.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/Alireza-Tabatabaeian/django-commerce
|
|
7
|
+
Project-URL: Issues, https://github.com/Alireza-Tabatabaeian/django-commerce/issues
|
|
8
|
+
Keywords: django,commerce,cart,offsite payment gateway,extensibility
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Environment :: Web Environment
|
|
11
|
+
Classifier: Framework :: Django
|
|
12
|
+
Classifier: Framework :: Django :: 4.2
|
|
13
|
+
Classifier: Framework :: Django :: 5.0
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
22
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
Requires-Dist: Django>=4.2
|
|
26
|
+
Requires-Dist: django-plugin-system>=2.0.8
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest-django>=4.8.0; extra == "dev"
|
|
30
|
+
Requires-Dist: mypy>=1.8; extra == "dev"
|
|
31
|
+
Requires-Dist: django-stubs>=5.0.0; extra == "dev"
|
|
32
|
+
Requires-Dist: black>=24.0.0; extra == "dev"
|
|
33
|
+
Requires-Dist: ruff>=0.5.0; extra == "dev"
|
|
34
|
+
|
|
35
|
+
# Django Commerce
|
|
36
|
+
|
|
37
|
+
A lightweight and extensible commerce foundation for Django projects.
|
|
38
|
+
|
|
39
|
+
`django-commerce` provides reusable commerce primitives such as products, carts, orders, transactions, and pluggable offsite payment gateways — designed to integrate cleanly with modular Django architectures and plugin-based systems.
|
|
40
|
+
|
|
41
|
+
This package is built on top of [django-plugin-system](https://github.com/Alireza-Tabatabaeian/django-plugin-system).
|
|
42
|
+
|
|
43
|
+
CAUTION: The package has been changed signifactly and README does not guide well right now. Will be modified in near future.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
# Features
|
|
48
|
+
|
|
49
|
+
* Base product abstraction
|
|
50
|
+
* Shopping cart system
|
|
51
|
+
* Order management
|
|
52
|
+
* Transaction tracking
|
|
53
|
+
* Extensible offsite payment gateway architecture
|
|
54
|
+
* Plugin-based payment providers
|
|
55
|
+
* Designed for reusable Django applications
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
# Installation
|
|
60
|
+
|
|
61
|
+
Install using pip:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pip install django-commerce
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Add applications to your `INSTALLED_APPS`:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
INSTALLED_APPS = [
|
|
71
|
+
...
|
|
72
|
+
|
|
73
|
+
'django_plugin_system',
|
|
74
|
+
|
|
75
|
+
'django_commerce.product',
|
|
76
|
+
'django_commerce.cart',
|
|
77
|
+
'django_commerce.order',
|
|
78
|
+
'django_commerce.transaction',
|
|
79
|
+
'django_commerce.offsite_payment_gateway',
|
|
80
|
+
]
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Run migrations:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
python manage.py migrate
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
# Package Structure
|
|
92
|
+
|
|
93
|
+
```text
|
|
94
|
+
django_commerce/
|
|
95
|
+
├── product/
|
|
96
|
+
├── cart/
|
|
97
|
+
├── order/
|
|
98
|
+
├── transaction/
|
|
99
|
+
└── offsite_payment_gateway/
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
# Base Product
|
|
105
|
+
|
|
106
|
+
The package provides a reusable `BaseProduct` model abstraction that can be extended in your own project.
|
|
107
|
+
|
|
108
|
+
Example:
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
from django.db import models
|
|
112
|
+
from django_commerce.product.models import BaseProduct
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class Product(BaseProduct):
|
|
116
|
+
description = models.TextField()
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
# Offsite Payment Gateway
|
|
122
|
+
|
|
123
|
+
The package includes an abstract offsite payment gateway system that allows developers to integrate external payment providers such as:
|
|
124
|
+
|
|
125
|
+
* Zarinpal
|
|
126
|
+
* Stripe Checkout
|
|
127
|
+
* PayPal
|
|
128
|
+
* Mollie
|
|
129
|
+
* Any redirect-based payment provider
|
|
130
|
+
|
|
131
|
+
To create a custom gateway, extend `AbstractOffsitePaymentGateway`.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
# Creating a Payment Gateway
|
|
136
|
+
|
|
137
|
+
Example:
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
from django.http import HttpRequest
|
|
141
|
+
|
|
142
|
+
from django_commerce.offsite_payment_gateway.models import (
|
|
143
|
+
AbstractOffsitePaymentGateway
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class ZarinpalGateway(AbstractOffsitePaymentGateway):
|
|
148
|
+
name = 'zarinpal'
|
|
149
|
+
_gateway_base_url = 'https://sandbox.zarinpal.com'
|
|
150
|
+
|
|
151
|
+
def get_payment_gateway(self, order):
|
|
152
|
+
transaction = self.create_transaction(order)
|
|
153
|
+
|
|
154
|
+
callback = self.callback_url(transaction)
|
|
155
|
+
|
|
156
|
+
return f'{self._gateway_base_url}/start/{transaction.id}?callback={callback}'
|
|
157
|
+
|
|
158
|
+
def verify_payment(self, transaction, callback_request: HttpRequest) -> bool:
|
|
159
|
+
authority = callback_request.GET.get('Authority')
|
|
160
|
+
|
|
161
|
+
if not authority:
|
|
162
|
+
return False
|
|
163
|
+
|
|
164
|
+
# Verify payment using provider API
|
|
165
|
+
# ...
|
|
166
|
+
|
|
167
|
+
transaction.is_verified = True
|
|
168
|
+
transaction.save()
|
|
169
|
+
|
|
170
|
+
return True
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
# Registering Gateway Plugins
|
|
176
|
+
|
|
177
|
+
Gateways are designed to work with the plugin system.
|
|
178
|
+
|
|
179
|
+
Example registration:
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
from django_plugin_system import register_plugin
|
|
183
|
+
|
|
184
|
+
from .gateway import ZarinpalGateway
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
register_plugin(
|
|
188
|
+
manager='django_commerce.offsite_payment_gateway',
|
|
189
|
+
title='Zarinpal Gateway',
|
|
190
|
+
plugin=ZarinpalGateway,
|
|
191
|
+
)
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
# Payment Callback View
|
|
197
|
+
|
|
198
|
+
The package provides a callback endpoint handler for payment verification.
|
|
199
|
+
|
|
200
|
+
Example URL configuration:
|
|
201
|
+
|
|
202
|
+
```python
|
|
203
|
+
from django.urls import path
|
|
204
|
+
|
|
205
|
+
from django_commerce.offsite_payment_gateway.views import callback_view
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
urlpatterns = [
|
|
209
|
+
path(
|
|
210
|
+
'payment-callback/<int:transaction_id>',
|
|
211
|
+
callback_view,
|
|
212
|
+
name='payment_callback'
|
|
213
|
+
),
|
|
214
|
+
]
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
After the payment provider redirects the user back to your application, the callback view:
|
|
218
|
+
|
|
219
|
+
1. Loads the related transaction
|
|
220
|
+
2. Resolves the payment gateway plugin
|
|
221
|
+
3. Calls `verify_payment`
|
|
222
|
+
4. Verifies and finalizes the transaction
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
# Transactions
|
|
227
|
+
|
|
228
|
+
Each payment attempt creates a `Transaction` object associated with:
|
|
229
|
+
|
|
230
|
+
* Order
|
|
231
|
+
* Payment plugin
|
|
232
|
+
* Verification state
|
|
233
|
+
|
|
234
|
+
This makes payment tracking and auditing easier across multiple providers.
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
# Design Goals
|
|
239
|
+
|
|
240
|
+
This package focuses on:
|
|
241
|
+
|
|
242
|
+
* Reusability
|
|
243
|
+
* Extensibility
|
|
244
|
+
* Minimal assumptions
|
|
245
|
+
* Plugin-oriented architecture
|
|
246
|
+
* Separation of concerns
|
|
247
|
+
|
|
248
|
+
It is intended to act as a commerce foundation rather than a complete e-commerce platform.
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
# Development
|
|
253
|
+
|
|
254
|
+
Clone the repository:
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
git clone <repository-url>
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Install in editable mode:
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
pip install -e .
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Run migrations from the development project:
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
python manage.py migrate
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
# License
|
|
275
|
+
|
|
276
|
+
MIT License
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
# Django Commerce
|
|
2
|
+
|
|
3
|
+
A lightweight and extensible commerce foundation for Django projects.
|
|
4
|
+
|
|
5
|
+
`django-commerce` provides reusable commerce primitives such as products, carts, orders, transactions, and pluggable offsite payment gateways — designed to integrate cleanly with modular Django architectures and plugin-based systems.
|
|
6
|
+
|
|
7
|
+
This package is built on top of [django-plugin-system](https://github.com/Alireza-Tabatabaeian/django-plugin-system).
|
|
8
|
+
|
|
9
|
+
CAUTION: The package has been changed signifactly and README does not guide well right now. Will be modified in near future.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Features
|
|
14
|
+
|
|
15
|
+
* Base product abstraction
|
|
16
|
+
* Shopping cart system
|
|
17
|
+
* Order management
|
|
18
|
+
* Transaction tracking
|
|
19
|
+
* Extensible offsite payment gateway architecture
|
|
20
|
+
* Plugin-based payment providers
|
|
21
|
+
* Designed for reusable Django applications
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
# Installation
|
|
26
|
+
|
|
27
|
+
Install using pip:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install django-commerce
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Add applications to your `INSTALLED_APPS`:
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
INSTALLED_APPS = [
|
|
37
|
+
...
|
|
38
|
+
|
|
39
|
+
'django_plugin_system',
|
|
40
|
+
|
|
41
|
+
'django_commerce.product',
|
|
42
|
+
'django_commerce.cart',
|
|
43
|
+
'django_commerce.order',
|
|
44
|
+
'django_commerce.transaction',
|
|
45
|
+
'django_commerce.offsite_payment_gateway',
|
|
46
|
+
]
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Run migrations:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
python manage.py migrate
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
# Package Structure
|
|
58
|
+
|
|
59
|
+
```text
|
|
60
|
+
django_commerce/
|
|
61
|
+
├── product/
|
|
62
|
+
├── cart/
|
|
63
|
+
├── order/
|
|
64
|
+
├── transaction/
|
|
65
|
+
└── offsite_payment_gateway/
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
# Base Product
|
|
71
|
+
|
|
72
|
+
The package provides a reusable `BaseProduct` model abstraction that can be extended in your own project.
|
|
73
|
+
|
|
74
|
+
Example:
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from django.db import models
|
|
78
|
+
from django_commerce.product.models import BaseProduct
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class Product(BaseProduct):
|
|
82
|
+
description = models.TextField()
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
# Offsite Payment Gateway
|
|
88
|
+
|
|
89
|
+
The package includes an abstract offsite payment gateway system that allows developers to integrate external payment providers such as:
|
|
90
|
+
|
|
91
|
+
* Zarinpal
|
|
92
|
+
* Stripe Checkout
|
|
93
|
+
* PayPal
|
|
94
|
+
* Mollie
|
|
95
|
+
* Any redirect-based payment provider
|
|
96
|
+
|
|
97
|
+
To create a custom gateway, extend `AbstractOffsitePaymentGateway`.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
# Creating a Payment Gateway
|
|
102
|
+
|
|
103
|
+
Example:
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
from django.http import HttpRequest
|
|
107
|
+
|
|
108
|
+
from django_commerce.offsite_payment_gateway.models import (
|
|
109
|
+
AbstractOffsitePaymentGateway
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class ZarinpalGateway(AbstractOffsitePaymentGateway):
|
|
114
|
+
name = 'zarinpal'
|
|
115
|
+
_gateway_base_url = 'https://sandbox.zarinpal.com'
|
|
116
|
+
|
|
117
|
+
def get_payment_gateway(self, order):
|
|
118
|
+
transaction = self.create_transaction(order)
|
|
119
|
+
|
|
120
|
+
callback = self.callback_url(transaction)
|
|
121
|
+
|
|
122
|
+
return f'{self._gateway_base_url}/start/{transaction.id}?callback={callback}'
|
|
123
|
+
|
|
124
|
+
def verify_payment(self, transaction, callback_request: HttpRequest) -> bool:
|
|
125
|
+
authority = callback_request.GET.get('Authority')
|
|
126
|
+
|
|
127
|
+
if not authority:
|
|
128
|
+
return False
|
|
129
|
+
|
|
130
|
+
# Verify payment using provider API
|
|
131
|
+
# ...
|
|
132
|
+
|
|
133
|
+
transaction.is_verified = True
|
|
134
|
+
transaction.save()
|
|
135
|
+
|
|
136
|
+
return True
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
# Registering Gateway Plugins
|
|
142
|
+
|
|
143
|
+
Gateways are designed to work with the plugin system.
|
|
144
|
+
|
|
145
|
+
Example registration:
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
from django_plugin_system import register_plugin
|
|
149
|
+
|
|
150
|
+
from .gateway import ZarinpalGateway
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
register_plugin(
|
|
154
|
+
manager='django_commerce.offsite_payment_gateway',
|
|
155
|
+
title='Zarinpal Gateway',
|
|
156
|
+
plugin=ZarinpalGateway,
|
|
157
|
+
)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
# Payment Callback View
|
|
163
|
+
|
|
164
|
+
The package provides a callback endpoint handler for payment verification.
|
|
165
|
+
|
|
166
|
+
Example URL configuration:
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
from django.urls import path
|
|
170
|
+
|
|
171
|
+
from django_commerce.offsite_payment_gateway.views import callback_view
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
urlpatterns = [
|
|
175
|
+
path(
|
|
176
|
+
'payment-callback/<int:transaction_id>',
|
|
177
|
+
callback_view,
|
|
178
|
+
name='payment_callback'
|
|
179
|
+
),
|
|
180
|
+
]
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
After the payment provider redirects the user back to your application, the callback view:
|
|
184
|
+
|
|
185
|
+
1. Loads the related transaction
|
|
186
|
+
2. Resolves the payment gateway plugin
|
|
187
|
+
3. Calls `verify_payment`
|
|
188
|
+
4. Verifies and finalizes the transaction
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
# Transactions
|
|
193
|
+
|
|
194
|
+
Each payment attempt creates a `Transaction` object associated with:
|
|
195
|
+
|
|
196
|
+
* Order
|
|
197
|
+
* Payment plugin
|
|
198
|
+
* Verification state
|
|
199
|
+
|
|
200
|
+
This makes payment tracking and auditing easier across multiple providers.
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
# Design Goals
|
|
205
|
+
|
|
206
|
+
This package focuses on:
|
|
207
|
+
|
|
208
|
+
* Reusability
|
|
209
|
+
* Extensibility
|
|
210
|
+
* Minimal assumptions
|
|
211
|
+
* Plugin-oriented architecture
|
|
212
|
+
* Separation of concerns
|
|
213
|
+
|
|
214
|
+
It is intended to act as a commerce foundation rather than a complete e-commerce platform.
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
# Development
|
|
219
|
+
|
|
220
|
+
Clone the repository:
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
git clone <repository-url>
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Install in editable mode:
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
pip install -e .
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Run migrations from the development project:
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
python manage.py migrate
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
# License
|
|
241
|
+
|
|
242
|
+
MIT License
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools >= 77.0.3"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "extensible-django-commerce"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Basic yet highly extensible requirements for commerce in django."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license-files = ["LICEN[CS]E*"]
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Alireza Tabatabaeian", email = "alireza.tabatabaeian@gmail.com" }
|
|
14
|
+
]
|
|
15
|
+
keywords = ["django", "commerce", "cart", "offsite payment gateway", "extensibility"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Environment :: Web Environment",
|
|
19
|
+
"Framework :: Django",
|
|
20
|
+
"Framework :: Django :: 4.2",
|
|
21
|
+
"Framework :: Django :: 5.0",
|
|
22
|
+
"Intended Audience :: Developers",
|
|
23
|
+
"License :: OSI Approved :: MIT License",
|
|
24
|
+
"Programming Language :: Python",
|
|
25
|
+
"Programming Language :: Python :: 3",
|
|
26
|
+
"Programming Language :: Python :: 3.10",
|
|
27
|
+
"Programming Language :: Python :: 3.11",
|
|
28
|
+
"Programming Language :: Python :: 3.12",
|
|
29
|
+
"Topic :: Software Development :: Libraries :: Application Frameworks",
|
|
30
|
+
"Topic :: Internet :: WWW/HTTP",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
dependencies = [
|
|
34
|
+
"Django>=4.2",
|
|
35
|
+
"django-plugin-system>=2.0.8",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[project.urls]
|
|
39
|
+
Homepage = "https://github.com/Alireza-Tabatabaeian/django-commerce"
|
|
40
|
+
Issues = "https://github.com/Alireza-Tabatabaeian/django-commerce/issues"
|
|
41
|
+
|
|
42
|
+
[tool.setuptools]
|
|
43
|
+
include-package-data = true
|
|
44
|
+
|
|
45
|
+
[tool.setuptools.packages.find]
|
|
46
|
+
where = ["src"]
|
|
47
|
+
include = ["django_commerce", "django_commerce.*"]
|
|
48
|
+
|
|
49
|
+
[tool.setuptools.package-data]
|
|
50
|
+
# If you add migrations/templates/static later, keep them here.
|
|
51
|
+
django_plugin_system = ["py.typed"]
|
|
52
|
+
|
|
53
|
+
[tool.mypy]
|
|
54
|
+
python_version = "3.10"
|
|
55
|
+
warn_unused_ignores = true
|
|
56
|
+
ignore_missing_imports = true
|
|
57
|
+
no_implicit_optional = true
|
|
58
|
+
strict_optional = true
|
|
59
|
+
check_untyped_defs = true
|
|
60
|
+
|
|
61
|
+
[tool.black]
|
|
62
|
+
line-length = 100
|
|
63
|
+
target-version = ["py310"]
|
|
64
|
+
|
|
65
|
+
[tool.pytest.ini_options]
|
|
66
|
+
DJANGO_SETTINGS_MODULE = "tests.settings"
|
|
67
|
+
python_files = ["tests/test_*.py"]
|
|
68
|
+
|
|
69
|
+
[project.optional-dependencies]
|
|
70
|
+
dev = [
|
|
71
|
+
"pytest>=8",
|
|
72
|
+
"pytest-django>=4.8.0",
|
|
73
|
+
"mypy>=1.8",
|
|
74
|
+
"django-stubs>=5.0.0",
|
|
75
|
+
"black>=24.0.0",
|
|
76
|
+
"ruff>=0.5.0",
|
|
77
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Register your models here.
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Register your models here.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Generated by Django 6.0.4 on 2026-05-09 12:36
|
|
2
|
+
|
|
3
|
+
import django.db.models.deletion
|
|
4
|
+
from django.conf import settings
|
|
5
|
+
from django.db import migrations, models
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Migration(migrations.Migration):
|
|
9
|
+
initial = True
|
|
10
|
+
|
|
11
|
+
dependencies = [
|
|
12
|
+
("product", "0001_initial"),
|
|
13
|
+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
operations = [
|
|
17
|
+
migrations.CreateModel(
|
|
18
|
+
name="CartItem",
|
|
19
|
+
fields=[
|
|
20
|
+
(
|
|
21
|
+
"id",
|
|
22
|
+
models.BigAutoField(
|
|
23
|
+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
|
|
24
|
+
),
|
|
25
|
+
),
|
|
26
|
+
("quantity", models.PositiveIntegerField()),
|
|
27
|
+
(
|
|
28
|
+
"product",
|
|
29
|
+
models.ForeignKey(
|
|
30
|
+
on_delete=django.db.models.deletion.CASCADE, to="product.baseproduct"
|
|
31
|
+
),
|
|
32
|
+
),
|
|
33
|
+
],
|
|
34
|
+
),
|
|
35
|
+
migrations.CreateModel(
|
|
36
|
+
name="Cart",
|
|
37
|
+
fields=[
|
|
38
|
+
(
|
|
39
|
+
"id",
|
|
40
|
+
models.BigAutoField(
|
|
41
|
+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
|
|
42
|
+
),
|
|
43
|
+
),
|
|
44
|
+
(
|
|
45
|
+
"user",
|
|
46
|
+
models.OneToOneField(
|
|
47
|
+
on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL
|
|
48
|
+
),
|
|
49
|
+
),
|
|
50
|
+
("cart_items", models.ManyToManyField(to="cart.cartitem")),
|
|
51
|
+
],
|
|
52
|
+
),
|
|
53
|
+
]
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Generated by Django 6.0.4 on 2026-06-23 12:06
|
|
2
|
+
|
|
3
|
+
import django.db.models.deletion
|
|
4
|
+
from django.db import migrations, models
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Migration(migrations.Migration):
|
|
8
|
+
|
|
9
|
+
dependencies = [
|
|
10
|
+
("cart", "0001_initial"),
|
|
11
|
+
("product", "0001_initial"),
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
operations = [
|
|
15
|
+
migrations.AddField(
|
|
16
|
+
model_name="cart",
|
|
17
|
+
name="active",
|
|
18
|
+
field=models.BooleanField(default=True),
|
|
19
|
+
),
|
|
20
|
+
migrations.AddField(
|
|
21
|
+
model_name="cartitem",
|
|
22
|
+
name="_final_price",
|
|
23
|
+
field=models.DecimalField(decimal_places=2, max_digits=20, null=True),
|
|
24
|
+
),
|
|
25
|
+
migrations.AddField(
|
|
26
|
+
model_name="cartitem",
|
|
27
|
+
name="active",
|
|
28
|
+
field=models.BooleanField(default=True),
|
|
29
|
+
),
|
|
30
|
+
migrations.AlterField(
|
|
31
|
+
model_name="cartitem",
|
|
32
|
+
name="product",
|
|
33
|
+
field=models.ForeignKey(
|
|
34
|
+
on_delete=django.db.models.deletion.PROTECT, to="product.baseproduct"
|
|
35
|
+
),
|
|
36
|
+
),
|
|
37
|
+
]
|