微信小程序集成b2b交易代码
vue:
// 微信支付,将参数传后端获取数据掉起微信支付
wxPayOrder(orderData).then(response => {
let params = response.data
wx.requestCommonPayment({
signData: params.signData,
mode: params.mode,
paySig: params.paySig,
signature: params.signature,
success(res) {
console.log('支付成功', res);
this.paySuccess()
},
fail(res) {
console.log('支付失败,res', res);
uni.showToast({
title: '支付失败,如果您已支付,请勿反复支付',
icon: 'none'
})
}
});
java
//微信支付下单统一参数封装
Map<String, Object> payParams = Maps.newHashMapWithExpectedSize(CommonConstants.INTEGER_TYPE.TYPE_EIGHT);
payParams.put("mchid", wxConfig.getB2bMchId());
payParams.put("description", request.getOrderName());
payParams.put("out_trade_no", DateUtil.getDateline() + "b2b" + request.getOutTradeNo());
Map<String, Object> outTradeNoParams = Maps.newHashMapWithExpectedSize(CommonConstants.INTEGER_TYPE.TYPE_ONE);
outTradeNoParams.put("out_trade_no", request.getOutTradeNo());
payParams.put("attach", JsonUtils.toJsonString(outTradeNoParams));
Map<String, Object> amountMap = Maps.newHashMapWithExpectedSize(CommonConstants.INTEGER_TYPE.TYPE_ONE);
amountMap.put("order_amount", MoneyUtils.yuanToFen(request.getFee()));
payParams.put("amount", amountMap);
payParams.put("env", 0);
String uri = "requestCommonPayment";
String url2 = uri + "&" + JSON.toJSONString(payParams);
Map<String, Object> connect = remoteConnectService.queryConnect(
LoginHelper.getUserId() + "", SourceEnum.WECHAT_MP_OPEN_ID.name()
);
if (connect == null) {
log.error("未获取到用户{}:{}登录信息", LoginHelper.getUserId() + "", SourceEnum.WECHAT_MP_OPEN_ID.name());
return null;
}
String paramsStr = JSON.toJSONString(payParams);
log.info("统一下单API,支付参数:{}", paramsStr);
PayResponse payResponse = new PayResponse();
payResponse.setSignData(paramsStr);
payResponse.setMode(wxConfig.getB2bMode());
payResponse.setPaySig(HmacSha256Util.hmacSha256Hex(wxConfig.getB2bAppKey(), url2));
payResponse.setSignature(HmacSha256Util.hmacSha256Hex(connect.get("sessionKey").toString(), paramsStr));
log.info("统一下单最后返回支付参数:{}", JSON.toJSONString(payResponse));
编码工具类:
package com.honghan.common.core.utils;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
public class HmacSha256Util {
public static String hmacSha256Hex(String key, String data) {
try {
Mac sha256Hmac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
sha256Hmac.init(secretKey);
byte[] hashBytes = sha256Hmac.doFinal(data.getBytes(StandardCharsets.UTF_8));
return bytesToHex(hashBytes);
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
throw new RuntimeException("Error while generating HMAC-SHA256", e);
}
}
private static String bytesToHex(byte[] bytes) {
StringBuilder hexString = new StringBuilder();
for (byte b : bytes) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1)
hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
}
// Example usage:
public static void main(String[] args) {
String sessionKey = "yourSessionKey";
String signData = "yourSignData";
String signature = hmacSha256Hex(sessionKey, signData);
System.out.println("Signature: " + signature);
}
}
b2b支付回调通知已经请求加解密方法:
package com.honghan.trade.controller.pay;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.net.URLDecoder;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.honghan.common.core.constant.CommonConstants;
import com.honghan.common.core.constant.Constants;
import com.honghan.common.core.domain.ResultResponse;
import com.honghan.common.core.enums.OrderStatus;
import com.honghan.common.core.exception.ServiceException;
import com.honghan.common.core.utils.HttpUtils;
import com.honghan.common.core.utils.TimeUtils;
import com.honghan.common.core.utils.ajax.AjaxUtils;
import com.honghan.common.core.utils.copy.CopyUtils;
import com.honghan.mall.api.RemoteProductSkuStockService;
import com.honghan.site.api.RemoteCompanyService;
import com.honghan.store.api.RemoteStoreOrderService;
import com.honghan.store.api.RemoteStoreStockService;
import com.honghan.system.api.RemoteSysConfigService;
import com.honghan.trade.api.call.PaySuccessCallback;
import com.honghan.trade.api.call.ResultWechatBack;
import com.honghan.trade.api.domain.PaymentSuccessParamsVO;
import com.honghan.trade.api.domain.TradeOrder;
import com.honghan.trade.api.domain.pay.PayResponse;
import com.honghan.trade.config.pay.config.WxConfig;
import com.honghan.trade.config.pay.sign.WechatPayValidator;
import com.honghan.trade.domain.vo.TradeOrderVO;
import com.honghan.trade.payment.RefundLogService;
import com.honghan.trade.payment.entity.RefundLog;
import com.honghan.trade.payment.entity.enums.PaymentMethodEnum;
import com.honghan.trade.payment.param.PaymentSuccessParams;
import com.honghan.trade.service.*;
import com.honghan.trade.service.pay.AliPayService;
import com.honghan.trade.service.pay.call.OrderPayCallback;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.util.crypto.PKCS7Encoder;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
/**
* 订单回调
*/
@Slf4j
@RestController
@RequestMapping("/order/notify")
public class TradeOrderNotifyController {
private final ReentrantLock lock = new ReentrantLock();
@Resource
private TradeOrderService tradeOrderService;
@Resource
private TradeOrderProductService tradeOrderProductService;
@Resource
private TradeMessageService tradeMessageService;
@Resource
private IBillOrderService billOrderService;
@Resource
private ISubOrderService subOrderService;
@Resource
private PaySuccessCallback paySuccessCallback;
@Resource
private AliPayService aliPayService;
@Resource
private TradeMatchService tradeMatchService;
@Resource
private TradeMatchDetailService tradeMatchDetailService;
@Resource
private WxConfig wxConfig;
@Resource
private Verifier verifier;
@Resource
private IPaymentOrderService paymentOrderService;
@Resource
private OrderPayCallback orderPayCallback;
@DubboReference
private RemoteCompanyService companyService;
@DubboReference
private RemoteStoreStockService remoteStoreStockService;
@DubboReference
private RemoteCompanyService remoteCompanyService;
@DubboReference
private RemoteSysConfigService remoteSysConfigService;
@DubboReference
private RemoteStoreOrderService remoteStoreOrderService;
@DubboReference
private RemoteProductSkuStockService remoteProductSkuStockService;
/**
* 退款日志
*/
@Autowired
private RefundLogService refundLogService;
/**
* 支付宝回调接口
*/
@PostMapping("/alipayNotify")
public void aliPayNotify(HttpServletRequest request, HttpServletResponse response) {
ResultResponse<PayResponse> result = aliPayService.asyncNotify(request, paySuccessCallback);
if (result.getCode() == Constants.SUCCESS) {
AjaxUtils.renderText(response, CommonConstants.SUCCESS);
} else {
AjaxUtils.renderText(response, CommonConstants.FAIL);
}
}
/**
* 微信回调接口
*/
@PostMapping("/wxPayNotify")
public Map<String, String> wxPayNotify(HttpServletRequest request, HttpServletResponse response) {
String orderNo = "";
try {
//处理通知参数
String body = HttpUtils.readData(request);
log.info("微信支付回调参数:{}", body);
//转换为Map
Map<String, Object> bodyMap = JSONObject.parseObject(body, new TypeReference<Map<String, Object>>() {
});
//微信的通知ID(通知的唯一ID)
String notifyId = bodyMap.get("id").toString();
//构造验证签名信息
WechatPayValidator wxCheckSign = new WechatPayValidator(verifier, notifyId, body);
if (!wxCheckSign.validate(request)) {
return ResultWechatBack.falseMsg(response);
}
if (lock.tryLock(6, TimeUnit.SECONDS)) {
//解密resource中的通知数据
String resource = bodyMap.get("resource").toString();
Map<String, Object> resourceMap = WechatPayValidator.decryptFromResource(resource, wxConfig.getApiV3Key(), 1);
//解密后返回相关订单详情
log.info("微信支付回调解密后返回相关订单详情---{}", resourceMap);
orderNo = resourceMap.get("out_trade_no").toString();
String transactionId = resourceMap.get("transaction_id").toString();
String tradeState = resourceMap.get("trade_state").toString();
if (StringUtils.equals(WxConfig.SUCCESS, tradeState)) {
String _payTime = resourceMap.get("success_time").toString();
DateTime dateTime = new DateTime(_payTime);
//转换成Date对象
Date timeDate = dateTime.toJdkDate();
String payParamStr = resourceMap.get("attach").toString();
String payParamJson = URLDecoder.decode(payParamStr, StandardCharsets.UTF_8);
//PayParam payParam = JSONUtil.toBean(payParamJson, PayParam.class);
String tradeNo_ = resourceMap.get("transaction_id").toString();
Double totalAmount = null;
PaymentSuccessParams paymentSuccessParams = new PaymentSuccessParams(
PaymentMethodEnum.WECHAT.name(),
tradeNo_,
totalAmount,
null
);
paySuccessCallback.doWhilePaySuccess(CopyUtils.copy(paymentSuccessParams, PaymentSuccessParamsVO.class), orderNo, transactionId, timeDate);
// if(orderNo.startsWith("Y")) {
// //购物车下的订单
// List<SubOrder> subOrder = subOrderService.getOrderNos(orderNo);
// List<String> orderNoList = subOrder.stream().map(SubOrder::getCarOrderNo).collect(Collectors.toList());
// List<TradeOrder> tradeOrders = tradeOrderService.getByPayOrder(orderNoList);
// for (TradeOrder tradeOrder : tradeOrders) {
// if (tradeOrder.getType().equals(OrderType.MALL.getValue())) {
// Asserts.isTrue(tradeOrder.getStatus().equals(OrderStatus.CONFIRMED.getValue()), "订单状态不是待付款状态!");
// tradeOrder.setStatus(OrderStatus.PAYED.getValue());
// } else if (tradeOrder.getType().equals(OrderType.MALL_MATCH.getValue())) {
// Asserts.isTrue(tradeOrder.getStatus().equals(OrderStatus.CONFIRMED.getValue()), "订单状态不是待付款状态!");
// tradeOrder.setStatus(OrderStatus.COUNT.getValue());
// }
// tradeOrder.setPayTime(TimeUtils.getNowDate());
// orderPayCallback.paySuccess(orderNo, transactionId, tradeOrder);
// }
// }else if (orderNo.startsWith("W") || orderNo.startsWith("F")) {
// PaymentOrder payMentOrder = paymentOrderService.queryOrderNo(orderNo);
// Asserts.isTrue(payMentOrder.getStatus().equals(0), "付款单状态不是待付款状态!");
// TradeOrder tradeOrder = tradeOrderService.getById(payMentOrder.getOriginalOrderId());
// payMentOrder.setPayType(tradeOrder.getPayType());
// payMentOrder.setStatus(PaymentStatusEnum.FINISH.getValue());
// payMentOrder.setPayTime(TimeUtils.getNowDate());
// payMentOrder.setPayNo(transactionId);
// boolean updateById = paymentOrderService.updateById(payMentOrder);
// if (updateById) {
// Integer total = payMentOrder.getTotal();
// List<PaymentOrder> paymentOrders = paymentOrderService.queryOriginalOrderId(payMentOrder.getOriginalOrderId(), null);
// long payAmountNum = paymentOrders.stream().filter(paymentOrder-> paymentOrder.getStatus() == 1).count();
// //付款方式 1 先款后货 2 先付订金 后付尾款 3 先货后款
// if(total == 1 && payMentOrder.getPaymentType() == 3) {
// //付完钱直接完成
// tradeOrder.setStatus(OrderStatus.COUNT.getValue());
// }else if(total == 2){
// if(payMentOrder.getRemark().equals(PaymentPayTypeEnum.FIRST_PAYMENT.getValue())){
// tradeOrder.setIfPickUp(1);
// tradeOrder.setStatus(OrderStatus.PART_PAYED.getValue());
// } else {
// tradeOrder.setStatus(OrderStatus.COUNT.getValue());
// }
// }else {
// tradeOrder.setIfPickUp(1);
// tradeOrder.setStatus(OrderStatus.COUNT.getValue());
// }
// boolean paymentUpdateById = tradeOrderService.updateById(tradeOrder);
// if(paymentUpdateById){
// tradeOrderProductService.batchUpdateProductSaleNum(tradeOrder.getId());
// }
// }
// }else {
// LambdaQueryWrapper<TradeOrder> queryWrapper = Wrappers.lambdaQuery();
// queryWrapper.eq(TradeOrder::getIdentifier, orderNo);
// queryWrapper.last("LIMIT 1");
// TradeOrder tradeOrder = tradeOrderService.getOne(queryWrapper);
// Asserts.isTrue(tradeOrder.getStatus().equals(OrderStatus.CONFIRMED.getValue()), "订单状态不是待付款状态!");
// tradeOrder.setIfPickUp(1);
// tradeOrder.setStatus(OrderStatus.PAYED.getValue());
// orderPayCallback.paySuccess(orderNo, transactionId, tradeOrder);
// }
//成功应答 响应微信
log.info("微信支付回调成功----商户订单号:{}", orderNo);
return ResultWechatBack.trueMsg(response);
} else {
log.warn("微信支付回调状态并不是SUCCESS:{}", resourceMap);
return ResultWechatBack.falseMsg(response);
}
}
} catch (Exception e) {
log.error("微信支付回调处理失败, orderNo = " + orderNo, e);
return ResultWechatBack.falseMsg(response);
} finally {
//要主动释放锁
lock.unlock();
}
return ResultWechatBack.trueMsg(response);
}
private static final String TOKEN = "xxx"; // Token
private static final String ENCODING_AES_KEY = "xxx"; // EncodingAESKey
private static final String APPID = "xxx"; // 小程序Appid
/**
* 微信消息推送
*/
@RequestMapping("/callback")
public void callback(HttpServletRequest request, HttpServletResponse httpServletResponse) throws IOException {
// 从 URL 参数中获取
String signature = request.getParameter("signature");
String timestamp = request.getParameter("timestamp");
String nonce = request.getParameter("nonce");
String echostr = request.getParameter("echostr");
if (StringUtils.isNotBlank(echostr) && checkSignature(signature, timestamp, nonce)) {
// 返回 echostr 表示验证通过
httpServletResponse.getWriter().write(echostr);
return null;
}
String msgSignature = request.getParameter("msg_signature");
String encryptType = request.getParameter("encrypt_type"); // "aes" 是加密类型
// 打印请求的 URL 参数
System.out.println("signature: " + signature);
System.out.println("timestamp: " + timestamp);
System.out.println("nonce: " + nonce);
System.out.println("msg_signature: " + msgSignature);
System.out.println("encrypt_type: " + encryptType);
// 从请求体中获取 JSON 数据(Encrypt 字段)
StringBuilder body = new StringBuilder();
try (BufferedReader reader = request.getReader()) {
String line;
while ((line = reader.readLine()) != null) {
body.append(line);
}
}
String jsonBody = body.toString();
System.out.println("Request Body (JSON): " + jsonBody);
// 解析 JSON 数据中的 Encrypt 字段
String encrypt = parseEncryptField(jsonBody);
System.out.println("Encrypt: " + encrypt);
// 校验 msg_signature 签名是否正确
if (!checkMsgSignature(msgSignature, timestamp, nonce, encrypt)) {
return ResponseEntity.status(400).body("Signature verification failed!");
}
// 解密消息体中的 Encrypt 内容
String decryptedMessage = decryptMessage(encrypt); // 解密消息
System.out.println("Decrypted message: " + decryptedMessage);
cn.hutool.json.JSONObject entries = JSONUtil.parseObj(decryptedMessage);
if (!entries.getStr("appid").equals(APPID)) {
throw new RuntimeException("AppId 不匹配,非法请求");
}
entries = entries.getJSONObject("msg");
String event = entries.getStr("Event");
if (event.equalsIgnoreCase("retail_pay_notify")) {
String orderNo = "";
String transactionId = entries.getStr("order_id").toString();
String tradeState = entries.getStr("pay_status").toString();
if (StringUtils.equalsIgnoreCase("ORDER_PAY_SUCC", tradeState)) {
String _payTime = entries.getStr("pay_time").toString();
DateTime dateTime = new DateTime(_payTime);
//转换成Date对象
Date timeDate = dateTime.toJdkDate();
String payParamStr = entries.getStr("attach").toString();
orderNo = JSONUtil.parseObj(payParamStr).getStr("out_trade_no");
if (orderNo.contains("b2b")) {
orderNo = orderNo.split("b2b")[1];
}
String tradeNo_ = entries.getStr("order_id").toString();
Double totalAmount = null;
PaymentSuccessParams paymentSuccessParams = new PaymentSuccessParams(
PaymentMethodEnum.WECHAT.name(),
tradeNo_,
totalAmount,
null
);
try {
paySuccessCallback.doWhilePaySuccess(CopyUtils.copy(paymentSuccessParams, PaymentSuccessParamsVO.class), orderNo, transactionId, timeDate);
} catch (Exception e) {
log.error("微信支付回调处理失败, orderNo = " + orderNo, e);
}
//成功应答 响应微信
log.info("微信支付回调成功----商户订单号:{}", orderNo);
} else {
log.warn("微信支付回调状态并不是SUCCESS:{}", tradeState);
}
} else if (event.equalsIgnoreCase("retail_refund_notify")) {
String orderNo = "";
String transactionId = entries.getStr("order_id").toString();
String refundId = entries.getStr("refund_id").toString();
orderNo = entries.getStr("out_trade_no");
if (orderNo.contains("b2b")) {
orderNo = orderNo.split("b2b")[1];
}
String outRefundNo = entries.getStr("out_refund_no").toString();//商户退款单号
String refundStatus = entries.getStr("refund_status").toString();//微信退款状态
if (StringUtils.isNotBlank(refundStatus) && StringUtils.equalsIgnoreCase("REFUND_SUCC", refundStatus)) {
TradeOrderVO tradeOrderVO = tradeOrderService.selectVoByOutRefundNo(outRefundNo);
if (Objects.isNull(tradeOrderVO)) {
log.warn("微信该笔订单号商家不存在,退款订单号:{}", outRefundNo);
new ServiceException("微信该笔订单号商家不存在,退款订单号:" + outRefundNo);
}
String successTime = entries.getStr("refund_time").toString();//微信退款成功时间
TradeOrder tradeOrder = new TradeOrder();
tradeOrder.setId(tradeOrderVO.getId());
tradeOrder.setStatus(OrderStatus.CANCELLED.getValue());
try {
if (StringUtils.isNotBlank(successTime)) {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
Date date = formatter.parse(successTime);
Date refundSuccessTime = TimeUtils.parseDate(TimeUtils.format(date, CommonConstants.DATE.FORMAT_DEFAULT), CommonConstants.DATE.FORMAT_DEFAULT);
tradeOrder.setRefundTime(refundSuccessTime);
} else {
tradeOrder.setRefundTime(TimeUtils.getNowDate());
}
} catch (Exception e) {
log.error("商城订单-微信退款回调处理失败, orderNo = " + orderNo, e);
}
tradeOrderService.updateById(tradeOrder);
log.info("商城订单微信退款回调成功success----商户订单号:{}", orderNo);
RefundLog refundLog = refundLogService.getOne(new LambdaQueryWrapper<RefundLog>().eq(RefundLog::getPaymentReceivableNo,
transactionId));
if (refundLog != null) {
refundLog.setIsRefund(true);
refundLog.setReceivableNo(refundId);
refundLogService.saveOrUpdate(refundLog);
}
}
}
// 处理解密后的消息逻辑(根据需求)
// String responseMessage = "{\"demo_resp\":\"good luck\"}";
// 构造回包
// String response = createResponse(responseMessage);
httpServletResponse.getWriter().write("success");
}
/**
* 验证微信签名
*
* @param signature 微信传来的签名
* @param timestamp 微信传来的时间戳
* @param nonce 微信传来的随机数
* @return 是否是合法请求
*/
public static boolean checkSignature(String signature, String timestamp, String nonce) {
try {
String[] arr = new String[]{TOKEN, timestamp, nonce};
// 1. 字典序排序
Arrays.sort(arr);
// 2. 拼接成一个字符串
StringBuilder content = new StringBuilder();
for (String s : arr) {
content.append(s);
}
// 3. SHA1加密
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] digest = md.digest(content.toString().getBytes());
// 4. 转成十六进制字符串
StringBuilder hexStr = new StringBuilder();
for (byte b : digest) {
String shaHex = Integer.toHexString(b & 0xFF);
if (shaHex.length() < 2) {
hexStr.append(0);
}
hexStr.append(shaHex);
}
// 5. 比较签名
return hexStr.toString().equals(signature);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 解析请求体中的 Encrypt 字段
*/
private String parseEncryptField(String jsonBody) {
try {
JSONObject jsonObject = JSON.parseObject(jsonBody);
return jsonObject.getString("Encrypt");
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 校验 msg_signature 签名
*/
private boolean checkMsgSignature(String signature, String timestamp, String nonce, String encrypt) {
try {
String[] arr = new String[]{encrypt, timestamp, nonce, TOKEN};
Arrays.sort(arr);
StringBuilder sb = new StringBuilder();
for (String str : arr) {
sb.append(str);
}
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] digest = md.digest(sb.toString().getBytes());
StringBuilder hexStr = new StringBuilder();
for (byte b : digest) {
String shaHex = Integer.toHexString(b & 0xFF);
if (shaHex.length() < 2) {
hexStr.append(0);
}
hexStr.append(shaHex);
}
return hexStr.toString().equals(signature);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 解密消息体中的 Encrypt 内容
*/
private String decryptMessage(String encrypt) {
try {
// 1. Base64 解密 Encrypt 字段
byte[] encryptedBytes = Base64.getDecoder().decode(encrypt);
// 2. 获取 AESKey
byte[] aesKeyBytes = Base64.getDecoder().decode(ENCODING_AES_KEY + "=");
SecretKeySpec secretKey = new SecretKeySpec(aesKeyBytes, "AES");
IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKeyBytes, 0, 16)); // 使用前16字节作为 IV
// 3. AES 解密
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
byte[] decrypted = cipher.doFinal(encryptedBytes);
// 4.去除补位字符
decrypted = PKCS7Encoder.decode(decrypted);
// 5. 拆解结构 random(16B) + msg_len(4B) + msg + appid
ByteBuffer byteBuffer = ByteBuffer.wrap(decrypted);
// 读取 random
byte[] randomBytes = new byte[16];
byteBuffer.get(randomBytes);
String randomStr = new String(randomBytes, StandardCharsets.UTF_8);
// 读取 msg_len(4 字节,网络字节序,大端)
int msgLen = byteBuffer.getInt();
// 读取 msg
byte[] msgBytes = new byte[msgLen];
byteBuffer.get(msgBytes);
String msg = new String(msgBytes, StandardCharsets.UTF_8);
// 读取 appid
byte[] appIdBytes = new byte[byteBuffer.remaining()];
byteBuffer.get(appIdBytes);
String appId = new String(appIdBytes, StandardCharsets.UTF_8);
// 5. 将解密后的数据以 JSON 格式返回
JSONObject jsonResponse = new JSONObject();
jsonResponse.put("random", randomStr);
jsonResponse.put("msg_len", msgLen);
jsonResponse.put("msg", msg);
jsonResponse.put("appid", appId);
return jsonResponse.toString(); // 返回 JSON 格式字符串
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 构造回包并加密
*/
private String createResponse(String message) {
try {
// 1. 构造 FullStr
String randomStr = generateRandomString(16); // 随机生成16字节
byte[] msgBytes = message.getBytes("UTF-8");
// 2. 构造消息头:random(16B) + msg_len(4B) + msg
byte[] msgLenBytes = intToByteArray(msgBytes.length); // 长度占4字节
byte[] fullMessage = new byte[randomStr.length() + 4 + msgBytes.length];
// 拼接消息
System.arraycopy(randomStr.getBytes("UTF-8"), 0, fullMessage, 0, randomStr.length());
System.arraycopy(msgLenBytes, 0, fullMessage, randomStr.length(), msgLenBytes.length);
System.arraycopy(msgBytes, 0, fullMessage, randomStr.length() + 4, msgBytes.length);
// 3. 用 AES 加密消息
byte[] encryptedMessage = encryptWithAES(fullMessage);
// 4. Base64 编码加密结果
String encrypt = Base64.getEncoder().encodeToString(encryptedMessage);
// 5. 生成签名
String msgSignature = generateMsgSignature(encrypt);
// 6. 构造回包
long timeStamp = System.currentTimeMillis() / 1000; // 当前时间戳,单位秒
int nonce = new Random().nextInt(Integer.MAX_VALUE); // 随机生成一个 nonce
// 7. 构造回包
return "{\"Encrypt\": \"" + encrypt + "\", \"MsgSignature\": \"" + msgSignature + "\", \"TimeStamp\": " + timeStamp + ", \"Nonce\": " + nonce + "}";
} catch (Exception e) {
e.printStackTrace();
return "{\"error\":\"Failed to create response\"}";
}
}
/**
* AES 加密
*/
private byte[] encryptWithAES(byte[] data) throws Exception {
byte[] aesKeyBytes = Base64.getDecoder().decode(ENCODING_AES_KEY + "=");
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec secretKey = new SecretKeySpec(aesKeyBytes, "AES");
IvParameterSpec iv = new IvParameterSpec(aesKeyBytes, 0, 16); // 使用前16字节作为 IV
cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv); // 使用空的 16 字节偏移量
return cipher.doFinal(data);
}
/**
* 生成消息签名
*/
private String generateMsgSignature(String encrypt) throws Exception {
String[] arr = new String[]{"1713424427", "415670741", TOKEN, encrypt};
Arrays.sort(arr);
StringBuilder sb = new StringBuilder();
for (String str : arr) {
sb.append(str);
}
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] digest = md.digest(sb.toString().getBytes());
StringBuilder hexStr = new StringBuilder();
for (byte b : digest) {
String shaHex = Integer.toHexString(b & 0xFF);
if (shaHex.length() < 2) {
hexStr.append(0);
}
hexStr.append(shaHex);
}
return hexStr.toString();
}
// 将整数转化为网络字节序的4字节
private byte[] intToByteArray(int num) {
return new byte[]{
(byte) (num >>> 24),
(byte) (num >>> 16),
(byte) (num >>> 8),
(byte) num
};
}
/**
* 生成固定长度的随机字符串
*/
private String generateRandomString(int length) {
StringBuilder sb = new StringBuilder();
String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
Random random = new Random();
for (int i = 0; i < length; i++) {
sb.append(chars.charAt(random.nextInt(chars.length())));
}
return sb.toString();
}
}