feat 新增微信获取OpenId相关接口, 微信分账者绑定支持扫码获取OpenId

This commit is contained in:
bootx
2024-06-16 01:30:18 +08:00
parent ed2b1c535b
commit 0f9b86fd89
22 changed files with 281 additions and 113 deletions

View File

@@ -0,0 +1,42 @@
package cn.daxpay.single.admin.controller.extra;
import cn.bootx.platform.common.core.annotation.IgnoreAuth;
import cn.bootx.platform.common.core.rest.Res;
import cn.bootx.platform.common.core.rest.ResResult;
import cn.daxpay.single.service.core.extra.WeChatOpenIdService;
import cn.daxpay.single.service.dto.extra.WeChatAuthUrlResult;
import cn.daxpay.single.service.dto.extra.WeChatOpenIdResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
*
* @author xxm
* @since 2024/6/15
*/
@IgnoreAuth
@Tag(name = "微信OpenId服务类")
@RestController
@RequestMapping("/wechat/openId/")
@RequiredArgsConstructor
public class WechatOpenIdController {
private final WeChatOpenIdService wechatOpenIdService;
@Operation(summary = "返回获取OpenId授权页面地址和标识码")
@PostMapping("/generateAuthUrl")
public ResResult<WeChatAuthUrlResult> generateAuthUrl(){
return Res.ok(wechatOpenIdService.generateAuthUrl());
}
@Operation(summary = "根据标识码查询OpenId")
@GetMapping("/queryOpenId")
public ResResult<WeChatOpenIdResult> queryOpenId(String code){
return Res.ok(wechatOpenIdService.queryOpenId(code));
}
}

View File

@@ -17,6 +17,6 @@ import lombok.experimental.Accessors;
@Schema(title = "获取微信AccessToken参数")
public class WxAccessTokenParam extends PaymentCommonParam {
@Schema(description = "微信code")
@Schema(description = "微信认证code")
private String code;
}

View File

