文章目录

  • 一、支付微服务
    • 1、微信支付 API
    • 2、HttpClient 工具类
    • 3、支付微服务搭建
  • 二、微信支付二维码生成
  • 三、检测支付状态
  • 四、内网穿透
  • 五、支付结果通知
    • 1、支付结果回调通知
    • 2、Rabbit MQ 配置
      • (1)发送支付状态
      • (2)监听
  • 六、修改订单状态
  • 七、超时订单处理
  • 八、关闭订单与回滚库存
  • 九、总结

代码链接: https://github.com/betterGa/ChangGou

一、支付微服务

1、微信支付 API

微信支付提供了 SDK 和 Demo(然而我并没有找到 sdk,可能微信团队已经把它上传到 Maven 中了):

使用微信支付SDK,在 common 工程的 pom.xml 中中引入依赖

<!--微信支付-->
<dependency><groupId>com.github.wxpay</groupId><artifactId>wxpay-sdk</artifactId><version>0.0.3</version>
</dependency>

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

获取随机字符串

WXPayUtil.generateNonceStr()

MAP 转换为 XML 字符串(自动添加签名)

 WXPayUtil.generateSignedXml(param, partnerkey)

XML 字符串转换为 MAP(当然,Map 也可以转化成 XML)

WXPayUtil.xmlToMap(result)

在 changgou-common工程下引入依赖:

<!--微信支付-->
<dependency><groupId>com.github.wxpay</groupId><artifactId>wxpay-sdk</artifactId><version>0.0.3</version>
</dependency>

进行测试:

public class WeixinPayTest {@Testpublic void test() throws Exception {// 生成随机字符System.out.println("随机字符串" + WXPayUtil.generateNonceStr());// 将 Map 转化成 XMLMap<String, String> dataMap = new HashMap<>();dataMap.put("id", "No.1");dataMap.put("title", "畅购商城");dataMap.put("money", "520");String xml = WXPayUtil.mapToXml(dataMap);System.out.println(xml);// (1)生成签名System.out.println("带签名的字符串" + WXPayUtil.generateSignedXml(dataMap, "secret"));// 将 XML 转化成 MapSystem.out.println(WXPayUtil.xmlToMap(xml));}
}

运行结果:

     注意到,(1)处,WXPayUtil.generateSignedXml 方法,传 Map 和 密钥 作为参数,可以生成带 签名 的字符串,签名是这么生成的。

2、HttpClient 工具类

HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。HttpClient 已经应用在很多的项目中,比如 Apache Jakarta 上很著名的另外两个开源项目 Cactus 和 HTMLUnit 都使用了 HttpClient。
    HttpClient 通俗讲就是模拟了浏览器的行为,如果我们需要在后端向某一地址提交数据获取结果,就可以使用 HttpClient.

使用 HttpClient 需要先导入依赖:

   <!--httpclient支持--><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId></dependency>

关于HttpClient(原生)具体的使用不属于我们本章的学习内容,为了简化 HttpClient 的使用,提供了工具类 HttpClient(对原生 HttpClient 进行了封装)。

HttpClient工具类代码:

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() {// 信任所有@Overridepublic 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;}
}

进行测试:

public class HttpClientClass {/*** 测试 HttpClient 工具类的使用*/@Testpublic void test() throws IOException {// 发送 Http 请求String url = "https://api.mch.weixin.qq.com/pay/orderquery";HttpClient httpClient = new HttpClient(url);// 发送指定参数String xml = "<xml><name>用户</name></xml>";httpClient.setXmlParam(xml);// 使用 Https 协议httpClient.setHttps(true);// 发送请求httpClient.post();// 获取响应数据String result = httpClient.getContent();System.out.println(result);}
}

运行结果:

3、支付微服务搭建

需要支付微服务与微信支付服务器进行对接。
     创建 changgou-service-pay 工程。

创建application.yml,配置文件如下:

server:port: 18092
spring:application:name: paymain:allow-bean-definition-overriding: true
eureka:client:service-url:defaultZone: http://127.0.0.1:7001/eurekainstance:prefer-ip-address: true
feign:hystrix:enabled: true
#hystrix 配置
hystrix:command:default:execution:timeout:#如果enabled设置为false,则请求超时交给ribbon控制enabled: trueisolation:strategy: SEMAPHORE#微信支付信息配置
weixin:appid: wx8397f8696b538317partner: 1473426802partnerkey: T6m9iK73b0kn9g5v426MKfHQH7X8rKwbnotifyurl: http://www.itcast.cn

参数说明:

