import java.nio.file.Files; import java.nio.file.Path; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.LinkedHashMap; import java.util.Map; 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; /** * 只读探针 + 一笔 ¥0.01 的测试下单(不会有人支付,15 分钟后自然过期,不动钱)。 * *

目的:拿到某商户在**当前通道配置**下小程序支付的真实回执——库里的创建失败回执会被 * 关单覆盖,只有直接问通道才看得到原文。 * *

用法:java ... PaymentProbe openid 取一个该商户用过的真实 openid * java ... PaymentProbe jspay <huifuId> <openid> <reqSeqId> */ public class PaymentProbe { private static final String DB = "jdbc:mysql://127.0.0.1:8134/talent_card2?useUnicode=true&characterEncoding=utf8" + "&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false"; public static void main(String[] args) throws Exception { if ("openid".equals(args[0])) { Class.forName("com.mysql.cj.jdbc.Driver"); try (Connection c = DriverManager.getConnection(DB, "root", "123456"); Statement s = c.createStatement(); ResultSet rs = s.executeQuery("select t.id, t.openid, o.order_no, o.collect_huifu_id from benefit_order o " + "join talent_user t on t.id = o.talent_id " + "where o.collect_huifu_id = '" + args[1] + "' and t.openid is not null and t.openid <> '' " + "order by o.id desc limit 3")) { while (rs.next()) System.out.println(rs.getString(2) + " | talent=" + rs.getString(1) + " order=" + rs.getString(3)); } return; } if (!"jspay".equals(args[0])) throw new IllegalArgumentException("mode?"); Map root = new Yaml().load(Files.readString(Path.of("talent-card-server/src/main/resources/application.yml"))); Map config = find(root); BasePay.debug = false; BasePay.prodMode = BasePay.MODE_PROD; MerConfig mer = new MerConfig(); mer.setSysId("" + config.get("system-id")); mer.setProductId("" + config.get("product-id")); mer.setRsaPrivateKey("" + config.get("private-key")); mer.setRsaPublicKey("" + config.get("platform-public-key")); BasePay.initWithMerConfig(mer); Map request = new LinkedHashMap<>(); request.put("req_date", DateTimeFormatter.ofPattern("yyyyMMdd").format(LocalDateTime.now(ZoneId.of("Asia/Shanghai")))); request.put("req_seq_id", args[3]); request.put("huifu_id", args[1]); request.put("goods_desc", "probe-test-goods"); request.put("trans_amt", "0.01"); request.put("trade_type", "T_MINIAPP"); Map wxData = new LinkedHashMap<>(); wxData.put("sub_openid", args[2]); wxData.put("sub_appid", ("" + config.get("mini-program-app-id")).trim()); Map extend = new LinkedHashMap<>(); // 官方《聚合正扫》T_MINIAPP 示例把 wx_data / notify_url 放在**顶层**(不在 extend_info 里)。 request.put("wx_data", JSON.toJSONString(wxData)); request.put("notify_url", ("" + config.get("notify-url")).trim()); extend.put("delay_acct_flag", "Y"); extend.put("time_expire", DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now(ZoneId.of("Asia/Shanghai")).plusMinutes(15))); request.put("extend_info", extend); System.out.println("REQUEST " + JSON.toJSONString(request)); System.out.println("RESPONSE " + JSON.toJSONString(BasePayClient.request(request, "v3.trade.payment.jspay", false))); } static Map find(Map root) { if (root.containsKey("system-id") && root.containsKey("private-key")) return root; for (Object v : root.values()) if (v instanceof Map m) { Map r = find(m); if (r != null) return r; } return null; } }