摘要:最近的一个项目中涉及到了支付业务,其中用到了微信支付和支付宝支付,在做的过程中也遇到些问题,所以现在总结梳理一下,分享给有需要的人,也为自己以后回顾留个思路。

一:微信支付接入准备工作

首先,微信支付,只支持企业用户,个人用户是不能接入微信支付的,所以要想接入微信支付,首先需要有微信公众号,这个的企业才能申请。有了微信公众号,就能申请微信支付的相关内容,所以在准备开始写代码之前需要先把下面的这些参数申请好:公众账号ID、微信支付商户号、API密钥、AppSecret是APPID对应的接口密码、回调地址(回调必须保证外网能访问到此地址)、发起请求的电脑IP

二:微信支付流程说明:

有了上面提到的这些参数,那我们就可以接入微信支付了,下面我来看下微信支付的官方文档(https://pay.weixin.qq.com/wiki/doc/api/index.html)、访问该地址可以看到有多种支付方式可以选择,我们这里选择扫码支付的方式(https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1)

这里我们选择模式二,下面看下模式二的时序图,如下图:

模式二与模式一相比,流程更为简单,不依赖设置的回调支付URL。商户后台系统先调用微信支付的统一下单接口,微信后台系统返回链接参数code_url,商户后台系统将code_url值生成二维码图片,用户使用微信客户端扫码后发起支付。注意:code_url有效期为2小时,过期后扫码不能再发起支付。

业务流程说明:

(1)商户后台系统根据用户选购的商品生成订单。

(2)用户确认支付后调用微信支付【统一下单API】生成预支付交易;

(3)微信支付系统收到请求后生成预支付交易单,并返回交易会话的二维码链接code_url。

(4)商户后台系统根据返回的code_url生成二维码。

(5)用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统。

(6)微信支付系统收到客户端请求,验证链接有效性后发起用户支付,要求用户授权。

(7)用户在微信客户端输入密码,确认支付后,微信客户端提交授权。

(8)微信支付系统根据用户授权完成支付交易。

(9)微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。

(10)微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。

(11)未收到支付通知的情况,商户后台系统调用【查询订单API】。

(12)商户确认订单已支付后给用户发货。

三:微信支付所需要的maven依赖:

<!--生成二维码jar--><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.2.1</version></dependency>

四:微信支付调用统一下单接口的核心代码

3.1:微信支付工具类:

HttpUtil.java
package com.micai.springboot.util.pay.wx;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 HttpUtil {private static final Logger logger = LoggerFactory.getLogger(HttpUtil.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;  }  }
MD5Util.java
package com.micai.springboot.util.pay.wx;import java.security.MessageDigest;public class MD5Util {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();  }  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];  }  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;  }  private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",  "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };  }

PayConfigUtil.java

package com.micai.springboot.util.pay.wx;public class PayConfigUtil {//初始化
//  public final static String APP_ID = "11111111111"; //公众账号appid(改为自己实际的)
//  public final static String APP_SECRET = "";
//  public final static String MCH_ID = "111111"; //商户号(改为自己实际的)
//  public final static String API_KEY = "11111111111"; //(改为自己实际的)key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置//统一下单public final static String UFDODER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
//  public final static String NOTIFY_URL = "http://xxxxxxx"; //微信支付回调接口,就是微信那边收到(改为自己实际的)
//  //企业向个人账号付款的URL
//  public final static String SEND_EED_PACK_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
//
//  public final static String CREATE_IP = "113.69.246.11";//发起支付ip(改为自己实际的)}
PayToolUtil.java
package com.micai.springboot.util.pay.wx;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);}}

QRUtil.java

