springboot对接微信支付的完整流程(附前后端代码)

作者:小小舍 时间:2021-11-12 15:08:42 

展示图:

springboot对接微信支付的完整流程(附前后端代码) 

springboot对接微信支付的完整流程(附前后端代码)

对接的完整流程如下

首先是配置


gzh.appid=公众号appid
wxPay.mchId=商户号
wxPay.key=支付密钥
wxPay.notifyUrl=域名回调地址

常量:


/**微信支付统一下单接口*/
   public static final String unifiedOrderUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";

public static String SUCCESSxml = "<xml> \r\n" +
           "\r\n" +
           "  <return_code><![CDATA[SUCCESS]]></return_code>\r\n" +
           "   <return_msg><![CDATA[OK]]></return_msg>\r\n" +
           " </xml> \r\n" +
           "";
   public static String ERRORxml =  "<xml> \r\n" +
           "\r\n" +
           "  <return_code><![CDATA[FAIL]]></return_code>\r\n" +
           "   <return_msg><![CDATA[invalid sign]]></return_msg>\r\n" +
           " </xml> \r\n" +
           "";

工具类准备:


package com.jc.utils.util;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.SAXBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.text.SimpleDateFormat;
import java.util.*;

public class CommUtils {
   private static Logger logger = LoggerFactory.getLogger(CommUtils.class);
   // 连接超时时间,默认10秒
   private static int socketTimeout = 60000;

// 传输超时时间,默认30秒
   private static int connectTimeout= 60000;
   /**
    * Util工具类方法
    * 获取一定长度的随机字符串,范围0-9,a-z
    * @param length:指定字符串长度
    * @return 一定长度的随机字符串
    */
   public static String getRandomStringByLength(int 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();
   }

/**
    * 获取订单号
    * @return
    */
   public static String getOrderNo(){

SimpleDateFormat ft = new SimpleDateFormat("yyyyMMddHHmmss");
       String time = ft.format(new Date());
       int mathCode = (int) ((Math.random() * 9 + 1) * 10000);// 5位随机数
       String resultCode = time+mathCode;
       return resultCode;
   }

/**
    * Util工具类方法
    * 获取真实的ip地址
    * @param request
    * @return
    */
   public static String getIpAddr(HttpServletRequest request) {
       String ip = request.getHeader("X-Forwarded-For");
       if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(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();

}

/**
    * 签名字符串
    * @param text 需要签名的字符串
    * @param key 密钥
    * @param input_charset 编码格式
    * @return 签名结果
    */
   public static String sign(String text, String key, String input_charset) {
       text = text + "&key=" + key;
       System.out.println(text);
       return DigestUtils.md5Hex(getContentBytes(text, input_charset));
   }

/**
    * 签名字符串
    * @param text 需要签名的字符串
    * @param sign 签名结果
    * @param key 密钥
    * @param input_charset 编码格式
    * @return 签名结果
    */
   public static boolean verify(String text, String sign, String key, String input_charset) {
       text = text + key;
       String mysign = DigestUtils.md5Hex(getContentBytes(text, input_charset));
       if (mysign.equals(sign)) {
           return true;
       } else {
           return false;
       }
   }
   /**
    * @param content
    * @param charset
    * @return
    * @throws UnsupportedEncodingException
    */
   public static byte[] getContentBytes(String content, String charset) {
       if (charset == null || "".equals(charset)) {
           return content.getBytes();
       }
       try {
           return content.getBytes(charset);
       } catch (UnsupportedEncodingException e) {
           throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);
       }
   }

/**
    * 生成6位或10位随机数 param codeLength(多少位)
    * @return
    */
   public static String createCode(int codeLength) {
       String code = "";
       for (int i = 0; i < codeLength; i++) {
           code += (int) (Math.random() * 9);
       }
       return code;
   }

@SuppressWarnings("unused")
   private static boolean isValidChar(char ch) {
       if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))
           return true;
       if ((ch >= 0x4e00 && ch <= 0x7fff) || (ch >= 0x8000 && ch <= 0x952f))
           return true;// 简体中文汉字编码
       return false;
   }

