12
0

4 Коммитууд f2f9a8d69f ... 82f56a74f2

Эзэн SHA1 Мессеж Огноо
  fcw 82f56a74f2 refactor(entity): 优化支付宝直付通创建记录实体类结构 6 өдөр өмнө
  fcw 12cbe6dc4c Merge remote-tracking branch 'origin/sit' into sit 6 өдөр өмнө
  fcw 279d7321d5 Merge remote-tracking branch 'origin/sit' into sit 1 долоо хоног өмнө
  fcw 31bc7d9bb1 feat(payment): 新增支付宝直付通二级商户支付功能 1 долоо хоног өмнө
24 өөрчлөгдсөн 2079 нэмэгдсэн , 49 устгасан
  1. 3 0
      alien-dining/src/main/java/shop/alien/dining/service/impl/StoreOrderServiceImpl.java
  2. 82 0
      alien-entity/src/main/java/shop/alien/entity/store/AlipayZftCreateRecord.java
  3. 5 0
      alien-entity/src/main/java/shop/alien/entity/store/StoreInfo.java
  4. 50 0
      alien-entity/src/main/java/shop/alien/entity/store/dto/AlipayZftBizRequestDto.java
  5. 81 0
      alien-entity/src/main/java/shop/alien/entity/store/dto/AlipayZftMerchantCreateDto.java
  6. 201 0
      alien-entity/src/main/java/shop/alien/entity/store/dto/AlipayZftMerchantSimplecreateDto.java
  7. 3 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/OrderInfoVO.java
  8. 13 0
      alien-entity/src/main/java/shop/alien/mapper/AlipayZftCreateRecordMapper.java
  9. 135 0
      alien-job/src/main/java/shop/alien/job/store/AlipayJob.java
  10. 62 0
      alien-store/src/main/java/shop/alien/store/controller/AlipayPartnerPaymentController.java
  11. 111 0
      alien-store/src/main/java/shop/alien/store/controller/AlipayZftOnboardingController.java
  12. 89 10
      alien-store/src/main/java/shop/alien/store/controller/PaymentController.java
  13. 53 0
      alien-store/src/main/java/shop/alien/store/service/AlipayZftOnboardingService.java
  14. 140 39
      alien-store/src/main/java/shop/alien/store/service/MerchantPaymentSyncScheduler.java
  15. 8 0
      alien-store/src/main/java/shop/alien/store/service/StorePaymentConfigService.java
  16. 800 0
      alien-store/src/main/java/shop/alien/store/service/impl/AlipayZftOnboardingServiceImpl.java
  17. 11 0
      alien-store/src/main/java/shop/alien/store/service/impl/StorePaymentConfigServiceImpl.java
  18. 12 0
      alien-store/src/main/java/shop/alien/store/strategy/payment/PaymentStrategy.java
  19. 197 0
      alien-store/src/main/java/shop/alien/store/strategy/payment/impl/AlipayPartnerPaymentStrategyImpl.java
  20. 5 0
      alien-store/src/main/java/shop/alien/store/strategy/payment/impl/AlipayPaymentStrategyImpl.java
  21. 5 0
      alien-store/src/main/java/shop/alien/store/strategy/payment/impl/WeChatPartnerPaymentStrategyImpl.java
  22. 5 0
      alien-store/src/main/java/shop/alien/store/strategy/payment/impl/WeChatPaymentMininProgramStrategyImpl.java
  23. 6 0
      alien-store/src/main/java/shop/alien/store/strategy/payment/impl/WeChatPaymentStrategyImpl.java
  24. 2 0
      alien-util/src/main/java/shop/alien/util/common/constant/PaymentEnum.java

+ 3 - 0
alien-dining/src/main/java/shop/alien/dining/service/impl/StoreOrderServiceImpl.java

@@ -1432,6 +1432,7 @@ public class StoreOrderServiceImpl extends ServiceImpl<StoreOrderMapper, StoreOr
         String storeAddress = "";
         String storeBlurb = "";
         String storeType = "";
+        String smid = "";
         Integer businessStatus = null;
         if (storeInfo != null) {
             storeName = storeInfo.getStoreName();
@@ -1440,6 +1441,7 @@ public class StoreOrderServiceImpl extends ServiceImpl<StoreOrderMapper, StoreOr
             storeBlurb = storeInfo.getStoreBlurb();
             storeType = storeInfo.getStoreType();
             businessStatus = storeInfo.getBusinessStatus();
+            smid = storeInfo.getAlipaySmid();
         }
 
         // 4. 查询优惠券信息(如果有)
@@ -1490,6 +1492,7 @@ public class StoreOrderServiceImpl extends ServiceImpl<StoreOrderMapper, StoreOr
         vo.setPayType(order.getPayType());
         vo.setCreatedTime(order.getCreatedTime());
         vo.setPayTime(order.getPayTime());
+        vo.setSmid(smid);
         
         log.info("查询订单信息完成, orderId={}, itemCount={}", orderId, items.size());
         return vo;

+ 82 - 0
alien-entity/src/main/java/shop/alien/entity/store/AlipayZftCreateRecord.java

@@ -0,0 +1,82 @@
+package shop.alien.entity.store;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 支付宝直付通二级商户标准进件(ant.merchant.expand.indirect.zft.create)本地提交记录。
+ */
+@Data
+@ApiModel(value = "AlipayZftCreateRecord对象", description = "支付宝直付通二级商户标准进件提交记录")
+@TableName("alipay_zft_create_record")
+public class AlipayZftCreateRecord {
+
+    @ApiModelProperty("主键")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty("门店ID(可选)")
+    @TableField("store_id")
+    private Integer storeId;
+
+    @ApiModelProperty("外部商户号 external_id")
+    @TableField("external_id")
+    private String externalId;
+
+    @ApiModelProperty("商户名称快照")
+    @TableField("merchant_name")
+    private String merchantName;
+
+    @ApiModelProperty("提交请求JSON")
+    @TableField("request_json")
+    private String requestJson;
+
+    @ApiModelProperty("支付宝响应原文")
+    @TableField("response_body")
+    private String responseBody;
+
+    @ApiModelProperty("是否调用成功")
+    @TableField("invoke_success")
+    private Boolean invokeSuccess;
+
+    @ApiModelProperty("支付宝子错误码")
+    @TableField("sub_code")
+    private String subCode;
+
+    @ApiModelProperty("支付宝子错误描述")
+    @TableField("sub_msg")
+    private String subMsg;
+
+    @ApiModelProperty("订单ID")
+    @TableField("order_id")
+    private String orderId;
+
+    @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
+    @TableField("delete_flag")
+    @TableLogic
+    private Integer deleteFlag;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "创建人ID")
+    @TableField(value = "created_user_id", fill = FieldFill.INSERT)
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "更新时间")
+    @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+
+    @ApiModelProperty(value = "更新人ID")
+    @TableField(value = "updated_user_id", fill = FieldFill.INSERT_UPDATE)
+    private Integer updatedUserId;
+
+}

+ 5 - 0
alien-entity/src/main/java/shop/alien/entity/store/StoreInfo.java

@@ -366,4 +366,9 @@ public class StoreInfo {
     @ApiModelProperty(value = "微信支付特约商户号(服务商进件审核通过后回写 sub_mchid)")
     @TableField("wechat_sub_mchid")
     private String wechatSubMchid;
+
+    @ApiModelProperty(value = "支付宝支付特约商户号(服务商进件审核通过后回写 smid)")
+    @TableField("alipay_smid")
+    private String alipaySmid;
+
 }

+ 50 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/AlipayZftBizRequestDto.java

@@ -0,0 +1,50 @@
+package shop.alien.entity.store.dto;
+
+import com.fasterxml.jackson.annotation.JsonAlias;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * 支付宝直付通 / 二级商户进件类接口统一请求体。
+ * <p>
+ * {@code bizContent} 需与官方接口的 {@code biz_content} 一致,字段以
+ * <a href="https://opendocs.alipay.com/solution/0dec7x">支付宝方案文档</a> 为准。
+ * <p>
+ * 公共参数中 {@code format}、{@code charset}、{@code sign_type}、签名、时间戳等由 SDK 自动处理。
+ * {@link #appId} 可显式传入;不传则使用门店支付配置中的应用 ID。第三方代调用时可传 {@link #appAuthToken}。
+ */
+@Data
+@ApiModel("支付宝直付通 biz_content 请求")
+public class AlipayZftBizRequestDto implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "门店ID(用于选取该门店已配置的支付宝应用与证书)", required = true)
+    private Integer storeId;
+
+    @ApiModelProperty("异步通知地址,进件类接口按文档选填")
+    private String notifyUrl;
+
+    @ApiModelProperty("支付宝应用 app_id;不传则使用门店 store_payment_config.app_id(须与证书同属该应用)")
+    @JsonProperty("app_id")
+    @JsonAlias("appId")
+    private String appId;
+
+    @ApiModelProperty("应用授权令牌 app_auth_token(第三方代调用时选填,见开放平台应用授权)")
+    @JsonProperty("app_auth_token")
+    @JsonAlias("appAuthToken")
+    private String appAuthToken;
+
+    @ApiModelProperty("接口版本 api_version,一般无需传;需覆盖 SDK 默认时填写,如 1.0")
+    @JsonProperty("api_version")
+    @JsonAlias("apiVersion")
+    private String apiVersion;
+
+    @ApiModelProperty(value = "对应支付宝接口的 biz_content(JSON 对象);不传或为空时按 {} 提交")
+    private Map<String, Object> bizContent;
+}

+ 81 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/AlipayZftMerchantCreateDto.java

