fix wechat virtual payment signature

This commit is contained in:
kdletters
2026-05-30 15:15:53 +08:00
parent 0edfc21a46
commit c02dcd8aaf

View File

@@ -1217,7 +1217,8 @@ fn build_wechat_virtual_pay_params(
} }
let sign_data = sign_data.to_string(); let sign_data = sign_data.to_string();
let pay_sig = calc_wechat_virtual_payment_signature(state, &sign_data, false)?; 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 { Ok(WechatMiniProgramVirtualPayParamsResponse {
mode: mode.to_string(), mode: mode.to_string(),
@@ -1251,7 +1252,7 @@ fn calc_wechat_virtual_payment_signature(
.ok_or_else(|| { .ok_or_else(|| {
AppError::from_status(StatusCode::BAD_REQUEST).with_message("微信虚拟支付 AppKey 未配置") 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>( 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)) .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, key: &str,
sign_data: &str, sign_data: &str,
) -> Result<String, AppError> { ) -> Result<String, AppError> {
@@ -1276,6 +1277,18 @@ fn calc_wechat_virtual_payment_signature_with_key(
Ok(to_lower_hex(mac.finalize().into_bytes().as_slice())) 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<String, AppError> {
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 { fn to_lower_hex(bytes: &[u8]) -> String {
const HEX: &[u8; 16] = b"0123456789abcdef"; const HEX: &[u8; 16] = b"0123456789abcdef";
let mut output = String::with_capacity(bytes.len() * 2); let mut output = String::with_capacity(bytes.len() * 2);
@@ -1765,8 +1778,9 @@ mod tests {
}; };
use super::{ use super::{
build_wechat_virtual_pay_params, format_profile_wallet_ledger_source_type, build_wechat_virtual_pay_params, calc_wechat_virtual_payment_pay_signature_with_key,
normalize_admin_invite_code_metadata, calc_wechat_virtual_payment_user_signature_with_key,
format_profile_wallet_ledger_source_type, normalize_admin_invite_code_metadata,
}; };
use axum::{ use axum::{
@@ -2345,6 +2359,29 @@ mod tests {
assert_eq!(sign_data["outTradeNo"], "item01order01"); 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] #[tokio::test]
async fn profile_feedback_requires_authentication() { async fn profile_feedback_requires_authentication() {
let app = build_router(AppState::new(AppConfig::default()).expect("state should build")); let app = build_router(AppState::new(AppConfig::default()).expect("state should build"));