微信小程序支付以及退款功能(超详细)

Springboot整合微信小程序支付

  • 第一步:
  • 第二步:导入微信支付的依赖
  • 第三步:在springboot核心配置文件中配置参数
  • 第四步:创建配置类WxPayAppConfig
  • 第五步:支付方法
    • service层方法
    • Controller层方法

第一步:

先准备好
appID:小程序id
mchID: 商户号
key: 秘钥
certPath:从微信商户平台下载的安全证书存放的路径(apiclient_cert.p12的路径)
payNotifyUrl: 微信支付成功的异步通知接口 (小程序的话这个路径必须是https的)

第二步:导入微信支付的依赖

		<dependency>
			<groupId>com.github.wxpay</groupId>
			<artifactId>wxpay-sdk</artifactId>
			<version>0.0.3</version>
		</dependency>

第三步:在springboot核心配置文件中配置参数

	#微信app支付
pay:
  wxpay:
     app:
        appID:小程序id
        mchID: 商户号
        key: ak秘钥
        certPath:从微信商户平台下载的安全证书存放的路径(apiclient_cert.p12的路径)
      	payNotifyUrl: 微信支付成功的异步通知接口  (小程序的话这个路径必须是https的)

第四步:创建配置类WxPayAppConfig

下面展示一些 内联代码片

/**
 * 配置我们自己的信息
 */
@Component
@ConfigurationProperties(prefix = "pay.wxpay.app")
public class WxPayAppConfig implements WXPayConfig {
    /**
     * appID
     */
    private String appID;

    /**
     * 商户号
     */
    private String mchID;

    /**
     * API 密钥
     */
    private String key;

    /**
     * API证书绝对路径 (本项目放在了 resources/cert/wxpay/apiclient_cert.p12")
     */
    private String certPath;

    /**
     * HTTP(S) 连接超时时间,单位毫秒
     */
    private int httpConnectTimeoutMs = 8000;

    /**
     * HTTP(S) 读数据超时时间,单位毫秒
     */
    private int httpReadTimeoutMs = 10000;

    /**
     * 微信支付异步通知地址
     */
    private String payNotifyUrl;

    /**
     * 微信退款异步通知地址
     */
    private String refundNotifyUrl;

    /**
     * 获取商户证书内容(这里证书需要到微信商户平台进行下载)
     *
     * @return 商户证书内容
     */
    @Override
    public InputStream getCertStream() {
        InputStream certStream  =getClass().getClassLoader().getResourceAsStream(certPath);
        return certStream;
    }

第五步:支付方法

service层方法

下面展示 支付代码片

