微信电脑版二维码( NATIVE 扫码支付)

后端代码

/**
* 微信支付->扫码支付(模式二)->统一下单->微信二维码
* @return
*/
@PostMapping (value = “/qrcode”)
@RequestLog(“微信支付二维码”)
@ApiOperation(“微信支付二维码”)
@AnonAccess
public ResponseEntity wxpayPay(@Validated @RequestBody SysMemberRecordVo sysMemberRecordVo,
HttpServletRequest request,HttpServletResponse response) {

    String urlCode = null;

// String out_trade_no = UUID.randomUUID().toString().replace(“-”, “”);
String out_trade_no=getCurrentTimeStamp();
// 账号信息
String currTime = PayToolUtil.getCurrTime();
String strTime = currTime.substring(8, currTime.length());
String strRandom = String.valueOf(PayToolUtil.buildRandom(4));
String nonce_str = strTime + strRandom;
String total_fee = String.valueOf(Integer.parseInt(sysMemberRecordVo.getType().getValue()) * 100);
// (Double.parseDouble(sysMemberRecordVo.getType().getValue()) * 100)

    SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();
    packageParams.put("appid", WXConstants.appid);//公众账号ID
    packageParams.put("mch_id", WXConstants.mchid);//商户号
    packageParams.put("nonce_str", nonce_str);//随机字符串
    packageParams.put("body", "开通会员");  //商品描述
    packageParams.put("out_trade_no", out_trade_no);//商户订单号
    packageParams.put("total_fee", total_fee); //标价金额 订单总金额,单位为分
    packageParams.put("spbill_create_ip", WxChatPayCommonUtil.getIp(request));//终端IP APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP
    packageParams.put("notify_url", WXConstants.NOTIFY_URL);//通知地址 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数
    packageParams.put("trade_type", "NATIVE");//交易类型 NATIVE 扫码支付
    // 签名
    String sign = PayToolUtil.createSign("UTF-8", packageParams, WXConstants.key);
    packageParams.put("sign", sign);

    // 将请求参数转换为xml格式的string
    String requestXML = PayToolUtil.getRequestXml(packageParams);
    logger.info("requestXML:{}", requestXML);

    // 调用微信支付统一下单接口
    String resXml = HttpUtils.postData(PayConfigUtil.UFDODER_URL, requestXML);
    logger.info("resXml: {}", resXml);

    // 解析微信支付结果
    Map map = null;
    try {
        map = XMLUtil4jdom.doXMLParse(resXml);
        logger.info("map: {}", map);
    } catch (JDOMException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

    // 返回微信支付的二维码连接
    urlCode = (String) map.get("code_url");
    logger.info("urlCode:{}", urlCode);
    if (!"".equals(urlCode)){
        sysMemberRecordVo.setOutTradeNo(Long.valueOf(out_trade_no));
        sysMemberRecordVo.setPayState((long)0);
        sysMemberRecordFacade.createSysMemberRecord(sysMemberRecordVo);
    }
    map.put("out_trade_no",out_trade_no);
    map.remove("appid");
    map.remove("mch_id");
    map.remove("sign");
    map.remove("trade_type");
    return new ResponseEntity<>(map,HttpStatus.OK);

}

/**
* 微信支付-回调
* @param request
* @param response
*/
@PostMapping(“/wxNotify”)
@RequestLog(“微信支付回调”)
@ApiOperation(“微信支付回调”)
@AnonAccess
public String wxpayNotify(HttpServletRequest request, HttpServletResponse response) {
//读取参数
InputStream inputStream ;
StringBuffer sb = null;
try {
sb = new StringBuffer();
inputStream = request.getInputStream();
String s ;
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, “UTF-8”));
while ((s = in.readLine()) != null){
sb.append(s);
}
in.close();
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}

    //解析xml成map
    Map<String, String> map = new HashMap<String, String>();
    try {
        map = XMLUtil4jdom.doXMLParse(sb.toString());
    } catch (JDOMException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

    //过滤空 设置 TreeMap
    SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();
    Iterator it = map.keySet().iterator();
    while (it.hasNext()) {
        String parameter = (String) it.next();
        String parameterValue = map.get(parameter);
        String v = "";
        if(null != parameterValue) {
            v = parameterValue.trim();
        }
        packageParams.put(parameter, v);
    }
    //判断签名是否正确
    if(PayToolUtil.isTenpaySign("UTF-8", packageParams, WXConstants.key)) {
        //------------------------------
        //处理业务开始
        //------------------------------
        String resXml = "";
        if("SUCCESS".equals((String)packageParams.get("result_code"))){
            // 这里是支付成功
            //执行自己的业务逻辑
            String mch_id = (String)packageParams.get("mch_id");
            String openid = (String)packageParams.get("openid");
            String is_subscribe = (String)packageParams.get("is_subscribe");
            String out_trade_no = (String)packageParams.get("out_trade_no");
            String total_fee = (String)packageParams.get("total_fee");

            //执行自己的业务逻辑
            //暂时使用最简单的业务逻辑来处理:只是将业务处理结果保存到session中
            //(根据自己的实际业务逻辑来调整,很多时候,我们会操作业务表,将返回成功的状态保留下来)
            String code =sysMemberRecordFacade.wxpayNotify(out_trade_no);

            //通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
            resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
                    + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";

        } else {
            resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                    + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
            return ("fail");
        }
        //------------------------------
        //处理业务完毕
        //------------------------------
        try {
            BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
            out.write(resXml.getBytes());
            out.flush();
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    } else{
        logger.info("通知签名验证失败");
    }
    return ("success");
}

//生成订单号方法
public static String getCurrentTimeStamp(){
SimpleDateFormat sdf = new SimpleDateFormat(“yyyyMMddHHmmss”);
return sdf.format(new Date());
}

//PayToolUtil类
package com.zohokeyes.les.bootstrap.utils;

import java.text.SimpleDateFormat;
import java.util.*;

public class PayToolUtil {

/**
 * 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
 * @return boolean
 */
public static boolean isTenpaySign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {
    StringBuffer sb = new StringBuffer();
    Set es = packageParams.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(!"sign".equals(k) && null != v && !"".equals(v)) {
            sb.append(k + "=" + v + "&");
        }
    }

    sb.append("key=" + API_KEY);

    //算出摘要
    String mysign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toLowerCase();
    String tenpaySign = ((String)packageParams.get("sign")).toLowerCase();

    //System.out.println(tenpaySign + "    " + mysign);
    return tenpaySign.equals(mysign);
}

/**
 * 创建sign签名
 * @param characterEncoding 编码格式
 * @param packageParams 请求参数
 * @param API_KEY API密钥
 * @return
 */
public static String createSign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {
    StringBuffer sb = new StringBuffer();
    Set es = packageParams.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 (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
            sb.append(k + "=" + v + "&");
        }
    }
    sb.append("key=" + API_KEY);
    String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
    return sign;
}