@@ -0,0 +1,81 @@
+package shop.alien.entity.store.dto;
+
+import com.fasterxml.jackson.annotation.JsonAlias;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * ant.merchant.expand.indirect.zft.create(二级商户创建)请求。
+ * <p>
+ * 与官方网关 multipart 中 {@code biz_content} 一致:可将整段业务 JSON 放在 {@link #bizContent} 中;
+ * 也可仅用顶层 {@link #externalId}、{@link #merchantType} 等与 {@link #extraBiz} 组合。
+ * <p>
+ * 合并顺序:先 {@link #bizContent},再 {@link #extraBiz},最后非空的顶层字段覆盖同名键(便于局部纠偏)。
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+@ApiModel("直付通二级商户创建(zft.create)")
+public class AlipayZftMerchantCreateDto implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "门店ID(选取该门店支付宝应用与证书)", required = true)
+    private Integer storeId;
+
+    @ApiModelProperty("异步通知地址,按文档选填")
+    private String notifyUrl;
+
+    @ApiModelProperty("支付宝应用 app_id;不传则使用门店配置")
+    @JsonProperty("app_id")
+    @JsonAlias("appId")
+    private String appId;
+
+    @ApiModelProperty("应用授权令牌 app_auth_token(第三方代调用时选填)")
+    @JsonProperty("app_auth_token")
+    @JsonAlias("appAuthToken")
+    private String appAuthToken;
+
+    @ApiModelProperty("接口版本 api_version,一般无需传")
+    @JsonProperty("api_version")
+    @JsonAlias("apiVersion")
+    private String apiVersion;
+
+    @ApiModelProperty("商户编号 external_id;可与 biz_content 二选一(biz_content 内已含则可不传顶层)")
+    @JsonProperty("external_id")
+    @JsonAlias("externalId")
+    private String externalId;
+
+    @ApiModelProperty("商户类型 merchant_type;可与 biz_content 二选一")
+    @JsonProperty("merchant_type")
+    @JsonAlias("merchantType")
+    private String merchantType;
+
+    @ApiModelProperty("商户名称;可与 biz_content 二选一")
+    private String name;
+
+    @ApiModelProperty("商户别名 alias_name;可与 biz_content 二选一")
+    @JsonProperty("alias_name")
+    @JsonAlias("aliasName")
+    private String aliasName;
+
+    @ApiModelProperty("商户类目 MCC;可与 biz_content 二选一")
+    private String mcc;
+
+    @ApiModelProperty("开通服务 service;可与 biz_content 二选一")
+    private List<String> service;
+
+    @ApiModelProperty("与官方接口 biz_content 一致的整体对象(推荐)。与 extraBiz、顶层字段合并后作为最终 biz_content")
+    @JsonProperty("biz_content")
+    @JsonAlias("bizContent")
+    private Map<String, Object> bizContent;
+
+    @ApiModelProperty("其余 biz_content 字段(可选),在 biz_content 之后合并;同名字段以顶层显式字段为准")
+    private Map<String, Object> extraBiz;
+}

+ 201 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/AlipayZftMerchantSimplecreateDto.java

@@ -0,0 +1,201 @@
+package shop.alien.entity.store.dto;
+
+import com.fasterxml.jackson.annotation.JsonAlias;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * ant.merchant.expand.indirect.zft.simplecreate(直付通二级商户免证照进件)请求体。
+ * <p>
+ * 已含联系人、结算卡、开票、站点、行业资质等;公共参数中 {@link #appId}、{@code app_auth_token}、{@code api_version} 可显式传入。
+ */
+@Data
+@ApiModel("直付通二级商户简化进件(zft.simplecreate)")
+public class AlipayZftMerchantSimplecreateDto implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "门店ID", required = true)
+    private Integer storeId;
+
+    @ApiModelProperty("异步通知地址")
+    private String notifyUrl;
+
+    @ApiModelProperty("支付宝应用 app_id;不传则使用门店配置")
+    @JsonProperty("app_id")
+    @JsonAlias("appId")
+    private String appId;
+
+    @ApiModelProperty("应用授权令牌 app_auth_token(第三方代调用时选填)")
+    @JsonProperty("app_auth_token")
+    @JsonAlias("appAuthToken")
+    private String appAuthToken;
+
+    @ApiModelProperty("接口版本 api_version,一般无需传")
+    @JsonProperty("api_version")
+    @JsonAlias("apiVersion")
+    private String apiVersion;
+
+    @ApiModelProperty(value = "商户编号 external_id", required = true)
+    @JsonProperty("external_id")
+    @JsonAlias("externalId")
+    private String externalId;
+
+    @ApiModelProperty(value = "签约支付宝账户 binding_alipay_logon_id", required = true)
+    @JsonProperty("binding_alipay_logon_id")
+    @JsonAlias("bindingAlipayLogonId")
+    private String bindingAlipayLogonId;
+
+    @ApiModelProperty(value = "商户别名 alias_name", required = true)
+    @JsonProperty("alias_name")
+    @JsonAlias("aliasName")
+    private String aliasName;
+
+    @ApiModelProperty("商户客服电话 service_phone")
+    @JsonProperty("service_phone")
+    @JsonAlias("servicePhone")
+    private String servicePhone;
+
+    @ApiModelProperty(value = "二级商户名称 name", required = true)
+    private String name;
+
+    @ApiModelProperty(value = "商户类目 mcc", required = true)
+    private String mcc;
+
+    @ApiModelProperty(value = "开通服务列表 service", required = true)
+    private List<String> service;
+
+    @ApiModelProperty(value = "结算支付宝账号 alipay_logon_id(结算到支付宝时与 default_settle_rule 配合使用)")
+    @JsonProperty("alipay_logon_id")
+    @JsonAlias("alipayLogonId")
+    private String alipayLogonId;
+
+    @ApiModelProperty(value = "联系人 contact_infos(姓名、手机号在业务上必填)")
+    @JsonProperty("contact_infos")
+    @JsonAlias("contactInfos")
+    private ContactPart contactInfos;
+
+    @ApiModelProperty("默认结算规则 default_settle_rule")
+    @JsonProperty("default_settle_rule")
+    @JsonAlias("defaultSettleRule")
+    private DefaultSettleRulePart defaultSettleRule;
+
+    @ApiModelProperty("结算银行卡 biz_cards(结算到银行卡时填写,与支付宝结算二选一按文档)")
+    @JsonProperty("biz_cards")
+    @JsonAlias("bizCards")
+    private SettleCardPart bizCards;
+
+    @ApiModelProperty("经营地址 business_address(使用当面付等时按文档必填省市区与详细地址)")
+    @JsonProperty("business_address")
+    @JsonAlias("businessAddress")
+    private AddressPart businessAddress;
+
+    @ApiModelProperty("门头照,图片上传接口返回的 oss key")
+    @JsonProperty("out_door_images")
+    @JsonAlias("outDoorImages")
+    private String outDoorImages;
+
+    @ApiModelProperty("内景照,图片上传接口返回的 oss key")
+    @JsonProperty("in_door_images")
+    @JsonAlias("inDoorImages")
+    private String inDoorImages;
+
+    @ApiModelProperty("授权函/说明函图片,图片上传接口返回的 oss key")
+    @JsonProperty("license_auth_letter_image")
+    @JsonAlias("licenseAuthLetterImage")
+    private String licenseAuthLetterImage;
+
+    @Data
+    @ApiModel("联系人")
+    public static class ContactPart implements Serializable {
+        private static final long serialVersionUID = 1L;
+        @ApiModelProperty("姓名")
+        private String name;
+        @ApiModelProperty("手机号")
+        private String mobile;
+        @JsonProperty("id_card_no")
+        @JsonAlias("idCardNo")
+        @ApiModelProperty("身份证号")
+        private String idCardNo;
+        @ApiModelProperty("电话")
+        private String phone;
+        @ApiModelProperty("邮箱")
+        private String email;
+    }
+
+    @Data
+    @ApiModel("默认结算规则")
+    public static class DefaultSettleRulePart implements Serializable {
+        private static final long serialVersionUID = 1L;
+        @JsonProperty("default_settle_type")
+        @JsonAlias("defaultSettleType")
+        @ApiModelProperty("alipayAccount / bankCard")
+        private String defaultSettleType;
+        @JsonProperty("default_settle_target")
+        @JsonAlias("defaultSettleTarget")
+        @ApiModelProperty("结算目标(支付宝登录号或银行卡号等)")
+        private String defaultSettleTarget;
+    }
+
+    @Data
+    @ApiModel("结算银行卡")
+    public static class SettleCardPart implements Serializable {
+        private static final long serialVersionUID = 1L;
+        @JsonProperty("account_inst_name")
+        @JsonAlias("accountInstName")
+        private String accountInstName;
+        @JsonProperty("bank_code")
+        @JsonAlias("bankCode")
+        private String bankCode;
+        @JsonProperty("account_type")
+        @JsonAlias("accountType")
+        private String accountType;
+        @JsonProperty("usage_type")
+        @JsonAlias("usageType")
+        private String usageType;
+        @JsonProperty("account_holder_name")
+        @JsonAlias("accountHolderName")
+        private String accountHolderName;
+        @JsonProperty("account_inst_city")
+        @JsonAlias("accountInstCity")
+        private String accountInstCity;
+        @JsonProperty("account_inst_id")
+        @JsonAlias("accountInstId")
+        private String accountInstId;
+        @JsonProperty("account_no")
+        @JsonAlias("accountNo")
+        private String accountNo;
+        @JsonProperty("account_branch_name")
+        @JsonAlias("accountBranchName")
+        private String accountBranchName;
+        @JsonProperty("account_inst_province")
+        @JsonAlias("accountInstProvince")
+        private String accountInstProvince;
+    }
+
+    @Data
+    @ApiModel("地址")
+    public static class AddressPart implements Serializable {
+        private static final long serialVersionUID = 1L;
+        private String address;
+        @JsonProperty("district_code")
+        @JsonAlias("districtCode")
+        private String districtCode;
+        private String latitude;
+        @JsonProperty("city_code")
+        @JsonAlias("cityCode")
+        private String cityCode;
+        private String poiid;
+        @JsonProperty("province_code")
+        @JsonAlias("provinceCode")
+        private String provinceCode;
+        private String longitude;
+        @ApiModelProperty("如 BUSINESS_ADDRESS")
+        private String type;
+    }
+}

+ 3 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/OrderInfoVO.java