    public ResultMap unifiedOrder(String orderNo, double amount, String body,String spbill_create_ip) {
        Map<String, String> returnMap = new HashMap<>();
        Map<String, String> responseMap = new HashMap<>();
        Map<String, String> requestMap = new HashMap<>();
        try {
            Long time = System.currentTimeMillis() / 1000;
            String timestamp = time.toString();
            WXPay wxpay = new WXPay(wxPayAppConfig);
            requestMap.put("body", body);                                     // 商品描述
            requestMap.put("out_trade_no", orderNo);                          // 商户订单号
            requestMap.put("total_fee", String.valueOf((int)(amount*100)));   // 总金额
            requestMap.put("spbill_create_ip",spbill_create_ip); // 终端IP
            requestMap.put("trade_type", "APP");                              // App支付类型
            Orders orders = ordersMapper.FindOrder(orderNo);
            UserInfo u =userMapper.seluserByid(orders.getOpenId());
            String payVerifApp = u.getPayVerifApp();
            requestMap.put("openid",payVerifApp);
            requestMap.put("notify_url", wxPayAppConfig.getPayNotifyUrl());   // 接收微信支付异步通知回调地址
            Map<String, String> resultMap = wxpay.unifiedOrder(requestMap);
            //获取返回码
            String returnCode = resultMap.get("return_code");
            String returnMsg = resultMap.get("return_msg");
            //若返回码为SUCCESS,则会返回一个result_code,再对该result_code进行判断
            if ("SUCCESS".equals(returnCode)) {
               /* int update =ordersMapper.updateOrder(DateUtil.now(),orderNo);*/
                String resultCode = resultMap.get("result_code");
                String errCodeDes = resultMap.get("err_code_des");
                if ("SUCCESS".equals(resultCode)) {
                    responseMap = resultMap;
                }
            }
            if (responseMap == null || responseMap.isEmpty()) {
                return ResultMap.error("获取预支付交易会话标识失败");
            }
            // 3、签名生成算法
            returnMap.put("appid",wxPayAppConfig.getAppID());
            returnMap.put("partnerid",wxPayAppConfig.getMchID());
            returnMap.put("prepayid", responseMap.get("prepay_id"));
            returnMap.put("noncestr", responseMap.get("nonce_str"));
            returnMap.put("timestamp", timestamp);
            returnMap.put("package", "Sign=WXPay");
            returnMap.put("sign", WXPayUtil.generateSignature(returnMap,wxPayAppConfig.getKey()));//微信支付签名

            return ResultMap.ok().put("data", returnMap);
        } catch (Exception e) {
            logger.error("订单号:{},错误信息:{}", orderNo, e.getMessage());
            return ResultMap.error("微信支付统一下单失败");
        }
    }

下面展示 支付回调方法

// An highlighted block

public String notify(String notifyStr) {
        String xmlBack = " ";
        try {
            // 转换成map
            Map<String, String> resultMap = WXPayUtil.xmlToMap(notifyStr);
            WXPay wxpayApp = new WXPay(wxPayAppConfig);
            if (wxpayApp.isPayResultNotifySignatureValid(resultMap)) {
                String returnCode = resultMap.get("return_code");  //状态
                String outTradeNo = resultMap.get("out_trade_no");//商户订单号
                String transactionId = resultMap.get("transaction_id");
                if (returnCode.equals("SUCCESS")) {
                    if (StringUtils.isNotBlank(outTradeNo)) {
                        /**
                         * 注意!!!
                         * 请根据业务流程,修改数据库订单支付状态,和其他数据的相应状态
                         */
                        
                        xmlBack = "";

                    }
                }else {
                 
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return xmlBack;
    }

下面展示退款代码片


    @Override
    public ResultMap refund(String orderNo){
    	//获取订单
        Orders orders = ordersMapper.FindOrder(orderNo);
        String checkInTime = orders.getCheckInTime();
        Integer payState = orders.getPayState();
        Double amount=orders.getFactPrice();
        //判断订单是否可以退款
        if (payState != 1 && payState != 8 && payState != 5 && payState != 10) {
            return ResultMap.error("该订单无法申请退款");
        }
        SimpleDateFormat formatter= new SimpleDateFormat("yyyy-MM-dd");
        Date date = new Date();
        String today = formatter.format(date);
        int compareTo = today.compareTo(checkInTime);
        if(payState != 8){
            if (compareTo >= 0 || BetweenDate.dayBetween(today, checkInTime).size() == 1) {
                return ResultMap.error("该订单无法申请退款");
            }
        }

        if(StringUtils.isBlank(orderNo)){
            return ResultMap.error("订单编号不能为空");
        }

        if(amount <= 0){
            return ResultMap.error("退款金额必须大于0");
        }
		//退款开始
        Map<String, String> responseMap = new HashMap<>();
/*        Map requestMap = new HashMap<>();*/
      /*  WXPay wxpay = new WXPay(wxPayAppConfig);*/
        Map<String, String> resultData= new HashMap<>();
        try {
          /*  requestMap.put("appid",wxPayAppConfig.getAppID());
            requestMap.put("op_user_id",wxPayAppConfig.getMchID());
            requestMap.put("nonce_str", orderNo);
            requestMap.put("out_trade_no", orderNo);
            requestMap.put("out_refund_no",orderNo);
    *//*    requestMap.put("out_refund_no", UUIDGenerator.getOrderNo());*//*
            requestMap.put("total_fee", String.valueOf(amount));
            requestMap.put("refund_fee", String.valueOf((int)(amount*100)));//所需退款金额
            requestMap.put("refund_desc", "民宿退款");
            requestMap.put("refund_fee_type", "CNY");
            requestMap.put("sign", WXPayUtil.generateSignature(requestMap,wxPayAppConfig.getKey()));
            responseMap = wxpay.refund(requestMap);*/
            String outRefundNo = IdUtil.fastSimpleUUID();
            DecimalFormat df = new DecimalFormat("######0");
            String total_fee = String.valueOf(df.format((orders.getFactPrice() * 100)));
            HashMap<String, String> map = new HashMap<String, String>();
            map.put("appid", wxPayAppConfig.getAppID());
            map.put("mch_id", wxPayAppConfig.getMchID());
            map.put("nonce_str", outRefundNo);
            map.put("out_trade_no", orderNo);
            map.put("out_refund_no", outRefundNo);
            map.put("total_fee", total_fee);
            map.put("refund_fee", total_fee);
            map.put("op_user_id", wxPayAppConfig.getMchID());
            map.put("refund_fee_type", "CNY");
            String paySign = getPayCustomSign(map, wxPayAppConfig.getKey());
            map.put("sign", paySign);
            String xml = ArrayToXml(map);
            String url = "https://api.mch.weixin.qq.com/secapi/pay/refund";
            String post = post(url, xml);
            resultData = doXMLParse(post);
        } catch (Exception e) {
            e.printStackTrace();
        }
        String return_code = resultData.get("return_code");   //返回状态码
        String return_msg = resultData.get("return_msg");     //返回信息
        if ("SUCCESS".equals(return_code)) {
            String result_code = resultData.get("result_code");       //业务结果
            String err_code_des = resultData.get("err_code_des");     //错误代码描述
            if ("SUCCESS".equals(result_code)) {
                //修改用户订单状态为已退款
                Integer refund = ordersMapper.refund(orderNo);
                return ResultMap.ok("退款申请成功");
            } else {
                logger.info("订单号:{}错误信息:{}", orderNo, err_code_des);
                return ResultMap.error(err_code_des);
            }
        } else {
            logger.info("订单号:{}错误信息:{}", orderNo, return_msg);
            return ResultMap.error(return_msg);
        }
    }

支付所需要的方法 代码片


    public static String getPayCustomSign(HashMap<String, String> bizObj, String key) throws Exception {
        String bizString = FormatBizQueryParaMap(bizObj, false);
        return sign(bizString, key);
    }


    public static String FormatBizQueryParaMap(HashMap<String, String> paraMap, boolean urlencode) throws Exception {
        String buff = "";
        try {
            List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>(paraMap.entrySet());
            Collections.sort(infoIds, new Comparator<Map.Entry<String, String>>() {
                public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) {
                    return (o1.getKey()).toString().compareTo(o2.getKey());
                }
            });
            for (int i = 0; i < infoIds.size(); i++) {
                Map.Entry<String, String> item = infoIds.get(i);
                if (item.getKey() != "") {
                    String key = item.getKey();
                    String val = item.getValue();
                    if (urlencode) {
                        val = URLEncoder.encode(val, "utf-8");
                    }
                    buff += key + "=" + val + "&";
                }
            }
            if (buff.isEmpty() == false) {
                buff = buff.substring(0, buff.length() - 1);
            }
        } catch (Exception e) {
            throw new Exception(e.getMessage());
        }
        return buff;
    }

    public static String sign(String content, String key) throws Exception {
        String signStr = "";
        signStr = content + "&key=" + key;
        return MD5Util.md5Encrypt32Upper(signStr);
    }
    public static String ArrayToXml(Map<String, String> arr) {
        String xml = "";
        Iterator<Map.Entry<String, String>> iter = arr.entrySet().iterator();
        while (iter.hasNext()) {
            Map.Entry<String, String> entry = iter.next();
            String key = entry.getKey();
            String val = entry.getValue();
            if (IsNumeric(val)) {
                xml += "<" + key + ">" + val + " + key + ">";
            } else
                xml += "<" + key + "> + val + "]]> + key + ">";
        }
        xml += "";
        return xml;
    }

