品优购微信支付





今日目标:

(1)掌握二维码生成插件 qrious 的使用

(2)理解微信支付开发的整体思路

(3)调用微信支付接口(统一下单)生成支付二维码

(4)调用微信接口(查询订单)查询支付状态

(5)实现支付日志的生成与订单状态的修改

 

目录

1、工程搭建

1.1 建立支付服务接口工程(pay-interface)

1.2 建立支付服务实现工程(pay-service)

2、微信支付二维码生成

2.1 需求分析

2.2 后端

2.3 前端

3、检测支付状态

3.1 需求分析

3.2 后端

3.3 前端

3.4 二维码超时处理

3.5 支付成功显示支付金额

4、支付日志

4.1 需求分析

4.2 插入支付日志

4.3 读取支付日志

5、支付成功,修改订单状态


 

1、工程搭建

 

1.1 建立支付服务接口工程(pay-interface)

参考其他服务层接口

 

1.2 建立支付服务实现工程(pay-service)

(1)依赖pay-interface和common,其他依赖参考其他服务层工程

(2)添加微信支付 SDK 依赖


 
 
  1. <dependency>
  2. <groupId>com.github.wxpay </groupId>
  3. <artifactId>wxpay-sdk </artifactId>
  4. <version>0.0.3 </version>
  5. </dependency>

(3)在common工程中放入HttpClient的工具类,并在pom.xml文件中引入依赖


 
 
  1. <dependency>
  2. <groupId>org.apache.httpcomponents </groupId>
  3. <artifactId>httpclient </artifactId>
  4. </dependency>

(4)添加微信支付配置文件

(5)引入 qrious 的js文件到cart-web的plugins目录中

 

 

 

2、微信支付二维码生成

 

2.1 需求分析

在支付页面上生成支付二维码,并显示订单号和金额

用户拿出手机,打开微信扫描页面上的二维码,然后在微信中完成支付

 

2.2 后端

(1)服务层接口(pay-interface),新建WinxinPayService接口


 
 
  1. package com.pinyougou.pay.service;
  2. import java.util.Map;
  3. /**
  4. * 微信支付接口
  5. * Author xushuai
  6. * Description
  7. */
  8. public interface WeixinPayService {
  9. /**
  10. * 生成本地支付的数据信息
  11. *
  12. * @param out_trade_no 外部订单号
  13. * @param total_fee 金额,单位为:分
  14. * @return java.util.Map
  15. */
  16. Map createNative(String out_trade_no, String total_fee);
  17. }

