第三方支付——微信web端支付(java)

3 篇文章 0 订阅
2 篇文章 1 订阅

大概思路:将参数组装为一个map集合,签名后发送http请求给微信,微信会返回一个xml字符串,将这个xml字符串解析并验签,得到code_url参数,传给前台,前台将这个code_url生成一个二维码,就可以了。

1、创建支付

/**
     * 微信支付
     * @param model
     * @param order_id
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/doWechatPay", method = RequestMethod.POST)
    @ResponseBody
    public Map createWechatPay(Model model, Integer order_id,HttpServletRequest request) throws Exception {
        Map json = new HashMap();
        User user = (User) model.asMap().get("user");
        ShareOrderInfo order = orderInfoMapper.selectByPrimaryKey(order_id);
        //生成一笔预付订单流水
        String trad_no = "PC_WECHAT" + OrderNoUtil.leadsNo();//订单流水号
        ShareUserTrad trad = new ShareUserTrad();
        trad.setResourceTradId(-1);
        trad.setUserId(user.getId());
        trad.setCreatedBy(user.getId());
        trad.setLastUpdBy(user.getId());
        trad.setOnlineOfflineFlag("0");//线上
        trad.setOrderNo(order.getOrderNo());
        trad.setUserTradAmount(order.getToBePaid());
        trad.setTradMethod("4");//支付宝
        trad.setPayReceiveFlag("2");//支出
        trad.setSuccessFlag("0");//交易进行中
        trad.setTradType("1");//订单支付
        trad.setTradNo(trad_no);
        trad.setModifyNum(0);
        shareUserTradMapper.insertSelective(trad);
        //随机字符串
        String nonce_str = PayUtil.getRandomString(32);
        String UTF8 = "UTF-8";
        Map<String,String> map = new HashMap<String,String>();
        map.put("body","订单["+order.getOrderNo()+"]支付");
        map.put("trade_type","NATIVE");
        map.put("mch_id",Config.wechat_mch_id);
        map.put("sign_type","MD5");
        map.put("nonce_str",nonce_str);
        map.put("fee_type","CNY");
        map.put("device_info","WEB");
        map.put("out_trade_no",trad_no);
        map.put("total_fee",order.getToBePaid().multiply(new BigDecimal(100)).toBigInteger().toString());
//        map.put("total_fee","1");//测试
        map.put("appid",Config.wechat_app_id);
        map.put("notify_url",Config.wechat_notify_url);
        map.put("spbill_create_ip",PayUtil.getIp(request));
        String sign = PayUtil.getWechatSign(map);
        String reqBody = "<xml>" +
                "<body>"+map.get("body")+"</body>" +
                "<trade_type>"+map.get("trade_type")+"</trade_type>" +
                "<mch_id>"+map.get("mch_id")+"</mch_id>" +
                "<sign_type>"+map.get("sign_type")+"</sign_type>" +
                "<nonce_str>"+map.get("nonce_str")+"</nonce_str>" +
                "<detail />"+
                "<fee_type>"+map.get("fee_type")+"</fee_type>" +
                "<device_info>"+map.get("device_info")+"</device_info>" +
                "<out_trade_no>"+map.get("out_trade_no")+"</out_trade_no>" +
                "<total_fee>"+map.get("total_fee")+"</total_fee>" +
                "<appid>"+map.get("appid")+"</appid>" +
                "<notify_url>"+map.get("notify_url")+"</notify_url>" +
                "<sign>"+sign+"</sign>" +
                "<spbill_create_ip>"+map.get("spbill_create_ip")+"</spbill_create_ip>" +
                "</xml>";
        URL httpUrl = new URL(Config.wechat_url);
        HttpURLConnection httpURLConnection = (HttpURLConnection) httpUrl.openConnection();
        httpURLConnection.setRequestProperty("Host", "api.mch.weixin.qq.com");
        httpURLConnection.setDoOutput(true);
        httpURLConnection.setRequestMethod("POST");
        httpURLConnection.setConnectTimeout(10*1000);
        httpURLConnection.setReadTimeout(10*1000);
        httpURLConnection.connect();
        OutputStream outputStream = httpURLConnection.getOutputStream();
        outputStream.write(reqBody.getBytes(UTF8));
        //获取内容
        InputStream inputStream = httpURLConnection.getInputStream();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, UTF8));
        final StringBuffer stringBuffer = new StringBuffer();
        String line = null;
        while ((line = bufferedReader.readLine()) != null) {
            stringBuffer.append(line);
        }
        String resp = stringBuffer.toString();
        if (stringBuffer!=null) {
            try {
                bufferedReader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (inputStream!=null) {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (outputStream!=null) {
            try {
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        Map<String, String> respData = PayUtil.xmlToMap(resp);
        String return_code = "";
        if (respData.containsKey("return_code")) {
            return_code = respData.get("return_code");
        }
        if("SUCCESS".equals(return_code)){
            if (respData.containsKey("sign") ) {
                String respSign = respData.get("sign");
                if(respData.get("sign").equals(PayUtil.getWechatSign(respData))){
                    json.put("code_url",respData.get("code_url"));
                }
            }
        }
        System.out.println(resp);
        return json;
    }
respData.get("code_url")

这个就是我们要的二维码内容。

PayUtil:

import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.servlet.http.HttpServletRequest;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.security.MessageDigest;
import java.util.*;

public class PayUtil {
    /**
     * MD5加密
     * @param data
     * @return
     * @throws Exception
     */
    public static String MD5(String data) throws Exception {
        java.security.MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] array = md.digest(data.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sb.toString().toUpperCase();
    }
    /**
     * XMLMAP
     * @param strXML
     * @return
     * @throws Exception
     */
    public static Map<String, String> xmlToMap(String strXML) throws Exception {
        try {
            Map<String, String> data = new HashMap<String, String>();
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
            InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
            org.w3c.dom.Document doc = documentBuilder.parse(stream);
            doc.getDocumentElement().normalize();
            NodeList nodeList = doc.getDocumentElement().getChildNodes();
            for (int idx = 0; idx < nodeList.getLength(); ++idx) {
                Node node = nodeList.item(idx);
                if (node.getNodeType() == Node.ELEMENT_NODE) {
                    org.w3c.dom.Element element = (org.w3c.dom.Element) node;
                    data.put(element.getNodeName(), element.getTextContent());
                }
            }
            try {
                stream.close();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            return data;
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    }

    /**
     * 获取随机字符串
     * @param length
     * @return
     */
    public static String getRandomString(int length) { //length表示生成字符串的长度
        String base = "abcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }

    /**
     * 获取IP地址
     * @param request
     * @return
     */
    public static String getIp(HttpServletRequest request){
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

    /**
     * 获取微信支付签名
     * @param map
     * @return
     * @throws Exception
     */
    public static String getWechatSign(Map<String,String> map) throws  Exception{
        Set<String> keySet = map.keySet();
        String[] keyArray = keySet.toArray(new String[keySet.size()]);
        Arrays.sort(keyArray);
        StringBuilder sb = new StringBuilder();
        for (String k : keyArray) {
            if (k.equals("sign")) {
                continue;
            }
            if (map.get(k).trim().length() > 0) {// 参数值为空,则不参与签名
                sb.append(k).append("=").append(map.get(k).trim()).append("&");
            }
        }
        sb.append("key=").append(Config.wechat_key);
        return PayUtil.MD5(sb.toString()).toUpperCase();
    }
    /**
     *
     * Mapxml数据
     */
    public static String mapToXML(Map<String,String> param){
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        for (Map.Entry<String,String> entry : param.entrySet()) {
            sb.append("<"+ entry.getKey() +">");
            sb.append(entry.getValue());
            sb.append("</"+ entry.getKey() +">");
        }
        sb.append("</xml>");
        return sb.toString();
    }
}

2、前台根据code_url生成支付二维码

这里我使用了第三方js控件qrcode来生成二维码。

function toWechatPay(){
    var data = "order_id=" + ${id};
    $.ajax({
        url: rootPath + '/pay/doWechatPay',
        type: "post",
        data: data,
        processData: true,
        success: function (result) {
            var json = eval('('+result+')');
            $("#wechatQrcode").qrcode({
                render: "table", //table方式
                width: 200, //宽度
                height:200, //高度
                text: json.code_url //任意内容
            });
            $(document).ready(function () {
                setInterval("ajaxstatus()", 3000);
            });
        },
        error: function (XMLHttpRequest, error, errorThrown) {
            layer.msg("支付失败,原因:保存预支付交易流水失败");
        }
    });
}

因为二维码为静态的,所以设置一个三秒的定时任务查询订单的状态,来改变页面。

function ajaxstatus() {
    var data = "order_id=" + ${id};
    $.ajax({
        url: rootPath + '/pay/getPayStatus',
        type: "GET",
        dataType:"json",
        data: data,
        success: function (data) {
            if (data.status == "3") { //订单状态为3表示支付成功
                layer.msg("支付成功,请刷新页面~",{shift: -1,time:2000},function(){
                    var index = parent.layer.getFrameIndex(window.name);
                    parent.layer.close(index);
                });
            }
        },
        error: function () {
            layer.msg("请求订单状态出错");
        }
    });
}

3、异步通知

/**
     * 微信支付异步通知
     * @param request
     * @param response
     * @throws Exception
     */
    @RequestMapping(value = "/wechatPayNo", method = RequestMethod.POST)
    @ResponseBody
    public void wechatPayNotify(HttpServletRequest request,HttpServletResponse response) throws Exception {
        ServletInputStream instream = request.getInputStream();
        StringBuffer sb = new StringBuffer();
        int len = -1;
        byte[] buffer = new byte[1024];

        while((len = instream.read(buffer)) != -1){
            sb.append(new String(buffer,0,len));
        }
        instream.close();
        Map<String,String> requestMap = PayUtil.xmlToMap(sb.toString());//接受微信的通知参数
        String return_code = "";
        String returnXml = "";
//        BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
        if (requestMap.containsKey("return_code")) {
            return_code = requestMap.get("return_code");
        }else{
            returnXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
             + "<return_msg><![CDATA[return_code不存在]]></return_msg>" + "</xml> ";
//            out.write(returnXml.getBytes());
        }
        if("SUCCESS".equals(return_code)){
            if (requestMap.containsKey("sign") ) {
                String respSign = requestMap.get("sign");
                if(respSign.equals(PayUtil.getWechatSign(requestMap))){
                    DealUserTradModel dealUserTradModel = new DealUserTradModel();
                    dealUserTradModel.setOut_trad_no(requestMap.get("transaction_id"));
                    dealUserTradModel.setTrad_no(requestMap.get("out_trade_no"));
                    dealUserTradModel.setCompany_amount(new BigDecimal(requestMap.get("total_fee")).divide(new BigDecimal("100")));//微信以分为单位,除以100……
                    dealUserTradModel.setPay_amount(new BigDecimal(requestMap.get("total_fee")).divide(new BigDecimal("100")));
                    dealUserTradModel.setUser_account(requestMap.get("openid"));
                    dealUserTradModel.setUser_account_name(requestMap.get("openid"));
                    payService.dealTrad(dealUserTradModel);
//                    returnXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
//                            + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
//                    out.write(returnXml.getBytes());
                    String msg = "success";
                    response.setContentType("text/xml");
                    response.getWriter().println(msg);
                }else{
                    returnXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                            + "<return_msg><![CDATA[sign不正确]]></return_msg>" + "</xml> ";
//                    out.write(returnXml.getBytes());
                }
            }else{
                returnXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                        + "<return_msg><![CDATA[sign不正确]]></return_msg>" + "</xml> ";
//                out.write(returnXml.getBytes());
            }
        }else{
            returnXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                    + "<return_msg><![CDATA[return_code不正确]]></return_msg>" + "</xml> ";
//            out.write(returnXml.getBytes());
        }
//        out.flush();
//        out.close();
    }

校验参数,并修改数据库的订单状态,同时返回success,否则微信会重复通知。

String msg = "success";
                    response.setContentType("text/xml");
                    response.getWriter().println(msg);

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
实现微信支付需要以下步骤: 1. 获取微信支付的相关配置信息(如appId、商户号、支付密钥等)。 2. 创建统一下单接口,用于生成预支付订单。在此接口中需要将订单信息(如订单编号、订单金额等)通过统一下单API发送给微信服务器,并获取到预支付订单的信息(如prepay_id)。 3. 创建支付回调接口,用于接收微信服务器回调的支付结果。在此接口中需要对支付结果进行验证,并根据支付结果更新订单状态。 以下是代码实现: 1. 配置文件中添加微信支付相关配置 ``` # 微信支付相关配置 wxpay.appId=微信公众号appId wxpay.mchId=商户号 wxpay.key=支付密钥 wxpay.notifyUrl=http://localhost:8080/api/wxpay/notify ``` 2. 创建统一下单接口 ```java @RestController @RequestMapping("/api/wxpay") public class WxPayController { @Autowired private WxPayService wxPayService; @PostMapping("/unifiedorder") public ResultData unifiedOrder(@RequestBody WxPayUnifiedOrderRequest request) throws Exception { WxPayUnifiedOrderResult result = wxPayService.unifiedOrder(request); return ResultData.success(result); } } ``` 3. 创建支付回调接口 ```java @RestController @RequestMapping("/api/wxpay") public class WxPayNotifyController { @Autowired private WxPayService wxPayService; @PostMapping("/notify") public String notify(HttpServletRequest request) throws Exception { String result = wxPayService.notify(request); return result; } } ``` 4. 创建微信支付服务类 ```java @Service public class WxPayService { @Autowired private WxPayConfig wxPayConfig; @Autowired private WxPayService wxPayService; @Autowired private OrderService orderService; /** * 创建预支付订单 * @param request * @return * @throws Exception */ public WxPayUnifiedOrderResult unifiedOrder(WxPayUnifiedOrderRequest request) throws Exception { // 设置回调URL request.setNotifyUrl(wxPayConfig.getNotifyUrl()); // 生成订单信息 Order order = orderService.createOrder(request.getOrderId(), request.getOrderAmount()); // 组装统一下单API参数 WxPayUnifiedOrderRequest wxPayUnifiedOrderRequest = new WxPayUnifiedOrderRequest(); wxPayUnifiedOrderRequest.setOutTradeNo(order.getOrderNo()); wxPayUnifiedOrderRequest.setTotalFee(order.getActualAmount().intValue()); wxPayUnifiedOrderRequest.setBody(order.getOrderName()); wxPayUnifiedOrderRequest.setSpbillCreateIp(request.getSpbillCreateIp()); wxPayUnifiedOrderRequest.setTradeType(request.getTradeType()); // 调用统一下单API生成预支付订单 WxPayUnifiedOrderResult wxPayUnifiedOrderResult = wxPayService.unifiedOrder(wxPayUnifiedOrderRequest); // 返回预支付订单信息 return wxPayUnifiedOrderResult; } /** * 处理支付回调 * @param request * @return * @throws Exception */ public String notify(HttpServletRequest request) throws Exception { // 处理回调通知 WxPayOrderNotifyResult result = wxPayService.parseOrderNotifyResult(request.getInputStream()); // 验证签名 if (!wxPayService.checkSignature(result, wxPayConfig.getKey())) { return WxPayNotifyResponse.fail("签名验证失败").toXml(); } // 验证订单状态 Order order = orderService.getOrder(result.getOutTradeNo()); if (order == null) { return WxPayNotifyResponse.fail("订单不存在").toXml(); } if (order.getStatus() == OrderStatus.PAID.getValue()) { return WxPayNotifyResponse.success("订单已处理").toXml(); } // 修改订单状态 orderService.updateOrderStatus(order.getOrderNo(), OrderStatus.PAID.getValue()); // 返回成功响应 return WxPayNotifyResponse.success("订单处理成功").toXml(); } } ``` 以上是SpringBoot实现微信支付的基本代码,具体实现还需要根据自己的业务逻辑进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值