787 lines
31 KiB
Java
787 lines
31 KiB
Java
package com.ruoyi.system.ControllerUtil;
|
||
|
||
import com.alibaba.fastjson2.JSONObject;
|
||
import org.springframework.http.HttpEntity;
|
||
import org.springframework.http.HttpHeaders;
|
||
import org.springframework.http.HttpMethod;
|
||
import org.springframework.http.ResponseEntity;
|
||
import org.springframework.web.client.RestTemplate;
|
||
|
||
import javax.crypto.Mac;
|
||
import javax.crypto.spec.SecretKeySpec;
|
||
import javax.servlet.http.HttpServletRequest;
|
||
import java.io.BufferedReader;
|
||
import java.io.InputStreamReader;
|
||
import java.math.BigDecimal;
|
||
import java.nio.charset.StandardCharsets;
|
||
import java.security.MessageDigest;
|
||
import java.text.SimpleDateFormat;
|
||
import java.util.*;
|
||
|
||
/**
|
||
* 微信支付工具类
|
||
*
|
||
* 提供微信支付相关的完整功能实现
|
||
* 主要功能:
|
||
* 1. 统一下单支付
|
||
* 2. 订单状态查询
|
||
* 3. 找朋友代付功能
|
||
* 4. 支付结果回调处理
|
||
* 5. 退款申请和查询
|
||
* 6. 签名生成和验证
|
||
* 7. 支付安全验证
|
||
*
|
||
* @author Mr. Zhang Pan
|
||
* @date 2025-01-03
|
||
* @version 1.0
|
||
*/
|
||
public class WechatPayUtil {
|
||
|
||
/**
|
||
* 微信支付配置常量
|
||
* 注意:实际使用时需要在配置文件中配置这些值
|
||
*/
|
||
private static final String WECHAT_APP_ID = "your_wechat_appid";
|
||
private static final String WECHAT_MCH_ID = "your_merchant_id";
|
||
private static final String WECHAT_API_KEY = "your_api_key";
|
||
private static final String WECHAT_CERT_PATH = "/path/to/cert.p12"; // 微信支付证书路径
|
||
|
||
/**
|
||
* 微信支付API地址
|
||
*/
|
||
private static final String WECHAT_PAY_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
|
||
private static final String WECHAT_QUERY_URL = "https://api.mch.weixin.qq.com/pay/orderquery";
|
||
private static final String WECHAT_REFUND_URL = "https://api.mch.weixin.qq.com/secapi/pay/refund";
|
||
private static final String WECHAT_TRANSFER_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers"; // 企业付款
|
||
|
||
/**
|
||
* 其他配置常量
|
||
*/
|
||
private static final String TRADE_TYPE_JSAPI = "JSAPI";
|
||
private static final String CURRENCY = "CNY"; // 货币类型
|
||
private static final String SUCCESS_CODE = "SUCCESS";
|
||
private static final String FAIL_CODE = "FAIL";
|
||
|
||
/**
|
||
* RestTemplate实例
|
||
*/
|
||
private static final RestTemplate restTemplate = new RestTemplate();
|
||
|
||
/**
|
||
* 统一下单 - 微信小程序支付
|
||
*
|
||
* @param orderInfo 订单信息
|
||
* @return 支付参数Map
|
||
*/
|
||
public static Map<String, Object> unifiedOrder(Map<String, Object> orderInfo) {
|
||
Map<String, Object> result = new HashMap<>();
|
||
|
||
try {
|
||
// 1. 参数验证
|
||
String validationError = validateOrderParams(orderInfo);
|
||
if (validationError != null) {
|
||
result.put("success", false);
|
||
result.put("message", validationError);
|
||
return result;
|
||
}
|
||
|
||
// 2. 构建统一下单参数
|
||
Map<String, String> params = buildUnifiedOrderParams(orderInfo);
|
||
|
||
// 3. 生成签名
|
||
String sign = generateSign(params, WECHAT_API_KEY);
|
||
params.put("sign", sign);
|
||
|
||
// 4. 发送请求
|
||
String xmlRequest = mapToXml(params);
|
||
ResponseEntity<String> response = restTemplate.postForEntity(WECHAT_PAY_URL, xmlRequest, String.class);
|
||
|
||
// 5. 解析响应
|
||
Map<String, String> responseMap = xmlToMap(response.getBody());
|
||
|
||
if (SUCCESS_CODE.equals(responseMap.get("return_code")) &&
|
||
SUCCESS_CODE.equals(responseMap.get("result_code"))) {
|
||
|
||
// 6. 构建小程序支付参数
|
||
Map<String, Object> payParams = buildMiniProgramPayParams(responseMap.get("prepay_id"));
|
||
|
||
result.put("success", true);
|
||
result.put("payParams", payParams);
|
||
result.put("prepayId", responseMap.get("prepay_id"));
|
||
result.put("message", "统一下单成功");
|
||
|
||
} else {
|
||
result.put("success", false);
|
||
result.put("message", "统一下单失败:" +
|
||
(responseMap.get("err_code_des") != null ? responseMap.get("err_code_des") :
|
||
responseMap.get("return_msg")));
|
||
}
|
||
|
||
} catch (Exception e) {
|
||
result.put("success", false);
|
||
result.put("message", "统一下单异常:" + e.getMessage());
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* 查询订单状态
|
||
*
|
||
* @param orderNo 订单号
|
||
* @param transactionId 微信订单号(可选,与orderNo二选一)
|
||
* @return 查询结果
|
||
*/
|
||
public static Map<String, Object> queryOrder(String orderNo, String transactionId) {
|
||
Map<String, Object> result = new HashMap<>();
|
||
|
||
try {
|
||
// 1. 参数验证
|
||
if ((orderNo == null || orderNo.trim().isEmpty()) &&
|
||
(transactionId == null || transactionId.trim().isEmpty())) {
|
||
result.put("success", false);
|
||
result.put("message", "订单号和微信订单号不能同时为空");
|
||
return result;
|
||
}
|
||
|
||
// 2. 构建查询参数
|
||
Map<String, String> params = new HashMap<>();
|
||
params.put("appid", WECHAT_APP_ID);
|
||
params.put("mch_id", WECHAT_MCH_ID);
|
||
params.put("nonce_str", generateNonceStr());
|
||
|
||
if (orderNo != null && !orderNo.trim().isEmpty()) {
|
||
params.put("out_trade_no", orderNo.trim());
|
||
}
|
||
if (transactionId != null && !transactionId.trim().isEmpty()) {
|
||
params.put("transaction_id", transactionId.trim());
|
||
}
|
||
|
||
// 3. 生成签名
|
||
String sign = generateSign(params, WECHAT_API_KEY);
|
||
params.put("sign", sign);
|
||
|
||
// 4. 发送请求
|
||
String xmlRequest = mapToXml(params);
|
||
ResponseEntity<String> response = restTemplate.postForEntity(WECHAT_QUERY_URL, xmlRequest, String.class);
|
||
|
||
// 5. 解析响应
|
||
Map<String, String> responseMap = xmlToMap(response.getBody());
|
||
|
||
if (SUCCESS_CODE.equals(responseMap.get("return_code")) &&
|
||
SUCCESS_CODE.equals(responseMap.get("result_code"))) {
|
||
|
||
Map<String, Object> orderInfo = new HashMap<>();
|
||
orderInfo.put("tradeState", responseMap.get("trade_state"));
|
||
orderInfo.put("tradeStateDesc", responseMap.get("trade_state_desc"));
|
||
orderInfo.put("transactionId", responseMap.get("transaction_id"));
|
||
orderInfo.put("outTradeNo", responseMap.get("out_trade_no"));
|
||
orderInfo.put("totalFee", responseMap.get("total_fee"));
|
||
orderInfo.put("cashFee", responseMap.get("cash_fee"));
|
||
orderInfo.put("timeEnd", responseMap.get("time_end"));
|
||
|
||
result.put("success", true);
|
||
result.put("orderInfo", orderInfo);
|
||
result.put("message", "查询订单成功");
|
||
|
||
} else {
|
||
result.put("success", false);
|
||
result.put("message", "查询订单失败:" +
|
||
(responseMap.get("err_code_des") != null ? responseMap.get("err_code_des") :
|
||
responseMap.get("return_msg")));
|
||
}
|
||
|
||
} catch (Exception e) {
|
||
result.put("success", false);
|
||
result.put("message", "查询订单异常:" + e.getMessage());
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* 找朋友代付功能 - 生成代付订单
|
||
*
|
||
* @param payForInfo 代付信息
|
||
* @return 代付订单结果
|
||
*/
|
||
public static Map<String, Object> createPayForOrder(Map<String, Object> payForInfo) {
|
||
Map<String, Object> result = new HashMap<>();
|
||
|
||
try {
|
||
// 1. 参数验证
|
||
String validationError = validatePayForParams(payForInfo);
|
||
if (validationError != null) {
|
||
result.put("success", false);
|
||
result.put("message", validationError);
|
||
return result;
|
||
}
|
||
|
||
// 2. 生成代付订单号
|
||
String payForOrderNo = generatePayForOrderNo((String) payForInfo.get("orderNo"));
|
||
|
||
// 3. 构建代付订单信息
|
||
Map<String, Object> payForOrderInfo = new HashMap<>();
|
||
payForOrderInfo.put("orderNo", payForOrderNo);
|
||
payForOrderInfo.put("openid", payForInfo.get("payerOpenid"));
|
||
payForOrderInfo.put("totalFee", payForInfo.get("totalFee"));
|
||
payForOrderInfo.put("body", "代付-" + payForInfo.get("body"));
|
||
payForOrderInfo.put("notifyUrl", payForInfo.get("notifyUrl"));
|
||
payForOrderInfo.put("attach", buildPayForAttach(payForInfo));
|
||
|
||
// 4. 设置30分钟过期时间
|
||
Calendar calendar = Calendar.getInstance();
|
||
calendar.add(Calendar.MINUTE, 30);
|
||
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
|
||
payForOrderInfo.put("timeExpire", sdf.format(calendar.getTime()));
|
||
|
||
// 5. 调用统一下单
|
||
Map<String, Object> unifiedResult = unifiedOrder(payForOrderInfo);
|
||
|
||
if ((Boolean) unifiedResult.get("success")) {
|
||
result.put("success", true);
|
||
result.put("payForOrderNo", payForOrderNo);
|
||
result.put("payParams", unifiedResult.get("payParams"));
|
||
result.put("prepayId", unifiedResult.get("prepayId"));
|
||
result.put("expireTime", payForOrderInfo.get("timeExpire"));
|
||
result.put("message", "代付订单创建成功");
|
||
} else {
|
||
result.put("success", false);
|
||
result.put("message", "代付订单创建失败:" + unifiedResult.get("message"));
|
||
}
|
||
|
||
} catch (Exception e) {
|
||
result.put("success", false);
|
||
result.put("message", "创建代付订单异常:" + e.getMessage());
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* 支付结果通知回调处理
|
||
*
|
||
* @param request HTTP请求对象
|
||
* @return 处理结果
|
||
*/
|
||
public static Map<String, Object> handlePayNotify(HttpServletRequest request) {
|
||
Map<String, Object> result = new HashMap<>();
|
||
|
||
try {
|
||
// 1. 读取请求数据
|
||
StringBuilder xmlData = new StringBuilder();
|
||
BufferedReader reader = new BufferedReader(
|
||
new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8));
|
||
String line;
|
||
while ((line = reader.readLine()) != null) {
|
||
xmlData.append(line);
|
||
}
|
||
reader.close();
|
||
|
||
// 2. 解析XML数据
|
||
Map<String, String> notifyData = xmlToMap(xmlData.toString());
|
||
|
||
// 3. 验证签名
|
||
if (!verifySign(notifyData, WECHAT_API_KEY)) {
|
||
result.put("success", false);
|
||
result.put("responseXml", buildNotifyResponse(FAIL_CODE, "签名验证失败"));
|
||
result.put("message", "签名验证失败");
|
||
return result;
|
||
}
|
||
|
||
// 4. 检查支付结果
|
||
if (!SUCCESS_CODE.equals(notifyData.get("return_code")) ||
|
||
!SUCCESS_CODE.equals(notifyData.get("result_code"))) {
|
||
result.put("success", false);
|
||
result.put("responseXml", buildNotifyResponse(FAIL_CODE, "支付失败"));
|
||
result.put("message", "支付失败:" + notifyData.get("err_code_des"));
|
||
return result;
|
||
}
|
||
|
||
// 5. 构建支付信息
|
||
Map<String, Object> paymentInfo = new HashMap<>();
|
||
paymentInfo.put("transactionId", notifyData.get("transaction_id"));
|
||
paymentInfo.put("outTradeNo", notifyData.get("out_trade_no"));
|
||
paymentInfo.put("totalFee", notifyData.get("total_fee"));
|
||
paymentInfo.put("cashFee", notifyData.get("cash_fee"));
|
||
paymentInfo.put("timeEnd", notifyData.get("time_end"));
|
||
paymentInfo.put("attach", notifyData.get("attach"));
|
||
paymentInfo.put("openid", notifyData.get("openid"));
|
||
|
||
// 6. 判断是否为代付订单
|
||
String orderNo = notifyData.get("out_trade_no");
|
||
boolean isPayFor = isPayForOrder(orderNo);
|
||
|
||
result.put("success", true);
|
||
result.put("responseXml", buildNotifyResponse(SUCCESS_CODE, "OK"));
|
||
result.put("paymentInfo", paymentInfo);
|
||
result.put("isPayFor", isPayFor);
|
||
result.put("message", "支付通知处理成功");
|
||
|
||
} catch (Exception e) {
|
||
result.put("success", false);
|
||
result.put("responseXml", buildNotifyResponse(FAIL_CODE, "处理异常"));
|
||
result.put("message", "处理支付通知异常:" + e.getMessage());
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* 申请退款
|
||
*
|
||
* @param refundInfo 退款信息
|
||
* @return 退款结果
|
||
*/
|
||
public static Map<String, Object> refund(Map<String, Object> refundInfo) {
|
||
Map<String, Object> result = new HashMap<>();
|
||
|
||
try {
|
||
// 1. 参数验证
|
||
String validationError = validateRefundParams(refundInfo);
|
||
if (validationError != null) {
|
||
result.put("success", false);
|
||
result.put("message", validationError);
|
||
return result;
|
||
}
|
||
|
||
// 2. 构建退款参数
|
||
Map<String, String> params = buildRefundParams(refundInfo);
|
||
|
||
// 3. 生成签名
|
||
String sign = generateSign(params, WECHAT_API_KEY);
|
||
params.put("sign", sign);
|
||
|
||
// 4. 发送请求(需要证书)
|
||
String xmlRequest = mapToXml(params);
|
||
// 注意:退款接口需要使用客户端证书,这里简化处理
|
||
ResponseEntity<String> response = restTemplate.postForEntity(WECHAT_REFUND_URL, xmlRequest, String.class);
|
||
|
||
// 5. 解析响应
|
||
Map<String, String> responseMap = xmlToMap(response.getBody());
|
||
|
||
if (SUCCESS_CODE.equals(responseMap.get("return_code")) &&
|
||
SUCCESS_CODE.equals(responseMap.get("result_code"))) {
|
||
|
||
Map<String, Object> refundResult = new HashMap<>();
|
||
refundResult.put("refundId", responseMap.get("refund_id"));
|
||
refundResult.put("outRefundNo", responseMap.get("out_refund_no"));
|
||
refundResult.put("refundFee", responseMap.get("refund_fee"));
|
||
refundResult.put("settlementRefundFee", responseMap.get("settlement_refund_fee"));
|
||
|
||
result.put("success", true);
|
||
result.put("refundInfo", refundResult);
|
||
result.put("message", "申请退款成功");
|
||
|
||
} else {
|
||
result.put("success", false);
|
||
result.put("message", "申请退款失败:" +
|
||
(responseMap.get("err_code_des") != null ? responseMap.get("err_code_des") :
|
||
responseMap.get("return_msg")));
|
||
}
|
||
|
||
} catch (Exception e) {
|
||
result.put("success", false);
|
||
result.put("message", "申请退款异常:" + e.getMessage());
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* 企业付款到零钱(用于代付完成后的资金转移)
|
||
*
|
||
* @param transferInfo 付款信息
|
||
* @return 付款结果
|
||
*/
|
||
public static Map<String, Object> transferToUser(Map<String, Object> transferInfo) {
|
||
Map<String, Object> result = new HashMap<>();
|
||
|
||
try {
|
||
// 1. 参数验证
|
||
String validationError = validateTransferParams(transferInfo);
|
||
if (validationError != null) {
|
||
result.put("success", false);
|
||
result.put("message", validationError);
|
||
return result;
|
||
}
|
||
|
||
// 2. 构建付款参数
|
||
Map<String, String> params = buildTransferParams(transferInfo);
|
||
|
||
// 3. 生成签名
|
||
String sign = generateSign(params, WECHAT_API_KEY);
|
||
params.put("sign", sign);
|
||
|
||
// 4. 发送请求(需要证书)
|
||
String xmlRequest = mapToXml(params);
|
||
// 注意:企业付款接口需要使用客户端证书,这里简化处理
|
||
ResponseEntity<String> response = restTemplate.postForEntity(WECHAT_TRANSFER_URL, xmlRequest, String.class);
|
||
|
||
// 5. 解析响应
|
||
Map<String, String> responseMap = xmlToMap(response.getBody());
|
||
|
||
if (SUCCESS_CODE.equals(responseMap.get("return_code")) &&
|
||
SUCCESS_CODE.equals(responseMap.get("result_code"))) {
|
||
|
||
Map<String, Object> transferResult = new HashMap<>();
|
||
transferResult.put("partnerTradeNo", responseMap.get("partner_trade_no"));
|
||
transferResult.put("paymentNo", responseMap.get("payment_no"));
|
||
transferResult.put("paymentTime", responseMap.get("payment_time"));
|
||
|
||
result.put("success", true);
|
||
result.put("transferInfo", transferResult);
|
||
result.put("message", "企业付款成功");
|
||
|
||
} else {
|
||
result.put("success", false);
|
||
result.put("message", "企业付款失败:" +
|
||
(responseMap.get("err_code_des") != null ? responseMap.get("err_code_des") :
|
||
responseMap.get("return_msg")));
|
||
}
|
||
|
||
} catch (Exception e) {
|
||
result.put("success", false);
|
||
result.put("message", "企业付款异常:" + e.getMessage());
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
// ========== 私有工具方法 ==========
|
||
|
||
/**
|
||
* 验证统一下单参数
|
||
*/
|
||
private static String validateOrderParams(Map<String, Object> orderInfo) {
|
||
if (orderInfo == null) return "订单信息不能为空";
|
||
if (orderInfo.get("orderNo") == null || orderInfo.get("orderNo").toString().trim().isEmpty()) {
|
||
return "订单号不能为空";
|
||
}
|
||
if (orderInfo.get("openid") == null || orderInfo.get("openid").toString().trim().isEmpty()) {
|
||
return "用户openid不能为空";
|
||
}
|
||
if (orderInfo.get("totalFee") == null) {
|
||
return "支付金额不能为空";
|
||
}
|
||
if (orderInfo.get("body") == null || orderInfo.get("body").toString().trim().isEmpty()) {
|
||
return "商品描述不能为空";
|
||
}
|
||
if (orderInfo.get("notifyUrl") == null || orderInfo.get("notifyUrl").toString().trim().isEmpty()) {
|
||
return "回调地址不能为空";
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* 验证代付参数
|
||
*/
|
||
private static String validatePayForParams(Map<String, Object> payForInfo) {
|
||
if (payForInfo == null) return "代付信息不能为空";
|
||
if (payForInfo.get("orderNo") == null || payForInfo.get("orderNo").toString().trim().isEmpty()) {
|
||
return "原订单号不能为空";
|
||
}
|
||
if (payForInfo.get("payerOpenid") == null || payForInfo.get("payerOpenid").toString().trim().isEmpty()) {
|
||
return "代付人openid不能为空";
|
||
}
|
||
if (payForInfo.get("payeeOpenid") == null || payForInfo.get("payeeOpenid").toString().trim().isEmpty()) {
|
||
return "被代付人openid不能为空";
|
||
}
|
||
if (payForInfo.get("totalFee") == null) {
|
||
return "代付金额不能为空";
|
||
}
|
||
if (payForInfo.get("body") == null || payForInfo.get("body").toString().trim().isEmpty()) {
|
||
return "商品描述不能为空";
|
||
}
|
||
if (payForInfo.get("notifyUrl") == null || payForInfo.get("notifyUrl").toString().trim().isEmpty()) {
|
||
return "回调地址不能为空";
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* 验证退款参数
|
||
*/
|
||
private static String validateRefundParams(Map<String, Object> refundInfo) {
|
||
if (refundInfo == null) return "退款信息不能为空";
|
||
if (refundInfo.get("orderNo") == null || refundInfo.get("orderNo").toString().trim().isEmpty()) {
|
||
return "订单号不能为空";
|
||
}
|
||
if (refundInfo.get("refundNo") == null || refundInfo.get("refundNo").toString().trim().isEmpty()) {
|
||
return "退款单号不能为空";
|
||
}
|
||
if (refundInfo.get("totalFee") == null) {
|
||
return "订单总金额不能为空";
|
||
}
|
||
if (refundInfo.get("refundFee") == null) {
|
||
return "退款金额不能为空";
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* 验证企业付款参数
|
||
*/
|
||
private static String validateTransferParams(Map<String, Object> transferInfo) {
|
||
if (transferInfo == null) return "付款信息不能为空";
|
||
if (transferInfo.get("partnerTradeNo") == null || transferInfo.get("partnerTradeNo").toString().trim().isEmpty()) {
|
||
return "商户订单号不能为空";
|
||
}
|
||
if (transferInfo.get("openid") == null || transferInfo.get("openid").toString().trim().isEmpty()) {
|
||
return "用户openid不能为空";
|
||
}
|
||
if (transferInfo.get("amount") == null) {
|
||
return "付款金额不能为空";
|
||
}
|
||
if (transferInfo.get("desc") == null || transferInfo.get("desc").toString().trim().isEmpty()) {
|
||
return "付款描述不能为空";
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* 构建统一下单参数
|
||
*/
|
||
private static Map<String, String> buildUnifiedOrderParams(Map<String, Object> orderInfo) {
|
||
Map<String, String> params = new HashMap<>();
|
||
params.put("appid", WECHAT_APP_ID);
|
||
params.put("mch_id", WECHAT_MCH_ID);
|
||
params.put("nonce_str", generateNonceStr());
|
||
params.put("body", orderInfo.get("body").toString());
|
||
params.put("out_trade_no", orderInfo.get("orderNo").toString());
|
||
params.put("total_fee", orderInfo.get("totalFee").toString());
|
||
params.put("spbill_create_ip", "127.0.0.1");
|
||
params.put("notify_url", orderInfo.get("notifyUrl").toString());
|
||
params.put("trade_type", TRADE_TYPE_JSAPI);
|
||
params.put("openid", orderInfo.get("openid").toString());
|
||
|
||
if (orderInfo.get("attach") != null) {
|
||
params.put("attach", orderInfo.get("attach").toString());
|
||
}
|
||
if (orderInfo.get("timeExpire") != null) {
|
||
params.put("time_expire", orderInfo.get("timeExpire").toString());
|
||
}
|
||
|
||
return params;
|
||
}
|
||
|
||
/**
|
||
* 构建退款参数
|
||
*/
|
||
private static Map<String, String> buildRefundParams(Map<String, Object> refundInfo) {
|
||
Map<String, String> params = new HashMap<>();
|
||
params.put("appid", WECHAT_APP_ID);
|
||
params.put("mch_id", WECHAT_MCH_ID);
|
||
params.put("nonce_str", generateNonceStr());
|
||
params.put("out_trade_no", refundInfo.get("orderNo").toString());
|
||
params.put("out_refund_no", refundInfo.get("refundNo").toString());
|
||
params.put("total_fee", refundInfo.get("totalFee").toString());
|
||
params.put("refund_fee", refundInfo.get("refundFee").toString());
|
||
|
||
if (refundInfo.get("refundDesc") != null) {
|
||
params.put("refund_desc", refundInfo.get("refundDesc").toString());
|
||
}
|
||
if (refundInfo.get("notifyUrl") != null) {
|
||
params.put("notify_url", refundInfo.get("notifyUrl").toString());
|
||
}
|
||
|
||
return params;
|
||
}
|
||
|
||
/**
|
||
* 构建企业付款参数
|
||
*/
|
||
private static Map<String, String> buildTransferParams(Map<String, Object> transferInfo) {
|
||
Map<String, String> params = new HashMap<>();
|
||
params.put("mch_appid", WECHAT_APP_ID);
|
||
params.put("mchid", WECHAT_MCH_ID);
|
||
params.put("nonce_str", generateNonceStr());
|
||
params.put("partner_trade_no", transferInfo.get("partnerTradeNo").toString());
|
||
params.put("openid", transferInfo.get("openid").toString());
|
||
params.put("check_name", transferInfo.get("checkName") != null ?
|
||
transferInfo.get("checkName").toString() : "NO_CHECK");
|
||
params.put("amount", transferInfo.get("amount").toString());
|
||
params.put("desc", transferInfo.get("desc").toString());
|
||
params.put("spbill_create_ip", "127.0.0.1");
|
||
|
||
if (transferInfo.get("reUserName") != null) {
|
||
params.put("re_user_name", transferInfo.get("reUserName").toString());
|
||
}
|
||
|
||
return params;
|
||
}
|
||
|
||
/**
|
||
* 构建小程序支付参数
|
||
*/
|
||
private static Map<String, Object> buildMiniProgramPayParams(String prepayId) {
|
||
Map<String, Object> payParams = new HashMap<>();
|
||
String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
|
||
String nonceStr = generateNonceStr();
|
||
String packageStr = "prepay_id=" + prepayId;
|
||
String signType = "MD5";
|
||
|
||
// 构建签名参数
|
||
Map<String, String> signParams = new HashMap<>();
|
||
signParams.put("appId", WECHAT_APP_ID);
|
||
signParams.put("timeStamp", timeStamp);
|
||
signParams.put("nonceStr", nonceStr);
|
||
signParams.put("package", packageStr);
|
||
signParams.put("signType", signType);
|
||
|
||
String paySign = generateSign(signParams, WECHAT_API_KEY);
|
||
|
||
payParams.put("timeStamp", timeStamp);
|
||
payParams.put("nonceStr", nonceStr);
|
||
payParams.put("package", packageStr);
|
||
payParams.put("signType", signType);
|
||
payParams.put("paySign", paySign);
|
||
|
||
return payParams;
|
||
}
|
||
|
||
/**
|
||
* 构建代付附加数据
|
||
*/
|
||
private static String buildPayForAttach(Map<String, Object> payForInfo) {
|
||
JSONObject attach = new JSONObject();
|
||
attach.put("type", "payfor");
|
||
attach.put("originalOrderNo", payForInfo.get("orderNo"));
|
||
attach.put("payerOpenid", payForInfo.get("payerOpenid"));
|
||
attach.put("payeeOpenid", payForInfo.get("payeeOpenid"));
|
||
if (payForInfo.get("remark") != null) {
|
||
attach.put("remark", payForInfo.get("remark"));
|
||
}
|
||
return attach.toString();
|
||
}
|
||
|
||
/**
|
||
* 生成代付订单号
|
||
*/
|
||
private static String generatePayForOrderNo(String originalOrderNo) {
|
||
return "PF" + originalOrderNo + System.currentTimeMillis();
|
||
}
|
||
|
||
/**
|
||
* 判断是否为代付订单
|
||
*/
|
||
private static boolean isPayForOrder(String orderNo) {
|
||
return orderNo != null && orderNo.startsWith("PF");
|
||
}
|
||
|
||
/**
|
||
* 构建通知响应XML
|
||
*/
|
||
private static String buildNotifyResponse(String returnCode, String returnMsg) {
|
||
StringBuilder xml = new StringBuilder();
|
||
xml.append("<xml>");
|
||
xml.append("<return_code><![CDATA[").append(returnCode).append("]]></return_code>");
|
||
xml.append("<return_msg><![CDATA[").append(returnMsg).append("]]></return_msg>");
|
||
xml.append("</xml>");
|
||
return xml.toString();
|
||
}
|
||
|
||
/**
|
||
* 生成随机字符串
|
||
*/
|
||
private static String generateNonceStr() {
|
||
return UUID.randomUUID().toString().replace("-", "").substring(0, 32);
|
||
}
|
||
|
||
/**
|
||
* 生成签名
|
||
*/
|
||
private static String generateSign(Map<String, String> params, String key) {
|
||
try {
|
||
// 1. 排序参数
|
||
Map<String, String> sortedParams = new TreeMap<>(params);
|
||
|
||
// 2. 拼接参数
|
||
StringBuilder stringBuilder = new StringBuilder();
|
||
for (Map.Entry<String, String> entry : sortedParams.entrySet()) {
|
||
if (entry.getValue() != null && !entry.getValue().isEmpty() && !"sign".equals(entry.getKey())) {
|
||
stringBuilder.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
|
||
}
|
||
}
|
||
stringBuilder.append("key=").append(key);
|
||
|
||
// 3. MD5加密
|
||
MessageDigest md5 = MessageDigest.getInstance("MD5");
|
||
byte[] digest = md5.digest(stringBuilder.toString().getBytes(StandardCharsets.UTF_8));
|
||
|
||
// 4. 转换为大写16进制字符串
|
||
StringBuilder result = new StringBuilder();
|
||
for (byte b : digest) {
|
||
result.append(String.format("%02X", b));
|
||
}
|
||
|
||
return result.toString();
|
||
|
||
} catch (Exception e) {
|
||
throw new RuntimeException("生成签名失败", e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 验证签名
|
||
*/
|
||
private static boolean verifySign(Map<String, String> params, String key) {
|
||
String sign = params.get("sign");
|
||
if (sign == null || sign.isEmpty()) {
|
||
return false;
|
||
}
|
||
|
||
Map<String, String> paramsWithoutSign = new HashMap<>(params);
|
||
paramsWithoutSign.remove("sign");
|
||
|
||
String generatedSign = generateSign(paramsWithoutSign, key);
|
||
return sign.equals(generatedSign);
|
||
}
|
||
|
||
/**
|
||
* Map转XML
|
||
*/
|
||
private static String mapToXml(Map<String, String> params) {
|
||
StringBuilder xml = new StringBuilder();
|
||
xml.append("<xml>");
|
||
for (Map.Entry<String, String> entry : params.entrySet()) {
|
||
xml.append("<").append(entry.getKey()).append("><![CDATA[")
|
||
.append(entry.getValue()).append("]]></").append(entry.getKey()).append(">");
|
||
}
|
||
xml.append("</xml>");
|
||
return xml.toString();
|
||
}
|
||
|
||
/**
|
||
* XML转Map(简化实现)
|
||
*/
|
||
private static Map<String, String> xmlToMap(String xml) {
|
||
Map<String, String> map = new HashMap<>();
|
||
try {
|
||
// 简化的XML解析实现
|
||
xml = xml.replaceAll("<xml>", "").replaceAll("</xml>", "");
|
||
String[] elements = xml.split("</\\w+>");
|
||
|
||
for (String element : elements) {
|
||
if (element.trim().isEmpty()) continue;
|
||
|
||
int startTag = element.indexOf("<");
|
||
int endTag = element.indexOf(">");
|
||
if (startTag >= 0 && endTag > startTag) {
|
||
String key = element.substring(startTag + 1, endTag);
|
||
String value = element.substring(endTag + 1);
|
||
|
||
// 处理CDATA
|
||
if (value.startsWith("<![CDATA[") && value.endsWith("]]>")) {
|
||
value = value.substring(9, value.length() - 3);
|
||
}
|
||
|
||
map.put(key, value);
|
||
}
|
||
}
|
||
} catch (Exception e) {
|
||
throw new RuntimeException("XML解析失败", e);
|
||
}
|
||
return map;
|
||
}
|
||
} |