feat(pay): 支付支持代付——任意登录用户均可为订单支付,余额到账订单创建者

移除所有支付相关接口的订单归属校验和 opc_member 角色限制:

【算力充值 pay/service.py + pay/routers.py】
- build_pay_params: 移除 order.user_id != user.id 归属校验
- recharge_pay_params: require_roles(opc_member) → get_current_user(任意登录用户)
- recharge_status: 移除归属校验,角色放宽为任意登录用户

【市场购买 market/service.py + market/routers.py】
- build_pay_params: 移除归属校验
- query_status: 移除归属校验
- market_pay_params / market_order_status: 角色放宽为任意登录用户

【到账逻辑不变】
- process_paid_order 按订单 engine_user_id(订单创建者)加款
- 支付者 B 的 openid 仅用于 JSAPI 下单,不影响到账目标
- 审计日志 user_id 记录订单创建者

场景:A 用户 web 端创建充值订单 → B 用户小程序扫码 → B 登录并支付 → 余额到账 A 用户
This commit is contained in:
Pine
2026-09-02 12:13:21 +08:00
parent 86c6d24b17
commit 5db03936f9
4 changed files with 19 additions and 19 deletions
+5 -4
View File
@@ -156,12 +156,13 @@ async def market_create_order(
raise HTTPException(status_code=503, detail=str(exc)) from exc
@router.get("/orders/{order_no}/pay-params", summary="小程序扫码确认支付(JSAPI 参数)")
@router.get("/orders/{order_no}/pay-params", summary="小程序扫码确认支付(JSAPI 参数,支持代付")
async def market_pay_params(
order_no: str,
db: Database = Depends(get_db),
user: dict = Depends(require_roles("opc_member")),
user: dict = Depends(get_current_user),
):
"""支持代付:任意登录用户均可为该订单支付,商品发放到订单创建者。"""
try:
return await service.build_pay_params(db, user, order_no)
except ValueError as exc:
@@ -170,11 +171,11 @@ async def market_pay_params(
raise HTTPException(status_code=503, detail=str(exc)) from exc
@router.post("/orders/{order_no}/status", summary="查询购买订单状态(含对账补单)")
@router.post("/orders/{order_no}/status", summary="查询购买订单状态(含对账补单,支持代付查询")
async def market_order_status(
order_no: str,
db: Database = Depends(get_db),
user: dict = Depends(require_roles("opc_member")),
user: dict = Depends(get_current_user),
):
try:
return await service.query_status(db, user, order_no)
+3 -4
View File
@@ -322,15 +322,14 @@ async def _expires_iso(seconds: int) -> str:
async def build_pay_params(db, user: dict, order_no: str) -> dict:
"""小程序扫码进入确认支付页:校验归属后 JSAPI 下单,返回 wx.requestPayment 参数。"""
"""小程序扫码进入确认支付页:按当前小程序用户 openid 发起 JSAPI 下单,
返回 wx.requestPayment 参数。支持代付:任意登录用户均可支付,商品发放到订单创建者。"""
from ..pay import wxpay
from ..infrastructure.repositories import utcnow_iso as _now
r = await db.session.scalar(select(MarketPurchaseOrder).where(MarketPurchaseOrder.order_no == order_no))
if r is None:
raise ValueError("订单不存在")
if r.user_id != user["id"]:
raise ValueError("请使用下单账号登录的小程序扫码(订单归属不一致)")
if r.status != "pending":
return {"order": _order_view(r), "pay_params": None, "finished": True}
if r.expires_at and r.expires_at < _now():
@@ -356,7 +355,7 @@ async def query_status(db, user: dict, order_no: str) -> dict:
from ..infrastructure.repositories import utcnow_iso as _now
r = await db.session.scalar(select(MarketPurchaseOrder).where(MarketPurchaseOrder.order_no == order_no))
if r is None or r.user_id != user["id"]:
if r is None:
raise ValueError("订单不存在")
if r.status == "pending":
if r.expires_at and r.expires_at < _now():
+8 -7
View File
@@ -14,7 +14,7 @@ import tempfile
from fastapi import APIRouter, Depends, File, HTTPException, Request, Response, UploadFile
from pydantic import BaseModel, Field
from ..api.dependencies import get_db
from ..api.dependencies import get_current_user, get_db
from ..infrastructure.repositories import Database
from ..rbac import require_roles
from . import profitsharing, service, wxpay
@@ -87,13 +87,14 @@ async def recharge_create(
return order
@router.get("/opc/compute/recharge/orders/{order_no}/pay-params", summary="小程序扫码确认支付(归属校验 + JSAPI 参数)")
@router.get("/opc/compute/recharge/orders/{order_no}/pay-params", summary="小程序扫码确认支付(JSAPI 参数,支持代付")
async def recharge_pay_params(
order_no: str,
db: Database = Depends(get_db),
user: dict = Depends(require_roles("opc_member")),
user: dict = Depends(get_current_user),
):
"""微信扫桌面端小程序码 → 打开小程序本接口取 wx.requestPayment 参数。"""
"""微信扫桌面端小程序码 → 打开小程序本接口取 wx.requestPayment 参数。
支持代付:任意登录用户均可为该订单支付,余额到账到订单创建者。"""
try:
return await service.build_pay_params(db, user, order_no)
except ValueError as exc:
@@ -102,15 +103,15 @@ async def recharge_pay_params(
raise HTTPException(status_code=503, detail=str(exc)) from exc
@router.post("/opc/compute/recharge/orders/{order_no}/status", summary="查询充值订单状态(含主动对账补单)")
@router.post("/opc/compute/recharge/orders/{order_no}/status", summary="查询充值订单状态(含主动对账补单,支持代付查询")
async def recharge_status(
order_no: str,
db: Database = Depends(get_db),
user: dict = Depends(require_roles("opc_member")),
user: dict = Depends(get_current_user),
):
repo = _repo(db)
order = await repo.get_by_order_no(order_no)
if order is None or order["user_id"] != user["id"]:
if order is None:
raise HTTPException(status_code=404, detail="订单不存在")
order = await service.reconcile(db, order)
return {
+3 -4
View File
@@ -204,13 +204,12 @@ async def create_order(db: Database, user: dict, *, client_type: str,
async def build_pay_params(db: Database, user: dict, order_no: str) -> dict:
"""小程序扫码进入「确认支付」页:校验归属后按当前小程序用户 openid 发起 JSAPI 下单,
返回订单摘要 + wx.requestPayment 参数。"""
"""小程序扫码进入「确认支付」页:按当前小程序用户 openid 发起 JSAPI 下单,
返回订单摘要 + wx.requestPayment 参数。支持代付:任意登录用户均可支付,
余额到账到订单创建者(按订单 user_id)。"""
order = await db.compute_recharges.get_by_order_no(order_no)
if order is None:
raise ValueError("订单不存在")
if order["user_id"] != user["id"]:
raise ValueError("请使用下单账号登录的小程序扫码(订单归属不一致)")
if order["status"] != "pending":
return {"order": _order_view(order), "pay_params": None, "finished": True}
if order["expires_at"] and order["expires_at"] < utcnow_iso():