[TOC]

一、微信支付介绍

1微信扫码支付申请

微信扫码支付是商户系统按微信支付协议生成支付二维码,用户再用微信“扫一扫”完成支付的模式。该模式适用于PC网站支付、实体店单品或订单支付、媒体广告支付等场景。
申请步骤:(了解)
第一步:注册公众号(类型须为:服务号)
请根据营业执照类型选择以下主体注册:个体工商户| 企业/公司| 政府| 媒体| 其他类型
第二步:认证公众号
公众号认证后才可申请微信支付,认证费:300元/年。
第三步:提交资料申请微信支付
登录公众平台,点击左侧菜单【微信支付】,开始填写资料等待审核,审核时间为1-5个工作日内。
第四步:开户成功,登录商户平台进行验证
资料审核通过后,请登录联系人邮箱查收商户号和密码,并登录商户平台填写财付通备付金打的小额资金数额,完成账户验证。
第五步:在线签署协议
本协议为线上电子协议,签署后方可进行交易及资金结算,签署完立即生效。

2开发文档

微信支付接口调用的整体思路:
按API要求组装参数,以XML方式发送(POST)给微信支付接口(URL),微信支付接口也是以XML方式给予响应。程序根据返回的结果(其中包括支付URL)生成二维码或判断订单状态。
在线微信支付开发文档:
https://pay.weixin.qq.com/wiki/doc/api/index.html

  1. appid:微信公众账号或开放平台APP的唯一标识
  2. mch_id:商户号 (配置文件中的partner)
  3. partnerkey:商户密钥
  4. sign:数字签名, 根据微信官方提供的密钥和一套算法生成的一个加密信息, 就是为了保证交易的安全性

    3微信支付SDK

    微信支付 - 图1
    添加依赖

com.github.wxpay
wxpay-sdk
0.0.3

我们主要会用到微信支付SDK的以下功能:

  1. 获取随机字符串 | WXPayUtil.generateNonceStr() | | —- |

  2. MAP转换为XML字符串(自动添加签名) | WXPayUtil.generateSignedXml(param, partnerkey) | | —- |

  3. XML字符串转换为MAP | WXPayUtil.xmlToMap(result) | | —- |

1 微信支付开发

1、api接口

场景:用户扫描商户展示在各种场景的二维码进行支付
使用案例:
线下:家乐福超市、7-11便利店、上品折扣线下店等
线上:大众点评网站、携程网站、唯品会、美丽说网站等

开发模式:
模式一:商户在后台给你生成二维码,用户打开扫一扫
模式二:商户后台系统调用微信支付【统一下单API】生成预付交易,将接口返回的链接生成二维码,用户扫码后输入密码完成支付交易。注意:该模式的预付单有效期为2小时,过期后无法支付。
微信支付:生成xml发送请求
操作模块:service-order

1.1 引入依赖

<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>

1.2 添加配置

在application.properties中添加商户信息

spring.redis.host=192.168.44.165spring.redis.port=6379spring.redis.database= 0spring.redis.timeout=1800000spring.redis.lettuce.pool.max-active=20spring.redis.lettuce.pool.max-wait=-1#最大阻塞等待时间(负数表示没限制)spring.redis.lettuce.pool.max-idle=5spring.redis.lettuce.pool.min-idle=0

#关联的公众号appidweixin.pay.appid=wx74862e0dfcf69954#商户号weixin.pay.partner=1558950191#商户keyweixin.pay.partnerkey=T6m9iK73b0kn9g5v426MKfHQH7X8rKwb

1.3引入工具类

| @Componentpublic class ConstantPropertiesUtils implements InitializingBean {

@Value(“${weixin.appid}”)
private String appid;

@Value(**"${weixin.partner}"**)<br />    **private **String **partner**;

@Value(**"${weixin.partnerkey}"**)<br />    **private **String **partnerkey**;

**public static **String _APPID_;<br />    **public static **String _PARTNER_;<br />    **public static **String _PARTNERKEY_;<br />    @Override<br />    **public void **afterPropertiesSet() **throws **Exception {<br />        _APPID _= **appid**;<br />        _PARTNER _= **partner**;<br />        _PARTNERKEY _= **partnerkey**;<br />    }<br />} |

