网页、小程序支付链接等相关;新增测试鉴权、支付demo类
parent
4887cd8b20
commit
4c161e4aed
26
pom.xml
26
pom.xml
|
|
@ -72,6 +72,32 @@
|
|||
<version>3.1.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.16.10</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>4.3.14.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-redis -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-redis</artifactId>
|
||||
<version>2.0.3.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
package com.weixin.pay.constants;
|
||||
|
||||
/**
|
||||
* 微信基础常量类
|
||||
*
|
||||
* @author yclimb
|
||||
* @date 2018/8/17
|
||||
*/
|
||||
public class WXConstants {
|
||||
|
||||
/**
|
||||
* 对于前端访问返回参数,本例使用string,推荐自主封装json对象
|
||||
*/
|
||||
public static final String SUCCESS = "success";
|
||||
public static final String ERROR = "error";
|
||||
|
||||
/**
|
||||
* 授权作用域 不弹出授权页面,直接跳转,只能获取用户openid
|
||||
**/
|
||||
public static final String OAUTH_BASE_SCOPE = "snsapi_base";
|
||||
|
||||
/**
|
||||
* 授权作用域 弹出授权页面 能获取昵称、头像等信息
|
||||
**/
|
||||
public static final String OAUTH_USERINFO_SCOPE = "snsapi_userinfo";
|
||||
|
||||
/**
|
||||
* 网页授权 重定向后会带上state参数
|
||||
*/
|
||||
public static final String OAUTH_STATE = "xxx";
|
||||
|
||||
/**
|
||||
* 微信全局accessToken
|
||||
*/
|
||||
public static final String WECHAT_ACCESSTOKEN = OAUTH_STATE + ":wx:accessToken:";
|
||||
|
||||
/**
|
||||
* 微信全局accessTokenLock
|
||||
*/
|
||||
public static final String WECHAT_ACCESSTOKEN_LOCK = OAUTH_STATE + ":wx:accessTokenLock:";
|
||||
|
||||
/**
|
||||
* 微信网页授权openid,时限:7200秒
|
||||
*/
|
||||
public static final String WECHAT_JSAPI_OPENID = OAUTH_STATE + ":wx:jsapi:openid:";
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
package com.weixin.pay.constants;
|
||||
|
||||
/**
|
||||
* 微信基础URL链接
|
||||
*
|
||||
* @author yclimb
|
||||
* @date 2018/8/17
|
||||
*/
|
||||
public class WXURL {
|
||||
|
||||
/**
|
||||
* 请求URL之获取jsapi_ticket
|
||||
*/
|
||||
public static final String PAGE_URL_SIGN = "jsapi_ticket={0}&noncestr={1}×tamp={2}&url={3}";
|
||||
|
||||
/**
|
||||
* 请求URL之获取access_token
|
||||
*/
|
||||
public static final String BASE_ACCESS_TOKEN = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}";
|
||||
|
||||
/**
|
||||
* 请求URL之获取jsapi_ticket
|
||||
*/
|
||||
public static final String BASE_JSAPI_TICKET = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={0}&type=jsapi";
|
||||
|
||||
/**
|
||||
* 请求URL之创建菜单
|
||||
*/
|
||||
public static final String MENU_CREATE = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token={0}";
|
||||
|
||||
/**
|
||||
* 请求URL之查询菜单
|
||||
*/
|
||||
public static final String MENU_QUERY = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token={0}";
|
||||
|
||||
/**
|
||||
* 请求URL之删除菜单
|
||||
*/
|
||||
public static final String MENU_DELETE = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token={0}";
|
||||
|
||||
/**
|
||||
* 页面授权获取code地址
|
||||
*/
|
||||
public static final String OAUTH_CODE_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=" + WXConstants.OAUTH_STATE + "#wechat_redirect";
|
||||
|
||||
/**
|
||||
* 通过code换取网页授权access_token
|
||||
*/
|
||||
public static final String OAUTH_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={0}&secret={1}&code={2}&grant_type=authorization_code";
|
||||
|
||||
/**
|
||||
* 页面授权获取指定微信号的基础信息
|
||||
*/
|
||||
public static final String OAUTH_GET_USERINFO_URL = "https://api.weixin.qq.com/sns/userinfo?access_token={0}&openid={1}&lang=zh_CN";
|
||||
|
||||
/**
|
||||
* 获取指定微信号的基础信息 通过全局access_token
|
||||
*/
|
||||
public static final String GET_USERINFO_URL = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";
|
||||
|
||||
/**
|
||||
* 微信模板消息发送
|
||||
*/
|
||||
public static final String WX_TEMPLATE_SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={0}";
|
||||
|
||||
/**
|
||||
* 微信客户消息发送
|
||||
*/
|
||||
public static final String WX_CUSTMOER_SERVICE_SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token={0}";
|
||||
|
||||
/***
|
||||
* 微信创建二维码ticket
|
||||
*/
|
||||
public static final String WX_TICKET_CREATE = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={0}";
|
||||
|
||||
/**
|
||||
* 小程序登录校验
|
||||
*/
|
||||
public static final String WX_MINI_LOGIN = "https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&js_code={2}&grant_type=authorization_code";
|
||||
|
||||
/**
|
||||
* 小程序模板信息
|
||||
*/
|
||||
public static final String WX_MINI_TEMPLATE_MSG = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token={0}";
|
||||
|
||||
/**
|
||||
* 获取小程序二维码,通过该接口生成的小程序码,永久有效,数量暂无限制
|
||||
*/
|
||||
public static final String WX_MINI_QR_CODE_URL = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token={0}";
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
package com.weixin.pay.redis;
|
||||
|
||||
/**
|
||||
* Redis 枚举类
|
||||
*
|
||||
* @author yclimb
|
||||
* @date 2018/4/19
|
||||
*/
|
||||
public enum RedisKeyEnum {
|
||||
|
||||
/**
|
||||
* 生成带参数的小程序二维码KEY
|
||||
*/
|
||||
XXX_MINI_WX_CODE(RedisKeyUtil.KEY_PREFIX, "mini", "getwxacodeunlimit", "生成永久无限制微信二维码")
|
||||
|
||||
;
|
||||
|
||||
/**
|
||||
* 系统标识
|
||||
*/
|
||||
private String keyPrefix;
|
||||
|
||||
/**
|
||||
* 模块名称
|
||||
*/
|
||||
private String module;
|
||||
|
||||
/**
|
||||
* 方法名称
|
||||
*/
|
||||
private String func;
|
||||
|
||||
/**
|
||||
* remark
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
RedisKeyEnum(String keyPrefix, String module, String func, String remark) {
|
||||
this.keyPrefix = keyPrefix;
|
||||
this.module = module;
|
||||
this.func = func;
|
||||
this.remark = remark;
|
||||
}
|
||||
|
||||
public String getKeyPrefix() {
|
||||
return keyPrefix;
|
||||
}
|
||||
|
||||
public void setKeyPrefix(String keyPrefix) {
|
||||
this.keyPrefix = keyPrefix;
|
||||
}
|
||||
|
||||
public String getModule() {
|
||||
return module;
|
||||
}
|
||||
|
||||
public void setModule(String module) {
|
||||
this.module = module;
|
||||
}
|
||||
|
||||
public String getFunc() {
|
||||
return func;
|
||||
}
|
||||
|
||||
public void setFunc(String func) {
|
||||
this.func = func;
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
return remark;
|
||||
}
|
||||
|
||||
public void setRemark(String remark) {
|
||||
this.remark = remark;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
package com.weixin.pay.redis;
|
||||
|
||||
/**
|
||||
* Redis 工具类
|
||||
*
|
||||
* @author yclimb
|
||||
* @date 2018/4/19
|
||||
*/
|
||||
public class RedisKeyUtil {
|
||||
|
||||
/**
|
||||
* 主数据系统标识
|
||||
*/
|
||||
public static final String KEY_PREFIX = "xxx";
|
||||
|
||||
/**
|
||||
* 分割字符,默认[:]
|
||||
*/
|
||||
public static final String KEY_SPLIT_CHAR = ":";
|
||||
|
||||
/**
|
||||
* redis的key键规则定义
|
||||
*
|
||||
* @param module 模块名称
|
||||
* @param func 方法名称
|
||||
* @return key
|
||||
*/
|
||||
public static String keyBuilder(String module, String func) {
|
||||
return keyBuilder(null, module, func, (String[]) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* redis的key键规则定义
|
||||
*
|
||||
* @param module 模块名称
|
||||
* @param func 方法名称
|
||||
* @param args 参数..
|
||||
* @return key
|
||||
*/
|
||||
public static String keyBuilder(String module, String func, String... args) {
|
||||
return keyBuilder(null, module, func, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* redis的key键规则定义
|
||||
*
|
||||
* @param module 模块名称
|
||||
* @param func 方法名称
|
||||
* @param objStr 对象.toString()
|
||||
* @return key
|
||||
*/
|
||||
public static String keyBuilder(String module, String func, String objStr) {
|
||||
return keyBuilder(null, module, func, new String[]{objStr});
|
||||
}
|
||||
|
||||
/**
|
||||
* redis的key键规则定义
|
||||
*
|
||||
* @param prefix 项目前缀
|
||||
* @param module 模块名称
|
||||
* @param func 方法名称
|
||||
* @param objStr 对象.toString()
|
||||
* @return key
|
||||
*/
|
||||
public static String keyBuilder(String prefix, String module, String func, String objStr) {
|
||||
return keyBuilder(prefix, module, func, new String[]{objStr});
|
||||
}
|
||||
|
||||
/**
|
||||
* redis的key键规则定义
|
||||
*
|
||||
* @param prefix 项目前缀
|
||||
* @param module 模块名称
|
||||
* @param func 方法名称
|
||||
* @param args 参数..
|
||||
* @return key
|
||||
*/
|
||||
public static String keyBuilder(String prefix, String module, String func, String... args) {
|
||||
// 项目前缀
|
||||
if (prefix == null) {
|
||||
prefix = KEY_PREFIX;
|
||||
}
|
||||
|
||||
StringBuilder key = new StringBuilder(prefix);
|
||||
// KEY_SPLIT_CHAR 为分割字符
|
||||
key.append(KEY_SPLIT_CHAR).append(module).append(KEY_SPLIT_CHAR).append(func);
|
||||
|
||||
// args 为空时不需要循环
|
||||
if (args == null || args.length <= 0) {
|
||||
return key.toString();
|
||||
}
|
||||
|
||||
// args 不为空时循环拼接字符
|
||||
for (String arg : args) {
|
||||
key.append(KEY_SPLIT_CHAR).append(arg);
|
||||
}
|
||||
|
||||
return key.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* redis的key键规则定义
|
||||
*
|
||||
* @param redisEnum 枚举对象
|
||||
* @return key
|
||||
*/
|
||||
public static String keyBuilder(RedisKeyEnum redisEnum) {
|
||||
return keyBuilder(redisEnum.getKeyPrefix(), redisEnum.getModule(), redisEnum.getFunc(), (String[]) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* redis的key键规则定义
|
||||
*
|
||||
* @param redisEnum 枚举对象
|
||||
* @param objStr 对象.toString()
|
||||
* @return key
|
||||
*/
|
||||
public static String keyBuilder(RedisKeyEnum redisEnum, String objStr) {
|
||||
return keyBuilder(redisEnum.getKeyPrefix(), redisEnum.getModule(), redisEnum.getFunc(), objStr);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package com.weixin.pay.util;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
/**
|
||||
* 对于微信用户名称emoji等特殊字符处理
|
||||
*
|
||||
* @author yclimb
|
||||
* @date 2018/8/17
|
||||
*/
|
||||
@Slf4j
|
||||
public class WXUserUtil {
|
||||
|
||||
/**
|
||||
* 编码用户昵称
|
||||
*
|
||||
* @param nickName 未编码等名称
|
||||
* @return base64 str
|
||||
*/
|
||||
public static String encodeNickName(String nickName) {
|
||||
try {
|
||||
return Base64.encodeBase64String(nickName.toString().getBytes("utf-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
log.error("编码用户昵称报错", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解码用户昵称
|
||||
*
|
||||
* @param nickName base64 str
|
||||
* @return 原始名称
|
||||
*/
|
||||
public static String decodeNickName(String nickName) {
|
||||
return new String(Base64.decodeBase64(nickName));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,279 @@
|
|||
package com.weixin.pay.util;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.weixin.pay.constants.WXConstants;
|
||||
import com.weixin.pay.constants.WXPayConstants;
|
||||
import com.weixin.pay.constants.WXURL;
|
||||
import com.weixin.pay.redis.RedisKeyEnum;
|
||||
import com.weixin.pay.redis.RedisKeyUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 微信小工具类
|
||||
*
|
||||
* @author yclimb
|
||||
* @date 2018/8/17
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class WXUtils {
|
||||
|
||||
@Resource
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
@Resource
|
||||
private RedisTemplate redisTemplate;
|
||||
|
||||
/**
|
||||
* 获取微信全局accessToken
|
||||
* 测试环境只能通过生产接口调用获取accessToken 不然会存在accessToken相互覆盖 导致互相不可用
|
||||
*
|
||||
* @param code
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public String getAccessToken(String code) throws Exception {
|
||||
|
||||
// 取redis数据
|
||||
String key = WXConstants.WECHAT_ACCESSTOKEN + code;
|
||||
String accessToken = (String) redisTemplate.opsForValue().get(key);
|
||||
if (accessToken != null) {
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
// 通过接口取得access_token
|
||||
JSONObject jsonObject = restTemplate.getForObject(MessageFormat.format(WXURL.BASE_ACCESS_TOKEN, WXPayConstants.APP_ID, WXPayConstants.SECRET), JSONObject.class);
|
||||
String token = (String) jsonObject.get("access_token");
|
||||
if (StringUtils.isNotBlank(token)) {
|
||||
// 存储redis
|
||||
redisTemplate.opsForValue().set(key, token, 7000, TimeUnit.SECONDS);
|
||||
return token;
|
||||
} else {
|
||||
log.error("获取微信accessToken出错,微信返回信息为:[{}]", jsonObject.toString());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取小程序静默登录返回信息
|
||||
*
|
||||
* @param code code
|
||||
* @param appId appId
|
||||
* @param appSecret appSecret
|
||||
* @return json
|
||||
*/
|
||||
public JSONObject getMiniBaseUserInfo(String code, String appId, String appSecret) {
|
||||
log.info("getMiniBaseUserInfo:params:[{}]", code);
|
||||
String data = restTemplate.getForObject(WXURL.WX_MINI_LOGIN, String.class, appId, appSecret, code);
|
||||
log.info("getMiniBaseUserInfo:result:[{}]", data);
|
||||
return JSONObject.parseObject(data);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 网页授权获取用户信息时用于获取access_token以及openid
|
||||
* 请求路径:https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code(最后一个参数不变)
|
||||
* @param code c
|
||||
* @return access_token json obj
|
||||
*
|
||||
* @author yclimb
|
||||
* @date 2018/7/30
|
||||
*/
|
||||
public JSONObject getJsapiAccessTokenByCode(String code) {
|
||||
if (StringUtils.isBlank(code)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
// 获取access_token
|
||||
String access_token_json = restTemplate.getForObject(WXURL.OAUTH_ACCESS_TOKEN_URL, String.class,
|
||||
WXPayConstants.APP_ID_XXX, WXPayConstants.SECRET_XXX, code);
|
||||
log.info("getAccessToken:access_token_json:{}", access_token_json);
|
||||
if (StringUtils.isBlank(access_token_json)) {
|
||||
return null;
|
||||
}
|
||||
JSONObject jsonObject = JSON.parseObject(access_token_json);
|
||||
if (StringUtils.isBlank(jsonObject.getString("access_token"))) {
|
||||
return null;
|
||||
}
|
||||
return jsonObject;
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过access_token和openid请求获取用户信息
|
||||
* 请求路径:https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
|
||||
* @param access_token t
|
||||
* @param openid o
|
||||
* @return userinfo json obj
|
||||
*
|
||||
* @author yclimb
|
||||
* @date 2018/7/30
|
||||
*/
|
||||
public JSONObject getJsapiUserinfo(String access_token, String openid) {
|
||||
if (StringUtils.isBlank(access_token) || StringUtils.isBlank(openid)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
// 获取access_token和openid
|
||||
String userinfo_json = restTemplate.getForObject(WXURL.OAUTH_GET_USERINFO_URL, String.class, access_token, openid);
|
||||
log.info("getUserinfo:userinfo_json:{}", userinfo_json);
|
||||
if (StringUtils.isBlank(userinfo_json)) {
|
||||
return null;
|
||||
}
|
||||
JSONObject jsonObject = JSON.parseObject(userinfo_json);
|
||||
if (0 != jsonObject.getIntValue("errcode")) {
|
||||
return null;
|
||||
}
|
||||
return jsonObject;
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成带参数的小程序二维码[]
|
||||
*
|
||||
* @param scene 参数
|
||||
* @param page 小程序页面
|
||||
* @return img path
|
||||
*/
|
||||
public String getWxMiniQRImg(String scene, String page) {
|
||||
InputStream inputStream = null;
|
||||
String imgUrl = "";
|
||||
try {
|
||||
|
||||
// redis key
|
||||
String redisKey = RedisKeyUtil.keyBuilder(RedisKeyEnum.XXX_MINI_WX_CODE, scene + RedisKeyUtil.KEY_SPLIT_CHAR + page);
|
||||
|
||||
// 从redis中获取缓存图片
|
||||
Object obj = redisTemplate.opsForValue().get(redisKey);
|
||||
if (obj != null) {
|
||||
return obj.toString();
|
||||
}
|
||||
|
||||
// 获取微信永久无限制二维码
|
||||
byte[] code = this.getwxacodeunlimit(scene, page);
|
||||
if (code == null || code.length <= 0) {
|
||||
return imgUrl;
|
||||
}
|
||||
|
||||
// 将返回字节数组转为输入流
|
||||
inputStream = new ByteArrayInputStream(code);
|
||||
|
||||
// 取得uuid的文件名称
|
||||
String newFileName = UUID.randomUUID().toString().replaceAll("-", "").replace(".", "") + ".png";
|
||||
log.info("getWxMiniQRImg:fileName:" + newFileName);
|
||||
|
||||
// 上传图片到OSS服务器
|
||||
// imgUrl = ossUtils.uploadOss(inputStream, ossUtils.getImgPathYYYYMMDD(), newFileName);
|
||||
|
||||
// 图片为空直接返回
|
||||
if (StringUtils.isBlank(imgUrl)) {
|
||||
return imgUrl;
|
||||
}
|
||||
// 设置到redis中,下次取直接拿缓存即可,防止多次生成
|
||||
redisTemplate.opsForValue().set(redisKey, imgUrl);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("getWxMiniQRImg:调用小程序生成微信永久小程序码URL接口异常", e);
|
||||
} finally {
|
||||
if (inputStream != null) {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return imgUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 application/json;charset=UTF-8 的 HttpHeaders 对象
|
||||
*
|
||||
* @return HttpHeaders
|
||||
* @author yclimb
|
||||
* @date 2018/7/18
|
||||
*/
|
||||
public HttpHeaders getHttpHeadersUTF8JSON() {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
|
||||
headers.add("Accept", MediaType.APPLICATION_JSON_VALUE);
|
||||
return headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* 作用:生成永久无限制微信二维码<br>
|
||||
* 场景:微信二维码生成,根据参数和页面配置微信二维码,返回二维码字节流
|
||||
* 接口链接:https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=ACCESS_TOKEN
|
||||
* 接口文档地址:https://developers.weixin.qq.com/miniprogram/dev/api/qrcode.html
|
||||
*
|
||||
* @param scene 最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~,其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式)
|
||||
* @param page 必须是已经发布的小程序存在的页面(否则报错),例如 "pages/index/index" ,根路径前不要填加'/',不能携带参数(参数请放在scene字段里),如果不填写这个字段,默认跳主页面
|
||||
* @return 二维码字节流
|
||||
* @author yclimb
|
||||
* @date 2018/7/18
|
||||
*/
|
||||
public byte[] getwxacodeunlimit(String scene, String page) {
|
||||
try {
|
||||
|
||||
// 获取access token
|
||||
String accessToken = this.getAccessToken("xxx");
|
||||
|
||||
// 拼接传入参数
|
||||
Map<String, Object> param = new HashMap<>(5);
|
||||
param.put("scene", scene);
|
||||
param.put("page", page);
|
||||
// 默认:430;二维码的宽度,最小为280
|
||||
param.put("width", 280);
|
||||
// 默认:false;自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调
|
||||
param.put("auto_color", false);
|
||||
|
||||
// 默认:{"r":"0","g":"0","b":"0"};二维码图片颜色参数,auto_color 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"} 十进制表示
|
||||
Map<String, Object> line_color = new HashMap<>(3);
|
||||
line_color.put("r", 0);
|
||||
line_color.put("g", 0);
|
||||
line_color.put("b", 0);
|
||||
param.put("line_color", line_color);
|
||||
|
||||
// map转换为json传输
|
||||
String jsonParam = JSON.toJSONString(param);
|
||||
log.info("getwxacodeunlimit:param:" + jsonParam);
|
||||
|
||||
// 请求微信接口,得到返回结果[二进制流]
|
||||
HttpEntity<String> entity = new HttpEntity<>(jsonParam, this.getHttpHeadersUTF8JSON());
|
||||
ResponseEntity<byte[]> responseEntity = restTemplate.postForEntity(WXURL.WX_MINI_QR_CODE_URL, entity, byte[].class, accessToken);
|
||||
|
||||
// return byte[]
|
||||
return responseEntity.getBody();
|
||||
} catch (Exception e) {
|
||||
log.error("getwxacodeunlimit:postForEntity:" + e.getMessage(), e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
package controller;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.weixin.pay.constants.WXConstants;
|
||||
import com.weixin.pay.util.WXUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 微信用户授权控制类
|
||||
*
|
||||
* @author yclimb
|
||||
* @date 2018/7/30
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/weixin/auth")
|
||||
public class WXAuthController {
|
||||
|
||||
@Resource
|
||||
private WXUtils wxUtils;
|
||||
|
||||
/**
|
||||
* 微信网页授权
|
||||
* https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842
|
||||
* 第一步:用户同意授权,获取code
|
||||
* 第二步:通过code换取网页授权access_token
|
||||
* @return str
|
||||
*
|
||||
* @author yclimb
|
||||
* @date 2018/7/30
|
||||
*/
|
||||
/*@ApiOperation(value = "微信支付|网页授权", httpMethod = "GET", notes = "获取前端微信用户的网页授权,得到用户基础信息")*/
|
||||
@GetMapping("/authorize")
|
||||
public String authorize(HttpServletRequest request) throws Exception {
|
||||
|
||||
// 跳转页面标识
|
||||
String state = request.getParameter("state");
|
||||
// 通过code获取access_token
|
||||
String code = request.getParameter("code");
|
||||
log.info("authorize:code:{}", code);
|
||||
|
||||
// 获取access_token和openid
|
||||
JSONObject jsonToken = wxUtils.getJsapiAccessTokenByCode(code);
|
||||
if (null == jsonToken) {
|
||||
return WXConstants.ERROR;
|
||||
}
|
||||
|
||||
return WXConstants.SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过access_token和openid请求获取用户信息(需scope为 snsapi_userinfo)
|
||||
* @return str
|
||||
*
|
||||
* @author yclimb
|
||||
* @date 2018/7/31
|
||||
*/
|
||||
/*@ApiOperation(value = "微信支付|通过access_token和openid请求获取用户信息", httpMethod = "POST", notes = "通过access_token和openid请求获取用户信息")*/
|
||||
@PostMapping("/userinfo/{access_token}/{openid}")
|
||||
public String userinfo(@PathVariable String access_token, @PathVariable String openid) {
|
||||
|
||||
// 通过access_token和openid请求获取用户信息
|
||||
JSONObject jsonUserinfo = wxUtils.getJsapiUserinfo(access_token, openid);
|
||||
if (null == jsonUserinfo) {
|
||||
return WXConstants.ERROR;
|
||||
}
|
||||
|
||||
// 判断用户是否在系统中是一个用户
|
||||
String unionid = jsonUserinfo.getString("unionid");
|
||||
if (StringUtils.isBlank(unionid)) {
|
||||
return WXConstants.ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
// 存储用户信息到数据库
|
||||
User user = userService.queryByUnionId(unionid);
|
||||
if (user == null) {
|
||||
user = JSON.parseObject(jsonUserinfo.toJSONString(), User.class);
|
||||
user.setUserId(user.getId());
|
||||
user.setCreateDate(new Date());
|
||||
user.setIsDel(CommonConstantEnum.UNDELETED.getCode());
|
||||
// 处理微信昵称emoji表情
|
||||
if (StringUtils.isNotBlank(user.getNickName())) {
|
||||
// 编码Base64.decodeBase64()
|
||||
user.setNickName(UserNickUtil.encodeNickName(user.getNickName()));
|
||||
}
|
||||
userService.createEntity(user);
|
||||
}
|
||||
|
||||
// 用户账户信息
|
||||
Map<String, Object> map = new HashMap<>(2);
|
||||
// 用户名称解码
|
||||
user.setNickName(UserNickUtil.decodeNickName(user.getNickName()));
|
||||
UserAccount userAccount = userAccountService.queryByUserId(user.getId());
|
||||
map.put("user", user);
|
||||
|
||||
return AppMessage.success(map);*/
|
||||
|
||||
return WXConstants.SUCCESS;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,302 @@
|
|||
package controller;
|
||||
|
||||
import com.weixin.pay.constants.WXConstants;
|
||||
import com.weixin.pay.constants.WXPayConstants;
|
||||
import com.weixin.pay.util.AESUtil;
|
||||
import com.weixin.pay.util.WXPayUtil;
|
||||
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 javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 微信支付Controller
|
||||
*
|
||||
* @author yclimb
|
||||
* @date 2018/6/15
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/weixin/pay")
|
||||
public class WXPayController {
|
||||
|
||||
/**
|
||||
* 返回成功xml
|
||||
*/
|
||||
private String resSuccessXml = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
|
||||
|
||||
/**
|
||||
* 返回失败xml
|
||||
*/
|
||||
private String resFailXml = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[报文为空]]></return_msg></xml>";
|
||||
|
||||
/**
|
||||
* 该链接是通过【统一下单API】中提交的参数notify_url设置,如果链接无法访问,商户将无法接收到微信通知。
|
||||
* 通知url必须为直接可访问的url,不能携带参数。示例:notify_url:“https://pay.weixin.qq.com/wxpay/pay.action”
|
||||
* <p>
|
||||
* 支付完成后,微信会把相关支付结果和用户信息发送给商户,商户需要接收处理,并返回应答。
|
||||
* 对后台通知交互时,如果微信收到商户的应答不是成功或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知最终能成功。
|
||||
* (通知频率为15/15/30/180/1800/1800/1800/1800/3600,单位:秒)
|
||||
* 注意:同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。
|
||||
* 推荐的做法是,当收到通知进行处理时,首先检查对应业务数据的状态,判断该通知是否已经处理过,如果没有处理过再进行处理,如果处理过直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。
|
||||
* 特别提醒:商户系统对于支付结果通知的内容一定要做签名验证,防止数据泄漏导致出现“假通知”,造成资金损失。
|
||||
*
|
||||
* @author yclimb
|
||||
* @date 2018/6/15
|
||||
*/
|
||||
/*@ApiOperation(value = "微信支付|支付回调接口", httpMethod = "POST", notes = "该链接是通过【统一下单API】中提交的参数notify_url设置,如果链接无法访问,商户将无法接收到微信通知。")*/
|
||||
@RequestMapping("/wxnotify")
|
||||
public void wxnotify(HttpServletRequest request, HttpServletResponse response) {
|
||||
|
||||
String resXml = "";
|
||||
InputStream inStream;
|
||||
try {
|
||||
|
||||
inStream = request.getInputStream();
|
||||
ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[1024];
|
||||
int len = 0;
|
||||
while ((len = inStream.read(buffer)) != -1) {
|
||||
outSteam.write(buffer, 0, len);
|
||||
}
|
||||
|
||||
WXPayUtil.getLogger().info("wxnotify:微信支付----start----");
|
||||
|
||||
// 获取微信调用我们notify_url的返回信息
|
||||
String result = new String(outSteam.toByteArray(), "utf-8");
|
||||
WXPayUtil.getLogger().info("wxnotify:微信支付----result----=" + result);
|
||||
|
||||
// 关闭流
|
||||
outSteam.close();
|
||||
inStream.close();
|
||||
|
||||
// xml转换为map
|
||||
Map<String, String> map = WXPayUtil.xmlToMap(result);
|
||||
boolean isSuccess = false;
|
||||
if (WXPayConstants.SUCCESS.equalsIgnoreCase(map.get(WXPayConstants.RESULT_CODE))) {
|
||||
|
||||
WXPayUtil.getLogger().info("wxnotify:微信支付----返回成功");
|
||||
|
||||
if (WXPayUtil.isSignatureValid(map, WXPayConstants.API_KEY)) {
|
||||
|
||||
// 订单处理 操作 orderconroller 的回写操作?
|
||||
WXPayUtil.getLogger().info("wxnotify:微信支付----验证签名成功");
|
||||
|
||||
// 通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
|
||||
resXml = resSuccessXml;
|
||||
isSuccess = true;
|
||||
|
||||
} else {
|
||||
WXPayUtil.getLogger().error("wxnotify:微信支付----判断签名错误");
|
||||
}
|
||||
|
||||
} else {
|
||||
WXPayUtil.getLogger().error("wxnotify:支付失败,错误信息:" + map.get(WXPayConstants.ERR_CODE_DES));
|
||||
resXml = resFailXml;
|
||||
}
|
||||
|
||||
/*// 根据付款单号查询付款记录
|
||||
Payment payment = paymentService.queryPaymentByFlowNumer(map.get("out_trade_no"), PaymentConstantEnum.PAYMENT_TYPE_ORDER.getCode());
|
||||
|
||||
// 付款记录修改 & 记录付款日志
|
||||
int resultPay = paymentService.modifyPaymentByWxnotify(payment, isSuccess);
|
||||
if (resultPay > 0) {
|
||||
// 处理业务 - 修改订单状态
|
||||
WXPayUtil.getLogger().info("wxnotify:微信支付回调:修改的订单===>" + map.get("out_trade_no"));
|
||||
int updateResult = tradeService.modifyWxnotifyByRelationId(payment.getRelationId(), payment.getPrepayId(), isSuccess);
|
||||
if (updateResult > 0) {
|
||||
WXPayUtil.getLogger().info("wxnotify:微信支付回调:修改订单支付状态成功");
|
||||
} else {
|
||||
WXPayUtil.getLogger().error("wxnotify:微信支付回调:修改订单支付状态失败");
|
||||
}
|
||||
}*/
|
||||
|
||||
} catch (Exception e) {
|
||||
WXPayUtil.getLogger().error("wxnotify:支付回调发布异常:", e);
|
||||
} finally {
|
||||
try {
|
||||
// 处理业务完毕
|
||||
BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
|
||||
out.write(resXml.getBytes());
|
||||
out.flush();
|
||||
out.close();
|
||||
} catch (IOException e) {
|
||||
WXPayUtil.getLogger().error("wxnotify:支付回调发布异常:out:", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款结果通知
|
||||
* <p>
|
||||
* 在申请退款接口中上传参数“notify_url”以开通该功能
|
||||
* 如果链接无法访问,商户将无法接收到微信通知。
|
||||
* 通知url必须为直接可访问的url,不能携带参数。示例:notify_url:“https://pay.weixin.qq.com/wxpay/pay.action”
|
||||
* <p>
|
||||
* 当商户申请的退款有结果后,微信会把相关结果发送给商户,商户需要接收处理,并返回应答。
|
||||
* 对后台通知交互时,如果微信收到商户的应答不是成功或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知最终能成功。
|
||||
* (通知频率为15/15/30/180/1800/1800/1800/1800/3600,单位:秒)
|
||||
* 注意:同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。
|
||||
* 推荐的做法是,当收到通知进行处理时,首先检查对应业务数据的状态,判断该通知是否已经处理过,如果没有处理过再进行处理,如果处理过直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。
|
||||
* 特别说明:退款结果对重要的数据进行了加密,商户需要用商户秘钥进行解密后才能获得结果通知的内容
|
||||
* @param request req
|
||||
* @param response resp
|
||||
* @return res xml
|
||||
*
|
||||
* @author yclimb
|
||||
* @date 2018/6/21
|
||||
*/
|
||||
/*@ApiOperation(value = "微信支付|微信退款回调接口", httpMethod = "POST", notes = "该链接是通过【微信退款API】中提交的参数notify_url设置,如果参数中传了notify_url,则商户平台上配置的回调地址将不会生效。")*/
|
||||
@RequestMapping("/refund")
|
||||
public void refund(HttpServletRequest request, HttpServletResponse response) {
|
||||
|
||||
String resXml = "";
|
||||
InputStream inStream;
|
||||
try {
|
||||
|
||||
inStream = request.getInputStream();
|
||||
ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[1024];
|
||||
int len = 0;
|
||||
while ((len = inStream.read(buffer)) != -1) {
|
||||
outSteam.write(buffer, 0, len);
|
||||
}
|
||||
WXPayUtil.getLogger().info("refund:微信退款----start----");
|
||||
|
||||
// 获取微信调用我们notify_url的返回信息
|
||||
String result = new String(outSteam.toByteArray(), "utf-8");
|
||||
WXPayUtil.getLogger().info("refund:微信退款----result----=" + result);
|
||||
|
||||
// 关闭流
|
||||
outSteam.close();
|
||||
inStream.close();
|
||||
|
||||
// xml转换为map
|
||||
Map<String, String> map = WXPayUtil.xmlToMap(result);
|
||||
boolean isSuccess = false;
|
||||
if (WXPayConstants.SUCCESS.equalsIgnoreCase(map.get(WXPayConstants.RETURN_CODE))) {
|
||||
|
||||
WXPayUtil.getLogger().info("refund:微信退款----返回成功");
|
||||
|
||||
/*if (WXPayUtil.isSignatureValid(map, WXPayConstants.API_KEY)) {*/
|
||||
|
||||
/** 以下字段在return_code为SUCCESS的时候有返回: **/
|
||||
// 加密信息:加密信息请用商户秘钥进行解密,详见解密方式
|
||||
String req_info = map.get("req_info");
|
||||
|
||||
/**
|
||||
* 解密方式
|
||||
* 解密步骤如下:
|
||||
* (1)对加密串A做base64解码,得到加密串B
|
||||
* (2)对商户key做md5,得到32位小写key* ( key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置 )
|
||||
* (3)用key*对加密串B做AES-256-ECB解密(PKCS7Padding)
|
||||
*/
|
||||
String resultStr = AESUtil.decryptData(req_info);
|
||||
|
||||
// WXPayUtil.getLogger().info("refund:解密后的字符串:" + resultStr);
|
||||
Map<String, String> aesMap = WXPayUtil.xmlToMap(resultStr);
|
||||
|
||||
|
||||
/** 以下为返回的加密字段: **/
|
||||
// 商户退款单号 是 String(64) 1.21775E+27 商户退款单号
|
||||
String out_refund_no = aesMap.get("out_refund_no");
|
||||
// 退款状态 是 String(16) SUCCESS SUCCESS-退款成功、CHANGE-退款异常、REFUNDCLOSE—退款关闭
|
||||
String refund_status = aesMap.get("refund_status");
|
||||
/*// 微信订单号 是 String(32) 1.21775E+27 微信订单号
|
||||
String transaction_id = null;
|
||||
// 商户订单号 是 String(32) 1.21775E+27 商户系统内部的订单号
|
||||
String out_trade_no = null;
|
||||
// 微信退款单号 是 String(32) 1.21775E+27 微信退款单号
|
||||
String refund_id = null;
|
||||
// 订单金额 是 Int 100 订单总金额,单位为分,只能为整数,详见支付金额
|
||||
String total_fee = null;
|
||||
// 应结订单金额 否 Int 100 当该订单有使用非充值券时,返回此字段。应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。
|
||||
String settlement_total_fee = null;
|
||||
// 申请退款金额 是 Int 100 退款总金额,单位为分
|
||||
String refund_fee = null;
|
||||
// 退款金额 是 Int 100 退款金额=申请退款金额-非充值代金券退款金额,退款金额<=申请退款金额
|
||||
String settlement_refund_fee = null;*/
|
||||
|
||||
// 退款是否成功
|
||||
if (!WXPayConstants.SUCCESS.equals(refund_status)) {
|
||||
resXml = resFailXml;
|
||||
} else {
|
||||
// 通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
|
||||
resXml = resSuccessXml;
|
||||
isSuccess = true;
|
||||
}
|
||||
|
||||
/*// 根据付款单号查询付款记录
|
||||
Payment payment = paymentService.queryPaymentByFlowNumer(out_refund_no, PaymentConstantEnum.PAYMENT_TYPE_REFUND.getCode());
|
||||
|
||||
// 付款记录修改 & 记录付款日志
|
||||
int resultPay = paymentService.modifyPaymentByWxnotify(payment, isSuccess);
|
||||
if (resultPay > 0) {
|
||||
|
||||
// 退款订单记录
|
||||
List<PaymentOrderRefund> paymentOrderRefundList = paymentOrderRefundService.queryListByPaymentId(payment.getId());
|
||||
|
||||
// 处理业务 - 修改订单状态
|
||||
WXPayUtil.getLogger().info("refund:微信支付回调:修改的订单===>" + out_refund_no);
|
||||
int updateResult = tradeService.modifyWxrefundByRelationId(payment.getRelationId(), paymentOrderRefundList, isSuccess);
|
||||
if (updateResult > 0) {
|
||||
WXPayUtil.getLogger().info("refund:微信支付回调:修改订单支付状态成功");
|
||||
} else {
|
||||
WXPayUtil.getLogger().error("refund:微信支付回调:修改订单支付状态失败");
|
||||
}
|
||||
}*/
|
||||
|
||||
/*} else {
|
||||
WXPayUtil.getLogger().error("refund:微信支付----判断签名错误");
|
||||
}*/
|
||||
|
||||
} else {
|
||||
WXPayUtil.getLogger().error("refund:支付失败,错误信息:" + map.get(WXPayConstants.RETURN_MSG));
|
||||
resXml = resFailXml;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
WXPayUtil.getLogger().error("refund:微信退款回调发布异常:", e);
|
||||
} finally {
|
||||
try {
|
||||
// 处理业务完毕
|
||||
BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
|
||||
out.write(resXml.getBytes());
|
||||
out.flush();
|
||||
out.close();
|
||||
} catch (IOException e) {
|
||||
WXPayUtil.getLogger().error("refund:微信退款回调发布异常:out:", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 企业付款到零钱
|
||||
* @return msg
|
||||
*
|
||||
* @author yclimb
|
||||
* @date 2018/7/30
|
||||
*/
|
||||
/*@Token(remove = true)
|
||||
@ApiOperation(value = "微信支付|企业付款到零钱", httpMethod = "POST", notes = "用于企业向微信用户个人付款")*/
|
||||
@PostMapping("/transfers")
|
||||
public String transfers(HttpServletRequest request) {
|
||||
try {
|
||||
String remoteAddr = request.getRemoteAddr();
|
||||
return WXConstants.SUCCESS;
|
||||
} catch (Exception e) {
|
||||
WXPayUtil.getLogger().error("transfers:微信提现支付异常:", e);
|
||||
}
|
||||
return WXConstants.ERROR;
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue