前言
最近在工作中有一个需求需要使用到JSAPI支付。我简单看了一下JSAPI支付,需要传递一些参数,其中就有openId,也就是说在支付之前需要获取用户的openId,也就是网页授权,所以就从网页授权开始做起。
微信公众号网页授权
1.首先想要获取用户信息的话,需要用到微信公众号的接口。如果没有认证的公众号的话可以申请测试公众号,可以提供测试接口。链接:https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login
2.当我们有了测试服务号之后,就可以设置接口了。第一步先设置授权回调页面域名,如果没有备案域名,可以搜索natapp进行购买通道,方便快捷。
3.先简单描述一下微信公众号的授权流程。
- 获取code
1:向微信服务器发送请求:https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URL&response_type=code&scope=snsapi_base&state=123#wechat_redirect 2:微信服务器接受到请求之后,将会回调到redirect_uri链接,然后将code传递过来 - 3:可能出现的错误请求
- 通过code换取网页授权access_token与openid
1:向微信服务器发送请求,请求链接: https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code - 2:请求成功之后将会返回一个JSON数据包,如下图所示。
- 3:可能出现的错误请求
- 刷新access_token(如果需要)
1:因为access_token的有效期比较短,这个时候我可以选择刷新access_token来保证access_token的有效性,请求链接:https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN 请求参数说明如下图所示 - 2:请求成功之后将返回正确的JSON数据包
- 3:可能出现的错误请求
- 拉取用户信息(需scope为 snsapi_userinfo)
1:向微信服务器发送请求:https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN - 2:请求成功之后将会获得正确的数据包,如下所示
- 3:可能会出现的错误情况
4.上面描述完了授权流程之后,下面说一下主要的代码实现。
在github中有很多方便的sdk对微信进行了封装,在这里我选用weixin-java-mp这个jar包,
[github地址](https://github.com/Wechat-Group/WxJava)
<!--微信授权-->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId>
<version>${weixin-java-mp.version}</version>
</dependency>
使用weixin-java-mp工具之前,需要对其进行初始化操作,初始化有很多种方法,这里我只说一下我的
- 配置文件
#公众号appid
wx_appid=
#公众号appsecret
wx_appsecret=
wx_token=
wx_aeskey=
- 第一步:配置config类,从properties文件中读取到这些数据
@Configuration
public class WxMpConfig {
@Value("#{wxProperties.wx_token}")
private String token;
@Value("#{wxProperties.wx_appid}")
private String appid;
@Value("#{wxProperties.wx_appsecret}")
private String appSecret;
@Value("#{wxProperties.wx_aeskey}")
private String aesKey;
public String getToken() {
return this.token;
}
public String getAppid() {
return this.appid;
}
public String getAppSecret() {
return this.appSecret;
}
public String getAesKey() {
return this.aesKey;
}
}
- 第二步:继承WxMpServiceImpl类,通过@PostConstruct注解对其进行初始化操作
@Service
public class WeixinService extends WxMpServiceImpl {
@Autowired
private WxMpConfig wxConfig;
@PostConstruct
public void init() {
final WxMpDefaultConfigImpl config = new WxMpDefaultConfigImpl();
// 设置微信公众号的appid
config.setAppId(this.wxConfig.getAppid());
// 设置微信公众号的app corpSecret
config.setSecret(this.wxConfig.getAppSecret());
// 设置微信公众号的token
config.setToken(this.wxConfig.getToken());
// 设置消息加解密密钥
config.setAesKey(this.wxConfig.getAesKey());
super.setWxMpConfigStorage(config);
}
}
- 第三步:编写controller类
@Controller
@RequestMapping("/api/wechat")
@Api(tags = "微信授权")
@RequiredArgsConstructor
public class WeChatController {
private static Logger logger = LogManager.getLogger(WeChatController.class.getName());
@Autowired
private WxMpService wxMpService;
@ApiOperation("微信授权")
@GetMapping("/authorize")
public String authorize(@RequestParam("returnUrl") String returnUrl) {
String url = WeChatNotifyUrl.WECHAT_AUTH_NOTIFY_URL;
String redirectUrl = wxMpService.oauth2buildAuthorizationUrl(url, WxConsts.OAuth2Scope.SNSAPI_USERINFO, URLEncoder.encode(returnUrl));
logger.info("【微信网页授权】获取code,redirectUrl=" + redirectUrl);
//重定向到下面一个方法
return "redirect:" + redirectUrl;
}
@ApiOperation("获取用户信息")
@GetMapping("/userInfo")
public String userInfo(@RequestParam("code") String code,
@RequestParam("state") String returnUrl) {
//获取openId
WxMpOAuth2AccessToken wxMpOAuth2AccessToken;
try {
wxMpOAuth2AccessToken = wxMpService.oauth2getAccessToken(code);
} catch (WxErrorException e) {
logger.error("wxErrorExceptionMsg === {}",e);
throw new ServiceException(e);
}
//获取用户基本信息
WxMpUser wxMpUser = null;
try {
wxMpUser = wxMpService.oauth2getUserInfo(wxMpOAuth2AccessToken, null);
} catch (WxErrorException e) {
logger.error("wxErrorExceptionMsg === {}",e);
throw new ServiceException(e);
}
Assert.notNull(wxMpUser);
logger.info("openId === {} ; unionId === {}",wxMpOAuth2AccessToken.getOpenId(),wxMpUser.getUnionId());
return "redirect:" + returnUrl + "?openId=" + wxMpUser.getOpenId() + "&unionId=" + wxMpUser.getUnionId();
}
- 以上就是授权的步骤了,当授权成功之后可以自定义需要跳转的页面与传递的参数,大家可以自行测试
微信公众号JSAPI支付
2.下面主要讲一下weixin-java-pay的使用
- 首先导入weixin-java-pay的依赖,这个工具的作者与weixin-java-mp的作者是同一位,github地址
<!--微信支付框架-->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
<version>3.6.0</version>
</dependency>
- 配置文件
#微信支付商户号
mchId=
#微信支付平台商户API密钥
mchKey=
#apiclient_cert.p12 证书文件的绝对路径
keyPath=
#微信小程序appId
appId=
#服务商模式下的子商户公众账号ID
subAppId=
#服务商模式下的子商户号
subMchId=
- 对WxPayService进行初始化
@Configuration
public class WxPayConfiguration {
@Value("#{wxPayProperties.appId}")
private String appId;
@Value("#{wxPayProperties.mchId}")
private String mchId;
@Value("#{wxPayProperties.mchKey}")
private String mchKey;
@Value("#{wxPayProperties.subAppId}")
private String subAppId;
@Value("#{wxPayProperties.subMchId}")
private String subMchId;
@Value("#{wxPayProperties.keyPath}")
private String keyPath;
@Bean
public WxPayConfig payConfig() {
WxPayConfig payConfig = new WxPayConfig();
payConfig.setAppId(this.appId);
payConfig.setMchId(this.mchId);
payConfig.setMchKey(this.mchKey);
payConfig.setSubAppId(this.subAppId);
payConfig.setSubMchId(this.subMchId);
payConfig.setKeyPath(this.keyPath);
return payConfig;
}
@Bean
public WxPayService payService() {
WxPayService payService = new WxPayServiceImpl();
payService.setConfig(payConfig());
return payService;
}
}
- 创建订单,向前端返回固定格式的数据
@ApiOperation("支付")
@GetMapping("/createOrder")
@ResponseBody
public WxPayMpOrderResult createOrder( String openId, Integer totalFee) {
String date = DateUtil.format(new Date(), "yyyyMMddHHmmss");
WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
String nonceStr = WeChatPayUtil.createNonceStr();
wxPayConfig.getMchKey();
String notifyUrl = WeChatNotifyUrl.WECHAT_PAY_NOTIFY_URL;
String tradeNo = OrderUtil.createOrderNumber();
//创建map创建签名
TreeMap<String, String> map = new TreeMap<>();
map.put("appid", wxPayConfig.getAppId());
map.put("mch_id", wxPayConfig.getMchId());
map.put("device_info", "WEB");
map.put("body", "test");
map.put("trade_type", WxConstant.PAY_TYPE);
map.put("nonce_str", nonceStr);
map.put("notify_url", notifyUrl);
map.put("out_trade_no", tradeNo);
map.put("total_fee", String.valueOf(totalFee));
map.put("openid", openId);
//签名
String sign = WeChatPayUtil.createSign(map, wxPayConfig.getMchKey());
//构造订单请求数据
orderRequest.setOpenid(openId);
orderRequest.setAppid(wxPayConfig.getAppId());
orderRequest.setTimeStart(date);
orderRequest.setMchId(wxPayConfig.getMchId());
orderRequest.setDeviceInfo("WEB");
orderRequest.setBody("test");
orderRequest.setNonceStr(nonceStr);
orderRequest.setTotalFee(totalFee);
orderRequest.setOutTradeNo(tradeNo);
orderRequest.setSign(sign);
orderRequest.setTradeType(WxConstant.PAY_TYPE);
orderRequest.setNotifyUrl(notifyUrl);
orderRequest.setSpbillCreateIp("127.0.0.1");
WxPayMpOrderResult order = null;
try {
order = wxPayService.createOrder(orderRequest);
} catch (WxPayException e) {
logger.error("wxPayError === {}", e);
throw new ServiceException(e);
}
return order;
}
- 接受支付之后,微信服务器向客户端发送的异步通知
@ApiOperation("异步通知")
@GetMapping("/payNotify")
@ResponseBody
public String parseOrderNotifyResult(@RequestBody String xmlData) throws WxPayException {
WxPayOrderNotifyResult notifyResult = wxPayService.parseOrderNotifyResult(xmlData);
Assert.notNull(notifyResult);
//TODO 执行自己的业务逻辑
return WxPayNotifyResponse.success("ok");
}
- 在WxPayNotifyResponse.success(“ok”)这个调用中,weixin-java-pay对返回进行了封装,做了验证sign等操作,方便了调用
- 以上就是支付工具的使用,在实际的开发工作中能够发挥很大作用,省去了很多麻烦的事情