diff --git a/src/handlers/payment.rs b/src/handlers/payment.rs index 35de385..d142ced 100644 --- a/src/handlers/payment.rs +++ b/src/handlers/payment.rs @@ -128,9 +128,9 @@ fn rsa2_verify(content: &str, sign: &str, public_key_pem: &str) -> Result Result { - let client = reqwest::Client::builder() - .timeout(std::time::Duration::from_secs(30)) - .build() - .map_err(|e| format!("HTTP 客户端创建失败: {}", e))?; - let biz_content = serde_json::json!({ "out_trade_no": out_trade_no, "total_amount": total_amount, @@ -166,79 +161,44 @@ async fn call_alipay_trade_page_pay( params.insert("notify_url", notify_url); params.insert("return_url", return_url); - // 构造待签名串 + // 构造待签名串(注意:签名源使用原始值,不做 URL 编码) let sign_source: String = params .iter() - .map(|(k, v)| format!("{}={}", k, urlencoding(v))) + .map(|(k, v)| format!("{}={}", k, *v)) .collect::>() .join("&"); let sign = rsa2_sign(&sign_source, &config.private_key)?; - // 构建 POST body - let query: String = params - .iter() - .map(|(k, v)| format!("{}={}", urlencoding(k), urlencoding(v))) - .chain(std::iter::once(format!("sign={}", urlencoding(&sign)))) - .collect::>() - .join("&"); - - let resp = client - .post(&config.gateway) - .header("Content-Type", "application/x-www-form-urlencoded") - .body(query) - .send() - .await - .map_err(|e| format!("请求支付宝失败: {}", e))?; - - let body = resp.text().await.map_err(|e| format!("读取响应失败: {}", e))?; - - // 支付宝返回格式: alipay_trade_page_pay_response={...}&sign=xxx - let parts: Vec<&str> = body.splitn(2, "&sign=").collect(); - if parts.len() != 2 { - // 如果直接返回 HTML 表单(沙箱环境可能直接返回表单),直接返回 - if body.contains(""#, + urlencoding(k), + urlencoding(v) )); } + form_fields.push_str(&format!( + r#""#, + urlencoding(&sign) + )); - let json_str = parts[0] - .strip_prefix("alipay_trade_page_pay_response=") - .unwrap_or(parts[0]); + let html = format!( + r##" + +正在跳转支付宝... + +
+ {} +
+ + +"##, + config.gateway, form_fields + ); - let sign_from_alipay = parts[1]; - - if !rsa2_verify(json_str, sign_from_alipay, &config.alipay_public_key)? { - return Err("支付宝响应验签失败".to_string()); - } - - let json: serde_json::Value = - serde_json::from_str(json_str).map_err(|e| format!("JSON 解析失败: {}", e))?; - - if json.get("code").and_then(|v| v.as_str()) != Some("10000") { - let sub_msg = json - .get("sub_msg") - .and_then(|v| v.as_str()) - .unwrap_or(""); - return Err(format!( - "支付宝接口错误: {} - {}", - json.get("msg") - .and_then(|v| v.as_str()) - .unwrap_or("未知"), - sub_msg - )); - } - - let form_html = json - .get("form_html") - .and_then(|v| v.as_str()) - .ok_or_else(|| "响应中缺少 form_html".to_string())?; - - Ok(form_html.to_string()) + Ok(html) } // ===== 提取 JWT token ===== @@ -663,16 +623,14 @@ function confirmMockPay() {{ let total_amount_str = format!("{:.2}", pkg.amount as f64 / 100.0); - match call_alipay_trade_page_pay( + match build_alipay_form_html( &config, &order_no, &total_amount_str, pkg.display_name, ¬ify_url, &return_url, - ) - .await - { + ) { Ok(form_html) => Ok(HttpResponse::Ok() .content_type("text/html; charset=utf-8") .body(form_html)), @@ -764,16 +722,14 @@ function confirmMockPay() {{ let total_amount_str = format!("{:.2}", pkg.amount as f64 / 100.0); - match call_alipay_trade_page_pay( + match build_alipay_form_html( &config, &query.order_no, &total_amount_str, pkg.display_name, ¬ify_url, &return_url, - ) - .await - { + ) { Ok(form_html) => Ok(HttpResponse::Ok() .content_type("text/html; charset=utf-8") .body(form_html)),