1、引入依赖:(对于依赖冲突自行解决)

<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
<!--<version>3.4.9.B</version>-->
<version>3.5.0</version>
<exclusions>
<exclusion>
<artifactId>httpclient</artifactId>
<groupId>org.apache.httpcomponents</groupId>
</exclusion>
<exclusion>
<artifactId>commons-lang3</artifactId>
<groupId>org.apache.commons</groupId>
</exclusion>
<exclusion>
<artifactId>commons-beanutils</artifactId>
<groupId>commons-beanutils</groupId>
</exclusion>
<exclusion>
<artifactId>guava</artifactId>
<groupId>com.google.guava</groupId>
</exclusion>
</exclusions>
</dependency>

2、

  1. package com.dhht.wechat.controller;
  2.  
  3. import com.dhht.wechat.VO.PayVO;
    import com.dhht.wechat.service.PayService;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
  4.  
  5. import javax.annotation.Resource;
    import javax.servlet.http.HttpServletRequest;
  6.  
  7. /*
    * @Author: sh
    * @Description: WxPayController
    * @Date: 11:06 2019/7/17
    */
  8.  
  9. @RestController
    @RequestMapping("wechat/")
    public class WxPayController {
  10.  
  11. @Resource
    private PayService payService;
  12.  
  13. /**
    * 微信支付统一下单接口(H5)
    * @param payVO
    * @param request
    * @return
    */
    @PostMapping(value = "pay",produces = "application/json;charset=UTF-8")
    public String unifiedOrder(@RequestBody PayVO payVO, HttpServletRequest request){
    return payService.unifiedOrder(payVO,request);
    }
  14.  
  15. /**
    * 微信支付接收通知接口(供微信服务调用)
    * @param xmlData
    * @return
    */
    @PostMapping(value = "pay/notify",produces = "application/json;charset=UTF-8")
    public synchronized String payNotify(@RequestBody String xmlData){
  16.  
  17. return payService.payNotifyCallBack(xmlData);
    }
  18.  
  19. /**
    * 退款结果通知接口
    * @param xmlData
    * @return
    */
    @PostMapping("/notify/refund")
    public synchronized String parseRefundNotifyResult(@RequestBody String xmlData) {
    return payService.parseRefundNotifyResult(xmlData);
    }
  20.  
  21. }
  1. package com.dhht.wechat.service;
  2.  
  3. import com.alibaba.fastjson.JSON;
    import com.dhht.wechat.VO.PayVO;
    import com.dhht.wechat.service.impservice.OrderRefundResultImplService;
    import com.dhht.wechat.service.impservice.SealOrderImplService;
    import com.dhht.wechat.service.impservice.SealOrderSealImplService;
    import com.dhht.wechat.util.ConstantUtil;
    import com.dhht.wechat.util.DateUtil;
    import com.dhht.wechat.wxpay.MyX509TrustManager;
    import com.dhht.wechat.wxpay.PayRespCodeMsg;
    import com.dhht.wechat.wxpay.PayResultVO;
    import com.dhht.wechat.wxpay.WechatPayConfigBean;
    import com.dhht.wechat.wxpay.config.WxPayProperties;
    import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
    import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
    import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
    import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
    import com.github.binarywang.wxpay.bean.request.*;
    import com.github.binarywang.wxpay.bean.result.WxPayOrderCloseResult;
    import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryResult;
    import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
    import com.github.binarywang.wxpay.service.WxPayService;
    import net.sf.json.JSONObject;
    import org.apache.commons.lang3.StringUtils;
    import org.jdom.Document;
    import org.jdom.Element;
    import org.jdom.input.SAXBuilder;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Service;
  4.  
  5. import javax.annotation.Resource;
    import javax.net.ssl.HttpsURLConnection;
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.SSLSocketFactory;
    import javax.net.ssl.TrustManager;
    import javax.servlet.http.HttpServletRequest;
    import java.io.*;
    import java.math.BigDecimal;
    import java.net.ConnectException;
    import java.net.URL;
    import java.security.MessageDigest;
    import java.text.MessageFormat;
    import java.util.*;
  6.  
  7. /**
    * WxPayService
    */
    @Service
    public class PayService {
  8.  
  9. @Resource
    private WechatPayConfigBean wechatPayConfigBean;
  10.  
  11. @Resource
    private WxPayProperties wxPayProperties;// 第三方
  12.  
  13. @Resource
    private SealOrderImplService sealOrderImplService;
  14.  
  15. @Resource
    private WxPayService wxService;// 第三方sdk
  16.  
  17. // @Resource
    // private OrderPayResultImplService orderPayResultImplService;
  18.  
  19. @Resource
    private OrderRefundResultImplService orderRefundResultImplService;
  20.  
  21. @Value("${spbill_create_ip}")
    private String SPBILL_CREATE_IP;
  22.  
  23. @Value("${appSecret}")
    private String appSecret;
  24.  
  25. @Resource
    WeiXinService weiXinService;
  26.  
  27. @Resource
    SealOrderSealImplService sealOrderSealImplService;
  28.  
  29. /**
    * 微信支付统一下单接口(H5)
    *
    * @param payVO
    * @param request
    * @return
    */
    public String unifiedOrder(PayVO payVO, HttpServletRequest request) {
    try {
  30.  
  31. String orderId = payVO.getOrderId();
    String code = payVO.getCode();
    if (StringUtils.isEmpty(orderId) || StringUtils.isEmpty(code)) {
    PayResultVO payResultVO = new PayResultVO(-1, "NO", null);
    return JSON.toJSONString(payResultVO);
    }
  32.  
  33. WxPayOrderQueryResult payOrderQueryResult = wechatOrderQuery(orderId);// wxService.queryOrder(null, orderId);// 查询订单状态
    String payOrderQuery_return_code = payOrderQueryResult.getReturnCode();// SUCCESS/FAIL
    if (ConstantUtil.PAY_RETURN_CODE_NO.equals(payOrderQuery_return_code)) {// 通信失败:FAIL
    PayResultVO queryPayResultVO = new PayResultVO(-1, payOrderQueryResult.getReturnMsg(), null);
    return JSON.toJSONString(queryPayResultVO);// 订单查询接口通信失败,直接返回
    }
  34.  
  35. // 订单查询通信成功:SUCCESS
  36.  
  37. // 若业务成功
  38.  
  39. /**
    * trade_state状态如下:
    * SUCCESS—支付成功
    * REFUND—转入退款
    * NOTPAY—未支付
    * CLOSED—已关闭
    * REVOKED—已撤销(刷卡支付)
    * USERPAYING--用户支付中
    * PAYERROR--支付失败(其他原因,如银行返回失败)商户订单支付失败需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付
    */
    String trade_state = payOrderQueryResult.getTradeState();
    if (!ConstantUtil.PAY_TRADE_STATE_NOTPAY.equals(trade_state) && null != trade_state) {// 非未支付状态下(可以为null-代表还未生成订单,故需过滤掉)
    PayResultVO tradePayResultVO = new PayResultVO(-1, ConstantUtil.PAY_STATUS_MAP.get(trade_state), null);
    return JSON.toJSONString(tradePayResultVO);
    }
    // 以下转入订单未支付处理或还没有生成订单(trade_state==null)的处理逻辑
  40.  
  41. //String openUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="+wxPayProperties.getAppId()+"&secret="+appSecret+"&code="+code+"&grant_type=authorization_code";
    String openId = weiXinService.getOpenId(code);//SendMsgUtil.httpRequest(openUrl,"GET","{}").getString("openid");// 获取openid
    // 更新订单openId--2019-09-17
    Map<String, Object> paMap = new HashMap<>();
    paMap.put("orderId", orderId);
    paMap.put("userOpenId", openId);
    sealOrderImplService.updateOrderInfo(paMap);
    // 获取本系统印章订单信息
    Map<String, Object> orderDetails = sealOrderImplService.getOrderMapById(orderId);
    String SUB_MCH_ID = (String) orderDetails.get("SUB_MCH_ID");// 营业网点扩展信息表中的子商户号-20190910
    if (StringUtils.isEmpty(SUB_MCH_ID)) {// 若无配置子商户号退出
    PayResultVO payResultVO = new PayResultVO(-1, "NO-SUB_MCH_ID", null);
    return JSON.toJSONString(payResultVO);
    }
    String siteName = (String) orderDetails.get("siteName");// 营业网点名称(刻章店名称)
    float orderPrice = ((BigDecimal) orderDetails.get("ORDER_AMOUNT")).floatValue();// 订单总金额
    String bodyName = siteName + "-" + ConstantUtil.ADVANCE_PAY_GOODS_NAME;// 商品名称严格按照规范-公众号支付-例如(商家名称-销售商品类目)
    // 设置统一下单请求参数
    // 请求对象,注意一些参数如appid、mchid等不用设置,方法内会自动从配置对象中获取到(前提是对应配置中已经设置)
    WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
    orderRequest.setAppid(wxPayProperties.getAppId());// 服务商appid
    orderRequest.setMchId(wxPayProperties.getMchId());// 商户号
    orderRequest.setSubMchId(SUB_MCH_ID);// 子商户号
    String nonceStr = DateUtil.get32UUIDMilli();
    orderRequest.setNonceStr(nonceStr);// 随机串,32位以内!
    orderRequest.setSignType(wxPayProperties.getSignType());// 签名类型
    orderRequest.setBody(bodyName);
    orderRequest.setOutTradeNo(orderId);
    orderRequest.setTotalFee(BaseWxPayRequest.yuanToFen(orderPrice + ""/*order.getTotalFee()*/));//元转成分
    orderRequest.setSpbillCreateIp(SPBILL_CREATE_IP);// 终端IP
    orderRequest.setNotifyUrl(wxPayProperties.getNotifyUrl());// 支付结果异步回调地址
    orderRequest.setTradeType(wxPayProperties.getTradeType());// 交易类型
    orderRequest.setOpenid(openId);// 设置openid
    WxPayMpOrderResult wxPayUnifiedOrderResult = wxService.createOrder(orderRequest);// 统一下单接口调用结果
  42.  
  43. String appId = wxPayUnifiedOrderResult.getAppId();
    String timeStamp = create_timestamp();
    String package_ = wxPayUnifiedOrderResult.getPackageValue();// ConstantUtil.PACKAGE_SUFFIX + wxPayUnifiedOrderResult.getPackageValue();
    String paySign = wxPayUnifiedOrderResult.getPaySign();
    Map<String, Object> resultMapData = new HashMap<>();
    resultMapData.put("appId", appId);
    resultMapData.put("timeStamp", timeStamp);
    resultMapData.put("package", package_);
    resultMapData.put("paySign", paySign);
    resultMapData.put("nonceStr", wxPayUnifiedOrderResult.getNonceStr());
    resultMapData.put("signType", wxPayProperties.getSignType());
    PayResultVO paySucessResultVO = new PayResultVO(0, "OK", resultMapData);
    return JSON.toJSONString(paySucessResultVO);
    } catch (Exception e) {
    Map map = new HashMap();
    map.put("data", e.getMessage());
    PayResultVO payResultVO = new PayResultVO(-1, "NO", map);
    return JSON.toJSONString(payResultVO);
    }
    }
  44.  
  45. /**
    * 支付回调
    *
    * @param xmlData
    * @return
    */
    public synchronized String payNotifyCallBack(String xmlData) {
    try {
  46.  
  47. WxPayOrderNotifyResult wxPayOrderNotifyResult = wxService.parseOrderNotifyResult(xmlData);// 解析xml字符串
    String return_code = wxPayOrderNotifyResult.getReturnCode();// SUCCESS/FAIL
  48.  
  49. if (ConstantUtil.PAY_RETURN_CODE_NO.equals(return_code)) {// 支付通信失败
    return WxPayNotifyResponse.fail("FAIL");
    }
    // 以下通信成功操作
    String out_trade_no = wxPayOrderNotifyResult.getOutTradeNo();// 商户系统内部订单号(即本系统印章订单号)
    // 内部订单支付情况
    Map<String, Object> orderDetails = sealOrderImplService.getOrderMapById(out_trade_no);
    if (null != orderDetails) {
    String paySta = (Integer) orderDetails.get("PAY_STATUS") + "";
    if ((ConstantUtil.ORDER_PAYSTATUS_1_ + "").equals(paySta)) {// 已支付直接返回
    return WxPayNotifyResponse.success("OK");
    }
    }
  50.  
  51. if (ConstantUtil.PAY_RETURN_CODE_OK.equals(return_code)) {// 支付接口通信成功
    String result_code = wxPayOrderNotifyResult.getResultCode();// SUCCESS/FAIL 是否真实支付成功
  52.  
  53. if (ConstantUtil.PAY_RETURN_CODE_OK.equals(result_code)) {// 支付成功
    // 检查本库是否已经插入支付成功记录
    // orderPayResultImplService.deleteByOrderId(out_trade_no);
    //orderPayResultImplService.add(wxPayOrderNotifyResult);
    //orderPayResultImplService.insertSelective(wxPayOrderNotifyResult);
    sealOrderImplService.updateOrderPaySta(out_trade_no, ConstantUtil.ORDER_PAYSTATUS_1_);// 更新支付状态
    // 推送微信内容-2019-09-18
    sealOrderSealImplService.sendWXMsgToSite(out_trade_no);
  54.  
  55. }
    if (ConstantUtil.PAY_RETURN_CODE_NO.equals(result_code)) {// 支付失败
    closePayOrder(out_trade_no);// 支付失败的话,需要关闭订单,才能重新支付
    sealOrderImplService.upOrderRelation(out_trade_no);// 更新该订单的id,主要用于重新支付
    sealOrderImplService.updateOrderPaySta(out_trade_no, ConstantUtil.ORDER_PAYSTATUS_2_);// 更新支付状态
    }
    return WxPayNotifyResponse.success("OK");
    } else {// 支付结果通信失败
    return WxPayNotifyResponse.fail("FAIL");
    }
  56.  
  57. } catch (Exception e) {
    return WxPayNotifyResponse.fail("FAIL");
    }
    }
  58.  
  59. /**
    * 查询订单状态
    *
    * @param orderId
    * @return
    */
    public WxPayOrderQueryResult wechatOrderQuery(String orderId) {
    try {
    if (StringUtils.isEmpty(orderId)) {
    return null;
    }
    Map<String, Object> orderDetails = sealOrderImplService.getOrderMapById(orderId);// 获取本系统订单的相关信息
    String SUB_MCH_ID = (String) orderDetails.get("SUB_MCH_ID");// 营业网点扩展信息表中的子商户号-20190910
    WxPayOrderQueryRequest wxPayOrderQueryRequest = new WxPayOrderQueryRequest();
    wxPayOrderQueryRequest.setAppid(wxPayProperties.getAppId());
    wxPayOrderQueryRequest.setMchId(wxPayProperties.getMchId());
    wxPayOrderQueryRequest.setSubMchId(SUB_MCH_ID);// 子商户好最终通过orderId从本系统获取
    wxPayOrderQueryRequest.setOutTradeNo(orderId);// 微信内部订单号与本系统订单号二选一
    wxPayOrderQueryRequest.setNonceStr(DateUtil.get32UUIDMilli());
    wxPayOrderQueryRequest.setSignType(wxPayProperties.getSignType());
    WxPayOrderQueryResult wxPayOrderQueryResult = wxService.queryOrder(wxPayOrderQueryRequest);
    return wxPayOrderQueryResult;
    } catch (Exception e) {
    WxPayOrderQueryResult wxPayOrderQueryResult = new WxPayOrderQueryResult();
    wxPayOrderQueryResult.setReturnCode(ConstantUtil.PAY_RETURN_CODE_OK);
    wxPayOrderQueryResult.setResultCode(ConstantUtil.PAY_RETURN_CODE_OK);
    wxPayOrderQueryResult.setTradeState(null);
    return wxPayOrderQueryResult;
    }
    }
  60.  
  61. /**
    * 关闭订单(关闭订单之后,重新提交支付需要更新订单号!)
    *
    * @param orderId
    * @return
    */
    public WxPayOrderCloseResult closePayOrder(String orderId) {
    if (StringUtils.isEmpty(orderId)) {
    return null;
    }
    try {
    Map<String, Object> orderDetails = sealOrderImplService.getOrderMapById(orderId);// 获取本系统订单的相关信息
    String SUB_MCH_ID = (String) orderDetails.get("SUB_MCH_ID");// 营业网点扩展信息表中的子商户号-20190910
    WxPayOrderCloseRequest wxPayOrderCloseRequest = new WxPayOrderCloseRequest();
    wxPayOrderCloseRequest.setAppid(wxPayProperties.getAppId());
    wxPayOrderCloseRequest.setMchId(wxPayProperties.getMchId());
    wxPayOrderCloseRequest.setSubMchId(SUB_MCH_ID);// 从本系统获取通过orderDetails
    wxPayOrderCloseRequest.setOutTradeNo(orderId);
    wxPayOrderCloseRequest.setNonceStr(DateUtil.get32UUIDMilli());
    wxPayOrderCloseRequest.setSignType(wxPayProperties.getSignType());
    WxPayOrderCloseResult wxPayOrderCloseResult = wxService.closeOrder(wxPayOrderCloseRequest);
    return wxPayOrderCloseResult;
    } catch (Exception e) {
    return null;
    }
    }
  62.  
  63. /**
    * 订单退款(接口请求并非退款结果)
    *
    * @param orderId
    * @return
    */
    public PayResultVO refundPayOrder(String orderId) {
    try {
    if (StringUtils.isEmpty(orderId)) {
    return null;
    }
    Map<String, Object> orderDetails = sealOrderImplService.getOrderMapById(orderId);// 获取本系统订单的相关信息
  64.  
  65. // 查询微信支付订单信息
    WxPayOrderQueryResult wxPayOrderQueryResult = wechatOrderQuery(orderId);
    String order_return_code = wxPayOrderQueryResult.getReturnCode();// 订单查询网络状态
    if (ConstantUtil.PREPAYID_RETURN_CODE_NO.equals(order_return_code)) {
    PayResultVO returnPayResultVO = new PayResultVO(-1, ConstantUtil.PAY_NETWORK_EXCEP, null);
    return returnPayResultVO;
    }
    String order_result_code = wxPayOrderQueryResult.getResultCode();// 订单查询业务状态
    if (ConstantUtil.PREPAYID_RETURN_CODE_NO.equals(order_result_code)) {
    PayResultVO resultPayResultVO = new PayResultVO(-1, ConstantUtil.REFUND_FAIL_MSG, null);
    return resultPayResultVO;
    }
    String order_trade_state = wxPayOrderQueryResult.getTradeState();// 订单状态(已支付、未支付等等)
    if (!ConstantUtil.PAY_TRADE_STATE_SUCCESS.equals(order_trade_state)) {// 未支付成功,直接返回
    PayResultVO tradePayResultVO = new PayResultVO(-1, ConstantUtil.REFUND_FAIL_MSG, null);
    }
    // 以下为订单已支付后续操作(只有order_trade_state为SUCCESS可获取如下参数)
    String transaction_id = wxPayOrderQueryResult.getTransactionId();// 微信内部支付订单号
    int total_fee = wxPayOrderQueryResult.getTotalFee();// 订单总金额
    String sub_mch_id = wxPayOrderQueryResult.getSubMchId();// 订单子商户号
    String out_trade_no = wxPayOrderQueryResult.getOutTradeNo();
  66.  
  67. WxPayRefundRequest wxPayRefundRequest = new WxPayRefundRequest();
    wxPayRefundRequest.setAppid(wxPayProperties.getAppId());
    wxPayRefundRequest.setMchId(wxPayProperties.getMchId());
    wxPayRefundRequest.setSubMchId(sub_mch_id);// 从本系统通过orderId获取
    wxPayRefundRequest.setNonceStr(DateUtil.get32UUIDMilli());
    wxPayRefundRequest.setSignType(wxPayProperties.getSignType());
    wxPayRefundRequest.setTransactionId(transaction_id);
    wxPayRefundRequest.setOutTradeNo(out_trade_no);// 商户内部订单号
    String refundNo = DateUtil.get32UUIDMilli();// 生成商户内部退款单号
    wxPayRefundRequest.setOutRefundNo(refundNo);// 商户内部退款单号!!!
    wxPayRefundRequest.setTotalFee(total_fee);
    wxPayRefundRequest.setRefundFee(total_fee);// !!!申请退款金额????
    wxPayRefundRequest.setNotifyUrl(wxPayProperties.getRefundNotifyUrl());// 退款结果通知url
    WxPayRefundResult wxPayRefundResult = wxService.refund(wxPayRefundRequest);// 订单退款接口
    String refund_return_code = wxPayRefundResult.getReturnCode();// 退款接口网络状态
    if (ConstantUtil.PREPAYID_RETURN_CODE_NO.equals(refund_return_code)) {
    PayResultVO refundExPayVO = new PayResultVO(-1, ConstantUtil.REFUND_FAIL_MSG, null);
    return refundExPayVO;
    }
    String refund_result_code = wxPayRefundResult.getResultCode();// 退款接口结果状态
    if (ConstantUtil.PREPAYID_RETURN_CODE_NO.equals(refund_result_code)) {
    PayResultVO refundFailPayVO = new PayResultVO(-1, ConstantUtil.REFUND_FAIL_MSG, null);
    return refundFailPayVO;
    }
  68.  
  69. // 以下为退款成功操作
    PayResultVO refundSuceesResultVO = new PayResultVO(0, "OK", wxPayRefundResult);
    Map<String, Object> paramMap = new HashMap<>();
    paramMap.put("refundNo", refundNo);// 内部退款单号
    sealOrderImplService.updateOrderInfo(paramMap);
    return refundSuceesResultVO;
    } catch (Exception e) {
    return null;
    }
    }
  70.  
  71. /**
    * 退款回调通知地址
    *
    * @param xmlData
    * @return
    */
    public synchronized String parseRefundNotifyResult(String xmlData) {
    try {
    WxPayRefundNotifyResult result = wxService.parseRefundNotifyResult(xmlData);
    String return_code = result.getReturnCode();
    if (ConstantUtil.PREPAYID_RETURN_CODE_NO.equals(return_code)) {// 通信失败
    return WxPayNotifyResponse.fail("FAIL");
    }
    // 以下为通信成功返回的字段信息
    WxPayRefundNotifyResult.ReqInfo reqInfo = result.getReqInfo();// 获取结果中的加密信息
  72.  
  73. /**
    * SUCCESS-退款成功
    * CHANGE-退款异常
    * REFUNDCLOSE—退款关闭
    */
    String refund_status = reqInfo.getRefundStatus();// 退款状态
  74.  
  75. if (ConstantUtil.REFUND_STATUS_0.equals(refund_status)) {// 成功
    refund_status = ConstantUtil.ORDER_PAYSTATUS_3_ + "";
    }
    if (ConstantUtil.REFUND_STATUS_1.equals(refund_status)) {// 异常
    refund_status = ConstantUtil.ORDER_PAYSTATUS_4_ + "";
    }
    if (ConstantUtil.REFUND_STATUS_2.equals(refund_status)) {// 关闭
    refund_status = ConstantUtil.ORDER_PAYSTATUS_4_ + "";
    }
    String out_trade_no = reqInfo.getOutTradeNo();// 内部订单号
    Map<String, Object> paramMap = new HashMap<>();
    paramMap.put("refundNo", out_trade_no);// 内部退款单号
    paramMap.put("payStatus", refund_status);// 更新支付状态
    sealOrderImplService.updateOrderInfo(paramMap);
    orderRefundResultImplService.deleteByOrderdId(out_trade_no);// 删除订单
    orderRefundResultImplService.addRefundInfo(result);// 添加退款 记录
    return WxPayNotifyResponse.success("OK");
    } catch (Exception e) {
    return WxPayNotifyResponse.fail("FAIL");
    }
    }
  76.  
  77. //******************************************************************************************************************
  78.  
  79. /**
    * 获取微信签名信息(预支付订单信息)
    *
    * @param payVO
    * @param request
    * @return
    */
    public synchronized String getWxPaySign(PayVO payVO, HttpServletRequest request) {
    try {
    if (null == payVO) {
    return null;
    }
    String orderId = payVO.getOrderId();
    String wxCode = payVO.getCode();
    if (StringUtils.isEmpty(orderId) || StringUtils.isEmpty(wxCode)) {
    return null;
    }
    // 获取openId
    JSONObject openIdJson = getAccess_tokenByCode(wxCode);
    int errcode_openId = openIdJson.getInt("errcode");
    if (errcode_openId != 0) {// 调用获取openId接口不成功
    PayRespCodeMsg payRespCodeMsg = new PayRespCodeMsg(errcode_openId, openIdJson.getString("errmsg"));
    PayResultVO payResultVO = new PayResultVO(payRespCodeMsg, null);
    return JSON.toJSONString(payResultVO);
    }
    String openid = openIdJson.getString("openid");// 用户唯一标识
    String session_key = openIdJson.getString("session_key");// 会话秘钥
  80.  
  81. // 获取本地订单相关信息
    Map<String, Object> orderDetails = sealOrderImplService.getOrderMapById(orderId);
    String siteName = (String) orderDetails.get("siteName");// 营业网点名称(刻章店名称)
    int orderPrice = ((BigDecimal) orderDetails.get("ORDER_AMOUNT")).intValue() * 100;
    String bodyName = siteName + "-" + ConstantUtil.ADVANCE_PAY_GOODS_NAME;// 商品名称严格按照规范-公众号支付-例如(商家名称-销售商品类目)
    String orderno = orderId;// "1234567890";
    Integer total_fee = orderPrice;// "8.88";// 订单总费用,不能有小数点,以分为单位
    String nonce_str = create_nonce_str();
    String timestamp = create_timestamp();
  82.  
  83. // 获取预支付订单prepayId
    SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
    String prepayIdResult = getPrepayId(request, bodyName, orderno, total_fee, openid);// 获取预支付订单id方法,返回的xml字符串结果
    Map<String, String> prepayIdResultMap = doXMLParse(prepayIdResult);// xml字符串转map
    String prepayId_return_code = prepayIdResultMap.get("return_code");// SUCCESS/FAIL,此字段是通信标识,非交易标识,交易是否成功需要查看result_code来判断
    if (ConstantUtil.PREPAYID_RETURN_CODE_NO.equals(prepayId_return_code)) {// 获取prepayId接口通信异常
    PayRespCodeMsg preRespCodeMsg = new PayRespCodeMsg(-1, ConstantUtil.WECHAT_INTERFACE_EX_MSG);//
    PayResultVO preResultVO = new PayResultVO(preRespCodeMsg, null);
    return JSON.toJSONString(preResultVO);
    }
    // 获取prepayId的接口调用通信成功后
    String prepayId_result_code = prepayIdResultMap.get("result_code");// SUCCESS/FAIL,业务结果
    if (ConstantUtil.PAY_RETURN_CODE_NO.equals(prepayId_result_code)) {// 获取prepayId失败
    PayRespCodeMsg preNo = new PayRespCodeMsg(-1, "NO");
    PayResultVO preNoVO = new PayResultVO(preNo, null);
    return JSON.toJSONString(preNoVO);
    }
  84.  
  85. // 微信预支付接口通信成功且业务结果成功
    // 重新生成签名
    parameters.put("appId", wechatPayConfigBean.getAppId());
    parameters.put("timeStamp", timestamp);
    parameters.put("nonceStr", nonce_str);
    parameters.put("package", "prepay_id=" + prepayIdResultMap.get("prepay_id"));
    parameters.put("signType", "MD5");
    String sign = createSign("UTF-8", parameters);
    parameters.put("prepay_id", "prepay_id=" + prepayIdResultMap.get("prepay_id"));
    parameters.put("paySign", sign);
  86.  
  87. PayResultVO okResult = new PayResultVO(0, "OK", parameters);
    return JSON.toJSONString(okResult);
  88.  
  89. } catch (Exception e) {
    PayResultVO resultVO = new PayResultVO(-1, "FALSE", null);
    return JSON.toJSONString(resultVO);
    }
    }
  90.  
  91. public SortedMap<Object, Object> WapSignSignatureAction (HttpServletRequest request)throws Exception {
    SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
    String code = request.getParameter("code");
    System.out.println("code-------------" + code);
    // code作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。
    // 通过code换取网页授权access_token
    Map<String, String> data = getAccess_tokenByCode(code);
    String openid = data.get("openid");
    String tName = "名称";
    String orderno = "1234567890";
    String total_fee = "8.88";
    String nonce_str = create_nonce_str();
    String timestamp = create_timestamp();
    // 获取prepayId
    try {
    String result = getPrepayId(request, tName, orderno, Integer.parseInt(total_fee), openid);
    Map<String, String> map = doXMLParse(result);
    // 重新生成签名
    parameters.put("appId", wechatPayConfigBean.getAppId()
    );
    parameters.put("timeStamp", timestamp);
    parameters.put("nonceStr", nonce_str);
    parameters.put("package", "prepay_id=" + map.get("prepay_id"));
    parameters.put("signType", "MD5");
    String sign = createSign("UTF-8", parameters);
    parameters.put("prepay_id", "prepay_id=" + map.get("prepay_id"));
    parameters.put("paySign", sign);
    } catch (Exception e) {
    e.printStackTrace();
    }
    return parameters;
    }
  92.  
  93. /**
    * 根据用户授权code获取access_token
    */
    private JSONObject getAccess_tokenByCode(String code) {
    Map<String, String> data = new HashMap<String, String>();
    //String requestUrlMessageFormat = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={0}&secret={1}&code={2}&grant_type=authorization_code";
    String requestUrlMessageFormat = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={0}&secret={1}&code={2}&grant_type=authorization_code";
    String requestUrl = MessageFormat.format(requestUrlMessageFormat, wxPayProperties.getAppId()
  94.  
  95. , appSecret
  96.  
  97. , code);
    String requestMethod = "GET";
    String outputStr = "";
    JSONObject json = httpRequest(requestUrl, requestMethod, outputStr);
    //String access_token = (String) json.get("access_token");// 此参数最新接口文档没发现的
    String openid = (String) json.get("openid");// 用户唯一标识
    String session_key = json.getString("session_key");// 会话秘钥
    String unionid = json.getString("unionid");// 用户在开放平台的唯一标识符,在满足 UnionID 下发条件的情况下会返回
    int errcode = json.getInt("errcode");// 错误码
    String errmsg = json.getString("errmsg");// 错误信息
    //data.put("access_token", access_token);
    data.put("openid", openid);
    data.put("session_key", session_key);
    data.put("unionid", unionid);
    data.put("errcode", errcode + "");
    data.put("errmsg", errmsg);
    return json;
    }
  98.  
  99. /**
    * 发起https请求并获取结果
    *
    * @param requestUrl 请求地址
    * @param requestMethod 请求方式(GET、POST)
    * @param outputStr 提交的数据
    * @return JSONObject(通过JSONObject.get ( key)的方式获取json对象的属性值)
    */
    public static JSONObject httpRequest(String requestUrl, String requestMethod, String outputStr) {
    JSONObject jsonObject = null;
    StringBuffer buffer = new StringBuffer();
    try {
    // 创建SSLContext对象,并使用我们指定的信任管理器初始化
    TrustManager[] tm = {new MyX509TrustManager()};
    SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
    sslContext.init(null, tm, new java.security.SecureRandom());
    // 从上述SSLContext对象中得到SSLSocketFactory对象
    SSLSocketFactory ssf = sslContext.getSocketFactory();
  100.  
  101. URL url = new URL(requestUrl);
    HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();
    httpUrlConn.setSSLSocketFactory(ssf);
  102.  
  103. httpUrlConn.setDoOutput(true);
    httpUrlConn.setDoInput(true);
    httpUrlConn.setUseCaches(false);
    // 设置请求方式(GET/POST)
    httpUrlConn.setRequestMethod(requestMethod);
  104.  
  105. if ("GET".equalsIgnoreCase(requestMethod))
    httpUrlConn.connect();
  106.  
  107. // 当有数据需要提交时
    if (null != outputStr) {
    OutputStream outputStream = httpUrlConn.getOutputStream();
    // 注意编码格式,防止中文乱码
    outputStream.write(outputStr.getBytes("UTF-8"));
    outputStream.close();
    }
  108.  
  109. // 将返回的输入流转换成字符串
    InputStream inputStream = httpUrlConn.getInputStream();
    InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
    BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
  110.  
  111. String str = null;
    while ((str = bufferedReader.readLine()) != null) {
    buffer.append(str);
    }
    bufferedReader.close();
    inputStreamReader.close();
    // 释放资源
    inputStream.close();
    inputStream = null;
    httpUrlConn.disconnect();
    jsonObject = JSONObject.fromObject(buffer.toString());
    } catch (ConnectException ce) {
    System.err.println("Weixin server connection timed out.");
    } catch (Exception e) {
    System.err.println("https request error");
    }
    return jsonObject;
    }
  112.  
  113. /**
    * 获取预支付订单id
    *
    * @param request
    * @param name
    * @param orderno
    * @param total_fee
    * @param openid
    * @return
    * @throws Exception
    */
    public String getPrepayId(HttpServletRequest request, String name, String orderno, Integer total_fee,
    String openid) throws Exception {
    SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
    parameters.put("appid", wechatPayConfigBean.getAppId()
  114.  
  115. );// 服务商id
    parameters.put("mch_id", wechatPayConfigBean.getMchId()
  116.  
  117. );// 商户号
    parameters.put("nonce_str", CreateNoncestr());// 随机字符串
    parameters.put("body", name);// 商品描述
    parameters.put("out_trade_no", orderno);// 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*且在同一个商户号下唯一
    parameters.put("total_fee", total_fee);// 支付金额单位:分
    parameters.put("spbill_create_ip", getIp2(request));// 支持IPV4和IPV6两种格式的IP地址。调用微信支付API的机器IP
    parameters.put("notify_url", wechatPayConfigBean.getPayResultNotifyUrl());// 接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数
    parameters.put("trade_type", "JSAPI");// 交易类型,小程序取值如下:JSAPI,详细说明见
    parameters.put("openid", openid);// 商户标识
    parameters.put("time_expire", DateUtil.getTimeStamp());// (非必填)交易结束时间,需要动态传入,格式为yyyyMMddHHmmss-20190719114936
    String sign = createSign("UTF-8", parameters);
    parameters.put("sign", sign);
    String requestXML = getRequestXml(parameters);
    // 调用统一下单接口
    String result = httpsRequest("https://api.mch.weixin.qq.com/pay/unifiedorder", "POST", requestXML);
    System.out.println(result);
    return result;
    }
  118.  
  119. public static String CreateNoncestr() {
    String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    String res = "";
    for (int i = 0; i < 16; i++) {
    Random rd = new Random();
    res += chars.charAt(rd.nextInt(chars.length() - 1));
    }
    return res;
    }
  120.  
  121. public static String getIp2(HttpServletRequest request) {
    String ip = request.getHeader("X-Forwarded-For");
    if (!org.apache.commons.lang3.StringUtils.isEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
    // 多次反向代理后会有多个ip值,第一个ip才是真实ip
    int index = ip.indexOf(",");
    if (index != -1) {
    return ip.substring(0, index);
    } else {
    return ip;
    }
    }
    ip = request.getHeader("X-Real-IP");
    if (!org.apache.commons.lang3.StringUtils.isEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
    return ip;
    }
    return request.getRemoteAddr();
    }
  122.  
  123. @SuppressWarnings("rawtypes")
    public String createSign(String characterEncoding, SortedMap<Object, Object> parameters) {
    StringBuffer sb = new StringBuffer();
    Set es = parameters.entrySet();
    Iterator it = es.iterator();
    while (it.hasNext()) {
    Map.Entry entry = (Map.Entry) it.next();
    String k = (String) entry.getKey();
    Object v = entry.getValue();
    if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
    sb.append(k + "=" + v + "&");
    }
    }
    sb.append("key=" + wechatPayConfigBean.getApiKey()
    );
    String sign = MD5Encode(sb.toString(), characterEncoding).toUpperCase();
    return sign;
    }
  124.  
  125. public static String MD5Encode(String origin, String charsetname) {
    String resultString = null;
    try {
    resultString = new String(origin);
    MessageDigest md = MessageDigest.getInstance("MD5");
    if (charsetname == null || "".equals(charsetname))
    resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
    else
    resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));
    } catch (Exception exception) {
    }
    return resultString;
    }
  126.  
  127. private static String byteArrayToHexString(byte b[]) {
    StringBuffer resultSb = new StringBuffer();
    for (int i = 0; i < b.length; i++)
    resultSb.append(byteToHexString(b[i]));
    return resultSb.toString();
    }
  128.  
  129. private static String byteToHexString(byte b) {
    int n = b;
    if (n < 0)
    n += 256;
    int d1 = n / 16;
    int d2 = n % 16;
    return hexDigits[d1] + hexDigits[d2];
    }
  130.  
  131. private static final String hexDigits[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d",
    "e", "f"};
  132.  
  133. @SuppressWarnings("rawtypes")
    public static String getRequestXml(SortedMap<Object, Object> parameters) {
    StringBuffer sb = new StringBuffer();
    sb.append("<xml>");
    Set es = parameters.entrySet();
    Iterator it = es.iterator();
    while (it.hasNext()) {
    Map.Entry entry = (Map.Entry) it.next();
    String k = (String) entry.getKey();
    String v = (String) entry.getValue();
    if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {
    sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
    } else {
    sb.append("<" + k + ">" + v + "</" + k + ">");
    }
    }
    sb.append("</xml>");
    return sb.toString();
    }
  134.  
  135. /**
    * * 发送https请求
    *
    * @param requestUrl 请求地址
    * @param requestMethod 请求方式(GET、POST)
    * @param outputStr 提交的数据
    * @return 返回微信服务器响应的信息
    */
    public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {
    try {
    // 创建SSLContext对象,并使用我们指定的信任管理器初始化
    TrustManager[] tm = {new MyX509TrustManager()};
    SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
    sslContext.init(null, tm, new java.security.SecureRandom());
    // 从上述SSLContext对象中得到SSLSocketFactory对象
    SSLSocketFactory ssf = sslContext.getSocketFactory();
    URL url = new URL(requestUrl);
    HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
    conn.setSSLSocketFactory(ssf);
    conn.setDoOutput(true);
    conn.setDoInput(true);
    conn.setUseCaches(false);
    // 设置请求方式(GET/POST)
    conn.setRequestMethod(requestMethod);
    conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
    // 当outputStr不为null时向输出流写数据
    if (null != outputStr) {
    OutputStream outputStream = conn.getOutputStream();
    // 注意编码格式
    outputStream.write(outputStr.getBytes("UTF-8"));
    outputStream.close();
    }
    // 从输入流读取返回内容
    InputStream inputStream = conn.getInputStream();
    InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
    BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
    String str = null;
    StringBuffer buffer = new StringBuffer();
    while ((str = bufferedReader.readLine()) != null) {
    buffer.append(str);
    }
    // 释放资源
    bufferedReader.close();
    inputStreamReader.close();
    inputStream.close();
    inputStream = null;
    conn.disconnect();
    return buffer.toString();
    } catch (ConnectException ce) {
    System.out.println("连接超时");
    } catch (Exception e) {
    System.out.println("请求异常");
    }
    return null;
    }
  136.  
  137. public static String create_nonce_str() {
    return UUID.randomUUID().toString().replace("-", "");
    }
  138.  
  139. public static String create_timestamp() {
    return Long.toString(System.currentTimeMillis() / 1000);
    }
  140.  
  141. /**
    * 解析xml
    *
    * @param strxml
    * @return
    * @throws Exception
    */
    @SuppressWarnings({"rawtypes", "unchecked"})
    public static Map doXMLParse(String strxml) throws Exception {
    strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
  142.  
  143. if (null == strxml || "".equals(strxml)) {
    return null;
    }
  144.  
  145. Map m = new HashMap();
  146.  
  147. InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
    SAXBuilder builder = new SAXBuilder();
    Document doc = builder.build(in);
    Element root = doc.getRootElement();
    List list = root.getChildren();
    Iterator it = list.iterator();
    while (it.hasNext()) {
    Element e = (Element) it.next();
    String k = e.getName();
    String v = "";
    List children = e.getChildren();
    if (children.isEmpty()) {
    v = e.getTextNormalize();
    } else {
    v = getChildrenText(children);
    }
  148.  
  149. m.put(k, v);
    }
    // 关闭流
    in.close();
    return m;
    }
  150.  
  151. /**
    * 获取子结点的xml
    *
    * @param children
    * @return
    */
    @SuppressWarnings("rawtypes")
    public static String getChildrenText(List children) {
    StringBuffer sb = new StringBuffer();
    if (!children.isEmpty()) {
    Iterator it = children.iterator();
    while (it.hasNext()) {
    Element e = (Element) it.next();
    String name = e.getName();
    String value = e.getTextNormalize();
    List list = e.getChildren();
    sb.append("<" + name + ">");
    if (!list.isEmpty()) {
    sb.append(getChildrenText(list));
    }
    sb.append(value);
    sb.append("</" + name + ">");
    }
    }
    return sb.toString();
    }
  152.  
  153. }

