微信支付-上传图片
This commit is contained in:
@@ -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>
|
||||
|
||||
|
||||
30
cash-sdk/aggregation-pay/pom.xml
Normal file
30
cash-sdk/aggregation-pay/pom.xml
Normal 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>
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -13,6 +13,7 @@
|
||||
<description>第三方内容</description>
|
||||
<modules>
|
||||
<module>czg-pay</module>
|
||||
<module>aggregation-pay</module>
|
||||
</modules>
|
||||
<artifactId>cash-sdk</artifactId>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user