找回密码
 立即注册

QQ登录

只需一步,快速开始

参考文章

关注:4

所属分类: 微信开发 微信公众号开发资源 参考文章

微信公众号开发相关参考文章,不定期更新

微信支付开发流程梳理

[复制链接]
查看: 1462|回复: 7
最佳答案
0 

4

主题

4

帖子

72

积分

新人求带

积分
72
 楼主| 发表于 2017-12-19 14:45:46 | 显示全部楼层 |阅读模式
用第三方工具SDK微信支付

官方文档:
https://pay.weixin.qq.com/wiki

第三方SDK:
https://github.com/Pay-Group/best-pay-sdk

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

1.引入依赖
  1. <dependency>
  2.             <groupId>cn.springboot</groupId>
  3.             <artifactId>best-pay-sdk</artifactId>
  4.             <version>1.1.0</version>
  5.         </dependency>
复制代码

2.项目配置文件配置
  1. wechat:
  2.   mpAppId: wxd898fcb01713c658
  3.   mpAppSecret: 47ccc303338cee6e62894fxxxxxxxxxxx
  4.   openAppId: wx6ad144e54af67d87
  5.   openAppSecret: 91a2ff6d38a2bbccfb7e9f9079108e2e
  6.   mchId: 1483469312
  7.   mchKey: 06C56A89949D617xxxxxxxxxxx
  8. //上面三个在微信商家平台得到
  9. keyPath: /var/weixin_cert/h5.p12
  10. notifyUrl: http://sell.natapp4.cc/sell/pay/notify------没这个就不能发起支付
复制代码

3.创建参数配置文件,在授权的基础上补全
  1. @Data
  2. @Component
  3. @ConfigurationProperties(prefix = "wechat")
  4. public class WechatAccountConfig {

  5.     /**
  6.      * 公众平台id
  7.      */
  8.     private String mpAppId;

  9.     /**
  10.      * 公众平台密钥
  11.      */
  12.     private String mpAppSecret;

  13.     /**
  14.      * 开放平台id
  15.      */
  16.     private String openAppId;

  17.     /**
  18.      * 开放平台密钥
  19.      */
  20.     private String openAppSecret;

  21.     /**
  22.      * 商户号
  23.      */
  24.     private String mchId;

  25.     /**
  26.      * 商户密钥
  27.      */
  28.     private String mchKey;

  29.     /**
  30.      * 商户证书路径
  31.      */
  32.     private String keyPath;

  33.     /**
  34.      * 微信支付异步通知地址
  35.      */
  36.     private String notifyUrl;

  37.     /**
  38.      * 微信模版id
  39.      */
  40.     private Map<String, String> templateId;
  41. }
  42. /
复制代码

4.配置支付配置文件
  1. @Component
  2. public class WechatPayConfig {

  3.     @Autowired
  4.     private WechatAccountConfig accountConfig;

  5.     @Bean
  6.     public BestPayServiceImpl bestPayService() {
  7.         BestPayServiceImpl bestPayService = new BestPayServiceImpl();
  8.         bestPayService.setWxPayH5Config(wxPayH5Config());
  9.         return bestPayService;
  10.     }

  11.     @Bean
  12.     public WxPayH5Config wxPayH5Config() {
  13.         WxPayH5Config wxPayH5Config = new WxPayH5Config();
  14.         wxPayH5Config.setAppId(accountConfig.getMpAppId());
  15.         wxPayH5Config.setAppSecret(accountConfig.getMpAppSecret());
  16.         wxPayH5Config.setMchId(accountConfig.getMchId());
  17.         wxPayH5Config.setMchKey(accountConfig.getMchKey());
  18.         wxPayH5Config.setKeyPath(accountConfig.getKeyPath());
  19.         wxPayH5Config.setNotifyUrl(accountConfig.getNotifyUrl());
  20.         return wxPayH5Config;
  21.     }
  22. }
复制代码

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

+ View Code
6-1.参数json格式化 
  1. public class JsonUtil {

  2.     public static String toJson(Object object) {
  3.         GsonBuilder gsonBuilder = new GsonBuilder();
  4.         gsonBuilder.setPrettyPrinting();
  5.         Gson gson = gsonBuilder.create();
  6.         return gson.toJson(object);
  7.     }
  8. }
复制代码

