ComfyUI-Ranking-API / router_wallet.py
ZHIWEI666's picture
Upload 3 files
6f73fb4 verified
raw
history blame
10.2 kB
# router_wallet.py
from fastapi import APIRouter, Depends, HTTPException, Request
from fastapi.responses import Response
from sqlalchemy.orm import Session
import time
import uuid
import hashlib
import os
from database_sql import get_db
from models_sql import Wallet, Transaction, Ownership
from models import RechargeRequest, WithdrawRequest, PurchaseRequest, TipRequest
import 数据库连接 as json_db
router = APIRouter()
try:
from alipay import AliPay
from alipay.utils import AliPayConfig
alipay = AliPay(
appid=os.environ.get("ALIPAY_APPID", ""),
app_notify_url="https://zhiwei666-comfyui-ranking-api.hf.space/api/wallet/alipay_notify",
app_private_key_string=os.environ.get("ALIPAY_PRIVATE_KEY", "").replace("\\n", "\n"),
alipay_public_key_string=os.environ.get("ALIPAY_PUBLIC_KEY", "").replace("\\n", "\n"),
sign_type="RSA2",
debug=False,
config=AliPayConfig(timeout=15)
)
except Exception as e:
alipay = None
def calculate_tx_hash(tx_id, account, tx_type, amount, prev_hash):
data = f"{tx_id}{account}{tx_type}{amount}{prev_hash}"
return hashlib.sha256(data.encode()).hexdigest()
@router.post("/api/wallet/create_recharge_order")
async def create_recharge_order(req: RechargeRequest):
if not alipay:
raise HTTPException(status_code=500, detail="支付网关未配置或初始化失败")
order_id = f"PAY_{int(time.time())}_{uuid.uuid4().hex[:6]}"
subject = f"ComfyUI Community Points - {req.account}"
order_string = alipay.api_alipay_trade_precreate(
out_trade_no=order_id,
total_amount=str(req.amount),
subject=subject
)
qr_code_url = order_string.get("qr_code")
if not qr_code_url:
raise HTTPException(status_code=500, detail="生成支付二维码失败")
return {"status": "success", "order_id": order_id, "qr_code": qr_code_url}
# 🟢 业务流转细节修复:正确解析 application/x-www-form-urlencoded
@router.post("/api/wallet/alipay_notify")
async def alipay_notify(request: Request, db: Session = Depends(get_db)):
# 强制将表单数据解析为纯字典,防止由于数据类型错误导致验签失败
form_data = await request.form()
data = dict(form_data.items())
signature = data.pop("sign", None)
data.pop("sign_type", None)
if not alipay or not signature or not alipay.verify(data, signature):
return Response(content="fail", media_type="text/plain")
if data.get("trade_status") in ("TRADE_SUCCESS", "TRADE_FINISHED"):
order_id = data.get("out_trade_no")
existing_tx = db.query(Transaction).filter(Transaction.tx_id == order_id).first()
if not existing_tx:
amount = int(float(data.get("total_amount", 0)))
account = data.get("subject", "").split(" - ")[-1]
wallet = db.query(Wallet).filter(Wallet.account == account).with_for_update().first()
if not wallet:
wallet = Wallet(account=account)
db.add(wallet)
wallet.balance += amount
last_tx = db.query(Transaction).filter(Transaction.account == account).order_by(Transaction.created_at.desc()).first()
prev_hash = last_tx.tx_hash if last_tx else "GENESIS_HASH"
tx_hash = calculate_tx_hash(order_id, account, "RECHARGE", amount, prev_hash)
new_tx = Transaction(
tx_id=order_id, account=account, tx_type="RECHARGE", amount=amount,
prev_hash=prev_hash, tx_hash=tx_hash
)
db.add(new_tx)
db.commit()
return Response(content="success", media_type="text/plain")
@router.get("/api/wallet/check_order/{order_id}")
async def check_order(order_id: str, db: Session = Depends(get_db)):
tx = db.query(Transaction).filter(Transaction.tx_id == order_id).first()
if tx:
return {"status": "SUCCESS"}
return {"status": "PENDING"}
@router.get("/api/wallet/{account}")
async def get_wallet(account: str, db: Session = Depends(get_db)):
wallet = db.query(Wallet).filter(Wallet.account == account).first()
if not wallet:
return {"balance": 0, "earn_balance": 0, "tip_balance": 0}
return {
"balance": wallet.balance,
"earn_balance": wallet.earn_balance,
"tip_balance": wallet.tip_balance
}
@router.post("/api/wallet/purchase")
async def purchase_item(req: PurchaseRequest, db: Session = Depends(get_db)):
items_db = json_db.load_data("items.json", default_data=[])
item = next((i for i in items_db if i["id"] == req.item_id), None)
if not item:
raise HTTPException(status_code=404, detail="商品不存在")
price = int(item.get("price", 0))
seller_account = item.get("author")
if price <= 0 or req.account == seller_account:
return {"status": "success", "already_owned": True}
owned = db.query(Ownership).filter(Ownership.account == req.account, Ownership.item_id == req.item_id).first()
if owned:
return {"status": "success", "already_owned": True}
buyer_wallet = db.query(Wallet).filter(Wallet.account == req.account).with_for_update().first()
if not buyer_wallet or buyer_wallet.balance < price:
raise HTTPException(status_code=402, detail="余额不足,请先充值")
seller_wallet = db.query(Wallet).filter(Wallet.account == seller_account).with_for_update().first()
if not seller_wallet:
seller_wallet = Wallet(account=seller_account)
db.add(seller_wallet)
buyer_wallet.balance -= price
seller_wallet.earn_balance += price
new_ownership = Ownership(account=req.account, item_id=req.item_id)
db.add(new_ownership)
tx_id = f"BUY_{int(time.time())}_{uuid.uuid4().hex[:6]}"
last_tx = db.query(Transaction).filter(Transaction.account == req.account).order_by(Transaction.created_at.desc()).first()
prev_hash = last_tx.tx_hash if last_tx else "GENESIS_HASH"
tx_hash = calculate_tx_hash(tx_id, req.account, "PURCHASE", -price, prev_hash)
new_tx = Transaction(
tx_id=tx_id, account=req.account, tx_type="PURCHASE", amount=-price,
target_account=seller_account, prev_hash=prev_hash, tx_hash=tx_hash
)
db.add(new_tx)
db.commit()
return {"status": "success", "already_owned": False}
@router.post("/api/wallet/tip")
async def tip_user(req: TipRequest, db: Session = Depends(get_db)):
if req.amount <= 0:
raise HTTPException(status_code=400, detail="打赏金额必须大于0")
sender_wallet = db.query(Wallet).filter(Wallet.account == req.sender_account).with_for_update().first()
if not sender_wallet or sender_wallet.balance < req.amount:
raise HTTPException(status_code=402, detail="余额不足")
target_wallet = db.query(Wallet).filter(Wallet.account == req.target_account).with_for_update().first()
if not target_wallet:
target_wallet = Wallet(account=req.target_account)
db.add(target_wallet)
sender_wallet.balance -= req.amount
target_wallet.tip_balance += req.amount
tx_id = f"TIP_{int(time.time())}_{uuid.uuid4().hex[:6]}"
last_tx = db.query(Transaction).filter(Transaction.account == req.sender_account).order_by(Transaction.created_at.desc()).first()
prev_hash = last_tx.tx_hash if last_tx else "GENESIS_HASH"
tx_hash = calculate_tx_hash(tx_id, req.sender_account, "TIP", -req.amount, prev_hash)
new_tx = Transaction(
tx_id=tx_id, account=req.sender_account, tx_type="TIP", amount=-req.amount,
target_account=req.target_account, prev_hash=prev_hash, tx_hash=tx_hash
)
db.add(new_tx)
db.commit()
from notifications import add_notification
display_sender = "匿名用户" if req.is_anonymous else req.sender_account
add_notification(req.target_account, {
"type": "tip",
"from_user": "system",
"target_item_title": "您的主页",
"content": f"🎉 {display_sender} 给您打赏了 {req.amount} 积分!"
})
return {"status": "success", "balance": sender_wallet.balance}
@router.post("/api/wallet/withdraw")
async def withdraw(req: WithdrawRequest, db: Session = Depends(get_db)):
key = f"{req.account}_withdraw"
code_data = VERIFY_CODES.get(key)
if not code_data or code_data["code"] != req.code or time.time() > code_data["expires"]:
raise HTTPException(status_code=400, detail="验证码无效或已过期")
wallet = db.query(Wallet).filter(Wallet.account == req.account).with_for_update().first()
if not wallet:
raise HTTPException(status_code=400, detail="钱包不存在")
total_withdrawable = wallet.earn_balance + wallet.tip_balance
if req.amount > total_withdrawable:
raise HTTPException(status_code=400, detail="可提现余额不足")
if req.amount <= wallet.earn_balance:
wallet.earn_balance -= req.amount
else:
remaining = req.amount - wallet.earn_balance
wallet.earn_balance = 0
wallet.tip_balance -= remaining
wallet.frozen_balance += req.amount
tx_id = f"WD_{int(time.time())}_{uuid.uuid4().hex[:6]}"
last_tx = db.query(Transaction).filter(Transaction.account == req.account).order_by(Transaction.created_at.desc()).first()
prev_hash = last_tx.tx_hash if last_tx else "GENESIS_HASH"
tx_hash = calculate_tx_hash(tx_id, req.account, "WITHDRAW", -req.amount, prev_hash)
new_tx = Transaction(
tx_id=tx_id, account=req.account, tx_type="WITHDRAW", amount=-req.amount,
prev_hash=prev_hash, tx_hash=tx_hash
)
db.add(new_tx)
db.commit()
del VERIFY_CODES[key]
return {"status": "success"}