本文代码绝对可用,本站微信支付就是使用这套代码的!
准备工作
因为微信升级了微信支付的API,从API-V2升级到了API-V3,安全机制方面变动很大,所以在上一篇文章中,我们将整个微信支付API-V3所需要的证书、秘钥之类的封装成了一个HTTP请求类,只要使用这个类调用微信支付的API,即可自动完成所需的安全认证操作。没看的朋友可以点击链接阅读Java Springboot使用OkHttp实现微信支付API-V3签名、证书的管理和使用 。上篇文章中,我们一共封装了四个类,这里简单介绍一下:
WxOkHttpUtil
这个类就是我们之后调用微信支付API的HTTP请求类。OkHttpUtil
这个类是上面类的基础,只是简单的HTTP请求封装,没有微信相关的业务逻辑。AesUtil
和WxCertUtil
微信安全机制相关的秘钥、证书加解密类,有兴趣的可以看看,没兴趣的直接粘贴到项目里用就行了。里面的参数介绍,在上一篇文章中。
还有两个POJO类,这里就不解释了,直接粘贴到你的项目中即可。
JSAPI微信下单接口
下单接口都差不多,无非是个别参数有所不同。这里以微信支付JSAPI API-V3接口为例。
新建一个springboot的service(如果你没有使用springboot,那就新建一个静态类),然后编写下单接口的代码,完整代码如下:
package com.coderbbb.book1.service;
import com.coderbbb.book1.utils.WxOkHttpUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
@Service
public class WxPayService {
public String prePay() throws Exception {
/**
* 支付金额
*/
BigDecimal money = new BigDecimal("10086");
BigDecimal fen = money.multiply(new BigDecimal("100"));
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
String timeExpire = format.format(new Date(System.currentTimeMillis() + 300 * 1000));
/**
* 改成你的APP_ID
*/
String appId = "WX_APP_ID";
/**
* 商户号
*/
String mchId = "WX_MCH_ID";
String notifyUrl = "通知URL,请填写https://开头的完整网址";
/**
* 发起支付的用户的OpenID
*/
String openId = "openid";
HashMap<String, Object> amountMap = new HashMap<>(1);
amountMap.put("total", fen.intValue());
HashMap<String, Object> payerMap = new HashMap<>(1);
payerMap.put("openid", openId);
HashMap<String, Object> map = new HashMap<>();
map.put("appid", appId);
map.put("mchid", mchId);
map.put("description", "支付商品描述");
map.put("out_trade_no", "商户订单号");
map.put("time_expire", timeExpire);
map.put("notify_url", notifyUrl);
map.put("amount", amountMap);
map.put("payer", payerMap);
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(map);
String content = WxOkHttpUtil.wxPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi", json, null);
if (content == null) {
throw new Exception("下单失败");
}
JsonNode jsonNode = mapper.readTree(content);
if (!jsonNode.has("prepay_id")) {
throw new Exception("下单失败");
}
return jsonNode.get("prepay_id").asText();
}
}
上面的代码中,有些参数需要改成你自己的,这里简单列举一下:
money
变量,这个是支付金额,不用解释了吧。appId
变量,这个是从你的微信支付所绑定的公众号复制的。mchId
变量,商户号,从微信支付后台复制。notifyUrl
变量,就是支付成功后,微信通知你的URL。每次支付完成,微信都会访问这个变量里填的URL,来通知你支付结果,是异步通知。同步通知是每次支付完,你调用微信的查单接口,去查结果。map
里的description
,商品描述信息,显示在用户支付成功后的账单里。map
里的out_trade_no
,商户订单号,就是你自己生成的订单号。长度不超过32位。
调用这个方法之后,会返回一些支付信息,这些支付信息要传给前端,让前端H5网页唤醒微信支付。当用户支付成功之后,微信调用你的notifyUrl来通知你支付成功。
微信JSAPI支付前端代码
JSAPI调起支付API,需要一些参数,微信支付官方文档介绍如下:
上图所示的所有参数,这里逐一介绍一下:
appId
:商户申请的公众号对应的appid,由微信支付生成,可在公众号后台查看。若下单时传了sub_appid,可为sub_appid的值。示例值:wx8888888888888888timeStamp
:时间戳,标准北京时间,时区为东八区,自1970年1月1日 0点0分0秒以来的秒数。注意:部分系统取到的值为毫秒级,需要转换成秒(10位数字)。 示例值:1414561699nonceStr
:随机字符串,不长于32位。示例值:5K8264ILTKCH16CQ2502SI8ZNMTM67VSpackage
:填写下单接口的返回值即可。上面微信下单接口的代码,返回了一个字符串,填入这里即可。signType
:填入RSApaySign
:签名。签名方法已经在前面的文章中提供代码了。使用WxCertUtil.rsaSign()
方法即可得到签名结果。
演示代码如下:
/**
* 构造微信前端JSAPI调起支付API的参数
* @param prePay
* @return
*/
public WxPayJsDTO getWxPayJsDTO(String prePay) {
prePay = "prepay_id=" + prePay;
WxPayJsDTO wxPayJsDTO = new WxPayJsDTO();
wxPayJsDTO.setPrePay(prePay);
wxPayJsDTO.setNonceStr(RandomStringUtils.random(21, true, true));
wxPayJsDTO.setTimeStamp(String.valueOf(System.currentTimeMillis() / 1000));
wxPayJsDTO.setSignType("RSA");
String appId = websiteConfigService.getConfig(BaseWebsiteConfig.WX_FWH_APP_ID);
String signStr = Stream.of(appId, wxPayJsDTO.getTimeStamp(), wxPayJsDTO.getNonceStr(), prePay).collect(Collectors.joining("\n", "", "\n"));
wxPayJsDTO.setPaySign(WxCertUtil.rsaSign(signStr));
return wxPayJsDTO;
}
上面的代码简单演示了如何构造前端JSAPI调起支付API的参数,仅供参考。
微信支付异步通知验签
当支付成功后,微信会调用我们的notifyUrl来通知我们。此时,我们需要验证微信携带的签名等信息是否合法。这部分,我们在上篇文章中,已经提供了封装好的验签类WxOkHttpUtil.checkServletRequestSign
。使用方法截图如下: