integrace api, stripe, vytvoření commecre app

This commit is contained in:
2025-10-05 23:41:14 +02:00
parent f5cf8bbaa7
commit 10796dcb31
26 changed files with 436 additions and 140 deletions

View File

@@ -1,3 +1,23 @@
from django.contrib import admin
from .models import Order
# Register your models here.
@admin.register(Order)
class OrderAdmin(admin.ModelAdmin):
list_display = ("id", "amount", "currency", "status", "created_at")
list_filter = ("status", "currency", "created_at")
search_fields = ("id", "stripe_session_id", "stripe_payment_intent")
readonly_fields = ("created_at", "stripe_session_id", "stripe_payment_intent")
fieldsets = (
(None, {
"fields": ("amount", "currency", "status")
}),
("Stripe info", {
"fields": ("stripe_session_id", "stripe_payment_intent"),
"classes": ("collapse",),
}),
("Metadata", {
"fields": ("created_at",),
}),
)
ordering = ("-created_at",)

View File

@@ -1,3 +1,21 @@
from django.db import models
# Create your models here.
class Order(models.Model):
STATUS_CHOICES = [
("pending", "Pending"),
("paid", "Paid"),
("failed", "Failed"),
("cancelled", "Cancelled"),
]
amount = models.DecimalField(max_digits=10, decimal_places=2)
currency = models.CharField(max_length=10, default="czk")
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default="pending")
stripe_session_id = models.CharField(max_length=255, blank=True, null=True)
stripe_payment_intent = models.CharField(max_length=255, blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"Order {self.id} - {self.status}"

View File

@@ -1,12 +1,54 @@
from rest_framework import serializers
from rest_framework import serializers
from .models import Product, Carrier, Order
class StripeCheckoutRequestSerializer(serializers.Serializer):
amount = serializers.DecimalField(max_digits=12, decimal_places=2, min_value=0.01)
product_name = serializers.CharField(required=False, default="Example Product")
success_url = serializers.URLField(required=False)
cancel_url = serializers.URLField(required=False)
from ...commerce.serializers import ProductSerializer, CarrierSerializer
class StripeCheckoutResponseSerializer(serializers.Serializer):
url = serializers.URLField()
class OrderSerializer(serializers.ModelSerializer):
product = ProductSerializer(read_only=True)
product_id = serializers.PrimaryKeyRelatedField(
queryset=Product.objects.all(), source="product", write_only=True
)
carrier = CarrierSerializer(read_only=True)
carrier_id = serializers.PrimaryKeyRelatedField(
queryset=Carrier.objects.all(), source="carrier", write_only=True
)
class Meta:
model = Order
fields = [
"id",
"product", "product_id",
"carrier", "carrier_id",
"quantity",
"total_price",
"status",
"stripe_session_id",
"created_at",
"updated_at",
]
read_only_fields = ("total_price", "status", "stripe_session_id", "created_at", "updated_at")
queryset=Product.objects.all(), source="product", write_only=True
carrier = CarrierSerializer(read_only=True)
carrier_id = serializers.PrimaryKeyRelatedField(
queryset=Carrier.objects.all(), source="carrier", write_only=True
)
class Meta:
model = Order
fields = [
"id",
"product", "product_id",
"carrier", "carrier_id",
"quantity",
"total_price",
"status",
"stripe_session_id",
"created_at",
"updated_at",
]
read_only_fields = ("total_price", "status", "stripe_session_id", "created_at", "updated_at")

View File

@@ -1,6 +1,6 @@
from django.urls import path
from .views import StripeCheckoutCZKView
from .views import CreateCheckoutSessionView
urlpatterns = [
path('checkout/', StripeCheckoutCZKView.as_view(), name='stripe-checkout-czk'),
path("orders/create-checkout/", CreateCheckoutSessionView.as_view(), name="create-checkout"),
]

View File

@@ -1,71 +1,73 @@
import stripe
import os
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from django.views.decorators.csrf import csrf_exempt
from django.conf import settings
from django.http import HttpResponse
from rest_framework import generics
from rest_framework.response import Response
from rest_framework.views import APIView
from drf_spectacular.utils import extend_schema, OpenApiResponse, OpenApiExample, OpenApiParameter
from .serializers import (
StripeCheckoutRequestSerializer,
StripeCheckoutResponseSerializer,
)
from .models import Order
from .serializers import OrderSerializer
stripe.api_key = os.getenv("STRIPE_SECRET_KEY")
import stripe
stripe.api_key = settings.STRIPE_SECRET_KEY # uložený v .env
class StripeCheckoutCZKView(APIView):
permission_classes = [IsAuthenticated]
@extend_schema(
tags=["Stripe"],
summary="Create Stripe Checkout session in CZK",
description="Creates a Stripe Checkout session for payment in Czech Koruna (CZK). Requires authentication.",
request=StripeCheckoutRequestSerializer,
responses={
200: OpenApiResponse(response=StripeCheckoutResponseSerializer, description="Stripe Checkout session URL returned successfully."),
400: OpenApiResponse(description="Amount is required or invalid."),
},
examples=[
OpenApiExample(
"Success",
value={"url": "https://checkout.stripe.com/pay/cs_test_123456"},
response_only=True,
status_codes=["200"],
),
OpenApiExample(
"Missing amount",
value={"error": "Amount is required"},
response_only=True,
status_codes=["400"],
),
]
)
class CreateCheckoutSessionView(APIView):
def post(self, request):
serializer = StripeCheckoutRequestSerializer(data=request.data)
if not serializer.is_valid():
return Response(serializer.errors, status=400)
serializer = OrderSerializer(data=request.data) #obecný serializer
serializer.is_valid(raise_exception=True)
amount = serializer.validated_data.get("amount")
product_name = serializer.validated_data.get("product_name", "Example Product")
success_url = serializer.validated_data.get("success_url", "https://yourfrontend.com/success")
cancel_url = serializer.validated_data.get("cancel_url", "https://yourfrontend.com/cancel")
# Stripe expects amount in the smallest currency unit (haléř = 1/100 CZK)
amount_in_haler = int(amount * 100)
session = stripe.checkout.Session.create(
payment_method_types=['card'],
line_items=[{
'price_data': {
'currency': 'czk',
'product_data': {
'name': product_name,
},
'unit_amount': amount_in_haler,
},
'quantity': 1,
}],
mode='payment',
success_url=success_url,
cancel_url=cancel_url,
customer_email=getattr(request.user, 'email', None)
order = Order.objects.create(
amount=serializer.validated_data["amount"],
currency=serializer.validated_data.get("currency", "czk"),
)
return Response({"url": session.url})
# Vytvoření Stripe Checkout Session
session = stripe.checkout.Session.create(
payment_method_types=["card"],
line_items=[{
"price_data": {
"currency": order.currency,
"product_data": {"name": f"Order {order.id}"},
"unit_amount": int(order.amount * 100), # v centech
},
"quantity": 1,
}],
mode="payment",
success_url=request.build_absolute_uri(f"/payment/success/{order.id}"),
cancel_url=request.build_absolute_uri(f"/payment/cancel/{order.id}"),
)
order.stripe_session_id = session.id
order.stripe_payment_intent = session.payment_intent
order.save()
data = OrderSerializer(order).data
data["checkout_url"] = session.url
return Response(data)
@csrf_exempt
def stripe_webhook(request):
payload = request.body
sig_header = request.META.get("HTTP_STRIPE_SIGNATURE")
event = None
try:
event = stripe.Webhook.construct_event(
payload, sig_header, settings.STRIPE_WEBHOOK_SECRET
)
except stripe.error.SignatureVerificationError:
return HttpResponse(status=400)
if event["type"] == "checkout.session.completed":
session = event["data"]["object"]
order = Order.objects.filter(stripe_session_id=session.get("id")).first()
if order:
order.status = "paid"
order.save()
return HttpResponse(status=200)