feat 新增支付宝获取OpenId相关接口

This commit is contained in:
bootx
2024-06-16 15:57:47 +08:00
parent 0f9b86fd89
commit 676c0b3232
15 changed files with 301 additions and 39 deletions

View File

@@ -2,12 +2,13 @@
2.0.8: 转账/撤销接口和系统优化
- [x] 支持撤销接口
- [x] 增加转账接口功能
- [ ] 支付宝支持JSAPI方式支付
- [ ] 细分各种支付异常类和编码(部分)
- [ ] 增加分账回调处理(支付宝)
- [ ] 增加分账修复功能
- [ ] 增加扫码获取微信OpenID和支付宝OpenId功能
- [x] 增加扫码获取微信OpenID和支付宝OpenId功能
- [x] 微信
- [ ] 支付宝
- [x] 支付宝
- [ ] DEMO增加转账演示功能
- [x] 支付宝微信等消息通知地址支持一键生成
- [ ] 管理端界面支持扫码绑定对账接收方功能

View File

@@ -0,0 +1,43 @@
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.AliPayAuthService;
import cn.daxpay.single.service.dto.extra.AuthUrlResult;
import cn.daxpay.single.service.dto.extra.OpenIdResult;
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/16
*/
@IgnoreAuth
@Tag(name = "支付宝认证控制器")
@RestController
@RequestMapping("/alipay/auth")
@RequiredArgsConstructor
public class AliPayAuthController {
private final AliPayAuthService aliPayAuthService;
@Operation(summary = "返回获取OpenId授权页面地址和标识码")
@PostMapping("/generateAuthUrl")
public ResResult<AuthUrlResult> generateAuthUrl(){
return Res.ok(aliPayAuthService.generateAuthUrl());
}
@Operation(summary = "根据标识码查询OpenId")
@GetMapping("/queryOpenId")
public ResResult<OpenIdResult> queryOpenId(String code){
return Res.ok(aliPayAuthService.queryOpenId(code));
}
}

View File

