DeepLearning101 commited on
Commit
0928d52
·
verified ·
1 Parent(s): cee18dd

Update main.py

Browse files

💣 地雷一:空購物車或異常資料會讓「庫存 API」直接崩潰 (Server Error 500)
💣 地雷二:「補付款 API」金額鎖死 1000 元,外帶掉單會虧錢!

Files changed (1) hide show
  1. main.py +31 -14
main.py CHANGED
@@ -63,7 +63,7 @@ class RepayPayload(BaseModel):
63
  order_id: str
64
 
65
  # ==========================================
66
- # 🌟 新增:背景自動救援掉單機器人 🌟
67
  # ==========================================
68
  async def auto_rescue_dropped_order(order_id: str, amount: int):
69
  # 讓程式在背景默默等待 3 分鐘 (180秒)
@@ -135,7 +135,6 @@ async def auto_rescue_dropped_order(order_id: str, amount: int):
135
  def read_root():
136
  return {"status": "online", "message": "Cié Cié FastAPI is running."}
137
 
138
- # 🌟 修改:加入 background_tasks 參數 🌟
139
  @app.post("/api/submit_booking")
140
  async def submit_booking(payload: OrderPayload, background_tasks: BackgroundTasks):
141
  if not supabase:
@@ -238,10 +237,7 @@ async def confirm_payment(payload: ConfirmPayload):
238
  res_data = res.json()
239
 
240
  if res_data.get("returnCode") == "0000":
241
- # 1. 更新資料庫狀態為已付款
242
  update_res = supabase.table("bookings").update({"status": "待處理 (已付訂金)"}).ilike("remarks", f"%{payload.order_id}%").execute()
243
-
244
- # 2. 發送通知給老闆
245
  if update_res.data:
246
  b = update_res.data[0]
247
  notify_boss(b['name'], b['tel'], b['date'], b['time'], b['pax'], payload.amount)
@@ -252,7 +248,7 @@ async def confirm_payment(payload: ConfirmPayload):
252
  except Exception as e:
253
  raise HTTPException(status_code=500, detail=str(e))
254
 
255
- # 處理重新產生付款連結的 API
256
  @app.post("/api/linepay/repay")
257
  async def repay_payment(payload: RepayPayload):
258
  if not supabase: raise HTTPException(status_code=500, detail="資料庫未連線")
@@ -265,7 +261,22 @@ async def repay_payment(payload: RepayPayload):
265
  if "已付" in booking.get("status", "") or "確認" in booking.get("status", ""):
266
  raise HTTPException(status_code=400, detail="此訂單已完成付款或確認,無需重新結帳")
267
 
268
- amount = 1000
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
269
  new_order_id = f"{payload.order_id}-R{int(time.time())}"
270
 
