22import uuid
33from fastapi import APIRouter , HTTPException
44from pydantic import BaseModel
5- from datetime import datetime
5+ from botocore .exceptions import ClientError
6+ from datetime import datetime , timedelta
67import stripe
78from be .api .v1 .templates .non_auth_route import create_non_auth_router
89from be .api .v1 .models .cart import PriceModel , Cart
10+ from be .api .v1 .models .order_hold_entry import OrderHoldEntry , ReservedProduct
911from be .api .v1 .models .orders import OrderItem , Order , OrderStatus
1012from be .api .v1 .utils .cart_utils import calc_cart_value , describe_cart , generate_order_items_from_cart
1113from utils .dal .order import dal_create_order
14+ from utils .dal .products import dal_increment_stock_count
15+ from utils .dal .order_hold import dal_create_order_hold_entry
1216
1317stripe .api_key = os .environ ["STRIPE_SECRET_KEY" ]
18+ DEFAULT_ORDER_EXPIRY_TIME = 1
1419
1520router = APIRouter (prefix = "/cart/checkout" , tags = ["cart" ])
1621
@@ -30,6 +35,7 @@ class PostCheckoutResponseModel(BaseModel):
3035 items : list [OrderItem ]
3136 price : PriceModel
3237 payment : PaymentModel
38+ expiry : int
3339
3440
3541@router .post ("" , response_model = PostCheckoutResponseModel )
@@ -45,10 +51,9 @@ async def post_checkout(req: CheckoutRequestBodyModel):
4551 # calculate subtotal
4652 items_products = generate_order_items_from_cart (req )
4753
54+ orderID = uuid .uuid4 ().__str__ ()
4855 price = calc_cart_value (items_products )
49- description = describe_cart (items_products )
50-
51- # todo: create "pending" order here - in db
56+ description = describe_cart (items_products , orderID )
5257
5358 payment_intent = stripe .PaymentIntent .create (
5459 payment_method_types = ["paynow" ],
@@ -58,14 +63,13 @@ async def post_checkout(req: CheckoutRequestBodyModel):
5863 receipt_email = req .email ,
5964 description = f"SCSE Merch Purchase:\n { description } "
6065 )
61-
62- orderID = uuid .uuid4 ().__str__ ()
6366 orderDateTime = datetime .now ().__str__ ()
6467 customerEmail = req .email
6568 transactionID = payment_intent .id
6669 paymentPlatform = "stripe"
6770 orderItems = items_products
6871 status = OrderStatus .PENDING_PAYMENT
72+ expiry = datetime .now () + timedelta (hours = int (os .environ .get ("ORDER_EXPIRY_TIME" , DEFAULT_ORDER_EXPIRY_TIME )))
6973
7074 order = Order (
7175 orderID = orderID ,
@@ -77,7 +81,14 @@ async def post_checkout(req: CheckoutRequestBodyModel):
7781 status = status
7882 )
7983
84+ for orderItem in orderItems :
85+ dal_increment_stock_count (orderItem .id , - orderItem .quantity , orderItem .size , orderItem .colorway )
86+
87+ reservedProducts = [ReservedProduct (productID = item .productId , qty = item .quantity ) for item in req .items ]
88+ orderHoldEntry = OrderHoldEntry (transactionID = transactionID , expiry = int (expiry .timestamp ()), reservedProducts = reservedProducts )
89+
8090 dal_create_order (order )
91+ dal_create_order_hold_entry (orderHoldEntry )
8192
8293 return {
8394 "orderId" : orderID ,
@@ -87,11 +98,18 @@ async def post_checkout(req: CheckoutRequestBodyModel):
8798 "paymentGateway" : "stripe" ,
8899 "clientSecret" : payment_intent .client_secret
89100 },
90- "email" : req .email
101+ "email" : req .email ,
102+ "expiry" : int (expiry .timestamp ())
91103 }
92104
105+ except ClientError as e :
106+ if e .response ['Error' ]['Code' ] == 'ConditionalCheckFailedException' :
107+ raise HTTPException (status_code = 400 , detail = "Current quantity cannot be less than 0 and must be available for sale" )
108+ else :
109+ raise HTTPException (status_code = 500 , detail = e )
93110
94111 except Exception as e :
112+ print ("Error checking out:" , e )
95113 raise HTTPException (status_code = 500 , detail = e )
96114
97115
0 commit comments