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), ¶ms.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![],¶ms, 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![],¶ms, 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), ¶ms.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![], ¶ms, 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), ¶ms, 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), ¶ms.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), ¶ms.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), ¶ms.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}