|
|
@@ -23,13 +23,14 @@ import org.springframework.data.redis.core.StringRedisTemplate;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
+import javax.crypto.Mac;
|
|
|
+import javax.crypto.spec.SecretKeySpec;
|
|
|
import java.math.BigDecimal;
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
+import java.security.SecureRandom;
|
|
|
import java.time.LocalDateTime;
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
-import java.util.Collections;
|
|
|
-import java.util.HashMap;
|
|
|
-import java.util.List;
|
|
|
-import java.util.Map;
|
|
|
+import java.util.*;
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
/**
|
|
|
@@ -903,6 +904,60 @@ public class PayScoreServiceImpl implements PayScoreService {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ @Override
|
|
|
+ public Map<String, Object> getPayscoreDetailSign(String outTradeNo) {
|
|
|
+ if (wxPayConfig == null) {
|
|
|
+ throw new IllegalStateException("微信支付未配置");
|
|
|
+ }
|
|
|
+
|
|
|
+ String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
|
|
|
+ String nonceStr = generateNonceStr(32);
|
|
|
+ String signType = "HMAC-SHA256";
|
|
|
+
|
|
|
+ // 按 key 字典序拼接: mch_id\nout_order_no\nservice_id\nsign_type\ntimestamp\nnonce_str\n
|
|
|
+ String signMessage = "mch_id=" + wxPayConfig.getMchId() + "&"
|
|
|
+ + "out_order_no=" + outTradeNo + "&"
|
|
|
+ + "service_id=" + wxPayConfig.getServiceId() + "&"
|
|
|
+ + "sign_type=" + signType + "&"
|
|
|
+ + "timestamp=" + timestamp + "&"
|
|
|
+ + "nonce_str=" + nonceStr;
|
|
|
+
|
|
|
+ String sign;
|
|
|
+ try {
|
|
|
+ Mac mac = Mac.getInstance("HmacSHA256");
|
|
|
+ byte[] keyBytes = wxPayConfig.getV3ApiKey().getBytes(StandardCharsets.UTF_8);
|
|
|
+ SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "HmacSHA256");
|
|
|
+ mac.init(secretKey);
|
|
|
+ byte[] signBytes = mac.doFinal(signMessage.getBytes(StandardCharsets.UTF_8));
|
|
|
+ sign = Base64.getEncoder().encodeToString(signBytes);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[支付分服务] 生成openBusinessView签名失败", e);
|
|
|
+ throw new RuntimeException("生成签名失败", e);
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<String, Object> data = new HashMap<>();
|
|
|
+ data.put("mchId", wxPayConfig.getMchId());
|
|
|
+ data.put("serviceId", wxPayConfig.getServiceId());
|
|
|
+ data.put("outOrderNo", outTradeNo);
|
|
|
+ data.put("timestamp", timestamp);
|
|
|
+ data.put("nonceStr", nonceStr);
|
|
|
+ data.put("signType", signType);
|
|
|
+ data.put("sign", sign);
|
|
|
+
|
|
|
+ log.info("[支付分服务] 生成支付分详情页签名 - outTradeNo: {}", outTradeNo);
|
|
|
+ return data;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String generateNonceStr(int length) {
|
|
|
+ String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
|
+ SecureRandom random = new SecureRandom();
|
|
|
+ StringBuilder sb = new StringBuilder(length);
|
|
|
+ for (int i = 0; i < length; i++) {
|
|
|
+ sb.append(chars.charAt(random.nextInt(chars.length())));
|
|
|
+ }
|
|
|
+ return sb.toString();
|
|
|
+ }
|
|
|
+
|
|
|
private void storePreAuthToRedis(String deviceId, Long userId, PayScoreResult result) {
|
|
|
if (stringRedisTemplate == null) {
|
|
|
return;
|