@@ -18,5 +18,4 @@
<version>${daxpay.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -1,23 +0,0 @@
package cn.daxpay.single.gateway.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 嵌入式h5项目转发控制器, 不用输入 index.html也可以正常访问
* @author xxm
* @since 2024/2/10
*/
@Controller
@RequestMapping
public class ForwardH5Controller {
/**
* 将/h5/*的访问请求代理到/h5/index.html*
*/
@GetMapping("/h5/")
public String toH5(){
return "forward:/h5/index.html";
}
}

View File

@@ -4,6 +4,7 @@ import cn.bootx.platform.common.core.annotation.IgnoreAuth;
import cn.daxpay.single.service.core.channel.alipay.service.AliPayCallbackService;
import cn.daxpay.single.service.core.channel.union.service.UnionPayCallbackService;
import cn.daxpay.single.service.core.channel.wechat.service.WeChatPayCallbackService;
import cn.daxpay.single.service.core.extra.WeChatOpenIdService;
import cn.daxpay.single.service.sdk.union.api.UnionPayKit;
import com.egzosn.pay.union.api.UnionPayConfigStorage;
import com.ijpay.alipay.AliPayApi;
@@ -14,15 +15,15 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
/**
* 包括支付成功/退款/转账等一系列类型的回调处理
* 也包括获取微信OpenId/支付宝UserId等的授权回调
* @author xxm
* @since 2021/2/27
*/
@@ -36,6 +37,8 @@ public class PayCallbackController {
private final AliPayCallbackService aliPayCallbackService;
private final WeChatOpenIdService wechatOpenIdService;
private final WeChatPayCallbackService weChatPayCallbackService;
private final UnionPayCallbackService unionPayCallbackService;
@@ -57,6 +60,14 @@ public class PayCallbackController {
return weChatPayCallbackService.callback(params);
}
@Operation(summary = "微信获取OpenId授权回调方法")
@GetMapping("/wechat/openId/{code}")
public ModelAndView wxAuthCallback(@RequestParam("code") String authCode, @PathVariable("code") String code){
wechatOpenIdService.authCallback(authCode, code);
// 调用页面自动关闭
return new ModelAndView("forward:/h5/openIdCallbackClose.html");
}
@SuppressWarnings({"unchecked", "rawtypes"})
@SneakyThrows
@Operation(summary = "云闪付支付信息回调")

View File

@@ -11,7 +11,7 @@ import cn.daxpay.single.result.assist.WxAccessTokenResult;
import cn.daxpay.single.result.assist.WxAuthUrlResult;
import cn.daxpay.single.service.annotation.InitPaymentContext;
import cn.daxpay.single.service.annotation.PaymentVerify;
import cn.daxpay.single.service.core.payment.assist.service.UniPayAssistService;
import cn.daxpay.single.service.core.extra.WeChatOpenIdService;
import cn.daxpay.single.util.DaxRes;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -32,14 +32,14 @@ import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/unipay/assist")
@RequiredArgsConstructor
public class UniPayAssistController {
private final UniPayAssistService uniPayAssistService;
private final WeChatOpenIdService wechatOpenIdService;
@PaymentVerify
@InitPaymentContext(PaymentApiCode.GET_WX_AUTH_URL)
@Operation(summary = "获取微信OAuth2授权链接")
@PostMapping("/getWxAuthUrl")
public DaxResult<WxAuthUrlResult> getWxAuthUrl(@RequestBody WxAuthUrlParam param){
return DaxRes.ok(uniPayAssistService.getWxAuthUrl(param));
return DaxRes.ok(wechatOpenIdService.getWxAuthUrl(param));
}
@PaymentVerify
@@ -47,7 +47,7 @@ public class UniPayAssistController {
@Operation(summary = "获取微信AccessToken")
@PostMapping("/getWxAccessToken")
public ResResult<WxAccessTokenResult> getWxAccessToken(@RequestBody WxAccessTokenParam param){
return Res.ok(uniPayAssistService.getWxAccessToken(param));
return Res.ok(wechatOpenIdService.getWxAccessToken(param));
}
}

View File

@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>获取成功</title>
</head>
获取成功...
<body>
<script>
// 常规关闭
setTimeout(function (){
window.opener = window;
const win = window.open("", "_self");
win.close();
//frame的时候
top.close();
},500)
// 微信浏览器关闭
setTimeout(function (){
WeixinJSBridge.call('closeWindow')
},500)
</script>
</body>

View File

@@ -1,7 +1,6 @@
package cn.daxpay.single.service.core.channel.wechat.service;
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
import cn.daxpay.single.code.PayChannelEnum;
import cn.daxpay.single.code.ReconcileTradeEnum;
import cn.daxpay.single.exception.pay.PayFailureException;
import cn.daxpay.single.service.code.ReconcileFileTypeEnum;
@@ -60,7 +59,7 @@ import static cn.daxpay.single.service.code.WeChatPayCode.ACCOUNT_TYPE_BASIC;
@Slf4j
@Service
@RequiredArgsConstructor
public class WechatPayReconcileService{
public class WeChatPayReconcileService {
private final WxReconcileBillTotalManger reconcileBillTotalManger;
private final WxReconcileBillDetailManager reconcileBillDetailManager;
private final FileStorageService fileStorageService;

View File

@@ -31,7 +31,7 @@ import static cn.daxpay.single.service.code.WeChatPayCode.*;
@Slf4j
@Service
@RequiredArgsConstructor
public class WechatPayRefundService {
public class WeChatPayRefundService {
/**
* 退款方法

View File

@@ -0,0 +1,130 @@
package cn.daxpay.single.service.core.extra;
import cn.bootx.platform.common.redis.RedisClient;
import cn.daxpay.single.param.assist.WxAccessTokenParam;
import cn.daxpay.single.param.assist.WxAuthUrlParam;
import cn.daxpay.single.result.assist.WxAccessTokenResult;
import cn.daxpay.single.result.assist.WxAuthUrlResult;
import cn.daxpay.single.service.core.channel.wechat.entity.WeChatPayConfig;
import cn.daxpay.single.service.core.channel.wechat.service.WeChatPayConfigService;
import cn.daxpay.single.service.core.system.config.entity.PlatformConfig;
import cn.daxpay.single.service.core.system.config.service.PlatformConfigService;
import cn.daxpay.single.service.dto.extra.WeChatAuthUrlResult;
import cn.daxpay.single.service.dto.extra.WeChatOpenIdResult;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
import org.springframework.stereotype.Service;
import java.util.Objects;
/**
* 支付支撑服务类
* @author xxm
* @since 2024/2/10
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class WeChatOpenIdService {
public static final String OPEN_ID_KEY_PREFIX = "daxpay:wechat:openId:";
private final WeChatPayConfigService weChatPayConfigService;
private final PlatformConfigService platformsConfigService;
private final RedisClient redisClient;
/**
* 构建微信oauth2授权的url连接
*/
public WxAuthUrlResult getWxAuthUrl(WxAuthUrlParam param) {
WxMpService wxMpService = this.getWxMpService();
String url = wxMpService.getOAuth2Service()
.buildAuthorizationUrl(param.getUrl(), WxConsts.OAuth2Scope.SNSAPI_BASE, param.getState());
return new WxAuthUrlResult().setUrl(url);
}
/**
* 获取微信AccessToken数据
*/
@SneakyThrows
public WxAccessTokenResult getWxAccessToken(WxAccessTokenParam wxAccessToken){
WxMpService wxMpService = this.getWxMpService();
WxOAuth2AccessToken accessToken = wxMpService.getOAuth2Service().getAccessToken(wxAccessToken.getCode());
return new WxAccessTokenResult()
.setAccessToken(accessToken.getAccessToken())
.setOpenId(accessToken.getOpenId());
}
/**
* 根据传入的标识码code生成一个用于微信授权页面的链接
*/
public WeChatAuthUrlResult generateAuthUrl() {
PlatformConfig config = platformsConfigService.getConfig();
String code = RandomUtil.randomString(10);
// 构建出授权后重定向页面链接
String redirectUrl = StrUtil.format("{}/callback/pay/wechat/openId/{}", config.getWebsiteUrl(), code);
WxAuthUrlResult result = this.getWxAuthUrl(new WxAuthUrlParam().setUrl(redirectUrl));
// 写入Redis, 五分钟有效期
redisClient.setWithTimeout(OPEN_ID_KEY_PREFIX + code, "", 5*60*1000L);
return new WeChatAuthUrlResult()
.setCode(code)
.setAuthUrl(result.getUrl());
}
/**
* 微信授权回调页面, 通过获取到authCode获取到OpenId, 存到到Redis中对应code关联的键值对中
* @param authCode 微信返回的授权码
* @param code 标识码
*/
public void authCallback(String authCode, String code) {
// 获取OpenId
WxAccessTokenParam param = new WxAccessTokenParam();
param.setCode(authCode);
WxAccessTokenResult wxAccessToken = this.getWxAccessToken(new WxAccessTokenParam().setCode(authCode));
// 写入Redis
redisClient.setWithTimeout(OPEN_ID_KEY_PREFIX + code, wxAccessToken.getOpenId(), 60*1000L);
}
/**
* 通过标识码轮训获取OpenId
*/
public WeChatOpenIdResult queryOpenId(String code) {
// 从redis中获取
String openId = redisClient.get(OPEN_ID_KEY_PREFIX + code);
// 不为空存在
if (StrUtil.isNotBlank(openId)){
return new WeChatOpenIdResult().setOpenId(openId).setStatus("success");
}
// 为空获取中
if (Objects.equals(openId, "")){
return new WeChatOpenIdResult().setStatus("pending");
}
// null不存在
return new WeChatOpenIdResult().setStatus("fail");
}
/**
* 获取微信公众号API的Service
*/
private WxMpService getWxMpService() {
WeChatPayConfig config = weChatPayConfigService.getConfig();
WxMpService wxMpService = new WxMpServiceImpl();
WxMpDefaultConfigImpl wxMpConfig = new WxMpDefaultConfigImpl();
wxMpConfig.setAppId(config.getWxAppId()); // 设置微信公众号的appid
wxMpConfig.setSecret(config.getAppSecret()); // 设置微信公众号的app corpSecret
wxMpService.setWxMpConfigStorage(wxMpConfig);
return wxMpService;
}
}

View File

@@ -1,64 +0,0 @@
package cn.daxpay.single.service.core.payment.assist.service;
import cn.daxpay.single.param.assist.WxAccessTokenParam;
import cn.daxpay.single.param.assist.WxAuthUrlParam;
import cn.daxpay.single.result.assist.WxAccessTokenResult;
import cn.daxpay.single.result.assist.WxAuthUrlResult;
import cn.daxpay.single.service.core.channel.wechat.entity.WeChatPayConfig;
import cn.daxpay.single.service.core.channel.wechat.service.WeChatPayConfigService;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
import org.springframework.stereotype.Service;
/**
* 支付支撑服务类
* @author xxm
* @since 2024/2/10
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class UniPayAssistService {
private final WeChatPayConfigService weChatPayConfigService;
/**
* 构建微信oauth2授权的url连接
*/
public WxAuthUrlResult getWxAuthUrl(WxAuthUrlParam param) {
WxMpService wxMpService = this.getWxMpService();
String url = wxMpService.getOAuth2Service()
.buildAuthorizationUrl(param.getUrl(), WxConsts.OAuth2Scope.SNSAPI_BASE, param.getState());
return new WxAuthUrlResult().setUrl(url);
}
/**
* 获取微信AccessToken数据
*/
@SneakyThrows
public WxAccessTokenResult getWxAccessToken(WxAccessTokenParam wxAccessToken){
WxMpService wxMpService = this.getWxMpService();
WxOAuth2AccessToken accessToken = wxMpService.getOAuth2Service().getAccessToken(wxAccessToken.getCode());
return new WxAccessTokenResult()
.setAccessToken(accessToken.getAccessToken())
.setOpenId(accessToken.getOpenId());
}
/**
* 获取微信公众号API的Service
*/
private WxMpService getWxMpService() {
WeChatPayConfig config = weChatPayConfigService.getConfig();
WxMpService wxMpService = new WxMpServiceImpl();
WxMpDefaultConfigImpl wxMpConfig = new WxMpDefaultConfigImpl();
wxMpConfig.setAppId(config.getWxAppId()); // 设置微信公众号的appid
wxMpConfig.setSecret(config.getAppSecret()); // 设置微信公众号的app corpSecret
wxMpService.setWxMpConfigStorage(wxMpConfig);
return wxMpService;
}
}

View File

@@ -5,7 +5,7 @@ import cn.daxpay.single.code.PayChannelEnum;
import cn.daxpay.single.service.code.ReconcileFileTypeEnum;
import cn.daxpay.single.service.core.channel.wechat.entity.WeChatPayConfig;
import cn.daxpay.single.service.core.channel.wechat.service.WeChatPayConfigService;
import cn.daxpay.single.service.core.channel.wechat.service.WechatPayReconcileService;
import cn.daxpay.single.service.core.channel.wechat.service.WeChatPayReconcileService;
import cn.daxpay.single.service.func.AbsReconcileStrategy;
import cn.hutool.core.date.DatePattern;
import lombok.RequiredArgsConstructor;
@@ -28,7 +28,7 @@ import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROT
@RequiredArgsConstructor
public class WechatPayReconcileStrategy extends AbsReconcileStrategy {
private final WechatPayReconcileService reconcileService;
private final WeChatPayReconcileService reconcileService;
private final WeChatPayConfigService weChatPayConfigService;

View File

@@ -3,7 +3,7 @@ package cn.daxpay.single.service.core.payment.refund.strategy;
import cn.daxpay.single.code.PayChannelEnum;
import cn.daxpay.single.service.core.channel.wechat.entity.WeChatPayConfig;
import cn.daxpay.single.service.core.channel.wechat.service.WeChatPayConfigService;
import cn.daxpay.single.service.core.channel.wechat.service.WechatPayRefundService;
import cn.daxpay.single.service.core.channel.wechat.service.WeChatPayRefundService;
import cn.daxpay.single.service.func.AbsRefundStrategy;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Scope;
@@ -23,7 +23,7 @@ public class WeChatRefundStrategy extends AbsRefundStrategy {
private final WeChatPayConfigService weChatPayConfigService;
private final WechatPayRefundService wechatPayRefundService;
private final WeChatPayRefundService wechatPayRefundService;
private WeChatPayConfig weChatPayConfig;

View File

@@ -0,0 +1,25 @@
package cn.daxpay.single.service.dto.extra;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 微信获取OpenId授权链接和查询标识返回类
* @author xxm
* @since 2024/6/15
*/
@Data
@Accessors(chain = true)
@Schema(title = "微信授权链接和查询标识返回类")
public class WeChatAuthUrlResult {
/** 授权访问链接 */
@Schema(description = "授权访问链接")
private String authUrl;
/** 标识码, 用于查询是否获取到了OpenId */
@Schema(description = "标识码")
private String code;
}

View File

@@ -0,0 +1,28 @@
package cn.daxpay.single.service.dto.extra;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 微信OpenId查询结果
* @author xxm
* @since 2024/6/15
*/
@Data
@Accessors(chain = true)
@Schema(title = "微信OpenId查询结果")
public class WeChatOpenIdResult {
@Schema(description = "OpenId")
private String openId;
/**
* 状态
* pending:查询中
* success:查询成功
* fail:查询失败
*/
@Schema(description = "状态")
private String status;
}