/**
    * 除去数组中的空值和签名参数
    * @param sArray 签名参数组
    * @return 去掉空值与签名参数后的新签名参数组
    */
   public static Map<String, String> paraFilter(Map<String, String> sArray) {
       Map<String, String> result = new HashMap<>();
       if (sArray == null || sArray.size() <= 0) {
           return result;
       }
       for (String key : sArray.keySet()) {
           String value = sArray.get(key);
           if (value == null || value.equals("") || key.equalsIgnoreCase("sign")
                   || key.equalsIgnoreCase("sign_type")) {
               continue;
           }
           result.put(key, value);
       }
       return result;
   }

/**
    * 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
    * @param params 需要排序并参与字符拼接的参数组
    * @return 拼接后字符串
    */
   public static String createLinkString(Map<String, String> params) {
       List<String> keys = new ArrayList<>(params.keySet());
       Collections.sort(keys);
       String prestr = "";
       for (int i = 0; i < keys.size(); i++) {
           String key = keys.get(i);
           String value = params.get(key);
           if (i == keys.size() - 1) {// 拼接时,不包括最后一个&字符
               prestr = prestr + key + "=" + value;
           } else {
               prestr = prestr + key + "=" + value + "&";
           }
       }
       return prestr;
   }
   /**
    *
    * @param requestUrl 请求地址
    * @param requestMethod 请求方法
    * @param outputStr 参数
    */
   public static String httpRequest(String requestUrl,String requestMethod,String outputStr){
       logger.warn("请求报文:"+outputStr);
       StringBuffer buffer = null;
       try{
           URL url = new URL(requestUrl);
           HttpURLConnection conn = (HttpURLConnection) url.openConnection();
           conn.setRequestMethod(requestMethod);
           conn.setDoOutput(true);
           conn.setDoInput(true);
           conn.connect();
           //往服务器端写内容
           if(null !=outputStr){
               OutputStream os=conn.getOutputStream();
               os.write(outputStr.getBytes("utf-8"));
               os.close();
           }
           // 读取服务器端返回的内容
           InputStream is = conn.getInputStream();
           InputStreamReader isr = new InputStreamReader(is, "utf-8");
           BufferedReader br = new BufferedReader(isr);
           buffer = new StringBuffer();
           String line = null;
           while ((line = br.readLine()) != null) {
               buffer.append(line);
           }
           br.close();
       }catch(Exception e){
           e.printStackTrace();
       }
       logger.warn("返回报文:"+buffer.toString());
       return buffer.toString();
   }

/**
    * POST请求
    * @param url           请求url
    * @param xmlParam      请求参数
    * @param apiclient     证书
    * @param mch_id        商户号
    * @return
    * @throws Exception
    */
   public static String post(String url, String xmlParam,String apiclient,String mch_id) throws Exception {
       logger.warn("请求报文:"+xmlParam);
       StringBuilder sb = new StringBuilder();
       try {
           KeyStore keyStore = KeyStore.getInstance("PKCS12");
           FileInputStream instream = new FileInputStream(new File(apiclient));
           try {
               keyStore.load(instream, mch_id.toCharArray());
           } finally {
               instream.close();
           }
           // 证书
           SSLContext sslcontext = SSLContexts.custom()
                   .loadKeyMaterial(keyStore, mch_id.toCharArray()).build();
           // 只允许TLSv1协议
           SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                   sslcontext, new String[]{"TLSv1"}, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
           //创建基于证书的httpClient,后面要用到
           CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(sslsf).build();
           HttpPost httpPost = new HttpPost(url);//退款接口
           StringEntity reqEntity = new StringEntity(xmlParam,"UTF-8");
           // 设置类型
           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 (KeyStoreException e) {
           e.printStackTrace();
       } catch (ClientProtocolException e) {
           e.printStackTrace();
       } catch (IOException e) {
           e.printStackTrace();
       } catch (Exception e) {
           e.printStackTrace();
       }
       logger.warn("返回报文:"+sb.toString());
       return sb.toString();
   }

public static String urlEncodeUTF8(String source){
       String result=source;
       try {
           result=java.net.URLEncoder.encode(source, "UTF-8");
       } catch (UnsupportedEncodingException e) {
           // TODO Auto-generated catch block
           e.printStackTrace();
       }
       return result;
   }
   /**
    * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
    * @param strxml
    * @return
    * @throws org.jdom2.JDOMException
    * @throws IOException
    */
   public static Map doXMLParse(String strxml) throws Exception {
       if(null == strxml || "".equals(strxml)) {
           return null;
       }

Map m = new HashMap();
       InputStream in = String2Inputstream(strxml);
       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);
           }

