Merge branch 'wxpay'

# Conflicts:
#	nxhs-service/src/main/resources/application.yml
door
LI-CCONG\李聪聪 7 months ago
commit 5b1e161954

@ -48,6 +48,7 @@
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
</dependency>
</dependencies>
<build>
@ -71,6 +72,29 @@
</executions>
</plugin>
</plugins>
<resources>
<resource>
<!-- 指定配置文件所在的resource目录 -->
<directory>src/main/resources</directory>
<includes>
<include>application.yml</include>
<include>application-${environment}.yml</include>
<include>logback-${environment}.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<!-- 指定配置文件所在的resource目录 -->
<directory>src/main/resources</directory>
<includes>
<include>apiclient_cert.pem</include>
<include>apiclient_key.pem</include>
<include>wxpay_v3.properties</include>
<include>nxhs.jks</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>

@ -2,10 +2,26 @@ package cc.yunxi.controller;
import cc.yunxi.common.domain.CommonResult;
import cc.yunxi.common.exception.BadRequestException;
import cc.yunxi.common.exception.BizIllegalException;
import cc.yunxi.config.props.WxPayV3Properties;
import cc.yunxi.domain.query.TestQuery;
import cc.yunxi.enums.UserTypeEnum;
import cc.yunxi.service.ITestService;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.hutool.log.Log;
import com.ijpay.core.IJPayHttpResponse;
import com.ijpay.core.enums.RequestMethodEnum;
import com.ijpay.core.kit.PayKit;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.enums.WxDomainEnum;
import com.ijpay.wxpay.enums.v3.TransferApiEnum;
import com.ijpay.wxpay.model.TransferModel;
import com.ijpay.wxpay.model.v3.BatchTransferModel;
import com.ijpay.wxpay.model.v3.TransferDetailInput;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
@ -14,6 +30,9 @@ import org.springframework.scheduling.annotation.Async;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.concurrent.ExecutionException;
@Api(tags = "测试接口")
@ -25,6 +44,9 @@ public class TestController {
private final ITestService testService;
@Resource
private WxPayV3Properties wxPayV3Properties;
@ApiOperation("测试接口成功")
@GetMapping("/test01")
public CommonResult<String> success() {
@ -71,4 +93,58 @@ public class TestController {
}
return CommonResult.success("数据处理完成");
}
@ApiOperation(value = "测试支付", hidden = true)
@GetMapping("/tQ7lA2mG9f")
public CommonResult<String> wxCash(@RequestParam("openid") String openid, @RequestParam("amount") Integer amount) throws Exception {
// String openId = "oYkV86-mE9DakrcP5us474KscefQ";
BatchTransferModel batchTransferModel = new BatchTransferModel()
.setAppid(wxPayV3Properties.getAppId())
.setOut_batch_no(PayKit.generateStr())
.setBatch_name("测试商户转账到零钱")
.setBatch_remark("测试商户转账到零钱")
.setTotal_amount(amount)
.setTotal_num(1)
.setTransfer_detail_list(Collections.singletonList(
new TransferDetailInput()
.setOut_detail_no(PayKit.generateStr())
.setTransfer_amount(amount)
.setTransfer_remark("测试商户转账到零钱")
.setOpenid(openid)));
log.info("发起商家转账请求参数 {}", JSONUtil.toJsonStr(batchTransferModel));
// 删除
IJPayHttpResponse response = WxPayApi.v3(
RequestMethodEnum.POST,
WxDomainEnum.CHINA.toString(),
TransferApiEnum.TRANSFER_BATCHES.toString(),
wxPayV3Properties.getMchId(),
getSerialNumber(),
null,
wxPayV3Properties.getKeyPath(),
JSONUtil.toJsonStr(batchTransferModel)
);
log.info("发起商家转账响应 {}", response);
if (response.getStatus() != 200) {
log.warn("提现失败: {}", response.getBody());
throw new BizIllegalException("提现失败");
}
return CommonResult.success(response.getBody());
}
// 商户API证书序列号
private String getSerialNumber() {
// 获取证书序列号
X509Certificate certificate = PayKit.getCertificate(wxPayV3Properties.getCertPath());
if (certificate == null) {
throw new BizIllegalException("商户证书序列号获取失败!");
}
String serialNo = certificate.getSerialNumber().toString(16).toUpperCase();
// 提前两天检查证书是否有效
boolean isValid = PayKit.checkCertificateIsValid(certificate, wxPayV3Properties.getMchId(), -2);
log.info("证书是否可用 {} 证书有效期为 {}", isValid, DateUtil.format(certificate.getNotAfter(), DatePattern.NORM_DATETIME_PATTERN));
return serialNo;
}
}