@@ -34,6 +34,9 @@ public class OrderInfoVO {
     @ApiModelProperty(value = "门店电话")
     private String storeTel;
 
+    @ApiModelProperty(value = "smid")
+    private String smid;
+
     @ApiModelProperty(value = "门店地址")
     private String storeAddress;
 

+ 13 - 0
alien-entity/src/main/java/shop/alien/mapper/AlipayZftCreateRecordMapper.java

@@ -0,0 +1,13 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.entity.store.AlipayZftCreateRecord;
+
+/**
+ * 支付宝直付通二级商户标准进件提交记录 Mapper
+ */
+@Mapper
+public interface AlipayZftCreateRecordMapper extends BaseMapper<AlipayZftCreateRecord> {
+
+}

+ 135 - 0
alien-job/src/main/java/shop/alien/job/store/AlipayJob.java

@@ -0,0 +1,135 @@
+package shop.alien.job.store;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.alipay.api.AlipayApiException;
+import com.alipay.api.AlipayClient;
+import com.alipay.api.AlipayConfig;
+import com.alipay.api.DefaultAlipayClient;
+import com.alipay.api.domain.AntMerchantExpandIndirectZftorderQueryModel;
+import com.alipay.api.request.AntMerchantExpandIndirectZftorderQueryRequest;
+import com.alipay.api.response.AntMerchantExpandIndirectZftorderQueryResponse;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.xxl.job.core.handler.annotation.XxlJob;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+import shop.alien.entity.store.AlipayZftCreateRecord;
+import shop.alien.entity.store.StoreInfo;
+import shop.alien.mapper.AlipayZftCreateRecordMapper;
+import shop.alien.store.service.StoreInfoService;
+
+import java.util.List;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class AlipayJob {
+
+    private final AlipayZftCreateRecordMapper alipayZftCreateRecordMapper;
+
+    private final StoreInfoService storeInfoService;
+
+    /**
+     * 查询支付宝二级商户进件结果,并从响应中回写门店 smid。
+     */
+    @XxlJob("getAlipayPartnerQuery")
+    public void getAlipayPartnerQuery() throws AlipayApiException {
+        AlipayClient alipayClient = new DefaultAlipayClient(getAlipayConfig());
+
+        LambdaQueryWrapper<AlipayZftCreateRecord> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(AlipayZftCreateRecord::getInvokeSuccess, Boolean.TRUE);
+        wrapper.isNotNull(AlipayZftCreateRecord::getOrderId);
+        List<AlipayZftCreateRecord> records = alipayZftCreateRecordMapper.selectList(wrapper);
+
+        for (AlipayZftCreateRecord record : records) {
+            String orderId = record.getOrderId();
+            if (StringUtils.isBlank(orderId)) {
+                continue;
+            }
+
+            AntMerchantExpandIndirectZftorderQueryRequest request = new AntMerchantExpandIndirectZftorderQueryRequest();
+            AntMerchantExpandIndirectZftorderQueryModel model = new AntMerchantExpandIndirectZftorderQueryModel();
+            model.setOrderId(orderId.trim());
+            request.setBizModel(model);
+
+            AntMerchantExpandIndirectZftorderQueryResponse response = alipayClient.execute(request);
+            if (response == null) {
+                log.warn("order.query 返回为空 orderId={}", orderId);
+                continue;
+            }
+
+            if (!response.isSuccess()) {
+                log.warn("order.query 失败 orderId={} subMsg={}", orderId, response.getSubMsg());
+                continue;
+            }
+
+            String body = response.getBody();
+            String smid = extractSmidFromZftOrderQueryBody(body);
+            if (StringUtils.isBlank(smid)) {
+                log.debug("order.query 未解析到 smid orderId={}", orderId);
+                continue;
+            }
+
+            Integer storeId = record.getStoreId();
+            if (storeId == null) {
+                log.debug("进件记录无 storeId,跳过回写 orderId={}", orderId);
+                continue;
+            }
+
+            StoreInfo storeInfo = storeInfoService.getById(storeId);
+            if (storeInfo == null) {
+                log.warn("门店不存在 storeId={} orderId={}", storeId, orderId);
+                continue;
+            }
+
+            if (smid.equals(storeInfo.getAlipaySmid())) {
+                continue;
+            }
+
+            storeInfo.setAlipaySmid(smid);
+            storeInfoService.updateById(storeInfo);
+            log.info("已回写门店 alipay_smid storeId={} orderId={}", storeId, orderId);
+        }
+    }
+
+    /**
+     * 从 ant.merchant.expand.indirect.zftorder.query 响应 body 中取 orders 首条的 smid。
+     */
+    private static String extractSmidFromZftOrderQueryBody(String body) {
+        if (StringUtils.isBlank(body)) {
+            return null;
+        }
+        try {
+            JSONObject root = JSONObject.parseObject(body);
+            JSONObject resp = root.getJSONObject("ant_merchant_expand_indirect_zftorder_query_response");
+            if (resp == null) {
+                return null;
+            }
+            JSONArray orders = resp.getJSONArray("orders");
+            if (orders == null || orders.isEmpty()) {
+                return null;
+            }
+            JSONObject first = orders.getJSONObject(0);
+            return first == null ? null : first.getString("smid");
+        } catch (Exception e) {
+            log.warn("解析 zftorder.query 响应 body 失败: {}", e.getMessage());
+            return null;
+        }
+    }
+
+    private static AlipayConfig getAlipayConfig() {
+        String privateKey  = "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCBTOvScNrk0mw+WbR6Qtx2+HrPxB5IU/V2HSPGZUTDNiCiSU9UZcuXlo0UNwMksFYR43btk4BHW9OsZETdr3uJzbEEko4jQPrY49V6V8d3yFUiexcbLKf9W2X+h7EwdPDahHk6wmM73Lm500xzv8JxdGeo/d2uGTh+qD66HcPW+QYj1YbjRV1JyCOiJ2OiwLcb28arNNLOgj6PktQ69FPSWS4hWrM7nl6DLRr6SdOlyfBT47gwPtW0BQ96/2b9O+qxKBPfrcqdRDH323HvrGFDwa8GoWTdchnKwFjmfIFHMvf+eq3tzilFi8H7vQRtzVRrndwrNa0z+1ss07fcWFDRAgMBAAECggEAItgU0OAizPk7vE22SiBMgy8RAX5rXrhpdIwDwQo3Tpf+kV1KKIdKJy6mFCWDDlcKysVOnlVag2BmmZVnzYnls8wfgQjxjuSK9Pno5JBVK51r+9/J6UPOfYMs6Duu700EPw7mEISj81TXJBGiD6tEfgiNisfm/mzDgbZbORKeXQbaTyrtB+GZn6FNSyyHA1vraARMrgfMEGNzQ4AbtfcUxGO+mejdTFs0PxAq6lovHBY3fYYHI1Nx6kf9iPoom/G4UrcMO67W6QU+1tOCZCXjy4bD2y421z/8XD73+WDyYp+Tjy0hTLqVZc7TpYAOximo1vMIUe23EdJJngdlkdpDFQKBgQDFyETL0knwBSakLfAe2BmFb2x++B4YXUnt4dGbCFBnVooxf5i06GVt/CrfkJhYK6hBSowOScIRf8P6BOSQptRZb2/I1ngQm4vcpAZw6EjUTlgOj/J3XJ+ApUNQnRqE28jDrE4m2RHg4BkQo6yA3DizJAluPCtFoCYDm1a7dV7u7wKBgQCnXEH5sD8VSxURv02/gn80g/uZIP/EOU3ycjBEqZdRGkNINwXT+zqrlZIGYb+bxLvU/R2OqKC5vhcyAL3T1A8ORYqPu5KLnAxg7C+rHuVilUWwCEH7POpCk+ETPXCZwcNvLNa5PIqBH/gdV9Y9PBTef6J4rN6V6TDFgosf5by8PwKBgDpVG71Fk1sAGep4RgbC05wgRc6Y3T9wXDqVzJ098YDY7D83E9HfbPLoWbjAS75Nef1vwCkCpgNFPIbD5KmpGp4aGM0SPC0hwzlbAy9PwxMi3CPHXsrHfZ+SnmzrOQQQUoErk40vnm9FiP74VwtWaD6llUZ25ohNeIi9yvHU5x/vAoGAdU2d1JOq85LHtsO+i9+8pyNnAsJ1YqTDtI5CtK2lqKvewswGIrlxOvi//AchVN3ExZmP0QDyfp31BhAs/T8iOl+Vqf7PzVjX+Eszch5aqwlzadmv3ZepnnamCGVE+hAsmkz0R6tebPjqYC7Ds/HbssQFLc4EyVBD5fwE5ZuR+OMCgYAvGHUYpi0dY9uMHXzL721tIopiwUfKCgLAn3yhSH3p7644NxHBqLLaBLVT2q7JAZQUaZUvXlwiyxU1zvo0xmvcbnB/Vd2qp8KbEUkvHyIYVJkM6Fn+9xBosorcrHv+7B2V1XR9WQcXvppxbN/8farWGuAA0anBD+UGrxd8B0/hHg==";
+        String alipayPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnxevClLpYc6c8A9lwmnftPO2BeU+X6aZ/+b/n1Cvq096VJKiHmqcsRgRbrnSlmPDHRrQpDti4en2Ys0L2lx7CObIr/2xP/jJVwjIO1iWHUj/w/NAbjv7dLW/FFY4SeNp8rU+hlgGgviyUxzonfNfr3v+o8grFqQq27/hiZJAofsQRMQu1dEQqoKdJj7eQLkTstiK5miJMyQ+Y3tLztrEUMBz/zRgaCEfGqmFmRZ2diy2X+1dGaX6H4+0QJ2u50eY2QTBkNuvREGbAn6/lttAgvg/+CywPYKGeC4xOfnl5wP8iA1QXYbXrVJRkZjU097nyOmSNhLy9KvJH2BNpojS1QIDAQAB";
+        AlipayConfig alipayConfig = new AlipayConfig();
+        alipayConfig.setServerUrl("https://openapi.alipay.com/gateway.do");
+        alipayConfig.setAppId("2021005196608960");
+        alipayConfig.setPrivateKey(privateKey);
+        alipayConfig.setFormat("json");
+        alipayConfig.setAlipayPublicKey(alipayPublicKey);
+        alipayConfig.setCharset("UTF-8");
+        alipayConfig.setSignType("RSA2");
+        return alipayConfig;
+    }
+}

+ 62 - 0
alien-store/src/main/java/shop/alien/store/controller/AlipayPartnerPaymentController.java

@@ -0,0 +1,62 @@
+package shop.alien.store.controller;
+
+import io.swagger.annotations.Api;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import shop.alien.entity.result.R;
+import shop.alien.store.strategy.payment.PaymentStrategyFactory;
+import shop.alien.util.common.constant.PaymentEnum;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.Map;
+
+@Slf4j
+@Api(tags = {"支付宝服务商支付接口"})
+@CrossOrigin
+@RestController
+@RequestMapping("/alipayPartnerPayment")
+@RequiredArgsConstructor
+public class AlipayPartnerPaymentController {
+
+    private final PaymentStrategyFactory paymentStrategyFactory;
+
+    /**
+     * 支付宝支付回调通知接口
+     * 异步回调地址状态码 (http 状态码) 为 200 时表示异步通知成功,返回码为 404 或 500 时则表示服务器内部错误,需要商家自行排查。
+     * @param notifyData 回调 JSON 报文
+     * @return 204 无 body 或 5XX + 失败报文
+     */
+    /**
+     * 服务商模式支付宝支付回调(与直连支付宝回调分离,需在商户平台配置 notify_url 指向本地址)
+     */
+    @RequestMapping("/alipayPartnerNotify")
+    public ResponseEntity<?> alipayPartnerNotify(@RequestBody String notifyData, HttpServletRequest request) throws Exception {
+        log.info("[支付宝服务商回调] 收到请求, Content-Length={}, Alipay-Serial={}",
+                request.getContentLength(), request.getHeader("Alipay-Serial"));
+        try {
+            R result = paymentStrategyFactory.getStrategy(PaymentEnum.ALIPAY_PARTNER.getType()).handleNotify(notifyData);
+            if (R.isSuccess(result)) {
+                return ResponseEntity.noContent().build();
+            }
+            String message = result.getMsg() != null ? result.getMsg() : "失败";
+            Map<String, String> failBody = new HashMap<>(2);
+            failBody.put("code", "FAIL");
+            failBody.put("message", message);
+            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(failBody);
+        } catch (Exception e) {
+            log.error("[支付宝服务商回调] 处理异常", e);
+            String msg = e.getMessage() != null ? e.getMessage() : "失败";
+            Map<String, String> failBody = new HashMap<>(2);
+            failBody.put("code", "FAIL");
+            failBody.put("message", msg);
+            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(failBody);
+        }
+    }
+}

+ 111 - 0
alien-store/src/main/java/shop/alien/store/controller/AlipayZftOnboardingController.java

@@ -0,0 +1,111 @@
+package shop.alien.store.controller;
+
+import com.alipay.api.AlipayApiException;
+import com.alipay.api.domain.AntMerchantExpandIndirectZftCreateModel;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiOperationSupport;
+import io.swagger.annotations.ApiSort;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.dto.AlipayZftBizRequestDto;
+import shop.alien.entity.store.dto.AlipayZftMerchantCreateDto;
+import shop.alien.entity.store.dto.AlipayZftMerchantSimplecreateDto;
+import shop.alien.store.service.AlipayZftOnboardingService;
+
+/**
+ * 支付宝直付通二级商户入驻(接口 API 进件)— 对应开放平台
+ * ant.merchant.expand.indirect.zft.* / ant.merchant.expand.order.query 等。
+ *
+ * @see <a href="https://opendocs.alipay.com/solution/0dec7x">支付宝方案文档</a>
+ */
+@Slf4j
+@Api(tags = {"支付宝直付通-二级商户进件"})
+@ApiSort(19)
+@CrossOrigin
+@RestController
+@RequestMapping("/store/alipay/zft")
+@RequiredArgsConstructor
+public class AlipayZftOnboardingController {
+
+    private final AlipayZftOnboardingService alipayZftOnboardingService;
+
+    @ApiOperationSupport(order = 1)
+    @ApiOperation(value = "进件前咨询", notes = "ant.merchant.expand.indirect.zft.consult")
+    @PostMapping("/consult")
+    public R<String> consult(@RequestBody AlipayZftBizRequestDto request) {
+        log.info("AlipayZftOnboardingController.consult storeId={}", request != null ? request.getStoreId() : null);
+        return alipayZftOnboardingService.zftConsult(request);
+    }
+
+    @ApiOperationSupport(order = 2)
+    @ApiOperation(value = "二级商户标准进件", notes = "ant.merchant.expand.indirect.zft.create;业务参数可与官方网关一致放在 body.biz_content(JSON 对象),"
+            + "或顶层 external_id 等与 extraBiz 合并;app_auth_token、notify_url、app_id 等同 curl 公共参数")
+    @PostMapping("/create")
+    public R<String> create(@RequestBody AntMerchantExpandIndirectZftCreateModel request,
+                            @RequestParam(value = "storeId", required = false) Integer storeId) throws AlipayApiException {
+        log.info("AlipayZftOnboardingController.create storeId={}", storeId);
+        return alipayZftOnboardingService.zftCreate(request, storeId);
+    }
+
+    @ApiOperationSupport(order = 3)
+    @ApiOperation(value = "二级商户简化进件(免证照)", notes = "ant.merchant.expand.indirect.zft.simplecreate,结构化 body 对应 AntMerchantExpandIndirectZftSimplecreateModel")
+    @PostMapping("/simplecreate")
+    public R<String> simplecreate(@RequestBody AlipayZftMerchantSimplecreateDto request) {
+        log.info("AlipayZftOnboardingController.simplecreate storeId={}", request != null ? request.getStoreId() : null);
+        return alipayZftOnboardingService.zftSimplecreate(request);
+    }
+
+    @ApiOperationSupport(order = 4)
+    @ApiOperation(value = "申请单查询", notes = "ant.merchant.expand.order.query;传 orderId(申请单号)")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "orderId", value = "申请单 id(支付宝 order_id)", required = true, dataType = "string", paramType = "query")
+    })
+    @PostMapping("/order/query")
+    public R<String> orderQuery(@RequestParam String orderId) {
+        log.info("AlipayZftOnboardingController.orderQuery orderId={}", orderId);
+        return alipayZftOnboardingService.orderQuery(orderId);
+    }
+
+    @ApiOperationSupport(order = 5)
+    @ApiOperation(value = "二级商户信息修改", notes = "ant.merchant.expand.indirect.zft.modify")
+    @PostMapping("/modify")
+    public R<String> modify(@RequestBody AlipayZftBizRequestDto request) throws AlipayApiException {
+        log.info("AlipayZftOnboardingController.modify storeId={}", request != null ? request.getStoreId() : null);
+        return alipayZftOnboardingService.zftModify(request);
+    }
+
+    @ApiOperationSupport(order = 6)
+    @ApiOperation(value = "申请单补件", notes = "ant.merchant.expand.indirect.supplement.create")
+    @PostMapping("/supplement/create")
+    public R<String> supplementCreate(@RequestBody AlipayZftBizRequestDto request) {
+        log.info("AlipayZftOnboardingController.supplementCreate storeId={}", request != null ? request.getStoreId() : null);
+        return alipayZftOnboardingService.supplementCreate(request);
+    }
+
+    @ApiOperationSupport(order = 7)
+    @ApiOperation(value = "进件图片上传", notes = "ant.merchant.expand.indirect.image.upload")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店ID(取对应支付宝配置)", required = true, dataType = "int", paramType = "form"),
+            @ApiImplicitParam(name = "imageType", value = "图片格式:bmp/jpg/jpeg/png/gif", required = true, dataType = "string", paramType = "form"),
+            @ApiImplicitParam(name = "imageContent", value = "图片文件(二进制,<=10MB)", required = true, dataType = "file", paramType = "form"),
+            @ApiImplicitParam(name = "app_id", value = "支付宝应用 app_id;不传则使用门店支付配置", required = false, dataType = "string", paramType = "form"),
+            @ApiImplicitParam(name = "appAuthToken", value = "应用授权令牌 app_auth_token(第三方代调用时选填)", required = false, dataType = "string", paramType = "form"),
+            @ApiImplicitParam(name = "apiVersion", value = "接口版本 api_version(一般无需传)", required = false, dataType = "string", paramType = "form")
+    })
+    @PostMapping("/image/upload")
+    public R<String> imageUpload(@RequestParam(required = false) Integer storeId,
+                                 @RequestParam String imageType,
+                                 @RequestParam("imageContent") MultipartFile imageContent,
+                                 @RequestParam(value = "app_id", required = false) String appId,
+                                 @RequestParam(required = false) String appAuthToken,
+                                 @RequestParam(required = false) String apiVersion) throws AlipayApiException {
+        log.info("AlipayZftOnboardingController.imageUpload storeId={}, imageType={}", storeId, imageType);
+        return alipayZftOnboardingService.imageUpload(storeId, imageType, imageContent, appId, appAuthToken, apiVersion);
+    }
+}

+ 89 - 10
alien-store/src/main/java/shop/alien/store/controller/PaymentController.java

@@ -7,13 +7,20 @@ import io.swagger.annotations.ApiImplicitParams;
 import io.swagger.annotations.ApiOperation;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.CrossOrigin;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 import shop.alien.entity.result.R;
+import shop.alien.store.feign.DiningServiceFeign;
 import shop.alien.store.strategy.payment.PaymentStrategyFactory;
+import shop.alien.util.common.constant.PaymentEnum;
 
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
 import java.util.Map;
 
 /**
@@ -30,32 +37,95 @@ import java.util.Map;
 public class PaymentController {
 
     private final PaymentStrategyFactory paymentStrategyFactory;
+    private final DiningServiceFeign diningServiceFeign;
 
+    private static String authHeader(HttpServletRequest request) {
+        String h = request.getHeader("Authorization");
+        if (h == null || h.isEmpty()) {
+            h = request.getHeader("authorization");
+        }
+        return h;
+    }
 
     @ApiOperation("创建预支付订单")
     @ApiImplicitParams({
             @ApiImplicitParam(name = "price", value = "订单金额", required = true, paramType = "query", dataType = "String"),
             @ApiImplicitParam(name = "subject", value = "订单标题", required = true, paramType = "query", dataType = "String"),
-            @ApiImplicitParam(name = "payType", value = "支付类型(alipay:支付宝, wechatPay:微信支付)", required = true, paramType = "query", dataType = "String")
+            @ApiImplicitParam(name = "smid", value = "smid", required = true, paramType = "query", dataType = "String"),
+            @ApiImplicitParam(name = "payType", value = "支付类型(alipay:支付宝, wechatPay:微信支付, wechatPayPartner:微信服务商模式, wechatPayMininProgram:微信小程序点餐)", required = true, paramType = "query", dataType = "String"),
+            @ApiImplicitParam(name = "storeId", value = "门店ID(wechatPayPartner 必填,用于读取 store_info.wechat_sub_mchid)", required = false, paramType = "query", dataType = "int")
     })
     @RequestMapping("/prePay")
-    public R prePay(String price, String subject, String payType) {
-        log.info("PaymentController:prePay, price: {}, subject: {}, payType: {}", price, subject, payType);
+    public R prePay(
+            HttpServletRequest request,
+            @RequestParam String price,
+            @RequestParam String subject,
+            @RequestParam String payType,
+            @RequestParam(required = false) String smid,
+            @RequestParam(required = false) String payer,
+            @RequestParam(required = false) String orderNo,
+            @RequestParam(required = false) Integer storeId,
+            @RequestParam(required = false) Integer couponId,
+            @RequestParam(required = false) Integer payerId,
+            @RequestParam(required = false) String tablewareFee,
+            @RequestParam(required = false) String discountAmount,
+            @RequestParam(required = false) String payAmount) {
+        log.info("PaymentController:prePay, price: {}, subject: {}, payType: {}, orderNo: {}, storeId: {}", price, subject, payType, orderNo, storeId);
         try {
-            return paymentStrategyFactory.getStrategy(payType).createPrePayOrder(price, subject);
+            if ("wechatPayMininProgram".equals(payType) && storeId != null && orderNo != null && payer != null) {
+                return diningServiceFeign.prePay(
+                        authHeader(request), price, subject, payType, payer, orderNo, storeId,
+                        couponId, payerId, tablewareFee, discountAmount, payAmount);
+            }
+            // 微信服务商 APP:传入 orderNo 时与小程序策略一致,绑定/刷新 store_order 与微信商户单号
+            if ("wechatPayPartner".equals(payType) && storeId != null && orderNo != null) {
+                return paymentStrategyFactory.getStrategy(payType).createPrePayOrder(
+                        price, subject, payer, orderNo, storeId, couponId, payerId,
+                        tablewareFee, discountAmount, payAmount);
+            }
+            if ("aliPayPartner".equals(payType) && storeId != null && orderNo != null && payer != null) {
+                return paymentStrategyFactory.getStrategy(payType).createPreAliPayOrder(orderNo, price, subject, smid, storeId);
+            }
+            return paymentStrategyFactory.getStrategy(payType).createPrePayOrder(price, subject, storeId);
         } catch (Exception e) {
             return R.fail(e.getMessage());
         }
     }
 
     /**
-     * 通知接口 之后可能会用
-     * @param notifyData
-     * @return
+     * 微信服务商 APP 支付回调(需在商户平台将 notify_url 配置为本地址;验签通过后返回 204)
+     */
+    @RequestMapping("/weChatPartnerNotify")
+    public ResponseEntity<?> weChatPartnerNotify(@RequestBody String notifyData, HttpServletRequest request) throws Exception {
+        log.info("[微信服务商APP回调] 收到请求, Content-Length={}, Wechatpay-Serial={}",
+                request.getContentLength(), request.getHeader("Wechatpay-Serial"));
+        try {
+            R<?> result = paymentStrategyFactory.getStrategy(PaymentEnum.WECHAT_PAY_PARTNER.getType())
+                    .handleNotify(notifyData, request);
+            if (R.isSuccess(result)) {
+                return ResponseEntity.noContent().build();
+            }
+            String message = result != null && result.getMsg() != null ? result.getMsg() : "失败";
+            Map<String, String> failBody = new HashMap<>(2);
+            failBody.put("code", "FAIL");
+            failBody.put("message", message);
+            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(failBody);
+        } catch (Exception e) {
+            log.error("[微信服务商APP回调] 处理异常", e);
+            String msg = e.getMessage() != null ? e.getMessage() : "失败";
+            Map<String, String> failBody = new HashMap<>(2);
+            failBody.put("code", "FAIL");
+            failBody.put("message", msg);
+            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(failBody);
+        }
+    }
+
+    /**
+     * 预留:非微信 V3 回调(当前未使用)
      */
     @RequestMapping("/notify")
     public R notify(String notifyData) {
-        return  null;
+        return null;
     }
 
     /**
@@ -64,10 +134,19 @@ public class PaymentController {
      * @param payType 支付类型
      * @return 订单状态信息
      */