微信支付第三方sdk使用的更多相关文章

  1. 微信支付的SDK曝出重大漏洞(XXE漏洞)

    一.背景 昨天(2018-07-04)微信支付的SDK曝出重大漏洞(XXE漏洞),通过该漏洞,攻击者可以获取服务器中目录结构.文件内容,如代码.各种私钥等.获取这些信息以后,攻击者便可以为所欲为,其中 ...

  2. 微信支付V3 SDK Payment Spring Boot 1.0.6 发布,实现留守红包,助力抗疫

    春节将至,为防控疫情,多地政府提倡员工.外来务工者留守本地过年,并鼓励企业向员工发放"留守红包".为此,微信支付推出"春节留守红包"能力,希望可以协助有发放需求 ...

  3. 微信支付PHP SDK —— 公众号支付代码详解

    在微信支付 开发者文档页面 下载最新的 php SDK http://mch.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1 这里假设你已经申请完微 ...

  4. 微信支付官方SDK V3 .NET版的坑

    但是支付成功后却不能正确的执行支付结果js回调函数.看看其页面的点击事件是放在asp:Button上面的.我们知道在asp.net webform中,按钮的点击是有页面回调后台的.也就是其实点击了之后 ...

  5. Unity接入微信登录 微信分享 微信支付 支付宝SDK

    你将会学到的unity集成SDK游戏中接入微信支付与支付宝支付游戏中接入微信登录与微信分享 目录 mp4格式,大小2.2GB 扫码时备注或说明中留下邮箱 付款后如未回复请至https://shop13 ...

  6. Android应用之——微信微博第三方sdk登录分享使用过程中的一些常见问题

    前言 近期在使用第三方登录和分享的过程中遇到了非常多问题,一方面能够归结为自己经验的不足,还有一方面事实上也说明了官方文档的含糊不清.这篇博文不会写关于怎样使用第三方登录分享,由于官方文档已经写明了步 ...

  7. 微信支付.NET SDK 中的BUG(存疑)

    BUG出现在类文件WxPayData.cs中的FromXml(string xml)方法 /** * @将xml转为WxPayData对象并返回对象内部的数据 * @param string 待转换的 ...

  8. Android微信支付SDK

    App对接微信调起微信支付需要在微信平台注册,鉴别的标识就是App的包名,所以将申请的包名单独打包成一个Apk文件,则在其他的App调起此Apk的时候同样可以起到调用微信支付的功能.这样就实现了调起微 ...

  9. iOS-关于微信支付

    突然发现的一篇文章,这位博主介绍的还是挺详细的,给大家分享一下 不懂的也可以咨询我qq:564702640 1.申请接入 详见 微信支付申请接入 . 创建应用+审核通过,你将得到:APP_ID.APP ...

随机推荐

  1. x86平台inline hook原理和实现

    概念 inline hook是一种通过修改机器码的方式来实现hook的技术. 原理 对于正常执行的程序,它的函数调用流程大概是这样的: 0x1000地址的call指令执行后跳转到0x3000地址处执行 ...

  2. java调用存储过程(stored procedures)的HelloWorld例子

    1.java调用存储过程(stored procedures)的HelloWorld程序 有点数据 库基础的人都知道.存储过程(stored procedures)和java没什么关系.它是一段纯粹的 ...

  3. 吴裕雄--天生自然C++语言学习笔记:C++ 命名空间

    假设这样一种情况,当一个班上有两个名叫 Zara 的学生时,为了明确区分它们,在使用名字之外,不得不使用一些额外的信息,比如他们的家庭住址,或者他们父母的名字等等. 同样的情况也出现在 C++ 应用程 ...

  4. 吴裕雄 Bootstrap 前端框架开发——Bootstrap 字体图标(Glyphicons):glyphicon glyphicon-barcode

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name ...

  5. RabbitMQ系列文章

    详解 RabbitMQ 管理界面解析框架 (一) RabbitMQ系列(二)深入了解RabbitMQ工作原理及简单使用 RabbitMQ windows安装步骤 RabbitMQ管理页面各种属性详解 ...

  6. 第二篇MTV模型、基本命令、简单配置

    MTV模型.基本命令.简单配置 阅读目录(Content) MTV模型 基本命令 简单配置 MTV模型 Django的MTV分别代表: Model(模型):负责业务对象与数据库的对象(ORM) Tem ...

  7. 查看mysql表空间

    ,),'MB') as data_size, concat(,),'MB') as index_size from information_schema.tables group by TABLE_S ...

  8. UVA - 10384 The Wall Pusher(推门游戏)(IDA*)

    题意:从起点出发,可向东南西北4个方向走,如果前面没有墙则可走:如果前面只有一堵墙,则可将墙向前推一格,其余情况不可推动,且不能推动游戏区域边界上的墙.问走出迷宫的最少步数,输出任意一个移动序列. 分 ...

  9. 用Visual studio11在Windows8上开发驱动实现注册表监控和过滤

    在Windows NT中,80386保护模式的“保护”比Windows 95中更坚固,这个“镀金的笼子”更加结实,更加难以打破.在Windows 95中,至少应用程序I/O操作是不受限制的,而在Win ...

  10. 用Python复制文件的9个方法(转)

    转自:https://zhuanlan.zhihu.com/p/35725217 用Python复制文件的9个方法 Python 中有许多“开盖即食”的模块(比如 os,subprocess 和 sh ...