Files
trade-assistant/backend/app/api/v1/payment.py
T
TradeMate Dev 3e39cf0170 refactor: replace direct WeChat/Alipay with unified pay-api gateway
Switch from direct WeChat Pay / Alipay integrations to the unified
宇之然 pay-api gateway (HMAC-SHA256 auth). Removes wechat_pay.py,
keeps PaymentGateway abstraction, adds UnifiedPayService. Simplifies
payment.py create_order to {plan, pay_type} params. Single webhook
endpoint replaces separate WeChat/Alipay notify handlers.
2026-05-29 18:36:50 +08:00

114 lines
3.1 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, Request, Query
from sqlalchemy.ext.asyncio import AsyncSession
from pydantic import BaseModel
from typing import Optional
from app.database import get_db
from app.services.payment import PaymentService
from app.api.v1.deps import get_current_user_id
from app.core.csrf import require_csrf_token
router = APIRouter()
class CreateOrderRequest(BaseModel):
plan: str
pay_type: str = "alipay"
class RefundRequest(BaseModel):
order_no: str
reason: str = ""
@router.get("/plans")
async def get_plans():
svc = PaymentService(None)
return await svc.get_plans()
@router.get("/subscription")
async def get_subscription(
user_id: str = Depends(get_current_user_id),
db: AsyncSession = Depends(get_db),
):
svc = PaymentService(db)
return await svc.get_current_subscription(user_id)
@router.post("/create-order")
async def create_order(
data: CreateOrderRequest,
user_id: str = Depends(get_current_user_id),
db: AsyncSession = Depends(get_db),
_csrf: str = Depends(require_csrf_token),
):
svc = PaymentService(db)
try:
return await svc.create_order(user_id, data.plan, data.pay_type)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
@router.get("/query/{order_no}")
async def query_payment(
order_no: str,
user_id: str = Depends(get_current_user_id),
db: AsyncSession = Depends(get_db),
):
svc = PaymentService(db)
try:
return await svc.query_payment(user_id, order_no)
except ValueError as e:
raise HTTPException(status_code=404, detail=str(e))
@router.get("/transactions")
async def list_transactions(
page: int = Query(1, ge=1),
size: int = Query(20, ge=1, le=100),
user_id: str = Depends(get_current_user_id),
db: AsyncSession = Depends(get_db),
):
svc = PaymentService(db)
return await svc.list_transactions(user_id, page, size)
@router.post("/refund")
async def refund(
data: RefundRequest,
user_id: str = Depends(get_current_user_id),
db: AsyncSession = Depends(get_db),
_csrf: str = Depends(require_csrf_token),
):
svc = PaymentService(db)
try:
return await svc.refund(user_id, data.order_no, data.reason)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
@router.post("/webhook")
async def unified_webhook(request: Request, db: AsyncSession = Depends(get_db)):
body = await request.body()
body_str = body.decode("utf-8")
import json
try:
data = json.loads(body_str)
except json.JSONDecodeError:
raise HTTPException(status_code=400, detail="无效的 JSON")
event = data.get("event", "")
pay_data = data.get("data", {})
merchant_order_id = pay_data.get("merchant_order_id", "")
order_id = pay_data.get("order_id", "")
transaction_id = pay_data.get("transaction_id", "")
amount = pay_data.get("amount", 0)
success = event == "recharge.completed"
svc = PaymentService(db)
await svc.handle_callback(
merchant_order_id, order_id, transaction_id,
success, amount, body_str,
)
return {"code": 0, "message": "OK"}