(2)服务层实现(pay-service),新建WinxinPayServiceImpl实现WinxinPayService


 
 
  1. package com.pinyougou.pay.service.impl;
  2. import com.alibaba.dubbo.config.annotation.Service;
  3. import com.github.wxpay.sdk.WXPayUtil;
  4. import com.pinyougou.pay.service.WeixinPayService;
  5. import org.springframework.beans.factory.annotation.Value;
  6. import util.HttpClient;
  7. import java.util.HashMap;
  8. import java.util.Map;
  9. /**
  10. * 微信支付接口实现
  11. * Author xushuai
  12. * Description
  13. */
  14. @Service
  15. public class WeixinPayServieImpl implements WeixinPayService {
  16. @Value( "${appid}"</span>)</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="21"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">private</span> String WEIXIN_APPID;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="22"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-meta">@Value</span>(<span class="hljs-string">"${partner}")
  17. private String WEIXIN_PARTNER;
  18. @Value( "${partnerkey}"</span>)</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="25"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">private</span> String WEIXIN_PARTNERKEY;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="26"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-meta">@Value</span>(<span class="hljs-string">"${notifyurl}")
  19. private String WEIXIN_NOTIFYURL;
  20. @Override
  21. public Map createNative(String out_trade_no, String total_fee) {
  22. try {
  23. // 1.封装微信支付请求参数
  24. Map<String, String> param = buildParam(out_trade_no, total_fee);
  25. // 2.使用请求参数生成请求xml数据
  26. String signXml = WXPayUtil.generateSignedXml(param, WEIXIN_PARTNERKEY);
  27. System.out.println( "xml数据:" + signXml);
  28. // 3.发送请求
  29. HttpClient httpClient = new HttpClient( "https://api.mch.weixin.qq.com/pay/unifiedorder");
  30. httpClient.setHttps( true);
  31. httpClient.setXmlParam(signXml);
  32. httpClient.post();
  33. // 4.获取请求响应结果
  34. String response = httpClient.getContent();
  35. Map<String, String> responseMap = WXPayUtil.xmlToMap(response);
  36. System.out.println( "响应结果:" + responseMap);
  37. // 5.生成返回结果
  38. if (responseMap.get( "return_code").equals( "SUCCESS")) {
  39. Map resultMap = new HashMap();
  40. resultMap.put( "code_url", responseMap.get( "code_url")); // 二维码链接地址
  41. resultMap.put( "out_trade_no", out_trade_no); // 订单号
  42. resultMap.put( "total_fee", total_fee); // 金额
  43. return resultMap;
  44. }
  45. } catch (Exception e) {
  46. e.printStackTrace();
  47. }
  48. return null;
  49. }
  50. /**
  51. * 构造微信支付请求参数
  52. *
  53. * @param out_trade_no 外部订单号
  54. * @param total_fee 金额
  55. */
  56. private Map<String, String> buildParam(String out_trade_no, String total_fee) {
  57. Map<String, String> param = new HashMap<>();
  58. // 公众账号ID
  59. param.put( "appid", WEIXIN_APPID);
  60. // 商户号
  61. param.put( "mch_id", WEIXIN_PARTNER);
  62. // 设备号(自定义参数,可以为终端设备号(门店号或收银设备ID),PC网页或公众号内支付可以传"WEB")
  63. //param.put("device_info", "WEB");
  64. // 随机字符串(随机字符串,长度要求在32位以内。推荐随机数生成算法)
  65. param.put( "nonce_str", WXPayUtil.generateNonceStr());
  66. // 商品描述
  67. param.put( "body", "品优购");
  68. // 商品详情
  69. //param.put("detail", WEIXIN_APPID);
  70. // 商户订单号
  71. param.put( "out_trade_no", out_trade_no);
  72. // 标价金额
  73. param.put( "total_fee", total_fee);
  74. // 终端IP
  75. param.put( "spbill_create_ip", "127.0.0.1");
  76. // 通知地址
  77. param.put( "notify_url", "http://test.itcast.cn");
  78. // 交易类型
  79. param.put( "trade_type", "NATIVE");
  80. return param;
  81. }
  82. }

(3)控制层(cart-web),新建PayController


 
 
  1. package com.pinyougou.cart.controller;
  2. import com.alibaba.dubbo.config.annotation.Reference;
  3. import com.pinyougou.pay.service.WeixinPayService;
  4. import org.springframework.web.bind.annotation.RequestMapping;
  5. import org.springframework.web.bind.annotation.RestController;
  6. import util.IdWorker;
  7. import java.util.Map;
  8. /**
  9. * 支付控制层
  10. * Author xushuai
  11. * Description
  12. */
  13. @RestController
  14. @RequestMapping( "/pay")
  15. public class PayController {
  16. @Reference
  17. private WeixinPayService weixinPayService;
  18. /**
  19. * 生成二维码数据
  20. *
  21. * @return java.util.Map
  22. */
  23. @RequestMapping( "/createNative")
  24. public Map createNative(){
  25. IdWorker idWorker = new IdWorker();
  26. return weixinPayService.createNative(String.valueOf(idWorker.nextId()), "1");
  27. }
  28. }

 

 

2.3 前端

(1)新建payService.js


 
 
  1. app.service( 'payService', function($http</span>)</span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-comment">//本地支付</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="3"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">this</span>.createNative=<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="4"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">return</span> $http.get('pay/createNative.do');
  2. }
  3. });