@ -177,22 +177,23 @@ public class ClientServiceImpl extends ServiceImpl<ClientMapper, Client> impleme
@Override
@Transactional(rollbackFor = Exception.class)
public void cashBalance(String openId, Integer amount) throws Exception {
// 模拟提现成功
this.changeBalance(openId, new BigDecimal(amount), null, null, BalanceChangeTypeEnum.CASH_OUT);
/* BatchTransferModel batchTransferModel = new BatchTransferModel()
// 微信提现(单位:分)
amount = amount * 100;
BatchTransferModel batchTransferModel = new BatchTransferModel()
.setAppid(wxPayV3Properties.getAppId())
.setOut_batch_no(PayKit.generateStr())
.setBatch_name("测试商户转账到零钱")
.setBatch_remark("测试商户转账到零钱")
.setBatch_name("提现到零钱")
.setBatch_remark("提现到零钱")
.setTotal_amount(amount)
.setTotal_num(1)
.setTransfer_detail_list(Collections.singletonList(
new TransferDetailInput()
.setOut_detail_no(PayKit.generateStr())
.setTransfer_amount(1)
.setTransfer_remark("测试商户转账到零钱")
.setOpenid(openId)));
.setTransfer_amount(amount)
.setTransfer_remark("提现到零钱")
.setOpenid(openId))
);
log.info("发起商家转账请求参数 {}", JSONUtil.toJsonStr(batchTransferModel));
// 删除
IJPayHttpResponse response = WxPayApi.v3(
@ -206,8 +207,8 @@ public class ClientServiceImpl extends ServiceImpl<ClientMapper, Client> impleme
JSONUtil.toJsonStr(batchTransferModel)
);
log.info("发起商家转账响应 {}", response);
// 根据证书序列号查询对应的证书来验证签名结果
boolean verifySignature = WxPayKit.verifySignature(response, wxPayV3Properties.getPlatformCertPath());
// 验证签名不校验
// boolean verifySignature = WxPayKit.verifySignature(response, wxPayV3Properties.getPlatformCertPath());
// log.info("verifySignature: {}", verifySignature);
// if (response.getStatus() == OK && verifySignature) {
// return response.getBody();
@ -215,7 +216,7 @@ public class ClientServiceImpl extends ServiceImpl<ClientMapper, Client> impleme
if (response.getStatus() != OK) {
throw new BizIllegalException("提现失败");
}
// return response.getBody(); */
// return response.getBody();
}
@ -282,7 +283,7 @@ public class ClientServiceImpl extends ServiceImpl<ClientMapper, Client> impleme
accountBill.setAccountBalance(fund);
accountBill.setPayoutAmount(amount);
accountBill.setOrderNumber(orderNo);
accountBill.setRemark("支付给散户"+ amountStr + "元");
accountBill.setRemark("支付给散户" + amountStr + "元");
accountBill.setCreatorTime(now);
accountBillMapper.insert(accountBill);
@ -291,7 +292,7 @@ public class ClientServiceImpl extends ServiceImpl<ClientMapper, Client> impleme
throw new BizIllegalException("余额不足");
}
balance = balance.subtract(amount);
remark = "微信提现" + amountStr + "元";
remark = "微信提现" + amountStr + "元";
}
// 更新散户余额
client.setBanlance(balance);

@ -12,3 +12,12 @@ spring:
host: 222.71.165.188
port: 6379
password: qweasd,.123 # 密码为空时,请将本行注释
logging:
config: classpath:logback-dev.xml
level:
cc.yunxi: debug
pattern:
dateformat: yyyy-MM-dd HH:mm:ss:SSS
file:
path: "${user.dir}/logs/${spring.application.name}"

@ -12,3 +12,12 @@ spring:
host: 127.0.0.1
port: 6379
password: yx0818 # 密码为空时,请将本行注释
logging:
config: classpath:logback-prod.xml
level:
cc.yunxi: debug
pattern:
dateformat: yyyy-MM-dd HH:mm:ss:SSS
file:
path: "${user.dir}/logs/${spring.application.name}"