/**
 * 将请求参数转换为xml格式的string
 * @param parameters 请求参数
 * @return 转换后的字符串
 */
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();
}

/**
 * 取出一个指定长度大小的随机正整数
 * @param length int 设定所取出随机数的长度。length小于11
 * @return int 返回生成的随机数。
 */
public static int buildRandom(int length) {
    int num = 1;
    double random = Math.random();
    if (random < 0.1) {
        random = random + 0.1;
    }
    for (int i = 0; i < length; i++) {
        num = num * 10;
    }
    return (int) ((random * num));
}

/**
 * 获取当前时间 yyyyMMddHHmmss
 * @return
 */
public static String getCurrTime() {
    Date now = new Date();
    SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");
    return outFormat.format(now);
}

}

//WxChatPayCommonUtil类
package com.zohokeyes.les.bootstrap.utils;

import org.springframework.util.StringUtils;

import javax.net.ssl.HttpsURLConnection;
import javax.servlet.http.HttpServletRequest;

import java.io.*;
import java.net.URL;

/**

  • 自定义微信支付工具类
    /
    public class WxChatPayCommonUtil {
    /
    *

    • 发送 http 请求
    • @param requestUrl 请求路径
    • @param requestMethod 请求方式(GET/POST/PUT/DELETE/…)
    • @param outputStr 请求参数体
    • @return 结果信息
      */
      public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {
      try {
      URL url = new URL(requestUrl);
      HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
      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 (Exception e) {
      e.printStackTrace();
      }
      return null;
      }

    /**

    • 获取ip
    • @param request 请求
    • @return ip 地址
      */
      public static String getIp(HttpServletRequest request) {
      if (request == null) {
      return “”;
      }
      String ip = request.getHeader(“X-Requested-For”);
      if (StringUtils.isEmpty(ip) || “unknown”.equalsIgnoreCase(ip)) {
      ip = request.getHeader(“X-Forwarded-For”);
      }
      if (StringUtils.isEmpty(ip) || “unknown”.equalsIgnoreCase(ip)) {
      ip = request.getHeader(“Proxy-Client-IP”);
      }
      if (StringUtils.isEmpty(ip) || “unknown”.equalsIgnoreCase(ip)) {
      ip = request.getHeader(“WL-Proxy-Client-IP”);
      }
      if (StringUtils.isEmpty(ip) || “unknown”.equalsIgnoreCase(ip)) {
      ip = request.getHeader(“HTTP_CLIENT_IP”);
      }
      if (StringUtils.isEmpty(ip) || “unknown”.equalsIgnoreCase(ip)) {
      ip = request.getHeader(“HTTP_X_FORWARDED_FOR”);
      }
      if (StringUtils.isEmpty(ip) || “unknown”.equalsIgnoreCase(ip)) {
      ip = request.getRemoteAddr();
      }
      return ip;
      }

    /**

    • 从流中读取微信返回的xml数据

    • @param httpServletRequest

    • @return

    • @throws IOException
      */
      public static String readXmlFromStream(HttpServletRequest httpServletRequest) throws IOException {
      InputStream inputStream = httpServletRequest.getInputStream();
      BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
      final StringBuffer sb = new StringBuffer();
      String line = null;
      try {
      while ((line = bufferedReader.readLine()) != null) {
      sb.append(line);
      }
      } finally {
      bufferedReader.close();
      inputStream.close();
      }

      return sb.toString();
      }

    /**

    • 设置返回给微信服务器的xml信息
    • @param returnCode
    • @param returnMsg
    • @return
      */
      public static String setReturnXml(String returnCode, String returnMsg) {
      return “<return_code><![CDATA[" + returnCode + "]]></return_code><return_msg><![CDATA[" + returnMsg + "]]></return_msg>”;
      }

}