m.put(k, v);
       }
       in.close();

return m;
   }

/**
    * 获取子结点的xml
    * @param children
    * @return String
    */
   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();
   }
   public static InputStream String2Inputstream(String str) {
       return new ByteArrayInputStream(str.getBytes());
   }

}

controller:


package com.jch.mng.controller;

import com.jch.boot.component.CommonInfo;
import com.jch.boot.component.Result;
import com.jch.boot.component.ServiceCommonInfo;
import com.jch.mng.dto.input.gzh.WxPayDto;
import com.jch.mng.service.WxPayService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
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;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* Created by xxs on 2021/7/30 10:54
*
* @Description 公众号微信支付
* @Version 2.9
*/
@RestController
@RequestMapping("/wxPay")
public class WxPayController {

@Autowired
   private WxPayService payService;

/**
   * @Author: xxs
   * @param dto
    * @param request
   * @Date: 2021/7/30 11:55
   * @Description:  公众号微信支付
   * @Version: 2.9
   * @Return: com.jch.boot.component.Result<java.lang.String>
   */
   @PostMapping("/pay")
   public Result<String> pay(@RequestBody WxPayDto dto, HttpServletRequest request) throws Exception {
       ServiceCommonInfo<Object> result = payService.pay(dto,request);
       return CommonInfo.controllerBack(result);
   }

/**
   * @Author: xxs
   * @param request
    * @param response
   * @Date: 2021/7/30 11:55
   * @Description:  支付回调
   * @Version: 2.9
   * @Return: void
   */
   @PostMapping("/notify")
   public void notify(HttpServletRequest request, HttpServletResponse response) throws Exception {
       payService.notify(request,response);
   }

}

service接口:


package com.jch.mng.service;

import com.jch.boot.component.ServiceCommonInfo;
import com.jch.mng.dto.input.gzh.WxPayDto;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* Created by xxs on 2021/7/30 9:56
*
* @Description
* @Version 2.9
*/
public interface WxPayService {

ServiceCommonInfo<Object> pay(WxPayDto dto, HttpServletRequest request) throws Exception;

void notify(HttpServletRequest request, HttpServletResponse response) throws Exception;
}

接口实现:


package com.jch.mng.service.impl;

import com.alibaba.fastjson.JSON;
import com.jc.utils.util.CommUtils;
import com.jch.boot.component.ServiceCommonInfo;
import com.jch.mng.constant.WeChatConstants;
import com.jch.mng.dto.input.gzh.WxPayDto;
import com.jch.mng.service.WxPayService;
import com.jch.mng.utils.DoubleUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;

