1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import java.nio.file.*;
import java.time.*;
import java.time.format.*;
import java.util.*;
import com.alibaba.fastjson.JSON;
import com.huifu.bspay.sdk.opps.client.BasePayClient;
import com.huifu.bspay.sdk.opps.core.BasePay;
import com.huifu.bspay.sdk.opps.core.config.MerConfig;
import org.yaml.snakeyaml.Yaml;
 
/**
 * Ticket02 只读能力核验面板:对一个商户拉取详情、微信配置、微信费率、多费率、实名状态。
 * 不走 Spring、不连数据库、不含任何写入接口。
 *
 * 用法:java QueryCapabilities <huifu_id> [apply_no]
 */
public class QueryCapabilities {
 
  /** 汇付官方文档里的只读查询端点;写入端点一律不允许出现在这里。 */
  private static final Set<String> READ_ONLY = Set.of(
      "v2.merchant.basicdata.query",
      "v2.merchant.busi.config.query",
      "v2.merchant.busi.realname.query",
      "v2.merchant.basicdata.status.query",
      "v2.merchant.fee-rate.query",
      "v2.merchant.busi.multiFee.query");
 
  public static void main(String[] args) throws Exception {
    String target = args.length > 0 ? args[0] : "6666000241335656";
    String applyNo = args.length > 1 ? args[1] : "";
 
    Map root = new Yaml().load(Files.readString(Path.of("talent-card-server/src/main/resources/application.yml")));
    Map config = find(root);
    String identity = "" + config.get("system-id");
 
    MerConfig mer = new MerConfig();
    mer.setSysId(identity);
    mer.setProductId("" + config.get("product-id"));
    mer.setRsaPrivateKey("" + config.get("private-key"));
    mer.setRsaPublicKey("" + config.get("platform-public-key"));
    BasePay.debug = false;
    BasePay.prodMode = BasePay.MODE_PROD;
    BasePay.initWithMerConfig(mer);
 
    System.out.println("### identity sys_id=" + identity + ", product_id=" + mer.getProductId());
    System.out.println("### target huifu_id=" + target);
    System.out.println();
 
    query("v2.merchant.basicdata.query", mer, request(target));
    query("v2.merchant.busi.config.query", mer, request(target));
    query("v2.merchant.fee-rate.query", mer, request(target));
    query("v2.merchant.busi.multiFee.query", mer, request(target));
 
    Map<String, Object> realname = request(target);
    realname.put("pay_scene", "01");
    query("v2.merchant.busi.realname.query", mer, realname);
 
    if (!applyNo.isBlank()) {
      Map<String, Object> status = request(target);
      status.put("apply_no", applyNo);
      query("v2.merchant.basicdata.status.query", mer, status);
    }
  }
 
  private static Map<String, Object> request(String huifuId) {
    Map<String, Object> request = new LinkedHashMap<>();
    request.put("req_seq_id", UUID.randomUUID().toString().replace("-", ""));
    request.put("req_date", LocalDate.now(ZoneId.of("Asia/Shanghai")).format(DateTimeFormatter.BASIC_ISO_DATE));
    request.put("huifu_id", huifuId);
    return request;
  }
 
  private static void query(String function, MerConfig mer, Map<String, Object> request) {
    if (!READ_ONLY.contains(function)) throw new IllegalArgumentException("Read only query required: " + function);
    System.out.println("=== " + function + " ===");
    System.out.println("req=" + JSON.toJSONString(request));
    try {
      Map<String, Object> response = BasePayClient.request(request, function, false);
      System.out.println("resp=" + JSON.toJSONString(response, true));
    } catch (Exception exception) {
      System.out.println("sdk-exception=" + exception.getClass().getSimpleName() + ": " + exception.getMessage());
      rawDiagnostic(function, mer, request);
    }
    System.out.println();
  }
 
  /**
   * SDK 不认识该端点(或抛异常)时,直接发一次同样签名的原始 HTTP,取出状态码与网关返回体。
   * 只用于诊断与读取,不作为能力生效证据。
   */
  private static void rawDiagnostic(String function, MerConfig mer, Map<String, Object> request) {
    try {
      String body = JSON.toJSONString(request);
      String signature = com.huifu.bspay.sdk.opps.core.utils.RsaUtils.sign(
          com.huifu.bspay.sdk.opps.core.sign.JsonUtils.sort4JsonString(body, 0), mer.getRsaPrivateKey());
      String envelope = JSON.toJSONString(Map.of(
          "sys_id", mer.getSysId(), "product_id", mer.getProductId(), "data", request, "sign", signature));
      var http = java.net.http.HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(15)).build();
      var req = java.net.http.HttpRequest.newBuilder(java.net.URI.create("https://api.huifu.com/" + function.replace('.', '/')))
          .timeout(Duration.ofSeconds(20)).header("Content-Type", "application/json")
          .POST(java.net.http.HttpRequest.BodyPublishers.ofString(envelope)).build();
      var res = http.send(req, java.net.http.HttpResponse.BodyHandlers.ofString());
      System.out.println("http-status=" + res.statusCode());
      System.out.println("http-body=" + res.body());
    } catch (Exception diagnostic) {
      System.out.println("diagnostic-failed=" + diagnostic);
    }
  }
 
  private static Map find(Map root) {
    if (root.containsKey("system-id") && root.containsKey("private-key")) return root;
    for (Object value : root.values()) {
      if (value instanceof Map nested) {
        Map found = find(nested);
        if (found != null) return found;
      }
    }
    return null;
  }
}