diff --git a/src/db.rs b/src/db.rs index 04cc370..182897f 100644 --- a/src/db.rs +++ b/src/db.rs @@ -643,7 +643,10 @@ pub async fn confirm_payment_order_by_orderno( None => return Err(AppError::NotFound("订单不存在".to_string())), }; - if status != "pending" { + // 支付宝回调已通过 RSA2 验证。如果订单被用户误取消,重新激活。 + if status == "cancelled" { + tracing::warn!("订单 {} 已被取消,但支付宝确认已收款,重新激活并处理支付", order_no); + } else if status != "pending" { return Ok(()); } @@ -662,7 +665,7 @@ pub async fn confirm_payment_order_by_orderno( let result = sqlx::query( r#" WITH updated_order AS ( - UPDATE payment_orders SET status = 'paid', paid_at = NOW() WHERE order_no = $1 AND status = 'pending' RETURNING package_type, user_id + UPDATE payment_orders SET status = 'paid', paid_at = NOW() WHERE order_no = $1 AND status IN ('pending', 'cancelled') RETURNING package_type, user_id ) UPDATE users SET is_member = true, @@ -1123,6 +1126,22 @@ pub async fn cancel_payment_order( return Err(AppError::BadRequest(format!("订单状态为 {},无法取消", status))); } + // 防止用户误取消:支付宝支付确认通常在 5-30 秒内到达 + // 2 分钟内的订单不允许取消,避免用户付款后误触取消按钮 + let order_age: f64 = sqlx::query_scalar( + r#"SELECT EXTRACT(EPOCH FROM (NOW() - created_at)) FROM payment_orders WHERE order_no = $1"#, + ) + .bind(order_no) + .fetch_one(pool) + .await + .map_err(|e| AppError::Database(format!("查询订单创建时间失败: {}", e)))?; + + if order_age < 120.0 { + return Err(AppError::BadRequest( + "订单刚刚创建,支付可能仍在处理中,请 2 分钟后再试".to_string(), + )); + } + sqlx::query("UPDATE payment_orders SET status = 'cancelled' WHERE order_no = $1") .bind(order_no) .execute(pool) diff --git a/src/handlers/payment.rs b/src/handlers/payment.rs index 351dcfb..65b6efa 100644 --- a/src/handlers/payment.rs +++ b/src/handlers/payment.rs @@ -901,6 +901,11 @@ fn build_success_html(order_no: &str) -> String {