142 lines
4.8 KiB
Python
142 lines
4.8 KiB
Python
# -*- coding = utf-8 -*-
|
|
# @Time : 2025/2/24 上午9:03
|
|
# @File : routes.py
|
|
# @Software : PyCharm
|
|
# @Author : xingc
|
|
# @Desc :
|
|
import logging
|
|
from xml.parsers.expat import ExpatError
|
|
|
|
import xmltodict
|
|
from fastapi import APIRouter, Request, HTTPException, status, Depends, BackgroundTasks
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from core.base import schema as base_schema
|
|
from core.db.crud import CRUD
|
|
from core.db.engine import get_async_session
|
|
from core.db.models import Order, Product
|
|
from core.routers.auth.schema import LoginUser
|
|
from core.routers.auth.services import get_current_user_form_http
|
|
from core.utils.helper import generate_keyname, generate_order_number
|
|
from core.wechat.api import wechat_pay_api
|
|
|
|
from . import schema
|
|
from .actions import actions_after_payment
|
|
|
|
logger = logging.getLogger(__name__)
|
|
pay_router = APIRouter(prefix='/pay', tags=['支付模块'])
|
|
|
|
|
|
@pay_router.get(
|
|
'/order/check/{order_id}',
|
|
summary='获取订单状态',
|
|
response_model=base_schema.BaseResponse[schema.CheckOrderOut])
|
|
async def check_order(
|
|
order_id: str,
|
|
login_user: LoginUser = Depends(get_current_user_form_http),
|
|
db_session: AsyncSession = Depends(get_async_session)
|
|
):
|
|
order = await CRUD(db_session, Order).get(filters={
|
|
'order_id': order_id, 'user_id': login_user.user_id
|
|
})
|
|
if order is None:
|
|
raise HTTPException(status.HTTP_404_NOT_FOUND, detail='订单不存在')
|
|
return {'data': order}
|
|
|
|
|
|
@pay_router.post(
|
|
'/order/create',
|
|
summary='订单创建',
|
|
response_model=base_schema.BaseResponse[schema.CreateOrderOut]
|
|
)
|
|
async def create_order(
|
|
request: Request,
|
|
payload: schema.CreateOrderIn,
|
|
login_user: LoginUser = Depends(get_current_user_form_http),
|
|
db_session: AsyncSession = Depends(get_async_session),
|
|
):
|
|
db_crud = CRUD(db_session, Order)
|
|
product = await db_crud.get(model=Product, filters={'id': payload.product_id})
|
|
if product is None:
|
|
raise HTTPException(status.HTTP_404_NOT_FOUND, detail='产品不存在')
|
|
|
|
if product.price != payload.total_fee:
|
|
raise HTTPException(status.HTTP_400_BAD_REQUEST, detail='产品费用不合法')
|
|
|
|
client_ip = (
|
|
request.headers.get('X-Real-IP') or
|
|
request.headers.get('x-real-ip') or
|
|
request.client.host
|
|
)
|
|
out_trade_no = generate_order_number()
|
|
prepay_id = await wechat_pay_api.create_unified_order(
|
|
**payload.model_dump(exclude_none=True, exclude={'product_id'}),
|
|
out_trade_no=out_trade_no,
|
|
create_ip=client_ip
|
|
)
|
|
payment_params = wechat_pay_api.generate_payment_params(prepay_id)
|
|
|
|
order: Order = await db_crud.create(
|
|
data={
|
|
'order_id': out_trade_no,
|
|
'user_id': login_user.user_id,
|
|
'product_id': payload.product_id,
|
|
'total_fee': payload.total_fee,
|
|
'status': base_schema.OrderStatusType.WAIT_PAYMENT
|
|
},
|
|
filters={
|
|
'order_id': out_trade_no,
|
|
}
|
|
)
|
|
return {'data': {'payment_params': payment_params, 'order_id': order.order_id}}
|
|
|
|
|
|
@pay_router.post('/wechat/notify', summary='处理支付结果通知', response_model=base_schema.BaseResponse)
|
|
async def wechat_notify(
|
|
request: Request,
|
|
background_tasks: BackgroundTasks,
|
|
db_session: AsyncSession = Depends(get_async_session)
|
|
):
|
|
body = await request.body()
|
|
logger.debug(body.decode())
|
|
try:
|
|
result = xmltodict.parse(body.decode())
|
|
except ExpatError as e:
|
|
raise HTTPException(status.HTTP_422_UNPROCESSABLE_ENTITY, detail='无法处理的实体') from e
|
|
|
|
# 验证签名
|
|
params = result.get('xml', {})
|
|
sign = params.pop('sign', None)
|
|
if sign is None:
|
|
raise HTTPException(status.HTTP_422_UNPROCESSABLE_ENTITY, detail='无法处理的实体')
|
|
|
|
local_sign = wechat_pay_api.generate_sign(params)
|
|
if sign != local_sign:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail='签名验证失败'
|
|
)
|
|
|
|
# 处理支付结果
|
|
db_crud = CRUD(db_session, Order)
|
|
record = {
|
|
'order_id': params['out_trade_no'],
|
|
'transaction_id': params['transaction_id']
|
|
}
|
|
if params['return_code'] == 'SUCCESS' and params['result_code'] == 'SUCCESS':
|
|
record.update({
|
|
'notify_total_fee': params['total_fee'],
|
|
'status': base_schema.OrderStatusType.PAID
|
|
})
|
|
result = {'status': 200, 'message': 'OK'}
|
|
background_tasks.add_task(actions_after_payment, notify_params=params, db_session=db_session)
|
|
else:
|
|
result = {'status': 400, 'message': '支付失败'}
|
|
record.update({
|
|
'status': base_schema.OrderStatusType.FAIL,
|
|
})
|
|
|
|
await db_crud.update(data=record, filters={'order_id': record['order_id']})
|
|
|
|
return result
|