#!/usr/bin/env python # -*- coding: utf-8 -*- """支付相关接口""" from fastapi import APIRouter, Depends, HTTPException, Request, BackgroundTasks from sqlalchemy.orm import Session from sqlalchemy import and_ from datetime import datetime import json from app.core.database import get_db from app.core.config import settings from app.models.order import Order, OrderStatus from app.models.work import Work from app.models.download import DownloadRecord from app.ysm_sdk import create_payment, query_order, PaymentNotify from app.api.orders import get_current_user from app.models.user import User router = APIRouter(prefix="/payment", tags=["支付"]) @router.post("/create/{order_id}") async def create_payment_url( order_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """ 创建支付链接(真实支付) Args: order_id: 订单ID Returns: { "pay_url": "支付链接", "order_number": "订单号", "amount": 金额(分) } """ # 查询订单 order = db.query(Order).filter( and_( Order.id == order_id, Order.user_id == current_user.id ) ).first() if not order: raise HTTPException(status_code=404, detail="订单不存在") # 检查订单状态 if order.status == OrderStatus.PAID: raise HTTPException(status_code=400, detail="订单已支付") if order.status == OrderStatus.CANCELLED: raise HTTPException(status_code=400, detail="订单已取消") # 查询作品信息 work = db.query(Work).filter(Work.id == order.work_id).first() if not work: raise HTTPException(status_code=404, detail="作品不存在") try: # 创建支付 pay_url = await create_payment( appid=settings.YSM_APPID, appsecret=settings.YSM_APPSECRET, order_id=order.order_no, # 使用 order_no 字段 description=f"{work.title} - 爱设计作品购买", amount=int(order.amount * 100), # 元转分 notify_url=settings.YSM_NOTIFY_URL, nopay_url=settings.YSM_NOPAY_URL, callback_url=settings.YSM_CALLBACK_URL, pay_type=1 # 默认微信内支付 ) return { "pay_url": pay_url, "order_number": order.order_no, # 使用 order_no 字段 "amount": int(order.amount * 100), "description": f"{work.title} - 爱设计作品购买" } except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.post("/notify") async def payment_notify( request: Request, background_tasks: BackgroundTasks, db: Session = Depends(get_db) ): """ 支付回调接口(易收米异步通知) 当用户支付成功后,支付平台会异步请求此接口 """ # 创建通知处理器 notify_handler = PaymentNotify( appid=settings.YSM_APPID, appsecret=settings.YSM_APPSECRET ) # 获取请求数据 try: request_data = await request.body() data = json.loads(request_data.decode('utf-8')) except Exception as e: return {"error": "数据格式错误"} # 验证签名 success, message = await notify_handler.process(data) if not success: return {"error": message} # 获取商户订单号 mch_orderid = data.get('mch_orderid') # 查询订单 order = db.query(Order).filter(Order.order_no == mch_orderid).first() if not order: return {"error": "订单不存在"} # 防止重复处理 if order.status == OrderStatus.PAID: return "success" # 更新订单状态 if data.get('state') == 'SUCCESS': try: # 更新订单 order.status = OrderStatus.PAID order.payment_method = "ysm_wechat" # 易收米微信支付 order.paid_at = datetime.now() # 创建下载记录 download_record = DownloadRecord( user_id=order.user_id, work_id=order.work_id, order_id=order.id ) db.add(download_record) # 更新作品下载次数 work = db.query(Work).filter(Work.id == order.work_id).first() if work: work.downloads += 1 db.commit() return "success" except Exception as e: db.rollback() print(f"处理支付回调失败: {str(e)}") return {"error": "处理失败"} return {"error": "支付未完成"} @router.get("/query/{order_number}") async def query_order_status( order_number: str, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """ 查询订单支付状态 Args: order_number: 订单号 Returns: 订单状态信息 """ # 查询本地订单 order = db.query(Order).filter( and_( Order.order_no == order_number, Order.user_id == current_user.id ) ).first() if not order: raise HTTPException(status_code=404, detail="订单不存在") try: # 查询易收米订单状态 result = await query_order( appid=settings.YSM_APPID, mch_orderid=order_number ) if result: # 如果远程订单已支付,但本地订单未更新,则更新本地订单 if result.get('state') == 'SUCCESS' and order.status != OrderStatus.PAID: order.status = OrderStatus.PAID order.payment_method = "ysm_wechat" order.paid_at = datetime.now() # 创建下载记录 download_record = DownloadRecord( user_id=order.user_id, work_id=order.work_id, order_id=order.id ) db.add(download_record) # 更新作品下载次数 work = db.query(Work).filter(Work.id == order.work_id).first() if work: work.downloads += 1 db.commit() return { "order_number": order_number, "local_status": order.status, "remote_status": result.get('state'), "amount": order.amount, "paid_at": order.paid_at.isoformat() if order.paid_at else None, "remote_info": result } else: raise HTTPException(status_code=500, detail="查询订单失败") except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.post("/cancel/{order_id}") async def cancel_order( order_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """ 取消订单 Args: order_id: 订单ID """ # 查询订单 order = db.query(Order).filter( and_( Order.id == order_id, Order.user_id == current_user.id ) ).first() if not order: raise HTTPException(status_code=404, detail="订单不存在") # 只能取消待支付的订单 if order.status != OrderStatus.PENDING: raise HTTPException(status_code=400, detail="订单状态不允许取消") order.status = OrderStatus.CANCELLED db.commit() return {"message": "订单已取消"}