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

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

首先,微信支付,只支持企业用户,个人用户是不能接入微信支付的,所以要想接入微信支付,首先需要有微信公众号,这个的企业才能申请。有了微信公众号,就能申请微信支付的相关内容,所以在准备开始写代码之前需要先把下面的这些参数申请好:公众账号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;//公众账号ID
private 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);//公众账号ID
packageParams.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的机器IP
packageParams.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格式的string
String 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成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, 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");
} }

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

## 微信支付配置 测试环境(如果需要测试,改成自己的正式环境) ##
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,该工具既有免费通道也有收费通道,收费通道也很便宜,如果只是测试,免费通道就够用了,另外还有一点要注意,就是微信支付的回调,默认微信是回调好几次的,所以会有重复回调的问题,这里留给大家一个思考,怎么防止微信的多次回调,以免影响业务,希望有兴趣的小伙伴可以留言交流。以上就是微信支付(扫码支付模式二)的全部内容了,有想要完全源代码的小伙伴,可以加群交流,群号:715224124。

Java之微信支付(扫码支付模式二)案例实战的更多相关文章

  1. 小D课堂-SpringBoot 2.x微信支付在线教育网站项目实战_6-3.微信网站扫码支付介绍

    笔记 3.微信网站扫码支付介绍     简介:讲解微信网页扫码支付         1.扫码支付文档:https://pay.weixin.qq.com/wiki/doc/api/native.php ...

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

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

  3. [支付]微信NATIVE扫码支付JAVA实现

    步骤: 1.预订单 2.接受微信返回的url 3.将url转为二维码显示到页面上 4.扫码支付 5.接收微信的异步通知,在这步修改订单的状态 6.收到异步通知的同时给微信返回指定数据,告知对方已成功处 ...

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

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

  5. (实用篇)微信支付扫码支付php版

    本文实例为大家分享了php微信扫码支付源码,供大家参考,具体内容如下 代码中包含四个文件createUrl.php.ArrayToXML.php.returnGoodsUrl.php.notifyUr ...

  6. Java实现微信客户端扫码登录

    此篇文章记录自己开发中的微信客户端扫码登录的实例以及步骤,便于以后自行学习记起的关键,看到的网友有借鉴的地方就借鉴,看不懂的也请别吐槽,毕竟每个人的思维和思路以及记录东西的方式不一样: 1.首先需要一 ...

  7. 【JAVA笔记】JAVA后端实现统一扫码支付:微信篇

    最近做完了一个项目,正好没事做,产品经理就给我安排了一个任务.   做一个像收钱吧这样可以统一扫码收钱的功能.   一开始并不知道是怎么实现的,咨询了好几个朋友,才知道大概的业务流程:先是开一个网页用 ...

  8. JAVA后端实现统一扫码支付:微信篇

    最近做完了一个项目,正好没事做,产品经理就给我安排了一个任务.   做一个像收钱吧这样可以统一扫码收钱的功能.   一开始并不知道是怎么实现的,咨询了好几个朋友,才知道大概的业务流程:先是开一个网页用 ...

  9. .NET MVC结构框架下的微信扫码支付模式二 API接口开发测试

    直接上干货 ,我们的宗旨就是为人民服务.授人以鱼不如授人以渔.不吹毛求疵.不浮夸.不虚伪.不忽悠.一切都是为了社会共同进步,繁荣昌盛,小程序猿.大程序猿.老程序猿还是嫩程序猿,希望这个社会不要太急功近 ...

随机推荐

  1. A Walk Through the Forest (最短路+记忆化搜索)

    Jimmy experiences a lot of stress at work these days, especially since his accident made working dif ...

  2. istio部署

    Istio的部署介绍 目录 Istio的部署介绍 部署模型 集群模式 单集群 多集群 网络模型 单网络 多网络 控制面模型 身份和信任模型 网格中的信任 网格之间的信任 网格模型 单网格 多网格 租户 ...

  3. 条件竞争(race condition)

    条件竞争漏洞是一种服务器端的漏洞,由于服务器端在处理不同用户的请求时是并发进行的,因此,如果并发处理不当或相关操作逻辑顺序设计的不合理时,将会导致此类问题的发生. 参考了一些资料,发现一个比较能说明问 ...

  4. redis在windows下安装教程

    安装过程 1.首先先把下载的压缩包解压到一个文件夹中2.打开cmd指令窗口3.输入你刚才解压的文件路径4.然后输入redis-server redis.windows.conf 命令接下来部署Redi ...

  5. Azure Blob (三)参数设置说明

    一,引言 上一篇将 Azure Blob 存储的时候,有使用到一个 .NET  Core Web 项目,通过代码的方式进行操作 Azure Blob 的数据,接着上一篇的内容,今天继续看一下代码,具体 ...

  6. JDK8(jdk-8u212-windows-x64) 下载 安装 及设置

    JDK8 下载页面 http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 19.8.2 ...

  7. leetcode刷题-88.合并两个有序数组

    题目 给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组. 说明: 初始化 nums1 和 nums2 的元素数量分别为 m ...

  8. HTTP 协议类

    HTTP 协议的主要特点 简单快速:每个资源的URL是固定的 灵活:在每个 http 协议中都有一个头部分有一个数据类型,通过一个 http 协议就可以完成不同数据类型的传输 无连接:连接一次就好断掉 ...

  9. xss原理解析

    xss->跨站脚本攻击 xss是指攻击者在网页中嵌入客户端脚本.通常是指javascript编写的一个危险代码,当用户使用浏览器浏览网页时,脚本就会在用户的浏览器上执行,从而达到攻击者的目的. ...

  10. in多值优化

    〇.问题 今天ocp群里有人问 SELECT * FROM table WHERE id IN(11,2,3,44,...) 在in里面有大量数据4000+,有什么 好的处理方式吗? 我的优化方案的总 ...