//http工具类,负责发起post请求并获取的返回
//HttpUtils
package com.zohokeyes.les.bootstrap.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.net.URLConnection;

/**

  • http工具类,负责发起post请求并获取的返回
    */
    public class HttpUtils {

    private static final Logger logger = LoggerFactory.getLogger(HttpUtils.class);

    private final static int CONNECT_TIMEOUT = 5000; // in milliseconds
    private final static String DEFAULT_ENCODING = “UTF-8”;

    public static String postData(String urlStr, String data){
    return postData(urlStr, data, null);
    }

    public static String postData(String urlStr, String data, String contentType){
    BufferedReader reader = null;
    try {
    URL url = new URL(urlStr);
    URLConnection conn = url.openConnection();
    conn.setDoOutput(true);
    conn.setConnectTimeout(CONNECT_TIMEOUT);
    conn.setReadTimeout(CONNECT_TIMEOUT);
    if(contentType != null)
    conn.setRequestProperty(“content-type”, contentType);
    OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), DEFAULT_ENCODING);
    if(data == null)
    data = “”;
    writer.write(data);
    writer.flush();
    writer.close();

         reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), DEFAULT_ENCODING));
         StringBuilder sb = new StringBuilder();
         String line = null;
         while ((line = reader.readLine()) != null) {
             sb.append(line);
             sb.append("\r\n");
         }
         return sb.toString();
     } catch (IOException e) {
         logger.error("Error connecting to " + urlStr + ": " + e.getMessage());
     } finally {
         try {
             if (reader != null)
                 reader.close();
         } catch (IOException e) {
             e.printStackTrace();
         }
     }
     return null;
    

    }

}

前端代码vue
引入import QRCode from ‘qrcode’ // 引入生成二维码插件qrcode
在data中的return定义qrCodeImgUrl存放微信二维码用的。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

算了,具体可看https://blog.csdn.net/fat_shady/article/details/12841091这个帖子.

总结:后端代码中的APPID啥的用自己的就行,然后那几个工具类可以合到一起。我因为项目忙实现了就没整理。很多工具类的方法没用到后期可以删除。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值