34.前台系统-微信支付
提示
需要使用微信扫码支付
- name: 🚀 微信支付介绍
desc: '接入微信支付'
link: /pages/390482/
bgColor: '#DFEEE7'
textColor: '#2A3344'
2
3
4
5
# 需求分析
业务流程
# 二维码&交易记录 service-order
# 添加依赖
在pom文件中添加依赖
<!-- 微信支付 -->
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
2
3
4
5
6
# 添加配置
添加商户信息的配置key,以及redis配置
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.database= 0
spring.redis.timeout=1800000
spring.redis.lettuce.pool.max-active=20
spring.redis.lettuce.pool.max-wait=-1
#最大阻塞等待时间(负数表示没限制)
spring.redis.lettuce.pool.max-idle=5
spring.redis.lettuce.pool.min-idle=0
#关联的公众号appid
weixin.pay.appid=wx74862e0dfcf69954
#商户号
weixin.pay.partner=1558950191
#商户key
weixin.pay.partnerkey=T6m9iK73b0kn9g5v426MKfHQH7X8rKwb
#证书,todo后期预约退款时添加
weixin.pay.cert=
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 添加属性读取类
创建com.stt.yygh.order.util.ConstantPropertiesUtils
读取商户配置信息
package com.stt.yygh.order.util;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class ConstantPropertiesUtils implements InitializingBean {
@Value("${weixin.appid}")
private String appid;
@Value("${weixin.partner}")
private String partner;
@Value("${weixin.partnerkey}")
private String partnerkey;
public static String APPID;
public static String PARTNER;
public static String PARTNERKEY;
@Override
public void afterPropertiesSet() throws Exception {
APPID = appid;
PARTNER = partner;
PARTNERKEY = partnerkey;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 添加httpClient封装客户端
注意:由于本项目中使用远程调用的httpClient很多,在service-util
中也封装了HttpUtil,那么后期的优化点可以将这2个调用合成一个统一的工具类,同时可以针对微信支付特别处理的部分抽取出来
本客户端需要使用微信的证书文件,用于退款使用
package com.stt.yygh.order.util;
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.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.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.text.ParseException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* http请求客户端
*/
public class HttpClient {
private String url;
private Map<String, String> param;
private int statusCode;
private String content;
private String xmlParam;
private boolean isHttps;
private boolean isCert = false;
//证书密码 微信商户号(mch_id)
private String certPassword;
public boolean isHttps() {
return isHttps;
}
public void setHttps(boolean isHttps) {
this.isHttps = isHttps;
}
public boolean isCert() {
return isCert;
}
public void setCert(boolean cert) {
isCert = cert;
}
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 String getCertPassword() {
return certPassword;
}
public void setCertPassword(String certPassword) {
this.certPassword = certPassword;
}
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 IOException {
CloseableHttpClient httpClient = null;
try {
if (isHttps) {
if (isCert) {
FileInputStream inputStream = new FileInputStream(new File(ConstantPropertiesUtils.CERT));
KeyStore keystore = KeyStore.getInstance("PKCS12");
char[] partnerId2charArray = certPassword.toCharArray();
keystore.load(inputStream, partnerId2charArray);
SSLContext sslContext = SSLContexts.custom().loadKeyMaterial(keystore, partnerId2charArray).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext,
new String[]{"TLSv1"},
null,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
} else {
// 信任所有
SSLContext sslContext = new SSLContextBuilder()
.loadTrustMaterial(null, (chain, authType) -> 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;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# 添加交易记录实体类 model
在model中添加 com.stt.yygh.model.order.PaymentInfo
类
package com.stt.yygh.model.order;
import com.stt.yygh.model.base.BaseEntity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
@Data
@ApiModel(description = "PaymentInfo")
@TableName("payment_info")
public class PaymentInfo extends BaseEntity {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "对外业务编号")
@TableField("out_trade_no")
private String outTradeNo;
@ApiModelProperty(value = "订单编号")
@TableField("order_id")
private Long orderId;
@ApiModelProperty(value = "支付类型(微信 支付宝)")
@TableField("payment_type")
private Integer paymentType;
@ApiModelProperty(value = "交易编号")
@TableField("trade_no")
private String tradeNo;
@ApiModelProperty(value = "支付金额")
@TableField("total_amount")
private BigDecimal totalAmount;
@ApiModelProperty(value = "交易内容")
@TableField("subject")
private String subject;
@ApiModelProperty(value = "支付状态")
@TableField("payment_status")
private Integer paymentStatus;
@ApiModelProperty(value = "回调时间")
@TableField("callback_time")
private Date callbackTime;
@ApiModelProperty(value = "回调信息")
@TableField("callback_content")
private String callbackContent;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
添加支付状态枚举类 com.stt.yygh.enums.PaymentStatusEnum
类
package com.stt.yygh.enums;
public enum PaymentStatusEnum {
UNPAID(1,"支付中"),
PAID(2,"已支付"),
REFUND(-1,"已退款");
private Integer status ;
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private String name ;
PaymentStatusEnum(Integer status, String name) {
this.status = status;
this.name=name;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
支付类型枚举类
package com.stt.yygh.enums;
public enum PaymentTypeEnum {
ALIPAY(1,"支付宝"),
WEIXIN(2,"微信" );
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
private Integer status ;
private String comment ;
PaymentTypeEnum(Integer status, String comment ){
this.status = status;
this.comment=comment;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 添加交易记录mapper
在service-order
中添加交易记录mapper类
package com.stt.yygh.order.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.stt.yygh.model.order.PaymentInfo;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface PaymentInfoMapper extends BaseMapper<PaymentInfo> {
}
2
3
4
5
6
7
8
9
# 添加交易记录 serivce 接口与实现
在service-order中添加PaymentService 类
package com.stt.yygh.order.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.stt.yygh.model.order.OrderInfo;
import com.stt.yygh.model.order.PaymentInfo;
public interface PaymentService extends IService<PaymentInfo> {
/**
* 保存交易记录
* @param order
* @param paymentType 支付类型(1:微信 2:支付宝)
*/
void savePaymentInfo(OrderInfo order, Integer paymentType);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
添加对应的实现
package com.stt.yygh.order.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.stt.yygh.enums.PaymentStatusEnum;
import com.stt.yygh.model.order.OrderInfo;
import com.stt.yygh.model.order.PaymentInfo;
import com.stt.yygh.order.mapper.PaymentInfoMapper;
import com.stt.yygh.order.service.PaymentService;
import org.joda.time.DateTime;
import org.springframework.stereotype.Service;
import java.util.Date;
@Service
public class PaymentServiceImpl extends ServiceImpl<PaymentInfoMapper, PaymentInfo> implements PaymentService {
/**
* 保存交易记录
*
* @param orderInfo
* @param paymentType 支付类型(1:微信 2:支付宝)
*/
@Override
public void savePaymentInfo(OrderInfo orderInfo, Integer paymentType) {
// 如果支付订单已经存在,则不做处理
if (paymentExisted(orderInfo.getId(), paymentType)) {
return;
}
// 保存交易记录
PaymentInfo paymentInfo = createPayment(orderInfo,paymentType);
baseMapper.insert(paymentInfo);
}
private boolean paymentExisted(Long orderId, Integer paymentType) {
QueryWrapper<PaymentInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("order_id", orderId);
queryWrapper.eq("payment_type", paymentType);
Integer count = baseMapper.selectCount(queryWrapper);
return count > 0;
}
private PaymentInfo createPayment(OrderInfo order,Integer paymentType){
PaymentInfo re = new PaymentInfo();
re.setCreateTime(new Date());
re.setOrderId(order.getId());
re.setPaymentType(paymentType);
re.setOutTradeNo(order.getOutTradeNo());
re.setTotalAmount(order.getAmount());
re.setPaymentStatus(PaymentStatusEnum.UNPAID.getStatus());
re.setSubject(new DateTime(order.getReserveDate()).toString("yyyy-MM-dd") + "|" +
order.getHosname() + "|" +
order.getDepname() + "|" +
order.getTitle());
return re;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# 添加微信支付service 接口与实现
创建 com.stt.yygh.order.service.WeixinService
类
package com.stt.yygh.order.service;
import java.util.Map;
public interface WeixinService {
/**
* 根据订单号下单,生成支付链接
*/
Map createNative(Long orderId);
}
2
3
4
5
6
7
8
9
10
对应实现
package com.stt.yygh.order.service.impl;
import com.github.wxpay.sdk.WXPayUtil;
import com.stt.yygh.enums.PaymentTypeEnum;
import com.stt.yygh.model.order.OrderInfo;
import com.stt.yygh.order.service.OrderService;
import com.stt.yygh.order.service.PaymentService;
import com.stt.yygh.order.service.WeixinService;
import com.stt.yygh.order.util.ConstantPropertiesUtils;
import com.stt.yygh.order.util.HttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
@Service
public class WeixinServiceImpl implements WeixinService {
@Autowired
private OrderService orderService;
@Autowired
private PaymentService paymentService;
@Autowired
private RedisTemplate redisTemplate;
/**
* 根据订单号下单,生成支付链接
*/
@Override
public Map createNative(Long orderId) {
try {
Map payMap = readCache(orderId.toString());
if (!Objects.isNull(payMap)) {
return payMap;
}
//根据id获取订单信息
OrderInfo order = orderService.getById(orderId);
// 保存交易记录
paymentService.savePaymentInfo(order, PaymentTypeEnum.WEIXIN.getStatus());
//1. 设置微信参数
Map<String, String> param = createWeixinParam(order);
//2. 发送请求给微信
Map<String, String> resultMap = sendToWeixin(param);
//3、封装返回结果集
return result(order, resultMap);
} catch (Exception e) {
e.printStackTrace();
}
return new HashMap<>();
}
private Map readCache(String key) {
return (Map) redisTemplate.opsForValue().get(key);
}
private Map<String, String> createWeixinParam(OrderInfo order) {
Map<String, String> re = new HashMap<>();
re.put("appid", ConstantPropertiesUtils.APPID);
re.put("mch_id", ConstantPropertiesUtils.PARTNER);
re.put("nonce_str", WXPayUtil.generateNonceStr()); // 随机字符串
re.put("body", order.getReserveDate() + "就诊" + order.getDepname());
re.put("out_trade_no", order.getOutTradeNo());
re.put("spbill_create_ip", "127.0.0.1");
re.put("trade_type", "NATIVE");
re.put("notify_url", "http://guli.shop/api/order/weixinPay/weixinNotify");
//paramMap.put("total_fee", order.getAmount().multiply(new BigDecimal("100")).longValue()+"");
// 测试中使用1分钱进行测试
re.put("total_fee", "1");
return re;
}
private Map<String, String> sendToWeixin(Map<String, String> param) throws Exception {
//HTTPClient来根据URL访问第三方接口并且传递参数
HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");
//client设置参数,对参数使用商户key进行加密
client.setXmlParam(WXPayUtil.generateSignedXml(param, ConstantPropertiesUtils.PARTNERKEY));
client.setHttps(true);
client.post();
//返回第三方的数据 client返回的是xml格式,需要转换为map
return WXPayUtil.xmlToMap(client.getContent());
}
// 封装结果
private Map<String, Object> result(OrderInfo order, Map<String, String> weixinResult) {
Map<String, Object> re = new HashMap<>();
re.put("orderId", order.getId());
re.put("totalFee", order.getAmount());
re.put("resultCode", weixinResult.get("result_code"));
re.put("codeUrl", weixinResult.get("code_url")); // 二维码url
if (!Objects.isNull(weixinResult.get("result_code"))) {
// 微信支付二维码 2小时过期,可采取2小时未支付取消订单
redisTemplate.opsForValue().set(order.getId().toString(), re, 120, TimeUnit.MINUTES);
}
return re;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# 添加微信支付controller
创建com.stt.yygh.order.controller.WeixinController
类
package com.stt.yygh.order.controller;
import com.stt.yygh.common.result.Result;
import com.stt.yygh.order.service.WeixinService;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/order/weixin")
public class WeixinController {
@Autowired
private WeixinService service;
/**
* 下单 生成二维码
*/
@GetMapping("/createNative/{orderId}")
public Result createNative(@ApiParam(name = "orderId", value = "订单id", required = true)
@PathVariable("orderId") Long orderId) {
return Result.ok(service.createNative(orderId));
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 二维码&交易记录 yygh-site
# 封装api
创建api/order/weixin.js
文件
import request from '@/utils/request'
export function createNative(orderId) {
return request({
url: `/api/order/weixin/createNative/${orderId}`,
method: 'get'
})
}
2
3
4
5
6
7
8
# 引入 vue-qriously 生成二维码
安装
npm install vue-qriously
引入:修改plugins/my-plugins.js
文件,添加如下配置
...
import VueQriously from 'vue-qriously'
Vue.use(VueQriously)
2
3
# 修改页面组件
修改/pages/order/show.vue组件
<template>
<!-- header -->
<div class="nav-container page-component">
...
<!-- 微信支付弹出框 -->
<el-dialog :visible.sync="dialogPayVisible" style="text-align: left" :append-to-body="true" width="500px"
@close="closeDialog">
<div class="container">
<div class="operate-view" style="height: 350px;">
<div class="wrapper wechat">
<qriously :value="payObj.codeUrl" :size="220"></qriously>
<span style="text-align: center;line-height: 25px;margin-bottom: 40px;">
请使用微信扫一扫<br/>扫描二维码支付
</span>
</div>
</div>
</div>
</el-dialog>
</div>
<!-- footer -->
</template>
<script>
import '~/assets/css/hospital_personal.css'
import '~/assets/css/hospital.css'
import { getOrders } from '@/api/order/orderInfo'
import { createNative } from '@/api/order/weixin'
export default {
data() {
return {
...
// 微信支付
dialogPayVisible: false,
payObj: {},
timer: null // 定时器,用于定时查询支付状态
}
},
...
methods: {
...
pay() { // 打开微信支付
this.dialogPayVisible = true
createNative(this.orderId).then(res => {
this.payObj = res.data
if (this.payObj.codeUrl == '') {
this.dialogPayVisible = false
this.$message.error('支付错误')
} else {
this.timer = setInterval(() => {
this.queryPayStatus(this.orderId)
}, 3000)
}
})
},
queryPayStatus(orderId) { // 定时轮询查支付状态
},
closeDialog() {
clearInterval(this.timer)
}
}
}
</script>
...
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# 测试
从挂号订单中进入挂号订单页面,点击详情->点击支付按键,出现要支付的二维码
手机扫码后显示
同时在payment_info表中生成一个支付记录,支付状态的值为1,表示支付中
# 查询支付状态 service-order
# 添加service接口与实现
在WeixinService类添加接口
package com.stt.yygh.order.service;
import java.util.Map;
public interface WeixinService {
...
/**
* 根据订单号去微信第三方查询支付状态
*/
Map queryPayStatus(Long orderId, String paymentType);
}
2
3
4
5
6
7
8
9
10
11
对应的实现
- 访问weixin的https://api.mch.weixin.qq.com/pay/orderquery获取订单状态
package com.stt.yygh.order.service.impl;
import com.github.wxpay.sdk.WXPayUtil;
import com.stt.yygh.enums.PaymentTypeEnum;
import com.stt.yygh.model.order.OrderInfo;
import com.stt.yygh.order.service.OrderService;
import com.stt.yygh.order.service.PaymentService;
import com.stt.yygh.order.service.WeixinService;
import com.stt.yygh.order.util.ConstantPropertiesUtils;
import com.stt.yygh.order.util.HttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
@Service
public class WeixinServiceImpl implements WeixinService {
@Autowired
private OrderService orderService;
@Autowired
private PaymentService paymentService;
@Autowired
private RedisTemplate redisTemplate;
...
@Override
public Map queryPayStatus(Long orderId, String paymentType) {
try {
OrderInfo orderInfo = orderService.getById(orderId);
//1、封装参数
Map<String, String> param = new HashMap<>();
param.put("appid", ConstantPropertiesUtils.APPID);
param.put("mch_id", ConstantPropertiesUtils.PARTNER);
param.put("out_trade_no", orderInfo.getOutTradeNo());
param.put("nonce_str", WXPayUtil.generateNonceStr());
//2、设置请求
HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");
client.setXmlParam(WXPayUtil.generateSignedXml(param, ConstantPropertiesUtils.PARTNERKEY));
client.setHttps(true);
client.post();
//3、返回第三方的数据,转成Map
return WXPayUtil.xmlToMap(client.getContent());
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# 添加controller方法
如果支付成功,则要更新支付状态、通知医院该预约单已经支付成功
package com.stt.yygh.order.controller;
import com.stt.yygh.common.result.Result;
import com.stt.yygh.enums.PaymentTypeEnum;
import com.stt.yygh.order.service.WeixinService;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
import java.util.Objects;
@RestController
@RequestMapping("/api/order/weixin")
public class WeixinController {
@Autowired
private WeixinService service;
...
@ApiOperation(value = "查询支付状态")
@GetMapping("/queryPayStatus/{orderId}")
public Result queryPayStatus(@ApiParam(name = "orderId", value = "订单id", required = true)
@PathVariable("orderId") Long orderId) {
//调用查询接口
Map<String, String> resultMap = service.queryPayStatus(orderId, PaymentTypeEnum.WEIXIN.name());
//出错
if (Objects.isNull(resultMap)) {
return Result.fail().message("支付出错");
}
//如果成功
if ("SUCCESS".equals(resultMap.get("trade_state"))) {
// todo 更改订单状态,处理支付结果
return Result.ok().message("支付成功");
}
return Result.ok().message("支付中");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 查询支付状态 yygh-site
# 封装api请求
在api/order/weixin.js
文件中添加接口
import request from '@/utils/request'
...
export function queryPayStatus(orderId) {
return request({
url: `/api/order/weixin/queryPayStatus/${orderId}`,
method: 'get'
})
}
2
3
4
5
6
7
8
# 修改页面组件
修改/pages/order/show.vue组件,添加轮询查询逻辑,当非【支付中】的状态时,刷新页面重新获取订单状态
<template>
...
<script>
...
import { createNative, queryPayStatus } from '@/api/order/weixin'
export default {
...
methods: {
...
queryPayStatus(orderId) { // 定时轮询查支付状态
queryPayStatus(orderId).then(res => {
if (res.message === '支付中') return
clearInterval(this.timer);
window.location.reload()
})
},
...
}
}
</script>
...
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 处理支付成功 service-order
# 添加service接口与实现
修改 PaymentService类,添加支付成功后的逻辑处理
package com.stt.yygh.order.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.stt.yygh.model.order.OrderInfo;
import com.stt.yygh.model.order.PaymentInfo;
import java.util.Map;
public interface PaymentService extends IService<PaymentInfo> {
/**
* 保存交易记录
* @param order
* @param paymentType 支付类型(1:微信 2:支付宝)
*/
void savePaymentInfo(OrderInfo order, Integer paymentType);
/**
* 支付成功
*/
void paySuccess(String outTradeNo, Integer paymentType, Map<String, String> paramMap);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
对应实现
package com.stt.yygh.order.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.stt.yygh.common.exception.YyghException;
import com.stt.yygh.common.result.ResultCodeEnum;
import com.stt.yygh.enums.OrderStatusEnum;
import com.stt.yygh.enums.PaymentStatusEnum;
import com.stt.yygh.model.order.OrderInfo;
import com.stt.yygh.model.order.PaymentInfo;
import com.stt.yygh.order.mapper.PaymentInfoMapper;
import com.stt.yygh.order.service.OrderService;
import com.stt.yygh.order.service.PaymentService;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.Map;
import java.util.Objects;
@Service
public class PaymentServiceImpl extends ServiceImpl<PaymentInfoMapper, PaymentInfo> implements PaymentService {
@Autowired
private OrderService orderService;
...
/**
* 支付成功
*/
@Override
public void paySuccess(String outTradeNo, Integer paymentType, Map<String, String> paramMap) {
PaymentInfo paymentInfo = this.getPaymentInfo(outTradeNo, paymentType);
if (Objects.isNull(paymentInfo)) {
throw new YyghException(ResultCodeEnum.PARAM_ERROR);
}
if (!Objects.equals(paymentInfo.getPaymentStatus(), PaymentStatusEnum.UNPAID.getStatus())) {
return;
}
//修改支付状态
PaymentInfo paymentInfoUpd = new PaymentInfo();
paymentInfoUpd.setPaymentStatus(PaymentStatusEnum.PAID.getStatus());
paymentInfoUpd.setTradeNo(paramMap.get("transaction_id"));
paymentInfoUpd.setCallbackTime(new Date());
paymentInfoUpd.setCallbackContent(paramMap.toString());
this.updatePaymentInfo(outTradeNo, paymentInfoUpd);
//修改订单状态
OrderInfo orderInfo = orderService.getById(paymentInfo.getOrderId());
orderInfo.setOrderStatus(OrderStatusEnum.PAID.getStatus());
orderService.updateById(orderInfo);
// todo 调用医院接口,通知更新支付状态
}
/**
* 获取支付记录
*/
private PaymentInfo getPaymentInfo(String outTradeNo, Integer paymentType) {
QueryWrapper<PaymentInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("out_trade_no", outTradeNo);
queryWrapper.eq("payment_type", paymentType);
return baseMapper.selectOne(queryWrapper);
}
/**
* 更改支付记录
*/
private void updatePaymentInfo(String outTradeNo, PaymentInfo paymentInfoUpd) {
QueryWrapper<PaymentInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("out_trade_no", outTradeNo);
baseMapper.update(paymentInfoUpd, queryWrapper);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# 更新医院系统支付状态
参考【附录:医院接口模拟系统说明】业务接口 -- 更新支付状态
修改 PaymentService类,调用医院模拟系统接口
package com.stt.yygh.order.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.stt.yygh.common.exception.YyghException;
import com.stt.yygh.common.helper.HttpRequestHelper;
import com.stt.yygh.common.result.ResultCodeEnum;
import com.stt.yygh.enums.OrderStatusEnum;
import com.stt.yygh.enums.PaymentStatusEnum;
import com.stt.yygh.hosp.client.HospitalFeignClient;
import com.stt.yygh.model.order.OrderInfo;
import com.stt.yygh.model.order.PaymentInfo;
import com.stt.yygh.order.mapper.PaymentInfoMapper;
import com.stt.yygh.order.service.OrderService;
import com.stt.yygh.order.service.PaymentService;
import com.stt.yygh.vo.hosp.SignInfoVo;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@Service
public class PaymentServiceImpl extends ServiceImpl<PaymentInfoMapper, PaymentInfo> implements PaymentService {
@Autowired
private OrderService orderService;
@Autowired
private HospitalFeignClient hospitalFeignClient;
...
/**
* 支付成功
*/
@Override
public void paySuccess(String outTradeNo, Integer paymentType, Map<String, String> paramMap) {
PaymentInfo paymentInfo = this.getPaymentInfo(outTradeNo, paymentType);
if (Objects.isNull(paymentInfo)) {
throw new YyghException(ResultCodeEnum.PARAM_ERROR);
}
if (!Objects.equals(paymentInfo.getPaymentStatus(), PaymentStatusEnum.UNPAID.getStatus())) {
return;
}
//修改支付状态
PaymentInfo paymentInfoUpd = new PaymentInfo();
paymentInfoUpd.setPaymentStatus(PaymentStatusEnum.PAID.getStatus());
paymentInfoUpd.setTradeNo(paramMap.get("transaction_id"));
paymentInfoUpd.setCallbackTime(new Date());
paymentInfoUpd.setCallbackContent(paramMap.toString());
this.updatePaymentInfo(outTradeNo, paymentInfoUpd);
//修改订单状态
OrderInfo orderInfo = orderService.getById(paymentInfo.getOrderId());
orderInfo.setOrderStatus(OrderStatusEnum.PAID.getStatus());
orderService.updateById(orderInfo);
// 调用医院接口,通知更新支付状态
updatePayStatusForRemoteHospSystem(orderInfo);
}
private void updatePayStatusForRemoteHospSystem(OrderInfo order){
SignInfoVo signInfoVo = hospitalFeignClient.getSignInfoVo(order.getHoscode());
if(Objects.isNull(signInfoVo)) {
throw new YyghException(ResultCodeEnum.PARAM_ERROR);
}
// 调用远端接口,更新医院模拟系统数据
Map<String, Object> param = new HashMap<>();
param.put("hoscode",order.getHoscode());
param.put("hosRecordId",order.getHosRecordId());
param.put("timestamp", HttpRequestHelper.getTimestamp());
param.put("sign", HttpRequestHelper.getSign(param, signInfoVo.getSignKey()));
JSONObject result = HttpRequestHelper.sendRequest(param, signInfoVo.getApiUrl()+"/order/updatePayStatus");
if(result.getInteger("code") != 200) {
throw new YyghException(result.getString("message"), ResultCodeEnum.FAIL.getCode());
}
}
...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84