springboot整合微信二维码支付

首页 / 文章分类 / 正文

微信支付官方文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/api.shtml

下图是微信支付的一个流程图:

我们需要做的是:

  • 1、调用微信下单接口,生成支付链接。
  • 2、根据链接生成二维码图片(可以使用qrious)
  • 3、扫码支付支付
  • 4、支付成功会有一个回调函数,根据实际情况来进行支付完成后的业务操作

微信没有提供maven仓库坐标,因此我们必须下载使用。下载链接https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1

微信SDK提供了一个统一的微信支付工具类:WXPay:

我们主要关注其中的unifiedOrder方法,统一下单:

/**      * 作用:统一下单
* 场景:公共号支付、扫码支付、APP支付 * @param reqData 向wxpay post的请求数据 * @return API返回数据 * @throws Exception */ public Map unifiedOrder(Map reqData) throws Exception { return this.unifiedOrder(reqData, config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs()); }

这里的请求参数是:Map reqData,就是官方API说明中的请求参数了,不过并不需要我们填写所有参数,而只需要下面的:

  • body:商品描述
  • out_trade_no:订单编号
  • total_fee:订单应支付金额
  • spbill_create_ip:设备IP
  • notify_url:回调地址
  • trade_type:交易类型

剩下的:appidmch_idnonce_strsign_typesign参数都有WXPay对象帮我们设置,那么问题来了:这些参数数据WXPay是怎么拿到的呢?

其中,

  • nonce_str:是随机字符串,因此由WXPay随机生成,
  • sign_type:是签名算法,由WXPay指定,默认是HMACSHA256;
  • sign:是签名,有签名算法结合密钥加密而来,因此这里的关键是密钥:key
  • appid、mch_id是商家信息,需要配置

也就是说,这例需要配置的包括:appid、mch_id、密钥key。这些从哪里来呢?

看下WXPay的构造函数:

public WXPay(final WXPayConfig config) throws Exception {     this(config, null, true, false); }

这里需要一个WXPayConfig对象,显然是配置对象。

WXPayConfig配置

WXPay依赖于WXPayConfig进行配置,那么WXPayConfig是什么呢?

看下源码中的关键部分:

public abstract class WXPayConfig {     /**      * 获取 App ID      *      * @return App ID      */     abstract String getAppID();     /**      * 获取 Mch ID      *      * @return Mch ID      */     abstract String getMchID();     /**      * 获取 API 密钥      *      * @return API密钥      */     abstract String getKey();          // 。。。省略 }

这不就是WXPay中需要配置的3个属性嘛,当我们实现这个类,并且给出其中的值,把WXPayConfig传递给WXPay时,WXPay就会获取到这些数据:

1580547534423

当我们利用WXPay发送请求时,WXPay就会帮我们封装到请求参数中:

1580547680115

而在我提供给大家的SDK中,就编写了一个WXPayConfig的实现:

package com.github.wxpay.sdk;  import lombok.Data;  import java.io.InputStream;  /**  * @author 黑马程序员  */ @Data public class WXPayConfigImpl extends WXPayConfig {     /**      * 公众账号ID      */     private String appID;     /**      * 商户号      */     private String mchID;     /**      * 生成签名的密钥      */     private String key;     /**      * 支付回调地址      */     private String notifyUrl;     /**      * 支付方式      */     private String payType;      public InputStream getCertStream(){         return null;     }      public IWXPayDomain getWXPayDomain(){         return WXPayDomainSimpleImpl.instance();     } }

将来我们只需要new出这个实现类对象,并且给这3个参数赋值即可。

话不多说,开撸

首先,把下载的SDK打包并安装到本地的maven仓库,方便在项目中使用。

直接对SDK进行打包,在项目maven中执行如下命令:

mvn source:jar install -Dmaven.test.skip=true

如图所示:

image-20200403112617377

打包完成后

引入依赖(除了wxpay的依赖,还需要一个转换xml的依赖)

     com.github.wxpay     wxpay-sdk     3.0.9       com.fasterxml.jackson.dataformat     jackson-dataformat-xml 

 在yml中配置 (如果服务运行在本地,那么回调接口就无法回调成功,需要使用内网穿透工具)

#微信APP支付参数 pay:   wx:     #商户应用appId     appId:      #设备号     mchId:      #商户key:api秘钥(32位)     key:      #回调接口     notifyUrl: http://服务ip:port/wx/notify     #支付类型(扫码)     payType: NATIVEVE

 将这些属性注入到PayProperties中:

import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component;  @Data @Component @ConfigurationProperties(prefix = "pay.wx") public class PayProperties {     private String appId;     private String mchId;     private String key;     private String notifyUrl;     private String payType; }

配置微信支付所需要的对象注入到spring容器中:

import com.github.wxpay.sdk.PayConfig; import com.github.wxpay.sdk.WXPay; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;  /**  * 初始化微信支付需要的对象  */ @Configuration public class PayConfiguration {     @Autowired     private PayProperties payProps;      @Bean     public WXPay wxPay() throws Exception {         PayConfig payConfig = new PayConfig();         payConfig.setAppID(payProps.getAppId());         payConfig.setMchID(payProps.getMchId());         payConfig.setKey(payProps.getKey());         return new WXPay(payConfig);     }  }

封装支付工具类