| —- | | 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.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.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 HttpClient {
private String url;
private Map param;
private int statusCode;
private String content;
private String xmlParam;
private boolean isHttps;
private boolean isCert = false;
//证书密码 微信商户号(mchid)
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 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 map) {
param = map;
}
public void addParameter(String key, String value) {
if (param == null)
param = new HashMap();
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 nvps = new LinkedList();
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) {
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, 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*
;
}
} |

1.4 添加交易记录接口

1.4.1 添加Mapper

public interface PaymentInfoMapper extends BaseMapper {
}

1.4.2 添加service接口与实现

1、添加PaymentService 类

| public interface PaymentService extends IService {
/*
保存交易记录
*
@param order
* @param __paymentType 支付类型(1:微信 2:支付宝)
*/
void savePaymentInfo(OrderInfo order, Integer paymentType);

} | | —- |

2、添加PaymentServiceImpl实现类

@Servicepublic class PaymentServiceImpl extends
ServiceImpl implements PaymentService {
/*
保存交易记录
*
@param orderInfo
* @param __paymentType 支付类型(1:微信 2:支付宝)
*/
@Override
public void savePaymentInfo(OrderInfo orderInfo, Integer paymentType) {
QueryWrapper queryWrapper = new QueryWrapper<>();
queryWrapper.eq(“order_id”, orderInfo.getId());
queryWrapper.eq(“payment_type”, paymentType);
Integer count = baseMapper.selectCount(queryWrapper);
if(count >0) return;
// 保存交易记录
_PaymentInfo paymentInfo = new PaymentInfo();
paymentInfo.setCreateTime(new Date());
paymentInfo.setOrderId(orderInfo.getId());
paymentInfo.setPaymentType(paymentType);
paymentInfo.setOutTradeNo(orderInfo.getOutTradeNo());
paymentInfo.setPaymentStatus(PaymentStatusEnum.**_UNPAID
.getStatus());
String subject =
new DateTime(orderInfo.getReserveDate()).toString(“yyyy-MM-dd”)+“|”+orderInfo.getHosname()+“|”+orderInfo.getDepname()+“|”+orderInfo.getTitle();
paymentInfo.setSubject(subject);
paymentInfo.setTotalAmount(orderInfo.getAmount());
baseMapper**.insert(paymentInfo);
}
}

1.5 添加支付service接口与实现

1、添加com.atguigu.yygh.order.service.WeixinService类

public interface WeixinService {
/*
根据订单号下单,生成支付链接
_*/
_Map createNative(Long orderId);
}

2、添加com.atguigu.yygh.order.service.impl.WeixinServiceImpl类