+    /**
+     * wechatPayPartner 查单需传 storeId,与预下单一致,用于解析子商户号。
+     */
     @RequestMapping("/searchOrderByOutTradeNoPath")
-    public R searchOrderByOutTradeNoPath(String transactionId, String payType) {
+    public R searchOrderByOutTradeNoPath(
+            @RequestParam String transactionId,
+            @RequestParam String payType,
+            @RequestParam(required = false) Integer storeId) {
         try {
-            return paymentStrategyFactory.getStrategy(payType).searchOrderByOutTradeNoPath(transactionId);
+            if ("wechatPayMininProgram".equals(payType) && storeId != null) {
+                return diningServiceFeign.searchOrderByOutTradeNoPath(transactionId, payType, storeId);
+            }
+            return paymentStrategyFactory.getStrategy(payType).searchOrderByOutTradeNoPath(transactionId, storeId);
         } catch (Exception e) {
             return R.fail(e.getMessage());
         }

+ 53 - 0
alien-store/src/main/java/shop/alien/store/service/AlipayZftOnboardingService.java

@@ -0,0 +1,53 @@
+package shop.alien.store.service;
+
+import com.alipay.api.AlipayApiException;
+import com.alipay.api.domain.AntMerchantExpandIndirectZftCreateModel;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.dto.AlipayZftBizRequestDto;
+import shop.alien.entity.store.dto.AlipayZftMerchantCreateDto;
+import shop.alien.entity.store.dto.AlipayZftMerchantSimplecreateDto;
+import org.springframework.web.multipart.MultipartFile;
+
+/**
+ * 支付宝直付通二级商户入驻相关接口封装(调用支付宝开放平台 API)。
+ */
+public interface AlipayZftOnboardingService {
+
+    /**
+     * ant.merchant.expand.indirect.zft.consult — 进件前咨询
+     */
+    R<String> zftConsult(AlipayZftBizRequestDto request);
+
+    /**
+     * ant.merchant.expand.indirect.zft.create — 二级商户标准进件(结构化必填:external_id、merchant_type、name、alias_name、mcc、service)
+     */
+    R<String> zftCreate(AntMerchantExpandIndirectZftCreateModel request, Integer storeId) throws AlipayApiException;
+
+    /**
+     * ant.merchant.expand.indirect.zft.simplecreate — 二级商户免证照进件(SDK setBizModel)
+     */
+    R<String> zftSimplecreate(AlipayZftMerchantSimplecreateDto request);
+
+    /**
+     * ant.merchant.expand.order.query — 商户申请单查询
+     *
+     * @param orderId 申请单 id(支付宝 order_id)
+     */
+    R<String> orderQuery(String orderId);
+
+    /**
+     * ant.merchant.expand.indirect.zft.modify — 二级商户信息修改
+     */
+    R<String> zftModify(AlipayZftBizRequestDto request) throws AlipayApiException;
+
+    /**
+     * ant.merchant.expand.indirect.supplement.create — 申请单补件
+     */
+    R<String> supplementCreate(AlipayZftBizRequestDto request);
+
+    /**
+     * ant.merchant.expand.indirect.image.upload — 图片上传
+     */
+    R<String> imageUpload(Integer storeId, String imageType, MultipartFile imageContent,
+                          String appId, String appAuthToken, String apiVersion) throws AlipayApiException;
+}

+ 140 - 39
alien-store/src/main/java/shop/alien/store/service/MerchantPaymentSyncScheduler.java

@@ -1,63 +1,164 @@
 package shop.alien.store.service;
 
+import com.alibaba.fastjson.JSONObject;
+import com.alipay.api.internal.util.AlipaySignature;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.MediaType;
 import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Component;
-import shop.alien.entity.result.R;
-import shop.alien.entity.store.MerchantPaymentOrder;
-import shop.alien.store.strategy.merchantPayment.MerchantPaymentStrategy;
-import shop.alien.store.strategy.merchantPayment.MerchantPaymentStrategyFactory;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import shop.alien.entity.store.StorePaymentConfig;
 
+import javax.servlet.http.HttpServletRequest;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.Date;
-import java.util.List;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
- * 商户支付状态定时同步(无异步回调时的兜底)
- * 定时扫描近期待支付单,主动向支付宝/微信查单并更新订单状态。
+ * 1)商户支付状态定时同步(无异步回调时的兜底),由 {@code payment.sync.enabled} 控制是否执行;<br/>
+ * 2)支付宝开放平台<strong>消息通知</strong>:二级商户进件审核通过 {@code ant.merchant.expand.indirect.zft.passed},
+ * POST 至应用网关(请在开放平台配置网关 URL,例如 .../store/alipay/message/zft-passed),
+ * 验签通过后更新门店支付配置中的二级商户标识等。
  *
  * @author system
  */
 @Slf4j
-@Component
+@RestController
+@RequestMapping("/store/alipay/message")
 @RequiredArgsConstructor
-@ConditionalOnProperty(name = "payment.sync.enabled", havingValue = "true", matchIfMissing = false)
 public class MerchantPaymentSyncScheduler {
 
-    /** 扫描最近多少分钟内创建的待支付单 */
-    private static final int WITHIN_MINUTES = 30;
+    private static final String MSG_ZFT_PASSED = "ant.merchant.expand.indirect.zft.passed";
 
-    private final MerchantPaymentOrderService merchantPaymentOrderService;
-    private final MerchantPaymentStrategyFactory merchantPaymentStrategyFactory;
+    /** 为 false 时不跑同步任务;消息通知接口始终可用 */
+    @Value("${payment.sync.enabled:false}")
+    private boolean paymentSyncEnabled;
+
+    /** 生产环境应为 true;本地联调可临时关闭验签 */
+    @Value("${payment.alipay.zft-passed.verify-signature:true}")
+    private boolean verifyZftPassedSignature;
+
+    private final StorePaymentConfigService storePaymentConfigService;
+
+    /**
+     * 支付宝异步消息:直付通二级商户进件审核通过。
+     * <p>
+     * Content-Type: application/x-www-form-urlencoded,公共参数含 notify_id、utc_timestamp、msg_method、
+     * app_id、version、biz_content、sign、sign_type、charset 等;biz_content 内含 order_id、smid、
+     * external_id、card_alias_no、memo。
+     * </p>
+     * 处理成功请返回纯文本 {@code success},失败返回 {@code fail}(支付宝将按策略重试)。
+     */
+    @PostMapping(value = "/zft-passed", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, produces = "text/plain;charset=UTF-8")
+    public String onZftPassedMessage(HttpServletRequest request) {
+        try {
+            Map<String, String> params = toSingleValueMap(request);
+            String msgMethod = params.get("msg_method");
+            if (!MSG_ZFT_PASSED.equals(msgMethod)) {
+                log.debug("忽略非 zft.passed 消息 msg_method={}", msgMethod);
+                return "success";
+            }
+            String appId = params.get("app_id");
+            StorePaymentConfig config = storePaymentConfigService.getByAppId(appId);
+            if (config == null) {
+                log.warn("zft.passed 未找到 app_id 对应支付配置 appId={}", appId);
+                return "fail";
+            }
+            if (verifyZftPassedSignature && !verifyAlipaySignature(params, config)) {
+                log.warn("zft.passed 验签失败 appId={}", appId);
+                return "fail";
+            }
+            String bizRaw = params.get("biz_content");
+            if (StringUtils.isBlank(bizRaw)) {
+                return "fail";
+            }
+            JSONObject biz = JSONObject.parseObject(bizRaw);
+            applyZftPassed(config, biz);
+            return "success";
+        } catch (Exception e) {
+            log.error("zft.passed 消息处理异常", e);
+            return "fail";
+        }
+    }
+
+    private void applyZftPassed(StorePaymentConfig config, JSONObject biz) {
+        String smid = biz.getString("smid");
+        if (StringUtils.isNotBlank(smid)) {
+            config.setStoreAliId(smid);
+        }
+        log.info("直付通二级商户进件审核通过 storeId={} orderId={} smid={} externalId={} cardAliasNo={} memo={}",
+                config.getStoreId(),
+                biz.getString("order_id"),
+                smid,
+                biz.getString("external_id"),
+                biz.getString("card_alias_no"),
+                biz.getString("memo"));
+        storePaymentConfigService.updateById(config);
+    }
+
+    private boolean verifyAlipaySignature(Map<String, String> params, StorePaymentConfig config) {
+        if (config.getAlipayPublicCert() == null || config.getAlipayPublicCert().length == 0) {
+            log.warn("验签失败:未配置支付宝公钥证书");
+            return false;
+        }
+        Path temp = null;
+        try {
+            temp = Files.createTempFile("alipay_pub_", ".crt");
+            Files.write(temp, config.getAlipayPublicCert());
+            String signType = StringUtils.defaultIfBlank(params.get("sign_type"), "RSA2");
+            return AlipaySignature.rsaCertCheckV1(params, temp.toAbsolutePath().toString(), "UTF-8", signType);
+        } catch (Exception e) {
+            log.error("支付宝消息验签异常", e);
+            return false;
+        } finally {
+            if (temp != null) {
+                try {
+                    Files.deleteIfExists(temp);
+                } catch (Exception ignored) {
+                    // ignore
+                }
+            }
+        }
+    }
+
+    private static Map<String, String> toSingleValueMap(HttpServletRequest request) {
+        Map<String, String> params = new HashMap<>();
+        Map<String, String[]> raw = request.getParameterMap();
+        for (Map.Entry<String, String[]> e : raw.entrySet()) {
+            String[] v = e.getValue();
+            if (v == null || v.length == 0) {
+                params.put(e.getKey(), "");
+            } else if (v.length == 1) {
+                params.put(e.getKey(), v[0]);
+            } else {
+                StringBuilder sb = new StringBuilder();
+                for (int i = 0; i < v.length; i++) {
+                    if (i > 0) {
+                        sb.append(',');
+                    }
+                    sb.append(v[i]);
+                }
+                params.put(e.getKey(), sb.toString());
+            }
+        }
+        return params;
+    }
 
     /**
-     * 每 2 分钟执行一次:拉取近期待支付单,逐笔查第三方并更新状态
+     * 每 2 分钟执行一次:拉取近期待支付单,逐笔查第三方并更新状态(需开启 payment.sync.enabled)
      */
     @Scheduled(fixedDelayString = "${payment.sync.interval-ms:120000}")
     public void syncPaymentStatus() {
-        //暂时不使用定时
-         log.info("商户支付同步:本批时间={}", new Date().getTime());
-
-//        List<MerchantPaymentOrder> list = merchantPaymentOrderService.listUnpaidRecent(WITHIN_MINUTES);
-//        if (list == null || list.isEmpty()) {
-//            return;
-//        }
-//        log.debug("商户支付同步:本批待支付单数={}", list.size());
-//        for (MerchantPaymentOrder po : list) {
-//            try {
-//                if (po.getPayType() == null || !merchantPaymentStrategyFactory.supports(po.getPayType())) {
-//                    log.warn("商户支付同步:不支持的 payType={}, outTradeNo={}", po.getPayType(), po.getOutTradeNo());
-//                    continue;
-//                }
-//                MerchantPaymentStrategy strategy = merchantPaymentStrategyFactory.getStrategy(po.getPayType());
-//                R<Object> r = strategy.queryPayStatus(po.getStoreId(), po.getOutTradeNo());
-//                if (r != null && R.isSuccess(r)) {
-//                    log.info("商户支付同步:已更新为已支付,outTradeNo={}", po.getOutTradeNo());
-//                }
-//            } catch (Exception e) {
-//                log.warn("商户支付同步:查单异常 outTradeNo={}", po.getOutTradeNo(), e);
-//            }
-//        }
+        if (!paymentSyncEnabled) {
+            return;
+        }
+        log.info("商户支付同步:本批时间={}", new Date().getTime());
+        // 待启用:按 MerchantPaymentOrder 查单并回写状态,可注入 MerchantPaymentOrderService、MerchantPaymentStrategyFactory
     }
 }

+ 8 - 0
alien-store/src/main/java/shop/alien/store/service/StorePaymentConfigService.java

