diff options
author | Matthew Lemon <y@yulqen.org> | 2024-10-15 21:01:31 +0100 |
---|---|---|
committer | Matthew Lemon <y@yulqen.org> | 2024-10-15 21:01:31 +0100 |
commit | eeaddb27560d723ca7d61359744ceb2709fccd2d (patch) | |
tree | 04ddbc49ae7b73d5f5a9e1716d7227aecd3b9f85 /pyblackbird_cc/payments | |
parent | 7a3044c859043837e6c7c95bb4894d04e9b2cbc2 (diff) |
Renamed from pyblackbird_cc to alphabetlearning - everywhere
Diffstat (limited to 'pyblackbird_cc/payments')
19 files changed, 0 insertions, 691 deletions
diff --git a/pyblackbird_cc/payments/__init__.py b/pyblackbird_cc/payments/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/pyblackbird_cc/payments/__init__.py +++ /dev/null diff --git a/pyblackbird_cc/payments/admin.py b/pyblackbird_cc/payments/admin.py deleted file mode 100644 index f6a00a2..0000000 --- a/pyblackbird_cc/payments/admin.py +++ /dev/null @@ -1,38 +0,0 @@ -from django.contrib import admin - -from .models import CartItem -from .models import Price -from .models import Product -from .models import ShoppingCart -from .models import Subscription -from .models import SubscriptionPlan - - -class PriceInlineAdmin(admin.TabularInline): - model = Price - extra = 0 - -class ProductAdmin(admin.ModelAdmin): - inlines = [PriceInlineAdmin] - -class SubscriptionPlanAdmin(admin.ModelAdmin): - list_display = ('name', 'price', 'description', 'allowed_downloads') - - -class ShoppingCartAdmin(admin.ModelAdmin): - list_display = ('user', 'created_at', 'updated_at') - - -class CartItemAdmin(admin.ModelAdmin): - list_display = ('cart', 'resource', 'quantity', 'added_at') - - -class SubscriptionAdmin(admin.ModelAdmin): - list_display = ('user', 'is_active', 'start_date', 'end_date') - - -admin.site.register(Product, ProductAdmin) -admin.site.register(SubscriptionPlan) -admin.site.register(ShoppingCart) -admin.site.register(CartItem) -admin.site.register(Subscription) diff --git a/pyblackbird_cc/payments/apps.py b/pyblackbird_cc/payments/apps.py deleted file mode 100644 index 89e29f4..0000000 --- a/pyblackbird_cc/payments/apps.py +++ /dev/null @@ -1,9 +0,0 @@ -from django.apps import AppConfig - - -class PaymentsConfig(AppConfig): - default_auto_field = "django.db.models.BigAutoField" - name = "pyblackbird_cc.payments" - - def ready(self): - import pyblackbird_cc.payments.signals # noqa: F401 diff --git a/pyblackbird_cc/payments/migrations/0001_initial.py b/pyblackbird_cc/payments/migrations/0001_initial.py deleted file mode 100644 index 33b7602..0000000 --- a/pyblackbird_cc/payments/migrations/0001_initial.py +++ /dev/null @@ -1,103 +0,0 @@ -# Generated by Django 5.0.4 on 2024-09-03 19:21 - -import django.db.models.deletion -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ("resources", "0019_alter_pdfpagesnapshot_options_and_more"), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name="ShoppingCart", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("created_at", models.DateTimeField(auto_now_add=True)), - ("updated_at", models.DateTimeField(auto_now=True)), - ( - "user", - models.OneToOneField( - on_delete=django.db.models.deletion.CASCADE, - to=settings.AUTH_USER_MODEL, - ), - ), - ], - ), - migrations.CreateModel( - name="Subscription", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("is_active", models.BooleanField(default=False)), - ("start_date", models.DateTimeField(blank=True, null=True)), - ("end_date", models.DateTimeField(blank=True, null=True)), - ( - "stripe_subscription_id", - models.CharField(blank=True, max_length=255, null=True), - ), - ( - "user", - models.OneToOneField( - on_delete=django.db.models.deletion.CASCADE, - to=settings.AUTH_USER_MODEL, - ), - ), - ], - ), - migrations.CreateModel( - name="CartItem", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("quantity", models.PositiveIntegerField(default=1)), - ("added_at", models.DateTimeField(auto_now_add=True)), - ( - "resource", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="resources.resource", - ), - ), - ( - "cart", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="items", - to="payments.shoppingcart", - ), - ), - ], - options={ - "unique_together": {("cart", "resource")}, - }, - ), - ] diff --git a/pyblackbird_cc/payments/migrations/0002_subscriptionplan_and_more.py b/pyblackbird_cc/payments/migrations/0002_subscriptionplan_and_more.py deleted file mode 100644 index cab49b5..0000000 --- a/pyblackbird_cc/payments/migrations/0002_subscriptionplan_and_more.py +++ /dev/null @@ -1,36 +0,0 @@ -# Generated by Django 5.0.4 on 2024-09-03 19:32 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("payments", "0001_initial"), - ] - - operations = [ - migrations.CreateModel( - name="SubscriptionPlan", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("name", models.CharField(max_length=255)), - ("price", models.DecimalField(decimal_places=2, max_digits=6)), - ("description", models.TextField()), - ("allowed_downloads", models.PositiveIntegerField()), - ("stripe_plan_id", models.CharField(max_length=255)), - ], - ), - migrations.RemoveField( - model_name="subscription", - name="stripe_subscription_id", - ), - ] diff --git a/pyblackbird_cc/payments/migrations/0003_product_price.py b/pyblackbird_cc/payments/migrations/0003_product_price.py deleted file mode 100644 index b12d5dc..0000000 --- a/pyblackbird_cc/payments/migrations/0003_product_price.py +++ /dev/null @@ -1,53 +0,0 @@ -# Generated by Django 5.0.4 on 2024-09-04 19:01 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("payments", "0002_subscriptionplan_and_more"), - ] - - operations = [ - migrations.CreateModel( - name="Product", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("name", models.CharField(max_length=255)), - ("stripe_product_id", models.CharField(max_length=100)), - ], - ), - migrations.CreateModel( - name="Price", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("price", models.IntegerField(default=0)), - ("stripe_product_id", models.CharField(max_length=100)), - ( - "product", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="payments.product", - ), - ), - ], - ), - ] diff --git a/pyblackbird_cc/payments/migrations/0004_rename_stripe_product_id_price_stripe_price_id.py b/pyblackbird_cc/payments/migrations/0004_rename_stripe_product_id_price_stripe_price_id.py deleted file mode 100644 index e5a339f..0000000 --- a/pyblackbird_cc/payments/migrations/0004_rename_stripe_product_id_price_stripe_price_id.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.0.4 on 2024-09-04 19:17 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ("payments", "0003_product_price"), - ] - - operations = [ - migrations.RenameField( - model_name="price", - old_name="stripe_product_id", - new_name="stripe_price_id", - ), - ] diff --git a/pyblackbird_cc/payments/migrations/0005_remove_subscriptionplan_stripe_plan_id.py b/pyblackbird_cc/payments/migrations/0005_remove_subscriptionplan_stripe_plan_id.py deleted file mode 100644 index 1e642b4..0000000 --- a/pyblackbird_cc/payments/migrations/0005_remove_subscriptionplan_stripe_plan_id.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 5.0.4 on 2024-09-08 19:23 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ("payments", "0004_rename_stripe_product_id_price_stripe_price_id"), - ] - - operations = [ - migrations.RemoveField( - model_name="subscriptionplan", - name="stripe_plan_id", - ), - ] diff --git a/pyblackbird_cc/payments/migrations/0006_subscription_plan.py b/pyblackbird_cc/payments/migrations/0006_subscription_plan.py deleted file mode 100644 index f54b5f9..0000000 --- a/pyblackbird_cc/payments/migrations/0006_subscription_plan.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 5.0.4 on 2024-09-08 20:21 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("payments", "0005_remove_subscriptionplan_stripe_plan_id"), - ] - - operations = [ - migrations.AddField( - model_name="subscription", - name="plan", - field=models.ForeignKey( - default=1, - on_delete=django.db.models.deletion.CASCADE, - to="payments.subscriptionplan", - ), - preserve_default=False, - ), - ] diff --git a/pyblackbird_cc/payments/migrations/0007_remove_cartitem_quantity_and_more.py b/pyblackbird_cc/payments/migrations/0007_remove_cartitem_quantity_and_more.py deleted file mode 100644 index e976059..0000000 --- a/pyblackbird_cc/payments/migrations/0007_remove_cartitem_quantity_and_more.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 5.0.4 on 2024-09-14 14:40 - -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('payments', '0006_subscription_plan'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.RemoveField( - model_name='cartitem', - name='quantity', - ), - migrations.AddConstraint( - model_name='subscription', - constraint=models.UniqueConstraint(fields=('user', 'plan'), name='unique_user_plan'), - ), - ] diff --git a/pyblackbird_cc/payments/migrations/__init__.py b/pyblackbird_cc/payments/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/pyblackbird_cc/payments/migrations/__init__.py +++ /dev/null diff --git a/pyblackbird_cc/payments/models.py b/pyblackbird_cc/payments/models.py deleted file mode 100644 index a7cb8a7..0000000 --- a/pyblackbird_cc/payments/models.py +++ /dev/null @@ -1,71 +0,0 @@ -from django.conf import settings -from django.core.exceptions import ValidationError -from django.db import models - -from pyblackbird_cc.resources.models import Resource - - -class Product(models.Model): - name = models.CharField(max_length=255) - stripe_product_id = models.CharField(max_length=100) - - -class Price(models.Model): - product = models.ForeignKey(Product, on_delete=models.CASCADE) - price = models.IntegerField(default=0) - stripe_price_id = models.CharField(max_length=100) - - def get_display_price(self): - return "{0:.2f}".format(self.price / 100) - - -class ShoppingCart(models.Model): - user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) - created_at = models.DateTimeField(auto_now_add=True) - updated_at = models.DateTimeField(auto_now=True) - - def __str__(self): - return f"Shopping cart for {self.user.email}" - - def add_resource(self, resource: Resource): - if CartItem.objects.filter(cart=self, resource=resource).exists(): - raise ValidationError(f"{resource.name} is already in your shopping cart.") - item, created = CartItem.objects.get_or_create( - cart=self, - resource=resource, - ) - if not created: - item.save() - - -class CartItem(models.Model): - cart = models.ForeignKey(ShoppingCart, on_delete=models.CASCADE, related_name="items") - resource = models.ForeignKey(Resource, on_delete=models.CASCADE) - added_at = models.DateTimeField(auto_now_add=True) - - class Meta: - unique_together = ("cart", "resource") - - def __str__(self): - return f"{self.resource.name} in {self.cart.user.username}'s cart" - - -class SubscriptionPlan(models.Model): - name = models.CharField(max_length=255) - price = models.DecimalField(max_digits=6, decimal_places=2) - description = models.TextField() - allowed_downloads = models.PositiveIntegerField() - - -class Subscription(models.Model): - user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) - plan = models.ForeignKey(SubscriptionPlan, on_delete=models.CASCADE) - is_active = models.BooleanField(default=False) - start_date = models.DateTimeField(null=True, blank=True) - end_date = models.DateTimeField(null=True, blank=True) - - class Meta: - constraints = [models.UniqueConstraint(fields=["user", "plan"], name="unique_user_plan")] - - def __str__(self): - return f"Subscription for {self.user.username}" diff --git a/pyblackbird_cc/payments/signals.py b/pyblackbird_cc/payments/signals.py deleted file mode 100644 index 50d988b..0000000 --- a/pyblackbird_cc/payments/signals.py +++ /dev/null @@ -1,40 +0,0 @@ -from datetime import timedelta - -from allauth.account.signals import user_signed_up -from django.db import transaction -from django.dispatch import receiver -from django.utils import timezone - -from .models import ShoppingCart -from .models import Subscription -from .models import SubscriptionPlan - - -@receiver(user_signed_up) -def assign_default_subscription(sender, request, user, **kwargs): - with transaction.atomic(): - # Get or create the free plan subscription - free_plan, _ = SubscriptionPlan.objects.get_or_create( - name="Free Plan", - defaults={ - "price": 0, - "description": "Free plan description", - "allowed_downloads": 10, - }, - ) - - # Create a SubscriptionPlan for the new user - Subscription.objects.create( - user=user, - plan=free_plan, - is_active=True, - start_date=timezone.now(), - end_date=timezone.now() + timedelta(days=365), # Example: 30 days - ) - - -@receiver(user_signed_up) -def assign_user_a_shopping_cart(sender, request, user, **kwargs): - with transaction.atomic(): - # Create a ShoppingCart for the new user - ShoppingCart.objects.create(user=user) diff --git a/pyblackbird_cc/payments/tests/__init__.py b/pyblackbird_cc/payments/tests/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/pyblackbird_cc/payments/tests/__init__.py +++ /dev/null diff --git a/pyblackbird_cc/payments/tests/conftest.py b/pyblackbird_cc/payments/tests/conftest.py deleted file mode 100644 index 7919170..0000000 --- a/pyblackbird_cc/payments/tests/conftest.py +++ /dev/null @@ -1,6 +0,0 @@ -import pytest - - -@pytest.fixture -def user_data(): - return {"email": "testuser@example.com", "password": "testpassword123"} diff --git a/pyblackbird_cc/payments/tests/test_models.py b/pyblackbird_cc/payments/tests/test_models.py deleted file mode 100644 index 5f67e01..0000000 --- a/pyblackbird_cc/payments/tests/test_models.py +++ /dev/null @@ -1,116 +0,0 @@ -import pytest -from allauth.account.signals import user_signed_up -from django.contrib.auth import get_user_model -from django.core.exceptions import ValidationError -from django.db import IntegrityError -from django.test import RequestFactory - -from pyblackbird_cc.payments.models import ShoppingCart -from pyblackbird_cc.payments.models import Subscription -from pyblackbird_cc.payments.models import SubscriptionPlan - -User = get_user_model() - - -@pytest.fixture -def user_data(): - return {"email": "testuser@example.com", "password": "testpassword123"} - - -@pytest.mark.django_db -def test_subscription_user_unique(): - # Ensure the free plan exists - free_plan, _ = SubscriptionPlan.objects.get_or_create( - name="Free Plan", - defaults={ - "price": 0, - "description": "Free plan description", - "allowed_downloads": 10, - }, - ) - - # Create a new user - user_data = {"email": "testuser@example.com", "password": "testpassword123"} - user = User.objects.create_user(**user_data) # type: ignore - - # Create a subscription for the user - Subscription.objects.create(user=user, plan=free_plan) - - # Try to create another subscription for the same user - with pytest.raises(IntegrityError): - Subscription.objects.create(user=user, plan=free_plan) - - -@pytest.mark.django_db -def test_user_signup_assigns_free_subscription(user_data): - # Ensure the free plan exists - free_plan, _ = SubscriptionPlan.objects.get_or_create( - name="Free Plan", - defaults={ - "price": 0, - "description": "Free plan description", - "allowed_downloads": 10, - }, - ) # Create a new user - user = User.objects.create_user(**user_data) # type: ignore - # Manually trigger the user_signed_up signal - request = RequestFactory().get("/") - user_signed_up.send(sender=user.__class__, request=request, user=user) - - # Check if a SubscriptionPlan was created for the user - subscription = user.subscription - assert subscription is not None - - # Check if the assigned plan is the free plan - assert subscription.plan == free_plan - - # Additional assertions can be added here to check other properties - # of the SubscriptionPlan or Subscription as needed - - -@pytest.mark.django_db -def test_shopping_cart_is_created_when_user_is_created(user_data): - user = User.objects.create_user(**user_data) # type: ignore - request = RequestFactory().get("/") - user_signed_up.send(sender=user.__class__, request=request, user=user) - shopping_cart = ShoppingCart.objects.get(user=user) - assert shopping_cart is not None - assert str(shopping_cart) == "Shopping cart for {}".format(user.email) - - -# When the user adds a `Resource` to their cart, create a new `CartItem` instance and associate it with the user's `ShoppingCart` and the selected `Resource`. - - -@pytest.mark.django_db -def test_cart_item_is_created_when_resource_is_added_to_cart(user_data, resource): - user = User.objects.create_user(**user_data) # type: ignore - request = RequestFactory().get("/") - user_signed_up.send(sender=user.__class__, request=request, user=user) - users_cart = ShoppingCart.objects.get(user=user) - users_cart.add_resource(resource) - assert users_cart.items.count() == 1 # type: ignore - - -@pytest.mark.django_db -def test_cannot_add_the_same_resource_to_cart_twice(user_data, resource): - user = User.objects.create_user(**user_data) # type: ignore - request = RequestFactory().get("/") - user_signed_up.send(sender=user.__class__, request=request, user=user) - users_cart = ShoppingCart.objects.get(user=user) - users_cart.add_resource(resource) - assert users_cart.items.count() == 1 # type: ignore - with pytest.raises(ValidationError): - users_cart.add_resource(resource) - - -@pytest.mark.django_db -def test_can_add_multiple_different_items_to_cart(user_data, resources): - user = User.objects.create_user(**user_data) # type: ignore - request = RequestFactory().get("/") - user_signed_up.send(sender=user.__class__, request=request, user=user) - users_cart = ShoppingCart.objects.get(user=user) - users_cart.add_resource(resources[0]) - users_cart.add_resource(resources[1]) - users_cart.add_resource(resources[2]) - users_cart.add_resource(resources[3]) - assert users_cart.items.count() == 4 # type: ignore diff --git a/pyblackbird_cc/payments/tests/test_views.py b/pyblackbird_cc/payments/tests/test_views.py deleted file mode 100644 index 7d0f8b5..0000000 --- a/pyblackbird_cc/payments/tests/test_views.py +++ /dev/null @@ -1,19 +0,0 @@ -import pytest -from django.urls import reverse - - -@pytest.mark.django_db -def test_cart_view(client, user): - url = reverse("payments:cart_detail") - client.force_login(user) - response = client.get(url) - assert response.status_code == 200 - assert "My basket" in str(response.content) - - -@pytest.mark.django_db -def test_add_resource_to_cart(client, resource, user): - url = reverse("payments:add_to_cart", kwargs={"resource_id": resource.id}) - client.force_login(user) - response = client.get(url) - assert response.status_code == 200 diff --git a/pyblackbird_cc/payments/urls.py b/pyblackbird_cc/payments/urls.py deleted file mode 100644 index 52e2451..0000000 --- a/pyblackbird_cc/payments/urls.py +++ /dev/null @@ -1,22 +0,0 @@ -from django.urls import path - -from . import views -from .views import CancelView -from .views import SuccessView - -app_name = "payments" - -urlpatterns = [ - path("checkout/", views.checkout, name="checkout"), - path("cart/", views.cart_detail, name="cart_detail"), - path("success/", SuccessView.as_view(), name="success"), - path("cancel/", CancelView.as_view(), name="cancel"), - path( - "create-checkout-session/<int:pk>/", - views.CreateCheckoutSessionView.as_view(), - name="create-checkout-session", - ), - path("add-to-card/<int:resource_id>", views.add_to_cart, name="add_to_cart"), - path("landing/", views.ProductLandingPageView.as_view(), name="landing"), - # path("webhook/", views.webhook, name="webhook"), -] diff --git a/pyblackbird_cc/payments/views.py b/pyblackbird_cc/payments/views.py deleted file mode 100644 index 8c32caa..0000000 --- a/pyblackbird_cc/payments/views.py +++ /dev/null @@ -1,96 +0,0 @@ -import stripe -from django.conf import settings -from django.contrib.auth.decorators import login_required -from django.shortcuts import get_object_or_404 -from django.shortcuts import redirect -from django.shortcuts import render -from django.views import View -from django.views.generic import TemplateView - -from pyblackbird_cc.resources.models import Resource -from pyblackbird_cc.users.models import User - -from .models import CartItem -from .models import Price -from .models import Product -from .models import ShoppingCart - -stripe.api_key = settings.STRIPE_SECRET_KEY - - -class CreateCheckoutSessionView(View): - def post(self, request, *args, **kwargs): - price = Price.objects.get(id=self.kwargs["pk"]) - domain = "http://localhost:8000" - checkout_session = stripe.checkout.Session.create( - payment_method_types=["card"], - line_items=[ - { - "price": price.stripe_price_id, - "quantity": 1, - }, - ], - mode="payment", - success_url=domain + "payments/success/", - cancel_url=domain + "payments/cancel/", - ) - return redirect(checkout_session.url, code=303) - - -class SuccessView(TemplateView): - template_name = "payments/success.html" - - -class CancelView(TemplateView): - template_name = "payments/cancel.html" - - -class ProductLandingPageView(TemplateView): - template_name = "payments/landingpage.html" - - def get_context_data(self, **kwargs): - product = Product.objects.get(name="Worksheet 1") - prices = Price.objects.filter(product=product) - context = super(ProductLandingPageView, self).get_context_data(**kwargs) - context.update({"product": product, "prices": prices}) - return context - - -@login_required -def add_to_cart(request, resource_id): - resource = get_object_or_404(Resource, id=resource_id) - cart, created = ShoppingCart.objects.get_or_create(user=request.user) - cart_item, created = CartItem.objects.get_or_create(cart=cart, resource=resource) - # cart_item.quantity += 1 - cart_item.save() - return render(request, "payments/cart_detail.html", {"cart": cart}) - - -@login_required -def cart_detail(request): - cart, created = ShoppingCart.objects.get_or_create(user=request.user) - return render(request, "payments/cart_detail.html", {"cart": cart}) - - -# def cart_detail(request): -# cart, created = ShoppingCart.objects.get_or_create(user=request.user) -# return render(request, "payments/cart_detail.html", {"cart": cart}) - - -@login_required -def checkout(request): - cart = ShoppingCart.objects.get(user=request.user) - total = sum(item.get_total_price() for item in cart.items.all()) - - if request.method == "POST": - # Create Stripe PaymentIntent - intent = stripe.PaymentIntent.create( - amount=int(total * 100), # Stripe amount is in cents - currency="usd", - automatic_payment_methods={"enabled": True}, - ) - - # Redirect to Stripe checkout or handle payment confirmation - return render(request, "cart/checkout_success.html") - - return render(request, "cart/checkout.html", {"cart": cart, "total": total}) |