mirror of
https://codeup.aliyun.com/67c68d4e484ca2f0a13ac3c1/ydc/jsowell-charger-web.git
synced 2026-04-22 03:55:17 +08:00
commit
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
package com.jsowell.wxpay.utils;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.spec.GCMParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Base64;
|
||||
|
||||
public class AesUtil {
|
||||
|
||||
static final int KEY_LENGTH_BYTE = 32;
|
||||
static final int TAG_LENGTH_BIT = 128;
|
||||
private final byte[] aesKey;
|
||||
|
||||
public AesUtil(byte[] key) {
|
||||
if (key.length != KEY_LENGTH_BYTE) {
|
||||
throw new IllegalArgumentException("无效的ApiV3Key,长度必须为32个字节");
|
||||
}
|
||||
this.aesKey = key;
|
||||
}
|
||||
|
||||
public String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext)
|
||||
throws GeneralSecurityException, IOException {
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
|
||||
|
||||
SecretKeySpec key = new SecretKeySpec(aesKey, "AES");
|
||||
GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);
|
||||
|
||||
cipher.init(Cipher.DECRYPT_MODE, key, spec);
|
||||
cipher.updateAAD(associatedData);
|
||||
|
||||
return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8");
|
||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
|
||||
throw new IllegalStateException(e);
|
||||
} catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.jsowell.wxpay.utils;
|
||||
|
||||
import com.google.zxing.LuminanceSource;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
public class BufferedImageLuminanceSource extends LuminanceSource {
|
||||
|
||||
private final BufferedImage image;
|
||||
private final int left;
|
||||
private final int top;
|
||||
|
||||
public BufferedImageLuminanceSource(BufferedImage image) {
|
||||
this(image, 0, 0, image.getWidth(), image.getHeight());
|
||||
}
|
||||
|
||||
public BufferedImageLuminanceSource(BufferedImage image, int left, int top, int width, int height) {
|
||||
super(width, height);
|
||||
|
||||
int sourceWidth = image.getWidth();
|
||||
int sourceHeight = image.getHeight();
|
||||
if (left + width > sourceWidth || top + height > sourceHeight) {
|
||||
throw new IllegalArgumentException("Crop rectangle does not fit within image data.");
|
||||
}
|
||||
|
||||
for (int y = top; y < top + height; y++) {
|
||||
for (int x = left; x < left + width; x++) {
|
||||
if ((image.getRGB(x, y) & 0xFF000000) == 0) {
|
||||
image.setRGB(x, y, 0xFFFFFFFF); // = white
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.image = new BufferedImage(sourceWidth, sourceHeight, BufferedImage.TYPE_BYTE_GRAY);
|
||||
this.image.getGraphics().drawImage(image, 0, 0, null);
|
||||
this.left = left;
|
||||
this.top = top;
|
||||
}
|
||||
|
||||
public byte[] getRow(int y, byte[] row) {
|
||||
if (y < 0 || y >= getHeight()) {
|
||||
throw new IllegalArgumentException("Requested row is outside the image: " + y);
|
||||
}
|
||||
int width = getWidth();
|
||||
if (row == null || row.length < width) {
|
||||
row = new byte[width];
|
||||
}
|
||||
image.getRaster().getDataElements(left, top + y, width, 1, row);
|
||||
return row;
|
||||
}
|
||||
|
||||
public byte[] getMatrix() {
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
int area = width * height;
|
||||
byte[] matrix = new byte[area];
|
||||
image.getRaster().getDataElements(left, top, width, height, matrix);
|
||||
return matrix;
|
||||
}
|
||||
|
||||
public boolean isCropSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public LuminanceSource crop(int left, int top, int width, int height) {
|
||||
return new BufferedImageLuminanceSource(image, this.left + left, this.top + top, width, height);
|
||||
}
|
||||
|
||||
public boolean isRotateSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public LuminanceSource rotateCounterClockwise() {
|
||||
int sourceWidth = image.getWidth();
|
||||
int sourceHeight = image.getHeight();
|
||||
AffineTransform transform = new AffineTransform(0.0, -1.0, 1.0, 0.0, 0.0, sourceWidth);
|
||||
BufferedImage rotatedImage = new BufferedImage(sourceHeight, sourceWidth, BufferedImage.TYPE_BYTE_GRAY);
|
||||
Graphics2D g = rotatedImage.createGraphics();
|
||||
g.drawImage(image, transform, null);
|
||||
g.dispose();
|
||||
int width = getWidth();
|
||||
return new BufferedImageLuminanceSource(rotatedImage, top, sourceWidth - (left + width), getHeight(), width);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
package com.jsowell.wxpay.utils;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
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.HttpClientBuilder;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class HttpUtils {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);
|
||||
|
||||
private static final ObjectMapper JSON = new ObjectMapper();
|
||||
|
||||
/**
|
||||
* get方法
|
||||
*
|
||||
* @param url
|
||||
* @return
|
||||
*/
|
||||
public static JsonNode doGet(String url) {
|
||||
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
|
||||
HttpGet httpget = new HttpGet(url);
|
||||
httpget.addHeader("Content-Type", "application/json;charset=UTF-8");
|
||||
httpget.addHeader("Accept", "application/json");
|
||||
try {
|
||||
String token = WechatPayUtils.getToken("GET", new URL(url), "");
|
||||
httpget.addHeader("Authorization", token);
|
||||
CloseableHttpResponse httpResponse = httpClient.execute(httpget);
|
||||
if (httpResponse.getStatusLine().getStatusCode() == 200) {
|
||||
String jsonResult = EntityUtils.toString(httpResponse.getEntity());
|
||||
return JSON.readTree(jsonResult);
|
||||
} else {
|
||||
log.warn(EntityUtils.toString(httpResponse.getEntity()));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("HttpUtils.doGet error", e);
|
||||
} finally {
|
||||
try {
|
||||
httpClient.close();
|
||||
} catch (Exception e) {
|
||||
log.error("httpClient.close() error", e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 封装post
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static Map<String, Object> doPost(String url, String body) {
|
||||
CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
httpPost.addHeader("Content-Type", "application/json;chartset=utf-8");
|
||||
httpPost.addHeader("Accept", "application/json");
|
||||
try {
|
||||
String token = WechatPayUtils.getToken("POST", new URL(url), body);
|
||||
httpPost.addHeader("Authorization", token);
|
||||
if (body == null) {
|
||||
throw new IllegalArgumentException("data参数不能为空");
|
||||
}
|
||||
StringEntity stringEntity = new StringEntity(body, "utf-8");
|
||||
httpPost.setEntity(stringEntity);
|
||||
|
||||
CloseableHttpResponse httpResponse = httpClient.execute(httpPost);
|
||||
HttpEntity httpEntity = httpResponse.getEntity();
|
||||
|
||||
if (httpResponse.getStatusLine().getStatusCode() == 200) {
|
||||
String jsonResult = EntityUtils.toString(httpEntity);
|
||||
return JSON.readValue(jsonResult, HashMap.class);
|
||||
} else {
|
||||
log.warn("微信支付错误信息" + EntityUtils.toString(httpEntity));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("HttpUtils.doPost error", e);
|
||||
} finally {
|
||||
try {
|
||||
httpClient.close();
|
||||
} catch (Exception e) {
|
||||
log.error("httpClient.close() error", e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 封装post
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static Map<String, Object> doPostWexin(String url, String body) {
|
||||
CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
httpPost.addHeader("Content-Type", "application/json;chartset=utf-8");
|
||||
httpPost.addHeader("Accept", "application/json");
|
||||
try {
|
||||
String token = WechatPayUtils.getToken("POST", new URL(url), body);
|
||||
httpPost.addHeader("Authorization", token);
|
||||
if (body == null) {
|
||||
throw new IllegalArgumentException("data参数不能为空");
|
||||
}
|
||||
StringEntity stringEntity = new StringEntity(body, "utf-8");
|
||||
httpPost.setEntity(stringEntity);
|
||||
CloseableHttpResponse httpResponse = httpClient.execute(httpPost);
|
||||
HttpEntity httpEntity = httpResponse.getEntity();
|
||||
if (httpResponse.getStatusLine().getStatusCode() == 200) {
|
||||
String jsonResult = EntityUtils.toString(httpEntity);
|
||||
return JSON.readValue(jsonResult, HashMap.class);
|
||||
} else {
|
||||
log.error("微信支付错误信息:{}", EntityUtils.toString(httpEntity));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("HttpUtils.doPostWexin error", e);
|
||||
} finally {
|
||||
try {
|
||||
httpClient.close();
|
||||
} catch (Exception e) {
|
||||
log.error("httpClient.close() error", e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
package com.jsowell.wxpay.utils;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.BinaryBitmap;
|
||||
import com.google.zxing.DecodeHintType;
|
||||
import com.google.zxing.EncodeHintType;
|
||||
import com.google.zxing.MultiFormatReader;
|
||||
import com.google.zxing.MultiFormatWriter;
|
||||
import com.google.zxing.Result;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.common.HybridBinarizer;
|
||||
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.geom.RoundRectangle2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Hashtable;
|
||||
|
||||
public class QRCodeUtil {
|
||||
private static final String CHARSET = "utf-8";
|
||||
private static final String FORMAT_NAME = "JPG";
|
||||
// 二维码尺寸
|
||||
private static final int QRCODE_SIZE = 300;
|
||||
// LOGO宽度
|
||||
private static final int WIDTH = 90;
|
||||
// LOGO高度
|
||||
private static final int HEIGHT = 90;
|
||||
|
||||
private static BufferedImage createImage(String content, String imgPath, boolean needCompress) throws Exception {
|
||||
Hashtable hints = new Hashtable();
|
||||
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
|
||||
hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
|
||||
hints.put(EncodeHintType.MARGIN, 1);
|
||||
BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE,
|
||||
hints);
|
||||
int width = bitMatrix.getWidth();
|
||||
int height = bitMatrix.getHeight();
|
||||
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
for (int x = 0; x < width; x++) {
|
||||
for (int y = 0; y < height; y++) {
|
||||
image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
|
||||
}
|
||||
}
|
||||
if (imgPath == null || "".equals(imgPath)) {
|
||||
return image;
|
||||
}
|
||||
// 插入图片
|
||||
QRCodeUtil.insertImage(image, imgPath, needCompress);
|
||||
return image;
|
||||
}
|
||||
|
||||
private static void insertImage(BufferedImage source, String imgPath, boolean needCompress) throws Exception {
|
||||
File file = new File(imgPath);
|
||||
if (!file.exists()) {
|
||||
System.err.println("" + imgPath + " 该文件不存在!");
|
||||
return;
|
||||
}
|
||||
Image src = ImageIO.read(new File(imgPath));
|
||||
int width = src.getWidth(null);
|
||||
int height = src.getHeight(null);
|
||||
if (needCompress) { // 压缩LOGO
|
||||
if (width > WIDTH) {
|
||||
width = WIDTH;
|
||||
}
|
||||
if (height > HEIGHT) {
|
||||
height = HEIGHT;
|
||||
}
|
||||
Image image = src.getScaledInstance(width, height, Image.SCALE_SMOOTH);
|
||||
BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics g = tag.getGraphics();
|
||||
g.drawImage(image, 0, 0, null); // 绘制缩小后的图
|
||||
g.dispose();
|
||||
src = image;
|
||||
}
|
||||
// 插入LOGO
|
||||
Graphics2D graph = source.createGraphics();
|
||||
int x = (QRCODE_SIZE - width) / 2;
|
||||
int y = (QRCODE_SIZE - height) / 2;
|
||||
graph.drawImage(src, x, y, width, height, null);
|
||||
Shape shape = new RoundRectangle2D.Float(x, y, width, width, 6, 6);
|
||||
graph.setStroke(new BasicStroke(3f));
|
||||
graph.draw(shape);
|
||||
graph.dispose();
|
||||
}
|
||||
|
||||
public static void encode(String content, String imgPath, String destPath, boolean needCompress) throws Exception {
|
||||
BufferedImage image = QRCodeUtil.createImage(content, imgPath, needCompress);
|
||||
mkdirs(destPath);
|
||||
ImageIO.write(image, FORMAT_NAME, new File(destPath));
|
||||
}
|
||||
|
||||
public static BufferedImage encode(String content, String imgPath, boolean needCompress) throws Exception {
|
||||
BufferedImage image = QRCodeUtil.createImage(content, imgPath, needCompress);
|
||||
return image;
|
||||
}
|
||||
|
||||
public static void mkdirs(String destPath) {
|
||||
File file = new File(destPath);
|
||||
// 当文件夹不存在时,mkdirs会自动创建多层目录,区别于mkdir.(mkdir如果父目录不存在则会抛出异常)
|
||||
if (!file.exists() && !file.isDirectory()) {
|
||||
file.mkdirs();
|
||||
}
|
||||
}
|
||||
|
||||
public static void encode(String content, String imgPath, String destPath) throws Exception {
|
||||
QRCodeUtil.encode(content, imgPath, destPath, false);
|
||||
}
|
||||
|
||||
|
||||
public static void encode(String content, String destPath) throws Exception {
|
||||
QRCodeUtil.encode(content, null, destPath, false);
|
||||
}
|
||||
|
||||
public static void encode(String content, String imgPath, OutputStream output, boolean needCompress)
|
||||
throws Exception {
|
||||
BufferedImage image = QRCodeUtil.createImage(content, imgPath, needCompress);
|
||||
ImageIO.write(image, FORMAT_NAME, output);
|
||||
}
|
||||
|
||||
public static void encode(String content, OutputStream output) throws Exception {
|
||||
QRCodeUtil.encode(content, null, output, false);
|
||||
}
|
||||
|
||||
public static String decode(File file) throws Exception {
|
||||
BufferedImage image;
|
||||
image = ImageIO.read(file);
|
||||
if (image == null) {
|
||||
return null;
|
||||
}
|
||||
BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(image);
|
||||
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
|
||||
Result result;
|
||||
Hashtable hints = new Hashtable();
|
||||
hints.put(DecodeHintType.CHARACTER_SET, CHARSET);
|
||||
result = new MultiFormatReader().decode(bitmap, hints);
|
||||
String resultStr = result.getText();
|
||||
return resultStr;
|
||||
}
|
||||
|
||||
public static String decode(String path) throws Exception {
|
||||
return QRCodeUtil.decode(new File(path));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
package com.jsowell.wxpay.utils;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.jsowell.wxpay.common.WeChatPayParameter;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.Signature;
|
||||
import java.security.SignatureException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
|
||||
/**
|
||||
* 参考官网 https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_0.shtml
|
||||
*/
|
||||
public class WechatPayUtils {
|
||||
/**
|
||||
* 获取私钥。
|
||||
*
|
||||
* @param filename 私钥文件路径 (required)
|
||||
* @return 私钥对象
|
||||
*/
|
||||
public static PrivateKey getPrivateKey(String filename) throws IOException {
|
||||
// System.out.println("filename:" + filename);
|
||||
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("无效的密钥格式");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成token 也就是生成签名
|
||||
*
|
||||
* @param method
|
||||
* @param url
|
||||
* @param body
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String getToken(String method, URL url, String body) throws Exception {
|
||||
String nonceStr = getNonceStr();
|
||||
long timestamp = System.currentTimeMillis() / 1000;
|
||||
String message = buildMessage(method, url, timestamp, nonceStr, body);
|
||||
String signature = sign(message.getBytes("utf-8"));
|
||||
|
||||
return "WECHATPAY2-SHA256-RSA2048 " + "mchid=\"" + WeChatPayParameter.mchId + "\","
|
||||
+ "nonce_str=\"" + nonceStr + "\","
|
||||
+ "timestamp=\"" + timestamp + "\","
|
||||
+ "serial_no=\"" + WeChatPayParameter.mchSerialNo + "\","
|
||||
+ "signature=\"" + signature + "\"";
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成token
|
||||
*
|
||||
* @param method
|
||||
* @param url
|
||||
* @param body
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static Map<String, Object> getTokenWeixin(String method, URL url, String body, String prepay_id) throws Exception {
|
||||
String nonceStr = getNonceStr();
|
||||
long timestamp = System.currentTimeMillis() / 1000;
|
||||
String message = buildMessage(method, url, timestamp, nonceStr, body);
|
||||
String signature = sign(message.getBytes("utf-8"));
|
||||
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("timeStamp", String.valueOf(timestamp));
|
||||
map.put("nonceStr", nonceStr);
|
||||
map.put("package", "prepay_id=" + prepay_id);
|
||||
map.put("signType", "RSA");
|
||||
map.put("paySign", signature);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 生成签名
|
||||
*
|
||||
* @param message
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String sign(byte[] message) throws Exception {
|
||||
Signature sign = Signature.getInstance("SHA256withRSA");
|
||||
sign.initSign(WeChatPayParameter.privateKey);
|
||||
sign.update(message);
|
||||
return Base64.getEncoder().encodeToString(sign.sign());
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成签名串
|
||||
*
|
||||
* @param method
|
||||
* @param url
|
||||
* @param timestamp
|
||||
* @param nonceStr
|
||||
* @param body
|
||||
* @return
|
||||
*/
|
||||
public static String buildMessage(String method, URL url, long timestamp, String nonceStr, String body) {
|
||||
String canonicalUrl = url.getPath();
|
||||
if (url.getQuery() != null) {
|
||||
canonicalUrl += "?" + url.getQuery();
|
||||
}
|
||||
return method + "\n"
|
||||
+ canonicalUrl + "\n"
|
||||
+ timestamp + "\n"
|
||||
+ nonceStr + "\n"
|
||||
+ body + "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成随机数
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String getNonceStr() {
|
||||
return UUID.randomUUID().toString()
|
||||
.replaceAll("-", "")
|
||||
.substring(0, 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取平台证书
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static Map<String, X509Certificate> refreshCertificate() throws Exception {
|
||||
Map<String, X509Certificate> certificateMap = new HashMap();
|
||||
// 1: 执行get请求
|
||||
JsonNode jsonNode = HttpUtils.doGet(WeChatPayParameter.certificatesUrl);
|
||||
// 2: 获取平台验证的相关参数信息
|
||||
JsonNode data = jsonNode.get("data");
|
||||
if (data != null) {
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
JsonNode encrypt_certificate = data.get(i).get("encrypt_certificate");
|
||||
//对关键信息进行解密
|
||||
AesUtil aesUtil = new AesUtil(WeChatPayParameter.v3Key.getBytes());
|
||||
String associated_data = encrypt_certificate.get("associated_data").toString().replaceAll("\"", "");
|
||||
String nonce = encrypt_certificate.get("nonce").toString().replaceAll("\"", "");
|
||||
String ciphertext = encrypt_certificate.get("ciphertext").toString().replaceAll("\"", "");
|
||||
//证书内容
|
||||
String certStr = aesUtil.decryptToString(associated_data.getBytes(), nonce.getBytes(), ciphertext);
|
||||
//证书内容转成证书对象
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X509");
|
||||
X509Certificate x509Cert = (X509Certificate) cf.generateCertificate(
|
||||
new ByteArrayInputStream(certStr.getBytes("utf-8"))
|
||||
);
|
||||
String serial_no = data.get(i).get("serial_no").toString().replaceAll("\"", "");
|
||||
certificateMap.put(serial_no, x509Cert);
|
||||
}
|
||||
}
|
||||
return certificateMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证签名
|
||||
*
|
||||
* @param certificate
|
||||
* @param message
|
||||
* @param signature
|
||||
* @return
|
||||
*/
|
||||
public static boolean verify(X509Certificate certificate, byte[] message, String signature) {
|
||||
try {
|
||||
Signature sign = Signature.getInstance("SHA256withRSA");
|
||||
sign.initVerify(certificate);
|
||||
sign.update(message);
|
||||
return sign.verify(Base64.getDecoder().decode(signature));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException("当前Java环境不支持SHA256withRSA", e);
|
||||
} catch (SignatureException e) {
|
||||
throw new RuntimeException("签名验证过程发生了错误", e);
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new RuntimeException("无效的证书", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 参考网站 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_4.shtml
|
||||
*
|
||||
* @param appId
|
||||
* @param prepay_id
|
||||
* @return
|
||||
* @throws IOException
|
||||
* @throws SignatureException
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws InvalidKeyException
|
||||
*/
|
||||
public static HashMap<String, Object> getTokenWeixin(String appId, String prepay_id) throws Exception {
|
||||
// 获取随机字符串
|
||||
String nonceStr = getNonceStr();
|
||||
// 获取微信小程序支付package
|
||||
String packagestr = "prepay_id=" + prepay_id;
|
||||
long timestamp = System.currentTimeMillis() / 1000;
|
||||
//签名,使用字段appId、timeStamp、nonceStr、package计算得出的签名值
|
||||
String message = buildMessageTwo(appId, timestamp, nonceStr, packagestr);
|
||||
//获取对应的签名
|
||||
String signature = sign(message.getBytes("utf-8"));
|
||||
// 组装返回
|
||||
HashMap<String, Object> map = new HashMap<>();
|
||||
map.put("timeStamp", String.valueOf(timestamp));
|
||||
map.put("nonceStr", nonceStr);
|
||||
map.put("package", packagestr);
|
||||
map.put("signType", "RSA");
|
||||
map.put("paySign", signature);
|
||||
return map;
|
||||
}
|
||||
|
||||
private static String buildMessageTwo(String appId, long timestamp, String nonceStr, String packag) {
|
||||
return appId + "\n"
|
||||
+ timestamp + "\n"
|
||||
+ nonceStr + "\n"
|
||||
+ packag + "\n";
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user