微信支付-上传图片

This commit is contained in:
gong
2025-12-26 11:38:48 +08:00
parent 6bddf05c36
commit 303db8bc49
8 changed files with 349 additions and 0 deletions

View File

@@ -42,6 +42,7 @@
<pinyin.version>2.5.1</pinyin.version>
<IJPay.version>2.9.10</IJPay.version>
<netty.version>4.1.128.Final</netty.version>
<wechatpay.version>0.2.17</wechatpay.version>
</properties>
<dependencyManagement>
@@ -268,6 +269,12 @@
<artifactId>netty-codec-mqtt</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>${wechatpay.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.czg</groupId>
<artifactId>cash-sdk</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>aggregation-pay</artifactId>
<packaging>jar</packaging>
<name>聚合支付</name>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,47 @@
package com.czg.wechat;
import com.czg.wechat.dto.config.WechatPayConfigDto;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAPublicKeyConfig;
import com.wechat.pay.java.service.file.FileUploadService;
/**
* 微信支付 配置
*
* @author yjjie
* @date 2025/12/26 09:33
*/
public class WechatConfig {
/**
* 获取微信支付配置
* @param dto 微信支付配置
* @return 微信支付配置
*/
public static Config getConfig(WechatPayConfigDto dto) {
return new RSAPublicKeyConfig.Builder()
.merchantId(dto.getMerchantId())
.privateKey(dto.getPrivateKey())
.publicKey(dto.getPublicKey())
.publicKeyId(dto.getPublicKeyId())
.merchantSerialNumber(dto.getSerialNumber())
.apiV3Key(dto.getApiV3Key())
.build();
}
public static FileUploadService getFileUploadService(Config config) {
return new FileUploadService.Builder()
.config(config)
.build();
}
/**
* 获取文件上传服务
* @param dto 微信支付配置
* @return 文件上传服务
*/
public static FileUploadService getFileUploadService(WechatPayConfigDto dto) {
return getFileUploadService(getConfig(dto));
}
}

View File

@@ -0,0 +1,47 @@
package com.czg.wechat;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* 微信支付加解密
* @author yjjie
* @date 2025/12/26 10:58
*/
public class WechatEncrypt {
public static String getFileBytesSha256(byte[] input) throws NoSuchAlgorithmException {
return bytesToHex(calculateSha256(input));
}
/**
* 核心方法:计算字节数组的 SHA-256 哈希值
* @param input 待计算的字节数组
* @return SHA-256 哈希值32 字节的字节数组)
*/
private static byte[] calculateSha256(byte[] input) throws NoSuchAlgorithmException {
// 获取 SHA-256 消息摘要实例Java 21 完全兼容)
MessageDigest digest = MessageDigest.getInstance("SHA-256");
// 计算哈希值
return digest.digest(input);
}
/**
* 辅助方法将字节数组转换为十六进制字符串SHA-256 结果常用格式)
* @param bytes 待转换的字节数组
* @return 十六进制字符串SHA-256 对应 64 位字符串)
*/
private static String bytesToHex(byte[] bytes) {
StringBuilder hexString = new StringBuilder();
// Java 21 可使用 var 简化声明
for (var b : bytes) {
// 将字节转换为两位十六进制数,不足补 0
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
}

View File

@@ -0,0 +1,157 @@
package com.czg.wechat;
import com.alibaba.fastjson2.JSONObject;
import com.czg.wechat.dto.config.WechatPayConfigDto;
import com.wechat.pay.java.service.file.FileUploadService;
import com.wechat.pay.java.service.file.model.FileUploadResponse;
import lombok.extern.slf4j.Slf4j;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
/**
* 微信支付进件 管理
*
* @author yjjie
* @date 2025/12/26 10:57
*/
@Slf4j
public class WechatEntryManager {
public static String uploadImage(WechatPayConfigDto configDto, String url) {
// 校验入参
if (configDto == null || url == null || url.trim().isEmpty()) {
log.error("上传图片失败配置或URL参数为空");
return "";
}
FileUploadService service = WechatConfig.getFileUploadService(configDto);
try {
// 获取图片字节数组
byte[] bytes = downloadImage(url);
if (bytes.length == 0) {
log.error("下载的图片内容为空URL{}", url);
return "";
}
// 2. 计算SHA256
String sha256Hex = WechatEncrypt.getFileBytesSha256(bytes);
// 3. 构造元数据从URL提取文件名或自定义
JSONObject meta = new JSONObject();
meta.put("sha256", sha256Hex);
// 从URL提取文件名若提取失败则使用默认名
String fileName = extractFileNameFromUrl(url);
meta.put("filename", fileName);
// 4. 上传图片到微信接口
FileUploadResponse uploadResponse = service.uploadImage(
configDto.getDomain() + "/v3/merchant/media/upload",
meta.toJSONString(),
fileName,
bytes
);
return uploadResponse.getMediaId();
} catch (Exception e) {
log.error("微信上传图片报错URL{},错误信息:{}", url, e.getMessage(), e);
}
return "";
}
public static byte[] downloadImage(String url) throws Exception {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.GET()
.build();
HttpResponse<byte[]> response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
if (response.statusCode() != 200) {
throw new RuntimeException("Failed to download image, status code: " + response.statusCode());
}
return response.body();
}
/**
* 从URL中提取文件名
*
* @param url 图片URL
* @return 提取的文件名,失败则返回默认名
*/
private static String extractFileNameFromUrl(String url) {
try {
if (url.contains("/")) {
String fileName = url.substring(url.lastIndexOf("/") + 1);
// 如果文件名包含参数,截取?之前的部分
if (fileName.contains("?")) {
fileName = fileName.substring(0, fileName.indexOf("?"));
}
// 如果提取的文件名有效,直接返回
if (fileName.contains(".")) {
return fileName;
}
}
} catch (Exception e) {
log.warn("提取文件名失败,使用默认文件名", e);
}
// 默认文件名
return "upload_" + System.currentTimeMillis() + ".png";
}
public static void main(String[] args) {
WechatPayConfigDto dto = new WechatPayConfigDto()
.setMerchantId("1643779408")
.setApiV3Key("a92baac5eb7a36ed8ec198113e769a03")
.setSerialNumber("4DE9BAC2EA584C3F274F694C9753CA814C4E9BF4")
.setPublicKey("""
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtbeWXEEjBaYtw2OyM+SC
aCEMbryRXi4duKxx3vYG4rUVix+d5/Jz7Khev4Upml9zC+Xxvv/G9bHWAUyolzqD
wefahGurIxr43r4GJVnQ4i5G6BbBVw5d4Vuz0y/9Zd14zmc/EmBpT0Ml26H7tOZl
n1LSbQ4xNFkrRKrNEcExBLxkCd+K5K2TREZznywIi0izbHImvuzM8IneuR51FiqK
pdFnAjTwb126EIj6ECkL6KLCl8RWqpfiX8SFiolSQLs1/w79O0sIUk96X2zWpnoW
rTDFatPif/VEKl3y2dTlxxDxoZtVi48yTDW00OMzVl5D67oX3FVK0KsjHJSCfGlZ
6wIDAQAB
-----END PUBLIC KEY-----""")
.setPrivateKey("""
-----BEGIN PRIVATE KEY-----
MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDqnAZhTxT572fo
6wvSr8Rt0vRXg+EFKC6UvUiNE24oocQU5fjedX9KL/+fmoqay/xqIxvxvCNFTs4J
zlMqavSl6bMWCpjvf/Ry82JmN1v2kJEO4lgP8BsEiqlUpObCH8BMAVUOn1j+9q4Q
uZJJcbtRvec2fNweDM8EJp4B7RlUdDbHm86lfcDVp8iini7VjDp6D7aHT+C8A8OT
ugxQIquDec778wVd9r2Sv3+t6rAzFs+n+Zu++2xtFEPhO5N0wjrLHaukl+9crU+1
lktjDzcPd07SwGZ+A+3BTmW3UCramI3506e/3MWBECB7ge+gM4URAV0nJJCLH/Im
WgEvPMr5AgMBAAECggEBAKv+wraoMWqiVv1tA/froAgbtcJLDranJK8qrXuvmPz0
yzm+91qvrSgIVFEADUk67swo/R2Vng37nhWWS2Y3jy/rSr2H+2Lp3Z5ATA0/3I3A
onfU/FaC4mvL9CP32KzMdj/CYkccDzSsSCQ+x75MQNXGcTGDDCSDo2kZnpEu73j3
aqvO1jbqTGWigRfjOIaIhStjQIT8nEm/3mJ4f5dM9M6FMz33mhax8EahSgvdahYB
t45iaGOWw81OJhmry47EvpwjXBl7jtO2HX3LiLbq5Ebcwu1zqDz5NM7ttnnGAqWC
6y7JN5Vt4wPYrCydiUDe7dj0looffr2yw6MkNfYjLGECgYEA+FAvbEInQEi9YguS
CQtLHngqvYeai66tvyykog9o38KHnPGx2Myf+rn/Hcp7KNRfjd5G34CCNg7KLnrx
YIYh6+2bY3jirzdYUxuNKGbvM4gky/6M/P9zHF/uALKOE02yArdqemf4UxUvrYCc
JdXsAMqvbpdvW1aGgNRB32YCkG0CgYEA8d89vawsCjNCEUh0VWTMoBLFoex3qBMZ
rfzYQeBo6bDCRlAlUVzl+ouBUxSYxP/U8rzeNaRzGUwRLmlGMjyIr58FBlHsg2cR
DlsX3HVCUjpS6sgPXOqakdiLfhMcHZAspislSyVfeS3TbUWiA45+5PuNUq+EZYwl
ESsB1+yfRT0CgYEA0Ct49ksnWM8iZbXJgeeD3FFlk2rBd2TDqEem5W4Bv8T3p+0/
6b7yR2HyrGj5gys3yFmWFP1JLESN3xWWkhMhEQcrg+LuN3Iwi8vHNR3GXu892f7W
96q4OAt8Hf2S+j/igkB99YyANDbIt63gOh/zMF67X/14j5wkOpC3gK+maqkCgYEA
s7nIrPoUt3ejLiiCmTmPe5q3VDzcJP4cZNau8zSHgK6hjZHcSPsYwPWMoWl6o1fe
qoiBLacHB9MoKS58xLOKdcVZ/Ho/ntylJd+2eVCAeY1xM5h5IfgJ5znbXVFh4O3S
357L1Wzt5qOQqW/GlZH65LevKbPWU4axvHISqpnfN5kCgYEAqiqLuAPu84VSsqsE
GFh25t+1f0JY1sNfilE3/t9AdQeeCFv/5z9KB1kMt3a5ZFeVonsFIvCyaOJjhSkj
4HCB94vWS7NuN5G9r874lMaPbZYQGwrcVaf265tN7cYYr3gUx1qB6+u+fh/kcft1
BBmTzhb0zp5k8ngwAkA1g/LK204=
-----END PRIVATE KEY-----""")
.setPublicKeyId("PUB_KEY_ID_0116437794082025111000382377001000")
.setDomain("https://api.mch.weixin.qq.com");
String string = uploadImage(dto, "https://czg-qr-order.oss-cn-beijing.aliyuncs.com/indexs/shuangbackground.png");
log.info("图片上传成功:{}", string);
}
}

View File

@@ -0,0 +1,11 @@
package com.czg.wechat;
/**
* @author yjjie
* @date 2025/12/26 09:15
*/
public class WechatPayManager {
public static void main(String[] args) {
}
}

View File

@@ -0,0 +1,49 @@
package com.czg.wechat.dto.config;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* @author yjjie
* @date 2025/12/26 09:21
*/
@Data
@Accessors(chain = true)
public class WechatPayConfigDto {
/**
* 商户号
*/
private String merchantId;
/**
* Api V3密钥
*/
private String apiV3Key;
/**
* 商户证书序列号
*/
private String serialNumber;
/**
* 商户私钥
*/
private String privateKey;
/**
* 商户公钥
*/
private String publicKey;
/**
* 商户公钥 ID
*/
private String publicKeyId;
/**
* 微信支付域名
* <a href="https://api.mch.weixin.qq.com"></a>
*/
private String domain;
}

View File

@@ -13,6 +13,7 @@
<description>第三方内容</description>
<modules>
<module>czg-pay</module>
<module>aggregation-pay</module>
</modules>
<artifactId>cash-sdk</artifactId>