官网文档:微信支付-开发者文档
官方SDK: https://github.com/wechatpay-apiv3/wechatpay-java
JSAPI下单 准备参数不写,只写关键方法 JSAPI是通过微信获取prepay_id来完成支付
第一步: 引入SDK依赖
<!-- https://mvnrepository.com/artifact/com.github.wechatpay-apiv3/wechatpay-java -->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.5</version>
</dependency>
第二步:编写获取预订单id方法
//自行封装请求参数
PrepayRequest prepayRequest = new PrepayRequest();
//getJsapiService()方法是我自己封装的
//prepay sdk中有的方法 PrepayResponse 属性有prepay_id
PrepayResponse prepay = getJsapiService(one).prepay(prepayRequest);
private JsapiService getJsapiService(WxConfig one) {
//one参数里面封装的所需要的参数
Config config = getJsapiConfig(one);
HttpClient httpClient =
new DefaultHttpClientBuilder()
.config(config)
.connectTimeoutMs(5000)
.build();
return new JsapiService.Builder().httpClient(httpClient).build();
}
private static final Map<String, Config> HASH_MAP = new ConcurrentHashMap<>();
private Config getJsapiConfig(WxConfig one) {
Config config;
String mchId = one.getMchId();
config = HASH_MAP.get(mchId);
//由于程序运行后 SDK中将已经创建的RSAAutoCertificateConfig保存下来,如果再次创建就会抛出异常
//我就引入了双重检锁 还用的Redisson
//如果写的不对评论区告诉我
/*
merchantId: 商户号Id
privateKeyFromPath: 保存私钥的路径,这个就是申请的郑虎会带有私钥文件apiclient_key.pem
merchantSerialNumber:这个值在申请证书之后,商户后台证书可以看到
apiV3Key:apiv3填写的值
*/
if (config == null) {
RLock lock = redisson.getLock("lock:" + mchId);
lock.lock(1000, TimeUnit.MILLISECONDS);
try {
config = HASH_MAP.get(mchId);
if (config == null) {
config =
new RSAAutoCertificateConfig.Builder()
.merchantId(one.getMchId())
.privateKeyFromPath(one.getPrivateKey())
.merchantSerialNumber(one.getMerchantSerialNumber())
.apiV3Key(one.getApiV3Key())
.build();
HASH_MAP.put(mchId, config);
}
} catch (Exception e) {
log.error("getJsapiService异常: {}", ExceptionUtils.getStackTrace(e));
throw new ServiceException("微信服务异常");
} finally {
lock.unlock();
}
}
return config;
}
JSAPI调起支付API
调起支付页面使用JS完成,参数我是由后端接口封装返回
签名生成-接口规则 | 微信支付商户平台文档中心 这是官方提供的示例 我根据这个改的
private String getPaySign(String appId, String timeStamp, String nonceStr, String prePay, WxConfig config) throws Exception {
String paySignBefore = appId +
"\n" +
timeStamp +
"\n" +
nonceStr +
"\n" +
prePay +
"\n";
return SignUtil.sign(paySignBefore, config.getPrivateKey());
}
public class SignUtil {
//获取paySign工具类 参数 apiclient_key.pem文件路径
public static String sign(String param, String fileName) throws NoSuchAlgorithmException, SignatureException, IOException, InvalidKeyException {
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(getPrivateKey(fileName));
sign.update(param.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(sign.sign());
}
/**
* 获取私钥。
*
* @param fileName 私钥文件路径 (required)
* @return 私钥对象
*/
public static PrivateKey getPrivateKey(String fileName) throws IOException {
String content = new String(Files.readAllBytes(Paths.get(fileName)), "utf-8");
try {
String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replaceAll("\\s+", "");
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(
new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("当前Java环境不支持RSA", e);
} catch (InvalidKeySpecException e) {
throw new RuntimeException("无效的密钥格式");
}
}
}
支付结果通知 用来接收支付结果
这是github上面的说明
/*
map 就是post请求发送过来的参数 我自己创建实体类接收 解析的时候抛出异常 我看官方内容 我就改成了Map就可以了
*/
private Transaction getTransaction(HttpServletRequest request, WxConfig wxConfig, Map<String, Object> map) {
String characterEncoding = request.getCharacterEncoding();
System.out.println("characterEncoding=" + characterEncoding);
//从请求头获取验签字段
String timestamp = request.getHeader("Wechatpay-Timestamp");
String nonce = request.getHeader("Wechatpay-Nonce");
String signature = request.getHeader("Wechatpay-Signature");
String serial = request.getHeader("Wechatpay-Serial");
// // 构造 RequestParam
com.wechat.pay.java.core.notification.RequestParam requestParam = new com.wechat.pay.java.core.notification.RequestParam.Builder()
.serialNumber(serial)
.nonce(nonce)
.signature(signature)
.timestamp(timestamp)
.body(JSON.toJSONString(map))
.build();
NotificationConfig config = (NotificationConfig) getJsapiConfig(wxConfig);
// 初始化 NotificationParser
NotificationParser parser = new NotificationParser(config);
// 以支付通知回调为例,验签、解密并转换成 Transaction
return parser.parse(requestParam, Transaction.class);
}
注: 如果有写的不清晰的地方,评论区联系,以上我只是写的调用微信的关键步骤