  • appid:微信公众账号或开放平台 APP 的唯一标识
  • partner:财付通平台的商户账号
  • partnerkey:财付通平台的商户密钥
  • notifyurl::回调地址

提供启动类:

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableEurekaClient
public class PayApplication {public static void main(String[] args) {SpringApplication.run(PayApplication.class, args);}
}

二、微信支付二维码生成

在支付页面上生成支付二维码,并显示订单号和金额,用户拿出手机,打开微信扫描页面上的二维码,然后在微信中完成支付。

实现思路:
     通过 HttpClient 工具类实现对远程支付接口的调用。
接口链接:https://api.mch.weixin.qq.com/pay/unifiedorder

具体参数参见 “统一下单” API, 构建参数发送给统一下单的 url ,返回的信息中有支付 url,根据 url 生成二维码,显示的订单号和金额也在返回的信息中。
    
代码实现:

  • 业务层
    提供接口:
public interface WeixinPayService {Map createNative(Map<String,String> parameterMap) throws Exception;
}

实现:

@Service
public class WeiXinPayServiceImpl implements WeixinPayService {// 应用 ID@Value("${weixin.appid}")private String appid;// 商户 ID@Value("${weixin.partner}")private String partner;// 密钥@Value("${weixin.partnerkey}")private String partnerkey;// 支付回调地址@Value("${weixin.notifyurl}")private String notifyurl;/*** 创建二维码** @param parameterMap* @return*/@Overridepublic Map createNative(Map<String, String> parameterMap) throws Exception {/*** 封装参数*/Map<String, String> paramMap = new HashMap<>();// 公众账号 IDparamMap.put("appid", appid);// 商户号paramMap.put("mch_id", partner.trim());// 随机字符串paramMap.put("nonce_str", WXPayUtil.generateNonceStr());// 商品描述paramMap.put("body", "畅购商城");// 商品订单号paramMap.put("out_trade_no", parameterMap.get("outtradeno"));// 标价金额paramMap.put("total_fee", parameterMap.get("totalfee"));// 终端 IPparamMap.put("spbill_create_ip", "127.0.0.1");// 通知地址paramMap.put("notify_url", notifyurl);// 交易类型paramMap.put("trade_type", "NATIVE");// 传入密钥,生成的 xml 中就会带有签名 sign 信息String xmlParameters = WXPayUtil.generateSignedXml(paramMap, partnerkey);System.out.println("xml:"+xmlParameters);/*** URL*/String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";/*** 提交方式*/HttpClient httpClient = new HttpClient(url);httpClient.setHttps(true);/*** 提交参数*/httpClient.setXmlParam(xmlParameters);/*** 执行请求*/httpClient.post();// 返回数据String content = httpClient.getContent();Map<String,String> resultMap=WXPayUtil.xmlToMap(content);return resultMap;}
}
  • 控制层
@RestController
@RequestMapping(value = "/weixin/pay")
@CrossOrigin
public class WeiXinPayController {@Autowiredprivate WeixinPayService weixinPayService;@GetMapping(value = "/create/native")Result createNative(@RequestParam Map<String, String> parameterMap) throws Exception {Map resultMap = weixinPayService.createNative(parameterMap);return new Result(true, StatusCode.OK,"创建二维码预付订单成功!",resultMap);}
}

测试如下:

打开支付页面 /pay.html,修改 value 路径,改为响应的 code_url 的值:

这时访问 pay.html 页面,会出现付款码,金额为 0.1 元。

三、检测支付状态

当用户支付成功后跳转到成功页面:

当返回异常时跳转到错误页面:

本身服务器会给商户后台系统发状态信息,但是可能有差错,导致商户后台系统没收到,所以需要主动向服务器查询状态信息。

实现思路:通过 HttpClient 工具类实现对远程接口的调用。
接口链接:https://api.mch.weixin.qq.com/pay/orderquery

具体参数参见 “查询订单” API:

代码实现:

