SpringCloud商城day13 微信扫码支付-2021-10-26

一. 调用微信支付api-开发文档

1. Native支付-> JAVA SDK/DEMO-> 申请微信支付(公众号/提交资料/开户成功/在线签署/)-> 获取appid唯一标识 / mch_id商户号 / key商户秘钥

2. 调用思路: 组装API参数-> XML方式发送POST请求-> 到微信URL接口-> 微信以XML方式响应-> 畅购根据结果(支付URL)生成qrcode-> 判断订单状态

3. 统一下单: 微信SDK: com.github.wxpay.sdk.WXPay类

(1)微信SDK依赖: wxpay-sdk
(2)com.github.wxpay.sdk包名不可变
public class MyConfig extends WXPayConfig {
    String getAppID() {
        return "wx8397f.....";
    }
    String getMchID() {
        return "1473.....";
    }​
    String getKey() {
        return "T6m9iK73b0k..........";
    }
    InputStream getCertStream() {
        return null;
    }​
    IWXPayDomain getWXPayDomain() {
        return new IWXPayDomain() {
            public void report(String domain, long elapsedTimeMillis, Exception ex) {
            }
            public DomainInfo getDomain(WXPayConfig config) {
                return new DomainInfo("api.mch.weixin.qq.com",true);
            }
        };
    }
}

