alipay_sdk_rust/
pay.rs

1//! 支付模块,包括各种支付接口函数。
2#![allow(unused)]
3// use anyhow::Result;
4use std::collections::HashMap;
5use std::hash::BuildHasher;
6
7// use std::io::{Error, ErrorKind};
8
9use std::borrow::{Borrow, BorrowMut};
10use std::sync::Arc;
11
12use crate::error::AliPaySDKError::AliPayError;
13use crate::error::{AliPayResult, AliPaySDKError};
14use crate::sign::{builder, Signer};
15use gostd::builtin::byte;
16use gostd::net::http;
17use gostd::net::url;
18use serde::{Deserialize, Serialize};
19
20use crate::biz::{
21    self, BizContenter, BizObject, TradeAppPayBiz, TradeCancelBiz, TradeCloseBiz, TradeCreateBiz,
22    TradeFastpayRefundQueryBiz, TradeOrderSettleBiz, TradeOrderSettleQueryBiz, TradePagePayBiz,
23    TradePageRefundBiz, TradePayBiz, TradePrecreateBiz, TradeQueryBiz, TradeRefundBiz,
24    TradeRoyaltyRelationBindBiz, TradeRoyaltyRelationUnBindBiz, TradeWapPayBiz,
25};
26use crate::request::{Request, Requester};
27use crate::response::{
28    self, TradeCancelResponse, TradeCloseResponse, TradeCreateResponse,
29    TradeFastpayRefundQueryResponse, TradeOrderSettleQueryResponse, TradeOrderSettleResponse,
30    TradePageRefundResponse, TradePayResponse, TradePrecreateResponse, TradeQueryResponse,
31    TradeRefundResponse, TradeRoyaltyRelationBindResponse, TradeRoyaltyRelationUnBindResponse,
32};
33use crate::util::{self, build_form, json_get};
34pub trait Payer {
35    fn trade_create(&self, biz_content: &TradeCreateBiz) -> AliPayResult<TradeCreateResponse>;
36
37    fn trade_pay(&self, biz_content: &TradePayBiz) -> AliPayResult<TradePayResponse>;
38
39    fn trade_precreate(
40        &self,
41        biz_content: &TradePrecreateBiz,
42    ) -> AliPayResult<TradePrecreateResponse>;
43
44    fn trade_app_pay(&self, biz_content: &TradeAppPayBiz) -> AliPayResult<String>;
45
46    fn trade_wap_pay(&self, biz_content: &TradeWapPayBiz) -> AliPayResult<String>;
47
48    fn trade_page_pay(&self, biz_content: &TradePagePayBiz) -> AliPayResult<String>;
49
50    fn trade_query(&self, biz_content: &TradeQueryBiz) -> AliPayResult<TradeQueryResponse>;
51
52    fn trade_cancel(&self, biz_content: &TradeCancelBiz) -> AliPayResult<TradeCancelResponse>;
53
54    fn trade_refund(&self, biz_content: &TradeRefundBiz) -> AliPayResult<TradeRefundResponse>;
55
56    fn trade_page_refund(
57        &self,
58        biz_content: &TradePageRefundBiz,
59    ) -> AliPayResult<TradePageRefundResponse>;
60    fn trade_fastpay_refund_query(
61        &self,
62        biz_content: &TradeFastpayRefundQueryBiz,
63    ) -> AliPayResult<TradeFastpayRefundQueryResponse>;
64
65    fn trade_order_settle(
66        &self,
67        biz_content: &TradeOrderSettleBiz,
68    ) -> AliPayResult<TradeOrderSettleResponse>;
69
70    fn trade_order_settle_query(
71        &self,
72        biz_content: &TradeOrderSettleQueryBiz,
73    ) -> AliPayResult<TradeOrderSettleQueryResponse>;
74
75    fn trade_royalty_relation_bind(
76        &self,
77        biz_content: &TradeRoyaltyRelationBindBiz,
78    ) -> AliPayResult<TradeRoyaltyRelationBindResponse>;
79
80    fn trade_royalty_relation_unbind(
81        &self,
82        biz_content: &TradeRoyaltyRelationUnBindBiz,
83    ) -> AliPayResult<TradeRoyaltyRelationUnBindResponse>;
84
85    fn trade_close(&self, biz_content: &TradeCloseBiz) -> AliPayResult<TradeCloseResponse>;
86    fn async_verify_sign(&self, raw_body: &[u8]) -> AliPayResult<bool>;
87}
88
89/// 实现Payer接口的各种方法
90///
91/// # Example
92///
93/// ## 初始化client
94///
95/// 避免每次输入大量参数,建议像如下例子,单独写一个函数需要使用的时候调用函数就可以。
96///
97/// ```
98/// use alipay_sdk_rust::biz::{self, BizContenter};
99/// use alipay_sdk_rust::error::AliPayResult;
100/// use alipay_sdk_rust::pay::{PayClient, Payer};
101/// use alipay_sdk_rust::response::TradeCreateResponse;
102/// fn new_pay_client() -> AliPayResult<impl Payer> {
103///     let client = PayClient::builder()
104/// .api_url("https://openapi.alipaydev.com/gateway.do")
105/// .app_id("2021000117650139")
106/// .alipay_root_cert_sn("687b59193f3f462dd5336e5abf83c5d8_02941eef3187dddf3d3b83462e1dfcf6")
107/// .alipay_public_key("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoEz4Tegg1Ytfn2N+jWzDTOBbUNhcpIXz7dV3K8yIsDzinPwYTzzZhlCLYlKbfJGpSDVU0K3I6TnyGfNPEg+R7EC/3ycY1nETh8mxQ4dgrz8irKlLxSRDHVtPg4luncDz2u0hMab9QJdqF8DXD5H+r0Pdt6CSJgKJqLa0awPV3/8cTQZbhZ4ontnRdcwWWcp4/TunkEc891Aa5FmzWp4hgBYcu3BGcazS1HQA4r6wTwRkiqKsCwZeVTag4CiOeqe/vRFTxTMKF4gjRzdhTapUfcBCmXblEA3i8/7OILEyNHceRAxxxIpUjTyoRJ4/2xd0kWbw1gmkLFM0Fzee0eVgoQIDAQAB")
108/// .app_cert_sn("8c68b9753e5b9e0bb7704a981936ecce")
109/// .charset_utf8()
110/// .format_json()
111/// .private_key("MIIEogIBAAKCAQEAog0N+rHllTO+e42Bc5mpvowolWVStyurL3Ou/86uRMN8im7WG1v44h09IaZpw4k6dpYEj89d7aLd7IwnBR5Wg84Ox2LMR/Y/Pzo10hjlvJJOk+igqepSTtB/4UX0cG/9tWceHAWOFuD8uw/SSJegC91a0MmLUBUpd4wWnN1iOSi0442iNvNk79Z6xLKIs4LNJGCNddxcofvpdqq4/5ywxHo24m5zPQf6/ttGk5jQQVrF+y2ckdHKd2h7ZSOYI7nzlzbZqK0UOMDuTvRs696fPa5wSEshE0RQBcn5iCltNTPyLsL3RGUUlsLOPsyT6cFZtUAKJ+J6wEqQxM5TrvNHywIDAQABAoIBAGDTuhGccFipVVzP3ZSsMV+4sZsqsrTd8+hjkCIrZbeSsvyoY2hvmRPKcreDjtiWS4eF9e3T8wTF9yKbT8lgKkORQQVkBDnPalUmO/hwhf0Z0rfQHQfKCiorrO129iqk0AyvM698pj0HbBt9xaE4cBoGxnfQpVxReLiEzRInucP6lhE79v1BwXCwMtRVvFPPIFaLJ02JGIywN+jnkpLwJj8TAu5u3JawlRnsFJgQeTdsHs4G6E11WBeo7OZtKPiKWMcj1nPU0Dnr+6VG89Rx/cxqlMrlTJBhKsLEzcVQwcc3M3UnMOU3Of7Mj1olnUGJ90apVukDFM5OI4Mfqi9etekCgYEA9ViJqVUdzJwqTK/gmbAsRvri9+rmWhfoqBMGeUoHktOGR/hMKJ/LrVa1oBIcVVNdLbI7Ks0kySGCa6qm/4YP2DCihj0GKwnHdPQ94pd3lWvUFZimNKi5V/+sREU0dKSqK3b7F0njtpR6zn+x8KpktO7izL3o8740KpSb7xGb0BUCgYEAqRaLShbDYjhSfvIzWAbuPNpvvtDWUNip1cuJwzTvDthECV5ltkhLGVWVnStch6OeTbK+llLDVw+j/YT1KetQcZ60tw2spn8nq6UvC2IFa2h61zpa8VWeRDfhyEIzoBE8DFAyeWjqYHyHJlh0BzRA2P3ts2LwwwhRa6OHhYzQ0F8CgYAUUvpMab2nNoSWh7dOY/a3Bo+IxA/DBNoEGldd8tD/y8AC9EGy19HykQ1Irldkhhxg7bPTDt1uP/Vi3+cnob5sRVMhVarOI+g++wCpZazFVwJhq5yRHi0EaiymFymKRB3IrfmM61UOyewGcTOXYTYoeuWU2mKS1n3RzS/BtS64JQKBgB2husVAGftzfVmL3l2V0VhOu3iIJpbCcXjrE3hnJWHHmpy9sztvjeGhsvd5Kt0GWm6pXWcAmAUA069RBpnTCCTxOCBAQDppXC1jZEwtYF/DTou7SUazx2mTFXk/yMZLXueVglLuhOxlxlV8+NBuYtLkJSzjsOes5H/lh5Fq7QknAoGAJ/LzTBLPy3terUgqejSxB4pr6PMbNd8wEStHN1RmR2v9Msuto7PUT7OOjQYIJwQLnxQUDr65bB5uR35v+L/rC6XUkzJM18YWvmOhFM8OsIYc4HdDhSmeFpMXdbd6entMJEX0bWrTbS/UdEcqE30kwuNuEFQ07LopGY1gBEe1G8U=")
112/// .public_key("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAog0N+rHllTO+e42Bc5mpvowolWVStyurL3Ou/86uRMN8im7WG1v44h09IaZpw4k6dpYEj89d7aLd7IwnBR5Wg84Ox2LMR/Y/Pzo10hjlvJJOk+igqepSTtB/4UX0cG/9tWceHAWOFuD8uw/SSJegC91a0MmLUBUpd4wWnN1iOSi0442iNvNk79Z6xLKIs4LNJGCNddxcofvpdqq4/5ywxHo24m5zPQf6/ttGk5jQQVrF+y2ckdHKd2h7ZSOYI7nzlzbZqK0UOMDuTvRs696fPa5wSEshE0RQBcn5iCltNTPyLsL3RGUUlsLOPsyT6cFZtUAKJ+J6wEqQxM5TrvNHywIDAQAB")
113/// .sign_type_rsa2()
114/// .version_1_0()
115/// .build()?;
116///     Ok(client)
117/// }
118/// ```
119#[derive(Debug, Default)]
120pub struct PayClient {
121    api_url: String,             // `json:"-"`                    // 接口网关地址
122    private_key: String,         // `json:"-"`                    // rsa私钥单行文本字符串
123    public_key: String,          // `json:"-"`                    // rsa公钥单行文本字符串
124    alipay_public_key: String, // `json:"_"`                    // 证书解析出的支付宝公钥 从alipayCertPublicKey_RSA2.crt 文件中提取
125    app_cert_sn: String, // `json:"app_cert_sn"`          // 应用公钥证书 SN 从appCertPublicKey_2021000117650139.crt 文件提取
126    alipay_root_cert_sn: String, // `json:"alipay_root_cert_sn"`  // 支付宝根证书 SN 从alipayRootCert.crt 文件中提取
127    app_id: String, // `json:"app_id"`               // 是	32	支付宝分配给开发者的应用ID	2014072300007148
128    format: String, // `json:"format,omitempty"`     // 否	40	仅支持JSON	JSON
129    charset: String, // `json:"charset"`              // 是	10	请求使用的编码格式,如utf-8,gbk,gb2312等	utf-8
130    sign_type: String, // `json:"sign_type"`            // 是	10	商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2	RSA2
131    version: String,   // `json:"version"`              // 是	3	调用的接口版本,固定为:1.0	1.0
132    return_url: String, // `json:"return_url,omitempty"` // 否 前台回跳地址 return_url 自动跳转回商户页面
133    notify_url: String, // `json:"notify_url,omitempty"` 支付宝服务器主动通知callback商户服务器里指定的页面http/https路径
134}
135
136impl Payer for PayClient {
137    /// <https://opendocs.alipay.com/apis/api_1/alipay.trade.create>
138    ///
139    /// alipay.trade.create(统一收单交易创建接口)
140    fn trade_create(&self, biz_content: &TradeCreateBiz) -> AliPayResult<TradeCreateResponse> {
141        let body = self.do_alipay(biz_content)?;
142        let res: TradeCreateResponse = serde_json::from_slice(&body)?;
143        if res.response.code != Some("10000".to_string()) {
144            log::debug!("{}", serde_json::to_string(&res)?);
145            return Err(AliPayError(format!(
146                "trade_create failed: {} code:{}",
147                res.response.sub_msg.unwrap().as_str(),
148                res.response.sub_code.unwrap().as_str()
149            )));
150        }
151        Ok(res)
152    }
153
154    /// <https://opendocs.alipay.com/apis/api_1/alipay.trade.pay>
155    ///
156    /// alipay.trade.pay(统一收单交易支付接口)
157    fn trade_pay(&self, biz_content: &TradePayBiz) -> AliPayResult<TradePayResponse> {
158        let body = self.do_alipay(biz_content)?;
159        let res: TradePayResponse = serde_json::from_slice(&body)?;
160        if res.response.code != Some("10000".to_string()) {
161            log::debug!("{}", serde_json::to_string(&res)?);
162            return Err(AliPayError(format!(
163                "trade_pay failed: {} code:{}",
164                res.response.sub_msg.unwrap().as_str(),
165                res.response.sub_code.unwrap().as_str()
166            )));
167        }
168        Ok(res)
169    }
170
171    /// <https://opendocs.alipay.com/apis/api_1/alipay.trade.precreate>
172    ///
173    /// alipay.trade.precreate(统一收单线下交易预创建)当面付-商家生成条码
174    fn trade_precreate(
175        &self,
176        biz_content: &TradePrecreateBiz,
177    ) -> AliPayResult<TradePrecreateResponse> {
178        let body = self.do_alipay(biz_content)?;
179        let res: TradePrecreateResponse = serde_json::from_slice(&body)?;
180        if res.response.code != Some("10000".to_string()) {
181            log::debug!("{}", serde_json::to_string(&res)?);
182            return Err(AliPayError(format!(
183                "trade_precreate failed: {} code:{}",
184                res.response.sub_msg.unwrap().as_str(),
185                res.response.sub_code.unwrap().as_str()
186            )));
187        }
188        Ok(res)
189    }
190
191    /// <https://opendocs.alipay.com/apis/api_1/alipay.trade.app.pay>
192    ///
193    /// alipay.trade.app.pay(app支付接口2.0)后端只生成form数据给前端调用
194    fn trade_app_pay(&self, biz_content: &TradeAppPayBiz) -> AliPayResult<String> {
195        let body = self.do_alipay(biz_content)?;
196        let res = String::from_utf8(body)?;
197        Ok(res)
198    }
199
200    /// <https://opendocs.alipay.com/apis/api_1/alipay.trade.wap.pay>
201    ///
202    /// alipay.trade.wap.pay(手机网站支付接口2.0)后端只生成form数据给前端调用
203    fn trade_wap_pay(&self, biz_content: &TradeWapPayBiz) -> AliPayResult<String> {
204        let body = self.do_alipay(biz_content)?;
205        let res = String::from_utf8(body)?;
206        Ok(res)
207    }
208
209    /// <https://opendocs.alipay.com/apis/api_1/alipay.trade.page.pay>
210    ///
211    /// alipay.trade.page.pay(统一收单下单并支付页面接口)后端只生成form数据给前端调用
212    fn trade_page_pay(&self, biz_content: &TradePagePayBiz) -> AliPayResult<String> {
213        let body = self.do_alipay(biz_content)?;
214        let res = String::from_utf8(body)?;
215        Ok(res)
216    }
217
218    /// <https://opendocs.alipay.com/apis/api_1/alipay.trade.query>
219    ///
220    /// alipay.trade.query(统一收单线下交易查询)
221    fn trade_query(&self, biz_content: &TradeQueryBiz) -> AliPayResult<TradeQueryResponse> {
222        let body = self.do_alipay(biz_content)?;
223        let res: TradeQueryResponse = serde_json::from_slice(&body)?;
224        if res.response.code != Some("10000".to_string()) {
225            log::debug!("{}", serde_json::to_string(&res)?);
226            return Err(AliPayError(format!(
227                "trade_query failed: {} code:{}",
228                res.response.sub_msg.unwrap().as_str(),
229                res.response.sub_code.unwrap().as_str()
230            )));
231        }
232        Ok(res)
233    }
234
235    ///  <https://opendocs.alipay.com/apis/api_1/alipay.trade.cancel>
236    ///
237    /// alipay.trade.cancel(统一收单交易撤销接口)
238    fn trade_cancel(&self, biz_content: &TradeCancelBiz) -> AliPayResult<TradeCancelResponse> {
239        let body = self.do_alipay(biz_content)?;
240        let res: TradeCancelResponse = serde_json::from_slice(&body)?;
241        if res.response.code != Some("10000".to_string()) {
242            log::debug!("{}", serde_json::to_string(&res)?);
243            return Err(AliPayError(format!(
244                "trade_cancel failed: {} code:{}",
245                res.response.sub_msg.unwrap().as_str(),
246                res.response.sub_code.unwrap().as_str()
247            )));
248        }
249        Ok(res)
250    }
251
252    /// <https://opendocs.alipay.com/apis/api_1/alipay.trade.refund>
253    ///
254    /// alipay.trade.refund(统一收单交易退款接口)
255    fn trade_refund(&self, biz_content: &TradeRefundBiz) -> AliPayResult<TradeRefundResponse> {
256        let body = self.do_alipay(biz_content)?;
257        let res: TradeRefundResponse = serde_json::from_slice(&body)?;
258        if res.response.code != Some("10000".to_string()) {
259            log::debug!("{}", serde_json::to_string(&res)?);
260            return Err(AliPayError(format!(
261                "trade_refund failed: {} code:{}",
262                res.response.sub_msg.unwrap().as_str(),
263                res.response.sub_code.unwrap().as_str()
264            )));
265        }
266        Ok(res)
267    }
268
269    /// <https://opendocs.alipay.com/apis/api_1/alipay.trade.page.refund>
270    ///
271    /// alipay.trade.page.refund(统一收单退款页面接口)
272    fn trade_page_refund(
273        &self,
274        biz_content: &TradePageRefundBiz,
275    ) -> AliPayResult<TradePageRefundResponse> {
276        let body = self.do_alipay(biz_content)?;
277        let res: TradePageRefundResponse = serde_json::from_slice(&body)?;
278        if res.response.code != Some("10000".to_string()) {
279            log::debug!("{}", serde_json::to_string(&res)?);
280            return Err(AliPayError(format!(
281                "trade_page_refund failed: {} code:{}",
282                res.response.sub_msg.unwrap().as_str(),
283                res.response.sub_code.unwrap().as_str()
284            )));
285        }
286        Ok(res)
287    }
288    /// <https://opendocs.alipay.com/apis/api_1/alipay.trade.fastpay.refund.query>
289    ///
290    /// alipay.trade.fastpay.refund.query(统一收单交易退款查询)
291    fn trade_fastpay_refund_query(
292        &self,
293        biz_content: &TradeFastpayRefundQueryBiz,
294    ) -> AliPayResult<TradeFastpayRefundQueryResponse> {
295        let body = self.do_alipay(biz_content)?;
296        let res: TradeFastpayRefundQueryResponse = serde_json::from_slice(&body)?;
297        if res.response.code != Some("10000".to_string()) {
298            log::debug!("{}", serde_json::to_string(&res)?);
299            return Err(AliPayError(format!(
300                "trade_fastpay_refund_query failed: {} code:{}",
301                res.response.sub_msg.unwrap().as_str(),
302                res.response.sub_code.unwrap().as_str()
303            )));
304        }
305        Ok(res)
306    }
307
308    /// <https://opendocs.alipay.com/open/c3b24498_alipay.trade.order.settle?pathHash=8790ac59&scene=common>
309    ///
310    /// alipay.trade.order.settle(统一收单交易结算接口) 用于在卖家交易成功之后,基于交易订单,进行卖家与第三方(如供应商或平台商)的资金再分配。一般用于第三方从卖家抽佣场景。
311    fn trade_order_settle(
312        &self,
313        biz_content: &TradeOrderSettleBiz,
314    ) -> AliPayResult<TradeOrderSettleResponse> {
315        let body = self.do_alipay(biz_content)?;
316        let res: TradeOrderSettleResponse = serde_json::from_slice(&body)?;
317        if res.response.code != Some("10000".to_string()) {
318            log::debug!("{}", serde_json::to_string(&res)?);
319            return Err(AliPayError(format!(
320                "trade_order_settle failed: {} code:{}",
321                res.response.sub_msg.unwrap().as_str(),
322                res.response.sub_code.unwrap().as_str()
323            )));
324        }
325        Ok(res)
326    }
327
328    /// <https://opendocs.alipay.com/open/9ef980b7_alipay.trade.order.settle.query?pathHash=131bacfc&scene=common>
329    ///
330    /// alipay.trade.order.settle.query(交易分账查询接口) 根据分账请求号查询交易分账结果
331    fn trade_order_settle_query(
332        &self,
333        biz_content: &TradeOrderSettleQueryBiz,
334    ) -> AliPayResult<TradeOrderSettleQueryResponse> {
335        let body = self.do_alipay(biz_content)?;
336        let res: TradeOrderSettleQueryResponse = serde_json::from_slice(&body)?;
337        if res.response.code != Some("10000".to_string()) {
338            log::debug!("{}", serde_json::to_string(&res)?);
339            return Err(AliPayError(format!(
340                "trade_order_settle_query failed: {} code:{}",
341                res.response.msg.unwrap().as_str(),
342                res.response.code.unwrap().as_str()
343            )));
344        }
345        Ok(res)
346    }
347
348    /// <https://opendocs.alipay.com/open/c21931d6_alipay.trade.royalty.relation.bind?pathHash=08a24dae&scene=common>
349    ///
350    /// alipay.trade.royalty.relation.bind(分账关系绑定)当商户签约分账产品后,授权ISV帮其进行分账关系的维护。本接口用于商户与分账方的关系绑定。
351    fn trade_royalty_relation_bind(
352        &self,
353        biz_content: &TradeRoyaltyRelationBindBiz,
354    ) -> AliPayResult<TradeRoyaltyRelationBindResponse> {
355        let body = self.do_alipay(biz_content)?;
356        let res: TradeRoyaltyRelationBindResponse = serde_json::from_slice(&body)?;
357        if res.response.code != Some("10000".to_string()) {
358            log::debug!("{}", serde_json::to_string(&res)?);
359            return Err(AliPayError(format!(
360                "trade_royalty_relation_bind failed: {} code:{}",
361                res.response.msg.unwrap().as_str(),
362                res.response.code.unwrap().as_str()
363            )));
364        }
365        Ok(res)
366    }
367
368    /// <https://opendocs.alipay.com/open/3613f4e1_alipay.trade.royalty.relation.unbind?pathHash=2cbd3197&scene=common>
369    ///
370    /// alipay.trade.royalty.relation.unbind(分账关系解绑)当商户签约分账产品后,授权ISV帮其进行分账关系的维护。本接口用于商户与分账方的关系解绑。
371    fn trade_royalty_relation_unbind(
372        &self,
373        biz_content: &TradeRoyaltyRelationUnBindBiz,
374    ) -> AliPayResult<TradeRoyaltyRelationUnBindResponse> {
375        let body = self.do_alipay(biz_content)?;
376        let res: TradeRoyaltyRelationUnBindResponse = serde_json::from_slice(&body)?;
377        if res.response.code != Some("10000".to_string()) {
378            log::debug!("{}", serde_json::to_string(&res)?);
379            return Err(AliPayError(format!(
380                "trade_royalty_relation_unbind failed: {} code:{}",
381                res.response.msg.unwrap().as_str(),
382                res.response.code.unwrap().as_str()
383            )));
384        }
385        Ok(res)
386    }
387
388    /// <https://opendocs.alipay.com/apis/api_1/alipay.trade.close>
389    ///
390    /// alipay.trade.close(统一收单交易关闭接口)
391    fn trade_close(&self, biz_content: &TradeCloseBiz) -> AliPayResult<TradeCloseResponse> {
392        let body = self.do_alipay(biz_content)?;
393        let res: TradeCloseResponse = serde_json::from_slice(&body)?;
394        if res.response.code != Some("10000".to_string()) {
395            log::debug!("{}", serde_json::to_string(&res)?);
396            return Err(AliPayError(format!(
397                "trade_close failed: {} code:{}",
398                res.response.sub_msg.unwrap().as_str(),
399                res.response.sub_code.unwrap().as_str(),
400            )));
401        }
402        Ok(res)
403    }
404    /// 自行实现签名文档 https://opendocs.alipay.com/common/02mse7?pathHash=096e611e
405    ///
406    /// 支付宝异步回调信息,用支付宝公钥验证签名,确认消息是支付宝服务器发出的,必须设置过异步通知的回调url连接和notify_url参数
407    fn async_verify_sign(&self, raw_body: &[u8]) -> AliPayResult<bool> {
408        let (source, sign, sign_type) = util::get_async_callback_msg_source(raw_body)?;
409        let mut singer = builder().set_sign_type(&sign_type).build();
410        singer.set_public_key(&self.alipay_public_key())?;
411        return Ok(singer.verify(&source, &sign)?);
412    }
413}
414
415/// 构造器
416#[derive(Debug, Default, Clone, Copy)]
417pub struct PayClientBuilder<'a> {
418    api_url: Option<&'a str>, // `json:"-"`                    // 接口网关地址
419    private_key: Option<&'a str>, // `json:"-"`                    // rsa私钥单行文本字符串
420    public_key: Option<&'a str>, // `json:"-"`                    // rsa公钥单行文本字符串
421    alipay_public_key: Option<&'a str>, // `json:"_"`                    // 证书解析出的支付宝公钥
422    app_cert_sn: Option<&'a str>, // `json:"app_cert_sn"`          // 应用公钥证书 SN
423    alipay_root_cert_sn: Option<&'a str>, // `json:"alipay_root_cert_sn"`  // 支付宝根证书 SN
424    app_id: Option<&'a str>, // `json:"app_id"`               // 是	32	支付宝分配给开发者的应用ID	2014072300007148
425    format: Option<&'a str>, // `json:"format,omitempty"`     // 否	40	仅支持JSON	JSON
426    charset: Option<&'a str>, // `json:"charset"`              // 是	10	请求使用的编码格式,如utf-8,gbk,gb2312等	utf-8
427    sign_type: Option<&'a str>, // `json:"sign_type"`            // 是	10	商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2	RSA2
428    version: Option<&'a str>, // `json:"version"`              // 是	3	调用的接口版本,固定为:1.0	1.0
429    return_url: Option<&'a str>, // `json:"return_url,omitempty"` // 否 前台回跳地址 return_url 自动跳转回商户页面
430    notify_url: Option<&'a str>, // `json:"notify_url,omitempty"`  支付宝服务器主动通知callback商户服务器里指定的页面http/https路径
431}
432
433impl PayClient {
434    pub fn builder<'a>() -> PayClientBuilder<'a> {
435        PayClientBuilder::default()
436    }
437
438    pub fn api_url(&self) -> String {
439        self.api_url.to_owned()
440    }
441
442    pub fn private_key(&self) -> String {
443        self.private_key.to_owned()
444    }
445
446    pub fn public_key(&self) -> String {
447        self.public_key.to_owned()
448    }
449
450    pub fn app_cert_sn(&self) -> String {
451        self.app_cert_sn.to_owned()
452    }
453
454    pub fn alipay_root_cert_sn(&self) -> String {
455        self.alipay_root_cert_sn.to_owned()
456    }
457
458    pub fn alipay_public_key(&self) -> String {
459        self.alipay_public_key.to_owned()
460    }
461
462    pub fn app_id(&self) -> String {
463        self.app_id.to_owned()
464    }
465
466    pub fn format(&self) -> String {
467        self.format.to_owned()
468    }
469
470    pub fn charset(&self) -> String {
471        self.charset.to_owned()
472    }
473
474    pub fn sign_type(&self) -> String {
475        self.sign_type.to_owned()
476    }
477
478    pub fn version(&self) -> String {
479        self.version.to_owned()
480    }
481
482    pub fn return_url(&self) -> String {
483        self.return_url.to_owned()
484    }
485
486    pub fn notify_url(&self) -> String {
487        self.notify_url.to_owned()
488    }
489
490    pub fn set_notify_url(&mut self, notify_url: &str) {
491        self.notify_url = notify_url.to_owned()
492    }
493
494    pub fn set_sign_type(&mut self, sign_type: &str) {
495        self.sign_type = sign_type.to_owned()
496    }
497
498    pub fn set_charset(&mut self, charset: &str) {
499        self.charset = charset.to_owned()
500    }
501
502    pub fn execute(&self, req: &mut impl Requester) -> AliPayResult<Vec<byte>> {
503        let method = req.method();
504        let payload = req.encode_payload()?;
505        let mut request = http::Request::default();
506        let mut client = http::Client::New();
507        if method == "alipay.trade.precreate" {
508            request = http::Request::New(
509                http::Method::Get,
510                &format!("{}?{}", self.api_url, payload),
511                None,
512            )?;
513        } else {
514            request = http::Request::New(http::Method::Post, &self.api_url, Some(payload.into()))?;
515        }
516        //  设置客户端请求的编号格式utf-8, 解决中文有乱码问题。
517        request.Header.Set(
518            "Content-Type",
519            "application/x-www-form-urlencoded;charset=utf-8",
520        );
521        //  设置客户端接受返回数据的编号格式utf-8, 解决中文有乱码问题。
522        request
523            .Header
524            .Set("Accept", "application/json;charset=utf-8");
525
526        let res = client.Do(request.borrow_mut())?;
527        match res.Body {
528            Some(body) => Ok(body.into()),
529            None => Err(AliPaySDKError::AliPayError("body is NONE".to_string())),
530        }
531    }
532    /// 该方法对返回结果自带同步验签
533    pub fn do_alipay(&self, biz_content: &impl BizContenter) -> AliPayResult<Vec<byte>> {
534        // 同步验签
535        let sync_verigy_sign = |response: &[byte]| -> AliPayResult<bool> {
536            let result = std::str::from_utf8(response)?;
537            let get_raw_source = || -> String {
538                let key = biz::get_response_key(biz_content);
539                json_get(result, &key)
540            };
541
542            let get_signture = || -> String { json_get(result, "sign") };
543
544            let mut singer = builder().set_sign_type(self.sign_type().as_str()).build();
545
546            singer.set_public_key(self.alipay_public_key().as_str())?;
547            let passed = singer.verify(&get_raw_source(), &get_signture())?;
548            if !passed {
549                return Ok(false);
550            }
551            Ok(true)
552        };
553        match biz_content.method().as_str() {
554            "alipay.trade.wap.pay" | "alipay.trade.page.pay" => {
555                Ok(self.create_clien_page_form(biz_content)?)
556            }
557            "alipay.trade.app.pay" => self.create_clien_sdk_request(biz_content),
558            _ => {
559                let mut request = Request::new_with_config(self);
560                request
561                    .set_biz_content(biz_content)
562                    .set_method(biz_content.method().as_str());
563                let res = self.execute(&mut request)?;
564                // dbg!(String::from_utf8(res.to_vec()));
565                let is_pass = sync_verigy_sign(&res)?;
566                if !is_pass {
567                    return Err(AliPayError("syncVerifySign no passed!".to_string()));
568                }
569                Ok(res)
570            }
571        }
572    }
573
574    fn create_clien_page_form(&self, biz: &impl BizContenter) -> AliPayResult<Vec<byte>> {
575        let encode_query = Request::new_with_config(self)
576            .set_biz_content(biz)
577            .set_method(biz.method().as_str())
578            .encode_payload()?;
579
580        let values = url::ParseQuery(encode_query.as_str())?;
581        let mut parameters: HashMap<String, String> = HashMap::new();
582        for (k, v) in values {
583            parameters.insert(k, v[0].to_string());
584        }
585
586        let form = build_form(&self.api_url(), &mut parameters)?
587            .as_bytes()
588            .to_vec();
589        Ok(form)
590    }
591
592    fn create_clien_sdk_request(&self, biz: &impl BizContenter) -> AliPayResult<Vec<byte>> {
593        let client_sdk_request = Request::new_with_config(self)
594            .set_biz_content(biz)
595            .set_method(biz.method().as_str())
596            .encode_payload()?;
597        let form = client_sdk_request.as_bytes().to_vec();
598        Ok(form)
599    }
600}
601
602impl<'a> PayClientBuilder<'a> {
603    /// 设置接口网关地址 ,dev沙箱环境为:https://openapi.alipaydev.com/gateway.do,prod正式环境为:https://openapi.alipay.com/gateway.do
604    pub fn api_url(&mut self, api_url: &'a str) -> &mut Self {
605        self.api_url = Some(api_url);
606        self.borrow_mut()
607    }
608
609    /// 设置应用私钥 rsa私钥单行文本字符串,来源xxx_私钥.txt文件内容(PKCS1(非java适用))
610    pub fn private_key(&mut self, private_key: &'a str) -> &mut Self {
611        self.private_key = Some(private_key);
612        self.borrow_mut()
613    }
614    /// 设置应用公钥 rsa公钥单行文本字符串,来源xxx_公钥.txt文件内容(PKCS1(非java适用))
615    pub fn public_key(&mut self, public_key: &'a str) -> &mut Self {
616        self.public_key = Some(public_key);
617        self.borrow_mut()
618    }
619    /// 设置支付宝证书公钥, 来源 alipayCertPublicKey_RSA2.crt解析出的公钥
620    pub fn alipay_public_key(&mut self, alipay_public_key: &'a str) -> &mut Self {
621        self.alipay_public_key = Some(alipay_public_key);
622        self.borrow_mut()
623    }
624    /// 设置应用公钥证书 SN,来源appCertPublicKey_2021000117650139.crt文件解析
625    pub fn app_cert_sn(&mut self, app_cert_sn: &'a str) -> &mut Self {
626        self.app_cert_sn = Some(app_cert_sn);
627        self.borrow_mut()
628    }
629    /// 设置支付宝根证书 SN,来源alipayRootCert.crt文件解析
630    pub fn alipay_root_cert_sn(&mut self, alipay_root_cert_sn: &'a str) -> &mut Self {
631        self.alipay_root_cert_sn = Some(alipay_root_cert_sn);
632        self.borrow_mut()
633    }
634    /// 设置应用ID
635    pub fn app_id(&mut self, app_id: &'a str) -> &mut Self {
636        self.app_id = Some(app_id);
637        self.borrow_mut()
638    }
639    /// 设置请求格式,固定值json
640    pub fn format_json(&mut self) -> &mut Self {
641        self.format = Some("JSON");
642        self.borrow_mut()
643    }
644    /// 设置字符集,固定值 utf-8
645    pub fn charset_utf8(&mut self) -> &mut Self {
646        self.charset = Some("utf-8");
647        self.borrow_mut()
648    }
649    /// 设置签名类型,固定值RSA2
650    pub fn sign_type_rsa2(&mut self) -> &mut Self {
651        self.sign_type = Some("RSA2");
652        self.borrow_mut()
653    }
654    /// 设置接口版本号,固定值1.0
655    pub fn version_1_0(&mut self) -> &mut Self {
656        self.version = Some("1.0");
657        self.borrow_mut()
658    }
659    /// 设置前台回跳地址 return_url 自动跳转回商户页面
660    pub fn return_url(&mut self, return_url: &'a str) -> &mut Self {
661        self.return_url = Some(return_url);
662        self.borrow_mut()
663    }
664    /// 支付宝服务器主动通知callback商户服务器里指定的页面http/https路径
665    pub fn notify_url(&mut self, notify_url: &'a str) -> &mut Self {
666        self.notify_url = Some(notify_url);
667        self.borrow_mut()
668    }
669
670    /// 构造PayClient
671    pub fn build(self) -> AliPayResult<impl Payer> {
672        let mut p = PayClient::default();
673        if let Some(api_url) = self.api_url {
674            p.api_url = api_url.to_owned();
675        } else {
676            return Err(AliPaySDKError::AliPayError(
677                "api_url is required".to_string(),
678            ));
679        }
680
681        if let Some(private_key) = self.private_key {
682            p.private_key = private_key.to_owned();
683        } else {
684            return Err(AliPaySDKError::AliPayError(
685                "private_key is required".to_string(),
686            ));
687        }
688
689        if let Some(public_key) = self.public_key {
690            p.public_key = public_key.to_owned();
691        } else {
692            return Err(AliPaySDKError::AliPayError(
693                "public_key is required".to_string(),
694            ));
695        }
696
697        if let Some(app_cert_sn) = self.app_cert_sn {
698            p.app_cert_sn = app_cert_sn.to_owned();
699        } else {
700            return Err(AliPaySDKError::AliPayError(
701                "app_cert_sn is required".to_string(),
702            ));
703        }
704
705        if let Some(alipay_root_cert_sn) = self.alipay_root_cert_sn {
706            p.alipay_root_cert_sn = alipay_root_cert_sn.to_owned();
707        } else {
708            return Err(AliPayError("alipay_root_cert_sn is required".to_string()));
709        }
710
711        if let Some(app_id) = self.app_id {
712            p.app_id = app_id.to_owned();
713        } else {
714            return Err(AliPayError("app_id is required".to_string()));
715        }
716
717        if let Some(alipay_public_key) = self.alipay_public_key {
718            p.alipay_public_key = alipay_public_key.to_owned();
719        } else {
720            return Err(AliPayError("alipay_public_key is required".to_string()));
721        }
722
723        if let Some(format) = self.format {
724            p.format = format.to_owned();
725        } else {
726            p.format = "JSON".to_owned();
727        }
728
729        if let Some(charset) = self.charset {
730            p.charset = charset.to_owned();
731        } else {
732            p.charset = "utf-8".to_owned();
733        }
734
735        if let Some(sign_type) = self.sign_type {
736            p.sign_type = sign_type.to_owned();
737        } else {
738            p.sign_type = "RSA2".to_owned();
739        }
740
741        if let Some(version) = self.version {
742            p.version = version.to_owned();
743        } else {
744            p.version = "1.0".to_owned()
745        }
746
747        if let Some(return_url) = self.return_url {
748            p.return_url = return_url.to_owned();
749        }
750
751        if let Some(notify_url) = self.notify_url {
752            p.notify_url = notify_url.to_owned()
753        }
754
755        Ok(p)
756    }
757}