|
@@ -174,4 +174,61 @@ class LeshouyingSignerTest {
|
|
|
inbound.put("total_amount", "2");
|
|
inbound.put("total_amount", "2");
|
|
|
assertFalse(signer.verify(inbound, KEY), "反向守卫:改一个字节必须被拒");
|
|
assertFalse(signer.verify(inbound, KEY), "反向守卫:改一个字节必须被拒");
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * ★ <b>入站验签的第三条独立向量 —— 这条来自上游本人,不是文档、也不是我们自算的。</b>
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p>2026-09-28 用测试环境({@code https://test.gdyfsk.com/yfpay})发了一次**真实的**
|
|
|
|
|
+ * {@code POST /v3/queryorder}(查一个刻意不存在的订单),上游回了 481 字节,**应答里自带
|
|
|
|
|
+ * {@code sign}**。把那条应答原样喂进 {@code verify()}:它必须认。
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p>★ <b>为什么非要有这一条</b>:本类其余 {@code verify*} 用例的期望值要么由 {@code sign()}
|
|
|
|
|
+ * 产出(自洽),要么由**文档**给出({@link #verify_acceptsSignBuiltFromTheDocumentedVectorIndependently})。
|
|
|
|
|
+ * 两条都答不了「<b>上游真实发出来的报文,我们的验签认不认</b>」。而验签是回调入口的**唯一身份证明**,
|
|
|
|
|
+ * 认错了就是「伪造回调把订单改成已支付」。
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p>★ <b>这条向量顺带钉住了一条只能由真货证伪的规则</b>:应答里 26 个字段有 24 个是显式
|
|
|
|
|
+ * {@code null}。上游的 sign 是**只对两个非 null 字段**算的
|
|
|
|
|
+ * ({@code return_code=FAIL&return_msg=订单信息不存在!})。
|
|
|
|
|
+ * 若哪天有人把 {@code buildSource()} 的 {@code filter(v != null)} 去掉(改成「null 当空串参与」),
|
|
|
|
|
+ * 本类其余用例**全绿**,而线上**每一条回调都会被判成验签失败** —— 这条会红。
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p>向量的稳定性:两次独立运行(不同 {@code query_trace_no} / {@code pay_time})拿到**同一个**
|
|
|
|
|
+ * {@code sign},因为参与签名的只有那两个非 null 字段 —— 它不随我们的查询参数变化。
|
|
|
|
|
+ */
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void verify_acceptsRealUpstreamResponseFromTestEnv() {
|
|
|
|
|
+ // 逐字抄自上游应答(字段顺序照原文;那 24 个 null 是上游真发的,不是我们补的)
|
|
|
|
|
+ Map<String, Object> resp = params(
|
|
|
|
|
+ "return_code", "FAIL",
|
|
|
|
|
+ "return_msg", "订单信息不存在!",
|
|
|
|
|
+ "result_code", null,
|
|
|
|
|
+ "pay_type", null,
|
|
|
|
|
+ "mch_name", null,
|
|
|
|
|
+ "shop_name", null,
|
|
|
|
|
+ "mch_no", null,
|
|
|
|
|
+ "shop_no", null,
|
|
|
|
|
+ "query_trace_no", null,
|
|
|
|
|
+ "pay_trace_no", null,
|
|
|
|
|
+ "pay_time", null,
|
|
|
|
|
+ "total_amount", null,
|
|
|
|
|
+ "end_time", null,
|
|
|
|
|
+ "trade_no", null,
|
|
|
|
|
+ "td_trade_no", null,
|
|
|
|
|
+ "qd_trade_no", null,
|
|
|
|
|
+ "user_id", null,
|
|
|
|
|
+ "attach", null,
|
|
|
|
|
+ "trade_state", null,
|
|
|
|
|
+ "receipt_amount", null,
|
|
|
|
|
+ "pay_amount", null,
|
|
|
|
|
+ "coupon_amount", null,
|
|
|
|
|
+ "discount_amount", null);
|
|
|
|
|
+ resp.put("sign", "a3cb8169d863e438486a6ea0ae72fea7");
|
|
|
|
|
+
|
|
|
|
|
+ assertTrue(signer.verify(resp, KEY), "上游真实应答必须被认 —— 这条挂了说明入站验签与上游不一致");
|
|
|
|
|
+
|
|
|
|
|
+ // 反向守卫:改了被签字段必须被拒(少了它,上面那句在「verify 恒真」下也会绿)
|
|
|
|
|
+ resp.put("return_msg", "订单信息存在!");
|
|
|
|
|
+ assertFalse(signer.verify(resp, KEY), "反向守卫:改动被签字段必须被拒");
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|