一、微信扫码支付流程
1、微信支付开发文档:微信支付开发文档
2、微信扫码支付业务流程
3、个人理解的支付流程
①用户在商户平台下单
②商户生成商户订单,并调起微信统一下单接口
③微信生成预支付订单,返回预支付的交易连接(code_url),我们根据code_url 生成二维码,提供给消费者扫码。值得注意的是:微信对于同一商户订单号且同一价格的商品可以生成多个预付单,我们可以设置预付单的有效时间(time_expire),预付单过期后,刷新二维码支付页面便会生成一个预付单,此时会发现二维码变了,即返回的预支付的交易连接(code_url)变了。
④用户扫描二维码,跳出支付页面
⑤用户确认支付,输入密码,支付成功
⑥交易成功后,微信返回支付成功页面。
前台界面编写
文档结构
支付页面:wxpay.html
qruous.js下载:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<!-- 引入vue组件进行调用 -->
<script src="js/vue.js"></script>
<script src="js/axios.min.js"></script>
<!-- 二维码生成插件 -->
<script src="js/qrious.js"></script>
</head>
<body>
<input type="button" name="buy" id="buy" value="支付" onClick="buy(11)" />
<input type="button" name="buy" id="buy" value="查询" onClick="queryPay(568718542894128)" />
<div class="erweima" id = "erweima">
<div class="hr">
<div class="tc" @click="yinc"></div>
</div>
<img id="qrious">
<div class="saosao">
<p>请使用微信扫一扫</p>
<p>扫描二维码支付</p>
</div>
<div id="zfcg">支付成功!</div>
</div>
</body>
<script>
// 点击支付按钮调用后端代码生成预付链接,将预付链接信息生成到QRious value中动态生成二维码链接
function buy(orderId) {
document.getElementById("qrious").setAttribute("src", "");
document.getElementById("erweima").style.visibility= "visible";
document.getElementById("erweima").style.width="600px";
document.getElementById("erweima").style.opacity="1";
let order = {};
order.orderId = "568718542894128";
order.fee = "1";//默认金额 单位分
order.expirTime = 5; //失效时间
axios.post(`http://localhost:8080/order/createPay`, order).then(res=>{
let obj = res.data;
console.log(obj)
if (res.status === 200) {
var qr = new QRious({
element: document.getElementById('qrious'),
size: 250,
level: 'H',
value: obj.data.code_url
});
setTimeout(() => {
queryPay(orderId)
}, 10000);
}
})
}
function queryPay(orderId){
let order = {};
order.orderId=orderId;
axios.post(`http://localhost:8080/order/queryPayStatus`, order).then(res=>{
let obj = res.data;
console.log(obj)
setTimeout(() => {
location.href = "success.html";
}, 10000);
})
}
</script>
<style>
#buy {
float: none;
}
.erweima {
/* width: 600px; */
width: 0px;
height: 500px;
background-color: gray;
opacity: 0.98;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
z-index: 99;
/* display: none; */
visibility: hidden;
opacity: 0;
transition: 0.45s all;
}
.hr {
height: 30px;
background-color: #333;
color: #fff;
}
.tc::after {
content: "X";
position: absolute;
right: 0px;
/* line-height: 30px; */
/* background-color: red; */
padding: 5px 8px;
font-size: 15px;
transition: 0.4s all;
}
.tc:hover::after {
background-color: #f00;
}
#qrious {
width: 250px;
height: 250px;
/* margin: 100px auto; */
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
background-image: url(./images/jiaz.gif);
background-position: center;
}
.saosao {
position: absolute;
left: 50%;
top: 85%;
transform: translate(-50%, -50%);
text-align: center;
color: #fff;
}
.saosao p {
margin-top: 10px;
}
#zfcg {
width: 250px;
height: 250px;
color: #0cdd0c;
background-color: black;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
text-align: center;
line-height: 250px;
font-size: 28px;
opacity: 0.85;
font-weight: 600;
display: none;
}
.bottom {
height: 100px;
width: 100%;
float: left;
}
</style>
</html>
说明:上面静态代码已将订单编号写死,应根据实际业务动态填写相应订单编号;设置10秒自动跳转,无法判断是否已支付。
成功后跳转页面:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<h1>支付成功跳转页面</h1>
</body>
</html>
后台代码:
引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--微信支付依赖包-->
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
<!--实现http请求的依赖包-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
vo类:
支付定义实体
package com.weiah.wxpay.vo;
import lombok.Data;
@Data
public class PayVo {
private String orderId;//订单编号
private String fee; //金额
private Integer expirTime; //失效时间
}
定义返回结果:
package com.weiah.wxpay.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class BusiResult implements Serializable {
private Integer status; //200成功 201失败
private String msg; //提示信息
private Object data; //服务器返回值数据
public static BusiResult fail(){
return new BusiResult(201,"业务调用失败!!",null);
}
public static BusiResult fail(int code, String msg){
return new BusiResult(code, msg, null);
}
public static BusiResult success(){
return new BusiResult(200,"业务调用成功!!",null);
}
public static BusiResult success(Object data){
return new BusiResult(200,"业务调用成功!!",data);
}
public static BusiResult success(String msg,Object data){
return new BusiResult(200,msg,data);
}
}
新建Http请求工具类:
package com.weiah.wxpay.vo;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.ParseException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* http请求客户端
*/
public class HttpUtil {
private String url;
private Map<String, String> param;
private int statusCode;
private String content;
private String xmlParam;
private boolean isHttps;
public boolean isHttps() {
return isHttps;
}
public void setHttps(boolean isHttps) {
this.isHttps = isHttps;
}
public String getXmlParam() {
return xmlParam;
}
public void setXmlParam(String xmlParam) {
this.xmlParam = xmlParam;
}
public HttpUtil(String url, Map<String, String> param) {
this.url = url;
this.param = param;
}
public HttpUtil(String url) {
this.url = url;
}
public void setParameter(Map<String, String> map) {
param = map;
}
public void addParameter(String key, String value) {
if (param == null)
param = new HashMap<String, String>();
param.put(key, value);
}
public void post() throws ClientProtocolException, IOException {
HttpPost http = new HttpPost(url);
setEntity(http);
execute(http);
}
public void put() throws ClientProtocolException, IOException {
HttpPut http = new HttpPut(url);
setEntity(http);
execute(http);
}
public void get() throws ClientProtocolException, IOException {
if (param != null) {
StringBuilder url = new StringBuilder(this.url);
boolean isFirst = true;
for (String key : param.keySet()) {
if (isFirst)
url.append("?");
else
url.append("&");
url.append(key).append("=").append(param.get(key));
}
this.url = url.toString();
}
HttpGet http = new HttpGet(url);
execute(http);
}
/**
* set http post,put param
*/
private void setEntity(HttpEntityEnclosingRequestBase http) {
if (param != null) {
List<NameValuePair> nvps = new LinkedList<NameValuePair>();
for (String key : param.keySet())
nvps.add(new BasicNameValuePair(key, param.get(key))); // 参数
http.setEntity(new UrlEncodedFormEntity(nvps, Consts.UTF_8)); // 设置参数
}
if (xmlParam != null) {
http.setEntity(new StringEntity(xmlParam, Consts.UTF_8));
}
}
private void execute(HttpUriRequest http) throws ClientProtocolException,
IOException {
CloseableHttpClient httpClient = null;
try {
if (isHttps) {
SSLContext sslContext = new SSLContextBuilder()
.loadTrustMaterial(null, new TrustStrategy() {
// 信任所有
public boolean isTrusted(X509Certificate[] chain,
String authType)
throws CertificateException {
return true;
}
}).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslContext);
httpClient = HttpClients.custom().setSSLSocketFactory(sslsf)
.build();
} else {
httpClient = HttpClients.createDefault();
}
CloseableHttpResponse response = httpClient.execute(http);
try {
if (response != null) {
if (response.getStatusLine() != null)
statusCode = response.getStatusLine().getStatusCode();
HttpEntity entity = response.getEntity();
// 响应内容
content = EntityUtils.toString(entity, Consts.UTF_8);
}
} finally {
response.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
httpClient.close();
}
}
public int getStatusCode() {
return statusCode;
}
public String getContent() throws ParseException, IOException {
return content;
}
}
定义微信支付公共工具类:
package com.weiah.wxpay.util;
import com.github.wxpay.sdk.WXPayUtil;
import org.springframework.beans.factory.annotation.Value;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
public class WXPayPubUtil {
private static final String APP_ID = "wx8397f8696b538317";
private static final String PARTNER = "1473426802";
private static final String PARTNER_KEY = "T6m9iK73b0kn9g5v426MKfHQH7X8rKwb";
private static final String PAY_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
private static final String QRY_URL = "https://api.mch.weixin.qq.com/pay/orderquery";
/**
*
* @param out_trade_no 订单编号
* @param total_fee 总金额
* @param expireTimeMin 失效时间
* @return
*/
public static Map createNative(String out_trade_no, String total_fee , Integer expireTimeMin) {
Map<String, String> param = new HashMap();
param.put("appid", APP_ID);
param.put("mch_id", PARTNER);
param.put("nonce_str", WXPayUtil.generateNonceStr());
param.put("body", "测试支付功能"); //定义订单内容
param.put("out_trade_no", out_trade_no); //商户订单编号
param.put("total_fee", total_fee); //总金额
param.put("spbill_create_ip", "127.0.0.1");
param.put("notify_url", "http://www.tedu.cn"); //回调地址
param.put("trade_type", "NATIVE");
Date date = new Date();
long time = date.getTime();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
String timeStart = simpleDateFormat.format(time);
String timeEnd = simpleDateFormat.format(time + expireTimeMin * 60000);
param.put("time_start", timeStart); //订单创建时间
param.put("time_expire", timeEnd); //定义失效时间
try {
String xmlParam = WXPayUtil.generateSignedXml(param, PARTNER_KEY); //参数转换
HttpUtil client = new HttpUtil(PAY_URL);
client.setHttps(true);
client.setXmlParam(xmlParam);
client.post();
String result = client.getContent();
Map<String, String> resultMap = WXPayUtil.xmlToMap(result);
if (resultMap.get("err_code") != null && resultMap.get("err_code").equals("ORDERPAID")) {
System.out.println("<--"+out_trade_no+"--> :订单已经支付!");
}
Map<String, String> map = new HashMap<>();
map.put("code_url", resultMap.get("code_url"));
map.put("total_fee", total_fee);
map.put("out_trade_no", out_trade_no);
return map;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* @param out_trade_no 订单id
* @return
*/
public static Map queryPayStatus(String out_trade_no) {
Map param = new HashMap();
param.put("appid", APP_ID);
param.put("mch_id", PARTNER);
param.put("out_trade_no", out_trade_no);
param.put("nonce_str", WXPayUtil.generateNonceStr());
try {
String xmlParam = WXPayUtil.generateSignedXml(param, PARTNER_KEY);
HttpUtil client = new HttpUtil(QRY_URL);
client.setHttps(true);
client.setXmlParam(xmlParam);
client.post();
String result = client.getContent();
Map<String, String> map = WXPayUtil.xmlToMap(result);
if (map.get("err_code") != null && map.get("err_code").equals("ORDERNOTEXIST")) {
System.out.println("订单不存在!");
}
if (map.get("trade_state").equals("SUCCESS")) {
//支付成功,未支付为:NOTPAY
}
return map;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
controller类:
package com.weiah.wxpay.controller;
import com.weiah.wxpay.util.WXPayPubUtil;
import com.weiah.wxpay.vo.BusiResult;
import com.weiah.wxpay.vo.PayVo;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController
@CrossOrigin
@RequestMapping("/order")
public class PayController {
@PostMapping("/createPay")
public BusiResult createPayOrder(@RequestBody PayVo payVo){
//向微信发起调用生成预付链接
Map res = WXPayPubUtil.createNative(payVo.getOrderId(),
payVo.getFee(),
payVo.getExpirTime());
System.out.println(res);
return BusiResult.success(res);
}
@PostMapping("/queryPayStatus")
public BusiResult queryPayStatus(@RequestBody PayVo payVo){
//向微信发起调用生成预付链接
Map res = WXPayPubUtil.queryPayStatus(payVo.getOrderId());
System.out.println(res);
return BusiResult.success(res);
}
}
以上可实现简单支付查询功能。