diff --git a/Cargo.toml b/Cargo.toml index a17ee6b..5573be8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "labrador" -version = "0.1.8" +version = "0.1.9" authors = ["mrpan <1049058427@qq.com>"] edition = "2018" description = "Labrador - Mini thirdpart client for rust." diff --git a/README.md b/README.md index 2a395fa..329d8f1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Labrador   [![Build Status]][actions] [![Latest Version]][crates.io] [![labrador: rustc 1.13+]][Rust 1.13] +# Labrador   [![Docs][docs-image]][docs-url] [![Build Status]][actions] [![Latest Version]][crates.io] [![labrador: rustc 1.13+]][Rust 1.13] [Build Status]: https://img.shields.io/docsrs/labrador/0.1.0?style=plastic [actions]: https://github.com/wslongchen/labrador/actions?query=branch%3Amaster @@ -7,7 +7,8 @@ [labrador: rustc 1.13+]: https://img.shields.io/badge/labrador-rustc__1.31%2B-lightgrey [Rust 1.13]: https://blog.rust-lang.org/2016/11/10/Rust-1.13.html [Rust 1.31]: https://blog.rust-lang.org/2018/12/06/Rust-1.31-and-rust-2018.html - +[docs-image]: https://img.shields.io/badge/文档-中文-blue.svg +[docs-url]: https://github.com/wslongchen/labrador/blob/master/README_CN.md ```Labrador - Mini client for rust ``` @@ -70,15 +71,15 @@ labrador = { version = "0.1.0", features = ["wechat", "alipay"] } ### With Wechat(微信开放平台、包含微信支付) ```rust -use labrador::{WeChatPayClient, SimpleStorage, TradeType, WeChatPayRequestV3, Amount, Payer}; +use labrador::{WechatPayClient, SimpleStorage, TradeType, WechatPayRequestV3, Amount, Payer}; use chrono::{Local, SecondsFormat}; #[tokio::main] async fn main() { - let c = WeChatPayClient::new("appid", "secret", SimpleStorage::new()); + let c = WechatPayClient::new("appid", "secret", SimpleStorage::new()); let mut client =c.wxpay(); let date = Local::now().to_rfc3339_opts(SecondsFormat::Secs, false); - let result = client.unified_order_v3(TradeType::Jsapi, WeChatPayRequestV3 { + let result = client.unified_order_v3(TradeType::Jsapi, WechatPayRequestV3 { appid: "appid".to_string().into(), mch_id: "mchid".to_string(), description: "测试商品支付".to_string(), diff --git a/README_CN.md b/README_CN.md new file mode 100644 index 0000000..6429dba --- /dev/null +++ b/README_CN.md @@ -0,0 +1,233 @@ +# Labrador   [![Docs][docs-image]][docs-url] [![Build Status]][actions] [![Latest Version]][crates.io] [![labrador: rustc 1.13+]][Rust 1.13] + +[Build Status]: https://img.shields.io/docsrs/labrador/0.1.0?style=plastic +[actions]: https://github.com/wslongchen/labrador/actions?query=branch%3Amaster +[Latest Version]: https://img.shields.io/crates/v/labrador?style=plastic +[crates.io]: https://crates.io/crates/labrador +[labrador: rustc 1.13+]: https://img.shields.io/badge/labrador-rustc__1.31%2B-lightgrey +[Rust 1.13]: https://blog.rust-lang.org/2016/11/10/Rust-1.13.html +[Rust 1.31]: https://blog.rust-lang.org/2018/12/06/Rust-1.31-and-rust-2018.html +[docs-image]: https://img.shields.io/badge/文档-英文-blue.svg +[docs-url]: https://github.com/wslongchen/labrador/blob/master/README.md +```Labrador - 一个迷你的便捷的第三方服务客户端SDK ``` + + +Features: + +* ```taobao``` - 淘宝客 +* ```alipay``` - 支付宝 +* ```pdd``` - 拼多多 +* ```jd``` - 京东 +* ```wechat``` - 微信 + +### Supported Platform + +| 平台 | 是否支持 | +|----------------------------------------------------|------| +| Wechat:mp(微信公众号),cp(企业微信),miniapp(微信小程序),pay(微信支付) | √ | +| Alipay(支付宝) | √ | +| Taobao(淘宝客) | √ | +| JD(京东联盟) | √ | +| PDD(拼多多-多多客) | √ | + + +--- + +如下: + +- [An overview of Labrador](https://crates.io/crates/labrador) +- [Examples](https://github.com/wslongchen/labrador/blob/0.1.0/example/simple.rs) +- [API documentation](https://docs.rs/labrador/0.1.0/labrador/) +- [Release notes](https://github.com/wslongchen/labrador/releases) + +## 使用 + +
+ +Click to show Cargo.toml. +Run this code in the playground. + + +```toml +[dependencies] + +# The core APIs +labrador = { version = "0.1.0", features = ["wechat", "alipay"] } + +``` + +
+

