1 开发流程
微信支付开发流程
业务流程说明:
(1)商户后台系统根据用户选购的商品生成订单。
(2)用户确认支付后调用微信支付【统一下单API】生成预支付交易;
(3)微信支付系统收到请求后生成预支付交易单,并返回交易会话的二维码链接code_url。
(4)商户后台系统根据返回的code_url生成二维码。
(5)用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统。
(6)微信支付系统收到客户端请求,验证链接有效性后发起用户支付,要求用户授权。
(7)用户在微信客户端输入密码,确认支付后,微信客户端提交授权。
(8)微信支付系统根据用户授权完成支付交易。
(9)微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。
(10)微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。
(11)未收到支付通知的情况,商户后台系统调用【查询订单API】。
(12)商户确认订单已支付后给用户发货。
下载微信SDK
微信没有提供m*en仓库坐标,因此我们必须下载使用
微信SDK下载,点击下载j*a对应的SDK,然后安装到m*en的本地仓库中。
进入下载好的SDK的项目目录,然后打开黑窗口,输入命令:
mvn source:jar install -Dm*en.test.skip=true
在pom.xml中引入
<dependency>
<groupId>com.github.wxpaygroupId>
<artifactId>wxpay-sdkartifactId>
<version>3.0.9version>
dependency>
WXPay工具
微信SDK提供了一个统一的微信支付工具类:WXPay:
com.github.wxpay.sdk.WXPay类下提供了对应的方法:
生成支付链接
根据微信提供的WXPay类编写了一个支付工具类PayUtils
import com.github.wxpay.sdk.PayConfig;
import com.github.wxpay.sdk.WXPay;
import org.apache.commons.lang3.StringUtils;
import j*a.text.SimpleDateFormat;
import j*a.util.Date;
import j*a.util.HashMap;
import j*a.util.Map;
/**
* 微信支付工具类
*/
public class PayUtils {
private static WXPay wxPay;
// 支付成功回调地址
private static String notifyUrl = "http://xxx.com/test/PayNotify";
// 初始化微信支付
static {
try {
PayConfig payConfig = new PayConfig();
payConfig.set***ID("wxXXXX"); // 公众账号ID
payConfig.setMchID("XXXXX");// 商户号
payConfig.setKey("XXXXXX");// 生成签名的密钥
wxPay = new WXPay(payConfig);
} catch (Exception e) {
// e.printStackTrace();
throw new RuntimeException(e.getMessage());
}
}
// 生成微信订单支付 url
public static String createOrder(String orderId, Integer totalPay) {
Map<String, String> data = new HashMap<>();
// 商品描述
data.put("body", "商品支付");
// 订单号
data.put("out_trade_no", orderId);
//金额,单位是分
data.put("total_fee", totalPay.toString());
//调用微信支付的终端IP
data.put("spbill_create_ip", "127.0.0.1");
//回调地址
data.put("notify_url", notifyUrl);
// 支付有效时间10分钟
Date now = new Date();
Date now_10 = new Date(now.getTime() + 600000); //10分钟后的时间
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");//可以方便地修改日期格式
String nowTime_10 = dateFormat.format(now_10);
data.put("time_expire", nowTime_10);
// 交易类型为扫码支付
data.put("trade_type", "NATIVE");
// 利用wxPay工具,完成下单
Map<String, String> result = null;
try {
result = wxPay.unifiedOrder(data);
} catch (Exception e) {
throw new RuntimeException("微信下单失败", e);
}
// 校验业务状态
checkResultCode(result);
// 下单成功,获取支付链接
String url = result.get("code_url");
if (StringUtils.isBlank(url)) {
throw new RuntimeException("微信下单失败,支付链接为空");
}
return url;
}
// 检查业务状态
public static void checkResultCode(Map<String, String> result) {
String resultCode = result.get("result_code");
if ("FAIL".equals(resultCode)) {
throw new RuntimeException("【微信支付】微信支付业务失败");
}
}
}
下单并生成支付链接
@RestController
@RequestM***ing("/test")
public class TestController {
@GetM***ing("createOrder")
public String createOrder() throws Exception {
String orderId = UUID.randomUUID().toString();
orderId = orderId.substring(orderId.length()-33,orderId.length()-1);
Integer totalPay = 1;
String payUrl = PayUtils.createOrder(orderId, totalPay);
return payUrl;
}
}
前端通过调用这个节课就可以生成一个微信支付的订单链接,
然后使用qrcode.js生成一个微信支付的二维码.
pay.html
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0,user-scalable=no,minimal-ui">
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" href="../img/asset-f*ico.ico">
<title>支付页面title>
<script src="../plugins/vue/vue.js">script>
<script src="../plugins/vue/axios-0.18.0.js">script>
<script src="../js/qrious.js">script>
<script src="../js/qrcode.js">script>
head>
<body data-spy="scroll" data-target="#myN*bar" data-offset="150">
<div id="***" class="***">
<center><h1>请扫描下方二维码进行支付!h1>center>
<hr>
<div class="contentBox" style="width: 100%">
<img alt="" id="qrious" style="height: 30%;width: 30%;display: block;margin:0 auto;">
div>
div>
body>
<script>
var vue = new Vue({
el: '#***',
data: {
payUrl: ''
},
created() {
// alert(id);
axios.get('/test/createOrder').then(res => {
if (res.status === 200) {
// alert("111")
console.log(res)
this.payUrl = res.data;
// 生成二维码
// new QRCode(document.getElementById("qrcode"), this.payUrl)
new QRious({
element: document.getElementById('qrious'),
level: 'L',
// 注意级别越高可能会影响到识别度
size: 300,
value: this.payUrl
})
}
})
}
});
script>
<script>
// 生成二维码
new QRious({
element: document.getElementById('qrious'),
level: 'L',
// 注意级别越高可能会影响到识别度
size: 300,
value: '${payUrl}'
})
script>
html>
支付结果通知
:8080/test/PayNotify这个地址是在支付成功后的异步结果通知。**介绍如下:
支付完成后和融通扫码支付,微信会把相关支付结果和用户信息发送给商户,商户需要接收处理,并返回应答。
所以,此处的地址必须是一个外网可访问地址,而且我们要定义好回调的处理接口。
内网穿透
此处我们肯定不能写::8080/tr*el/pay/notify,这个域名是本地,是不被外网识别的。可以通过内网穿透来实现。
内网穿透的目的是:让外网能访问你本地的应用,例如在外网打开你本地指向的Web站点。
使用nat***实现内网穿透
使用的nat***得到的域名,设置指向到 PayUtils工具类的支付成功回调地址。
支付结果通知API
支付完成后,微信会把相关支付结果和用户信息发送给商户,商户需要接收处理,并返回应答。
对后台通知交互时,如果微信收到商户的应答不是成功或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知最终能成功。 (通知频率为15/15/30/180/1800/1800/1800/1800/3600,单位:秒)
注意:同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。
推荐的做法是,当收到通知进行处理时,首先检查对应业务数据的状态,判断该通知是否已经处理过,如果没有处理过再进行处理,如果处理过直接返回结果成功。在对业务数据进行状态检查和处理之前和融通扫码支付,要***用数据锁进行并发控制,以避免函数重入造成的数据混乱。
特别提醒:商户系统对于支付结果通知的内容一定要做 签名验证,并校验返回的订单金额是否与商户侧的订单金额一致 ,防止数据泄漏导致出现“***通知”,造成资金损失。
支付完成后,微信服务会自动向 :8080/test/PayNotify 地址发起POST请求,请求参数是xml格式:
通信成功,会返回下面信息:
编写回调api: /test/PayNotify
@PostM***ing("PayNotify")
public void PayNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 1.接收请求参数(xml)
ServletInputStream in = request.getInputStream();
// 2.将xml转为j*a对象
XmlM***er xmlM***er = new XmlM***er();
Map param = xmlM***er.readValue(in, Map.class);
// 3.调用orderService,修改订单状态
// orderService.updateState(param);
// 4.返回微信平台,接收成功..
HashMap<String, String> result = new HashMap<>();
result.put("return_code", "SUCCESS");
result.put("return_msg", "OK");
// 将map转为xml
String xml = xmlM***er.writeValueAsString(result);
response.setContentType("***lication/xml;charset=utf-8");
response.getWriter().write(xml);
// return "yyy";
}
支付状态查询
当用户扫码支付成功,会自动调用回调接口,从而修改订单状态,完成订单支付。
可以在 /test/PayNotify 的第三步修改用户支付的该订单的支付状态pay_status为已支付;然后在页面的payOrder() 支付成功后调用this.isPay(),会***用定时任务,不断查询订单支付的状态:
<script>
var vue = new Vue({
el: '#***',
data: {
payUrl: '',
orderNo: ''
},
methods: {
payOrder() {
axios.get('/order/createPayOrder/' + this.orderNo).then(res => {
if (res.status === 200) {
// alert("111")
console.log(res)
this.payUrl = res.data.data;
// 生成二维码
// new QRCode(document.getElementById("qrcode"), this.payUrl)
new QRious({
element: document.getElementById('qrious'),
level: 'L',
// 注意级别越高可能会影响到识别度
size: 300,
value: this.payUrl
});
this.isPay()
}
})
},
isPay() {
// 开启定时任务,查询付款状态
let num = setInterval(function () {
// let url = '/order/isPay/'+this.orderNo;
axios.get('/order/isPay/' + this.orderNo).then(res => {
console.log(res.data)
if (res.data.flag && res.data.data === '1') {
alert("支付成功")
window.location.href = "/pages/pay-success.html";
}
})
}, 5000);
// 同时设置一个定时任务,10分钟后,终止查询,认为付款失败
setTimeout(function () {
clearInterval(num);
alert("支付失败")
window.location.href = "/pages/pay-fail.html";
}, 600000)
}
},
created() {
// alert(orderNo);
this.orderNo = orderNo;
this.payOrder()
}
});
script>
每隔5秒就会查询一次,为了防止用户一直不支付的情况,又设置了一个定时任务,10分钟后跳转到支付失败页面
@GetM***ing("/isPay/{orderNo}")
public Result isPay(@PathVariable("orderNo") String orderNo){
String isPay = orderService.isPay(orderNo);
return new Result(true,"",isPay);
}
Tag:
微信支付
微信扫码支付
移动支付
微信api
微商户