6-2. serviceimpl传入参数//这一步已经完成调用统一下单api,并返回预付单信息
  1. @Service
  2. @Slf4j
  3. public class PayServiceImpl implements PayService {

  4.     private static final String ORDER_NAME = "微信点餐订单";//自定义

  5.     @Autowired
  6.     private BestPayServiceImpl bestPayService;

  7.     @Autowired
  8.     private OrderService orderService;

  9.     @Override
  10.     public PayResponse create(OrderDTO orderDTO) {
  11.         PayRequest payRequest = new PayRequest();
  12.         payRequest.setOpenid(orderDTO.getBuyerOpenid());
  13.         payRequest.setOrderAmount(orderDTO.getOrderAmount().doubleValue());
  14.         payRequest.setOrderId(orderDTO.getOrderId());
  15.         payRequest.setOrderName(ORDER_NAME);
  16.         payRequest.setPayTypeEnum(BestPayTypeEnum.WXPAY_H5);
  17.         log.info("【微信支付】发起支付, request={}", JsonUtil.toJson(payRequest));

  18.         PayResponse payResponse = bestPayService.pay(payRequest);
  19.         log.info("【微信支付】发起支付, response={}", JsonUtil.toJson(payResponse));
  20.         return payResponse;
  21.     }
  22. }
复制代码

7-1.静态注入参数,每次测试需手动修改数据由网页发起支付,代码放在后端,static文件夹下 pay.html-----------参数就是6-2返回的参数
  1. <script>
  2.     function onBridgeReady(){
  3.         WeixinJSBridge.invoke(
  4.             'getBrandWCPayRequest', {
  5.                 "appId":"wxd898fcb01713c658",     //公众号名称,由商户传入
  6.                 "timeStamp":"1499569906",         //时间戳,自1970年以来的秒数
  7.                 "nonceStr":"bVsQpcfsKUAzO8r0", //随机串
  8.                 "package":"prepay_id=wx2017070911112036b51eaddc0529394957",
  9.                 "signType":"MD5",         //微信签名方式:
  10.                 "paySign":"78CA85306AB823156E1032EFB5BB1C76" //微信签名
  11.             },
  12.             function(res){
  13.                 if(res.err_msg == "get_brand_wcpay_request:ok" ) {}     // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回    ok,但并不保证它绝对可靠。
  14.             }
  15.         );
  16.     }
  17.     if (typeof WeixinJSBridge == "undefined"){
  18.         if( document.addEventListener ){
  19.             document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
  20.         }else if (document.attachEvent){
  21.             document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
  22.             document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
  23.         }
  24.     }else{
  25.         onBridgeReady();
  26.     }
  27. </script>
复制代码

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

7-2-1 引入freemaker依赖
  1. <dependency>
  2.             <groupId>org.springframework.boot</groupId>
  3.             <artifactId>spring-boot-starter-freemarker</artifactId>
  4.         </dependency>
复制代码

7-2-2在controller里引入模板引擎名称
  1. @Controller
  2. @RequestMapping("/pay")
  3. public class PayController {

  4.     @Autowired
  5.     private OrderService orderService;

  6.     @Autowired
  7.     private PayService payService;

  8.     @GetMapping("/create")
  9.     public ModelAndView create(@RequestParam("orderId") String orderId,
  10.                                @RequestParam("returnUrl") String returnUrl,
  11.                                Map<String, Object> map) {
  12.         //1. 查询订单
  13.         OrderDTO orderDTO = orderService.findOne(orderId);
  14.         if (orderDTO == null) {
  15.             throw new SellException(ResultEnum.ORDER_NOT_EXIST);
  16.         }

  17.         //2. 发起支付
  18.         PayResponse payResponse = payService.create(orderDTO);

  19.         map.put("payResponse", payResponse);
  20.         map.put("returnUrl", returnUrl);

  21.         return new ModelAndView("pay/create", map);
  22.     }
  23. }}
复制代码

7-2-3 创建create模板,放在templates文件夹下 create.ftl
  1. <script>
  2.     function onBridgeReady(){
  3.         WeixinJSBridge.invoke(
  4.                 'getBrandWCPayRequest', {
  5.                     "appId":"${payResponse.appId}",     //公众号名称,由商户传入
  6.                     "timeStamp":"${payResponse.timeStamp}",         //时间戳,自1970年以来的秒数
  7.                     "nonceStr":"${payResponse.nonceStr}", //随机串
  8.                     "package":"${payResponse.packAge}",
  9.                     "signType":"MD5",         //微信签名方式:
  10.                     "paySign":"${payResponse.paySign}" //微信签名
  11.                 },
  12.                 function(res){
  13. //                    if(res.err_msg == "get_brand_wcpay_request:ok" ) {

  14. //                    }     // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回    ok,但并不保证它绝对可靠。
  15.                     location.href = "${returnUrl}";
  16.                 }
  17.         );
  18.     }
  19.     if (typeof WeixinJSBridge == "undefined"){
  20.         if( document.addEventListener ){
  21.             document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
  22.         }else if (document.attachEvent){
  23.             document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
  24.             document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
  25.         }
  26.     }else{
  27.         onBridgeReady();
  28.     }
  29. </script>
