微信支付&处理支付结果&取消预约

微信支付二维码生成

  • 根据orderid生成二维码,放入redis中,设置过期时间。向支付表中添加支付信息。
  • 二维码的生成需要设置参数,调用微信生成的二维码接口,设置的参数包括申请的公众号appid,商户号,商户key,就诊人的信息,订单编号,订单金额,回调地址
  • 用HttpClient来根据URL访问第三方接口并且传递参数,使用商户key将数据转成xml格式然后加密发送请求。
  • 从微信方得到相关参数。
  • 将返回的参数转为map形式,包括code_url,即二维码地址
  • 如果code_url不空,则放入redis中,orderid为key,map为value
//生成微信支付二维码
    @Override
    public Map createNative(Long orderId) {
        try {
            //从redis获取数据
            Map payMap = (Map) redisTemplate.opsForValue().get(orderId.toString());
            if(null != payMap) {
                return payMap;
            }
            //1 根据orderId获取订单信息
            OrderInfo order = orderService.getById(orderId);
            //2 向支付记录表中添加信息
            paymentService.savePaymentInfo(order, PaymentTypeEnum.WEIXIN.getStatus());
            //3 设置参数,调用微信生成的二维码接口
            //把参数转换成xml格式,使用商户key进行加密
            Map paramMap=new HashMap();
            paramMap.put("appid", ConstantPropertiesUtils.APPID);
            paramMap.put("mch_id", ConstantPropertiesUtils.PARTNER);
            paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
            String body = order.getReserveDate() + "就诊"+ order.getDepname();
            //就诊人主题
            paramMap.put("body", body);
            //订单交易号,是根据当前时间+随机数生成的
            paramMap.put("out_trade_no", order.getOutTradeNo());
            //paramMap.put("total_fee", order.getAmount().multiply(new BigDecimal("100")).longValue()+"");
            //订单金额
            paramMap.put("total_fee", "1");
            paramMap.put("spbill_create_ip", "127.0.0.1");
            paramMap.put("notify_url", "http://guli.shop/api/order/weixinPay/weixinNotify");
            //支付类型,微信扫码
            paramMap.put("trade_type", "NATIVE");

            //4 HTTPClient来根据URL访问第三方接口并且传递参数
            HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");
            //client设置参数
            //ConstantPropertiesUtils.PARTNERKEY申请认证得到的,用来加密的,
            //通过这个key将数据转成xml格式然后加密
            client.setXmlParam(WXPayUtil.generateSignedXml(paramMap, ConstantPropertiesUtils.PARTNERKEY));
            client.setHttps(true);
            client.post();
            //5 微信返回相关数据
            String xml=client.getContent();
            //转换map集合
            Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);
            System.out.println("resultMap:"+resultMap);
            //封装返回的结果集
            Map map = new HashMap<>();
            map.put("orderId", orderId);
            map.put("totalFee", order.getAmount());
            map.put("resultCode", resultMap.get("result_code"));
            //二维码地址
            map.put("codeUrl", resultMap.get("code_url"));
            if(null != resultMap.get("result_code")) {
                //微信支付二维码2小时过期,可采取2小时未支付取消订单
                redisTemplate.opsForValue().set(orderId.toString(), map, 120, TimeUnit.MINUTES);
            }
            return map;

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

前端二维码显示

根据code_url,用vue-qriously显示

			<!-- 显示支付的二维码 -->
              <qriously :value="payObj.codeUrl" :size="220"/>

查询支付状态

前端

每隔3秒调用查询支付状态的接口,setInterval(),设置定时器

//生成支付二维码方法
    pay() {
      //支付弹框显示
      this.dialogPayVisible = true;
      weixinApi.createNative(this.orderId).then((response) => {
        this.payObj = response.data;
        if (this.payObj.codeUrl == "") {
          this.dialogPayVisible = false;
          this.$message.error("支付错误");
        } else {
          //每隔3秒调用查询支付状态的接口
          this.timer = setInterval(() => {
            this.queryPayStatus(this.orderId);
          }, 3000);
        }
      });
    },
    //查询支付状态
    queryPayStatus(orderId) {
      weixinApi.queryPayStatus(orderId).then(response => {
        if (response.message == '支付中') {
          return
        }
        // 清楚定时器效果
        clearInterval(this.timer);
        window.location.reload()
      })
    },

controller

  • 调用微信接口实现支付状态查询queryPayStatus,
  • 通过返回的map的trade_status查看是否成功,
  • 成功则更改订单状态,处理支付结果paysuccess,也就是把orderInfo表和支付表paymentInfo的支付状态修改成已支付,并且更新时间。
  • 支付成功后前端显示支付成功,在payment_info表中,payment_status更新为2,医院的order_info中更新order_status=1。(注意order_info表是yygh_manage的而不是yygh_order的)
//根据订单id,查询支付状态
    @ApiOperation(value = "查询支付状态")
    @GetMapping("/queryPayStatus/{orderId}")
    public Result queryPayStatus(
            @ApiParam(name = "orderId", value = "订单id", required = true)
            @PathVariable("orderId") Long orderId) {
        //调用微信接口实现支付状态查询
        Map<String, String> resultMap = weixinService.queryPayStatus(orderId, PaymentTypeEnum.WEIXIN.name());
        if (resultMap == null) {//出错
            return Result.fail().message("支付出错");
        }
        if ("SUCCESS".equals(resultMap.get("trade_state"))) {//如果成功
            //更改订单状态,处理支付结果
            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("支付中");
    }
微信接口查询订单状态queryPayStatus,返回map
  • 根据orderId获取订单信息,封装参数后向微信接口发送请求,查询订单状态,将结果返回map,通过map的trade_status查看是否成功
//调用微信接口实现支付状态查询
    @Override
    public Map queryPayStatus(Long orderId, String paymentType) {
        try {
            //根据orderid获取订单信息
            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<String, String> resultMap = WXPayUtil.xmlToMap(xml);
            //4、返回
            return resultMap;
        } catch (Exception e) {
            return null;
        }
    }
支付成功paySuccess
  • 更新orderInfo的支付状态和时间
  • 更新paymentInfo的支付状态
  • 远程调用医院接口更新订单支付信息,即再医院接口中更新orderInfo的orderStatus=1
//    更改订单状态,处理支付结果
    @Override
    public void paySuccess(String out_trade_no, Integer paymentType, Map<String, String> paramMap) {
        //1 根据订单编号得到支付记录
        PaymentInfo paymentInfo = this.getPaymentInfo(out_trade_no, paymentType);
        if (null == paymentInfo) {
            throw new YyghException(ResultCodeEnum.PARAM_ERROR);
        }
        if (paymentInfo.getPaymentStatus() != PaymentStatusEnum.UNPAID.getStatus()) {
            return;
        }
        //2 更新修改支付状态
        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(out_trade_no, paymentInfoUpd);
        //3 根据订单号得到订单信息
        OrderInfo orderInfo = orderService.getById(paymentInfo.getOrderId());
        //4 修改订单状态
        orderInfo.setOrderStatus(OrderStatusEnum.PAID.getStatus());
        orderService.updateById(orderInfo);
        //5 调用医院接口,通知更新支付状态
    }
    /**
     * 获取支付记录
     */
    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);
    }

