使用微信H5支付
限制条件:只能在微信环境外部发起
微信官方文档:H5下单 - H5支付 | 微信支付商户文档中心
此业务逻辑为用户购买VIP,根据实际业务进行修改
wxH5Pay:
transactionAmount:付款金额
userId:用户ID
cardId:卡片ID
package com.yys.szcp.controller.app;
import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson.JSONObject;
import com.yys.szcp.config.jwt.JwtIgnore;
import com.yys.szcp.entity.SharingConfig;
import com.yys.szcp.entity.TAppUser;
import com.yys.szcp.entity.TCard;
import com.yys.szcp.entity.TMember;
import com.yys.szcp.service.SharingConfigService;
import com.yys.szcp.service.TAppUserService;
import com.yys.szcp.service.TCardService;
import com.yys.szcp.service.TMemberService;
import com.yys.szcp.utils.ResultUtil;
import com.yys.szcp.utils.WebUtils;
import com.yys.szcp.utils.WechatPayApiV3Util;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import static com.yys.szcp.utils.WebUtils.getRequest;
@RequestMapping("app/user")
@RestController
public class H5Controller {
@Autowired
private SharingConfigService sharingConfigService;
@Autowired
private TAppUserService userService;
@Resource
private TMemberService tMemberService;
@Autowired
private TCardService tCardService;
private static final Logger logger = LoggerFactory.getLogger(H5Controller.class);
String result = null;
@JwtIgnore
@ApiOperation(value = "支付", notes = "支付")
@PostMapping("/wxH5Pay")
public ResultUtil wxH5Pay(HttpServletRequest request,
@RequestParam("transactionAmount") Double transactionAmount,
@RequestParam("userId") String userId,
Integer cardId) {
TAppUser userInfo = userService.getById(userId);
if (Objects.isNull(userInfo)) {
return ResultUtil.error("用户不存在");
}
JSONObject jsonObject = new JSONObject();
//设置请求参数
String appid = sharingConfigService.getGlobalConfig(SharingConfig.MINI_APPID);
jsonObject.put("appid", appid);
//设置请求参数(商户号)
String mch_id = sharingConfigService.getGlobalConfig(SharingConfig.MCH_ID);
jsonObject.put("mchid", mch_id);
jsonObject.put("description", "VIP");
DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
Calendar calendar = Calendar.getInstance();
String dateName = df.format(calendar.getTime());
int v = (int) ((Math.random() * 9 + 1) * 1000000);
//设置商户订单号
String outTradeNo = dateName + v;
jsonObject.put("out_trade_no", outTradeNo);
jsonObject.put("attach", userInfo.getId().toString());
String notifyHost = sharingConfigService.getGlobalConfig(SharingConfig.DOMAIN_HOST);
jsonObject.put("notify_url", notifyHost + "/app/user/payCallback");
JSONObject amount = new JSONObject();
amount.put("total", transactionAmount.intValue());
amount.put("currency", "CNY");
jsonObject.put("amount", amount);
JSONObject scene_info = new JSONObject();
scene_info.put("payer_client_ip", WebUtils.getRemoteAddr(request));
JSONObject h5_info = new JSONObject();
h5_info.put("type", getDeviceType());
scene_info.put("h5_info", h5_info);
jsonObject.put("scene_info", scene_info);
String url = "https://api.mch.weixin.qq.com/v3/pay/transactions/h5";
try {
result = HttpRequest.post(url)
.header(Header.CONTENT_TYPE, "application/json")
.header("ACCEPT", "application/json")
// 签名
.header("Authorization", "WECHATPAY2-SHA256-RSA2048" + " "
+ WechatPayApiV3Util.getToken("POST", url, jsonObject.toJSONString(), mch_id, "5CD531F298000804A4AEB3A798A878DA0BA0AFBF", "pem/apiclient_key_jq.pem"))
.body(jsonObject.toJSONString())
.execute().body();
//应该创建 支付表数据
if (result != null) {
//业务逻辑-----------
System.out.println("微信接口调用成功 并且新增支付信息成功");
}
return ResultUtil.success(JSONObject.parse(result));
} catch (Exception e) {
e.printStackTrace();
return ResultUtil.error("支付失败");
}
}
}
payCallback:回调
/**
* 充值vip回调
*/
@RequestMapping(value = "/payCallback")
@JwtIgnore
@ResponseBody
@ApiOperation(value = "充值vip回调", notes = "充值vip回调")
public void payCallback(HttpServletRequest request, HttpServletResponse response) {
System.out.println("微信回调接口方法 start");
String inputLine = "";
String notityXml = "";
try {
while ((inputLine = request.getReader().readLine()) != null) {
notityXml += inputLine;
}
//关闭流
request.getReader().close();
logger.info("微信回调内容信息:" + notityXml);
//解析成json
JSONObject data = JSONObject.parseObject(notityXml);
//判断 支付是否成功
if ("TRANSACTION.SUCCESS".equals(data.get("event_type"))) {
// 在这里处理接收到的支付结果通知数据
Map<String, Object> resourceMap = handlePaymentNotification(data);
// 获取 trade_state 值
String tradeState = resourceMap.get("trade_state").toString();
if (tradeState.equals("SUCCESS")) {
// 获取 amount 字段
Map<String, Object> amountMap = (Map<String, Object>) resourceMap.get("amount");
//获得 返回的商户订单号
String outTradeNo = resourceMap.get("out_trade_no").toString();
String attach = resourceMap.get("attach").toString();
String total_fee = amountMap.get("payer_total").toString();
//业务逻辑------------
// 返回成功响应
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().write("OK");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
handlePaymentNotification:
private Map<String, Object> handlePaymentNotification(JSONObject data) {
logger.info("进入解析resource:" + data.toString());
Map<String, Object> resourceMap = null;
// 在这里解析和处理接收到的支付结果通知数据
System.out.println("接收到支付结果通知:" + data.toJSONString());
//解密resource中的通知数据
String resource = data.get("resource").toString();
resourceMap = WechatPayApiV3Util.decryptFromResource(resource, "e10adc3949ba59abbe56e057f20f883e", 1);
logger.info("解析resource结果:" + resourceMap);
return resourceMap;
}
getDeviceType:
public static String getDeviceType() {
HttpServletRequest request = getRequest();
String sign = request.getHeader("user-agent");
if (sign.contains("Android")) {
return "android";
} else if (sign.contains("iPhone")) {
return "ios";
}
return "Wap";
}
WebUtils:没有用到的方法可以删除
package com.yys.szcp.utils;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang.StringUtils;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
public class WebUtils {
private static final Logger log = LoggerFactory.getLogger(WebUtils.class);
/**
* 获取系统上下文路径
* @param request
* @return
*/
public static String getContextPath(HttpServletRequest request) {
return request.getSession().getServletContext().getContextPath();
}
/**
* 获取用户的真正IP地址
* @param request
* @return
*/
public static String getRemoteAddr(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (StringUtils.isBlank(ip) || StringUtils.equalsIgnoreCase(ip, "unknown")) {
ip = request.getHeader("Proxy-Client-IP");
}
if (StringUtils.isBlank(ip) || StringUtils.equalsIgnoreCase(ip, "unknown")) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (StringUtils.isBlank(ip) || StringUtils.equalsIgnoreCase(ip, "unknown")) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
// 民安赖波经理提供的从head的什么值获取IP地址
if (StringUtils.isBlank(ip) || StringUtils.equalsIgnoreCase(ip, "unknown")) {
ip = request.getHeader("X-Real-IP");
}
if (StringUtils.isBlank(ip) || StringUtils.equalsIgnoreCase(ip, "unknown")) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (StringUtils.isBlank(ip) || StringUtils.equalsIgnoreCase(ip, "unknown")) {
ip = request.getRemoteAddr();
}
if (StringUtils.isNotBlank(ip) && StringUtils.indexOf(ip, ",") > 0) {
String[] ipArray = StringUtils.split(ip, ",");
ip = ipArray[0];
}
return ip;
}
/**
* 获取本地IP地址
*/
public static String getLocalAddr() throws UnknownHostException {
InetAddress addr = InetAddress.getLocalHost();
return addr.getHostAddress();
}
/**
* 处理乱码
*/
public static String encodingHelp(String s) throws Exception {
return new String(s.getBytes("ISO-8859-1"), "UTF-8");
}
/**
* 对ajax提交过来的参数进行解码
* @param s
* @return
* @throws Exception
*/
public static String ajaxDecode(String s) throws Exception {
return URLDecoder.decode(s, "UTF8");
}
/**
* 页面弹出提示信息
* @param response
* @param msg
* @throws Exception
*/
public static void alertMsg(HttpServletResponse response, String msg) throws Exception {
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html");
StringBuilder sb = new StringBuilder();
sb.append("<script type='text/javascript'>");
sb.append("alert(\""+msg+"\");");
sb.append("</script>");
PrintWriter out = response.getWriter();
out.print(sb.toString());
out.close();
}
/**
* 获取request
*/
public static HttpServletRequest getRequest()
{
try
{
return getRequestAttributes().getRequest();
}
catch (Exception e)
{
return null;
}
}
public static ServletRequestAttributes getRequestAttributes()
{
try
{
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
return (ServletRequestAttributes) attributes;
}
catch (Exception e)
{
return null;
}
}
// public static HttpServletRequest getRequest() {
// return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
// }
/**
* 判断是否为ajax请求
* @param request
* @return
*/
public static boolean isAjaxReqest(HttpServletRequest request) {
if ((request.getHeader("accept") != null && (request.getHeader("accept").indexOf("application/json") > -1) || (request.getHeader("X-Requested-With") != null && request.getHeader(
"X-Requested-With").indexOf("XMLHttpRequest") > -1))) {
return true;
}
return false;
}
public static String MD5(String sourceStr) {
String result = "";
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(sourceStr.getBytes());
byte b[] = md.digest();
int i;
StringBuffer buf = new StringBuffer("");
for (int offset = 0; offset < b.length; offset++) {
i = b[offset];
if (i < 0) {
i += 256;
}
if (i < 16) {
buf.append("0");
}
buf.append(Integer.toHexString(i));
}
result = buf.toString();
} catch (NoSuchAlgorithmException e) {
System.out.println(e);
}
return result;
}
@SuppressWarnings("rawtypes")
public static Map<String, String> doXMLParse(String strxml) throws Exception {
if (null == strxml || "".equals(strxml)) {
return null;
}
Map<String, String> m = new HashMap<String, String>();
InputStream in = String2Inputstream(strxml);
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(in);
Element root = doc.getRootElement();
List list = root.getChildren();
Iterator it = list.iterator();
while (it.hasNext()) {
Element e = (Element) it.next();
String k = e.getName();
String v = "";
List children = e.getChildren();
if (children.isEmpty()) {
v = e.getTextNormalize();
} else {
v = getChildrenText(children);
}
m.put(k, v);
}
//关闭流
in.close();
return m;
}
@SuppressWarnings("rawtypes")
private static String getChildrenText(List children) {
StringBuffer sb = new StringBuffer();
if (!children.isEmpty()) {
Iterator it = children.iterator();
while (it.hasNext()) {
Element e = (Element) it.next();
String name = e.getName();
String value = e.getTextNormalize();
List list = e.getChildren();
sb.append("<" + name + ">");
if (!list.isEmpty()) {
sb.append(getChildrenText(list));
}
sb.append(value);
sb.append("</" + name + ">");
}
}
return sb.toString();
}
public static InputStream String2Inputstream(String str) {
return new ByteArrayInputStream(str.getBytes());
}
/**
* 3 * 方法名: getRemotePortData
* 4 * 描述: 发送远程请求 获得代码示例
* 5 * 参数: @param urls 访问路径
* 6 * 参数: @param param 访问参数-字符串拼接格式, 例:port_d=10002&port_g=10007&country_a=
* 7 * 创建人: Xia ZhengWei
* 8 * 创建时间: 2017年3月6日 下午3:20:32
* 9 * 版本号: v1.0
* 10 * 返回类型: String
* 11
*/
public static String getRemotePortData(String urls, String param) {
try {
URL url = new URL(urls);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// 设置连接超时时间
conn.setConnectTimeout(30000);
// 设置读取超时时间
conn.setReadTimeout(30000);
conn.setRequestMethod("POST");
if (!StringUtils.isBlank(param)) {
conn.setRequestProperty("Origin", "https://sirius.searates.com");// 主要参数
conn.setRequestProperty("Referer", "https://sirius.searates.com/cn/port?A=ChIJP1j2OhRahjURNsllbOuKc3Y&D=567&G=16959&shipment=1&container=20st&weight=1&product=0&request=&weightcargo=1&");
conn.setRequestProperty("X-Requested-With", "XMLHttpRequest");// 主要参数
}
// 需要输出
conn.setDoInput(true);
// 需要输入
conn.setDoOutput(true);
// 设置是否使用缓存
conn.setUseCaches(false);
// 设置请求属性
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestProperty("Connection", "Keep-Alive");// 维持长连接
conn.setRequestProperty("Charset", "UTF-8");
if (!StringUtils.isBlank(param)) {
// 建立输入流,向指向的URL传入参数
DataOutputStream dos = new DataOutputStream(conn.getOutputStream());
dos.writeBytes(param);
dos.flush();
dos.close();
}
// 输出返回结果
InputStream input = conn.getInputStream();
int resLen = 0;
byte[] res = new byte[1024];
StringBuilder sb = new StringBuilder();
while ((resLen = input.read(res)) != -1) {
sb.append(new String(res, 0, resLen));
}
return sb.toString();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
/**
* 发送ajax
* @param response
* @param code
* @param msg
* @param data
* @throws Exception
*/
/*public static void ajaxJson(HttpServletResponse response, Integer code, String msg) throws Exception {
String json = JsonUtils.objectToJson(new ResultUtil(code, msg));
response.setContentType("text/json; charset=utf-8");
response.setHeader("Cache-Control", "no-cache"); // 取消浏览器缓存
PrintWriter out = response.getWriter();
out.print(json);
out.flush();
out.close();
}*/
/**
2 *
3 * 方法用途: 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序),并且生成url参数串<br>
4 * 实现步骤: <br>
5 *
6 * @param paraMap 要排序的Map对象
7 * @param urlEncode 是否需要URLENCODE
8 * @param keyToLower 是否需要将Key转换为全小写
9 * true:key转化成小写,false:不转化
10 * @return
11 */
public static String formatUrlMap(Map<String, String> paraMap, boolean urlEncode, boolean keyToLower) {
String buff = "";
Map<String, String> tmpMap = paraMap;
try
{
List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>(tmpMap.entrySet());
// 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序)
Collections.sort(infoIds, new Comparator<Map.Entry<String, String>>()
{
@Override
public int compare (Map.Entry < String, String > o1, Map.Entry < String, String > o2)
{
return (o1.getKey()).toString().compareTo(o2.getKey());
}
});
// 构造URL 键值对的格式
StringBuilder buf = new StringBuilder();
for (Map.Entry<String, String> item : infoIds)
{
if (StringUtils.isNotBlank(item.getKey()))
{
String key = item.getKey();
String val = item.getValue();
if (urlEncode)
{
val = URLEncoder.encode(val, "utf-8");
}
if (keyToLower)
{
buf.append(key.toLowerCase() + "=" + val);
} else
{
buf.append(key + "=" + val);
}
buf.append("&");
}
}
buff = buf.toString();
if (buff.isEmpty() == false)
{
buff = buff.substring(0, buff.length() - 1);
}
} catch(Exception e)
{
return null;
}
return buff;
}
public static String md5(String msg) {
if (null == msg) {
return null;
}
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] out = md.digest(msg.getBytes());
String str = org.apache.commons.codec.binary.Base64.encodeBase64String(out);
return str;
} catch (Exception e) {
return "";
}
}
public static Map<String, String> sendPay(String url, Map<String, Object> params) throws Exception {
try {
// String payload = objectMapper.writeValueAsString(params);
// 创建ObjectMapper实例
ObjectMapper objectMapper = new ObjectMapper();
// 将"pay_amount"和"valid_time"字段的值从字符串转换为数字
params.put("total_amount", Integer.parseInt((String) params.get("total_amount")));
params.put("valid_time", Integer.parseInt((String) params.get("valid_time")));
// 将Map转换回JSON字符串
String modifiedJsonStr = objectMapper.writeValueAsString(params);
// 打印修改后的JSON字符串
System.out.println(modifiedJsonStr);
URL apiUrl = new URL(url);
HttpURLConnection connection = (HttpURLConnection) apiUrl.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/json");
connection.setDoOutput(true);
OutputStream outputStream = connection.getOutputStream();
outputStream.write(modifiedJsonStr.getBytes(StandardCharsets.UTF_8));
outputStream.flush();
outputStream.close();
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
JsonNode jsonResponse = objectMapper.readTree(connection.getInputStream());
// 解析响应JSON数据并返回一个Map<String, String>对象
// 将JsonNode转换为Map对象
Map<String, String> responseMap = objectMapper.convertValue(jsonResponse, Map.class);
return responseMap;
} else {
String errorMessage = responseCode + " - " + connection.getResponseMessage();
return Collections.singletonMap("err_no", errorMessage);
}
} catch (IOException e) {
// 处理异常并返回适当的错误信息
e.printStackTrace();
return Collections.singletonMap("error", "请求失败:" + e.getMessage());
}
}
public static Map<String, String> send(String url, Map<String, Object> params) throws Exception {
try {
// 创建ObjectMapper实例
ObjectMapper objectMapper = new ObjectMapper();
// 将"withdraw_amount"字段的值从字符串转换为数字
// params.put("withdraw_amount", Integer.parseInt((String) params.get("withdraw_amount")));
// 将Map转换回JSON字符串
String modifiedJsonStr = objectMapper.writeValueAsString(params);
// 打印修改后的JSON字符串
System.out.println(modifiedJsonStr);
URL apiUrl = new URL(url);
HttpURLConnection connection = (HttpURLConnection) apiUrl.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/json");
connection.setDoOutput(true);
OutputStream outputStream = connection.getOutputStream();
outputStream.write(modifiedJsonStr.getBytes(StandardCharsets.UTF_8));
outputStream.flush();
outputStream.close();
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
JsonNode jsonResponse = objectMapper.readTree(connection.getInputStream());
// 解析响应JSON数据并返回一个Map<String, String>对象
Map<String, String> responseMap = objectMapper.convertValue(jsonResponse, Map.class);
return responseMap;
} else {
String errorMessage = responseCode + " - " + connection.getResponseMessage();
return Collections.singletonMap("err_no", errorMessage);
}
} catch (IOException e) {
// 处理异常并返回适当的错误信息
e.printStackTrace();
return Collections.singletonMap("error", "请求失败:" + e.getMessage());
}
}
}
WechatPayApiV3Util:
package com.yys.szcp.utils;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import lombok.extern.slf4j.Slf4j;
import okhttp3.HttpUrl;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.core.io.ClassPathResource;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Map;
import java.util.Random;
import java.util.Scanner;
/**
* @Author sanji
* @Date 2023/11/27 19:00
* @Version 1.0
* @description
*/
@Slf4j
public class WechatPayApiV3Util {
/**
* 获取微信支付平台证书 注意不是商户api证书
*/
private static final String WechatPaySerial = "https://api.mch.weixin.qq.com/v3/certificates";
/**
* 微信支付API v3 签名
*
* @param method 请求类型GET、POST
* @param url 请求地址
* @param body 请求数据 GET: 传"" POST: json串
* @param merchantId 商户号
* @param certSerialNo 商户证书(Api证书)序列号
* @param filename 商户证书(Api证书) 私钥
* @return
* @throws Exception
*/
public static String getToken(String method, String url, String body, String merchantId, String certSerialNo, String filename) throws Exception {
String signStr = "";
HttpUrl httpurl = HttpUrl.parse(url);
// 随机字符串
String nonceStr = getRandomString(32);
// 时间戳
long timestamp = System.currentTimeMillis() / 1000;
if (StringUtils.isEmpty(body)) {
body = "";
}
String message = buildMessage(method, httpurl, timestamp, nonceStr, body);
String signature = sign(message.getBytes("utf-8"), filename);
signStr = "mchid=\"" + merchantId + "\","
+ "nonce_str=\"" + nonceStr + "\","
+ "timestamp=\"" + timestamp + "\","
+ "serial_no=\"" + certSerialNo + "\","
+ "signature=\"" + signature + "\"";
log.info("Authorization Token:" + signStr);
return signStr;
}
public static String buildMessage(String method, HttpUrl url, long timestamp, String nonceStr, String body) {
String canonicalUrl = url.encodedPath();
if (url.encodedQuery() != null) {
canonicalUrl += "?" + url.encodedQuery();
}
return method + "\n"
+ canonicalUrl + "\n"
+ timestamp + "\n"
+ nonceStr + "\n"
+ body + "\n";
}
public static String sign(byte[] message, String filename) throws Exception {
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(getPrivateKey(filename));
sign.update(message);
return Base64.encodeBase64String(sign.sign());
}
/**
* 获取私钥。
*
* @return 私钥对象
*/
public static PrivateKey getPrivateKey(String filename) throws IOException {
// 编译后的相对路径
ClassPathResource classPathResource = new ClassPathResource(filename);
InputStream inputStream = classPathResource.getInputStream();
Scanner scanner = new Scanner(inputStream, "UTF-8");
String content = scanner.useDelimiter("\\A").next();
// 绝对路径
// String content = new String(Files.readAllBytes(Paths.get("F:\\key\\publicKey.pem")), "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.decodeBase64(privateKey)));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("当前Java环境不支持RSA", e);
} catch (InvalidKeySpecException e) {
log.info("异常:" + e);
throw new RuntimeException("无效的密钥格式");
}
}
/**
* 获取微信支付平台证书
*
* @param token
* @return
* @throws IOException
*/
public static String getWechatPaySerial(String token) throws IOException {
CloseableHttpClient httpclient = HttpClients.createDefault();
CloseableHttpResponse response = null;
HttpEntity entity = null;
try {
HttpGet httpGet = new HttpGet(WechatPaySerial);
// NOTE: 建议指定charset=utf-8。低于4.4.6版本的HttpCore,不能正确的设置字符集,可能导致签名错误
httpGet.addHeader("Content-Type", "application/json");
// 添加认证信息
httpGet.addHeader("Authorization", "WECHATPAY2-SHA256-RSA2048" + " " + token);
httpGet.addHeader("Accept", "application/json");
//httpGet.addHeader("User-Agent", " ");
response = httpclient.execute(httpGet);
entity = response.getEntity();//获取返回的数据
log.info("返回结果:{}", EntityUtils.toString(entity));
return EntityUtils.toString(entity);
} catch (Exception e) {
log.info("提现失败:{}", e.getMessage());
} finally {
// 关闭流
response.close();
}
return null;
}
/**
* 获取商户证书。
*
* @return X509证书
*/
public static X509Certificate getCertificate() throws IOException {
String APICLIENT_CERT = "pem/apiclient_cert.pem";
InputStream fis = WechatPayApiV3Util.class.getClassLoader().getResourceAsStream(APICLIENT_CERT);
try (BufferedInputStream bis = new BufferedInputStream(fis)) {
CertificateFactory cf = CertificateFactory.getInstance("X509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(bis);
cert.checkValidity();
return cert;
} catch (CertificateExpiredException e) {
throw new RuntimeException("证书已过期", e);
} catch (CertificateNotYetValidException e) {
throw new RuntimeException("证书尚未生效", e);
} catch (CertificateException e) {
throw new RuntimeException("无效的证书文件", e);
}
}
/**
* 获取商户证书序列号
*
* @return
* @throws IOException
*/
public static String getSerialNo() throws IOException {
X509Certificate certificate = getCertificate();
return certificate.getSerialNumber().toString(16).toUpperCase();
}
/**
* 生成随机数
* @param length
* @return
*/
public static String getRandomString(int length) {
// 创建一个随机数生成器。
SecureRandom random = new SecureRandom();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; ++i) {
// 返回下一个伪随机数,它是此随机数生成器的序列中均匀分布的 int 值。
int number = random.nextInt(3);
long result = 0;
// Math 类包含用于执行基本数学运算的方法,如初等指数、对数、平方根和三角函数。
// Math.random() 返回带正号的 double 值,该值大于等于 0.0 且小于 1.0。
// Math.round(Math.random() * 25 + 97) 返回最接近参数的 long。
switch (number) {
case 0:
result = Math.round(Math.random() * 25 + 65);
sb.append(String.valueOf((char) result));
break;
case 1:
result = Math.round(Math.random() * 25 + 97);
sb.append(String.valueOf((char) result));
break;
case 2:
sb.append(String.valueOf(new Random().nextInt(10)));
break;
}
}
return sb.toString();
}
/**
* 对称解密,异步通知的加密数据
* @param resource 加密数据
* @param apiV3Key apiV3密钥
* @param type 1-支付,2-退款
* @return
*/
public static Map<String, Object> decryptFromResource(String resource, String apiV3Key, Integer type) {
String msg = type==1?"支付成功":"退款成功";
log.info(msg+",回调通知,密文解密");
try {
//通知数据
Map<String, String> resourceMap = JSONObject.parseObject(resource, new TypeReference<Map<String, String>>() {});
//数据密文
String ciphertext = resourceMap.get("ciphertext");
//随机串
String nonce = resourceMap.get("nonce");
//附加数据
String associatedData = resourceMap.get("associated_data");
log.info("密文: {}", ciphertext);
AesUtil aesUtil = new AesUtil(apiV3Key.getBytes(StandardCharsets.UTF_8));
String resourceStr = aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8),
nonce.getBytes(StandardCharsets.UTF_8),
ciphertext);
log.info(msg+",回调通知,解密结果 : {}", resourceStr);
return JSONObject.parseObject(resourceStr, new TypeReference<Map<String, Object>>(){});
}catch (Exception e){
throw new RuntimeException("回调参数,解密失败!");
}
}
}