diff --git a/src/handlers/payment.rs b/src/handlers/payment.rs index c710b78..08c9bee 100644 --- a/src/handlers/payment.rs +++ b/src/handlers/payment.rs @@ -1024,20 +1024,50 @@ pub async fn create_order( }))) } +/// 检查 Mock 支付是否允许 +/// 规则(依次): +/// 1. 已配置支付宝 → 禁用(真实支付优先) +/// 2. MOCK_PAY_ENABLED != true → 禁用 +/// 3. MOCK_PAY_KEY 已设置 → 验证 X-Mock-Key 请求头 +fn check_mock_payment_allowed(req: &HttpRequest) -> Result<(), AppError> { + // 规则 1:有支付宝时永不走 Mock + if AlipayConfig::from_env().is_some() { + return Err(AppError::BadRequest("真实支付已启用,Mock 支付不可用".to_string())); + } + + // 规则 2:必须显式启用 Mock 支付 + if std::env::var("MOCK_PAY_ENABLED").ok() != Some("true".to_string()) { + return Err(AppError::Forbidden("Mock 支付未启用".to_string())); + } + + // 规则 3:如果设了 MOCK_PAY_KEY,验证请求头 + if let Ok(key) = std::env::var("MOCK_PAY_KEY") { + if !key.is_empty() { + let header_key = req + .headers() + .get("X-Mock-Key") + .and_then(|v| v.to_str().ok()) + .unwrap_or(""); + if header_key != key { + return Err(AppError::Forbidden("Mock 支付密钥错误".to_string())); + } + } + } + + Ok(()) +} + /// POST /api/payment/mock-confirm -/// 仅在未配置支付宝时可用(否则用户可绕过真实支付) +/// 所有 Mock 操作均需密钥授权(通过 MOCK_PAY_ENABLED + MOCK_PAY_KEY 控制) #[post("/api/payment/mock-confirm")] pub async fn mock_confirm( + req: HttpRequest, pool: web::Data, claims: web::ReqData, body: web::Json, ) -> Result { check_payment_maintenance()?; - - // 安全守卫:已配置支付宝时禁用 Mock 支付,防止绕过 - if AlipayConfig::from_env().is_some() { - return Err(AppError::BadRequest("真实支付已启用,Mock 支付不可用".to_string())); - } + check_mock_payment_allowed(&req)?; let user_id = claims.user_id; let expires_at =