取消预约

退款接口

未支付取消订单,直接通知医院更新取消预约状态

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

  • 根据orderid查询订单
  • 要判断是否可以取消,即现在的是否再允许取消的时间范围内
  • 远程调用医院的接口,更改订单状态为取消预约,根据hoscode获取医院签名,封装订单参数发送请求
  • 如果返回值为200,判断此订单是否已经退过款,即orderInfo的支付状态和枚举类进行比较
  • 调用微信的refund方法进行退款
  • 更新orderinfo的状态
  • 发送mq,更新预约数量,我们与下单成功更新预约数使用相同的mq信息,不设置可预约数与剩余预约数,接收端可预约数加1即可
if(null != orderMqVo.getAvailableNumber()) {
            //下单成功更新预约数
            Schedule schedule = scheduleService.getById(orderMqVo.getScheduleId());
            schedule.setReservedNumber(orderMqVo.getReservedNumber());
            schedule.setAvailableNumber(orderMqVo.getAvailableNumber());
            //update() :  {scheduleRepository.save(schedule);},更新的是mongo中的剩余预约和可预约数量
            scheduleService.update(schedule);
        }else {
            //取消预约更新预约数
            Schedule schedule = scheduleService.getById(orderMqVo.getScheduleId());
            int availableNumber = schedule.getAvailableNumber().intValue() + 1;
            schedule.setAvailableNumber(availableNumber);
            scheduleService.update(schedule);
        }
  • 向用户发送预约取消的短信,短信的内容封装在orderMqVo的MsmVo中,包括就诊人名字,日期等
