fix: handle wechat message url verification

This commit is contained in:
kdletters
2026-06-01 22:52:51 +08:00
parent fc4b04a812
commit fae4db6a09
2 changed files with 74 additions and 25 deletions

View File

@@ -901,35 +901,15 @@ pub async fn handle_wechat_virtual_payment_message_push_verify(
Ok(value) => value,
Err(error) => return build_wechat_message_push_verify_error_response(error),
};
let signature = query
.msg_signature
.as_deref()
.or(query.signature.as_deref())
.map(str::trim)
.filter(|value| !value.is_empty())
.unwrap_or("");
let timestamp = query.timestamp.as_deref().map(str::trim).unwrap_or("");
let nonce = query.nonce.as_deref().map(str::trim).unwrap_or("");
let echostr = query.echostr.as_deref().map(str::trim).unwrap_or("");
if signature.is_empty() || timestamp.is_empty() || nonce.is_empty() || echostr.is_empty() {
return build_wechat_message_push_verify_error_response(WechatPayError::InvalidRequest(
"微信消息推送校验参数不完整".to_string(),
));
}
if !verify_wechat_message_push_signature(token, timestamp, nonce, echostr, signature) {
return build_wechat_message_push_verify_error_response(WechatPayError::InvalidSignature(
"微信消息推送校验签名无效".to_string(),
));
}
match decrypt_wechat_message_push_ciphertext(
match resolve_wechat_message_push_verify_response(
token,
aes_key,
echostr,
state
.config
.wechat_mini_program_app_id
.as_deref()
.or(state.config.wechat_app_id.as_deref()),
&query,
) {
Ok(plaintext) => (StatusCode::OK, plaintext).into_response(),
Err(error) => build_wechat_message_push_verify_error_response(error),
@@ -1139,6 +1119,50 @@ fn build_wechat_message_push_verify_error_response(error: WechatPayError) -> Res
(StatusCode::BAD_REQUEST, message).into_response()
}
fn resolve_wechat_message_push_verify_response(
token: &str,
aes_key: &str,
expected_app_id: Option<&str>,
query: &WechatMiniProgramMessagePushQuery,
) -> Result<String, WechatPayError> {
let timestamp = query.timestamp.as_deref().map(str::trim).unwrap_or("");
let nonce = query.nonce.as_deref().map(str::trim).unwrap_or("");
let echostr = query.echostr.as_deref().map(str::trim).unwrap_or("");
if timestamp.is_empty() || nonce.is_empty() || echostr.is_empty() {
return Err(WechatPayError::InvalidRequest(
"微信消息推送校验参数不完整".to_string(),
));
}
let msg_signature = query
.msg_signature
.as_deref()
.map(str::trim)
.filter(|value| !value.is_empty());
if let Some(signature) = msg_signature {
if !verify_wechat_message_push_signature(token, timestamp, nonce, echostr, signature) {
return Err(WechatPayError::InvalidSignature(
"微信消息推送 msg_signature 无效".to_string(),
));
}
return decrypt_wechat_message_push_ciphertext(aes_key, echostr, expected_app_id);
}
let signature = query
.signature
.as_deref()
.map(str::trim)
.filter(|value| !value.is_empty())
.ok_or_else(|| {
WechatPayError::InvalidRequest("微信消息推送校验参数不完整".to_string())
})?;
if !verify_wechat_message_push_signature(token, timestamp, nonce, "", signature) {
return Err(WechatPayError::InvalidSignature(
"微信消息推送校验签名无效".to_string(),
));
}
Ok(echostr.to_string())
}
fn parse_wechat_mini_program_message_push_payload(
body: &[u8],
) -> Result<WechatMiniProgramEncryptedMessage, WechatPayError> {
@@ -2217,6 +2241,31 @@ mod tests {
));
}
#[test]
fn wechat_message_push_plain_get_verify_returns_echostr() {
let token = "AAAAA";
let timestamp = "1714036504";
let nonce = "1514711492";
let echostr = "4375120948345356249";
let signature = "f464b24fc39322e44b38aa78f5edd27bd1441696";
let plaintext = resolve_wechat_message_push_verify_response(
token,
"unused-aes-key",
Some("wx-test-app"),
&WechatMiniProgramMessagePushQuery {
signature: Some(signature.to_string()),
timestamp: Some(timestamp.to_string()),
nonce: Some(nonce.to_string()),
echostr: Some(echostr.to_string()),
msg_signature: None,
},
)
.expect("plain url verification should return echostr");
assert_eq!(plaintext, echostr);
}
#[test]
fn wechat_message_push_decrypts_safe_mode_ciphertext() {
let app_id = "wx-test-app";