use serde::Deserialize;
use crate::error::{WxPayError, WxPayResult};
use crate::notify::handler::{NotifyRequest, NotifyResource};
pub struct NotifyParser;
impl NotifyParser {
pub fn parse(body: &str) -> WxPayResult<NotifyRequest> {
serde_json::from_str(body)
.map_err(|e| WxPayError::InvalidNotifyFormat(format!("解析通知失败: {}", e)))
}
pub fn parse_bytes(bytes: &[u8]) -> WxPayResult<NotifyRequest> {
serde_json::from_slice(bytes)
.map_err(|e| WxPayError::InvalidNotifyFormat(format!("解析通知失败: {}", e)))
}
pub fn get_notify_type(body: &str) -> WxPayResult<String> {
#[derive(Deserialize)]
struct NotifyType {
#[serde(rename = "type")]
notify_type: String,
}
let notify: NotifyType = serde_json::from_str(body)
.map_err(|e| WxPayError::InvalidNotifyFormat(format!("解析通知类型失败: {}", e)))?;
Ok(notify.notify_type)
}
pub fn get_notify_id(body: &str) -> WxPayResult<String> {
#[derive(Deserialize)]
struct NotifyId {
id: String,
}
let notify: NotifyId = serde_json::from_str(body)
.map_err(|e| WxPayError::InvalidNotifyFormat(format!("解析通知 ID 失败: {}", e)))?;
Ok(notify.id)
}
pub fn validate(body: &str) -> WxPayResult<bool> {
let notify = Self::parse(body)?;
if notify.id.is_empty() {
return Err(WxPayError::InvalidNotifyFormat(
"通知 ID 不能为空".to_string(),
));
}
if notify.create_time.is_empty() {
return Err(WxPayError::InvalidNotifyFormat(
"通知创建时间不能为空".to_string(),
));
}
if notify.notify_type.is_empty() {
return Err(WxPayError::InvalidNotifyFormat(
"通知类型不能为空".to_string(),
));
}
if notify.resource.algorithm.is_empty() {
return Err(WxPayError::InvalidNotifyFormat(
"加密算法不能为空".to_string(),
));
}
if notify.resource.ciphertext.is_empty() {
return Err(WxPayError::InvalidNotifyFormat("密文不能为空".to_string()));
}
if notify.resource.nonce.is_empty() {
return Err(WxPayError::InvalidNotifyFormat(
"随机串不能为空".to_string(),
));
}
Ok(true)
}
pub fn get_resource(body: &str) -> WxPayResult<NotifyResource> {
let notify = Self::parse(body)?;
Ok(notify.resource)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_notify() {
let body = r#"{
"id": "EV-2018022511223320873",
"create_time": "2015-05-20T13:29:35+08:00",
"type": "TRANSACTION.SUCCESS",
"resource": {
"algorithm": "AEAD_AES_256_GCM",
"ciphertext": "...",
"associated_data": "transaction",
"nonce": "..."
}
}"#;
let notify = NotifyParser::parse(body).unwrap();
assert_eq!(notify.id, "EV-2018022511223320873");
assert_eq!(notify.notify_type, "TRANSACTION.SUCCESS");
}
#[test]
fn test_get_notify_type() {
let body = r#"{
"id": "EV-2018022511223320873",
"create_time": "2015-05-20T13:29:35+08:00",
"type": "TRANSACTION.SUCCESS",
"resource": {
"algorithm": "AEAD_AES_256_GCM",
"ciphertext": "...",
"associated_data": "transaction",
"nonce": "..."
}
}"#;
let notify_type = NotifyParser::get_notify_type(body).unwrap();
assert_eq!(notify_type, "TRANSACTION.SUCCESS");
}
#[test]
fn test_get_notify_id() {
let body = r#"{
"id": "EV-2018022511223320873",
"create_time": "2015-05-20T13:29:35+08:00",
"type": "TRANSACTION.SUCCESS",
"resource": {
"algorithm": "AEAD_AES_256_GCM",
"ciphertext": "...",
"associated_data": "transaction",
"nonce": "..."
}
}"#;
let notify_id = NotifyParser::get_notify_id(body).unwrap();
assert_eq!(notify_id, "EV-2018022511223320873");
}
#[test]
fn test_validate() {
let body = r#"{
"id": "EV-2018022511223320873",
"create_time": "2015-05-20T13:29:35+08:00",
"type": "TRANSACTION.SUCCESS",
"resource": {
"algorithm": "AEAD_AES_256_GCM",
"ciphertext": "...",
"associated_data": "transaction",
"nonce": "..."
}
}"#;
let result = NotifyParser::validate(body).unwrap();
assert!(result);
}
#[test]
fn test_validate_invalid_json() {
let body = "invalid json";
let result = NotifyParser::validate(body);
assert!(result.is_err());
}
}