|
|
@@ -1,108 +1,392 @@
|
|
|
package com.fs.core.config;
|
|
|
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
+import com.fs.common.core.redis.RedisCache;
|
|
|
+import com.fs.config.saas.ProjectConfig;
|
|
|
+import com.fs.system.domain.SysConfig;
|
|
|
+import com.fs.system.mapper.SysConfigMapper;
|
|
|
import lombok.Data;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
-import org.springframework.boot.context.properties.ConfigurationProperties;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
|
|
|
-@ConfigurationProperties(prefix = "wx.pay")
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
+
|
|
|
+@Component
|
|
|
@Data
|
|
|
+@Slf4j
|
|
|
public class WxPayProperties {
|
|
|
- /**
|
|
|
- * 设置微信公众号或者小程序等的appid
|
|
|
- */
|
|
|
- private String appId;
|
|
|
-
|
|
|
- /**
|
|
|
- * 微信支付商户号
|
|
|
- */
|
|
|
- private String mchId;
|
|
|
|
|
|
- /**
|
|
|
- * 微信支付商户密钥
|
|
|
- */
|
|
|
- private String mchKey;
|
|
|
+ @Autowired
|
|
|
+ private SysConfigMapper sysConfigMapper;
|
|
|
+ @Autowired
|
|
|
+ private RedisCache redisCache;
|
|
|
|
|
|
- private String v3Key;
|
|
|
+ // Redis缓存Key
|
|
|
+ private static final String PROJECT_CONFIG_CACHE_KEY = "project:config:data";
|
|
|
+ // 缓存时间:30分钟
|
|
|
+ private static final Integer CACHE_EXPIRE_TIME = 30;
|
|
|
+ private static final TimeUnit CACHE_EXPIRE_UNIT = TimeUnit.MINUTES;
|
|
|
|
|
|
/**
|
|
|
- * 服务商模式下的子商户公众账号ID,普通模式请不要配置,请在配置文件中将对应项删除
|
|
|
+ * 公用方法:查询数据库获取微信支付配置
|
|
|
*/
|
|
|
- private String subAppId;
|
|
|
+ private ProjectConfig.Wx.Pay getWxPayConfigFromDB() {
|
|
|
+ try {
|
|
|
+ // 1. 先从Redis缓存中获取
|
|
|
+ ProjectConfig cachedConfig = getConfigFromCache();
|
|
|
+ if (cachedConfig != null) {
|
|
|
+ log.debug("从Redis缓存中获取ProjectConfig配置");
|
|
|
+ return cachedConfig.getWx().getPay();
|
|
|
+ }
|
|
|
|
|
|
- /**
|
|
|
- * 服务商模式下的子商户号,普通模式请不要配置,最好是请在配置文件中将对应项删除
|
|
|
- */
|
|
|
- private String subMchId;
|
|
|
+ String configKey = "projectConfig";
|
|
|
+ SysConfig sysConfig = sysConfigMapper.selectConfigByConfigKey(configKey);
|
|
|
|
|
|
- /**
|
|
|
- * apiclient_cert.p12文件的绝对路径,或者如果放在项目中,请以classpath:开头指定
|
|
|
- */
|
|
|
- private String keyPath;
|
|
|
+ if (sysConfig == null || sysConfig.getConfigValue() == null) {
|
|
|
+ log.warn("未找到微信支付配置");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
|
|
|
- private String notifyUrl;
|
|
|
+ ProjectConfig projectConfig = JSONObject.parseObject(
|
|
|
+ sysConfig.getConfigValue(),
|
|
|
+ ProjectConfig.class
|
|
|
+ );
|
|
|
|
|
|
- private String privateKeyPath;
|
|
|
+ if (projectConfig == null || projectConfig.getWx() == null) {
|
|
|
+ log.warn("配置中未找到wx节点");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
|
|
|
- private String privateCertPath;
|
|
|
+ ProjectConfig.Wx.Pay payConfig = projectConfig.getWx().getPay();
|
|
|
+ if (payConfig == null) {
|
|
|
+ log.warn("配置中未找到pay节点");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
|
|
|
- private String certSerialNo;
|
|
|
+ log.debug("查询到微信支付配置: appId={}, mchId={}",
|
|
|
+ payConfig.getAppId(), payConfig.getMchId());
|
|
|
|
|
|
- private String nativeNotifyUrl;
|
|
|
+ saveConfigToCache(projectConfig);
|
|
|
|
|
|
- /* ===================== 动态 getter ===================== */
|
|
|
+ return payConfig;
|
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("查询微信支付配置失败", e);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 保存配置到Redis缓存
|
|
|
+ */
|
|
|
+ private void saveConfigToCache(ProjectConfig config) {
|
|
|
+ try {
|
|
|
+ String configJson = JSONObject.toJSONString(config);
|
|
|
+ redisCache.setCacheObject(PROJECT_CONFIG_CACHE_KEY, configJson, CACHE_EXPIRE_TIME, CACHE_EXPIRE_UNIT);
|
|
|
+ log.debug("微信支付配置已存入Redis缓存,有效期{}分钟", CACHE_EXPIRE_TIME);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("保存微信支付配置到Redis缓存失败", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 从Redis缓存获取ProjectConfig
|
|
|
+ */
|
|
|
+ private ProjectConfig getConfigFromCache() {
|
|
|
+ try {
|
|
|
+ String cachedJson = redisCache.getCacheObject(PROJECT_CONFIG_CACHE_KEY);
|
|
|
+ if (cachedJson != null && !cachedJson.isEmpty()) {
|
|
|
+ return JSONObject.parseObject(cachedJson, ProjectConfig.class);
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("从Redis缓存获取ProjectConfig失败", e);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取appId(每次查询数据库)
|
|
|
+ */
|
|
|
public String getAppId() {
|
|
|
- return tenantOrDefault("wx.pay.appId", appId);
|
|
|
+ ProjectConfig.Wx.Pay payConfig = getWxPayConfigFromDB();
|
|
|
+ if (payConfig == null) {
|
|
|
+ return tenantOrDefault("wx.pay.appId", "");
|
|
|
+ }
|
|
|
+ String value = payConfig.getAppId() != null ? payConfig.getAppId() : "";
|
|
|
+ return StringUtils.isNotBlank(value) ? value : tenantOrDefault("wx.pay.appId", "");
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 获取商户号(每次查询数据库)
|
|
|
+ */
|
|
|
public String getMchId() {
|
|
|
- return tenantOrDefault("wx.pay.mchId", mchId);
|
|
|
+ ProjectConfig.Wx.Pay payConfig = getWxPayConfigFromDB();
|
|
|
+ if (payConfig == null) {
|
|
|
+ return tenantOrDefault("wx.pay.mchId", "");
|
|
|
+ }
|
|
|
+ String value = payConfig.getMchId() != null ? payConfig.getMchId() : "";
|
|
|
+ return StringUtils.isNotBlank(value) ? value : tenantOrDefault("wx.pay.mchId", "");
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 获取商户密钥(每次查询数据库)
|
|
|
+ */
|
|
|
public String getMchKey() {
|
|
|
- return tenantOrDefault("wx.pay.mchKey", mchKey);
|
|
|
+ ProjectConfig.Wx.Pay payConfig = getWxPayConfigFromDB();
|
|
|
+ if (payConfig == null) {
|
|
|
+ return tenantOrDefault("wx.pay.mchKey", "");
|
|
|
+ }
|
|
|
+ String value = payConfig.getMchKey() != null ? payConfig.getMchKey() : "";
|
|
|
+ return StringUtils.isNotBlank(value) ? value : tenantOrDefault("wx.pay.mchKey", "");
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 获取V3密钥(每次查询数据库)
|
|
|
+ */
|
|
|
public String getV3Key() {
|
|
|
- return tenantOrDefault("wx.pay.v3Key", v3Key);
|
|
|
+ ProjectConfig.Wx.Pay payConfig = getWxPayConfigFromDB();
|
|
|
+ if (payConfig == null) {
|
|
|
+ return tenantOrDefault("wx.pay.v3Key", "");
|
|
|
+ }
|
|
|
+ String value = payConfig.getV3Key() != null ? payConfig.getV3Key() : "";
|
|
|
+ return StringUtils.isNotBlank(value) ? value : tenantOrDefault("wx.pay.v3Key", "");
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 获取子商户appId(每次查询数据库)
|
|
|
+ */
|
|
|
public String getSubAppId() {
|
|
|
- return tenantOrDefault("wx.pay.subAppId", subAppId);
|
|
|
+ ProjectConfig.Wx.Pay payConfig = getWxPayConfigFromDB();
|
|
|
+ if (payConfig == null) {
|
|
|
+ return tenantOrDefault("wx.pay.subAppId", "");
|
|
|
+ }
|
|
|
+ String value = payConfig.getSubAppId() != null ? payConfig.getSubAppId() : "";
|
|
|
+ return StringUtils.isNotBlank(value) ? value : tenantOrDefault("wx.pay.subAppId", "");
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 获取子商户号(每次查询数据库)
|
|
|
+ */
|
|
|
public String getSubMchId() {
|
|
|
- return tenantOrDefault("wx.pay.subMchId", subMchId);
|
|
|
+ ProjectConfig.Wx.Pay payConfig = getWxPayConfigFromDB();
|
|
|
+ if (payConfig == null) {
|
|
|
+ return tenantOrDefault("wx.pay.subMchId", "");
|
|
|
+ }
|
|
|
+ String value = payConfig.getSubMchId() != null ? payConfig.getSubMchId() : "";
|
|
|
+ return StringUtils.isNotBlank(value) ? value : tenantOrDefault("wx.pay.subMchId", "");
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 获取密钥文件路径(每次查询数据库)
|
|
|
+ */
|
|
|
public String getKeyPath() {
|
|
|
- return tenantOrDefault("wx.pay.keyPath", keyPath);
|
|
|
+ ProjectConfig.Wx.Pay payConfig = getWxPayConfigFromDB();
|
|
|
+ if (payConfig == null) {
|
|
|
+ return tenantOrDefault("wx.pay.keyPath", "");
|
|
|
+ }
|
|
|
+ String value = payConfig.getKeyPath() != null ? payConfig.getKeyPath() : "";
|
|
|
+ return StringUtils.isNotBlank(value) ? value : tenantOrDefault("wx.pay.keyPath", "");
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 获取回调URL(每次查询数据库)
|
|
|
+ */
|
|
|
public String getNotifyUrl() {
|
|
|
- return tenantOrDefault("wx.pay.notifyUrl", notifyUrl);
|
|
|
+ ProjectConfig.Wx.Pay payConfig = getWxPayConfigFromDB();
|
|
|
+ if (payConfig == null) {
|
|
|
+ return tenantOrDefault("wx.pay.notifyUrl", "");
|
|
|
+ }
|
|
|
+ String value = payConfig.getNotifyUrl() != null ? payConfig.getNotifyUrl() : "";
|
|
|
+ return StringUtils.isNotBlank(value) ? value : tenantOrDefault("wx.pay.notifyUrl", "");
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 获取私钥路径(每次查询数据库)
|
|
|
+ */
|
|
|
public String getPrivateKeyPath() {
|
|
|
- return tenantOrDefault("wx.pay.privateKeyPath", privateKeyPath);
|
|
|
+ ProjectConfig.Wx.Pay payConfig = getWxPayConfigFromDB();
|
|
|
+ if (payConfig == null) {
|
|
|
+ return tenantOrDefault("wx.pay.privateKeyPath", "");
|
|
|
+ }
|
|
|
+ String value = payConfig.getPrivateKeyPath() != null ? payConfig.getPrivateKeyPath() : "";
|
|
|
+ return StringUtils.isNotBlank(value) ? value : tenantOrDefault("wx.pay.privateKeyPath", "");
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 获取私钥证书路径(每次查询数据库)
|
|
|
+ */
|
|
|
public String getPrivateCertPath() {
|
|
|
- return tenantOrDefault("wx.pay.privateCertPath", privateCertPath);
|
|
|
+ ProjectConfig.Wx.Pay payConfig = getWxPayConfigFromDB();
|
|
|
+ if (payConfig == null) {
|
|
|
+ return tenantOrDefault("wx.pay.privateCertPath", "");
|
|
|
+ }
|
|
|
+ String value = payConfig.getPrivateCertPath() != null ? payConfig.getPrivateCertPath() : "";
|
|
|
+ return StringUtils.isNotBlank(value) ? value : tenantOrDefault("wx.pay.privateCertPath", "");
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 获取证书序列号(每次查询数据库)
|
|
|
+ */
|
|
|
public String getCertSerialNo() {
|
|
|
- return tenantOrDefault("wx.pay.certSerialNo", certSerialNo);
|
|
|
+ ProjectConfig.Wx.Pay payConfig = getWxPayConfigFromDB();
|
|
|
+ if (payConfig == null) {
|
|
|
+ return tenantOrDefault("wx.pay.certSerialNo", "");
|
|
|
+ }
|
|
|
+ String value = payConfig.getCertSerialNo() != null ? payConfig.getCertSerialNo() : "";
|
|
|
+ return StringUtils.isNotBlank(value) ? value : tenantOrDefault("wx.pay.certSerialNo", "");
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 获取Native支付回调URL(每次查询数据库)
|
|
|
+ */
|
|
|
public String getNativeNotifyUrl() {
|
|
|
- return tenantOrDefault("wx.pay.nativeNotifyUrl", nativeNotifyUrl);
|
|
|
+ ProjectConfig.Wx.Pay payConfig = getWxPayConfigFromDB();
|
|
|
+ if (payConfig == null) {
|
|
|
+ return tenantOrDefault("wx.pay.nativeNotifyUrl", "");
|
|
|
+ }
|
|
|
+ String value = payConfig.getNativeNotifyUrl() != null ? payConfig.getNativeNotifyUrl() : "";
|
|
|
+ return StringUtils.isNotBlank(value) ? value : tenantOrDefault("wx.pay.nativeNotifyUrl", "");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查配置是否完整(每次查询数据库)
|
|
|
+ */
|
|
|
+ public boolean isConfigValid() {
|
|
|
+ ProjectConfig.Wx.Pay payConfig = getWxPayConfigFromDB();
|
|
|
+ if (payConfig == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ String appId = payConfig.getAppId();
|
|
|
+ String mchId = payConfig.getMchId();
|
|
|
+ String mchKey = payConfig.getMchKey();
|
|
|
+
|
|
|
+ boolean basicValid = StringUtils.isNotBlank(appId) &&
|
|
|
+ StringUtils.isNotBlank(mchId) &&
|
|
|
+ StringUtils.isNotBlank(mchKey);
|
|
|
+
|
|
|
+ log.debug("微信支付配置有效性检查: {}", basicValid);
|
|
|
+ return basicValid;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查V3配置是否完整(每次查询数据库)
|
|
|
+ */
|
|
|
+ public boolean isV3ConfigValid() {
|
|
|
+ ProjectConfig.Wx.Pay payConfig = getWxPayConfigFromDB();
|
|
|
+ if (payConfig == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ String appId = payConfig.getAppId();
|
|
|
+ String mchId = payConfig.getMchId();
|
|
|
+ String v3Key = payConfig.getV3Key();
|
|
|
+ String certSerialNo = payConfig.getCertSerialNo();
|
|
|
+ String privateKeyPath = payConfig.getPrivateKeyPath();
|
|
|
+
|
|
|
+ boolean v3Valid = StringUtils.isNotBlank(appId) &&
|
|
|
+ StringUtils.isNotBlank(mchId) &&
|
|
|
+ StringUtils.isNotBlank(v3Key) &&
|
|
|
+ StringUtils.isNotBlank(certSerialNo) &&
|
|
|
+ StringUtils.isNotBlank(privateKeyPath);
|
|
|
+
|
|
|
+ log.debug("微信支付V3配置有效性检查: {}", v3Valid);
|
|
|
+ return v3Valid;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取配置摘要(每次查询数据库)
|
|
|
+ */
|
|
|
+ public String getConfigSummary() {
|
|
|
+ ProjectConfig.Wx.Pay payConfig = getWxPayConfigFromDB();
|
|
|
+ if (payConfig == null) {
|
|
|
+ return "微信支付配置为空";
|
|
|
+ }
|
|
|
+
|
|
|
+ return String.format("appId: %s, mchId: %s",
|
|
|
+ payConfig.getAppId(), payConfig.getMchId());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取完整的配置对象(每次查询数据库)
|
|
|
+ */
|
|
|
+ public ProjectConfig.Wx.Pay getWxPayConfig() {
|
|
|
+ return getWxPayConfigFromDB();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 刷新配置(实际是空方法,因为每次都是实时查询)
|
|
|
+ */
|
|
|
+ public void refresh() {
|
|
|
+ log.info("刷新微信支付配置(每次都是实时查询,无需刷新缓存)");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 安全获取方法 - 返回非null值
|
|
|
+ */
|
|
|
+ public String getSafeAppId() {
|
|
|
+ String appId = getAppId();
|
|
|
+ return StringUtils.isNotBlank(appId) ? appId : "";
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getSafeMchId() {
|
|
|
+ String mchId = getMchId();
|
|
|
+ return StringUtils.isNotBlank(mchId) ? mchId : "";
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getSafeMchKey() {
|
|
|
+ String mchKey = getMchKey();
|
|
|
+ return StringUtils.isNotBlank(mchKey) ? mchKey : "";
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getSafeV3Key() {
|
|
|
+ String v3Key = getV3Key();
|
|
|
+ return StringUtils.isNotBlank(v3Key) ? v3Key : "";
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getSafeNotifyUrl() {
|
|
|
+ String notifyUrl = getNotifyUrl();
|
|
|
+ return StringUtils.isNotBlank(notifyUrl) ? notifyUrl : "";
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getSafeNativeNotifyUrl() {
|
|
|
+ String nativeNotifyUrl = getNativeNotifyUrl();
|
|
|
+ return StringUtils.isNotBlank(nativeNotifyUrl) ? nativeNotifyUrl : "";
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取完整的回调URL(拼接基础URL)
|
|
|
+ */
|
|
|
+ public String getFullNotifyUrl(String baseUrl) {
|
|
|
+ String notifyPath = getSafeNotifyUrl();
|
|
|
+ if (StringUtils.isBlank(notifyPath)) {
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+
|
|
|
+ if (notifyPath.startsWith("http")) {
|
|
|
+ return notifyPath;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (StringUtils.isBlank(baseUrl)) {
|
|
|
+ return notifyPath;
|
|
|
+ }
|
|
|
+
|
|
|
+ String base = baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 1) : baseUrl;
|
|
|
+ String path = notifyPath.startsWith("/") ? notifyPath : "/" + notifyPath;
|
|
|
+
|
|
|
+ return base + path;
|
|
|
}
|
|
|
|
|
|
/* ===================== 公共兜底方法 ===================== */
|
|
|
|
|
|
private String tenantOrDefault(String jsonPath, String defaultValue) {
|
|
|
- String tenantValue = TenantConfigContext.getString(jsonPath);
|
|
|
- return StringUtils.isNotBlank(tenantValue) ? tenantValue : defaultValue;
|
|
|
+ try {
|
|
|
+ String tenantValue = TenantConfigContext.getString(jsonPath);
|
|
|
+ return StringUtils.isNotBlank(tenantValue) ? tenantValue : defaultValue;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("获取租户配置失败: {}", jsonPath, e);
|
|
|
+ return defaultValue;
|
|
|
+ }
|
|
|
}
|
|
|
}
|