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.
This commit is contained in:
@@ -1,10 +1,9 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, Header
|
||||
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.services.wechat_pay import WeChatPayService
|
||||
from app.api.v1.deps import get_current_user_id
|
||||
from app.core.csrf import require_csrf_token
|
||||
|
||||
@@ -13,12 +12,12 @@ router = APIRouter()
|
||||
|
||||
class CreateOrderRequest(BaseModel):
|
||||
plan: str
|
||||
pay_type: str = "jsapi"
|
||||
pay_type: str = "alipay"
|
||||
|
||||
|
||||
class PaymentCallbackRequest(BaseModel):
|
||||
payment_id: str
|
||||
success: bool
|
||||
class RefundRequest(BaseModel):
|
||||
order_no: str
|
||||
reason: str = ""
|
||||
|
||||
|
||||
@router.get("/plans")
|
||||
@@ -50,42 +49,65 @@ async def create_order(
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.post("/callback")
|
||||
async def payment_callback(
|
||||
data: PaymentCallbackRequest,
|
||||
@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)
|
||||
success = await svc.handle_payment_callback(data.payment_id, data.success)
|
||||
if not success:
|
||||
raise HTTPException(status_code=404, detail="Order not found")
|
||||
return {"status": "ok"}
|
||||
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("/notify")
|
||||
async def wechat_pay_notify(request: Request, db: AsyncSession = Depends(get_db)):
|
||||
@router.post("/webhook")
|
||||
async def unified_webhook(request: Request, db: AsyncSession = Depends(get_db)):
|
||||
body = await request.body()
|
||||
body_str = body.decode("utf-8")
|
||||
headers = dict(request.headers)
|
||||
|
||||
wxpay = WeChatPayService()
|
||||
if not wxpay.verify_callback(headers, body_str):
|
||||
raise HTTPException(status_code=401, detail="签名验证失败")
|
||||
|
||||
import json
|
||||
data = json.loads(body_str)
|
||||
resource = data.get("resource", {})
|
||||
ciphertext = resource.get("ciphertext", "")
|
||||
nonce = resource.get("nonce", "")
|
||||
associated_data = resource.get("associated_data", "")
|
||||
try:
|
||||
data = json.loads(body_str)
|
||||
except json.JSONDecodeError:
|
||||
raise HTTPException(status_code=400, detail="无效的 JSON")
|
||||
|
||||
plaintext = wxpay.decrypt_callback(ciphertext, nonce, associated_data)
|
||||
pay_data = json.loads(plaintext)
|
||||
out_trade_no = pay_data.get("out_trade_no", "")
|
||||
trade_state = pay_data.get("trade_state", "")
|
||||
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"
|
||||
|
||||
success = trade_state == "SUCCESS"
|
||||
svc = PaymentService(db)
|
||||
await svc.handle_payment_callback(out_trade_no, success)
|
||||
return {"code": "SUCCESS", "message": "OK"}
|
||||
await svc.handle_callback(
|
||||
merchant_order_id, order_id, transaction_id,
|
||||
success, amount, body_str,
|
||||
)
|
||||
return {"code": 0, "message": "OK"}
|
||||
|
||||
Reference in New Issue
Block a user