Răsfoiți Sursa

fix(payment): 修复支付宝直付通订单ID设置问题

- 在响应对象不为空时正确设置订单ID字段
- 添加空值检查避免潜在的空指针异常
- 确保订单记录中的订单ID与实际响应保持一致

# Conflicts:
#	alien-store/src/main/java/shop/alien/store/service/impl/AlipayZftOnboardingServiceImpl.java
fcw 1 săptămână în urmă
părinte
comite
97813ad061

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

@@ -0,0 +1,801 @@
+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());
+            record.setOrderId(request1 != null ? response.getOrderId() : null);
+        }
+        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;
+    }
+}