 import com.github.wxpay.sdk.WXPay; import com.zhiwan.exception.ZwException; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component;  import java.util.HashMap; import java.util.Map;  @Component @Slf4j public class PayHelper {      @Autowired     private WXPay wxPay;     @Autowired     private PayProperties payProps;      /**      * 生成支付链接      */     public String getPayUrl(String orderId,Long totalFee){         // 请求参数:         Map data = new HashMap();         data.put("body", "乐优商城-商品订单支付");         data.put("out_trade_no", orderId.toString());         data.put("total_fee", totalFee.toString());         data.put("spbill_create_ip", "123.12.12.123");         data.put("notify_url", payProps.getNotifyUrl());         data.put("trade_type", payProps.getPayType());  // 此处指定为扫码支付          try {             Map resp = wxPay.unifiedOrder(data);              if(resp.get("return_code").equals("SUCCESS")&&                     resp.get("result_code").equals("SUCCESS")){                 log.info("【微信支付】生成支付链接成功");                 return resp.get("code_url");             }else{                 log.error("【微信支付】生成支付链接失败,原因:"+resp.get("return_code"));                 throw new ZwException(500,"【微信支付】生成支付链接失败");             }         } catch (Exception e) {             e.printStackTrace();             throw new ZwException(500,"【微信支付】生成支付链接失败,"+e.getMessage());         }     }  }

到这生成微信支付的代码就已经完成了,调用即可

以下以供参考

controller调用生成支付链接:

 @RestController public class OrderController {      @Autowired     private OrderService orderService; /**      * 生成支付链接      *      */     @GetMapping("/order/url/{id}")     public ResponseEntity buildPayUrl(@PathVariable("id") Long id){         String payUrl = orderService.buildPayUrl(id);         return ResponseEntity.ok(payUrl);     } }

service代码:

可以设置支付链接的有效时间,可以使用redis缓存:

@Service @Slf4j @Transactional public class OrderService {   /**      * 生成微信支付链接      *      * @param id      * @return      */     public String buildPayUrl(String id) {         //1.先到redis取出当前订单的支付链接         String payUrl = redisTemplate.opsForValue().get("PAY_URL_" + id);         //2.如果redis有,则直接取出订单的支付链接         if (StringUtils.isNotEmpty(payUrl)) {             return payUrl;         }          //3.如果redis没有,则调用微信支付系统生成该订单的支付链接,把该链接存入redis,并设置有效期(2小时)         //根据订单id查询订单(获取支付金额)         //Order order = orderMapper.selectById(id);         //生成支付链接,这里我们直接先设置1分钱         payUrl = payHelper.getPayUrl(id,1L);         //把该链接存入redis,并设置有效期(2小时)         redisTemplate.opsForValue().set("PAY_URL_" + id, payUrl, 2, TimeUnit.HOURS);         return payUrl;     }  }

页面响应结果:

使用qrious.js将该链接转成二维码

springboot整合微信二维码支付

支付成功后微信会回调一个请求(回调的接口notifyUrl指定)

controller:

/**      * 微信支付回调方法      * 接受的参数是xml类型      */     @PostMapping(value = "/wx/notify", produces = "application/xml")     public Map wxNotify(@RequestBody Map paramMap) {         orderService.wxNotify(paramMap);          log.warn("【回调的信息:】"+paramMap.toString());          //返回成功信息给微信支付         Map resultMap = new HashMap<>();         resultMap.put("return_code", "SUCCESS");         resultMap.put("return_msg", "OK");         return resultMap;     }

service

/**      * 微信支付回调      *      * @param paramMap      */     public void wxNotify(Map paramMap) {         //1.订单ID         String orderId = (String) paramMap.get("out_trade_no");         //2.支付金额         Long totalFee = Long.valueOf((String) paramMap.get("total_fee"));          //3.查询订单         Order order = orderMapper.selectById(orderId);         log.warn("订单是:" + order.toString());          if (order == null) {             throw new ZwException(500, "订单不存在");         }          if (order.getActualFee() != totalFee) {             throw new ZwException(500, "订单金额不正确");         }          //修改订单状态         try {             order.setStatus(OrderStatusEnum.PAY_UP.value());             /**              * 微信支付1              */             order.setPaymentType(1);             order.setPayTime(new Date());             QueryWrapper query = Wrappers.query();             query.eq("order_id", orderId);             orderMapper.update(order, query);             log.info("【微信通知】更新订单状态成功");         } catch (Exception e) {             e.printStackTrace();             log.error("【微信通知】更新订单状态失败");             throw new ZwException(500, "更新订单状态失败");         }     }

到这里整个支付流程就完成了