@ -27,13 +27,13 @@ info:
spring:
application:
name: nxhs-service
profiles:
active: '@environment@'
servlet:
multipart: #文件传输配置
max-file-size: 100MB #单个数据大小限制
max-request-size: 100MB #请求总数据大小限制
enabled: true #是否启用分段上传支持
profiles:
active: dev
boot:
admin:
client:
@ -71,15 +71,6 @@ mybatis-plus:
id-type: auto
type-aliases-package: cc.yunxi.domain.po # 指定实体对象扫描包, 简化xml中resultType路径
logging:
level:
cc.yunxi: debug
pattern:
dateformat: yyyy-MM-dd HH:mm:ss:SSS
file:
path: "${user.dir}/logs/${spring.application.name}"
name: "${logging.file.path}/log_debug.log"
knife4j:
enable: true
openapi:

@ -179,29 +179,16 @@
<!--name:用来指定受此loger约束的某一个包或者具体的某一个类。-->
<!--addtivity:是否向上级loger传递打印信息。默认是true。-->
<!-- <logger name="cc.yunxi" level="debug" additivity="false">-->
<!-- <appender-ref ref="STDOUT" />-->
<!-- <appender-ref ref="DEBUG_FILE" />-->
<!-- </logger>-->
<logger name="cc.yunxi" level="debug" additivity="true" />
<!--生产环境:输出到文件-->
<springProfile name="pro">
<root level="info">
<appender-ref ref="DEBUG_FILE" />
<appender-ref ref="INFO_FILE" />
<appender-ref ref="ERROR_FILE" />
<appender-ref ref="WARN_FILE" />
</root>
</springProfile>
<!--开发环境:打印控制台-->
<springProfile name="dev">
<root level="info">
<appender-ref ref="STDOUT" />
<appender-ref ref="DEBUG_FILE" />
<appender-ref ref="INFO_FILE" />
<appender-ref ref="ERROR_FILE" />
<appender-ref ref="WARN_FILE" />
</root>
</springProfile>
<root level="info">
<!-- 生产环境: 打印控制台可关闭 -->
<appender-ref ref="STDOUT" />
<appender-ref ref="DEBUG_FILE" />
<appender-ref ref="INFO_FILE" />
<appender-ref ref="ERROR_FILE" />
<appender-ref ref="WARN_FILE" />
</root>
</configuration>

