yeg
2026-09-15 997a846c966110e45cc8c4785f985236d732e7a1
chore(merchant): 增加汇付商户详情独立调试示例
2个文件已添加
109 ■■■■■ 已修改文件
talent-card-server/docs/huifu-merchant-detail-demo.md 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/test/java/com/talentcard/server/business/payment/huifu/demo/HuifuMerchantDetailQueryDemo.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
talent-card-server/docs/huifu-merchant-detail-demo.md
New file
@@ -0,0 +1,22 @@
# 汇付商户详细信息查询 Demo
在 IDE 中打开 `src/test/java/com/talentcard/server/business/payment/huifu/demo/HuifuMerchantDetailQueryDemo.java`,点击 `main` 旁的 Run 或 Debug 即可。使用后端模块的测试 classpath、JDK 17,不需要启动后端或数据库,也不会被 JUnit 自动执行。
类顶部 `DEMO_` 常量用于调试:
| 常量 | 用途 / 默认行为 |
| --- | --- |
| `DEMO_PRODUCT_ID` | 调用产品号;留空读取现有配置 |
| `DEMO_SYS_ID` | 调用身份;留空读取现有配置 |
| `DEMO_RSA_PRIVATE_KEY` | 对应调用身份的私钥;留空读取现有配置 |
| `DEMO_RSA_PUBLIC_KEY` | 汇付验签公钥;留空读取现有配置 |
| `DEMO_HUIFU_ID` | 查询目标,默认 `6666000241335656` |
| `DEMO_REQ_SEQ_ID` | 留空每次生成新的 32 位 UUID |
| `DEMO_REQ_DATE` | 留空使用北京时间当天,格式 yyyyMMdd |
| `DEMO_ENVIRONMENT` | prod / test;留空读取现有配置 |
这里的“现有配置”特指 classpath 中 `application.yml` 的 `app.payment.huifu`,不启动 Spring,也不合并 profile 文件或解析环境变量占位符。有占位符时,在对应常量中填写调试值。修改常量不会写回 application.yml。示例中的 YYZY、系统号和示例密钥没有作为本项目的默认身份。
IDE 的 Program arguments 填 `--dry-run` 时只打印环境、调用身份和业务请求参数,不发送请求。去掉该参数后调用一次 `v2/merchant/basicdata/query`,打印 SDK 返回的完整响应。响应可能包含商户资料,分享前自行脱敏;请勿提交填写后的私钥。
与提供的官方示例相比,仅修正当前 SDK 的 `setProductId` 方法名、去除无用的 `Identify` 导入,并增加独立配置、自动流水号/日期及预览选项;实际调用保留 `BasePayRequest.requestBasePay(..., null, false)`。可以在该调用行打断点。
talent-card-server/src/test/java/com/talentcard/server/business/payment/huifu/demo/HuifuMerchantDetailQueryDemo.java
New file
@@ -0,0 +1,87 @@
package com.talentcard.server.business.payment.huifu.demo;
import com.alibaba.fastjson.JSON;
import com.huifu.bspay.sdk.opps.core.BasePay;
import com.huifu.bspay.sdk.opps.core.config.MerConfig;
import com.huifu.bspay.sdk.opps.core.net.BasePayRequest;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.io.ClassPathResource;
/** 商户详细信息查询:直接运行 main,不启动 Spring 或连接数据库。 */
public class HuifuMerchantDetailQueryDemo {
    // 以下四项留空时读取 src/main/resources/application.yml 的 app.payment.huifu 配置。
    // 可以在此填写独立的调试配置;不会修改正式配置文件。
    public static final String DEMO_PRODUCT_ID = "";
    public static final String DEMO_SYS_ID = "";
    public static final String DEMO_RSA_PRIVATE_KEY = "";
    public static final String DEMO_RSA_PUBLIC_KEY = "";
    // 被查询商户,与上面的调用身份 DEMO_SYS_ID 是两个不同参数。
    public static final String DEMO_HUIFU_ID = "6666000241335656";
    // 留空则每次生成新的 32 位流水号,日期使用北京时间当天。
    public static final String DEMO_REQ_SEQ_ID = "";
    public static final String DEMO_REQ_DATE = "";
    // prod 为生产环境,test 为测试环境;留空读取 application.yml。
    public static final String DEMO_ENVIRONMENT = "";
    public static void main(String[] args) throws Exception {
        YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
        yaml.setResources(new ClassPathResource("application.yml"));
        Properties properties = yaml.getObject();
        if (properties == null) {
            throw new IllegalStateException("application.yml is empty");
        }
        // 1. 初始化 SDK,使用当前项目 SDK 3.0.40 的 setProductId 方法。
        MerConfig merConfig = new MerConfig();
        merConfig.setProductId(config(DEMO_PRODUCT_ID, "product-id", properties));
        merConfig.setSysId(config(DEMO_SYS_ID, "system-id", properties));
        merConfig.setRsaPrivateKey(config(DEMO_RSA_PRIVATE_KEY, "private-key", properties));
        merConfig.setRsaPublicKey(config(DEMO_RSA_PUBLIC_KEY, "platform-public-key", properties));
        String environment = config(DEMO_ENVIRONMENT, "environment", properties);
        if (!"prod".equalsIgnoreCase(environment) && !"test".equalsIgnoreCase(environment)) {
            throw new IllegalArgumentException("Environment must be prod or test");
        }
        BasePay.debug = false;
        BasePay.prodMode = "prod".equalsIgnoreCase(environment) ? BasePay.MODE_PROD : BasePay.MODE_TEST;
        BasePay.initWithMerConfig(merConfig);
        // 2. 组装请求参数。
        Map<String, Object> paramsInfo = new LinkedHashMap<>();
        paramsInfo.put("req_seq_id", DEMO_REQ_SEQ_ID.isBlank()
                ? UUID.randomUUID().toString().replace("-", "") : DEMO_REQ_SEQ_ID);
        paramsInfo.put("req_date", DEMO_REQ_DATE.isBlank()
                ? LocalDate.now(ZoneId.of("Asia/Shanghai")).format(DateTimeFormatter.BASIC_ISO_DATE)
                : DEMO_REQ_DATE);
        paramsInfo.put("huifu_id", DEMO_HUIFU_ID);
        System.out.println("environment=" + environment);
        System.out.println("sys_id=" + merConfig.getSysId() + ", product_id=" + merConfig.getProductId());
        System.out.println("request=" + JSON.toJSONString(paramsInfo, true));
        if (args.length == 1 && "--dry-run".equals(args[0])) {
            System.out.println("Dry run: no API request sent");
            return;
        }
        // 3. 单次只读查询;在下一行打断点可以逐项检查参数。
        Map<String, Object> response = BasePayRequest.requestBasePay(
                "v2/merchant/basicdata/query", paramsInfo, null, false);
        System.out.println("response=" + JSON.toJSONString(response, true));
    }
    private static String config(String override, String key, Properties properties) {
        String value = override.isBlank() ? properties.getProperty("app.payment.huifu." + key) : override;
        if (value == null || value.isBlank() || value.contains("${")) {
            throw new IllegalArgumentException("Set demo configuration for " + key);
        }
        return value;
    }
}