use crate::config::WxOpenConfigStorage;
use crate::util::crypto::WxOpenCryptUtils;
#[derive(Debug, Clone, Default, PartialEq, serde::Deserialize)]
#[serde(rename = "xml")]
pub struct WxOpenXmlMessage {
#[serde(rename = "AppId", default)]
pub app_id: Option<String>,
#[serde(rename = "CreateTime", default)]
pub create_time: Option<i64>,
#[serde(rename = "InfoType", default)]
pub info_type: Option<String>,
#[serde(rename = "ComponentVerifyTicket", default)]
pub component_verify_ticket: Option<String>,
#[serde(rename = "AuthorizerAppid", default)]
pub authorizer_appid: Option<String>,
#[serde(rename = "AuthorizationCode", default)]
pub authorization_code: Option<String>,
#[serde(rename = "AuthorizationCodeExpiredTime", default)]
pub authorization_code_expired_time: Option<i64>,
#[serde(rename = "PreAuthCode", default)]
pub pre_auth_code: Option<String>,
#[serde(rename = "appid", default)]
pub sub_app_id: Option<String>,
#[serde(rename = "status", default)]
pub status: Option<i32>,
#[serde(rename = "auth_code", default)]
pub auth_code: Option<String>,
#[serde(rename = "msg", default)]
pub msg: Option<String>,
#[serde(rename = "info", default)]
pub info: Info,
#[serde(rename = "taskid", default)]
pub task_id: Option<String>,
#[serde(rename = "task_status", default)]
pub task_status: Option<i32>,
#[serde(rename = "apply_status", default)]
pub apply_status: Option<i32>,
#[serde(rename = "message", default)]
pub message: Option<String>,
#[serde(rename = "dispatch_info", default)]
pub dispatch_info: Option<DispatchInfo>,
#[serde(rename = "expired", default)]
pub expired: Option<i64>,
#[serde(rename = "task_id", default)]
pub icp_verify_task_id: Option<String>,
#[serde(rename = "verify_appid", default)]
pub verify_app_id: Option<String>,
#[serde(rename = "result", default)]
pub result: Option<i32>,
#[serde(rename = "along_with_auth_result", default)]
pub along_with_auth_result: Option<i32>,
#[serde(rename = "authorizer_appid", default)]
pub beian_authorizer_app_id: Option<String>,
#[serde(rename = "beian_status", default)]
pub beian_status: Option<i32>,
#[serde(rename = "procedure_id", default)]
pub procedure_id: Option<String>,
#[serde(rename = "procedure_status", default)]
pub procedure_status: Option<i32>,
#[serde(default, skip)]
pub context: Option<String>,
}
#[derive(Debug, Clone, Default, PartialEq, serde::Deserialize)]
pub struct Info {
#[serde(rename = "name", default)]
pub name: Option<String>,
#[serde(rename = "code", default)]
pub code: Option<String>,
#[serde(rename = "code_type", default)]
pub code_type: Option<i32>,
#[serde(rename = "legal_persona_wechat", default)]
pub legal_persona_wechat: Option<String>,
#[serde(rename = "legal_persona_name", default)]
pub legal_persona_name: Option<String>,
#[serde(rename = "component_phone", default)]
pub component_phone: Option<String>,
#[serde(rename = "wxuser", default)]
pub wxuser: Option<String>,
#[serde(rename = "idname", default)]
pub idname: Option<String>,
#[serde(rename = "unique_id", default)]
pub unique_id: Option<String>,
}
#[derive(Debug, Clone, Default, PartialEq, serde::Deserialize)]
pub struct DispatchInfo {
#[serde(rename = "provider", default)]
pub provider: Option<String>,
#[serde(rename = "contact", default)]
pub contact: Option<String>,
#[serde(rename = "dispatch_time", default)]
pub dispatch_time: Option<i64>,
}
impl WxOpenXmlMessage {
pub fn from_xml(xml: &str) -> Result<Self, String> {
let xml = xml.replace("</PicList><PicList>", "");
quick_xml::de::from_str(&xml).map_err(|e| format!("WxOpenXmlMessage 解析失败: {e}"))
}
pub fn from_encrypted_xml(
encrypted_xml: &str,
config: &dyn WxOpenConfigStorage,
timestamp: &str,
nonce: &str,
msg_signature: &str,
) -> Result<Self, String> {
let crypt_util = WxOpenCryptUtils::new(config)?;
let plain_text = crypt_util.decrypt_xml(msg_signature, timestamp, nonce, encrypted_xml)?;
if plain_text.trim().is_empty() {
return Err("解密后的xml消息内容为空,请检查加密参数是否正确".to_string());
}
let mut message = Self::from_xml(&plain_text)?;
message.context = Some(plain_text);
Ok(message)
}
pub fn from_encrypted_mp_xml(
encrypted_xml: &str,
config: &dyn WxOpenConfigStorage,
timestamp: &str,
nonce: &str,
msg_signature: &str,
) -> Result<String, String> {
let crypt_util = WxOpenCryptUtils::new(config)?;
crypt_util.decrypt_xml(msg_signature, timestamp, nonce, encrypted_xml)
}
}
pub fn wx_mp_out_xml_message_to_encrypted_xml(
plain_xml: &str,
config: &dyn WxOpenConfigStorage,
) -> Result<String, String> {
let crypt_util = WxOpenCryptUtils::new(config)?;
crypt_util.encrypt(plain_xml)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_verify_ticket() {
let xml = r#"<xml>
<AppId><![CDATA[wxb1234567890abcdef]]></AppId>
<CreateTime>1413192605</CreateTime>
<InfoType><![CDATA[verify_ticket]]></InfoType>
<ComponentVerifyTicket><![CDATA[ticket@@@6iJtQHC1]]></ComponentVerifyTicket>
</xml>"#;
let m = WxOpenXmlMessage::from_xml(xml).unwrap();
assert_eq!(m.app_id.as_deref(), Some("wxb1234567890abcdef"));
assert_eq!(m.create_time, Some(1413192605));
assert_eq!(m.info_type.as_deref(), Some("verify_ticket"));
assert_eq!(
m.component_verify_ticket.as_deref(),
Some("ticket@@@6iJtQHC1")
);
assert_eq!(m.context, None);
}
#[test]
fn parse_fasteregister() {
let xml = r#"<xml>
<AppId><![CDATA[wx1234567890abcdef]]></AppId>
<CreateTime>1492747804</CreateTime>
<InfoType><![CDATA[notify_third_fasteregister]]></InfoType>
<appid><![CDATA[wx1234567890abcdef]]></appid>
<status>0</status>
<auth_code><![CDATA[queryauthcode@@@123]]></auth_code>
<msg><![CDATA[]]></msg>
<info><name><![CDATA[test]]></name><code><![CDATA[123456]]></code><code_type>1</code_type><legal_persona_wechat><![CDATA[wx123]]></legal_persona_wechat><legal_persona_name><![CDATA[张三]]></legal_persona_name><component_phone><![CDATA[13800000000]]></component_phone></info>
</xml>"#;
let m = WxOpenXmlMessage::from_xml(xml).unwrap();
assert_eq!(m.info_type.as_deref(), Some("notify_third_fasteregister"));
assert_eq!(m.sub_app_id.as_deref(), Some("wx1234567890abcdef"));
assert_eq!(m.status, Some(0));
assert_eq!(m.auth_code.as_deref(), Some("queryauthcode@@@123"));
assert_eq!(m.info.name.as_deref(), Some("test"));
assert_eq!(m.info.code.as_deref(), Some("123456"));
assert_eq!(m.info.code_type, Some(1));
assert_eq!(m.info.legal_persona_name.as_deref(), Some("张三"));
}
}