Spaces:
Running
Running
更新支付宝付款
Browse files- router_wallet.py +97 -5
router_wallet.py
CHANGED
|
@@ -175,18 +175,110 @@ async def purchase_item(req: PurchaseRequest, db: Session = Depends(get_db)):
|
|
| 175 |
db.commit()
|
| 176 |
return {"status": "success"}
|
| 177 |
|
| 178 |
-
# =============== 以下为
|
|
|
|
| 179 |
@router.post("/api/wallet/create_recharge_order")
|
| 180 |
async def create_recharge_order(req: RechargeRequest, db: Session = Depends(get_db)):
|
|
|
|
|
|
|
|
|
|
|
|
|
| 181 |
order_id = f"R_{int(time.time())}_{uuid.uuid4().hex[:6]}"
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 185 |
|
| 186 |
@router.get("/api/wallet/check_order/{order_id}")
|
| 187 |
async def check_order(order_id: str, db: Session = Depends(get_db)):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 188 |
return {"status": "PENDING"}
|
| 189 |
|
| 190 |
@router.post("/api/wallet/alipay_notify")
|
| 191 |
async def alipay_notify(request: Request, db: Session = Depends(get_db)):
|
| 192 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 175 |
db.commit()
|
| 176 |
return {"status": "success"}
|
| 177 |
|
| 178 |
+
# =============== 以下为真实的支付宝充值与回调逻辑 ===============
|
| 179 |
+
|
| 180 |
@router.post("/api/wallet/create_recharge_order")
|
| 181 |
async def create_recharge_order(req: RechargeRequest, db: Session = Depends(get_db)):
|
| 182 |
+
if not alipay:
|
| 183 |
+
raise HTTPException(status_code=500, detail="支付宝支付组件未配置或秘钥加载失败")
|
| 184 |
+
|
| 185 |
+
# 生成带时间戳和随机哈希的全局唯一订单号
|
| 186 |
order_id = f"R_{int(time.time())}_{uuid.uuid4().hex[:6]}"
|
| 187 |
+
|
| 188 |
+
try:
|
| 189 |
+
# 调用支付宝当面付接口生成二维码链接 (1积分 = 1元)
|
| 190 |
+
result = alipay.api_alipay_trade_precreate(
|
| 191 |
+
subject=f"ComfyUI社区积分充值 - {req.account}",
|
| 192 |
+
out_trade_no=order_id,
|
| 193 |
+
total_amount=str(req.amount),
|
| 194 |
+
)
|
| 195 |
+
|
| 196 |
+
if result.get("code") == "10000":
|
| 197 |
+
return {"status": "success", "order_id": order_id, "qr_code_url": result.get("qr_code")}
|
| 198 |
+
else:
|
| 199 |
+
raise HTTPException(status_code=400, detail=result.get("msg", "创建支付宝订单失败"))
|
| 200 |
+
except Exception as e:
|
| 201 |
+
raise HTTPException(status_code=500, detail=f"支付网关异常: {str(e)}")
|
| 202 |
|
| 203 |
@router.get("/api/wallet/check_order/{order_id}")
|
| 204 |
async def check_order(order_id: str, db: Session = Depends(get_db)):
|
| 205 |
+
"""前端轮询查单接口:主动向支付宝查询订单状态,防止异步回调延迟或丢失"""
|
| 206 |
+
if not alipay:
|
| 207 |
+
return {"status": "PENDING"}
|
| 208 |
+
|
| 209 |
+
try:
|
| 210 |
+
# 主动向支付宝发起查单请求
|
| 211 |
+
result = alipay.api_alipay_trade_query(out_trade_no=order_id)
|
| 212 |
+
if result.get("code") == "10000" and result.get("trade_status") == "TRADE_SUCCESS":
|
| 213 |
+
|
| 214 |
+
# 查单发现已支付,检查数据库防重复加钱
|
| 215 |
+
existing_tx = db.query(Transaction).filter(Transaction.tx_id == order_id).first()
|
| 216 |
+
if not existing_tx:
|
| 217 |
+
# 提取金额并加锁更新账户
|
| 218 |
+
amount = int(float(result.get("total_amount", 0)))
|
| 219 |
+
account = result.get("subject", "").split(" - ")[-1]
|
| 220 |
+
|
| 221 |
+
wallet = db.query(Wallet).filter(Wallet.account == account).with_for_update().first()
|
| 222 |
+
if not wallet:
|
| 223 |
+
wallet = Wallet(account=account)
|
| 224 |
+
db.add(wallet)
|
| 225 |
+
|
| 226 |
+
wallet.balance += amount
|
| 227 |
+
|
| 228 |
+
# 手动记录一条流水,tx_id 强制绑定为支付宝的 order_id 防止重复处理
|
| 229 |
+
last_tx = db.query(Transaction).filter(Transaction.account == account).order_by(Transaction.created_at.desc()).first()
|
| 230 |
+
prev_hash = last_tx.tx_hash if last_tx else "GENESIS_HASH"
|
| 231 |
+
tx_hash = calculate_tx_hash(order_id, account, "RECHARGE", amount, prev_hash)
|
| 232 |
+
|
| 233 |
+
new_tx = Transaction(
|
| 234 |
+
tx_id=order_id, account=account, tx_type="RECHARGE", amount=amount,
|
| 235 |
+
prev_hash=prev_hash, tx_hash=tx_hash
|
| 236 |
+
)
|
| 237 |
+
db.add(new_tx)
|
| 238 |
+
db.commit()
|
| 239 |
+
|
| 240 |
+
return {"status": "SUCCESS"}
|
| 241 |
+
except Exception:
|
| 242 |
+
pass
|
| 243 |
+
|
| 244 |
return {"status": "PENDING"}
|
| 245 |
|
| 246 |
@router.post("/api/wallet/alipay_notify")
|
| 247 |
async def alipay_notify(request: Request, db: Session = Depends(get_db)):
|
| 248 |
+
"""支付宝异步回调 Webhook (最高优先级的到账通知)"""
|
| 249 |
+
data = dict(await request.form())
|
| 250 |
+
signature = data.pop("sign", None)
|
| 251 |
+
|
| 252 |
+
# 核心风控:验证签名是否真的是支付宝发来的
|
| 253 |
+
if not alipay or not alipay.verify(data, signature):
|
| 254 |
+
return Response(content="fail", media_type="text/plain")
|
| 255 |
+
|
| 256 |
+
if data.get("trade_status") in ("TRADE_SUCCESS", "TRADE_FINISHED"):
|
| 257 |
+
order_id = data.get("out_trade_no")
|
| 258 |
+
|
| 259 |
+
# 检查是否已经被轮询查单处理过了
|
| 260 |
+
existing_tx = db.query(Transaction).filter(Transaction.tx_id == order_id).first()
|
| 261 |
+
if not existing_tx:
|
| 262 |
+
amount = int(float(data.get("total_amount", 0)))
|
| 263 |
+
account = data.get("subject", "").split(" - ")[-1]
|
| 264 |
+
|
| 265 |
+
wallet = db.query(Wallet).filter(Wallet.account == account).with_for_update().first()
|
| 266 |
+
if not wallet:
|
| 267 |
+
wallet = Wallet(account=account)
|
| 268 |
+
db.add(wallet)
|
| 269 |
+
|
| 270 |
+
wallet.balance += amount
|
| 271 |
+
|
| 272 |
+
last_tx = db.query(Transaction).filter(Transaction.account == account).order_by(Transaction.created_at.desc()).first()
|
| 273 |
+
prev_hash = last_tx.tx_hash if last_tx else "GENESIS_HASH"
|
| 274 |
+
tx_hash = calculate_tx_hash(order_id, account, "RECHARGE", amount, prev_hash)
|
| 275 |
+
|
| 276 |
+
new_tx = Transaction(
|
| 277 |
+
tx_id=order_id, account=account, tx_type="RECHARGE", amount=amount,
|
| 278 |
+
prev_hash=prev_hash, tx_hash=tx_hash
|
| 279 |
+
)
|
| 280 |
+
db.add(new_tx)
|
| 281 |
+
db.commit()
|
| 282 |
+
|
| 283 |
+
# 必须向支付宝返回 success,否则支付宝会按阶梯间隔持续重发请求
|
| 284 |
+
return Response(content="success", media_type="text/plain")
|