复制代码

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

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

  1. <xml>
  2.     <return_code><![CDATA[SUCCESS]]></return_code>
  3.     <return_msg><![CDATA[OK]]></return_msg>
  4. </xml>
复制代码

controller层

  1. @Controller
  2. @RequestMapping("/pay")
  3. public class PayController {

  4.     @Autowired
  5.     private OrderService orderService;

  6.     @Autowired
  7.     private PayService payService;

  8.     @GetMapping("/create")
  9.     public ModelAndView create(@RequestParam("orderId") String orderId,
  10.                                @RequestParam("returnUrl") String returnUrl,
  11.                                Map<String, Object> map) {
  12.         //1. 查询订单
  13.         OrderDTO orderDTO = orderService.findOne(orderId);
  14.         if (orderDTO == null) {
  15.             throw new SellException(ResultEnum.ORDER_NOT_EXIST);
  16.         }

  17.         //2. 发起支付
  18.         PayResponse payResponse = payService.create(orderDTO);

  19.         map.put("payResponse", payResponse);
  20.         map.put("returnUrl", returnUrl);

  21.         return new ModelAndView("pay/create", map);
  22.     }

  23.     /**
  24.      * 微信异步通知
  25.      * @param notifyData
  26.      */
  27.     @PostMapping("/notify")
  28.     public ModelAndView notify(@RequestBody String notifyData) {
  29.         payService.notify(notifyData);

  30.         //返回给微信处理结果
  31.         return new ModelAndView("pay/success");
  32.     }
  33. }
复制代码

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

金额校验精度处理
  1. public class MathUtil {

  2.     private static final Double MONEY_RANGE = 0.01;

  3.     /**
  4.      * 比较2个金额是否相等
  5.      * @param d1
  6.      * @param d2
  7.      * @return
  8.      */
  9.     public static Boolean equals(Double d1, Double d2) {
  10.         Double result = Math.abs(d1 - d2);
  11.         if (result < MONEY_RANGE) {
  12.             return true;
  13.         }else {
  14.             return false;
  15.         }
  16.     }
  17. }
复制代码

serviceimpl层
  1. @Service
  2. @Slf4j
  3. public class PayServiceImpl implements PayService {

  4.     private static final String ORDER_NAME = "微信点餐订单";

  5.     @Autowired
  6.     private BestPayServiceImpl bestPayService;

  7.     @Autowired
  8.     private OrderService orderService;

  9.     @Override
  10.     public PayResponse create(OrderDTO orderDTO) {
  11.         PayRequest payRequest = new PayRequest();
  12.         payRequest.setOpenid(orderDTO.getBuyerOpenid());
  13.         payRequest.setOrderAmount(orderDTO.getOrderAmount().doubleValue());
  14.         payRequest.setOrderId(orderDTO.getOrderId());
  15.         payRequest.setOrderName(ORDER_NAME);
  16.         payRequest.setPayTypeEnum(BestPayTypeEnum.WXPAY_H5);
  17.         log.info("【微信支付】发起支付, request={}", JsonUtil.toJson(payRequest));

  18.         PayResponse payResponse = bestPayService.pay(payRequest);
  19.         log.info("【微信支付】发起支付, response={}", JsonUtil.toJson(payResponse));
  20.         return payResponse;
  21.     }

  22.     @Override
  23.     public PayResponse notify(String notifyData) {
  24.         //1. 验证签名
  25.         //2. 支付的状态
  26.         //3. 支付金额
  27.         //4. 支付人(下单人 == 支付人)

  28.         PayResponse payResponse = bestPayService.asyncNotify(notifyData);
  29.         log.info("【微信支付】异步通知, payResponse={}", JsonUtil.toJson(payResponse));

  30.         //查询订单
  31.         OrderDTO orderDTO = orderService.findOne(payResponse.getOrderId());

  32.         //判断订单是否存在
  33.         if (orderDTO == null) {
  34.             log.error("【微信支付】异步通知, 订单不存在, orderId={}", payResponse.getOrderId());
  35.             throw new SellException(ResultEnum.ORDER_NOT_EXIST);
  36.         }

  37.         //判断金额是否一致(0.10   0.1)
  38.         if (!MathUtil.equals(payResponse.getOrderAmount(), orderDTO.getOrderAmount().doubleValue())) {
  39.             log.error("【微信支付】异步通知, 订单金额不一致, orderId={}, 微信通知金额={}, 系统金额={}",
  40.                     payResponse.getOrderId(),
  41.                     payResponse.getOrderAmount(),
  42.                     orderDTO.getOrderAmount());
  43.             throw new SellException(ResultEnum.WXPAY_NOTIFY_MONEY_VERIFY_ERROR);
  44.         }

  45.         //修改订单的支付状态
  46.         orderService.paid(orderDTO);

  47.         return payResponse;
  48.     }
  49. }
