diff --git a/daxpay-channel/daxpay-channel-hkrt/pom.xml b/daxpay-channel/daxpay-channel-hkrt/pom.xml
new file mode 100644
index 000000000..aea2f8afc
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/pom.xml
@@ -0,0 +1,16 @@
+
+
+ 4.0.0
+
+
+ cn.daxpay.open
+ daxpay-channel
+ 4.0.0-SNAPSHOT
+
+
+ daxpay-channel-hkrt
+ jar
+ 海科融通支付通道
+
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/HkrtChannelApp.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/HkrtChannelApp.java
new file mode 100644
index 000000000..0c730bb3e
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/HkrtChannelApp.java
@@ -0,0 +1,16 @@
+package cn.daxpay.open.channel.hkrt;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
+import org.springframework.context.annotation.ComponentScan;
+
+/// # 海科融通支付实现
+///
+@AutoConfiguration
+@ConfigurationPropertiesScan
+@MapperScan(annotationClass = Mapper.class)
+@ComponentScan
+public class HkrtChannelApp {
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/HkrtChannelClient.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/HkrtChannelClient.java
new file mode 100644
index 000000000..c45c8f4e5
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/HkrtChannelClient.java
@@ -0,0 +1,56 @@
+package cn.daxpay.open.channel.hkrt.client;
+
+import cn.daxpay.open.channel.hkrt.client.req.HkrtCallbackParseReq;
+import cn.daxpay.open.channel.hkrt.client.req.HkrtCloseReq;
+import cn.daxpay.open.channel.hkrt.client.req.HkrtPayReq;
+import cn.daxpay.open.channel.hkrt.client.req.HkrtRefundReq;
+import cn.daxpay.open.channel.hkrt.client.req.HkrtRefundSyncReq;
+import cn.daxpay.open.channel.hkrt.client.req.HkrtSyncReq;
+import cn.daxpay.open.channel.hkrt.client.resp.HkrtCallbackParseResp;
+import cn.daxpay.open.channel.hkrt.client.resp.HkrtCloseResp;
+import cn.daxpay.open.channel.hkrt.client.resp.HkrtPayResp;
+import cn.daxpay.open.channel.hkrt.client.resp.HkrtRefundResp;
+import cn.daxpay.open.channel.hkrt.client.resp.HkrtRefundSyncResp;
+import cn.daxpay.open.channel.hkrt.client.resp.HkrtSyncResp;
+import cn.daxpay.open.payment.common.result.DaxResult;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.service.annotation.HttpExchange;
+import org.springframework.web.service.annotation.PostExchange;
+
+/// # 海科融通通道客户端
+///
+/// 声明式 HTTP 接口, 调用子应用 dax-pay-channel-two 的海科融通通道接口。
+/// 海科融通为服务商模式, 路径前缀 `/channel/hkrt`。
+/// 回调验签与解析统一转发子应用(parse-pay / parse-refund), 主应用不再自验签。
+@HttpExchange(accept = MediaType.APPLICATION_JSON_VALUE, contentType = MediaType.APPLICATION_JSON_VALUE)
+public interface HkrtChannelClient {
+
+ /// 支付下单
+ @PostExchange("/channel/hkrt/pay")
+ DaxResult pay(@RequestBody HkrtPayReq req);
+
+ /// 支付同步(查询海科融通订单状态)
+ @PostExchange("/channel/hkrt/sync")
+ DaxResult sync(@RequestBody HkrtSyncReq req);
+
+ /// 关闭订单
+ @PostExchange("/channel/hkrt/close")
+ DaxResult close(@RequestBody HkrtCloseReq req);
+
+ /// 退款
+ @PostExchange("/channel/hkrt/refund")
+ DaxResult refund(@RequestBody HkrtRefundReq req);
+
+ /// 退款同步(查询退款状态)
+ @PostExchange("/channel/hkrt/refund-sync")
+ DaxResult refundSync(@RequestBody HkrtRefundSyncReq req);
+
+ /// 支付回调验签解析(转发子应用)
+ @PostExchange("/channel/hkrt/callback/parse-pay")
+ DaxResult parsePayCallback(@RequestBody HkrtCallbackParseReq req);
+
+ /// 退款回调验签解析(转发子应用)
+ @PostExchange("/channel/hkrt/callback/parse-refund")
+ DaxResult parseRefundCallback(@RequestBody HkrtCallbackParseReq req);
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/credential/HkrtSdkCredential.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/credential/HkrtSdkCredential.java
new file mode 100644
index 000000000..b7938ef6e
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/credential/HkrtSdkCredential.java
@@ -0,0 +1,26 @@
+package cn.daxpay.open.channel.hkrt.client.credential;
+
+import lombok.Data;
+
+/// # 海科融通 SDK 凭证(主应用侧)
+///
+/// 与子应用 dax-pay-channel-two 的 `HkrtSdkCredential` 镜像, 跨 HTTP 传输时字段对齐。
+/// 主应用从服务商密钥配置(HkrtIsvKeyConfig) + 通道商户绑定(HkrtIsvChannelMerchant) 组装,
+/// 下发给子应用发起海科融通 API 调用。
+///
+/// 海科融通签名机制: 仅用 accessKey 做 MD5 大写签名(参数字母升序 + 末尾拼 accessKey), 无需证书。
+@Data
+public class HkrtSdkCredential {
+ /// 服务商编号(agent_no)
+ private String agentNo;
+ /// 海科商户编号(merch_no)
+ private String merchNo;
+ /// 接入机构标识(access_id)
+ private String accessId;
+ /// SAAS 终端号(pn)
+ private String pn;
+ /// 签名密钥(access_key, MD5 大写签名)
+ private String accessKey;
+ /// 是否沙箱环境
+ private Boolean sandbox;
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/enums/HkrtPayBodyType.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/enums/HkrtPayBodyType.java
new file mode 100644
index 000000000..b2243758f
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/enums/HkrtPayBodyType.java
@@ -0,0 +1,13 @@
+package cn.daxpay.open.channel.hkrt.client.enums;
+
+/// # 海科融通支付内容类型(主应用侧, 与子应用镜像)
+///
+/// 海科融通无 LINK(跳转链接) 类型, 仅 QR_CODE / JSAPI / IDENTIFIER 三种。
+public enum HkrtPayBodyType {
+ /// 二维码内容
+ QR_CODE,
+ /// JSAPI/小程序调起参数 JSON
+ JSAPI,
+ /// 通用标识码
+ IDENTIFIER;
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/enums/HkrtPayMethod.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/enums/HkrtPayMethod.java
new file mode 100644
index 000000000..89925a875
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/enums/HkrtPayMethod.java
@@ -0,0 +1,17 @@
+package cn.daxpay.open.channel.hkrt.client.enums;
+
+/// # 海科融通支付方式(主应用侧, 与子应用镜像)
+///
+/// 海科融通的支付方式由 method 单独决定, 不像拉卡拉需要 accountType + transType 三要素。
+public enum HkrtPayMethod {
+ /// 微信 JSAPI(公众号 / 小程序)
+ WECHAT_JSAPI,
+ /// 支付宝扫码(当面付二维码)
+ ALIPAY_QR,
+ /// 支付宝 JSAPI(生活号 / 小程序)
+ ALIPAY_JSAPI,
+ /// 银联二维码(云闪付)
+ UNION_QR,
+ /// 条码支付(微信 / 支付宝 / 银联付款码被扫)
+ BARCODE;
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/req/HkrtCallbackParseReq.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/req/HkrtCallbackParseReq.java
new file mode 100644
index 000000000..7fb920b42
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/req/HkrtCallbackParseReq.java
@@ -0,0 +1,24 @@
+package cn.daxpay.open.channel.hkrt.client.req;
+
+import cn.daxpay.open.channel.hkrt.client.credential.HkrtSdkCredential;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+/// # 海科融通回调验签解析请求(主应用侧, 与子应用镜像)
+///
+/// 主应用收到海科异步通知后, 将原始报文转发到子应用做验签与字段解析。
+@Data
+public class HkrtCallbackParseReq {
+
+ /// 通道调用凭证(用 accessKey 验签)
+ @NotNull(message = "{validation.field.credential.notNull}")
+ private HkrtSdkCredential credential;
+
+ /// 海科回调原始报文(JSON 字符串)
+ @NotBlank(message = "{validation.field.rawData.notBlank}")
+ private String rawData;
+
+ /// 是否退款回调(true=退款回调, false=支付回调)
+ private boolean refund;
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/req/HkrtCloseReq.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/req/HkrtCloseReq.java
new file mode 100644
index 000000000..280b0d5aa
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/req/HkrtCloseReq.java
@@ -0,0 +1,20 @@
+package cn.daxpay.open.channel.hkrt.client.req;
+
+import cn.daxpay.open.channel.hkrt.client.credential.HkrtSdkCredential;
+import lombok.Data;
+
+/// # 海科融通通道关单请求(主应用侧, 与子应用镜像)
+@Data
+public class HkrtCloseReq {
+
+ private HkrtSdkCredential credential;
+
+ /// 原商户订单号(与 originTradeNo 二选一)
+ private String originOutTradeNo;
+
+ /// 原海科融通交易号(与 originOutTradeNo 二选一)
+ private String originTradeNo;
+
+ /// 客户端IP
+ private String clientIp;
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/req/HkrtPayReq.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/req/HkrtPayReq.java
new file mode 100644
index 000000000..cf696318a
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/req/HkrtPayReq.java
@@ -0,0 +1,52 @@
+package cn.daxpay.open.channel.hkrt.client.req;
+
+import cn.daxpay.open.channel.hkrt.client.credential.HkrtSdkCredential;
+import cn.daxpay.open.channel.hkrt.client.enums.HkrtPayBodyType;
+import cn.daxpay.open.channel.hkrt.client.enums.HkrtPayMethod;
+import lombok.Data;
+
+import java.time.OffsetDateTime;
+
+/// # 海科融通通道支付请求(主应用侧, 与子应用镜像)
+///
+/// 海科融通的支付方式由 method 单独决定, 不需要 accountType/transType/payBodyType 三要素。
+/// amount 单位为分, 透传给子应用, 由子应用负责分→元转换。
+@Data
+public class HkrtPayReq {
+
+ /// 通道调用凭证
+ private HkrtSdkCredential credential;
+
+ /// 商户订单号(主应用支付交易号)
+ private String outTradeNo;
+
+ /// 订单金额(单位: 分)
+ private Long amount;
+
+ /// 商品标题
+ private String title;
+
+ /// 商品描述
+ private String description;
+
+ /// 支付方式(WECHAT_JSAPI / ALIPAY_QR / ALIPAY_JSAPI / UNION_QR / BARCODE)
+ private HkrtPayMethod method;
+
+ /// 用户标识(JSAPI/MINI 必填)
+ private String openId;
+
+ /// 付款码(BARCODE 必填)
+ private String authCode;
+
+ /// 客户端IP
+ private String clientIp;
+
+ /// 异步通知地址
+ private String notifyUrl;
+
+ /// 订单过期时间
+ private OffsetDateTime expireTime;
+
+ /// 支付内容类型(主应用预先计算, 子应用透传到响应)
+ private HkrtPayBodyType payBodyType;
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/req/HkrtRefundReq.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/req/HkrtRefundReq.java
new file mode 100644
index 000000000..468c61397
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/req/HkrtRefundReq.java
@@ -0,0 +1,29 @@
+package cn.daxpay.open.channel.hkrt.client.req;
+
+import cn.daxpay.open.channel.hkrt.client.credential.HkrtSdkCredential;
+import lombok.Data;
+
+/// # 海科融通通道退款请求(主应用侧, 与子应用镜像)
+@Data
+public class HkrtRefundReq {
+
+ private HkrtSdkCredential credential;
+
+ /// 商户退款单号
+ private String outRefundNo;
+
+ /// 原商户订单号
+ private String originOutTradeNo;
+
+ /// 原海科融通交易号
+ private String originTradeNo;
+
+ /// 退款金额(单位: 分)
+ private Long amount;
+
+ /// 退款原因
+ private String reason;
+
+ /// 客户端IP
+ private String clientIp;
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/req/HkrtRefundSyncReq.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/req/HkrtRefundSyncReq.java
new file mode 100644
index 000000000..dcea462b9
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/req/HkrtRefundSyncReq.java
@@ -0,0 +1,17 @@
+package cn.daxpay.open.channel.hkrt.client.req;
+
+import cn.daxpay.open.channel.hkrt.client.credential.HkrtSdkCredential;
+import lombok.Data;
+
+/// # 海科融通通道退款查询请求(主应用侧, 与子应用镜像)
+@Data
+public class HkrtRefundSyncReq {
+
+ private HkrtSdkCredential credential;
+
+ /// 商户退款单号(与 originTradeNo 二选一)
+ private String outRefundNo;
+
+ /// 原海科融通退款交易号(与 outRefundNo 二选一)
+ private String originTradeNo;
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/req/HkrtSyncReq.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/req/HkrtSyncReq.java
new file mode 100644
index 000000000..514114961
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/req/HkrtSyncReq.java
@@ -0,0 +1,17 @@
+package cn.daxpay.open.channel.hkrt.client.req;
+
+import cn.daxpay.open.channel.hkrt.client.credential.HkrtSdkCredential;
+import lombok.Data;
+
+/// # 海科融通通道订单查询请求(主应用侧, 与子应用镜像)
+@Data
+public class HkrtSyncReq {
+
+ private HkrtSdkCredential credential;
+
+ /// 商户订单号(与 tradeNo 二选一)
+ private String outTradeNo;
+
+ /// 海科融通交易号(与 outTradeNo 二选一)
+ private String tradeNo;
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/resp/HkrtCallbackParseResp.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/resp/HkrtCallbackParseResp.java
new file mode 100644
index 000000000..6ae17985c
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/resp/HkrtCallbackParseResp.java
@@ -0,0 +1,45 @@
+package cn.daxpay.open.channel.hkrt.client.resp;
+
+import lombok.Data;
+
+import java.time.OffsetDateTime;
+
+/// # 海科融通回调解析响应(主应用侧, 与子应用镜像)
+///
+/// 子应用对海科异步通知验签通过后, 将解析出的标准化字段回传主应用。
+@Data
+public class HkrtCallbackParseResp {
+
+ /// 是否验签通过
+ private Boolean success;
+
+ /// 海科交易号(trade_no)
+ private String tradeNo;
+
+ /// 商户订单号(out_trade_no, 支付回调)或商户退款单号(退款回调)
+ private String outTradeNo;
+
+ /// 银行交易号(bank_trade_no)
+ private String transOrderNo;
+
+ /// 交易状态(抽象态 SUCCESS / FAIL, 已屏蔽海科数字码)
+ private String tradeStatus;
+
+ /// 金额(单位: 分, order_amount / refund_amount 元转分)
+ private Long amount;
+
+ /// 完成时间(东八区 OffsetDateTime)
+ private OffsetDateTime finishTime;
+
+ /// 支付类型(扩展字段: WX/ALI/UNIONQR)
+ private String payType;
+
+ /// 支付模式(扩展字段: JSAPI/NATIVE)
+ private String payMode;
+
+ /// 买家标识(微信 openid / 支付宝 buyer_openid)
+ private String buyerId;
+
+ /// 银行卡类型(银联回调时的 card_type)
+ private String bankType;
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/resp/HkrtCloseResp.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/resp/HkrtCloseResp.java
new file mode 100644
index 000000000..561cb7b95
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/resp/HkrtCloseResp.java
@@ -0,0 +1,8 @@
+package cn.daxpay.open.channel.hkrt.client.resp;
+
+import lombok.Data;
+
+/// # 海科融通通道关单响应(主应用侧, 与子应用镜像)
+@Data
+public class HkrtCloseResp {
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/resp/HkrtPayResp.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/resp/HkrtPayResp.java
new file mode 100644
index 000000000..0ca7a6afd
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/resp/HkrtPayResp.java
@@ -0,0 +1,21 @@
+package cn.daxpay.open.channel.hkrt.client.resp;
+
+import cn.daxpay.open.channel.hkrt.client.enums.HkrtPayBodyType;
+import lombok.Data;
+
+import java.time.OffsetDateTime;
+
+/// # 海科融通通道支付响应(主应用侧, 与子应用镜像)
+@Data
+public class HkrtPayResp {
+
+ private String outTradeNo;
+ private String tradeNo;
+ private String payBody;
+ private HkrtPayBodyType payBodyType;
+ private Boolean complete;
+ private Long totalAmount;
+ private Long payerAmount;
+ private OffsetDateTime finishTime;
+ private String buyerId;
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/resp/HkrtRefundResp.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/resp/HkrtRefundResp.java
new file mode 100644
index 000000000..92fc2640d
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/resp/HkrtRefundResp.java
@@ -0,0 +1,25 @@
+package cn.daxpay.open.channel.hkrt.client.resp;
+
+import lombok.Data;
+
+import java.time.OffsetDateTime;
+
+/// # 海科融通通道退款响应(主应用侧, 与子应用镜像)
+@Data
+public class HkrtRefundResp {
+
+ /// 商户退款单号(透传请求 outRefundNo)
+ private String outRefundNo;
+
+ /// 海科退款订单号(refund_no)
+ private String tradeNo;
+
+ /// 交易状态(抽象态 SUCCESS / FAIL / PROCESSING, 已屏蔽海科 trade_status 数字码)
+ private String tradeStatus;
+
+ /// 是否已终态完成
+ private Boolean complete;
+
+ /// 完成时间
+ private OffsetDateTime finishTime;
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/resp/HkrtRefundSyncResp.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/resp/HkrtRefundSyncResp.java
new file mode 100644
index 000000000..c9d6701f6
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/resp/HkrtRefundSyncResp.java
@@ -0,0 +1,17 @@
+package cn.daxpay.open.channel.hkrt.client.resp;
+
+import lombok.Data;
+
+import java.time.OffsetDateTime;
+
+/// # 海科融通通道退款查询响应(主应用侧, 与子应用镜像)
+@Data
+public class HkrtRefundSyncResp {
+
+ private String outRefundNo;
+ private String tradeNo;
+ /// 退款状态: SUCCESS / FAIL / PROCESSING
+ private String refundStatus;
+ private OffsetDateTime finishTime;
+ private String syncData;
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/resp/HkrtSyncResp.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/resp/HkrtSyncResp.java
new file mode 100644
index 000000000..7cb3b66ba
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/client/resp/HkrtSyncResp.java
@@ -0,0 +1,19 @@
+package cn.daxpay.open.channel.hkrt.client.resp;
+
+import lombok.Data;
+
+import java.time.OffsetDateTime;
+
+/// # 海科融通通道订单查询响应(主应用侧, 与子应用镜像)
+@Data
+public class HkrtSyncResp {
+
+ private String outTradeNo;
+ private String tradeNo;
+ /// 交易状态: SUCCESS / FAIL / CLOSED / 其他(处理中)
+ private String tradeState;
+ private Long totalAmount;
+ private String buyerId;
+ private OffsetDateTime finishTime;
+ private String syncData;
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/config/HkrtClientConfig.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/config/HkrtClientConfig.java
new file mode 100644
index 000000000..6babdd41e
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/config/HkrtClientConfig.java
@@ -0,0 +1,35 @@
+package cn.daxpay.open.channel.hkrt.config;
+
+import cn.daxpay.open.channel.hkrt.client.HkrtChannelClient;
+import cn.daxpay.open.platform.common.config.properties.DaxpayChannelProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.client.RestClient;
+import org.springframework.web.client.support.RestClientAdapter;
+import org.springframework.web.service.invoker.HttpServiceProxyFactory;
+
+/// # 海科融通通道客户端配置
+///
+/// 复用全局 [RestClient](由 common-spring 的 RestClientConfiguration 提供, 已预装:
+/// OTel traceparent 透传 / BusinessContextInterceptor 业务上下文透传 / Apache HttpClient5 连接池),
+/// 经 `mutate()` 派生出绑定 channel-two baseUrl 的实例。
+///
+/// baseUrl 从 [DaxpayChannelProperties] 统一读取。
+@Configuration
+public class HkrtClientConfig {
+
+ @Bean
+ public HkrtChannelClient hkrtChannelClient(
+ RestClient restClient,
+ DaxpayChannelProperties channelProperties) {
+ // mutate 派生: 继承全局 RestClient 的拦截器 / observation / requestFactory, 仅覆盖 baseUrl
+ RestClient channelClient = restClient
+ .mutate()
+ .baseUrl(channelProperties.getTwo().getBaseUrl())
+ .build();
+ return HttpServiceProxyFactory
+ .builderFor(RestClientAdapter.create(channelClient))
+ .build()
+ .createClient(HkrtChannelClient.class);
+ }
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/controller/callback/HkrtCallbackController.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/controller/callback/HkrtCallbackController.java
new file mode 100644
index 000000000..37ba217fe
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/controller/callback/HkrtCallbackController.java
@@ -0,0 +1,45 @@
+package cn.daxpay.open.channel.hkrt.controller.callback;
+
+import cn.daxpay.open.channel.hkrt.service.callback.HkrtPayCallbackService;
+import cn.daxpay.open.channel.hkrt.service.callback.HkrtRefundCallbackService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/// # 海科融通支付回调通知控制器
+///
+/// 海科融通异步通知入口(支付/退款), 不走 Sa-Token 认证(由安全配置放行 `/unipay/callback/**`)。
+/// 海科融通回调验签只需全局服务商 accessKey(从 HkrtIsvKeyConfig 读取), 不需 channelMchNo,
+/// 因此路径不带 channelMchNo, 凭 out_trade_no 反查 PayTrade。
+@Tag(name = "海科融通支付回调通知控制器")
+@RestController
+@RequestMapping("/unipay/callback/{mchNo}/{appId}/hkrt")
+@RequiredArgsConstructor
+public class HkrtCallbackController {
+
+ private final HkrtPayCallbackService hkrtPayCallbackService;
+ private final HkrtRefundCallbackService hkrtRefundCallbackService;
+
+ /// 海科融通支付回调
+ @Operation(summary = "海科融通支付回调")
+ @PostMapping("/pay")
+ public String payNotify(@PathVariable("mchNo") String mchNo,
+ @PathVariable("appId") String appId,
+ HttpServletRequest request) {
+ return hkrtPayCallbackService.payHandle(request);
+ }
+
+ /// 海科融通退款回调
+ @Operation(summary = "海科融通退款回调")
+ @PostMapping("/refund")
+ public String refundNotify(@PathVariable("mchNo") String mchNo,
+ @PathVariable("appId") String appId,
+ HttpServletRequest request) {
+ return hkrtRefundCallbackService.refundHandle(request);
+ }
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/controller/isv/HkrtIsvChannelMerchantController.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/controller/isv/HkrtIsvChannelMerchantController.java
new file mode 100644
index 000000000..954a52f50
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/controller/isv/HkrtIsvChannelMerchantController.java
@@ -0,0 +1,57 @@
+package cn.daxpay.open.channel.hkrt.controller.isv;
+
+import cn.daxpay.open.channel.hkrt.param.isv.HkrtIsvChannelMerchantCreateParam;
+import cn.daxpay.open.channel.hkrt.result.isv.HkrtIsvChannelMerchantResult;
+import cn.daxpay.open.channel.hkrt.service.isv.HkrtIsvChannelMerchantService;
+import cn.daxpay.open.platform.core.annotation.PermCode;
+import cn.daxpay.open.platform.core.rest.Res;
+import cn.daxpay.open.platform.core.rest.result.Result;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.constraints.NotBlank;
+import lombok.RequiredArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/// # 海科融通通道商户管理
+///
+@PermCode(menuCode = "channel:merchant")
+@Validated
+@Tag(name = "海科融通通道商户管理")
+@RestController
+@RequestMapping("/admin/hkrt/isv-channel-merchant")
+@RequiredArgsConstructor
+public class HkrtIsvChannelMerchantController {
+
+ private final HkrtIsvChannelMerchantService hkrtIsvChannelMerchantService;
+
+ @PermCode(code = "view", nameCn = "通道商户查看", nameEn = "Channel Merchant View")
+ @Operation(summary = "根据通道商户号查询海科融通通道商户配置")
+ @GetMapping("/find-by-channel-mch-no")
+ public Result findByChannelMchNo(
+ @NotBlank(message = "{validation.field.channelMchNo.notBlank}") String channelMchNo) {
+ return Res.ok(hkrtIsvChannelMerchantService.findByChannelMchNo(channelMchNo));
+ }
+
+ @PermCode(code = "manage", nameCn = "通道商户管理", nameEn = "Channel Merchant Manage")
+ @Operation(summary = "创建海科融通通道商户")
+ @PostMapping("/create")
+ public Result create(@RequestBody @Validated HkrtIsvChannelMerchantCreateParam param) {
+ hkrtIsvChannelMerchantService.create(param);
+ return Res.ok();
+ }
+
+ @PermCode(code = "manage", nameCn = "通道商户管理", nameEn = "Channel Merchant Manage")
+ @Operation(summary = "更新SAAS终端号")
+ @PostMapping("/update-pn")
+ public Result updatePn(
+ @NotBlank(message = "{validation.field.channelMerchantNo.notBlank}") String channelMchNo,
+ @NotBlank(message = "{validation.field.pn.notBlank}") String pn) {
+ hkrtIsvChannelMerchantService.updatePn(channelMchNo, pn);
+ return Res.ok();
+ }
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/controller/isv/HkrtIsvKeyConfigController.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/controller/isv/HkrtIsvKeyConfigController.java
new file mode 100644
index 000000000..301f2bef4
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/controller/isv/HkrtIsvKeyConfigController.java
@@ -0,0 +1,48 @@
+package cn.daxpay.open.channel.hkrt.controller.isv;
+
+import cn.daxpay.open.channel.hkrt.convert.isv.HkrtIsvKeyConfigConvert;
+import cn.daxpay.open.channel.hkrt.param.isv.HkrtIsvKeyConfigParam;
+import cn.daxpay.open.channel.hkrt.result.isv.HkrtIsvKeyConfigResult;
+import cn.daxpay.open.channel.hkrt.service.isv.HkrtIsvKeyConfigService;
+import cn.daxpay.open.platform.core.annotation.PermCode;
+import cn.daxpay.open.platform.core.rest.Res;
+import cn.daxpay.open.platform.core.rest.result.Result;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.constraints.NotBlank;
+import lombok.RequiredArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/// # 海科融通服务商密钥配置
+///
+@PermCode(menuCode = "payment:hkrt:isv")
+@Validated
+@Tag(name = "海科融通服务商密钥配置")
+@RestController
+@RequestMapping("/admin/hkrt/isv-key-config")
+@RequiredArgsConstructor
+public class HkrtIsvKeyConfigController {
+
+ private final HkrtIsvKeyConfigService hkrtIsvKeyConfigService;
+
+ @PermCode(code = "view", nameCn = "海科融通服务商查看", nameEn = "Hkrt ISV View")
+ @Operation(summary = "查询海科融通服务商密钥配置")
+ @GetMapping("/find-config")
+ public Result findConfig(
+ @NotBlank(message = "{validation.field.product.notBlank}") String product) {
+ return Res.ok(HkrtIsvKeyConfigConvert.CONVERT.toResult(hkrtIsvKeyConfigService.findByProduct(product)));
+ }
+
+ @PermCode(code = "manage", nameCn = "海科融通服务商管理", nameEn = "Hkrt ISV Manage")
+ @Operation(summary = "保存海科融通服务商密钥配置")
+ @PostMapping("/save-config")
+ public Result saveConfig(@RequestBody @Validated HkrtIsvKeyConfigParam param) {
+ hkrtIsvKeyConfigService.saveConfig(param);
+ return Res.ok();
+ }
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/convert/isv/HkrtIsvChannelMerchantConvert.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/convert/isv/HkrtIsvChannelMerchantConvert.java
new file mode 100644
index 000000000..75e8bd2a2
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/convert/isv/HkrtIsvChannelMerchantConvert.java
@@ -0,0 +1,16 @@
+package cn.daxpay.open.channel.hkrt.convert.isv;
+
+import cn.daxpay.open.channel.hkrt.entity.isv.HkrtIsvChannelMerchant;
+import cn.daxpay.open.channel.hkrt.result.isv.HkrtIsvChannelMerchantResult;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+/// # 海科融通通道商户绑定转换
+///
+@Mapper
+public interface HkrtIsvChannelMerchantConvert {
+
+ HkrtIsvChannelMerchantConvert CONVERT = Mappers.getMapper(HkrtIsvChannelMerchantConvert.class);
+
+ HkrtIsvChannelMerchantResult toResult(HkrtIsvChannelMerchant entity);
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/convert/isv/HkrtIsvKeyConfigConvert.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/convert/isv/HkrtIsvKeyConfigConvert.java
new file mode 100644
index 000000000..92606b2e4
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/convert/isv/HkrtIsvKeyConfigConvert.java
@@ -0,0 +1,28 @@
+package cn.daxpay.open.channel.hkrt.convert.isv;
+
+import cn.daxpay.open.channel.hkrt.entity.isv.HkrtIsvKeyConfig;
+import cn.daxpay.open.channel.hkrt.param.isv.HkrtIsvKeyConfigParam;
+import cn.daxpay.open.channel.hkrt.result.isv.HkrtIsvKeyConfigResult;
+import org.mapstruct.BeanMapping;
+import org.mapstruct.Mapper;
+import org.mapstruct.MappingTarget;
+import org.mapstruct.NullValuePropertyMappingStrategy;
+import org.mapstruct.factory.Mappers;
+
+/// # 海科融通服务商密钥配置转换
+///
+@Mapper
+public interface HkrtIsvKeyConfigConvert {
+
+ HkrtIsvKeyConfigConvert CONVERT = Mappers.getMapper(HkrtIsvKeyConfigConvert.class);
+
+ /// 转换为返回对象
+ HkrtIsvKeyConfigResult toResult(HkrtIsvKeyConfig entity);
+
+ /// 转换为实体
+ HkrtIsvKeyConfig toEntity(HkrtIsvKeyConfigParam param);
+
+ /// 更新源数据到实体(空值不覆盖, 密钥为空时保留原值)
+ @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
+ void copy(HkrtIsvKeyConfigParam param, @MappingTarget HkrtIsvKeyConfig entity);
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/dao/isv/HkrtIsvChannelMerchantManager.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/dao/isv/HkrtIsvChannelMerchantManager.java
new file mode 100644
index 000000000..7c177df2e
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/dao/isv/HkrtIsvChannelMerchantManager.java
@@ -0,0 +1,28 @@
+package cn.daxpay.open.channel.hkrt.dao.isv;
+
+import cn.daxpay.open.channel.hkrt.entity.isv.HkrtIsvChannelMerchant;
+import cn.daxpay.open.platform.common.mybatisplus.impl.BaseManager;
+import org.springframework.stereotype.Repository;
+
+import java.util.Optional;
+
+/// # 海科融通通道商户绑定
+///
+@Repository
+public class HkrtIsvChannelMerchantManager extends BaseManager {
+
+ /// 校验同一商户下海科商户号不重复
+ public boolean existsByMchNoAndMerchNo(String mchNo, String merchNo) {
+ return lambdaQuery()
+ .eq(HkrtIsvChannelMerchant::getMchNo, mchNo)
+ .eq(HkrtIsvChannelMerchant::getMerchNo, merchNo)
+ .exists();
+ }
+
+ /// 根据通道商户号查询
+ public Optional findByChannelMchNo(String channelMchNo) {
+ return lambdaQuery()
+ .eq(HkrtIsvChannelMerchant::getChannelMchNo, channelMchNo)
+ .oneOpt();
+ }
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/dao/isv/HkrtIsvChannelMerchantMapper.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/dao/isv/HkrtIsvChannelMerchantMapper.java
new file mode 100644
index 000000000..375cf7eca
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/dao/isv/HkrtIsvChannelMerchantMapper.java
@@ -0,0 +1,11 @@
+package cn.daxpay.open.channel.hkrt.dao.isv;
+
+import cn.daxpay.open.channel.hkrt.entity.isv.HkrtIsvChannelMerchant;
+import com.github.yulichang.base.MPJBaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+/// # 海科融通通道商户绑定
+///
+@Mapper
+public interface HkrtIsvChannelMerchantMapper extends MPJBaseMapper {
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/dao/isv/HkrtIsvKeyConfigManager.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/dao/isv/HkrtIsvKeyConfigManager.java
new file mode 100644
index 000000000..258c0ed5f
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/dao/isv/HkrtIsvKeyConfigManager.java
@@ -0,0 +1,22 @@
+package cn.daxpay.open.channel.hkrt.dao.isv;
+
+import cn.daxpay.open.channel.hkrt.entity.isv.HkrtIsvKeyConfig;
+import cn.daxpay.open.platform.common.mybatisplus.impl.BaseManager;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.Optional;
+
+/// # 海科融通服务商密钥配置
+///
+@Slf4j
+@Service
+public class HkrtIsvKeyConfigManager extends BaseManager {
+
+ /// 根据产品编码查询(平台为唯一服务商, 密钥全局唯一)
+ public Optional findByProduct(String product) {
+ return lambdaQuery()
+ .eq(HkrtIsvKeyConfig::getProduct, product)
+ .oneOpt();
+ }
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/dao/isv/HkrtIsvKeyConfigMapper.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/dao/isv/HkrtIsvKeyConfigMapper.java
new file mode 100644
index 000000000..9f3769604
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/dao/isv/HkrtIsvKeyConfigMapper.java
@@ -0,0 +1,11 @@
+package cn.daxpay.open.channel.hkrt.dao.isv;
+
+import cn.daxpay.open.channel.hkrt.entity.isv.HkrtIsvKeyConfig;
+import com.github.yulichang.base.MPJBaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+/// # 海科融通服务商密钥配置
+///
+@Mapper
+public interface HkrtIsvKeyConfigMapper extends MPJBaseMapper {
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/entity/isv/HkrtIsvChannelMerchant.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/entity/isv/HkrtIsvChannelMerchant.java
new file mode 100644
index 000000000..3b3780c50
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/entity/isv/HkrtIsvChannelMerchant.java
@@ -0,0 +1,41 @@
+package cn.daxpay.open.channel.hkrt.entity.isv;
+
+import cn.daxpay.open.channel.hkrt.convert.isv.HkrtIsvChannelMerchantConvert;
+import cn.daxpay.open.channel.hkrt.result.isv.HkrtIsvChannelMerchantResult;
+import cn.daxpay.open.payment.common.entity.merchant.MchBaseEntity;
+import cn.daxpay.open.platform.common.mybatisplus.function.ToResult;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+/// # 海科融通通道商户绑定
+///
+/// 海科融通服务商模式下, 子商户绑定海科商户号(merchNo) + SAAS 终端号(pn),
+/// 密钥(agentNo/accessId/accessKey)由服务商全局配置([HkrtIsvKeyConfig])提供。
+/// 同一商户下海科商户号不重复。
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Accessors(chain = true)
+@TableName(value = "hkrt_isv_channel_merchant", autoResultMap = true)
+public class HkrtIsvChannelMerchant extends MchBaseEntity implements ToResult {
+
+ /// 通道商户号(HKRT+雪花)
+ private String channelMchNo;
+
+ /// 所属支付产品
+ /// @see cn.daxpay.open.platform.core.enums.pay.channel.ProductEnum
+ private String product;
+
+ /// 海科商户号(merch_no)
+ private String merchNo;
+
+ /// SAAS 终端号(pn)
+ private String pn;
+
+ /// 转换
+ @Override
+ public HkrtIsvChannelMerchantResult toResult() {
+ return HkrtIsvChannelMerchantConvert.CONVERT.toResult(this);
+ }
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/entity/isv/HkrtIsvKeyConfig.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/entity/isv/HkrtIsvKeyConfig.java
new file mode 100644
index 000000000..5f75c3ed3
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/entity/isv/HkrtIsvKeyConfig.java
@@ -0,0 +1,46 @@
+package cn.daxpay.open.channel.hkrt.entity.isv;
+
+import cn.daxpay.open.channel.hkrt.convert.isv.HkrtIsvKeyConfigConvert;
+import cn.daxpay.open.channel.hkrt.result.isv.HkrtIsvKeyConfigResult;
+import cn.daxpay.open.platform.common.mybatisplus.base.MpBaseEntity;
+import cn.daxpay.open.platform.common.mybatisplus.function.ToResult;
+import cn.daxpay.open.platform.common.mybatisplus.handler.encrypt.DataEncryptTypeHandler;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+/// # 海科融通服务商密钥配置
+///
+/// 海科融通为收单机构服务商模式, 服务商密钥全局唯一(按 product 查询),
+/// 子商户仅需商户号(merchNo) + SAAS 终端号(pn), 见 [HkrtIsvChannelMerchant]。
+///
+/// 签名算法: MD5 大写(参数字母升序 + 末尾拼 accessKey), 无需证书。
+/// 敏感字段(accessKey)通过 [DataEncryptTypeHandler] 加密入库。
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Accessors(chain = true)
+@TableName(value = "hkrt_isv_key_config", autoResultMap = true)
+public class HkrtIsvKeyConfig extends MpBaseEntity implements ToResult {
+
+ /// 产品编码
+ /// @see cn.daxpay.open.platform.core.enums.pay.channel.ProductEnum
+ private String product;
+
+ /// 服务商编号(agent_no)
+ private String agentNo;
+
+ /// 接入机构标识(access_id)
+ private String accessId;
+
+ /// 签名密钥(access_key, MD5 大写签名, 加密存储)
+ @TableField(typeHandler = DataEncryptTypeHandler.class)
+ private String accessKey;
+
+ /// 转换
+ @Override
+ public HkrtIsvKeyConfigResult toResult() {
+ return HkrtIsvKeyConfigConvert.CONVERT.toResult(this);
+ }
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/param/isv/HkrtIsvChannelMerchantCreateParam.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/param/isv/HkrtIsvChannelMerchantCreateParam.java
new file mode 100644
index 000000000..c8901fab7
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/param/isv/HkrtIsvChannelMerchantCreateParam.java
@@ -0,0 +1,34 @@
+package cn.daxpay.open.channel.hkrt.param.isv;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+/// # 海科融通通道商户创建参数
+///
+@Data
+@Accessors(chain = true)
+@Schema(title = "海科融通通道商户创建参数")
+public class HkrtIsvChannelMerchantCreateParam {
+
+ /// 商户号
+ @Schema(description = "商户号")
+ @NotBlank(message = "{validation.field.mchNo.notBlank}")
+ private String mchNo;
+
+ /// 通道商户名称
+ @Schema(description = "通道商户名称")
+ @NotBlank(message = "{validation.field.channelMerchantName.notBlank}")
+ private String channelMerchantName;
+
+ /// 所属支付产品
+ @Schema(description = "所属支付产品")
+ @NotBlank(message = "{validation.field.product.notBlank}")
+ private String product;
+
+ /// 海科商户号
+ @Schema(description = "海科商户号")
+ @NotBlank(message = "{validation.field.merchNo.notBlank}")
+ private String merchNo;
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/param/isv/HkrtIsvKeyConfigParam.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/param/isv/HkrtIsvKeyConfigParam.java
new file mode 100644
index 000000000..06c12d0ac
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/param/isv/HkrtIsvKeyConfigParam.java
@@ -0,0 +1,29 @@
+package cn.daxpay.open.channel.hkrt.param.isv;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+/// # 海科融通服务商密钥配置保存参数
+///
+@Data
+@Accessors(chain = true)
+@Schema(title = "海科融通服务商密钥配置保存参数")
+public class HkrtIsvKeyConfigParam {
+
+ @NotBlank(message = "{validation.field.product.notBlank}")
+ @Schema(description = "产品编码")
+ private String product;
+
+ @NotBlank(message = "{validation.field.agentNo.notBlank}")
+ @Schema(description = "服务商编号")
+ private String agentNo;
+
+ @NotBlank(message = "{validation.field.accessId.notBlank}")
+ @Schema(description = "接入机构标识")
+ private String accessId;
+
+ @Schema(description = "签名密钥(加密存储)")
+ private String accessKey;
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/result/isv/HkrtIsvChannelMerchantResult.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/result/isv/HkrtIsvChannelMerchantResult.java
new file mode 100644
index 000000000..156b40659
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/result/isv/HkrtIsvChannelMerchantResult.java
@@ -0,0 +1,28 @@
+package cn.daxpay.open.channel.hkrt.result.isv;
+
+import cn.daxpay.open.payment.common.result.MchTradeBaseResult;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+/// # 海科融通通道商户绑定结果
+///
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Accessors(chain = true)
+@Schema(title = "海科融通通道商户绑定结果")
+public class HkrtIsvChannelMerchantResult extends MchTradeBaseResult {
+
+ @Schema(description = "通道商户号")
+ private String channelMchNo;
+
+ @Schema(description = "所属支付产品")
+ private String product;
+
+ @Schema(description = "海科商户号")
+ private String merchNo;
+
+ @Schema(description = "SAAS 终端号")
+ private String pn;
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/result/isv/HkrtIsvKeyConfigResult.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/result/isv/HkrtIsvKeyConfigResult.java
new file mode 100644
index 000000000..247617e9b
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/result/isv/HkrtIsvKeyConfigResult.java
@@ -0,0 +1,30 @@
+package cn.daxpay.open.channel.hkrt.result.isv;
+
+import cn.daxpay.open.platform.common.json.sensitive.SensitiveInfo;
+import cn.daxpay.open.platform.core.result.BaseResult;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+/// # 海科融通服务商密钥配置
+///
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Accessors(chain = true)
+@Schema(title = "海科融通服务商密钥配置")
+public class HkrtIsvKeyConfigResult extends BaseResult {
+
+ @Schema(description = "产品编码")
+ private String product;
+
+ @Schema(description = "服务商编号")
+ private String agentNo;
+
+ @Schema(description = "接入机构标识")
+ private String accessId;
+
+ @SensitiveInfo(front = 4, end = 4)
+ @Schema(description = "签名密钥(加密存储)")
+ private String accessKey;
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/service/callback/HkrtPayCallbackService.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/service/callback/HkrtPayCallbackService.java
new file mode 100644
index 000000000..e3de0ede5
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/service/callback/HkrtPayCallbackService.java
@@ -0,0 +1,111 @@
+package cn.daxpay.open.channel.hkrt.service.callback;
+
+import cn.daxpay.open.channel.hkrt.client.HkrtChannelClient;
+import cn.daxpay.open.channel.hkrt.client.credential.HkrtSdkCredential;
+import cn.daxpay.open.channel.hkrt.client.req.HkrtCallbackParseReq;
+import cn.daxpay.open.channel.hkrt.client.resp.HkrtCallbackParseResp;
+import cn.daxpay.open.channel.hkrt.dao.isv.HkrtIsvKeyConfigManager;
+import cn.daxpay.open.channel.hkrt.entity.isv.HkrtIsvKeyConfig;
+import cn.daxpay.open.payment.common.callback.CallbackData;
+import cn.daxpay.open.payment.core.trade.service.PayCallbackService;
+import cn.daxpay.open.platform.core.enums.pay.channel.ProductEnum;
+import cn.daxpay.open.platform.core.enums.pay.notice.CallbackStatusEnum;
+import cn.hutool.extra.servlet.JakartaServletUtil;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.Objects;
+
+/// # 海科融通支付回调处理服务
+///
+/// 海科融通异步通知 → 主应用接收原始 JSON body → 转发子应用验签与解析 →
+/// 构建 [CallbackData] 交由 [PayCallbackService] 更新订单状态。
+///
+/// 验签与字段解析统一由子应用 dax-pay-channel-two 的 HkrtCallbackParseService 完成
+/// (MD5 大写签名, 参数字母升序 + 末尾拼 accessKey), 主应用不再自验签。
+/// 子应用返回抽象态 tradeStatus(SUCCESS/FAIL), 主应用映射为 CallbackStatusEnum 后交框架处理。
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class HkrtPayCallbackService {
+
+ private static final String NOTIFY_SUCCESS = "success";
+ private static final String NOTIFY_FAIL = "fail";
+
+ private final HkrtIsvKeyConfigManager hkrtIsvKeyConfigManager;
+ private final PayCallbackService payCallbackService;
+ private final HkrtChannelClient hkrtChannelClient;
+
+ /// 支付回调处理
+ public String payHandle(HttpServletRequest request) {
+ // 1. 提取回调原始数据(JSON body)
+ String body = JakartaServletUtil.getBody(request);
+
+ // 2. 获取全局服务商 accessKey(只读查询, 不创建记录)
+ HkrtIsvKeyConfig keyConfig = hkrtIsvKeyConfigManager.findByProduct(ProductEnum.HKRT_PAY.getCode())
+ .orElse(null);
+ if (keyConfig == null || keyConfig.getAccessKey() == null) {
+ log.error("海科融通支付回调: 服务商密钥未配置, 无法验签");
+ return NOTIFY_FAIL;
+ }
+
+ // 3. 转发子应用验签与解析
+ HkrtCallbackParseResp resp = parseCallback(body, keyConfig.getAccessKey(), false);
+ if (resp == null) {
+ return NOTIFY_FAIL;
+ }
+ try {
+ // 4. 构建 CallbackData 交框架更新订单状态
+ CallbackData callbackData = toCallbackData(resp);
+ payCallbackService.payCallback(callbackData);
+ } catch (Exception e) {
+ log.error("海科融通支付回调业务处理失败: tradeNo={}", resp.getOutTradeNo(), e);
+ return NOTIFY_FAIL;
+ }
+ return NOTIFY_SUCCESS;
+ }
+
+ /// 转发子应用验签解析(支付回调)
+ private HkrtCallbackParseResp parseCallback(String body, String accessKey, boolean refund) {
+ HkrtSdkCredential credential = new HkrtSdkCredential();
+ credential.setAccessKey(accessKey);
+ HkrtCallbackParseReq req = new HkrtCallbackParseReq();
+ req.setCredential(credential);
+ req.setRawData(body);
+ req.setRefund(refund);
+ var result = hkrtChannelClient.parsePayCallback(req);
+ if (result.getCode() != 0) {
+ log.error("海科融通支付回调: 子应用解析失败: {}", result.getMsg());
+ return null;
+ }
+ HkrtCallbackParseResp resp = result.getData();
+ if (resp == null || !Boolean.TRUE.equals(resp.getSuccess())) {
+ log.error("海科融通支付回调验签失败");
+ return null;
+ }
+ return resp;
+ }
+
+ /// 子应用解析结果 → CallbackData(抽象态 → 框架状态码)
+ ///
+ /// 框架 [PayCallbackService] 以 `CallbackStatusEnum.SUCCESS.getCode()`(success) 比对 tradeStatus:
+ /// - SUCCESS → success(走成功分支)
+ /// - 其他 → 不设 tradeStatus(走 fail 分支), 记错误信息
+ private CallbackData toCallbackData(HkrtCallbackParseResp resp) {
+ CallbackData callbackData = new CallbackData();
+ // out_trade_no = 下单时传入的平台 tradeNo
+ callbackData.setTradeNo(resp.getOutTradeNo());
+ // trade_no = 海科融通交易号
+ callbackData.setOutTradeNo(resp.getTradeNo());
+ // 抽象态 → 框架状态码
+ if (Objects.equals(resp.getTradeStatus(), "SUCCESS")) {
+ callbackData.setTradeStatus(CallbackStatusEnum.SUCCESS.getCode());
+ } else {
+ callbackData.setCallbackErrorMsg("海科融通回调状态非成功: " + resp.getTradeStatus());
+ }
+ callbackData.setFinishTime(resp.getFinishTime());
+ return callbackData;
+ }
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/service/callback/HkrtRefundCallbackService.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/service/callback/HkrtRefundCallbackService.java
new file mode 100644
index 000000000..fed831f0b
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/service/callback/HkrtRefundCallbackService.java
@@ -0,0 +1,93 @@
+package cn.daxpay.open.channel.hkrt.service.callback;
+
+import cn.daxpay.open.channel.hkrt.client.HkrtChannelClient;
+import cn.daxpay.open.channel.hkrt.client.credential.HkrtSdkCredential;
+import cn.daxpay.open.channel.hkrt.client.req.HkrtCallbackParseReq;
+import cn.daxpay.open.channel.hkrt.client.resp.HkrtCallbackParseResp;
+import cn.daxpay.open.channel.hkrt.dao.isv.HkrtIsvKeyConfigManager;
+import cn.daxpay.open.channel.hkrt.entity.isv.HkrtIsvKeyConfig;
+import cn.daxpay.open.platform.core.enums.pay.channel.ProductEnum;
+import cn.hutool.extra.servlet.JakartaServletUtil;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.Objects;
+
+/// # 海科融通退款回调处理服务
+///
+/// 海科融通退款异步通知 → 主应用接收原始 JSON body → 转发子应用验签与解析 →
+/// 记录退款回调结果。
+///
+/// 验签与字段解析统一由子应用 dax-pay-channel-two 的 HkrtCallbackParseService 完成,
+/// 主应用不再自验签。子应用返回抽象态 tradeStatus(SUCCESS/FAIL)。
+///
+/// TODO 退款单状态更新待接入退款回调框架(平台目前无独立 RefundCallbackService,
+/// 后续可通过 PayRefundService 或新增退款回调入口完成状态流转)。本期仅记录日志。
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class HkrtRefundCallbackService {
+
+ private static final String NOTIFY_SUCCESS = "success";
+ private static final String NOTIFY_FAIL = "fail";
+ /// 退款成功(抽象态, 由子应用统一转换)
+ private static final String REFUND_STATUS_SUCCESS = "SUCCESS";
+
+ private final HkrtIsvKeyConfigManager hkrtIsvKeyConfigManager;
+ private final HkrtChannelClient hkrtChannelClient;
+
+ /// 退款回调处理
+ public String refundHandle(HttpServletRequest request) {
+ // 1. 提取回调原始数据(JSON body)
+ String body = JakartaServletUtil.getBody(request);
+
+ // 2. 获取全局服务商 accessKey(只读查询, 不创建记录)
+ HkrtIsvKeyConfig keyConfig = hkrtIsvKeyConfigManager.findByProduct(ProductEnum.HKRT_PAY.getCode())
+ .orElse(null);
+ if (keyConfig == null || keyConfig.getAccessKey() == null) {
+ log.error("海科融通退款回调: 服务商密钥未配置, 无法验签");
+ return NOTIFY_FAIL;
+ }
+
+ // 3. 转发子应用验签与解析
+ HkrtCallbackParseResp resp = parseCallback(body, keyConfig.getAccessKey());
+ if (resp == null) {
+ return NOTIFY_FAIL;
+ }
+
+ // 4. 记录退款回调结果, TODO 接入退款单状态更新框架
+ String outRefundNo = resp.getOutTradeNo();
+ String refundNo = resp.getTradeNo();
+ String refundStatus = resp.getTradeStatus();
+ log.info("海科融通退款回调: outRefundNo={}, refundNo={}, refundStatus={}, amount={}, finishTime={}",
+ outRefundNo, refundNo, refundStatus, resp.getAmount(), resp.getFinishTime());
+ if (Objects.equals(REFUND_STATUS_SUCCESS, refundStatus)) {
+ // TODO 退款成功, 更新退款单状态(待接入退款回调框架)
+ log.info("海科融通退款成功: outRefundNo={}", outRefundNo);
+ }
+ return NOTIFY_SUCCESS;
+ }
+
+ /// 转发子应用验签解析(退款回调)
+ private HkrtCallbackParseResp parseCallback(String body, String accessKey) {
+ HkrtSdkCredential credential = new HkrtSdkCredential();
+ credential.setAccessKey(accessKey);
+ HkrtCallbackParseReq req = new HkrtCallbackParseReq();
+ req.setCredential(credential);
+ req.setRawData(body);
+ req.setRefund(true);
+ var result = hkrtChannelClient.parseRefundCallback(req);
+ if (result.getCode() != 0) {
+ log.error("海科融通退款回调: 子应用解析失败: {}", result.getMsg());
+ return null;
+ }
+ HkrtCallbackParseResp resp = result.getData();
+ if (resp == null || !Boolean.TRUE.equals(resp.getSuccess())) {
+ log.error("海科融通退款回调验签失败");
+ return null;
+ }
+ return resp;
+ }
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/service/isv/HkrtIsvChannelMerchantService.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/service/isv/HkrtIsvChannelMerchantService.java
new file mode 100644
index 000000000..8f2716bc9
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/service/isv/HkrtIsvChannelMerchantService.java
@@ -0,0 +1,80 @@
+package cn.daxpay.open.channel.hkrt.service.isv;
+
+import cn.daxpay.open.channel.hkrt.dao.isv.HkrtIsvChannelMerchantManager;
+import cn.daxpay.open.channel.hkrt.entity.isv.HkrtIsvChannelMerchant;
+import cn.daxpay.open.channel.hkrt.param.isv.HkrtIsvChannelMerchantCreateParam;
+import cn.daxpay.open.channel.hkrt.result.isv.HkrtIsvChannelMerchantResult;
+import cn.daxpay.open.payment.channel.dao.mch.ChannelMerchantManager;
+import cn.daxpay.open.payment.channel.entity.mch.ChannelMerchant;
+import cn.daxpay.open.platform.core.code.CommonErrorCode;
+import cn.daxpay.open.platform.core.enums.channel.ChannelMerchantSourceEnum;
+import cn.daxpay.open.platform.core.exception.BizInfoException;
+import cn.daxpay.open.platform.core.exception.DataNotExistException;
+import cn.daxpay.open.platform.core.util.ChannelMchNoGenerateUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+/// # 海科融通通道商户管理
+///
+/// 海科融通服务商模式下, 子商户关联到服务商本身(密钥全局唯一), 不挂靠具体应用。
+/// 一个商户下同一海科商户号(merchNo)只允许绑定一次。
+///
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class HkrtIsvChannelMerchantService {
+
+ private final ChannelMerchantManager channelMerchantManager;
+ private final HkrtIsvChannelMerchantManager hkrtIsvChannelMerchantManager;
+
+ /// 创建海科融通通道商户
+ @Transactional(rollbackFor = Exception.class)
+ public void create(HkrtIsvChannelMerchantCreateParam param) {
+ // 校验同一商户下海科商户号不重复
+ if (hkrtIsvChannelMerchantManager.existsByMchNoAndMerchNo(
+ param.getMchNo(), param.getMerchNo())) {
+ // 海科融通: 该商户下已存在此海科商户号
+ throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "channel.error.hkrtMerchantDuplicate");
+ }
+ // 生成通道商户号: 通道前缀 + 雪花ID(无分隔符, 仅供排障辨识)
+ String channelMchNo = ChannelMchNoGenerateUtil.generate("HKRT");
+ // 写通用通道商户主表
+ ChannelMerchant channelMerchant = new ChannelMerchant();
+ channelMerchant.setMchNo(param.getMchNo());
+ channelMerchant.setChannelMchNo(channelMchNo);
+ channelMerchant.setChannelMerchantName(param.getChannelMerchantName());
+ channelMerchant.setProduct(param.getProduct());
+ channelMerchant.setSource(ChannelMerchantSourceEnum.MANUAL.getCode());
+ channelMerchant.setEnable(true);
+ channelMerchantManager.save(channelMerchant);
+ // 写海科融通绑定表
+ var entity = new HkrtIsvChannelMerchant();
+ entity.setMchNo(param.getMchNo());
+ entity.setChannelMchNo(channelMchNo);
+ entity.setProduct(param.getProduct());
+ entity.setMerchNo(param.getMerchNo());
+ hkrtIsvChannelMerchantManager.save(entity);
+ }
+
+ /// 根据通道商户号查询海科融通通道商户配置
+ public HkrtIsvChannelMerchantResult findByChannelMchNo(String channelMchNo) {
+ return hkrtIsvChannelMerchantManager.lambdaQuery()
+ .eq(HkrtIsvChannelMerchant::getChannelMchNo, channelMchNo)
+ .oneOpt()
+ .map(HkrtIsvChannelMerchant::toResult)
+ // 海科融通: 通道商户配置不存在
+ .orElseThrow(() -> new DataNotExistException("error.payment.channel.channelMerchantNotExist"));
+ }
+
+ /// 更新 SAAS 终端号(pn)
+ @Transactional(rollbackFor = Exception.class)
+ public void updatePn(String channelMchNo, String pn) {
+ var entity = hkrtIsvChannelMerchantManager.findByChannelMchNo(channelMchNo)
+ // 海科融通: 通道商户配置不存在
+ .orElseThrow(() -> new DataNotExistException("error.payment.channel.channelMerchantNotExist"));
+ entity.setPn(pn);
+ hkrtIsvChannelMerchantManager.updateById(entity);
+ }
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/service/isv/HkrtIsvConfigAssembler.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/service/isv/HkrtIsvConfigAssembler.java
new file mode 100644
index 000000000..dc37ec7f7
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/service/isv/HkrtIsvConfigAssembler.java
@@ -0,0 +1,75 @@
+package cn.daxpay.open.channel.hkrt.service.isv;
+
+import cn.daxpay.open.channel.hkrt.client.credential.HkrtSdkCredential;
+import cn.daxpay.open.channel.hkrt.dao.isv.HkrtIsvChannelMerchantManager;
+import cn.daxpay.open.channel.hkrt.entity.isv.HkrtIsvChannelMerchant;
+import cn.daxpay.open.channel.hkrt.entity.isv.HkrtIsvKeyConfig;
+import cn.daxpay.open.payment.masterdata.constants.product.dao.PayProductConfigManager;
+import cn.daxpay.open.payment.masterdata.constants.product.entity.PayProductConfig;
+import cn.daxpay.open.platform.core.code.CommonErrorCode;
+import cn.daxpay.open.platform.core.enums.pay.channel.ProductEnum;
+import cn.daxpay.open.platform.core.enums.pay.config.PayEnvEnum;
+import cn.daxpay.open.platform.core.exception.BizInfoException;
+import cn.daxpay.open.platform.core.exception.DataNotExistException;
+import cn.hutool.core.util.StrUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+/// # 海科融通服务商通道凭证组装器
+///
+/// 从服务商密钥配置([HkrtIsvKeyConfig]) + 通道商户绑定([HkrtIsvChannelMerchant]) 组装通道调用凭证,
+/// 下发给子应用 dax-pay-channel-two 发起海科融通 API 调用。
+///
+/// 字段映射(对齐海科融通接口):
+/// - agent_no / access_id / access_key ← [HkrtIsvKeyConfig](服务商级, 全局唯一)
+/// - merch_no ← [HkrtIsvChannelMerchant.merchNo](海科商户号)
+/// - pn ← [HkrtIsvChannelMerchant.pn](SAAS 终端号)
+///
+/// 供支付策略([cn.daxpay.open.channel.hkrt.strategy.*])组装通道调用凭证。
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class HkrtIsvConfigAssembler {
+
+ private final HkrtIsvChannelMerchantManager hkrtIsvChannelMerchantManager;
+ private final HkrtIsvKeyConfigService hkrtIsvKeyConfigService;
+ private final PayProductConfigManager payProductConfigManager;
+
+ /// 组装海科融通通道调用凭证(下发给子应用)
+ ///
+ /// @param mchNo 商户号(保留参数对齐签名, 服务商密钥全局唯一不依赖此字段)
+ /// @param channelMchNo 通道商户号(海科商户绑定主键)
+ /// @param capability 支付能力编码(保留参数, 海科融通不按能力路由)
+ /// @return 海科融通 SDK 凭证(含服务商密钥 + 商户号/终端号)
+ public HkrtSdkCredential buildConfig(String mchNo, String channelMchNo, String capability) {
+ // 服务商密钥(全局唯一, 含 agentNo/accessId/accessKey; 缺失或关键字段为空时 fail-fast)
+ HkrtIsvKeyConfig keyConfig = hkrtIsvKeyConfigService.getByProductForPay(ProductEnum.HKRT_PAY.getCode());
+ // 通道商户绑定(取 merchNo + pn)
+ HkrtIsvChannelMerchant channelMerchant = hkrtIsvChannelMerchantManager.findByChannelMchNo(channelMchNo)
+ // 海科融通: 通道商户配置不存在
+ .orElseThrow(() -> new DataNotExistException("error.payment.channel.channelMerchantNotExist"));
+
+ HkrtSdkCredential credential = new HkrtSdkCredential();
+ // 服务商身份与密钥
+ credential.setAgentNo(keyConfig.getAgentNo());
+ credential.setAccessId(keyConfig.getAccessId());
+ credential.setAccessKey(keyConfig.getAccessKey());
+ // 沙箱状态读取支付产品配置的生效环境(不挂在密钥配置上)
+ boolean sandbox = payProductConfigManager.findByProduct(ProductEnum.HKRT_PAY.getCode())
+ .map(PayProductConfig::getActiveEnv)
+ .map(PayEnvEnum.SANDBOX.getCode()::equals)
+ .orElse(false);
+ credential.setSandbox(sandbox);
+ // 子商户身份
+ credential.setMerchNo(channelMerchant.getMerchNo());
+ // SAAS 终端号(从通道商户配置取)
+ String pn = channelMerchant.getPn();
+ if (StrUtil.isBlank(pn)) {
+ // 海科融通: 终端号未配置, 请在商户配置中填写
+ throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "channel.error.hkrtTermNoNotConfigured");
+ }
+ credential.setPn(pn);
+ return credential;
+ }
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/service/isv/HkrtIsvKeyConfigService.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/service/isv/HkrtIsvKeyConfigService.java
new file mode 100644
index 000000000..780c60c14
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/service/isv/HkrtIsvKeyConfigService.java
@@ -0,0 +1,60 @@
+package cn.daxpay.open.channel.hkrt.service.isv;
+
+import cn.daxpay.open.channel.hkrt.convert.isv.HkrtIsvKeyConfigConvert;
+import cn.daxpay.open.channel.hkrt.dao.isv.HkrtIsvKeyConfigManager;
+import cn.daxpay.open.channel.hkrt.entity.isv.HkrtIsvKeyConfig;
+import cn.daxpay.open.channel.hkrt.param.isv.HkrtIsvKeyConfigParam;
+import cn.daxpay.open.platform.core.exception.BizInfoException;
+import cn.hutool.core.util.StrUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+/// # 海科融通服务商密钥配置
+///
+/// 管理服务商密钥配置, 查询时不存在则创建默认记录(平台为唯一服务商, 密钥全局唯一)。
+///
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class HkrtIsvKeyConfigService {
+
+ private final HkrtIsvKeyConfigManager hkrtIsvKeyConfigManager;
+
+ /// 根据产品编码查询密钥配置, 不存在则创建默认记录
+ @Transactional(rollbackFor = Exception.class)
+ public HkrtIsvKeyConfig findByProduct(String product) {
+ var existing = hkrtIsvKeyConfigManager.findByProduct(product);
+ if (existing.isPresent()) {
+ return existing.get();
+ }
+ var config = new HkrtIsvKeyConfig()
+ .setProduct(product);
+ hkrtIsvKeyConfigManager.save(config);
+ return config;
+ }
+
+ /// 支付场景查询服务商密钥(必填校验, 不创建记录)
+ ///
+ /// 与 [findByProduct] 的 upsert 语义不同, 此方法只读不写:
+ /// 记录不存在或关键字段(agentNo/accessId/accessKey)任一为空时 fail-fast,
+ /// 避免空凭证下发到子应用后子应用才发现问题。
+ public HkrtIsvKeyConfig getByProductForPay(String product) {
+ HkrtIsvKeyConfig config = hkrtIsvKeyConfigManager.findByProduct(product)
+ // 海科融通: 服务商密钥未配置
+ .orElseThrow(() -> new BizInfoException("channel.error.hkrtIsvKeyNotConfigured"));
+ if (StrUtil.hasBlank(config.getAgentNo(), config.getAccessId(), config.getAccessKey())) {
+ throw new BizInfoException("channel.error.hkrtIsvKeyNotConfigured");
+ }
+ return config;
+ }
+
+ /// 保存服务商密钥配置
+ @Transactional(rollbackFor = Exception.class)
+ public void saveConfig(HkrtIsvKeyConfigParam param) {
+ var config = this.findByProduct(param.getProduct());
+ HkrtIsvKeyConfigConvert.CONVERT.copy(param, config);
+ hkrtIsvKeyConfigManager.updateById(config);
+ }
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/service/payment/HkrtCloseService.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/service/payment/HkrtCloseService.java
new file mode 100644
index 000000000..b618ef93e
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/service/payment/HkrtCloseService.java
@@ -0,0 +1,48 @@
+package cn.daxpay.open.channel.hkrt.service.payment;
+
+import cn.daxpay.open.channel.hkrt.client.HkrtChannelClient;
+import cn.daxpay.open.channel.hkrt.client.credential.HkrtSdkCredential;
+import cn.daxpay.open.channel.hkrt.client.req.HkrtCloseReq;
+import cn.daxpay.open.channel.hkrt.client.resp.HkrtCloseResp;
+import cn.daxpay.open.payment.common.result.DaxResult;
+import cn.daxpay.open.payment.core.trade.entity.PayTrade;
+import cn.daxpay.open.platform.core.code.DaxPayErrorCode;
+import cn.daxpay.open.platform.core.enums.pay.pay.CloseTypeEnum;
+import cn.daxpay.open.platform.core.exception.BizInfoException;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+/// # 海科融通服务商关单业务服务
+///
+/// 通过 [HkrtChannelClient] 调用子应用关闭海科融通订单。
+/// 海科融通仅提供关单接口, 无撤销接口, useCancel 参数忽略。
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class HkrtCloseService {
+
+ private final HkrtChannelClient hkrtChannelClient;
+
+ /// 关闭订单
+ ///
+ /// @param order 支付订单
+ /// @param credential 通道凭证
+ /// @param useCancel 是否撤销(海科融通不支持撤销, 忽略此参数)
+ /// @param clientIp 客户端IP(取自原下单订单, 透传至海科融通)
+ /// @return 关闭类型(恒为 CLOSE)
+ public CloseTypeEnum close(PayTrade order, HkrtSdkCredential credential, boolean useCancel, String clientIp) {
+ HkrtCloseReq req = new HkrtCloseReq();
+ req.setCredential(credential);
+ req.setOriginOutTradeNo(order.getTradeNo());
+ req.setOriginTradeNo(order.getOutOrderNo());
+ req.setClientIp(clientIp);
+
+ DaxResult result = hkrtChannelClient.close(req);
+ if (result.getCode() != 0) {
+ throw new BizInfoException(DaxPayErrorCode.TRADE_FAIL, "channel.error.hkrtCloseFailed", result.getMsg());
+ }
+ // 海科融通只有关单, 返回 CLOSE
+ return CloseTypeEnum.CLOSE;
+ }
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/service/payment/HkrtPayService.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/service/payment/HkrtPayService.java
new file mode 100644
index 000000000..f014a768a
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/service/payment/HkrtPayService.java
@@ -0,0 +1,143 @@
+package cn.daxpay.open.channel.hkrt.service.payment;
+
+import cn.daxpay.open.channel.hkrt.client.HkrtChannelClient;
+import cn.daxpay.open.channel.hkrt.client.credential.HkrtSdkCredential;
+import cn.daxpay.open.channel.hkrt.client.enums.HkrtPayBodyType;
+import cn.daxpay.open.channel.hkrt.client.enums.HkrtPayMethod;
+import cn.daxpay.open.channel.hkrt.client.req.HkrtPayReq;
+import cn.daxpay.open.channel.hkrt.client.resp.HkrtPayResp;
+import cn.daxpay.open.payment.common.result.DaxResult;
+import cn.daxpay.open.payment.core.trade.bo.PayTradeResultBo;
+import cn.daxpay.open.payment.core.trade.entity.PayTrade;
+import cn.daxpay.open.payment.unipay.param.trade.pay.NormalPayParam;
+import cn.daxpay.open.platform.core.code.CommonErrorCode;
+import cn.daxpay.open.platform.core.code.DaxPayErrorCode;
+import cn.daxpay.open.platform.core.enums.pay.channel.PayMethodEnum;
+import cn.daxpay.open.platform.core.enums.unipay.PayBodyTypeEnum;
+import cn.daxpay.open.platform.core.exception.BizInfoException;
+import cn.daxpay.open.platform.system.service.config.PlatformUrlConfigService;
+import cn.hutool.core.util.StrUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+/// # 海科融通服务商支付执行业务服务
+///
+/// 通过 [HkrtChannelClient] 调用子应用 dax-pay-channel-two 完成海科融通支付下单。
+/// 请求构建、响应解析全部在本类中完成。
+///
+/// 海科融通聚合通道: 一个接口承载微信/支付宝/银联三种底层渠道, 由 method 单独决定路由,
+/// 不像拉卡拉需要 accountType + transType 三要素。
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class HkrtPayService {
+
+ private final HkrtChannelClient hkrtChannelClient;
+ private final PlatformUrlConfigService platformUrlConfigService;
+
+ /// 执行海科融通支付
+ ///
+ /// @param order 支付订单(tradeNo 作为 out_trade_no)
+ /// @param payParam 支付参数
+ /// @param credential 通道调用凭证(服务商密钥 + 商户号/终端号)
+ /// @return 支付结果
+ public PayTradeResultBo pay(PayTrade order, NormalPayParam payParam, HkrtSdkCredential credential) {
+ // 平台支付方式 → 海科融通 method + bodyType
+ MethodMapping mapping = mapMethod(payParam.getMethod());
+
+ // 构建请求
+ HkrtPayReq req = new HkrtPayReq();
+ // 使用支付交易号作为商户订单号透传给海科融通, 回调时凭此反查 PayTrade
+ req.setOutTradeNo(order.getTradeNo());
+ req.setAmount(payParam.getAmount());
+ req.setTitle(payParam.getTitle());
+ req.setDescription(payParam.getDescription());
+ req.setMethod(mapping.method);
+ req.setPayBodyType(mapping.bodyType);
+ // 通道专属参数透传
+ req.setOpenId(payParam.getOpenId());
+ req.setAuthCode(payParam.getAuthCode());
+ req.setClientIp(payParam.getClientIp());
+ req.setNotifyUrl(this.buildNotifyUrl(order));
+ req.setExpireTime(order.getExpiredTime());
+ req.setCredential(credential);
+
+ // 调用子应用
+ DaxResult result = hkrtChannelClient.pay(req);
+ if (result.getCode() != 0) {
+ throw new BizInfoException(DaxPayErrorCode.TRADE_FAIL, "channel.error.hkrtPayFailed", result.getMsg());
+ }
+
+ return toPayResult(result.getData());
+ }
+
+ /// 生成海科融通支付异步通知地址(海科融通→平台)
+ ///
+ /// 路径约定: `{backendBaseUrl}/unipay/callback/{mchNo}/{appId}/hkrt/pay`
+ private String buildNotifyUrl(PayTrade order) {
+ String base = platformUrlConfigService.getUrlConfig().getBackendBaseUrl();
+ if (StrUtil.isBlank(base)) {
+ throw new BizInfoException(DaxPayErrorCode.CONFIG_ERROR, "error.common.backendBaseUrlNotConfigured");
+ }
+ return StrUtil.format("{}/unipay/callback/{}/{}/hkrt/pay",
+ base, order.getMchNo(), order.getAppId());
+ }
+
+ /// 平台支付方式([PayMethodEnum] code) → 海科融通 method + bodyType
+ private static MethodMapping mapMethod(String methodCode) {
+ PayMethodEnum m = PayMethodEnum.findByCode(methodCode);
+ HkrtPayMethod method = switch (m) {
+ // 微信 JSAPI / 小程序
+ case WECHAT_JSAPI, WECHAT_MINI -> HkrtPayMethod.WECHAT_JSAPI;
+ // 支付宝扫码(当面付二维码)
+ case ALIPAY_QR -> HkrtPayMethod.ALIPAY_QR;
+ // 支付宝 JSAPI / 小程序
+ case ALIPAY_JSAPI, ALIPAY_MINI -> HkrtPayMethod.ALIPAY_JSAPI;
+ // 银联二维码(云闪付)
+ case UNION_QR -> HkrtPayMethod.UNION_QR;
+ // 条码支付(微信/支付宝/银联付款码被扫)
+ case WECHAT_BARCODE, ALIPAY_BARCODE, UNION_PAY_BARCODE -> HkrtPayMethod.BARCODE;
+ default -> throw new BizInfoException(CommonErrorCode.UN_SUPPORTED_OPERATE,
+ "channel.error.hkrtUnsupportedPayMethod", methodCode);
+ };
+ // 支付内容类型由 method 推导
+ HkrtPayBodyType bodyType = switch (method) {
+ case WECHAT_JSAPI, ALIPAY_JSAPI -> HkrtPayBodyType.JSAPI;
+ case ALIPAY_QR, UNION_QR -> HkrtPayBodyType.QR_CODE;
+ case BARCODE -> null;
+ };
+ return new MethodMapping(method, bodyType);
+ }
+
+ /// 解析子应用响应为支付结果 BO
+ private PayTradeResultBo toPayResult(HkrtPayResp resp) {
+ PayTradeResultBo bo = new PayTradeResultBo()
+ .setOutOrderNo(resp.getTradeNo())
+ .setComplete(Boolean.TRUE.equals(resp.getComplete()))
+ .setPayBody(resp.getPayBody());
+
+ // 支付内容类型映射(子应用 HkrtPayBodyType → 平台 PayBodyTypeEnum)
+ // 海科融通无 LINK 类型
+ if (resp.getPayBodyType() != null) {
+ switch (resp.getPayBodyType()) {
+ case QR_CODE -> bo.setPayBodyType(PayBodyTypeEnum.QR_CODE);
+ case JSAPI -> bo.setPayBodyType(PayBodyTypeEnum.JSAPI);
+ case IDENTIFIER -> bo.setPayBodyType(PayBodyTypeEnum.IDENTIFIER);
+ }
+ }
+
+ // 完成时间与金额(条码同步成功时返回)
+ bo.setFinishTime(resp.getFinishTime());
+ bo.setTotalAmount(resp.getTotalAmount());
+ bo.setRealAmount(resp.getPayerAmount());
+ bo.setBuyerPayAmount(resp.getPayerAmount());
+ // 用户标识
+ bo.setBuyerId(resp.getBuyerId());
+ return bo;
+ }
+
+ /// 支付方式映射内部封装
+ private record MethodMapping(HkrtPayMethod method, HkrtPayBodyType bodyType) {
+ }
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/service/payment/HkrtRefundService.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/service/payment/HkrtRefundService.java
new file mode 100644
index 000000000..26314a092
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/service/payment/HkrtRefundService.java
@@ -0,0 +1,73 @@
+package cn.daxpay.open.channel.hkrt.service.payment;
+
+import cn.daxpay.open.channel.hkrt.client.HkrtChannelClient;
+import cn.daxpay.open.channel.hkrt.client.credential.HkrtSdkCredential;
+import cn.daxpay.open.channel.hkrt.client.req.HkrtRefundReq;
+import cn.daxpay.open.channel.hkrt.client.resp.HkrtRefundResp;
+import cn.daxpay.open.payment.common.enums.RefundOrderStatusEnum;
+import cn.daxpay.open.payment.common.result.DaxResult;
+import cn.daxpay.open.payment.core.trade.bo.RefundResultBo;
+import cn.daxpay.open.payment.core.trade.entity.PayRefundOrder;
+import cn.daxpay.open.platform.core.code.DaxPayErrorCode;
+import cn.daxpay.open.platform.core.exception.BizInfoException;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+/// # 海科融通服务商退款执行业务服务
+///
+/// 通过 [HkrtChannelClient] 调用子应用完成海科融通退款。
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class HkrtRefundService {
+
+ private final HkrtChannelClient hkrtChannelClient;
+ private final HkrtRefundSyncService hkrtRefundSyncService;
+
+ /// 执行退款
+ public RefundResultBo refund(PayRefundOrder refundOrder, HkrtSdkCredential credential) {
+ HkrtRefundReq req = new HkrtRefundReq();
+ req.setCredential(credential);
+ // 退款单号(作为海科融通 out_refund_no)
+ req.setOutRefundNo(refundOrder.getRefundNo());
+ // 原支付订单号(平台 tradeNo 作为原 out_trade_no)
+ req.setOriginOutTradeNo(refundOrder.getOrderNo());
+ // 原通道交易号(退款同步查询用, 首次退款时可能为空)
+ req.setOriginTradeNo(refundOrder.getOutOrderNo());
+ req.setAmount(refundOrder.getAmount());
+ req.setReason(refundOrder.getReason());
+ req.setClientIp(refundOrder.getClientIp());
+
+ DaxResult result = hkrtChannelClient.refund(req);
+ if (result.getCode() != 0) {
+ throw new BizInfoException(DaxPayErrorCode.TRADE_FAIL, "channel.error.hkrtRefundFailed", result.getMsg());
+ }
+
+ HkrtRefundResp resp = result.getData();
+ RefundResultBo bo = new RefundResultBo()
+ .setOutRefundNo(resp.getTradeNo())
+ .setFinishTime(resp.getFinishTime())
+ .setComplete(Boolean.TRUE.equals(resp.getComplete()));
+ // 退款状态(子应用统一输出抽象态, 屏蔽海科数字码)
+ String tradeStatus = resp.getTradeStatus();
+ if ("FAIL".equals(tradeStatus)) {
+ // 退款失败
+ bo.setStatus(RefundOrderStatusEnum.FAIL);
+ throw new BizInfoException(DaxPayErrorCode.TRADE_FAIL, "channel.error.hkrtRefundFailed",
+ "refund trade_status=FAIL");
+ }
+ bo.setStatus(Boolean.TRUE.equals(resp.getComplete())
+ ? RefundOrderStatusEnum.SUCCESS
+ : RefundOrderStatusEnum.PROGRESS);
+ // 退款同步成功但无完成时间(海科退款接口不返回 end_time), 立即补查 refund-query 拿 end_time,
+ // 避免订单进 SUCCESS 终态后被退款同步跳过导致 finishTime 永久为空
+ if (bo.isComplete() && bo.getFinishTime() == null) {
+ RefundResultBo syncBo = hkrtRefundSyncService.sync(refundOrder, credential);
+ if (syncBo.getFinishTime() != null) {
+ bo.setFinishTime(syncBo.getFinishTime());
+ }
+ }
+ return bo;
+ }
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/service/payment/HkrtRefundSyncService.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/service/payment/HkrtRefundSyncService.java
new file mode 100644
index 000000000..6c39a8775
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/service/payment/HkrtRefundSyncService.java
@@ -0,0 +1,63 @@
+package cn.daxpay.open.channel.hkrt.service.payment;
+
+import cn.daxpay.open.channel.hkrt.client.HkrtChannelClient;
+import cn.daxpay.open.channel.hkrt.client.credential.HkrtSdkCredential;
+import cn.daxpay.open.channel.hkrt.client.req.HkrtRefundSyncReq;
+import cn.daxpay.open.channel.hkrt.client.resp.HkrtRefundSyncResp;
+import cn.daxpay.open.payment.common.enums.RefundOrderStatusEnum;
+import cn.daxpay.open.payment.common.result.DaxResult;
+import cn.daxpay.open.payment.core.trade.bo.RefundResultBo;
+import cn.daxpay.open.payment.core.trade.entity.PayRefundOrder;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.Objects;
+
+/// # 海科融通服务商退款同步业务服务
+///
+/// 通过 [HkrtChannelClient] 调用子应用查询海科融通退款最终状态。
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class HkrtRefundSyncService {
+
+ private final HkrtChannelClient hkrtChannelClient;
+
+ /// 同步退款状态
+ public RefundResultBo sync(PayRefundOrder refundOrder, HkrtSdkCredential credential) {
+ HkrtRefundSyncReq req = new HkrtRefundSyncReq();
+ req.setCredential(credential);
+ req.setOutRefundNo(refundOrder.getRefundNo());
+ req.setOriginTradeNo(refundOrder.getOutRefundNo());
+
+ DaxResult result = hkrtChannelClient.refundSync(req);
+ RefundResultBo bo = new RefundResultBo();
+ if (result.getCode() != 0) {
+ bo.setSyncSuccess(false);
+ bo.setSyncErrorMsg(result.getMsg());
+ return bo;
+ }
+
+ HkrtRefundSyncResp resp = result.getData();
+ bo.setSyncSuccess(true);
+ bo.setOutRefundNo(resp.getTradeNo());
+ bo.setFinishTime(resp.getFinishTime());
+ bo.setStatus(mapRefundStatus(resp.getRefundStatus()));
+ // 退款完成(子应用输出抽象态 SUCCESS)
+ bo.setComplete(Objects.equals(resp.getRefundStatus(), "SUCCESS"));
+ return bo;
+ }
+
+ /// 抽象态(SUCCESS/FAIL/PROCESSING) → 平台退款状态
+ private RefundOrderStatusEnum mapRefundStatus(String refundStatus) {
+ if (Objects.equals(refundStatus, "SUCCESS")) {
+ return RefundOrderStatusEnum.SUCCESS;
+ }
+ if (Objects.equals(refundStatus, "FAIL")) {
+ return RefundOrderStatusEnum.FAIL;
+ }
+ // 处理中
+ return RefundOrderStatusEnum.PROGRESS;
+ }
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/service/payment/HkrtSyncService.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/service/payment/HkrtSyncService.java
new file mode 100644
index 000000000..8a2ecde24
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/service/payment/HkrtSyncService.java
@@ -0,0 +1,72 @@
+package cn.daxpay.open.channel.hkrt.service.payment;
+
+import cn.daxpay.open.channel.hkrt.client.HkrtChannelClient;
+import cn.daxpay.open.channel.hkrt.client.credential.HkrtSdkCredential;
+import cn.daxpay.open.channel.hkrt.client.req.HkrtSyncReq;
+import cn.daxpay.open.channel.hkrt.client.resp.HkrtSyncResp;
+import cn.daxpay.open.payment.common.enums.PayFundStatusEnum;
+import cn.daxpay.open.payment.common.result.DaxResult;
+import cn.daxpay.open.payment.core.trade.bo.PaySyncResultBo;
+import cn.daxpay.open.payment.core.trade.entity.PayTrade;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.Objects;
+
+/// # 海科融通服务商订单同步业务服务
+///
+/// 通过 [HkrtChannelClient] 调用子应用查询海科融通订单状态。
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class HkrtSyncService {
+
+ private final HkrtChannelClient hkrtChannelClient;
+
+ /// 同步订单状态
+ public PaySyncResultBo sync(PayTrade order, HkrtSdkCredential credential) {
+ HkrtSyncReq req = new HkrtSyncReq();
+ req.setCredential(credential);
+ req.setOutTradeNo(order.getTradeNo());
+ req.setTradeNo(order.getOutOrderNo());
+
+ DaxResult result = hkrtChannelClient.sync(req);
+ PaySyncResultBo bo = new PaySyncResultBo();
+ if (result.getCode() != 0) {
+ // 同步失败(不抛异常, 由核心层决定重试)
+ bo.setSyncSuccess(false);
+ bo.setSyncErrorMsg(result.getMsg());
+ return bo;
+ }
+
+ HkrtSyncResp resp = result.getData();
+ bo.setSyncSuccess(true);
+ bo.setSyncData(resp.getSyncData());
+ bo.setOutOrderNo(resp.getTradeNo());
+ bo.setPayStatus(mapTradeState(resp.getTradeState()));
+ // 成功时补充金额/时间/买家
+ if (Objects.equals(resp.getTradeState(), "SUCCESS")) {
+ bo.setAmount(resp.getTotalAmount());
+ bo.setRealAmount(resp.getTotalAmount());
+ bo.setFinishTime(resp.getFinishTime());
+ bo.setBuyerId(resp.getBuyerId());
+ }
+ return bo;
+ }
+
+ /// 海科融通 trade_state → 平台资金状态
+ private PayFundStatusEnum mapTradeState(String tradeState) {
+ if (Objects.equals(tradeState, "SUCCESS")) {
+ return PayFundStatusEnum.SUCCESS;
+ }
+ if (Objects.equals(tradeState, "FAIL")) {
+ return PayFundStatusEnum.FAIL;
+ }
+ if (Objects.equals(tradeState, "CLOSED")) {
+ return PayFundStatusEnum.CLOSE;
+ }
+ // 其他(处理中)
+ return PayFundStatusEnum.PROCESSING;
+ }
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/strategy/HkrtCloseStrategy.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/strategy/HkrtCloseStrategy.java
new file mode 100644
index 000000000..de5efa8c7
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/strategy/HkrtCloseStrategy.java
@@ -0,0 +1,51 @@
+package cn.daxpay.open.channel.hkrt.strategy;
+
+import cn.daxpay.open.channel.hkrt.client.credential.HkrtSdkCredential;
+import cn.daxpay.open.channel.hkrt.service.isv.HkrtIsvConfigAssembler;
+import cn.daxpay.open.channel.hkrt.service.payment.HkrtCloseService;
+import cn.daxpay.open.payment.core.strategy.pay.AbsPayCloseStrategy;
+import cn.daxpay.open.payment.core.strategy.pay.PayStrategyContext;
+import cn.daxpay.open.payment.core.trade.entity.NormalPayOrder;
+import cn.daxpay.open.payment.core.trade.entity.PayTrade;
+import cn.daxpay.open.platform.core.enums.pay.channel.ProductEnum;
+import cn.daxpay.open.platform.core.enums.pay.pay.CloseTypeEnum;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+/// # 海科融通服务商支付关闭策略
+///
+/// 从上下文容器读取通道路由参数(channelMchNo / capability),
+/// 组装通道凭证(委托 [HkrtIsvConfigAssembler]), 关闭执行委托给 [HkrtCloseService]。
+///
+/// 注意: 海科融通仅提供关单接口, 无撤销接口(useCancel 参数由 service 层忽略)。
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class HkrtCloseStrategy extends AbsPayCloseStrategy {
+
+ private final HkrtCloseService hkrtCloseService;
+ private final HkrtIsvConfigAssembler hkrtIsvConfigAssembler;
+
+ @Override
+ public ProductEnum getProduct() {
+ return ProductEnum.HKRT_PAY;
+ }
+
+ @Override
+ public CloseTypeEnum doClose(PayStrategyContext context, boolean useCancel) {
+ // 从上下文容器读取通道路由参数
+ NormalPayOrder normalOrder = context.getContainer();
+ String channelMchNo = normalOrder != null ? normalOrder.getChannelMchNo() : null;
+ String capability = normalOrder != null ? normalOrder.getCapability() : null;
+ PayTrade trade = context.getTrade();
+
+ // 组装通道调用凭证
+ HkrtSdkCredential credential = hkrtIsvConfigAssembler.buildConfig(
+ trade.getMchNo(), channelMchNo, capability);
+
+ // 客户端IP取自原下单订单(超时关单等非HTTP场景亦可用)
+ String clientIp = normalOrder != null ? normalOrder.getClientIp() : null;
+ return hkrtCloseService.close(trade, credential, useCancel, clientIp);
+ }
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/strategy/HkrtPayStrategy.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/strategy/HkrtPayStrategy.java
new file mode 100644
index 000000000..abce5730f
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/strategy/HkrtPayStrategy.java
@@ -0,0 +1,50 @@
+package cn.daxpay.open.channel.hkrt.strategy;
+
+import cn.daxpay.open.channel.hkrt.client.credential.HkrtSdkCredential;
+import cn.daxpay.open.channel.hkrt.service.isv.HkrtIsvConfigAssembler;
+import cn.daxpay.open.channel.hkrt.service.payment.HkrtPayService;
+import cn.daxpay.open.payment.core.strategy.pay.AbsNormalPayStrategy;
+import cn.daxpay.open.payment.core.strategy.pay.PayStrategyContext;
+import cn.daxpay.open.payment.core.trade.bo.PayTradeResultBo;
+import cn.daxpay.open.payment.unipay.param.trade.pay.NormalPayParam;
+import cn.daxpay.open.platform.core.enums.pay.channel.ProductEnum;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+/// # 海科融通服务商支付策略
+///
+/// 海科融通支付(ProductEnum.HKRT_PAY)下发起支付的具体执行策略。
+/// 配置组装在 [doBeforePay] 阶段完成(委托 [HkrtIsvConfigAssembler]), 支付执行委托给 [HkrtPayService]。
+///
+/// 海科融通为聚合服务商模式, 仅有一个产品 HKRT_PAY。
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class HkrtPayStrategy extends AbsNormalPayStrategy {
+
+ private final HkrtPayService hkrtPayService;
+ private final HkrtIsvConfigAssembler hkrtIsvConfigAssembler;
+
+ @Override
+ public ProductEnum getProduct() {
+ return ProductEnum.HKRT_PAY;
+ }
+
+ /// 支付前预处理: 从容器读取通道路由参数, 组装通道凭证写入上下文
+ /// 配置缺失在此阶段抛异常, fail-fast, 不会进入 doPay
+ @Override
+ public void doBeforePay(PayStrategyContext context) {
+ NormalPayParam payParam = context.getPayParam();
+ HkrtSdkCredential credential = hkrtIsvConfigAssembler.buildConfig(
+ payParam.getMchNo(), payParam.getChannelMchNo(), payParam.getCapability());
+ context.setChannelConfig(credential);
+ }
+
+ @Override
+ public PayTradeResultBo doPay(PayStrategyContext context) {
+ // 直接使用 doBeforePay 预组装的通道凭证
+ HkrtSdkCredential credential = context.getChannelConfig(HkrtSdkCredential.class);
+ return hkrtPayService.pay(context.getTrade(), context.getPayParam(), credential);
+ }
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/strategy/HkrtProductStrategy.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/strategy/HkrtProductStrategy.java
new file mode 100644
index 000000000..25051c179
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/strategy/HkrtProductStrategy.java
@@ -0,0 +1,69 @@
+package cn.daxpay.open.channel.hkrt.strategy;
+
+import cn.daxpay.open.platform.core.enums.pay.channel.ChannelApiCallMode;
+import cn.daxpay.open.platform.core.enums.pay.channel.ChannelPayIdType;
+import cn.daxpay.open.platform.core.enums.pay.channel.PayCapabilityEnum;
+import cn.daxpay.open.platform.core.enums.pay.channel.PayMethodEnum;
+import cn.daxpay.open.platform.core.enums.pay.channel.PayProviderEnum;
+import cn.daxpay.open.platform.core.enums.pay.channel.ProductEnum;
+import cn.daxpay.open.payment.core.strategy.product.AbsProductStrategy;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Map;
+
+import static cn.daxpay.open.platform.core.enums.pay.channel.PayProviderEnum.ALIPAY;
+import static cn.daxpay.open.platform.core.enums.pay.channel.PayProviderEnum.UNION_PAY;
+import static cn.daxpay.open.platform.core.enums.pay.channel.PayProviderEnum.WECHAT;
+
+/// # 海科融通支付产品策略
+///
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class HkrtProductStrategy extends AbsProductStrategy {
+
+ /// 海科融通支持: 微信 / 支付宝 / 银联 的扫码(JSAPI/MINI 归为 JSAPI) + 条码
+ private static final Map> METHOD_CAP_MAP = Map.ofEntries(
+ Map.entry(PayMethodEnum.WECHAT_BARCODE, List.of(PayCapabilityEnum.WECHAT_BARCODE)),
+ Map.entry(PayMethodEnum.ALIPAY_BARCODE, List.of(PayCapabilityEnum.ALIPAY_BARCODE)),
+ Map.entry(PayMethodEnum.UNION_PAY_BARCODE, List.of(PayCapabilityEnum.UNION_PAY_BARCODE)),
+ Map.entry(PayMethodEnum.WECHAT_JSAPI, List.of(PayCapabilityEnum.WECHAT_JSAPI)),
+ Map.entry(PayMethodEnum.WECHAT_MINI, List.of(PayCapabilityEnum.WECHAT_MINI)),
+ Map.entry(PayMethodEnum.ALIPAY_QR, List.of(PayCapabilityEnum.ALIPAY_QR)),
+ Map.entry(PayMethodEnum.ALIPAY_JSAPI, List.of(PayCapabilityEnum.ALIPAY_JSAPI)),
+ Map.entry(PayMethodEnum.ALIPAY_MINI, List.of(PayCapabilityEnum.ALIPAY_MINI)),
+ Map.entry(PayMethodEnum.UNION_QR, List.of(PayCapabilityEnum.UNION_PAY_QR)));
+
+ @Override
+ public ProductEnum getProduct() {
+ return ProductEnum.HKRT_PAY;
+ }
+
+ @Override
+ public boolean isIsv() { return true; }
+
+ @Override
+ public boolean isTerminal() { return false; }
+
+ @Override
+ public boolean isSandbox() { return true; }
+
+ @Override
+ public ChannelApiCallMode getApiCallMode() { return ChannelApiCallMode.ISV; }
+
+ @Override
+ public ChannelPayIdType getPayIdType() { return ChannelPayIdType.MCH; }
+
+ @Override
+ public List supportedPayProviders() {
+ return List.of(WECHAT, ALIPAY, UNION_PAY);
+ }
+
+ @Override
+ public Map> methodCapabilityMapping() {
+ return METHOD_CAP_MAP;
+ }
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/strategy/HkrtRefundStrategy.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/strategy/HkrtRefundStrategy.java
new file mode 100644
index 000000000..8cd059dae
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/strategy/HkrtRefundStrategy.java
@@ -0,0 +1,38 @@
+package cn.daxpay.open.channel.hkrt.strategy;
+
+import cn.daxpay.open.channel.hkrt.client.credential.HkrtSdkCredential;
+import cn.daxpay.open.channel.hkrt.service.isv.HkrtIsvConfigAssembler;
+import cn.daxpay.open.channel.hkrt.service.payment.HkrtRefundService;
+import cn.daxpay.open.payment.core.strategy.refund.AbsRefundStrategy;
+import cn.daxpay.open.payment.core.trade.bo.RefundResultBo;
+import cn.daxpay.open.payment.core.trade.entity.PayRefundOrder;
+import cn.daxpay.open.platform.core.enums.pay.channel.ProductEnum;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+/// # 海科融通服务商退款策略
+///
+/// 从退款订单读取通道路由参数(channelMchNo / capability),
+/// 组装通道凭证(委托 [HkrtIsvConfigAssembler]), 退款执行委托给 [HkrtRefundService]。
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class HkrtRefundStrategy extends AbsRefundStrategy {
+
+ private final HkrtRefundService hkrtRefundService;
+ private final HkrtIsvConfigAssembler hkrtIsvConfigAssembler;
+
+ @Override
+ public ProductEnum getProduct() {
+ return ProductEnum.HKRT_PAY;
+ }
+
+ @Override
+ public RefundResultBo doRefund(PayRefundOrder refundOrder) {
+ // 组装通道调用凭证
+ HkrtSdkCredential credential = hkrtIsvConfigAssembler.buildConfig(
+ refundOrder.getMchNo(), refundOrder.getChannelMchNo(), refundOrder.getCapability());
+ return hkrtRefundService.refund(refundOrder, credential);
+ }
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/strategy/HkrtSyncRefundStrategy.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/strategy/HkrtSyncRefundStrategy.java
new file mode 100644
index 000000000..4621a23eb
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/strategy/HkrtSyncRefundStrategy.java
@@ -0,0 +1,38 @@
+package cn.daxpay.open.channel.hkrt.strategy;
+
+import cn.daxpay.open.channel.hkrt.client.credential.HkrtSdkCredential;
+import cn.daxpay.open.channel.hkrt.service.isv.HkrtIsvConfigAssembler;
+import cn.daxpay.open.channel.hkrt.service.payment.HkrtRefundSyncService;
+import cn.daxpay.open.payment.core.strategy.refund.AbsSyncRefundStrategy;
+import cn.daxpay.open.payment.core.trade.bo.RefundResultBo;
+import cn.daxpay.open.payment.core.trade.entity.PayRefundOrder;
+import cn.daxpay.open.platform.core.enums.pay.channel.ProductEnum;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+/// # 海科融通服务商退款同步策略
+///
+/// 从退款订单读取通道路由参数(channelMchNo / capability),
+/// 组装通道凭证(委托 [HkrtIsvConfigAssembler]), 同步执行委托给 [HkrtRefundSyncService]。
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class HkrtSyncRefundStrategy extends AbsSyncRefundStrategy {
+
+ private final HkrtRefundSyncService hkrtRefundSyncService;
+ private final HkrtIsvConfigAssembler hkrtIsvConfigAssembler;
+
+ @Override
+ public ProductEnum getProduct() {
+ return ProductEnum.HKRT_PAY;
+ }
+
+ @Override
+ public RefundResultBo doSync(PayRefundOrder refundOrder) {
+ // 组装通道调用凭证
+ HkrtSdkCredential credential = hkrtIsvConfigAssembler.buildConfig(
+ refundOrder.getMchNo(), refundOrder.getChannelMchNo(), refundOrder.getCapability());
+ return hkrtRefundSyncService.sync(refundOrder, credential);
+ }
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/strategy/HkrtSyncStrategy.java b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/strategy/HkrtSyncStrategy.java
new file mode 100644
index 000000000..7001402aa
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/java/cn/daxpay/open/channel/hkrt/strategy/HkrtSyncStrategy.java
@@ -0,0 +1,45 @@
+package cn.daxpay.open.channel.hkrt.strategy;
+
+import cn.daxpay.open.channel.hkrt.client.credential.HkrtSdkCredential;
+import cn.daxpay.open.channel.hkrt.service.isv.HkrtIsvConfigAssembler;
+import cn.daxpay.open.channel.hkrt.service.payment.HkrtSyncService;
+import cn.daxpay.open.payment.core.strategy.pay.PayStrategyContext;
+import cn.daxpay.open.payment.core.strategy.sync.AbsSyncPayOrderStrategy;
+import cn.daxpay.open.payment.core.trade.bo.PaySyncResultBo;
+import cn.daxpay.open.payment.core.trade.entity.NormalPayOrder;
+import cn.daxpay.open.platform.core.enums.pay.channel.ProductEnum;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+/// # 海科融通服务商支付同步策略
+///
+/// 从上下文容器读取通道路由参数(channelMchNo / capability),
+/// 组装通道凭证(委托 [HkrtIsvConfigAssembler]), 同步执行委托给 [HkrtSyncService]。
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class HkrtSyncStrategy extends AbsSyncPayOrderStrategy {
+
+ private final HkrtSyncService hkrtSyncService;
+ private final HkrtIsvConfigAssembler hkrtIsvConfigAssembler;
+
+ @Override
+ public ProductEnum getProduct() {
+ return ProductEnum.HKRT_PAY;
+ }
+
+ @Override
+ public PaySyncResultBo doSync(PayStrategyContext context) {
+ // 从上下文容器读取通道路由参数
+ NormalPayOrder normalOrder = context.getContainer();
+ String channelMchNo = normalOrder != null ? normalOrder.getChannelMchNo() : null;
+ String capability = normalOrder != null ? normalOrder.getCapability() : null;
+
+ // 组装通道调用凭证
+ HkrtSdkCredential credential = hkrtIsvConfigAssembler.buildConfig(
+ context.getTrade().getMchNo(), channelMchNo, capability);
+
+ return hkrtSyncService.sync(context.getTrade(), credential);
+ }
+}
diff --git a/daxpay-channel/daxpay-channel-hkrt/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/daxpay-channel/daxpay-channel-hkrt/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 000000000..ec29d5a76
--- /dev/null
+++ b/daxpay-channel/daxpay-channel-hkrt/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1 @@
+cn.daxpay.open.channel.hkrt.HkrtChannelApp