/**
* Created by xxs on 2021/7/30 9:56
*
* @Description
* @Version 2.9
*/
@Service
public class WxPayServiceImpl implements WxPayService {
   public  String appId;

public  String mch_id;

public  String notify_url;

public  String key;

@Value("${gzh.appid}")
   public void setAppId(String appId) {
       this.appId = appId;
   }
   @Value("${wxPay.mchId}")
   public void setMch_id(String mch_id) {
       this.mch_id = mch_id;
   }
   @Value("${wxPay.notifyUrl}")
   public void setNotify_url(String notify_url) {
       this.notify_url = notify_url;
   }
   @Value("${wxPay.key}")
   public void setKey(String key) {
       this.key = key;
   }

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

/**
   * @Author: xxs
   * @param dto
    * @param request
   * @Date: 2021/7/30 11:01
   * @Description:  微信支付
   * @Version: 2.9
   * @Return: com.jch.boot.component.ServiceCommonInfo<java.lang.Object>
   */
   @Override
   public ServiceCommonInfo<Object> pay(WxPayDto dto, HttpServletRequest request) throws Exception {
       logger.info("公众号微信支付, 入参:{}", JSON.toJSONString(dto));
       String openid = dto.getOpenid();
       String outTradeNo = dto.getOutTradeNo();
       String body = dto.getBody();
       Double totalFee = dto.getTotalFee();
       String nonce_str = CommUtils.getRandomStringByLength(32);
       String spbill_create_ip = CommUtils.getIpAddr(request);
       Map<String, String> packageParams = new HashMap<>();
       packageParams.put("appid", appId);
       packageParams.put("mch_id",mch_id);
       packageParams.put("nonce_str", nonce_str);
       packageParams.put("body", body);
       packageParams.put("out_trade_no", outTradeNo);
       double t = DoubleUtil.parseDouble(totalFee);//保留两位小数
       int aDouble = Integer.parseInt(new java.text.DecimalFormat("0").format(t*100));
       packageParams.put("total_fee", aDouble+"");
       packageParams.put("spbill_create_ip", spbill_create_ip);
       packageParams.put("notify_url", notify_url);
       packageParams.put("trade_type","JSAPI");
       packageParams.put("openid", openid);

packageParams = CommUtils.paraFilter(packageParams);
       String prestr = CommUtils.createLinkString(packageParams);
       String sign = CommUtils.sign(prestr, key, "utf-8").toUpperCase();
       logger.info("统一下单请求签名:" + sign );
       String xml = "<xml version='1.0' encoding='gbk'>" + "<appid>" + appId + "</appid>"
               + "<body><![CDATA[" + body + "]]></body>"
               + "<mch_id>" + mch_id + "</mch_id>"
               + "<nonce_str>" + nonce_str + "</nonce_str>"
               + "<notify_url>" + notify_url+ "</notify_url>"
               + "<openid>" + openid + "</openid>"
               + "<out_trade_no>" + outTradeNo + "</out_trade_no>"
               + "<spbill_create_ip>" + spbill_create_ip + "</spbill_create_ip>"
               + "<total_fee>" + aDouble+"" + "</total_fee>"
               + "<trade_type>" + "JSAPI" + "</trade_type>"
               + "<sign>" + sign + "</sign>"
               + "</xml>";

String result = CommUtils.httpRequest(WeChatConstants.unifiedOrderUrl, "POST", xml);
       Map map = CommUtils.doXMLParse(result);
       Object return_code =  map.get("return_code");
       logger.info("统一下单返回return_code:" + return_code );
       if(return_code == "SUCCESS"  || return_code.equals(return_code)){
           Map<String,String> resultMap=new HashMap<String, String>();
           String prepay_id = (String) map.get("prepay_id");
           resultMap.put("appId", appId);
           Long timeStamp = System.currentTimeMillis() / 1000;
           resultMap.put("timeStamp", timeStamp + "");
           resultMap.put("nonceStr", nonce_str);
           resultMap.put("package", "prepay_id=" + prepay_id);
           resultMap.put("signType", "MD5");
           logger.info("参与paySign签名数据, 入参:{}", JSON.toJSONString(resultMap));
           String linkString = CommUtils.createLinkString(resultMap);
           String paySign = CommUtils.sign(linkString, key, "utf-8").toUpperCase();
           logger.info("获取到paySign:"+paySign);
           resultMap.put("paySign", paySign);
           return ServiceCommonInfo.success("ok", resultMap);
       }
       return ServiceCommonInfo.serviceFail("支付失败", null);
   }

/**
   * @Author: xxs
   * @param request
    * @param response
   * @Date: 2021/7/31 15:17
   * @Description:  微信支付回调
   * @Version: 2.9
   * @Return: void
   */
   @Override
   public void notify(HttpServletRequest request, HttpServletResponse response) throws Exception {
       logger.info("进入支付回调啦啦啦啦*-*");
       String resXml = "";
       BufferedReader br = new BufferedReader(new InputStreamReader((ServletInputStream) request.getInputStream()));
       String line = null;
       StringBuilder sb = new StringBuilder();
       while ((line = br.readLine()) != null) {
           sb.append(line);
       }
       br.close();
       String notityXml = sb.toString();
       logger.info("支付回调返回数据:"+notityXml);
       Map map = CommUtils.doXMLParse(notityXml);
       Object returnCode = map.get("return_code");
       Object result_code = map.get("result_code");
       if ("SUCCESS".equals(returnCode) && "SUCCESS".equals(result_code)) {
           Map<String, String> validParams = CommUtils.paraFilter(map);  //回调验签时需要去除sign和空值参数
           String validStr = CommUtils.createLinkString(validParams);//把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
           String sign = CommUtils.sign(validStr, key , "utf-8").toUpperCase();//拼装生成服务器端验证的签名
           logger.info("支付回调生成签名:"+sign);
           String transaction_id = (String) map.get("transaction_id");
           String order_no = (String) map.get("out_trade_no");
           String time_end = (String) map.get("time_end");
           String total_fee = (String) map.get("total_fee");
           //签名验证,并校验返回的订单金额是否与商户侧的订单金额一致
           if (sign.equals(map.get("sign"))) {
               logger.info("支付回调验签通过");
               //通知微信服务器已经支付成功
               resXml = WeChatConstants.SUCCESSxml;
           } else {
               logger.info("微信支付回调失败!签名不一致");
           }
       }else{
           resXml = WeChatConstants.ERRORxml;
       }
       System.out.println(resXml);
       logger.info("微信支付回调返回数据:"+resXml);
       logger.info("微信支付回调数据结束");
       BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
       out.write(resXml.getBytes());
       out.flush();
       out.close();
   }
}