复制代码

8.退款

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

  1. wechat:
  2.   mpAppId: wxd898fcb01713c658
  3.   mpAppSecret: 47ccc303338cee6e62894fxxxxxxxxxxx
  4.   openAppId: wx6ad144e54af67d87
  5.   openAppSecret: 91a2ff6d38a2bbccfb7e9f9079108e2e
  6.   mchId: 1483469312
  7.   mchKey: 06C56A89949D617xxxxxxxxxxx
  8.   keyPath: /var/weixin_cert/h5.p12//这个是API安全证书文件配置路径
复制代码

orderserviceimpl
  1. @Autowired
  2.     private PayService payService;

  3. @Override
  4.     @Transactional
  5.     public OrderDTO cancel(OrderDTO orderDTO) {
  6.         OrderMaster orderMaster = new OrderMaster();

  7.         //判断订单状态
  8.         if (!orderDTO.getOrderStatus().equals(OrderStatusEnum.NEW.getCode())) {
  9.             log.error("【取消订单】订单状态不正确, orderId={}, orderStatus={}", orderDTO.getOrderId(), orderDTO.getOrderStatus());
  10.             throw new SellException(ResultEnum.ORDER_STATUS_ERROR);
  11.         }

  12.         //修改订单状态
  13.         orderDTO.setOrderStatus(OrderStatusEnum.CANCEL.getCode());
  14.         BeanUtils.copyProperties(orderDTO, orderMaster);
  15.         OrderMaster updateResult = orderMasterRepository.save(orderMaster);
  16.         if (updateResult == null) {
  17.             log.error("【取消订单】更新失败, orderMaster={}", orderMaster);
  18.             throw new SellException(ResultEnum.ORDER_UPDATE_FAIL);
  19.         }

  20.         //返回库存
  21.         if (CollectionUtils.isEmpty(orderDTO.getOrderDetailList())) {
  22.             log.error("【取消订单】订单中无商品详情, orderDTO={}", orderDTO);
  23.             throw new SellException(ResultEnum.ORDER_DETAIL_EMPTY);
  24.         }
  25.         List<CartDTO> cartDTOList = orderDTO.getOrderDetailList().stream()
  26.                 .map(e -> new CartDTO(e.getProductId(), e.getProductQuantity()))
  27.                 .collect(Collectors.toList());
  28.         productService.increaseStock(cartDTOList);

  29.         //如果已支付, 需要退款
  30.         if (orderDTO.getPayStatus().equals(PayStatusEnum.SUCCESS.getCode())) {
  31.             payService.refund(orderDTO);
  32.         }

  33.         return orderDTO;
  34.     }
复制代码

