支付
This commit is contained in:
@@ -18,6 +18,8 @@ public class RedisConstant {
|
|||||||
|
|
||||||
//企业微信TOKEN
|
//企业微信TOKEN
|
||||||
public final static String REDIS_KEY_QIYE_WECHAT_ACCESS_TOKEN = "qiyewx:token:";
|
public final static String REDIS_KEY_QIYE_WECHAT_ACCESS_TOKEN = "qiyewx:token:";
|
||||||
|
// 微信公众号TOKEN
|
||||||
|
public final static String REDIS_KEY_WECHAT_ACCESS_TOKEN = "wechat:token:";
|
||||||
|
|
||||||
//健步走小程序TOKEN
|
//健步走小程序TOKEN
|
||||||
public final static String REDIS_KEY_WE_APP_ACCESS_TOKEN = "weapp:token:";
|
public final static String REDIS_KEY_WE_APP_ACCESS_TOKEN = "weapp:token:";
|
||||||
|
|||||||
@@ -49,6 +49,11 @@ public class PaymentTransaction extends BaseModel {
|
|||||||
@ColDefine(type = ColType.VARCHAR,width = 30)
|
@ColDefine(type = ColType.VARCHAR,width = 30)
|
||||||
private String tradingType;
|
private String tradingType;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("交易状态")
|
||||||
|
@ColDefine(type = ColType.VARCHAR,width = 50)
|
||||||
|
private String tradingState;
|
||||||
|
|
||||||
@Column
|
@Column
|
||||||
@Comment("用户ID")
|
@Comment("用户ID")
|
||||||
@ColDefine(type = ColType.VARCHAR,width = 32)
|
@ColDefine(type = ColType.VARCHAR,width = 32)
|
||||||
|
|||||||
@@ -16,6 +16,12 @@ import lombok.NoArgsConstructor;
|
|||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
public class WxPayV3Bean {
|
public class WxPayV3Bean {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务号
|
||||||
|
* appid: wxf12fa6175ec7c75f
|
||||||
|
* app_secret: f65bbb41d3de4a93c8c93d8e2fd50369
|
||||||
|
*/
|
||||||
|
|
||||||
private String appId;
|
private String appId;
|
||||||
private String keyPath;
|
private String keyPath;
|
||||||
private String publicKeyPath;
|
private String publicKeyPath;
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package io.v.nutz.base.payment.wxpay.controller;
|
||||||
|
|
||||||
|
import io.v.nutz.base.annontation.ViReturn;
|
||||||
|
import io.v.nutz.base.payment.wxpay.service.WxPayCommonService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.nutz.ioc.loader.annotation.Inject;
|
||||||
|
import org.nutz.ioc.loader.annotation.IocBean;
|
||||||
|
import org.nutz.mvc.annotation.At;
|
||||||
|
import org.nutz.mvc.annotation.Ok;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @version 1.0
|
||||||
|
* @Author zzr
|
||||||
|
* @name:WxPayCommonController
|
||||||
|
* @Date 2025/3/10 19:28
|
||||||
|
* @注释
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@IocBean
|
||||||
|
@Ok("json:full")
|
||||||
|
@At("/platform/wxPay")
|
||||||
|
public class WxPayCommonController {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private WxPayCommonService wxPayCommonService;
|
||||||
|
|
||||||
|
|
||||||
|
@At
|
||||||
|
@Ok("void")
|
||||||
|
@ViReturn
|
||||||
|
public void notifyMessage(HttpServletRequest request, HttpServletResponse response){
|
||||||
|
wxPayCommonService.payNotify(request, response);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -66,4 +66,12 @@ public interface WxPayCommonService {
|
|||||||
* @param response 响应
|
* @param response 响应
|
||||||
*/
|
*/
|
||||||
void refundNotify(HttpServletRequest request, HttpServletResponse response);
|
void refundNotify(HttpServletRequest request, HttpServletResponse response);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取openId
|
||||||
|
* @param code 用户授权后的Code,可以获取用户信息
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
String getOpenId(String code);
|
||||||
}
|
}
|
||||||
|
|||||||
+41
-3
@@ -20,18 +20,24 @@ import com.ijpay.wxpay.enums.WxDomainEnum;
|
|||||||
import com.ijpay.wxpay.enums.v3.BasePayApiEnum;
|
import com.ijpay.wxpay.enums.v3.BasePayApiEnum;
|
||||||
import com.ijpay.wxpay.model.v3.*;
|
import com.ijpay.wxpay.model.v3.*;
|
||||||
import io.v.nutz.base.payment.enums.TradingChannelEnum;
|
import io.v.nutz.base.payment.enums.TradingChannelEnum;
|
||||||
|
import io.v.nutz.base.payment.enums.TradingStateEnum;
|
||||||
import io.v.nutz.base.payment.enums.TradingTypeEnum;
|
import io.v.nutz.base.payment.enums.TradingTypeEnum;
|
||||||
import io.v.nutz.base.payment.enums.WxPayResultCode;
|
import io.v.nutz.base.payment.enums.WxPayResultCode;
|
||||||
import io.v.nutz.base.payment.model.PaymentTransaction;
|
import io.v.nutz.base.payment.model.PaymentTransaction;
|
||||||
import io.v.nutz.base.payment.wxpay.bean.WxPayV3Bean;
|
import io.v.nutz.base.payment.wxpay.bean.WxPayV3Bean;
|
||||||
import io.v.nutz.base.payment.wxpay.service.WxPayCommonService;
|
import io.v.nutz.base.payment.wxpay.service.WxPayCommonService;
|
||||||
|
import io.v.nutz.base.payment.wxpay.util.WeChatUtils;
|
||||||
import io.v.nutz.base.payment.wxpay.vo.WxPayNeedParamVo;
|
import io.v.nutz.base.payment.wxpay.vo.WxPayNeedParamVo;
|
||||||
import io.v.nutz.base.result.Result;
|
import io.v.nutz.base.result.Result;
|
||||||
import io.v.nutz.sys.models.User;
|
import io.v.nutz.sys.models.User;
|
||||||
import io.v.nutz.sys.services.SysUserService;
|
import io.v.nutz.sys.services.SysUserService;
|
||||||
import io.v.nutz.web.commons.utils.ShiroUtil;
|
import io.v.nutz.web.commons.utils.ShiroUtil;
|
||||||
|
import io.v.nutz.zhgh.memberCost.model.MemberPaymentInfo;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||||
|
import org.nutz.dao.Chain;
|
||||||
import org.nutz.dao.Cnd;
|
import org.nutz.dao.Cnd;
|
||||||
|
import org.nutz.ioc.aop.Aop;
|
||||||
import org.nutz.ioc.loader.annotation.Inject;
|
import org.nutz.ioc.loader.annotation.Inject;
|
||||||
import org.nutz.ioc.loader.annotation.IocBean;
|
import org.nutz.ioc.loader.annotation.IocBean;
|
||||||
import org.nutz.lang.random.R;
|
import org.nutz.lang.random.R;
|
||||||
@@ -85,6 +91,8 @@ public class WxPayCommonServiceImpl implements WxPayCommonService {
|
|||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private SysUserService sysUserService;
|
private SysUserService sysUserService;
|
||||||
|
@Inject
|
||||||
|
private WeChatUtils weChatUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 微信扫描二维码支付
|
* 微信扫描二维码支付
|
||||||
@@ -93,6 +101,7 @@ public class WxPayCommonServiceImpl implements WxPayCommonService {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
public Result nativePay(WxPayNeedParamVo paramVo) {
|
public Result nativePay(WxPayNeedParamVo paramVo) {
|
||||||
try {
|
try {
|
||||||
// 商户订单号,存储当前用户的订单
|
// 商户订单号,存储当前用户的订单
|
||||||
@@ -148,6 +157,7 @@ public class WxPayCommonServiceImpl implements WxPayCommonService {
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
public Result jsApiPay(WxPayNeedParamVo paramVo) {
|
public Result jsApiPay(WxPayNeedParamVo paramVo) {
|
||||||
try {
|
try {
|
||||||
// 商户订单号,存储当前用户的订单
|
// 商户订单号,存储当前用户的订单
|
||||||
@@ -187,7 +197,7 @@ public class WxPayCommonServiceImpl implements WxPayCommonService {
|
|||||||
Map<String, String> map = WxPayKit.jsApiCreateSign(WX_PAY_V3_BEAN.getAppId(), prepayId, WX_PAY_V3_BEAN.getKeyPath());
|
Map<String, String> map = WxPayKit.jsApiCreateSign(WX_PAY_V3_BEAN.getAppId(), prepayId, WX_PAY_V3_BEAN.getKeyPath());
|
||||||
log.info("唤起支付参数:{}", map);
|
log.info("唤起支付参数:{}", map);
|
||||||
// 唤起支付成功,向数据库插入订单参数
|
// 唤起支付成功,向数据库插入订单参数
|
||||||
|
insertTradingOrder(paramVo, outTradeNo, body);
|
||||||
return Result.success(JSONUtil.toJsonStr(map));
|
return Result.success(JSONUtil.toJsonStr(map));
|
||||||
}
|
}
|
||||||
// return JSONUtil.toJsonStr(response);
|
// return JSONUtil.toJsonStr(response);
|
||||||
@@ -200,6 +210,7 @@ public class WxPayCommonServiceImpl implements WxPayCommonService {
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
public Result wapPay(WxPayNeedParamVo paramVo) {
|
public Result wapPay(WxPayNeedParamVo paramVo) {
|
||||||
try {
|
try {
|
||||||
// 商户订单号,存储当前用户的订单
|
// 商户订单号,存储当前用户的订单
|
||||||
@@ -238,10 +249,11 @@ public class WxPayCommonServiceImpl implements WxPayCommonService {
|
|||||||
log.info("verifySignature: {}", verifySignature);
|
log.info("verifySignature: {}", verifySignature);
|
||||||
if (response.getStatus() == OK && verifySignature) {
|
if (response.getStatus() == OK && verifySignature) {
|
||||||
String body = response.getBody();
|
String body = response.getBody();
|
||||||
|
insertTradingOrder(paramVo, outTradeNo, body);
|
||||||
return Result.success(JSONUtil.toJsonStr(body));
|
return Result.success(JSONUtil.toJsonStr(body));
|
||||||
}
|
}
|
||||||
// return JSONUtil.toJsonStr(response);
|
// return JSONUtil.toJsonStr(response);
|
||||||
return null;
|
return Result.error();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("系统异常", e);
|
log.error("系统异常", e);
|
||||||
return Result.error(e.getMessage());
|
return Result.error(e.getMessage());
|
||||||
@@ -276,8 +288,21 @@ public class WxPayCommonServiceImpl implements WxPayCommonService {
|
|||||||
// WX_PAY_V3_BEAN.getApiKey3(), WX_PAY_V3_BEAN.getPlatformCertPath());
|
// WX_PAY_V3_BEAN.getApiKey3(), WX_PAY_V3_BEAN.getPlatformCertPath());
|
||||||
// }
|
// }
|
||||||
log.info("支付通知明文 {}", plainText);
|
log.info("支付通知明文 {}", plainText);
|
||||||
|
|
||||||
if (StrUtil.isNotEmpty(plainText)) {
|
if (StrUtil.isNotEmpty(plainText)) {
|
||||||
|
// 支付结果
|
||||||
|
JSONObject jsonResult = JSONUtil.parseObj(plainText);
|
||||||
|
// 获取缴费记录,并修改缴费结果
|
||||||
|
PaymentTransaction transaction = sysUserService.dao().fetch(PaymentTransaction.class, Cnd.where("outTradeNo", "=", jsonResult.getStr("out_trade_no")));
|
||||||
|
transaction.setTradingState(TradingStateEnum.PAY_SUCCESS.getTrdingStateName());
|
||||||
|
transaction.setTradingOrderNo(jsonResult.getStr("transaction_id"));
|
||||||
|
sysUserService.dao().update(transaction);
|
||||||
|
|
||||||
|
sysUserService.dao().update(MemberPaymentInfo.class,
|
||||||
|
Chain.make("isPayment", true).add("paymentTime", DateUtil.date(jsonResult.getLong("success_time")))
|
||||||
|
.add("paymentMoney", String.valueOf(transaction.getTradingOrderNo())),
|
||||||
|
Cnd.where("payId", "=", transaction.getId())
|
||||||
|
);
|
||||||
|
|
||||||
response.setStatus(200);
|
response.setStatus(200);
|
||||||
map.put("code", "SUCCESS");
|
map.put("code", "SUCCESS");
|
||||||
map.put("message", "SUCCESS");
|
map.put("message", "SUCCESS");
|
||||||
@@ -285,7 +310,10 @@ public class WxPayCommonServiceImpl implements WxPayCommonService {
|
|||||||
response.setStatus(500);
|
response.setStatus(500);
|
||||||
map.put("code", "ERROR");
|
map.put("code", "ERROR");
|
||||||
map.put("message", "签名错误");
|
map.put("message", "签名错误");
|
||||||
|
log.error("签名错误");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 支付回调,返回给微信,自己也接收不到结果,这里的返回完全没用
|
||||||
response.setHeader("Content-type", ContentType.JSON.toString());
|
response.setHeader("Content-type", ContentType.JSON.toString());
|
||||||
response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
|
response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
|
||||||
response.flushBuffer();
|
response.flushBuffer();
|
||||||
@@ -404,6 +432,12 @@ public class WxPayCommonServiceImpl implements WxPayCommonService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getOpenId(String code) {
|
||||||
|
JSONObject jsonObject = weChatUtils.getWebAccessTokenAndOpenid(code);
|
||||||
|
return jsonObject.getStr("openid");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 公共方法,获取证书序列号
|
* 公共方法,获取证书序列号
|
||||||
* @return
|
* @return
|
||||||
@@ -522,6 +556,7 @@ public class WxPayCommonServiceImpl implements WxPayCommonService {
|
|||||||
transaction.setUserName(user.getUsername());
|
transaction.setUserName(user.getUsername());
|
||||||
transaction.setUnitName(user.getUnitname());
|
transaction.setUnitName(user.getUnitname());
|
||||||
transaction.setUnionName(user.getUnionname());
|
transaction.setUnionName(user.getUnionname());
|
||||||
|
transaction.setTradingState(TradingStateEnum.WAITING_FOR_PAYMENT.name());
|
||||||
// 获取订单金额
|
// 获取订单金额
|
||||||
int total = paramVo.getAmount().getTotal();
|
int total = paramVo.getAmount().getTotal();
|
||||||
BigDecimal bigDecimalValue = new BigDecimal(String.valueOf(total));
|
BigDecimal bigDecimalValue = new BigDecimal(String.valueOf(total));
|
||||||
@@ -534,5 +569,8 @@ public class WxPayCommonServiceImpl implements WxPayCommonService {
|
|||||||
transaction.setOpenId(null);
|
transaction.setOpenId(null);
|
||||||
transaction.setResultJson(result);
|
transaction.setResultJson(result);
|
||||||
sysUserService.dao().insert(transaction);
|
sysUserService.dao().insert(transaction);
|
||||||
|
|
||||||
|
sysUserService.dao().update(MemberPaymentInfo.class,
|
||||||
|
Chain.make("payId", transaction.getId()), Cnd.where("id", "=", paramVo.getMemberPaymentInfoId()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
package io.v.nutz.base.payment.wxpay.util;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.http.HttpUtil;
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import io.v.nutz.base.constant.RedisConstant;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.nutz.http.Http;
|
||||||
|
import org.nutz.integration.jedis.RedisService;
|
||||||
|
import org.nutz.ioc.loader.annotation.Inject;
|
||||||
|
import org.nutz.ioc.loader.annotation.IocBean;
|
||||||
|
import org.nutz.json.Json;
|
||||||
|
import org.nutz.lang.util.NutMap;
|
||||||
|
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @version 1.0
|
||||||
|
* @Author zzr
|
||||||
|
* @name:WeChatUtils
|
||||||
|
* @Date 2025/3/10 14:13
|
||||||
|
* @注释
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@IocBean
|
||||||
|
public class WeChatUtils {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private RedisService redisService;
|
||||||
|
|
||||||
|
public static final String APPID = "wxf12fa6175ec7c75f";
|
||||||
|
|
||||||
|
public static final String SECRET = "f65bbb41d3de4a93c8c93d8e2fd50369";
|
||||||
|
|
||||||
|
// 获取code
|
||||||
|
public final static String CODE_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect";
|
||||||
|
|
||||||
|
//获取网页access_token的接口地址
|
||||||
|
public final static String WEB_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=APPSECRET&code=CODE&grant_type=authorization_code";
|
||||||
|
|
||||||
|
|
||||||
|
//获取用户信息地址
|
||||||
|
public final static String USERINFO_URL = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";
|
||||||
|
|
||||||
|
// 获取access_token的接口地址(GET) 限2000(次/天)
|
||||||
|
public final static String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
|
||||||
|
// 发送模板消息
|
||||||
|
public static final String SEND_MESSAGE_URL = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成获取code的url
|
||||||
|
*
|
||||||
|
* @param redirectUrl
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getCodeUrl(String redirectUrl, String state) {
|
||||||
|
String requestUrl = null;
|
||||||
|
state = state == null ? "" : state;
|
||||||
|
try {
|
||||||
|
requestUrl = CODE_URL.replace("APPID", APPID).replace("REDIRECT_URI", URLEncoder.encode(redirectUrl, "UTF-8")).replace("STATE", state);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return requestUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取微信网页access_token 和 openid
|
||||||
|
*
|
||||||
|
* @param code
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public JSONObject getWebAccessTokenAndOpenid(String code) {
|
||||||
|
String requestUrl = WEB_ACCESS_TOKEN_URL.replace("APPID", APPID).replace("APPSECRET", SECRET).replace("CODE", code);
|
||||||
|
return JSONUtil.parseObj(HttpUtil.post(requestUrl, new JSONObject()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网页授权作用域为snsapi_userinfo,则此时开发者可以通过access_token和openid拉取用户信息了
|
||||||
|
* <a href="https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html">...</a>
|
||||||
|
*
|
||||||
|
* @param webAccessToken
|
||||||
|
* @param openid
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public JSONObject getUserInfo(String webAccessToken, String openid) {
|
||||||
|
String requestUrl = USERINFO_URL.replace("ACCESS_TOKEN", webAccessToken).replace("OPENID", openid);
|
||||||
|
return JSONUtil.parseObj(HttpUtil.post(requestUrl, new JSONObject()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取access_token 先从Redis获取,如果没有再调微信获取并存Redis
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getAccessToken() {
|
||||||
|
String token = redisService.get(RedisConstant.REDIS_KEY_WECHAT_ACCESS_TOKEN);
|
||||||
|
if (StrUtil.isNotBlank(token)) {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
String accessTokenUrl = ACCESS_TOKEN_URL.formatted(APPID, SECRET);
|
||||||
|
String accessToken = Json.fromJson(NutMap.class, Http.get(accessTokenUrl).getContent()).getString("access_token");
|
||||||
|
redisService.setex(RedisConstant.REDIS_KEY_WECHAT_ACCESS_TOKEN, 60 * 60 * 2, accessToken);
|
||||||
|
return accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -36,4 +36,9 @@ public class WxPayNeedParamVo {
|
|||||||
* 其余参数可忽略
|
* 其余参数可忽略
|
||||||
*/
|
*/
|
||||||
private Payer payer;
|
private Payer payer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 会员支付信息ID,必填
|
||||||
|
*/
|
||||||
|
private String memberPaymentInfoId;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,37 @@
|
|||||||
package io.v.nutz.sys.controllers.platform.sys;
|
package io.v.nutz.sys.controllers.platform.sys;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.http.HttpUtil;
|
||||||
|
import cn.hutool.socket.SocketUtil;
|
||||||
|
import io.v.nutz.base.payment.wxpay.util.WeChatUtils;
|
||||||
|
import io.v.nutz.base.utils.LoginUtil;
|
||||||
|
import io.v.nutz.sys.models.Sys_user;
|
||||||
|
import io.v.nutz.sys.services.SysUserService;
|
||||||
|
import io.v.nutz.web.commons.base.Globals;
|
||||||
|
import io.v.nutz.web.commons.slog.SLogService;
|
||||||
|
import org.apache.shiro.authc.AuthenticationException;
|
||||||
|
import org.apache.shiro.authc.AuthenticationToken;
|
||||||
|
import org.apache.shiro.authc.LockedAccountException;
|
||||||
|
import org.apache.shiro.authc.UnknownAccountException;
|
||||||
|
import org.nutz.dao.Cnd;
|
||||||
|
import org.nutz.dao.Dao;
|
||||||
|
import org.nutz.integration.jedis.RedisService;
|
||||||
|
import org.nutz.ioc.loader.annotation.Inject;
|
||||||
import org.nutz.ioc.loader.annotation.IocBean;
|
import org.nutz.ioc.loader.annotation.IocBean;
|
||||||
|
import org.nutz.json.Json;
|
||||||
|
import org.nutz.lang.util.NutMap;
|
||||||
|
import org.nutz.mvc.annotation.At;
|
||||||
|
import org.nutz.mvc.annotation.Attr;
|
||||||
|
import org.nutz.mvc.annotation.Ok;
|
||||||
|
import org.nutz.mvc.annotation.Param;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 微信公众号
|
* 微信公众号
|
||||||
@@ -9,5 +40,167 @@ import org.nutz.ioc.loader.annotation.IocBean;
|
|||||||
* @date 2023/07/06
|
* @date 2023/07/06
|
||||||
*/
|
*/
|
||||||
@IocBean
|
@IocBean
|
||||||
|
@At("/platform/wechat")
|
||||||
public class SysWechatController {
|
public class SysWechatController {
|
||||||
|
|
||||||
|
private static String ACCESS_TOKEN_URL = "https://%s/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code";
|
||||||
|
|
||||||
|
private static final String APPID = "wxf12fa6175ec7c75f";
|
||||||
|
|
||||||
|
private static final String SECRET = "f65bbb41d3de4a93c8c93d8e2fd50369";
|
||||||
|
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private Dao dao;
|
||||||
|
@Inject
|
||||||
|
private WeChatUtils weChatUtils;
|
||||||
|
@Inject
|
||||||
|
private SysUserService sysUserService;
|
||||||
|
@Inject
|
||||||
|
private RedisService redisService;
|
||||||
|
@Inject
|
||||||
|
private SLogService sLogService;
|
||||||
|
@Inject
|
||||||
|
private LoginUtil loginUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* oauth
|
||||||
|
*
|
||||||
|
* @return {@link String}
|
||||||
|
*/
|
||||||
|
@At
|
||||||
|
@Ok("re")
|
||||||
|
public String oauth(@Param("redirectUrl") String redirectUrl) {
|
||||||
|
String redirect = Globals.AppDomain + "/platform/wechat/getCode";
|
||||||
|
if (StrUtil.isNotBlank(redirect)) {
|
||||||
|
redirect += "?redirect=" + redirect;
|
||||||
|
}
|
||||||
|
String codeUrl = weChatUtils.getCodeUrl(redirect, "");
|
||||||
|
return "redirect:" + codeUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取code 登录用户
|
||||||
|
*
|
||||||
|
* @param code 代码
|
||||||
|
* @return {@link String}
|
||||||
|
*/
|
||||||
|
@At
|
||||||
|
@Ok("re")
|
||||||
|
public String getCode(@Param("code") String code, @Param("redirect") String redirect, HttpSession session, HttpServletRequest request) {
|
||||||
|
String host;
|
||||||
|
try {
|
||||||
|
Socket socket = SocketUtil.connect("api.weixin.qq.com", 443, 1000);
|
||||||
|
if (socket.isConnected()) {
|
||||||
|
host = "api.weixin.qq.com";
|
||||||
|
} else {
|
||||||
|
InetAddress inetAddress = InetAddress.getLocalHost();
|
||||||
|
host = inetAddress.getHostAddress();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
host = "api.weixin.qq.com";
|
||||||
|
}
|
||||||
|
String tokenUrl = ACCESS_TOKEN_URL.formatted(host, APPID, SECRET, code);
|
||||||
|
|
||||||
|
NutMap resMap = Json.fromJson(NutMap.class, HttpUtil.get(tokenUrl));
|
||||||
|
String openid = resMap.getString("openid");
|
||||||
|
Sys_user sysUser = dao.fetch(Sys_user.class, Cnd.where("openId", "=", openid));
|
||||||
|
|
||||||
|
if (sysUser == null) {
|
||||||
|
String s = Globals.AppDomain + "/platform/wechat/doLogin?openid=" + openid;
|
||||||
|
if (StrUtil.isNotBlank(redirect)) {
|
||||||
|
s += "&redirect=" + URLEncoder.encode(redirect, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
// return ">>:" + Globals.CasAddress + "/login?service=" + URLEncoder.encode(s, StandardCharsets.UTF_8);
|
||||||
|
return ">>:" + s;
|
||||||
|
}
|
||||||
|
|
||||||
|
String loginName = Optional.ofNullable(sysUser).map(x -> x.getLoginname()).orElse(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
loginUtil.doLogin(loginName, request, session, LoginUtil.LoginOrigin.WEI_XIN);
|
||||||
|
if (StrUtil.isNotBlank(redirect)) {
|
||||||
|
return ">>:" + redirect;
|
||||||
|
}
|
||||||
|
return ">>:" + Globals.AppDomain + "/platform/home";
|
||||||
|
} catch (LockedAccountException e) {
|
||||||
|
request.setAttribute("errMsg", "帐号被锁定,请5分钟后再试");
|
||||||
|
return ">>:" + Globals.AppDomain + "/platform/home/LockedAccountError";
|
||||||
|
} catch (UnknownAccountException e) {
|
||||||
|
request.setAttribute("errMsg", "系统中没有此用户");
|
||||||
|
return ">>:" + Globals.AppDomain + "/error/UnknownAccountError.html";
|
||||||
|
} catch (AuthenticationException e) {
|
||||||
|
request.setAttribute("errMsg", "系统中没有此用户");
|
||||||
|
return ">>:" + Globals.AppDomain + "/error/UnknownAccountError.html";
|
||||||
|
} catch (Exception e) {
|
||||||
|
request.setAttribute("errMsg", "用户名或密码错误");
|
||||||
|
return ">>:" + Globals.AppDomain + "/error/500.html";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@At
|
||||||
|
@Ok("re")
|
||||||
|
public Object doLogin(@Attr("platformLoginToken") AuthenticationToken token,
|
||||||
|
HttpServletRequest request, HttpSession session,
|
||||||
|
@Param("redirect") String redirect, @Param("openid") String openid) {
|
||||||
|
// int errCount = NumberUtils.toInt(Strings.sNull(SecurityUtils.getSubject().getSession(true).getAttribute("platformErrCount")));
|
||||||
|
// try {
|
||||||
|
// loginUtil.doLogin(token, request, session, LoginUtil.LoginOrigin.WEI_XIN);
|
||||||
|
//
|
||||||
|
// return Result.success("login.success");
|
||||||
|
// } catch (CaptchaIncorrectException e) {
|
||||||
|
// return Result.error(1, "login.error.captcha");
|
||||||
|
// } catch (CaptchaEmptyException e) {
|
||||||
|
// return Result.error(2, "login.error.captcha");
|
||||||
|
// } catch (LockedAccountException e) {
|
||||||
|
// return Result.error(3, "login.error.locked");
|
||||||
|
// } catch (UnknownAccountException e) {
|
||||||
|
// errCount++;
|
||||||
|
// SecurityUtils.getSubject().getSession(true).setAttribute("platformErrCount", errCount);
|
||||||
|
// return Result.error(4, "login.error.user");
|
||||||
|
// } catch (AuthenticationException e) {
|
||||||
|
// errCount++;
|
||||||
|
// SecurityUtils.getSubject().getSession(true).setAttribute("platformErrCount", errCount);
|
||||||
|
// return Result.error(5, "login.error.user");
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// errCount++;
|
||||||
|
// SecurityUtils.getSubject().getSession(true).setAttribute("platformErrCount", errCount);
|
||||||
|
// return Result.error(6, "login.error.system");
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// Assertion assertion = (Assertion) request.getSession().getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION);
|
||||||
|
// if (assertion == null) {
|
||||||
|
// return ">>:" + Globals.AppDomain + "/platform/home/UnknownAccountError";
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// String principal = assertion.getPrincipal().toString();
|
||||||
|
// //工号 手机号 身份证
|
||||||
|
// Sys_user user = dao.fetch(Sys_user.class, Cnd.NEW().and("loginname", "=", principal).or("mobile", "=", principal).or("idcard", "=", principal));
|
||||||
|
// String loginName = Optional.ofNullable(user).map(v -> v.getLoginname()).orElse(null);
|
||||||
|
//
|
||||||
|
// try {
|
||||||
|
// loginUtil.doLogin(loginName, request, session, LoginUtil.LoginOrigin.WEI_XIN);
|
||||||
|
// //绑定openId
|
||||||
|
// if (StrUtil.isNotBlank(openid)) {
|
||||||
|
// dao.update(Sys_user.class, Chain.make("openId", openid), Cnd.where("loginName", "=", loginName));
|
||||||
|
// }
|
||||||
|
// if (StrUtil.isNotBlank(redirect)) {
|
||||||
|
// return ">>:" + redirect;
|
||||||
|
// }
|
||||||
|
// return ">>:" + Globals.AppDomain + "/mobile/index";
|
||||||
|
// } catch (LockedAccountException e) {
|
||||||
|
// request.setAttribute("errMsg", "帐号被锁定,请5分钟后再试");
|
||||||
|
// return ">>:" + Globals.AppDomain + "/platform/home/LockedAccountError";
|
||||||
|
// } catch (UnknownAccountException e) {
|
||||||
|
// request.setAttribute("errMsg", "系统中没有此用户");
|
||||||
|
// return ">>:" + Globals.AppDomain + "/platform/home/UnknownAccountError";
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// request.setAttribute("errMsg", "用户名或密码错误");
|
||||||
|
// return ">>:" + Globals.AppDomain + "/platform/home/500";
|
||||||
|
// }
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -626,6 +626,11 @@ public class Sys_user extends BaseModel implements Serializable {
|
|||||||
@ColDefine(type = ColType.VARCHAR, width = 300)
|
@ColDefine(type = ColType.VARCHAR, width = 300)
|
||||||
private String weAppOpenid;
|
private String weAppOpenid;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("微信openId")
|
||||||
|
@ColDefine(type = ColType.VARCHAR, width = 300)
|
||||||
|
private String openId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 2021-05-18 end
|
* 2021-05-18 end
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package io.v.nutz.zhgh.memberCost.controller;
|
||||||
|
|
||||||
|
import io.v.nutz.base.payment.wxpay.service.WxPayCommonService;
|
||||||
|
import io.v.nutz.base.payment.wxpay.util.WeChatUtils;
|
||||||
|
import io.v.nutz.zhgh.memberCost.service.MemberPaymentService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
|
import org.nutz.dao.Dao;
|
||||||
|
import org.nutz.integration.jedis.RedisService;
|
||||||
|
import org.nutz.ioc.loader.annotation.Inject;
|
||||||
|
import org.nutz.ioc.loader.annotation.IocBean;
|
||||||
|
import org.nutz.mvc.annotation.At;
|
||||||
|
import org.nutz.mvc.annotation.Ok;
|
||||||
|
import org.nutz.mvc.annotation.Param;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @version 1.0
|
||||||
|
* @Author zzr
|
||||||
|
* @name:MemberPaymentTestController
|
||||||
|
* @Date 2025/3/10 14:55
|
||||||
|
* @注释
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@IocBean
|
||||||
|
@Ok("json:full")
|
||||||
|
@At("/platform/member/payment/test")
|
||||||
|
public class MemberPaymentTestController {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private Dao dao;
|
||||||
|
@Inject
|
||||||
|
private WeChatUtils weChatUtils;
|
||||||
|
@Inject
|
||||||
|
private RedisService redisService;
|
||||||
|
@Inject
|
||||||
|
private WxPayCommonService wxPayCommonService;
|
||||||
|
@Inject
|
||||||
|
private MemberPaymentService memberPaymentService;
|
||||||
|
|
||||||
|
@At("")
|
||||||
|
@Ok("beetl:/platform/membercost/test/index.html")
|
||||||
|
@RequiresPermissions("member.payment.test")
|
||||||
|
public void index() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信jsapi支付需要网页授权认证
|
||||||
|
* @param redirect
|
||||||
|
* @param userId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@At
|
||||||
|
@Ok("re")
|
||||||
|
public String oauth(String redirect, String userId){
|
||||||
|
String url = weChatUtils.getCodeUrl("https://schoolhd.top/platform/wxPay/getCode", userId);
|
||||||
|
redisService.setex("wx_pay_redirect_url", 60 * 60, "https://schoolhd.top/" + redirect);
|
||||||
|
return "redirect:" + url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上面授权后会转到这个地址,然后去改数据库的openId,
|
||||||
|
* 后续对微信做一个免登录
|
||||||
|
* @param code
|
||||||
|
* @param state
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@At
|
||||||
|
@Ok("re")
|
||||||
|
public String getCode(@Param("code") String code, @Param("state") String state){
|
||||||
|
String openId = wxPayCommonService.getOpenId(code);
|
||||||
|
// WxPayNeedParamVo paramVo = new WxPayNeedParamVo();
|
||||||
|
// paramVo.setDescription("支付");
|
||||||
|
// paramVo.setAttach("");
|
||||||
|
// paramVo.setAmount(new Amount().setTotal(1));
|
||||||
|
// paramVo.setPayer(new Payer().setOpenid(openId));
|
||||||
|
// Result result = wxPayCommonService.jsApiPay(paramVo);
|
||||||
|
return null;
|
||||||
|
// return "redirect:" + redisService.get("wx_pay_redirect_url");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
<!--#
|
||||||
|
layout("/mobile/platform.html"){
|
||||||
|
#-->
|
||||||
|
<div id="app" v-cloak>
|
||||||
|
<!-- 微信唤起jsapi支付的HTML方法 -->
|
||||||
|
</div>
|
||||||
|
<script src="https://res2.wx.qq.com/open/js/jweixin-1.4.0.js"></script>
|
||||||
|
<script>
|
||||||
|
function onBridgeReady(json) {
|
||||||
|
// const data = JSON.parse(json)
|
||||||
|
WeixinJSBridge.invoke("getBrandWCPayRequest", JSON.parse(json), function (res) {
|
||||||
|
if (res.err_msg == "get_brand_wcpay_request:ok") {
|
||||||
|
// 使用以上方式判断前端返回,微信团队郑重提示:
|
||||||
|
//res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
new Vue({
|
||||||
|
el: "#app",
|
||||||
|
data() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async initData() {
|
||||||
|
const resp = await $.post("/platform/member/payment/test/getCode")
|
||||||
|
debugger
|
||||||
|
if (resp.code === 0) {
|
||||||
|
if (typeof WeixinJSBridge == "undefined") {
|
||||||
|
if (document.addEventListener) {
|
||||||
|
document.addEventListener("WeixinJSBridgeReady", onBridgeReady(resp.data))
|
||||||
|
} else if (document.attachEvent) {
|
||||||
|
document.attachEvent("WeixinJSBridgeReady", onBridgeReady(resp.data))
|
||||||
|
document.attachEvent("onWeixinJSBridgeReady", onBridgeReady(resp.data))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
onBridgeReady(resp.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
// this.initData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<!--#
|
||||||
|
}
|
||||||
|
#-->
|
||||||
Reference in New Issue
Block a user