@ -0,0 +1,194 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL如果设置为WARN则低于WARN的信息都不会输出 -->
<!-- scan:当此属性设置为true时配置文件如果发生改变将会被重新加载默认值为true -->
<!-- scanPeriod:设置监测配置文件是否有修改的时间间隔如果没有给出时间单位默认单位是毫秒。当scan为true时此属性生效。默认的时间间隔为1分钟。 -->
<!-- debug:当此属性设置为true时将打印出logback内部日志信息实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="10 seconds">
<contextName>logback</contextName>
<!-- name的值是变量的名称value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后可以使“${}”来使用变量-->
<!-- <property name="CONSOLE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg %n"/>-->
<springProperty scope="context" name="log.path" source="logging.file.path"/>
<property name="CONSOLE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%-5level) --- [%15.15(%thread)] %cyan(%-40.40(%logger{40})) : %msg%n"/>
<!--输出到控制台-->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!--此日志appender是为开发使用只配置最底级别控制台输出的日志级别是大于或等于此级别的日志信息-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>debug</level>
</filter>
<encoder>
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
<!-- 设置字符集 -->
<charset>UTF-8</charset>
</encoder>
</appender>
<!--输出到文件-->
<!-- 时间滚动输出 level为 DEBUG 日志 -->
<appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<!--先将今天的日志保存在这个文件中-->
<file>${log.path}/log_debug.log</file>
<!--日志文件输出格式 %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
%d{HH: mm:ss.SSS}——日志输出时间
%thread——输出日志的进程名字这在Web应用以及异步任务处理中很有用
%-5level——日志级别并且使用5个字符靠左对齐
%logger{36}——日志输出者的名字
%msg——日志消息
%n——平台的换行符
-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- rollover daily -->
<!--如果第二天输出日志,会将当天的日志记录在<file>${log.path}/log_debug.log</file>,然后将昨天的日志归档到下面的文件中-->
<!--以分钟切分 %d{yyyy-MM-dd_HH-mm}-->
<fileNamePattern>${log.path}/debug/log-debug-%d{yyyy-MM-dd_HH-mm}.%i.log</fileNamePattern>
<!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB -->
<!--单个日志文件最大100M到了这个值就会再创建一个日志文件日志文件的名字最后+1-->
<maxFileSize>100MB</maxFileSize>
<!--日志文件保留天数-->
<maxHistory>30</maxHistory>
<!--所有的日志文件最大20G超过就会删除旧的日志-->
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
<!--
此日志文件只记录debug级别的
onMatch和onMismatch都有三个属性值分别为Accept、DENY和NEUTRAL
onMatch="ACCEPT" 表示匹配该级别及以上
onMatch="DENY" 表示不匹配该级别及以上
onMatch="NEUTRAL" 表示该级别及以上的由下一个filter处理如果当前是最后一个则表示匹配该级别及以上
onMismatch="ACCEPT" 表示匹配该级别以下
onMismatch="NEUTRAL" 表示该级别及以下的由下一个filter处理如果当前是最后一个则不匹配该级别以下的
onMismatch="DENY" 表示不匹配该级别以下的
-->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>debug</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 时间滚动输出 level为 INFO 日志 -->
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/log_info.log</file>
<!--日志文件输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- rollover daily -->
<!--如果第二天输出日志,会将当天的日志记录在<file>${log.path}/log_debug.log</file>,然后将昨天的日志归档到下面的文件中-->
<!--以分钟切分 %d{yyyy-MM-dd_HH-mm}-->
<fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB -->
<!--单个日志文件最大100M到了这个值就会再创建一个日志文件日志文件的名字最后+1-->
<maxFileSize>100MB</maxFileSize>
<!--日志文件保留天数-->
<maxHistory>30</maxHistory>
<!--所有的日志文件最大20G超过就会删除旧的日志-->
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
<!--SizeAndTimeBasedRollingPolicy配置更灵活,所以改用SizeAndTimeBasedRollingPolicy-->
<!--<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
&lt;!&ndash; 每天日志归档路径以及格式 &ndash;&gt;
<fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
&lt;!&ndash;日志文件保留天数&ndash;&gt;
<maxHistory>15</maxHistory>
</rollingPolicy>-->
<!-- 此日志文件只记录info级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>info</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 时间滚动输出 level为 WARN 日志 -->
<appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/log_warn.log</file>
<!--日志文件输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- rollover daily -->
<!--如果第二天输出日志,会将当天的日志记录在<file>${log.path}/log_debug.log</file>,然后将昨天的日志归档到下面的文件中-->
<!--以分钟切分 %d{yyyy-MM-dd_HH-mm}-->
<fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB -->
<!--单个日志文件最大100M到了这个值就会再创建一个日志文件日志文件的名字最后+1-->
<maxFileSize>100MB</maxFileSize>
<!--日志文件保留天数-->
<maxHistory>30</maxHistory>
<!--所有的日志文件最大20G超过就会删除旧的日志-->
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
<!-- 此日志文件只记录warn级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>warn</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 时间滚动输出 level为 ERROR 日志 -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/log_error.log</file>
<!--日志文件输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- rollover daily -->
<!--如果第二天输出日志,会将当天的日志记录在<file>${log.path}/log_debug.log</file>,然后将昨天的日志归档到下面的文件中-->
<!--以分钟切分 %d{yyyy-MM-dd_HH-mm}-->
<fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB -->
<!--单个日志文件最大100M到了这个值就会再创建一个日志文件日志文件的名字最后+1-->
<maxFileSize>100MB</maxFileSize>
<!--日志文件保留天数-->
<maxHistory>30</maxHistory>
<!--所有的日志文件最大20G超过就会删除旧的日志-->
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
<!-- 此日志文件只记录ERROR级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!--name:用来指定受此loger约束的某一个包或者具体的某一个类。-->
<!--addtivity:是否向上级loger传递打印信息。默认是true。-->
<logger name="cc.yunxi" level="debug" additivity="true" />
<root level="info">
<!-- 生产环境: 打印控制台可关闭 -->
<appender-ref ref="STDOUT" />
<appender-ref ref="DEBUG_FILE" />
<appender-ref ref="INFO_FILE" />
<appender-ref ref="ERROR_FILE" />
<appender-ref ref="WARN_FILE" />
</root>
</configuration>

@ -98,6 +98,28 @@
</dependency>
</dependencies>
<profiles>
<!-- 开发 -->
<profile>
<id>dev</id>
<activation>
<!--默认激活配置-->
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<environment>dev</environment>
</properties>
</profile>
<!-- 生产 -->
<profile>
<id>prod</id>
<properties>
<environment>prod</environment>
</properties>
</profile>
</profiles>
<build>
<plugins>
<plugin>

Loading…
Cancel
Save