package com.micai.springboot.util.pay.wx;import com.google.zxing.common.BitMatrix;import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;/*** 二维码生产工具类*/
public class QRUtil {private static final int BLACK = 0xFF000000; private static final int WHITE = 0xFFFFFFFF; private QRUtil() {} public static BufferedImage toBufferedImage(BitMatrix matrix) {int width = matrix.getWidth(); int height = matrix.getHeight(); BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { image.setRGB(x, y, matrix.get(x, y) ? BLACK : WHITE); } } return image; } public static void writeToFile(BitMatrix matrix, String format, File file)throws IOException { BufferedImage image = toBufferedImage(matrix); if (!ImageIO.write(image, format, file)) { throw new IOException("Could not write an image of format " + format + " to " + file); } }public static void writeToStream(BitMatrix matrix, String format, OutputStream stream)throws IOException { BufferedImage image = toBufferedImage(matrix); if (!ImageIO.write(image, format, stream)) { throw new IOException("Could not write an image of format " + format); }}
}
XMLUtil4jdom.java
package com.micai.springboot.util.pay.wx;import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;public class XMLUtil4jdom {/** * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。 * @param strxml * @return * @throws JDOMException * @throws IOException */  public static Map doXMLParse(String strxml) throws JDOMException, IOException {  strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");  if(null == strxml || "".equals(strxml)) {return null;  }Map<String, String> m = new HashMap<String, String>();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 = XMLUtil4jdom.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(XMLUtil4jdom.getChildrenText(list));  }  sb.append(value);  sb.append("</" + name + ">");  }  }  return sb.toString();}}

3.2:微信支付实体对象:

WxpayVo.java
package com.micai.springboot.vo.pay;import java.io.Serializable;/*** @Auther: zhaoxinguo* @Date: 2018/8/31 11:34* @Description:*/
public class WxpayVo implements Serializable {private String app_id;//公众账号IDprivate String mch_id;//微信支付商户号private String key;//API密钥private String app_secret;//AppSecret是APPID对应的接口密码private String out_trade_no;// 商户订单号private String currTime;private String strTime;private String strRandom;private String nonce_str;//随机字符串private String spbill_create_ip;private String notify_url;private String trade_type;private String total_fee;public String getApp_id() {return app_id;}public void setApp_id(String app_id) {this.app_id = app_id;}public String getMch_id() {return mch_id;}public void setMch_id(String mch_id) {this.mch_id = mch_id;}public String getKey() {return key;}public void setKey(String key) {this.key = key;}public String getApp_secret() {return app_secret;}public void setApp_secret(String app_secret) {this.app_secret = app_secret;}public String getOut_trade_no() {return out_trade_no;}public void setOut_trade_no(String out_trade_no) {this.out_trade_no = out_trade_no;}public String getCurrTime() {return currTime;}public void setCurrTime(String currTime) {this.currTime = currTime;}public String getStrTime() {return strTime;}public void setStrTime(String strTime) {this.strTime = strTime;}public String getStrRandom() {return strRandom;}public void setStrRandom(String strRandom) {this.strRandom = strRandom;}public String getNonce_str() {return nonce_str;}public void setNonce_str(String nonce_str) {this.nonce_str = nonce_str;}public String getSpbill_create_ip() {return spbill_create_ip;}public void setSpbill_create_ip(String spbill_create_ip) {this.spbill_create_ip = spbill_create_ip;}public String getNotify_url() {return notify_url;}public void setNotify_url(String notify_url) {this.notify_url = notify_url;}public String getTrade_type() {return trade_type;}public void setTrade_type(String trade_type) {this.trade_type = trade_type;}public String getTotal_fee() {return total_fee;}public void setTotal_fee(String total_fee) {this.total_fee = total_fee;}
}

3.3:微信支付业务对象:

PayBaseController.java
package com.micai.springboot.controller.pay;import com.micai.springboot.base.BaseController;
import org.springframework.beans.factory.annotation.Value;/*** @Auther: zhaoxinguo* @Date: 2018/8/31 13:40* @Description:*/
public abstract class PayBaseController extends BaseController {// 支付宝支付参数配置 //@Value("${ALIPAY.APPID}")protected String app_id;//应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号@Value("${ALIPAY.PRIVATEKEY}")protected String merchant_private_key;//商户私钥,您的PKCS8格式RSA2私钥@Value("${ALIPAY.PUBLICKEY}")protected String alipay_public_key;//支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。@Value("${ALIPAY.NOTIFY_URL}")protected String notify_url;//服务器异步通知页面路径@Value("${ALIPAY.RETURNA_URL}")protected String return_url;//页面跳转同步通知页面路径@Value("${ALIPAY.SIGN}")protected String sign_type = "RSA2";//签名方式protected String charset = "utf-8";//字符编码格式@Value("${ALIPAY.GATEWAY_URL}")protected String gateway_url;//支付宝网关// 微信支付参数配置 //@Value("${WXPAY.APPID}")protected String APPID;//公众账号ID@Value("${WXPAY.MCHID}")protected String MCHID;//微信支付商户号@Value("${WXPAY.KEY}")protected String KEY;//API密钥@Value("${WXPAY.APPSECRET}")protected String APPSECRET;//AppSecret是APPID对应的接口密码@Value("${WXPAY.NOTIFY_URL}")protected String NOTIFY_URL;//回调地址。测试回调必须保证外网能访问到此地址@Value("${WXPAY.CREATE_IP}")protected String CREATE_IP;//发起请求的电脑IP}
WxpayController.java
package com.micai.springboot.controller.pay;import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.micai.springboot.util.pay.wx.*;
import com.micai.springboot.vo.pay.WxpayVo;
import org.jdom.JDOMException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.*;/*** @Auther: zhaoxinguo* @Date: 2018/8/31 10:37* @Description: 微信支付后台接口*/
@RestController
@RequestMapping(value = "/wxpay")
public class WxpayController extends PayBaseController {/*** 微信支付->扫码支付(模式二)->统一下单->微信二维码* @return*/@GetMapping("/qrcode")public void wxpayPay(HttpServletResponse response) {String urlCode = null;// 获取订单信息WxpayVo vo = new WxpayVo();String out_trade_no = UUID.randomUUID().toString().replace("-", "");vo.setOut_trade_no(out_trade_no);// 账号信息vo.setApp_id(APPID);vo.setMch_id(MCHID);vo.setKey(KEY);String currTime = PayToolUtil.getCurrTime();vo.setCurrTime(currTime);String strTime = currTime.substring(8, currTime.length());vo.setStrTime(strTime);String strRandom = String.valueOf(PayToolUtil.buildRandom(4));vo.setStrRandom(strRandom);String nonce_str = strTime + strRandom;vo.setNonce_str(nonce_str);vo.setSpbill_create_ip(CREATE_IP);vo.setNotify_url(NOTIFY_URL);vo.setTrade_type("NATIVE");String total_fee = "1";vo.setTotal_fee(total_fee);//价格的单位为分SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();packageParams.put("appid", APPID);//公众账号IDpackageParams.put("mch_id", 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", CREATE_IP);//终端IP APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IPpackageParams.put("notify_url", NOTIFY_URL);//通知地址 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数packageParams.put("trade_type", "NATIVE");//交易类型 NATIVE 扫码支付// 签名String sign = PayToolUtil.createSign("UTF-8", packageParams, KEY);packageParams.put("sign", sign);// 将请求参数转换为xml格式的stringString requestXML = PayToolUtil.getRequestXml(packageParams);logger.info("requestXML:{}", requestXML);// 调用微信支付统一下单接口String resXml = HttpUtil.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);try {int width = 300;int height = 300;//二维码的图片格式String format = "gif";Hashtable hints = new Hashtable();//内容所使用编码hints.put(EncodeHintType.CHARACTER_SET, "utf-8");BitMatrix bitMatrix;bitMatrix = new MultiFormatWriter().encode(urlCode, BarcodeFormat.QR_CODE, width, height, hints);QRUtil.writeToStream(bitMatrix, format, response.getOutputStream());} catch (WriterException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}/*** 微信支付-回调* @param request* @param response*/@PostMapping("/notify")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成mapMap<String, String> map = new HashMap<String, String>();try {map = XMLUtil4jdom.doXMLParse(sb.toString());} catch (JDOMException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}//过滤空 设置 TreeMapSortedMap<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, 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中//(根据自己的实际业务逻辑来调整,很多时候,我们会操作业务表,将返回成功的状态保留下来)request.getSession().setAttribute("_PAY_RESULT", "OK");logger.info("支付成功");//通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.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");}}

五:访问支付URL:http://dvnq2b.natappfree.cc/wxpay/qrcode、回返回微信的支付二维码,用户通过微信的扫一扫,就可以扫码支付了、这里注意:由于微信回调需要能外网访问的域名,所以我们这里使用了一个内网穿透工具natapp,具体怎么实现内网穿透,直接官网就可以了很简单,这是附上natapp的官网地址:NATAPP-内网穿透 基于ngrok的国内高速内网映射工具、配置好内容穿透工具后,修改下微信回调的配置文件,如下:

## 微信支付配置 测试环境(如果需要测试,改成自己的正式环境) ##
WXPAY.APPID=wx82b23234467t34347661
WXPAY.MCHID=1234dsdf5345
WXPAY.KEY=rtert345dfgh345fg34ddfg345fdg
WXPAY.APPSECRET=36546dfghdfgdszdfsdffg45354dfg
WXPAY.NOTIFY_URL= http://dvnq2b.natappfree.cc/wxpay/notify
WXPAY.CREATE_IP=192.168.0.190

访问支付url返回微信二维码,如下图:

使用微信的扫一扫,扫码支付,如下图:

微信支付回调,如下图:

这里对于回调只是简单输出了日志,你可以根据自己的实际情况选择做相应的处理,一般都是对订单的支付状态做更新。

六:总结:

经过上面的所以流程,相信大家都明白了微信支付的流程,这里我们对上面的流程做个总结,要想接入微信支付,必须是企业用户才行,个人用户不支持,所以在开始写代码之前,要和公司的相关负责人申请好微信支付的相关配置参数,有了这些才能进行下面的工作,这里最重要的一点就是微信支付的回调了,回调,在生产环境必须配置可以外网访问的URL,同时域名必须是备案过的,二级域名也可以,这里我们为了方便测试,所以就使用了内网穿透工具natapp,该工具既有免费通道也有收费通道,收费通道也很便宜,如果只是测试,免费通道就够用了,另外还有一点要注意,就是微信支付的回调,默认微信是回调好几次的,所以会有重复回调的问题,这里留给大家一个思考,怎么防止微信的多次回调,以免影响业务,希望有兴趣的小伙伴可以留言交流。以上就是微信支付(扫码支付模式二)的全部内容了,有想要完全源代码的小伙伴,可以加群交流,QQ群号:278298761。

最后:如果哪位小伙伴有问题,可以加楼主的微信,楼主会尽力帮大家解决遇到的问题!

微信号:dlzhaoxinguo

七:下载源码:

micai-springboot: 学习Spring Boot的Demo - Gitee.com

Java之微信支付(扫码支付模式二)案例实战相关推荐

  1. 微信网页扫码支付(公众号)JAVA实现

    今天我们来说说微信网页扫码支付,这个支付的步骤和微信公众号网页是差不多的,也和微信小程序的步骤是一致的,不过appid是微信公众号的,我自己收集的微信开发文档希望对大家有用: https://blog ...

  2. Java 微信native扫码支付 亲测有用

    最近在网上总结了spring cloud 微信扫码支付的流程, 本人是刚入行的小白,有不对的地方请大家指出 也欢迎大家来多多交流 我的商户APPID和秘钥的一些配置信息,是公司的 这些需要微信的商户认 ...

  3. 关于微信PC扫码支付

    关于微信PC扫码支付 扫码支付 有两种模式 官网有说  这里采用简单明白的模式2 前期准备  1 需要公众号 开通了微信支付权限 2 需要微信支付商户平台账号 服务器生成订单号 发送请求 https: ...

  4. 微信支付宝扫码支付聚合系统

    一.简介 微信支付宝扫码支付聚合系统,聚合了微信支付和支付宝的所有扫码支付模式.并支持微信支付服务商子商户模式.支持五种扫码技术. 二.主要功能 1. 微信扫码支付:包括扫码支付模式一.扫码支付模式二 ...

  5. Win10环境前后端分离项目基于Vue.js+Django+Python3实现微信(wechat)扫码支付流程(2021年最新攻略)

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_182 之前的一篇文章:mpvue1.0+python3.7+Django2.0.4实现微信小程序的支付功能,主要介绍了微信小程序内 ...

  6. Win10环境前后端分离项目基于Vue.js+Tornado+Python3实现微信(wechat)扫码支付流程

    在生活具有广泛性.高效性.使用方便性的支付方式是扫码支付,扫码的优点在于推广成本低,上至钓鱼台国宾馆,下至发廊地摊都能用,打印出来就完事了,而相比其他支付方式,现金的找零及假钞问题,信用卡的办理门槛. ...

  7. ThinkPHP 整合微信支付 扫码支付 模式二 图文教程

    这篇文章主要介绍扫码支付场景二. 目前有两种模式,模式一比模式二稍微复杂点,至于模式一与模式二的具体内容,流程,微信开发文档都有详细介绍,这里就不多说废话,接下来赶紧上教程! Table of Con ...

  8. 微信支付宝扫码支付简介

    微信平台 支付模式 付款码支付 Native支付 JSAPI支付 APP支付 H5支付 小程序支付 各种模式的定义可以查看微信官方文档 这里应用Native支付,它是商户系统按微信支付协议生成支付二维 ...

  9. 微信「扫码支付」被诉侵犯专利权;苹果回应“iOS 13 频繁提醒 App 定位”;Python 2 退休 | 极客头条...

    整理 | 屠敏 快来收听极客头条音频版吧,智能播报由标贝科技提供技术支持. 「极客头条」-- 技术人员的新闻圈! CSDN 的读者朋友们早上好哇,「极客头条」来啦,快来看今天都有哪些值得我们技术人关注 ...

最新文章

  1. 在pymongo中使用distinct
  2. mvvm模式和mvc的区别_mvvm 和 mvc 区别?
  3. 中文URL是否有利于网站SEO
  4. 数据结构:关于AVL树的平衡旋转详解
  5. Nettiers快速使用入门(一) 数据库
  6. 被未知进程占用端口的解决办法
  7. 逆向Android软件的步骤
  8. 代码审计中的SQL注入
  9. 杭州的马路上,突然多了6万张阿里人的笑脸
  10. 实验7 OpenGL光照
  11. 对称加密、非对称加密、数字签名、数字证书、签名加密
  12. python宿舍管理系统_python实现宿舍管理系统
  13. 冒险岛2计算机内存不足建议使用,冒险岛2游戏设置详解 低配电脑如何流畅运行冒险岛2...
  14. seo之html优化,SEO优化技巧之HTML优化
  15. python入门学习随记(十二)
  16. ngx_stream_core_module
  17. 【论文】解读AM-GCN: Adaptive Multi-channel Graph Convolutional
  18. UE4像素流pixelstream的一些坑
  19. 射频信号源进阶使用技巧【转载自微信公众号微波射频网】
  20. Python模块字典

热门文章

  1. golang学习笔记(结构体+json)
  2. Maven 常用的jar包依赖
  3. Disruptor框架中生产者、消费者的各种复杂依赖场景下的使用总结-我见过最好的Disruptor
  4. photoshop之logo图标为透明色
  5. 【算法】_013_矩阵乘法
  6. 1-0 读《IT运维服务管理》思维导图笔记-0 书籍简述
  7. 电商直播的带货技巧有哪些?
  8. 哇!火星玩家Mars全球合伙人招募计划正式启动
  9. 对bmp灰度图像的Huffman编解码
  10. endnote出现select a reference,endnote错误