diff --git a/server-rs/crates/api-server/src/runtime_profile.rs b/server-rs/crates/api-server/src/runtime_profile.rs index effe4f8d..5bb731b7 100644 --- a/server-rs/crates/api-server/src/runtime_profile.rs +++ b/server-rs/crates/api-server/src/runtime_profile.rs @@ -1217,7 +1217,8 @@ fn build_wechat_virtual_pay_params( } let sign_data = sign_data.to_string(); let pay_sig = calc_wechat_virtual_payment_signature(state, &sign_data, false)?; - let signature = calc_wechat_virtual_payment_signature_with_key(&session_key, &sign_data)?; + let signature = + calc_wechat_virtual_payment_user_signature_with_key(&session_key, &sign_data)?; Ok(WechatMiniProgramVirtualPayParamsResponse { mode: mode.to_string(), @@ -1251,7 +1252,7 @@ fn calc_wechat_virtual_payment_signature( .ok_or_else(|| { AppError::from_status(StatusCode::BAD_REQUEST).with_message("微信虚拟支付 AppKey 未配置") })?; - calc_wechat_virtual_payment_signature_with_key(app_key, sign_data) + calc_wechat_virtual_payment_pay_signature_with_key(app_key, sign_data) } fn required_wechat_virtual_payment_config<'a>( @@ -1264,7 +1265,7 @@ fn required_wechat_virtual_payment_config<'a>( .ok_or_else(|| AppError::from_status(StatusCode::SERVICE_UNAVAILABLE).with_message(message)) } -fn calc_wechat_virtual_payment_signature_with_key( +fn calc_wechat_virtual_payment_pay_signature_with_key( key: &str, sign_data: &str, ) -> Result { @@ -1276,6 +1277,18 @@ fn calc_wechat_virtual_payment_signature_with_key( Ok(to_lower_hex(mac.finalize().into_bytes().as_slice())) } +fn calc_wechat_virtual_payment_user_signature_with_key( + session_key: &str, + sign_data: &str, +) -> Result { + let mut mac = HmacSha256::new_from_slice(session_key.as_bytes()).map_err(|_| { + AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR) + .with_message("微信虚拟支付用户态签名密钥初始化失败") + })?; + mac.update(sign_data.as_bytes()); + Ok(to_lower_hex(mac.finalize().into_bytes().as_slice())) +} + fn to_lower_hex(bytes: &[u8]) -> String { const HEX: &[u8; 16] = b"0123456789abcdef"; let mut output = String::with_capacity(bytes.len() * 2); @@ -1765,8 +1778,9 @@ mod tests { }; use super::{ - build_wechat_virtual_pay_params, format_profile_wallet_ledger_source_type, - normalize_admin_invite_code_metadata, + build_wechat_virtual_pay_params, calc_wechat_virtual_payment_pay_signature_with_key, + calc_wechat_virtual_payment_user_signature_with_key, + format_profile_wallet_ledger_source_type, normalize_admin_invite_code_metadata, }; use axum::{ @@ -2345,6 +2359,29 @@ mod tests { assert_eq!(sign_data["outTradeNo"], "item01order01"); } + #[test] + fn wechat_virtual_payment_signatures_match_official_examples() { + let post_body = r#"{"openid": "xxx", "user_ip": "127.0.0.1", "env": 0}"#; + + let pay_sig = + calc_wechat_virtual_payment_pay_signature_with_key("12345", post_body) + .expect("pay signature should build"); + let signature = calc_wechat_virtual_payment_user_signature_with_key( + "9hAb/NEYUlkaMBEsmFgzig==", + post_body, + ) + .expect("user signature should build"); + + assert_eq!( + pay_sig, + "a1ab2651b927b6a766152cf864033417b85c1448fc3c6e1bedbbd7f49416e92f" + ); + assert_eq!( + signature, + "089d9e8dc5d308977360c4b79ec600a93d736802802a807d634192328032f6c7" + ); + } + #[tokio::test] async fn profile_feedback_requires_authentication() { let app = build_router(AppState::new(AppConfig::default()).expect("state should build"));