@@ -3,9 +3,9 @@ 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 cn.daxpay.single.service.core.extra.WeChatAuthService;
import cn.daxpay.single.service.dto.extra.AuthUrlResult;
import cn.daxpay.single.service.dto.extra.OpenIdResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
@@ -15,28 +15,28 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
*
* 微信认证控制器
* @author xxm
* @since 2024/6/15
*/
@IgnoreAuth
@Tag(name = "微信OpenId服务类")
@Tag(name = "微信认证控制器")
@RestController
@RequestMapping("/wechat/openId/")
@RequestMapping("/wechat/auth/")
@RequiredArgsConstructor
public class WechatOpenIdController {
private final WeChatOpenIdService wechatOpenIdService;
public class WechatAuthController {
private final WeChatAuthService wechatAuthService;
@Operation(summary = "返回获取OpenId授权页面地址和标识码")
@PostMapping("/generateAuthUrl")
public ResResult<WeChatAuthUrlResult> generateAuthUrl(){
return Res.ok(wechatOpenIdService.generateAuthUrl());
public ResResult<AuthUrlResult> generateAuthUrl(){
return Res.ok(wechatAuthService.generateAuthUrl());
}
@Operation(summary = "根据标识码查询OpenId")
@GetMapping("/queryOpenId")
public ResResult<WeChatOpenIdResult> queryOpenId(String code){
return Res.ok(wechatOpenIdService.queryOpenId(code));
public ResResult<OpenIdResult> queryOpenId(String code){
return Res.ok(wechatAuthService.queryOpenId(code));
}
}

View File

@@ -0,0 +1,23 @@
package cn.daxpay.single.param.assist;
import cn.daxpay.single.param.PaymentCommonParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 获取微信AccessToken参数
* @author xxm
* @since 2024/6/16
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "获取支付宝AccessToken参数")
public class AliPayAccessTokenParam extends PaymentCommonParam {
@Schema(description = "支付宝认证code")
private String code;
}

View File

@@ -0,0 +1,28 @@
package cn.daxpay.single.param.assist;
import cn.daxpay.single.param.PaymentCommonParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 构造微信OAuth2授权的url链接
* @author xxm
* @since 2024/6/16
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "构造微信OAuth2授权的url链接")
public class AliPayAuthUrlParam extends PaymentCommonParam {
@Schema(description = "回调地址")
private String url;
/**
* 重定向后会带上state参数开发者可以填写a-zA-Z0-9的参数值最多128字节
*/
@Schema(description = "重定向后原样带回,可以为空")
private String state;
}

View File

@@ -7,14 +7,14 @@ import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 构造oauth2授权的url
* 构造微信OAuth2授权的url
* @author xxm
* @since 2024/2/10
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "构造oauth2授权的url")
@Schema(title = "构造微信OAuth2授权的url")
public class WxAuthUrlParam extends PaymentCommonParam {
@Schema(description = "回调地址")

View File

@@ -4,7 +4,8 @@ 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.core.extra.AliPayAuthService;
import cn.daxpay.single.service.core.extra.WeChatAuthService;
import cn.daxpay.single.service.sdk.union.api.UnionPayKit;
import com.egzosn.pay.union.api.UnionPayConfigStorage;
import com.ijpay.alipay.AliPayApi;
@@ -37,12 +38,14 @@ public class PayCallbackController {
private final AliPayCallbackService aliPayCallbackService;
private final WeChatOpenIdService wechatOpenIdService;
private final WeChatAuthService wechatAuthService;
private final WeChatPayCallbackService weChatPayCallbackService;
private final UnionPayCallbackService unionPayCallbackService;
private final AliPayAuthService aliPayAuthService;
@SneakyThrows
@Operation(summary = "支付宝信息回调")
@PostMapping("/alipay")
@@ -51,6 +54,15 @@ public class PayCallbackController {
return aliPayCallbackService.callback(stringStringMap);
}
@Operation(summary = "支付宝认证授权回调")
@GetMapping("/alipay/auth/{code}")
public ModelAndView wechatCallback(@RequestParam("code") String authCode, @PathVariable("code") String code){
aliPayAuthService.authCallback(authCode, code);
// 调用页面自动关闭
return new ModelAndView("forward:/h5/openIdCallbackClose.html");
}
@SneakyThrows
@Operation(summary = "微信支付信息回调")
@PostMapping("/wechat")
@@ -60,10 +72,10 @@ public class PayCallbackController {
return weChatPayCallbackService.callback(params);
}
@Operation(summary = "微信获取OpenId授权回调方法")
@GetMapping("/wechat/openId/{code}")
@Operation(summary = "微信宝认证授权回调")
@GetMapping("/wechat/auth/{code}")
public ModelAndView wxAuthCallback(@RequestParam("code") String authCode, @PathVariable("code") String code){
wechatOpenIdService.authCallback(authCode, code);
wechatAuthService.authCallback(authCode, code);
// 调用页面自动关闭
return new ModelAndView("forward:/h5/openIdCallbackClose.html");
}

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.extra.WeChatOpenIdService;
import cn.daxpay.single.service.core.extra.WeChatAuthService;
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 WeChatOpenIdService wechatOpenIdService;
private final WeChatAuthService wechatAuthService;
@PaymentVerify
@InitPaymentContext(PaymentApiCode.GET_WX_AUTH_URL)
@Operation(summary = "获取微信OAuth2授权链接")
@PostMapping("/getWxAuthUrl")
public DaxResult<WxAuthUrlResult> getWxAuthUrl(@RequestBody WxAuthUrlParam param){
return DaxRes.ok(wechatOpenIdService.getWxAuthUrl(param));
return DaxRes.ok(wechatAuthService.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(wechatOpenIdService.getWxAccessToken(param));
return Res.ok(wechatAuthService.getWxAccessToken(param));
}
}

View File

@@ -0,0 +1,21 @@
<script src = "https://gw.alipayobjects.com/as/g/h5-lib/alipayjsapi/3.1.1/alipayjsapi.min.js"> </script>
<script>
const params = new URLSearchParams(window.location.search);
// 获取AppId
const appid = params.get("appId");
// 回调地址
const redirectUrl = params.get("redirectUrl");
// 重定向后原样带回,可以为空
const state = params.get("state");
ap.getAuthCode ({
appId : appid ,
scopes : ['auth_base'],
},function(res){
const authCode = res.authCode;
// 跳转到回调地址
window.location.replace(`${redirectUrl}?state=${state}&code=${authCode}`)
// window.location.href = `${redirectUrl}?code=${authCode}`;
});
</script>

View File

@@ -19,5 +19,9 @@
setTimeout(function (){
WeixinJSBridge.call('closeWindow')
},500)
// 支付宝浏览器关闭
setTimeout(function (){
AlipayJSBridge.call('closeWebview')
},500)
</script>
</body>

View File

@@ -19,7 +19,7 @@ public class AliPayWay {
// 支付方式
private static final List<PayMethodEnum> PAY_WAYS = Arrays.asList(PayMethodEnum.WAP, PayMethodEnum.APP, PayMethodEnum.WEB,
PayMethodEnum.QRCODE, PayMethodEnum.BARCODE);
PayMethodEnum.QRCODE, PayMethodEnum.BARCODE, PayMethodEnum.JSAPI);
/**
* 根据编码获取

View File

@@ -0,0 +1,130 @@
package cn.daxpay.single.service.core.extra;
import cn.bootx.platform.common.core.exception.BizException;
import cn.bootx.platform.common.redis.RedisClient;
import cn.daxpay.single.service.core.channel.alipay.entity.AliPayConfig;
import cn.daxpay.single.service.core.channel.alipay.service.AliPayConfigService;
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.AuthUrlResult;
import cn.daxpay.single.service.dto.extra.OpenIdResult;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayConfig;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipaySystemOauthTokenRequest;
import com.alipay.api.response.AlipaySystemOauthTokenResponse;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Objects;
/**
* 支付宝认证服务类
* @author xxm
* @since 2024/6/16
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class AliPayAuthService {
public static final String OPEN_ID_KEY_PREFIX = "daxpay:wechat:openId:";
private final AliPayConfigService aliPayConfigService;
private final PlatformConfigService platformsConfigService;
private final RedisClient redisClient;
/**
* 根据传入的标识码code生成一个用于微信授权页面的链接
*/
public AuthUrlResult generateAuthUrl() {
PlatformConfig platformConfig = platformsConfigService.getConfig();
AliPayConfig aliPayConfig = aliPayConfigService.getConfig();
String code = RandomUtil.randomString(10);
// 构建出授权成功后重定向页面链接
String redirectUrl = StrUtil.format("{}/callback/pay/alipay/auth/{}", platformConfig.getWebsiteUrl(), code);
// 构造出授权页地址
String url = StrUtil.format("{}/h5/alipayAuth.html?appId={}&redirectUrl={}",
platformConfig.getWebsiteUrl(), aliPayConfig.getAppId(), redirectUrl);
// 写入Redis, 五分钟有效期
redisClient.setWithTimeout(OPEN_ID_KEY_PREFIX + code, "", 5*60*1000L);
return new AuthUrlResult()
.setCode(code)
.setAuthUrl(url);
}
/**
* 微信授权回调页面, 通过获取到authCode获取到OpenId, 存到到Redis中对应code关联的键值对中
* @param authCode 微信返回的授权码
* @param code 标识码
*/
public void authCallback(String authCode, String code) {
// 获取OpenId
String openId = this.getOpenId(authCode);
// 写入Redis
redisClient.setWithTimeout(OPEN_ID_KEY_PREFIX + code, openId, 60*1000L);
}
/**
* 通过标识码轮训获取OpenId
*/
public OpenIdResult queryOpenId(String code) {
// 从redis中获取
String openId = redisClient.get(OPEN_ID_KEY_PREFIX + code);
// 不为空存在
if (StrUtil.isNotBlank(openId)){
return new OpenIdResult().setOpenId(openId).setStatus("success");
}
// 为空获取中
if (Objects.equals(openId, "")){
return new OpenIdResult().setStatus("pending");
}
// null不存在
return new OpenIdResult().setStatus("fail");
}
@SneakyThrows
public String getOpenId(String authCode) {
// 初始化SDK
AlipayClient alipayClient = new DefaultAlipayClient(this.getAlipayConfig());
// 构造请求参数以调用接口
AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
// 设置授权码
request.setCode(authCode);
// 设置授权方式
request.setGrantType("authorization_code");
AlipaySystemOauthTokenResponse response = alipayClient.execute(request);
if (!response.isSuccess()) {
log.warn("获取支付宝OpenId失败,原因:{}", response.getSubMsg());
throw new BizException("获取支付宝OpenId失败");
}
return response.getOpenId();
}
/**
* 获取支付宝SDK的配置
*/
private AlipayConfig getAlipayConfig(){
AliPayConfig aliPayConfig = aliPayConfigService.getConfig();
String privateKey = aliPayConfig.getPrivateKey();
String alipayPublicKey =aliPayConfig.getAlipayPublicKey();
AlipayConfig alipayConfig = new AlipayConfig();
alipayConfig.setServerUrl(aliPayConfig.getServerUrl());
alipayConfig.setAppId(aliPayConfig.getAppId());
alipayConfig.setPrivateKey(privateKey);
alipayConfig.setFormat("json");
alipayConfig.setAlipayPublicKey(alipayPublicKey);
alipayConfig.setCharset("UTF-8");
alipayConfig.setSignType(aliPayConfig.getSignType());
return alipayConfig;
}
}

View File

@@ -9,8 +9,8 @@ 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.daxpay.single.service.dto.extra.AuthUrlResult;
import cn.daxpay.single.service.dto.extra.OpenIdResult;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
@@ -33,7 +33,7 @@ import java.util.Objects;
@Slf4j
@Service
@RequiredArgsConstructor
public class WeChatOpenIdService {
public class WeChatAuthService {
public static final String OPEN_ID_KEY_PREFIX = "daxpay:wechat:openId:";
@@ -66,18 +66,18 @@ public class WeChatOpenIdService {
}
/**
* 根据传入的标识码code生成一个用于微信授权页面的链接
* 生成一个用于微信授权页面的链接和code标识
*/
public WeChatAuthUrlResult generateAuthUrl() {
public AuthUrlResult generateAuthUrl() {
PlatformConfig config = platformsConfigService.getConfig();
String code = RandomUtil.randomString(10);
// 构建出授权后重定向页面链接
String redirectUrl = StrUtil.format("{}/callback/pay/wechat/openId/{}", config.getWebsiteUrl(), code);
String redirectUrl = StrUtil.format("{}/callback/pay/wechat/auth/{}", 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()
return new AuthUrlResult()
.setCode(code)
.setAuthUrl(result.getUrl());
}
@@ -99,19 +99,19 @@ public class WeChatOpenIdService {
/**
* 通过标识码轮训获取OpenId
*/
public WeChatOpenIdResult queryOpenId(String code) {
public OpenIdResult queryOpenId(String code) {
// 从redis中获取
String openId = redisClient.get(OPEN_ID_KEY_PREFIX + code);
// 不为空存在
if (StrUtil.isNotBlank(openId)){
return new WeChatOpenIdResult().setOpenId(openId).setStatus("success");
return new OpenIdResult().setOpenId(openId).setStatus("success");
}
// 为空获取中
if (Objects.equals(openId, "")){
return new WeChatOpenIdResult().setStatus("pending");
return new OpenIdResult().setStatus("pending");
}
// null不存在
return new WeChatOpenIdResult().setStatus("fail");
return new OpenIdResult().setStatus("fail");
}

View File

@@ -12,7 +12,7 @@ import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
@Schema(title = "微信授权链接和查询标识返回类")
public class WeChatAuthUrlResult {
public class AuthUrlResult {
/** 授权访问链接 */
@Schema(description = "授权访问链接")

View File

@@ -12,7 +12,7 @@ import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
@Schema(title = "微信OpenId查询结果")
public class WeChatOpenIdResult {
public class OpenIdResult {
@Schema(description = "OpenId")
private String openId;