支付提交增加 Tx 预校验:提交前链上验签收款地址与金额,防止转错地址;409 错误展示友好中文提示并自动对账恢复

- 新增 validate_intent_tx 方法及 POST /api/payments/intents/{id}/validate 端点,
  在提交前查链上 receipt 对比收款地址和金额,mismatch 直接拦截
- 新增 handleSubmit409 辅助函数,根据后端错误详情分流处理:
  已支付→自动 reconcile,已过期→提示重下单,其他→透传具体原因
- submit/validate 路由透传后端 detail 字段,生产环境也能看到具体错误
- 手动转账面板粘贴 tx hash 后自动触发验证,绿色/红色提示,
  验证不通过时禁用提交按钮

Tested: tsc --noEmit + ruff check . 均通过
@
This commit is contained in:
2569718930@qq.com
2026-05-22 04:44:14 +08:00
parent 5e0dc3ec53
commit 105713c799
7 changed files with 413 additions and 4 deletions
+4
View File
@@ -376,6 +376,10 @@ class SubmitPaymentTxRequest(BaseModel):
from_address: Optional[str] = None
class ValidatePaymentTxRequest(BaseModel):
tx_hash: str = Field(..., min_length=10)
class ConfirmPaymentTxRequest(BaseModel):
tx_hash: Optional[str] = None
+11
View File
@@ -6,6 +6,7 @@ from web.core import (
ConfirmPaymentTxRequest,
CreatePaymentIntentRequest,
SubmitPaymentTxRequest,
ValidatePaymentTxRequest,
WalletChallengeRequest,
WalletUnbindRequest,
WalletVerifyRequest,
@@ -21,6 +22,7 @@ from web.services.payment_api import (
reconcile_latest_payment,
submit_payment_tx as submit_payment_tx_service,
unbind_payment_wallet,
validate_payment_tx,
verify_payment_wallet,
)
@@ -67,6 +69,15 @@ async def payment_get_intent(request: Request, intent_id: str):
return get_payment_intent(request, intent_id)
@router.post("/api/payments/intents/{intent_id}/validate")
async def payment_validate_tx(
request: Request,
intent_id: str,
body: ValidatePaymentTxRequest,
):
return validate_payment_tx(request, intent_id, body)
@router.post("/api/payments/intents/{intent_id}/submit")
async def payment_submit_tx(
request: Request,
+17
View File
@@ -11,6 +11,7 @@ from web.core import (
ConfirmPaymentTxRequest,
CreatePaymentIntentRequest,
SubmitPaymentTxRequest,
ValidatePaymentTxRequest,
WalletChallengeRequest,
WalletUnbindRequest,
WalletVerifyRequest,
@@ -155,6 +156,22 @@ def submit_payment_tx(
_raise_payment_error(exc)
def validate_payment_tx(
request: Request,
intent_id: str,
body: ValidatePaymentTxRequest,
) -> Dict[str, Any]:
identity = _require_payment_identity(request)
try:
return legacy_routes.PAYMENT_CHECKOUT.validate_intent_tx(
user_id=identity["user_id"],
intent_id=intent_id,
tx_hash=body.tx_hash,
)
except legacy_routes.PaymentCheckoutError as exc:
_raise_payment_error(exc)
def confirm_payment_tx(
request: Request,
intent_id: str,