payserviceimpl
  1. @Service
  2. @Slf4j
  3. public class PayServiceImpl implements PayService {

  4.     private static final String ORDER_NAME = "微信点餐订单";

  5.     @Autowired
  6.     private BestPayServiceImpl bestPayService;

  7.     @Autowired
  8.     private OrderService orderService;

  9.     @Override
  10.     public PayResponse create(OrderDTO orderDTO) {
  11.         PayRequest payRequest = new PayRequest();
  12.         payRequest.setOpenid(orderDTO.getBuyerOpenid());
  13.         payRequest.setOrderAmount(orderDTO.getOrderAmount().doubleValue());
  14.         payRequest.setOrderId(orderDTO.getOrderId());
  15.         payRequest.setOrderName(ORDER_NAME);
  16.         payRequest.setPayTypeEnum(BestPayTypeEnum.WXPAY_H5);
  17.         log.info("【微信支付】发起支付, request={}", JsonUtil.toJson(payRequest));

  18.         PayResponse payResponse = bestPayService.pay(payRequest);
  19.         log.info("【微信支付】发起支付, response={}", JsonUtil.toJson(payResponse));
  20.         return payResponse;
  21.     }

  22.     @Override
  23.     public PayResponse notify(String notifyData) {
  24.         //1. 验证签名
  25.         //2. 支付的状态
  26.         //3. 支付金额
  27.         //4. 支付人(下单人 == 支付人)

  28.         PayResponse payResponse = bestPayService.asyncNotify(notifyData);
  29.         log.info("【微信支付】异步通知, payResponse={}", JsonUtil.toJson(payResponse));

  30.         //查询订单
  31.         OrderDTO orderDTO = orderService.findOne(payResponse.getOrderId());

  32.         //判断订单是否存在
  33.         if (orderDTO == null) {
  34.             log.error("【微信支付】异步通知, 订单不存在, orderId={}", payResponse.getOrderId());
  35.             throw new SellException(ResultEnum.ORDER_NOT_EXIST);
  36.         }

  37.         //判断金额是否一致(0.10   0.1)
  38.         if (!MathUtil.equals(payResponse.getOrderAmount(), orderDTO.getOrderAmount().doubleValue())) {
  39.             log.error("【微信支付】异步通知, 订单金额不一致, orderId={}, 微信通知金额={}, 系统金额={}",
  40.                     payResponse.getOrderId(),
  41.                     payResponse.getOrderAmount(),
  42.                     orderDTO.getOrderAmount());
  43.             throw new SellException(ResultEnum.WXPAY_NOTIFY_MONEY_VERIFY_ERROR);
  44.         }

  45.         //修改订单的支付状态
  46.         orderService.paid(orderDTO);

  47.         return payResponse;
  48.     }

  49.     /**
  50.      * 退款
  51.      * @param orderDTO
  52.      */
  53.     @Override
  54.     public RefundResponse refund(OrderDTO orderDTO) {
  55.         RefundRequest refundRequest = new RefundRequest();
  56.         refundRequest.setOrderId(orderDTO.getOrderId());
  57.         refundRequest.setOrderAmount(orderDTO.getOrderAmount().doubleValue());
  58.         refundRequest.setPayTypeEnum(BestPayTypeEnum.WXPAY_H5);
  59.         log.info("【微信退款】request={}", JsonUtil.toJson(refundRequest));

  60.         RefundResponse refundResponse = bestPayService.refund(refundRequest);
  61.         log.info("【微信退款】response={}", JsonUtil.toJson(refundResponse));

  62.         return refundResponse;
  63.     }
  64. }
复制代码

总结:

支付流程

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

支付

授权+支付

回复

使用道具 举报

最佳答案
0 

0

主题

242

帖子

696

积分

专家路上

积分
696
发表于 2017-12-19 15:32:30 | 显示全部楼层
微币  微币  微币
回复 支持 反对

使用道具 举报

最佳答案
0 

0

主题

24

帖子

441

积分

略知一二

积分
441
发表于 2017-12-20 09:19:35 | 显示全部楼层
正想学习微信支付这块,谢谢分享。
回复 支持 反对

使用道具 举报

最佳答案
0 

1

主题

203

帖子

1567

积分

专家路上

积分
1567
发表于 2017-12-24 09:24:14 | 显示全部楼层
回复

使用道具 举报

最佳答案
0 

0

主题

94

帖子

523

积分

略知一二

积分
523
发表于 2018-1-5 16:34:01 | 显示全部楼层
dddddddddddddddddddddd
回复 支持 反对

使用道具 举报

最佳答案
0 

0

主题

24

帖子

291

积分

新人求带

积分
291
发表于 2018-1-10 21:44:00 | 显示全部楼层
很不错,多谢分享!
回复 支持 反对

使用道具 举报

最佳答案
0 

8

主题

172

帖子

1534

积分

专家路上

积分
1534
发表于 2018-1-12 10:33:50 | 显示全部楼层
回复

使用道具 举报

最佳答案
0 

8

主题

172

帖子

1534

积分

专家路上

积分
1534
发表于 前天 08:40 | 显示全部楼层
6666666666666666666666666666
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



henkuai.com是专业的第三方微信开发者平台,为生态而生。


本站为第三方微信开发者平台,非腾讯官方网站。

天津市滨海新区
中新生态城中成大道生态建设公寓9号楼3层301

欢迎来这里一起喝喝茶,
聊聊你的产品。

微信公众号gongzhongkaifa

工作日12小时内回复。

广告推广
zhongcong@henkuai.com

工作日12小时内回复。

市场合作
songchang@henkuai.com