|
|
@@ -34,7 +34,7 @@ import com.haha.service.payment.payscore.PayScoreService;
|
|
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
-
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
@@ -538,6 +538,9 @@ public class HahaCallbackServiceImpl implements HahaCallbackService {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ @Value("${haha.api.app-secret}")
|
|
|
+ private String appSecret;
|
|
|
+
|
|
|
@Override
|
|
|
public boolean validateSign(Map<String, Object> params) {
|
|
|
String receivedSign = extractParam(params, "sign");
|
|
|
@@ -546,47 +549,45 @@ public class HahaCallbackServiceImpl implements HahaCallbackService {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- // 回调验签使用 ticket(从 /token/get 接口获取),而非 appSecret
|
|
|
- String ticket;
|
|
|
- try {
|
|
|
- // 触发 token 获取/刷新,同时会填充 ticket
|
|
|
- hahaClient.getAccessToken();
|
|
|
- ticket = hahaClient.getTicket();
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("获取验签 ticket 失败", e);
|
|
|
- return false;
|
|
|
- }
|
|
|
+ String signContent = buildSignContent(params);
|
|
|
|
|
|
- if (ticket == null || ticket.isEmpty()) {
|
|
|
- log.error("ticket 为空,无法验证签名");
|
|
|
- return false;
|
|
|
+ // 诊断模式:同时尝试 appsecret 和 ticket 两种方式,日志中明确标注匹配情况
|
|
|
+ // 待确认哪种方式正确后,去掉不需要的分支
|
|
|
+ boolean matched = false;
|
|
|
+
|
|
|
+ // 方式1:末尾拼接 &appsecret=<appSecret>(哈哈API文档描述的方式)
|
|
|
+ if (appSecret != null && !appSecret.isEmpty()) {
|
|
|
+ String sign1 = md5(signContent + "&appsecret=" + appSecret);
|
|
|
+ if (sign1.equalsIgnoreCase(receivedSign)) {
|
|
|
+ log.debug("签名验证通过 (appsecret方式)");
|
|
|
+ matched = true;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- try {
|
|
|
- // 哈哈平台回调签名算法:
|
|
|
- // 1. 排除 sign 字段,按 key 字典序排列
|
|
|
- // 2. 拼接为 key1=value1&key2=value2&...
|
|
|
- // 3. 末尾拼接 &ticket=<ticket>
|
|
|
- // 4. MD5 得到签名值
|
|
|
- // 参考: haha-sdk CallbackUtils.verifySign()
|
|
|
-
|
|
|
- String signContent = buildSignContent(params);
|
|
|
- String signStr = signContent + "&ticket=" + ticket;
|
|
|
- String computedSign = md5(signStr);
|
|
|
-
|
|
|
- boolean isValid = computedSign.equalsIgnoreCase(receivedSign);
|
|
|
- if (!isValid) {
|
|
|
- log.warn("签名验证失败 - 计算签名: {}, 接收签名: {}, 签名内容: {}",
|
|
|
- computedSign, receivedSign, signContent);
|
|
|
- } else {
|
|
|
- log.debug("签名验证通过");
|
|
|
+ // 方式2:末尾拼接 &ticket=<ticket>(SDK CallbackUtils 描述的方式)
|
|
|
+ if (!matched) {
|
|
|
+ String ticket = null;
|
|
|
+ try {
|
|
|
+ hahaClient.getAccessToken();
|
|
|
+ ticket = hahaClient.getTicket();
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.debug("获取 ticket 失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+ if (ticket != null && !ticket.isEmpty()) {
|
|
|
+ String sign2 = md5(signContent + "&ticket=" + ticket);
|
|
|
+ if (sign2.equalsIgnoreCase(receivedSign)) {
|
|
|
+ log.debug("签名验证通过 (ticket方式)");
|
|
|
+ matched = true;
|
|
|
+ }
|
|
|
}
|
|
|
- return isValid;
|
|
|
+ }
|
|
|
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("签名验证异常", e);
|
|
|
- return false;
|
|
|
+ if (!matched) {
|
|
|
+ log.warn("签名验证失败(两种方式均不匹配) - 接收签名: {}, 签名内容: {}",
|
|
|
+ receivedSign, signContent);
|
|
|
}
|
|
|
+
|
|
|
+ return matched;
|
|
|
}
|
|
|
|
|
|
/**
|