parent
e1215ca441
commit
7f8db2a727
|
|
@ -0,0 +1,228 @@
|
|||
package com.czg;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.czg.entity.CzgBaseReqParams;
|
||||
import com.czg.entity.CzgBaseRespParams;
|
||||
import com.czg.entity.req.*;
|
||||
import com.czg.enums.CzgPayEnum;
|
||||
import com.czg.resp.CzgResult;
|
||||
import com.czg.utils.MD5Util;
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author ww
|
||||
* @description 超掌柜支付类
|
||||
*/
|
||||
@Slf4j
|
||||
public class CzgPayUtils {
|
||||
|
||||
/**
|
||||
* h5支付
|
||||
*
|
||||
* @param domain 域名
|
||||
* @param appId 应用id tb_shop_merchant 表中的 app_id
|
||||
* @param appSecret 应用密钥 tb_shop_merchant 表中的 app_secret
|
||||
*/
|
||||
public static CzgResult<Object> h5Pay(@NonNull String domain, @NonNull String appId, @NonNull String appSecret, CzgH5PayReq bizData) {
|
||||
return sendCzg(domain.concat(CzgPayEnum.H5PAY.getUri()), CzgBaseReqParams.getInstance(appId, appSecret, bizData));
|
||||
}
|
||||
|
||||
/**
|
||||
* js支付
|
||||
*
|
||||
* @param domain 域名
|
||||
* @param appId 应用id tb_shop_merchant 表中的 app_id
|
||||
* @param appSecret 应用密钥 tb_shop_merchant 表中的 app_secret
|
||||
*/
|
||||
public static CzgResult<Object> jsPay(@NonNull String domain, @NonNull String appId, @NonNull String appSecret, CzgJsPayReq bizData) {
|
||||
return sendCzg(domain.concat(CzgPayEnum.JSPAY.getUri()), CzgBaseReqParams.getInstance(appId, appSecret, bizData));
|
||||
}
|
||||
|
||||
/**
|
||||
* 小程序支付
|
||||
*
|
||||
* @param domain 域名
|
||||
* @param appId 应用id tb_shop_merchant 表中的 app_id
|
||||
* @param appSecret 应用密钥 tb_shop_merchant 表中的 app_secret
|
||||
*/
|
||||
public static CzgResult<Object> ltPay(@NonNull String domain, @NonNull String appId, @NonNull String appSecret, CzgLtPayReq bizData) {
|
||||
return sendCzg(domain.concat(CzgPayEnum.LTPAY.getUri()), CzgBaseReqParams.getInstance(appId, appSecret, bizData));
|
||||
}
|
||||
|
||||
/**
|
||||
* PC扫码支付
|
||||
*
|
||||
* @param domain 域名
|
||||
* @param appId 应用id tb_shop_merchant 表中的 app_id
|
||||
* @param appSecret 应用密钥 tb_shop_merchant 表中的 app_secret
|
||||
*/
|
||||
public static CzgResult<Object> scanPay(@NonNull String domain, @NonNull String appId, @NonNull String appSecret, CzgScanPayReq bizData) {
|
||||
return sendCzg(domain.concat(CzgPayEnum.SCANPAY.getUri()), CzgBaseReqParams.getInstance(appId, appSecret, bizData));
|
||||
}
|
||||
|
||||
/**
|
||||
* 聚合反扫
|
||||
*
|
||||
* @param domain 域名
|
||||
* @param appId 应用id tb_shop_merchant 表中的 app_id
|
||||
* @param appSecret 应用密钥 tb_shop_merchant 表中的 app_secret
|
||||
*/
|
||||
public static CzgResult<Object> microPay(@NonNull String domain, @NonNull String appId, @NonNull String appSecret, CzgMicroPayReq bizData) {
|
||||
return sendCzg(domain.concat(CzgPayEnum.MICROPAY.getUri()), CzgBaseReqParams.getInstance(appId, appSecret, bizData));
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单查询
|
||||
*
|
||||
* @param domain 域名
|
||||
* @param appId 应用id tb_shop_merchant 表中的 app_id
|
||||
* @param appSecret 应用密钥 tb_shop_merchant 表中的 app_secret
|
||||
* @param payOrderId 平台订单号
|
||||
* @param mchOrderNo 商户订单号
|
||||
*/
|
||||
public static CzgResult<Object> queryPayOrder(@NonNull String domain, @NonNull String appId, @NonNull String appSecret,
|
||||
String payOrderId, String mchOrderNo) {
|
||||
JSONObject queryPayOrder = new JSONObject();
|
||||
queryPayOrder.put("payOrderId", payOrderId);
|
||||
queryPayOrder.put("mchOrderNo", mchOrderNo);
|
||||
return sendCzg(domain.concat(CzgPayEnum.TRADE.getUri()), CzgBaseReqParams.getInstance(appId, appSecret, queryPayOrder));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 订单退款
|
||||
*
|
||||
* @param domain 域名
|
||||
* @param appId 应用id tb_shop_merchant 表中的 app_id
|
||||
* @param appSecret 应用密钥 tb_shop_merchant 表中的 app_secret
|
||||
*/
|
||||
public static CzgResult<Object> refundOrder(@NonNull String domain, @NonNull String appId, @NonNull String appSecret, CzgRefundReq bizData) {
|
||||
return sendCzg(domain.concat(CzgPayEnum.REFUND.getUri()), CzgBaseReqParams.getInstance(appId, appSecret, bizData));
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款订单查询
|
||||
*
|
||||
* @param domain 域名
|
||||
* @param appId 应用id tb_shop_merchant 表中的 app_id
|
||||
* @param appSecret 应用密钥 tb_shop_merchant 表中的 app_secret
|
||||
* @param mchRefundNo 商户退款订单号 二选一
|
||||
* @param refundOrderId 平台退款订单号 二选一
|
||||
*/
|
||||
public static CzgResult<Object> queryRefundOrder(@NonNull String domain, @NonNull String appId, @NonNull String appSecret,
|
||||
String mchRefundNo, String refundOrderId) {
|
||||
JSONObject queryPayOrder = new JSONObject();
|
||||
queryPayOrder.put("mchRefundNo", mchRefundNo);
|
||||
queryPayOrder.put("refundOrderId", refundOrderId);
|
||||
return sendCzg(domain.concat(CzgPayEnum.QUERY_REFUND.getUri()), CzgBaseReqParams.getInstance(appId, appSecret, queryPayOrder));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 回调数据处理 返回任意实体
|
||||
*
|
||||
* @param dataJsonStr 带解析数据
|
||||
* @param clazz 返回的实体类
|
||||
*/
|
||||
public static <T> T getCzg(String dataJsonStr, Class<T> clazz) throws Exception {
|
||||
if (StrUtil.isNotEmpty(dataJsonStr)) {
|
||||
CzgBaseRespParams respParams = JSONObject.parseObject(dataJsonStr, CzgBaseRespParams.class);
|
||||
log.info("超掌柜交易请求响应,{}", respParams);
|
||||
if (!"000000".equals(respParams.getCode())) {
|
||||
log.error("超掌柜回调响应失败,{}", respParams);
|
||||
throw new Exception("超掌柜回调响应失败");
|
||||
}
|
||||
|
||||
if (StrUtil.isNotBlank(respParams.getSign())) {
|
||||
validateSign(respParams.getSign(), respParams.getBizData());
|
||||
}
|
||||
return JSONObject.parseObject(respParams.getBizData(), clazz);
|
||||
} else {
|
||||
throw new Exception("超掌柜回调接收内容异常");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认Post
|
||||
*
|
||||
* @param url 请求地址
|
||||
* @param params 参数实体
|
||||
* @return {
|
||||
* "code": "状态码", 200为成功
|
||||
* "data": "返回Json数据/返回错误描述",
|
||||
* }
|
||||
*/
|
||||
private static CzgResult<Object> sendCzg(String url, CzgBaseReqParams params) {
|
||||
CzgResult<Object> result = CzgResult.success();
|
||||
Map<String, Object> reqMap = BeanUtil.beanToMap(params, false, false);
|
||||
params.setSign(MD5Util.md5AsHex(sortFields(new TreeMap<>(reqMap))));
|
||||
log.info("超掌柜交易请求参数,{}", params);
|
||||
try (HttpResponse resp = HttpRequest.post(url).body(params.toString()).execute()) {
|
||||
if (resp.isOk()) {
|
||||
// 获取响应体
|
||||
String respStr = resp.body();
|
||||
if (StrUtil.isNotEmpty(respStr)) {
|
||||
CzgBaseRespParams respParams = JSONObject.parseObject(respStr, CzgBaseRespParams.class);
|
||||
log.info("超掌柜交易请求响应,{}", respParams);
|
||||
|
||||
result.setCode("000000".equals(respParams.getCode()) ? 200 : Integer.parseInt(respParams.getCode()));
|
||||
result.setMsg(respParams.getMsg());
|
||||
if ("000000".equals(respParams.getCode()) && StrUtil.isNotBlank(respParams.getSign())) {
|
||||
validateSign(respParams.getSign(), respParams.getBizData());
|
||||
result.setData(respParams.getBizData());
|
||||
}
|
||||
} else {
|
||||
throw new Exception("超掌柜交易请求异常");
|
||||
}
|
||||
} else {
|
||||
result.setCode(resp.getStatus());
|
||||
result.setMsg("超掌柜交易请求失败");
|
||||
log.error("超掌柜交易请求失败,状态码: {}", resp.getStatus());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("超掌柜交易请求异常", e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sign 签名
|
||||
* @param dataJsonStr 业务数据
|
||||
* @throws Exception 验签失败
|
||||
*/
|
||||
private static void validateSign(String sign, String dataJsonStr) throws Exception {
|
||||
Map dataMap = JSONObject.parseObject(dataJsonStr, Map.class);
|
||||
String newSign = MD5Util.md5AsHex(sortFields(new TreeMap<>(dataMap)));
|
||||
if (!sign.equals(newSign)) {
|
||||
throw new Exception("验签失败");
|
||||
}
|
||||
}
|
||||
|
||||
private static String sortFields(TreeMap<String, Object> map) {
|
||||
if (CollectionUtil.isEmpty(map)) {
|
||||
log.error("超掌柜支付参数为空!!!");
|
||||
throw new RuntimeException("参数为空");
|
||||
}
|
||||
String sortParam = map.entrySet().stream()
|
||||
// 过滤掉键为 "sign" 的元素
|
||||
.filter(entry -> !"sign".equals(entry.getKey()) && !"appSecret".equals(entry.getKey()))
|
||||
// 将每个键值对转换为 "key=value" 格式的字符串
|
||||
.map(entry -> entry.getKey() + "=" + entry.getValue())
|
||||
// 使用 "&" 连接所有转换后的字符串
|
||||
.collect(Collectors.joining("&"));
|
||||
if (map.containsKey("appSecret")) {
|
||||
sortParam = sortParam.concat("&" + "appSecret=" + map.get("appSecret"));
|
||||
}
|
||||
return sortParam;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue