首先是一些基本参数
########################微信支付参数#######################################
#微信商户号
wechat.mchId = ******
#商户在微信公众平台申请服务号对应的APPID
wechat.appId = ******
#商户平台设置的密钥key
wechat.key = ******
#回调报文解密V3密钥key
wechat.v3Key = ******
#微信获取平台证书列表地址
wechat.certificates.url = https://api.mch.weixin.qq.com/v3/certificates
#微信APP下单URL
wechat.unifiedOrder.url = https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi
#微信APP查询订单URL
wechat.queryOrder.url = https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/%s?mchid=%s
#异步接收微信支付结果通知的回调地址 必须是https
wechat.notify.url = https://xxxx/web/order/payNotify
#微信API证书路径
wechat.certificate.path = F:/develop/wechat/apiclient_cert.pem
#微信API密钥路径
wechat.key.path = F:/develop/wechat/apiclient_key.pem
############################################################################
启动项目时加载的数据
package com.bhome.broad_training.common;
import com.bhome.broad_training.utils.wechatpay.PayRequestUtils;
import com.bhome.broad_training.utils.wechatpay.PayResponseUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
import java.io.FileInputStream;
import java.security.cert.X509Certificate;
import java.util.List;
@Slf4j
@Component
@PropertySource("classpath:globalParameter.properties")
public class MyCommandLineRunner implements CommandLineRunner {
/**
* 微信商户号
*/
@Value("${wechat.mchId}")
private String wechatMchId;
/**
* 商户在微信公众平台申请服务号对应的APPID
*/
@Value("${wechat.appId}")
private String wechatAppId;
/**
* 商户平台设置的密钥key
*/
@Value("${wechat.key}")
private String wechatKey;
/**
* 回调报文解密V3密钥key
*/
@Value("${wechat.v3Key}")
private String wechatV3Key;
/**
* 微信获取平台证书列表地址
*/
@Value("${wechat.certificates.url}")
private String wechatCertificatesUrl;
/**
* 微信APP下单URL
*/
@Value("${wechat.unifiedOrder.url}")
private String wechatUnifiedOrderUrl;
/**
* 微信APP查询订单URL
*/
@Value("${wechat.queryOrder.url}")
private String wechatQueryOrderUrl;
/**
* 异步接收微信支付结果通知的回调地址
*/
@Value("${wechat.notify.url}")
private String wechatNotifyUrl;
/**
* 微信证书路径
*/
@Value("${wechat.certificate.path}")
private String wechatCertificatePath;
/**
* 微信密钥路径
*/
@Value("${wechat.key.path}")
private String wechatKeyPath;
@Override
public void run(String... args) throws Exception {
//微信支付
StaticParameter.wechatMchId = wechatMchId;
StaticParameter.wechatAppId = wechatAppId;
StaticParameter.wechatKey = wechatKey;
StaticParameter.wechatV3Key = wechatV3Key;
StaticParameter.wechatCertificatesUrl = wechatCertificatesUrl;
StaticParameter.wechatUnifiedOrderUrl = wechatUnifiedOrderUrl;
StaticParameter.wechatQueryOrderUrl = wechatQueryOrderUrl;
StaticParameter.wechatNotifyUrl = wechatNotifyUrl;
X509Certificate certificate = PayRequestUtils.getCertificate(new FileInputStream(wechatCertificatePath));
StaticParameter.privateKey = PayRequestUtils.getPrivateKey(wechatKeyPath);
StaticParameter.serialNo = certificate.getSerialNumber().toString(16).toUpperCase();
StaticParameter.certificateMap = PayResponseUtils.refreshCertificate();
}
}
StaticParameter类
package com.bhome.broad_training.common;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 静态参数类
*/
public class StaticParameter {
/**
* 微信商户号
*/
public static String wechatMchId;
/**
* 商户在微信公众平台申请服务号对应的APPID
*/
public static String wechatAppId;
/**
* 商户平台设置的密钥key
*/
public static String wechatKey;
/**
* 回调报文解密V3密钥key
*/
public static String wechatV3Key;
/**
* 微信获取平台证书列表地址
*/
public static String wechatCertificatesUrl;
/**
* 微信APP下单URL
*/
public static String wechatUnifiedOrderUrl;
/**
* 微信APP查询订单URL
*/
public static String wechatQueryOrderUrl;
/**
* 异步接收微信支付结果通知的回调地址
*/
public static String wechatNotifyUrl;
/**
* 微信证书私钥
*/
public static PrivateKey privateKey;
/**
* 微信证书序列号
*/
public static String serialNo;
// 定义全局容器 保存微信平台证书公钥 注意线程安全
public static Map<String, X509Certificate> certificateMap = new ConcurrentHashMap<>();
}
微信付款请求工具类PayRequestUtils
package com.bhome.broad_training.utils.wechatpay;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.bhome.broad_training.common.StaticParameter;
import com.bhome.broad_training.exception.RequestWechatException;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.util.Base64Utils;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.cert.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 微信付款请求工具类
* @author shenlu
* @date 2020/12/16 16:43
*/
public class PayRequestUtils {
/**
* V3 SHA256withRSA 移动端请求签名.
* @param timestamp 当前时间戳
* @param nonceStr 随机字符串
* @param prepayId 统一下单接口返回的prepay_id参数值,提交格式如:prepay_id=***
* @return
* @throws InvalidKeyException
* @throws NoSuchAlgorithmException
* @throws SignatureException
* @throws MalformedURLException
*/
public static String appSign(long timestamp, String nonceStr, String prepayId) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException, MalformedURLException {
String signatureStr = Stream.of(StaticParameter.wechatAppId, String.valueOf(timestamp), nonceStr, prepayId)
.collect(Collectors.joining("\n", "", "\n"));
return getSign(signatureStr);
}
/**
* V3 SHA256withRSA http请求签名.
*
* @param method 请求方法 GET POST PUT DELETE 等
* @param canonicalUrl 请求地址
* @param timestamp 当前时间戳 因为要配置到TOKEN 中所以 签名中的要跟TOKEN 保持一致
* @param nonceStr 随机字符串 要和TOKEN中的保持一致
* @param body 请求体 GET 为 "" POST 为JSON
* @return the string
*/
private static String httpSign(String method, String canonicalUrl, String body, long timestamp, String nonceStr) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException, MalformedURLException {
URL url = new URL(canonicalUrl);
String signUrl;
if ("GET".equals(method)&&url.getQuery()!=null) {
signUrl = url.getPath() + "?" + url.getQuery();
}else{
signUrl = url.getPath();
}
String signatureStr = Stream.of(method, signUrl, String.valueOf(timestamp), nonceStr, body)
.collect(Collectors.joining("\n", "", "\n"));
return getSign(signatureStr);
}
/**
* 获取签名
* @param signatureStr 签名字符串
* @return
* @throws InvalidKeyException
* @throws NoSuchAlgorithmException
* @throws SignatureException
*/
public static String getSign(String signatureStr) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException {
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(StaticParameter.privateKey);
sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
return Base64Utils.encodeToString(sign.sign());
}
/**
* 生成Token http请求
*
* @param method 请求方法 GET POST PUT DELETE 等
* @param canonicalUrl 请求地址
* @param body 请求体 GET 为 "" POST 为JSON
* @return the string
*/
private static String httpToken(String method, String canonicalUrl, String body) throws MalformedURLException, NoSuchAlgorithmException, InvalidKeyException, SignatureException {
Long timestamp = System.currentTimeMillis()/1000;
String nonceStr = UUID.randomUUID().toString().replace("-","");
String signature = httpSign(method,canonicalUrl,body,timestamp,nonceStr);
final String TOKEN_PATTERN = "WECHATPAY2-SHA256-RSA2048 mchid=\"%s\",nonce_str=\"%s\",timestamp=\"%d\",serial_no=\"%s\",signature=\"%s\"";
// 生成token
return String.format(TOKEN_PATTERN,
StaticParameter.wechatMchId,
nonceStr, timestamp, StaticParameter.serialNo, signature);
}
public static<T> T wechatHttpPost(String url, String jsonStr, Class<T> t) throws RequestWechatException {
//创建httpClient实例
CloseableHttpClient httpClient = HttpClients.createDefault();
T instance = null;
try {
instance = t.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
//生成Token http请求
String token = null;
try {
token = httpToken("POST",url,jsonStr);
} catch (Exception e) {
e.printStackTrace();
}
HttpPost httppost = new HttpPost(url);
httppost.addHeader("Content-Type", "application/json;charset=UTF-8");
httppost.addHeader("Accept", "application/json");
httppost.addHeader("Authorization", token);
//设置连接超时时间和数据获取超时时间--单位:ms
RequestConfig requestConfig = Re