252 lines
7.2 KiB
Python
252 lines
7.2 KiB
Python
#!/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
|
|
from app.services.download_tracker import record_download
|
|
|
|
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()
|
|
|
|
work = db.query(Work).filter(Work.id == order.work_id).first()
|
|
buyer = db.query(User).filter(User.id == order.user_id).first()
|
|
if work and buyer:
|
|
record_download(db, order, work, buyer)
|
|
|
|
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()
|
|
|
|
work = db.query(Work).filter(Work.id == order.work_id).first()
|
|
if work:
|
|
record_download(db, order, work, current_user)
|
|
|
|
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": "订单已取消"}
|