zoukankan      html  css  js  c++  java
  • 微信支付

    用第三方工具SDK微信支付

    官方文档:
     https://pay.weixin.qq.com/wiki
    第三方SDK:
     https://github.com/Pay-Group/best-pay-sdk

    注意:与官方文档步骤做对比

    1.引入依赖

    <dependency>
    			<groupId>cn.springboot</groupId>
    			<artifactId>best-pay-sdk</artifactId>
    			<version>1.1.0</version>
    		</dependency>
    

      2.项目配置文件配置

    wechat:
      mpAppId: wxd898fcb01713c658
      mpAppSecret: 47ccc303338cee6e62894fxxxxxxxxxxx
      openAppId: wx6ad144e54af67d87
      openAppSecret: 91a2ff6d38a2bbccfb7e9f9079108e2e
      mchId: 1483469312
      mchKey: 06C56A89949D617xxxxxxxxxxx
    //上面三个在微信商家平台得到
    keyPath: /var/weixin_cert/h5.p12
    notifyUrl: http://sell.natapp4.cc/sell/pay/notify------没这个就不能发起支付
    

      

    3.创建参数配置文件,在授权的基础上补全

    @Data
    @Component
    @ConfigurationProperties(prefix = "wechat")
    public class WechatAccountConfig {
    
        /**
         * 公众平台id
         */
        private String mpAppId;
    
        /**
         * 公众平台密钥
         */
        private String mpAppSecret;
    
        /**
         * 开放平台id
         */
        private String openAppId;
    
        /**
         * 开放平台密钥
         */
        private String openAppSecret;
    
        /**
         * 商户号
         */
        private String mchId;
    
        /**
         * 商户密钥
         */
        private String mchKey;
    
        /**
         * 商户证书路径
         */
        private String keyPath;
    
        /**
         * 微信支付异步通知地址
         */
        private String notifyUrl;
    
        /**
         * 微信模版id
         */
        private Map<String, String> templateId;
    }
    /
    

      

    4.配置支付配置文件

    @Component
    public class WechatPayConfig {
    
        @Autowired
        private WechatAccountConfig accountConfig;
    
        @Bean
        public BestPayServiceImpl bestPayService() {
            BestPayServiceImpl bestPayService = new BestPayServiceImpl();
            bestPayService.setWxPayH5Config(wxPayH5Config());
            return bestPayService;
        }
    
        @Bean
        public WxPayH5Config wxPayH5Config() {
            WxPayH5Config wxPayH5Config = new WxPayH5Config();
            wxPayH5Config.setAppId(accountConfig.getMpAppId());
            wxPayH5Config.setAppSecret(accountConfig.getMpAppSecret());
            wxPayH5Config.setMchId(accountConfig.getMchId());
            wxPayH5Config.setMchKey(accountConfig.getMchKey());
            wxPayH5Config.setKeyPath(accountConfig.getKeyPath());
            wxPayH5Config.setNotifyUrl(accountConfig.getNotifyUrl());
            return wxPayH5Config;
        }
    }
    

     5.创建payservice与serviceImpl,先测试配置文件中参数是否能得到

    public interface PayService {
    
        PayResponse create(OrderDTO orderDTO);
    }
    
    
    
    public class PayServiceImpl implements PayService {
    
       
        @Autowired
        private BestPayServiceImpl bestPayService;
    
        @Autowired
        private OrderService orderService;
    
        @Override
        public PayResponse create(OrderDTO orderDTO) {
         PayRequest payRequest = new PayRequest();
         bestPayService.pay(payRequest);      
    }
    }
    

    6-1.参数json格式化 

    public class JsonUtil {
    
        public static String toJson(Object object) {
            GsonBuilder gsonBuilder = new GsonBuilder();
            gsonBuilder.setPrettyPrinting();
            Gson gson = gsonBuilder.create();
            return gson.toJson(object);
        }
    }
    

      6-2. serviceimpl传入参数//这一步已经完成调用统一下单api,并返回预付单信息

    @Service
    @Slf4j
    public class PayServiceImpl implements PayService {
    
        private static final String ORDER_NAME = "微信点餐订单";//自定义
    
        @Autowired
        private BestPayServiceImpl bestPayService;
    
        @Autowired
        private OrderService orderService;
    
        @Override
        public PayResponse create(OrderDTO orderDTO) {
            PayRequest payRequest = new PayRequest();
            payRequest.setOpenid(orderDTO.getBuyerOpenid());
            payRequest.setOrderAmount(orderDTO.getOrderAmount().doubleValue());
            payRequest.setOrderId(orderDTO.getOrderId());
            payRequest.setOrderName(ORDER_NAME);
            payRequest.setPayTypeEnum(BestPayTypeEnum.WXPAY_H5);
            log.info("【微信支付】发起支付, request={}", JsonUtil.toJson(payRequest));
    
            PayResponse payResponse = bestPayService.pay(payRequest);
            log.info("【微信支付】发起支付, response={}", JsonUtil.toJson(payResponse));
            return payResponse;
        }
    }
    

      7-1.静态注入参数,每次测试需手动修改数据由网页发起支付,代码放在后端,static文件夹下 pay.html-----------参数就是6-2返回的参数

    <script>
        function onBridgeReady(){
            WeixinJSBridge.invoke(
                'getBrandWCPayRequest', {
                    "appId":"wxd898fcb01713c658",     //公众号名称,由商户传入
                    "timeStamp":"1499569906",         //时间戳,自1970年以来的秒数
                    "nonceStr":"bVsQpcfsKUAzO8r0", //随机串
                    "package":"prepay_id=wx2017070911112036b51eaddc0529394957",
                    "signType":"MD5",         //微信签名方式:
                    "paySign":"78CA85306AB823156E1032EFB5BB1C76" //微信签名
                },
                function(res){
                    if(res.err_msg == "get_brand_wcpay_request:ok" ) {}     // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回    ok,但并不保证它绝对可靠。
                }
            );
        }
        if (typeof WeixinJSBridge == "undefined"){
            if( document.addEventListener ){
                document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
            }else if (document.attachEvent){
                document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
                document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
            }
        }else{
            onBridgeReady();
        }
    </script>
    

      7-2.动态注入参数 使用freemaker模板引擎

    7-2-1 引入freemaker依赖

    <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-freemarker</artifactId>
            </dependency>
    

      7-2-2在controller里引入模板引擎名称

    @Controller
    @RequestMapping("/pay")
    public class PayController {
    
        @Autowired
        private OrderService orderService;
    
        @Autowired
        private PayService payService;
    
        @GetMapping("/create")
        public ModelAndView create(@RequestParam("orderId") String orderId,
                                   @RequestParam("returnUrl") String returnUrl,
                                   Map<String, Object> map) {
            //1. 查询订单
            OrderDTO orderDTO = orderService.findOne(orderId);
            if (orderDTO == null) {
                throw new SellException(ResultEnum.ORDER_NOT_EXIST);
            }
    
            //2. 发起支付
            PayResponse payResponse = payService.create(orderDTO);
    
            map.put("payResponse", payResponse);
            map.put("returnUrl", returnUrl);
    
            return new ModelAndView("pay/create", map);
        }
    }}
    

      

      7-2-3 创建create模板,放在templates文件夹下 create.ftl

    <script>
        function onBridgeReady(){
            WeixinJSBridge.invoke(
                    'getBrandWCPayRequest', {
                        "appId":"${payResponse.appId}",     //公众号名称,由商户传入
                        "timeStamp":"${payResponse.timeStamp}",         //时间戳,自1970年以来的秒数
                        "nonceStr":"${payResponse.nonceStr}", //随机串
                        "package":"${payResponse.packAge}",
                        "signType":"MD5",         //微信签名方式:
                        "paySign":"${payResponse.paySign}" //微信签名
                    },
                    function(res){
    //                    if(res.err_msg == "get_brand_wcpay_request:ok" ) {
    
    //                    }     // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回    ok,但并不保证它绝对可靠。
                        location.href = "${returnUrl}";
                    }
            );
        }
        if (typeof WeixinJSBridge == "undefined"){
            if( document.addEventListener ){
                document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
            }else if (document.attachEvent){
                document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
                document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
            }
        }else{
            onBridgeReady();
        }
    </script>
    

      7-2-4微信异步通知,改变支付状态

    返回给微信处理结果的模板引擎

    <xml>
        <return_code><![CDATA[SUCCESS]]></return_code>
        <return_msg><![CDATA[OK]]></return_msg>
    </xml>
    

      

    controller层

    @Controller
    @RequestMapping("/pay")
    public class PayController {
    
        @Autowired
        private OrderService orderService;
    
        @Autowired
        private PayService payService;
    
        @GetMapping("/create")
        public ModelAndView create(@RequestParam("orderId") String orderId,
                                   @RequestParam("returnUrl") String returnUrl,
                                   Map<String, Object> map) {
            //1. 查询订单
            OrderDTO orderDTO = orderService.findOne(orderId);
            if (orderDTO == null) {
                throw new SellException(ResultEnum.ORDER_NOT_EXIST);
            }
    
            //2. 发起支付
            PayResponse payResponse = payService.create(orderDTO);
    
            map.put("payResponse", payResponse);
            map.put("returnUrl", returnUrl);
    
            return new ModelAndView("pay/create", map);
        }
    
        /**
         * 微信异步通知
         * @param notifyData
         */
        @PostMapping("/notify")
        public ModelAndView notify(@RequestBody String notifyData) {
            payService.notify(notifyData);
    
            //返回给微信处理结果
            return new ModelAndView("pay/success");
        }
    }
    

      serviceimpl层 特别注意:金额校验时有精度差别,所以必须处理

    金额校验精度处理

    public class MathUtil {
    
        private static final Double MONEY_RANGE = 0.01;
    
        /**
         * 比较2个金额是否相等
         * @param d1
         * @param d2
         * @return
         */
        public static Boolean equals(Double d1, Double d2) {
            Double result = Math.abs(d1 - d2);
            if (result < MONEY_RANGE) {
                return true;
            }else {
                return false;
            }
        }
    }
    

      serviceimpl层

    @Service
    @Slf4j
    public class PayServiceImpl implements PayService {
    
        private static final String ORDER_NAME = "微信点餐订单";
    
        @Autowired
        private BestPayServiceImpl bestPayService;
    
        @Autowired
        private OrderService orderService;
    
        @Override
        public PayResponse create(OrderDTO orderDTO) {
            PayRequest payRequest = new PayRequest();
            payRequest.setOpenid(orderDTO.getBuyerOpenid());
            payRequest.setOrderAmount(orderDTO.getOrderAmount().doubleValue());
            payRequest.setOrderId(orderDTO.getOrderId());
            payRequest.setOrderName(ORDER_NAME);
            payRequest.setPayTypeEnum(BestPayTypeEnum.WXPAY_H5);
            log.info("【微信支付】发起支付, request={}", JsonUtil.toJson(payRequest));
    
            PayResponse payResponse = bestPayService.pay(payRequest);
            log.info("【微信支付】发起支付, response={}", JsonUtil.toJson(payResponse));
            return payResponse;
        }
    
        @Override
        public PayResponse notify(String notifyData) {
            //1. 验证签名
            //2. 支付的状态
            //3. 支付金额
            //4. 支付人(下单人 == 支付人)
    
            PayResponse payResponse = bestPayService.asyncNotify(notifyData);
            log.info("【微信支付】异步通知, payResponse={}", JsonUtil.toJson(payResponse));
    
            //查询订单
            OrderDTO orderDTO = orderService.findOne(payResponse.getOrderId());
    
            //判断订单是否存在
            if (orderDTO == null) {
                log.error("【微信支付】异步通知, 订单不存在, orderId={}", payResponse.getOrderId());
                throw new SellException(ResultEnum.ORDER_NOT_EXIST);
            }
    
            //判断金额是否一致(0.10   0.1)
            if (!MathUtil.equals(payResponse.getOrderAmount(), orderDTO.getOrderAmount().doubleValue())) {
                log.error("【微信支付】异步通知, 订单金额不一致, orderId={}, 微信通知金额={}, 系统金额={}",
                        payResponse.getOrderId(),
                        payResponse.getOrderAmount(),
                        orderDTO.getOrderAmount());
                throw new SellException(ResultEnum.WXPAY_NOTIFY_MONEY_VERIFY_ERROR);
            }
    
            //修改订单的支付状态
            orderService.paid(orderDTO);
    
            return payResponse;
        }
    }
    

     8.退款

    8-1 在官方文档中下载API安全证书,并配置文件路径

    wechat:
      mpAppId: wxd898fcb01713c658
      mpAppSecret: 47ccc303338cee6e62894fxxxxxxxxxxx
      openAppId: wx6ad144e54af67d87
      openAppSecret: 91a2ff6d38a2bbccfb7e9f9079108e2e
      mchId: 1483469312
      mchKey: 06C56A89949D617xxxxxxxxxxx
      keyPath: /var/weixin_cert/h5.p12//这个是API安全证书文件配置路径
    

      orderserviceimpl

    @Autowired
        private PayService payService;
    
    @Override
        @Transactional
        public OrderDTO cancel(OrderDTO orderDTO) {
            OrderMaster orderMaster = new OrderMaster();
    
            //判断订单状态
            if (!orderDTO.getOrderStatus().equals(OrderStatusEnum.NEW.getCode())) {
                log.error("【取消订单】订单状态不正确, orderId={}, orderStatus={}", orderDTO.getOrderId(), orderDTO.getOrderStatus());
                throw new SellException(ResultEnum.ORDER_STATUS_ERROR);
            }
    
            //修改订单状态
            orderDTO.setOrderStatus(OrderStatusEnum.CANCEL.getCode());
            BeanUtils.copyProperties(orderDTO, orderMaster);
            OrderMaster updateResult = orderMasterRepository.save(orderMaster);
            if (updateResult == null) {
                log.error("【取消订单】更新失败, orderMaster={}", orderMaster);
                throw new SellException(ResultEnum.ORDER_UPDATE_FAIL);
            }
    
            //返回库存
            if (CollectionUtils.isEmpty(orderDTO.getOrderDetailList())) {
                log.error("【取消订单】订单中无商品详情, orderDTO={}", orderDTO);
                throw new SellException(ResultEnum.ORDER_DETAIL_EMPTY);
            }
            List<CartDTO> cartDTOList = orderDTO.getOrderDetailList().stream()
                    .map(e -> new CartDTO(e.getProductId(), e.getProductQuantity()))
                    .collect(Collectors.toList());
            productService.increaseStock(cartDTOList);
    
            //如果已支付, 需要退款
            if (orderDTO.getPayStatus().equals(PayStatusEnum.SUCCESS.getCode())) {
                payService.refund(orderDTO);
            }
    
            return orderDTO;
        }
    

      payserviceimpl

    @Service
    @Slf4j
    public class PayServiceImpl implements PayService {
    
        private static final String ORDER_NAME = "微信点餐订单";
    
        @Autowired
        private BestPayServiceImpl bestPayService;
    
        @Autowired
        private OrderService orderService;
    
        @Override
        public PayResponse create(OrderDTO orderDTO) {
            PayRequest payRequest = new PayRequest();
            payRequest.setOpenid(orderDTO.getBuyerOpenid());
            payRequest.setOrderAmount(orderDTO.getOrderAmount().doubleValue());
            payRequest.setOrderId(orderDTO.getOrderId());
            payRequest.setOrderName(ORDER_NAME);
            payRequest.setPayTypeEnum(BestPayTypeEnum.WXPAY_H5);
            log.info("【微信支付】发起支付, request={}", JsonUtil.toJson(payRequest));
    
            PayResponse payResponse = bestPayService.pay(payRequest);
            log.info("【微信支付】发起支付, response={}", JsonUtil.toJson(payResponse));
            return payResponse;
        }
    
        @Override
        public PayResponse notify(String notifyData) {
            //1. 验证签名
            //2. 支付的状态
            //3. 支付金额
            //4. 支付人(下单人 == 支付人)
    
            PayResponse payResponse = bestPayService.asyncNotify(notifyData);
            log.info("【微信支付】异步通知, payResponse={}", JsonUtil.toJson(payResponse));
    
            //查询订单
            OrderDTO orderDTO = orderService.findOne(payResponse.getOrderId());
    
            //判断订单是否存在
            if (orderDTO == null) {
                log.error("【微信支付】异步通知, 订单不存在, orderId={}", payResponse.getOrderId());
                throw new SellException(ResultEnum.ORDER_NOT_EXIST);
            }
    
            //判断金额是否一致(0.10   0.1)
            if (!MathUtil.equals(payResponse.getOrderAmount(), orderDTO.getOrderAmount().doubleValue())) {
                log.error("【微信支付】异步通知, 订单金额不一致, orderId={}, 微信通知金额={}, 系统金额={}",
                        payResponse.getOrderId(),
                        payResponse.getOrderAmount(),
                        orderDTO.getOrderAmount());
                throw new SellException(ResultEnum.WXPAY_NOTIFY_MONEY_VERIFY_ERROR);
            }
    
            //修改订单的支付状态
            orderService.paid(orderDTO);
    
            return payResponse;
        }
    
        /**
         * 退款
         * @param orderDTO
         */
        @Override
        public RefundResponse refund(OrderDTO orderDTO) {
            RefundRequest refundRequest = new RefundRequest();
            refundRequest.setOrderId(orderDTO.getOrderId());
            refundRequest.setOrderAmount(orderDTO.getOrderAmount().doubleValue());
            refundRequest.setPayTypeEnum(BestPayTypeEnum.WXPAY_H5);
            log.info("【微信退款】request={}", JsonUtil.toJson(refundRequest));
    
            RefundResponse refundResponse = bestPayService.refund(refundRequest);
            log.info("【微信退款】response={}", JsonUtil.toJson(refundResponse));
    
            return refundResponse;
        }
    }
    

      

    总结:

    支付流程

    授权:遇到redirect_url错误时,立马确定授权地址填错了

    支付

    授权+支付

      

     

  • 相关阅读:
    Jquery清除style样式
    合并单元格式
    SQL根据下标,返回split分割后字符串
    js功能比较全面的yyyyMMdd格式的日期验证正则
    DataTable to Json
    List<T>下的Find,FindAll等条件过滤函数的使用方法
    获取iframe内部DOM对象
    PowerDesigner取消Name与Code同步
    再次回归
    最近遇到一个比较有意思的题目
  • 原文地址:https://www.cnblogs.com/tanghao666/p/8053095.html
Copyright © 2011-2022 走看看