# -*- 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 asyncer import asyncify from fastapi import APIRouter, Request, HTTPException, status, Depends, Response from sqlalchemy.ext.asyncio import AsyncSession from core.base import schema as base_schema from core.base.scheduler import add_task 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='处理支付结果通知') async def wechat_notify( request: Request, db_session: AsyncSession = Depends(get_async_session) ): def generate_response(): xml_response = """ """ return Response(content=xml_response.strip(), media_type="application/xml") 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: return generate_response() # 处理支付结果 db_crud = CRUD(db_session, Order) order = await db_crud.get(filters={'order_id': params['out_trade_no']}) if order and order.status.value not in ['ok', 'fail']: 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 }) await asyncify(add_task)( actions_after_payment, notify_params=params, task_id=record['order_id'], ) else: record.update({ 'status': base_schema.OrderStatusType.FAIL, }) await db_crud.update( data=record, filters={ 'order_id': record['order_id'], 'status': base_schema.OrderStatusType.WAIT_PAYMENT } ) return generate_response()