(2)新建payController.js 


 
 
  1. app.controller( 'payController' , function($scope ,payService</span>)</span>{ </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-comment">//本地生成二维码</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="3"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> $scope.createNative=<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="4"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> payService.createNative().success(</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="5"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">response</span>)</span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="6"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> $scope.money= (response.total_fee/<span class="hljs-number">100</span>).toFixed(<span class="hljs-number">2</span>) ; <span class="hljs-comment">//金额</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="7"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> $scope.out_trade_no= response.out_trade_no;//订单号
  2. //二维码
  3. var qr = new QRious({
  4. element: document.getElementById( 'qrious'),
  5. size: 250,
  6. level: 'H',
  7. value:response.code_url
  8. });
  9. }
  10. );
  11. }
  12. });

(3)在pay.html页面中引入JS文件和基础指令

(4)设置展示二维码的img标签的id为:qrious

(5)绑定变量,展示订单ID和订单总金额

 

 

 

3、检测支付状态

 

3.1 需求分析

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

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

 

 

3.2 后端

(1)服务层接口(pay-interface),在WinxinPayService新增方法


 
 
  1. /**
  2. * 查询订单支付状态
  3. *
  4. * @param out_trade_no 外部订单号
  5. * @return java.util.Map
  6. */
  7. Map queryPayStatus(String out_trade_no);

(2)服务层实现(pay-service),在WinxinPayServiceImpl新增实现


 
 
  1. @Override
  2. public Map queryPayStatus(String out_trade_no) {
  3. try {
  4. // 1.封装请求参数
  5. Map<String, String> param = buildQueryPayStatusParam(out_trade_no);
  6. // 2.使用请求参数生成请求xml数据
  7. String signXml = WXPayUtil.generateSignedXml(param, WEIXIN_PARTNERKEY);
  8. System.out.println( "xml数据:" + signXml);
  9. // 3.发送请求
  10. HttpClient httpClient = new HttpClient( "https://api.mch.weixin.qq.com/pay/orderquery");
  11. httpClient.setHttps( true);
  12. httpClient.setXmlParam(signXml);
  13. httpClient.post();
  14. // 4.获取请求结果
  15. String response = httpClient.getContent();
  16. Map<String, String> responseMap = WXPayUtil.xmlToMap(response);
  17. System.out.println( "响应结果:" + response);
  18. return responseMap;
  19. } catch (Exception e) {
  20. e.printStackTrace();
  21. }
  22. return null;
  23. }
  24. /**
  25. * 构造查询支付状态的请求参数
  26. *
  27. * @param out_trade_no 商户订单号
  28. * @return java.util.Map<java.lang.String,java.lang.String>
  29. */
  30. private Map<String, String> buildQueryPayStatusParam(String out_trade_no) {
  31. Map<String, String> param = new HashMap<>();
  32. // 公众账号ID
  33. param.put( "appid", WEIXIN_APPID);
  34. // 商户号
  35. param.put( "mch_id", WEIXIN_PARTNER);
  36. // 商户订单号
  37. param.put( "out_trade_no", out_trade_no);
  38. // 随机字符串
  39. param.put( "nonce_str", WXPayUtil.generateNonceStr());
  40. return param;
  41. }

(3)控制层(cart-web),在PayController中新增方法


 
 
  1. /**
  2. * 查询订单状态(三秒查询一次,直到支付成功)
  3. *
  4. * @param out_trade_no 商家订单号
  5. * @return entity.Result
  6. */
  7. @RequestMapping( "/queryPayStatus")
  8. public Result queryPayStatus(String out_trade_no){
  9. Result result = null;
  10. while ( true) {
  11. // 查询订单状态
  12. Map map = weixinPayService.queryPayStatus(out_trade_no);
  13. if (map == null) {
  14. result = Result.error( "支付出错");
  15. break;
  16. }
  17. if (map.get( "trade_state").equals( "SUCCESS")) {
  18. result = Result.success( "支付成功");
  19. break;
  20. }
  21. try {
  22. // 三秒查询一次订单状态
  23. Thread.sleep( 3000);
  24. } catch (InterruptedException e) {
  25. e.printStackTrace();
  26. }
  27. }
  28. return result;
  29. }

 

 