@@ -29,6 +29,14 @@ public interface StorePaymentConfigService extends IService<StorePaymentConfig>
     StorePaymentConfig getByStoreUserId(Integer storeUserId);
 
     /**
+     * 根据支付宝应用 app_id 精确查询一条支付配置(异步通知验签、按 app_id 反查门店)
+     *
+     * @param appId 应用ID
+     * @return 多条时取一条,不存在返回 null
+     */
+    StorePaymentConfig getByAppId(String appId);
+
+    /**
      * 分页查询支付配置列表
      *
      * @param page   分页参数

+ 800 - 0
alien-store/src/main/java/shop/alien/store/service/impl/AlipayZftOnboardingServiceImpl.java

@@ -0,0 +1,800 @@
+package shop.alien.store.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.alipay.api.*;
+import com.alipay.api.domain.*;
+import com.alipay.api.AlipayClient;
+import com.alipay.api.request.*;
+import com.alipay.api.response.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.AlipayZftCreateRecord;
+import shop.alien.entity.store.StorePaymentConfig;
+import shop.alien.entity.store.dto.AlipayZftBizRequestDto;
+import shop.alien.entity.store.dto.AlipayZftMerchantCreateDto;
+import shop.alien.entity.store.dto.AlipayZftMerchantSimplecreateDto;
+import shop.alien.mapper.AlipayZftCreateRecordMapper;
+import shop.alien.store.service.AlipayZftOnboardingService;
+import shop.alien.store.service.StorePaymentConfigService;
+
+import java.lang.reflect.Method;
+import java.util.Date;
+import java.nio.charset.StandardCharsets;
+import java.util.LinkedHashMap;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 使用门店 {@link StorePaymentConfig} 中的证书与密钥,以服务商/平台应用身份调用支付宝进件接口。
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class AlipayZftOnboardingServiceImpl implements AlipayZftOnboardingService {
+
+    @Value("${payment.aliPay.host}")
+    private String aliPayHost;
+
+    private final StorePaymentConfigService storePaymentConfigService;
+
+    private final AlipayZftCreateRecordMapper alipayZftCreateRecordMapper;
+
+    @Override
+    public R<String> zftConsult(AlipayZftBizRequestDto request) {
+        AntMerchantExpandIndirectZftConsultRequest api = new AntMerchantExpandIndirectZftConsultRequest();
+        return invoke(request, api);
+    }
+
+    @Override
+    public R<String> zftCreate(AntMerchantExpandIndirectZftCreateModel request1, Integer storeId) throws AlipayApiException {
+        // 初始化SDK
+        AlipayClient alipayClient = new DefaultAlipayClient(getAlipayConfig());
+
+        // 构造请求参数以调用接口
+        AntMerchantExpandIndirectZftCreateRequest request = new AntMerchantExpandIndirectZftCreateRequest();
+        AntMerchantExpandIndirectZftCreateModel model = new AntMerchantExpandIndirectZftCreateModel();
+
+        // 设置商户编号
+        model.setExternalId("2088560449225278");
+
+        // 设置商户类型
+        model.setMerchantType("01");
+
+        // 设置进件的二级商户名称
+        model.setName("爱丽恩(大连)餐饮有限公司");
+
+        // 设置商户别名
+        model.setAliasName("爱丽恩(大连)餐饮有限公司");
+
+        // 设置商户类别码 mcc
+        model.setMcc("B0002");
+
+        // 设置商户使用服务
+        List<String> service = new ArrayList<String>();
+        service.add("当面付");
+        service.add("app支付");
+        model.setService(service);
+
+        // 设置内景照
+        List<String> inDoorImages = new ArrayList<String>();
+        inDoorImages.add("889493da-4b52-4506-9a25-5dc29b96753b.png");
+        model.setInDoorImages(inDoorImages);
+
+        // 设置商户证件编号
+        model.setCertNo("91210202MAE7M17B6P");
+
+        // 设置商户证件类型
+        model.setCertType("201");
+
+        // 设置商户证件图片url
+        model.setCertImage("c2a7a12b-26c1-4eb3-bfcd-2f404fc2ca63.png");
+
+        // 设置证件反面图片
+//        model.setCertImageBack("25a10d04-ca9c-41aa-896f-3e0904bec470.jpg");
+
+        // 设置法人名称
+        model.setLegalName("唐永顺");
+
+        // 设置法人身份证号
+        model.setLegalCertNo("210106196804141555");
+
+        // 设置法人身份证正面url
+//        model.setLegalCertFrontImage("25a10d04-ca9c-41aa-896f-3e0904bec470.jpg");
+
+        // 设置法人身份证反面url
+//        model.setLegalCertBackImage("25a10d04-ca9c-41aa-896f-3e0904bec470.jpg");
+
+        // 设置经营地址
+        AddressInfo businessAddress = new AddressInfo();
+        businessAddress.setAddress("大连市中山区港湾街12A号8A-7号");
+        businessAddress.setDistrictCode("210202");
+//        businessAddress.setLatitude("60.270001");
+        businessAddress.setCityCode("210200");
+//        businessAddress.setPoiid("B0FFIVU189");
+        businessAddress.setProvinceCode("210000");
+//        businessAddress.setLongitude("120.760001");
+        model.setBusinessAddress(businessAddress);
+
+        // 设置客服电话
+//        model.setServicePhone("0571-85022088");
+
+        // 设置商户联系人信息
+        List<ContactInfo> contactInfos = new ArrayList<ContactInfo>();
+        ContactInfo contactInfos0 = new ContactInfo();
+//        contactInfos0.setIdCardNo("110000199001011234");
+//        contactInfos0.setPhone("0571-85022088");
+        contactInfos0.setName("王骏");
+        contactInfos0.setMobile("13352287427");
+//        contactInfos0.setEmail("user@domain.com");
+        contactInfos.add(contactInfos0);
+        model.setContactInfos(contactInfos);
+
+        // 设置结算银行卡
+//        List<SettleCardInfo> bizCards = new ArrayList<SettleCardInfo>();
+//        SettleCardInfo bizCards0 = new SettleCardInfo();
+//        bizCards0.setAccountInstName("招商银行");
+//        bizCards0.setBankCode("103290003044");
+//        bizCards0.setAccountType("DC");
+//        bizCards0.setUsageType("01");
+//        bizCards0.setAccountHolderName("张三");
+//        bizCards0.setAccountInstCity("杭州市");
+//        bizCards0.setAccountInstId("CMB");
+//        bizCards0.setAccountNo("6214855710610408");
+//        bizCards0.setAccountInstProvince("浙江省");
+//        bizCards0.setAccountBranchName("招商银行杭州高新支行");
+//        bizCards.add(bizCards0);
+//        model.setBizCards(bizCards);
+
+        // 设置商户行业资质
+//        List<IndustryQualificationInfo> qualifications = new ArrayList<IndustryQualificationInfo>();
+//        IndustryQualificationInfo qualifications0 = new IndustryQualificationInfo();
+//        qualifications0.setIndustryQualificationImage("c6c0c7a1-b9d5-4e5d-b9d4-9eed39f00e65.jpg");
+//        qualifications0.setIndustryQualificationType("323");
+//        qualifications.add(qualifications0);
+//        model.setQualifications(qualifications);
+
+        // 设置门头照
+        List<String> outDoorImages = new ArrayList<String>();
+        outDoorImages.add("64212801-093d-49a8-9769-5e2b288fb858.png");
+        model.setOutDoorImages(outDoorImages);
+
+        // 设置授权函
+//        model.setLicenseAuthLetterImage("c6c0c7a1-b9d5-4e5d-b9d4-9eed39f00e65.jpg");
+
+
+
+        // 设置二级商户与服务商的签约时间
+//        model.setSignTimeWithIsv("2015-04-15");
+
+        // 设置结算支付宝账号
+        model.setAlipayLogonId("ailien@alienyan.cn");
+
+        // 设置sites
+        List<SiteInfo> sites = new ArrayList<SiteInfo>();
+        SiteInfo sites0 = new SiteInfo();
+//        sites0.setIcpOrgName("支付宝(中国)网络技术有限公司");
+        sites0.setSiteType("02");
+//        sites0.setSiteDomain("www.alipay.com");
+//        sites0.setScreenshotImage("c6c0c7a1-b9d5-4e5d-b9d4-9eed39f00e65.jpg");
+//        sites0.setRemark("备注说明");
+//        sites0.setAuthLetterImage("c6c0c7a1-b9d5-4e5d-b9d4-9eed39f00e65.jpg");
+        sites0.setSiteName("应用2.0签约2025092468860692");
+//        sites0.setMarket("豌豆荚");
+//        sites0.setPassword("123456");
+//        sites0.setDownload("https://itunes.apple.com/cn/app/id333206289?mt=8");
+//        sites0.setTinyAppId("2021004105652035");
+//        sites0.setSiteUrl("https://open.alipay.com");
+//        sites0.setIcpServiceName("支付宝");
+//        sites0.setRemarkImage("c6c0c7a1-b9d5-4e5d-b9d4-9eed39f00e65.jpg");
+//        sites0.setAccount("测试账号");
+//        sites0.setIcpNo("沪ICP备15027489号-2");
+//        sites0.setStatus("ONLINE");
+        sites.add(sites0);
+        model.setSites(sites);
+
+        // 设置开票资料信息
+//        MerchantInvoiceInfo invoiceInfo = new MerchantInvoiceInfo();
+//        invoiceInfo.setMailTelephone("057162288888");
+//        invoiceInfo.setTaxPayerQualification("01");
+//        invoiceInfo.setAddress("浙江省杭州市西湖区西溪路蚂蚁金服");
+//        invoiceInfo.setAcceptElectronic(true);
+//        invoiceInfo.setTelephone("057162288888");
+//        invoiceInfo.setTitle("蚂蚁金服(杭州)信息技术有限公司");
+//        invoiceInfo.setMailName("张三");
+//        invoiceInfo.setAutoInvoice(true);
+//        invoiceInfo.setTaxPayerValid("19981011");
+//        invoiceInfo.setTaxNo("51010482542598631219");
+//        invoiceInfo.setBankName("中国银行");
+//        AddressInfo mailAddress = new AddressInfo();
+//        mailAddress.setAddress("万塘路18号黄龙时代广场B座");
+//        mailAddress.setDistrictCode("371002");
+//        mailAddress.setLatitude("60.270001");
+//        mailAddress.setCityCode("371000");
+//        mailAddress.setPoiid("B0FFIVU189");
+//        mailAddress.setProvinceCode("370000");
+//        mailAddress.setType("BUSINESS_ADDRESS");
+//        mailAddress.setLongitude("120.760001");
+//        invoiceInfo.setMailAddress(mailAddress);
+//        invoiceInfo.setBankAccount("1234567812345678123");
+//        model.setInvoiceInfo(invoiceInfo);
+
+        // 设置目前只有个体工商户商户类型要求填入本字段
+//        model.setCertName("xxxx小卖铺");
+
+        // 设置签约支付宝账户
+        model.setBindingAlipayLogonId("ailien@alienyan.cn");
+
+        // 设置默认可不填
+//        model.setLegalCertType("100");
+
+        // 设置默认结算规则
+//        DefaultSettleRule defaultSettleRule = new DefaultSettleRule();
+//        defaultSettleRule.setDefaultSettleType("alipayAccount");
+//        defaultSettleRule.setDefaultSettleTarget("myalipay@alipay.com");
+//        model.setDefaultSettleRule(defaultSettleRule);
+
+        // 设置商家性质
+//        model.setMerchantNature("01");
+
+        //java测试用
+//        request.setBizModel(model);
+        request.setBizModel(request1);
+        // 第三方代调用模式下请设置app_auth_token
+        // request.putOtherTextParam("app_auth_token", "<-- 请填写应用授权令牌 -->");
+
+        Date now = new Date();
+        AlipayZftCreateRecord record = new AlipayZftCreateRecord();
+        record.setStoreId(storeId);
+        record.setExternalId(request1 != null ? request1.getExternalId() : null);
+        record.setMerchantName(request1 != null ? request1.getName() : null);
+        record.setRequestJson(request1 != null ? JSON.toJSONString(request1) : "{}");
+        record.setCreatedTime(now);
+        record.setUpdatedTime(now);
+
+        AntMerchantExpandIndirectZftCreateResponse response;
+        try {
+            response = alipayClient.execute(request);
+        } catch (AlipayApiException e) {
+            record.setInvokeSuccess(false);
+            record.setSubMsg(StringUtils.isNotBlank(e.getErrMsg()) ? e.getErrMsg() : e.getMessage());
+            alipayZftCreateRecordMapper.insert(record);
+            throw e;
+        }
+        record.setResponseBody(response != null ? response.getBody() : null);
+        record.setInvokeSuccess(response != null && response.isSuccess());
+        if (response != null) {
+            record.setSubCode(response.getSubCode());
+            record.setSubMsg(response.getSubMsg());
+        }
+        alipayZftCreateRecordMapper.insert(record);
+
+        if (response.isSuccess()) {
+            return R.data("调用成功", response.getBody());
+        } else {
+            return R.fail("调用失败:" + response.getBody());
+        }
+    }
+
+    private static AlipayConfig getAlipayConfig() {
+
+        String privateKey  = "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCBTOvScNrk0mw+WbR6Qtx2+HrPxB5IU/V2HSPGZUTDNiCiSU9UZcuXlo0UNwMksFYR43btk4BHW9OsZETdr3uJzbEEko4jQPrY49V6V8d3yFUiexcbLKf9W2X+h7EwdPDahHk6wmM73Lm500xzv8JxdGeo/d2uGTh+qD66HcPW+QYj1YbjRV1JyCOiJ2OiwLcb28arNNLOgj6PktQ69FPSWS4hWrM7nl6DLRr6SdOlyfBT47gwPtW0BQ96/2b9O+qxKBPfrcqdRDH323HvrGFDwa8GoWTdchnKwFjmfIFHMvf+eq3tzilFi8H7vQRtzVRrndwrNa0z+1ss07fcWFDRAgMBAAECggEAItgU0OAizPk7vE22SiBMgy8RAX5rXrhpdIwDwQo3Tpf+kV1KKIdKJy6mFCWDDlcKysVOnlVag2BmmZVnzYnls8wfgQjxjuSK9Pno5JBVK51r+9/J6UPOfYMs6Duu700EPw7mEISj81TXJBGiD6tEfgiNisfm/mzDgbZbORKeXQbaTyrtB+GZn6FNSyyHA1vraARMrgfMEGNzQ4AbtfcUxGO+mejdTFs0PxAq6lovHBY3fYYHI1Nx6kf9iPoom/G4UrcMO67W6QU+1tOCZCXjy4bD2y421z/8XD73+WDyYp+Tjy0hTLqVZc7TpYAOximo1vMIUe23EdJJngdlkdpDFQKBgQDFyETL0knwBSakLfAe2BmFb2x++B4YXUnt4dGbCFBnVooxf5i06GVt/CrfkJhYK6hBSowOScIRf8P6BOSQptRZb2/I1ngQm4vcpAZw6EjUTlgOj/J3XJ+ApUNQnRqE28jDrE4m2RHg4BkQo6yA3DizJAluPCtFoCYDm1a7dV7u7wKBgQCnXEH5sD8VSxURv02/gn80g/uZIP/EOU3ycjBEqZdRGkNINwXT+zqrlZIGYb+bxLvU/R2OqKC5vhcyAL3T1A8ORYqPu5KLnAxg7C+rHuVilUWwCEH7POpCk+ETPXCZwcNvLNa5PIqBH/gdV9Y9PBTef6J4rN6V6TDFgosf5by8PwKBgDpVG71Fk1sAGep4RgbC05wgRc6Y3T9wXDqVzJ098YDY7D83E9HfbPLoWbjAS75Nef1vwCkCpgNFPIbD5KmpGp4aGM0SPC0hwzlbAy9PwxMi3CPHXsrHfZ+SnmzrOQQQUoErk40vnm9FiP74VwtWaD6llUZ25ohNeIi9yvHU5x/vAoGAdU2d1JOq85LHtsO+i9+8pyNnAsJ1YqTDtI5CtK2lqKvewswGIrlxOvi//AchVN3ExZmP0QDyfp31BhAs/T8iOl+Vqf7PzVjX+Eszch5aqwlzadmv3ZepnnamCGVE+hAsmkz0R6tebPjqYC7Ds/HbssQFLc4EyVBD5fwE5ZuR+OMCgYAvGHUYpi0dY9uMHXzL721tIopiwUfKCgLAn3yhSH3p7644NxHBqLLaBLVT2q7JAZQUaZUvXlwiyxU1zvo0xmvcbnB/Vd2qp8KbEUkvHyIYVJkM6Fn+9xBosorcrHv+7B2V1XR9WQcXvppxbN/8farWGuAA0anBD+UGrxd8B0/hHg==";
+        String alipayPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnxevClLpYc6c8A9lwmnftPO2BeU+X6aZ/+b/n1Cvq096VJKiHmqcsRgRbrnSlmPDHRrQpDti4en2Ys0L2lx7CObIr/2xP/jJVwjIO1iWHUj/w/NAbjv7dLW/FFY4SeNp8rU+hlgGgviyUxzonfNfr3v+o8grFqQq27/hiZJAofsQRMQu1dEQqoKdJj7eQLkTstiK5miJMyQ+Y3tLztrEUMBz/zRgaCEfGqmFmRZ2diy2X+1dGaX6H4+0QJ2u50eY2QTBkNuvREGbAn6/lttAgvg/+CywPYKGeC4xOfnl5wP8iA1QXYbXrVJRkZjU097nyOmSNhLy9KvJH2BNpojS1QIDAQAB";
+        AlipayConfig alipayConfig = new AlipayConfig();
+        alipayConfig.setServerUrl("https://openapi.alipay.com/gateway.do");
+        alipayConfig.setAppId("2021005196608960");
+        alipayConfig.setPrivateKey(privateKey);
+        alipayConfig.setFormat("json");
+        alipayConfig.setAlipayPublicKey(alipayPublicKey);
+        alipayConfig.setCharset("UTF-8");
+        alipayConfig.setSignType("RSA2");
+        return alipayConfig;
+    }
+
+
+
+    @Override
+    public R<String> zftSimplecreate(AlipayZftMerchantSimplecreateDto request) {
+        if (request == null) {
+            return R.fail("请求体不能为空");
+        }
+        if (request.getStoreId() == null) {
+            return R.fail("门店ID不能为空");
+        }
+        R<StorePaymentConfig> cfgCheck = loadConfigByStoreId(request.getStoreId());
+        if (R.isNotSuccess(cfgCheck) || cfgCheck.getData() == null) {
+            return R.fail(cfgCheck.getMsg());
+        }
+        try {
+            AntMerchantExpandIndirectZftSimplecreateRequest api = new AntMerchantExpandIndirectZftSimplecreateRequest();
+            api.setBizModel(toSimplecreateModel(request));
+            if (StringUtils.isNotBlank(request.getNotifyUrl())) {
+                api.setNotifyUrl(request.getNotifyUrl());
+            }
+            applyAlipayCommonParams(api, request.getAppAuthToken(), request.getApiVersion());
+            String appId = resolveAppId(cfgCheck.getData(), request.getAppId());
+            if (StringUtils.isBlank(appId)) {
+                return R.fail("app_id 不能为空,请传 app_id 或在门店支付配置中维护应用 ID");
+            }
+            AlipayClient client = new DefaultAlipayClient(buildAlipayConfig(cfgCheck.getData(), appId));
+            AntMerchantExpandIndirectZftSimplecreateResponse response = client.certificateExecute(api);
+            if (response == null) {
+                return R.fail("支付宝返回为空");
+            }
+            if (!response.isSuccess()) {
+                String sub = response.getSubMsg();
+                String msg = StringUtils.isNotBlank(sub) ? sub : "调用失败";
+                log.warn("支付宝 simplecreate 失败 subCode={} subMsg={}", response.getSubCode(), response.getSubMsg());
+                return R.fail(msg);
+            }
+            return R.data(response.getBody());
+        } catch (ReflectiveOperationException e) {
+            log.error("支付宝 simplecreate 公共参数设置失败", e);
+            return R.fail("构建请求失败:" + e.getMessage());
+        } catch (AlipayApiException e) {
+            log.error("支付宝 simplecreate 异常", e);
+            return R.fail(StringUtils.isNotBlank(e.getErrMsg()) ? e.getErrMsg() : e.getMessage());
+        }
+    }
+
+    @Override
+    public R<String> orderQuery(String orderId) {
+
+        if (StringUtils.isBlank(orderId)) {
+            return R.fail("orderId 不能为空");
+        }
+//        R<StorePaymentConfig> cfgCheck = loadConfigByStoreId(storeId);
+//        if (R.isNotSuccess(cfgCheck) || cfgCheck.getData() == null) {
+//            return R.fail(cfgCheck.getMsg());
+//        }
+        try {
+            // 初始化SDK
+            AlipayClient alipayClient = new DefaultAlipayClient(getAlipayConfig());
+            AntMerchantExpandIndirectZftorderQueryRequest request = new AntMerchantExpandIndirectZftorderQueryRequest();
+            AntMerchantExpandIndirectZftorderQueryModel model = new AntMerchantExpandIndirectZftorderQueryModel();
+            model.setOrderId(orderId.trim());
+            request.setBizModel(model);
+//            String appId = resolveAppId(cfgCheck.getData(), null);
+//            if (StringUtils.isBlank(appId)) {
+//                return R.fail("app_id 不能为空,请在门店支付配置中维护应用 ID");
+//            }
+            AntMerchantExpandIndirectZftorderQueryResponse response = alipayClient.execute(request);
+//            AntMerchantExpandIndirectZftorderQueryResponse response = client.certificateExecute(request);
+            if (response == null) {
+                return R.fail("支付宝返回为空");
+            }
+            if (!response.isSuccess()) {
+                String sub = response.getSubMsg();
+                String msg = StringUtils.isNotBlank(sub) ? sub : "调用失败";
+                log.warn("支付宝 order.query 失败 subCode={} subMsg={}", response.getSubCode(), response.getSubMsg());
+                return R.fail(msg);
+            }
+            return R.data(response.getBody());
+        } catch (AlipayApiException e) {
+            log.error("支付宝 order.query 异常 orderId={}", orderId, e);
+            return R.fail(StringUtils.isNotBlank(e.getErrMsg()) ? e.getErrMsg() : e.getMessage());
+        }
+    }
+
+    @Override
+    public R<String> zftModify(AlipayZftBizRequestDto request1) throws AlipayApiException {
+
+                AlipayClient alipayClient = new DefaultAlipayClient(getAlipayConfig());
+// 直付通二级商户作废接口:ant.merchant.expand.indirect.zft.delete
+        AntMerchantExpandIndirectZftDeleteRequest request = new AntMerchantExpandIndirectZftDeleteRequest();
+        request.setBizContent("{"
+                + "\"smid\":\"2088480335620690\"," // 二级商户号
+                + "\"external_id\":\"你的平台商户ID\"" // 可选,平台内部商户标识
+                + "}");
+        AntMerchantExpandIndirectZftDeleteResponse response = alipayClient.execute(request);
+        if (response.isSuccess()) {
+            return R.data("调用成功", response.getBody());
+        } else {
+            return R.data("调用失败", response.getBody());
+            // sdk版本是"4.38.0.ALL"及以上,可以参考下面的示例获取诊断链接
+            // String diagnosisUrl = DiagnosisUtils.getDiagnosisUrl(response);
+            // System.out.println(diagnosisUrl);
+        }
+    }
+
+    @Override
+    public R<String> supplementCreate(AlipayZftBizRequestDto request) {
+        AntMerchantExpandIndirectSupplementCreateRequest api = new AntMerchantExpandIndirectSupplementCreateRequest();
+        return invoke(request, api);
+    }
+
+    @Override
+    public R<String> imageUpload(Integer storeId, String imageType, MultipartFile imageContent,
+                                 String appId, String appAuthToken, String apiVersion) throws AlipayApiException {
+        // 初始化SDK
+        AlipayClient alipayClient = new DefaultAlipayClient(getAlipayConfig());
+//        if (storeId == null) {
+//            return R.fail("门店ID不能为空");
+//        }
+        if (StringUtils.isBlank(imageType)) {
+            return R.fail("imageType 不能为空");
+        }
+        if (imageContent == null || imageContent.isEmpty()) {
+            return R.fail("imageContent 不能为空");
+        }
+        if (imageContent.getSize() > 10L * 1024 * 1024) {
+            return R.fail("图片大小不能超过10MB");
+        }
+//        R<StorePaymentConfig> cfgCheck = loadConfigByStoreId(storeId);
+//        if (R.isNotSuccess(cfgCheck) || cfgCheck.getData() == null) {
+//            return R.fail(cfgCheck.getMsg());
+//        }
+        try {
+            AntMerchantExpandIndirectImageUploadRequest request = new AntMerchantExpandIndirectImageUploadRequest();
+            request.setImageType(imageType.trim().toLowerCase());
+            String fileName = StringUtils.defaultIfBlank(imageContent.getOriginalFilename(), "image_upload.bin");
+            request.setImageContent(new FileItem(fileName, imageContent.getBytes()));
+            applyAlipayCommonParams(request, appAuthToken, apiVersion);
+//            String resolvedAppId = resolveAppId(cfgCheck.getData(), appId);
+//            if (StringUtils.isBlank(resolvedAppId)) {
+//                return R.fail("app_id 不能为空,请传 app_id 或在门店支付配置中维护应用 ID");
+//            }
+//            AlipayClient client = new DefaultAlipayClient(buildAlipayConfig(cfgCheck.getData(), resolvedAppId));
+            AntMerchantExpandIndirectImageUploadResponse response = alipayClient.execute(request);
+            if (response == null) {
+                return R.fail("支付宝返回为空");
+            }
+            if (!response.isSuccess()) {
+                String sub = response.getSubMsg();
+                return R.fail(StringUtils.isNotBlank(sub) ? sub : "图片上传失败");
+            }
+            return R.data(response.getBody());
+        } catch (ReflectiveOperationException e) {
+            log.error("支付宝图片上传公共参数设置失败 storeId={}", storeId, e);
+            return R.fail("构建请求失败:" + e.getMessage());
+        } catch (Exception e) {
+            log.error("支付宝图片上传异常 storeId={}", storeId, e);
+            if (e instanceof AlipayApiException) {
+                AlipayApiException ex = (AlipayApiException) e;
+                return R.fail(StringUtils.isNotBlank(ex.getErrMsg()) ? ex.getErrMsg() : ex.getMessage());
+            }
+            return R.fail("图片上传失败:" + e.getMessage());
+        }
+    }
+
+    private <T extends AlipayResponse> R<String> invoke(AlipayZftBizRequestDto dto, AlipayRequest<T> apiRequest) {
+        if (dto == null) {
+            return R.fail("请求体不能为空");
+        }
+        R<StorePaymentConfig> cfgCheck = loadConfig(dto);
+        if (R.isNotSuccess(cfgCheck) || cfgCheck.getData() == null) {
+            return R.fail(cfgCheck.getMsg());
+        }
+        Map<String, Object> biz = dto.getBizContent();
+        String bizJson = (biz == null || biz.isEmpty()) ? "{}" : JSON.toJSONString(biz);
+        // 各 *Request 类实现 AlipayRequest 接口,自带 bizContent/notifyUrl 字段与 setter,并非继承 AlipayObject
+        try {
+            applyBizContentAndNotify(dto, apiRequest, bizJson);
+            applyAlipayCommonParams(apiRequest, dto.getAppAuthToken(), dto.getApiVersion());
+        } catch (ReflectiveOperationException e) {
+            log.error("设置支付宝请求参数失败", e);
+            return R.fail("构建请求失败:" + e.getMessage());
+        }
+        try {
+            String appId = resolveAppId(cfgCheck.getData(), dto.getAppId());
+            if (StringUtils.isBlank(appId)) {
+                return R.fail("app_id 不能为空,请传 app_id 或在门店支付配置中维护应用 ID");
+            }
+            AlipayClient client = new DefaultAlipayClient(buildAlipayConfig(cfgCheck.getData(), appId));
+            T response = client.certificateExecute(apiRequest);
+            if (response == null) {
+                return R.fail("支付宝返回为空");
+            }
+            if (!response.isSuccess()) {
+                String sub = response.getSubMsg();
+                String msg = StringUtils.isNotBlank(sub) ? sub : "调用失败";
+                log.warn("支付宝进件接口失败 method={} subCode={} subMsg={}",
+                        apiRequest.getApiMethodName(), response.getSubCode(), response.getSubMsg());
+                return R.fail(msg);
+            }
+            return R.data(response.getBody());
+        } catch (AlipayApiException e) {
+            log.error("支付宝进件接口异常 method={}", apiRequest.getApiMethodName(), e);
+            return R.fail(StringUtils.isNotBlank(e.getErrMsg()) ? e.getErrMsg() : e.getMessage());
+        }
+    }
+
+    private R<StorePaymentConfig> loadConfig(AlipayZftBizRequestDto dto) {
+        if (dto == null || dto.getStoreId() == null) {
+            return R.fail("门店ID不能为空");
+        }
+        return loadConfigByStoreId(dto.getStoreId());
+    }
+
+    private R<StorePaymentConfig> loadConfigByStoreId(Integer storeId) {
+        StorePaymentConfig config = storePaymentConfigService.getByStoreId(storeId);
+        if (config == null) {
+            return R.fail("该门店未配置支付参数");
+        }
+        if (StringUtils.isBlank(config.getAppSecretCert())
+                || config.getAppPublicCert() == null || config.getAppPublicCert().length == 0
+                || config.getAlipayPublicCert() == null || config.getAlipayPublicCert().length == 0
+                || config.getAlipayRootCert() == null || config.getAlipayRootCert().length == 0) {
+            return R.fail("门店支付配置不完整(缺少应用私钥或证书)");
+        }
+        return R.data(config);
+    }
+
+    /**
+     * @param appId 已解析出的应用 ID(请求覆盖或门店配置)
+     */
+    private com.alipay.api.AlipayConfig buildAlipayConfig(StorePaymentConfig config, String appId) {
+        com.alipay.api.AlipayConfig alipayConfig = new com.alipay.api.AlipayConfig();
+        alipayConfig.setServerUrl(aliPayHost);
+        alipayConfig.setAppId(appId);
+        alipayConfig.setPrivateKey(config.getAppSecretCert());
+        alipayConfig.setFormat("json");
+        alipayConfig.setCharset("UTF-8");
+        alipayConfig.setSignType("RSA2");
+        if (config.getAppPublicCert() != null && config.getAppPublicCert().length > 0) {
+            alipayConfig.setAppCertContent(new String(config.getAppPublicCert(), StandardCharsets.UTF_8));
+        }
+        if (config.getAlipayPublicCert() != null && config.getAlipayPublicCert().length > 0) {
+            alipayConfig.setAlipayPublicCertContent(new String(config.getAlipayPublicCert(), StandardCharsets.UTF_8));
+        }
+        if (config.getAlipayRootCert() != null && config.getAlipayRootCert().length > 0) {
+            alipayConfig.setRootCertContent(new String(config.getAlipayRootCert(), StandardCharsets.UTF_8));
+        }
+        return alipayConfig;
+    }
+
+    /**
+     * 请求体中的 app_id 优先,否则使用门店支付配置中的应用 ID。
+     */
+    private static String resolveAppId(StorePaymentConfig config, String overrideFromRequest) {
+        if (StringUtils.isNotBlank(overrideFromRequest)) {
+            return overrideFromRequest.trim();
+        }
+        if (config != null && StringUtils.isNotBlank(config.getAppId())) {
+            return config.getAppId().trim();
+        }
+        return null;
+    }
+
+    /**
+     * 支付宝 OpenAPI 的 Request 类均提供 {@code setBizContent(String)}、{@code setNotifyUrl(String)},
+     * 但声明在各自类上,接口 {@link AlipayRequest} 未包含这些方法,故用反射统一设置。
+     */
+    private void applyBizContentAndNotify(AlipayZftBizRequestDto dto, Object apiRequest, String bizJson)
+            throws ReflectiveOperationException {
+        Class<?> clazz = apiRequest.getClass();
+        Method setBiz = clazz.getMethod("setBizContent", String.class);
+        setBiz.invoke(apiRequest, bizJson);
+        if (StringUtils.isNotBlank(dto.getNotifyUrl())) {
+            Method setNotify = clazz.getMethod("setNotifyUrl", String.class);
+            setNotify.invoke(apiRequest, dto.getNotifyUrl());
+        }
+    }
+
+    /**
+     * 支付宝开放平台公共参数中需业务侧显式传入的部分:<br/>
+     * - {@code app_auth_token}:第三方代调用时通过 {@code putOtherTextParam} 追加;<br/>
+     * - {@code api_version}:若请求类支持 {@code setApiVersion} 则设置。<br/>
+     * 其余如 {@code app_id}、{@code method}、{@code format}、{@code charset}、{@code sign_type}、签名、{@code timestamp}、{@code version}
+     * 由 {@link DefaultAlipayClient} 与 {@link com.alipay.api.AlipayConfig} 自动处理。
+     */
+    private void applyAlipayCommonParams(Object apiRequest, String appAuthToken, String apiVersion)
+            throws ReflectiveOperationException {
+        if (apiRequest == null) {
+            return;
+        }
+        Class<?> clazz = apiRequest.getClass();
+        if (StringUtils.isNotBlank(appAuthToken)) {
+            Method put = clazz.getMethod("putOtherTextParam", String.class, String.class);
+            put.invoke(apiRequest, "app_auth_token", appAuthToken.trim());
+        }
+        if (StringUtils.isNotBlank(apiVersion)) {
+            try {
+                Method setVer = clazz.getMethod("setApiVersion", String.class);
+                setVer.invoke(apiRequest, apiVersion.trim());
+            } catch (NoSuchMethodException ignored) {
+                log.debug("当前请求类无 setApiVersion:{}", clazz.getName());
+            }
+        }
+    }
+
+    /**
+     * @return 错误文案;null 表示校验通过
+     */
+    private String validateZftCreateRequest(AlipayZftMerchantCreateDto dto) {
+        if (dto == null) {
+            return "请求体不能为空";
+        }
+        if (dto.getStoreId() == null) {
+            return "门店ID不能为空";
+        }
+        Map<String, Object> biz = buildZftCreateBizContent(dto);
+        return validateZftCreateBizMap(biz);
+    }
+
+    /**
+     * 校验合并后的 biz_content(字段可全部来自 biz_content,或来自顶层 + extraBiz)。
+     */
+    private static String validateZftCreateBizMap(Map<String, Object> biz) {
+        if (biz == null || biz.isEmpty()) {
+            return "biz_content 不能为空:请提供 biz_content,或提供 external_id、merchant_type、name、alias_name、mcc、service 等顶层字段";
+        }
+        String externalId = bizString(biz, "external_id");
+        if (StringUtils.isBlank(externalId)) {
+            return "external_id 不能为空";
+        }
+        if (externalId.length() > 64) {
+            return "external_id 长度不能超过64";
+        }
+        String merchantType = bizString(biz, "merchant_type");
+        if (StringUtils.isBlank(merchantType)) {
+            return "merchant_type 不能为空";
+        }
+        if (merchantType.length() > 20) {
+            return "merchant_type 长度不能超过20";
+        }
+        String name = bizString(biz, "name");
+        if (StringUtils.isBlank(name)) {
+            return "name 不能为空";
+        }
+        if (name.length() > 128) {
+            return "name 长度不能超过128";
+        }
+        String aliasName = bizString(biz, "alias_name");
+        if (StringUtils.isBlank(aliasName)) {
+            return "alias_name 不能为空";
+        }
+        if (aliasName.length() > 128) {
+            return "alias_name 长度不能超过128";
+        }
+        String mcc = bizString(biz, "mcc");
+        if (StringUtils.isBlank(mcc)) {
+            return "mcc 不能为空";
+        }
+        if (mcc.length() > 10) {
+            return "mcc 长度不能超过10";
+        }
+        if (!bizHasNonEmptyService(biz)) {
+            return "service 不能为空";
+        }
+        return null;
+    }
+
+    private static String bizString(Map<String, Object> biz, String key) {
+        Object v = biz == null ? null : biz.get(key);
+        if (v == null) {
+            return null;
+        }
+        if (v instanceof String) {
+            return ((String) v).trim();
+        }
+        return String.valueOf(v).trim();
+    }
+
+    private static boolean bizHasNonEmptyService(Map<String, Object> biz) {
+        Object v = biz.get("service");
+        if (v == null) {
+            return false;
+        }
+        if (v instanceof List) {
+            return !((List<?>) v).isEmpty();
+        }
+        if (v instanceof String) {
+            return StringUtils.isNotBlank((String) v);
+        }
+        if (v instanceof String[]) {
+            return ((String[]) v).length > 0;
+        }
+        return true;
+    }
+
+    /**
+     * 合并顺序:biz_content → extraBiz → 非空顶层字段(后者覆盖同名键)。
+     */
+    private Map<String, Object> buildZftCreateBizContent(AlipayZftMerchantCreateDto dto) {
+        Map<String, Object> biz = new LinkedHashMap<>();
+        if (dto.getBizContent() != null && !dto.getBizContent().isEmpty()) {
+            biz.putAll(dto.getBizContent());
+        }
+        if (dto.getExtraBiz() != null && !dto.getExtraBiz().isEmpty()) {
+            biz.putAll(dto.getExtraBiz());
+        }
+        if (StringUtils.isNotBlank(dto.getExternalId())) {
+            biz.put("external_id", dto.getExternalId().trim());
+        }
+        if (StringUtils.isNotBlank(dto.getMerchantType())) {
+            biz.put("merchant_type", dto.getMerchantType().trim());
+        }
+        if (StringUtils.isNotBlank(dto.getName())) {
+            biz.put("name", dto.getName().trim());
+        }
+        if (StringUtils.isNotBlank(dto.getAliasName())) {
+            biz.put("alias_name", dto.getAliasName().trim());
+        }
+        if (StringUtils.isNotBlank(dto.getMcc())) {
+            biz.put("mcc", dto.getMcc().trim());
+        }
+        if (dto.getService() != null && !dto.getService().isEmpty()) {
+            biz.put("service", dto.getService());
+        }
+        return biz;
+    }
+
+    private AntMerchantExpandIndirectZftSimplecreateModel toSimplecreateModel(AlipayZftMerchantSimplecreateDto d) {
+        AntMerchantExpandIndirectZftSimplecreateModel m = new AntMerchantExpandIndirectZftSimplecreateModel();
+        m.setExternalId(d.getExternalId());
+        m.setBindingAlipayLogonId(d.getBindingAlipayLogonId());
+        m.setAliasName(d.getAliasName());
+        m.setServicePhone(d.getServicePhone());
+        m.setName(d.getName());
+        m.setMcc(d.getMcc());
+        m.setService(d.getService());
+        m.setAlipayLogonId(d.getAlipayLogonId());
+        m.setOutDoorImages(d.getOutDoorImages());
+        m.setInDoorImages(d.getInDoorImages());
+        m.setLicenseAuthLetterImage(d.getLicenseAuthLetterImage());
+        if (d.getContactInfos() != null) {
+            m.setContactInfos(toContactInfo(d.getContactInfos()));
+        }
+        if (d.getDefaultSettleRule() != null) {
+            m.setDefaultSettleRule(toDefaultSettleRule(d.getDefaultSettleRule()));
+        }
+        if (d.getBizCards() != null) {
+            m.setBizCards(toSettleCardInfo(d.getBizCards()));
+        }
+        if (d.getBusinessAddress() != null) {
+            m.setBusinessAddress(toAddressInfo(d.getBusinessAddress()));
+        }
+        return m;
+    }
+
+    private static ContactInfo toContactInfo(AlipayZftMerchantSimplecreateDto.ContactPart p) {
+        ContactInfo c = new ContactInfo();
+        c.setName(p.getName());
+        c.setMobile(p.getMobile());
+        c.setIdCardNo(p.getIdCardNo());
+        c.setPhone(p.getPhone());
+        c.setEmail(p.getEmail());
+        return c;
+    }
+
+    private static DefaultSettleRule toDefaultSettleRule(AlipayZftMerchantSimplecreateDto.DefaultSettleRulePart p) {
+        DefaultSettleRule r = new DefaultSettleRule();
+        r.setDefaultSettleType(p.getDefaultSettleType());
+        r.setDefaultSettleTarget(p.getDefaultSettleTarget());
+        return r;
+    }
+
+    private static SettleCardInfo toSettleCardInfo(AlipayZftMerchantSimplecreateDto.SettleCardPart p) {
+        SettleCardInfo s = new SettleCardInfo();
+        s.setAccountInstName(p.getAccountInstName());
+        s.setBankCode(p.getBankCode());
+        s.setAccountType(p.getAccountType());
+        s.setUsageType(p.getUsageType());
+        s.setAccountHolderName(p.getAccountHolderName());
+        s.setAccountInstCity(p.getAccountInstCity());
+        s.setAccountInstId(p.getAccountInstId());
+        s.setAccountNo(p.getAccountNo());
+        s.setAccountBranchName(p.getAccountBranchName());
+        s.setAccountInstProvince(p.getAccountInstProvince());
+        return s;
+    }
+
+    private static AddressInfo toAddressInfo(AlipayZftMerchantSimplecreateDto.AddressPart p) {
+        AddressInfo a = new AddressInfo();
+        a.setAddress(p.getAddress());
+        a.setDistrictCode(p.getDistrictCode());
+        a.setLatitude(p.getLatitude());
+        a.setCityCode(p.getCityCode());
+        a.setPoiid(p.getPoiid());
+        a.setProvinceCode(p.getProvinceCode());
+        a.setLongitude(p.getLongitude());
+        a.setType(p.getType());
+        return a;
+    }
+}

