微信支付简介
商户后台系统先调用微信支付的统一下单接口,微信后台系统返回链接参数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)商户确认订单已支付后给用户发货。
生成微信支付二维码
应用场景
除付款码支付场景以外,商户系统先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易会话标识后再按Native、JSAPI、APP等不同场景生成交易串调起支付。
本次按Native
状态机
接口链接
URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder
URL地址:https://api2.mch.weixin.qq.com/pay/unifiedorder(备用域名)见跨城冗灾方案
是否需要证书:否
具体要传入参数,参考下面文档
https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1
SpringBoot工程搭建
引入依赖
<dependencies>
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.28</version>
</dependency>
<!--httpclient-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.1</version>
</dependency>
</dependencies>
工具类
1.根据时间和随机生成数组合成订单号
**
* 订单号工具类
*
* @author qy
* @since 1.0
*/
public class OrderNoUtil {
/**
* 获取订单号
* @return
*/
public static String getOrderNo() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String newDate = sdf.format(new Date());
String result = "";
Random random = new Random();
for (int i = 0; i < 3; i++) {
result += random.nextInt(10);
}
return newDate + result;
}
}
2.发送请求类 HttpClient
/**
* http请求客户端
*
* @author qy
*
*/
public class HttpClient {
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 HttpClient(String url, Map<String, String> param) {
this.url = url;
this.param = param;
}
public HttpClient(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;
}
}
controller
@ApiOperation(value = "根据订单编号生成支付二维码")
@GetMapping("createNative/{orderNo}")
public R createNative(@PathVariable String orderNo){
Map<String,Object> map= payLogService.createNative(orderNo);
return R.ok().data(map);
}
service
//根据订单编号生成支付二维码
@Override
public Map<String, Object> createNative(String orderNo) {
try {
//1查询订单信息
QueryWrapper<TOrder> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("order_no", orderNo);
TOrder order = orderService.getOne(queryWrapper);
//1.1校验订单
if (order == null) {
throw new GuliException(20001, "订单失效");
}
//2封装支付参数
Map m = new HashMap();
//设置支付参数
m.put("appid", "wx74862e0dfcf69954");//必填应用id
m.put("mch_id", "1558950191");//必填商户号
m.put("nonce_str", WXPayUtil.generateNonceStr());//必填微信生成随机字符串
m.put("body", order.getCourseTitle());//自己的业务内容课程名称
m.put("out_trade_no", orderNo);//订单编号
m.put("total_fee", order.getTotalFee().multiply(new BigDecimal("100")).longValue() + "");//交易金额
m.put("spbill_create_ip", "127.0.0.1");//终端ip
m.put("notify_url", "http://guli.shop/api/order/weixinPay/weixinNotify\n");//必填回调地址
m.put("trade_type", "NATIVE");//交易类型
//3通过httpclient发送请求,参数转化成xml
HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");//官方请求地址
//client设置参数
client.setXmlParam(WXPayUtil.generateSignedXml(m, "T6m9iK73b0kn9g5v426MKfHQH7X8rKwb"));//必填商户密钥
client.setHttps(true);
client.post();
//4 获取返回值
String xml = client.getContent();
System.out.println("xml=" + xml);
//解析返回map集合
Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);
//5、封装返回结果集
Map map = new HashMap<>();
map.put("out_trade_no", orderNo);//订单号
map.put("course_id", order.getCourseId());//自定义的商品id
map.put("total_fee", order.getTotalFee());//金额
map.put("result_code", resultMap.get("result_code"));//支付结果码
map.put("code_url", resultMap.get("code_url"));//支付二维码链接
return map;
} catch (Exception e) {
e.printStackTrace();
throw new GuliException(20001, "获取二维码失败");
}
}
返回的结果例子如下:
<xml>
<appid>wx2421b1c4370ec43b</appid>
<attach>支付测试</attach>
<body>JSAPI支付测试</body>
<mch_id>10000100</mch_id>
<detail><![CDATA[{ "goods_detail":[ { "goods_id":"iphone6s_16G", "wxpay_goods_id":"1001", "goods_name":"iPhone6s 16G", "quantity":1, "price":528800, "goods_category":"123456", "body":"苹果手机" }, { "goods_id":"iphone6s_32G", "wxpay_goods_id":"1002", "goods_name":"iPhone6s 32G", "quantity":1, "price":608800, "goods_category":"123789", "body":"苹果手机" } ] }]]></detail>
<nonce_str>1add1a30ac87aa2db72f57a2375d8fec</nonce_str>
<notify_url>https://wxpay.wxutil.com/pub_v2/pay/notify.v2.php</notify_url>
<openid>oUpF8uMuAJO_M2pxb1Q9zNjWeS6o</openid>
<out_trade_no>1415659990</out_trade_no>
<spbill_create_ip>14.23.150.211</spbill_create_ip>
<total_fee>1</total_fee>
<trade_type>JSAPI</trade_type>
<sign>0CB01533B8C1EF103065174F50BCA001</sign>
</xml>
最后把返回的code_url支付二维码链接,放进前端的二维码里面即可
查询支付情况
应用场景
该接口提供所有微信支付订单的查询,商户可以通过查询订单接口主动查询订单状态,完成下一步的业务逻辑。
需要调用查询接口的情况:
◆ 当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知(查单实现可参考:支付回调和查单实现指引);
◆ 调用支付接口后,返回系统错误或未知交易状态情况;
◆ 调用付款码支付API,返回USERPAYING的状态;
◆ 调用关单或撤销接口API之前,需确认支付状态;
接口链接
URL地址:https://api.mch.weixin.qq.com/pay/orderquery
URL地址:https://api2.mch.weixin.qq.com/pay/orderquery(备用域名)见跨城冗灾方案
是否需要证书:不需要
请求参数看文档:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_2
controller
@GetMapping("queryPayStatus/{orderNo}")
@ApiOperation(value = "根据订单编号查询支付状态")
public R queryPayStatus(@PathVariable String orderNo) {
//1调用微信接口查询支付状态
Map<String, String> map = payLogService.queryPayStatus(orderNo);
//2判断支付状态
if (map == null) {
return R.error().message("支付出错");
}
if ("SUCCESS".equals(map.get("trade_state"))) {
//3 支付成功后,更新订单状态,记录支付日志
payLogService.updateOrderStatus(map);
return R.ok().message("支付成功");
}
return R.ok().code(25000).message("支付中");
}
service
//调用微信接口查询支付状态
@Override
public Map<String, String> queryPayStatus(String orderNo) {
try {
//1封装支付参数
Map m = new HashMap();
//设置支付参数
m.put("appid", "wx74862e0dfcf69954");//必填应用id
m.put("mch_id", "1558950191");//必填商户号
m.put("out_trade_no", orderNo);//必填订单号
m.put("nonce_str", WXPayUtil.generateNonceStr());//微信工具生成随机字符
//2、设置请求
HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");//官方查询请求地址
client.setXmlParam(WXPayUtil.generateSignedXml(m, "T6m9iK73b0kn9g5v426MKfHQH7X8rKwb"));//商户密钥
client.setHttps(true);
client.post();
//3、返回第三方的数据
String xml = client.getContent();
System.out.println("订单状态="+xml);
//转义成map
Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);
//6、转成Map
//7、返回
return resultMap;
} catch (Exception e) {
e.printStackTrace();
throw new GuliException(20001,"查询订单状态失败");
}
}
返回结果例子:
<xml>
<appid>wx2421b1c4370ec43b</appid>
<mch_id>10000100</mch_id>
<nonce_str>ec2316275641faa3aacf3cc599e8730f</nonce_str>
<transaction_id>1008450740201411110005820873</transaction_id>
<sign>FDD167FAA73459FD921B144BAF4F4CA2</sign>
</xml>
提示
前端可以设置定时器每隔几秒查询一次支付状态,直到支付成功或失败
//每隔5秒钟,查询一下订单支付状态
//需要创建定时器setIntervar(()=>{},5000)
data() {
return {
timer1: ""
};
},
mounted() {
this.timer1 = setInterval(() => {
this.queryPayStatusInfo();
}, 5000);
},
methods: {
queryPayStatusInfo() {
pay.queryPayStatus(this.orderNo).then(response => {
if (response.data.success) {
//如果支付成功,清除定时器
clearInterval(this.timer1);
//提示成功
this.$message({
type: "success",
message: "支付成功!"
});
//跳转到课程详情页面观看视频
this.$router.push({ path: "/course/" + this.payObj.course_id });
}
});
}
}