(3)测试:
public class PayTest {
    public static void main(String[] args) throws Exception {
        MyConfig myConfig = new MyConfig();
        WXPay wxPay = new WXPay(myConfig);
        Map<String, String> map = new HashMap<>();
        //需要传递的数据
        map.put("body","xx");
        map.put("out_trade_no", "4567542....");
        map.put("total_fee","1");
        map.put("spbill_create_ip","127.0.0.1");
        map.put("notify_url","http://www.baidu.com");
        map.put("trade_type","NATIVE");

        Map<String, String> result = wxPay.unifiedOrder(map);
        System.out.println(result);
    }

4. 二维码显示qrcode:

(1) 插件QRcode.js: 生成二维码的JavaScript库

二. 统一下单生成微信支付二维码

1. 购物支付整体流程: 加入购物车-> 添加购物车addCart-> 点击结算-> 点击提交订单-> 选择支付方式-> 微信支付-> 微信支付二维码-> 支付成功页面

2. 订单结算页面-> 跳转到订单提交成功页面pay.html

(1)问题: 如何获取订单Id和订单金额?
在changgou_service_order中, user/controller/OrderController.java
    @Autowired
    private TokenDecode tokenDecode;
    /***
     * 新增数据
     * @param order
     * @return
     */
    @PostMapping
    public Result<String> add(@RequestBody Order order){
        //获取登录人名称
        String username = tokenDecode.getUserInfo().get("username");
        order.setUsername(username);
        String orderId = orderService.add(order);
        //把订单Id返回到支付方式pay.html页面
        return new Result(true,StatusCode.OK,"添加成功", orderId);
    }

3. 订单结算页面pay.html -> 获取订单id和金额

changgou_web_order渲染微服务中, web/order/controler/OrderController.java
    @GetMapping("/toPayPage")
    public String toPayPage(String orderId, Model model) {
        //获取订单相关信息
        Order order = orderFeign.findById(orderId).getData();
        model.addAttribute("orderId", orderId);
        model.addAttribute("payMoney", order.getPayMoney());
        return "pay";
    }

4. 支付微服务 changgou_service_pay

(1) 依赖: wxpay-sdk / spring-boot-starter / spring-boot-starter-amqp
(2) yml配置文件
server:
  port: 9010
spring:
  application:
    name: pay
  rabbitmq:
    host: 192.168.200.128
  main:
    allow-bean-definition-overriding: true #当遇到同样名字的时候,是否允许覆盖注册
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:6868/eureka
  instance:
    prefer-ip-address: true
wxpay:
  notify_url: http://www.laibiao.cross.echosite.cn/wxpay/notify  #回调地址

(3) com.github.wxpay.sdk.WXPay类  
public class MyConfig extends WXPayConfig {
    String getAppID() {
        return "wx8397f8696b538317";
    }
    String getMchID() {
        return "1473426802";
    }
    String getKey() {
        return "T6m9iK73b0kn9g5v426MKfHQH7X8rKwb";
    }
    InputStream getCertStream() {
        return null;
    }
    IWXPayDomain getWXPayDomain() {
        return new IWXPayDomain() {
            public void report(String domain, long elapsedTimeMillis, Exception ex) {
            }
            public DomainInfo getDomain(WXPayConfig config) {
                return new DomainInfo("api.mch.weixin.qq.com",true);
            }
        };
    }
} 

(4) com.changgou.pay.service.impl.WxpayServiceImpl.java
    @Autowired
    private WXPay wxPay;
    @Value("${wxpay.notify_url}")
    private String notify_url;
    //统一下单接口调用
    @Override
    public Map nativePay(String orderId, Integer money) {
        try {
            //1. 封装请求参数
            Map<String, String> map = new HashMap<>();
            map.put("body","畅购");
            map.put("out_trade_no",orderId);
            BigDecimal payMoney = new BigDecimal("0.01");
            BigDecimal fen = payMoney.multiply(new BigDecimal("100")); //值为1.00
            fen.setScale(0, BigDecimal.ROUND_UP);  //值为1
            map.put("total_fee", String.valueOf(fen));
            map.put("spbill_create_ip", "127.0.0.1");
            map.put("notify_url",notify_url);
            map.put("trade_type", "NATIVE");

            //2. 基于WXPay完成统一下单接口调用, 并获取返回结果
            Map<String, String> result = wxPay.unifiedOrder(map);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

(5) com.changgou.pay.controller.WxpayController.java
//下单
    @GetMapping("/nativePay")
    public Result nativePay(@RequestParam("orderId")String orderId, @RequestParam("money")Integer money) {
        Map resultMap = wxPayService.nativePay(orderId, money);
        return new Result(true, StatusCode.OK, "", resultMap);
    }

5. 对接渲染微服务

(1)changgou_service_pay_api: 对外暴露feign远程调用接口
@FeignClient(name = "pay")
public interface PayFeign {
    //下单
    @GetMapping("/wxpay/nativePay")
    public Result nativePay(@RequestParam("orderId")String orderId, @RequestParam("money")Integer money);
    //基于微信查询订单
    @GetMapping("/wxpay/query/{orderId}")
    public Result queryOrder(@PathVariable("orderId")String orderId);
    //基于微信关闭订单
    @PutMapping("/wxpay/close/{orderId}")
    public Result closeOrder(@PathVariable("orderId")String orderId);
}


(2)changgou_web_order服务: web/order/controller/PayController.java
@Controller
@RequestMapping("/wxpay")
public class PayController {
    @Autowired
    private OrderFeign orderFeign;
    @Autowired
    private PayFeign payFeign;
    //跳转到微信支付二维码
    @GetMapping
    public String wxPay(String orderId, Model model) {
        //1. 根据orderId查询订单, 如果订单不存在, 跳转到错误页面
        Result<Order> orderResult = orderFeign.findById(orderId);
        if (orderResult.getData()==null){
            return "fail";
        }
        //2. 判断订单支付转态, 未支付则跳转错误页面
        Order order = orderResult.getData();
        //不是未支付, 则跳转错误页
        if (!"0".equals(order.getPayStatus())){
            return "fail";
        }
        //3. 基于payFeign调用统计下单接口, 并获取返回结果
        Result payResult = payFeign.nativePay(orderId, order.getPayMoney());
        if (payResult.getData()==null){
            return "fail";
        }
        //4. 封装结果数据
        Map payMap = (Map) payResult.getData();
        payMap.put("orderId", orderId);
        payMap.put("payMoney", order.getPayMoney());
        model.addAllAttributes(payMap);
        return "wxpay";
    }
    //支付成功页面的跳转
    @RequestMapping("/toPaySuccess")
    public String toPaySuccess(Integer payMoney, Model model) {
        model.addAttribute("payMoney",payMoney);
        return "paysuccess";
    }
}

三. 支付回调 EcoSite(内网映射工具)

1. 用户支付完成后-> 微信发送响应-> 商家接收消息-> 修改订单支付状态

2. 配置Echosite-> config.yml文件-> 启动命令: echosite -config=config.yml start-all

3. 域名映射到本地ip的支付服务的端口9010

http://laibiao.cross.echosite.cn->127.0.0.1:9010

4. 查询订单验证通知: 微信查询订单-> 发送MQ-> 订单服务接收-> 修改订单状态

changgou_service_pay: pay.controller.WxpayController.java

 //测试ecosite内网穿透 -> 支付回调
    @RequestMapping("/notify")
    public void notifyLogic(HttpServletRequest request, HttpServletResponse response) {
        System.out.println("支付成功回调");
        try {
            //输入流转换为字符串
            String xml = ConvertUtils.convertToString(request.getInputStream());
            System.out.println(xml);
            //基于微信发送的通知类容, 完成后续的业务逻辑处理
            Map<String, String> map = WXPayUtil.xmlToMap(xml);
            if ("SUCCESS".equals(map.get("result_code"))){
                //查询订单
                Map result = wxPayService.queryOrder(map.get("out_trade_no"));
                System.out.println("查询订单结果" + result);
                if ("SUCCESS".equals(result.get("result_code"))){
                    //将订单的消息发送到mq
                    Map message = new HashMap();
                    message.put("orderId", result.get("out_trad_no"));
                    message.put("transactionId", result.get("transaction_id"));
                    //消息发送
                    rabbitTemplate.convertAndSend("", RabbitMQConfig.ORDER_PAY, JSON.toJSONString(message));
                    //完成双向通信
                    rabbitTemplate.convertAndSend("paynotify", "", result.get("out_trade_no"));
                }
            }else {
                //输出错误原因
                System.out.println("err_code_des");
            }
            //给微信支付一个成功的响应
            response.setContentType("text/xml");
            String data = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
            response.getWriter().write(data);
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

 changgou_service_pay: pay.service.impl.WxpayServiceImpl.java

 //基于微信查询订单
    @Override
    public Map queryOrder(String orderId) {
        try {
            Map<String, String> map = new HashMap();
            map.put("out_trade_no", orderId);
            Map<String, String> resultMap = wxPay.orderQuery(map);
            return resultMap;
        } catch(Exception e) {
            e.printStackTrace();
            return null;
        }
    }

5. 对接订单服务:  修改订单状态, 记录订单日志

(1)  order/service/impl/OrderServiceImpl.java
//修改订单的支付状态, 并记录日志
    @Override
    @Transactional
    public void updatePayStatus(String orderId, String transactionId) {
        //1.查询订单
        Order order = orderMapper.selectByPrimaryKey(orderId);
        if (order !=null && "0".equals(order.getPayStatus())){
            //2. 修改订单状态
            order.setPayStatus("1");
            order.setOrderStatus("1");
            order.setUpdateTime(new Date());
            order.setPayTime(new Date());
            order.setTransactionId(transactionId); //微信返回的交易流水号

            orderMapper.updateByPrimaryKey(order);

            //3. 记录订单日志
            OrderLog orderLog = new OrderLog();
            orderLog.setId(idWorker.nextId()+"");
            orderLog.setOperater("system");
            orderLog.setOperateTime(new Date());
            orderLog.setOrderStatus("1");
            orderLog.setPayStatus("1");
            orderLog.setRemarks("交易流水号"+transactionId);
            orderLog.setOrderId(orderId);

            orderLogMapper.insert(orderLog);
        }
    }

(2) order/listener/OrderPayListener.java
@Component
public class OrderPayListener {
    @Autowired
    private OrderService orderService;
    @RabbitListener(queues = RabbitMQConfig.ORDER_PAY)
    public void receivePayMessage(String message) {
        System.out.println("接收到了订单支付的消息" + message);
        Map map = JSON.parseObject(message, Map.class);
        //调用业务层, 完成订单数据的修改
        orderService.updatePayStatus((String)map.get("orderId"),(String)map.get("transactionId"));
    }
}

四. 支付结果通知推送-> paySuccess.html

推送方案:
(1) AJAX短轮询: 通过页面端JS定时异步刷新任务实现数据加载. 前端每隔2秒查询-> 后端提供方法(微信支付接口查询支付状态)-> 后端返回支付成功-> 跳转paySuccess.html
缺点: 服务端压力大, 实时效果差

(2) AJAX长轮询: 服务器端在没有数据时-> 请求阻塞-> 产生新数据/请求超时返回-> 客户端重新获取连接; 后端循环查询订单号的支付状态
缺点: 占用服务器资源

(3) WebSocket双向通信: 浏览器和服务器的全双工通信
    1) RabbitMQ Web STOMP插件: STOMP即Simple (or Streaming) Text Orientated Messaging Protocol,简单(流)文本定向消息协议。前身是TTMP协议(一个简单的基于文本的协议),专为消息中间件设计
    2) rabbitmq-plugins enable 
    rabbitmq_web_stomp 
    rabbitmq_web_stomp_examples
    

2. 完成双向通信:

(1) com.changgou.pay.controller
//消息发送
rabbitTemplate.convertAndSend("", RabbitMQConfig.ORDER_PAY, JSON.toJSONString(message));
//完成双向通信
rabbitTemplate.convertAndSend("paynotify", "", result.get("out_trade_no"));

(2) com.changgou.web.order.controller
//支付成功页面的跳转
    @RequestMapping("/toPaySuccess")
    public String toPaySuccess(Integer payMoney, Model model) {
        model.addAttribute("payMoney",payMoney);
        return "paysuccess";

    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值