| @Servicepublic class WeixinServiceImpl implements WeixinService {

@Autowired<br />    **private **OrderService **orderService**;<br />    @Autowired<br />    **private **PaymentService **paymentService**;<br />    @Autowired<br />    **private **RedisTemplate **redisTemplate**;<br />    _/**<br />     * 根据订单号下单,生成支付链接_**_     _**_*/<br />    _@Override<br />    **public **Map createNative(Long orderId) {<br />        **try **{<br />            Map payMap = (Map) **redisTemplate**.opsForValue().get(orderId.toString());<br />            **if**(**null **!= payMap) **return **payMap;<br />            _//根据id获取订单信息<br />            _OrderInfo order = **orderService**.getById(orderId);<br />            _// 保存交易记录<br />            _**paymentService**.savePaymentInfo(order, PaymentTypeEnum.**_WEIXIN_**.getStatus());<br />            _//1、设置参数<br />            _Map paramMap = **new **HashMap();<br />            paramMap.put(**"appid"**, ConstantPropertiesUtils._APPID_);<br />            paramMap.put(**"mch_id"**, ConstantPropertiesUtils._PARTNER_);<br />            paramMap.put(**"nonce_str"**, WXPayUtil._generateNonceStr_());<br />            String body = order.getReserveDate() + **"就诊"**+ order.getDepname();<br />            paramMap.put(**"body"**, body);<br />            paramMap.put(**"out_trade_no"**, order.getOutTradeNo());<br />            _//paramMap.put("total_fee", order.getAmount().multiply(new BigDecimal("100")).longValue()+"");<br />            _paramMap.put(**"total_fee"**, **"1"**);<br />            paramMap.put(**"spbill_create_ip"**, **"127.0.0.1"**);<br />            paramMap.put(**"notify_url"**, **"http://guli.shop/api/order/weixinPay/weixinNotify"**);<br />            paramMap.put(**"trade_type"**, **"NATIVE"**);<br />            _//2、HTTPClient来根据URL访问第三方接口并且传递参数<br />            _HttpClient client = **new **HttpClient(**"https://api.mch.weixin.qq.com/pay/unifiedorder"**);<br />            _//client设置参数<br />            _client.setXmlParam(WXPayUtil._generateSignedXml_(paramMap, ConstantPropertiesUtils._PARTNERKEY_));<br />            client.setHttps(**true**);<br />            client.post();<br />            _//3、返回第三方的数据<br />            _String xml = client.getContent();<br />            Map<String, String> resultMap = WXPayUtil._xmlToMap_(xml);<br />            _//4、封装返回结果集<br />            _Map map = **new **HashMap<>();<br />            map.put(**"orderId"**, orderId);<br />            map.put(**"totalFee"**, order.getAmount());<br />            map.put(**"resultCode"**, resultMap.get(**"result_code"**));<br />            map.put(**"codeUrl"**, resultMap.get(**"code_url"**));<br />            **if**(**null **!= resultMap.get(**"result_code"**)) {<br />                _//微信支付二维码2小时过期,可采取2小时未支付取消订单<br />                _**redisTemplate**.opsForValue().set(orderId.toString(), map, 1000, TimeUnit.**_MINUTES_**);<br />            }<br />            **return **map;<br />        } **catch **(Exception e) {<br />            e.printStackTrace();<br />            **return new **HashMap<>();<br />        }<br />    }<br />} |

| —- |

1.6 添加controller方法

@RestController
@RequestMapping(“/api/order/weixin”)public class WeixinController {
@Autowired
private WeixinService weixinPayService;
/*
下单 生成二维码
*/
@GetMapping(“/createNative/{orderId}”)
public Result createNative(
@ApiParam(name = “orderId”, value = “订单id”, required = true)
@PathVariable(“orderId”) Long orderId) {
return Result.ok(weixinPayService.createNative(orderId));
}
}

2 前端

2.1封装api请求

添加/api/order/weixin.js文件

createNative(orderId) {
return request({
url: /api/order/weixin/createNative/${orderId},
method: ‘get’
})
},

2.2 引入vue-qriously生成二维码

1、安装vue-qriously
npm install vue-qriously
2、引入
在/plugins/myPlugin.js文件添加引入

import VueQriously from ‘vue-qriously’Vue.use(VueQriously)

2.3 页面展示

修改/pages/order/show.vue组件



说明:我们只有轮询查看支付状态,接下来我们处理支付查询接口

2.4 添加查询api请求

在/api/weixin.js文件添加方法

queryPayStatus(orderId) {
return request({
url: /api/order/weixin/queryPayStatus/${orderId},
method: ‘get’
})
},

3 处理支付结果

3.1 支付查询

3.1.1 添加service接口与实现

1、在WeixinService类添加接口

/*
根据订单号去微信第三方查询支付状态
_*/_Map queryPayStatus(Long orderId, String paymentType);

2、在WeixinServiceImpl类添加实现

@Overridepublic Map queryPayStatus(Long orderId, String paymentType) {
try {
OrderInfo orderInfo = orderService.getById(orderId);
//1、封装参数
_Map paramMap = new HashMap<>();
paramMap.put(“appid”, ConstantPropertiesUtils._APPID
);
paramMap.put(“mch_id”, ConstantPropertiesUtils.PARTNER);
paramMap.put(“out_trade_no”, orderInfo.getOutTradeNo());
paramMap.put(“nonce_str”, WXPayUtil.generateNonceStr());
//2、设置请求
_HttpClient client = new HttpClient(https://api.mch.weixin.qq.com/pay/orderquery);
client.setXmlParam(WXPayUtil._generateSignedXml
(paramMap, ConstantPropertiesUtils.PARTNERKEY));
client.setHttps(true);
client.post();
//3、返回第三方的数据,转成Map
_String xml = client.getContent();
Map resultMap = WXPayUtil._xmlToMap
(xml);
//4、返回
return resultMap;
} catch (Exception e) {
return null;
}
}

3.1.2 添加controller方法

在WeixinController类添加方法

@Autowiredprivate PaymentService paymentService;
@ApiOperation(value = “查询支付状态”)@GetMapping(“/queryPayStatus/{orderId}”)public Result queryPayStatus(
@ApiParam(name = “orderId”, value = “订单id”, required = true)
@PathVariable(“orderId”) Long orderId) {
//调用查询接口
_Map resultMap = weixinPayService.queryPayStatus(orderId, PaymentTypeEnum.**_WEIXIN
.name());
if (resultMap == null) {//出错
return Result.fail().message(“支付出错”);
}
if (“SUCCESS”.equals(resultMap.get(“tradestate”**))) {//如果成功
//更改订单状态,处理支付结果
String out_trade_no = resultMap.get(“out_trade_no”);
paymentService.paySuccess(out_trade_no, PaymentTypeEnum.**_WEIXIN
.getStatus(), resultMap);
return Result.ok().message(“支付成功”);
}
return Result.ok().message(“支付中”**);
}

说明:如果支付成功,我们要更新支付状态、通知医院该预约单已经支付成功

3.2 处理支付成功逻辑

3.2.1 添加service接口与实现

1、在PaymentService类添加接口

/*
支付成功
*/void paySuccess(String outTradeNo, Integer paymentType, Map paramMap);

2、在PaymentServiceImpl类添加实现

/*
支付成功
*/@Overridepublic void paySuccess(String outTradeNo,Integer paymentType, Map paramMap) {
PaymentInfo paymentInfo = this.getPaymentInfo(outTradeNo, paymentType);
if (null == paymentInfo) {
throw new YyghException(ResultCodeEnum.PARAM_ERROR);
}
if (paymentInfo.getPaymentStatus() != PaymentStatusEnum.UNPAID.getStatus()) {
return;
}
//修改支付状态
_PaymentInfo paymentInfoUpd = new PaymentInfo();
paymentInfoUpd.setPaymentStatus(PaymentStatusEnum.**_PAID
.getStatus());
paymentInfoUpd.setTradeNo(paramMap.get(
“transactionid”));
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);
// 调用医院接口,通知更新支付状态}_/

获取支付记录** **/private PaymentInfo getPaymentInfo(String outTradeNo, Integer paymentType) {
QueryWrapper 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 queryWrapper =
new QueryWrapper<>();
queryWrapper.eq(
“out_trade_no”, outTradeNo);
baseMapper**.update(paymentInfoUpd, queryWrapper);
}

3.2.2 更新医院支付状态

参考《尚医通API接口文档.docx》业务接口5.2.更新支付状态

/*
支付成功
*/@Overridepublic void paySuccess(String outTradeNo,Integer paymentType, Map paramMap) {
PaymentInfo paymentInfo = this.getPaymentInfo(outTradeNo, paymentType);
if (null == paymentInfo) {
throw new YyghException(ResultCodeEnum.PARAM_ERROR);
}
if (paymentInfo.getPaymentStatus() != PaymentStatusEnum.UNPAID.getStatus()) {
return;
}
//修改支付状态
_PaymentInfo paymentInfoUpd = new PaymentInfo();
paymentInfoUpd.setPaymentStatus(PaymentStatusEnum.**_PAID
.getStatus());
paymentInfoUpd.setTradeNo(paramMap.get(
“transactionid”));
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);
_// 调用医院接口,通知更新支付状态
_SignInfoVo signInfoVo
=
hospitalFeignClient.getSignInfoVo(orderInfo.getHoscode());
if(null == signInfoVo) {
throw new YyghException(ResultCodeEnum.PARAM_ERROR);
}
Map reqMap =
new HashMap<>();
reqMap.put(
“hoscode”,orderInfo.getHoscode());
reqMap.put(
“hosRecordId”,orderInfo.getHosRecordId());
reqMap.put(
“timestamp”, HttpRequestHelper.getTimestamp());
String sign = HttpRequestHelper.getSign(reqMap, signInfoVo.getSignKey());
reqMap.put(
“sign”, sign);
JSONObject result = HttpRequestHelper.sendRequest(reqMap, signInfoVo.getApiUrl()+
“/order/updatePayStatus”);
if(result.getInteger(“code”) != 200) {
throw new YyghException(result.getString(“message”), ResultCodeEnum.FAIL**.getCode());
}}

4 取消预约

5 需求描述

取消订单分两种情况:

  1. 未支付取消订单,直接通知医院更新取消预约状态
  2. 已支付取消订单,先退款给用户,然后通知医院更新取消预约状态

    6 开发微信退款接口

    参考文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4
    该接口需要使用证书,详情参考文档并下载证书

    2.1 配置证书

    请下载的证书放在service-order模块/resources/cert文件夹下
    在application.properties文件配置证书路径
weixin.cert=
C:\\yygh_parent\\service\\service_order\\src\\main\\resources\\cert\\apiclient_cert.p12

2.2 添加获取支付记录接口

退款我们是根据支付记录发起退款的
1、在PaymentService类添加接口

/*
获取支付记录
*
@param __orderId* @param __paymentType* @return_*/_PaymentInfo getPaymentInfo(Long orderId, Integer paymentType);

2、在PaymentServiceImpl类添加实现

@Overridepublic PaymentInfo getPaymentInfo(Long orderId, Integer paymentType) {
QueryWrapper queryWrapper = new QueryWrapper<>();
queryWrapper.eq(“order_id”, orderId);
queryWrapper.eq(“payment_type”, paymentType);
return baseMapper.selectOne(queryWrapper);
}

2.3 添加退款记录

2.3 .1 添加mapper

public interface RefundInfoMapper extends BaseMapper {
}

2.3 .2 添加service接口与实现

1、添加service接口

public interface RefundInfoService extends IService {
/*
保存退款记录
*
@param paymentInfo
_*/
_RefundInfo saveRefundInfo(PaymentInfo paymentInfo);
}

2、添加service接口实现

| @Servicepublic class RefundInfoServiceImpl extends ServiceImpl implements RefundInfoService {

@Autowired<br />    **private **RefundInfoMapper **refundInfoMapper**;

@Override<br />    **public **RefundInfo saveRefundInfo(PaymentInfo paymentInfo) {<br />        QueryWrapper<RefundInfo> queryWrapper = **new **QueryWrapper<>();<br />        queryWrapper.eq(**"order_id"**, paymentInfo.getOrderId());<br />        queryWrapper.eq(**"payment_type"**, paymentInfo.getPaymentType());<br />        RefundInfo refundInfo = **refundInfoMapper**.selectOne(queryWrapper);<br />        **if**(**null **!= refundInfo) **return **refundInfo;_        // 保存交易记录<br />        _refundInfo = **new **RefundInfo();<br />        refundInfo.setCreateTime(**new **Date());<br />        refundInfo.setOrderId(paymentInfo.getOrderId());<br />        refundInfo.setPaymentType(paymentInfo.getPaymentType());<br />        refundInfo.setOutTradeNo(paymentInfo.getOutTradeNo());<br />        refundInfo.setRefundStatus(RefundStatusEnum.**_UNREFUND_**.getStatus());<br />        refundInfo.setSubject(paymentInfo.getSubject());_//paymentInfo.setSubject("test");<br />        _refundInfo.setTotalAmount(paymentInfo.getTotalAmount());<br />        **refundInfoMapper**.insert(refundInfo);<br />        **return **refundInfo;<br />    }<br />} |

| —- |

2.4 添加微信退款接口

1、在WeixinService添加接口

/**
退款
*
@param orderId
* @return
__
_*/_Boolean refund(Long orderId);

2、在WeixinServiceImpl添加实现

| @Overridepublic Boolean refund(Long orderId) {
try {
PaymentInfo paymentInfoQuery = paymentService.getPaymentInfo(orderId, PaymentTypeEnum.WEIXIN.getStatus());

        RefundInfo refundInfo = **refundInfoService**.saveRefundInfo(paymentInfoQuery);<br />            **if**(refundInfo.getRefundStatus().intValue() == RefundStatusEnum.**_REFUND_**.getStatus().intValue()) {<br />                **return true**;<br />            }<br />            Map<String,String> paramMap = **new **HashMap<>(8);<br />            paramMap.put(**"appid"**,ConstantPropertiesUtils._APPID_);       _//公众账号ID<br />            _paramMap.put(**"mch_id"**,ConstantPropertiesUtils._PARTNER_);   _//商户编号<br />            _paramMap.put(**"nonce_str"**,WXPayUtil._generateNonceStr_());<br />            paramMap.put(**"transaction_id"**,paymentInfoQuery.getTradeNo()); _//微信订单号<br />            _paramMap.put(**"out_trade_no"**,paymentInfoQuery.getOutTradeNo()); _//商户订单编号<br />            _paramMap.put(**"out_refund_no"**,**"tk"**+paymentInfoQuery.getOutTradeNo()); _//商户退款单号<br />//       paramMap.put("total_fee",paymentInfoQuery.getTotalAmount().multiply(new BigDecimal("100")).longValue()+"");<br />//       paramMap.put("refund_fee",paymentInfoQuery.getTotalAmount().multiply(new BigDecimal("100")).longValue()+"");<br />            _paramMap.put(**"total_fee"**,**"1"**);<br />            paramMap.put(**"refund_fee"**,**"1"**);<br />            String paramXml = WXPayUtil._generateSignedXml_(paramMap,ConstantPropertiesUtils._PARTNERKEY_);<br />            HttpClient client = **new **HttpClient(**"https://api.mch.weixin.qq.com/secapi/pay/refund"**);<br />            client.setXmlParam(paramXml);<br />            client.setHttps(**true**);<br />            client.setCert(**true**);<br />            client.setCertPassword(ConstantPropertiesUtils._PARTNER_);<br />            client.post();_//3、返回第三方的数据<br />            _String xml = client.getContent();<br />            Map<String, String> resultMap = WXPayUtil._xmlToMap_(xml);<br />            **if **(**null **!= resultMap && WXPayConstants.**_SUCCESS_**.equalsIgnoreCase(resultMap.get(**"result_code"**))) {<br />                refundInfo.setCallbackTime(**new **Date());<br />                refundInfo.setTradeNo(resultMap.get(**"refund_id"**));<br />                refundInfo.setRefundStatus(RefundStatusEnum.**_REFUND_**.getStatus());<br />                refundInfo.setCallbackContent(JSONObject._toJSONString_(resultMap));<br />                **refundInfoService**.updateById(refundInfo);<br />                **return true**;<br />            }<br />            **return false**;<br />        }  **catch **(Exception e) {<br />            e.printStackTrace();<br />        }<br />        **return false**;<br />} |

| —- |

2.5 完成取消预约

参考《尚医通API接口文档.docx》业务接口5.3.取消预约

2.5.1 添加service接口与实现

1、在OrderService添加接口

/*
取消订单
*
@param orderId
_*/_Boolean cancelOrder(Long orderId);

2、在OrderServiceImpl添加实现

| @Overridepublic Boolean cancelOrder(Long orderId) {
OrderInfo orderInfo = this.getById(orderId);//当前时间大约退号时间,不能取消预约
_DateTime quitTime = new DateTime(orderInfo.getQuitTime());
if(quitTime.isBeforeNow()) {
throw new YyghException(ResultCodeEnum.**_CANCEL_ORDER_NO
);
}
SignInfoVo signInfoVo =
hospitalFeignClient.getSignInfoVo(orderInfo.getHoscode());
if(null == signInfoVo) {
throw new YyghException(ResultCodeEnum.PARAM_ERROR);
}
Map reqMap =
new HashMap<>();
reqMap.put(
“hoscode”,orderInfo.getHoscode());
reqMap.put(
“hosRecordId”,orderInfo.getHosRecordId());
reqMap.put(
“timestamp”, HttpRequestHelper.getTimestamp());
String sign = HttpRequestHelper.getSign(reqMap, signInfoVo.getSignKey());
reqMap.put(
“sign”**, sign);

    JSONObject result = HttpRequestHelper._sendRequest_(reqMap, signInfoVo.getApiUrl()+**"/order/updateCancelStatus"**);

    **if**(result.getInteger(**"code"**) != 200) {<br />            **throw new **YyghException(result.getString(**"message"**), ResultCodeEnum.**_FAIL_**.getCode());<br />        } **else **{_//是否支付 退款<br />            _**if**(orderInfo.getOrderStatus().intValue() == OrderStatusEnum.**_PAID_**.getStatus().intValue()) {_//已支付 退款<br />                _**boolean **isRefund = **weixinService**.refund(orderId);<br />                **if**(!isRefund) {<br />                    **throw new **YyghException(ResultCodeEnum.**_CANCEL_ORDER_FAIL_**);<br />                }<br />            }_//更改订单状态<br />            _orderInfo.setOrderStatus(OrderStatusEnum.**_CANCLE_**.getStatus());<br />            **this**.updateById(orderInfo);_//发送mq信息更新预约数 我们与下单成功更新预约数使用相同的mq信息,不设置可预约数与剩余预约数,接收端可预约数减1即可<br />            _OrderMqVo orderMqVo = **new **OrderMqVo();<br />            orderMqVo.setScheduleId(orderInfo.getScheduleId());_//短信提示<br />            _MsmVo msmVo = **new **MsmVo();<br />            msmVo.setPhone(orderInfo.getPatientPhone());<br />            msmVo.setTemplateCode(**"SMS_194640722"**);<br />            String reserveDate = **new **DateTime(orderInfo.getReserveDate()).toString(**"yyyy-MM-dd"**) + (orderInfo.getReserveTime()==0 ? **"上午"**: **"下午"**);<br />            Map<String,Object> param = **new **HashMap<String,Object>(){{<br />                put(**"title"**, orderInfo.getHosname()+**"&#124;"**+orderInfo.getDepname()+**"&#124;"**+orderInfo.getTitle());<br />                put(**"reserveDate"**, reserveDate);<br />                put(**"name"**, orderInfo.getPatientName());<br />            }};<br />            msmVo.setParam(param);<br />            orderMqVo.setMsmVo(msmVo);<br />            **rabbitService**.sendMessage(MqConst.**_EXCHANGE_DIRECT_ORDER_**, MqConst.**_ROUTING_ORDER_**, orderMqVo);<br />        }<br />        **return true**;<br />} |

| —- |

2.5.2 添加controller方法

在OrderApiController添加方法

@ApiOperation(value = “取消预约”)@GetMapping(“auth/cancelOrder/{orderId}”)public Result cancelOrder(
@ApiParam(name = “orderId”, value = “订单id”, required = true)
@PathVariable(“orderId”) Long orderId) {
return Result.ok(orderService.cancelOrder(orderId));
}

2.6 修改监听

操作:service-hosp模块
修改HospitalReceiver 类

@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = MqConst.QUEUE_ORDER, durable = “true”),
exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_ORDER),
key = {MqConst.ROUTING_ORDER}
))public void receiver(OrderMqVo orderMqVo, Message message, Channel channel) throws IOException {
if(null != orderMqVo.getAvailableNumber()) {
//下单成功更新预约数
_Schedule schedule = scheduleService.getScheduleId(orderMqVo.getScheduleId());
schedule.setReservedNumber(orderMqVo.getReservedNumber());
schedule.setAvailableNumber(orderMqVo.getAvailableNumber());
scheduleService.update(schedule);
} else {**
//取消预约更新预约数
_ _Schedule schedule = scheduleService.getScheduleId(orderMqVo.getScheduleId());
int availableNumber = schedule.getAvailableNumber().intValue() + 1;
schedule.setAvailableNumber(availableNumber);
scheduleService.update(schedule);** }
//发送短信
MsmVo msmVo = orderMqVo.getMsmVo();
if(null != msmVo) {
rabbitService.sendMessage(MqConst.**_EXCHANGE_DIRECT_MSM
, MqConst.ROUTING_MSM_ITEM**, msmVo);
}
}

7 前端

3.1封装api请求

添加/api/weixin.js文件

cancelOrder(orderId) {
return request({
url: /api/order/orderInfo/auth/cancelOrder/${orderId},
method: ‘get’
})
},

3.2 页面展示

修改/pages/order/show.vue组件

cancelOrder() {
this.$confirm(‘确定取消预约吗?’, ‘提示’, {
confirmButtonText: ‘确定’,
cancelButtonText: ‘取消’,
type: ‘warning’
}).then(() => { // promise
// 点击确定,远程调用
return weixinApi.cancelOrder(this.orderId)
}).then((response) => {
this.$message.success(‘取消成功’)
this.init()
}).catch(() => {
this.$message.info(‘已取消取消预约’)
})
}