houseme/wxpay
Composer 安装命令:
composer require houseme/wxpay
包简介
Wechat pay library
README 文档
README
微信支付 API v3 的 Rust 实现 SDK,提供类型安全、高性能的微信支付接口封装。
✨ 特性
- 🔐 完整的签名与验签 - SHA256-RSA 签名算法,自动请求签名和应答验签
- 🔒 敏感信息加解密 - RSA-OAEP 加密/解密,AES-256-GCM 回调通知解密
- 📜 证书管理 - 自动定时下载和更新微信支付平台证书
- 💳 全支付方式支持 - JSAPI、Native、H5、APP 支付
- 🔄 回调通知处理 - 完整的支付回调验证和解密
- 📊 丰富的业务 API - 支付、退款、分账、转账等完整业务支持
- ⚡ 异步优先 - 基于 Tokio 的全异步实现,高性能并发
- 🛡️ 类型安全 - Rust 强类型系统保障,编译期错误检查
📦 安装
在 Cargo.toml 中添加:
[dependencies] wxpay-rs = "2.0.1" tokio = { version = "1", features = ["full"] } serde = { version = "1", features = ["derive"] } serde_json = "1"
🧭 迁移与 Webhook 示例入口
如果你是从 wechatpay-apiv3/wechatpay-go 迁移过来,或者准备直接接入支付/退款回调,可以先看这几份材料:
- examples/webhook_axum.rs:
Axum支付/退款回调可编译骨架 - examples/webhook_actix.rs:
Actix-Web支付/退款回调可编译骨架 - .env.example:本地联调所需环境变量模板
常见启动方式:
cp .env.example .env # Axum cargo run --example webhook_axum # Actix-Web cargo run --example webhook_actix
Go 风格兼容入口
为了让 wechatpay-go 迁移更平滑,当前还额外提供了一层薄兼容入口:
client.refunddomestic()/client.transferbatch()/client.profitsharing()client.query().query_order_by_out_trade_no(...)client.query().query_order_by_id(...)client.refunddomestic().query_by_out_refund_no(...)client.transferbatch().initiate_batch_transfer(...)client.transferbatch().get_transfer_batch_by_out_batch_no(...)client.profitsharing().create_order(...)client.profitsharing().query_order(...)client.profitsharing().finish_order(...)
如果你希望在第一轮迁移中尽量少改调用名,可以优先使用这层兼容入口,后续再逐步回收成 wxpay-rs 原生风格 API。
说明:
- 仓库内不再跟踪迁移草稿型文档,发布版以
README、crate API 和examples/为准。 - 如果你需要本地迁移笔记,建议放在已忽略的
local-docs/目录中自行维护。
🚀 快速开始
初始化客户端
use wxpay_rs::{ client::WxPayClient, config::WxPayConfig, error::WxPayResult, }; use std::path::PathBuf; #[tokio::main] async fn main() -> WxPayResult<()> { // 配置微信支付参数 let config = WxPayConfig::new( "wx888888888", // AppID "1900000109", // 商户号 "22222222", // 商户 API 密钥 PathBuf::from("/path/to/apiclient_cert.pem"), // 商户证书 PathBuf::from("/path/to/apiclient_key.pem"), // 商户私钥 ) .api_v3_key("your_api_v3_key") // APIv3 密钥 .timeout(6000) // 超时时间(毫秒) .build(); // 创建客户端 let client = WxPayClient::new(config).await?; println!("微信支付客户端初始化成功!"); Ok(()) }
JSAPI 支付示例
use wxpay_rs::services::payments::jsapi::*; use wxpay_rs::client::WxPayClient; async fn create_jsapi_order(client: &WxPayClient) -> WxPayResult<()> { // 创建预支付请求 let request = PrepayRequest { appid: "wx888888888".to_string(), mchid: "1900000109".to_string(), description: "测试商品".to_string(), out_trade_no: "20240101000001".to_string(), notify_url: "https://www.example.com/wxpay/notify".to_string(), amount: Some(Amount { total: 100, currency: Some("CNY".to_string()), }), payer: Some(Payer { openid: "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o".to_string(), }), ..Default::default() }; // 调用预支付接口 let result = client.jsapi().prepay(request).await?; println!("预支付结果:{:?}", result); // 生成前端支付参数 let pay_params = client.jsapi().build_pay_params(&result.prepay_id)?; println!("前端支付参数:{:?}", pay_params); Ok(()) }
Native 支付示例
use wxpay_rs::services::payments::native::*; async fn create_native_order(client: &WxPayClient) -> WxPayResult<()> { let request = PrepayRequest { appid: "wx888888888".to_string(), mchid: "1900000109".to_string(), description: "测试商品".to_string(), out_trade_no: "20240101000002".to_string(), notify_url: "https://www.example.com/wxpay/notify".to_string(), amount: Some(Amount { total: 100, currency: Some("CNY".to_string()), }), ..Default::default() }; let result = client.native().prepay(request).await?; println!("二维码链接:{}", result.code_url); Ok(()) }
查询订单
use wxpay_rs::services::payments::query::*; async fn query_order(client: &WxPayClient) -> WxPayResult<()> { // 通过商户订单号查询 let result = client.query().by_out_trade_no("20240101000001").await?; println!("订单状态:{:?}", result.trade_state); // 通过微信支付订单号查询 let result = client.query().by_transaction_id("4200001234202401010000001").await?; println!("订单详情:{:?}", result); Ok(()) }
申请退款
use wxpay_rs::services::refund::*; async fn create_refund(client: &WxPayClient) -> WxPayResult<()> { let request = RefundRequest { out_trade_no: "20240101000001".to_string(), out_refund_no: "R20240101000001".to_string(), reason: Some("商品质量问题".to_string()), amount: RefundAmount { refund: 100, total: 100, currency: "CNY".to_string(), }, ..Default::default() }; let result = client.refund().create(request).await?; println!("退款单号:{}", result.refund_id); Ok(()) }
处理回调通知
use wxpay_rs::notify::{NotifyHandler, NotifyPayload}; use wxpay_rs::services::payments::Transaction; async fn handle_payment_notify( handler: &NotifyHandler, body: &str, headers: &[(String, String)], ) -> WxPayResult<()> { // 验证签名并解密通知 let transaction: Transaction = handler.parse_notify_request(body, headers).await?; // 处理支付结果 match transaction.trade_state { TradeState::Success => { println!("支付成功!订单号:{}", transaction.out_trade_no); // 业务处理... } TradeState::Closed => { println!("订单已关闭:{}", transaction.out_trade_no); } _ => { println!("其他状态:{:?}", transaction.trade_state); } } Ok(()) }
📁 项目结构
wxpay-rs/
├── Cargo.toml # 项目配置
├── src/
│ ├── lib.rs # 库入口
│ ├── client.rs # HTTP 客户端实现
│ ├── config.rs # 配置管理
│ ├── error.rs # 错误类型定义
│ ├── auth/ # 认证模块
│ │ ├── mod.rs
│ │ ├── signer.rs # 签名器
│ │ ├── verifier.rs # 验签器
│ │ └── credentials.rs # 凭证管理
│ ├── cipher/ # 加解密模块
│ │ ├── mod.rs
│ │ ├── rsa.rs # RSA-OAEP 加解密
│ │ └── aes.rs # AES-256-GCM 加解密
│ ├── cert/ # 证书管理
│ │ ├── mod.rs
│ │ ├── downloader.rs # 证书下载器
│ │ └── visitor.rs # 证书访问器
│ ├── notify/ # 回调通知处理
│ │ ├── mod.rs
│ │ └── handler.rs # 通知处理器
│ ├── utils/ # 工具函数
│ │ ├── mod.rs
│ │ ├── nonce.rs # 随机数生成
│ │ ├── signature.rs # 签名工具
│ │ └── xml.rs # XML 处理(v2 兼容)
│ └── services/ # 业务服务模块
│ ├── mod.rs
│ ├── certificates.rs # 平台证书服务
│ ├── payments/ # 支付服务
│ │ ├── mod.rs
│ │ ├── jsapi.rs # JSAPI 支付
│ │ ├── native.rs # Native 支付
│ │ ├── h5.rs # H5 支付
│ │ ├── app.rs # APP 支付
│ │ └── query.rs # 订单查询
│ ├── refund.rs # 退款服务
│ ├── profit_sharing.rs # 分账服务
│ ├── transfer.rs # 转账服务
│ └── fileuploader.rs # 文件上传服务
├── examples/ # 示例代码
│ ├── jsapi_payment.rs
│ ├── native_payment.rs
│ ├── refund.rs
│ └── notify.rs
└── tests/ # 集成测试
├── integration_test.rs
└── mock_server.rs
🔧 配置选项
完整配置示例
use wxpay_rs::config::{WxPayConfig, SignType, AuthType}; use std::path::PathBuf; let config = WxPayConfig::builder() .app_id("wx888888888") .mch_id("1900000109") .api_v3_key("your_api_v3_key") .merchant_cert_path(PathBuf::from("/path/to/apiclient_cert.pem")) .merchant_key_path(PathBuf::from("/path/to/apiclient_key.pem")) .sign_type(SignType::Sha256Rsa) // 签名类型 .auth_type(AuthType::Certificate) // 认证类型:证书或公钥 .timeout(6000) // 超时时间 .auto_download_certs(true) // 自动下载证书 .cert_download_interval(3600) // 证书下载间隔(秒) .base_url("https://api.mch.weixin.qq.com") // API 基础 URL .sandbox(false) // 是否使用沙箱环境 .build()?;
使用公钥验签(新入驻商户)
let config = WxPayConfig::builder() .app_id("wx888888888") .mch_id("1900000109") .api_v3_key("your_api_v3_key") .merchant_cert_path(PathBuf::from("/path/to/apiclient_cert.pem")) .merchant_key_path(PathBuf::from("/path/to/apiclient_key.pem")) .auth_type(AuthType::PublicKey { public_key_id: "PUB_KEY_ID_00000000000000".to_string(), public_key_path: PathBuf::from("/path/to/public_key.pem"), }) .build()?;
📚 支持的 API 列表
支付服务
| API | 说明 | 状态 |
|---|---|---|
jsapi().prepay() |
JSAPI 预支付 | ✅ |
native().prepay() |
Native 预支付 | ✅ |
h5().prepay() |
H5 预支付 | ✅ |
app().prepay() |
APP 预支付 | ✅ |
query().by_transaction_id() |
微信支付单号查询 | ✅ |
query().by_out_trade_no() |
商户订单号查询 | ✅ |
query().by_filter() |
复杂条件查询 | ✅ |
query().close() |
关闭订单 | ✅ |
退款服务
| API | 说明 | 状态 |
|---|---|---|
refund().create() |
申请退款 | ✅ |
refund().query() |
查询退款 | ✅ |
分账服务
| API | 说明 | 状态 |
|---|---|---|
profit_sharing().create() |
创建分账 | ✅ |
profit_sharing().query() |
查询分账 | ✅ |
profit_sharing().add_receiver() |
添加分账接收方 | ✅ |
profit_sharing().delete_receiver() |
删除分账接收方 | ✅ |
profit_sharing().finish() |
完成分账 | ✅ |
转账服务
| API | 说明 | 状态 |
|---|---|---|
transfer().create() |
发起转账 | ✅ |
transfer().query() |
查询转账 | ✅ |
证书服务
| API | 说明 | 状态 |
|---|---|---|
certificates().get_certificates() |
获取平台证书 | ✅ |
文件服务
| API | 说明 | 状态 |
|---|---|---|
fileuploader().upload_image() |
上传图片 | ✅ |
fileuploader().upload_video() |
上传视频 | ✅ |
🔐 安全特性
签名算法
- SHA256-RSA - 默认签名算法,推荐使用
- HMAC-SHA256 - 备用签名算法
敏感信息加密
use wxpay_rs::cipher::RsaCipher; // 加密敏感信息 let encrypted = RsaCipher::encrypt_with_certificate( "敏感信息", &platform_certificate, )?; // 解密敏感信息 let decrypted = RsaCipher::decrypt_with_private_key( &encrypted_data, &merchant_private_key, )?;
回调通知验证
use wxpay_rs::notify::NotifyHandler; // 验证通知签名 let is_valid = handler.verify_signature( &signature, ×tamp, &nonce, &body, )?; // 解密通知数据 let decrypted = handler.decrypt_notification( &ciphertext, &nonce, &associated_data, )?;
🧪 测试
运行测试
# 运行所有测试 cargo test # 运行集成测试 cargo test --test integration_test # 运行示例 cargo run --example jsapi_payment
沙箱环境测试
let config = WxPayConfig::builder() .sandbox(true) // ... 其他配置 .build()?;
📖 示例代码
完整的支付流程示例
use wxpay_rs::{ client::WxPayClient, config::WxPayConfig, services::payments::jsapi::*, notify::NotifyHandler, }; use std::path::PathBuf; #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { // 1. 初始化配置 let config = WxPayConfig::new( "wx888888888", "1900000109", "22222222", PathBuf::from("/path/to/cert.pem"), PathBuf::from("/path/to/key.pem"), ) .api_v3_key("your_api_v3_key") .build()?; // 2. 创建客户端 let client = WxPayClient::new(config).await?; // 3. 创建预支付订单 let request = PrepayRequest { appid: "wx888888888".to_string(), mchid: "1900000109".to_string(), description: "测试商品".to_string(), out_trade_no: format!("ORDER_{}", chrono::Utc::now().timestamp()), notify_url: "https://www.example.com/notify".to_string(), amount: Some(Amount { total: 100, currency: Some("CNY".to_string()), }), payer: Some(Payer { openid: "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o".to_string(), }), ..Default::default() }; let prepay_result = client.jsapi().prepay(request).await?; // 4. 生成前端调起支付的参数 let pay_params = client.jsapi().build_pay_params(&prepay_result.prepay_id)?; println!("前端支付参数:{:#?}", pay_params); // 5. 处理支付回调(在回调接口中) // let handler = client.notify_handler(); // let transaction = handler.parse_notify_request(body, headers).await?; Ok(()) }
🤝 贡献
欢迎提交 Issue 和 Pull Request!
开发环境设置
# 克隆仓库 git clone https://github.com/houseme/wxpay.git cd wxpay # 运行测试 cargo test # 运行示例 cargo run --example jsapi_payment
📄 许可证
本项目采用 Apache-2.0 许可证。
🔗 相关链接
⚠️ 注意事项
- 密钥安全:请妥善保管商户私钥和 APIv3 密钥,不要泄露或提交到代码仓库
- 证书更新:建议启用自动证书下载功能,确保证书及时更新
- 错误处理:所有 API 调用都应进行适当的错误处理
- 日志记录:建议开启日志记录,便于问题排查
- 超时设置:根据业务需求合理设置超时时间
📞 支持
如有问题,请通过以下方式联系:
- 提交 GitHub Issue
- 邮件联系:housemecn@gmail.com
统计信息
- 总下载量: 31
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 2
- 点击次数: 0
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: Apache-2.0
- 更新时间: 2017-03-30