extensible-django-commerce 1.0.0__py3-none-any.whl
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.
- django_commerce/__init__.py +0 -0
- django_commerce/admin.py +1 -0
- django_commerce/apps.py +6 -0
- django_commerce/cart/__init__.py +0 -0
- django_commerce/cart/admin.py +1 -0
- django_commerce/cart/apps.py +6 -0
- django_commerce/cart/migrations/0001_initial.py +53 -0
- django_commerce/cart/migrations/0002_cart_active_cartitem__final_price_cartitem_active_and_more.py +37 -0
- django_commerce/cart/migrations/0003_remove_cart_user.py +17 -0
- django_commerce/cart/migrations/__init__.py +0 -0
- django_commerce/cart/models.py +86 -0
- django_commerce/cart/tests.py +1 -0
- django_commerce/cart/views.py +1 -0
- django_commerce/models.py +2 -0
- django_commerce/offsite_payment_gateway/__init__.py +0 -0
- django_commerce/offsite_payment_gateway/admin.py +1 -0
- django_commerce/offsite_payment_gateway/apps.py +11 -0
- django_commerce/offsite_payment_gateway/migrations/0001_initial.py +41 -0
- django_commerce/offsite_payment_gateway/migrations/__init__.py +0 -0
- django_commerce/offsite_payment_gateway/models.py +7 -0
- django_commerce/offsite_payment_gateway/plugins.py +35 -0
- django_commerce/offsite_payment_gateway/tests.py +1 -0
- django_commerce/offsite_payment_gateway/urls.py +7 -0
- django_commerce/offsite_payment_gateway/views.py +33 -0
- django_commerce/order/__init__.py +0 -0
- django_commerce/order/admin.py +1 -0
- django_commerce/order/apps.py +6 -0
- django_commerce/order/migrations/0001_initial.py +47 -0
- django_commerce/order/migrations/0002_remove_order_cart_items_order_cart_and_more.py +50 -0
- django_commerce/order/migrations/__init__.py +0 -0
- django_commerce/order/models.py +84 -0
- django_commerce/order/signals.py +5 -0
- django_commerce/order/tests.py +1 -0
- django_commerce/order/views.py +1 -0
- django_commerce/payment/__init__.py +0 -0
- django_commerce/payment/admin.py +1 -0
- django_commerce/payment/apps.py +11 -0
- django_commerce/payment/models.py +1 -0
- django_commerce/payment/plugins.py +11 -0
- django_commerce/payment/tests.py +1 -0
- django_commerce/payment/views.py +1 -0
- django_commerce/product/__init__.py +0 -0
- django_commerce/product/admin.py +1 -0
- django_commerce/product/apps.py +6 -0
- django_commerce/product/migrations/0001_initial.py +21 -0
- django_commerce/product/migrations/__init__.py +0 -0
- django_commerce/product/models.py +7 -0
- django_commerce/product/tests.py +1 -0
- django_commerce/product/views.py +1 -0
- django_commerce/tests.py +1 -0
- django_commerce/transaction/__init__.py +0 -0
- django_commerce/transaction/admin.py +1 -0
- django_commerce/transaction/apps.py +6 -0
- django_commerce/transaction/migrations/0001_initial.py +43 -0
- django_commerce/transaction/migrations/0002_remove_transaction_plugin_remove_transaction_result_and_more.py +30 -0
- django_commerce/transaction/migrations/__init__.py +0 -0
- django_commerce/transaction/models.py +19 -0
- django_commerce/transaction/tests.py +1 -0
- django_commerce/transaction/views.py +1 -0
- django_commerce/views.py +1 -0
- extensible_django_commerce-1.0.0.dist-info/METADATA +276 -0
- extensible_django_commerce-1.0.0.dist-info/RECORD +64 -0
- extensible_django_commerce-1.0.0.dist-info/WHEEL +5 -0
- extensible_django_commerce-1.0.0.dist-info/top_level.txt +1 -0
|
File without changes
|
django_commerce/admin.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Register your models here.
|
django_commerce/apps.py
ADDED
|
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
|
+
]
|
django_commerce/cart/migrations/0002_cart_active_cartitem__final_price_cartitem_active_and_more.py
ADDED
|
@@ -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
|
+
]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Generated by Django 6.0.4 on 2026-06-29 14:16
|
|
2
|
+
|
|
3
|
+
from django.db import migrations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
("cart", "0002_cart_active_cartitem__final_price_cartitem_active_and_more"),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.RemoveField(
|
|
14
|
+
model_name="cart",
|
|
15
|
+
name="user",
|
|
16
|
+
),
|
|
17
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from decimal import Decimal
|
|
2
|
+
|
|
3
|
+
from django.conf import settings
|
|
4
|
+
from django.db import models
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class CartItem(models.Model):
|
|
8
|
+
product = models.ForeignKey('product.BaseProduct', on_delete=models.PROTECT)
|
|
9
|
+
quantity = models.PositiveIntegerField()
|
|
10
|
+
_final_price = models.DecimalField(max_digits=20, decimal_places=2,null=True)
|
|
11
|
+
active = models.BooleanField(default=True)
|
|
12
|
+
|
|
13
|
+
def __str__(self):
|
|
14
|
+
return f"{self.product.name} - {self.quantity}"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
def final_price(self):
|
|
19
|
+
return self._final_price
|
|
20
|
+
|
|
21
|
+
@final_price.setter
|
|
22
|
+
def final_price(self, value):
|
|
23
|
+
self._final_price = value
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def product_price(self):
|
|
27
|
+
return self.product.price if self.active else self.final_price or Decimal(0)
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def total(self):
|
|
31
|
+
return self.quantity * self.product_price
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def increase_quantity(self):
|
|
35
|
+
self.quantity += 1
|
|
36
|
+
|
|
37
|
+
def decrease_quantity(self):
|
|
38
|
+
self.quantity -= 1
|
|
39
|
+
|
|
40
|
+
def payment_finished(self):
|
|
41
|
+
self.active = False
|
|
42
|
+
self.final_price = self.product.price
|
|
43
|
+
self.save()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class Cart(models.Model):
|
|
47
|
+
from django_commerce.product.models import BaseProduct
|
|
48
|
+
|
|
49
|
+
cart_items = models.ManyToManyField(CartItem)
|
|
50
|
+
active = models.BooleanField(default=True) # order is not paid
|
|
51
|
+
|
|
52
|
+
def add_to_cart(self, product: BaseProduct):
|
|
53
|
+
quantity_increased = False
|
|
54
|
+
for cart_item in self.cart_items.select_related('product').all():
|
|
55
|
+
if cart_item.product == product:
|
|
56
|
+
cart_item.increase_quantity()
|
|
57
|
+
cart_item.save()
|
|
58
|
+
quantity_increased = True
|
|
59
|
+
break
|
|
60
|
+
if not quantity_increased:
|
|
61
|
+
cart_item = CartItem.objects.create(product=product, quantity=1)
|
|
62
|
+
self.cart_items.add(cart_item)
|
|
63
|
+
|
|
64
|
+
def remove_from_cart(self, product: BaseProduct) -> bool:
|
|
65
|
+
for cart_item in self.cart_items.all():
|
|
66
|
+
if cart_item.product == product:
|
|
67
|
+
if cart_item.quantity == 1:
|
|
68
|
+
self.cart_items.remove(cart_item)
|
|
69
|
+
else:
|
|
70
|
+
cart_item.decrease_quantity()
|
|
71
|
+
cart_item.save()
|
|
72
|
+
return True
|
|
73
|
+
return False
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def total(self):
|
|
77
|
+
total = Decimal(0)
|
|
78
|
+
for cart_item in self.cart_items.all():
|
|
79
|
+
total += cart_item.total
|
|
80
|
+
return total
|
|
81
|
+
|
|
82
|
+
def close_cart(self):
|
|
83
|
+
for cart_item in self.cart_items.all():
|
|
84
|
+
cart_item.payment_finished()
|
|
85
|
+
self.active = False
|
|
86
|
+
self.save()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Create your tests here.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Create your views here.
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Register your models here.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from django.apps import AppConfig
|
|
2
|
+
from django_plugin_system import register_plugin_type
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class OffsitePaymentGatewayConfig(AppConfig):
|
|
6
|
+
default_auto_field = 'django.db.models.BigAutoField'
|
|
7
|
+
name = 'django_commerce.offsite_payment_gateway'
|
|
8
|
+
|
|
9
|
+
def ready(self):
|
|
10
|
+
from .plugins import OffsitePayment
|
|
11
|
+
register_plugin_type({"interface":OffsitePayment})
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Generated by Django 6.0.4 on 2026-06-23 12:08
|
|
2
|
+
|
|
3
|
+
import django.db.models.deletion
|
|
4
|
+
from django.db import migrations, models
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Migration(migrations.Migration):
|
|
8
|
+
|
|
9
|
+
initial = True
|
|
10
|
+
|
|
11
|
+
dependencies = [
|
|
12
|
+
("django_plugin_system", "0001_initial"),
|
|
13
|
+
("order", "0002_remove_order_cart_items_order_cart_and_more"),
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
operations = [
|
|
17
|
+
migrations.CreateModel(
|
|
18
|
+
name="InProgressPayment",
|
|
19
|
+
fields=[
|
|
20
|
+
(
|
|
21
|
+
"id",
|
|
22
|
+
models.BigAutoField(
|
|
23
|
+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
|
|
24
|
+
),
|
|
25
|
+
),
|
|
26
|
+
(
|
|
27
|
+
"order",
|
|
28
|
+
models.ForeignKey(
|
|
29
|
+
on_delete=django.db.models.deletion.PROTECT, to="order.order"
|
|
30
|
+
),
|
|
31
|
+
),
|
|
32
|
+
(
|
|
33
|
+
"plugin_instance",
|
|
34
|
+
models.ForeignKey(
|
|
35
|
+
on_delete=django.db.models.deletion.PROTECT,
|
|
36
|
+
to="django_plugin_system.plugininstance",
|
|
37
|
+
),
|
|
38
|
+
),
|
|
39
|
+
],
|
|
40
|
+
),
|
|
41
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
from django.db import models
|
|
2
|
+
from django_plugin_system.models import PluginInstance
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class InProgressPayment(models.Model):
|
|
6
|
+
plugin_instance = models.ForeignKey(PluginInstance, on_delete=models.PROTECT)
|
|
7
|
+
order = models.ForeignKey('order.Order', on_delete=models.PROTECT)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from typing import Tuple, Dict
|
|
2
|
+
|
|
3
|
+
from django.http import HttpRequest, HttpResponseRedirect
|
|
4
|
+
from django.urls import reverse
|
|
5
|
+
from django_plugin_system import required_plugin_item_method
|
|
6
|
+
|
|
7
|
+
from ..payment.plugins import PaymentPlugin
|
|
8
|
+
|
|
9
|
+
BASE_URL = 'http://127.0.0.1:8000'
|
|
10
|
+
|
|
11
|
+
class OffsitePayment(PaymentPlugin):
|
|
12
|
+
name = 'Offsite Payment'
|
|
13
|
+
|
|
14
|
+
def create_payment(self, order):
|
|
15
|
+
from .models import InProgressPayment
|
|
16
|
+
in_progress_payment = InProgressPayment.objects.create(
|
|
17
|
+
order=order,
|
|
18
|
+
plugin_instance=self.plugin_item.plugin_instance
|
|
19
|
+
)
|
|
20
|
+
callback_url = BASE_URL + HttpResponseRedirect(reverse('payment_callback', args=[in_progress_payment.pk])).url
|
|
21
|
+
return self.plugin_item.get_payment_gateway_url(order, callback_url)
|
|
22
|
+
|
|
23
|
+
@required_plugin_item_method
|
|
24
|
+
def get_payment_gateway_url(self, order, callback_url: str) -> Tuple[bool,str]:
|
|
25
|
+
"""
|
|
26
|
+
:param order:
|
|
27
|
+
:param callback_url:
|
|
28
|
+
:return: str
|
|
29
|
+
The return value should be the gateway url as string
|
|
30
|
+
"""
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
@required_plugin_item_method
|
|
34
|
+
def verify_payment(self, order, callback_request: HttpRequest | None = None) -> Tuple[bool, Dict]:
|
|
35
|
+
pass
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Create your tests here.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from django.conf import settings
|
|
2
|
+
from django.http import HttpRequest
|
|
3
|
+
from django.http import HttpResponse
|
|
4
|
+
from django.shortcuts import redirect
|
|
5
|
+
|
|
6
|
+
from .models import InProgressPayment
|
|
7
|
+
from ..transaction.models import Transaction
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def callback_view(request: HttpRequest, payment_id):
|
|
11
|
+
system_error = getattr(settings, "OFFSITE_PAYMENT_SYSTEM_ERROR", None)
|
|
12
|
+
payment_not_verified = getattr(settings, "OFFSITE_PAYMENT_NOT_VERIFIED", None)
|
|
13
|
+
payment_success = getattr(settings, "OFFSITE_PAYMENT_SUCCESS", None)
|
|
14
|
+
try:
|
|
15
|
+
processing_payment = InProgressPayment.objects.select_related('order', 'plugin_instance').get(pk=payment_id)
|
|
16
|
+
order = processing_payment.order
|
|
17
|
+
plugin_instance = processing_payment.plugin_instance
|
|
18
|
+
processing_payment.delete()
|
|
19
|
+
transaction = Transaction.objects.create(order=processing_payment.order)
|
|
20
|
+
plugin_implementation = plugin_instance.load_implementation()
|
|
21
|
+
if not plugin_implementation:
|
|
22
|
+
transaction.failed("Offsite payment gateway instance not found!")
|
|
23
|
+
return redirect(system_error) if system_error else "System Error: undefined payment gateway"
|
|
24
|
+
verified, details = plugin_implementation.verify_payment(order, request)
|
|
25
|
+
print(details)
|
|
26
|
+
if verified:
|
|
27
|
+
transaction.succeeded(details)
|
|
28
|
+
print('verified')
|
|
29
|
+
return redirect(payment_success) if payment_success else HttpResponse(status=200, content=details)
|
|
30
|
+
transaction.failed(details)
|
|
31
|
+
return redirect(payment_not_verified) if payment_not_verified else HttpResponse(status=403, content=details)
|
|
32
|
+
except InProgressPayment.DoesNotExist:
|
|
33
|
+
return redirect(system_error) if system_error else HttpResponse(status=404, content='payment not listed!')
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Register your models here.
|
|
@@ -0,0 +1,47 @@
|
|
|
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
|
+
("cart", "0001_initial"),
|
|
13
|
+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
operations = [
|
|
17
|
+
migrations.CreateModel(
|
|
18
|
+
name="Order",
|
|
19
|
+
fields=[
|
|
20
|
+
(
|
|
21
|
+
"id",
|
|
22
|
+
models.BigAutoField(
|
|
23
|
+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
|
|
24
|
+
),
|
|
25
|
+
),
|
|
26
|
+
(
|
|
27
|
+
"status",
|
|
28
|
+
models.IntegerField(
|
|
29
|
+
choices=[
|
|
30
|
+
(2, "Pending"),
|
|
31
|
+
(3, "Processing"),
|
|
32
|
+
(4, "Completed"),
|
|
33
|
+
(5, "Canceled"),
|
|
34
|
+
],
|
|
35
|
+
default=2,
|
|
36
|
+
),
|
|
37
|
+
),
|
|
38
|
+
("cart_items", models.ManyToManyField(to="cart.cartitem")),
|
|
39
|
+
(
|
|
40
|
+
"user",
|
|
41
|
+
models.ForeignKey(
|
|
42
|
+
on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL
|
|
43
|
+
),
|
|
44
|
+
),
|
|
45
|
+
],
|
|
46
|
+
),
|
|
47
|
+
]
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Generated by Django 6.0.4 on 2026-06-23 12:06
|
|
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
|
+
|
|
10
|
+
dependencies = [
|
|
11
|
+
("cart", "0002_cart_active_cartitem__final_price_cartitem_active_and_more"),
|
|
12
|
+
("order", "0001_initial"),
|
|
13
|
+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
operations = [
|
|
17
|
+
migrations.RemoveField(
|
|
18
|
+
model_name="order",
|
|
19
|
+
name="cart_items",
|
|
20
|
+
),
|
|
21
|
+
migrations.AddField(
|
|
22
|
+
model_name="order",
|
|
23
|
+
name="cart",
|
|
24
|
+
field=models.ForeignKey(
|
|
25
|
+
null=True, on_delete=django.db.models.deletion.PROTECT, to="cart.cart"
|
|
26
|
+
),
|
|
27
|
+
),
|
|
28
|
+
migrations.AlterField(
|
|
29
|
+
model_name="order",
|
|
30
|
+
name="status",
|
|
31
|
+
field=models.IntegerField(
|
|
32
|
+
choices=[
|
|
33
|
+
(0, "Draft"),
|
|
34
|
+
(1, "Processing"),
|
|
35
|
+
(2, "Pending"),
|
|
36
|
+
(3, "Paid"),
|
|
37
|
+
(4, "Completed"),
|
|
38
|
+
(5, "Canceled"),
|
|
39
|
+
],
|
|
40
|
+
default=0,
|
|
41
|
+
),
|
|
42
|
+
),
|
|
43
|
+
migrations.AlterField(
|
|
44
|
+
model_name="order",
|
|
45
|
+
name="user",
|
|
46
|
+
field=models.ForeignKey(
|
|
47
|
+
null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL
|
|
48
|
+
),
|
|
49
|
+
),
|
|
50
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Dict
|
|
3
|
+
|
|
4
|
+
from django.conf import settings
|
|
5
|
+
from django.contrib.auth.models import User
|
|
6
|
+
from django.db import models
|
|
7
|
+
from django_plugin_system import BasePluginItem
|
|
8
|
+
from django_plugin_system.models import PluginType
|
|
9
|
+
|
|
10
|
+
from .signals import order_payment_completed, order_payment_failed, order_canceled
|
|
11
|
+
from ..cart.models import Cart
|
|
12
|
+
from ..payment.plugins import PaymentPlugin
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class OrderState(models.IntegerChoices):
|
|
16
|
+
DRAFT = 0
|
|
17
|
+
PROCESSING = 1
|
|
18
|
+
PENDING = 2
|
|
19
|
+
PAID = 3
|
|
20
|
+
COMPLETED = 4
|
|
21
|
+
CANCELED = 5
|
|
22
|
+
|
|
23
|
+
IN_PROGRESS_ORDERS = [OrderState.DRAFT, OrderState.PROCESSING, OrderState.PENDING]
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class EmptyOrderException(Exception):
|
|
27
|
+
message: str = "The empty order can not be paid."
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class NoPaymentPluginException(Exception):
|
|
31
|
+
message: str = "No payment plugin is available."
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class Order(models.Model):
|
|
35
|
+
User = settings.AUTH_USER_MODEL
|
|
36
|
+
|
|
37
|
+
cart = models.ForeignKey(Cart, on_delete=models.PROTECT, null=True)
|
|
38
|
+
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
|
|
39
|
+
status = models.IntegerField(choices=OrderState, default=OrderState.DRAFT)
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def is_empty(self):
|
|
43
|
+
return self.cart.cart_items.count() == 0
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def total_price(self):
|
|
47
|
+
return self.cart.total
|
|
48
|
+
|
|
49
|
+
def do_payment(self, payment_plugin: BasePluginItem | None = None):
|
|
50
|
+
if self.is_empty:
|
|
51
|
+
raise EmptyOrderException()
|
|
52
|
+
if not payment_plugin:
|
|
53
|
+
payment_type = PluginType.get_plugin_type_by_class(PaymentPlugin)
|
|
54
|
+
payment_plugin_instance = payment_type.get_single_plugin()
|
|
55
|
+
if not payment_plugin_instance:
|
|
56
|
+
raise NoPaymentPluginException()
|
|
57
|
+
payment_plugin = payment_plugin_instance.load_implementation()
|
|
58
|
+
return payment_plugin.create_payment(self)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def order_paid_successfully(self, transaction_info: Dict):
|
|
62
|
+
self.cart.close_cart()
|
|
63
|
+
self.status = OrderState.PAID
|
|
64
|
+
self.save()
|
|
65
|
+
order_payment_completed.send(sender=self.__class__, order=self, info=transaction_info)
|
|
66
|
+
|
|
67
|
+
def order_payment_failed(self, transaction_info):
|
|
68
|
+
self.status = OrderState.PENDING
|
|
69
|
+
self.save()
|
|
70
|
+
order_payment_failed.send(sender=self.__class__, order=self, info=transaction_info)
|
|
71
|
+
|
|
72
|
+
def cancel_order(self):
|
|
73
|
+
self.status = OrderState.CANCELED
|
|
74
|
+
self.cart.close_cart()
|
|
75
|
+
self.save()
|
|
76
|
+
order_canceled.send(sender=self.__class__, order=self)
|
|
77
|
+
|
|
78
|
+
@staticmethod
|
|
79
|
+
def get_user_current_order(user: User):
|
|
80
|
+
try:
|
|
81
|
+
return Order.objects.get(user=user, status__in=IN_PROGRESS_ORDERS)
|
|
82
|
+
except Order.DoesNotExist:
|
|
83
|
+
cart = Cart.objects.create()
|
|
84
|
+
return Order.objects.create(user=user, cart=cart)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Create your tests here.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Create your views here.
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Register your models here.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from django.apps import AppConfig
|
|
2
|
+
from django_plugin_system import register_plugin_type
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class PaymentConfig(AppConfig):
|
|
6
|
+
default_auto_field = 'django.db.models.BigAutoField'
|
|
7
|
+
name = 'django_commerce.payment'
|
|
8
|
+
|
|
9
|
+
def ready(self):
|
|
10
|
+
from .plugins import PaymentPlugin
|
|
11
|
+
register_plugin_type({"interface": PaymentPlugin})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Create your tests here.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Create your views here.
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Register your models here.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Generated by Django 6.0 on 2026-01-05 11:38
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
initial = True
|
|
8
|
+
|
|
9
|
+
dependencies = [
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.CreateModel(
|
|
14
|
+
name='BaseProduct',
|
|
15
|
+
fields=[
|
|
16
|
+
('squid', models.CharField(max_length=30, primary_key=True, serialize=False)),
|
|
17
|
+
('name', models.CharField(max_length=30)),
|
|
18
|
+
('price', models.DecimalField(decimal_places=2, max_digits=10)),
|
|
19
|
+
],
|
|
20
|
+
),
|
|
21
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Create your tests here.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Create your views here.
|
django_commerce/tests.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Create your tests here.
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Register your models here.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Generated by Django 6.0.4 on 2026-05-09 12:37
|
|
2
|
+
|
|
3
|
+
import django.db.models.deletion
|
|
4
|
+
from django.db import migrations, models
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Migration(migrations.Migration):
|
|
8
|
+
initial = True
|
|
9
|
+
|
|
10
|
+
dependencies = [
|
|
11
|
+
("django_plugin_system", "__first__"),
|
|
12
|
+
("order", "0001_initial"),
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
operations = [
|
|
16
|
+
migrations.CreateModel(
|
|
17
|
+
name="Transaction",
|
|
18
|
+
fields=[
|
|
19
|
+
(
|
|
20
|
+
"id",
|
|
21
|
+
models.BigAutoField(
|
|
22
|
+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
|
|
23
|
+
),
|
|
24
|
+
),
|
|
25
|
+
("tracing_code", models.CharField(max_length=100, null=True)),
|
|
26
|
+
("result", models.TextField(null=True)),
|
|
27
|
+
("success", models.BooleanField(default=False)),
|
|
28
|
+
(
|
|
29
|
+
"order",
|
|
30
|
+
models.ForeignKey(
|
|
31
|
+
null=True, on_delete=django.db.models.deletion.PROTECT, to="order.order"
|
|
32
|
+
),
|
|
33
|
+
),
|
|
34
|
+
(
|
|
35
|
+
"plugin",
|
|
36
|
+
models.ForeignKey(
|
|
37
|
+
on_delete=django.db.models.deletion.PROTECT,
|
|
38
|
+
to="django_plugin_system.pluginitem",
|
|
39
|
+
),
|
|
40
|
+
),
|
|
41
|
+
],
|
|
42
|
+
),
|
|
43
|
+
]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Generated by Django 6.0.4 on 2026-06-23 12:06
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
("transaction", "0001_initial"),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.RemoveField(
|
|
14
|
+
model_name="transaction",
|
|
15
|
+
name="plugin",
|
|
16
|
+
),
|
|
17
|
+
migrations.RemoveField(
|
|
18
|
+
model_name="transaction",
|
|
19
|
+
name="result",
|
|
20
|
+
),
|
|
21
|
+
migrations.RemoveField(
|
|
22
|
+
model_name="transaction",
|
|
23
|
+
name="tracing_code",
|
|
24
|
+
),
|
|
25
|
+
migrations.AddField(
|
|
26
|
+
model_name="transaction",
|
|
27
|
+
name="info",
|
|
28
|
+
field=models.JSONField(null=True),
|
|
29
|
+
),
|
|
30
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from typing import Dict
|
|
2
|
+
from django.db import models
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Transaction(models.Model):
|
|
6
|
+
order = models.ForeignKey('order.Order', on_delete=models.PROTECT, null=True)
|
|
7
|
+
info = models.JSONField(null=True)
|
|
8
|
+
success = models.BooleanField(default=False)
|
|
9
|
+
|
|
10
|
+
def failed(self, reason: str | Dict):
|
|
11
|
+
self.info = reason if isinstance(reason, dict) else {"error": reason}
|
|
12
|
+
self.save()
|
|
13
|
+
self.order.order_payment_failed(self.info)
|
|
14
|
+
|
|
15
|
+
def succeeded(self, info: dict):
|
|
16
|
+
self.success = True
|
|
17
|
+
self.info = info
|
|
18
|
+
self.save()
|
|
19
|
+
self.order.order_paid_successfully(self.info)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Create your tests here.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Create your views here.
|
django_commerce/views.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Create your views here.
|
|
@@ -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,64 @@
|
|
|
1
|
+
django_commerce/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
django_commerce/admin.py,sha256=xZHn9b5u2_7bD6Q48FnN0WiRPjEbE9c9dIm4aIe1M34,30
|
|
3
|
+
django_commerce/apps.py,sha256=XvsQ2GOZ_MEMfNABRVnlCB5Ppob3QjPrAwcqikSri40,167
|
|
4
|
+
django_commerce/models.py,sha256=26UWatnbm6ZIwQMuu9NNzQ0IW1ACO4Oe9caModuTpWM,4
|
|
5
|
+
django_commerce/tests.py,sha256=p1bwJbR_nFqJjyfOygNEO5nXSrU3Ku0A3g2O8oZ74c0,27
|
|
6
|
+
django_commerce/views.py,sha256=gQmOOQQgt5SMeC_hOzhacKuRPDLbcVfNqURLhP0M1U4,27
|
|
7
|
+
django_commerce/cart/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
django_commerce/cart/admin.py,sha256=xZHn9b5u2_7bD6Q48FnN0WiRPjEbE9c9dIm4aIe1M34,30
|
|
9
|
+
django_commerce/cart/apps.py,sha256=IMwEVY-k_7sSmTeaNRBH9xe87lm6DbiuAn0cCHEnK8Y,162
|
|
10
|
+
django_commerce/cart/models.py,sha256=udeUsZSshq9DabTkLgYLim13w6POULUXAhuxWbb17e4,2631
|
|
11
|
+
django_commerce/cart/tests.py,sha256=p1bwJbR_nFqJjyfOygNEO5nXSrU3Ku0A3g2O8oZ74c0,27
|
|
12
|
+
django_commerce/cart/views.py,sha256=gQmOOQQgt5SMeC_hOzhacKuRPDLbcVfNqURLhP0M1U4,27
|
|
13
|
+
django_commerce/cart/migrations/0001_initial.py,sha256=A7k5pJHO9a3zfhSfNmOYhdgS5BxX0RxAEMFn3H90ePU,1673
|
|
14
|
+
django_commerce/cart/migrations/0002_cart_active_cartitem__final_price_cartitem_active_and_more.py,sha256=xaqDBmGEsBt292yJtaU8YhSdphSZebH9MJfQcniP5Es,1066
|
|
15
|
+
django_commerce/cart/migrations/0003_remove_cart_user.py,sha256=xUljWXDgzH5qoCrBYzY3IVfD2DzNnuiN4siG21O1KaM,378
|
|
16
|
+
django_commerce/cart/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
+
django_commerce/offsite_payment_gateway/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
+
django_commerce/offsite_payment_gateway/admin.py,sha256=xZHn9b5u2_7bD6Q48FnN0WiRPjEbE9c9dIm4aIe1M34,30
|
|
19
|
+
django_commerce/offsite_payment_gateway/apps.py,sha256=rZACkIqhQsCMbduKHy94mE3-oZtgC7J0_iOVVnaaTRo,382
|
|
20
|
+
django_commerce/offsite_payment_gateway/models.py,sha256=SMNzNL3xTLtAWj__WMpDAaUm-xdKXwI4MOEOKk2JUMk,285
|
|
21
|
+
django_commerce/offsite_payment_gateway/plugins.py,sha256=sekOT8w1cWHrI-kLJWLst11IhWmHY5Rad4Z2qjoZS3A,1245
|
|
22
|
+
django_commerce/offsite_payment_gateway/tests.py,sha256=p1bwJbR_nFqJjyfOygNEO5nXSrU3Ku0A3g2O8oZ74c0,27
|
|
23
|
+
django_commerce/offsite_payment_gateway/urls.py,sha256=HQLnfMXzppG92EL-gBKvICYqFLUic5h1ogJw0Snqrl0,176
|
|
24
|
+
django_commerce/offsite_payment_gateway/views.py,sha256=F-InU5jNGSc4UWi53tFo4bG0vjOMkS-KCghn7BkwxUc,1803
|
|
25
|
+
django_commerce/offsite_payment_gateway/migrations/0001_initial.py,sha256=ftRQyPIdrIHa62DF4Y4CXX_C3tTUzydsf14y1txrinA,1227
|
|
26
|
+
django_commerce/offsite_payment_gateway/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
27
|
+
django_commerce/order/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
|
+
django_commerce/order/admin.py,sha256=xZHn9b5u2_7bD6Q48FnN0WiRPjEbE9c9dIm4aIe1M34,30
|
|
29
|
+
django_commerce/order/apps.py,sha256=yyuB6wNl-xJryEMIjSwNcaYmaU34DQ0S0eXUUXNqH_g,164
|
|
30
|
+
django_commerce/order/models.py,sha256=oLMsEWtaS-OCkkv0er9sLsIgDG0Ie7kltuIF_w7POQg,2864
|
|
31
|
+
django_commerce/order/signals.py,sha256=QCijQSfLYqBDnT50ffUZc1pUnF9jQreSBd7zYMxe3BU,134
|
|
32
|
+
django_commerce/order/tests.py,sha256=p1bwJbR_nFqJjyfOygNEO5nXSrU3Ku0A3g2O8oZ74c0,27
|
|
33
|
+
django_commerce/order/views.py,sha256=gQmOOQQgt5SMeC_hOzhacKuRPDLbcVfNqURLhP0M1U4,27
|
|
34
|
+
django_commerce/order/migrations/0001_initial.py,sha256=t-eptwLQ3iIYQVP9ZOqu5b8JWar06kVwi4iuY2z8-Kg,1455
|
|
35
|
+
django_commerce/order/migrations/0002_remove_order_cart_items_order_cart_and_more.py,sha256=GqZdHE6k4YGrntKxIHTu8cwftPc0T3P8Eqe9A1sR5bU,1525
|
|
36
|
+
django_commerce/order/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
37
|
+
django_commerce/payment/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
|
+
django_commerce/payment/admin.py,sha256=xZHn9b5u2_7bD6Q48FnN0WiRPjEbE9c9dIm4aIe1M34,30
|
|
39
|
+
django_commerce/payment/apps.py,sha256=bfKVh3FXrReVzEdTjgeVnjRYevvUXDebK6yN3hqwjBI,351
|
|
40
|
+
django_commerce/payment/models.py,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
|
41
|
+
django_commerce/payment/plugins.py,sha256=w-7bmkzEf0i4Et3hhxsaO3ndgGMS43EAY3p2AsCiBWo,222
|
|
42
|
+
django_commerce/payment/tests.py,sha256=p1bwJbR_nFqJjyfOygNEO5nXSrU3Ku0A3g2O8oZ74c0,27
|
|
43
|
+
django_commerce/payment/views.py,sha256=gQmOOQQgt5SMeC_hOzhacKuRPDLbcVfNqURLhP0M1U4,27
|
|
44
|
+
django_commerce/product/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
45
|
+
django_commerce/product/admin.py,sha256=xZHn9b5u2_7bD6Q48FnN0WiRPjEbE9c9dIm4aIe1M34,30
|
|
46
|
+
django_commerce/product/apps.py,sha256=ewryYz-X52DG8eK3u1TUJj4wR8JxUIdnBYxhk1qN6W0,168
|
|
47
|
+
django_commerce/product/models.py,sha256=qmtFw3PZF88dMCRmO_Eh-YSOfqp8osCMQ27VMyTr-A0,241
|
|
48
|
+
django_commerce/product/tests.py,sha256=p1bwJbR_nFqJjyfOygNEO5nXSrU3Ku0A3g2O8oZ74c0,27
|
|
49
|
+
django_commerce/product/views.py,sha256=gQmOOQQgt5SMeC_hOzhacKuRPDLbcVfNqURLhP0M1U4,27
|
|
50
|
+
django_commerce/product/migrations/0001_initial.py,sha256=CT82hdrg3EQ31pxfGcJNW5n7OGzXCjO7OtCzLlDqtOY,569
|
|
51
|
+
django_commerce/product/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
52
|
+
django_commerce/transaction/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
53
|
+
django_commerce/transaction/admin.py,sha256=xZHn9b5u2_7bD6Q48FnN0WiRPjEbE9c9dIm4aIe1M34,30
|
|
54
|
+
django_commerce/transaction/apps.py,sha256=5LoPgmNyQWD6-YE3MwkKLGxDiafbbLglfVxeS8KVloA,176
|
|
55
|
+
django_commerce/transaction/models.py,sha256=PeztXfrNt25KnkoH3mInc4fx7qcoP9AsW4pu_MwV7nE,634
|
|
56
|
+
django_commerce/transaction/tests.py,sha256=p1bwJbR_nFqJjyfOygNEO5nXSrU3Ku0A3g2O8oZ74c0,27
|
|
57
|
+
django_commerce/transaction/views.py,sha256=gQmOOQQgt5SMeC_hOzhacKuRPDLbcVfNqURLhP0M1U4,27
|
|
58
|
+
django_commerce/transaction/migrations/0001_initial.py,sha256=M8TCDrNQMBMuCm1-Wz3ASBp900cI6MUXZVkexuuhzYk,1382
|
|
59
|
+
django_commerce/transaction/migrations/0002_remove_transaction_plugin_remove_transaction_result_and_more.py,sha256=1gDFNl8mSrekG1VUxagQtXCZ9Aw_L6DPz__-1jArNls,736
|
|
60
|
+
django_commerce/transaction/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
61
|
+
extensible_django_commerce-1.0.0.dist-info/METADATA,sha256=naPWReJ6hFeX3oZ_zUSVy8kX4-GvqOYEl8_qeq_PAnQ,6425
|
|
62
|
+
extensible_django_commerce-1.0.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
63
|
+
extensible_django_commerce-1.0.0.dist-info/top_level.txt,sha256=WO2yqDsJXl5hv5hRu4jwb8ZKtiRewlW-athKvDtKGfI,16
|
|
64
|
+
extensible_django_commerce-1.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
django_commerce
|