//取消订单
    @Override
    public Boolean cancelOrder(Long orderId) {
        //获取订单信息
        OrderInfo orderInfo = this.getById(orderId);
        //判断是否取消 现在时间超过quitTime,就不能退号了
        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<String, Object> 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");
//        System.out.println("退款result"+result);
        //根据results返回数据
        if(result.getInteger("code") != 200) {
            throw new YyghException(result.getString("message"), ResultCodeEnum.FAIL.getCode());
        } else {
            //是否支付 退款
            if(orderInfo.getOrderStatus().intValue() == OrderStatusEnum.PAID.getStatus().intValue()) {
            //已支付 退款
                boolean isRefund = weixinService.refund(orderId);
                if(!isRefund) {
                    throw new YyghException(ResultCodeEnum.CANCEL_ORDER_FAIL);
                }
            }
            //更改订单状态
            orderInfo.setOrderStatus(OrderStatusEnum.CANCLE.getStatus());
            baseMapper.updateById(orderInfo);
            //发送mq信息更新预约数 我们与下单成功更新预约数使用相同的mq信息,不设置可预约数与剩余预约数,接收端可预约数减1即可
            OrderMqVo orderMqVo = new OrderMqVo();
            orderMqVo.setScheduleId(orderInfo.getScheduleId());
            //短信提示
            MsmVo msmVo = new MsmVo();
            msmVo.setPhone(orderInfo.getPatientPhone());
            String reserveDate = new DateTime(orderInfo.getReserveDate()).toString("yyyy-MM-dd") + (orderInfo.getReserveTime()==0 ? "上午": "下午");
            Map<String,Object> param = new HashMap<String,Object>(){{
                put("title", orderInfo.getHosname()+"|"+orderInfo.getDepname()+"|"+orderInfo.getTitle());
                put("reserveDate", reserveDate);
                put("name", orderInfo.getPatientName());
            }};
            msmVo.setParam(param);
            orderMqVo.setMsmVo(msmVo);
            rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_ORDER, MqConst.ROUTING_ORDER, orderMqVo);
        }
        return true;
    }

微信的refund

  • 从payment表中获取支付记录信息 paymentInfo
  • 添加信息到退款记录表 refundInfo
  • 判断当前订单是否已经退款,看Status,已经退款就返回true,否则调用微信的退款接口
  • 微信接口实现退款,封装参数
  • 调用微信接口 Httpclient
  • 设置证书信息
  • 接收返回信息
  • refundInfo 根据返回的resultMap更新退款记录表(更新时间和退款id)
  • refundInfo 状态改为已退款
    //微信退款
    @Override
    public Boolean refund(Long orderId) {

        try {
            //获取支付记录信息
            PaymentInfo paymentInfo = paymentService.getPaymentInfo(orderId, PaymentTypeEnum.WEIXIN.getStatus());
            //添加信息到退款记录表
            RefundInfo refundInfo = refundInfoService.saveRefundInfo(paymentInfo);
            //判断当前订单是否已经退款,看Status,已经退款就返回true,否则调用微信的退款接口
            if(refundInfo.getRefundStatus().intValue() == RefundStatusEnum.REFUND.getStatus().intValue()) {
                return true;
            }
            //微信接口实现退款
            //封装参数
            Map<String,String> paramMap = new HashMap<>(8);
            paramMap.put("appid",ConstantPropertiesUtils.APPID);       //公众账号ID
            paramMap.put("mch_id",ConstantPropertiesUtils.PARTNER);   //商户编号
            paramMap.put("nonce_str",WXPayUtil.generateNonceStr());
            paramMap.put("transaction_id",paymentInfo.getTradeNo()); //微信订单号
            paramMap.put("out_trade_no",paymentInfo.getOutTradeNo()); //商户订单编号
            paramMap.put("out_refund_no","tk"+paymentInfo.getOutTradeNo()); //商户退款单号
//       paramMap.put("total_fee",paymentInfoQuery.getTotalAmount().multiply(new BigDecimal("100")).longValue()+"");
//       paramMap.put("refund_fee",paymentInfoQuery.getTotalAmount().multiply(new BigDecimal("100")).longValue()+"");
            paramMap.put("total_fee","1");
            paramMap.put("refund_fee","1");
            String paramXml = WXPayUtil.generateSignedXml(paramMap,ConstantPropertiesUtils.PARTNERKEY);
            //调用微信接口
            HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/secapi/pay/refund");
            client.setXmlParam(paramXml);
            client.setHttps(true);
            //设置证书信息
            client.setCert(true);
            client.setCertPassword(ConstantPropertiesUtils.PARTNER);
            client.post();
            //接受返回数据
            String xml = client.getContent();
            Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);
            System.out.println("退款resultMap"+resultMap);
            if (null != resultMap && WXPayConstants.SUCCESS.equalsIgnoreCase(resultMap.get("result_code"))) {
                //退款成功则更新退款记录表
                refundInfo.setCallbackTime(new Date());
                refundInfo.setTradeNo(resultMap.get("refund_id"));
                //status改成已退款
                refundInfo.setRefundStatus(RefundStatusEnum.REFUND.getStatus());
                refundInfo.setCallbackContent(JSONObject.toJSONString(resultMap));
                refundInfoService.updateById(refundInfo);
                return true;
            }
            return false;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值