  • 业务层
         在 com.changgou.service.WeixinPayService 提供方法:
/**** 查询订单状态* @param out_trade_no : 客户端自定义订单编号* @return*/
public Map queryPayStatus(String out_trade_no);

实现:

/**** 查询订单状态* @param out_trade_no : 客户端自定义订单编号* @return*/
@Override
public Map queryPayStatus(String out_trade_no) {/*** 查询订单状态* @param out_trade_no* @return*/@Overridepublic Map queryPayStatus(String out_trade_no) {try {//1.封装参数Map param = new HashMap();param.put("appid",appid);                            //应用IDparam.put("mch_id",partner);                         //商户号param.put("out_trade_no",out_trade_no);              //商户订单编号param.put("nonce_str",WXPayUtil.generateNonceStr()); //随机字符//2、将参数转成 xml 字符,并携带签名String paramXml = WXPayUtil.generateSignedXml(param,partnerkey);//3、发送请求HttpClient httpClient = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");httpClient.setHttps(true);httpClient.setXmlParam(paramXml);httpClient.post();//4、获取返回值,并将返回值转成 MapString content = httpClient.getContent();return WXPayUtil.xmlToMap(content);} catch (Exception e) {e.printStackTrace();}return null;}
}
  • 控制层
/**** 查询支付状态* @param outtradeno* @return*/@GetMapping(value = "/status/query")public Result queryStatus(String outtradeno) {Map<String, String> resultMap = weixinPayService.queryPayStatus(outtradeno);return new Result(true, StatusCode.OK, "查询状态成功!", resultMap);}

运行结果:

四、内网穿透

现在系统还有个问题需要解决:微信支付服务器需要访问本地服务器的,让外网访问到本地服务器,就需要用到内网穿透技术 NAT 。使用动态域名解析工具 花生壳 。
新建映射:

其中,内网主机即本地 IP,外网域名用的是花生壳赠送的,在 “域名列表” 中可以看到。

    可以看到,提供了一个访问地址。

要在诊断无误的情况下使用:

测试访问地址:

五、支付结果通知

1、支付结果回调通知

提供控制层:

  @RequestMapping(value = "/notify/url")public String notifyUrl(HttpServletRequest request) throws Exception {// 获取网络输入流ServletInputStream inputStream = request.getInputStream();ByteArrayOutputStream baos = new ByteArrayOutputStream();byte[] buffer = new byte[1024];int len = 0;while ((len = inputStream.read(buffer)) != -1) {baos.write(buffer, 0, len);}// 微信支付结果的字节数据byte[] bytes = baos.toByteArray();String xmlResult = new String(bytes, "utf-8");System.out.println("微信支付结果的 xml" + xmlResult);Map<String, String> resultMap = WXPayUtil.xmlToMap(xmlResult);System.out.println("微信支付结果的 Map" + resultMap);String result = "<xml>\n" +"  <return_code><![CDATA[SUCCESS]]></return_code>\n" +"  <return_msg><![CDATA[OK]]></return_msg>\n" +"</xml>";return result;}

需要在 application.yml 中配置访问地址,将该方法对应的路径作为配置文件中 weixin:notifyurl 的属性值。:

进行测试:
首先需要创建订单:

     把 “code_url” 放到 pay.html 中,生成付款码,用微信扫描后付款,这时,会在控制台看到输出:

    注意:方法是 @RequestMapping ,如果误写成 @GetMapping ,控制台输出:

它说不支持 @PostMapping,那我就改成 @PostMapping:

     可以看到,和使用 @RequestMapping 的效果是一样的,说明调用这个 notifyurl 的地方,是使用 Post 方法。

至此,在 pay 项目中就可以获取到支付结果,但是 order 项目并没有获取到结果。需要让 order 项目监听 MQ,当监听到支付成功后,需要把订单状态改为 支付成功。

2、Rabbit MQ 配置

需要先在 pay 工程中导入依赖:

 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency>
(1)发送支付状态

在 application.yml 中进行配置:

mq:pay:exchange:order: exchange.orderqueue:order: queue.orderrouting:key: queue.order


     需要清空 Exchanges 和 Queues。
     本项目中使用代码生成交换机和队列(实际企业开发应该是去 MQ 中生成的),在 pay 工程中 提供一个配置类:

@Configuration
public class MQConfig {// 读取配置文件中的内容对象@Autowiredprivate Environment environment;// 创建队列@Beanpublic Queue orderQueue() {return new Queue(environment.getProperty("mq.pay.queue.order"));   }// 创建交换机@Beanpublic Exchange orderExchange() {// 持久化,不自动删除return new DirectExchange(environment.getProperty("mq.pay.exchange.order"), true, false);}// 绑定public Binding binding(Queue queue, Exchange exchange) {return BindingBuilder.bind(queue).to(exchange).with(environment.getProperty("mq.pay.routing.key")).noargs();}
}

在 WeiXinPayController 的 notifyUrl 方法中,加入 把支付结果发送给 MQ 的逻辑:

还需要在 order 工程中,同样需要配置 RabbitMQ 参数,导入 spring-boot-starter-amqp 依赖。

(2)监听

提供监听类:

@Component
@RabbitListener(queues = "${mq.pay.queue.order}")
public class OrderMessageListener {/*** 支付结果监听*/@RabbitHandlerpublic void getMessage(String message){// 支付结果Map<String,String> resultMap = JSON.parseObject(message, Map.class);// 输出监听到的消息System.out.println(resultMap);// 通信标识String returnCode=resultMap.get("return_code");if(returnCode.equals("SUCCESS")){// 业务结果String resultCode = resultMap.get("result_code");// 订单号String outTradeNo = resultMap.get("out_trade_no");// 支付成功if(resultCode.equals("SUCCESS")){}else {// 如果支付失败,需要关闭订单,回滚库存}}}
}

注意:需要启动 pay 工程,生成付款码,然后用户进行支付,创建队列 queue.order,启动 order 工程,监听队列。不可以同时启动两个工程,否则 生产者模块所在的 pay 工程可以正常运行,但是消费者模块所在的 order 会报错误:

org.springframework.amqp.rabbit.listener.BlockingQueueConsumer$DeclarationException: Failed to declare queue(s):[topic.man] at org.springframework.amqp.rabbit.listener.BlockingQueueConsumer.attemptPassiveDeclarations(BlockingQueueConsumer.java:700) [spring-rabbit-2.2.7.RELEASE.jar:2.2.7.RELEASE]at               org.springframework.amqp.rabbit.listener.BlockingQueueConsumer.passiveDeclarations(Blocki   ngQueueConsumer.java:584) [spring-rabbit-2.2.7.RELEASE.jar:2.2.7.RELEASE]

因为此时还未付款,就不会在 rabbitmq 服务器里面创建还不存在的交换机和队列。仅限于第一次启动的时候,以后 rabbitmq 里面以及有对应的交换机和队列存在了,就不用这样做了。
    
付款后,可以看到 RabbitMQ 中新建队列和交换机成功了:


六、修改订单状态

接下来,需要根据支付的结果修改订单信息:

    
在 OrderService 中提供方法:

    /*** 修改订单状态** @param outradeno* @param paytime* @param transcationid*/void updateStatus(String outradeno, String paytime, String transcationid) throws ParseException;/*** 删除【逻辑删除,其实是修改订单状态】订单信息,回滚库存** @param outradeno*/void deleteOrder(String outradeno);

实现:

    /*** 修改订单状态** @param outradeno* @param paytime* @param transcationid*/@Overridepublic void updateStatus(String outradeno, String paytime, String transcationid) throws ParseException {// 查询订单Order order = orderMapper.selectByPrimaryKey(outradeno);// 时间转换SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss");// 获取支付时间Date payTime = simpleDateFormat.parse(paytime);// 修改订单信息order.setPayTime(payTime);order.setPayStatus("1");order.setTransactionId(transcationid);orderMapper.updateByPrimaryKey(order);}/*** 删除订单* @param outradeno*/@Overridepublic void deleteOrder(String outradeno) {// 查询订单Order order = orderMapper.selectByPrimaryKey(outradeno);// 支付失败order.setOrderStatus("2");orderMapper.updateByPrimaryKey(order);// 回滚库存,需要调用 goods 微服务,未实现// 微信支付服务器关闭订单,未实现}

如果用户订单支付失败了,或者支付超时了,我们需要删除用户订单,删除订单的同时需要取消订单、回滚库存:

七、超时订单处理

现在还有个问题,如果用户下单 30 分钟后还没有支付,需要把订单取消。(超过 5 分钟取消订单,是可以实现的)

     那么怎么让系统知道 30 分钟后用户是否支付成功了呢?可以轮询,但是轮询非常浪费资源,而且轮询的间隔不好控制;可以用延时队列。
     Rabbit MQ 本身不支持延时队列,不过可以自己实现,有两种方式:
(1)利用 2 个特性:Time To Live( TTL)、Dead Letter Exchange(DLX)、 [A 队列过期-> 转发给 B 队列]
(2)利用 RabbitMQ 中的插件 x-delay-message
     采用第一种方式来实现延时队列,实际上是用队列的超时特性:

     监听 Queue2 即可。
代码实现:

@Configuration
public class QueueConfig {// 创建 queue1@Beanpublic Queue orderDelayQueue(){return QueueBuilder.durable("orderDelayQueue")// queue1 消息过期,进入到死信【没被读取的消息】队列,需要绑定交换机.withArgument("x-dead-letter-exchange","orderListenerExchange")// queue1 消息过期,会路由到 queue2.withArgument("x-dead-letter-routing-key","orderListenerQueue").build();}// 创建 queue2@Beanpublic Queue orderListenerQueue(){// 持久化return new Queue("orderListenerQueue",true);}@Beanpublic Exchange orderListenerExchange(){return new DirectExchange("orderListenerExchange");}// 绑定@Beanpublic Binding binding(Queue orderListenerQueue,Exchange orderListenerExchange){return BindingBuilder.bind(orderListenerQueue).to(orderListenerExchange).with("orderListenerQueue").noargs();}
}

下单时发送消息:

提供监听:

@Component
@RabbitListener(queues = "orderListenerQueue")
public class DelayMessageListener {@RabbitHandlerpublic void delayMessage(String message) {System.out.println("监听到的消息" + message);SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");System.out.println("监听到消息时间" + simpleDateFormat.format(new Date()));}
}

这时进行测试,启动 order 工程,可以看到 RabbitMQ 中生成了队列和交换机:


接下来进行测试。
先下单:

可以看到,在控制台输出:

八、关闭订单与回滚库存

在 (五)支付结果通知,队列监听方法中,当监听到消息的 result_code 不为 SUCCESS 时,说明支付失败,需要关闭订单、回滚库存。回滚库存的逻辑可以参考上一篇文章中,下单-库存变更 的逻辑,在 Redis 中,查询用户对应的订单信息,然后根据 订单 order 查询出订单明细 orderItem,就能得到订单中商品的 num 数量,最后在 sku 商品表中需要把数量加回来:

需要在 skuController 中提供库存递增的方法:

 @GetMapping(value = "/asc")public Result ascCount(@RequestParam Map<String,String> ascMap){skuService.ascCount(ascMap);return new Result(true,StatusCode.OK,"库存递增成功");}

在 SkuService 接口中提供方法:

   void ascCount(Map<String,String> ascMap);

实现:

 @Overridepublic void ascCount(Map<String, String> ascMap) {for (Map.Entry<String, String> entry : ascMap.entrySet()) {// 商品 IDLong id = Long.parseLong(entry.getKey());// 数量Integer num = Integer.parseInt(entry.getValue());/*** 使用行级锁防止超卖,通过数据库的事务特性保证数据原子性*/int row = skuMapper.ascCount(id, num);}}

在 SkuMapper 中使用 SQL 语句行级锁:

@Update("update tb_sku set num=num+#{num} where id=#{id}")int ascCount(@Param(value = "id") Long id,  @Param(value = "num")Integer num);

提供 feign 调用:

    关闭订单用微信支付开发文档中提供的 ”关闭订单“ API。
    之前都是使用 HttpClient 工具,这次试着直接用 sdk,可以看到,在 WxPay 中,有相应方法:

    可以看到,需要传 reqData ,即 向 wxpay post 的数据(和接口文档中是对应的:
     必须有 appid、mch_id、out_trade_no、nonce_str、sign 参数,WeiXinPayServiceImpl 类有从配置文件中读取参数作为属性,所以可以在 WeiXinPayServiceImpl 类中提供方法,把这些参数传进去)。

/*** 构建 WXPay 对象*/public WXPay wxPay(){return new WXPay(new WXPayConfig() {@Overridepublic String getAppID() {return getAppid();}@Overridepublic String getMchID() {return getMchID();}@Overridepublic String getKey() {return getPartnerkey();}@Overridepublic InputStream getCertStream() {return null;}@Overridepublic int getHttpConnectTimeoutMs() {return 8000;}@Overridepublic int getHttpReadTimeoutMs() {return 10000;}});}@Overridepublic Map cancelOrder(String outTradeNo) throws Exception {// 封装参数Map<String,String> reqDate=new HashMap<>();reqDate.put("out_trade_no", outTradeNo);return wxPay().closeOrder(reqDate);}

可以看到,参数齐活了(不需要手动传 nonce_str、sign 是因为在 closeOrder 中,会调用 fillRequestData 方法,使用了 WXPayUtil.generateNonceStr() 作为 “nonce_str” 的值、 使用了 WXPayUtil.generateSignature(…)作为 “sign” 的值)。

需要在 WeiXinPayController 中再提供一个 取消订单的方法以供调用:

@RequestMapping(value = "/cancel/order")public Result cancelOrder(@RequestParam String outTradeNo) throws Exception {Map resultMap = weixinPayService.cancelOrder(outTradeNo);return new Result(true, StatusCode.OK, "取消订单成功", resultMap);}

先测试取消订单的方法:

返回这样的结果是因为订单已支付:

根据这个错误码,我们知道,关闭订单也是有可能出现错误信息的,不是全部的订单都可以成关闭,所以修改 WeiXinPayController 层的方法:

想让监听支付结果的 getMessage 方法里调用取消订单的方法,可以使用 Feign 调用:

@FeignClient("weixinpay")
@RequestMapping(value = "/weixin/pay")
public interface WeiXinPayFeign {/*** 取消订单* @param outTradeNo* @return*/@RequestMapping(value = "/cancel/order")public Result cancelOrder(@RequestParam String outTradeNo);
}


     同样地,在 (七) 超时处理中,需要把逻辑改为,监听队列的 DelayMessageListener 的 delayMessage 方法,在监听到消息时,需要判断订单是否支付成功,如果没有支付成功,需要关闭订单、回滚库存。

进行测试,然后发现 order 和 pay 工程,循环依赖了

微服务商城系统(十四)微信支付相关推荐

  1. 微服务商城系统(四)商品管理

    文章目录 一.SPU 与 SKU 1.SPU 与 SKU 的概念 2.tb_spu.tb_sku 表结构分析 二.商品管理 1.代码生成 2.查询分类品牌数据 3.新增商品 4.查询商品 5.修改商品 ...

  2. springcloud 整合 gateway_GitHub上最火的SpringCloud微服务商城系统项目,附全套教程

    项目介绍 mall-swarm是一套微服务商城系统,采用了 Spring Cloud Greenwich.Spring Boot 2.MyBatis.Docker.Elasticsearch等核心技术 ...

  3. mall-swarm是一套微服务商城系统

    介绍: mall-swarm是一套微服务商城系统,采用了 Spring Cloud Hoxton & Alibaba.Spring Boot 2.3.Oauth2.MyBatis.Elasti ...

  4. mall-swarm微服务商城系统

    mall-swarm是一套微服务商城系统,采用了 Spring Cloud 2021 & Alibaba.Spring Boot 2.7.Oauth2.MyBatis.Docker.Elast ...

  5. 微服务商城系统(十六)秒杀核心

    代码链接: https://github.com/betterGa/ChangGou 文章目录 一.防止秒杀重复排队 二. 并发超卖问题解决 三. 订单支付 1.实现根据不同类型订单识别不同操作队列 ...

  6. 微服务商城系统(十) Spring Security Oauth2 + JWT 用户认证

    文章目录 一.用户认证分析 1.认证 与 授权 2.单点登录 3.第三方账号登录 4.第三方认证 5.认证技术方案 6.Security Oauth 2.0 入门 7. 资源服务授权 (1)资源服务授 ...

  7. 微服务商城系统(十五)秒杀基础

    文章目录 一.秒杀业务分析 1.需求 2.表结构说明 3.秒杀需求分析 二.秒杀商品压入缓存 1.搭建秒杀服务工程 2.定时任务 3.秒杀商品压入缓存实现 三.秒杀频道页 四.下单实现 五.多线程抢单 ...

  8. 微服务商城系统(十三)订单、支付流程分析

    文章目录 一.订单 1.登录页面配置 2.用户收件地址查询 3. 下单 (1)表结构介绍 (2)下单实现 (3)库存变更 (4)增加积分 二. 支付流程分析 1. 二维码创建 2.微信扫码支付简介 ( ...

  9. 微服务商城系统(六)商品搜索 SpringBoot 整合 Elasticsearch

    文章目录 一.Elasticsearch 和 IK 分词器的安装 二.Kibana 使用 三.数据导入 Elasticsearch 1.SpringData Elasticsearch 介绍 2.搜索 ...

最新文章

  1. variant 字符串数组_VB数组部分核心知识总结
  2. 新型量子计算机首个基本元件问世,扩展性更强运算速度更快
  3. matlab计算一个长式子,matlab求积分,式子太长
  4. 【干货】mysql查询重复数据sql
  5. Linuxmint 美化之路
  6. mysql c#开发库_c# 开发+MySql数据库
  7. Retrofit2源码分析(一)
  8. Ubuntu 10不能通过改source.list装JDK 1.6
  9. Happy Necklace
  10. python imagedraw line_修复PIL.ImageDraw.Draw.宽线条线条线条
  11. 说说家乡的旅游景点吧...
  12. 蓝桥杯「鲁卡斯队列」
  13. JSPatch转换器:可直接将OC代码转换成JS代码
  14. IT 基础设施趋势合集 | 多云、超融合、SDS、容器之趋势解读与政策分析
  15. 电路中的过压保护和过流保护的区别
  16. 夫妻分居申办上海户口全攻略(zt)
  17. 计算图像的熵、编码效率、冗余度
  18. 夏日葵电商:开发一个微信商城系统多少钱
  19. 江苏省高等学校计算机一级成绩查询,江苏计算机等级考试成绩查询入口
  20. 一篇文章告诉你:SCI论文投稿流程到底是怎么一回事儿

热门文章

  1. 【开发教程14】AI语音人脸识别(会议记录仪/人脸打卡机)-AI人脸系统架构
  2. 【wpf】如果让Bingding 如何让后台数据强制更新界面
  3. 软件测试 | 测试开发 | Sikuli 基于图形识别的自动化测试技术
  4. Micro:Bit手柄试用之一MagicPad (解决蓝牙与gamePad包共存)
  5. 防止前端重复提交表单
  6. 如何编辑扫描的PDF文件?
  7. java spring+mybatis整合实现爬虫之《今日头条》搞笑动态图片爬取
  8. 结构化数据和非结构化数据
  9. 关机或重新启动电脑勾选再次登录时重新打开窗口导致无法上网
  10. 用什么方法可以将Word转换成PDF文档?