超掌柜支付 回调 封装 over

This commit is contained in:
wangw 2025-02-11 11:02:31 +08:00
parent 9cea09d2c1
commit 48a9376657
10 changed files with 386 additions and 56 deletions

1
.gitignore vendored
View File

@ -18,6 +18,7 @@ target/
*.iws
*.iml
*.ipr
*.jar
### NetBeans ###
/nbproject/private/

View File

@ -6,12 +6,15 @@ import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import com.czg.entity.CzgBaseReqParams;
import com.czg.entity.CzgBaseRespParams;
import com.czg.entity.req.*;
import com.czg.entity.resp.*;
import com.czg.enums.CzgPayEnum;
import com.czg.resp.CzgRespCode;
import com.czg.resp.CzgResult;
import com.czg.utils.AssertUtil;
import com.czg.utils.MD5Util;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
@ -34,8 +37,8 @@ public class CzgPayUtils {
* @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));
public static CzgResult<CzgH5PayResp> h5Pay(@NonNull String domain, @NonNull String appId, @NonNull String appSecret, CzgH5PayReq bizData) {
return sendCzg(domain.concat(CzgPayEnum.H5PAY.getUri()), CzgBaseReqParams.getInstance(appId, appSecret, bizData), CzgH5PayResp.class);
}
/**
@ -45,8 +48,8 @@ public class CzgPayUtils {
* @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));
public static CzgResult<CzgJsPayResp> jsPay(@NonNull String domain, @NonNull String appId, @NonNull String appSecret, CzgJsPayReq bizData) {
return sendCzg(domain.concat(CzgPayEnum.JSPAY.getUri()), CzgBaseReqParams.getInstance(appId, appSecret, bizData), CzgJsPayResp.class);
}
/**
@ -56,8 +59,8 @@ public class CzgPayUtils {
* @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));
public static CzgResult<CzgLtPayResp> ltPay(@NonNull String domain, @NonNull String appId, @NonNull String appSecret, CzgLtPayReq bizData) {
return sendCzg(domain.concat(CzgPayEnum.LTPAY.getUri()), CzgBaseReqParams.getInstance(appId, appSecret, bizData), CzgLtPayResp.class);
}
/**
@ -67,8 +70,8 @@ public class CzgPayUtils {
* @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));
public static CzgResult<CzgScanPayResp> scanPay(@NonNull String domain, @NonNull String appId, @NonNull String appSecret, CzgScanPayReq bizData) {
return sendCzg(domain.concat(CzgPayEnum.SCANPAY.getUri()), CzgBaseReqParams.getInstance(appId, appSecret, bizData), CzgScanPayResp.class);
}
/**
@ -78,8 +81,8 @@ public class CzgPayUtils {
* @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));
public static CzgResult<CzgMicroPayResp> microPay(@NonNull String domain, @NonNull String appId, @NonNull String appSecret, CzgMicroPayReq bizData) {
return sendCzg(domain.concat(CzgPayEnum.MICROPAY.getUri()), CzgBaseReqParams.getInstance(appId, appSecret, bizData), CzgMicroPayResp.class);
}
/**
@ -91,12 +94,12 @@ public class CzgPayUtils {
* @param payOrderId 平台订单号
* @param mchOrderNo 商户订单号
*/
public static CzgResult<Object> queryPayOrder(@NonNull String domain, @NonNull String appId, @NonNull String appSecret,
String payOrderId, String mchOrderNo) {
public static CzgResult<CzgBaseResp> 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));
return sendCzg(domain.concat(CzgPayEnum.TRADE.getUri()), CzgBaseReqParams.getInstance(appId, appSecret, queryPayOrder), CzgBaseResp.class);
}
@ -107,8 +110,8 @@ public class CzgPayUtils {
* @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));
public static CzgResult<CzgRefundResp> refundOrder(@NonNull String domain, @NonNull String appId, @NonNull String appSecret, CzgRefundReq bizData) {
return sendCzg(domain.concat(CzgPayEnum.REFUND.getUri()), CzgBaseReqParams.getInstance(appId, appSecret, bizData), CzgRefundResp.class);
}
/**
@ -120,12 +123,12 @@ public class CzgPayUtils {
* @param mchRefundNo 商户退款订单号 二选一
* @param refundOrderId 平台退款订单号 二选一
*/
public static CzgResult<Object> queryRefundOrder(@NonNull String domain, @NonNull String appId, @NonNull String appSecret,
String mchRefundNo, String refundOrderId) {
public static CzgResult<CzgRefundResp> 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));
return sendCzg(domain.concat(CzgPayEnum.QUERY_REFUND.getUri()), CzgBaseReqParams.getInstance(appId, appSecret, queryPayOrder), CzgRefundResp.class);
}
@ -136,22 +139,20 @@ public class CzgPayUtils {
* @param clazz 返回的实体类
*/
public static <T> T getCzg(String dataJsonStr, Class<T> clazz) {
if (StrUtil.isNotEmpty(dataJsonStr)) {
CzgBaseRespParams respParams = JSONObject.parseObject(dataJsonStr, CzgBaseRespParams.class);
log.info("超掌柜交易请求响应,{}", respParams);
if (!"000000".equals(respParams.getCode())) {
log.error("超掌柜回调响应失败,{}", respParams);
return null;
}
if (StrUtil.isNotBlank(respParams.getSign())) {
if (!validateSign(respParams.getSign(), respParams.getBizData())) {
log.error("超掌柜回调 验签失败,{}", respParams);
}
}
return JSONObject.parseObject(respParams.getBizData(), clazz);
} else {
AssertUtil.isBlank(dataJsonStr, "超掌柜交易 回调数据为空");
CzgBaseRespParams respParams = JSONObject.parseObject(dataJsonStr, CzgBaseRespParams.class);
log.info("超掌柜交易请求响应,{}", respParams);
if (!"000000".equals(respParams.getCode())) {
log.error("超掌柜回调响应失败,{}", respParams);
return null;
}
if (StrUtil.isNotBlank(respParams.getSign())) {
if (validateSign(respParams.getSign(), respParams.getBizData())) {
log.error("超掌柜回调 验签失败,{}", respParams);
}
}
return JSONObject.parseObject(respParams.getBizData(), clazz);
}
/**
@ -161,11 +162,12 @@ public class CzgPayUtils {
* @param params 参数实体
* @return {
* "code": "状态码", 200为成功
* "data": "返回Json数据/返回错误描述",
* "msg": "描述"
* "data": "返回Json数据",
* }
*/
private static CzgResult<Object> sendCzg(String url, CzgBaseReqParams params) {
CzgResult<Object> result = CzgResult.success();
private static <T> CzgResult<T> sendCzg(String url, CzgBaseReqParams params, Class<T> clazz) {
CzgResult<T> result = CzgResult.success();
Map<String, Object> reqMap = BeanUtil.beanToMap(params, false, false);
params.setSign(MD5Util.md5AsHex(sortFields(new TreeMap<>(reqMap))));
log.info("超掌柜交易请求参数,{}", params);
@ -180,11 +182,11 @@ public class CzgPayUtils {
result.setCode("000000".equals(respParams.getCode()) ? 200 : Integer.parseInt(respParams.getCode()));
result.setMsg(respParams.getMsg());
if ("000000".equals(respParams.getCode()) && StrUtil.isNotBlank(respParams.getSign())) {
if (!validateSign(respParams.getSign(), respParams.getBizData())) {
if (validateSign(respParams.getSign(), respParams.getBizData())) {
result.setCode(CzgRespCode.FAILURE.getCode());
result.setMsg("验签失败");
}
result.setData(respParams.getBizData());
result.setData(JSONObject.parseObject(result.getData().toString(), clazz));
}
} else {
result.setCode(resp.getStatus());
@ -204,15 +206,12 @@ public class CzgPayUtils {
/**
* @param sign 签名
* @param dataJsonStr 业务数据
* @return true 验签通过 false 验签失败
* @return false 验签通过 true 验签失败
*/
private static boolean validateSign(String sign, String dataJsonStr) {
Map dataMap = JSONObject.parseObject(dataJsonStr, Map.class);
Map<String, Object> dataMap = JSONObject.parseObject(dataJsonStr, new TypeReference<>() {});
String newSign = MD5Util.md5AsHex(sortFields(new TreeMap<>(dataMap)));
if (!sign.equals(newSign)) {
return false;
}
return true;
return !StrUtil.equals(sign, newSign);
}
private static String sortFields(TreeMap<String, Object> map) {

View File

@ -0,0 +1,45 @@
package com.czg.entity.notify;
import com.czg.entity.resp.CzgBaseResp;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author ww
* @description
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class CzgPayNotifyDTO extends CzgBaseResp {
/**
* 消息类型 String(30)
* sft.trade.notify 支付通知
* sft.refund.notify 退款通知
* sft.division.notify 分账通知
*/
private String msgType;
/**
* 门店ID String(30)
*/
private String storeId;
/**
* 货币代码
*/
private String currency;
private String subject;
private String body;
//非必填
private String userId;
/**
* 支付成功时间
* yyyy-MM-dd HH:mm:ss
*/
private String payTime;
}

View File

@ -0,0 +1,84 @@
package com.czg.entity.notify;
import lombok.Data;
/**
* @author ww
* @description
*/
@Data
public class CzgRefundNotifyDTO {
/**
* 消息类型 String(30)
* sft.trade.notify 支付通知
* sft.refund.notify 退款通知
* sft.division.notify 分账通知
*/
private String msgType;
/**
* 商户退款订单号 String(30)
* 商户上送的退款订单号
*/
private String mchRefundNo;
/**
* 平台退款订单号 String(30)
*
*/
private String refundOrderId;
/**
* 退款状态 String(10)
* INIT初始化
* ING退款中
* SUCCESS退款成功
* FAIL退款失败
* CLOSE退款关闭
*/
private String state;
/**
* 原平台交易单号 String(32)
*/
private String oriPayOrderId;
/**
* 商户号 String(30)
*/
private String mercNo;
/**
* 原订单交易金额
* 单位
*/
private Long oriAmount;
/**
* 退款金额
* 单位
*/
private Long refundAmt;
/**
* 退款原因
*/
private String refundReason;
//非必填
/**
* 所属渠道 String(10)
*/
private String ifCode;
/**
* 备注 String(30)
*/
private String note;
/**
* 退款完成时间 String(20)
* 格式yyyy-MM-dd HH:mm:ss
*/
private String refundTime;
/**
* 自定义扩展参数 String(256)
*/
private String extParam;
/**
* 支付方式
* WECHAT微信
* ALIPAY支付宝
* UNIONPAY银联云闪付
*/
private String payType;
}

View File

@ -16,7 +16,7 @@ public class CzgBaseResp {
*/
private Long amount;
/**
* String(32)
* String (32)
* 商户订单号
*/
private String mchOrderNo;
@ -31,19 +31,19 @@ public class CzgBaseResp {
/**
* 订单状态
* INIT - 订单初始化
* TRADE_AWAIT- 待支付
* TRADE_AWAIT - 待支付
* TRADE_SUCCESS - 支付成功
* TRADE_FAIL-支付失败
* TRADE_CANCEL-交易取消
* TRADE_REFUND-已退款
* TRADE_FAIL -支付失败
* TRADE_CANCEL -交易取消
* TRADE_REFUND -已退款
* REFUND_ING - 退款中
* TRADE_CLOSE-订单关闭
* TRADE_CLOSE -订单关闭
*/
private String state;
/**
* 订单类型
* 微信 WECHAT
* 支付宝 ALIPAY
* 微信 WECHAT
* 支付宝 ALIPAY
* 银联云闪付 UNIONPAY
*/
private String payType;
@ -58,12 +58,12 @@ public class CzgBaseResp {
//非必填范围
/**
* 发往渠道的流水号
* 微信/支付宝订单详情中的商户单号
* 微信 / 支付宝订单详情中的商户单号
*/
private String channelSendNo;
/**
* 渠道订单号
* 微信/支付宝订单号
* 微信 / 支付宝订单号
*/
private String channelTradeNo;
/**
@ -81,7 +81,7 @@ public class CzgBaseResp {
*/
private Long tradeFee;
/**
* 门店id
* 门店 id
*/
private String storeId;
/**
@ -89,7 +89,13 @@ public class CzgBaseResp {
*/
private String subject;
/**
* 付款卡类型
* 交易卡类型
* 00借记卡
* 01贷记卡
* 02零钱/余额
* 03花呗
* 04数字货币
* 99其他
*/
private String drType;
/**

View File

@ -1,5 +1,6 @@
package com.czg.entity.resp;
import com.czg.entity.resp.pay.CzgH5PayInfo;
import lombok.Data;
import lombok.EqualsAndHashCode;
@ -9,6 +10,7 @@ import lombok.EqualsAndHashCode;
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class CzgH5PayResp extends CzgBaseResp{
public class CzgH5PayResp extends CzgBaseResp {
private CzgH5PayInfo payInfo;
}

View File

@ -0,0 +1,16 @@
package com.czg.entity.resp;
import com.czg.entity.resp.pay.CzgScanPayInfo;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author ww
* @description H5支付响应参数
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class CzgScanPayResp extends CzgBaseResp {
private CzgScanPayInfo payInfo;
}

View File

@ -17,4 +17,11 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.czg</groupId>
<artifactId>czg-pay</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,100 @@
package com.czg.service;
import com.czg.entity.notify.CzgPayNotifyDTO;
import com.czg.entity.notify.CzgRefundNotifyDTO;
import com.czg.entity.req.*;
import com.czg.entity.resp.*;
import com.czg.resp.CzgResult;
import lombok.NonNull;
/**
* @author ww
* @description
*/
public interface CzgPayService {
/**
* h5支付
*
* @param appId 应用id tb_shop_merchant 表中的 app_id
* @param appSecret 应用密钥 tb_shop_merchant 表中的 app_secret
*/
CzgResult<CzgH5PayResp> h5Pay(@NonNull String domain, @NonNull String appId, @NonNull String appSecret, CzgH5PayReq bizData);
/**
* js支付
*
* @param appId 应用id tb_shop_merchant 表中的 app_id
* @param appSecret 应用密钥 tb_shop_merchant 表中的 app_secret
*/
CzgResult<CzgJsPayResp> jsPay(@NonNull String domain, @NonNull String appId, @NonNull String appSecret, CzgJsPayReq bizData);
/**
* 小程序支付
*
* @param appId 应用id tb_shop_merchant 表中的 app_id
* @param appSecret 应用密钥 tb_shop_merchant 表中的 app_secret
*/
CzgResult<CzgLtPayResp> ltPay(@NonNull String domain, @NonNull String appId, @NonNull String appSecret, CzgLtPayReq bizData);
/**
* PC扫码支付
*
* @param appId 应用id tb_shop_merchant 表中的 app_id
* @param appSecret 应用密钥 tb_shop_merchant 表中的 app_secret
*/
CzgResult<CzgScanPayResp> scanPay(@NonNull String domain, @NonNull String appId, @NonNull String appSecret, CzgScanPayReq bizData);
/**
* 聚合反扫
*
* @param appId 应用id tb_shop_merchant 表中的 app_id
* @param appSecret 应用密钥 tb_shop_merchant 表中的 app_secret
*/
CzgResult<CzgMicroPayResp> microPay(@NonNull String domain,@NonNull String appId, @NonNull String appSecret, CzgMicroPayReq bizData);
/**
* 订单查询
*
* @param appId 应用id tb_shop_merchant 表中的 app_id
* @param appSecret 应用密钥 tb_shop_merchant 表中的 app_secret
* @param payOrderId 平台订单号
* @param mchOrderNo 商户订单号
*/
CzgResult<CzgBaseResp> queryPayOrder(@NonNull String domain,@NonNull String appId, @NonNull String appSecret,
String payOrderId, String mchOrderNo);
/**
* 订单退款
*
* @param appId 应用id tb_shop_merchant 表中的 app_id
* @param appSecret 应用密钥 tb_shop_merchant 表中的 app_secret
*/
CzgResult<CzgRefundResp> refundOrder(@NonNull String domain, @NonNull String appId, @NonNull String appSecret, CzgRefundReq bizData);
/**
* 退款订单查询
*
* @param appId 应用id tb_shop_merchant 表中的 app_id
* @param appSecret 应用密钥 tb_shop_merchant 表中的 app_secret
* @param mchRefundNo 商户退款订单号 二选一
* @param refundOrderId 平台退款订单号 二选一
*/
CzgResult<CzgRefundResp> queryRefundOrder(@NonNull String domain,@NonNull String appId, @NonNull String appSecret,
String mchRefundNo, String refundOrderId);
/**
* 支付回调数据处理
*
* @param dataJsonStr 带解析数据
*/
CzgPayNotifyDTO getPayNotifyData(String dataJsonStr);
/**
* 退款回调数据处理
*
* @param dataJsonStr 带解析数据
*/
CzgRefundNotifyDTO getRefundNotifyData(String dataJsonStr);
}

View File

@ -0,0 +1,70 @@
package com.czg.service.Impl;
import com.czg.CzgPayUtils;
import com.czg.entity.notify.CzgPayNotifyDTO;
import com.czg.entity.notify.CzgRefundNotifyDTO;
import com.czg.entity.req.*;
import com.czg.entity.resp.*;
import com.czg.resp.CzgResult;
import com.czg.service.CzgPayService;
import lombok.NonNull;
import org.springframework.stereotype.Service;
/**
* @author ww
* @description 1
*/
@Service
public class CzgPayServiceImpl implements CzgPayService {
@Override
public CzgResult<CzgH5PayResp> h5Pay(@NonNull String domain, @NonNull String appId, @NonNull String appSecret, CzgH5PayReq bizData) {
return CzgPayUtils.h5Pay(domain, appId, appSecret, bizData);
}
@Override
public CzgResult<CzgJsPayResp> jsPay(@NonNull String domain, @NonNull String appId, @NonNull String appSecret, CzgJsPayReq bizData) {
return CzgPayUtils.jsPay(domain, appId, appSecret, bizData);
}
@Override
public CzgResult<CzgLtPayResp> ltPay(@NonNull String domain, @NonNull String appId, @NonNull String appSecret, CzgLtPayReq bizData) {
return CzgPayUtils.ltPay(domain, appId, appSecret, bizData);
}
@Override
public CzgResult<CzgScanPayResp> scanPay(@NonNull String domain, @NonNull String appId, @NonNull String appSecret, CzgScanPayReq bizData) {
return CzgPayUtils.scanPay(domain, appId, appSecret, bizData);
}
@Override
public CzgResult<CzgMicroPayResp> microPay(@NonNull String domain, @NonNull String appId, @NonNull String appSecret, CzgMicroPayReq bizData) {
return CzgPayUtils.microPay(domain, appId, appSecret, bizData);
}
@Override
public CzgResult<CzgBaseResp> queryPayOrder(@NonNull String domain, @NonNull String appId, @NonNull String appSecret, String payOrderId, String mchOrderNo) {
return CzgPayUtils.queryPayOrder(domain, appId, appSecret, payOrderId, mchOrderNo);
}
@Override
public CzgResult<CzgRefundResp> refundOrder(@NonNull String domain, @NonNull String appId, @NonNull String appSecret, CzgRefundReq bizData) {
return CzgPayUtils.refundOrder(domain, appId, appSecret, bizData);
}
@Override
public CzgResult<CzgRefundResp> queryRefundOrder(@NonNull String domain, @NonNull String appId, @NonNull String appSecret, String mchRefundNo, String refundOrderId) {
return CzgPayUtils.queryRefundOrder(domain, appId, appSecret, mchRefundNo,refundOrderId);
}
@Override
public CzgPayNotifyDTO getPayNotifyData(String dataJsonStr) {
return CzgPayUtils.getCzg(dataJsonStr,CzgPayNotifyDTO.class);
}
@Override
public CzgRefundNotifyDTO getRefundNotifyData(String dataJsonStr) {
return CzgPayUtils.getCzg(dataJsonStr,CzgRefundNotifyDTO.class);
}
}