271
  request_body = {
@@ -273,8 +284,8 @@ async def repay_payment(payload: RepayPayload):
273
  "currency": "TWD",
274
  "orderId": new_order_id,
275
  "packages": [{
276
- "id": "pkg_repay", "amount": amount, "name": "Cié Cié Taipei 補繳訂金",
277
- "products": [{"name": "餐飲訂金", "quantity": 1, "price": amount}]
278
  }],
279
  "redirectUrls": {
280
  "confirmUrl": f"{RETURN_URL}?action=payment_confirm&amount={amount}&orderId={payload.order_id}",
@@ -315,7 +326,7 @@ def notify_boss(name, tel, date, time, pax, amount):
315
  except: pass
316
 
317
  # ==========================================
318
- # 🌟 新增:計算某日庫存與銷量的 API 🌟
319
  # ==========================================
320
  @app.get("/api/inventory/{query_date}")
321
  async def get_inventory(query_date: str):
@@ -330,15 +341,21 @@ async def get_inventory(query_date: str):
330
  if "取消" in b.get("status", "") or "No-Show" in b.get("status", ""):
331
  continue
332
 
 
 
 
 
 
 
 
 
333
  # 計算這筆訂單買了什麼
334
- cart = b.get("cart", {})
335
  for item_id, qty in cart.items():
336
- # 防呆:確保 qty 是整數
337
  try: qty = int(qty)
338
  except: qty = 0
339
  sold_counts[item_id] = sold_counts.get(item_id, 0) + qty
340
 
341
- return sold_counts # 回傳格式如:{"steak": 2, "fries": 5}
342
  except Exception as e:
343
  print(f"Inventory Error: {e}")
344
- return {}
 
63
  order_id: str
64
 
65
  # ==========================================
66
+ # 🌟 背景自動救援掉單機器人 🌟
67
  # ==========================================
68
  async def auto_rescue_dropped_order(order_id: str, amount: int):
69
  # 讓程式在背景默默等待 3 分鐘 (180秒)
 
135
  def read_root():
136
  return {"status": "online", "message": "Cié Cié FastAPI is running."}
137
 
 
138
  @app.post("/api/submit_booking")
139
  async def submit_booking(payload: OrderPayload, background_tasks: BackgroundTasks):
140
  if not supabase:
 
237
  res_data = res.json()
238
 
239
  if res_data.get("returnCode") == "0000":
 
240
  update_res = supabase.table("bookings").update({"status": "待處理 (已付訂金)"}).ilike("remarks", f"%{payload.order_id}%").execute()
 
 
241
  if update_res.data:
242
  b = update_res.data[0]
243
  notify_boss(b['name'], b['tel'], b['date'], b['time'], b['pax'], payload.amount)
 
248
  except Exception as e:
249
  raise HTTPException(status_code=500, detail=str(e))
250
 
251
+ # 處理重新產生付款連結的 API (防呆升級版)
252
  @app.post("/api/linepay/repay")
253
  async def repay_payment(payload: RepayPayload):
254
  if not supabase: raise HTTPException(status_code=500, detail="資料庫未連線")
 
261
  if "已付" in booking.get("status", "") or "確認" in booking.get("status", ""):
262
  raise HTTPException(status_code=400, detail="此訂單已完成付款或確認,無需重新結帳")
263
 
264
+ # 🌟 修改:向 LINE Pay 查詢最初應付的正確金額,避免外帶單只收 1000 元 🌟
265
+ amount = 1000 # 預設防護底線
266
+ try:
267
+ chk_uri = "/v3/payments"
268
+ chk_query = urllib.parse.urlencode({"orderId": payload.order_id})
269
+ chk_nonce = str(uuid.uuid4())
270
+ chk_msg = LINE_PAY_CHANNEL_SECRET + chk_uri + chk_query + chk_nonce
271
+ chk_sig = base64.b64encode(hmac.new(LINE_PAY_CHANNEL_SECRET.encode(), chk_msg.encode(), hashlib.sha256).digest()).decode()
272
+ chk_headers = { "Content-Type": "application/json", "X-LINE-ChannelId": LINE_PAY_CHANNEL_ID, "X-LINE-Authorization-Nonce": chk_nonce, "X-LINE-Authorization": chk_sig }
273
+ chk_res = requests.get(f"{LINE_PAY_BASE_URL}{chk_uri}?{chk_query}", headers=chk_headers).json()
274
+ if chk_res.get("returnCode") == "0000" and chk_res.get("info"):
275
+ # 成功抓出這筆訂單原本設定的金額!
276
+ amount = chk_res["info"][0].get("payInfo", [{}])[0].get("amount", 1000)
277
+ except Exception as e:
278
+ print(f"無法取得原始金額,使用預設值: {e}")
279
+
280
  new_order_id = f"{payload.order_id}-R{int(time.time())}"
281
 
282
  request_body = {
 
284
  "currency": "TWD",
285
  "orderId": new_order_id,
286
  "packages": [{
287
+ "id": "pkg_repay", "amount": amount, "name": "Cié Cié Taipei 補繳結帳",
288
+ "products": [{"name": "餐飲訂金或外帶全額", "quantity": 1, "price": amount}]
289
  }],
290
  "redirectUrls": {
291
  "confirmUrl": f"{RETURN_URL}?action=payment_confirm&amount={amount}&orderId={payload.order_id}",
 
326
  except: pass
327
 
328
  # ==========================================
329
+ # 🌟 計算某日庫存與銷量的 API (防護升級版) 🌟
330
  # ==========================================
331
  @app.get("/api/inventory/{query_date}")
332
  async def get_inventory(query_date: str):
 
341
  if "取消" in b.get("status", "") or "No-Show" in b.get("status", ""):
342
  continue
343
 
344
+ # 🌟 修改:加入型別檢查防護,避免 null 或 字串 引發當機
345
+ cart = b.get("cart")
346
+ if not cart:
347
+ cart = {}
348
+ elif isinstance(cart, str):
349
+ try: cart = json.loads(cart)
350
+ except: cart = {}
351
+
352
  # 計算這筆訂單買了什麼
 
353
  for item_id, qty in cart.items():
 
354
  try: qty = int(qty)
355
  except: qty = 0
356
  sold_counts[item_id] = sold_counts.get(item_id, 0) + qty
357
 
358
+ return sold_counts
359
  except Exception as e:
360
  print(f"Inventory Error: {e}")
361
+ return {}