+ 11 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StorePaymentConfigServiceImpl.java

@@ -45,6 +45,17 @@ public class StorePaymentConfigServiceImpl extends ServiceImpl<StorePaymentConfi
     }
 
     @Override
+    public StorePaymentConfig getByAppId(String appId) {
+        if (!StringUtils.hasText(appId)) {
+            return null;
+        }
+        LambdaQueryWrapper<StorePaymentConfig> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(StorePaymentConfig::getAppId, appId);
+        wrapper.last("LIMIT 1");
+        return this.getOne(wrapper);
+    }
+
+    @Override
     public IPage<StorePaymentConfig> pageList(Page<StorePaymentConfig> page, Integer storeId, String appId) {
         LambdaQueryWrapper<StorePaymentConfig> wrapper = new LambdaQueryWrapper<>();
         if (storeId != null) {

+ 12 - 0
alien-store/src/main/java/shop/alien/store/strategy/payment/PaymentStrategy.java

@@ -23,6 +23,9 @@ public interface PaymentStrategy {
      */
     R createPrePayOrder(String price, String subject) throws Exception;
 
+
+    R createPreAliPayOrder(String orderId, String amount, String subject, String smid) throws Exception;
+
     /**
      * 创建预支付订单(可选门店维度:微信服务商等策略需传 storeId 以解析子商户号)
      *
@@ -103,6 +106,15 @@ public interface PaymentStrategy {
      */
     R searchRefundRecordByOutRefundNo(String outRefundNo ) throws Exception;
 
+    /**
+     * 创建预支付订单(可选门店维度:支付宝服务商等策略需传 storeId 以解析子商户号)
+     *
+     * @param
+     */
+    default R createPreAliPayOrder(String orderId, String amount, String subject, String smid, Integer storeId) throws Exception {
+        return createPreAliPayOrder(orderId, amount, subject, smid);
+    }
+
 
     /**
      * 获取策略类型字符串

+ 197 - 0
alien-store/src/main/java/shop/alien/store/strategy/payment/impl/AlipayPartnerPaymentStrategyImpl.java

@@ -0,0 +1,197 @@
+package shop.alien.store.strategy.payment.impl;
+
+import com.alipay.api.AlipayApiException;
+import com.alipay.api.AlipayClient;
+import com.alipay.api.DefaultAlipayClient;
+import com.alipay.api.AlipayConfig;
+import com.alipay.api.domain.*;
+import com.alipay.api.request.AlipayTradeAppPayRequest;
+import com.alipay.api.response.AlipayTradeAppPayResponse;
+
+import com.alipay.api.FileItem;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.map.HashedMap;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.AlipayZftCreateRecord;
+import shop.alien.mapper.AlipayZftCreateRecordMapper;
+import shop.alien.store.AlienStoreApplication;
+import shop.alien.store.strategy.payment.PaymentStrategy;
+import shop.alien.util.common.constant.PaymentEnum;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.Base64;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class AlipayPartnerPaymentStrategyImpl implements PaymentStrategy {
+
+    private final AlipayZftCreateRecordMapper alipayZftCreateRecordMapper;
+
+
+
+    /**
+     * 生成支付宝直付通二级商户支付预订单
+     */
+    public R createAlipayZftPreOrder(String orderNo, String amount, String subject, String smid) throws AlipayApiException {
+        // 初始化SDK
+        AlipayClient alipayClient = new DefaultAlipayClient(getAlipayConfig());
+
+
+
+        // 构造请求参数以调用接口
+        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
+        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
+
+        // 设置商户订单号
+        model.setOutTradeNo(orderNo);
+
+        // 设置订单总金额
+        // 除以100
+        BigDecimal total = new BigDecimal(amount).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
+        model.setTotalAmount(total.toPlainString());
+
+        // 设置订单标题
+        model.setSubject(subject);
+        // 1. 构建二级商户信息
+        SubMerchant subMerchant = new SubMerchant();
+        subMerchant.setMerchantId(smid); // 替换为实际的二级商户编号
+        // 【关键】设置二级商户信息(直付通必传)
+        model.setSubMerchant(subMerchant);
+
+        // 设置产品码
+//        model.setProductCode("QUICK_MSECURITY_PAY");
+
+        // 设置订单包含的商品列表信息
+//        List<GoodsDetail> goodsDetail = new ArrayList<GoodsDetail>();
+//        GoodsDetail goodsDetail0 = new GoodsDetail();
+//        goodsDetail0.setGoodsName("ipad");
+//        goodsDetail0.setAlipayGoodsId("20010001");
+//        goodsDetail0.setQuantity(1L);
+//        goodsDetail0.setPrice("2000");
+//        goodsDetail0.setGoodsId("apple-01");
+//        goodsDetail0.setGoodsCategory("34543238");
+//        goodsDetail0.setCategoriesTree("124868003|126232002|126252004");
+//        goodsDetail0.setShowUrl("http://www.alipay.com/xxx.jpg");
+//        goodsDetail.add(goodsDetail0);
+//        model.setGoodsDetail(goodsDetail);
+
+        // 设置订单绝对超时时间
+//        model.setTimeExpire("2016-12-31 10:05:00");
+
+        // 设置业务扩展参数
+//        ExtendParams extendParams = new ExtendParams();
+//        extendParams.setSysServiceProviderId("2088511833207846");
+//        extendParams.setHbFqSellerPercent("100");
+//        extendParams.setHbFqNum("3");
+//        extendParams.setIndustryRefluxInfo("{\"scene_code\":\"metro_tradeorder\",\"channel\":\"xxxx\",\"scene_data\":{\"asset_name\":\"ALIPAY\"}}");
+//        extendParams.setRoyaltyFreeze("true");
+//        extendParams.setCardType("S0JP0000");
+//        model.setExtendParams(extendParams);
+
+        // 设置公用回传参数
+//        model.setPassbackParams("merchantBizType%3d3C%26merchantBizNo%3d2016010101111");
+
+        // 设置商户的原始订单号
+//        model.setMerchantOrderNo("20161008001");
+
+        // 设置外部指定买家
+//        ExtUserInfo extUserInfo = new ExtUserInfo();
+//        extUserInfo.setCertType("IDENTITY_CARD");
+//        extUserInfo.setCertNo("362334768769238881");
+//        extUserInfo.setMobile("16587658765");
+//        extUserInfo.setName("李明");
+//        extUserInfo.setMinAge("18");
+//        extUserInfo.setNeedCheckInfo("F");
+//        extUserInfo.setIdentityHash("27bfcd1dee4f22c8fe8a2374af9b660419d1361b1c207e9b41a754a113f38fcc");
+//        model.setExtUserInfo(extUserInfo);
+
+        // 设置通知参数选项
+//        List<String> queryOptions = new ArrayList<String>();
+//        queryOptions.add("hyb_amount");
+//        queryOptions.add("enterprise_pay_info");
+//        model.setQueryOptions(queryOptions);
+
+        request.setNotifyUrl("https://frp-off.com:40279/alienStore/alipayPartnerNotify");
+        request.setBizModel(model);
+
+        // 第三方代调用模式下请设置app_auth_token
+        // request.putOtherTextParam("app_auth_token", "<-- 请填写应用授权令牌 -->");
+
+        AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
+        String orderStr = response.getBody();
+        System.out.println(orderStr);
+
+        if (response.isSuccess()) {
+            Map<String, Object> analyzeRequest = new HashedMap<>();
+            analyzeRequest.put("orderStr", orderStr);
+            return R.data(analyzeRequest);
+        } else {
+            return R.fail("调用失败");
+            // sdk版本是"4.38.0.ALL"及以上,可以参考下面的示例获取诊断链接
+            // String diagnosisUrl = DiagnosisUtils.getDiagnosisUrl(response);
+            // System.out.println(diagnosisUrl);
+        }
+    }
+
+    private static AlipayConfig getAlipayConfig() {
+        String privateKey  = "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCBTOvScNrk0mw+WbR6Qtx2+HrPxB5IU/V2HSPGZUTDNiCiSU9UZcuXlo0UNwMksFYR43btk4BHW9OsZETdr3uJzbEEko4jQPrY49V6V8d3yFUiexcbLKf9W2X+h7EwdPDahHk6wmM73Lm500xzv8JxdGeo/d2uGTh+qD66HcPW+QYj1YbjRV1JyCOiJ2OiwLcb28arNNLOgj6PktQ69FPSWS4hWrM7nl6DLRr6SdOlyfBT47gwPtW0BQ96/2b9O+qxKBPfrcqdRDH323HvrGFDwa8GoWTdchnKwFjmfIFHMvf+eq3tzilFi8H7vQRtzVRrndwrNa0z+1ss07fcWFDRAgMBAAECggEAItgU0OAizPk7vE22SiBMgy8RAX5rXrhpdIwDwQo3Tpf+kV1KKIdKJy6mFCWDDlcKysVOnlVag2BmmZVnzYnls8wfgQjxjuSK9Pno5JBVK51r+9/J6UPOfYMs6Duu700EPw7mEISj81TXJBGiD6tEfgiNisfm/mzDgbZbORKeXQbaTyrtB+GZn6FNSyyHA1vraARMrgfMEGNzQ4AbtfcUxGO+mejdTFs0PxAq6lovHBY3fYYHI1Nx6kf9iPoom/G4UrcMO67W6QU+1tOCZCXjy4bD2y421z/8XD73+WDyYp+Tjy0hTLqVZc7TpYAOximo1vMIUe23EdJJngdlkdpDFQKBgQDFyETL0knwBSakLfAe2BmFb2x++B4YXUnt4dGbCFBnVooxf5i06GVt/CrfkJhYK6hBSowOScIRf8P6BOSQptRZb2/I1ngQm4vcpAZw6EjUTlgOj/J3XJ+ApUNQnRqE28jDrE4m2RHg4BkQo6yA3DizJAluPCtFoCYDm1a7dV7u7wKBgQCnXEH5sD8VSxURv02/gn80g/uZIP/EOU3ycjBEqZdRGkNINwXT+zqrlZIGYb+bxLvU/R2OqKC5vhcyAL3T1A8ORYqPu5KLnAxg7C+rHuVilUWwCEH7POpCk+ETPXCZwcNvLNa5PIqBH/gdV9Y9PBTef6J4rN6V6TDFgosf5by8PwKBgDpVG71Fk1sAGep4RgbC05wgRc6Y3T9wXDqVzJ098YDY7D83E9HfbPLoWbjAS75Nef1vwCkCpgNFPIbD5KmpGp4aGM0SPC0hwzlbAy9PwxMi3CPHXsrHfZ+SnmzrOQQQUoErk40vnm9FiP74VwtWaD6llUZ25ohNeIi9yvHU5x/vAoGAdU2d1JOq85LHtsO+i9+8pyNnAsJ1YqTDtI5CtK2lqKvewswGIrlxOvi//AchVN3ExZmP0QDyfp31BhAs/T8iOl+Vqf7PzVjX+Eszch5aqwlzadmv3ZepnnamCGVE+hAsmkz0R6tebPjqYC7Ds/HbssQFLc4EyVBD5fwE5ZuR+OMCgYAvGHUYpi0dY9uMHXzL721tIopiwUfKCgLAn3yhSH3p7644NxHBqLLaBLVT2q7JAZQUaZUvXlwiyxU1zvo0xmvcbnB/Vd2qp8KbEUkvHyIYVJkM6Fn+9xBosorcrHv+7B2V1XR9WQcXvppxbN/8farWGuAA0anBD+UGrxd8B0/hHg==";
+        String alipayPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnxevClLpYc6c8A9lwmnftPO2BeU+X6aZ/+b/n1Cvq096VJKiHmqcsRgRbrnSlmPDHRrQpDti4en2Ys0L2lx7CObIr/2xP/jJVwjIO1iWHUj/w/NAbjv7dLW/FFY4SeNp8rU+hlgGgviyUxzonfNfr3v+o8grFqQq27/hiZJAofsQRMQu1dEQqoKdJj7eQLkTstiK5miJMyQ+Y3tLztrEUMBz/zRgaCEfGqmFmRZ2diy2X+1dGaX6H4+0QJ2u50eY2QTBkNuvREGbAn6/lttAgvg/+CywPYKGeC4xOfnl5wP8iA1QXYbXrVJRkZjU097nyOmSNhLy9KvJH2BNpojS1QIDAQAB";
+        AlipayConfig alipayConfig = new AlipayConfig();
+        alipayConfig.setServerUrl("https://openapi.alipay.com/gateway.do");
+        alipayConfig.setAppId("2021005196608960");
+        alipayConfig.setPrivateKey(privateKey);
+        alipayConfig.setFormat("json");
+        alipayConfig.setAlipayPublicKey(alipayPublicKey);
+        alipayConfig.setCharset("UTF-8");
+        alipayConfig.setSignType("RSA2");
+        return alipayConfig;
+    }
+
+    @Override
+    public R createPreAliPayOrder(String orderNo, String price, String subject, String smid) throws Exception {
+        return createAlipayZftPreOrder(orderNo, price, subject, smid);
+    }
+
+    @Override
+    public R createPrePayOrder(String price, String subject) throws Exception {
+        return null;
+    }
+
+    @Override
+    public R handleNotify(String notifyData) throws Exception {
+        log.info("处理支付宝支付通知,通知数据:{}", notifyData);
+        // 通过订单ID查询记录中对应数据,取得storeId
+        // 通过storeId查询storeInfo对应数据,
+        //更新smid
+        return null;
+    }
+
+    @Override
+    public R searchOrderByOutTradeNoPath(String transactionId) throws Exception {
+        return null;
+    }
+
+    @Override
+    public String handleRefund(Map<String, String> params) throws Exception {
+        return "";
+    }
+
+    @Override
+    public R searchRefundRecordByOutRefundNo(String outRefundNo) throws Exception {
+        return null;
+    }
+
+    @Override
+    public String getType() {
+        return PaymentEnum.ALIPAY_PARTNER.getType();
+    }
+}

+ 5 - 0
alien-store/src/main/java/shop/alien/store/strategy/payment/impl/AlipayPaymentStrategyImpl.java

@@ -183,6 +183,11 @@ public class AlipayPaymentStrategyImpl implements PaymentStrategy {
     }
 
     @Override
+    public R createPreAliPayOrder(String orderId, String amount, String subject, String smid) throws Exception {
+        return null;
+    }
+
+    @Override
     public R<Object> handleNotify(String notifyData) throws Exception {
         log.info("处理支付宝支付通知,通知数据:{}", notifyData);
         // TODO: 实现支付通知处理逻辑

+ 5 - 0
alien-store/src/main/java/shop/alien/store/strategy/payment/impl/WeChatPartnerPaymentStrategyImpl.java

@@ -175,6 +175,11 @@ public class WeChatPartnerPaymentStrategyImpl implements PaymentStrategy {
     }
 
     @Override
+    public R createPreAliPayOrder(String orderId, String amount, String subject, String smid) throws Exception {
+        return null;
+    }
+
+    @Override
     public R createPrePayOrder(String price, String subject, Integer storeId) throws Exception {
         return createPrePayOrder(price, subject, null, null, storeId, null, null, null, null, null);
     }

+ 5 - 0
alien-store/src/main/java/shop/alien/store/strategy/payment/impl/WeChatPaymentMininProgramStrategyImpl.java

@@ -210,6 +210,11 @@ public class WeChatPaymentMininProgramStrategyImpl implements PaymentStrategy {
     }
 
     @Override
+    public R createPreAliPayOrder(String orderId, String amount, String subject, String smid) throws Exception {
+        return null;
+    }
+
+    @Override
     public R handleNotify(String notifyData) throws Exception {
         return null;
     }

+ 6 - 0
alien-store/src/main/java/shop/alien/store/strategy/payment/impl/WeChatPaymentStrategyImpl.java

@@ -228,6 +228,12 @@ public class WeChatPaymentStrategyImpl implements PaymentStrategy {
     }
 
     @Override
+    public R createPreAliPayOrder(String orderId, String amount, String subject, String smid) throws Exception {
+        return null;
+    }
+
+
+    @Override
     public R handleNotify(String notifyData) throws Exception {
         /**
          * 目前没用,先写着

+ 2 - 0
alien-util/src/main/java/shop/alien/util/common/constant/PaymentEnum.java

@@ -9,6 +9,8 @@ package shop.alien.util.common.constant;
 public enum PaymentEnum {
     /** 支付宝 */
     ALIPAY("alipay"),
+    /** 支付宝服务商支付 */
+    ALIPAY_PARTNER("aliPayPartner"),
     /** 微信支付 */
     WECHAT_PAY("wechatPay"),
     /** 微信支付小程序 废弃 */