wxkefu_rs/
lib.rs

1#![doc = r#"
2wxkefu-rs
3
4A lightweight, extensible Rust crate for WeChat Customer Service (WeCom Kf) APIs.
5
6What this crate does
7- Provides a small HTTP client (`KfClient`) plus basic types for fetching access_token.
8- Supports two credential systems:
9  1) WeCom (Enterprise WeChat, for WeChat Customer Service/Kf)
10     - Use corpid (typically starts with `ww`) and the WeChat Customer Service Secret (corpsecret)
11     - Endpoint: https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ID&corpsecret=SECRET
12  2) Official Account / Mini Program (OA/MP)
13     - Use appid (starts with `wx`) and appsecret
14     - Endpoint: https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
15
16Important
17- If your goal is to call WeChat Customer Service (Kf) APIs (agent management, message send/receive, session routing, etc.), you must use WeCom credentials (corpid + WeCom Kf Secret). OA/MP tokens are NOT accepted by Kf endpoints.
18- Official Kf docs for acquiring access_token (WeCom): https://kf.weixin.qq.com/api/doc/path/93304
19
20Included
21- `Auth` enum for selecting the auth mode (`WeCom` or `OfficialAccount`)
22- `AccessToken` type with `access_token` and `expires_in`
23- `Error` enum for unified error handling (HTTP, WeChat error code, decoding issues, etc.)
24- `KfClient` with a simple `get_access_token(&Auth)` API
25
26Quick start (WeCom/Kf)
27- Get your corpid and the WeChat Customer Service Secret from: WeChat Customer Service Admin Portal → Developer Config.
28- Cache the access_token in your service to avoid hitting rate limits.
29
30Example (WeCom/Kf)
31```ignore
32use wxkefu_rs::{Auth, KfClient};
33
34#[tokio::main]
35async fn main() -> anyhow::Result<()> {
36    // Read from environment or your secure config center.
37    let corpid = std::env::var("WXKF_CORP_ID")?;
38    let corpsecret = std::env::var("WXKF_APP_SECRET")?;
39
40    let client = KfClient::default();
41    let token = client
42        .get_access_token(&Auth::WeCom {
43            corp_id: corpid,
44            corp_secret: corpsecret,
45        })
46        .await?;
47
48    // For demo only. Do NOT print sensitive values in production.
49    println!("access_token: {}, expires_in: {}", token.access_token, token.expires_in);
50
51    // Persist the token in your cache (memory/Redis/DB) and refresh on expiry.
52    Ok(())
53}
54```
55
56Optional (OA/MP) example
57- Only use this if you are working with OA/MP APIs (not Kf).
58```ignore
59use wxkefu_rs::{Auth, KfClient};
60
61#[tokio::main]
62async fn main() -> anyhow::Result<()> {
63    let appid = std::env::var("WX_APPID")?;
64    let appsecret = std::env::var("WX_APPSECRET")?;
65
66    let client = KfClient::default();
67    let token = client
68        .get_access_token(&Auth::OfficialAccount {
69            appid,
70            secret: appsecret,
71        })
72        .await?;
73
74    println!("access_token: {}, expires_in: {}", token.access_token, token.expires_in);
75    Ok(())
76}
77```
78
79Using a .env for local development
80```ignore
81use dotenvy::dotenv;
82use wxkefu_rs::{Auth, KfClient};
83
84#[tokio::main]
85async fn main() -> anyhow::Result<()> {
86    let _ = dotenv();
87
88    let client = KfClient::default();
89
90    // WeCom / Kf
91    if let (Ok(corpid), Ok(corpsecret)) = (std::env::var("WXKF_CORP_ID"), std::env::var("WXKF_APP_SECRET")) {
92        let token = client
93            .get_access_token(&Auth::WeCom {
94                corp_id: corpid,
95                corp_secret: corpsecret,
96            })
97            .await?;
98        println!("wecom access_token: {}, expires_in: {}", token.access_token, token.expires_in);
99    }
100
101    // OA/MP (not needed for Kf)
102    if let (Ok(appid), Ok(appsecret)) = (std::env::var("WX_APPID"), std::env::var("WX_APPSECRET")) {
103        let token = client
104            .get_access_token(&Auth::OfficialAccount {
105                appid,
106                secret: appsecret,
107            })
108            .await?;
109        println!("mp access_token: {}, expires_in: {}", token.access_token, token.expires_in);
110    }
111
112    Ok(())
113}
114```
115
116Best practices and notes (aligned with the official Kf docs)
117- Cache access_token and reuse it until it expires; do not call gettoken too frequently or you will be rate-limited.
118- expires_in is typically 7200 seconds. Repeated fetches during the valid window return the same token; new tokens are returned after expiry.
119- access_token may be invalidated early for operational reasons; always handle invalidation by re-fetching.
120- Reserve enough storage (at least 512 bytes) for the token string.
121- Never print secrets or tokens in logs. Redact sensitive data.
122
123Error handling
124- Non-zero WeChat error codes are mapped to `Error::Wx { code, message }`.
125- If the response body cannot be decoded, you get `Error::UnexpectedTokenResponse` with details to aid debugging.
126- Network/HTTP/decoding issues appear as `Error::Http` or similar.
127
128Roadmap
129- Add Kf account management, message send/receive, and session routing APIs on top of `KfClient`.
130- Provide an optional middleware for in-crate token caching and auto-refresh (currently expected to be implemented in your application layer).
131"#]
132
133pub mod token;
134pub use token::{AccessToken, Auth, Error, KfClient, Result};
135pub mod token_cache;
136pub use token_cache::{Error as TokenCacheError, RedisTokenManager, TokenManager};
137pub mod token_tool;
138pub use token_tool::{get_token, get_token_with, get_token_with_key};
139pub mod kf_sync_tool;
140pub use kf_sync_tool::{KfSyncTool, MsgHandler, SyncOptions};
141
142pub mod account;
143pub mod callback;
144pub mod customer;
145pub mod errors;
146pub mod keygen;
147pub mod media;
148pub mod recall_msg;
149pub mod send_msg;
150pub mod send_msg_on_event;
151pub mod sync_msg;