feat: 新增虚拟支付订单与网盘链接检测功能

This commit is contained in:
王鹏
2026-07-28 13:42:30 +08:00
parent 4070179f46
commit 2250f744d1
60 changed files with 3668 additions and 106 deletions

View File

@@ -0,0 +1,29 @@
package com.ruoyi.common.wx;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 微信小程序虚拟支付配置。
*/
@Data
@Component
@ConfigurationProperties(prefix = "wx.miniapp.virtual-pay")
public class VirtualPayConfig
{
/** 是否启用虚拟支付。 */
private boolean enabled;
/** 虚拟支付 OfferId。 */
private String offerId;
/** 虚拟支付现网 AppKey。 */
private String appKey;
/** 0-正式环境1-沙箱环境。 */
private int env = 0;
/** 小程序消息推送 Token用于校验支付通知。 */
private String callbackToken;
}

View File

@@ -0,0 +1,26 @@
package com.ruoyi.common.wx;
/**
* 微信 code2Session 的服务端结果。该对象禁止返回给客户端。
*/
public class WxCodeSession
{
private final String openId;
private final String sessionKey;
public WxCodeSession(String openId, String sessionKey)
{
this.openId = openId;
this.sessionKey = sessionKey;
}
public String getOpenId()
{
return openId;
}
public String getSessionKey()
{
return sessionKey;
}
}

View File

@@ -0,0 +1,81 @@
package com.ruoyi.common.wx;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.common.exception.ServiceException;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
/**
* 仅在服务端使用小程序临时 code 换取 OpenID 与 session_key。
*/
@Service
public class WxCodeSessionService
{
private static final String CODE_TO_SESSION_URL = "https://api.weixin.qq.com/sns/jscode2session";
private final WxPayConfig wxPayConfig;
private final ObjectMapper objectMapper;
private final OkHttpClient httpClient;
public WxCodeSessionService(WxPayConfig wxPayConfig, ObjectMapper objectMapper)
{
this.wxPayConfig = wxPayConfig;
this.objectMapper = objectMapper;
this.httpClient = new OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS)
.readTimeout(8, TimeUnit.SECONDS)
.build();
}
public WxCodeSession exchange(String code)
{
if (StringUtils.isBlank(code))
{
throw new ServiceException("微信登录凭证不能为空");
}
if (StringUtils.isAnyBlank(wxPayConfig.getAppid(), wxPayConfig.getSecret()))
{
throw new ServiceException("微信小程序 AppID 或 Secret 未配置");
}
HttpUrl url = HttpUrl.parse(CODE_TO_SESSION_URL).newBuilder()
.addQueryParameter("appid", wxPayConfig.getAppid())
.addQueryParameter("secret", wxPayConfig.getSecret())
.addQueryParameter("js_code", code)
.addQueryParameter("grant_type", "authorization_code")
.build();
Request request = new Request.Builder().url(url).get().build();
try (Response response = httpClient.newCall(request).execute())
{
if (!response.isSuccessful() || response.body() == null)
{
throw new ServiceException("微信登录服务暂不可用");
}
JsonNode result = objectMapper.readTree(response.body().string());
if (result.path("errcode").asInt(0) != 0)
{
throw new ServiceException("微信登录失败:" + result.path("errmsg").asText("未知错误"));
}
String openId = result.path("openid").asText();
String sessionKey = result.path("session_key").asText();
if (StringUtils.isAnyBlank(openId, sessionKey))
{
throw new ServiceException("微信登录结果不完整");
}
return new WxCodeSession(openId, sessionKey);
}
catch (IOException e)
{
throw new ServiceException("调用微信登录服务失败");
}
}
}