labrador/wechat/pay/api/
wxpay.rs

1use serde_json::Value;
2use 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};
3use crate::wechat::cryptos::{SignatureHeader, WechatCryptoV3};
4use crate::wechat::pay::method::{WechatPayMethod, WxPayMethod};
5use crate::wechat::pay::{TradeType};
6use crate::wechat::pay::request::WechatPayRequest;
7
8#[derive(Debug, Clone)]
9pub struct WxPay<'a, T: SessionStore> {
10    client: &'a WechatPayClient<T>,
11}
12
13#[allow(unused)]
14impl<'a, T: SessionStore> WxPay<'a, T> {
15
16    #[inline]
17    pub fn new(client: &WechatPayClient<T>) -> WxPay<T> {
18        WxPay {
19            client,
20        }
21    }
22
23    ///
24    /// # 统一下单
25    /// <pre>
26    /// 详见:[文档](https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1)
27    ///
28    /// 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识"
29    /// [接口地址](https://api.mch.weixin.qq.com/pay/unifiedorder)
30    /// </pre>
31    ///
32    /// # 示例
33    ///
34    /// ```no_run
35    ///
36    /// # use labrador::SimpleStorage;
37    /// # use labrador::WechatPayClient;
38    /// # use labrador::WechatPayRequest;
39    /// # use labrador::TradeType;
40    /// # async fn main() {
41    /// let client = WechatPayClient::new("appid","secret").wxpay();
42    /// let param = WechatPayRequest {
43    ///     appid: None,
44    ///     trade_type: TradeType::Micro,
45    ///     mch_id: "".to_string(),
46    ///     openid: "".to_string(),
47    ///     notify_url: None,
48    ///     body: "".to_string(),
49    ///     detail: "".to_string(),
50    ///     attach: "".to_string(),
51    ///     out_trade_no: "".to_string(),
52    ///     total_fee: "".to_string(),
53    ///     spbill_create_ip: "".to_string(),
54    ///     sign: "".to_string(),
55    ///     nonce_str: None,
56    ///     device_info: "".to_string(),
57    ///     product_id: "".to_string(),
58    ///     auth_code: "".to_string()
59    /// };
60    /// match client.unified_order(param).await {
61    ///     Ok(res) => {}
62    ///     Err(err) => {}
63    /// }
64    /// # }
65    ///
66    /// ```
67    ///
68    pub async fn unified_order(&self, mut params: WechatPayRequest) -> LabradorResult<WechatPayResponse> {
69        params.check_params()?;
70        let method = if params.trade_type == TradeType::Micro { WxPayMethod::MicroPay } else { WxPayMethod::UnifiedOrder };
71        params.appid = self.client.appid.to_owned().into();
72        if params.trade_type == TradeType::Micro {
73            // 將通知url置空
74            params.notify_url = None;
75        }
76        params.get_sign(&self.client.secret);
77        let res = self.client.post(WechatPayMethod::WxPay(method), &params.parse_xml(), RequestType::Xml).await?.text()?;
78        WechatPayResponse::parse_xml(res)
79    }
80
81    ///
82    /// # 统一下单 - V3版本
83    /// <pre>
84    /// 详见:[文档](https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1)
85    ///
86    /// 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识"
87    /// [接口地址](https://api.mch.weixin.qq.com/pay/unifiedorder)
88    /// </pre>
89    /// # 示例
90    ///
91    /// ```no_run
92    ///
93    /// # use labrador::SimpleStorage;
94    /// # use labrador::WechatPayClient;
95    /// # use labrador::WechatPayRequestV3;
96    /// # use labrador::TradeType;
97    /// # use labrador::Amount;
98    /// # use labrador::Payer;
99    /// # use chrono::NaiveDateTime;
100    /// # async fn main() {
101    /// let client = WechatPayClient::new("appid","secret").wxpay();
102    /// let param = WechatPayRequestV3 {
103    ///     appid: None,
104    ///     mch_id: "".to_string(),
105    ///     notify_url: "".to_string(),
106    ///     amount: Amount { total: 0,currency: None,payer_total: None,payer_currency: None},
107    ///     payer: Payer { openid: "".to_string()}.into(),
108    ///     detail: None,
109    ///     scene_info: None,attach: None,
110    ///     out_trade_no: "".to_string(),
111    ///     description: "".to_string(),
112    ///     time_expire: "".to_string(),
113    ///     settle_info: None
114    /// };
115    /// match client.unified_order_v3(TradeType::App, param).await {
116    ///     Ok(res) => {}
117    ///     Err(err) => {}
118    /// }
119    /// # }
120    ///
121    /// ```
122    ///
123    pub async fn unified_order_v3(&self, trade_type: TradeType, mut params: WechatPayRequestV3) -> LabradorResult<WechatPayResponseV3> {
124        if params.mch_id.is_empty() {
125            params.mch_id = self.client.mch_id.to_owned().unwrap_or_default();
126        }
127        if params.appid.is_none() {
128            params.appid = self.client.appid.to_owned().into();
129        }
130        let res = self.client.post_v3(params.mch_id.to_owned().into(), WechatPayMethod::WxPay(WxPayMethod::UnifiedOrderV3(trade_type)), vec![],&params, RequestType::Json).await?.json::<serde_json::Value>()?;
131        serde_json::from_value::<WechatPayResponseV3>(res).map_err(LabraError::from)
132    }
133
134    pub async fn isv_unified_order_v3(&self, trade_type: TradeType, mut params: IsvWechatPayRequestV3) -> LabradorResult<WechatPayResponseV3> {
135        let res = self.client.post_v3(None, WechatPayMethod::WxPay(WxPayMethod::IsvUnifiedOrderV3(trade_type)), vec![],&params, RequestType::Json).await?.json::<serde_json::Value>()?;
136        serde_json::from_value::<WechatPayResponseV3>(res).map_err(LabraError::from)
137    }
138
139    /// 调用统一下单接口,并组装生成支付所需参数对象.
140    pub async fn create_order_v3(&self, trade_type: TradeType, params: WechatPayRequestV3) -> LabradorResult<Value> {
141        let result = self.unified_order_v3(trade_type.to_owned(), params.to_owned()).await?;
142        result.get_pay_info(trade_type, params.appid, params.mch_id, self.client.private_key.to_owned())
143    }
144
145    /// 服务商调用统一下单接口,并组装生成支付所需参数对象.
146    pub async fn isv_create_order_v3(&self, trade_type: TradeType, params: IsvWechatPayRequestV3) -> LabradorResult<Value> {
147        let result = self.isv_unified_order_v3(trade_type.to_owned(), params.to_owned()).await?;
148        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())
149    }
150
151    ///
152    /// # 关闭订单
153    /// <pre>
154    /// 应用场景
155    /// 以下情况需要调用关单接口:
156    /// 1、商户订单支付失败需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付;
157    /// 2、系统下单后,用户支付超时,系统退出不再受理,避免用户继续,请调用关单接口。
158    /// 注意:关单没有时间限制,建议在订单生成后间隔几分钟(最短5分钟)再调用关单接口,避免出现订单状态同步不及时导致关单失败。
159    ///
160    /// [接口地址](https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_3.shtml)
161    /// </pre>
162    /// # 示例
163    ///
164    /// ```no_run
165    ///
166    /// # use labrador::SimpleStorage;
167    /// # use labrador::WechatPayClient;
168    /// # use labrador::WechatCloseOrderRequest;
169    /// # async fn main() {
170    /// let client = WechatPayClient::new("appid","secret").wxpay();
171    /// let param = WechatCloseOrderRequest {
172    ///     appid: None,
173    ///     mch_id: "".to_string(),
174    ///     out_trade_no: "".to_string(),
175    ///     sign: "".to_string(),
176    ///     nonce_str: None,
177    /// };
178    /// match client.close_order(param).await {
179    ///     Ok(res) => {}
180    ///     Err(err) => {}
181    /// }
182    /// # }
183    ///
184    /// ```
185    ///
186    pub async fn close_order(&self,
187                             mut params: WechatCloseOrderRequest) -> LabradorResult<WechatCloseOrderResponse> {
188        params.appid = self.client.appid.to_owned().into();
189        params.get_sign(&self.client.api_key.to_owned().unwrap_or_default());
190        let res = self.client.post(WechatPayMethod::WxPay(WxPayMethod::CloseOrder), &params.parse_xml(), RequestType::Xml).await?.text()?;
191        WechatCloseOrderResponse::parse_xml(res)
192    }
193
194    ///
195    /// # 关闭订单
196    /// <pre>
197    /// 应用场景
198    /// 以下情况需要调用关单接口:
199    /// 1、商户订单支付失败需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付;
200    /// 2、系统下单后,用户支付超时,系统退出不再受理,避免用户继续,请调用关单接口。
201    /// 注意:关单没有时间限制,建议在订单生成后间隔几分钟(最短5分钟)再调用关单接口,避免出现订单状态同步不及时导致关单失败。
202    ///
203    /// [接口地址](https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_3.shtml)
204    /// </pre>
205    /// # 示例
206    ///
207    /// ```no_run
208    ///
209    /// # use labrador::SimpleStorage;
210    /// # use labrador::WechatPayClient;
211    /// # use labrador::WechatCloseOrderRequestV3;
212    /// # async fn main() {
213    /// let client = WechatPayClient::new("appid","secret").wxpay();
214    /// let param = WechatCloseOrderRequestV3 {
215    ///     mchid: "".to_string(),
216    ///     out_trade_no: None,
217    /// };
218    /// match client.close_order_v3(param).await {
219    ///     Ok(res) => {}
220    ///     Err(err) => {}
221    /// }
222    /// # }
223    ///
224    /// ```
225    ///
226    pub async fn close_order_v3(&self, mut params: WechatCloseOrderRequestV3) -> LabradorResult<()> {
227        let out_trade_no = params.out_trade_no.to_owned().unwrap_or_default();
228        params.out_trade_no = None;
229        let res = self.client.post_v3(params.mchid.to_owned().into(), WechatPayMethod::WxPay(WxPayMethod::CloseOrderV3(out_trade_no)), vec![], &params, RequestType::Json).await?;
230        let _ = res.text()?;
231        // let s = res.json::<serde_json::Value>().await?;
232        Ok(())
233    }
234
235    ///
236    /// # 查询订单(适合于需要自定义子商户号和子商户appid的情形).
237    /// 详见:[文档](https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2)
238    /// <pre>
239    /// 该接口提供所有微信支付订单的查询,商户可以通过查询订单接口主动查询订单状态,完成下一步的业务逻辑。
240    /// 需要调用查询接口的情况:
241    ///    ◆ 当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知;
242    ///    ◆ 调用支付接口后,返回系统错误或未知交易状态情况;
243    ///    ◆ 调用被扫支付API,返回USERPAYING的状态;
244    ///    ◆ 调用关单或撤销接口API之前,需确认支付状态;
245    ///
246    /// 接口地址:
247    /// https://api.mch.weixin.qq.com/pay/orderquery
248    /// </pre>
249    /// # 示例
250    ///
251    /// ```no_run
252    ///
253    /// # use labrador::SimpleStorage;
254    /// # use labrador::WechatPayClient;
255    /// # use labrador::WechatQueryOrderRequest;
256    /// # async fn main() {
257    /// let client = WechatPayClient::new("appid","secret").wxpay();
258    /// let param = WechatQueryOrderRequest {
259    ///     transaction_id: None,
260    ///     out_trade_no: None,
261    ///     appid: None,
262    ///     mch_id: "".to_string(),
263    ///     sign: "".to_string(),
264    ///     nonce_str: None
265    /// };
266    /// match client.query_order(param).await {
267    ///     Ok(res) => {}
268    ///     Err(err) => {}
269    /// }
270    /// # }
271    ///
272    /// ```
273    ///
274    pub async fn query_order(&self, mut params: WechatQueryOrderRequest) -> LabradorResult<WechatQueryOrderResponse> {
275        let res = self.client.post(WechatPayMethod::WxPay(WxPayMethod::QueryOrder), &params, RequestType::Xml).await?;
276        let result = res.text()?;
277        WechatQueryOrderResponse::parse_xml(result)
278    }
279
280    ///
281    /// # 查询订单
282    /// 详见:[文档](https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_2.shtml)
283    /// <pre>
284    /// 商户可以通过查询订单接口主动查询订单状态,完成下一步的业务逻辑。查询订单状态可通过微信支付订单号或商户订单号两种方式查询
285    /// 注意:
286    /// 查询订单可通过微信支付订单号和商户订单号两种方式查询,两种查询方式返回结果相同
287    /// 需要调用查询接口的情况:
288    ///   ◆ 当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知。
289    ///   ◆ 调用支付接口后,返回系统错误或未知交易状态情况。
290    ///   ◆ 调用付款码支付API,返回USERPAYING的状态。
291    ///   ◆ 调用关单或撤销接口API之前,需确认支付状态。
292    ///
293    /// 接口地址:
294    /// https://api.mch.weixin.qq.com/v3/pay/transactions/id/{transaction_id}
295    /// https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{out_trade_no}
296    /// </pre>
297    /// # 示例
298    ///
299    /// ```no_run
300    ///
301    /// # use labrador::SimpleStorage;
302    /// # use labrador::WechatPayClient;
303    /// # use labrador::WechatQueryOrderRequestV3;
304    /// # async fn main() {
305    /// let client = WechatPayClient::new("appid","secret").wxpay();
306    /// let param = WechatQueryOrderRequestV3 {
307    ///     mchid: "".to_string(),
308    ///     transaction_id: None,
309    ///     out_trade_no: None,
310    /// };
311    /// match client.query_order_v3(param).await {
312    ///     Ok(res) => {}
313    ///     Err(err) => {}
314    /// }
315    /// # }
316    ///
317    /// ```
318    ///
319    pub async fn query_order_v3(&self, params: WechatQueryOrderRequestV3) -> LabradorResult<WechatQueryOrderResponseV3> {
320        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)
321            .await?.json::<WechatQueryOrderResponseV3>()
322    }
323
324
325
326
327    ///
328    ///
329    /// # 微信支付-查询退款(适合于需要自定义子商户号和子商户appid的情形).
330    /// 详见 [文档](https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_5)
331    /// <pre>
332    /// 应用场景:
333    ///  提交退款申请后,通过调用该接口查询退款状态。退款有一定延时,用零钱支付的退款20分钟内到账,
334    ///  银行卡支付的退款3个工作日后重新查询退款状态。
335    ///
336    /// 接口链接:https://api.mch.weixin.qq.com/pay/refundquery
337    /// </pre>
338    pub async fn query_refund_order(&self, mut params: WechatQueryRefundOrderRequest) -> LabradorResult<WechatQueryRefundResponse> {
339        params.appid = self.client.appid.to_owned().into();
340        params.get_sign(&self.client.api_key.to_owned().unwrap_or_default());
341        let res = self.client.post(WechatPayMethod::WxPay(WxPayMethod::QueryRefundOrder), params, RequestType::Xml)
342            .await?.text()?;
343        WechatQueryRefundResponse::parse_xml(res)
344    }
345
346
347    ///
348    ///
349    /// # 微信支付-查询退款API(支持单品).
350    /// 详见 [文档](https://pay.weixin.qq.com/wiki/doc/api/danpin.php?chapter=9_104&index=4)
351    /// <pre>
352    /// 应用场景
353    ///    提交退款申请后,通过调用该接口查询退款状态。退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款3个工作日后重新查询退款状态。
354    /// 注意:
355    /// 1、本接口支持查询单品优惠相关退款信息,且仅支持按微信退款单号或商户退款单号查询,若继续调用老查询退款接口,
356    ///    请见https://pay.weixin.qq.com/wiki/doc/api/jsapi_sl.php?chapter=9_5
357    /// 2、请求频率限制:300qps,即每秒钟正常的退款查询请求次数不超过300次
358    /// 3、错误或无效请求频率限制:6qps,即每秒钟异常或错误的退款查询请求不超过6次
359    ///
360    /// 接口地址
361    /// https://api.mch.weixin.qq.com/pay/refundqueryv2
362    /// https://api2.mch.weixin.qq.com/pay/refundqueryv2(备用域名)见跨城冗灾方案
363    ///
364    /// </pre>
365    pub async fn query_refund_order_v2(&self, params: WechatQueryRefundOrderRequest) -> LabradorResult<WechatQueryRefundResponse> {
366        let res = self.client.post(WechatPayMethod::WxPay(WxPayMethod::QueryRefundOrderV2), params, RequestType::Json)
367            .await?.text()?;
368        WechatQueryRefundResponse::parse_xml(res)
369    }
370
371    /// 
372    /// # 微信支付-查询退款
373    /// 详见 [文档](https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_10.shtml)
374    /// <pre>
375    /// 
376    /// 应用场景:
377    ///  提交退款申请后,通过调用该接口查询退款状态。退款有一定延时,建议在提交退款申请后1分钟发起查询退款状态,一般来说零钱支付的退款5分钟内到账,银行卡支付的退款1-3个工作日到账。
378    ///
379    /// 接口链接:https://api.mch.weixin.qq.com/v3/refund/domestic/refunds/{out_refund_no}
380    /// </pre>
381    pub async fn query_refund_order_v3(&self, out_refund_no: String) -> LabradorResult<WechatQueryRefundResponseV3> {
382        self.client.post_v3(None, WechatPayMethod::WxPay(WxPayMethod::QueryRefundOrderV3(out_refund_no)), vec![], "", RequestType::Json)
383            .await?.json::<WechatQueryRefundResponseV3>()
384    }
385
386    pub async fn isv_query_refund_order_v3(&self, out_refund_no: String, sub_mch_id: String) -> LabradorResult<WechatQueryRefundResponseV3> {
387        self.client.post_v3(None, WechatPayMethod::WxPay(WxPayMethod::QueryRefundOrderV3(out_refund_no)), vec![("sub_mchid".to_string(), sub_mch_id)], "", RequestType::Json)
388            .await?.json::<WechatQueryRefundResponseV3>()
389    }
390
391    /// # 解析支付结果通知.
392    /// 详见 [文档](https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7)
393    pub fn parse_order_notify(&self, xml: &str) -> LabradorResult<WechatPayNotifyResponse> {
394        WechatPayNotifyResponse::parse_xml(xml.to_string())
395    }
396
397    /// # 解析支付结果通知. - v3
398    /// 详见 [文档](https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_5.shtml)
399    pub async fn parse_order_notify_v3(&self, notify_data: &str, header: Option<SignatureHeader>) -> LabradorResult<WechatPayNotifyResponseV3> {
400
401        if header.is_none() {
402            return Err(LabraError::RequestError("非法请求,头部信息为空".to_string()));
403        }
404        let header = header.unwrap();
405        if !self.client.verify_notify_sign(&header, notify_data).await {
406            return Err(LabraError::RequestError("非法请求,头部信息验证失败".to_string()));
407        }
408        let origin = serde_json::from_str::<OriginNotifyResponse>(notify_data)?;
409        let resource = origin.resource.to_owned();
410        let v3_key = self.client.api_key_v3.to_owned().unwrap_or_default();
411        let crypto = WechatCryptoV3::new(&v3_key);
412        let decrypted = crypto.decrypt_data_v3(&resource)?;
413        let decrypt_notify_result = serde_json::from_slice::<DecryptNotifyResult>(&decrypted)?;
414        Ok(WechatPayNotifyResponseV3 {
415            raw_data: origin.into(),
416            result: decrypt_notify_result.into()
417        })
418    }
419
420    /// # 解析退款结果通知.
421    /// 详见 [文档](https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_16&index=9)
422    pub fn parse_refund_notify(&self, xml: &str) -> LabradorResult<WechatDecryptRefundNotifyResponse> {
423        WechatRefundNotifyResponse::parse_xml(xml.to_string(), &self.client.appid)
424    }
425
426    /// # 解析退款结果通知 - V3.
427    /// 详见 [文档](https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_16&index=9)
428    pub async fn parse_refund_notify_v3(&self, notify_data: &str, header: &Option<SignatureHeader>) -> LabradorResult<WechatRefundNotifyResponseV3> {
429        if header.is_none() {
430            return Err(LabraError::RequestError("非法请求,头部信息验证为空".to_string()));
431        }
432        let header = header.to_owned().unwrap();
433        if !self.client.verify_notify_sign(&header, notify_data).await {
434            return Err(LabraError::RequestError("非法请求,头部信息验证失败".to_string()));
435        }
436        let origin = serde_json::from_str::<OriginNotifyResponse>(notify_data)?;
437        let resource = origin.resource.to_owned();
438        let v3_key = self.client.api_key_v3.to_owned().unwrap_or_default();
439        let crypto = WechatCryptoV3::new(&v3_key);
440        let decrypted = crypto.decrypt_data_v3(&resource)?;
441        let decrypt_notify_result = serde_json::from_slice::<DecryptRefundNotifyResult>(&decrypted)?;
442        Ok(WechatRefundNotifyResponseV3 {
443            raw_data: origin.into(),
444            result: decrypt_notify_result.into()
445        })
446    }
447
448    /// # 解析扫码支付回调通知
449    /// 详见 [文档](https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_4)
450    pub fn parse_scan_pay_notify(&self, xml: &str) -> LabradorResult<WxScanPayNotifyResponse> {
451        WxScanPayNotifyResponse::parse_xml(xml.to_string())
452    }
453
454
455    /// 支付
456    pub async fn micro_pay(
457        &self,
458        mut pay_params: WechatPayRequest
459    ) -> LabradorResult<WechatPayResponse> {
460        pay_params.trade_type = TradeType::Micro;
461        self.unified_order(pay_params).await
462    }
463
464    /// JSAPI支付
465    pub async fn jsapi_pay(
466        &self,
467        mut pay_params: WechatPayRequest
468    ) -> LabradorResult<WechatPayResponse> {
469        pay_params.trade_type = TradeType::Jsapi;
470        self.unified_order(pay_params).await
471    }
472
473    /// APP支付
474    pub async fn app_pay(
475        &self,
476        mut pay_params: WechatPayRequest
477    ) -> LabradorResult<WechatPayResponse> {
478        pay_params.trade_type = TradeType::App;
479        self.unified_order(pay_params).await
480    }
481
482    ///
483    ///
484    /// # 申请退款API(支持单品).
485    /// 详见 [文档](https://pay.weixin.qq.com/wiki/doc/api/danpin.php?chapter=9_103&index=3)
486    /// <pre>
487    /// 应用场景
488    /// 当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退还给买家,微信支付将在收到退款请求并且验证成功之后,按照退款规则将支付款按原路退到买家帐号上。
489    ///
490    /// 注意:
491    /// 1、交易时间超过一年的订单无法提交退款;
492    /// 2、微信支付退款支持单笔交易分多次退款,多次退款需要提交原支付订单的商户订单号和设置不同的退款单号。申请退款总金额不能超过订单金额。 一笔退款失败后重新提交,请不要更换退款单号,请使用原商户退款单号。
493    /// 3、请求频率限制:150qps,即每秒钟正常的申请退款请求次数不超过150次
494    ///     错误或无效请求频率限制:6qps,即每秒钟异常或错误的退款申请请求不超过6次
495    /// 4、每个支付订单的部分退款次数不能超过50次
496    /// 5、本接口支持单品优惠订单全额退款和单品优惠订单部分退款,推荐使用本接口,如果使用不支持单品优惠部分退款的历史接口,请看https://pay.weixin.qq.com/wiki/doc/api/jsapi_sl.php?chapter=9_4
497    ///
498    /// 接口地址
499    /// https://api.mch.weixin.qq.com/secapi/pay/refundv2
500    /// https://api2.mch.weixin.qq.com/secapi/pay/refundv2(备用域名)见跨城冗灾方案
501    /// </pre>
502    ///
503    pub async fn refund(
504        &self,
505        mut params: WechatRefundRequest
506    ) -> LabradorResult<WechatRefundResponse> {
507        params.appid = self.client.appid.to_owned().into();
508        let mch_id = params.mch_id.as_str();
509        params.get_sign(&self.client.api_key.to_owned().unwrap_or_default());
510        let res = self.client.post(WechatPayMethod::WxPay(WxPayMethod::Refund), &params.parse_xml(), RequestType::Xml).await?.text()?;
511        WechatRefundResponse::parse_xml(res)
512    }
513
514    ///
515    ///
516    /// # 撤销订单API.
517    /// 详见 [文档](https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_11&index=3)
518    /// <pre>
519    /// 应用场景:
520    ///  支付交易返回失败或支付系统超时,调用该接口撤销交易。如果此订单用户支付失败,微信支付系统会将此订单关闭;
521    ///  如果用户支付成功,微信支付系统会将此订单资金退还给用户。
522    ///  注意:7天以内的交易单可调用撤销,其他正常支付的单如需实现相同功能请调用申请退款API。
523    ///  提交支付交易后调用【查询订单API】,没有明确的支付结果再调用【撤销订单API】。
524    ///  调用支付接口后请勿立即调用撤销订单API,建议支付后至少15s后再调用撤销订单接口。
525    ///  接口链接 :https://api.mch.weixin.qq.com/secapi/pay/reverse
526    ///  是否需要证书:请求需要双向证书。
527    /// </pre>
528    ///
529    pub async fn reverse_order(
530        &self,
531        mut params: WechatOrderReverseRequest
532    ) -> LabradorResult<WechatOrderReverseResponse> {
533        params.appid = self.client.appid.to_owned().into();
534        let mch_id = params.mch_id.as_str();
535        let res = self.client.post(WechatPayMethod::WxPay(WxPayMethod::ReverseOrder), &params.parse_xml(), RequestType::Xml).await?.text()?;
536        WechatOrderReverseResponse::parse_xml(res)
537    }
538
539    ///
540    ///
541    /// # 转换短链接.
542    /// 详见 [文档](https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_9&index=8)
543    /// <pre>
544    ///  应用场景:
545    ///     该接口主要用于扫码原生支付模式一中的二维码链接转成短链接(weixin://wxpay/s/XXXXXX),减小二维码数据量,提升扫描速度和精确度。
546    ///  接口地址:<a href="https://api.mch.weixin.qq.com/tools/shorturl">https://api.mch.weixin.qq.com/tools/shorturl</a>
547    ///  是否需要证书:否
548    /// </pre>
549    ///
550    pub async fn short_url(
551        &self,
552        mut params: WxPayShorturlRequest
553    ) -> LabradorResult<WxPayShortUrlResponse> {
554        params.appid = self.client.appid.to_owned().into();
555        let mch_id = params.mch_id.as_str();
556        let res = self.client.post(WechatPayMethod::WxPay(WxPayMethod::ShortUrl), &params.parse_xml(), RequestType::Xml).await?.text()?;
557        WxPayShortUrlResponse::parse_xml(res)
558    }
559
560    ///
561    ///
562    /// # 申请退款API(支持单品).
563    /// 详见 [文档](https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_9.shtml)
564    /// <pre>
565    /// 应用场景
566    /// 当交易发生之后一年内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付金额退还给买家,微信支付将在收到退款请求并且验证成功之后,将支付款按原路退还至买家账号上。
567    ///
568    /// 注意:
569    /// 1、交易时间超过一年的订单无法提交退款
570    /// 2、微信支付退款支持单笔交易分多次退款(不超50次),多次退款需要提交原支付订单的商户订单号和设置不同的退款单号。申请退款总金额不能超过订单金额。 一笔退款失败后重新提交,请不要更换退款单号,请使用原商户退款单号
571    /// 3、错误或无效请求频率限制:6qps,即每秒钟异常或错误的退款申请请求不超过6次
572    /// 4、每个支付订单的部分退款次数不能超过50次
573    /// 5、如果同一个用户有多笔退款,建议分不同批次进行退款,避免并发退款导致退款失败
574    /// 6、申请退款接口的返回仅代表业务的受理情况,具体退款是否成功,需要通过退款查询接口获取结果
575    /// 7、一个月之前的订单申请退款频率限制为:5000/min
576    ///
577    /// 接口地址
578    /// https://api.mch.weixin.qq.com/v3/refund/domestic/refunds
579    /// </pre>
580    ///
581    pub async fn refund_v3(
582        &self,
583        mut params: WechatRefundRequestV3
584    ) -> LabradorResult<WechatRefundResponseV3> {
585       self.client.post_v3(None, WechatPayMethod::WxPay(WxPayMethod::RefundV3), vec![],params, RequestType::Json).await?
586            .json::<WechatRefundResponseV3>()
587    }
588}
589
590
591
592#[cfg(test)]
593#[allow(unused, non_snake_case)]
594mod tests {
595    use std::fs::File;
596    use std::io::Read;
597    use std::ops::Add;
598    use chrono::{DateTime, Local, NaiveDateTime, SecondsFormat};
599    use crate::{Amount, Payer, request, SimpleStorage, TradeType, WechatCloseOrderRequestV3, WechatPayClient, WechatPayRequestV3};
600
601    #[test]
602    fn test_close_order_v3() {
603        let rt = tokio::runtime::Runtime::new().unwrap();
604        let mut private_key = Vec::new();
605        let s = File::open("src/wechat/pay/sec/apiclient_key.pem");
606        if s.is_err() {
607            return;
608        }
609        s.unwrap().read_to_end(&mut private_key);
610        let r = rt.spawn(async {
611            let c =  WechatPayClient::<SimpleStorage>::new("appid", "secret");
612            let mut client =c.wxpay();
613            let result = client.close_order_v3(WechatCloseOrderRequestV3 {
614                mchid: "mchid".to_string(),
615                out_trade_no: "23234234234".to_string().into()
616            });
617            match result.await {
618                Ok(res) => {
619                    println!("请求成功",);
620                }
621                Err(err) => {
622                    println!("err:{:?}", err);
623                }
624            }
625        });
626        rt.block_on(r);
627    }
628
629
630
631    #[test]
632    fn test_callback_v3() {
633        let rt = tokio::runtime::Runtime::new().unwrap();
634        let mut private_key = Vec::new();
635        let s = File::open("src/wechat/pay/sec/apiclient_key.pem");
636        if s.is_err() {
637            return;
638        }
639        s.unwrap().read_to_end(&mut private_key);
640        let r = rt.spawn(async {
641            let c =  WechatPayClient::<SimpleStorage>::new("appid", "secret");
642            let mut client =c.wxpay();
643            // .cert(MchCert {
644            //     mch_id: "1602920235".to_string().into(),
645            //     serial_no: "71785E680339B8B4B056BD61A41A1AD2020AE33E".to_string().into(),
646            //     private_key_path: String::from("src/wechat/pay/sec/apiclient_key.pem").into(),
647            //     private_key: String::from_utf8(private_key).unwrap().into(),
648            //     private_cert_path:  String::from("src/wechat/pay/sec/apiclient_cert.pem").into(),
649            //     pkcs12_path: None
650            // }).key_v3("364ae33e57cf4989b8aefaa66ddc7ca7".to_string())
651            let date = Local::now().to_rfc3339_opts(SecondsFormat::Secs, false);
652            /*let result = client.unified_order_v3(TradeType::Jsapi, WechatPayRequestV3 {
653                appid: "wx7959501b424a9e93".to_string().into(),
654                mch_id: "1602920235".to_string(),
655                description: "测试商品支付".to_string(),
656                out_trade_no: "1602920235sdfsdfas32234234".to_string(),
657                time_expire: date,
658                attach: None,
659                notify_url: "https://api.snackcloud.cn/trade/notify".to_string(),
660                amount: Amount {
661                    total: 1,
662                    currency: String::from("CNY").into()
663                },
664                payer: Payer {
665                    openid: "oUVZc6S_uGx3bsNPUA-davo4Dt7U".to_string()
666                },
667                detail: None,
668                scene_info: None,
669                settle_info: None
670            });*/
671            let result = client.close_order_v3(WechatCloseOrderRequestV3 {
672                mchid: "mchid".to_string(),
673                out_trade_no: "23234234234".to_string().into()
674            });
675            match result.await {
676                Ok(res) => {
677                    println!("请求成功",);
678                }
679                Err(err) => {
680                    println!("err:{:?}", err);
681                }
682            }
683        });
684        rt.block_on(r);
685    }
686
687    #[test]
688    fn test_create_order_v3() {
689        let rt = tokio::runtime::Runtime::new().unwrap();
690        let mut private_key = Vec::new();
691        let s = File::open("src/wechat/pay/sec/apiclient_key.pem");
692        if s.is_err() {
693            return;
694        }
695        s.unwrap().read_to_end(&mut private_key);
696        let r = rt.spawn(async {
697            let c =  WechatPayClient::<SimpleStorage>::new("appid", "secret");
698            let mut client =c.wxpay();
699            let date = Local::now().to_rfc3339_opts(SecondsFormat::Secs, false);
700            let result = client.unified_order_v3(TradeType::Jsapi, WechatPayRequestV3 {
701                appid: "appid".to_string().into(),
702                mch_id: "mchid".to_string(),
703                description: "测试商品支付".to_string(),
704                out_trade_no: "1602920235sdfsdfas32234234".to_string(),
705                time_expire: date,
706                attach: None,
707                notify_url: "https://xxx.cn/trade/notify".to_string(),
708                amount: Amount {
709                    total: 1,
710                    currency: String::from("CNY").into(),
711                    payer_total: None,
712                    payer_currency: None
713                },
714                payer: Payer {
715                    openid: "oUVZc6S_uGx3bsNPUA-davo4Dt7Us".to_string()
716                }.into(),
717                detail: None,
718                scene_info: None,
719                settle_info: None
720            });
721            match result.await {
722                Ok(res) => {
723                    println!("请求成功",);
724                }
725                Err(err) => {
726                    println!("err:{:?}", err);
727                }
728            }
729        });
730        rt.block_on(r);
731    }
732
733}