+ +## 文档 + +## 示例 + +### 微信开放平台、包含微信支付 + + ```rust +use labrador::{WechatPayClient, SimpleStorage, TradeType, WechatPayRequestV3, Amount, Payer}; +use chrono::{Local, SecondsFormat}; + + #[tokio::main] + async fn main() { + let c = WechatPayClient::new("appid", "secret", SimpleStorage::new()); + let mut client =c.wxpay(); + let date = Local::now().to_rfc3339_opts(SecondsFormat::Secs, false); + let result = client.unified_order_v3(TradeType::Jsapi, WechatPayRequestV3 { + appid: "appid".to_string().into(), + mch_id: "mchid".to_string(), + description: "测试商品支付".to_string(), + out_trade_no: "1602920235sdfsdfas32234234".to_string(), + time_expire: date, + attach: None, + notify_url: "https:xxx.cn/trade/notify".to_string(), + amount: Amount { + total: 1, + currency: String::from("CNY").into(), + payer_total: None, + payer_currency: None + }, + payer: Payer { + openid: "oUVZc6S_uGx3bsNPUA-davo4Dt7Us".to_string() + }.into(), + detail: None, + scene_info: None, + settle_info: None + }); + match result.await { + Ok(res) => {} + Err(err) => {} + } + } + ``` + +### 支付宝 + + ```rust +use labrador::{AlipayTradeWapPayRequest, AlipayClient}; + + #[tokio::main] + async fn main() { + let param = AlipayTradeWapPayRequest::default(); + let client = AlipayClient::new("appKey", false); + match client.wap_pay("POST".into(), param).await { + Ok(res) => {} + Err(err) => {} + } + match result.await { + Ok(res) => {} + Err(err) => {} + } + } + ``` + +### 淘宝客相关 + + ```rust +use labrador::{TbItemDetailRequest, TaobaoClient}; + + #[tokio::main] + async fn main() { + let client = TaobaoClient::::new("appkey", "secret"); + let req = TbItemDetailRequest { + num_iids: Some("597649283190".to_string()), + platform: None, + ip: None + }; + let result = client.get_item_detail(req); + match result.await { + Ok(res) => { + } + Err(err) => { + } + } + } + ``` + + +### 京东,目前暂时只支持联盟相关 + + ```rust +use labrador::{JDClient, JdOrderRawQueryParam}; +use chrono::{Local, SecondsFormat}; + + #[tokio::main] + async fn main() { + let client = JDClient::::new("appkey", "secert"); + let param = JdOrderRawQueryParam { + page_index: 1.into(), + page_size: 10.into(), + bill_type: 1, + start_time: "2022-08-02 21:23:00".to_string(), + end_time: "2022-08-02 21:43:00".to_string(), + child_union_id: None, + key: None, + fields: None + }; + let result = client.query_raw_order(param); + match result.await { + Ok(res) => { + } + Err(err) => { + } + } + } + ``` + +### 自定义请求 + +You can implement this trait and then use the custom request + ++ AlipayRequest - 支付宝 ++ JDRequest - 京东 ++ TaobaoRequest - 淘宝 + + +## 未来 + +我们将逐步完善相应的API +1. 首先非常欢迎和感谢对本项目发起 `Pull Request` 的热心小伙伴们。 +1. **特别提示:请务必在 `develop` 分支提交 `PR`,`release` 分支目前仅是正式版的代码,即发布正式版本后才会从 `develop` 分支进行合并。** +1. 本项目代码风格为使用2个空格代表一个Tab,因此在提交代码时请注意一下,否则很容易在IDE格式化代码后与原代码产生大量diff,这样会给其他人阅读代码带来极大的困扰。 +1. **提交代码前,请检查代码是否已经格式化,并且保证新增加或者修改的方法都有完整的参数说明,而pub方法必须拥有相应的单元测试并通过测试。** + +## 开发 + +To setup the development envrionment run `cargo run`. + +## 贡献者 + + MrPan <1049058427@qq.com> + +## Getting help + +拉布拉多是个人项目。一开始,我只是喜欢拉布拉多犬,因为我的爱好。 +我希望这个项目会变得越来越可爱。许多实用的其他函数将 +将在将来添加。我希望你能积极帮助这个项目成长并提出建议。 +我相信未来会越来越好。 + +[#general]: https://discord.com/channels/273534239310479360/274215136414400513 +[#beginners]: https://discord.com/channels/273534239310479360/273541522815713281 +[#rust-usage]: https://discord.com/channels/442252698964721669/443150878111694848 +[zulip]: https://rust-lang.zulipchat.com/#narrow/stream/122651-general +[stackoverflow]: https://stackoverflow.com/questions/tagged/rust +[/r/rust]: https://www.reddit.com/r/rust +[discourse]: https://users.rust-lang.org + +
+ +#### License + + +Licensed under either of Apache License, Version +2.0 or MIT license at your option. + + +
+ + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in Labrador by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. + \ No newline at end of file diff --git a/src/client.rs b/src/client.rs index aa898a5..77b3c45 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,6 +1,6 @@ use serde::Serialize; -use crate::{request::{LabraResponse, LabraRequest}, session::{SessionStore, SimpleStorage}, LabradorResult}; +use crate::{request::{LabraResponse, LabraRequest}, session::{SessionStore, SimpleStorage}, LabradorResult, RequestMethod, RequestType, Method}; /// API請求 #[derive(Debug, Clone)] @@ -89,6 +89,19 @@ impl APIClient { } req.request().await } + + /// 发送POST请求 + pub async fn post(&self, method: R, mut querys: Vec<(String, String)>, data: D, request_type: RequestType) -> LabradorResult { + let req = LabraRequest::new().url(method.get_method()).params(querys).method(Method::Post).json(data).req_type(request_type); + self.request(req).await + } + + /// 发送GET请求 + pub async fn get(&self, method: R, params: Vec<(&str, &str)>, request_type: RequestType) -> LabradorResult { + let querys = params.into_iter().map(|(k, v)| (k.to_string(), v.to_string())).collect::>(); + let req = LabraRequest::::new().url(method.get_method()).params(querys).method(Method::Get).req_type(request_type); + self.request(req).await + } } diff --git a/src/lib.rs b/src/lib.rs index 5eb0ab8..7f29911 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,15 +35,15 @@ //! ### With Wechat(微信开放平台、包含微信支付) //! //! ```rust -//! use labrador::{WeChatPayClient, SimpleStorage, TradeType, WeChatPayRequestV3, Amount, Payer}; +//! use labrador::{WechatPayClient, SimpleStorage, TradeType, WechatPayRequestV3, Amount, Payer}; //! use chrono::{Local, SecondsFormat}; //! //! #[tokio::main] //! async fn main() { -//! let c = WeChatPayClient::new("appid", "secret", SimpleStorage::new()); +//! let c = WechatPayClient::new("appid", "secret", SimpleStorage::new()); //! let mut client =c.wxpay(); //! let date = Local::now().to_rfc3339_opts(SecondsFormat::Secs, false); -//! let result = client.unified_order_v3(TradeType::Jsapi, WeChatPayRequestV3 { +//! let result = client.unified_order_v3(TradeType::Jsapi, WechatPayRequestV3 { //! appid: "appid".to_string().into(), //! mch_id: "mchid".to_string(), //! description: "测试商品支付".to_string(), @@ -198,7 +198,7 @@ pub use session::*; pub use util::*; pub use client::APIClient; pub use request::*; -pub use reqwest::multipart::Form; +pub use reqwest::multipart::{Form, Part}; pub use bytes; pub use serde_urlencoded; diff --git a/src/session.rs b/src/session.rs index 18830aa..8dd0d8a 100644 --- a/src/session.rs +++ b/src/session.rs @@ -51,9 +51,9 @@ impl ToRedisArgs for Store { fn write_redis_args(&self, out: &mut W) where W: ?Sized + redis::RedisWrite { - // let encoded: Vec = bincode::serialize(&self).unwrap(); - let encoded = serde_json::to_string(&self).unwrap_or_default(); - out.write_arg(encoded.as_bytes()) + let encoded: Vec = bincode::serialize(&self).unwrap(); + // let encoded = serde_json::to_string(&self).unwrap_or_default(); + out.write_arg(&encoded[..]) } } @@ -61,14 +61,8 @@ impl FromRedisValue for Store { fn from_redis_value(v: &redis::Value) -> redis::RedisResult { match *v { redis::Value::Data(ref bytes) => { - let data = String::from_utf8(bytes.to_vec()).unwrap_or_default(); - match serde_json::from_str::(&data) { - Ok(decoded) => Ok(decoded), - Err(_err) => { - // 出错了则直接返回该字符串 - Ok(Store::String(data)) - } - } + let data = bincode::deserialize::(bytes).unwrap_or(Store::Null); + Ok(data) }, redis::Value::Okay => Ok(Store::Null), _ => Err(redis::RedisError::from(( @@ -450,7 +444,7 @@ pub mod redis_store { - fn del>(&self, key: K) -> LabradorResult<()> { + pub fn del>(&self, key: K) -> LabradorResult<()> { let mut client = self.client_pool.get()?; if !client.check_connection() { return Err(LabraError::ApiError("error to get redis connection".to_string())) @@ -459,7 +453,7 @@ pub mod redis_store { Ok(()) } - fn zlcount, T: ToRedisArgs>(&self, key: K, min: T, max: T) -> LabradorResult> { + pub fn zlcount, T: ToRedisArgs>(&self, key: K, min: T, max: T) -> LabradorResult> { let mut client = self.client_pool.get()?; if !client.check_connection() { return Err(LabraError::ApiError("error to get redis connection".to_string())) @@ -467,7 +461,7 @@ pub mod redis_store { client.zcount(key.as_ref(), min, max).map_err(LabraError::from) } - fn zadd, T: ToRedisArgs>(&self, key: K, member: T, score: T) -> LabradorResult> { + pub fn zadd, T: ToRedisArgs>(&self, key: K, member: T, score: T) -> LabradorResult> { let mut client = self.client_pool.get()?; if !client.check_connection() { return Err(LabraError::ApiError("error to get redis connection".to_string())) @@ -489,7 +483,10 @@ pub mod redis_store { return Ok(default); } let v = if let Ok(value) = data { - Some(T::from_store(&value)) + match T::from_store_opt(&value) { + Ok(store) =>Some(store), + Err(_err) => None + } } else { default }; @@ -517,11 +514,14 @@ pub mod redis_store { #[test] fn test_simple() { println!("ssssssss"); - let session = SimpleStorage::new(); - println!("000000"); - let s = session.set("a", "n", Some(0)).unwrap(); - println!("1111"); - let v = session.get::<&str, String>("a", None).unwrap(); - - println!("v:{}" , v.unwrap_or_default()); + let encoded: Vec = bincode::serialize(&Store::String("234".to_string())).unwrap(); + let decode = bincode::deserialize::(&encoded).unwrap(); + println!("decode:{:?}", decode); + // let session = SimpleStorage::new(); + // println!("000000"); + // let s = session.set("a", "n", Some(0)).unwrap(); + // println!("1111"); + // let v = session.get::<&str, String>("a", None).unwrap(); + // + // println!("v:{}" , v.unwrap_or_default()); } \ No newline at end of file diff --git a/src/wechat/cp/api/codesession.rs b/src/wechat/cp/api/codesession.rs index 261e0c7..bc69e7d 100644 --- a/src/wechat/cp/api/codesession.rs +++ b/src/wechat/cp/api/codesession.rs @@ -1,20 +1,20 @@ use serde::{Serialize, Deserialize}; -use crate::{session::SessionStore, request::{RequestType}, WechatCommonResponse, LabradorResult, WeChatCpClient}; +use crate::{session::SessionStore, request::{RequestType}, WechatCommonResponse, LabradorResult, WechatCpClient}; use crate::wechat::cp::constants::{AUTHORIZATION_CODE, GRANT_TYPE, JS_CODE}; use crate::wechat::cp::method::WechatCpMethod; #[derive(Debug, Clone)] pub struct WechatCpCodeSession<'a, T: SessionStore> { - client: &'a WeChatCpClient, + client: &'a WechatCpClient, } #[allow(unused)] impl<'a, T: SessionStore> WechatCpCodeSession<'a, T> { #[inline] - pub fn new(client: &WeChatCpClient) -> WechatCpCodeSession { + pub fn new(client: &WechatCpClient) -> WechatCpCodeSession { WechatCpCodeSession { client, } diff --git a/src/wechat/cp/api/external_contact.rs b/src/wechat/cp/api/external_contact.rs index f624799..84647da 100644 --- a/src/wechat/cp/api/external_contact.rs +++ b/src/wechat/cp/api/external_contact.rs @@ -1,7 +1,7 @@ use serde::{Serialize, Deserialize}; use serde_json::{json, Value}; -use crate::{session::SessionStore, LabradorResult, RequestType, WeChatCpClient, LabraError, WechatCommonResponse}; +use crate::{session::SessionStore, LabradorResult, RequestType, WechatCpClient, LabraError, WechatCommonResponse}; use crate::wechat::cp::constants::{CURSOR, EXTERNAL_USERID, USERID, WELCOME_MSG_TYPE_FILE, WELCOME_MSG_TYPE_IMAGE, WELCOME_MSG_TYPE_LINK, WELCOME_MSG_TYPE_MINIPROGRAM, WELCOME_MSG_TYPE_VIDEO}; use crate::wechat::cp::method::{CpExternalContactMethod, WechatCpMethod}; @@ -9,13 +9,13 @@ use crate::wechat::cp::method::{CpExternalContactMethod, WechatCpMethod}; /// 外部联系人管理接口 #[derive(Debug, Clone)] pub struct WechatCpExternalContact<'a, T: SessionStore> { - client: &'a WeChatCpClient, + client: &'a WechatCpClient, } #[allow(unused)] impl<'a, T: SessionStore> WechatCpExternalContact<'a, T> { #[inline] - pub fn new(client: &WeChatCpClient) -> WechatCpExternalContact { + pub fn new(client: &WechatCpClient) -> WechatCpExternalContact { WechatCpExternalContact { client, } diff --git a/src/wechat/cp/api/group_robot.rs b/src/wechat/cp/api/group_robot.rs new file mode 100644 index 0000000..8a5cc44 --- /dev/null +++ b/src/wechat/cp/api/group_robot.rs @@ -0,0 +1,238 @@ +use serde::{Serialize, Deserialize}; + +use crate::{session::SessionStore, request::{RequestType}, WechatCommonResponse, LabradorResult, WechatCpClient, LabraError}; +use crate::wechat::cp::constants::{ GROUP_ROBOT_MSG_IMAGE, GROUP_ROBOT_MSG_MARKDOWN, GROUP_ROBOT_MSG_NEWS, GROUP_ROBOT_MSG_TEXT}; +use crate::wechat::cp::method::{WechatCpMethod}; + +/// 微信群机器人消息发送api +/// 文档地址:文档 +#[derive(Debug, Clone)] +pub struct WechatCpGroupRobot<'a, T: SessionStore> { + client: &'a WechatCpClient, +} + +#[allow(unused)] +impl<'a, T: SessionStore> WechatCpGroupRobot<'a, T> { + + #[inline] + pub fn new(client: &WechatCpClient) -> WechatCpGroupRobot { + WechatCpGroupRobot { + client, + } + } + + fn get_webhook_url(&self) -> LabradorResult { + if let Some(webhook_url) = &self.client.webhook_url { + Ok(webhook_url.to_string()) + } else { + return Err(LabraError::ApiError("请先设置WebhookKey".to_string())) + } + } + + ///
+    /// 发送text类型的消息
+    /// 
+ pub async fn send_text(&self, content: &str, mentioneds: Vec, mobiles: Vec) -> LabradorResult { + self.send_text_with_url(&self.get_webhook_url()?, content, mentioneds, mobiles).await + } + + + ///
+    /// 发送text类型的消息
+    /// 
+ pub async fn send_text_with_url(&self, webhook_url: &str, content: &str, mentioneds: Vec, mobiles: Vec) -> LabradorResult { + let req = WechatCpGroupRobotMessage { + msg_type: GROUP_ROBOT_MSG_TEXT.to_string(), + content: content.to_string().into(), + mentioned_list: mentioneds.into(), + mentioned_mobile_list: mobiles.into(), + base64: None, + md5: None, + articles: None, + media_id: None + }; + self.client.post(WechatCpMethod::Custom {need_token: false, method_url: webhook_url.to_string()}, vec![], req, RequestType::Json).await?.json::() + } + + ///
+    /// 发送markdown类型的消息
+    /// 
+ pub async fn send_markdown(&self, content: &str) -> LabradorResult { + self.send_markdown_with_url(&self.get_webhook_url()?, content).await + } + + + ///
+    /// 发送markdown类型的消息
+    /// 
+ pub async fn send_markdown_with_url(&self, webhook_url: &str, content: &str) -> LabradorResult { + let req = WechatCpGroupRobotMessage { + msg_type: GROUP_ROBOT_MSG_MARKDOWN.to_string(), + content: content.to_string().into(), + mentioned_list: None, + mentioned_mobile_list: None, + base64: None, + md5: None, + articles: None, + media_id: None + }; + self.client.post(WechatCpMethod::Custom {need_token: false, method_url: webhook_url.to_string()}, vec![], req, RequestType::Json).await?.json::() + } + + ///
+    /// 发送image类型的消息
+    /// 
+ pub async fn send_image(&self, base64: &str, md5: &str) -> LabradorResult { + self.send_image_with_url(&self.get_webhook_url()?, base64, md5).await + } + + + ///
+    /// 发送image类型的消息
+    /// 
+ pub async fn send_image_with_url(&self, webhook_url: &str, base64: &str, md5: &str) -> LabradorResult { + let req = WechatCpGroupRobotMessage { + msg_type: GROUP_ROBOT_MSG_IMAGE.to_string(), + content: None, + mentioned_list: None, + mentioned_mobile_list: None, + base64: base64.to_string().into(), + md5: md5.to_string().into(), + articles: None, + media_id: None + }; + self.client.post(WechatCpMethod::Custom {need_token: false, method_url: webhook_url.to_string()}, vec![], req, RequestType::Json).await?.json::() + } + + ///
+    /// 发送news类型的消息
+    /// 
+ pub async fn send_news(&self, articles: Vec) -> LabradorResult { + self.send_news_with_url(&self.get_webhook_url()?, articles).await + } + + + ///
+    /// 发送news类型的消息
+    /// 
+ pub async fn send_news_with_url(&self, webhook_url: &str, articles: Vec) -> LabradorResult { + let req = WechatCpGroupRobotMessage { + msg_type: GROUP_ROBOT_MSG_NEWS.to_string(), + content: None, + mentioned_list: None, + mentioned_mobile_list: None, + base64: None, + md5: None, + articles: articles.into(), + media_id: None + }; + self.client.post(WechatCpMethod::Custom {need_token: false, method_url: webhook_url.to_string()}, vec![], req, RequestType::Json).await?.json::() + } +} + +//---------------------------------------------------------------------------------------------------------------------------- + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpMenuInfo { + /// 企业编号 + pub buttons: Vec, + pub match_rule: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpMenuButton { + ///
+    /// 菜单的响应动作类型.
+    /// view表示网页类型,
+    /// click表示点击类型,
+    /// miniprogram表示小程序类型
+    /// 
+ #[serde(rename="type")] + pub r#type: String, + /// 菜单标题,不超过16个字节,子菜单不超过60个字节. + pub name: String, + ///
+    /// 菜单KEY值,用于消息接口推送,不超过128字节.
+    /// click等点击类型必须
+    /// 
+ pub key: Option, + ///
+    /// 网页链接.
+    /// 用户点击菜单可打开链接,不超过1024字节。type为miniprogram时,不支持小程序的老版本客户端将打开本url。
+    /// view、miniprogram类型必须
+    /// 
+ pub url: Option, + ///
+    /// 调用新增永久素材接口返回的合法media_id.
+    /// media_id类型和view_limited类型必须
+    /// 
+ pub media_id: Option, + ///
+    /// 调用发布图文接口获得的article_id.
+    /// article_id类型和article_view_limited类型必须
+    /// 
+ pub article_id: Option, + ///
+    /// 小程序的appid.
+    /// miniprogram类型必须
+    /// 
+ pub appid: Option, + ///
+    /// 小程序的页面路径.
+    /// miniprogram类型必须
+    /// 
+ pub pagepath: Option, + pub sub_button: Vec, +} + + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpMenuRule { + pub tag_id: Option, + pub sex: Option, + pub province: Option, + pub city: Option, + pub client_platform_type: Option, + pub language: Option, +} + + +/// 微信群机器人消息 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpGroupRobotMessage { + /// 消息类型 + pub msg_type: String, + /// 文本内容,最长不超过2048个字节,markdown内容,最长不超过4096个字节,必须是utf8编码 + pub content: Option, + /// userid的列表,提醒群中的指定成员(@某个成员),@all表示提醒所有人,如果开发者获取不到userid,可以使用mentioned_mobile_list + pub mentioned_list: Option>, + /// 手机号列表,提醒手机号对应的群成员(@某个成员),@all表示提醒所有人 + pub mentioned_mobile_list: Option>, + /// 图片内容的base64编码 + pub base64: Option, + /// 图片内容(base64编码前)的md5值 + pub md5: Option, + /// 图文消息,一个图文消息支持1到8条图文 + pub articles: Option>, + /// 文件id + pub media_id: Option, +} + + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpNewArticle { + /// 标题,不超过128个字节,超过会自动截断 + pub title: String, + /// 描述,不超过512个字节,超过会自动截断 + pub description: String, + /// 点击后跳转的链接 + pub url: Option, + /// 图文消息的图片链接,支持JPG、PNG格式,较好的效果为大图1068*455,小图150*150。 + pub pic_url: Option, + /// 按钮文字,仅在图文数为1条时才生效。 默认为“阅读全文”, 不超过4个文字,超过自动截断。该设置只在企业微信上生效,微工作台(原企业号)上不生效。 + pub btn_text: Option, + /// 小程序appid,必须是与当前应用关联的小程序,appid和pagepath必须同时填写,填写后会忽略url字段 + pub appid: Option, + /// 点击消息卡片后的小程序页面,仅限本小程序内的页面。appid和pagepath必须同时填写,填写后会忽略url字段 + pub pagepath: Option, +} diff --git a/src/wechat/cp/api/media.rs b/src/wechat/cp/api/media.rs index 9ae1cc1..eefd583 100644 --- a/src/wechat/cp/api/media.rs +++ b/src/wechat/cp/api/media.rs @@ -6,21 +6,21 @@ use bytes::Bytes; use serde::{Serialize, Deserialize}; use serde_json::Value; -use crate::{session::SessionStore, LabradorResult, RequestBody, RequestType, WeChatCpClient, WechatRequest, WechatCommonResponse, request, get_nonce_str}; +use crate::{session::SessionStore, LabradorResult, RequestBody, RequestType, WechatCpClient, WechatRequest, WechatCommonResponse, request, get_nonce_str}; use crate::wechat::cp::constants::{ATTACHMENT_TYPE, MEDIA_TYPE}; use crate::wechat::cp::method::{CpMediaMethod, WechatCpMethod}; #[derive(Debug, Clone)] pub struct WechatCpMedia<'a, T: SessionStore> { - client: &'a WeChatCpClient, + client: &'a WechatCpClient, } #[allow(unused)] impl<'a, T: SessionStore> WechatCpMedia<'a, T> { #[inline] - pub fn new(client: &WeChatCpClient) -> WechatCpMedia { + pub fn new(client: &WechatCpClient) -> WechatCpMedia { WechatCpMedia { client, } diff --git a/src/wechat/cp/api/menu.rs b/src/wechat/cp/api/menu.rs new file mode 100644 index 0000000..ab68e67 --- /dev/null +++ b/src/wechat/cp/api/menu.rs @@ -0,0 +1,143 @@ +use serde::{Serialize, Deserialize}; +use serde_json::Value; + +use crate::{session::SessionStore, request::{RequestType}, WechatCommonResponse, LabradorResult, WechatCpClient}; +use crate::wechat::cp::method::{CpMenuMethod, WechatCpMethod}; + +/// 菜单管理相关接口 +#[derive(Debug, Clone)] +pub struct WechatCpMenu<'a, T: SessionStore> { + client: &'a WechatCpClient, +} + +#[allow(unused)] +impl<'a, T: SessionStore> WechatCpMenu<'a, T> { + + #[inline] + pub fn new(client: &WechatCpClient) -> WechatCpMenu { + WechatCpMenu { + client, + } + } + + ///
+    /// 自定义菜单创建接口
+    /// 详情请见: 文档
+    ///
+    /// 注意: 这个方法使用配置里的agentId
+    /// 
+ pub async fn create(&self, menu: WechatCpMenuInfo) -> LabradorResult { + self.create_with_agentid(self.client.agent_id.to_owned().unwrap_or_default(), menu).await + } + + ///
+    /// 自定义菜单创建接口
+    /// 详情请见: 文档
+    /// 
+ pub async fn create_with_agentid(&self, agent_id: i32, req: WechatCpMenuInfo) -> LabradorResult { + self.client.post(WechatCpMethod::Menu(CpMenuMethod::Create(agent_id)), vec![], req, RequestType::Json).await?.json::() + } + + ///
+    /// 自定义菜单删除接口
+    /// 详情请见: 文档
+    ///
+    /// 注意: 这个方法使用配置里的agentId
+    /// 
+ pub async fn delete(&self) -> LabradorResult { + self.delete_with_agentid(self.client.agent_id.to_owned().unwrap_or_default()).await + } + + ///
+    /// 自定义菜单删除接口
+    /// 详情请见: 文档
+    /// 
+ pub async fn delete_with_agentid(&self, agent_id: i32) -> LabradorResult { + self.client.post(WechatCpMethod::Menu(CpMenuMethod::Delete(agent_id)), vec![], Value::Null, RequestType::Json).await?.json::() + } + + ///
+    /// 自定义菜单查询接口
+    /// 详情请见: 文档
+    ///
+    /// 注意: 这个方法使用配置里的agentId
+    /// 
+ pub async fn get(&self) -> LabradorResult { + self.delete_with_agentid(self.client.agent_id.to_owned().unwrap_or_default()).await + } + + ///
+    /// 自定义菜单查询接口
+    /// 详情请见: 文档
+    /// 
+ pub async fn get_with_agentid(&self, agent_id: i32) -> LabradorResult { + let v = self.client.post(WechatCpMethod::Menu(CpMenuMethod::Get(agent_id)), vec![], Value::Null, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } +} + +//---------------------------------------------------------------------------------------------------------------------------- + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpMenuInfo { + /// 企业编号 + pub buttons: Vec, + pub match_rule: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpMenuButton { + ///
+    /// 菜单的响应动作类型.
+    /// view表示网页类型,
+    /// click表示点击类型,
+    /// miniprogram表示小程序类型
+    /// 
+ #[serde(rename="type")] + pub r#type: String, + /// 菜单标题,不超过16个字节,子菜单不超过60个字节. + pub name: String, + ///
+    /// 菜单KEY值,用于消息接口推送,不超过128字节.
+    /// click等点击类型必须
+    /// 
+ pub key: Option, + ///
+    /// 网页链接.
+    /// 用户点击菜单可打开链接,不超过1024字节。type为miniprogram时,不支持小程序的老版本客户端将打开本url。
+    /// view、miniprogram类型必须
+    /// 
+ pub url: Option, + ///
+    /// 调用新增永久素材接口返回的合法media_id.
+    /// media_id类型和view_limited类型必须
+    /// 
+ pub media_id: Option, + ///
+    /// 调用发布图文接口获得的article_id.
+    /// article_id类型和article_view_limited类型必须
+    /// 
+ pub article_id: Option, + ///
+    /// 小程序的appid.
+    /// miniprogram类型必须
+    /// 
+ pub appid: Option, + ///
+    /// 小程序的页面路径.
+    /// miniprogram类型必须
+    /// 
+ pub pagepath: Option, + pub sub_button: Vec, +} + + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpMenuRule { + pub tag_id: Option, + pub sex: Option, + pub province: Option, + pub city: Option, + pub client_platform_type: Option, + pub language: Option, +} diff --git a/src/wechat/cp/api/message.rs b/src/wechat/cp/api/message.rs new file mode 100644 index 0000000..e40f20e --- /dev/null +++ b/src/wechat/cp/api/message.rs @@ -0,0 +1,415 @@ +use serde::{Serialize, Deserialize}; +use serde_json::{Value}; + +use crate::{session::SessionStore, request::{RequestType}, WechatCommonResponse, LabradorResult, WechatCpClient, WechatCpNewArticle, WechatMpNewsArticle}; +use crate::wechat::cp::method::{CpMessageMethod, WechatCpMethod}; + +/// 菜单管理相关接口 +#[derive(Debug, Clone)] +pub struct WechatCpMessage<'a, T: SessionStore> { + client: &'a WechatCpClient, +} + +#[allow(unused)] +impl<'a, T: SessionStore> WechatCpMessage<'a, T> { + + #[inline] + pub fn new(client: &WechatCpClient) -> WechatCpMessage { + WechatCpMessage { + client, + } + } + + ///
+    /// 发送消息
+    /// 详情请见: 文档
+    /// 
+ pub async fn send(&self, mut req: WechatCpMessageRequest) -> LabradorResult { + let agent_id = req.agent_id.unwrap_or_default(); + if agent_id == 0 { + req.agent_id = self.client.agent_id; + } + let v= self.client.post(WechatCpMethod::Message(CpMessageMethod::Send), vec![], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + + ///
+    /// 互联企业的应用支持推送文本、图片、视频、文件、图文等类型。
+    /// 详情请见: 文档
+    /// 
+ pub async fn send_linked_corp_message(&self, mut req: WechatCpLinkedCorpMessage) -> LabradorResult { + let agent_id = req.agent_id.unwrap_or_default(); + if agent_id == 0 { + req.agent_id = self.client.agent_id; + } + let v = self.client.post(WechatCpMethod::Message(CpMessageMethod::LinkedCorpSend), vec![], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + + ///
+    /// 发送「学校通知」
+    /// 

+ /// 学校可以通过此接口来给家长发送不同类型的学校通知,来满足多种场景下的学校通知需求。目前支持的消息类型为文本、图片、语音、视频、文件、图文。 + ///

+ /// 详情请见: 文档 + ///

+ pub async fn send_school_contact_message(&self, mut req: WechatCpSchoolContactMessage) -> LabradorResult { + let agent_id = req.agentid.unwrap_or_default(); + if agent_id == 0 { + req.agentid = self.client.agent_id; + } + let v = self.client.post(WechatCpMethod::Message(CpMessageMethod::LinkedCorpSend), vec![], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + + ///
+    /// 查询应用消息发送统计
+    /// 请求方式:POST(HTTPS)
+    /// 请求地址: 文档
+    ///
+    /// 详情请见:  文档
+    /// 
+ pub async fn get_statistics(&self, req: WechatCpLinkedCorpMessage) -> LabradorResult { + let v = self.client.post(WechatCpMethod::Message(CpMessageMethod::Statistics), vec![], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + +} + +//---------------------------------------------------------------------------------------------------------------------------- + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpMessageRequest { + pub to_user: String, + pub to_party: Option, + pub to_tag: Option, + pub agent_id: Option, + pub msg_type: String, + pub content: String, + pub media_id: Option, + pub thumb_media_id: Option, + pub title: Option, + pub description: Option, + pub music_url: Option, + pub hq_music_url: Option, + pub safe: Option, + pub url: Option, + pub btn_txt: Option, + pub app_id: Option, + pub page: Option, + /// 任务卡片特有的属性 + pub task_id: Option, + /// 模板卡片类型,文本通知型卡片填写 “text_notice”, + /// 图文展示型卡片此处填写 “news_notice”, + /// 按钮交互型卡片填写”button_interaction”, + /// 投票选择型卡片填写”vote_interaction”, + /// 多项选择型卡片填写 “multiple_interaction” + pub card_type: Option, + /// 卡片来源样式信息,不需要来源样式可不填写 + /// 来源图片的url + pub source_icon_url: Option, + /// 卡片来源样式信息,不需要来源样式可不填写 + /// 来源图片的描述,建议不超过20个字 + pub source_desc: Option, + /// 更多操作界面的描述 + pub action_menu_desc: Option, + /// 任务卡片特有的属性 + pub task_buttons: Option>, + pub emphasis_first_item: Option, + /// 来源文字的颜色,目前支持:0(默认) 灰色,1 黑色,2 红色,3 绿色 + pub source_desc_color: Option, + /// 表示是否开启id转译,0表示否,1表示是,默认0 + pub enable_id_trans: Option, + /// 表示是否开启重复消息检查,0表示否,1表示是,默认0 + pub enable_duplicate_check: Option, + /// 表示是否重复消息检查的时间间隔,默认1800s,最大不超过4小时 + pub duplicate_check_interval: Option, + pub content_items: Option, + pub articles: Option>, + pub mpnews_articles: Option>, + pub action_menu_action_list: Option>, + /// 一级标题,建议不超过36个字 + pub main_title: Option, + /// 标题辅助信息,建议不超过44个字 + pub main_title_desc: Option, + /// 图文展示型的卡片必须有图片字段。 + /// 图片的url. + pub card_image_url: Option, + /// 关键数据样式 + /// 关键数据样式的数据内容,建议不超过14个字. + pub emphasis_content_title: Option, + /// 关键数据样式的数据描述内容,建议不超过22个字 + pub emphasis_content_desc: Option, + /// 二级普通文本,建议不超过160个字 + pub sub_title_text: Option, + /// 卡片二级垂直内容,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过4 + pub vertical_contents: Option>, + /// 二级标题+文本列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6 + pub horizontal_contents: Option>, + /// 跳转指引样式的列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过3 + pub jumps: Option>, + /// 整体卡片的点击跳转事件,text_notice必填本字段 + /// 跳转事件类型,1 代表跳转url,2 代表打开小程序。text_notice卡片模版中该字段取值范围为[1,2] + pub card_action_type: Option, + /// 跳转事件的url,card_action.type是1时必填 + pub card_action_url: Option, + /// 跳转事件的小程序的appid,必须是与当前应用关联的小程序,card_action.type是2时必填 + pub card_action_appid: Option, + /// 跳转事件的小程序的pagepath,card_action.type是2时选填 + pub card_action_pagepath: Option, + /// 按钮交互型卡片需指定 + /// 按钮列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6 + pub buttons: Option>, + /// 投票选择型卡片需要指定 + /// 选择题key值,用户提交选项后,会产生回调事件,回调事件会带上该key值表示该题,最长支持1024字节 + pub checkbox_question_key: Option, + /// 选择题模式,单选:0,多选:1,不填默认0 + pub checkbox_mode: Option, + /// 选项list,选项个数不超过 20 个,最少1个 + pub options: Option>, + /// 按钮文案,建议不超过10个字,不填默认为提交 + pub submit_button_text: Option, + /// 提交按钮的key,会产生回调事件将本参数作为EventKey返回,最长支持1024字节 + pub submit_button_key: Option, + /// 下拉式的选择器列表,multiple_interaction类型的卡片该字段不可为空,一个消息最多支持 3 个选择器 + pub selects: Option>, + /// 引用文献样式 + pub quote_area: Option, + /// 图片的url. + pub card_image_aspect_ratio: Option, +} + +/// 引用文献样式 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct QuoteArea { + /// 非必填 引用文献样式区域点击事件,0或不填代表没有点击事件,1 代表跳转url,2 代表跳转小程序 + #[serde(rename="type")] + pub r#type: Option, + /// 点击跳转的url,quote_area.type是1时必填 + pub url: Option, + /// 点击跳转的小程序的appid,必须是与当前应用关联的小程序,quote_area.type是2时必填 + pub appid: Option, + /// 点击跳转的小程序的pagepath,quote_area.type是2时选填 + pub pagepath: Option, + /// 引用文献样式的标题 + pub title: Option, + /// 引用文献样式的引用文案 + pub quote_text: Option, +} + +/// 任务卡片按钮 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TaskCardButton { + pub key: Option, + pub name: Option, + pub color: Option, + pub bold: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ActionMenuItem { + /// 操作的描述文案 + pub text: Option, + /// 按钮key值,用户点击后,会产生回调事件将本参数作为EventKey返回,回调事件会带上该key值,最长支持1024字节,不可重复 + pub key: Option, +} + +/// 卡片二级垂直内容,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过4 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VerticalContent { + /// 卡片二级标题,建议不超过38个字.必填字段 + pub title: Option, + /// 二级普通文本,建议不超过160个字 + pub desc: Option, +} +/// 二级标题+文本列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct HorizontalContent { + /// 链接类型,0或不填代表不是链接,1 代表跳转url,2 代表下载附件 + #[serde(rename="type")] + pub r#type: Option, + /// 二级标题,建议不超过5个字 + pub keyname: Option, + /// 二级文本,如果horizontal_content_list.type是2,该字段代表文件名称(要包含文件类型),建议不超过30个字 + pub value: Option, + /// 链接跳转的url,horizontal_content_list.type是1时必填 + pub url: Option, + /// 附件的media_id,horizontal_content_list.type是2时必填 + pub media_id: Option, + /// 成员详情的userid,horizontal_content_list.type是3时必填 + pub userid: Option, +} + +/// 跳转指引样式的列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过3 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TemplateCardJump { + /// 跳转链接类型,0或不填代表不是链接,1 代表跳转url,2 代表跳转小程序 + #[serde(rename="type")] + pub r#type: Option, + /// 跳转链接样式的文案内容,建议不超过18个字 + pub title: Option, + /// 跳转链接的url,jump_list.type是1时必填 + pub url: Option, + /// 跳转链接的小程序的appid,必须是与当前应用关联的小程序,jump_list.type是2时必填 + pub appid: Option, + /// 跳转链接的小程序的pagepath,jump_list.type是2时选填 + pub pagepath: Option, +} + + +/// 按钮列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TemplateCardButton { + /// 按钮文案,建议不超过10个字 + pub text: Option, + /// 按钮样式,目前可填1~4,不填或错填默认1 + pub style: Option, + /// 按钮key值,用户点击后,会产生回调事件将本参数作为EventKey返回,回调事件会带上该key值,最长支持1024字节,不可重复 + pub key: Option, +} + + +/// 按钮列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CheckboxOption { + /// 选项id,用户提交选项后,会产生回调事件,回调事件会带上该id值表示该选项,最长支持128字节,不可重复 + pub id: String, + /// 选项文案描述,建议不超过17个字. + pub text: String, + /// 该选项是否要默认选中 + pub is_checked: bool, +} + + +/// 下拉式的选择器列表,multiple_interaction类型的卡片该字段不可为空,一个消息最多支持 3 个选择器 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MultipleSelect { + /// 下拉式的选择器题目的key,用户提交选项后,会产生回调事件,回调事件会带上该key值表示该题,最长支持1024字节,不可重复 + pub question_key: Option, + /// 下拉式的选择器上面的title + pub title: Option, + /// 默认选定的id,不填或错填默认第一个 + pub selected_id: Option, + /// 选项列表,下拉选项不超过 10 个,最少1个 + pub options: Option>, +} + +/// 应用消息发送统计信息 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpMessageSendStatistics { + pub statistics: Option>, +} + +/// 应用消息发送统计信息 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct StatisticItem { + /// 应用名 + pub app_name: Option, + /// 应用id + pub agentid: Option, + /// 发消息成功人次 + pub count: Option, +} + + +/// 互联企业消息 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpLinkedCorpMessage { + /// 1表示发送给应用可见范围内的所有人(包括互联企业的成员),默认为0 + pub is_to_all: Option, + /// 成员ID列表(消息接收者,最多支持1000个)。每个元素的格式为: corpid/userid,其中,corpid为该互联成员所属的企业,userid为该互联成员所属企业中的帐号。如果是本企业的成员,则直接传userid即可 + pub to_users: Option>, + /// 部门ID列表,最多支持100个。partyid在互联圈子内唯一。每个元素都是字符串类型,格式为:linked_id/party_id,其中linked_id是互联id,party_id是在互联圈子中的部门id。如果是本企业的部门,则直接传party_id即可。 + pub to_parties: Option>, + /// 本企业的标签ID列表,最多支持100个。 + pub to_tags: Option>, + /// 企业应用的id,整型。可在应用的设置页面查看 + pub agent_id: Option, + /// 企业应用的id,整型。可在应用的设置页面查看 + pub msg_type: String, + /// 消息内容,最长不超过2048个字节 + pub content: String, + /// 图片媒体文件id,可以调用上传临时素材接口获取 + pub media_id: Option, + pub thumb_media_id: Option, + pub title: Option, + pub description: Option, + pub url: Option, + pub appid: Option, + pub page: Option, + pub emphasis_first_item: Option, + pub content_items: Option, + pub btn_txt: Option, + pub articles: Option>, + pub mp_news_articles: Option>, + /// 表示是否是保密消息,0表示否,1表示是,默认0 + pub is_safe: Option, +} + + + +/// 发送「学校通知」 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpSchoolContactMessage { + /// 指定发送对象,0表示发送给家长,1表示发送给学生,2表示发送给家长和学生,默认为0。 + pub recv_scope: Option, + /// 家校通讯录家长列表,recv_scope为0或2表示发送给对应的家长,recv_scope为1忽略,(最多支持1000个) + pub to_parent_userid: Option>, + /// 家校通讯录学生列表,recv_scope为0表示发送给学生的所有家长,recv_scope为1表示发送给学生,recv_scope为2表示发送给学生和学生的所有家长(最多支持1000个) + pub to_student_userid: Option>, + /// 家校通讯录部门列表,recv_scope为0表示发送给班级的所有家长,recv_scope为1表示发送给班级的所有学生,recv_scope为2表示发送给班级的所有学生和家长(最多支持100个) + pub to_party: Option>, + /// 1表示字段生效,0表示字段无效。recv_scope为0表示发送给学校的所有家长,recv_scope为1表示发送给学校的所有学生,recv_scope为2表示发送给学校的所有学生和家长,默认为0 + pub to_all: Option, + /// 企业应用的id,整型。可在应用的设置页面查看 + pub agentid: Option, + /// 消息类型 + pub msgtype: String, + /// 消息内容,最长不超过2048个字节 + pub content: String, + /// 表示是否开启id转译,0表示否,1表示是,默认0 + pub enable_id_trans: Option, + /// 表示是否开启重复消息检查,0表示否,1表示是,默认0 + pub enable_duplicate_check: Option, + /// 表示是否重复消息检查的时间间隔,默认1800s,最大不超过4小时 + pub duplicate_check_interval: Option, + /// 图片媒体文件id,可以调用上传临时素材接口获取 + pub media_id: Option, + /// 小程序消息封面的mediaid,封面图建议尺寸为520*416 + pub thumb_media_id: Option, + pub title: Option, + pub description: Option, + pub url: Option, + pub appid: Option, + pub pagepath: Option, + pub articles: Option>, + pub mp_news_articles: Option>, +} + + + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpLinkedCorpMessageResponse { + pub invaliduser: Option>, + pub invalidparty: Option>, + pub invalidtag: Option>, +} + + + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpSchoolContactMessageResponse { + pub invalid_parent_userid: Option>, + pub invalid_student_userid: Option>, + pub invalid_party: Option>, +} + + + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpMessageResponse { + pub invaliduser: Option, + pub invalidparty: Option, + pub invalidtag: Option, + pub msgid: Option, +} diff --git a/src/wechat/cp/api/mod.rs b/src/wechat/cp/api/mod.rs index 910d8f8..c01b368 100644 --- a/src/wechat/cp/api/mod.rs +++ b/src/wechat/cp/api/mod.rs @@ -2,6 +2,9 @@ mod codesession; mod media; mod oauth2; mod external_contact; +mod menu; +mod group_robot; +mod message; // 企业微信 @@ -9,5 +12,6 @@ pub use self::codesession::*; pub use self::media::*; pub use self::oauth2::*; pub use self::external_contact::*; - - +pub use self::menu::*; +pub use self::group_robot::*; +pub use self::message::*; diff --git a/src/wechat/cp/api/oauth2.rs b/src/wechat/cp/api/oauth2.rs index 3c74562..ae955d3 100644 --- a/src/wechat/cp/api/oauth2.rs +++ b/src/wechat/cp/api/oauth2.rs @@ -1,21 +1,21 @@ use serde::{Serialize, Deserialize}; use serde_json::{json, Value}; -use crate::{session::SessionStore, request::{RequestType}, WechatCommonResponse, LabradorResult, WeChatCpClient}; +use crate::{session::SessionStore, request::{RequestType}, WechatCommonResponse, LabradorResult, WechatCpClient}; use crate::wechat::cp::constants::{AGENTID, CODE, SNSAPI_BASE, SNSAPI_PRIVATEINFO, SNSAPI_USERINFO}; use crate::wechat::cp::method::{CpOauth2Method, WechatCpMethod}; #[derive(Debug, Clone)] pub struct WechatCpOauth2<'a, T: SessionStore> { - client: &'a WeChatCpClient, + client: &'a WechatCpClient, } #[allow(unused)] impl<'a, T: SessionStore> WechatCpOauth2<'a, T> { #[inline] - pub fn new(client: &WeChatCpClient) -> WechatCpOauth2 { + pub fn new(client: &WechatCpClient) -> WechatCpOauth2 { WechatCpOauth2 { client, } diff --git a/src/wechat/cp/constants.rs b/src/wechat/cp/constants.rs index 5dd6c85..73ed8e5 100644 --- a/src/wechat/cp/constants.rs +++ b/src/wechat/cp/constants.rs @@ -13,12 +13,17 @@ pub static LANG: &str = "lang"; pub static ZH_CN: &str = "zh_CN"; pub static SECRET: &str = "secret"; pub static ACCESS_TOKEN: &str = "access_token"; +pub static PROVIDER_ACCESS_TOKEN: &str = "provider_access_token"; pub static REFRESH_TOKEN: &str = "refresh_token"; pub static EXTERNAL_USERID: &str = "external_userid"; pub static CURSOR: &str = "cursor"; pub static USERID: &str = "userid"; pub static MEDIA_TYPE: &str = "media_type"; +pub static TYPE: &str = "type"; pub static ATTACHMENT_TYPE: &str = "attachment_type"; +pub static AUTH_URL_INSTALL: &str = "https://open.work.weixin.qq.com/3rdapp/install"; + +pub static ACCESS_TOKEN_KEY: &str = ":accessTokenKey:"; /** * 不弹出授权页面,直接跳转,只能获取用户openid. @@ -56,4 +61,30 @@ pub static WELCOME_MSG_TYPE_MINIPROGRAM: &str = "miniprogram"; /** * 文件消息. */ -pub static WELCOME_MSG_TYPE_FILE: &str = "file"; \ No newline at end of file +pub static WELCOME_MSG_TYPE_FILE: &str = "file"; + + +/** + * 文本消息. + */ +pub static GROUP_ROBOT_MSG_TEXT: &str = "text"; + +/** + * 图片消息. + */ +pub static GROUP_ROBOT_MSG_IMAGE: &str = "image"; + +/** + * markdown消息. + */ +pub static GROUP_ROBOT_MSG_MARKDOWN: &str = "markdown"; + +/** + * 图文消息(点击跳转到外链). + */ +pub static GROUP_ROBOT_MSG_NEWS: &str = "news"; + +/** + * 文件类型消息. + */ +pub static GROUP_ROBOT_MSG_FILE: &str = "file"; \ No newline at end of file diff --git a/src/wechat/cp/method.rs b/src/wechat/cp/method.rs index 86ded6e..467442a 100644 --- a/src/wechat/cp/method.rs +++ b/src/wechat/cp/method.rs @@ -4,15 +4,26 @@ use crate::RequestMethod; #[derive(Debug, PartialEq, Clone)] pub enum WechatCpMethod { AccessToken, + GetProviderToken, + GetSuiteToken, + GetCorpToken, JsCode2Session, + GetPermanentCode, + GetPreAuthCode, GetJsapiTicket, GetAgentConfigTicket, + GetSuiteJsapiTicket, GetCallbackIp, + GetAuthInfo, Media(CpMediaMethod), + Tag(CpTagMethod), + License(CpLicenseMethod), Oauth2(CpOauth2Method), + Menu(CpMenuMethod), + Message(CpMessageMethod), ExternalContact(CpExternalContactMethod), /// 自定义方法 - Custom(String) + Custom{ need_token: bool, method_url: String } } impl RequestMethod for WechatCpMethod { @@ -20,13 +31,24 @@ impl RequestMethod for WechatCpMethod { match self { WechatCpMethod::AccessToken => String::from("/cgi-bin/gettoken"), WechatCpMethod::GetJsapiTicket => String::from("/cgi-bin/get_jsapi_ticket"), + WechatCpMethod::GetSuiteJsapiTicket => String::from("/cgi-bin/ticket/get"), + WechatCpMethod::GetPreAuthCode => String::from("/cgi-bin/service/get_pre_auth_code"), + WechatCpMethod::GetAuthInfo => String::from("/cgi-bin/service/get_auth_info"), + WechatCpMethod::GetPermanentCode => String::from("/cgi-bin/service/get_permanent_code"), + WechatCpMethod::GetProviderToken => String::from("/cgi-bin/service/get_provider_token"), + WechatCpMethod::GetCorpToken => String::from("/cgi-bin/service/get_corp_token"), + WechatCpMethod::GetSuiteToken => String::from("/cgi-bin/service/get_suite_token"), WechatCpMethod::JsCode2Session => String::from("/cgi-bin/miniprogram/jscode2session"), WechatCpMethod::GetCallbackIp => String::from("/cgi-bin/getcallbackip"), WechatCpMethod::GetAgentConfigTicket => String::from("/cgi-bin/ticket/get?&type=agent_config"), WechatCpMethod::Media(v) => v.get_method(), WechatCpMethod::ExternalContact(v) => v.get_method(), WechatCpMethod::Oauth2(v) => v.get_method(), - WechatCpMethod::Custom(v) => v.to_string(), + WechatCpMethod::Custom{ method_url, .. } => method_url.to_string(), + WechatCpMethod::Menu(v) => v.get_method(), + WechatCpMethod::Message(v) => v.get_method(), + WechatCpMethod::Tag(v) => v.get_method(), + WechatCpMethod::License(v) => v.get_method(), } } } @@ -36,6 +58,7 @@ impl WechatCpMethod { pub fn need_token(&self) -> bool { match self { + WechatCpMethod::Custom{ need_token, .. } => *need_token, WechatCpMethod::AccessToken => false, _ => true, } @@ -72,6 +95,126 @@ impl CpMediaMethod { } +#[allow(unused)] +#[derive(Debug, PartialEq, Clone)] +pub enum CpTagMethod { + Create, + Update, + List, + AddTagUsers, + DeleteTagUsers, + Delete(String), + Get(String), +} + +#[allow(unused)] +impl CpTagMethod { + pub fn get_method(&self) -> String { + match self { + CpTagMethod::Create => String::from("/cgi-bin/tag/create"), + CpTagMethod::Update => String::from("/cgi-bin/tag/update"), + CpTagMethod::List => String::from("/cgi-bin/tag/list"), + CpTagMethod::AddTagUsers => String::from("/cgi-bin/tag/addtagusers"), + CpTagMethod::DeleteTagUsers => String::from("/cgi-bin/tag/deltagusers"), + CpTagMethod::Delete(v) => format!("/cgi-bin/tag/delete?tagid={}", v), + CpTagMethod::Get(v) => format!("/cgi-bin/tag/get?tagid={}", v), + } + } +} + + + + +#[allow(unused)] +#[derive(Debug, PartialEq, Clone)] +pub enum CpLicenseMethod { + CreateOrder, + CreateRenewOrderJob, + SubmitOrderJob, + ListOrder, + GetOrder, + ListOrderCount, + ActiveAccount, + BatchActiveAccount, + GetActiveInfoByCode, + BatchGetActiveInfoByCode, + ListActivedAccount, + GetActiveInfoByUser, + BatchTransferLicense, +} + +#[allow(unused)] +impl CpLicenseMethod { + pub fn get_method(&self) -> String { + match self { + CpLicenseMethod::CreateOrder => String::from("/cgi-bin/license/create_new_order"), + CpLicenseMethod::CreateRenewOrderJob => String::from("/cgi-bin/license/create_renew_order_job"), + CpLicenseMethod::SubmitOrderJob => String::from("/cgi-bin/license/submit_order_job"), + CpLicenseMethod::ListOrder => String::from("/cgi-bin/license/list_order"), + CpLicenseMethod::GetOrder => String::from("/cgi-bin/license/get_order"), + CpLicenseMethod::ListOrderCount => String::from("/cgi-bin/license/list_order_account"), + CpLicenseMethod::ActiveAccount => String::from("/cgi-bin/license/active_account"), + CpLicenseMethod::BatchActiveAccount => String::from("/cgi-bin/license/batch_active_account"), + CpLicenseMethod::GetActiveInfoByCode => String::from("/cgi-bin/license/get_active_info_by_code"), + CpLicenseMethod::BatchGetActiveInfoByCode => String::from("/cgi-bin/license/batch_get_active_info_by_code"), + CpLicenseMethod::ListActivedAccount => String::from("/cgi-bin/license/list_actived_account"), + CpLicenseMethod::GetActiveInfoByUser => String::from("/cgi-bin/license/get_active_info_by_user"), + CpLicenseMethod::BatchTransferLicense => String::from("/cgi-bin/license/batch_transfer_license"), + } + } +} + + +#[allow(unused)] +#[derive(Debug, PartialEq, Clone)] +pub enum CpMenuMethod { + Create(i32), + Delete(i32), + Get(i32), +} + +#[allow(unused)] +impl CpMenuMethod { + pub fn get_method(&self) -> String { + match self { + CpMenuMethod::Create(v) => format!("/cgi-bin/menu/create?agentid={}", v), + CpMenuMethod::Delete(v) => format!("/cgi-bin/menu/delete?agentid={}", v), + CpMenuMethod::Get(v) => format!("/cgi-bin/menu/get?agentid={}", v), + } + } +} + + + + +#[allow(unused)] +#[derive(Debug, PartialEq, Clone)] +pub enum CpMessageMethod { + /// 发送应用消息 + Send, + /// 查询应用消息发送统计 + Statistics, + /// 互联企业发送应用消息 + /// https://developer.work.weixin.qq.com/document/path/90250 + LinkedCorpSend, + /// 发送学校通知 + /// https://developer.work.weixin.qq.com/document/path/92321 + ExternalContactSend, +} + +#[allow(unused)] +impl CpMessageMethod { + pub fn get_method(&self) -> String { + match self { + CpMessageMethod::Send => String::from("/cgi-bin/message/send"), + CpMessageMethod::Statistics => String::from("/cgi-bin/message/get_statistics"), + CpMessageMethod::LinkedCorpSend => String::from("/cgi-bin/linkedcorp/message/send"), + CpMessageMethod::ExternalContactSend => String::from("/cgi-bin/externalcontact/message/send"), + } + } +} + + #[allow(unused)] #[derive(Debug, PartialEq, Clone)] pub enum CpOauth2Method { diff --git a/src/wechat/cp/mod.rs b/src/wechat/cp/mod.rs index 1c36e1b..724f30c 100644 --- a/src/wechat/cp/mod.rs +++ b/src/wechat/cp/mod.rs @@ -1,24 +1,27 @@ -use crate::{session::SessionStore, client::APIClient, request::{Method, RequestType, LabraResponse, LabraRequest, RequestMethod}, util::current_timestamp, LabradorResult, SimpleStorage, WeChatCrypto, WechatRequest, get_timestamp, get_nonce_str, WechatCommonResponse}; +use crate::{session::SessionStore, client::APIClient, request::{Method, RequestType, LabraResponse, LabraRequest, RequestMethod}, util::current_timestamp, LabradorResult, SimpleStorage, WechatCrypto, WechatRequest, get_timestamp, get_nonce_str, WechatCommonResponse}; use serde::{Serialize, Deserialize}; -use serde_json::Value; +use serde_json::{json, Value}; mod method; mod api; #[allow(unused)] mod constants; +mod tp; pub use api::*; +pub use tp::*; use crate::wechat::cp::constants::{ACCESS_TOKEN, CORPID, CORPSECRET}; use crate::wechat::cp::method::{WechatCpMethod}; #[allow(unused)] #[derive(Debug, Clone)] -pub struct WeChatCpClient { +pub struct WechatCpClient { corp_id: String, corp_secret: String, token: Option, aes_key: Option, oauth2_redirect_uri: Option, + webhook_url: Option, agent_id: Option, client: APIClient, } @@ -59,16 +62,27 @@ pub struct AgentJsapiSignature { pub timestamp: i64, } + +#[allow(unused)] +#[derive(Serialize, Deserialize)] +pub struct WechatCpProviderToken { + /// 服务商的access_token,最长为512字节。 + pub provider_access_token: String, + /// provider_access_token有效期(秒) + pub expires_in: i64, +} + #[allow(unused)] -impl WeChatCpClient { +impl WechatCpClient { - fn from_client(client: APIClient) -> WeChatCpClient { - WeChatCpClient { + fn from_client(client: APIClient) -> WechatCpClient { + WechatCpClient { corp_id: client.app_key.to_owned(), corp_secret: client.secret.to_owned(), token: None, aes_key: None, oauth2_redirect_uri: None, + webhook_url: None, agent_id: None, client } @@ -89,14 +103,19 @@ impl WeChatCpClient { self } + pub fn webhook_url(mut self, webhook_url: &str) -> Self { + self.webhook_url = webhook_url.to_string().into(); + self + } + /// get the wechat client - pub fn new>(crop_id: S, crop_secret: S) -> WeChatCpClient { + pub fn new>(crop_id: S, crop_secret: S) -> WechatCpClient { let client = APIClient::::from_session(crop_id.into(), crop_secret.into(), "https://qyapi.weixin.qq.com", SimpleStorage::new()); - WeChatCpClient::::from_client(client) + WechatCpClient::::from_client(client) } /// get the wechat client - pub fn from_session>(crop_id: S, crop_secret: S, session: T) -> WeChatCpClient { + pub fn from_session>(crop_id: S, crop_secret: S, session: T) -> WechatCpClient { let client = APIClient::from_session(crop_id.into(), crop_secret.into(), "https://qyapi.weixin.qq.com", session); Self::from_client(client) } @@ -119,8 +138,6 @@ impl WeChatCpClient { let expires_in = res.expires_in; // 预留200秒的时间 let expires_at = current_timestamp() + expires_in - 200; - let token_key = format!("{}_access_token_cp", self.corp_id); - let expires_key = format!("{}_expires_at_cp", self.corp_id); session.set(&token_key, token.to_owned(), Some(expires_in as usize)); session.set(&expires_key, expires_at, Some(expires_in as usize)); Ok(token.to_string()) @@ -128,6 +145,22 @@ impl WeChatCpClient { Ok(token) } } + + ///
+    /// 获取服务商凭证
+    /// 文档地址:地址
+    /// 请求方式:POST(HTTPS)
+    /// 请求地址: 地址
+    /// 
+ #[inline] + pub async fn get_provider_token(&self, corp_id: &str, provider_secret: &str) -> LabradorResult { + let mut req = LabraRequest::new().url(WechatCpMethod::GetProviderToken.get_method()).json(json!({ + "corpid": corp_id, + "provider_secret": provider_secret, + })).method(Method::Post).req_type(RequestType::Json); + let res = self.client.request(req).await?.json::()?; + Ok(res) + } /// ///
@@ -135,8 +168,8 @@ impl WeChatCpClient {
     /// [详情](http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319&token=&lang=zh_CN)
     /// 
pub fn check_signature(&self, signature: &str, timestamp: i64, nonce: &str, data: &str) -> LabradorResult { - let crp = WeChatCrypto::new(&self.aes_key.to_owned().unwrap_or_default()); - let _ = crp.check_signature(signature, timestamp, nonce, data, "", &self.token.to_owned().unwrap_or_default())?; + let crp = WechatCrypto::new(&self.aes_key.to_owned().unwrap_or_default()); + let _ = crp.check_signature(signature, timestamp, nonce, data, &self.token.to_owned().unwrap_or_default())?; Ok(true) } @@ -150,7 +183,7 @@ impl WeChatCpClient { let timestamp = get_timestamp() / 1000; let noncestr = get_nonce_str(); let jsapi_ticket = self.get_jsapi_ticket(false).await?; - let signature = WeChatCrypto::get_sha1_sign(&vec!["jsapi_ticket=".to_string() + &jsapi_ticket, + let signature = WechatCrypto::get_sha1_sign(&vec!["jsapi_ticket=".to_string() + &jsapi_ticket, "noncestr=".to_string() + &noncestr, "timestamp=".to_string() + ×tamp.to_string(),"url=".to_string() + &url].join("&")); Ok(JsapiSignature{ @@ -172,7 +205,7 @@ impl WeChatCpClient { let timestamp = get_timestamp() / 1000; let noncestr = get_nonce_str(); let jsapi_ticket = self.get_jsapi_ticket(false).await?; - let signature = WeChatCrypto::get_sha1_sign(&vec!["jsapi_ticket=".to_string() + &jsapi_ticket, + let signature = WechatCrypto::get_sha1_sign(&vec!["jsapi_ticket=".to_string() + &jsapi_ticket, "noncestr=".to_string() + &noncestr, "timestamp=".to_string() + ×tamp.to_string(),"url=".to_string() + &url].join("&")); Ok(AgentJsapiSignature{ @@ -281,19 +314,16 @@ impl WeChatCpClient { if !access_token.is_empty() && method.need_token() { querys.push((ACCESS_TOKEN.to_string(), access_token)); } - let mut req = LabraRequest::new().url(method.get_method()).params(querys).method(Method::Post).json(data).req_type(request_type); - self.client.request(req).await + self.client.post(method, querys, data, request_type).await } /// 发送GET请求 - async fn get(&self, method: WechatCpMethod, params: Vec<(&str, &str)>, request_type: RequestType) -> LabradorResult { + async fn get(&self, method: WechatCpMethod, mut params: Vec<(&str, &str)>, request_type: RequestType) -> LabradorResult { let access_token = self.access_token(false).await?; - let mut querys = params.into_iter().map(|(k, v)| (k.to_string(), v.to_string())).collect::>(); if !access_token.is_empty() && method.need_token() { - querys.push((ACCESS_TOKEN.to_string(), access_token)); + params.push((ACCESS_TOKEN, access_token.as_str())); } - let mut req = LabraRequest::::new().url(method.get_method()).params(querys).method(Method::Get).req_type(request_type); - self.client.request(req).await + self.client.get(method, params, request_type).await } /// codesssion相关服务 diff --git a/src/wechat/cp/tp/license.rs b/src/wechat/cp/tp/license.rs new file mode 100644 index 0000000..9e4a54c --- /dev/null +++ b/src/wechat/cp/tp/license.rs @@ -0,0 +1,392 @@ +use serde::{Serialize, Deserialize}; +use serde_json::{json, Value}; + +use crate::{session::SessionStore, request::{RequestType}, WechatCommonResponse, LabradorResult, WechatCpTpClient}; +use crate::wechat::cp::constants::{PROVIDER_ACCESS_TOKEN}; +use crate::wechat::cp::method::{CpLicenseMethod, WechatCpMethod}; + +/// 服务商接口调用许可相关 +#[derive(Debug, Clone)] +pub struct WechatTpLicense<'a, T: SessionStore> { + client: &'a WechatCpTpClient, +} + +#[allow(unused)] +impl<'a, T: SessionStore> WechatTpLicense<'a, T> { + + #[inline] + pub fn new(client: &WechatCpTpClient) -> WechatTpLicense { + WechatTpLicense { + client, + } + } + + ///
+    /// 下单购买帐号
+    /// 服务商下单为企业购买新的帐号,可以同时购买基础帐号与互通帐号。
+    /// 下单之后,需要到服务商管理端发起支付,支付完成之后,订单才能生效。
+    /// 文档地址:文档
+    /// 
+ pub async fn create_order(&self, req: WechatCpTpLicenseNewOrderRequest) -> LabradorResult { + let v = self.client.post(WechatCpMethod::License(CpLicenseMethod::CreateOrder), vec![], req, RequestType::Json).await?.json::()?; + let v = WechatCommonResponse::parse::(v)?; + let order_id = v["order_id"].as_str().unwrap_or_default(); + Ok(order_id.to_string()) + } + + + /// 创建下单续期帐号任务 + ///
+    ///  可以下单为一批已激活帐号的成员续期,续期下单分为两个步骤:
+    /// 传入userid列表创建一个任务,创建之后,可以往同一个任务继续追加待续期的userid列表;
+    /// 根据步骤1得到的jobid提交订单。
+    /// 
+ pub async fn create_renew_order_job(&self, req: WechatCpTpLicenseRenewOrderJobRequest) -> LabradorResult { + let v = self.client.post(WechatCpMethod::License(CpLicenseMethod::CreateRenewOrderJob), vec![], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + + + ///
+    ///  提交续期订单
+    /// 创建续期任务之后,需要调用该接口,以提交订单任务。
+    /// 注意,提交之后,需要到服务商管理端发起支付,支付完成之后,订单才能生效。
+    /// 文档地址:文档
+    /// 
+ pub async fn submit_renew_order(&self, req: WechatCpTpLicenseRenewOrderRequest) -> LabradorResult { + let v = self.client.post(WechatCpMethod::License(CpLicenseMethod::SubmitOrderJob), vec![], req, RequestType::Json).await?.json::()?; + let v = WechatCommonResponse::parse::(v)?; + let order_id = v["order_id"].as_str().unwrap_or_default(); + Ok(order_id.to_string()) + } + + + ///
+    ///  获取订单列表
+    /// 服务商查询自己某段时间内的平台能力服务订单列表
+    /// 文档地址:文档
+    /// 
+ pub async fn get_order_list(&self, corp_id: &str, start: Option, end: Option, cursor: &str, limit: i32) -> LabradorResult { + let mut req = json!({ + "corpid": corp_id, + "cursor": cursor, + "limit": limit, + }); + if let Some(start) = start { + req["start_time"] = start.into(); + } + if let Some(end) = end { + req["end_time"] = end.into(); + } + let access_token = self.client.get_wechat_provider_token().await?; + let v = self.client.post(WechatCpMethod::License(CpLicenseMethod::ListOrder), vec![(PROVIDER_ACCESS_TOKEN.to_string(), access_token)], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + + + ///
+    ///  查询指定订单下的平台能力服务帐号列表。
+    /// 若为购买帐号的订单或者存量企业的版本付费迁移订单,则返回帐号激活码列表;
+    /// 若为续期帐号的订单,则返回续期帐号的成员列表。注意,若是购买帐号的订单,
+    /// 则仅订单支付完成时,系统才会生成帐号,故支付完成之前,该接口不会返回帐号激活码。
+    /// 文档地址:文档
+    /// 
+ pub async fn get_order_account_list(&self, order_id: &str, limit: i32, cursor: &str) -> LabradorResult { + let mut req = json!({ + "order_id": order_id, + "cursor": cursor, + "limit": limit, + }); + let access_token = self.client.get_wechat_provider_token().await?; + let v = self.client.post(WechatCpMethod::License(CpLicenseMethod::ListOrderCount), vec![(PROVIDER_ACCESS_TOKEN.to_string(), access_token)], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + + + ///
+    ///  激活帐号
+    /// 下单购买帐号并支付完成之后,先调用获取订单中的帐号列表接口获取到帐号激活码,
+    /// 然后可以调用该接口将激活码绑定到某个企业员工,以对其激活相应的平台服务能力。
+    /// 文档地址:文档
+    /// 
+ pub async fn active_code(&self, code: &str, corp_id: &str, user_id: &str) -> LabradorResult { + let mut req = json!({ + "active_code": code, + "corpid": corp_id, + "userid": user_id, + }); + let access_token = self.client.get_wechat_provider_token().await?; + self.client.post(WechatCpMethod::License(CpLicenseMethod::ActiveAccount), vec![(PROVIDER_ACCESS_TOKEN.to_string(), access_token)], req, RequestType::Json).await?.json::() + } + + ///
+    /// 批量激活帐号
+    /// 可在一次请求里为一个企业的多个成员激活许可帐号,便于服务商批量化处理。
+    /// 一个userid允许激活一个基础帐号以及一个互通帐号。
+    /// 单次激活的员工数量不超过1000
+    /// 
+ pub async fn batch_active_code(&self, corp_id: &str, active_accounts: Vec) -> LabradorResult { + let mut req = json!({ + "corp_id": corp_id, + "active_list": active_accounts, + }); + let access_token = self.client.get_wechat_provider_token().await?; + let v = self.client.post(WechatCpMethod::License(CpLicenseMethod::BatchActiveAccount), vec![(PROVIDER_ACCESS_TOKEN.to_string(), access_token)], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + + ///
+    /// 获取激活码详情
+    /// 查询某个帐号激活码的状态以及激活绑定情况。
+    /// 文档地址:文档
+    /// 
+ pub async fn get_active_info_by_code(&self, code: &str, corp_id: &str) -> LabradorResult { + let mut req = json!({ + "active_code": code, + "corpid": corp_id, + }); + let access_token = self.client.get_wechat_provider_token().await?; + let v = self.client.post(WechatCpMethod::License(CpLicenseMethod::GetActiveInfoByCode), vec![(PROVIDER_ACCESS_TOKEN.to_string(), access_token)], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + + + ///
+    /// 获取激活码详情
+    /// 查询某个帐号激活码的状态以及激活绑定情况。
+    /// 文档地址:文档
+    /// 
+ pub async fn batch_get_active_info_by_code(&self, codes: Vec<&str>, corp_id: &str) -> LabradorResult { + let mut req = json!({ + "active_code_list": codes, + "corpid": corp_id, + }); + let access_token = self.client.get_wechat_provider_token().await?; + let v = self.client.post(WechatCpMethod::License(CpLicenseMethod::BatchGetActiveInfoByCode), vec![(PROVIDER_ACCESS_TOKEN.to_string(), access_token)], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + + + ///
+    /// 获取企业的帐号列表
+    /// 查询指定企业下的平台能力服务帐号列表。
+    /// 文档地址:文档
+    /// 
+ pub async fn get_corp_account_list(&self, corp_id: &str, limit: i32, cursor: &str) -> LabradorResult { + let mut req = json!({ + "cursor": cursor, + "corpid": corp_id, + "limit": limit, + }); + let access_token = self.client.get_wechat_provider_token().await?; + let v = self.client.post(WechatCpMethod::License(CpLicenseMethod::ListActivedAccount), vec![(PROVIDER_ACCESS_TOKEN.to_string(), access_token)], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + + + ///
+    /// 获取成员的激活详情
+    /// 查询某个企业成员的激活情况。
+    /// 文档地址:文档
+    /// 
+ pub async fn get_active_info_by_user(&self, corp_id: &str, user_id: &str) -> LabradorResult { + let mut req = json!({ + "corpid": corp_id, + "user_id": user_id, + }); + let access_token = self.client.get_wechat_provider_token().await?; + let v = self.client.post(WechatCpMethod::License(CpLicenseMethod::GetActiveInfoByUser), vec![(PROVIDER_ACCESS_TOKEN.to_string(), access_token)], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + + + ///
+    /// 帐号继承
+    /// 在企业员工离职或者工作范围的有变更时,允许将其许可帐号继承给其他员工。
+    /// 文档地址:文档
+    /// 
+ pub async fn batch_transfer_license(&self, corp_id: &str, transfers: Vec) -> LabradorResult { + let mut req = json!({ + "corpid": corp_id, + "transfer_list": transfers, + }); + let access_token = self.client.get_wechat_provider_token().await?; + let v = self.client.post(WechatCpMethod::License(CpLicenseMethod::BatchTransferLicense), vec![(PROVIDER_ACCESS_TOKEN.to_string(), access_token)], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + +} + +//---------------------------------------------------------------------------------------------------------------------------- +/// 下单购买帐号 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpTpLicenseNewOrderRequest { + /// 企业ID + pub corpid: String, + /// 购买者ID + pub buyer_userid: String, + /// 账号个数 + pub account_count: WechatCpTpLicenseAccountCount, + /// 购买时长 + pub account_duration: WechatCpTpLicenseAccountDuration, +} + + +/// 创建下单续期帐号任务 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpTpLicenseRenewOrderJobRequest { + /// 企业ID + pub corpid: String, + /// 续费的用户UserId + pub account_list: Vec, + /// 任务id,若不传则默认创建一个新任务。若指定第一次调用后拿到jobid,可以通过该接口将jobid关联多个userid + pub jobid: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpTpLicenseAccountCount { + pub base_count: i32, + pub external_contact_count: i32, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpTpLicenseAccountDuration { + pub months: i32, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpTpLicenseBaseAccount { + pub userid: String, + #[serde(rename="type")] + pub r#type: i32, +} + +/// 创建下单购买帐号任务返回结果 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpTpLicenseRenewOrderJobResponse { + /// 任务ID + pub jobid: String, + /// 有效的续费账号列表 + pub invalid_account_list: Option>, +} + +/// 续期帐号订单 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpTpLicenseRenewOrderRequest { + pub buyer_userid: String, + pub jobid: String, + pub account_duration: WechatCpTpLicenseAccountDuration, +} + +/// 获取订单列表详情 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpTpLicenseOrderListResp { + pub next_cursor: Option, + pub has_more: Option, + pub order_list: Option>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpTpLicenseSimpleOrder { + pub order_id: Option, + pub order_type: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpTpLicenseOrderInfoResponse { + pub order: Option, +} + +/// 详细的订单信息 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpTpLicenseOrder { + pub order_id: Option, + pub corpid: Option, + pub order_type: Option, + pub order_status: Option, + pub price: Option, + pub create_time: Option, + pub pay_time: Option, + pub account_count: Option, + pub account_duration: Option, +} + +/// 获取订单中的帐号列表 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpTpLicenseOrderAccountListResponse { + pub next_cursor: Option, + pub has_more: Option, + pub account_list: Option, +} + +/// 订单账号信息 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpTpLicenseAccount { + /// 激活码 + pub active_code: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpTpLicenseActiveAccount { + /// 用户ID + pub userid: String, + /// 激活码 + pub active_code: String, +} + +/// 查询的激活码详情 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpTpLicenseCodeInfoResponse { + pub active_info: Option, +} + +/// 批量查询的激活码详情 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpTpLicenseBatchCodeInfoResponse { + pub active_info_list: Option>, + pub invalid_active_code_list: Option>, +} +/// 激活码信息 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpTpLicenseActiveCodeInfo { + pub active_code: Option, + pub status: Option, + pub create_time: Option, + pub active_time: Option, + pub expire_time: Option, +} +/// 企业的帐号列表(已激活) +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpTpLicenseCorpAccountListResponse { + pub next_cursor: Option, + pub has_more: Option, + pub account_list: Option>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpTpLicenseCorpAccount { + pub active_time: Option, + pub expire_time: Option, +} + +/// 某个企业成员的激活情况 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpTpLicenseActiveInfoByUserResponse { + pub active_status: Option, + pub active_info_list: Option>, +} + +/// 基础结果返回信息 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpTpLicenseBatchTransferResponse { + pub transfer_result: Option>, +} + +/// 基础的信息 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatCpTpLicenseTransfer { + /// 转移成员加密的userid + pub handover_userid: Option, + /// 接收成员加密的userid + pub takeover_userid: Option, +} \ No newline at end of file diff --git a/src/wechat/cp/tp/mod.rs b/src/wechat/cp/tp/mod.rs new file mode 100644 index 0000000..5c25786 --- /dev/null +++ b/src/wechat/cp/tp/mod.rs @@ -0,0 +1,590 @@ + +use serde::{Serialize, Deserialize}; +use serde_json::{json, Value}; + +use crate::{session::SessionStore, request::{RequestType}, WechatCommonResponse, LabradorResult, WechatCrypto, current_timestamp, LabraError, JsapiTicket, JsapiSignature, get_timestamp, get_nonce_str, APIClient, WechatRequest, LabraResponse, LabraRequest, SimpleStorage, WechatCpProviderToken}; +use crate::wechat::cp::constants::{ACCESS_TOKEN, ACCESS_TOKEN_KEY, AUTH_URL_INSTALL, TYPE}; +use crate::wechat::cp::method::WechatCpMethod; +use crate::wechat::cp::AccessTokenResponse; + +mod tag; +mod license; + + +/// 企业微信第三方应用API +#[allow(unused)] +#[derive(Debug, Clone)] +pub struct WechatCpTpClient { + token: Option, + /// 企微服务商企业ID,来自于企微配置 + corp_id: String, + /// 第三方应用的EncodingAESKey,用来检查签名 + aes_key: Option, + ///企业secret,来自于企微配置 + corp_secret: String, + /// 服务商secret + provider_secret: Option, + agent_id: Option, + /// 第三方应用的其他配置 + suite_id: Option, + suite_secret: Option, + client: APIClient, +} + +#[allow(unused)] +impl WechatCpTpClient { + + fn from_client(client: APIClient) -> WechatCpTpClient { + WechatCpTpClient { + corp_id: client.app_key.to_owned(), + corp_secret: client.secret.to_owned(), + token: None, + aes_key: None, + agent_id: None, + suite_id: None, + suite_secret: None, + client, + provider_secret: None + } + } + + pub fn aes_key(mut self, aes_key: &str) -> Self { + self.aes_key = aes_key.to_string().into(); + self + } + + pub fn token(mut self, token: &str) -> Self { + self.token = token.to_string().into(); + self + } + + pub fn agent_id(mut self, agent_id: i32) -> Self { + self.agent_id = agent_id.into(); + self + } + + pub fn provider_secret(mut self, provider_secret: &str) -> Self { + self.provider_secret = provider_secret.to_string().into(); + self + } + + pub fn suite_id(mut self, suite_id: &str) -> Self { + self.suite_id = suite_id.to_string().into(); + self + } + + pub fn suite_secret(mut self, suite_secret: &str) -> Self { + self.suite_secret = suite_secret.to_string().into(); + self + } + + fn key_with_prefix(&self, key: &str) -> String { + format!("cp:{}:{}", self.suite_id.to_owned().unwrap_or_default(), key) + } + + /// get the wechat client + pub fn new>(crop_id: S, crop_secret: S) -> WechatCpTpClient { + let client = APIClient::::from_session(crop_id.into(), crop_secret.into(), "https://qyapi.weixin.qq.com", SimpleStorage::new()); + WechatCpTpClient::::from_client(client) + } + + /// get the wechat client + pub fn from_session>(crop_id: S, crop_secret: S, session: T) -> WechatCpTpClient { + let client = APIClient::from_session(crop_id.into(), crop_secret.into(), "https://qyapi.weixin.qq.com", session); + Self::from_client(client) + } + + /// 授权企业的access token相关 + fn get_access_token(&self, auth_corp_id: &str) -> String { + let session = self.client.session(); + session.get::<_,String>(self.key_with_prefix(auth_corp_id) + ACCESS_TOKEN_KEY, None).unwrap_or(None).unwrap_or_default() + } + + ///
+    /// 验证推送过来的消息的正确性
+    /// 详情请见: 文档
+    /// 
+ pub fn check_signature(&self, signature: &str, timestamp: i64, nonce: &str, data: &str) -> LabradorResult { + let crp = WechatCrypto::new(&self.aes_key.to_owned().unwrap_or_default()); + let _ = crp.check_signature(signature, timestamp, nonce, data,&self.token.to_owned().unwrap_or_default())?; + Ok(true) + } + + /// 获得suite_ticket,不强制刷新suite_ticket + /// 由微信服务器推送 + pub fn get_suite_ticket(&self) -> LabradorResult { + let session = self.client.session(); + let token_key = format!("{}_suite_ticket_key_cp", self.corp_id); + let expires_key = format!("{}_suite_ticket_expires_at_cp", self.corp_id); + let token: String = session.get(&token_key, Some("".to_owned()))?.unwrap_or_default(); + let timestamp = current_timestamp(); + let expires_at: i64 = session.get(&expires_key, Some(timestamp))?.unwrap_or_default(); + if expires_at <= timestamp { + return Err(LabraError::ApiError("invaild suite ticket".to_string())) + } + Ok(token) + } + + /// 获得suite_ticket,不强制刷新suite_ticket + /// 由微信服务器推送 + pub fn set_suite_ticket_expire(&self, suite_ticket: &str, expire_second: i64) -> LabradorResult<()> { + let expires_at = current_timestamp() + expire_second; + let session = self.client.session(); + let token_key = format!("{}_suite_ticket_key_cp", self.corp_id); + let expires_key = format!("{}_suite_ticket_expires_at_cp", self.corp_id); + session.set(token_key, suite_ticket, Some(expire_second as usize))?; + session.set(expires_key, expires_at, Some(expire_second as usize))?; + Ok(()) + } + + ///
+    /// 保存企业微信定时推送的suite_ticket,(每10分钟)
+    /// 详情请见:文档
+    ///
+    /// 注意:微信不是固定10分钟推送suite_ticket的, 且suite_ticket的有效期为30分钟
+    /// 文档
+    /// 
+ pub fn set_suite_ticket(&self, suite_ticket: &str) -> LabradorResult<()> { + self.set_suite_ticket_expire(suite_ticket, 28 * 60) + } + + ///
+    /// 获取suite_access_token,本方法线程安全
+    /// 且在多线程同时刷新时只刷新一次,避免超出2000次/日的调用次数上限
+    /// 另:本service的所有方法都会在suite_access_token过期是调用此方法
+    /// 程序员在非必要情况下尽量不要主动调用此方法
+    /// 详情请见: 文档
+    /// 
+ pub async fn get_suite_access_token(&self) -> LabradorResult { + self.get_suite_access_token_force(false).await + } + + ///
+    /// 获取suite_access_token,本方法线程安全
+    /// 且在多线程同时刷新时只刷新一次,避免超出2000次/日的调用次数上限
+    /// 另:本service的所有方法都会在suite_access_token过期是调用此方法
+    /// 详情请见: 文档
+    /// 
+ pub async fn get_suite_access_token_force(&self, force_refresh: bool) -> LabradorResult { + let session = self.client.session(); + let token_key = format!("{}_suite_access_token_cp", self.corp_id); + let expires_key = format!("{}_suite_access_token_expires_at_cp", self.corp_id); + let token: String = session.get(&token_key, Some("".to_owned()))?.unwrap_or_default(); + let timestamp = current_timestamp(); + let expires_at: i64 = session.get(&expires_key, Some(timestamp))?.unwrap_or_default(); + if expires_at <= timestamp || force_refresh { + let suite_ticket = self.get_suite_ticket()?; + let req = json!({ + "suite_id": self.suite_id, + "suite_secret": self.suite_secret, + "suite_ticket": suite_ticket + }); + let result = self.client.post(WechatCpMethod::GetSuiteToken, vec![], req, RequestType::Json).await?.json::()?; + let token = result.suite_access_token; + let expires_in = result.expires_in; + // 预留200秒的时间 + let expires_at = current_timestamp() + expires_in - 200; + session.set(&token_key, token.to_owned(), Some(expires_in as usize)); + session.set(&expires_key, expires_at, Some(expires_in as usize)); + Ok(token.to_string()) + } else { + Ok(token) + } + } + + /// + ///
+    /// 获取应用的 jsapi ticket
+    /// 
+ pub async fn get_suite_jsapi_ticket(&self, auth_corp_id: &str) -> LabradorResult { + self.get_suite_jsapi_ticket_force(auth_corp_id, false).await + } + + /// + ///
+    /// 获取应用的 jsapi ticket, 支持强制刷新
+    /// 
+ pub async fn get_suite_jsapi_ticket_force(&self, auth_corp_id: &str, force_refresh: bool) -> LabradorResult { + let mut session = self.client.session(); + let ticket_key = format!("{}_suite_jsapi_ticket_cp", self.corp_id); + let expires_key = format!("{}_suite_jsapi_ticket_expires_at_cp", self.corp_id); + let ticket: String = session.get(&ticket_key, Some("".to_string()))?.unwrap_or_default(); + let timestamp = current_timestamp(); + let expires_at: i64 = session.get(&expires_key, Some(timestamp))?.unwrap_or_default(); + if expires_at <= timestamp || force_refresh { + let res = self.client.get(WechatCpMethod::GetSuiteJsapiTicket, vec![(TYPE, "agent_config"), (ACCESS_TOKEN, self.get_access_token(auth_corp_id).as_str())], RequestType::Json).await?.json::()?; + let ticket = res.ticket; + let expires_in = res.expires_in; + // 预留200秒的时间 + let expires_at = current_timestamp() + expires_in - 200; + session.set(&ticket_key, ticket.to_string(), Some(expires_in as usize)); + session.set(&expires_key, expires_at, Some(expires_in as usize)); + Ok(ticket.to_string()) + } else { + Ok(ticket) + } + } + + /// + ///
+    /// 获取授权企业的 jsapi ticket
+    /// 
+ pub async fn get_auth_corp_jsapi_ticket(&self, auth_corp_id: &str) -> LabradorResult { + self.get_auth_corp_jsapi_ticket_force(auth_corp_id, false).await + } + + /// + ///
+    /// 获取授权企业的 jsapi ticket, 支持强制刷新
+    /// 
+ pub async fn get_auth_corp_jsapi_ticket_force(&self, auth_corp_id: &str, force_refresh: bool) -> LabradorResult { + let mut session = self.client.session(); + let ticket_key = format!("{}_auth_corp_jsapi_ticket_cp", self.corp_id); + let expires_key = format!("{}_auth_corp_jsapi_ticket_expires_at_cp", self.corp_id); + let ticket: String = session.get(&ticket_key, Some("".to_string()))?.unwrap_or_default(); + let timestamp = current_timestamp(); + let expires_at: i64 = session.get(&expires_key, Some(timestamp))?.unwrap_or_default(); + if expires_at <= timestamp || force_refresh { + let res = self.client.get(WechatCpMethod::GetJsapiTicket, vec![(ACCESS_TOKEN, self.get_access_token(auth_corp_id).as_str())], RequestType::Json).await?.json::()?; + let ticket = res.ticket; + let expires_in = res.expires_in; + // 预留200秒的时间 + let expires_at = current_timestamp() + expires_in - 200; + session.set(&ticket_key, ticket.to_string(), Some(expires_in as usize)); + session.set(&expires_key, expires_at, Some(expires_in as usize)); + Ok(ticket.to_string()) + } else { + Ok(ticket) + } + } + + + + ///
+    /// 获取企业凭证
+    /// 
+ pub async fn get_corp_token(&self, auth_corpid: &str, permanent_code: &str) -> LabradorResult { + self.get_corp_token_force(auth_corpid, permanent_code,false).await + } + + ///
+    /// 获取企业凭证, 支持强制刷新
+    /// 
+ pub async fn get_corp_token_force(&self, auth_corpid: &str, permanent_code: &str, force_refresh: bool) -> LabradorResult { + let session = self.client.session(); + let token_key = format!("{}_corp_access_token_cp", auth_corpid); + let expires_key = format!("{}_corp_access_token_expires_at_cp", auth_corpid); + let token: String = session.get(&token_key, Some("".to_owned()))?.unwrap_or_default(); + let timestamp = current_timestamp(); + let expires_at: i64 = session.get(&expires_key, Some(timestamp))?.unwrap_or_default(); + if expires_at <= timestamp || force_refresh { + let suite_ticket = self.get_suite_ticket()?; + let req = json!({ + "auth_corpid": auth_corpid, + "permanent_code": permanent_code, + }); + let result = self.client.post(WechatCpMethod::GetCorpToken, vec![], req, RequestType::Json).await?.json::()?; + let token = result.access_token.to_string(); + let expires_in = result.expires_in; + // 预留200秒的时间 + let expires_at = current_timestamp() + expires_in - 200; + session.set(&token_key, token.to_owned(), Some(expires_in as usize)); + session.set(&expires_key, expires_at, Some(expires_in as usize)); + Ok(result) + } else { + Ok(AccessTokenResponse{ access_token: token.to_string(), expires_in: expires_at }) + } + } + + ///
+    /// 获取服务商providerToken
+    /// 
+ pub async fn get_wechat_provider_token(&self) -> LabradorResult { + let session = self.client.session(); + let token_key = format!("{}_provider_access_token_cp", self.corp_id); + let expires_key = format!("{}_provider_access_token_expires_at_cp", self.corp_id); + let token: String = session.get(&token_key, Some("".to_owned()))?.unwrap_or_default(); + let timestamp = current_timestamp(); + let expires_at: i64 = session.get(&expires_key, Some(timestamp))?.unwrap_or_default(); + if expires_at <= timestamp { + let suite_ticket = self.get_suite_ticket()?; + let req = json!({ + "corpid": self.corp_id, + "provider_secret": self.provider_secret, + }); + let result = self.client.post(WechatCpMethod::GetProviderToken, vec![], req, RequestType::Json).await?.json::()?; + let token = result.provider_access_token.to_string(); + let expires_in = result.expires_in; + // 预留200秒的时间 + let expires_at = current_timestamp() + expires_in - 200; + session.set(&token_key, token.to_owned(), Some(expires_in as usize)); + session.set(&expires_key, expires_at, Some(expires_in as usize)); + Ok(token) + } else { + Ok(token) + } + } + + ///
+    /// 获取企业永久授权码信息
+    /// 
+ pub async fn get_permanent_code_info(&self, auth_code: &str) -> LabradorResult { + let req = json!({ + "auth_code": auth_code, + }); + let result = self.client.post(WechatCpMethod::GetPermanentCode, vec![], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(result) + } + + ///
+    /// 获取预授权链接
+    /// 
+ pub async fn get_pre_auth_url(&self, redirect_uri: &str, state: Option<&str>) -> LabradorResult { + let result = self.client.get(WechatCpMethod::GetPreAuthCode, vec![], RequestType::Json).await?.json::()?; + let mut pre_auth_url = format!("{}?suite_id={}&pre_auth_code={}&redirect_uri={}", AUTH_URL_INSTALL, self.suite_id.to_owned().unwrap_or_default(), result.pre_auth_code, urlencoding::encode(redirect_uri)); + if let Some(state) = state { + pre_auth_url.push_str(&format!("&state={}", state)); + } + Ok(pre_auth_url) + } + + ///
+    /// 获取企业的授权信息
+    /// 
+ pub async fn get_auth_info(&self, auth_corp_id: &str, permanent_code: &str) -> LabradorResult { + let req = json!({ + "auth_corpid": auth_corp_id, + "permanent_code": permanent_code + }); + let result = self.client.post(WechatCpMethod::GetAuthInfo, vec![], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(result) + } + + /// + ///
+    /// 创建机构级jsApiTicket签名
+    /// 详情参见企业微信第三方应用开发文档[请见](https://work.weixin.qq.com/api/doc/90001/90144/90539)
+    /// 
+ pub async fn create_auth_corp_jsapi_signature(&self, url: &str, auth_corp_id: &str) -> LabradorResult { + Ok(self.created_wechat_jsapi_signature(url, auth_corp_id, &self.get_auth_corp_jsapi_ticket(auth_corp_id).await?)) + } + + /// + ///
+    /// 创建应用级jsapiTicket签名
+    /// 详情参见企业微信第三方应用开发文档[请见](https://work.weixin.qq.com/api/doc/90001/90144/90539)
+    /// 
+ pub async fn create_suite_jsapi_signature(&self, url: &str, auth_corp_id: &str) -> LabradorResult { + Ok(self.created_wechat_jsapi_signature(url, auth_corp_id, &self.get_suite_jsapi_ticket(auth_corp_id).await?)) + } + + fn created_wechat_jsapi_signature(&self, url: &str, auth_corp_id: &str, jsapi_ticket: &str) -> JsapiSignature { + let timestamp = get_timestamp() / 1000; + let noncestr = get_nonce_str(); + let signature = WechatCrypto::get_sha1_sign(&vec!["jsapi_ticket=".to_string() + &jsapi_ticket, + "noncestr=".to_string() + &noncestr, + "timestamp=".to_string() + ×tamp.to_string(),"url=".to_string() + &url].join("&")); + JsapiSignature{ + app_id: auth_corp_id.to_string(), + nonce_str: noncestr, + url: url.to_string(), + signature, + timestamp, + } + } + + ///
+    /// Service没有实现某个API的时候,可以用这个,
+    /// 比 get 和 post 方法更灵活,可以自己构造用来处理不同的参数和不同的返回类型。
+    /// 
+ async fn execute(&self, request: D) -> LabradorResult { + let mut querys = request.get_query_params(); + let params = querys.iter().map(|(k, v)| (k.to_string(), v.to_string())).collect::>(); + let mut req = LabraRequest::::new().url(request.get_api_method_name()) + .params(params).method(request.get_request_method()).req_type(request.get_request_type()).body(request.get_request_body::()); + self.client.request(req).await + } + + /// 发送POST请求 + async fn post(&self, method: WechatCpMethod, mut querys: Vec<(String, String)>, data: D, request_type: RequestType) -> LabradorResult { + self.client.post(method, querys, data, request_type).await + } + + /// 发送GET请求 + async fn get(&self, method: WechatCpMethod, mut params: Vec<(&str, &str)>, request_type: RequestType) -> LabradorResult { + self.client.get(method, params, request_type).await + } +} + +//---------------------------------------------------------------------------------------------------------------------------- + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct WechatCpSuiteAccessTokenResponse { + pub suite_access_token: String, + pub expires_in: i64, +} + +/// 服务商模式获取永久授权码信息 +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct WechatCpThirdPermanentCodeInfo { + pub access_token: String, + pub permanent_code: String, + /// 授权企业信息 + pub auth_corp_info: AuthCorpInfo, + /// 授权信息。如果是通讯录应用,且没开启实体应用,是没有该项的。通讯录应用拥有企业通讯录的全部信息读写权限 + pub auth_info: Option, + /// 授权用户信息 + pub auth_user_info: Option, + /// 企业当前生效的版本信息 + pub edition_info: Option, + pub expires_in: i64, +} + + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AuthCorpInfo { + pub corpid: String, + pub corp_name: String, + pub corp_type: Option, + pub corp_square_logo_url: Option, + pub corp_round_logo_url: Option, + pub corp_user_max: Option, + pub corp_agent_max: Option, + /// 所绑定的企业微信主体名称(仅认证过的企业有) + pub corp_full_name: Option, + /// 授权企业在微工作台(原企业号)的二维码,可用于关注微工作台 + pub corp_wxqrcode: Option, + pub corp_scale: Option, + pub corp_industry: Option, + pub corp_sub_industry: Option, + pub location: Option, + /// 认证到期时间 + pub verified_end_time: Option, + /// 企业类型,1. 企业; 2. 政府以及事业单位; 3. 其他组织, 4.团队号 + pub subject_type: Option, +} + + +/// 企业当前生效的版本信息 +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct EditionInfo { + pub agent: Option>, +} + + +/// 授权人员信息 +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AuthUserInfo { + pub userid: Option, + pub name: Option, + pub avatar: Option, + /// 授权管理员的open_userid,可能为空 + pub open_userid: Option, +} + + +/// 授权信息 +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AuthInfo { + /// 授权的应用信息,注意是一个数组,但仅旧的多应用套件授权时会返回多个agent,对新的单应用授权,永远只返回一个agent + pub agent: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct Agent { + pub agentid: i32, + pub name: String, + pub round_logo_url: Option, + pub square_logo_url: Option, + /// 版本id + pub edition_id: Option, + /// 版本名称 + pub edition_name: Option, + /// 付费状态 + ///
+ ///
    + ///
  • 0-没有付费;
  • + ///
  • 1-限时试用;
  • + ///
  • 2-试用过期;
  • + ///
  • 3-购买期内;
  • + ///
  • 4-购买过期;
  • + ///
  • 5-不限时试用;
  • + ///
  • 6-购买期内,但是人数超标, 注意,超标后还可以用7天;
  • + ///
  • 7-购买期内,但是人数超标, 且已经超标试用7天
  • + ///
+ pub app_status: Option, + /// 授权模式,0为管理员授权;1为成员授权 + pub auth_mode: Option, + /// 是否为代开发自建应用 + pub is_customized_app: Option, + /// 是否虚拟版本 + pub is_virtual_version: Option, + /// 是否由互联企业分享安装。详见 企业互联 + pub is_shared_from_other_corp: Option, + /// 用户上限。 + ///

特别注意, 以下情况该字段无意义,可以忽略:

+ ///
    + ///
  • 1. 固定总价购买
  • + ///
  • 2. app_status = 限时试用/试用过期/不限时试用
  • + ///
  • 3. 在第2条“app_status=不限时试用”的情况下,如果该应用的配置为“小企业无使用限制”,user_limit有效,且为限制的人数
  • + ///
+ pub user_limit: Option, + /// 版本到期时间, 秒级时间戳, 根据需要自行乘以1000(根据购买版本,可能是试用到期时间或付费使用到期时间)。 + ///

特别注意,以下情况该字段无意义,可以忽略:

+ ///
    + ///
  • 1. app_status = 不限时试用
  • + ///
+ pub expired_time: Option, + /// 应用权限 + pub privilege: Option, +} + +/// 应用对应的权限 +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct Privilege { + /// 权限等级。 + /// 1:通讯录基本信息只读 + /// 2:通讯录全部信息只读 + /// 3:通讯录全部信息读写 + /// 4:单个基本信息只读 + /// 5:通讯录全部信息只写 + pub level: Option, + pub allow_party: Option>, + pub allow_user: Option>, + pub extra_party: Option>, + pub extra_tag: Option>, + pub extra_user: Option>, +} + + +/// 预授权码返回 +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct WechatCpThirdPreauthCode { + pub pre_auth_code: String, + pub expires_in: i64, +} + + + +/// 服务商模式获取授权信息 +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct WechatCpThirdAuthInfo { + /// 服务商信息 + pub dealer_corp_info: Option, + /// 授权企业信息 + pub auth_corp_info: Option, + /// 授权信息。如果是通讯录应用,且没开启实体应用,是没有该项的。通讯录应用拥有企业通讯录的全部信息读写权限 + pub auth_info: Option, + /// 企业当前生效的版本信息 + pub edition_info: Option, +} + + + + +/// 服务商模式获取授权信息 +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct DealerCorpInfo { + pub corpid: Option, + pub corp_name: Option, +} diff --git a/src/wechat/cp/tp/tag.rs b/src/wechat/cp/tp/tag.rs new file mode 100644 index 0000000..a1f435e --- /dev/null +++ b/src/wechat/cp/tp/tag.rs @@ -0,0 +1,193 @@ +use serde::{Serialize, Deserialize}; +use serde_json::{json, Value}; + +use crate::{session::SessionStore, request::{RequestType}, WechatCommonResponse, LabradorResult, WechatCpTpClient}; +use crate::wechat::cp::method::{CpTagMethod, WechatCpMethod}; + +/// 企业微信第三方开发-标签相关 +#[derive(Debug, Clone)] +pub struct WechatTpTag<'a, T: SessionStore> { + client: &'a WechatCpTpClient, +} + +#[allow(unused)] +impl<'a, T: SessionStore> WechatTpTag<'a, T> { + + #[inline] + pub fn new(client: &WechatCpTpClient) -> WechatTpTag { + WechatTpTag { + client, + } + } + + /// 创建标签. + ///
+    /// 请求地址:文档
+    /// 文档地址:文档
+    /// 
+ pub async fn create(&self, name: &str, id: Option) -> LabradorResult { + let req = json!({ + "tagname": name, + "tagid": id, + }); + let v = self.client.post(WechatCpMethod::Tag(CpTagMethod::Create), vec![], req, RequestType::Json).await?.json::()?; + let v = WechatCommonResponse::parse::(v)?; + let tag_id = v["tagid"].as_str().unwrap_or_default(); + Ok(tag_id.to_string()) + } + + /// 更新标签. + pub async fn update(&self, tag_id: &str, tag_name: &str) -> LabradorResult { + let req = json!({ + "tagname": tag_name, + "tagid": tag_id, + }); + self.client.post(WechatCpMethod::Tag(CpTagMethod::Update), vec![], req, RequestType::Json).await?.json::() + } + + /// 删除标签. + pub async fn delete(&self, tag_id: &str) -> LabradorResult { + self.client.get(WechatCpMethod::Tag(CpTagMethod::Delete(tag_id.to_string())), vec![], RequestType::Json).await?.json::() + } + + /// 获取标签成员. + pub async fn get(&self, tag_id: &str) -> LabradorResult { + let v = self.client.get(WechatCpMethod::Tag(CpTagMethod::Get(tag_id.to_string())), vec![], RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + + /// 增加标签成员. + pub async fn add_users_tag(&self, tag_id: &str, user_ids: Vec, party_ids: Vec) -> LabradorResult { + let req = json!({ + "tagid": tag_id, + "userlist": user_ids, + "partylist": party_ids + }); + let v = self.client.post(WechatCpMethod::Tag(CpTagMethod::AddTagUsers), vec![], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + + /// 移除标签成员. + pub async fn remove_users_tag(&self, tag_id: &str, user_ids: Vec, party_ids: Vec) -> LabradorResult { + let req = json!({ + "tagid": tag_id, + "userlist": user_ids, + "partylist": party_ids + }); + let v = self.client.post(WechatCpMethod::Tag(CpTagMethod::DeleteTagUsers), vec![], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + + /// 获得标签列表. + pub async fn list_all(&self) -> LabradorResult> { + let v = self.client.get(WechatCpMethod::Tag(CpTagMethod::List), vec![], RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::>(v) + } +} + +//---------------------------------------------------------------------------------------------------------------------------- + +#[derive(Debug, Clone,Serialize, Deserialize)] +pub struct WechatCpTagGetResponse { + /// 用户列表 + pub userid: Vec, + /// 部门列表 + pub partylist: Vec, + pub tagname: Option, +} + +/// 为标签添加或移除用户结果对象类 +#[derive(Debug, Clone,Serialize, Deserialize)] +pub struct WechatCpTagAddOrRemoveUsersResponse { + pub invalidlist: Option, + pub invalidparty: Option>, +} + + +#[derive(Debug, Clone,Serialize, Deserialize)] +pub struct WechatCpTpTag { + pub tagid: Option, + pub tagname: Option>, +} + +/// 微信用户信息 +#[derive(Debug, Clone,Serialize, Deserialize)] +pub struct WechatCpUser { + pub userid: Option, + pub new_user_id: Option, + pub name: Option, + pub depart_ids: Option>, + pub orders: Option>, + pub position: Option, + pub mobile: Option, + pub email: Option, + pub biz_mail: Option, + pub thumb_avatar: Option, + pub main_department: Option, + /// 全局唯一。对于同一个服务商,不同应用获取到企业内同一个成员的open_userid是相同的,最多64个字节。仅第三方应用可获取 + pub open_user_id: Option, + pub address: Option, + pub avatar_media_id: Option, + /// 别名;第三方仅通讯录应用可获取 + pub alias: Option, + pub status: Option, + pub is_leader: Option, + /// is_leader_in_dept. + /// 个数必须和department一致,表示在所在的部门内是否为上级。1表示为上级,0表示非上级。在审批等应用里可以用来标识上级审批人 + pub is_leader_in_dept: Option>, + pub ext_attrs: Option>, + pub enable: Option, + pub avatar: Option, + pub gender: Option, + pub hide_mobile: Option, + pub english_name: Option, + pub telephone: Option, + pub to_invite: Option, + pub qr_code: Option, + pub positions: Option>, + /// 成员对外信息 + pub external_attrs: Option>, + pub external_position: Option, + pub external_corp_name: Option, + pub direct_leader: Option>, + pub wechat_channels: Option, +} + + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ExternalAttribute { + /// 属性类型: 0-本文 1-网页 2-小程序. + #[serde(rename = "type")] + pub r#type: Option, + /// 属性名称: 需要先确保在管理端有创建改属性,否则会忽略. + pub name: Option, + /// 文本属性内容,长度限制12个UTF8字符. + pub value: Option, + /// 网页的url,必须包含http或者https头. + pub url: Option, + /// 小程序的展示标题,长度限制12个UTF8字符. + pub title: Option, + /// 小程序appid,必须是有在本企业安装授权的小程序,否则会被忽略. + pub appid: Option, + /// 小程序的页面路径 + pub page_path: Option, +} + + +#[derive(Debug, Clone,Serialize, Deserialize)] +pub struct Attr { + /// 属性类型: 0-文本 1-网页 + #[serde(rename="type")] + pub r#type: Option, + pub name: Option, + pub text_value: Option, + pub web_url: Option, + pub web_title: Option, +} + + +#[derive(Debug, Clone,Serialize, Deserialize)] +pub struct WechatChannels { + pub nickname: Option, + pub status: Option, +} diff --git a/src/wechat/cryptos/mod.rs b/src/wechat/cryptos/mod.rs index 05b98f1..41adfcf 100644 --- a/src/wechat/cryptos/mod.rs +++ b/src/wechat/cryptos/mod.rs @@ -9,12 +9,12 @@ use serde::{Deserialize, Serialize}; use crate::prp::PrpCrypto; #[derive(Debug, Eq, PartialEq)] -pub struct WeChatCrypto { +pub struct WechatCrypto { key: Vec, } #[derive(Debug, Eq, PartialEq)] -pub struct WeChatCryptoV3 { +pub struct WechatCryptoV3 { v3_key: Vec, } @@ -68,11 +68,11 @@ pub struct EncryptV3 { } #[allow(unused)] -impl WeChatCrypto { - pub fn new(encoding_aes_key: &str) -> WeChatCrypto { +impl WechatCrypto { + pub fn new(encoding_aes_key: &str) -> WechatCrypto { let mut aes_key = encoding_aes_key.to_owned(); let key = base64::decode(&aes_key).unwrap_or_default(); - WeChatCrypto { + WechatCrypto { key: key, } } @@ -133,7 +133,7 @@ impl WeChatCrypto { /// timestamp 时间戳 /// nonce 随机字符串 /// echo_str 加密数据 - pub fn check_signature(&self, signature: &str, timestamp: i64, nonce: &str, echo_str: &str, id: &str, token: &str) -> LabradorResult { + pub fn check_signature(&self, signature: &str, timestamp: i64, nonce: &str, echo_str: &str, token: &str) -> LabradorResult { let real_signature = self.get_signature(timestamp, nonce, echo_str, token); if signature != &real_signature { return Err(LabraError::InvalidSignature("Unmatched signature.".to_string())); @@ -202,10 +202,10 @@ impl WeChatCrypto { #[allow(unused)] -impl WeChatCryptoV3 { +impl WechatCryptoV3 { pub fn new(v3_key: &str) -> Self { let v3_key = v3_key.as_bytes().to_vec(); - WeChatCryptoV3 { + WechatCryptoV3 { v3_key } } @@ -261,11 +261,11 @@ impl WeChatCryptoV3 { #[cfg(test)] #[allow(unused, non_snake_case)] mod tests { - use super::WeChatCrypto; + use super::WechatCrypto; #[test] fn test_get_signature() { - let crypto = WeChatCrypto::new( "kWxPEV2UEDyxWpmPdKC3F4dgPDmOvfKX1HGnEUDS1aR"); + let crypto = WechatCrypto::new( "kWxPEV2UEDyxWpmPdKC3F4dgPDmOvfKX1HGnEUDS1aR"); let signature = crypto.get_signature(123456i64, "test", "rust").unwrap(); assert_eq!("d6056f2bb3ad3e30f4afa5ef90cc9ddcdc7b7b27", &signature); } @@ -278,7 +278,7 @@ mod tests { let echo_str = "4ByGGj+sVCYcvGeQYhaKIk1o0pQRNbRjxybjTGblXrBaXlTXeOo1+bXFXDQQb1o6co6Yh9Bv41n7hOchLF6p+Q=="; // "123456", // "wx49f0ab532d5d035a" - let crypto = WeChatCrypto::new("kWxPEV2UEDyxWpmPdKC3F4dgPDmOvfKX1HGnEUDS1aR"); + let crypto = WechatCrypto::new("kWxPEV2UEDyxWpmPdKC3F4dgPDmOvfKX1HGnEUDS1aR"); match crypto.check_signature(signature, timestamp, nonce, echo_str) { Ok(_) => {}, Err(_) => panic!("Check signature failed"), @@ -291,7 +291,7 @@ mod tests { let sessionKey = "tiihtNczf5v6AKRyjwEUhQ=="; let encryptedData = "CiyLU1Aw2KjvrjMdj8YKliAjtP4gsMZMQmRzooG2xrDcvSnxIMXFufNstNGTyaGS9uT5geRa0W4oTOb1WT7fJlAC+oNPdbB+3hVbJSRgv+4lGOETKUQz6OYStslQ142dNCuabNPGBzlooOmB231qMM85d2/fV6ChevvXvQP8Hkue1poOFtnEtpyxVLW1zAo6/1Xx1COxFvrc2d7UL/lmHInNlxuacJXwu0fjpXfz/YqYzBIBzD6WUfTIF9GRHpOn/Hz7saL8xz+W//FRAUid1OksQaQx4CMs8LOddcQhULW4ucetDf96JcR3g0gfRK4PC7E/r7Z6xNrXd2UIeorGj5Ef7b1pJAYB6Y5anaHqZ9J6nKEBvB4DnNLIVWSgARns/8wR2SiRS7MNACwTyrGvt9ts8p12PKFdlqYTopNHR1Vf7XjfhQlVsAJdNiKdYmYVoKlaRv85IfVunYzO0IKXsyl7JCUjCpoG20f0a04COwfneQAGGwd5oa+T8yO5hzuyDb/XcxxmK01EpqOyuxINew=="; let iv = "r7BXXKkLb8qrSNn05n0qiA=="; - match WeChatCrypto::decrypt_data(sessionKey, encryptedData, iv) { + match WechatCrypto::decrypt_data(sessionKey, encryptedData, iv) { Ok(data) => { println!("success to decrypted data.{}", data); }, @@ -307,7 +307,7 @@ mod tests { let nonce = "437374424"; let echo_str = "4ByGGj+sVCYcvGeQYhaKIk1o0pQRNbRjxybjTGblXrBaXlTXeOo1+bXFXDQQb1o6co6Yh9Bv41n7hOchLF6p+Q=="; // , "wx49f0ab532d5d035a" - let crypto = WeChatCrypto::new("kWxPEV2UEDyxWpmPdKC3F4dgPDmOvfKX1HGnEUDS1aR"); + let crypto = WechatCrypto::new("kWxPEV2UEDyxWpmPdKC3F4dgPDmOvfKX1HGnEUDS1aR"); match crypto.check_signature(signature, timestamp, nonce, echo_str) { Ok(_) => {}, Err(_) => panic!("Check signature failed"), @@ -333,7 +333,7 @@ mod tests { \n\ "; // , "wx49f0ab532d5d035a" - let crypto = WeChatCrypto::new("kWxPEV2UEDyxWpmPdKC3F4dgPDmOvfKX1HGnEUDS1aR"); + let crypto = WechatCrypto::new("kWxPEV2UEDyxWpmPdKC3F4dgPDmOvfKX1HGnEUDS1aR"); let encrypted = crypto.encrypt_message(msg, timestamp, nonce).unwrap(); assert_eq!(expected, &encrypted); } @@ -357,7 +357,7 @@ mod tests { let timestamp = 1411525903; let nonce = "461056294"; // "wx49f0ab532d5d035a" - let crypto = WeChatCrypto::new("kWxPEV2UEDyxWpmPdKC3F4dgPDmOvfKX1HGnEUDS1aR"); + let crypto = WechatCrypto::new("kWxPEV2UEDyxWpmPdKC3F4dgPDmOvfKX1HGnEUDS1aR"); let decrypted = crypto.decrypt_message(xml, signature, timestamp, nonce).unwrap(); assert_eq!(expected, &decrypted); } diff --git a/src/wechat/miniapp/api/codesession.rs b/src/wechat/miniapp/api/codesession.rs index b0ae730..feb2b2b 100644 --- a/src/wechat/miniapp/api/codesession.rs +++ b/src/wechat/miniapp/api/codesession.rs @@ -2,19 +2,19 @@ use serde::{Serialize, Deserialize}; use crate::{session::SessionStore, request::{RequestType}, WechatCommonResponse, LabradorResult}; use crate::wechat::miniapp::method::WechatMaMethod; -use crate::wechat::miniapp::WeChatMaClient; +use crate::wechat::miniapp::WechatMaClient; #[derive(Debug, Clone)] pub struct WechatMaCodeSession<'a, T: SessionStore> { - client: &'a WeChatMaClient, + client: &'a WechatMaClient, } #[allow(unused)] impl<'a, T: SessionStore> WechatMaCodeSession<'a, T> { #[inline] - pub fn new(client: &WeChatMaClient) -> WechatMaCodeSession { + pub fn new(client: &WechatMaClient) -> WechatMaCodeSession { WechatMaCodeSession { client, } diff --git a/src/wechat/miniapp/api/media.rs b/src/wechat/miniapp/api/media.rs index fd2d4d2..b8d5048 100644 --- a/src/wechat/miniapp/api/media.rs +++ b/src/wechat/miniapp/api/media.rs @@ -7,19 +7,19 @@ use serde_json::Value; use crate::{session::SessionStore, LabradorResult, RequestBody, RequestType, WechatCommonResponse, request, get_nonce_str}; use crate::wechat::miniapp::method::{MaMediaMethod, WechatMaMethod}; -use crate::wechat::miniapp::{WeChatMaClient, WechatRequest}; +use crate::wechat::miniapp::{WechatMaClient, WechatRequest}; #[derive(Debug, Clone)] pub struct WechatMaMedia<'a, T: SessionStore> { - client: &'a WeChatMaClient, + client: &'a WechatMaClient, } #[allow(unused)] impl<'a, T: SessionStore> WechatMaMedia<'a, T> { #[inline] - pub fn new(client: &WeChatMaClient) -> WechatMaMedia { + pub fn new(client: &WechatMaClient) -> WechatMaMedia { WechatMaMedia { client, } diff --git a/src/wechat/miniapp/api/message.rs b/src/wechat/miniapp/api/message.rs index 907c1f4..cc65964 100644 --- a/src/wechat/miniapp/api/message.rs +++ b/src/wechat/miniapp/api/message.rs @@ -4,20 +4,20 @@ use serde_json::{ Value}; use crate::{session::SessionStore, request::{RequestType}, WechatCommonResponse, LabradorResult}; use crate::wechat::constants::{KEFU_MSGTYPE_IMAGE, KEFU_MSGTYPE_MA_PAGE, KEFU_MSGTYPE_TEXT}; use crate::wechat::miniapp::method::{MaMessageMethod, WechatMaMethod}; -use crate::wechat::miniapp::WeChatMaClient; +use crate::wechat::miniapp::WechatMaClient; /// 消息发送接口. #[derive(Debug, Clone)] pub struct WechatMaMessage<'a, T: SessionStore> { - client: &'a WeChatMaClient, + client: &'a WechatMaClient, } #[allow(unused)] impl<'a, T: SessionStore> WechatMaMessage<'a, T> { #[inline] - pub fn new(client: &WeChatMaClient) -> WechatMaMessage { + pub fn new(client: &WechatMaClient) -> WechatMaMessage { WechatMaMessage { client, } diff --git a/src/wechat/miniapp/api/qrcode.rs b/src/wechat/miniapp/api/qrcode.rs index cd0677c..2298f2a 100644 --- a/src/wechat/miniapp/api/qrcode.rs +++ b/src/wechat/miniapp/api/qrcode.rs @@ -2,7 +2,7 @@ use crate::{session::SessionStore, errors::LabraError, request::{RequestType}, L use bytes::Bytes; use serde::{Serialize, Deserialize}; use crate::wechat::miniapp::method::{MaQrCodeMethod, WechatMaMethod}; -use crate::wechat::miniapp::WeChatMaClient; +use crate::wechat::miniapp::WechatMaClient; ///
 /// 二维码相关操作接口.
@@ -13,14 +13,14 @@ use crate::wechat::miniapp::WeChatMaClient;
 ///
 #[derive(Debug, Clone)]
 pub struct WechatMaQrcode<'a, T: SessionStore> {
-    client: &'a WeChatMaClient,
+    client: &'a WechatMaClient,
 }
 
 #[allow(unused)]
 impl<'a, T: SessionStore> WechatMaQrcode<'a, T> {
 
     #[inline]
-    pub fn new(client: &WeChatMaClient) -> WechatMaQrcode {
+    pub fn new(client: &WechatMaClient) -> WechatMaQrcode {
         WechatMaQrcode {
             client,
         }
diff --git a/src/wechat/miniapp/api/user.rs b/src/wechat/miniapp/api/user.rs
index f94c10b..e94e2a9 100644
--- a/src/wechat/miniapp/api/user.rs
+++ b/src/wechat/miniapp/api/user.rs
@@ -3,29 +3,29 @@ use serde_json::{json};
 
 use serde::{Serialize, Deserialize};
 
-use crate::{session::SessionStore, errors::LabraError, wechat::{cryptos::WeChatCrypto}, request::RequestType, WechatCommonResponse, LabradorResult};
+use crate::{session::SessionStore, errors::LabraError, wechat::{cryptos::WechatCrypto}, request::RequestType, WechatCommonResponse, LabradorResult};
 use crate::wechat::miniapp::method::{MaUserMethod, WechatMaMethod};
-use crate::wechat::miniapp::WeChatMaClient;
+use crate::wechat::miniapp::WechatMaClient;
 
 /// 用户信息相关操作
 #[derive(Debug, Clone)]
-pub struct WeChatMaUser<'a, T: SessionStore> {
-    client: &'a WeChatMaClient,
+pub struct WechatMaUser<'a, T: SessionStore> {
+    client: &'a WechatMaClient,
 }
 
 #[allow(unused)]
-impl<'a, T: SessionStore> WeChatMaUser<'a, T> {
+impl<'a, T: SessionStore> WechatMaUser<'a, T> {
 
     #[inline]
-    pub fn new(client: &WeChatMaClient) -> WeChatMaUser {
-        WeChatMaUser {
+    pub fn new(client: &WechatMaClient) -> WechatMaUser {
+        WechatMaUser {
             client,
         }
     }
 
     /// 解密用户敏感数据
     pub fn decrypt_user_info(&self, session_key: &str, encrypted_data: &str, iv: &str) -> LabradorResult {
-        let result = WeChatCrypto::decrypt_data(session_key, encrypted_data, iv)?;
+        let result = WechatCrypto::decrypt_data(session_key, encrypted_data, iv)?;
         serde_json::from_str::(&result).map_err(LabraError::from)
     }
 
@@ -44,7 +44,7 @@ impl<'a, T: SessionStore> WeChatMaUser<'a, T> {
         let req = json!({
             "kv_list": params
         });
-        let signature = WeChatCrypto::create_hmac_sha256_sign(session_key, &req.to_string())?;
+        let signature = WechatCrypto::create_hmac_sha256_sign(session_key, &req.to_string())?;
         self.client.post(WechatMaMethod::User(MaUserMethod::SetUserStorage), vec![("appid".to_string(), self.client.secret.to_string()),
           ("signature".to_string(), signature),("openid".to_string(), openid.to_string()),("sig_method".to_string(), "hmac_sha256".to_string()),], &req, RequestType::Json).await?.json::()
     }
@@ -61,7 +61,7 @@ impl<'a, T: SessionStore> WeChatMaUser<'a, T> {
 
     /// 解密用户手机号信息.
     pub async fn decrypt_phone_info(&self, session_key: &str, encrypted_data: &str, iv: &str) -> LabradorResult {
-        let result = WeChatCrypto::decrypt_data(session_key, encrypted_data, iv)?;
+        let result = WechatCrypto::decrypt_data(session_key, encrypted_data, iv)?;
         serde_json::from_str::(&result).map_err(LabraError::from)
     }
 }
diff --git a/src/wechat/miniapp/mod.rs b/src/wechat/miniapp/mod.rs
index 816fc25..9cfab3a 100644
--- a/src/wechat/miniapp/mod.rs
+++ b/src/wechat/miniapp/mod.rs
@@ -1,4 +1,4 @@
-use crate::{session::SessionStore, client::APIClient, request::{Method, RequestType, LabraResponse, LabraRequest, RequestMethod}, util::current_timestamp, LabradorResult, SimpleStorage, WeChatCrypto, WechatRequest};
+use crate::{session::SessionStore, client::APIClient, request::{Method, RequestType, LabraResponse, LabraRequest, RequestMethod}, util::current_timestamp, LabradorResult, SimpleStorage, WechatCrypto, WechatRequest};
 use serde::{Serialize, Deserialize};
 
 mod method;
@@ -12,7 +12,7 @@ use crate::wechat::miniapp::method::WechatMaMethod;
 
 #[allow(unused)]
 #[derive(Debug, Clone)]
-pub struct WeChatMaClient {
+pub struct WechatMaClient {
     appid: String,
     secret: String,
     token: Option,
@@ -28,10 +28,10 @@ pub struct AccessTokenResponse{
 }
 
 #[allow(unused)]
-impl WeChatMaClient {
+impl WechatMaClient {
 
-    fn from_client(client: APIClient) -> WeChatMaClient {
-        WeChatMaClient {
+    fn from_client(client: APIClient) -> WechatMaClient {
+        WechatMaClient {
             appid: client.app_key.to_owned(),
             secret: client.secret.to_owned(),
             token: None,
@@ -51,13 +51,13 @@ impl WeChatMaClient {
     }
 
     /// get the wechat client
-    pub fn new>(appid: S, secret: S) -> WeChatMaClient {
+    pub fn new>(appid: S, secret: S) -> WechatMaClient {
         let client = APIClient::::from_session(appid.into(), secret.into(), "https://api.weixin.qq.com", SimpleStorage::new());
-        WeChatMaClient::::from_client(client)
+        WechatMaClient::::from_client(client)
     }
 
     /// get the wechat client
-    pub fn from_session>(appid: S, secret: S, session: T) -> WeChatMaClient {
+    pub fn from_session>(appid: S, secret: S, session: T) -> WechatMaClient {
         let client = APIClient::from_session(appid.into(), secret.into(), "https://api.weixin.qq.com", session);
         Self::from_client(client)
     }
@@ -95,8 +95,8 @@ impl WeChatMaClient {
     /// 详情(http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319&token=&lang=zh_CN)
     /// 
pub fn check_signature(&self, signature: &str, timestamp: i64, nonce: &str, echo_str: &str) -> LabradorResult { - let crp = WeChatCrypto::new(&self.aes_key.to_owned().unwrap_or_default()); - let _ = crp.check_signature(signature, timestamp, nonce, echo_str, "", &self.token.to_owned().unwrap_or_default())?; + let crp = WechatCrypto::new(&self.aes_key.to_owned().unwrap_or_default()); + let _ = crp.check_signature(signature, timestamp, nonce, echo_str, &self.token.to_owned().unwrap_or_default())?; Ok(true) } @@ -124,19 +124,16 @@ impl WeChatMaClient { if !access_token.is_empty() && method.need_token() { querys.push((ACCESS_TOKEN.to_string(), access_token)); } - let mut req = LabraRequest::new().url(method.get_method()).params(querys).method(Method::Post).json(data).req_type(request_type); - self.client.request(req).await + self.client.post(method, querys, data, request_type).await } /// 发送GET请求 - async fn get(&self, method: WechatMaMethod, params: Vec<(&str, &str)>, request_type: RequestType) -> LabradorResult { + async fn get(&self, method: WechatMaMethod, mut params: Vec<(&str, &str)>, request_type: RequestType) -> LabradorResult { let access_token = self.access_token(false).await?; - let mut querys = params.into_iter().map(|(k, v)| (k.to_string(), v.to_string())).collect::>(); if !access_token.is_empty() && method.need_token() { - querys.push((ACCESS_TOKEN.to_string(), access_token)); + params.push((ACCESS_TOKEN, access_token.as_str())); } - let mut req = LabraRequest::::new().url(method.get_method()).params(querys).method(Method::Get).req_type(request_type); - self.client.request(req).await + self.client.get(method, params, request_type).await } /// codesssion相关服务 @@ -149,8 +146,8 @@ impl WeChatMaClient { WechatMaQrcode::new(self) } /// 用户相关操作接口 - pub fn user(&self) -> WeChatMaUser { - WeChatMaUser::new(self) + pub fn user(&self) -> WechatMaUser { + WechatMaUser::new(self) } /// 媒体操作接口 pub fn media(&self) -> WechatMaMedia { diff --git a/src/wechat/mp/api/card.rs b/src/wechat/mp/api/card.rs index 8e1c5aa..7c7538a 100644 --- a/src/wechat/mp/api/card.rs +++ b/src/wechat/mp/api/card.rs @@ -3,22 +3,22 @@ use std::vec; use serde::{Serialize, Deserialize, Serializer}; use serde_json::{json, Value}; -use crate::{session::SessionStore, request::{RequestType}, WechatCommonResponse, WeChatMpClient, LabradorResult, LabraError, get_timestamp, TicketType, get_nonce_str, WeChatCrypto, BaseInfo, AdvancedInfo}; +use crate::{session::SessionStore, request::{RequestType}, WechatCommonResponse, WechatMpClient, LabradorResult, LabraError, get_timestamp, TicketType, get_nonce_str, WechatCrypto, BaseInfo, AdvancedInfo}; use crate::wechat::mp::constants::{QR_CODE}; use crate::wechat::mp::method::{MpCardMethod, WechatMpMethod}; /// 卡券相关. #[derive(Debug, Clone)] -pub struct WeChatMpCard<'a, T: SessionStore> { - client: &'a WeChatMpClient, +pub struct WechatMpCard<'a, T: SessionStore> { + client: &'a WechatMpClient, } #[allow(unused)] -impl<'a, T: SessionStore> WeChatMpCard<'a, T> { +impl<'a, T: SessionStore> WechatMpCard<'a, T> { #[inline] - pub fn new(client: &WeChatMpClient) -> WeChatMpCard { - WeChatMpCard { + pub fn new(client: &WechatMpClient) -> WechatMpCard { + WechatMpCard { client, } } @@ -49,7 +49,7 @@ impl<'a, T: SessionStore> WeChatMpCard<'a, T> { params.push(noncestr.to_string()); params.push(api_ticket); params.sort(); - let signature = WeChatCrypto::get_sha1_sign(¶ms.join("")); + let signature = WechatCrypto::get_sha1_sign(¶ms.join("")); Ok(WechatMpCardApiSignature{ app_id: self.client.appid.to_string(), card_id: "".to_string(), @@ -451,7 +451,7 @@ pub struct WechatMpCardApiSignature { #[allow(unused)] #[derive(Serialize, Deserialize)] pub struct WechatMpCardResponse { - pub card: WechatMpCard, + pub card: WechatMpCardInfo, pub user_card_status: Option, pub can_consume: bool, pub out_str: Option, @@ -463,7 +463,7 @@ pub struct WechatMpCardResponse { /// 微信卡券 #[allow(unused)] #[derive(Serialize, Deserialize)] -pub struct WechatMpCard { +pub struct WechatMpCardInfo { pub card_id: String, pub begin_time: i64, pub end_time: i64, diff --git a/src/wechat/mp/api/customservice.rs b/src/wechat/mp/api/customservice.rs index 553063f..ae26772 100644 --- a/src/wechat/mp/api/customservice.rs +++ b/src/wechat/mp/api/customservice.rs @@ -3,22 +3,22 @@ use std::vec; use serde::{Serialize, Deserialize}; use serde_json::{json, Value}; -use crate::{session::SessionStore, request::{RequestType}, errors::LabraError, WechatCommonResponse, WeChatMpClient, LabradorResult}; +use crate::{session::SessionStore, request::{RequestType}, errors::LabraError, WechatCommonResponse, WechatMpClient, LabradorResult}; use crate::util::md5::md5; use crate::wechat::mp::method::{MpCustomServiceMethod, WechatMpMethod}; /// 客服接口. #[derive(Debug, Clone)] -pub struct WeChatMpCustomService<'a, T: SessionStore> { - client: &'a WeChatMpClient, +pub struct WechatMpCustomService<'a, T: SessionStore> { + client: &'a WechatMpClient, } #[allow(unused)] -impl<'a, T: SessionStore> WeChatMpCustomService<'a, T> { +impl<'a, T: SessionStore> WechatMpCustomService<'a, T> { #[inline] - pub fn new(client: &WeChatMpClient) -> WeChatMpCustomService { - WeChatMpCustomService { + pub fn new(client: &WechatMpClient) -> WechatMpCustomService { + WechatMpCustomService { client, } } diff --git a/src/wechat/mp/api/media.rs b/src/wechat/mp/api/media.rs index 3d96629..de1f226 100644 --- a/src/wechat/mp/api/media.rs +++ b/src/wechat/mp/api/media.rs @@ -24,21 +24,21 @@ use bytes::Bytes; use serde::{Serialize, Deserialize}; use serde_json::{json, Value}; -use crate::{session::SessionStore, LabradorResult, RequestBody, RequestType, WeChatMpClient, WechatCommonResponse, WechatRequest, get_nonce_str, request}; +use crate::{session::SessionStore, LabradorResult, RequestBody, RequestType, WechatMpClient, WechatCommonResponse, WechatRequest, get_nonce_str, request}; use crate::wechat::mp::constants::MATERIAL_TYPE_NEWS; use crate::wechat::mp::method::{MpMediaMethod, WechatMpMethod}; #[derive(Debug, Clone)] pub struct WechatMpMedia<'a, T: SessionStore> { - client: &'a WeChatMpClient, + client: &'a WechatMpClient, } #[allow(unused)] impl<'a, T: SessionStore> WechatMpMedia<'a, T> { #[inline] - pub fn new(client: &WeChatMpClient) -> WechatMpMedia { + pub fn new(client: &WechatMpClient) -> WechatMpMedia { WechatMpMedia { client, } diff --git a/src/wechat/mp/api/member.rs b/src/wechat/mp/api/member.rs index e270a29..12deda5 100644 --- a/src/wechat/mp/api/member.rs +++ b/src/wechat/mp/api/member.rs @@ -3,22 +3,22 @@ use std::vec; use serde::{Serialize, Deserialize}; use serde_json::{json, Value}; -use crate::{session::SessionStore, request::{RequestType}, WechatCommonResponse, WeChatMpClient, LabradorResult, LabraError, get_timestamp}; +use crate::{session::SessionStore, request::{RequestType}, WechatCommonResponse, WechatMpClient, LabradorResult, LabraError, get_timestamp}; use crate::wechat::mp::constants::MEMBER_CARD; use crate::wechat::mp::method::{MpMemeberCardMethod, WechatMpMethod}; /// 会员卡相关. #[derive(Debug, Clone)] -pub struct WeChatMpMember<'a, T: SessionStore> { - client: &'a WeChatMpClient, +pub struct WechatMpMember<'a, T: SessionStore> { + client: &'a WechatMpClient, } #[allow(unused)] -impl<'a, T: SessionStore> WeChatMpMember<'a, T> { +impl<'a, T: SessionStore> WechatMpMember<'a, T> { #[inline] - pub fn new(client: &WeChatMpClient) -> WeChatMpMember { - WeChatMpMember { + pub fn new(client: &WechatMpClient) -> WechatMpMember { + WechatMpMember { client, } } diff --git a/src/wechat/mp/api/menu.rs b/src/wechat/mp/api/menu.rs index 8f5afab..08c47e3 100644 --- a/src/wechat/mp/api/menu.rs +++ b/src/wechat/mp/api/menu.rs @@ -21,21 +21,21 @@ //! use serde::{Deserialize, Serialize}; -use crate::{session::SessionStore, request::{RequestType}, errors::LabraError, WechatCommonResponse, WeChatMpClient, LabradorResult}; +use crate::{session::SessionStore, request::{RequestType}, errors::LabraError, WechatCommonResponse, WechatMpClient, LabradorResult}; use crate::wechat::mp::method::{MpMenuMethod, WechatMpMethod}; #[derive(Debug, Clone)] -pub struct WeChatMpMenu<'a, T: SessionStore> { - client: &'a WeChatMpClient, +pub struct WechatMpMenu<'a, T: SessionStore> { + client: &'a WechatMpClient, } #[allow(unused)] -impl<'a, T: SessionStore> WeChatMpMenu<'a, T> { +impl<'a, T: SessionStore> WechatMpMenu<'a, T> { #[inline] - pub fn new(client: &WeChatMpClient) -> WeChatMpMenu { - WeChatMpMenu { + pub fn new(client: &WechatMpClient) -> WechatMpMenu { + WechatMpMenu { client, } } diff --git a/src/wechat/mp/api/oauth2.rs b/src/wechat/mp/api/oauth2.rs index 606239b..0a4d4ef 100644 --- a/src/wechat/mp/api/oauth2.rs +++ b/src/wechat/mp/api/oauth2.rs @@ -1,20 +1,20 @@ use serde::{Serialize, Deserialize}; -use crate::{session::SessionStore, request::{RequestType}, wechat::{mp::method::WechatMpMethod}, WechatCommonResponse, WeChatMpClient, LabradorResult, LabraError}; +use crate::{session::SessionStore, request::{RequestType}, wechat::{mp::method::WechatMpMethod}, WechatCommonResponse, WechatMpClient, LabradorResult, LabraError}; use crate::wechat::mp::constants::{ACCESS_TOKEN, APPID, CODE, GRANT_TYPE, LANG, OPENID, REFRESH_TOKEN, SECRET, ZH_CN}; use crate::wechat::mp::method::Oauth2Method; #[derive(Debug, Clone)] pub struct WechatMpOauth2<'a, T: SessionStore> { - client: &'a WeChatMpClient, + client: &'a WechatMpClient, } #[allow(unused)] impl<'a, T: SessionStore> WechatMpOauth2<'a, T> { #[inline] - pub fn new(client: &WeChatMpClient) -> WechatMpOauth2 { + pub fn new(client: &WechatMpClient) -> WechatMpOauth2 { WechatMpOauth2 { client, } diff --git a/src/wechat/mp/api/ocr.rs b/src/wechat/mp/api/ocr.rs index 23b024a..35e35b3 100644 --- a/src/wechat/mp/api/ocr.rs +++ b/src/wechat/mp/api/ocr.rs @@ -6,22 +6,22 @@ use std::vec; use serde::{Serialize, Deserialize}; use serde_json::{Value}; -use crate::{session::SessionStore, request::{RequestType}, WechatCommonResponse, WeChatMpClient, LabradorResult, WechatRequest, RequestBody}; +use crate::{session::SessionStore, request::{RequestType}, WechatCommonResponse, WechatMpClient, LabradorResult, WechatRequest, RequestBody}; use crate::wechat::mp::constants::IMG_URL; use crate::wechat::mp::method::{MpOcrMethod, WechatMpMethod}; /// 微信连接WI-FI接口. #[derive(Debug, Clone)] -pub struct WeChatMpOcr<'a, T: SessionStore> { - client: &'a WeChatMpClient, +pub struct WechatMpOcr<'a, T: SessionStore> { + client: &'a WechatMpClient, } #[allow(unused)] -impl<'a, T: SessionStore> WeChatMpOcr<'a, T> { +impl<'a, T: SessionStore> WechatMpOcr<'a, T> { #[inline] - pub fn new(client: &WeChatMpClient) -> WeChatMpOcr { - WeChatMpOcr { + pub fn new(client: &WechatMpClient) -> WechatMpOcr { + WechatMpOcr { client, } } diff --git a/src/wechat/mp/api/qrcode.rs b/src/wechat/mp/api/qrcode.rs index d769396..b692543 100644 --- a/src/wechat/mp/api/qrcode.rs +++ b/src/wechat/mp/api/qrcode.rs @@ -1,20 +1,20 @@ -use crate::{session::SessionStore, errors::LabraError, request::{RequestType}, WechatCommonResponse, WeChatMpClient, LabradorResult}; +use crate::{session::SessionStore, errors::LabraError, request::{RequestType}, WechatCommonResponse, WechatMpClient, LabradorResult}; use serde::{Serialize, Deserialize}; use serde_json::{json, Value}; use crate::wechat::mp::constants::{QR_LIMIT_SCENE, QR_SCENE}; use crate::wechat::mp::method::{MpQrCodeMethod, WechatMpMethod}; #[derive(Debug, Clone)] -pub struct WeChatMpQRCode<'a, T: SessionStore> { - client: &'a WeChatMpClient, +pub struct WechatMpQRCode<'a, T: SessionStore> { + client: &'a WechatMpClient, } #[allow(unused)] -impl<'a, T: SessionStore> WeChatMpQRCode<'a, T> { +impl<'a, T: SessionStore> WechatMpQRCode<'a, T> { #[inline] - pub fn new(client: &WeChatMpClient) -> WeChatMpQRCode { - WeChatMpQRCode { + pub fn new(client: &WechatMpClient) -> WechatMpQRCode { + WechatMpQRCode { client, } } diff --git a/src/wechat/mp/api/subscribe_msg.rs b/src/wechat/mp/api/subscribe_msg.rs index 47f4306..ae0ed40 100644 --- a/src/wechat/mp/api/subscribe_msg.rs +++ b/src/wechat/mp/api/subscribe_msg.rs @@ -1,21 +1,21 @@ use serde::{Serialize, Deserialize}; use serde_json::{json, Value}; -use crate::{session::SessionStore, request::{RequestType}, WechatCommonResponse, WeChatMpClient, LabradorResult}; +use crate::{session::SessionStore, request::{RequestType}, WechatCommonResponse, WechatMpClient, LabradorResult}; use crate::wechat::mp::method::{MpSubscribeMessageMethod, WechatMpMethod}; /// 订阅消息服务接口 #[derive(Debug, Clone)] -pub struct WeChatMpSubscribeMessage<'a, T: SessionStore> { - client: &'a WeChatMpClient, +pub struct WechatMpSubscribeMessage<'a, T: SessionStore> { + client: &'a WechatMpClient, } #[allow(unused)] -impl<'a, T: SessionStore> WeChatMpSubscribeMessage<'a, T> { +impl<'a, T: SessionStore> WechatMpSubscribeMessage<'a, T> { #[inline] - pub fn new(client: &WeChatMpClient) -> WeChatMpSubscribeMessage { - WeChatMpSubscribeMessage { + pub fn new(client: &WechatMpClient) -> WechatMpSubscribeMessage { + WechatMpSubscribeMessage { client, } } diff --git a/src/wechat/mp/api/template_msg.rs b/src/wechat/mp/api/template_msg.rs index 69563a0..58393b5 100644 --- a/src/wechat/mp/api/template_msg.rs +++ b/src/wechat/mp/api/template_msg.rs @@ -1,21 +1,21 @@ use serde::{Serialize, Deserialize}; use serde_json::{json, Value}; -use crate::{session::SessionStore, request::{RequestType}, WechatCommonResponse, WeChatMpClient, LabradorResult}; +use crate::{session::SessionStore, request::{RequestType}, WechatCommonResponse, WechatMpClient, LabradorResult}; use crate::wechat::mp::method::{MpTemplateMessageMethod, WechatMpMethod}; #[derive(Debug, Clone)] -pub struct WeChatMpTemplateMessage<'a, T: SessionStore> { - client: &'a WeChatMpClient, +pub struct WechatMpTemplateMessage<'a, T: SessionStore> { + client: &'a WechatMpClient, } #[allow(unused)] -impl<'a, T: SessionStore> WeChatMpTemplateMessage<'a, T> { +impl<'a, T: SessionStore> WechatMpTemplateMessage<'a, T> { #[inline] - pub fn new(client: &WeChatMpClient) -> WeChatMpTemplateMessage { - WeChatMpTemplateMessage { + pub fn new(client: &WechatMpClient) -> WechatMpTemplateMessage { + WechatMpTemplateMessage { client, } } diff --git a/src/wechat/mp/api/user.rs b/src/wechat/mp/api/user.rs index 39fa028..913bbea 100644 --- a/src/wechat/mp/api/user.rs +++ b/src/wechat/mp/api/user.rs @@ -3,21 +3,21 @@ use serde_json::{json, Value}; use serde::{Serialize, Deserialize}; -use crate::{session::SessionStore, errors::LabraError, wechat::{cryptos::WeChatCrypto}, request::RequestType, WechatCommonResponse, WeChatMpClient, LabradorResult}; +use crate::{session::SessionStore, errors::LabraError, wechat::{cryptos::WechatCrypto}, request::RequestType, WechatCommonResponse, WechatMpClient, LabradorResult}; use crate::wechat::mp::method::{MpUserMethod, WechatMpMethod}; #[derive(Debug, Clone)] -pub struct WeChatMpUser<'a, T: SessionStore> { - client: &'a WeChatMpClient, +pub struct WechatMpUser<'a, T: SessionStore> { + client: &'a WechatMpClient, } #[allow(unused)] -impl<'a, T: SessionStore> WeChatMpUser<'a, T> { +impl<'a, T: SessionStore> WechatMpUser<'a, T> { #[inline] - pub fn new(client: &WeChatMpClient) -> WeChatMpUser { - WeChatMpUser { + pub fn new(client: &WechatMpClient) -> WechatMpUser { + WechatMpUser { client, } } @@ -50,7 +50,7 @@ impl<'a, T: SessionStore> WeChatMpUser<'a, T> { /// 解密用户信息 pub fn decrypt_user_info(&self, session_key: &str, encrypted_data: &str, iv: &str) -> LabradorResult { - let res = WeChatCrypto::decrypt_data(session_key, encrypted_data, iv)?; + let res = WechatCrypto::decrypt_data(session_key, encrypted_data, iv)?; match serde_json::from_str::(res.as_str()) { Ok(data) => { let openid = &data["openId"]; diff --git a/src/wechat/mp/api/wifi.rs b/src/wechat/mp/api/wifi.rs index 967f6bc..ffffad1 100644 --- a/src/wechat/mp/api/wifi.rs +++ b/src/wechat/mp/api/wifi.rs @@ -3,21 +3,21 @@ use std::vec; use serde::{Serialize, Deserialize}; use serde_json::{json, Value}; -use crate::{session::SessionStore, request::{RequestType}, WechatCommonResponse, WeChatMpClient, LabradorResult}; +use crate::{session::SessionStore, request::{RequestType}, WechatCommonResponse, WechatMpClient, LabradorResult}; use crate::wechat::mp::method::{MpWifiMethod, WechatMpMethod}; /// 微信连接WI-FI接口. #[derive(Debug, Clone)] -pub struct WeChatMpWifi<'a, T: SessionStore> { - client: &'a WeChatMpClient, +pub struct WechatMpWifi<'a, T: SessionStore> { + client: &'a WechatMpClient, } #[allow(unused)] -impl<'a, T: SessionStore> WeChatMpWifi<'a, T> { +impl<'a, T: SessionStore> WechatMpWifi<'a, T> { #[inline] - pub fn new(client: &WeChatMpClient) -> WeChatMpWifi { - WeChatMpWifi { + pub fn new(client: &WechatMpClient) -> WechatMpWifi { + WechatMpWifi { client, } } diff --git a/src/wechat/mp/events/click.rs b/src/wechat/mp/events/click.rs index 64c9794..4e64282 100644 --- a/src/wechat/mp/events/click.rs +++ b/src/wechat/mp/events/click.rs @@ -16,7 +16,7 @@ pub struct ClickEvent { } impl MessageParser for ClickEvent { - type WeChatMessage = ClickEvent; + type WechatMessage = ClickEvent; #[inline] fn from_xml(xml: &str) -> ClickEvent { diff --git a/src/wechat/mp/events/location.rs b/src/wechat/mp/events/location.rs index c373de0..716e843 100644 --- a/src/wechat/mp/events/location.rs +++ b/src/wechat/mp/events/location.rs @@ -18,7 +18,7 @@ pub struct LocationEvent { } impl MessageParser for LocationEvent { - type WeChatMessage = LocationEvent; + type WechatMessage = LocationEvent; #[inline] fn from_xml(xml: &str) -> LocationEvent { diff --git a/src/wechat/mp/events/qualification_verify_success.rs b/src/wechat/mp/events/qualification_verify_success.rs index 23e9dc7..cb09230 100644 --- a/src/wechat/mp/events/qualification_verify_success.rs +++ b/src/wechat/mp/events/qualification_verify_success.rs @@ -16,7 +16,7 @@ pub struct QualificationVerifySuccessEvent { } impl MessageParser for QualificationVerifySuccessEvent { - type WeChatMessage = QualificationVerifySuccessEvent; + type WechatMessage = QualificationVerifySuccessEvent; #[inline] fn from_xml(xml: &str) -> QualificationVerifySuccessEvent { diff --git a/src/wechat/mp/events/scan.rs b/src/wechat/mp/events/scan.rs index 51d5288..c89b9e0 100644 --- a/src/wechat/mp/events/scan.rs +++ b/src/wechat/mp/events/scan.rs @@ -17,7 +17,7 @@ pub struct ScanEvent { } impl MessageParser for ScanEvent { - type WeChatMessage = ScanEvent; + type WechatMessage = ScanEvent; #[inline] fn from_xml(xml: &str) -> ScanEvent { diff --git a/src/wechat/mp/events/subscribe.rs b/src/wechat/mp/events/subscribe.rs index dca1c4b..a118cdd 100644 --- a/src/wechat/mp/events/subscribe.rs +++ b/src/wechat/mp/events/subscribe.rs @@ -15,7 +15,7 @@ pub struct SubscribeEvent { } impl MessageParser for SubscribeEvent { - type WeChatMessage = SubscribeEvent; + type WechatMessage = SubscribeEvent; #[inline] fn from_xml(xml: &str) -> SubscribeEvent { diff --git a/src/wechat/mp/events/subscribe_scan.rs b/src/wechat/mp/events/subscribe_scan.rs index 567daa5..9c09c41 100644 --- a/src/wechat/mp/events/subscribe_scan.rs +++ b/src/wechat/mp/events/subscribe_scan.rs @@ -17,7 +17,7 @@ pub struct SubscribeScanEvent { } impl MessageParser for SubscribeScanEvent { - type WeChatMessage = SubscribeScanEvent; + type WechatMessage = SubscribeScanEvent; #[inline] fn from_xml(xml: &str) -> SubscribeScanEvent { diff --git a/src/wechat/mp/events/template_send_job_finish.rs b/src/wechat/mp/events/template_send_job_finish.rs index ef02d81..7fa04fb 100644 --- a/src/wechat/mp/events/template_send_job_finish.rs +++ b/src/wechat/mp/events/template_send_job_finish.rs @@ -15,7 +15,7 @@ pub struct TemplateSendJobFinishEvent { } impl MessageParser for TemplateSendJobFinishEvent { - type WeChatMessage = TemplateSendJobFinishEvent; + type WechatMessage = TemplateSendJobFinishEvent; #[inline] fn from_xml(xml: &str) -> TemplateSendJobFinishEvent { diff --git a/src/wechat/mp/events/unsubscribe.rs b/src/wechat/mp/events/unsubscribe.rs index 2fe44c5..54e85af 100644 --- a/src/wechat/mp/events/unsubscribe.rs +++ b/src/wechat/mp/events/unsubscribe.rs @@ -15,7 +15,7 @@ pub struct UnsubscribeEvent { } impl MessageParser for UnsubscribeEvent { - type WeChatMessage = UnsubscribeEvent; + type WechatMessage = UnsubscribeEvent; #[inline] fn from_xml(xml: &str) -> UnsubscribeEvent { diff --git a/src/wechat/mp/events/view.rs b/src/wechat/mp/events/view.rs index d9cbfa3..e02a731 100644 --- a/src/wechat/mp/events/view.rs +++ b/src/wechat/mp/events/view.rs @@ -16,7 +16,7 @@ pub struct ViewEvent { } impl MessageParser for ViewEvent { - type WeChatMessage = ViewEvent; + type WechatMessage = ViewEvent; #[inline] fn from_xml(xml: &str) -> ViewEvent { diff --git a/src/wechat/mp/messages/image.rs b/src/wechat/mp/messages/image.rs index 0a6db73..c090507 100644 --- a/src/wechat/mp/messages/image.rs +++ b/src/wechat/mp/messages/image.rs @@ -16,7 +16,7 @@ pub struct ImageMessage { } impl MessageParser for ImageMessage { - type WeChatMessage = ImageMessage; + type WechatMessage = ImageMessage; #[inline] fn from_xml(xml: &str) -> ImageMessage { diff --git a/src/wechat/mp/messages/link.rs b/src/wechat/mp/messages/link.rs index a04b6d5..c7b7a54 100644 --- a/src/wechat/mp/messages/link.rs +++ b/src/wechat/mp/messages/link.rs @@ -17,7 +17,7 @@ pub struct LinkMessage { } impl MessageParser for LinkMessage { - type WeChatMessage = LinkMessage; + type WechatMessage = LinkMessage; #[inline] fn from_xml(xml: &str) -> LinkMessage { diff --git a/src/wechat/mp/messages/location.rs b/src/wechat/mp/messages/location.rs index b764996..63d72c3 100644 --- a/src/wechat/mp/messages/location.rs +++ b/src/wechat/mp/messages/location.rs @@ -20,7 +20,7 @@ pub struct LocationMessage { } impl MessageParser for LocationMessage { - type WeChatMessage = LocationMessage; + type WechatMessage = LocationMessage; #[inline] fn from_xml(xml: &str) -> LocationMessage { diff --git a/src/wechat/mp/messages/mod.rs b/src/wechat/mp/messages/mod.rs index ea7f77e..1ffaff6 100644 --- a/src/wechat/mp/messages/mod.rs +++ b/src/wechat/mp/messages/mod.rs @@ -1,7 +1,7 @@ pub trait MessageParser { - type WeChatMessage; + type WechatMessage; - fn from_xml(xml: &str) -> Self::WeChatMessage; + fn from_xml(xml: &str) -> Self::WechatMessage; } mod text; diff --git a/src/wechat/mp/messages/shortvideo.rs b/src/wechat/mp/messages/shortvideo.rs index 3f03d44..cd46f51 100644 --- a/src/wechat/mp/messages/shortvideo.rs +++ b/src/wechat/mp/messages/shortvideo.rs @@ -17,7 +17,7 @@ pub struct ShortVideoMessage { } impl MessageParser for ShortVideoMessage { - type WeChatMessage = ShortVideoMessage; + type WechatMessage = ShortVideoMessage; #[inline] fn from_xml(xml: &str) -> ShortVideoMessage { diff --git a/src/wechat/mp/messages/text.rs b/src/wechat/mp/messages/text.rs index ce7c5bf..1888aa4 100644 --- a/src/wechat/mp/messages/text.rs +++ b/src/wechat/mp/messages/text.rs @@ -16,7 +16,7 @@ pub struct TextMessage { } impl MessageParser for TextMessage { - type WeChatMessage = TextMessage; + type WechatMessage = TextMessage; #[inline] fn from_xml(xml: &str) -> TextMessage { diff --git a/src/wechat/mp/messages/unknown.rs b/src/wechat/mp/messages/unknown.rs index 48468d5..169b794 100644 --- a/src/wechat/mp/messages/unknown.rs +++ b/src/wechat/mp/messages/unknown.rs @@ -15,7 +15,7 @@ pub struct UnknownMessage { } impl MessageParser for UnknownMessage { - type WeChatMessage = UnknownMessage; + type WechatMessage = UnknownMessage; #[inline] fn from_xml(xml: &str) -> UnknownMessage { diff --git a/src/wechat/mp/messages/video.rs b/src/wechat/mp/messages/video.rs index 4bb9fd5..82cba08 100644 --- a/src/wechat/mp/messages/video.rs +++ b/src/wechat/mp/messages/video.rs @@ -17,7 +17,7 @@ pub struct VideoMessage { } impl MessageParser for VideoMessage { - type WeChatMessage = VideoMessage; + type WechatMessage = VideoMessage; #[inline] fn from_xml(xml: &str) -> VideoMessage { diff --git a/src/wechat/mp/messages/voice.rs b/src/wechat/mp/messages/voice.rs index 99f47dd..24b16f9 100644 --- a/src/wechat/mp/messages/voice.rs +++ b/src/wechat/mp/messages/voice.rs @@ -18,7 +18,7 @@ pub struct VoiceMessage { } impl MessageParser for VoiceMessage { - type WeChatMessage = VoiceMessage; + type WechatMessage = VoiceMessage; #[inline] fn from_xml(xml: &str) -> VoiceMessage { diff --git a/src/wechat/mp/mod.rs b/src/wechat/mp/mod.rs index b9d45b1..1516784 100644 --- a/src/wechat/mp/mod.rs +++ b/src/wechat/mp/mod.rs @@ -1,4 +1,4 @@ -use crate::{session::SessionStore, client::APIClient, request::{Method, RequestType, LabraResponse, LabraRequest, RequestMethod}, WeChatCrypto, util::current_timestamp, LabradorResult, SimpleStorage, WechatRequest, WechatCommonResponse, JsapiSignature, get_timestamp, get_nonce_str}; +use crate::{session::SessionStore, client::APIClient, request::{Method, RequestType, LabraResponse, LabraRequest, RequestMethod}, WechatCrypto, util::current_timestamp, LabradorResult, SimpleStorage, WechatRequest, WechatCommonResponse, JsapiSignature, get_timestamp, get_nonce_str}; use serde::{Serialize, Deserialize}; use serde_json::{json, Value}; use crate::wechat::mp::method::WechatMpMethod; @@ -17,7 +17,7 @@ use crate::wechat::mp::method::WechatMpMethod::QrConnectUrl; #[allow(unused)] #[derive(Debug, Clone)] -pub struct WeChatMpClient { +pub struct WechatMpClient { appid: String, secret: String, token: Option, @@ -65,10 +65,10 @@ impl ToString for TicketType { } #[allow(unused)] -impl WeChatMpClient { +impl WechatMpClient { - fn from_client(client: APIClient) -> WeChatMpClient { - WeChatMpClient { + fn from_client(client: APIClient) -> WechatMpClient { + WechatMpClient { appid: client.app_key.to_owned(), secret: client.secret.to_owned(), token: None, @@ -79,13 +79,13 @@ impl WeChatMpClient { } /// get the wechat client - pub fn new>(appid: S, secret: S) -> WeChatMpClient { + pub fn new>(appid: S, secret: S) -> WechatMpClient { let client = APIClient::::from_session(appid.into(), secret.into(), "https://api.weixin.qq.com", SimpleStorage::new()); - WeChatMpClient::from_client(client) + WechatMpClient::from_client(client) } /// get the wechat client - pub fn from_session>(appid: S, secret: S, session: T) -> WeChatMpClient { + pub fn from_session>(appid: S, secret: S, session: T) -> WechatMpClient { let client = APIClient::from_session(appid.into(), secret.into(), "https://api.weixin.qq.com", session); Self::from_client(client) } @@ -201,7 +201,7 @@ impl WeChatMpClient { let timestamp = get_timestamp() / 1000; let noncestr = get_nonce_str(); let jsapi_ticket = self.get_jsapi_ticket(false).await?; - let signature = WeChatCrypto::get_sha1_sign(&vec!["jsapi_ticket=".to_string() + &jsapi_ticket, + let signature = WechatCrypto::get_sha1_sign(&vec!["jsapi_ticket=".to_string() + &jsapi_ticket, "noncestr=".to_string() + &noncestr, "timestamp=".to_string() + ×tamp.to_string(),"url=".to_string() + &url].join("&")); Ok(JsapiSignature{ @@ -254,8 +254,8 @@ impl WeChatMpClient { /// 详情(http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319&token=&lang=zh_CN) /// pub fn check_signature(&self, signature: &str, timestamp: i64, nonce: &str, echo_str: &str) -> LabradorResult { - let crp = WeChatCrypto::new(&self.aes_key.to_owned().unwrap_or_default()); - let _ = crp.check_signature(signature, timestamp, nonce, echo_str, "", &self.token.to_owned().unwrap_or_default())?; + let crp = WechatCrypto::new(&self.aes_key.to_owned().unwrap_or_default()); + let _ = crp.check_signature(signature, timestamp, nonce, echo_str, &self.token.to_owned().unwrap_or_default())?; Ok(true) } @@ -265,8 +265,7 @@ impl WeChatMpClient { if !access_token.is_empty() && method.need_token() { querys.push((ACCESS_TOKEN.to_string(), access_token)); } - let mut req = LabraRequest::new().url(method.get_method()).params(querys).method(Method::Post).json(data).req_type(request_type); - self.client.request(req).await + self.client.post(method, querys, data, request_type).await } ///
@@ -288,19 +287,17 @@ impl WeChatMpClient {
     }
 
     /// 发送GET请求
-    async fn get(&self, method: WechatMpMethod, params: Vec<(&str, &str)>, request_type: RequestType) -> LabradorResult {
+    async fn get(&self, method: WechatMpMethod, mut params: Vec<(&str, &str)>, request_type: RequestType) -> LabradorResult {
         let access_token = self.access_token(false).await?;
-        let mut querys = params.into_iter().map(|(k, v)| (k.to_string(), v.to_string())).collect::>();
         if !access_token.is_empty() && method.need_token() {
-            querys.push((ACCESS_TOKEN.to_string(), access_token));
+            params.push((ACCESS_TOKEN, access_token.as_str()));
         }
-        let mut req = LabraRequest::::new().url(method.get_method()).params(querys).method(Method::Get).req_type(request_type);
-        self.client.request(req).await
+        self.client.get(method, params, request_type).await
     }
 
     /// 用户相关服务
-    pub fn user(&self) -> WeChatMpUser {
-        WeChatMpUser::new(self)
+    pub fn user(&self) -> WechatMpUser {
+        WechatMpUser::new(self)
     }
 
     /// Oauth2授权相关服务
@@ -309,18 +306,18 @@ impl WeChatMpClient {
     }
 
     /// qrcode相关服务
-    pub fn qrcode(&self) -> WeChatMpQRCode {
-        WeChatMpQRCode::new(self)
+    pub fn qrcode(&self) -> WechatMpQRCode {
+        WechatMpQRCode::new(self)
     }
 
     /// 客服相关服务
-    pub fn custom_service(&self) -> WeChatMpCustomService {
-        WeChatMpCustomService::new(self)
+    pub fn custom_service(&self) -> WechatMpCustomService {
+        WechatMpCustomService::new(self)
     }
 
     /// 菜单相关服务
-    pub fn menu(&self) -> WeChatMpMenu {
-        WeChatMpMenu::new(self)
+    pub fn menu(&self) -> WechatMpMenu {
+        WechatMpMenu::new(self)
     }
 
     /// 多媒体服务
@@ -329,23 +326,23 @@ impl WeChatMpClient {
     }
 
     /// 模板消息服务
-    pub fn template_msg(&self) -> WeChatMpTemplateMessage {
-        WeChatMpTemplateMessage::new(self)
+    pub fn template_msg(&self) -> WechatMpTemplateMessage {
+        WechatMpTemplateMessage::new(self)
     }
 
     /// 订阅消息服务
-    pub fn subscribe_msg(&self) -> WeChatMpSubscribeMessage {
-        WeChatMpSubscribeMessage::new(self)
+    pub fn subscribe_msg(&self) -> WechatMpSubscribeMessage {
+        WechatMpSubscribeMessage::new(self)
     }
 
     /// Wifi服务
-    pub fn wifi(&self) -> WeChatMpWifi {
-        WeChatMpWifi::new(self)
+    pub fn wifi(&self) -> WechatMpWifi {
+        WechatMpWifi::new(self)
     }
 
     /// OCR服务
-    pub fn ocr(&self) -> WeChatMpOcr {
-        WeChatMpOcr::new(self)
+    pub fn ocr(&self) -> WechatMpOcr {
+        WechatMpOcr::new(self)
     }
 
 }
diff --git a/src/wechat/pay/api/wxpay.rs b/src/wechat/pay/api/wxpay.rs
index 0c6a5a2..2ba7577 100644
--- a/src/wechat/pay/api/wxpay.rs
+++ b/src/wechat/pay/api/wxpay.rs
@@ -1,20 +1,20 @@
 use serde_json::Value;
-use crate::{DecryptNotifyResult, DecryptRefundNotifyResult, IsvWeChatPayRequestV3, LabradorResult, LabraError, OriginNotifyResponse, RequestType, SessionStore, WeChatCloseOrderRequest, WeChatCloseOrderRequestV3, WeChatCloseOrderResponse, WeChatDecryptRefundNotifyResponse, WeChatOrderReverseRequest, WeChatOrderReverseResponse, WeChatPayClient, WeChatPayNotifyResponse, WeChatPayNotifyResponseV3, WeChatPayRequestV3, WeChatPayResponse, WeChatPayResponseV3, WeChatQueryOrderRequest, WeChatQueryOrderRequestV3, WeChatQueryOrderResponse, WeChatQueryOrderResponseV3, WeChatQueryRefundOrderRequest, WeChatQueryRefundResponse, WeChatQueryRefundResponseV3, WeChatRefundNotifyResponse, WeChatRefundNotifyResponseV3, WeChatRefundRequest, WeChatRefundRequestV3, WeChatRefundResponse, WeChatRefundResponseV3, WxPayShorturlRequest, WxPayShortUrlResponse, WxScanPayNotifyResponse};
-use crate::wechat::cryptos::{SignatureHeader, WeChatCryptoV3};
+use crate::{DecryptNotifyResult, DecryptRefundNotifyResult, IsvWechatPayRequestV3, LabradorResult, LabraError, OriginNotifyResponse, RequestType, SessionStore, WechatCloseOrderRequest, WechatCloseOrderRequestV3, WechatCloseOrderResponse, WechatDecryptRefundNotifyResponse, WechatOrderReverseRequest, WechatOrderReverseResponse, WechatPayClient, WechatPayNotifyResponse, WechatPayNotifyResponseV3, WechatPayRequestV3, WechatPayResponse, WechatPayResponseV3, WechatQueryOrderRequest, WechatQueryOrderRequestV3, WechatQueryOrderResponse, WechatQueryOrderResponseV3, WechatQueryRefundOrderRequest, WechatQueryRefundResponse, WechatQueryRefundResponseV3, WechatRefundNotifyResponse, WechatRefundNotifyResponseV3, WechatRefundRequest, WechatRefundRequestV3, WechatRefundResponse, WechatRefundResponseV3, WxPayShorturlRequest, WxPayShortUrlResponse, WxScanPayNotifyResponse};
+use crate::wechat::cryptos::{SignatureHeader, WechatCryptoV3};
 use crate::wechat::pay::method::{WechatPayMethod, WxPayMethod};
 use crate::wechat::pay::{TradeType};
-use crate::wechat::pay::request::WeChatPayRequest;
+use crate::wechat::pay::request::WechatPayRequest;
 
 #[derive(Debug, Clone)]
 pub struct WxPay<'a, T: SessionStore> {
-    client: &'a WeChatPayClient,
+    client: &'a WechatPayClient,
 }
 
 #[allow(unused)]
 impl<'a, T: SessionStore> WxPay<'a, T> {
 
     #[inline]
-    pub fn new(client: &WeChatPayClient) -> WxPay {
+    pub fn new(client: &WechatPayClient) -> WxPay {
         WxPay {
             client,
         }
@@ -34,12 +34,12 @@ impl<'a, T: SessionStore> WxPay<'a, T> {
     /// ```no_run
     ///
     /// # use labrador::SimpleStorage;
-    /// # use labrador::WeChatPayClient;
-    /// # use labrador::WeChatPayRequest;
+    /// # use labrador::WechatPayClient;
+    /// # use labrador::WechatPayRequest;
     /// # use labrador::TradeType;
     /// # async fn main() {
-    /// let client = WeChatPayClient::new("appid","secret", SimpleStorage::new()).wxpay();
-    /// let param = WeChatPayRequest {
+    /// let client = WechatPayClient::new("appid","secret").wxpay();
+    /// let param = WechatPayRequest {
     ///     appid: None,
     ///     trade_type: TradeType::Micro,
     ///     mch_id: "".to_string(),
@@ -65,7 +65,7 @@ impl<'a, T: SessionStore> WxPay<'a, T> {
     ///
     /// ```
     ///
-    pub async fn unified_order(&self, mut params: WeChatPayRequest) -> LabradorResult {
+    pub async fn unified_order(&self, mut params: WechatPayRequest) -> LabradorResult {
         params.check_params()?;
         let method = if params.trade_type == TradeType::Micro { WxPayMethod::MicroPay } else { WxPayMethod::UnifiedOrder };
         params.appid = self.client.appid.to_owned().into();
@@ -75,7 +75,7 @@ impl<'a, T: SessionStore> WxPay<'a, T> {
         }
         params.get_sign(&self.client.secret);
         let res = self.client.post(WechatPayMethod::WxPay(method), ¶ms.parse_xml(), RequestType::Xml).await?.text()?;
-        WeChatPayResponse::parse_xml(res)
+        WechatPayResponse::parse_xml(res)
     }
 
     ///
@@ -91,15 +91,15 @@ impl<'a, T: SessionStore> WxPay<'a, T> {
     /// ```no_run
     ///
     /// # use labrador::SimpleStorage;
-    /// # use labrador::WeChatPayClient;
-    /// # use labrador::WeChatPayRequestV3;
+    /// # use labrador::WechatPayClient;
+    /// # use labrador::WechatPayRequestV3;
     /// # use labrador::TradeType;
     /// # use labrador::Amount;
     /// # use labrador::Payer;
     /// # use chrono::NaiveDateTime;
     /// # async fn main() {
-    /// let client = WeChatPayClient::new("appid","secret", SimpleStorage::new()).wxpay();
-    /// let param = WeChatPayRequestV3 {
+    /// let client = WechatPayClient::new("appid","secret").wxpay();
+    /// let param = WechatPayRequestV3 {
     ///     appid: None,
     ///     mch_id: "".to_string(),
     ///     notify_url: "".to_string(),
@@ -120,7 +120,7 @@ impl<'a, T: SessionStore> WxPay<'a, T> {
     ///
     /// ```
     ///
-    pub async fn unified_order_v3(&self, trade_type: TradeType, mut params: WeChatPayRequestV3) -> LabradorResult {
+    pub async fn unified_order_v3(&self, trade_type: TradeType, mut params: WechatPayRequestV3) -> LabradorResult {
         if params.mch_id.is_empty() {
             params.mch_id = self.client.mch_id.to_owned().unwrap_or_default();
         }
@@ -128,22 +128,22 @@ impl<'a, T: SessionStore> WxPay<'a, T> {
             params.appid = self.client.appid.to_owned().into();
         }
         let res = self.client.post_v3(params.mch_id.to_owned().into(), WechatPayMethod::WxPay(WxPayMethod::UnifiedOrderV3(trade_type)), vec![],¶ms, RequestType::Json).await?.json::()?;
-        serde_json::from_value::(res).map_err(LabraError::from)
+        serde_json::from_value::(res).map_err(LabraError::from)
     }
 
-    pub async fn isv_unified_order_v3(&self, trade_type: TradeType, mut params: IsvWeChatPayRequestV3) -> LabradorResult {
+    pub async fn isv_unified_order_v3(&self, trade_type: TradeType, mut params: IsvWechatPayRequestV3) -> LabradorResult {
         let res = self.client.post_v3(None, WechatPayMethod::WxPay(WxPayMethod::IsvUnifiedOrderV3(trade_type)), vec![],¶ms, RequestType::Json).await?.json::()?;
-        serde_json::from_value::(res).map_err(LabraError::from)
+        serde_json::from_value::(res).map_err(LabraError::from)
     }
 
     /// 调用统一下单接口,并组装生成支付所需参数对象.
-    pub async fn create_order_v3(&self, trade_type: TradeType, params: WeChatPayRequestV3) -> LabradorResult {
+    pub async fn create_order_v3(&self, trade_type: TradeType, params: WechatPayRequestV3) -> LabradorResult {
         let result = self.unified_order_v3(trade_type.to_owned(), params.to_owned()).await?;
         result.get_pay_info(trade_type, params.appid, params.mch_id, self.client.private_key.to_owned())
     }
 
     /// 服务商调用统一下单接口,并组装生成支付所需参数对象.
-    pub async fn isv_create_order_v3(&self, trade_type: TradeType, params: IsvWeChatPayRequestV3) -> LabradorResult {
+    pub async fn isv_create_order_v3(&self, trade_type: TradeType, params: IsvWechatPayRequestV3) -> LabradorResult {
         let result = self.isv_unified_order_v3(trade_type.to_owned(), params.to_owned()).await?;
         result.get_pay_info(trade_type, params.sub_appid.to_owned(), params.sub_mchid.to_owned().unwrap_or_default(), self.client.private_key.to_owned())
     }
@@ -164,11 +164,11 @@ impl<'a, T: SessionStore> WxPay<'a, T> {
     /// ```no_run
     ///
     /// # use labrador::SimpleStorage;
-    /// # use labrador::WeChatPayClient;
-    /// # use labrador::WeChatCloseOrderRequest;
+    /// # use labrador::WechatPayClient;
+    /// # use labrador::WechatCloseOrderRequest;
     /// # async fn main() {
-    /// let client = WeChatPayClient::new("appid","secret", SimpleStorage::new()).wxpay();
-    /// let param = WeChatCloseOrderRequest {
+    /// let client = WechatPayClient::new("appid","secret").wxpay();
+    /// let param = WechatCloseOrderRequest {
     ///     appid: None,
     ///     mch_id: "".to_string(),
     ///     out_trade_no: "".to_string(),
@@ -184,11 +184,11 @@ impl<'a, T: SessionStore> WxPay<'a, T> {
     /// ```
     ///
     pub async fn close_order(&self,
-                             mut params: WeChatCloseOrderRequest) -> LabradorResult {
+                             mut params: WechatCloseOrderRequest) -> LabradorResult {
         params.appid = self.client.appid.to_owned().into();
         params.get_sign(&self.client.api_key.to_owned().unwrap_or_default());
         let res = self.client.post(WechatPayMethod::WxPay(WxPayMethod::CloseOrder), ¶ms.parse_xml(), RequestType::Xml).await?.text()?;
-        WeChatCloseOrderResponse::parse_xml(res)
+        WechatCloseOrderResponse::parse_xml(res)
     }
 
     ///
@@ -207,11 +207,11 @@ impl<'a, T: SessionStore> WxPay<'a, T> {
     /// ```no_run
     ///
     /// # use labrador::SimpleStorage;
-    /// # use labrador::WeChatPayClient;
-    /// # use labrador::WeChatCloseOrderRequestV3;
+    /// # use labrador::WechatPayClient;
+    /// # use labrador::WechatCloseOrderRequestV3;
     /// # async fn main() {
-    /// let client = WeChatPayClient::new("appid","secret", SimpleStorage::new()).wxpay();
-    /// let param = WeChatCloseOrderRequestV3 {
+    /// let client = WechatPayClient::new("appid","secret").wxpay();
+    /// let param = WechatCloseOrderRequestV3 {
     ///     mchid: "".to_string(),
     ///     out_trade_no: None,
     /// };
@@ -223,7 +223,7 @@ impl<'a, T: SessionStore> WxPay<'a, T> {
     ///
     /// ```
     ///
-    pub async fn close_order_v3(&self, mut params: WeChatCloseOrderRequestV3) -> LabradorResult<()> {
+    pub async fn close_order_v3(&self, mut params: WechatCloseOrderRequestV3) -> LabradorResult<()> {
         let out_trade_no = params.out_trade_no.to_owned().unwrap_or_default();
         params.out_trade_no = None;
         let res = self.client.post_v3(params.mchid.to_owned().into(), WechatPayMethod::WxPay(WxPayMethod::CloseOrderV3(out_trade_no)), vec![], ¶ms, RequestType::Json).await?;
@@ -251,11 +251,11 @@ impl<'a, T: SessionStore> WxPay<'a, T> {
     /// ```no_run
     ///
     /// # use labrador::SimpleStorage;
-    /// # use labrador::WeChatPayClient;
-    /// # use labrador::WeChatQueryOrderRequest;
+    /// # use labrador::WechatPayClient;
+    /// # use labrador::WechatQueryOrderRequest;
     /// # async fn main() {
-    /// let client = WeChatPayClient::new("appid","secret", SimpleStorage::new()).wxpay();
-    /// let param = WeChatQueryOrderRequest {
+    /// let client = WechatPayClient::new("appid","secret").wxpay();
+    /// let param = WechatQueryOrderRequest {
     ///     transaction_id: None,
     ///     out_trade_no: None,
     ///     appid: None,
@@ -271,10 +271,10 @@ impl<'a, T: SessionStore> WxPay<'a, T> {
     ///
     /// ```
     ///
-    pub async fn query_order(&self, mut params: WeChatQueryOrderRequest) -> LabradorResult {
+    pub async fn query_order(&self, mut params: WechatQueryOrderRequest) -> LabradorResult {
         let res = self.client.post(WechatPayMethod::WxPay(WxPayMethod::QueryOrder), ¶ms, RequestType::Xml).await?;
         let result = res.text()?;
-        WeChatQueryOrderResponse::parse_xml(result)
+        WechatQueryOrderResponse::parse_xml(result)
     }
 
     ///
@@ -299,11 +299,11 @@ impl<'a, T: SessionStore> WxPay<'a, T> {
     /// ```no_run
     ///
     /// # use labrador::SimpleStorage;
-    /// # use labrador::WeChatPayClient;
-    /// # use labrador::WeChatQueryOrderRequestV3;
+    /// # use labrador::WechatPayClient;
+    /// # use labrador::WechatQueryOrderRequestV3;
     /// # async fn main() {
-    /// let client = WeChatPayClient::new("appid","secret", SimpleStorage::new()).wxpay();
-    /// let param = WeChatQueryOrderRequestV3 {
+    /// let client = WechatPayClient::new("appid","secret").wxpay();
+    /// let param = WechatQueryOrderRequestV3 {
     ///     mchid: "".to_string(),
     ///     transaction_id: None,
     ///     out_trade_no: None,
@@ -316,9 +316,9 @@ impl<'a, T: SessionStore> WxPay<'a, T> {
     ///
     /// ```
     ///
-    pub async fn query_order_v3(&self, params: WeChatQueryOrderRequestV3) -> LabradorResult {
+    pub async fn query_order_v3(&self, params: WechatQueryOrderRequestV3) -> LabradorResult {
         self.client.post_v3(params.mchid.to_owned().into(), WechatPayMethod::WxPay(WxPayMethod::QueryOrderV3((params.out_trade_no.to_owned(), params.out_trade_no.to_owned()))), vec![], "", RequestType::Json)
-            .await?.json::()
+            .await?.json::()
     }
 
 
@@ -335,12 +335,12 @@ impl<'a, T: SessionStore> WxPay<'a, T> {
     ///
     /// 接口链接:https://api.mch.weixin.qq.com/pay/refundquery
     /// 
- pub async fn query_refund_order(&self, mut params: WeChatQueryRefundOrderRequest) -> LabradorResult { + pub async fn query_refund_order(&self, mut params: WechatQueryRefundOrderRequest) -> LabradorResult { params.appid = self.client.appid.to_owned().into(); params.get_sign(&self.client.api_key.to_owned().unwrap_or_default()); let res = self.client.post(WechatPayMethod::WxPay(WxPayMethod::QueryRefundOrder), params, RequestType::Xml) .await?.text()?; - WeChatQueryRefundResponse::parse_xml(res) + WechatQueryRefundResponse::parse_xml(res) } @@ -362,10 +362,10 @@ impl<'a, T: SessionStore> WxPay<'a, T> { /// https://api2.mch.weixin.qq.com/pay/refundqueryv2(备用域名)见跨城冗灾方案 /// /// - pub async fn query_refund_order_v2(&self, params: WeChatQueryRefundOrderRequest) -> LabradorResult { + pub async fn query_refund_order_v2(&self, params: WechatQueryRefundOrderRequest) -> LabradorResult { let res = self.client.post(WechatPayMethod::WxPay(WxPayMethod::QueryRefundOrderV2), params, RequestType::Json) .await?.text()?; - WeChatQueryRefundResponse::parse_xml(res) + WechatQueryRefundResponse::parse_xml(res) } /// @@ -378,25 +378,25 @@ impl<'a, T: SessionStore> WxPay<'a, T> { /// /// 接口链接:https://api.mch.weixin.qq.com/v3/refund/domestic/refunds/{out_refund_no} /// - pub async fn query_refund_order_v3(&self, out_refund_no: String) -> LabradorResult { + pub async fn query_refund_order_v3(&self, out_refund_no: String) -> LabradorResult { self.client.post_v3(None, WechatPayMethod::WxPay(WxPayMethod::QueryRefundOrderV3(out_refund_no)), vec![], "", RequestType::Json) - .await?.json::() + .await?.json::() } - pub async fn isv_query_refund_order_v3(&self, out_refund_no: String, sub_mch_id: String) -> LabradorResult { + pub async fn isv_query_refund_order_v3(&self, out_refund_no: String, sub_mch_id: String) -> LabradorResult { self.client.post_v3(None, WechatPayMethod::WxPay(WxPayMethod::QueryRefundOrderV3(out_refund_no)), vec![("sub_mchid".to_string(), sub_mch_id)], "", RequestType::Json) - .await?.json::() + .await?.json::() } /// # 解析支付结果通知. /// 详见 [文档](https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7) - pub fn parse_order_notify(&self, xml: &str) -> LabradorResult { - WeChatPayNotifyResponse::parse_xml(xml.to_string()) + pub fn parse_order_notify(&self, xml: &str) -> LabradorResult { + WechatPayNotifyResponse::parse_xml(xml.to_string()) } /// # 解析支付结果通知. - v3 /// 详见 [文档](https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_5.shtml) - pub async fn parse_order_notify_v3(&self, notify_data: &str, header: Option) -> LabradorResult { + pub async fn parse_order_notify_v3(&self, notify_data: &str, header: Option) -> LabradorResult { if header.is_none() { return Err(LabraError::RequestError("非法请求,头部信息为空".to_string())); @@ -408,10 +408,10 @@ impl<'a, T: SessionStore> WxPay<'a, T> { let origin = serde_json::from_str::(notify_data)?; let resource = origin.resource.to_owned(); let v3_key = self.client.api_key_v3.to_owned().unwrap_or_default(); - let crypto = WeChatCryptoV3::new(&v3_key); + let crypto = WechatCryptoV3::new(&v3_key); let decrypted = crypto.decrypt_data_v3(&resource)?; let decrypt_notify_result = serde_json::from_slice::(&decrypted)?; - Ok(WeChatPayNotifyResponseV3 { + Ok(WechatPayNotifyResponseV3 { raw_data: origin.into(), result: decrypt_notify_result.into() }) @@ -419,13 +419,13 @@ impl<'a, T: SessionStore> WxPay<'a, T> { /// # 解析退款结果通知. /// 详见 [文档](https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_16&index=9) - pub fn parse_refund_notify(&self, xml: &str) -> LabradorResult { - WeChatRefundNotifyResponse::parse_xml(xml.to_string(), &self.client.appid) + pub fn parse_refund_notify(&self, xml: &str) -> LabradorResult { + WechatRefundNotifyResponse::parse_xml(xml.to_string(), &self.client.appid) } /// # 解析退款结果通知 - V3. /// 详见 [文档](https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_16&index=9) - pub async fn parse_refund_notify_v3(&self, notify_data: &str, header: &Option) -> LabradorResult { + pub async fn parse_refund_notify_v3(&self, notify_data: &str, header: &Option) -> LabradorResult { if header.is_none() { return Err(LabraError::RequestError("非法请求,头部信息验证为空".to_string())); } @@ -436,10 +436,10 @@ impl<'a, T: SessionStore> WxPay<'a, T> { let origin = serde_json::from_str::(notify_data)?; let resource = origin.resource.to_owned(); let v3_key = self.client.api_key_v3.to_owned().unwrap_or_default(); - let crypto = WeChatCryptoV3::new(&v3_key); + let crypto = WechatCryptoV3::new(&v3_key); let decrypted = crypto.decrypt_data_v3(&resource)?; let decrypt_notify_result = serde_json::from_slice::(&decrypted)?; - Ok(WeChatRefundNotifyResponseV3 { + Ok(WechatRefundNotifyResponseV3 { raw_data: origin.into(), result: decrypt_notify_result.into() }) @@ -455,8 +455,8 @@ impl<'a, T: SessionStore> WxPay<'a, T> { /// 支付 pub async fn micro_pay( &self, - mut pay_params: WeChatPayRequest - ) -> LabradorResult { + mut pay_params: WechatPayRequest + ) -> LabradorResult { pay_params.trade_type = TradeType::Micro; self.unified_order(pay_params).await } @@ -464,8 +464,8 @@ impl<'a, T: SessionStore> WxPay<'a, T> { /// JSAPI支付 pub async fn jsapi_pay( &self, - mut pay_params: WeChatPayRequest - ) -> LabradorResult { + mut pay_params: WechatPayRequest + ) -> LabradorResult { pay_params.trade_type = TradeType::Jsapi; self.unified_order(pay_params).await } @@ -473,8 +473,8 @@ impl<'a, T: SessionStore> WxPay<'a, T> { /// APP支付 pub async fn app_pay( &self, - mut pay_params: WeChatPayRequest - ) -> LabradorResult { + mut pay_params: WechatPayRequest + ) -> LabradorResult { pay_params.trade_type = TradeType::App; self.unified_order(pay_params).await } @@ -502,13 +502,13 @@ impl<'a, T: SessionStore> WxPay<'a, T> { /// pub async fn refund( &self, - mut params: WeChatRefundRequest - ) -> LabradorResult { + mut params: WechatRefundRequest + ) -> LabradorResult { params.appid = self.client.appid.to_owned().into(); let mch_id = params.mch_id.as_str(); params.get_sign(&self.client.api_key.to_owned().unwrap_or_default()); let res = self.client.post(WechatPayMethod::WxPay(WxPayMethod::Refund), ¶ms.parse_xml(), RequestType::Xml).await?.text()?; - WeChatRefundResponse::parse_xml(res) + WechatRefundResponse::parse_xml(res) } /// @@ -528,12 +528,12 @@ impl<'a, T: SessionStore> WxPay<'a, T> { /// pub async fn reverse_order( &self, - mut params: WeChatOrderReverseRequest - ) -> LabradorResult { + mut params: WechatOrderReverseRequest + ) -> LabradorResult { params.appid = self.client.appid.to_owned().into(); let mch_id = params.mch_id.as_str(); let res = self.client.post(WechatPayMethod::WxPay(WxPayMethod::ReverseOrder), ¶ms.parse_xml(), RequestType::Xml).await?.text()?; - WeChatOrderReverseResponse::parse_xml(res) + WechatOrderReverseResponse::parse_xml(res) } /// @@ -580,10 +580,10 @@ impl<'a, T: SessionStore> WxPay<'a, T> { /// pub async fn refund_v3( &self, - mut params: WeChatRefundRequestV3 - ) -> LabradorResult { + mut params: WechatRefundRequestV3 + ) -> LabradorResult { self.client.post_v3(None, WechatPayMethod::WxPay(WxPayMethod::RefundV3), vec![],params, RequestType::Json).await? - .json::() + .json::() } } @@ -596,7 +596,7 @@ mod tests { use std::io::Read; use std::ops::Add; use chrono::{DateTime, Local, NaiveDateTime, SecondsFormat}; - use crate::{Amount, Payer, request, SimpleStorage, TradeType, WeChatCloseOrderRequestV3, WeChatPayClient, WeChatPayRequestV3}; + use crate::{Amount, Payer, request, SimpleStorage, TradeType, WechatCloseOrderRequestV3, WechatPayClient, WechatPayRequestV3}; #[test] fn test_close_order_v3() { @@ -604,9 +604,9 @@ mod tests { let mut private_key = Vec::new(); File::open("src/wechat/pay/sec/apiclient_key.pem").unwrap().read_to_end(&mut private_key).unwrap(); let r = rt.spawn(async { - let c = WeChatPayClient::new("appid", "secret", SimpleStorage::new()); + let c = WechatPayClient::new("appid", "secret"); let mut client =c.wxpay(); - let result = client.close_order_v3(WeChatCloseOrderRequestV3 { + let result = client.close_order_v3(WechatCloseOrderRequestV3 { mchid: "mchid".to_string(), out_trade_no: "23234234234".to_string().into() }); @@ -630,7 +630,7 @@ mod tests { let mut private_key = Vec::new(); File::open("src/wechat/pay/sec/apiclient_key.pem").unwrap().read_to_end(&mut private_key).unwrap(); let r = rt.spawn(async { - let c = WeChatPayClient::new("appid", "secret", SimpleStorage::new()); + let c = WechatPayClient::new("appid", "secret"); let mut client =c.wxpay(); // .cert(MchCert { // mch_id: "1602920235".to_string().into(), @@ -641,7 +641,7 @@ mod tests { // pkcs12_path: None // }).key_v3("364ae33e57cf4989b8aefaa66ddc7ca7".to_string()) let date = Local::now().to_rfc3339_opts(SecondsFormat::Secs, false); - /*let result = client.unified_order_v3(TradeType::Jsapi, WeChatPayRequestV3 { + /*let result = client.unified_order_v3(TradeType::Jsapi, WechatPayRequestV3 { appid: "wx7959501b424a9e93".to_string().into(), mch_id: "1602920235".to_string(), description: "测试商品支付".to_string(), @@ -660,7 +660,7 @@ mod tests { scene_info: None, settle_info: None });*/ - let result = client.close_order_v3(WeChatCloseOrderRequestV3 { + let result = client.close_order_v3(WechatCloseOrderRequestV3 { mchid: "mchid".to_string(), out_trade_no: "23234234234".to_string().into() }); @@ -682,10 +682,10 @@ mod tests { let mut private_key = Vec::new(); File::open("src/wechat/pay/sec/apiclient_key.pem").unwrap().read_to_end(&mut private_key).unwrap(); let r = rt.spawn(async { - let c = WeChatPayClient::new("appid", "secret", SimpleStorage::new()); + let c = WechatPayClient::new("appid", "secret"); let mut client =c.wxpay(); let date = Local::now().to_rfc3339_opts(SecondsFormat::Secs, false); - let result = client.unified_order_v3(TradeType::Jsapi, WeChatPayRequestV3 { + let result = client.unified_order_v3(TradeType::Jsapi, WechatPayRequestV3 { appid: "appid".to_string().into(), mch_id: "mchid".to_string(), description: "测试商品支付".to_string(), diff --git a/src/wechat/pay/mod.rs b/src/wechat/pay/mod.rs index e3b163f..4117d26 100644 --- a/src/wechat/pay/mod.rs +++ b/src/wechat/pay/mod.rs @@ -16,7 +16,7 @@ mod constants; pub use request::*; pub use response::*; use tracing::info; -use crate::wechat::cryptos::{SignatureHeader, WeChatCryptoV3}; +use crate::wechat::cryptos::{SignatureHeader, WechatCryptoV3}; use crate::wechat::pay::api::WxPay; use crate::wechat::pay::constants::{ACCEPT, AUTHORIZATION, CONTENT_TYPE_JSON}; use crate::wechat::pay::method::WechatPayMethod; @@ -65,7 +65,7 @@ impl TradeType { #[allow(unused)] #[derive(Debug, Clone)] -pub struct WeChatPayClient { +pub struct WechatPayClient { pub appid: String, secret: String, /// 私钥 V3 @@ -87,10 +87,10 @@ pub struct WeChatPayClient { #[allow(unused)] -impl WeChatPayClient { +impl WechatPayClient { - fn from_client(client: APIClient) -> WeChatPayClient { - WeChatPayClient { + fn from_client(client: APIClient) -> WechatPayClient { + WechatPayClient { appid: client.app_key.to_owned(), secret: client.secret.to_owned(), api_key_v3: None, @@ -105,13 +105,13 @@ impl WeChatPayClient { } /// get the wechat client - pub fn new>(appid: S, secret: S) -> WeChatPayClient { + pub fn new>(appid: S, secret: S) -> WechatPayClient { let client = APIClient::::from_session(appid.into(), secret.into(),"https://api.mch.weixin.qq.com", SimpleStorage::new()); - WeChatPayClient::::from_client(client) + WechatPayClient::::from_client(client) } /// get the wechat client - pub fn from_session>(appid: S, secret: S, session: T) -> WeChatPayClient { + pub fn from_session>(appid: S, secret: S, session: T) -> WechatPayClient { let client = APIClient::from_session(appid.into(), secret.into(), "https://api.mch.weixin.qq.com", session); Self::from_client(client) } @@ -205,7 +205,7 @@ impl WeChatPayClient { let nonce_str = get_nonce_str().to_uppercase(); let timestamp = get_timestamp() / 1000; - let signature = WeChatCryptoV3::signature_v3(&method, url, timestamp, &nonce_str, &body, &private_key)?; + let signature = WechatCryptoV3::signature_v3(&method, url, timestamp, &nonce_str, &body, &private_key)?; let token = format!("{} mchid=\"{}\",nonce_str=\"{}\",signature=\"{}\",timestamp=\"{}\",serial_no=\"{}\"", SCHEMA, mch_id, nonce_str, signature, timestamp, serial_no); Ok(token) @@ -261,7 +261,7 @@ impl WeChatPayClient { // V3 验证签名 let verify = if let Some(cert) = self.certs.get(&serial_no) { let content = String::from_utf8_lossy(&cert.public_key).to_string(); - WeChatCryptoV3::verify(&before_sign, &header.signature, &content).unwrap_or(false) + WechatCryptoV3::verify(&before_sign, &header.signature, &content).unwrap_or(false) } else { false }; @@ -272,7 +272,7 @@ impl WeChatPayClient { pub async fn verify(&self, serial_number: &str, message: &str, signature: &str) -> bool { if let Some(cert) = self.certs.get(serial_number) { let content = String::from_utf8_lossy(&cert.content).to_string(); - WeChatCryptoV3::verify(message, signature, &content).unwrap_or(false) + WechatCryptoV3::verify(message, signature, &content).unwrap_or(false) } else { false } @@ -290,7 +290,7 @@ impl WeChatPayClient { let bodys = serde_json::from_value::>(body["data"].to_owned())?; for body in bodys { let data =body.encrypt_certificate; - let crypto = WeChatCryptoV3::new(&self.api_key_v3.to_owned().unwrap_or_default()); + let crypto = WechatCryptoV3::new(&self.api_key_v3.to_owned().unwrap_or_default()); let res = crypto.decrypt_data_v3(&data)?; let mut cert = LabraCertificate::from_pem(res)?; let serial_no = body.serial_no; diff --git a/src/wechat/pay/request/mod.rs b/src/wechat/pay/request/mod.rs index c03c1dc..be6cd4f 100644 --- a/src/wechat/pay/request/mod.rs +++ b/src/wechat/pay/request/mod.rs @@ -11,7 +11,7 @@ use crate::wechat::pay::TradeType; #[derive(Debug, Serialize, Deserialize)] -pub struct WeChatPayRequest { +pub struct WechatPayRequest { pub appid: Option, /// 交易类型 pub trade_type: TradeType, @@ -49,7 +49,7 @@ pub struct WeChatPayRequest { #[derive(Debug, Serialize, Deserialize, Clone)] -pub struct WeChatPayRequestV3 { +pub struct WechatPayRequestV3 { #[serde(skip_serializing_if = "Option::is_none")] pub appid: Option, /// 直连商户号 @@ -84,7 +84,7 @@ pub struct WeChatPayRequestV3 { #[derive(Debug, Serialize, Deserialize, Clone)] -pub struct IsvWeChatPayRequestV3 { +pub struct IsvWechatPayRequestV3 { /// 由微信生成的应用ID,全局唯一。请求基础下单接口时请注意APPID的应用属性,例如公众号场景下,需使用应用属性为公众号的服务号APPID #[serde(skip_serializing_if = "Option::is_none")] pub sp_appid: Option, @@ -239,7 +239,7 @@ pub struct GoodsDetail { #[allow(unused)] -impl WeChatPayRequest { +impl WechatPayRequest { pub fn parse_xml(&self) -> String { let msg = format!( "\n\ @@ -329,7 +329,7 @@ impl WeChatPayRequest { #[derive(Debug, Serialize, Deserialize)] -pub struct WeChatCloseOrderRequest { +pub struct WechatCloseOrderRequest { pub appid: Option, /// 商户号 pub mch_id: String, @@ -343,7 +343,7 @@ pub struct WeChatCloseOrderRequest { } #[derive(Debug, Serialize, Deserialize)] -pub struct WeChatCloseOrderRequestV3 { +pub struct WechatCloseOrderRequestV3 { /// 商户号 pub mchid: String, /// 商户订单号 @@ -352,7 +352,7 @@ pub struct WeChatCloseOrderRequestV3 { } #[derive(Debug, Serialize, Deserialize)] -pub struct WeChatQueryOrderRequest { +pub struct WechatQueryOrderRequest { /// 微信支付订单号 二选一 微信的订单号,优先使用 pub transaction_id: Option, /// 商户订单号。商户系统内部的订单号,当没提供transaction_id时需要传这个。 @@ -368,7 +368,7 @@ pub struct WeChatQueryOrderRequest { #[derive(Debug, Serialize, Deserialize)] -pub struct WeChatQueryOrderRequestV3 { +pub struct WechatQueryOrderRequestV3 { /// 商户号 pub mchid: String, /// 微信支付订单号 @@ -381,7 +381,7 @@ pub struct WeChatQueryOrderRequestV3 { #[allow(unused)] -impl WeChatCloseOrderRequest { +impl WechatCloseOrderRequest { pub fn parse_xml(&self) -> String { let msg = format!( "\n\ @@ -415,7 +415,7 @@ impl WeChatCloseOrderRequest { } #[allow(unused)] -impl WeChatQueryOrderRequest { +impl WechatQueryOrderRequest { pub fn parse_xml(&self) -> String { let msg = format!( "\n\ @@ -472,7 +472,7 @@ pub struct RefundAmount { #[derive(Debug, Serialize, Deserialize)] -pub struct WeChatRefundRequestV3 { +pub struct WechatRefundRequestV3 { /// 交易编号 原支付交易对应的微信订单号。 与out_order_no二选一 #[serde(skip_serializing_if = "Option::is_none")] pub transaction_id: Option, @@ -497,7 +497,7 @@ pub struct WeChatRefundRequestV3 { #[derive(Debug, Serialize, Deserialize)] -pub struct WeChatRefundRequest { +pub struct WechatRefundRequest { pub appid: Option, /// 商户号 pub mch_id: String, @@ -522,7 +522,7 @@ pub struct WeChatRefundRequest { /// 撤销订单请求类 #[derive(Debug, Serialize, Deserialize)] -pub struct WeChatOrderReverseRequest { +pub struct WechatOrderReverseRequest { pub appid: Option, /// 商户号 pub mch_id: String, @@ -552,7 +552,7 @@ pub struct WeChatOrderReverseRequest { /// /// #[allow(unused)] -impl WeChatRefundRequest { +impl WechatRefundRequest { pub fn parse_xml(&self) -> String { let msg = format!( "\n\ @@ -605,7 +605,7 @@ impl WeChatRefundRequest { #[allow(unused)] -impl WeChatOrderReverseRequest { +impl WechatOrderReverseRequest { pub fn parse_xml(&self) -> String { let msg = format!( "\n\ @@ -642,7 +642,7 @@ impl WeChatOrderReverseRequest { #[derive(Debug, Serialize, Deserialize)] -pub struct WeChatQueryRefundOrderRequest { +pub struct WechatQueryRefundOrderRequest { pub appid: Option, /// 商户号 pub mch_id: String, @@ -664,7 +664,7 @@ pub struct WeChatQueryRefundOrderRequest { #[derive(Debug, Serialize, Deserialize)] -pub struct WeChatQueryRefundOrderRequestV3 { +pub struct WechatQueryRefundOrderRequestV3 { /// 退款订单号 /// 商户系统内部的退款单号,商户系统内部唯一,只能是数字、大小写字母_-|*@ ,同一退款单号多次请求只退一笔。 #[serde(skip_serializing_if = "Option::is_none")] @@ -672,7 +672,7 @@ pub struct WeChatQueryRefundOrderRequestV3 { } #[allow(unused)] -impl WeChatQueryRefundOrderRequest { +impl WechatQueryRefundOrderRequest { pub fn parse_xml(&self) -> String { let msg = format!( "\n\ diff --git a/src/wechat/pay/response/mod.rs b/src/wechat/pay/response/mod.rs index a25e705..541ac3a 100644 --- a/src/wechat/pay/response/mod.rs +++ b/src/wechat/pay/response/mod.rs @@ -3,7 +3,7 @@ use serde_json::{Value}; use crate::{Amount, errors::LabraError, GoodsDetail, LabradorResult, Payer, RefundAmount, SceneInfo, TradeType}; use crate::util::{get_nonce_str, get_timestamp, xmlutil}; -use crate::wechat::cryptos::{EncryptV3, WeChatCrypto, WeChatCryptoV3}; +use crate::wechat::cryptos::{EncryptV3, WechatCrypto, WechatCryptoV3}; //---------------------------------------------------------------------------------------------------------------------------- @@ -12,7 +12,7 @@ use crate::wechat::cryptos::{EncryptV3, WeChatCrypto, WeChatCryptoV3}; #[derive(Debug, Serialize, Deserialize)] -pub struct WeChatPayResponse { +pub struct WechatPayResponse { pub appid: Option, /// 交易类型 pub trade_type: String, @@ -54,7 +54,7 @@ pub struct PlatformCertificateResponse { } #[derive(Debug, Serialize, Deserialize)] -pub struct WeChatPayResponseV3 { +pub struct WechatPayResponseV3 { pub prepay_id: Option, /// 支付跳转链接(H5支付 会返回) pub h5_url: Option, @@ -62,7 +62,7 @@ pub struct WeChatPayResponseV3 { pub code_url: Option, } -impl WeChatPayResponseV3 { +impl WechatPayResponseV3 { pub fn get_pay_info(&self, trade_type: TradeType, appid: Option, mchid: String, private_key: Option) -> LabradorResult { let timpstamp = get_timestamp() / 1000; let nonce_str = get_nonce_str(); @@ -82,7 +82,7 @@ impl WeChatPayResponseV3 { sign_type: "RSA".to_string(), //签名类型,默认为RSA,仅支持RSA。 pay_sign: String::default() }; - result.pay_sign = WeChatCryptoV3::sign(&result.get_sign_str(), &private_key)?; + result.pay_sign = WechatCryptoV3::sign(&result.get_sign_str(), &private_key)?; Ok(serde_json::to_value(result)?) } TradeType::Native => { @@ -98,7 +98,7 @@ impl WeChatPayResponseV3 { prepay_id: self.prepay_id.to_owned().unwrap_or_default(), sign: "".to_string() }; - result.sign = WeChatCryptoV3::sign(&result.get_sign_str(), &private_key)?; + result.sign = WechatCryptoV3::sign(&result.get_sign_str(), &private_key)?; Ok(serde_json::to_value(result)?) } _ => Err(LabraError::RequestError("不支持的支付类型".to_string())) @@ -144,7 +144,7 @@ impl AppResult { #[allow(unused)] -impl WeChatPayResponse { +impl WechatPayResponse { pub(crate) fn parse_xml(xml: String) -> LabradorResult { let package = xmlutil::parse(xml.to_owned()); @@ -165,7 +165,7 @@ impl WeChatPayResponse { let mweb_url = xmlutil::evaluate(&doc, "//xml/mweb_url/text()").string(); let transaction_id = xmlutil::evaluate(&doc, "//xml/transaction_id/text()").string(); let sign = xmlutil::evaluate(&doc, "//xml/sign/text()").string(); - Ok(WeChatPayResponse { + Ok(WechatPayResponse { appid: appid.into(), trade_type, mch_id, @@ -191,7 +191,7 @@ impl WeChatPayResponse { #[derive(Debug, Serialize, Deserialize)] -pub struct WeChatCloseOrderResponse { +pub struct WechatCloseOrderResponse { pub appid: Option, /// 商户号 pub mch_id: String, @@ -223,7 +223,7 @@ pub struct WeChatCloseOrderResponse { /// /// /// -impl WeChatCloseOrderResponse { +impl WechatCloseOrderResponse { pub fn parse_xml(xml: String) -> LabradorResult { let package = xmlutil::parse(xml.to_owned()); @@ -238,7 +238,7 @@ impl WeChatCloseOrderResponse { let sign = xmlutil::evaluate(&doc, "//xml/sign/text()").string(); let err_code = xmlutil::evaluate(&doc, "//xml/err_code/text()").string(); let err_code_des = xmlutil::evaluate(&doc, "//xml/err_code_des/text()").string(); - Ok(WeChatCloseOrderResponse { + Ok(WechatCloseOrderResponse { appid: appid.into(), nonce_str: nonce_str.into(), mch_id, @@ -304,7 +304,7 @@ pub struct RefundPromotionDetail { #[derive(Debug, Serialize, Deserialize)] -pub struct WeChatQueryOrderResponseV3 { +pub struct WechatQueryOrderResponseV3 { pub appid: String, /// 商户号 pub mch_id: String, @@ -355,7 +355,7 @@ pub struct WeChatQueryOrderResponseV3 { #[derive(Debug, Serialize, Deserialize)] -pub struct WeChatQueryOrderResponse { +pub struct WechatQueryOrderResponse { pub appid: Option, /// 商户号 pub mch_id: String, @@ -419,7 +419,7 @@ pub struct WeChatQueryOrderResponse { /// /// 查询订单返回 /// -impl WeChatQueryOrderResponse { +impl WechatQueryOrderResponse { pub fn parse_xml(xml: String) -> LabradorResult { let package = xmlutil::parse(xml.to_owned()); let doc = package.as_document(); @@ -454,7 +454,7 @@ impl WeChatQueryOrderResponse { let coupon_count = xmlutil::evaluate(&doc, "//xml/coupon_count/text()").string(); let cash_fee = xmlutil::evaluate(&doc, "//xml/cash_fee/text()").string(); let cash_fee_type = xmlutil::evaluate(&doc, "//xml/cash_fee_type/text()").string(); - Ok(WeChatQueryOrderResponse { + Ok(WechatQueryOrderResponse { appid: appid.into(), nonce_str: nonce_str.into(), mch_id, @@ -499,7 +499,7 @@ impl WeChatQueryOrderResponse { #[derive(Debug, Serialize, Deserialize)] -pub struct WeChatRefundResponseV3 { +pub struct WechatRefundResponseV3 { /// 退款编号 pub refund_id: String, /// 商户订单编号 @@ -552,7 +552,7 @@ pub struct WeChatRefundResponseV3 { #[derive(Debug, Serialize, Deserialize)] -pub struct WeChatRefundResponse { +pub struct WechatRefundResponse { pub appid: Option, /// 商户号 pub mch_id: String, @@ -586,7 +586,7 @@ pub struct WeChatRefundResponse { #[derive(Debug, Serialize, Deserialize)] -pub struct WeChatOrderReverseResponse { +pub struct WechatOrderReverseResponse { pub appid: Option, /// 商户号 pub mch_id: String, @@ -607,7 +607,7 @@ pub struct WeChatOrderReverseResponse { } -impl WeChatOrderReverseResponse { +impl WechatOrderReverseResponse { pub fn parse_xml(xml: String) -> LabradorResult { let package = xmlutil::parse(xml.to_owned()); @@ -624,7 +624,7 @@ impl WeChatOrderReverseResponse { let err_code = xmlutil::evaluate(&doc, "//xml/err_code/text()").string(); let err_code_des = xmlutil::evaluate(&doc, "//xml/err_code_des/text()").string(); if result_code.eq("SUCCESS") { - Ok(WeChatOrderReverseResponse { + Ok(WechatOrderReverseResponse { appid: appid.into(), nonce_str: nonce_str.into(), mch_id, @@ -664,7 +664,7 @@ impl WeChatOrderReverseResponse { /// 1 /// /// -impl WeChatRefundResponse { +impl WechatRefundResponse { pub fn parse_xml(xml: String) -> LabradorResult { let package = xmlutil::parse(xml.to_owned()); let doc = package.as_document(); @@ -685,7 +685,7 @@ impl WeChatRefundResponse { let err_code = xmlutil::evaluate(&doc, "//xml/err_code/text()").string(); let err_code_des = xmlutil::evaluate(&doc, "//xml/err_code_des/text()").string(); if result_code.eq("SUCCESS") { - Ok(WeChatRefundResponse { + Ok(WechatRefundResponse { appid: appid.into(), nonce_str: nonce_str.into(), mch_id, @@ -718,7 +718,7 @@ impl WeChatRefundResponse { #[derive(Debug, Serialize, Deserialize)] -pub struct WeChatQueryRefundResponse { +pub struct WechatQueryRefundResponse { pub appid: Option, /// 商户号 pub mch_id: String, @@ -755,7 +755,7 @@ pub struct WeChatQueryRefundResponse { } -impl WeChatQueryRefundResponse { +impl WechatQueryRefundResponse { pub fn parse_xml(xml: String) -> LabradorResult { let package = xmlutil::parse(xml.to_owned()); let doc = package.as_document(); @@ -777,7 +777,7 @@ impl WeChatQueryRefundResponse { let fee_type = xmlutil::evaluate(&doc, "//xml/fee_type/text()").string(); let cash_fee = xmlutil::evaluate(&doc, "//xml/cash_fee/text()").string(); if result_code.eq("SUCCESS") { - Ok(WeChatQueryRefundResponse { + Ok(WechatQueryRefundResponse { appid: appid.into(), nonce_str: nonce_str.into(), mch_id, @@ -815,7 +815,7 @@ impl WeChatQueryRefundResponse { #[derive(Debug, Serialize, Deserialize)] -pub struct WeChatQueryRefundResponseV3 { +pub struct WechatQueryRefundResponseV3 { /// 微信支付退款号 pub refund_id: String, /// 微信交易编号 @@ -873,7 +873,7 @@ pub struct WeChatQueryRefundResponseV3 { #[derive(Debug, Serialize, Deserialize, Clone)] -pub struct WeChatPayNotifyResponse { +pub struct WechatPayNotifyResponse { pub appid: Option, /// 交易类型 pub trade_type: String, @@ -924,14 +924,14 @@ pub struct WeChatPayNotifyResponse { #[derive(Debug, Serialize, Deserialize, Clone)] -pub struct WeChatPayNotifyResponseV3 { +pub struct WechatPayNotifyResponseV3 { /// 源数据 pub raw_data: Option, /// 解密后的数据 pub result: Option, } -impl WeChatPayNotifyResponseV3 { +impl WechatPayNotifyResponseV3 { pub fn decrypt_result(&self) -> DecryptNotifyResult { if let Some(res) = self.result.to_owned() { res @@ -956,14 +956,14 @@ impl WeChatPayNotifyResponseV3 { #[derive(Debug, Serialize, Deserialize, Clone)] -pub struct WeChatRefundNotifyResponseV3 { +pub struct WechatRefundNotifyResponseV3 { /// 源数据 pub raw_data: Option, /// 解密后的数据 pub result: Option, } -impl WeChatRefundNotifyResponseV3 { +impl WechatRefundNotifyResponseV3 { pub fn decrypt_result(&self) -> DecryptRefundNotifyResult { if let Some(res) = self.result.to_owned() { res @@ -1162,7 +1162,7 @@ pub struct OriginNotifyResponse { } #[allow(unused)] -impl WeChatPayNotifyResponse { +impl WechatPayNotifyResponse { pub fn parse_xml(xml: String) -> LabradorResult { let package = xmlutil::parse(xml.to_owned()); let doc = package.as_document(); @@ -1191,7 +1191,7 @@ impl WeChatPayNotifyResponse { let attach = xmlutil::evaluate(&doc, "//xml/attach/text()").string(); let err_code = xmlutil::evaluate(&doc, "//xml/err_code/text()").string(); let err_code_des = xmlutil::evaluate(&doc, "//xml/err_code_des/text()").string(); - Ok(WeChatPayNotifyResponse { + Ok(WechatPayNotifyResponse { appid: appid.into(), trade_type, bank_type: bank_type.into(), @@ -1237,7 +1237,7 @@ impl WeChatPayNotifyResponse { /// /// #[derive(Debug, Serialize, Deserialize)] -pub struct WeChatRefundNotifyResponse { +pub struct WechatRefundNotifyResponse { pub appid: Option, /// 加密字符串 pub nonce_str: Option, @@ -1271,7 +1271,7 @@ pub struct WeChatRefundNotifyResponse { /// /// #[derive(Debug, Serialize, Deserialize,Clone)] -pub struct WeChatDecryptRefundNotifyResponse { +pub struct WechatDecryptRefundNotifyResponse { /// 退款编号 pub out_refund_no: String, /// 退款商户订单号 @@ -1301,8 +1301,8 @@ pub struct WeChatDecryptRefundNotifyResponse { } #[allow(unused)] -impl WeChatRefundNotifyResponse { - pub fn parse_xml(xml: String, appkey: &str) -> LabradorResult { +impl WechatRefundNotifyResponse { + pub fn parse_xml(xml: String, appkey: &str) -> LabradorResult { let package = xmlutil::parse(xml.to_owned()); let doc = package.as_document(); let return_code = xmlutil::evaluate(&doc, "//xml/return_code/text()").string(); @@ -1312,7 +1312,7 @@ impl WeChatRefundNotifyResponse { let _result_code = xmlutil::evaluate(&doc, "//xml/result_code/text()").string(); let _return_msg = xmlutil::evaluate(&doc, "//xml/return_msg/text()").string(); let req_info = xmlutil::evaluate(&doc, "//xml/req_info/text()").string(); - let _refund_msg = WeChatCrypto::decrypt_data_refund(appkey,req_info.as_str())?; + let _refund_msg = WechatCrypto::decrypt_data_refund(appkey,req_info.as_str())?; let refund_package = xmlutil::parse(_refund_msg.to_owned()); let refund_doc = refund_package.as_document(); let out_refund_no = xmlutil::evaluate(&refund_doc, "//root/out_refund_no/text()").string(); @@ -1328,7 +1328,7 @@ impl WeChatRefundNotifyResponse { let success_time = xmlutil::evaluate(&refund_doc, "//root/success_time/text()").string(); let total_fee = xmlutil::evaluate(&refund_doc, "//root/total_fee/text()").string(); let transaction_id = xmlutil::evaluate(&refund_doc, "//root/transaction_id/text()").string(); - Ok(WeChatDecryptRefundNotifyResponse { + Ok(WechatDecryptRefundNotifyResponse { out_refund_no, out_trade_no, refund_account,