前端页面:

springboot对接微信支付的完整流程(附前后端代码)


<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>Title</title>
   <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0;" name="viewport" />
</head>
<body>
   body: <input type="text" class="inp-body"><br>
   outTradeNo: <input type="text" class="inp-outTradeNo"><br>
   totalFee: <input type="text" class="inp-totalFee"><br>
   openid: <input type="text" class="inp-openid"><br>
   <button onclick="handleWxPay()">支付</button>
</body>
</html>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
   function handleWxPay(){
       let obj = {
           "body":$(".inp-body").val(),
           "outTradeNo":$(".inp-outTradeNo").val(),
           "totalFee":$(".inp-totalFee").val(),
           "openid":$(".inp-openid").val(),
       }
       $.ajax({
           type: "POST",
           url: "微信支付接口地址",
           data:JSON.stringify(obj),
           beforeSend: function(request) {
               request.setRequestHeader("Content-Type","application/json");
           },
           success: result=> {
               let obj = JSON.parse(result.data)
               onBridgeReady(obj)
           }
       });
   }

function onBridgeReady(obj){
       WeixinJSBridge.invoke(
           'getBrandWCPayRequest', obj,
           function(res){
               alert(JSON.stringify(res))
               if(res.err_msg == "get_brand_wcpay_request:ok" ){
                   // 使用以上方式判断前端返回,微信团队郑重提示:
                   //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
               }
           });
   }
</script>

 访问前端页面记得加依赖:


<dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-thymeleaf</artifactId>
       </dependency>

访问页面需要写控制类:


package com.jch.mng.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletResponse;