    public static boolean IsNumeric(String str) {
        if (str.matches("\\d *")) {
            return true;
        } else {
            return false;
        }
    }

    private static String post(String url, String xmlParam) {
        StringBuilder sb = new StringBuilder();
        try {
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            FileInputStream instream = new FileInputStream(new File("apiclient_cert.p12文件地址"));
            try {
                keyStore.load(instream, "商户号".toCharArray());
            } finally {
                instream.close();
            }
            SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, "商户号".toCharArray()).build();
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" },
                    null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
            CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(sslsf).build();

            HttpPost httpPost = new HttpPost(url);
            StringEntity reqEntity = new StringEntity(xmlParam);
            reqEntity.setContentType("application/x-www-form-urlencoded");
            httpPost.setEntity(reqEntity);
            CloseableHttpResponse response = client.execute(httpPost);
            try {
                HttpEntity entity = response.getEntity();
                System.out.println(response.getStatusLine());
                if (entity != null) {
                    BufferedReader bufferedReader = new BufferedReader(
                            new InputStreamReader(entity.getContent(), "UTF-8"));
                    String text = "";
                    while ((text = bufferedReader.readLine()) != null) {
                        sb.append(text);
                    }
                }
                EntityUtils.consume(entity);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sb.toString();
    }
    private static Map<String, String> doXMLParse(String xml) throws XmlPullParserException, IOException {

        InputStream inputStream = new ByteArrayInputStream(xml.getBytes());

        Map<String, String> map = null;

        XmlPullParser pullParser = XmlPullParserFactory.newInstance().newPullParser();

        pullParser.setInput(inputStream, "UTF-8");

        int eventType = pullParser.getEventType();

        while (eventType != XmlPullParser.END_DOCUMENT) {
            switch (eventType) {
                case XmlPullParser.START_DOCUMENT:
                    map = new HashMap<String, String>();
                    break;

                case XmlPullParser.START_TAG:
                    String key = pullParser.getName();
                    if (key.equals("xml"))
                        break;
                    String value = pullParser.nextText();
                    map.put(key, value);
                    break;
                case XmlPullParser.END_TAG:
                    break;
            }
            eventType = pullParser.next();
        }
        return map;
    }


    // 获取IP
    public String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (StringUtils.isNotEmpty(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 (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
            return ip;
        }
        return request.getRemoteAddr();
    }

Controller层方法

支付接口

 public ResultMap unifiedOrder(@RequestParam("orderNo") String orderNo,@RequestParam("payType") Integer payType,
                                  HttpServletRequest request){
        try {
            // 1、验证订单是否存在
            // 2、开始微信支付统一下单
            Orders orders = ordersService.FindOrder(orderNo);
            ResultMap resultMap =null;
            if(orders!=null){
                   String spbill_create_ip = getIpAddr(request);
                   resultMap = wxPayService.unifiedOrder(订单号,总价格,"商家名称",spbill_create_ip);
               return resultMap;
           
            }
          return resultMap;//系统通用的返回结果集
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println(e.getMessage());
            return ResultMap.error("服务器异常,付款失败");
        }


    }
   
  

获取终端ip的方法

    public String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (StringUtils.isNotEmpty(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 (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
            return ip;
        }
        return request.getRemoteAddr();
    }

微信支付异步通知接口

     /**
     * 微信支付异步通知
     */
    public String payNotify(HttpServletRequest request) {
        InputStream is = null;
        String xmlBack = " ";
        try {
            is = request.getInputStream();
            // 将InputStream转换成String
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            StringBuilder sb = new StringBuilder();
            String line = null;
            while ((line = reader.readLine()) != null) {
                sb.append(line + "\n");
            }
            xmlBack = wxPayService.notify(sb.toString());
        } catch (Exception e) {
            System.out.println("微信手机支付回调通知失败:"+e);
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return xmlBack;
    }

微信退款接口


	    /**
     *退款
     * @param orderNo 订单号
     * @return
     */
    public ResultMap enterrefund(String orderNo){
        
            ResultMap refund = wxPayService.refund(orderNo);
            return refund;
    
    }

你可能感兴趣的:(微信小程序支付以及退款功能(超详细))