3.3 前端

(1)在payService.js中新增方法


 
 
  1. //查询支付状态
  2. this.queryPayStatus = function (out_trade_no) {
  3. return $http.get( 'pay/queryPayStatus.do?out_trade_no=' + out_trade_no);
  4. }

(2)在payController.js中新增方法


 
 
  1. //查询支付状态
  2. queryPayStatus = function (out_trade_no) {
  3. payService.queryPayStatus(out_trade_no).success(
  4. function (response) {
  5. if (response.success) {
  6. location.href = "paysuccess.html";
  7. } else {
  8. location.href = "payfail.html";
  9. }
  10. }
  11. );
  12. }

(3)在生成二维码成功响应后,执行查询订单状态的方法

 

 

3.4 二维码超时处理

(1)后端:在控制层中的PayController中的查询订单状态方法加入逻辑

(2)前端:当二维码已过期时,重新生成二维码

 

 

3.5 支付成功显示支付金额

(1)前端:修改payController中支付成功后的逻辑

(2)前端:在payController中引入$location 服务

 

(3)前端:在payController中新增方法


 
 
  1. //获取金额
  2. $scope.getMoney=<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="3"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">return</span> $location.search()[ 'money'];
  3. }

(4)前端:在paysuccess.html中引入相关js文件和基础指令

(5)前端:页面绑定变量展示支付金额

 

 

 

4、支付日志

 

4.1 需求分析

我们现在系统还有两个问题需要解决:

  1. 系统中无法查询到支付记录
  2. 支付后订单状态没有改变

我们现在就来解决这两个问题。

实现思路:

(1)在用户下订单时,判断如果为微信支付,就想支付日志表添加一条记录,信息包括支付总金额、订单ID(多个)、用户ID 、下单时间等信息,支付状态为0(未支付)

(2)生成的支付日志对象放入redis中,以用户ID作为key,这样在生成支付二维码时就可以从redis中提取支付日志对象中的金额和订单号。

(3)当用户支付成功后,修改支付日志的支付状态为1(已支付),并记录微信传递给我们的交易流水号。根据订单ID(多个)修改订单的状态为2(已付款)。

 

 

4.2 插入支付日志

(1)生成订单时,生成支付日志,修改OrderServiceImpl中的add方法(order-service)


 
 
  1. /**
  2. * 增加
  3. */
  4. @Override
  5. public void add(TbOrder order) {
  6. // 1.从redis中提取购物车列表
  7. List<Cart> cartList = (List<Cart>) redisTemplate.boundHashOps( "cartList").get(order.getUserId());
  8. List<String> orderIdList = new ArrayList(); //订单ID列表
  9. double total_money = 0; //总金额 (元)
  10. // 2.循环购物车保存订单
  11. for (Cart cart : cartList) {
  12. // 生成订单对象
  13. TbOrder tbOrder = buildOrder(order);
  14. // 设置商家ID
  15. tbOrder.setSellerId(cart.getSellerId());
  16. // 合计金额
  17. double money = 0;
  18. // 循环购物车明细
  19. for (TbOrderItem orderItem : cart.getOrderItemList()) {
  20. // 补全订单明细数据
  21. orderItem.setOrderId(tbOrder.getOrderId());
  22. orderItem.setSellerId(cart.getSellerId());
  23. orderItem.setId(idWorker.nextId());
  24. // 保存
  25. orderItemMapper.insert(orderItem);
  26. // 累加合计金额
  27. money += orderItem.getTotalFee().doubleValue();
  28. }
  29. // 设置合计金额到订单
  30. tbOrder.setPayment(BigDecimal.valueOf(money));
  31. // 保存
  32. orderMapper.insert(tbOrder);
  33. // 添加到订单列表
  34. orderIdList.add(tbOrder.getOrderId() + "");
  35. // 累加到总金额
  36. total_money += money;
  37. }
  38. if ( "1".equals(order.getPaymentType())) { //如果是微信支付
  39. TbPayLog payLog = new TbPayLog();
  40. String outTradeNo = idWorker.nextId() + ""; //支付订单号
  41. payLog.setOutTradeNo(outTradeNo); //支付订单号
  42. payLog.setCreateTime( new Date()); //创建时间
  43. //订单号列表,逗号分隔
  44. String ids = orderIdList.toString().replace( "[", "").replace( "]", "").replace( " ", "");
  45. payLog.setOrderList(ids); //订单号列表,逗号分隔
  46. payLog.setPayType( "1"); //支付类型
  47. payLog.setTotalFee(( long) (total_money * 100)); //总金额(分)
  48. payLog.setTradeState( "0"); //支付状态
  49. payLog.setUserId(order.getUserId()); //用户ID
  50. payLogMapper.insert(payLog); //插入到支付日志表
  51. redisTemplate.boundHashOps( "payLog").put(order.getUserId(), payLog); //放入缓存
  52. }
  53. // 3.清除购物车中的数据
  54. redisTemplate.boundHashOps( "cartList").delete(order.getUserId());
  55. }

 

 