/**
* Created by xxs on 2021/7/31 12:29
*
* @Description
* @Version 2.9
*/
@Controller
public class TestPageController {

@RequestMapping("/wxPayTest")
   public String test(HttpServletResponse response)  {
       return "wxPay";
   }
}

运行项目访问。

springboot对接微信支付的完整流程(附前后端代码)

部署项目到服务器,用手机访问即可拉起支付。

附:名词解释

商户号:微信支付分配的商户号。支付审核通过后,申请人邮箱会收到腾讯下发的开户邮件, 邮件中包含商户平台的账号、密码等重要信息。

appid:商户通过微信管理后台,申请服务号、订阅号、小程序或APP应用成功之后,微信会为每个应用分配一个唯一标识id。

openid:用户在公众号内的身份标识,一旦确认,不会再变;同一用户在不同公众号拥有不同的openid。商户后台系统通过登录授权、支付通知、查询订单等API可获取到用户的openid。主要用途是判断同一个用户,对用户发送客服消息、模版消息等。

微信管理后台:微信有很多管理平台,容易混淆,我们主要关注下面三个平台:

1. 微信公众平台 微信公众账号申请入口和管理后台。商户可以在公众平台提交基本资料、业务资料、财务资料申请开通微信支付功能。帐号分类:服务号、订阅号、小程序、企业微信(也叫企业号,类似于企业OA)。

2. 微信商户平台 微信支付相关的商户功能集合,包括参数配置、支付数据查询与统计、在线退款、代金券或立减优惠运营等功能。

3. 微信开放平台 商户APP接入微信支付开放接口的申请入口,通过此平台可申请微信APP支付。

签名:商户后台和微信支付后台根据相同的密钥和算法生成一个结果,用于校验双方身份合法性。签名的算法 由微信支付制定并公开,常用的签名方式有:MD5、SHA1、SHA256、HMAC等。

密钥:作为签名算法中的盐,需要在微信平台及商户业务系统各存一份,要妥善保管。 key设置路径:微信商户平台(http://pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置。

总结

来源:https://blog.csdn.net/qq_43560721/article/details/119276262

标签:springboot,微信,支付
0
投稿

猜你喜欢

  • C#实现线程安全的简易日志记录方法

    2023-12-20 22:45:17
  • Java集合框架ArrayList源码分析(一)

    2022-05-12 19:32:50
  • Android编程动态按钮实现方法

    2021-12-31 18:05:17
  • SpringCloud超详细讲解负载均衡组件Ribbon源码

    2021-06-17 18:39:47
  • Android App调试内存泄露之Cursor篇

    2023-11-22 03:36:29
  • 一文详解Spring的Enablexxx注解使用实例

    2023-09-03 08:43:18
  • Android编程之防止反编译的实现方法

    2021-10-31 04:23:35
  • MyBatis-Plus 如何单元测试的实现

    2022-03-02 19:45:08
  • Java 字符串转float运算 float转字符串的方法

    2022-04-09 10:09:06
  • Android实现支持所有View的通用的下拉刷新控件

    2022-07-29 10:40:36
  • Android自带倒计时控件Chronometer使用方法详解

    2022-09-18 13:11:33
  • Java类获取Spring中bean的5种方式

    2022-03-11 06:23:39
  • Android开发TextvView实现镂空字体效果示例代码

    2023-07-14 03:03:20
  • 利用Java工具类Hutool实现验证码校验功能

    2022-07-15 02:31:22
  • C++实现图书管理系统

    2023-11-03 03:00:35
  • C# WPF如何反射加载Geometry几何图形数据图标

    2021-09-06 11:39:08
  • java多线程累加计数的实现方法

    2021-07-19 12:10:36
  • java判断两个时间是不是同一天的方法

    2022-09-23 03:09:16
  • Android 之Preference控件基本使用示例详解

    2022-10-20 23:42:34
  • C#封装的常用文件操作类实例

    2021-08-14 10:08:03
  • asp之家 软件编程 m.aspxhome.com