chore: initialize tuhui repository
This commit is contained in:
267
backend/app/api/payment.py
Normal file
267
backend/app/api/payment.py
Normal file
@@ -0,0 +1,267 @@
|
||||
#!/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": "订单已取消"}
|
||||
Reference in New Issue
Block a user