4.3 读取支付日志

(1)后端:服务层接口(order-interface),在OrderService中新增方法


 
 
  1. /**
  2. * 获取指定用户的支付日志
  3. *
  4. * @param username 用户登录名
  5. * @return com.pinyougou.pojo.TbPayLog
  6. */
  7. TbPayLog searchPayLogFromRedis(String username);

(2)后端:服务层实现(order-service),在OrderServiceImpl中实现该方法


 
 
  1. @Override
  2. public TbPayLog searchPayLogFromRedis(String username) {
  3. return (TbPayLog) redisTemplate.boundHashOps( "payLog").get(username);
  4. }

(3)后端:控制层(cart-web),修改PayController中的createNative方法的逻辑


 
 
  1. /**
  2. * 生成二维码数据
  3. *
  4. * @return java.util.Map
  5. */
  6. @RequestMapping( "/createNative")
  7. public Map createNative(){
  8. // 获取当前登录用户名
  9. String username = SecurityContextHolder.getContext().getAuthentication().getName();
  10. // 从缓存中取出支付日志
  11. TbPayLog tbPayLog = orderService.searchPayLogFromRedis(username);
  12. if (tbPayLog != null) {
  13. return weixinPayService.createNative(tbPayLog.getOutTradeNo(), String.valueOf(tbPayLog.getTotalFee()));
  14. }
  15. return new HashMap();
  16. }

 

 

 

5、支付成功,修改订单状态

(1)后端:服务层接口(order-interface),在OrderService中新增方法


 
 
  1. /**
  2. * 修改订单状态
  3. *
  4. * @param out_trade_no 支付订单号
  5. * @param transaction_id 微信返回的交易流水号
  6. */
  7. void updateOrderStatus(String out_trade_no, String transaction_id);

(2)后端:服务层实现(order-service),在OrderServiceImpl中实现该方法


 
 
  1. @Override
  2. public void updateOrderStatus(String out_trade_no, String transaction_id) {
  3. //1.修改支付日志状态
  4. TbPayLog payLog = payLogMapper.selectByPrimaryKey(out_trade_no);
  5. payLog.setPayTime( new Date());
  6. payLog.setTradeState( "1"); //已支付
  7. payLog.setTransactionId(transaction_id); //交易号
  8. payLogMapper.updateByPrimaryKey(payLog);
  9. //2.修改订单状态
  10. String orderList = payLog.getOrderList(); //获取订单号列表
  11. String[] orderIds = orderList.split( ","); //获取订单号数组
  12. for (String orderId : orderIds) {
  13. TbOrder order = orderMapper.selectByPrimaryKey(Long.parseLong(orderId));
  14. if (order != null) {
  15. order.setStatus(TbOrder.STATUS_PAY); //已付款
  16. orderMapper.updateByPrimaryKey(order);
  17. }
  18. }
  19. //清除redis缓存数据
  20. redisTemplate.boundHashOps( "payLog").delete(payLog.getUserId());
  21. }

(3)后端:控制层(cart-web),在支付成功调用该方法

 



  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值