Merge branch 'feature/admin' into preview

# Conflicts:
#	mcwl-admin/src/main/resources/application.yml
feature/comment
Diyu0904 2025-01-06 17:45:30 +08:00
commit 1941c2617c
6 changed files with 236 additions and 2 deletions

View File

@ -16,6 +16,14 @@
</description> </description>
<dependencies> <dependencies>
<!-- 微信支付依赖 -->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.4.7</version>
</dependency>
<dependency> <dependency>
<groupId>com.alipay.sdk</groupId> <groupId>com.alipay.sdk</groupId>
<artifactId>alipay-easysdk</artifactId> <artifactId>alipay-easysdk</artifactId>

View File

@ -1,4 +1,4 @@
package com.mcwl.web.controller.pay; package com.mcwl.web.controller.pay.AliPay;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.alipay.easysdk.factory.Factory; import com.alipay.easysdk.factory.Factory;

View File

@ -1,4 +1,4 @@
package com.mcwl.web.controller.pay; package com.mcwl.web.controller.pay.AliPay;
import cn.hutool.extra.qrcode.QrCodeUtil; import cn.hutool.extra.qrcode.QrCodeUtil;
import com.alipay.easysdk.factory.Factory; import com.alipay.easysdk.factory.Factory;

View File

@ -0,0 +1,33 @@
package com.mcwl.web.controller.pay.WxPay.config;
/**
*
* @author DaiZibo
* @date 2025/1/6
* @apiNote
*/
public interface DirectConnection {
//支付成功后的回调地址
String NOTIFY_URL = "";
//商户号
String MCH_ID = "";
//商户证书序列号
String MCH_SERIAL_NO = "";
//V3密钥
String API_3KEY = "";
//小程序或者公众号的ApId
String APP_ID = "";
// 商户证书序列号对应的证书秘钥
String privateKey = "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDOsZnx5Nh4qK7O" +
"vzbDOQu5UMtSdoZWyqOC+gFNVAB7aPAzQwgN7OAUt7G8synPRdovQ/l116dZ0ZiX" +
"XQX3Le8/o5szRH6LxpqcpFMaZg2N/HOydyTMaHI0wnZIc9BXR8aaXl7uVQnydF40" +
"FoWicge6vTCXOyjirTpS2PGKy9+hu0vx7GbX1NUDl2hNXkH54pdWn5eof1fnbh/V" +
"45q/OS7d9qnpYfs1ff+0nA==";
}

View File

@ -0,0 +1,49 @@
package com.mcwl.web.controller.pay.WxPay.util;
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;
/**
*
*
* @author DaiZibo
* @date 2025/1/6
* @apiNote
*/
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);
}
}
}

View File

@ -0,0 +1,144 @@
package com.mcwl.web.controller.pay.WxPay.util;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.mcwl.web.controller.pay.WxPay.config.DirectConnection;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.exception.HttpCodeException;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Base64;
/**
*
*
* @author DaiZibo
* @date 2025/1/6
* @apiNote
*/
public class PayUtil {
private CloseableHttpClient httpClient;
private CertificatesManager certificatesManager;
private Verifier verifier;
private PrivateKey merchantPrivateKey;
{
try {
merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(DirectConnection.privateKey.getBytes("utf-8")));
// 获取证书管理器实例
certificatesManager = CertificatesManager.getInstance();
// 向证书管理器增加需要自动更新平台证书的商户信息
certificatesManager.putMerchant(DirectConnection.MCH_ID, new WechatPay2Credentials(DirectConnection.MCH_ID,
new PrivateKeySigner(DirectConnection.MCH_SERIAL_NO, merchantPrivateKey)),
DirectConnection.API_3KEY.getBytes(StandardCharsets.UTF_8));
// 从证书管理器中获取verifier
verifier = certificatesManager.getVerifier(DirectConnection.MCH_ID);
httpClient = WechatPayHttpClientBuilder.create()
.withMerchant(DirectConnection.MCH_ID, DirectConnection.MCH_SERIAL_NO, merchantPrivateKey)
.withValidator(new WechatPay2Validator(certificatesManager.getVerifier(DirectConnection.MCH_ID)))
.build();
} catch (IOException | GeneralSecurityException | HttpCodeException | NotFoundException e) {
throw new RuntimeException(e);
}
}
/**
* prepay_id
*/
public String requestwxChatPay(String orderSn, int total, String description, String openid) throws Exception {
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectMapper objectMapper = new ObjectMapper();
//组合请求参数JSON格式
ObjectNode rootNode = objectMapper.createObjectNode();
rootNode.put("mchid", DirectConnection.MCH_ID)
.put("appid", DirectConnection.APP_ID)
.put("notify_url", DirectConnection.NOTIFY_URL + "returnNotify")
.put("description", description)
.put("out_trade_no", orderSn);
rootNode.putObject("amount")
// total金额以分为单位假如是10块钱那就要写 1000
.put("total", total)
.put("currency", "CNY");
rootNode.putObject("payer")
// openid用户在该小程序或者公众号下的openid
.put("openid", openid);
try {
objectMapper.writeValue(bos, rootNode);
httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
//获取预支付ID
CloseableHttpResponse response = httpClient.execute(httpPost);
String bodyAsString = EntityUtils.toString(response.getEntity());
//微信成功响应
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) {
//时间戳
String timestamp = System.currentTimeMillis() / 1000 + "";
//随机字符串
String nonce = RandomUtil.randomString(32);
StringBuilder builder = new StringBuilder();
// Appid
builder.append(DirectConnection.APP_ID).append("\n");
// 时间戳
builder.append(timestamp).append("\n");
// 随机字符串
builder.append(nonce).append("\n");
JsonNode jsonNode = objectMapper.readTree(bodyAsString);
// 预支付会话ID
builder.append("prepay_id=").append(jsonNode.get("prepay_id").textValue()).append("\n");
//获取签名
String sign = this.sign(builder.toString().getBytes("utf-8"), merchantPrivateKey);
JSONObject jsonMap = new JSONObject();
jsonMap.put("noncestr", nonce);
jsonMap.put("timestamp", timestamp);
jsonMap.put("prepayid", jsonNode.get("prepay_id").textValue());
jsonMap.put("sign", sign);
jsonMap.put("appid", DirectConnection.APP_ID);
jsonMap.put("partnerid", DirectConnection.MCH_ID);
return jsonMap.toJSONString();//响应签名数据前端拿着响应数据调起微信SDK
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
*
*/
private String sign(byte[] message, PrivateKey yourPrivateKey) {
try {
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(yourPrivateKey);
sign.update(message);
return Base64.getEncoder().encodeToString(sign.sign());
} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
e.printStackTrace();
}
return "";
}
}