use tracing::debug;
use crate::client::WxPayClient;
use crate::crypto::decrypt::decrypt_aes_256_gcm;
use crate::crypto::verify::verify_signature;
use crate::error::WxPayError;
use crate::model::notify::*;
impl WxPayClient {
pub async fn parse_notify(
&self,
headers: &NotifyHeaders,
body: &str,
) -> Result<NotifyEnvelope, WxPayError> {
self.ensure_certs().await?;
let ts: i64 = headers.timestamp.parse().map_err(|_| {
WxPayError::NotifyError(format!("invalid timestamp: {}", headers.timestamp))
})?;
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.expect("system clock is before UNIX epoch")
.as_secs() as i64;
let diff = (now - ts).abs();
if diff > 300 {
return Err(WxPayError::NotifyError(format!(
"notification timestamp too old or too new: diff={diff}s"
)));
}
debug!(serial = %headers.serial, "verifying notification signature");
let mgr = self.cert_manager.read().await;
let cert = mgr.get_cert(&headers.serial).ok_or_else(|| {
WxPayError::NotifyError(format!(
"platform certificate not found for serial: {}",
headers.serial
))
})?;
let valid = verify_signature(
&cert.verifying_key,
&headers.timestamp,
&headers.nonce,
body,
&headers.signature,
)?;
if !valid {
return Err(WxPayError::NotifyError(
"notification signature verification failed".into(),
));
}
serde_json::from_str(body)
.map_err(|e| WxPayError::NotifyError(format!("deserialize notification: {e}")))
}
pub fn decrypt_notify_resource(&self, resource: &NotifyResource) -> Result<String, WxPayError> {
decrypt_aes_256_gcm(
&self.config.api_v3_key,
&resource.nonce,
&resource.associated_data,
&resource.ciphertext,
)
}
pub async fn parse_transaction_notify(
&self,
headers: &NotifyHeaders,
body: &str,
) -> Result<TransactionNotify, WxPayError> {
let envelope = self.parse_notify(headers, body).await?;
let json = self.decrypt_notify_resource(&envelope.resource)?;
serde_json::from_str(&json)
.map_err(|e| WxPayError::NotifyError(format!("deserialize transaction notify: {e}")))
}
pub async fn parse_refund_notify(
&self,
headers: &NotifyHeaders,
body: &str,
) -> Result<RefundNotify, WxPayError> {
let envelope = self.parse_notify(headers, body).await?;
let json = self.decrypt_notify_resource(&envelope.resource)?;
serde_json::from_str(&json)
.map_err(|e| WxPayError::NotifyError(format!("deserialize refund notify: {e}")))
}
}