提交
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
package com.jeequan.jeepay.pay.anno;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 开放接口权限配置注解
|
||||
* @author deng
|
||||
* @since 2024-04-08
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface OpenLabel {
|
||||
|
||||
String value() default "";
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package com.jeequan.jeepay.pay.aop;
|
||||
|
||||
import com.jeequan.jeepay.core.aop.MethodLog;
|
||||
import com.jeequan.jeepay.core.beans.RequestKitBean;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.Signature;
|
||||
import org.aspectj.lang.annotation.AfterThrowing;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/***
|
||||
* 方法级日志切面组件
|
||||
*
|
||||
* @author terrfly
|
||||
* @since 2023/9/7 8:29
|
||||
*/
|
||||
@Component
|
||||
@Aspect
|
||||
public class MethodLogAop {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MethodLogAop.class);
|
||||
|
||||
@Autowired private RequestKitBean requestKitBean;
|
||||
|
||||
/**
|
||||
* 切点
|
||||
*/
|
||||
@Pointcut("@annotation(com.jeequan.jeepay.core.aop.MethodLog)")
|
||||
public void methodCachePointcut() { }
|
||||
|
||||
/**
|
||||
* 切面
|
||||
* @param point
|
||||
* @return
|
||||
* @throws Throwable
|
||||
*/
|
||||
@Around("methodCachePointcut()")
|
||||
public Object around(ProceedingJoinPoint point) throws Throwable {
|
||||
|
||||
//处理切面任务 发生异常将向外抛出 不记录日志
|
||||
Object result = point.proceed();
|
||||
|
||||
try {
|
||||
// 基础日志信息
|
||||
this.setBaseLogInfo(point, result);
|
||||
} catch (Exception e) {
|
||||
logger.error("methodLogError", e);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author: pangxiaoyu
|
||||
* @date: 2021/6/7 14:04
|
||||
* @describe: 记录异常操作请求信息
|
||||
*/
|
||||
@AfterThrowing(pointcut = "methodCachePointcut()", throwing="e")
|
||||
public void doException(JoinPoint joinPoint, Throwable e) throws Exception{
|
||||
|
||||
// 避免记录日志异常!
|
||||
try {
|
||||
// 基础日志信息
|
||||
this.setBaseLogInfo(joinPoint, ( e instanceof BizException ? ((BizException)e).getApiRes().toJSONString(): e.getMessage() ) );
|
||||
|
||||
} catch (Exception exception) {
|
||||
logger.error("methodLogError", exception);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取方法中的中文备注
|
||||
* @param joinPoint
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String getAnnotationRemark(JoinPoint joinPoint) throws Exception {
|
||||
|
||||
Signature sig = joinPoint.getSignature();
|
||||
Method m = joinPoint.getTarget().getClass().getMethod(joinPoint.getSignature().getName(), ((MethodSignature) sig).getParameterTypes());
|
||||
|
||||
MethodLog methodCache = m.getAnnotation(MethodLog.class);
|
||||
if (methodCache != null) {
|
||||
return methodCache.remark();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @author: pangxiaoyu
|
||||
* @date: 2021/6/7 14:12
|
||||
* @describe: 日志基本信息 公共方法
|
||||
*/
|
||||
private void setBaseLogInfo(JoinPoint joinPoint, Object resObject) throws Exception {
|
||||
|
||||
// 使用point.getArgs()可获取request,仅限于spring MVC参数包含request,改为通过contextHolder获取。
|
||||
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
|
||||
|
||||
// 日志名称
|
||||
String pointRemark = getAnnotationRemark(joinPoint);
|
||||
|
||||
// 请求路径
|
||||
String requestURL = request.getRequestURL().toString();
|
||||
|
||||
// 请求IP
|
||||
String clientIp = requestKitBean.getClientIp();
|
||||
|
||||
logger.info("[APILOG|{}], url={}, ip={} \r\n RQ:::{}\r\n RS:::{}", pointRemark, requestURL, clientIp,
|
||||
requestKitBean.getReqParamJSON(), resObject);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
package com.jeequan.jeepay.pay.aop;
|
||||
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.asymmetric.Sign;
|
||||
import cn.hutool.crypto.asymmetric.SignAlgorithm;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.jeequan.jeepay.converter.MchInfoConverter;
|
||||
import com.jeequan.jeepay.core.beans.RequestKitBean;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.model.openapi.AccessReq;
|
||||
import com.jeequan.jeepay.core.model.openapi.AccessResp;
|
||||
import com.jeequan.jeepay.core.utils.Base64;
|
||||
import com.jeequan.jeepay.db.entity.MchAppEntity;
|
||||
import com.jeequan.jeepay.db.entity.PackageOrder;
|
||||
import com.jeequan.jeepay.db.entity.ProductInfo;
|
||||
import com.jeequan.jeepay.pay.anno.OpenLabel;
|
||||
import com.jeequan.jeepay.service.impl.MchAppService;
|
||||
import com.jeequan.jeepay.service.impl.MchConfigService;
|
||||
import com.jeequan.jeepay.service.impl.PackageOrderService;
|
||||
import com.jeequan.jeepay.service.impl.ProductInfoService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.Signature;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
/***
|
||||
* 方法级日志切面组件
|
||||
*
|
||||
* @author terrfly
|
||||
* @since 2023/9/7 8:29
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@Aspect
|
||||
public class OpenLabelAop {
|
||||
|
||||
|
||||
@Autowired
|
||||
private RequestKitBean requestKitBean;
|
||||
|
||||
@Autowired
|
||||
private MchAppService mchAppService;
|
||||
|
||||
@Autowired
|
||||
private MchConfigService mchConfigService;
|
||||
|
||||
@Autowired
|
||||
private MchInfoConverter mchInfoConverter;
|
||||
|
||||
@Autowired
|
||||
private PackageOrderService packageOrderService;
|
||||
|
||||
@Autowired
|
||||
private ProductInfoService productInfoService;
|
||||
|
||||
/**
|
||||
* 切点
|
||||
*/
|
||||
@Pointcut("@annotation(com.jeequan.jeepay.pay.anno.OpenLabel)")
|
||||
public void methodCachePointcut() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 切面
|
||||
*
|
||||
* @param point
|
||||
* @return
|
||||
* @throws Throwable
|
||||
*/
|
||||
@Around("methodCachePointcut()")
|
||||
public Object around(ProceedingJoinPoint point) throws Throwable {
|
||||
JSONObject reqParamJSON = requestKitBean.getReqParamJSON();
|
||||
AccessReq accessReq = reqParamJSON.toJavaObject(AccessReq.class);
|
||||
|
||||
String appId = accessReq.getAppId();
|
||||
if (ObjUtil.isEmpty(appId)) {
|
||||
return AccessResp.fail(accessReq, "非法请求, 缺少应用[appId]参数");
|
||||
}
|
||||
|
||||
MchAppEntity mchApp = mchAppService.getById(appId);
|
||||
if (mchApp == null) {
|
||||
return AccessResp.fail(accessReq, "应用[appId]参数非法");
|
||||
}
|
||||
|
||||
// 先校验签名
|
||||
try {
|
||||
checkSign(accessReq, mchApp);
|
||||
} catch (Exception e) {
|
||||
return AccessResp.fail(accessReq, e.getMessage());
|
||||
}
|
||||
|
||||
// 权限的Key值
|
||||
String permissionKey = getAnnotationValue(point);
|
||||
|
||||
accessReq.setAppInfo(mchInfoConverter.toModel(mchApp));
|
||||
|
||||
if (!ObjUtil.isEmpty(permissionKey)) {
|
||||
// //校验应用的产品权限 TODO 方案的暂时没处理
|
||||
// //判断指定产品和指定应用是否开通
|
||||
// ProductInfo productInfo = productInfoService.getOne(ProductInfo.gw().eq(ProductInfo::getApiCode, permissionKey));
|
||||
// if (productInfo == null) {
|
||||
// return AccessResp.forbidden("未获取到商品信息!");
|
||||
// }
|
||||
// List<PackageOrder> packageOrderList = packageOrderService.lambdaQuery()
|
||||
// .eq(PackageOrder::getAppId, mchApp.getAppId())
|
||||
// .eq(PackageOrder::getProductId, productInfo.getProductId())// TODO: 如果兼容方案的话加上or语句 packageId
|
||||
// .eq(PackageOrder::getMchNo, mchApp.getMchNo())
|
||||
// .eq(PackageOrder::getState, PackageOrder.STATE_SUCCESS).list();
|
||||
//
|
||||
// if (packageOrderList.isEmpty()) {
|
||||
// return AccessResp.forbidden("当前应用未开通对应的产品功能, 请前往商家端后台开通对应的产品");
|
||||
// }
|
||||
boolean hasApiEnt = mchConfigService.queryMchAppHasApiEnt(mchApp.getMchNo(), mchApp.getAppId(), permissionKey);
|
||||
if (!hasApiEnt) {
|
||||
return AccessResp.forbidden("应用暂未开通接口权限");
|
||||
}
|
||||
}
|
||||
try {
|
||||
Object proceed = point.proceed();
|
||||
if (proceed instanceof AccessResp) {
|
||||
return proceed;
|
||||
}
|
||||
|
||||
return AccessResp.success(accessReq, proceed);
|
||||
} catch (Exception e) {
|
||||
log.warn("请求异常", e);
|
||||
return AccessResp.fail(accessReq, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void checkSign(AccessReq accessReq, MchAppEntity mchApp) {
|
||||
String sign = accessReq.getSign();
|
||||
String signType = accessReq.getSignType();
|
||||
|
||||
if (ObjUtil.isEmpty(sign)) {
|
||||
throw new BizException("缺少签名[sign]");
|
||||
}
|
||||
|
||||
if (ObjUtil.isEmpty(signType)) {
|
||||
throw new BizException("缺少签名类型[signType]");
|
||||
}
|
||||
|
||||
String appSecret = mchApp.getAppSecret();
|
||||
|
||||
if ("MD5".equals(signType)) {
|
||||
String signContent = accessReq.signContent() + "&appSecret=" + appSecret;
|
||||
log.info("验签串: {}", signContent);
|
||||
|
||||
String signVerify = SecureUtil.md5(signContent);
|
||||
|
||||
Assert.isTrue(signVerify.equals(sign), "验签失败");
|
||||
} else if ("RSA2".equals(signType)) {
|
||||
String pubKey = mchApp.getAppRsa2PublicKey();
|
||||
Sign signUtil = SecureUtil.sign(SignAlgorithm.SHA256withRSA, null, pubKey);
|
||||
String signContent = accessReq.signContent();
|
||||
byte[] signBytes = Base64.decodeBase64(accessReq.getSign().getBytes(StandardCharsets.UTF_8));
|
||||
try {
|
||||
boolean verify = signUtil.verify(signContent.getBytes(StandardCharsets.UTF_8), signBytes);
|
||||
Assert.isTrue(verify, "验签失败");
|
||||
} catch (Exception e) {
|
||||
log.info("验签异常", e);
|
||||
throw new BizException("验签异常");
|
||||
}
|
||||
} else {
|
||||
throw new BizException("非法的签名类型[signType]");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取方法中的中文备注
|
||||
*
|
||||
* @param joinPoint 请求节点信息
|
||||
*/
|
||||
public static String getAnnotationValue(JoinPoint joinPoint) throws Exception {
|
||||
|
||||
Signature sig = joinPoint.getSignature();
|
||||
Method m = joinPoint.getTarget().getClass().getMethod(joinPoint.getSignature().getName(), ((MethodSignature) sig).getParameterTypes());
|
||||
|
||||
OpenLabel methodCache = m.getAnnotation(OpenLabel.class);
|
||||
if (methodCache != null) {
|
||||
return methodCache.value();
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.jeequan.jeepay.pay.bootstrap;
|
||||
|
||||
import cn.hutool.core.date.DatePattern;
|
||||
import cn.hutool.crypto.SmUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.serializer.SerializeConfig;
|
||||
import com.alibaba.fastjson.serializer.SerializerFeature;
|
||||
import com.alibaba.fastjson.serializer.SimpleDateFormatSerializer;
|
||||
import com.jeequan.jeepay.pay.config.SystemYmlConfig;
|
||||
import com.jeequan.jeepay.service.impl.SysConfigService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/*
|
||||
* 项目初始化操作
|
||||
* 比如初始化配置文件, 读取基础数据, 资源初始化等。 避免在Main函数中写业务代码。
|
||||
* CommandLineRunner / ApplicationRunner都可以达到要求, 只是调用参数有所不同。
|
||||
*
|
||||
*
|
||||
* @author terrfly
|
||||
*
|
||||
* @date 2021/6/8 17:17
|
||||
*/
|
||||
@Component
|
||||
public class InitRunner implements CommandLineRunner {
|
||||
|
||||
@Autowired private SystemYmlConfig systemYmlConfig;
|
||||
@Autowired private SysConfigService sysConfigService;
|
||||
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
|
||||
// 配置是否使用缓存模式
|
||||
SysConfigService.IS_USE_CACHE = systemYmlConfig.getCacheConfig();
|
||||
|
||||
// 初始化系统秘钥
|
||||
SysConfigService.DB_ENCRYPT_SECRET = systemYmlConfig.getDbEncryptSecret();
|
||||
SysConfigService.DB_ENCRYPT_SM4 = SmUtil.sm4(SysConfigService.DB_ENCRYPT_SECRET.getBytes());
|
||||
SysConfigService.HTTP_MESSAGE_ENCRYPT_SECRET = systemYmlConfig.getHttpMessageEncryptSecret();
|
||||
SysConfigService.HTTP_MESSAGE_ENCRYPT_SM4 = SmUtil.sm4(SysConfigService.HTTP_MESSAGE_ENCRYPT_SECRET.getBytes());
|
||||
|
||||
// 配置是否通信加密 和 密码修改
|
||||
SysConfigService.HTTP_MSG_IS_ENCRYPT = sysConfigService.getDBSecurityConfig().httpMsgIsEncrypt();
|
||||
SysConfigService.PWD_EXPIRED_MUST_RESET = sysConfigService.getDBSecurityConfig().passwordExpiredIsMustModify();
|
||||
|
||||
// 配置 平台通信秘钥
|
||||
SysConfigService.PLATFORM_API_SECRET = sysConfigService.getDBSecurityConfig().getPlatformApiSecret();
|
||||
|
||||
//初始化处理fastjson格式
|
||||
SerializeConfig serializeConfig = SerializeConfig.getGlobalInstance();
|
||||
serializeConfig.put(Date.class, new SimpleDateFormatSerializer(DatePattern.NORM_DATETIME_PATTERN));
|
||||
|
||||
//解决json 序列化时候的 $ref:问题
|
||||
JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.DisableCircularReferenceDetect.getMask();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
package com.jeequan.jeepay.pay.bootstrap;
|
||||
|
||||
import com.alibaba.fastjson.parser.ParserConfig;
|
||||
import com.alibaba.fastjson.support.config.FastJsonConfig;
|
||||
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import com.jeequan.jeepay.core.task.XxlJobExecutorProp;
|
||||
import com.jeequan.jeepay.pay.config.SystemYmlConfig;
|
||||
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
|
||||
import org.hibernate.validator.HibernateValidator;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
|
||||
import javax.validation.Validation;
|
||||
import javax.validation.Validator;
|
||||
import javax.validation.ValidatorFactory;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @Author terrfly
|
||||
* @Date 2019/11/7 15:19
|
||||
* @Description spring-boot 主启动程序
|
||||
**/
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
@MapperScan("com.jeequan.jeepay.service.mapper") //Mybatis mapper接口路径
|
||||
@ComponentScan(basePackages = "com.jeequan.jeepay.*") //由于MainApplication没有在项目根目录, 需要配置basePackages属性使得成功扫描所有Spring组件;
|
||||
@Configuration
|
||||
public class JeepayPayApplication {
|
||||
|
||||
@Autowired private SystemYmlConfig systemYmlConfig;
|
||||
|
||||
/** main启动函数 **/
|
||||
public static void main(String[] args) {
|
||||
|
||||
//启动项目
|
||||
SpringApplication.run(JeepayPayApplication.class, args);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** fastJson 配置信息 **/
|
||||
@Bean
|
||||
public HttpMessageConverters fastJsonConfig(){
|
||||
|
||||
// 开启 FastJSON 安全模式!
|
||||
ParserConfig.getGlobalInstance().setSafeMode(true);
|
||||
|
||||
//新建fast-json转换器
|
||||
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
|
||||
|
||||
//fast-json 配置信息
|
||||
FastJsonConfig config = new FastJsonConfig();
|
||||
config.setDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
converter.setFastJsonConfig(config);
|
||||
|
||||
//设置响应的 Content-Type
|
||||
converter.setSupportedMediaTypes(Arrays.asList(new MediaType[]{MediaType.APPLICATION_JSON, MediaType.APPLICATION_JSON_UTF8}));
|
||||
return new HttpMessageConverters(converter);
|
||||
}
|
||||
|
||||
/** Mybatis plus 分页插件 **/
|
||||
@Bean
|
||||
public MybatisPlusInterceptor paginationInterceptor() {
|
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||
// 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false
|
||||
// paginationInterceptor.setOverflow(false);
|
||||
// 设置最大单页限制数量,默认 500 条,-1 不受限制
|
||||
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
|
||||
interceptor.addInnerInterceptor(paginationInnerInterceptor);
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
public LocalValidatorFactoryBean validatorFactoryBean(){
|
||||
return new LocalValidatorFactoryBean();
|
||||
}
|
||||
|
||||
|
||||
/** 默认为 失败快速返回模式 **/
|
||||
@Bean
|
||||
public Validator validator(){
|
||||
|
||||
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
|
||||
.configure()
|
||||
.failFast( true )
|
||||
.buildValidatorFactory();
|
||||
return validatorFactory.getValidator();
|
||||
}
|
||||
|
||||
/** 允许跨域请求 **/
|
||||
@Bean
|
||||
public CorsFilter corsFilter() {
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
|
||||
if(systemYmlConfig.getAllowCors()){
|
||||
CorsConfiguration config = new CorsConfiguration();
|
||||
config.setAllowCredentials(true); //带上cookie信息
|
||||
// config.addAllowedOrigin(CorsConfiguration.ALL); //允许跨域的域名, *表示允许任何域名使用
|
||||
config.addAllowedOriginPattern(CorsConfiguration.ALL); //使用addAllowedOriginPattern 避免出现 When allowCredentials is true, allowedOrigins cannot contain the special value "*" since that cannot be set on the "Access-Control-Allow-Origin" response header. To allow credentials to a set of origins, list them explicitly or consider using "allowedOriginPatterns" instead.
|
||||
config.addAllowedHeader(CorsConfiguration.ALL); //允许任何请求头
|
||||
config.addAllowedMethod(CorsConfiguration.ALL); //允许任何方法(post、get等)
|
||||
source.registerCorsConfiguration("/**", config); // CORS 配置对所有接口都有效
|
||||
}
|
||||
return new CorsFilter(source);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*** 注入 定时任务 执行器 **/
|
||||
|
||||
/** xxl-job执行器配置信息 **/
|
||||
@ConfigurationProperties(prefix = "xxl-job.executor")
|
||||
@Bean
|
||||
public XxlJobExecutorProp xxlJobExecutorProp() { return new XxlJobExecutorProp(); }
|
||||
|
||||
@Autowired
|
||||
private XxlJobExecutorProp xxlJobExecutorProp;
|
||||
|
||||
@Bean
|
||||
public XxlJobSpringExecutor xxlJobExecutor() {
|
||||
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
|
||||
xxlJobSpringExecutor.setAdminAddresses(xxlJobExecutorProp.getAdminAddress());
|
||||
xxlJobSpringExecutor.setAppname(xxlJobExecutorProp.getAppname());
|
||||
|
||||
if(xxlJobExecutorProp.getPort() != null){
|
||||
xxlJobSpringExecutor.setPort(xxlJobExecutorProp.getPort());
|
||||
}
|
||||
|
||||
xxlJobSpringExecutor.setAccessToken(xxlJobExecutorProp.getAccessToken());
|
||||
xxlJobSpringExecutor.setLogPath(xxlJobExecutorProp.getLogPath());
|
||||
xxlJobSpringExecutor.setLogRetentionDays(xxlJobExecutorProp.getLogretentiondays());
|
||||
return xxlJobSpringExecutor;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.jeequan.jeepay.pay.config;
|
||||
|
||||
import com.jeequan.jeepay.core.cache.RedisUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
|
||||
/*
|
||||
* RedisConfig
|
||||
*
|
||||
* @author terrfly
|
||||
*
|
||||
* @date 2021/6/8 17:25
|
||||
*/
|
||||
@Configuration
|
||||
public class RedisConfig {
|
||||
|
||||
@Value("${spring.redis.host}")
|
||||
private String host;
|
||||
|
||||
@Value("${spring.redis.port}")
|
||||
private Integer port;
|
||||
|
||||
@Value("${spring.redis.timeout}")
|
||||
private Integer timeout;
|
||||
|
||||
@Value("${spring.redis.database}")
|
||||
private Integer defaultDatabase;
|
||||
|
||||
@Value("${spring.redis.password}")
|
||||
private String password;
|
||||
|
||||
/** 作用:不同系统的前缀。 a.当连接不同的database时可以为空(物理隔离); b.当redis集群时因为必须同一个database,所以需通过前缀区分不同系统的业务。 **/
|
||||
@Value("${spring.redis.sys-prefix-key}")
|
||||
private String sysPrefixKey;
|
||||
|
||||
/** 当前系统的redis缓存操作对象 (主对象) **/
|
||||
@Primary
|
||||
@Bean(name = "defaultStringRedisTemplate")
|
||||
public StringRedisTemplate sysStringRedisTemplate() {
|
||||
|
||||
// 赋值前缀key
|
||||
RedisUtil.SYS_PREFIX_KEY = sysPrefixKey;
|
||||
|
||||
StringRedisTemplate template = new StringRedisTemplate();
|
||||
|
||||
LettuceConnectionFactory jedisConnectionFactory = new LettuceConnectionFactory();
|
||||
jedisConnectionFactory.setHostName(host);
|
||||
jedisConnectionFactory.setPort(port);
|
||||
jedisConnectionFactory.setTimeout(timeout);
|
||||
|
||||
if (!StringUtils.isEmpty(password)) {
|
||||
jedisConnectionFactory.setPassword(password);
|
||||
}
|
||||
|
||||
if (defaultDatabase != 0) {
|
||||
jedisConnectionFactory.setDatabase(defaultDatabase);
|
||||
}
|
||||
|
||||
jedisConnectionFactory.afterPropertiesSet();
|
||||
|
||||
template.setConnectionFactory(jedisConnectionFactory);
|
||||
return template;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.jeequan.jeepay.pay.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 系统Yml配置参数定义Bean
|
||||
*
|
||||
* @author terrfly
|
||||
*
|
||||
* @date 2021-04-27 15:50
|
||||
*/
|
||||
@Component
|
||||
@ConfigurationProperties(prefix="isys")
|
||||
@Data
|
||||
public class SystemYmlConfig {
|
||||
|
||||
/** 是否允许跨域请求 [生产环境建议关闭, 若api与前端项目没有在同一个域名下时,应开启此配置或在nginx统一配置允许跨域] **/
|
||||
private Boolean allowCors;
|
||||
|
||||
/** DB SM4 加解密秘钥 (必须16位) [每个系统配置必须相同,否则加解密不一致导致业务异常!] **/
|
||||
private String dbEncryptSecret;
|
||||
|
||||
/** web传输加解密 秘钥 (必须16位) [每个系统配置必须相同,否则加解密不一致导致业务异常!] **/
|
||||
private String httpMessageEncryptSecret;
|
||||
|
||||
/** 支付网关的公钥和私钥(系统级别!), 请妥善保存,用于回调商户的商户侧的验证, 首次设置好之后不可随意变更! **/
|
||||
private String sysRSA2PrivateKey;
|
||||
|
||||
/**支付网关的公钥和私钥(系统级别!), 请妥善保存,用于回调商户的商户侧的验证, 首次设置好之后不可随意变更! **/
|
||||
private String sysRSA2PublicKey;
|
||||
|
||||
/** 是否内存缓存配置信息: true表示开启如支付网关地址/商户应用配置/服务商配置等, 开启后需检查MQ的广播模式是否正常; false表示直接查询DB. **/
|
||||
private Boolean cacheConfig;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.jeequan.jeepay.pay.config;
|
||||
|
||||
import com.jeequan.jeepay.core.config.WebConfig;
|
||||
import org.springframework.boot.validation.MessageInterpolatorFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Role;
|
||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||
|
||||
/*
|
||||
* webmvc配置
|
||||
*
|
||||
* @author terrfly
|
||||
*
|
||||
* @date 2021/6/8 17:12
|
||||
*/
|
||||
@Configuration
|
||||
public class WebmvcConfig extends WebConfig {
|
||||
|
||||
@Bean
|
||||
@Role(2)
|
||||
public static LocalValidatorFactoryBean defaultValidator() {
|
||||
LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
|
||||
MessageInterpolatorFactory interpolatorFactory = new MessageInterpolatorFactory();
|
||||
factoryBean.setMessageInterpolator(interpolatorFactory.getObject());
|
||||
return factoryBean;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package com.jeequan.jeepay.pay.ctrl;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.ctrls.AbstractCtrl;
|
||||
import com.jeequan.jeepay.core.entity.MchApp;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.interfaces.IConfigContextQueryService;
|
||||
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
|
||||
import com.jeequan.jeepay.core.model.rqrs.AbstractMchAppRQ;
|
||||
import com.jeequan.jeepay.core.model.rqrs.AbstractRQ;
|
||||
import com.jeequan.jeepay.core.utils.JeepayKit;
|
||||
import com.jeequan.jeepay.core.utils.JeepayRSA2Kit;
|
||||
import com.jeequan.jeepay.pay.service.ValidateService;
|
||||
import com.jeequan.jeepay.service.impl.MchConfigService;
|
||||
import com.jeequan.jeepay.service.impl.SysConfigService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
* api 抽象接口, 公共函数
|
||||
*
|
||||
* @author terrfly
|
||||
*
|
||||
* @date 2021/6/8 17:28
|
||||
*/
|
||||
public abstract class ApiController extends AbstractCtrl {
|
||||
|
||||
@Autowired private ValidateService validateService;
|
||||
@Autowired private IConfigContextQueryService configContextQueryService;
|
||||
@Autowired private MchConfigService mchConfigService;
|
||||
|
||||
|
||||
/** 获取请求参数并转换为对象,通用验证 **/
|
||||
protected <T extends AbstractRQ> T getRQ(Class<T> cls){
|
||||
|
||||
T bizRQ = getObject(cls);
|
||||
|
||||
// [1]. 验证通用字段规则
|
||||
validateService.validate(bizRQ);
|
||||
|
||||
return bizRQ;
|
||||
}
|
||||
|
||||
|
||||
/** 获取请求参数并转换为对象,商户通用验证 **/
|
||||
protected <T extends AbstractRQ> T getRQByWithMchSign(Class<T> cls){
|
||||
|
||||
//获取请求RQ, and 通用验证
|
||||
T bizRQ = getRQ(cls);
|
||||
|
||||
AbstractMchAppRQ abstractMchAppRQ = (AbstractMchAppRQ)bizRQ;
|
||||
|
||||
//业务校验, 包括: 验签, 商户状态是否可用, 是否支持该支付方式下单等。
|
||||
String mchNo = abstractMchAppRQ.getMchNo();
|
||||
String appId = abstractMchAppRQ.getAppId();
|
||||
String sign = bizRQ.getSign();
|
||||
|
||||
if(StringUtils.isAnyBlank(mchNo, appId, sign)){
|
||||
throw new BizException("参数有误!");
|
||||
}
|
||||
|
||||
// pas非空: 验证通信秘钥
|
||||
if(StringUtils.isNotEmpty(abstractMchAppRQ.getPas())){
|
||||
if(!abstractMchAppRQ.getPas().equals(SysConfigService.PLATFORM_API_SECRET)){
|
||||
throw new BizException("通信秘钥错误!");
|
||||
}
|
||||
}else{ // pas是空的,说明商户自调用: 检查是否有接口权限
|
||||
|
||||
if(!mchConfigService.queryMchHasApiEnt(mchNo, abstractMchAppRQ.apiName())){
|
||||
throw new BizException("商户无此接口["+abstractMchAppRQ.apiName()+"]权限!");
|
||||
}
|
||||
}
|
||||
|
||||
MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfo(mchNo, appId);
|
||||
|
||||
if(mchAppConfigContext == null){
|
||||
throw new BizException("商户或商户应用不存在");
|
||||
}
|
||||
|
||||
if(mchAppConfigContext.getMchInfo() == null || mchAppConfigContext.getMchInfo().getState() != CS.YES){
|
||||
throw new BizException("商户信息不存在或商户状态不可用");
|
||||
}
|
||||
|
||||
MchApp mchApp = mchAppConfigContext.getMchApp();
|
||||
if(mchApp == null || mchApp.getState() != CS.YES){
|
||||
throw new BizException("商户应用不存在或应用状态不可用");
|
||||
}
|
||||
|
||||
if(!mchApp.getMchNo().equals(mchNo)){
|
||||
throw new BizException("参数appId与商户号不匹配");
|
||||
}
|
||||
|
||||
if(StringUtils.isEmpty(mchApp.getAppSignType())){
|
||||
throw new BizException("商户未配置签名方式");
|
||||
}
|
||||
|
||||
List<String> appSignType = JSONArray.parseArray(mchApp.getAppSignType(), String.class);
|
||||
if(!appSignType.contains(abstractMchAppRQ.getSignType().toUpperCase())){
|
||||
throw new BizException("商户不支持["+abstractMchAppRQ.getSignType()+"]签名方式");
|
||||
}
|
||||
|
||||
// 转换为 JSON
|
||||
JSONObject bizReqJSON = (JSONObject)JSONObject.toJSON(bizRQ);
|
||||
bizReqJSON.remove("sign");
|
||||
|
||||
// 验签
|
||||
if(MchApp.SIGN_TYPE_MD5.equalsIgnoreCase(abstractMchAppRQ.getSignType())){
|
||||
String appSecret = mchApp.getAppSecret();
|
||||
|
||||
if(!sign.equalsIgnoreCase(JeepayKit.getSign(bizReqJSON, appSecret))){
|
||||
throw new BizException("验签失败");
|
||||
}
|
||||
|
||||
}else if(MchApp.SIGN_TYPE_RSA2.equalsIgnoreCase(abstractMchAppRQ.getSignType())){
|
||||
String appRsa2PublicKey = mchApp.getAppRsa2PublicKey();
|
||||
try {
|
||||
if(!JeepayRSA2Kit.verify(bizReqJSON, appRsa2PublicKey, abstractMchAppRQ.getSign())){
|
||||
throw new BizException("验签失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new BizException("验签异常");
|
||||
}
|
||||
}else{
|
||||
throw new BizException("不支持的签名方式");
|
||||
}
|
||||
|
||||
return bizRQ;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.jeequan.jeepay.pay.ctrl;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import com.jeequan.jeepay.core.ctrls.AbstractCtrl;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
/*
|
||||
* 通用处理
|
||||
*
|
||||
* @author jmdhappy
|
||||
*
|
||||
* @date 2021/12/2 17:26
|
||||
*/
|
||||
@Slf4j
|
||||
@Controller
|
||||
@RequestMapping("/api/common")
|
||||
public class CommonController extends AbstractCtrl {
|
||||
|
||||
/**
|
||||
* 跳转到支付页面(适合网关支付form表单输出)
|
||||
* @param payData
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping(value = "/payForm/{payData}")
|
||||
private String toPayForm(@PathVariable("payData") String payData){
|
||||
request.setAttribute("payHtml", Base64.decodeStr(payData));
|
||||
return "common/toPay";
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转到支付页面(适合微信H5跳转与referer一致)
|
||||
* @param payData
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping(value = "/payUrl/{payData}")
|
||||
private String toPayUrl(@PathVariable("payData") String payData) {
|
||||
String payUrl = Base64.decodeStr(payData);
|
||||
request.setAttribute("payHtml", "<script>window.location.href = '"+payUrl+"';</script>");
|
||||
return "common/toPay";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.jeequan.jeepay.pay.ctrl;
|
||||
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONException;
|
||||
import com.jeequan.jeepay.converter.MchInfoConverter;
|
||||
import com.jeequan.jeepay.core.ctrls.AbstractCtrl;
|
||||
import com.jeequan.jeepay.core.exception.AccessMsgException;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.exception.openapi.ParamException;
|
||||
import com.jeequan.jeepay.core.model.openapi.AccessError;
|
||||
import com.jeequan.jeepay.core.model.openapi.AccessReq;
|
||||
import com.jeequan.jeepay.core.model.openapi.AccessResp;
|
||||
import com.jeequan.jeepay.db.entity.MchAppEntity;
|
||||
import com.jeequan.jeepay.pay.service.ValidateService;
|
||||
import com.jeequan.jeepay.pay.util.ApiResKit;
|
||||
import com.jeequan.jeepay.service.impl.MchAppService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* api 抽象接口, 公共函数
|
||||
* @author crystal
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class OpenApiController extends AbstractCtrl {
|
||||
|
||||
@Autowired private ValidateService validateService;
|
||||
@Autowired private MchAppService mchAppService;
|
||||
@Autowired private MchInfoConverter mchInfoConverter;
|
||||
|
||||
|
||||
/** 获取请求参数并转换为对象,通用验证 **/
|
||||
protected AccessReq getRQ(){
|
||||
AccessReq bizRQ = getObject(AccessReq.class);
|
||||
// [1]. 验证通用字段规则
|
||||
validateService.checkV1(bizRQ);
|
||||
preCheck(bizRQ);
|
||||
return bizRQ;
|
||||
}
|
||||
|
||||
/** 获取请求参数并转换为对象,通用验证 **/
|
||||
protected AccessResp getRS(AccessReq bizRQ, Object bizRet){
|
||||
return AccessResp.success(bizRQ, bizRet);
|
||||
}
|
||||
|
||||
/**
|
||||
* 公共参数校验
|
||||
* @param bizRQ
|
||||
*/
|
||||
protected void preCheck(AccessReq bizRQ){
|
||||
log.info("【apiV2】接口请求参数:{}", JSON.toJSONString(bizRQ));
|
||||
MchAppEntity appEntity = mchAppService.getById(bizRQ.getAppId());
|
||||
|
||||
if (!ObjUtil.isEmpty(bizRQ.getBizData())) {
|
||||
try {
|
||||
bizRQ.setBizDataJSON(JSON.parseObject(bizRQ.getBizData()));
|
||||
} catch (JSONException e) {
|
||||
log.info("业务参数格式异常, 业务参数: {}", bizRQ.getBizData());
|
||||
throw new BizException("业务参数格式异常");
|
||||
}
|
||||
}
|
||||
|
||||
if(appEntity == null){
|
||||
throw new ParamException("未知的应用[appId]参数");
|
||||
}
|
||||
|
||||
bizRQ.setAppInfo(mchInfoConverter.toModel(appEntity));
|
||||
bizRQ.setPlatPriKey(ApiResKit.getKey());
|
||||
if(MchAppEntity.NORMAL.byteValue() != appEntity.getState()){
|
||||
throw new AccessMsgException(AccessError.stateError("应用已被停用或已被关闭,不支持当前操作"));
|
||||
}
|
||||
List<String> appSignType = JSON.parseArray(appEntity.getAppSignType(), String.class);
|
||||
if(!appSignType.contains(bizRQ.getSignType())){
|
||||
throw new ParamException("非法的签名类型[signType]参数");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.jeequan.jeepay.pay.ctrl;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
/***
|
||||
* 静态资源ctrl
|
||||
* 建议使用Nginx进行跳转, 若没有配置Nginx则使用该ctrl进行跳转到: 对应的static静态页面。 比如cashier的vue history模式;
|
||||
*
|
||||
* @author terrfly
|
||||
*
|
||||
* @date 2021/12/17 19:29
|
||||
*/
|
||||
@Controller
|
||||
public class StaticController {
|
||||
|
||||
/** 聚合码收银台页面 **/
|
||||
@RequestMapping(value = "/pages/**")
|
||||
public String cashier(){
|
||||
return "forward://cashier/index.html";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.advert;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.ctrls.AbstractCtrl;
|
||||
import com.jeequan.jeepay.core.model.ApiRes;
|
||||
import com.jeequan.jeepay.db.entity.SysAdvertConfig;
|
||||
import com.jeequan.jeepay.service.impl.SysAdvertConfigService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 广告
|
||||
*
|
||||
* @author xiaoyu
|
||||
*
|
||||
* @date 2023/2/21 16:13
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/advert")
|
||||
public class AdvertController extends AbstractCtrl {
|
||||
|
||||
@Autowired SysAdvertConfigService advertConfigService;
|
||||
|
||||
/** 支付后广告 **/
|
||||
@GetMapping("")
|
||||
public ApiRes afterDevice() {
|
||||
Byte appPlace = getValByteDefault("appPlace", SysAdvertConfig.APP_PLACE_LITE);
|
||||
// 查询平台全局广告
|
||||
LambdaQueryWrapper<SysAdvertConfig> wrapper = SysAdvertConfig.gw();
|
||||
wrapper.eq(SysAdvertConfig::getAdvertType, SysAdvertConfig.ADVERT_TYPE_AFTER);
|
||||
wrapper.eq(SysAdvertConfig::getReleaseState, CS.YES);
|
||||
wrapper.eq(appPlace != null, SysAdvertConfig::getAppPlace, appPlace);
|
||||
wrapper.orderByDesc(SysAdvertConfig::getCreatedAt);
|
||||
List<SysAdvertConfig> list = advertConfigService.list(wrapper);
|
||||
if (list.size() > 0) {
|
||||
return ApiRes.ok(list.get(0));
|
||||
}
|
||||
return ApiRes.ok();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.applyment;
|
||||
|
||||
import com.jeequan.jeepay.components.mq.model.MchAuditThirdNotifyMQ;
|
||||
import com.jeequan.jeepay.components.mq.vender.IMQSender;
|
||||
import com.jeequan.jeepay.converter.MchInfoConverter;
|
||||
import com.jeequan.jeepay.core.ctrls.AbstractCtrl;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.exception.ResponseException;
|
||||
import com.jeequan.jeepay.core.interfaces.paychannel.IIsvmchApplymentNotifyService;
|
||||
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
|
||||
import com.jeequan.jeepay.db.entity.MchApplyment;
|
||||
import com.jeequan.jeepay.service.impl.MchApplymentService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.MutablePair;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 进件接口渠道侧的通知入口Controller
|
||||
*
|
||||
* @author terrfly
|
||||
* @date 2022/1/8 15:26
|
||||
*/
|
||||
@Slf4j
|
||||
@Controller
|
||||
public class MchApplymentChannelNotifyController extends AbstractCtrl {
|
||||
|
||||
@Autowired
|
||||
private MchApplymentService mchApplymentService;
|
||||
|
||||
@Autowired
|
||||
private IMQSender mqSender;
|
||||
|
||||
@Autowired
|
||||
private MchInfoConverter mchInfoConverter;
|
||||
|
||||
/**
|
||||
* 异步回调入口
|
||||
*
|
||||
* @param request
|
||||
* @param ifCode
|
||||
* @param customApplyId 通道方返回的参数,一般来说,该值由我方自定义。若回调地址不可变更,该参数将不存在
|
||||
* @return
|
||||
*/
|
||||
@ResponseBody
|
||||
@RequestMapping(value = {"/api/mchApplyment/notify/{ifCode}", "/api/mchApplyment/notify/{ifCode}/{customApplyId}"})
|
||||
public ResponseEntity doNotify(HttpServletRequest request, @PathVariable("ifCode") String ifCode, @PathVariable(value = "customApplyId", required = false) String customApplyId) {
|
||||
|
||||
String applyId = null;
|
||||
String logPrefix = "进入[" + ifCode + "]进件回调:urlApplyId:[" + StringUtils.defaultIfEmpty(customApplyId, "") + "] ";
|
||||
log.info("===== {} =====", logPrefix);
|
||||
|
||||
IIsvmchApplymentNotifyService notifyService = null;
|
||||
try {
|
||||
|
||||
// 参数有误
|
||||
if (StringUtils.isEmpty(ifCode)) {
|
||||
return ResponseEntity.badRequest().body("ifCode is empty");
|
||||
}
|
||||
|
||||
//查询支付接口是否存在
|
||||
notifyService = SpringBeansUtil.getBean(ifCode + "IsvmchApplymentNotifyService", IIsvmchApplymentNotifyService.class);
|
||||
|
||||
// 解析申请单号 和 请求参数
|
||||
MutablePair<String, Object> mutablePair = notifyService.parseParams(request, customApplyId);
|
||||
if (mutablePair == null) { // 解析数据失败, 响应已处理
|
||||
log.error("{}, mutablePair is null ", logPrefix);
|
||||
throw new BizException("解析数据异常!"); //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
|
||||
}
|
||||
|
||||
//解析到订单号
|
||||
applyId = mutablePair.left;
|
||||
log.info("{}, 解析数据为:applyId:{}, params:{}", logPrefix, applyId, mutablePair.getRight());
|
||||
|
||||
//获取进件数据 和 订单数据
|
||||
MchApplyment dbTbMchApplyment = mchApplymentService.getById(applyId);
|
||||
|
||||
// 订单不存在
|
||||
if (dbTbMchApplyment == null) {
|
||||
log.error("{}, 进件数据不存在. applyId={} ", logPrefix, applyId);
|
||||
return ResponseEntity.ok("SUCCESS");
|
||||
}
|
||||
|
||||
// 状态不是待审核, 先去掉看看效果
|
||||
// if(dbTbMchApplyment.getState() != MchApplyment.STATE_AUDITING){
|
||||
// log.error("{}, 进件状态不是审核中. applyId={}, state={}", logPrefix, applyId, dbTbMchApplyment.getState());
|
||||
// return notifyService.retOk(mutablePair.getRight());
|
||||
// }
|
||||
|
||||
//调起接口的回调判断
|
||||
MutablePair<com.jeequan.jeepay.core.entity.MchApplyment, ResponseEntity> notifyResult = notifyService.doNotify(request, mutablePair.getRight(), mchInfoConverter.toModel(dbTbMchApplyment));
|
||||
|
||||
if (notifyResult.left == null) {
|
||||
return notifyService.retOk(null);
|
||||
}
|
||||
|
||||
MchApplyment result = mchInfoConverter.toDbEntity(notifyResult.left);
|
||||
result.setApplyId(dbTbMchApplyment.getApplyId());
|
||||
mchApplymentService.updateById(result);
|
||||
|
||||
// 自动配置商户应用参数,进件成功 && 状态发生变化 && 自动配置应用不为空
|
||||
if ((result.getState() == MchApplyment.STATE_SUCCESS || result.getState() == MchApplyment.STATE_SUCCESS_NEED_SECOND_VERIFY) && !result.getState().equals(dbTbMchApplyment.getState())) {
|
||||
|
||||
mchApplymentService.onApplymentSuccess(result.getApplyId());
|
||||
mqSender.send(MchAuditThirdNotifyMQ.build(result.getApplyId(), MchAuditThirdNotifyMQ.TYPE_AUDIT));
|
||||
}
|
||||
|
||||
log.info("===== {}, 进件通知完成。 applyId={}, parseState = {} =====", logPrefix, applyId, result.getState());
|
||||
return notifyService.retOk(mutablePair.getRight());
|
||||
|
||||
} catch (BizException e) {
|
||||
log.error("{}, applyId={}, BizException", logPrefix, applyId, e);
|
||||
|
||||
if (notifyService != null) {
|
||||
return notifyService.retOk(null);
|
||||
}
|
||||
} catch (ResponseException e) {
|
||||
log.error("{}, applyId={}, ResponseException", logPrefix, applyId, e);
|
||||
|
||||
if (notifyService != null) {
|
||||
return notifyService.retOk(null);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("{}, applyId={}, 系统异常", logPrefix, applyId, e);
|
||||
|
||||
if (notifyService != null) {
|
||||
return notifyService.retOk(null);
|
||||
}
|
||||
}
|
||||
|
||||
return ResponseEntity.ok("SUCCESS");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.aqf;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alipay.api.AlipayApiException;
|
||||
import com.jeequan.jeepay.core.ctrls.AbstractCtrl;
|
||||
import com.jeequan.jeepay.core.model.ApiRes;
|
||||
import com.jeequan.jeepay.db.entity.TransferOrderEntity;
|
||||
import com.jeequan.jeepay.service.impl.SysConfigService;
|
||||
import com.jeequan.jeepay.service.impl.TransferInterfaceConfigService;
|
||||
import com.jeequan.jeepay.service.impl.TransferOrderService;
|
||||
import com.jeequan.jeepay.service.impl.TransferSubjectService;
|
||||
import com.jeequan.jeepay.thirdparty.channel.alipay.AliAqfV2Service;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.Date;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
public class AliAqfController extends AbstractCtrl {
|
||||
|
||||
|
||||
@Autowired
|
||||
protected TransferInterfaceConfigService transferInterfaceConfigService;
|
||||
@Autowired
|
||||
private TransferSubjectService transferSubjectService;
|
||||
@Autowired
|
||||
private AliAqfV2Service aliAqfV2Service;
|
||||
@Autowired
|
||||
private SysConfigService sysConfigService;
|
||||
@Autowired
|
||||
private TransferOrderService transferOrderService;
|
||||
|
||||
/**
|
||||
* 解约回调方法
|
||||
* @param request
|
||||
* @throws AlipayApiException
|
||||
*/
|
||||
@RequestMapping("/api/anf/reqUnsign")
|
||||
public String reqUnsign(HttpServletRequest request) {
|
||||
|
||||
// TODO: 2024/4/25 回调参数暂时先不处理!!
|
||||
/* logger.info("进入了安全发的解约回调!!!");
|
||||
JSONObject reqParamJSON = getReqParamJSON();
|
||||
TransferSubjectEntity subjectEntity = transferSubjectService.getOne(TransferSubjectEntity.gw().eq(TransferSubjectEntity::getExternalAgreementNo, reqParamJSON.getString("external_agreement_no")));
|
||||
try {
|
||||
//验签
|
||||
boolean resut = getcertAliPayRequest(reqParamJSON);
|
||||
if (!resut){
|
||||
logger.error("安全发回调验签失败:"+ reqParamJSON.toJSONString());
|
||||
subjectEntity.setRemark("安全发解约回调验签失败");
|
||||
}else {
|
||||
logger.info("签约回调通知参数为:"+ reqParamJSON.toJSONString());
|
||||
//已解约
|
||||
subjectEntity.setSignState(3);
|
||||
}
|
||||
}catch (Exception e){
|
||||
logger.error("安全发签约回调处理数据出现异常!!");
|
||||
subjectEntity.setRemark("安全发签约回调处理数据出现异常");
|
||||
}
|
||||
logger.info("解约回调通知参数为:"+ reqParamJSON.toJSONString());
|
||||
transferSubjectService.updateById(subjectEntity);*/
|
||||
return "success";
|
||||
}
|
||||
|
||||
/**
|
||||
* 资金专项拨入,代发回调
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping("/api/anf/orderChanged")
|
||||
public ApiRes orderChanged(String request) {
|
||||
JSONObject reqParamJSON = getReqParamJSON();
|
||||
log.info("【】回调参数"+reqParamJSON);
|
||||
|
||||
String input = reqParamJSON.getString("input");
|
||||
String[] requestSplit = input.split("&");
|
||||
|
||||
for (String requestSplitFor : requestSplit) {
|
||||
String[] real = requestSplitFor.split("=");
|
||||
if (real.length == 2){
|
||||
try {
|
||||
reqParamJSON.put(URLDecoder.decode(real[0], "UTF-8"),URLDecoder.decode(real[1], "UTF-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
log.error("解析回调参数异常!!");
|
||||
return ApiRes.customFail("解析回调参数异常!");
|
||||
}
|
||||
}else {
|
||||
return ApiRes.customFail("参数格式不正确!");
|
||||
}
|
||||
}
|
||||
log.info("解析后的回调参数"+reqParamJSON);
|
||||
JSONObject bizContent = reqParamJSON.getJSONObject("biz_content");
|
||||
TransferOrderEntity transferOrder = transferOrderService.getById(bizContent.getString("out_biz_no"));
|
||||
switch (bizContent.getString("status")){
|
||||
case "SUCCESS" :
|
||||
transferOrder.setState(TransferOrderEntity.STATE_SUCCESS);
|
||||
transferOrder.setSuccessTime(new Date());
|
||||
if (bizContent.containsKey("order_id")){
|
||||
transferOrder.setChannelOrderNo(bizContent.getString("order_id"));
|
||||
}
|
||||
if (bizContent.containsKey("pay_fund_order_id")){
|
||||
transferOrder.setFlowNo(bizContent.getString("pay_fund_order_id"));
|
||||
}
|
||||
break;
|
||||
case "WAIT_PAY" :
|
||||
transferOrder.setState(TransferOrderEntity.STATE_ING);
|
||||
break;
|
||||
case "CLOSED" :
|
||||
transferOrder.setState(TransferOrderEntity.STATE_CLOSED);
|
||||
break;
|
||||
case "FAIL" :
|
||||
transferOrder.setState(TransferOrderEntity.STATE_FAIL);
|
||||
break;
|
||||
case "DEALING" :
|
||||
transferOrder.setState(TransferOrderEntity.STATE_ING);
|
||||
break;
|
||||
default:
|
||||
transferOrder.setState(TransferOrderEntity.STATE_ING);
|
||||
break;
|
||||
}
|
||||
transferOrder.setChannelExtra(reqParamJSON.toJSONString());
|
||||
transferOrderService.updateById(transferOrder);
|
||||
return ApiRes.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 代发到户回调 暂时无用!
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping("/api/anf/accountOrderChanged")
|
||||
public ApiRes accountOrderChanged(String request) {
|
||||
|
||||
logger.info("进入了代发到户回调!!!");
|
||||
log.info("代发到户回调参数"+request);
|
||||
JSONObject reqParamJSON = new JSONObject();
|
||||
|
||||
String[] requestSplit = request.split("&");
|
||||
for (String requestSplitFor : requestSplit) {
|
||||
String[] real = requestSplitFor.split("=");
|
||||
if (real.length == 2){
|
||||
try {
|
||||
reqParamJSON.put(URLDecoder.decode(real[0], "UTF-8"),URLDecoder.decode(real[1], "UTF-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
log.error("解析回调参数异常!!");
|
||||
return ApiRes.customFail("解析回调参数异常!");
|
||||
}
|
||||
}
|
||||
}
|
||||
log.info("解析后的代发到户回调参数"+reqParamJSON);
|
||||
TransferOrderEntity transferOrder = transferOrderService.getOne(TransferOrderEntity.gw().eq(TransferOrderEntity::getChannelOrderNo, reqParamJSON.getJSONObject("biz_content").getString("order_id")));
|
||||
switch (reqParamJSON.getJSONObject("biz_content").getString("status")){
|
||||
case "SUCCESS" :
|
||||
transferOrder.setState((byte) 2);
|
||||
transferOrder.setSuccessTime(new Date());
|
||||
break;
|
||||
case "WAIT_PAY" :
|
||||
transferOrder.setState((byte) 1);
|
||||
break;
|
||||
case "CLOSED" :
|
||||
transferOrder.setState((byte) 4);
|
||||
break;
|
||||
case "FAIL" :
|
||||
transferOrder.setState((byte) 3);
|
||||
break;
|
||||
case "DEALING" :
|
||||
transferOrder.setState((byte) 1);
|
||||
break;
|
||||
default:
|
||||
transferOrder.setState((byte) 1);
|
||||
break;
|
||||
}
|
||||
transferOrder.setChannelExtra(reqParamJSON.toJSONString());
|
||||
transferOrderService.updateById(transferOrder);
|
||||
return ApiRes.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 代发到卡 暂时无用!
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping("/api/anf/cardOrderChanged")
|
||||
public ApiRes cardOrderChanged(String request) {
|
||||
|
||||
logger.info("进入了资金专项拨入回调!!!");
|
||||
log.info("专项资金拨入回调参数"+request);
|
||||
JSONObject reqParamJSON = new JSONObject();
|
||||
|
||||
String[] requestSplit = request.split("&");
|
||||
for (String requestSplitFor : requestSplit) {
|
||||
String[] real = requestSplitFor.split("=");
|
||||
if (real.length == 2){
|
||||
try {
|
||||
reqParamJSON.put(URLDecoder.decode(real[0], "UTF-8"),URLDecoder.decode(real[1], "UTF-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
log.error("解析回调参数异常!!");
|
||||
return ApiRes.customFail("解析回调参数异常!");
|
||||
}
|
||||
}
|
||||
}
|
||||
log.info("解析后的专项资金拨入回调参数"+reqParamJSON);
|
||||
TransferOrderEntity transferOrder = transferOrderService.getOne(TransferOrderEntity.gw().eq(TransferOrderEntity::getChannelOrderNo, reqParamJSON.getJSONObject("biz_content").getString("order_id")));
|
||||
switch (reqParamJSON.getJSONObject("biz_content").getString("status")){
|
||||
case "SUCCESS" :
|
||||
transferOrder.setState((byte) 2);
|
||||
transferOrder.setSuccessTime(new Date());
|
||||
break;
|
||||
case "WAIT_PAY" :
|
||||
transferOrder.setState((byte) 1);
|
||||
break;
|
||||
case "CLOSED" :
|
||||
transferOrder.setState((byte) 4);
|
||||
break;
|
||||
case "FAIL" :
|
||||
transferOrder.setState((byte) 3);
|
||||
break;
|
||||
case "DEALING" :
|
||||
transferOrder.setState((byte) 1);
|
||||
break;
|
||||
default:
|
||||
transferOrder.setState((byte) 1);
|
||||
break;
|
||||
}
|
||||
transferOrder.setChannelExtra(reqParamJSON.toJSONString());
|
||||
transferOrderService.updateById(transferOrder);
|
||||
return ApiRes.ok();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.cashout;
|
||||
|
||||
import com.jeequan.jeepay.core.ctrls.AbstractCtrl;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.exception.ResponseException;
|
||||
import com.jeequan.jeepay.core.interfaces.IConfigContextQueryService;
|
||||
import com.jeequan.jeepay.core.interfaces.cashout.IChannelCashoutNoticeService;
|
||||
import com.jeequan.jeepay.core.model.cashout.CashoutRetMsg;
|
||||
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
|
||||
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
|
||||
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
|
||||
import com.jeequan.jeepay.db.entity.ChannelAccountCashoutRecord;
|
||||
import com.jeequan.jeepay.service.impl.ChannelAccountCashoutRecordService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.MutablePair;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 渠道提现通知解析实现
|
||||
*
|
||||
* @author zx
|
||||
*
|
||||
* @date 2021/6/8 17:26
|
||||
*/
|
||||
@Slf4j
|
||||
@Controller
|
||||
public class MchChannelCashoutNoticeController extends AbstractCtrl {
|
||||
|
||||
@Autowired private ChannelAccountCashoutRecordService cashoutRecordService;
|
||||
@Autowired private IConfigContextQueryService configContextQueryService;
|
||||
|
||||
|
||||
/** 提现回调入口 **/
|
||||
@ResponseBody
|
||||
@RequestMapping(value= {"/api/channel/cashout/notify/{ifCode}", "/api/channel/cashout/notify/{ifCode}/{rid}"})
|
||||
public ResponseEntity doNotify(HttpServletRequest request, @PathVariable("ifCode") String ifCode, @PathVariable(value = "rid", required = false) String urlRid){
|
||||
|
||||
String rid = null;
|
||||
String logPrefix = "进入[" +ifCode+ "]提现回调:提现urlRid:["+ StringUtils.defaultIfEmpty(urlRid, "") + "] ";
|
||||
log.info("===== {} =====" , logPrefix);
|
||||
|
||||
try {
|
||||
|
||||
// 参数有误
|
||||
if(StringUtils.isEmpty(ifCode)){
|
||||
return ResponseEntity.badRequest().body("ifCode is empty");
|
||||
}
|
||||
|
||||
//查询通知接口是否存在
|
||||
IChannelCashoutNoticeService cashoutNoticeService = SpringBeansUtil.getBean(ifCode + "ChannelCashoutNoticeService", IChannelCashoutNoticeService.class);
|
||||
|
||||
// 提现通知接口实现不存在
|
||||
if(cashoutNoticeService == null){
|
||||
log.error("{}, interface not exists ", logPrefix);
|
||||
return ResponseEntity.badRequest().body("[" + ifCode + "] interface not exists");
|
||||
}
|
||||
|
||||
// 解析提现单号 和 请求参数
|
||||
MutablePair<String, Object> mutablePair = cashoutNoticeService.parseParams(request, urlRid);
|
||||
if(mutablePair == null){ // 解析数据失败, 响应已处理
|
||||
log.error("{}, mutablePair is null ", logPrefix);
|
||||
throw new BizException("解析数据异常!"); //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
|
||||
}
|
||||
|
||||
//解析到提现单号
|
||||
rid = mutablePair.left;
|
||||
log.info("{}, 解析数据为:rid:{}, params:{}", logPrefix, rid, mutablePair.getRight());
|
||||
|
||||
//获取提现单号 和 提现单数据
|
||||
ChannelAccountCashoutRecord cashoutRecord = cashoutRecordService.getById(rid);
|
||||
|
||||
// 提现单不存在
|
||||
if(cashoutRecord == null){
|
||||
log.error("{}, 提现单不存在. rid={} ", logPrefix, rid);
|
||||
return ResponseEntity.badRequest().body("cashout record not exists");
|
||||
}
|
||||
|
||||
//查询出商户应用的配置信息
|
||||
MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(cashoutRecord.getMchNo(), cashoutRecord.getAppId(),cashoutRecord.getMchExtNo());
|
||||
|
||||
|
||||
//调起接口的回调判断
|
||||
CashoutRetMsg cashoutRetMsg = cashoutNoticeService.doNotice(request, mutablePair.getRight(), cashoutRecord, mchAppConfigContext);
|
||||
// 返回null 表明出现异常, 无需处理通知下游等操作。
|
||||
if(cashoutRetMsg == null || cashoutRetMsg.getChannelState() == null || cashoutRetMsg.getResponseEntity() == null){
|
||||
log.error("{}, 处理回调事件异常 notifyResult data error, notifyResult ={} ",logPrefix, cashoutRetMsg);
|
||||
throw new BizException("处理回调事件异常!"); //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
|
||||
}
|
||||
|
||||
boolean updateOrderSuccess = true; //默认更新成功
|
||||
// 提现单是 【提现中状态】
|
||||
if(cashoutRecord.getState() == ChannelAccountCashoutRecord.CASHOUT_STATE_ING) {
|
||||
// 渠道提现单号
|
||||
cashoutRecord.setChannelRid(cashoutRetMsg.getChannelOrderId());
|
||||
// 明确成功
|
||||
if(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS == cashoutRetMsg.getChannelState()) {
|
||||
|
||||
updateOrderSuccess = cashoutRecordService.updateIng2Success(rid, cashoutRetMsg.getChannelOrderId(), cashoutRetMsg.getBankName(), cashoutRetMsg.getBankAccount(), cashoutRetMsg.getBankAccountName());
|
||||
|
||||
//明确失败
|
||||
}else if(ChannelRetMsg.ChannelState.CONFIRM_FAIL == cashoutRetMsg.getChannelState()) {
|
||||
String failMsg = String.format("failInfo=[code=%s, msg=%s]", cashoutRetMsg.getChannelErrCode(), cashoutRetMsg.getChannelErrMsg());
|
||||
updateOrderSuccess = cashoutRecordService.updateIng2Fail(rid, failMsg);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新提现单 异常
|
||||
if(!updateOrderSuccess){
|
||||
log.error("{}, updateOrderSuccess = {} ",logPrefix, updateOrderSuccess);
|
||||
return ResponseEntity.badRequest().body("update error");
|
||||
}
|
||||
|
||||
log.info("===== {}, 提现单通知完成。 rid={}, parseState = {} =====", logPrefix, rid, cashoutRetMsg.getChannelState());
|
||||
|
||||
return cashoutRetMsg.getResponseEntity();
|
||||
|
||||
} catch (BizException e) {
|
||||
log.error("{}, rid={}, BizException", logPrefix, rid, e);
|
||||
return ResponseEntity.badRequest().body(e.getMessage());
|
||||
|
||||
} catch (ResponseException e) {
|
||||
log.error("{}, rid={}, ResponseException", logPrefix, rid, e);
|
||||
return e.getResponseEntity();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("{}, rid={}, 系统异常", logPrefix, rid, e);
|
||||
return ResponseEntity.badRequest().body(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.channel;
|
||||
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alipay.api.AlipayApiException;
|
||||
import com.alipay.api.AlipayConstants;
|
||||
import com.alipay.api.internal.util.AlipaySignature;
|
||||
import com.jeequan.jeepay.components.mq.vender.IMQSender;
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.ctrls.AbstractCtrl;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.interfaces.IConfigContextQueryService;
|
||||
import com.jeequan.jeepay.core.interfaces.paychannel.IMchAutoAuthService;
|
||||
import com.jeequan.jeepay.core.model.DBApplicationConfig;
|
||||
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
|
||||
import com.jeequan.jeepay.core.model.params.alipay.AlipayConfig;
|
||||
import com.jeequan.jeepay.core.model.params.alipay.AlipayIsvParams;
|
||||
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
|
||||
import com.jeequan.jeepay.db.entity.MchAppEntity;
|
||||
import com.jeequan.jeepay.db.entity.PayInterfaceConfig;
|
||||
import com.jeequan.jeepay.service.impl.MchAppService;
|
||||
import com.jeequan.jeepay.service.impl.PayInterfaceConfigService;
|
||||
import com.jeequan.jeepay.service.impl.SysConfigService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* 渠道侧自定义业务ctrl
|
||||
*
|
||||
* @author terrfly
|
||||
*
|
||||
* @date 2021/7/15 11:49
|
||||
*/
|
||||
@Slf4j
|
||||
@Controller
|
||||
@RequestMapping("/api/channelbiz/alipay")
|
||||
public class AlipayBizController extends AbstractCtrl {
|
||||
|
||||
@Autowired private IConfigContextQueryService configContextQueryService;
|
||||
@Autowired private SysConfigService sysConfigService;
|
||||
@Autowired private PayInterfaceConfigService payInterfaceConfigService;
|
||||
@Autowired private MchAppService mchAppService;
|
||||
@Autowired private IMQSender mqSender;
|
||||
|
||||
/** 跳转到支付宝的授权页面 (统一从pay项目获取到isv配置信息)
|
||||
* isvAndMchNo 格式: ISVNO_MCHAPPID
|
||||
* example: https://pay.jeepay.cn/api/channelbiz/alipay/redirectAppToAppAuth/V1623998765_60cc41694ee0e6685f57eb1f
|
||||
* **/
|
||||
@RequestMapping("/redirectAppToAppAuth/{isvAndMchAppId}")
|
||||
public void redirectAppToAppAuth(@PathVariable("isvAndMchAppId") String isvAndMchAppId) throws IOException {
|
||||
|
||||
String isvNo = isvAndMchAppId.split("_")[0];
|
||||
|
||||
AlipayIsvParams alipayIsvParams = (AlipayIsvParams) configContextQueryService.queryIsvParams(isvNo, CS.IF_CODE.ALIPAY);
|
||||
alipayIsvParams.getSandbox();
|
||||
|
||||
String oauthUrl = AlipayConfig.PROD_APP_TO_APP_AUTH_URL;
|
||||
if(alipayIsvParams.getSandbox() != null && alipayIsvParams.getSandbox() == CS.YES){
|
||||
oauthUrl = AlipayConfig.SANDBOX_APP_TO_APP_AUTH_URL;
|
||||
}
|
||||
|
||||
String redirectUrl = sysConfigService.getDBApplicationConfig().getPaySiteUrl() + "/api/channelbiz/alipay/appToAppAuthCallback";
|
||||
response.sendRedirect(String.format(oauthUrl, alipayIsvParams.getAppId(), URLUtil.encodeAll(redirectUrl), isvAndMchAppId));
|
||||
}
|
||||
|
||||
/** 支付宝授权回调地址 **/
|
||||
@RequestMapping("/appToAppAuthCallback")
|
||||
public String appToAppAuthCallback() {
|
||||
|
||||
String errMsg = null;
|
||||
boolean isAlipaySysAuth = true; //是否 服务商登录支付宝后台系统发起的商户授权, 此时无法获取authCode和商户的信息。
|
||||
|
||||
try {
|
||||
// isvAndMchAppId 格式: ISVNO_MCHAPPID, 如果isvAndMchNo为空说明是: 支付宝后台的二维码授权之后的跳转链接。
|
||||
String isvAndMchAppId = getValString("state");
|
||||
String appAuthCode = getValString("app_auth_code"); // 支付宝授权code
|
||||
|
||||
if(StringUtils.isNotEmpty(isvAndMchAppId) && StringUtils.isNotEmpty(appAuthCode)){
|
||||
isAlipaySysAuth = false;
|
||||
String isvNo = isvAndMchAppId.split("_")[0];
|
||||
String mchAppId = isvAndMchAppId.split("_")[1];
|
||||
|
||||
MchAppEntity mchAppEntity = mchAppService.getById(mchAppId);
|
||||
|
||||
MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfo(mchAppEntity.getMchNo(), mchAppId);
|
||||
|
||||
IMchAutoAuthService mchAutoAuthService = SpringBeansUtil.getBean(CS.IF_CODE.ALIPAY + "MchAutoAuthService", IMchAutoAuthService.class);
|
||||
|
||||
JSONObject result = mchAutoAuthService.mchAuth(mchAppConfigContext, appAuthCode);
|
||||
|
||||
String appAuthToken = result.getString("appAuthToken");
|
||||
JSONObject ifParams = new JSONObject();
|
||||
ifParams.put("appAuthToken", appAuthToken); ifParams.put("refreshToken", result.getString("appRefreshToken")); ifParams.put("expireTimestamp", result.getString("expiresIn"));
|
||||
|
||||
PayInterfaceConfig dbRecord = payInterfaceConfigService.getByInfoIdAndIfCode(CS.SYS_ROLE_TYPE.MCH_APP, mchAppId, CS.IF_CODE.ALIPAY);
|
||||
|
||||
if(dbRecord != null){
|
||||
PayInterfaceConfig updateRecord = new PayInterfaceConfig();
|
||||
updateRecord.setId(dbRecord.getId()); updateRecord.setIfParams(ifParams.toJSONString());
|
||||
payInterfaceConfigService.updateById(updateRecord);
|
||||
}else{
|
||||
|
||||
dbRecord = new PayInterfaceConfig();
|
||||
dbRecord.setInfoType(CS.SYS_ROLE_TYPE.MCH_APP);
|
||||
dbRecord.setInfoId(mchAppId);
|
||||
dbRecord.setIfCode(CS.IF_CODE.ALIPAY);
|
||||
dbRecord.setIfParams(ifParams.toJSONString());
|
||||
dbRecord.setState(CS.YES);
|
||||
dbRecord.setCreatedBy("SYS");
|
||||
dbRecord.setCreatedUid(0L);
|
||||
payInterfaceConfigService.save(dbRecord);
|
||||
}
|
||||
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("error", e);
|
||||
errMsg = StringUtils.defaultIfBlank(e.getMessage(), "系统异常!");
|
||||
}
|
||||
|
||||
request.setAttribute("errMsg", errMsg);
|
||||
request.setAttribute("isAlipaySysAuth", isAlipaySysAuth);
|
||||
// 静态CDN地址
|
||||
DBApplicationConfig dbApplicationConfig = sysConfigService.getDBApplicationConfig();
|
||||
request.setAttribute("staticCdnHost", dbApplicationConfig.getStaticCdnHost());
|
||||
|
||||
return "channel/alipay/isvsubMchAuth";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 支付宝公共方法 加密函数。
|
||||
* **/
|
||||
private static String encryptAndSign(String bizContent, String alipayPublicKey, String cusPrivateKey, String charset,
|
||||
boolean isEncrypt, boolean isSign, String signType) throws AlipayApiException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (StringUtils.isEmpty(charset)) {
|
||||
charset = AlipayConstants.CHARSET_GBK;
|
||||
}
|
||||
sb.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>");
|
||||
if (isEncrypt) {// 加密
|
||||
sb.append("<alipay>");
|
||||
String encrypted = AlipaySignature.rsaEncrypt(bizContent, alipayPublicKey, charset);
|
||||
sb.append("<response>" + encrypted + "</response>");
|
||||
sb.append("<encryption_type>AES</encryption_type>");
|
||||
if (isSign) {
|
||||
String sign = AlipaySignature.rsaSign(encrypted, cusPrivateKey, charset, signType);
|
||||
sb.append("<sign>" + sign + "</sign>");
|
||||
sb.append("<sign_type>");
|
||||
sb.append(signType);
|
||||
sb.append("</sign_type>");
|
||||
}
|
||||
sb.append("</alipay>");
|
||||
} else if (isSign) {// 不加密,但需要签名
|
||||
sb.append("<alipay>");
|
||||
sb.append("<response>" + bizContent + "</response>");
|
||||
String sign = AlipaySignature.rsaSign(bizContent, cusPrivateKey, charset, signType);
|
||||
sb.append("<sign>" + sign + "</sign>");
|
||||
sb.append("<sign_type>");
|
||||
sb.append(signType);
|
||||
sb.append("</sign_type>");
|
||||
sb.append("</alipay>");
|
||||
} else {// 不加密,不加签
|
||||
sb.append(bizContent);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 接收 支付宝 应用 配置中: 【应用网关地址 : 用于接收支付宝沙箱异步通知消息(例如 From蚂蚁消息等),需要传入http(s)公网可访问的网页地址。选填,若不设置,则无法接收相应的异步通知消息。】
|
||||
*
|
||||
* **/
|
||||
@RequestMapping("/appGatewayMsgReceive")
|
||||
public ModelAndView alipayAppGatewayMsgReceive() {
|
||||
|
||||
JSONObject reqJSON = getReqParamJSON();
|
||||
|
||||
// 获取到报文信息, 然后 转发到对应的ctrl
|
||||
log.error("支付宝应用网关接收消息参数:{}", reqJSON);
|
||||
|
||||
// // 分账交易通知
|
||||
// if("alipay.trade.order.settle.notify123123".equals(reqJSON.getString("msg_method"))){
|
||||
//
|
||||
// // 直接转发到 分账通知的 URL去。
|
||||
// ModelAndView mv = new ModelAndView();
|
||||
// mv.setViewName("forward:/api/divisionRecordChannelNotify/" + CS.IF_CODE.ALIPAY);
|
||||
// return mv;
|
||||
// }
|
||||
|
||||
|
||||
// 测试。
|
||||
if(true){
|
||||
|
||||
AlipayIsvParams isvParams = (AlipayIsvParams)configContextQueryService.queryIsvParams("V1648865874", CS.IF_CODE.ALIPAY);
|
||||
|
||||
String publicKey = isvParams.getAlipayPublicKey();
|
||||
String PRIVATE_KEY = isvParams.getPrivateKey();
|
||||
|
||||
//支付宝响应消息
|
||||
String bizContent = "<success>true</success>";
|
||||
|
||||
try {
|
||||
//对响应内容加签
|
||||
String responseMsg = encryptAndSign(bizContent, publicKey, PRIVATE_KEY, "UTF-8", false, true, "RSA2");
|
||||
|
||||
log.error("响应支付宝:{}", responseMsg);
|
||||
|
||||
//http 内容应答
|
||||
response.reset();
|
||||
response.setContentType("text/xml;charset=GBK");
|
||||
PrintWriter printWriter = response.getWriter();
|
||||
printWriter.print(responseMsg);
|
||||
response.flushBuffer();
|
||||
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
throw new BizException("无此事件["+ reqJSON.getString("msg_method") +"]处理器");
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.channel;
|
||||
|
||||
import com.jeequan.jeepay.core.ctrls.AbstractCtrl;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import java.util.Enumeration;
|
||||
|
||||
/**
|
||||
* 渠道侧自定义业务ctrl
|
||||
* 盛付通
|
||||
*
|
||||
* @author terrfly
|
||||
*
|
||||
* @date 2022/10/21 14:29
|
||||
*/
|
||||
@Slf4j
|
||||
@Controller
|
||||
@RequestMapping("/api/channelbiz/shengpay")
|
||||
public class ShengpayBizController extends AbstractCtrl {
|
||||
|
||||
/** 修改费率通知地址 **/
|
||||
@RequestMapping("/modifyRateNotify")
|
||||
public ResponseEntity modifyRateNotify() throws Exception {
|
||||
|
||||
String logPrefix = "接收盛付通费率修改通知";
|
||||
|
||||
try {
|
||||
|
||||
log.info("{}, method: {}", logPrefix, request.getMethod());
|
||||
log.info("{}, QUERYSTRING: {}", logPrefix, request.getQueryString());
|
||||
|
||||
// 查询到所有的header
|
||||
Enumeration<String> list = request.getHeaderNames();
|
||||
while (list.hasMoreElements()){
|
||||
String key = list.nextElement();
|
||||
log.info("{}, Header: {}={}", logPrefix, key, request.getHeader(key));
|
||||
}
|
||||
|
||||
log.info("{}, reqParamJSON: {}", logPrefix, getReqParamJSON());
|
||||
} catch (Exception e) {
|
||||
log.error("{}", logPrefix, e);
|
||||
}
|
||||
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.setContentType(MediaType.TEXT_HTML);
|
||||
return new ResponseEntity("SUCCESS", httpHeaders, HttpStatus.OK);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.channel.alipay;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alipay.api.AlipayApiException;
|
||||
import com.alipay.api.internal.util.AlipaySignature;
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.interfaces.IConfigContextQueryService;
|
||||
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
|
||||
import com.jeequan.jeepay.core.model.params.alipay.AlipayIsvParams;
|
||||
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
|
||||
import com.jeequan.jeepay.db.entity.MchStoreDevice;
|
||||
import com.jeequan.jeepay.pay.ctrl.channel.alipay.model.response.AlipaySpiRes;
|
||||
import com.jeequan.jeepay.pay.ctrl.channel.alipay.model.response.SpiBaseResponse;
|
||||
import com.jeequan.jeepay.pay.ctrl.channel.alipay.service.AlipaySpiRuyiCashierService;
|
||||
import com.jeequan.jeepay.pay.ctrl.channel.alipay.service.AlipaySpiRuyiPayOrderService;
|
||||
import com.jeequan.jeepay.pay.ctrl.channel.alipay.service.AlipaySpiRuyiRefundOrderService;
|
||||
import com.jeequan.jeepay.pay.ctrl.channel.alipay.util.AlipayNotifyUtil;
|
||||
import com.jeequan.jeepay.pay.ctrl.payorder.AbstractPayOrderController;
|
||||
import com.jeequan.jeepay.service.impl.MchStoreDeviceService;
|
||||
import com.jeequan.jeepay.thirdparty.util.ChannelCertConfigKitBean;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 支付宝From消息接收ctrl
|
||||
*
|
||||
* @author zx
|
||||
*
|
||||
* @date 2022-03-21 15:50
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/from/alipay/notify")
|
||||
public class AlipayNotifyController extends AbstractPayOrderController {
|
||||
|
||||
@Autowired private AlipaySpiRuyiCashierService alipaySpiRuyiCashierService;
|
||||
@Autowired private AlipaySpiRuyiPayOrderService alipaySpiRuyiPayOrderService;
|
||||
@Autowired private AlipaySpiRuyiRefundOrderService alipaySpiRuyiRefundOrderService;
|
||||
@Autowired private MchStoreDeviceService mchStoreDeviceService;
|
||||
@Autowired private IConfigContextQueryService configContextQueryService;
|
||||
@Autowired private ChannelCertConfigKitBean channelCertConfigKitBean;
|
||||
|
||||
/** spi入口 **/
|
||||
@PostMapping
|
||||
public AlipaySpiRes entry() {
|
||||
String logPrefix = "【支付宝异步通知消息】";
|
||||
AlipayIsvParams alipayParams = null;
|
||||
|
||||
try {
|
||||
JSONObject reqParamJSON = getReqParamJSON();
|
||||
String method = getValStringDefault("method", AlipayNotifyUtil.NOTIFY_UNKNOWN);
|
||||
log.info("{}请求参数:{}", logPrefix + AlipayNotifyUtil.notifyMap.get(method), reqParamJSON.toJSONString());
|
||||
|
||||
String terminalBindInfo = getValStringRequired("terminal_bind_info"); // 绑定设备传的自定义参数
|
||||
UnifiedOrderRQ.DeviceInfo deviceInfo = JSONObject.parseObject(terminalBindInfo, UnifiedOrderRQ.DeviceInfo.class);
|
||||
|
||||
// 查询设备信息
|
||||
MchStoreDevice mchStoreDevice = mchStoreDeviceService.getByUniqueKey(deviceInfo.getProvider(), Byte.valueOf(deviceInfo.getDeviceType()), deviceInfo.getDeviceNo());
|
||||
if (mchStoreDevice == null || mchStoreDevice.getBindState() != CS.YES) {
|
||||
throw new BizException("设备不存在");
|
||||
}
|
||||
|
||||
// 验签&&获取服务商参数
|
||||
alipayParams = this.alipaySpiCheckSign(reqParamJSON, mchStoreDevice.getMchNo(), mchStoreDevice.getAppId());
|
||||
|
||||
// 分发spi请求
|
||||
SpiBaseResponse response;
|
||||
switch (method) {
|
||||
case AlipayNotifyUtil.NOTIFY_SP_OPERATION_RESULT:
|
||||
response = spOperationResult(mchStoreDevice);
|
||||
break;
|
||||
case AlipayNotifyUtil.NOTIFY_SHOP_SAVE_PASSED:
|
||||
response = shopSavePassed(mchStoreDevice);
|
||||
break;
|
||||
case AlipayNotifyUtil.NOTIFY_SHOP_SAVE_REJECTED:
|
||||
response = shopSaveRejected(mchStoreDevice);
|
||||
break;
|
||||
default:
|
||||
throw new BizException("支付宝异步通知消息未接入");
|
||||
}
|
||||
|
||||
return AlipaySpiRes.okWithSign(logPrefix, response, alipayParams.getPrivateKey());
|
||||
|
||||
}catch (AlipayApiException e) {
|
||||
log.error("{}异常:{}", logPrefix, e.getMessage(), e);
|
||||
if (alipayParams == null) {
|
||||
return AlipaySpiRes.fail(logPrefix, "BIZ_ERROR", StringUtils.defaultString(e.getMessage(), "服务商参数未配置"));
|
||||
}
|
||||
return AlipaySpiRes.failWithSign(logPrefix, alipayParams.getPrivateKey(), StringUtils.defaultString(e.getErrCode(), "SYSTEM_ERROR"),
|
||||
StringUtils.defaultString(e.getErrMsg(), "系统繁忙"));
|
||||
}catch (BizException e) {
|
||||
if (alipayParams == null) {
|
||||
return AlipaySpiRes.fail(logPrefix, "BIZ_ERROR", StringUtils.defaultString(e.getMessage(), "服务商参数未配置"));
|
||||
}
|
||||
log.error("{}异常:{}", logPrefix, e.getMessage(), e);
|
||||
return AlipaySpiRes.failWithSign(logPrefix, alipayParams.getPrivateKey(), "BIZ_ERROR", StringUtils.defaultString(e.getMessage(), "系统繁忙"));
|
||||
}catch (Exception e) {
|
||||
log.error("{}异常:{}", logPrefix, e.getMessage(), e);
|
||||
return AlipaySpiRes.failWithSign(logPrefix, alipayParams.getPrivateKey(), "SYSTEM_ERROR", "系统繁忙");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private SpiBaseResponse shopSaveRejected(MchStoreDevice mchStoreDevice) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private SpiBaseResponse shopSavePassed(MchStoreDevice mchStoreDevice) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private SpiBaseResponse spOperationResult(MchStoreDevice mchStoreDevice) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** spi请求参数验签 **/
|
||||
public AlipayIsvParams alipaySpiCheckSign(JSONObject reqParamJSON, String mchNo, String appId) throws AlipayApiException {
|
||||
|
||||
//查询出商户应用的配置信息
|
||||
MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfo(mchNo, appId);
|
||||
if(!mchAppConfigContext.isIsvsubMch()){
|
||||
throw new AlipayApiException("BIZ_ERROR", "仅支持特约商户");
|
||||
}
|
||||
|
||||
// 获取支付参数
|
||||
AlipayIsvParams alipayParams = (AlipayIsvParams) configContextQueryService.queryIsvParams(mchAppConfigContext.getMchApplyment().getIsvNo(), CS.IF_CODE.ALIPAY);
|
||||
if (alipayParams == null) {
|
||||
throw new AlipayApiException("BIZ_ERROR", "服务商参数未配置");
|
||||
}
|
||||
|
||||
boolean verifyResult;
|
||||
if(alipayParams.getUseCert() != null && alipayParams.getUseCert() == CS.YES){ //证书方式
|
||||
verifyResult = AlipaySignature.rsaCertCheckV1(reqParamJSON.toJavaObject(Map.class), getCertFilePath(alipayParams.getAlipayPublicCert()),
|
||||
com.jeequan.jeepay.core.model.params.alipay.AlipayConfig.CHARSET, alipayParams.getSignType());
|
||||
}else{
|
||||
verifyResult = AlipaySignature.rsaCheckV1(reqParamJSON.toJavaObject(Map.class),
|
||||
alipayParams.getAlipayPublicKey(), com.jeequan.jeepay.core.model.params.alipay.AlipayConfig.CHARSET, alipayParams.getSignType());
|
||||
}
|
||||
//验签失败
|
||||
if(!verifyResult){
|
||||
throw new AlipayApiException("BIZ_ERROR", "alipay spi请求验签失败");
|
||||
}
|
||||
return alipayParams;
|
||||
|
||||
}
|
||||
|
||||
/** 获取文件路径 **/
|
||||
protected String getCertFilePath(String certFilePath) {
|
||||
return channelCertConfigKitBean.getCertFilePath(certFilePath);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.channel.alipay;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alipay.api.AlipayApiException;
|
||||
import com.alipay.api.internal.util.AlipaySignature;
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.interfaces.IConfigContextQueryService;
|
||||
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
|
||||
import com.jeequan.jeepay.core.model.params.alipay.AlipayIsvParams;
|
||||
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
|
||||
import com.jeequan.jeepay.db.entity.MchStoreDevice;
|
||||
import com.jeequan.jeepay.pay.ctrl.channel.alipay.model.response.AlipaySpiRes;
|
||||
import com.jeequan.jeepay.pay.ctrl.channel.alipay.model.response.SpiBaseResponse;
|
||||
import com.jeequan.jeepay.pay.ctrl.channel.alipay.service.AlipaySpiRuyiCashierService;
|
||||
import com.jeequan.jeepay.pay.ctrl.channel.alipay.service.AlipaySpiRuyiPayOrderService;
|
||||
import com.jeequan.jeepay.pay.ctrl.channel.alipay.service.AlipaySpiRuyiRefundOrderService;
|
||||
import com.jeequan.jeepay.pay.ctrl.channel.alipay.util.AlipaySpiRuyiUtil;
|
||||
import com.jeequan.jeepay.pay.ctrl.payorder.AbstractPayOrderController;
|
||||
import com.jeequan.jeepay.service.impl.MchStoreDeviceService;
|
||||
import com.jeequan.jeepay.thirdparty.util.ChannelCertConfigKitBean;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 支付宝如意spi接口
|
||||
*
|
||||
* @author zx
|
||||
*
|
||||
* @date 2021-04-27 15:50
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/alipay/ruyi/spi")
|
||||
public class AlipayRuyiSpiController extends AbstractPayOrderController {
|
||||
|
||||
@Autowired private AlipaySpiRuyiCashierService alipaySpiRuyiCashierService;
|
||||
@Autowired private AlipaySpiRuyiPayOrderService alipaySpiRuyiPayOrderService;
|
||||
@Autowired private AlipaySpiRuyiRefundOrderService alipaySpiRuyiRefundOrderService;
|
||||
@Autowired private MchStoreDeviceService mchStoreDeviceService;
|
||||
@Autowired private IConfigContextQueryService configContextQueryService;
|
||||
@Autowired private ChannelCertConfigKitBean channelCertConfigKitBean;
|
||||
|
||||
/** 支付宝如意 spi入口 **/
|
||||
@PostMapping
|
||||
public AlipaySpiRes ruyiSpiEntry() {
|
||||
String logPrefix = "【支付宝如意Lite】";
|
||||
AlipayIsvParams alipayParams = null;
|
||||
|
||||
try {
|
||||
JSONObject reqParamJSON = getReqParamJSON();
|
||||
String method = getValStringDefault("method", AlipaySpiRuyiUtil.SPI_RUYI_UNKNOWN);
|
||||
log.info("{}请求参数:{}", logPrefix + AlipaySpiRuyiUtil.spiMap.get(method), reqParamJSON.toJSONString());
|
||||
|
||||
String terminalBindInfo = getValStringRequired("terminal_bind_info"); // 绑定设备传的自定义参数
|
||||
UnifiedOrderRQ.DeviceInfo deviceInfo = JSON.parseObject(terminalBindInfo, UnifiedOrderRQ.DeviceInfo.class);
|
||||
|
||||
// 查询设备信息
|
||||
MchStoreDevice mchStoreDevice = mchStoreDeviceService.getByUniqueKey(deviceInfo.getProvider(), Byte.valueOf(deviceInfo.getDeviceType()), deviceInfo.getDeviceNo());
|
||||
if (mchStoreDevice == null || mchStoreDevice.getBindState() != CS.YES) {
|
||||
throw new BizException("设备不存在");
|
||||
}
|
||||
|
||||
// 验签&&获取服务商参数
|
||||
alipayParams = this.alipaySpiCheckSign(reqParamJSON, mchStoreDevice.getMchNo(), mchStoreDevice.getAppId());
|
||||
|
||||
// 分发spi请求
|
||||
SpiBaseResponse response;
|
||||
switch (method) {
|
||||
case AlipaySpiRuyiUtil.SPI_RUYI_PAY:
|
||||
response = alipaySpiRuyiPayOrderService.pay(mchStoreDevice);
|
||||
break;
|
||||
case AlipaySpiRuyiUtil.SPI_RUYI_PAY_QUERY:
|
||||
response = alipaySpiRuyiPayOrderService.payQuery(mchStoreDevice);
|
||||
break;
|
||||
case AlipaySpiRuyiUtil.SPI_RUYI_BILL_DETAIL_QUERY:
|
||||
response = alipaySpiRuyiPayOrderService.billdetailQuery(mchStoreDevice,
|
||||
getValIntegerDefault("page_num", 1), getValIntegerDefault("page_size", 1), getValString("date"));
|
||||
break;
|
||||
case AlipaySpiRuyiUtil.SPI_RUYI_BILL_STATISTICS_QUERY:
|
||||
response = alipaySpiRuyiPayOrderService.billstatisticsQuery(mchStoreDevice, getValString("date"));
|
||||
break;
|
||||
case AlipaySpiRuyiUtil.SPI_RUYI_BILL_QUERY:
|
||||
response = alipaySpiRuyiRefundOrderService.billQuery(mchStoreDevice);
|
||||
break;
|
||||
case AlipaySpiRuyiUtil.SPI_RUYI_REFUND_AUTHCODE_APPLY:
|
||||
response = alipaySpiRuyiRefundOrderService.refundAuthCodeApply(mchStoreDevice);
|
||||
break;
|
||||
case AlipaySpiRuyiUtil.SPI_RUYI_REFUND:
|
||||
response = alipaySpiRuyiRefundOrderService.refund(mchStoreDevice);
|
||||
break;
|
||||
case AlipaySpiRuyiUtil.SPI_RUYI_REFUND_QUERY:
|
||||
response = alipaySpiRuyiRefundOrderService.refundQuery(mchStoreDevice);
|
||||
break;
|
||||
case AlipaySpiRuyiUtil.SPI_RUYI_CASHIER_SIGN:
|
||||
response = alipaySpiRuyiCashierService.cashierSign(mchStoreDevice);
|
||||
break;
|
||||
case AlipaySpiRuyiUtil.SPI_RUYI_CASHIER_UNSIGN:
|
||||
response = alipaySpiRuyiCashierService.cashierUnsign(mchStoreDevice);
|
||||
break;
|
||||
case AlipaySpiRuyiUtil.SPI_RUYI_CASHIER_BATCH_QUERY:
|
||||
response = alipaySpiRuyiCashierService.batchQuery(mchStoreDevice);
|
||||
break;
|
||||
case AlipaySpiRuyiUtil.SPI_RUYI_CASHIER_QUERY:
|
||||
response = alipaySpiRuyiCashierService.cashierQuery(mchStoreDevice);
|
||||
break;
|
||||
default:
|
||||
throw new BizException("如意Lite SPI请求未接入");
|
||||
}
|
||||
|
||||
return AlipaySpiRes.okWithSign(logPrefix, response, alipayParams.getPrivateKey());
|
||||
|
||||
}catch (AlipayApiException e) {
|
||||
log.error("{}异常:{}", logPrefix, e.getMessage(), e);
|
||||
if (alipayParams == null) {
|
||||
return AlipaySpiRes.fail(logPrefix, "BIZ_ERROR", StringUtils.defaultString(e.getMessage(), "服务商参数未配置"));
|
||||
}
|
||||
return AlipaySpiRes.failWithSign(logPrefix, alipayParams.getPrivateKey(), StringUtils.defaultString(e.getErrCode(), "SYSTEM_ERROR"),
|
||||
StringUtils.defaultString(e.getErrMsg(), "系统繁忙"));
|
||||
}catch (BizException e) {
|
||||
if (alipayParams == null) {
|
||||
return AlipaySpiRes.fail(logPrefix, "BIZ_ERROR", StringUtils.defaultString(e.getMessage(), "服务商参数未配置"));
|
||||
}
|
||||
log.error("{}异常:{}", logPrefix, e.getMessage(), e);
|
||||
return AlipaySpiRes.failWithSign(logPrefix, alipayParams.getPrivateKey(), "BIZ_ERROR", StringUtils.defaultString(e.getMessage(), "系统繁忙"));
|
||||
}catch (Exception e) {
|
||||
log.error("{}异常:{}", logPrefix, e.getMessage(), e);
|
||||
return AlipaySpiRes.failWithSign(logPrefix, alipayParams.getPrivateKey(), "SYSTEM_ERROR", "系统繁忙");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** spi请求参数验签 **/
|
||||
public AlipayIsvParams alipaySpiCheckSign(JSONObject reqParamJSON, String mchNo, String appId) throws AlipayApiException {
|
||||
|
||||
//查询出商户应用的配置信息
|
||||
MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfo(mchNo, appId);
|
||||
if(!mchAppConfigContext.isIsvsubMch()){
|
||||
throw new AlipayApiException("BIZ_ERROR", "仅支持特约商户");
|
||||
}
|
||||
|
||||
// 获取支付参数
|
||||
AlipayIsvParams alipayParams = (AlipayIsvParams) configContextQueryService.queryIsvParams(mchAppConfigContext.getMchApplyment().getIsvNo(), CS.IF_CODE.ALIPAY);
|
||||
if (alipayParams == null) {
|
||||
throw new AlipayApiException("BIZ_ERROR", "服务商参数未配置");
|
||||
}
|
||||
|
||||
boolean verifyResult;
|
||||
if(alipayParams.getUseCert() != null && alipayParams.getUseCert() == CS.YES){ //证书方式
|
||||
verifyResult = AlipaySignature.rsaCertCheckV1(reqParamJSON.toJavaObject(Map.class), getCertFilePath(alipayParams.getAlipayPublicCert()),
|
||||
com.jeequan.jeepay.core.model.params.alipay.AlipayConfig.CHARSET, alipayParams.getSignType());
|
||||
}else{
|
||||
verifyResult = AlipaySignature.rsaCheckV1(reqParamJSON.toJavaObject(Map.class),
|
||||
alipayParams.getAlipayPublicKey(), com.jeequan.jeepay.core.model.params.alipay.AlipayConfig.CHARSET, alipayParams.getSignType());
|
||||
}
|
||||
//验签失败
|
||||
if(!verifyResult){
|
||||
throw new AlipayApiException("BIZ_ERROR", "alipay spi请求验签失败");
|
||||
}
|
||||
return alipayParams;
|
||||
|
||||
}
|
||||
|
||||
/** 获取文件路径 **/
|
||||
protected String getCertFilePath(String certFilePath) {
|
||||
return channelCertConfigKitBean.getCertFilePath(certFilePath);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.channel.alipay.model.request;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.Range;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/*
|
||||
* 支付宝如意lite 支付宝请求服务商参数对象
|
||||
*
|
||||
* @author zx
|
||||
*
|
||||
* @date 2021/6/8 17:33
|
||||
*/
|
||||
@Data
|
||||
public class AlipaySpiPayOrderRequest {
|
||||
|
||||
/** 商户订单号 **/
|
||||
@JSONField(name = "merchant_order_no")
|
||||
private String mchOrderNo;
|
||||
|
||||
/** 支付金额, 单位:元 **/
|
||||
@JSONField(name = "total_amount")
|
||||
private String total_amount;
|
||||
|
||||
/** 支付金额, 单位:分,由total_amount计算而来 **/
|
||||
private Long amount;
|
||||
|
||||
/** 货币代码 **/
|
||||
@JSONField(name = "shop_id")
|
||||
private String shopId;
|
||||
|
||||
/** 客户端IP地址 **/
|
||||
private String clientIp;
|
||||
|
||||
/** 商品标题 **/
|
||||
@NotBlank(message="商品标题不能为空")
|
||||
private String subject;
|
||||
|
||||
/** 商品描述信息 **/
|
||||
@NotBlank(message="商品描述信息不能为空")
|
||||
private String body;
|
||||
|
||||
/** 商户的门店ID **/
|
||||
private String storeId;
|
||||
|
||||
/** 设备信息 **/
|
||||
private String deviceInfo;
|
||||
|
||||
/** 商户的码牌ID **/
|
||||
private Long qrcId;
|
||||
|
||||
/** 异步通知地址 **/
|
||||
private String notifyUrl;
|
||||
|
||||
/** 跳转通知地址 **/
|
||||
private String returnUrl;
|
||||
|
||||
/** 卖家备注 **/
|
||||
private String sellerRemark;
|
||||
|
||||
/** 买家备注 **/
|
||||
private String buyerRemark;
|
||||
|
||||
/** 订单失效时间, 单位:秒 **/
|
||||
private Integer expiredTime;
|
||||
|
||||
/** 终端编号(商户自行录入的 trmNo 字段 ) **/
|
||||
private String mchTrmNo;
|
||||
|
||||
/** 特定渠道发起额外参数 **/
|
||||
private String channelExtra;
|
||||
|
||||
/** 商户扩展参数 **/
|
||||
private String extParam;
|
||||
|
||||
/** 分账模式: 0-该笔订单不允许分账, 1-支付成功按配置自动完成分账, 2-商户手动分账(解冻商户金额) **/
|
||||
@Range(min = 0, max = 2, message = "分账模式设置值有误")
|
||||
private Byte divisionMode;
|
||||
|
||||
/** 渠道特殊业务数据 **/
|
||||
private String channelBizData;
|
||||
|
||||
|
||||
@Data
|
||||
public static class DeviceInfo {
|
||||
|
||||
/** 设备类型:qr_code-码牌,scan_pos-扫码POS,auto_pos-智能POS,other-其他 **/
|
||||
private String deviceType;
|
||||
|
||||
/** 设备号 **/
|
||||
private String deviceNo;
|
||||
|
||||
/** 设备厂商 (智能POS厂商即为 ifCode,扫码POS仅商户系统调用,已通过cahnnelBizData传值) **/
|
||||
private String provider;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.channel.alipay.model.response;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
* 支付宝SPI 账单流水返回对象
|
||||
*
|
||||
* @author zx
|
||||
*
|
||||
* @date 2023/3/7 10:35
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class AlipaySpiBilldetailQueryResponse extends SpiBaseResponse {
|
||||
|
||||
/** 账单总数量**/
|
||||
@JSONField(name = "total_size")
|
||||
private String totalSize;
|
||||
|
||||
/** 商家实收总金额(单位:元),精确到小数点后两位 **/
|
||||
@JSONField(name = "order_list")
|
||||
private List<Order> orderList;
|
||||
|
||||
@Data
|
||||
public static class Order {
|
||||
/** PAY|REFUND : 已支付 |已退款类型**/
|
||||
@JSONField(name = "order_type")
|
||||
private String orderType;
|
||||
|
||||
/** 商户外部订单号**/
|
||||
@JSONField(name = "merchant_order_no")
|
||||
private String merchantOrderNo;
|
||||
|
||||
/** ISV服务商内部订单号**/
|
||||
@JSONField(name = "isv_order_no")
|
||||
private String isvOrderNo;
|
||||
|
||||
/** 支付通道订单号**/
|
||||
@JSONField(name = "channel_order_no")
|
||||
private String channelOrderNo;
|
||||
|
||||
/** 商户外部退款单号**/
|
||||
@JSONField(name = "merchant_refund_no")
|
||||
private String merchantRefundNo;
|
||||
|
||||
/** ISV服务商内部退款订单号**/
|
||||
@JSONField(name = "isv_refund_no")
|
||||
private String isvRefundNo;
|
||||
|
||||
/** 支付通道退款单号**/
|
||||
@JSONField(name = "channel_refund_no")
|
||||
private String channelRefundNo;
|
||||
|
||||
/** 支付类型,001:支付宝,002:微信,007:银联二维码 100:自动识别类型等识别类型等**/
|
||||
@JSONField(name = "channel_type")
|
||||
private String channelType;
|
||||
|
||||
/** 支付订单金额,单位元,精确到小数点后2位 (REFUND |PAY状态时必填)**/
|
||||
@JSONField(name = "total_amount")
|
||||
private String totalAmount;
|
||||
|
||||
/** 实收金额,单位元,精确到小数点后2位 (PAY状态必填)**/
|
||||
@JSONField(name = "receipt_amount")
|
||||
private String receiptAmount;
|
||||
|
||||
/** 买家付款金额,单位元,精确到小数点后2位(PAY状态必填)**/
|
||||
@JSONField(name = "buyer_pay_amount")
|
||||
private String buyerPayAmount;
|
||||
|
||||
/** 商家退款金额,单位元,精确到小数点后2位(REFUND 状态必填)**/
|
||||
@JSONField(name = "refund_amount")
|
||||
private String refundAmount;
|
||||
|
||||
/** Pay时,代表支付完成时间;refund时,代表退款时间。格式:yyyy-MM-dd HH:mm:ss,需填写UTC时间**/
|
||||
@JSONField(name = "order_time")
|
||||
private String orderTime;
|
||||
|
||||
/** 收银终端设备序列号**/
|
||||
@JSONField(name = "terminal_id")
|
||||
private String terminalId;
|
||||
|
||||
/** 交易概述**/
|
||||
@JSONField(name = "subject")
|
||||
private String subject;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.channel.alipay.model.response;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/*
|
||||
* 支付宝SPI 退款返回对象
|
||||
*
|
||||
* @author zx
|
||||
*
|
||||
* @date 2023/3/7 10:35
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class AlipaySpiBillstatisticsQueryResponse extends SpiBaseResponse {
|
||||
|
||||
/** 格式:yyyy-MM-dd 00:00:00 , 账单日期 **/
|
||||
@JSONField(name = "statistic_time")
|
||||
private String statisticTime;
|
||||
|
||||
/** 商家实收总金额(单位:元),精确到小数点后两位 **/
|
||||
@JSONField(name = "receipt_amount")
|
||||
private String receiptAmount;
|
||||
|
||||
/** 商家实收总笔数(单位:笔) **/
|
||||
@JSONField(name = "receipt_count")
|
||||
private String receiptCount;
|
||||
|
||||
/** 商家退款总金额(单位:元),精确到小数点后两位 **/
|
||||
@JSONField(name = "refund_amount")
|
||||
private String refundAmount;
|
||||
|
||||
/** 商家退款总笔数(单位:笔) **/
|
||||
@JSONField(name = "refund_count")
|
||||
private String refundCount;
|
||||
|
||||
/** 商家收入结余(总实收-总退款,单位:元),精确到小数点后两位 **/
|
||||
@JSONField(name = "total_income")
|
||||
private String totalIncome;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.channel.alipay.model.response;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
* 支付宝SPI 收银员签到接口返回对象
|
||||
*
|
||||
* @author zx
|
||||
*
|
||||
* @date 2023/3/7 10:35
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class AlipaySpiCashierBatchQueryResponse extends SpiBaseResponse {
|
||||
|
||||
/** 签到失败原因 **/
|
||||
@JSONField(name = "cashier_list")
|
||||
private List<Cashier> cashierList;
|
||||
|
||||
@Data
|
||||
public static class Cashier {
|
||||
/** 签到失败原因 **/
|
||||
@JSONField(name = "cashier_id")
|
||||
private String cashierId;
|
||||
|
||||
/** 签到失败原因 **/
|
||||
@JSONField(name = "cashier_account")
|
||||
private String cashierAccount;
|
||||
|
||||
/** 签到失败原因 **/
|
||||
@JSONField(name = "cashier_name")
|
||||
private String cashierName;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.channel.alipay.model.response;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/*
|
||||
* 支付宝SPI 查询收银员状态接口返回对象
|
||||
*
|
||||
* @author zx
|
||||
*
|
||||
* @date 2023/3/7 10:35
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class AlipaySpiCashierQueryResponse extends SpiBaseResponse {
|
||||
|
||||
/** 签到状态,1成功, 0失败 **/
|
||||
@JSONField(name = "status")
|
||||
private String status;
|
||||
|
||||
/** 签到失败原因 **/
|
||||
@JSONField(name = "cashier_id")
|
||||
private String cashierId;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.channel.alipay.model.response;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/*
|
||||
* 支付宝SPI 收银员签到接口返回对象
|
||||
*
|
||||
* @author zx
|
||||
*
|
||||
* @date 2023/3/7 10:35
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class AlipaySpiCashierSignResponse extends SpiBaseResponse {
|
||||
|
||||
/** 签到状态,1成功, 0失败 **/
|
||||
@JSONField(name = "status")
|
||||
private String status;
|
||||
|
||||
/** 签到失败原因 **/
|
||||
@JSONField(name = "fail_reason")
|
||||
private String failReason;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.channel.alipay.model.response;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/*
|
||||
* 支付宝SPI 收银员签退接口返回对象
|
||||
*
|
||||
* @author zx
|
||||
*
|
||||
* @date 2023/3/7 10:35
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class AlipaySpiCashierUnSignResponse extends SpiBaseResponse {
|
||||
|
||||
/** 签到状态,1成功, 0失败 **/
|
||||
@JSONField(name = "status")
|
||||
private String status;
|
||||
|
||||
/** 签到失败原因 **/
|
||||
@JSONField(name = "fail_reason")
|
||||
private String failReason;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.channel.alipay.model.response;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/*
|
||||
* 支付宝SPI 查单接口返回对象
|
||||
*
|
||||
* @author zx
|
||||
*
|
||||
* @date 2023/3/7 10:35
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class AlipaySpiPayOrderQueryResponse extends SpiBaseResponse {
|
||||
|
||||
/** 商家外部订单号 **/
|
||||
@JSONField(name = "merchant_order_no")
|
||||
private String merchantOrderNo;
|
||||
|
||||
/** ISV 订单号 **/
|
||||
@JSONField(name = "isv_order_no")
|
||||
private String isvOrderNo;
|
||||
|
||||
/** 微信、支付宝等第三方支付交易号 **/
|
||||
@JSONField(name = "channel_order_no")
|
||||
private String channelOrderNo;
|
||||
|
||||
/** 支付通道类型,001:支付宝,002:微信 100:自动识别类型等 **/
|
||||
@JSONField(name = "channel_type")
|
||||
private String channelType;
|
||||
|
||||
/** 收银终端设备序列号,sn **/
|
||||
@JSONField(name = "terminal_id")
|
||||
private String terminalId;
|
||||
|
||||
/** 订单状态 **/
|
||||
@JSONField(name = "order_state")
|
||||
private String orderState;
|
||||
|
||||
/** 支付成功时间 **/
|
||||
@JSONField(name = "pay_time")
|
||||
private String payTime;
|
||||
|
||||
/** 订单金额 **/
|
||||
@JSONField(name = "total_amount")
|
||||
private String totalAmount;
|
||||
|
||||
/** 实付金额 **/
|
||||
@JSONField(name = "receipt_amount")
|
||||
private String receiptAmount;
|
||||
|
||||
/** 用户支付金额 **/
|
||||
@JSONField(name = "buyer_pay_amount")
|
||||
private String buyerPayAmount;
|
||||
|
||||
/** 支付宝用户ID **/
|
||||
@JSONField(name = "buyer_user_id")
|
||||
private String buyerUserId;
|
||||
|
||||
/** 支付宝登录账户 **/
|
||||
@JSONField(name = "buyer_account_name")
|
||||
private String buyerAccountName;
|
||||
|
||||
/** 支付请求参数原样返回 **/
|
||||
@JSONField(name = "attach_params")
|
||||
private String attachParams;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.channel.alipay.model.response;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/*
|
||||
* 支付宝SPI 接口返回对象
|
||||
*
|
||||
* @author zx
|
||||
*
|
||||
* @date 2023/3/7 10:35
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class AlipaySpiPayOrderResponse extends SpiBaseResponse {
|
||||
|
||||
/** 商家外部订单号 **/
|
||||
@JSONField(name = "merchant_order_no")
|
||||
private String merchantOrderNo;
|
||||
|
||||
/** ISV 订单号 **/
|
||||
@JSONField(name = "isv_order_no")
|
||||
private String isvOrderNo;
|
||||
|
||||
/** 微信、支付宝等第三方支付交易号 **/
|
||||
@JSONField(name = "channel_order_no")
|
||||
private String channelOrderNo;
|
||||
|
||||
/** 001 **/
|
||||
@JSONField(name = "channel_type")
|
||||
private String channelType;
|
||||
|
||||
/** 订单状态 **/
|
||||
@JSONField(name = "order_state")
|
||||
private String orderState;
|
||||
|
||||
/** 支付成功时间 **/
|
||||
@JSONField(name = "pay_time")
|
||||
private String payTime;
|
||||
|
||||
/** 订单金额 **/
|
||||
@JSONField(name = "total_amount")
|
||||
private String totalAmount;
|
||||
|
||||
/** 实付金额 **/
|
||||
@JSONField(name = "receipt_amount")
|
||||
private String receiptAmount;
|
||||
|
||||
/** 用户支付金额 **/
|
||||
@JSONField(name = "buyer_pay_amount")
|
||||
private String buyerPayAmount;
|
||||
|
||||
/** 支付宝用户ID **/
|
||||
@JSONField(name = "buyer_user_id")
|
||||
private String buyerUserId;
|
||||
|
||||
/** 支付宝登录账户 **/
|
||||
@JSONField(name = "buyer_account_name")
|
||||
private String buyerAccountName;
|
||||
|
||||
/** 支付请求参数原样返回 **/
|
||||
@JSONField(name = "attach_params")
|
||||
private String attachParams;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.channel.alipay.model.response;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/*
|
||||
* 支付宝SPI 收银员签到接口返回对象
|
||||
*
|
||||
* @author zx
|
||||
*
|
||||
* @date 2023/3/7 10:35
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class AlipaySpiRefundAuthCodeApplyResponse extends SpiBaseResponse {
|
||||
|
||||
/** 申请授权码是否成功 true/false **/
|
||||
@JSONField(name = "grant_result")
|
||||
private boolean grantResult;
|
||||
|
||||
/** 授权方式:1.邮件:EMAIL 2.短信: SMS 3.电话:TEL 4.其他: OTHER **/
|
||||
@JSONField(name = "grant_path")
|
||||
private String grantPath;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.channel.alipay.model.response;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/*
|
||||
* 支付宝SPI 退款返回对象
|
||||
*
|
||||
* @author zx
|
||||
*
|
||||
* @date 2023/3/7 10:35
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class AlipaySpiRefundOrderResponse extends SpiBaseResponse {
|
||||
|
||||
/** 商户系统内唯一订单号,对isv为外部订单号 **/
|
||||
@JSONField(name = "merchant_order_no")
|
||||
private String merchantOrderNo;
|
||||
|
||||
/** ISV 订单号 **/
|
||||
@JSONField(name = "isv_order_no")
|
||||
private String isvOrderNo;
|
||||
|
||||
/** 商户请求退款单号 **/
|
||||
@JSONField(name = "merchant_refund_no")
|
||||
private String merchantRefundNo;
|
||||
|
||||
/** ISV平台系统内退款单号 **/
|
||||
@JSONField(name = "isv_refund_no")
|
||||
private String isvRefundNo;
|
||||
|
||||
/** 支付通道退款单号 **/
|
||||
@JSONField(name = "channel_refund_no")
|
||||
private String channelRefundNo;
|
||||
|
||||
/**
|
||||
* 退款单状态。
|
||||
* PROCESSING:申请提交中,未知申请结果,机具端继续轮训;
|
||||
* ACCEPT:申请成功,未明确是否处理成功;
|
||||
* SUCCESS:申请成功,且明确退款处理成功。
|
||||
**/
|
||||
@JSONField(name = "refund_state")
|
||||
private String orderState;
|
||||
|
||||
/** 退款成功时间 **/
|
||||
@JSONField(name = "finish_time")
|
||||
private String finishTime;
|
||||
|
||||
/** 退款金额 **/
|
||||
@JSONField(name = "refund_amount")
|
||||
private String refundAmount;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.channel.alipay.model.response;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alipay.api.AlipayApiException;
|
||||
import com.alipay.api.internal.util.AlipaySignature;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/*
|
||||
* 支付宝SPI 接口返回对象
|
||||
*
|
||||
* @author zx
|
||||
*
|
||||
* @date 2023/3/7 10:35
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Slf4j
|
||||
public class AlipaySpiRes {
|
||||
|
||||
/** response **/
|
||||
private SpiBaseResponse response;
|
||||
|
||||
/** 签名值 **/
|
||||
private String sign;
|
||||
|
||||
/** 业务处理成功 **/
|
||||
public static AlipaySpiRes okWithSign(String logPrefix, SpiBaseResponse response, String privateKey) throws AlipayApiException {
|
||||
SpiBaseResponse.ok(response);
|
||||
AlipaySpiRes alipaySpiRes = new AlipaySpiRes(response, AlipaySignature.rsaSign(JSON.toJSONString(response), privateKey, "UTF-8", "RSA2"));
|
||||
log.info("{}处理成功,响应:{}", logPrefix, JSON.toJSONString(alipaySpiRes));
|
||||
return alipaySpiRes;
|
||||
}
|
||||
|
||||
/** 明确失败,需要重新扫码支付 **/
|
||||
public static AlipaySpiRes failWithSign(String logPrefix, String privateKey, String errCode, String errMsg) {
|
||||
try {
|
||||
AlipaySpiRes alipaySpiRes = new AlipaySpiRes();
|
||||
alipaySpiRes.setResponse(SpiBaseResponse.fail(errCode, errMsg));
|
||||
alipaySpiRes.setSign(AlipaySignature.rsaSign(JSON.toJSONString(alipaySpiRes.getResponse()), privateKey, "UTF-8", "RSA2"));
|
||||
|
||||
log.info("{}处理失败,响应:{}", logPrefix, JSON.toJSONString(alipaySpiRes));
|
||||
return alipaySpiRes;
|
||||
} catch (AlipayApiException e) {
|
||||
log.error("{}响应签名异常", logPrefix, e);
|
||||
return fail(logPrefix, errCode, errMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/** 明确失败,需要重新扫码支付 **/
|
||||
public static AlipaySpiRes fail(String logPrefix, String errCode, String errMsg) {
|
||||
|
||||
AlipaySpiRes alipaySpiRes = new AlipaySpiRes();
|
||||
alipaySpiRes.setResponse(SpiBaseResponse.fail(errCode, errMsg));
|
||||
|
||||
log.info("{}处理失败,响应:{}", logPrefix, JSON.toJSONString(alipaySpiRes));
|
||||
return alipaySpiRes;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.channel.alipay.model.response;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/*
|
||||
* 支付宝SPI 接口返回对象
|
||||
*
|
||||
* @author zx
|
||||
*
|
||||
* @date 2023/3/7 10:35
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class SpiBaseResponse {
|
||||
|
||||
/** 业务响应码 **/
|
||||
@JSONField(name = "code")
|
||||
private String code;
|
||||
|
||||
/** 业务响应信息 **/
|
||||
@JSONField(name = "msg")
|
||||
private String msg;
|
||||
|
||||
/** 业务响应码 **/
|
||||
@JSONField(name = "sub_code")
|
||||
private String subCode;
|
||||
|
||||
/** 业务响应信息 **/
|
||||
@JSONField(name = "sub_msg")
|
||||
private String subMsg;
|
||||
|
||||
/** ISV 自定义错误 **/
|
||||
@JSONField(name = "error_code")
|
||||
private String errorCode;
|
||||
|
||||
/** ISV 自定义错误描述 **/
|
||||
@JSONField(name = "error_desc")
|
||||
private String errorDesc;
|
||||
|
||||
/** 业务处理成功 **/
|
||||
public static void ok(SpiBaseResponse response){
|
||||
response.setCode("10000");
|
||||
response.setMsg("Success");
|
||||
}
|
||||
|
||||
/** 业务处理失败 **/
|
||||
public static SpiBaseResponse fail(String errCode, String errMsg){
|
||||
return new SpiBaseResponse("40004", "Business Failed", errCode, errMsg, errCode, errMsg);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.channel.alipay.service;
|
||||
|
||||
import com.jeequan.jeepay.db.entity.MchStoreDevice;
|
||||
import com.jeequan.jeepay.pay.ctrl.channel.alipay.model.response.AlipaySpiCashierBatchQueryResponse;
|
||||
import com.jeequan.jeepay.pay.ctrl.channel.alipay.model.response.AlipaySpiCashierQueryResponse;
|
||||
import com.jeequan.jeepay.pay.ctrl.channel.alipay.model.response.AlipaySpiCashierSignResponse;
|
||||
import com.jeequan.jeepay.pay.ctrl.channel.alipay.model.response.AlipaySpiCashierUnSignResponse;
|
||||
import com.jeequan.jeepay.pay.ctrl.payorder.AbstractPayOrderController;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 支付宝如意spi接口:收银员操作
|
||||
*
|
||||
* @author zx
|
||||
*
|
||||
* @date 2021-04-27 15:50
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class AlipaySpiRuyiCashierService extends AbstractPayOrderController {
|
||||
|
||||
/** 收银员签到 **/
|
||||
public AlipaySpiCashierSignResponse cashierSign(MchStoreDevice mchStoreDevice) {
|
||||
String cashierId = getValString("cashier_id");
|
||||
|
||||
AlipaySpiCashierSignResponse response = new AlipaySpiCashierSignResponse();
|
||||
response.setStatus("1");
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/** 收银员签退 **/
|
||||
public AlipaySpiCashierUnSignResponse cashierUnsign(MchStoreDevice mchStoreDevice) {
|
||||
String cashierId = getValString("cashier_id");
|
||||
|
||||
AlipaySpiCashierUnSignResponse response = new AlipaySpiCashierUnSignResponse();
|
||||
response.setStatus("1");
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/** 查询收银员列表 **/
|
||||
public AlipaySpiCashierBatchQueryResponse batchQuery(MchStoreDevice mchStoreDevice) {
|
||||
AlipaySpiCashierBatchQueryResponse response = new AlipaySpiCashierBatchQueryResponse();
|
||||
|
||||
/*List<AlipaySpiCashierBatchQueryResponse.Cashier> cashierList = new ArrayList<>();
|
||||
|
||||
AlipaySpiCashierBatchQueryResponse.Cashier cashier = new AlipaySpiCashierBatchQueryResponse.Cashier();
|
||||
cashier.setCashierId("1001");
|
||||
cashier.setCashierName("计全科技收银员1");
|
||||
cashierList.add(cashier);
|
||||
|
||||
response.setCashierList(cashierList);*/
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/** 查询收银员状态 **/
|
||||
public AlipaySpiCashierQueryResponse cashierQuery(MchStoreDevice mchStoreDevice) {
|
||||
AlipaySpiCashierQueryResponse response = new AlipaySpiCashierQueryResponse();
|
||||
response.setStatus("0");
|
||||
response.setCashierId("0");
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.channel.alipay.service;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.date.DateTime;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alipay.api.AlipayApiException;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.jeequan.jeepay.core.constants.ApiCodeEnum;
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.entity.MchApp;
|
||||
import com.jeequan.jeepay.core.entity.PayOrderCount;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.model.ApiRes;
|
||||
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
|
||||
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRS;
|
||||
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.AliBarOrderRQ;
|
||||
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.WxBarOrderRQ;
|
||||
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.YsfBarOrderRQ;
|
||||
import com.jeequan.jeepay.core.utils.AmountUtil;
|
||||
import com.jeequan.jeepay.core.utils.JeepayKit;
|
||||
import com.jeequan.jeepay.core.utils.JsonKit;
|
||||
import com.jeequan.jeepay.db.entity.MchStoreDevice;
|
||||
import com.jeequan.jeepay.db.entity.PayOrder;
|
||||
import com.jeequan.jeepay.pay.ctrl.channel.alipay.model.response.*;
|
||||
import com.jeequan.jeepay.pay.ctrl.channel.alipay.util.AlipaySpiRuyiUtil;
|
||||
import com.jeequan.jeepay.pay.ctrl.payorder.AbstractPayOrderController;
|
||||
import com.jeequan.jeepay.service.impl.PayOrderService;
|
||||
import com.jeequan.jeepay.service.impl.StatsTradeService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 支付宝如意spi接口:下单、查单
|
||||
*
|
||||
* @author zx
|
||||
*
|
||||
* @date 2021-04-27 15:50
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class AlipaySpiRuyiPayOrderService extends AbstractPayOrderController {
|
||||
|
||||
@Autowired private PayOrderService payOrderService;
|
||||
@Autowired private StatsTradeService statsTradeService;
|
||||
|
||||
/** 下单 **/
|
||||
public AlipaySpiPayOrderResponse pay(MchStoreDevice mchStoreDevice) throws Exception {
|
||||
String logPrefix = "【支付宝如意Lite】下单";
|
||||
|
||||
Long amountL = getRequiredAmountL("total_amount");
|
||||
String authCode = getValStringRequired("auth_code");
|
||||
String subject = getValStringDefault("subject", "支付宝如意Lite收款");
|
||||
String merchantOrderNo = getValStringRequired("merchant_order_no");
|
||||
String extendParams = getValString("extend_params");
|
||||
Integer payTimeout = getValInteger("pay_timeout"); // 订单有效时间,需要控制未支付超时关闭,单位为分钟,格式为整数。
|
||||
String attachParams = getValString("attach_params"); // 附加字段,在支付结果、查询、支付结果异步通知,原样返回。
|
||||
|
||||
// 端上超时会相同参数重复调用
|
||||
PayOrder payOrder = payOrderService.queryMchOrder(mchStoreDevice.getMchNo(), null, merchantOrderNo);
|
||||
if (payOrder != null) {
|
||||
return AlipaySpiRuyiUtil.covertToAlipaySpiPayOrderResponse(payOrder);
|
||||
}
|
||||
|
||||
// 封装下单参数
|
||||
UnifiedOrderRQ rq = buildRqByAuthCode(authCode);
|
||||
rq.setMchOrderNo(merchantOrderNo);
|
||||
rq.setCurrency("cny");
|
||||
rq.setAmount(amountL);
|
||||
rq.setSubject(subject);
|
||||
rq.setBody(subject);
|
||||
rq.setExtParam(attachParams);
|
||||
|
||||
rq.setMchNo(mchStoreDevice.getMchNo());
|
||||
rq.setAppId(mchStoreDevice.getAppId());
|
||||
rq.setStoreId(mchStoreDevice.getStoreId());
|
||||
|
||||
if (payTimeout != null) {
|
||||
rq.setExpiredTime(payTimeout * 60);
|
||||
}
|
||||
|
||||
// 设备信息
|
||||
UnifiedOrderRQ.DeviceInfo deviceInfo = new UnifiedOrderRQ.DeviceInfo();
|
||||
deviceInfo.setDeviceNo(mchStoreDevice.getDeviceNo());
|
||||
deviceInfo.setDeviceType(PayOrder.DEVICE_TYPE_ALIPAY_RUYI_LITE);
|
||||
deviceInfo.setProvider(mchStoreDevice.getProvider());
|
||||
rq.setDeviceInfo(JSONObject.toJSONString(deviceInfo));
|
||||
|
||||
rq.setSignType(MchApp.SIGN_TYPE_MD5); // 设置默认签名方式为MD5
|
||||
|
||||
ApiRes apiRes = this.unifiedOrder(rq.getWayCode(), rq, null,null);
|
||||
logger.info("{}调起支付,返回apiRes:{}", logPrefix, JSON.toJSONString(apiRes));
|
||||
if (ApiCodeEnum.SUCCESS.getCode() != apiRes.getCode()) {
|
||||
throw new AlipayApiException("BIZ_ERROR", apiRes.getMsg());
|
||||
}
|
||||
|
||||
UnifiedOrderRS aliBarOrderRS = (UnifiedOrderRS) apiRes.getData();
|
||||
|
||||
PayOrder dbRecord = payOrderService.getById(aliBarOrderRS.getPayOrderId());
|
||||
|
||||
AlipaySpiPayOrderResponse response = AlipaySpiRuyiUtil.covertToAlipaySpiPayOrderResponse(dbRecord);
|
||||
return response;
|
||||
}
|
||||
|
||||
/** 查单 **/
|
||||
public AlipaySpiPayOrderQueryResponse payQuery(MchStoreDevice mchStoreDevice) throws AlipayApiException {
|
||||
String payOrderId = getValString("isv_order_no");
|
||||
String mchOrderNo = getValString("merchant_order_no");
|
||||
|
||||
PayOrder payOrder = payOrderService.queryMchOrder(mchStoreDevice.getMchNo(), payOrderId, mchOrderNo);
|
||||
if (payOrder == null || !PayOrder.DEVICE_TYPE_ALIPAY_RUYI_LITE.equals(payOrder.getDeviceType()) || !payOrder.getDeviceNo().equals(mchStoreDevice.getDeviceNo())) {
|
||||
throw new AlipayApiException("ORDER_NOT_EXIST", "支付订单不存在");
|
||||
}
|
||||
return AlipaySpiRuyiUtil.covertToAlipaySpiPayOrderQueryResponse(payOrder);
|
||||
}
|
||||
|
||||
/** 账单流水查询 **/
|
||||
public SpiBaseResponse billdetailQuery(MchStoreDevice mchStoreDevice, Integer pageNum, Integer pageSize, String queryDate) {
|
||||
PayOrder payOrderParams = new PayOrder();
|
||||
// payOrderParams.setDeviceNo(mchStoreDevice.getDeviceNo()); // 设备号
|
||||
// payOrderParams.setDeviceType(PayOrder.DEVICE_TYPE_ALIPAY_RUYI_LITE); // 如意Lite设备类型
|
||||
payOrderParams.setMchNo(mchStoreDevice.getMchNo()); // 当前绑定的商户
|
||||
payOrderParams.setStoreId(mchStoreDevice.getStoreId()); // 当前绑定的门店
|
||||
payOrderParams.addExt("queryDateRange", covertQueryDateStr(getQueryDate(queryDate)));
|
||||
|
||||
// 分页查询
|
||||
IPage<PayOrder> page = payOrderService.listByPage(new Page(pageNum, pageSize), payOrderParams, JsonKit.newJson("unionOrderState", "2,4,5"));
|
||||
|
||||
List<AlipaySpiBilldetailQueryResponse.Order> orderList = new ArrayList<>();
|
||||
if (CollUtil.isNotEmpty(page.getRecords())) {
|
||||
page.getRecords().forEach(payOrder -> {
|
||||
AlipaySpiBilldetailQueryResponse.Order alipayOrder = new AlipaySpiBilldetailQueryResponse.Order();
|
||||
alipayOrder.setOrderType(payOrder.getState() == PayOrder.STATE_SUCCESS ? "PAY" : payOrder.getState() == PayOrder.STATE_REFUND ? "REFUND" : "PAY");
|
||||
alipayOrder.setMerchantOrderNo(payOrder.getMchOrderNo());
|
||||
alipayOrder.setIsvOrderNo(payOrder.getPayOrderId());
|
||||
alipayOrder.setChannelOrderNo(payOrder.getChannelOrderNo());
|
||||
alipayOrder.setChannelType(AlipaySpiRuyiUtil.covertPaywayTypeToChannelType(payOrder.getWayCodeType()));
|
||||
alipayOrder.setTotalAmount(AmountUtil.convertCent2Dollar(payOrder.getAmount()));
|
||||
alipayOrder.setTerminalId(payOrder.getDeviceNo());
|
||||
alipayOrder.setSubject(payOrder.getSubject());
|
||||
|
||||
if (payOrder.getState() == PayOrder.STATE_SUCCESS) {
|
||||
alipayOrder.setReceiptAmount(alipayOrder.getTotalAmount());
|
||||
alipayOrder.setBuyerPayAmount(alipayOrder.getTotalAmount());
|
||||
alipayOrder.setOrderTime(DateUtil.formatDateTime(payOrder.getSuccessTime()));
|
||||
}else if (payOrder.getState() == PayOrder.STATE_REFUND) {
|
||||
alipayOrder.setRefundAmount(AmountUtil.convertCent2Dollar(payOrder.getRefundAmount()));
|
||||
alipayOrder.setOrderTime(DateUtil.formatDateTime(payOrder.getSuccessTime()));
|
||||
}
|
||||
|
||||
// alipayOrder.setMerchantRefundNo();
|
||||
// alipayOrder.setIsvRefundNo();
|
||||
// alipayOrder.setChannelRefundNo();
|
||||
orderList.add(alipayOrder);
|
||||
});
|
||||
}
|
||||
|
||||
AlipaySpiBilldetailQueryResponse response = new AlipaySpiBilldetailQueryResponse();
|
||||
response.setTotalSize(String.valueOf(page.getTotal()));
|
||||
response.setOrderList(orderList);
|
||||
return response;
|
||||
}
|
||||
|
||||
/** 账单统计查询 **/
|
||||
public SpiBaseResponse billstatisticsQuery(MchStoreDevice mchStoreDevice, String queryDateStr) {
|
||||
|
||||
// 查询条件
|
||||
JSONObject paramJSON = new JSONObject();
|
||||
|
||||
// 门店ID条件
|
||||
List<String> storeIdList = new ArrayList<>();
|
||||
storeIdList.add(mchStoreDevice.getStoreId());
|
||||
paramJSON.put("storeIdList", storeIdList);
|
||||
|
||||
// 时间条件
|
||||
DateTime queryDate = getQueryDate(queryDateStr);
|
||||
paramJSON.put("queryDateRange", covertQueryDateStr(queryDate));
|
||||
|
||||
PayOrderCount orderCount = statsTradeService.selectTotalByStore(paramJSON, null);
|
||||
|
||||
AlipaySpiBillstatisticsQueryResponse response = new AlipaySpiBillstatisticsQueryResponse();
|
||||
response.setStatisticTime(DateUtil.formatDateTime(queryDate));
|
||||
Long payAmount = orderCount.getTotalSuccAmt();
|
||||
response.setReceiptAmount(BigDecimal.valueOf(payAmount).divide(new BigDecimal("100")).setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());
|
||||
response.setReceiptCount(orderCount.getTotalNum().toString());
|
||||
|
||||
Long refundAmt = orderCount.getTotalRefundAmt();
|
||||
response.setRefundAmount(BigDecimal.valueOf(refundAmt).divide(new BigDecimal("100")).setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());
|
||||
response.setRefundCount(orderCount.getTotalRefundNum().toString());
|
||||
response.setTotalIncome(BigDecimal.valueOf(payAmount- refundAmt).divide(new BigDecimal("100")).setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
private UnifiedOrderRQ buildRqByAuthCode(String authCode) {
|
||||
String wayCode = JeepayKit.getPayWayCodeByBarCode(authCode);
|
||||
if (CS.PAY_WAY_CODE.WX_BAR.equals(wayCode)) {
|
||||
WxBarOrderRQ wxBarOrderRQ = new WxBarOrderRQ();
|
||||
wxBarOrderRQ.setAuthCode(authCode);
|
||||
return wxBarOrderRQ;
|
||||
}else if (CS.PAY_WAY_CODE.ALI_BAR.equals(wayCode)) {
|
||||
AliBarOrderRQ aliBarOrderRQ = new AliBarOrderRQ();
|
||||
aliBarOrderRQ.setAuthCode(authCode);
|
||||
return aliBarOrderRQ;
|
||||
}else if (CS.PAY_WAY_CODE.YSF_BAR.equals(wayCode)) {
|
||||
YsfBarOrderRQ ysfBarOrderRQ = new YsfBarOrderRQ();
|
||||
ysfBarOrderRQ.setAuthCode(authCode);
|
||||
return ysfBarOrderRQ;
|
||||
}else {
|
||||
throw new BizException("不支持的条码");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取查询时间范围
|
||||
*/
|
||||
private DateTime getQueryDate(String queryDateStr) {
|
||||
// 时间搜索条件,默认今天
|
||||
DateTime queryDate = DateUtil.date();
|
||||
if (StringUtils.isNotBlank(queryDateStr)) {
|
||||
queryDate = DateUtil.parseDate(queryDateStr);
|
||||
}
|
||||
return queryDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换查询时间范围
|
||||
*/
|
||||
private String covertQueryDateStr(DateTime queryDate) {
|
||||
return "customDate_"+DateUtil.formatDate(DateUtil.beginOfDay(queryDate))+"_"+DateUtil.formatDate(DateUtil.endOfDay(queryDate));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.channel.alipay.service;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alipay.api.AlipayApiException;
|
||||
import com.jeequan.jeepay.core.constants.ApiCodeEnum;
|
||||
import com.jeequan.jeepay.core.entity.MchApp;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.model.ApiRes;
|
||||
import com.jeequan.jeepay.core.model.rqrs.refund.RefundOrderRQ;
|
||||
import com.jeequan.jeepay.core.model.rqrs.refund.RefundOrderRS;
|
||||
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
|
||||
import com.jeequan.jeepay.db.entity.MchStoreDevice;
|
||||
import com.jeequan.jeepay.db.entity.PayOrder;
|
||||
import com.jeequan.jeepay.db.entity.RefundOrder;
|
||||
import com.jeequan.jeepay.pay.ctrl.channel.alipay.model.response.AlipaySpiPayOrderQueryResponse;
|
||||
import com.jeequan.jeepay.pay.ctrl.channel.alipay.model.response.AlipaySpiRefundAuthCodeApplyResponse;
|
||||
import com.jeequan.jeepay.pay.ctrl.channel.alipay.model.response.AlipaySpiRefundOrderResponse;
|
||||
import com.jeequan.jeepay.pay.ctrl.channel.alipay.model.response.SpiBaseResponse;
|
||||
import com.jeequan.jeepay.pay.ctrl.channel.alipay.util.AlipaySpiRuyiUtil;
|
||||
import com.jeequan.jeepay.pay.ctrl.payorder.AbstractPayOrderController;
|
||||
import com.jeequan.jeepay.pay.ctrl.refund.RefundOrderController;
|
||||
import com.jeequan.jeepay.service.impl.PayOrderService;
|
||||
import com.jeequan.jeepay.service.impl.RefundOrderService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 支付宝如意spi接口:退款、退款查单
|
||||
*
|
||||
* @author zx
|
||||
*
|
||||
* @date 2021-04-27 15:50
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class AlipaySpiRuyiRefundOrderService extends AbstractPayOrderController {
|
||||
|
||||
@Autowired private PayOrderService payOrderService;
|
||||
@Autowired private RefundOrderService refundOrderService;
|
||||
|
||||
/** 通过交易凭证商户单号查询支付订单 **/
|
||||
public AlipaySpiPayOrderQueryResponse billQuery(MchStoreDevice mchStoreDevice) {
|
||||
String platformMchOrderNo = getValStringRequired("bill_merchant_no");
|
||||
|
||||
PayOrder payOrder = payOrderService.selectOneByPlatformMchOrderNo(platformMchOrderNo);
|
||||
if (payOrder == null || !PayOrder.DEVICE_TYPE_ALIPAY_RUYI_LITE.equals(payOrder.getDeviceType()) || !payOrder.getDeviceNo().equals(mchStoreDevice.getDeviceNo())) {
|
||||
throw new BizException("支付订单不存在");
|
||||
}
|
||||
return AlipaySpiRuyiUtil.covertToAlipaySpiPayOrderQueryResponse(payOrder);
|
||||
}
|
||||
|
||||
/** 申请退款授权码 **/
|
||||
public SpiBaseResponse refundAuthCodeApply(MchStoreDevice mchStoreDevice) {
|
||||
AlipaySpiRefundAuthCodeApplyResponse response = new AlipaySpiRefundAuthCodeApplyResponse();
|
||||
response.setGrantResult(true);
|
||||
response.setGrantPath("OTHER");
|
||||
return response;
|
||||
}
|
||||
|
||||
/** 退款 **/
|
||||
public AlipaySpiRefundOrderResponse refund(MchStoreDevice mchStoreDevice) throws AlipayApiException {
|
||||
String logPrefix = "【支付宝如意Lite】退款";
|
||||
|
||||
Long refundAmount = getRequiredAmountL("refund_amount");
|
||||
String payOrderId = getValStringRequired("isv_order_no"); // 服务商测订单号
|
||||
String merchantOrderNo = getValStringRequired("merchant_order_no"); // 商家订单号
|
||||
String merchantRefundNo = getValStringRequired("merchant_refund_no"); // 商家退款单号
|
||||
String authCode = getValString("auth_code");
|
||||
|
||||
// 端上超时会相同参数重复调用
|
||||
RefundOrder refundOrder = refundOrderService.queryMchOrder(mchStoreDevice.getMchNo(), merchantRefundNo, null);
|
||||
if (refundOrder != null) {
|
||||
return AlipaySpiRuyiUtil.covertToAlipaySpiRefundOrderResponse(refundOrder, merchantOrderNo);
|
||||
}
|
||||
|
||||
// 封装下单参数
|
||||
RefundOrderRQ rq = new RefundOrderRQ();
|
||||
rq.setMchOrderNo(merchantOrderNo);
|
||||
rq.setPayOrderId(payOrderId);
|
||||
rq.setMchRefundNo(merchantRefundNo);
|
||||
rq.setCurrency("cny");
|
||||
rq.setRefundAmount(refundAmount);
|
||||
rq.setRefundReason("如意Lite退款");
|
||||
rq.setClientIp(getClientIp());
|
||||
|
||||
rq.setMchNo(mchStoreDevice.getMchNo());
|
||||
rq.setAppId(mchStoreDevice.getAppId());
|
||||
|
||||
rq.setSignType(MchApp.SIGN_TYPE_MD5); // 设置默认签名方式为MD5
|
||||
|
||||
RefundOrderController refundOrderController = SpringBeansUtil.getBean(RefundOrderController.class);
|
||||
ApiRes apiRes = refundOrderController.refund(rq);
|
||||
logger.info("{}发起退款,返回apiRes:{}", logPrefix, JSON.toJSONString(apiRes));
|
||||
if (ApiCodeEnum.SUCCESS.getCode() != apiRes.getCode()) {
|
||||
throw new AlipayApiException("BIZ_ERROR", apiRes.getMsg());
|
||||
}
|
||||
|
||||
RefundOrderRS refundOrderRS = (RefundOrderRS) apiRes.getData();
|
||||
|
||||
RefundOrder dbRecord = refundOrderService.getById(refundOrderRS.getRefundOrderId());
|
||||
AlipaySpiRefundOrderResponse response = AlipaySpiRuyiUtil.covertToAlipaySpiRefundOrderResponse(dbRecord, merchantOrderNo);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/** 通过交易凭证商户单号查询支付订单 **/
|
||||
public AlipaySpiRefundOrderResponse refundQuery(MchStoreDevice mchStoreDevice) {
|
||||
String merchantOrderNo = getValStringRequired("merchant_order_no"); // 商家订单号
|
||||
String payOrderId = getValStringRequired("isv_order_no"); // 服务商订单号
|
||||
String merchantRefundNo = getValStringRequired("merchant_refund_no"); // 商家退款单号
|
||||
String refundOrderId = getValString("isv_refund_no"); // 服务商退款订单号
|
||||
|
||||
RefundOrder refundOrder = refundOrderService.queryMchOrder(mchStoreDevice.getMchNo(), merchantRefundNo, refundOrderId);
|
||||
if (refundOrder == null || !refundOrder.getPayOrderId().equals(payOrderId)) {
|
||||
throw new BizException("退款订单不存在");
|
||||
}
|
||||
return AlipaySpiRuyiUtil.covertToAlipaySpiRefundOrderResponse(refundOrder, merchantOrderNo);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.channel.alipay.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/*
|
||||
* 支付宝异步通知消息 工具类
|
||||
*
|
||||
* @author zx
|
||||
*
|
||||
* @date 2023/3/21 17:40
|
||||
*/
|
||||
public class AlipayNotifyUtil {
|
||||
|
||||
public static final String NOTIFY_UNKNOWN = "unknown"; // 未知消息
|
||||
public static final String NOTIFY_SP_OPERATION_RESULT = "alipay.open.sp.operation.result.notify"; // 服务商代运营操作结果通知
|
||||
public static final String NOTIFY_SHOP_SAVE_PASSED = "ant.merchant.expand.shop.save.passed"; // 蚂蚁店铺保存审核通过消息
|
||||
public static final String NOTIFY_SHOP_SAVE_REJECTED = "ant.merchant.expand.shop.save.rejected"; // 店铺保存拒绝消息
|
||||
|
||||
public static final Map<String, String> notifyMap = new HashMap<>();
|
||||
static {
|
||||
notifyMap.put(NOTIFY_UNKNOWN, "未知接口");
|
||||
notifyMap.put(NOTIFY_SP_OPERATION_RESULT, "服务商代运营操作结果通知");
|
||||
notifyMap.put(NOTIFY_SHOP_SAVE_PASSED, "蚂蚁店铺保存审核通过消息");
|
||||
notifyMap.put(NOTIFY_SHOP_SAVE_REJECTED, "店铺保存拒绝消息");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.channel.alipay.util;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.utils.AmountUtil;
|
||||
import com.jeequan.jeepay.db.entity.PayOrder;
|
||||
import com.jeequan.jeepay.db.entity.RefundOrder;
|
||||
import com.jeequan.jeepay.pay.ctrl.channel.alipay.model.response.AlipaySpiPayOrderQueryResponse;
|
||||
import com.jeequan.jeepay.pay.ctrl.channel.alipay.model.response.AlipaySpiPayOrderResponse;
|
||||
import com.jeequan.jeepay.pay.ctrl.channel.alipay.model.response.AlipaySpiRefundOrderResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/*
|
||||
* 支付宝spi工具
|
||||
*
|
||||
* @author zx
|
||||
*
|
||||
* @date 2021/6/8 17:40
|
||||
*/
|
||||
@Slf4j
|
||||
public class AlipaySpiRuyiUtil {
|
||||
|
||||
public static final String SPI_RUYI_UNKNOWN = "unknown";
|
||||
public static final String SPI_RUYI_PAY = "spi.alipay.merchant.order.commonisv.pay";
|
||||
public static final String SPI_RUYI_PAY_QUERY = "spi.alipay.merchant.order.commonisv.query";
|
||||
public static final String SPI_RUYI_BILL_QUERY = "spi.alipay.merchant.order.commonisv.bill.query";
|
||||
public static final String SPI_RUYI_REFUND_AUTHCODE_APPLY = "spi.alipay.commerce.refundauthcode.apply";
|
||||
public static final String SPI_RUYI_REFUND = "spi.alipay.merchant.order.commonisv.refund";
|
||||
public static final String SPI_RUYI_REFUND_QUERY = "spi.alipay.merchant.order.commonisv.refund.query";
|
||||
public static final String SPI_RUYI_BILL_DETAIL_QUERY = "spi.alipay.commerce.billdetail.query";
|
||||
public static final String SPI_RUYI_BILL_STATISTICS_QUERY = "spi.alipay.commerce.billstatistics.query";
|
||||
public static final String SPI_RUYI_CASHIER_SIGN = "spi.alipay.commerce.fmcgsaascashier.sign";
|
||||
public static final String SPI_RUYI_CASHIER_UNSIGN = "spi.alipay.commerce.fmcgsaascashier.unsign";
|
||||
public static final String SPI_RUYI_CASHIER_BATCH_QUERY = "spi.alipay.commerce.fmcgsaascashier.batchquery";
|
||||
public static final String SPI_RUYI_CASHIER_QUERY = "spi.alipay.commerce.fmcgsaascashier.query";
|
||||
|
||||
public static Map<String, String> spiMap = new HashMap<>();
|
||||
static {
|
||||
spiMap.put(SPI_RUYI_UNKNOWN, "未知接口");
|
||||
spiMap.put(SPI_RUYI_PAY, "支付下单接口");
|
||||
spiMap.put(SPI_RUYI_PAY_QUERY, "支付查单接口");
|
||||
spiMap.put(SPI_RUYI_BILL_QUERY, "扫支付凭证商户单号查单接口");
|
||||
spiMap.put(SPI_RUYI_REFUND_AUTHCODE_APPLY, "申请退款授权码接口");
|
||||
spiMap.put(SPI_RUYI_REFUND, "发起退款接口");
|
||||
spiMap.put(SPI_RUYI_REFUND_QUERY, "退款查单接口");
|
||||
spiMap.put(SPI_RUYI_BILL_DETAIL_QUERY, "账单流水查询接口");
|
||||
spiMap.put(SPI_RUYI_BILL_STATISTICS_QUERY, "账单统计查询接口");
|
||||
spiMap.put(SPI_RUYI_CASHIER_SIGN, "收银员签到接口");
|
||||
spiMap.put(SPI_RUYI_CASHIER_UNSIGN, "收银员签退接口");
|
||||
spiMap.put(SPI_RUYI_CASHIER_BATCH_QUERY, "查询收银员列表接口");
|
||||
spiMap.put(SPI_RUYI_CASHIER_QUERY, "查询收银员状态接口");
|
||||
}
|
||||
|
||||
/** 支付订单下单接口 转 支付宝订单响应 **/
|
||||
public static AlipaySpiPayOrderResponse covertToAlipaySpiPayOrderResponse(PayOrder payOrder) {
|
||||
AlipaySpiPayOrderResponse response = new AlipaySpiPayOrderResponse();
|
||||
response.setMerchantOrderNo(payOrder.getMchOrderNo());
|
||||
response.setIsvOrderNo(payOrder.getPayOrderId());
|
||||
response.setChannelOrderNo(StringUtils.defaultString(payOrder.getChannelOrderNo(), ""));
|
||||
response.setChannelType(covertPaywayTypeToChannelType(payOrder.getWayCodeType()));
|
||||
response.setOrderState(covertToAlipayOrderState(payOrder.getState(), payOrder.getRefundState()));
|
||||
response.setPayTime(DateUtil.formatDateTime(payOrder.getSuccessTime()));
|
||||
response.setTotalAmount(AmountUtil.convertCent2Dollar(payOrder.getAmount()));
|
||||
response.setReceiptAmount(AmountUtil.convertCent2Dollar(payOrder.getAmount()));
|
||||
response.setBuyerPayAmount(AmountUtil.convertCent2Dollar(payOrder.getAmount()));
|
||||
response.setBuyerUserId(payOrder.getChannelUser());
|
||||
response.setBuyerAccountName("");
|
||||
response.setAttachParams(payOrder.getExtParam());
|
||||
return response;
|
||||
}
|
||||
|
||||
/** 支付订单查单接口 转 支付宝订单响应 **/
|
||||
public static AlipaySpiPayOrderQueryResponse covertToAlipaySpiPayOrderQueryResponse(PayOrder payOrder) {
|
||||
AlipaySpiPayOrderQueryResponse response = new AlipaySpiPayOrderQueryResponse();
|
||||
response.setMerchantOrderNo(payOrder.getMchOrderNo());
|
||||
response.setIsvOrderNo(payOrder.getPayOrderId());
|
||||
response.setChannelOrderNo(payOrder.getChannelOrderNo());
|
||||
response.setChannelType(covertPaywayTypeToChannelType(payOrder.getWayCodeType()));
|
||||
response.setTerminalId(payOrder.getDeviceNo());
|
||||
response.setOrderState(covertToAlipayOrderState(payOrder.getState(), payOrder.getRefundState()));
|
||||
response.setPayTime(DateUtil.formatDateTime(payOrder.getSuccessTime()));
|
||||
response.setTotalAmount(AmountUtil.convertCent2Dollar(payOrder.getAmount()));
|
||||
response.setReceiptAmount(AmountUtil.convertCent2Dollar(payOrder.getAmount()));
|
||||
response.setBuyerPayAmount(AmountUtil.convertCent2Dollar(payOrder.getAmount()));
|
||||
response.setBuyerUserId(payOrder.getChannelUser());
|
||||
response.setBuyerAccountName("");
|
||||
response.setAttachParams(payOrder.getExtParam());
|
||||
return response;
|
||||
}
|
||||
|
||||
/** 退款订单 转 支付宝退款订单响应 **/
|
||||
public static AlipaySpiRefundOrderResponse covertToAlipaySpiRefundOrderResponse(RefundOrder refundOrder, String mchOrderNo) {
|
||||
AlipaySpiRefundOrderResponse response = new AlipaySpiRefundOrderResponse();
|
||||
response.setMerchantOrderNo(mchOrderNo);
|
||||
response.setIsvOrderNo(refundOrder.getPayOrderId());
|
||||
response.setMerchantRefundNo(refundOrder.getMchRefundNo());
|
||||
response.setIsvRefundNo(refundOrder.getRefundOrderId());
|
||||
response.setChannelRefundNo(refundOrder.getChannelOrderNo());
|
||||
response.setRefundAmount(AmountUtil.convertCent2Dollar(refundOrder.getRefundAmount()));
|
||||
|
||||
if (refundOrder.getState() == RefundOrder.STATE_SUCCESS) {
|
||||
response.setFinishTime(DateUtil.formatDateTime(refundOrder.getSuccessTime()));
|
||||
response.setOrderState("SUCCESS");
|
||||
}else if (refundOrder.getState() == RefundOrder.STATE_ING) {
|
||||
response.setOrderState("ACCEPT");
|
||||
}else if (refundOrder.getState() == RefundOrder.STATE_INIT) {
|
||||
response.setOrderState("PROCESSING");
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/** 转换支付订单状态 **/
|
||||
private static String covertToAlipayOrderState(Byte state, Byte refundState) {
|
||||
switch (state) {
|
||||
case PayOrder.STATE_INIT:
|
||||
case PayOrder.STATE_ING: return "WAIT_PAY";
|
||||
case PayOrder.STATE_SUCCESS: {
|
||||
if (refundState == PayOrder.REFUND_STATE_NONE) {
|
||||
return "ORDER_SUCCESS";
|
||||
}else if (refundState == PayOrder.REFUND_STATE_SUB) {
|
||||
return "REFUND_PART";
|
||||
}
|
||||
}
|
||||
case PayOrder.STATE_REFUND: return "REFUND_SUCCESS";
|
||||
default: return "ORDER_CLOSED";
|
||||
}
|
||||
}
|
||||
|
||||
/** 转换支付方式类型 **/
|
||||
public static String covertPaywayTypeToChannelType(String wayCodeType) {
|
||||
switch (wayCodeType) {
|
||||
case CS.PAY_WAY_CODE_TYPE.ALIPAY: return "001";
|
||||
case CS.PAY_WAY_CODE_TYPE.WECHAT: return "002";
|
||||
case CS.PAY_WAY_CODE_TYPE.YSFPAY:
|
||||
case CS.PAY_WAY_CODE_TYPE.UNIONPAY:
|
||||
return "007";
|
||||
default: return "100";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.device.speaker;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.interfaces.device.ISpeakerService;
|
||||
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
|
||||
import com.jeequan.jeepay.pay.ctrl.ApiController;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
* 音箱相关接口类型
|
||||
* @author crystal
|
||||
* @date 2023/12/28 11:23
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/speaker")
|
||||
public class SpeakerController extends ApiController {
|
||||
|
||||
@RequestMapping(value = "/bind/status/{provider}")
|
||||
public JSONObject bindStatus(@PathVariable("provider") String provider){
|
||||
JSONObject params = getReqParamJSON();
|
||||
//获取音箱接口配置
|
||||
ISpeakerService speakerService = SpringBeansUtil.getBean(provider + "SpeakerService", ISpeakerService.class);
|
||||
return speakerService.bindQuery(params);
|
||||
}
|
||||
@RequestMapping(value = "/get/url/{provider}")
|
||||
public JSONObject getUrl(@PathVariable("provider") String provider){
|
||||
JSONObject reqData = getReqParamJSON();
|
||||
//获取音箱接口配置
|
||||
ISpeakerService speakerService = SpringBeansUtil.getBean(provider + "SpeakerService", ISpeakerService.class);
|
||||
return speakerService.getUrl(reqData);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.division;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.jeequan.jeepay.core.aop.MethodLog;
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.entity.DivisionSettleInfo;
|
||||
import com.jeequan.jeepay.core.entity.DivisionSubjectEx;
|
||||
import com.jeequan.jeepay.core.entity.MchInfo;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.interfaces.IConfigContextQueryService;
|
||||
import com.jeequan.jeepay.core.interfaces.paychannel.IDivisionService;
|
||||
import com.jeequan.jeepay.core.model.ApiRes;
|
||||
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
|
||||
import com.jeequan.jeepay.core.model.rqrs.division.DivisionReceiverBindRQ;
|
||||
import com.jeequan.jeepay.core.model.rqrs.division.DivisionReceiverBindRS;
|
||||
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
|
||||
import com.jeequan.jeepay.core.utils.JeepayKit;
|
||||
import com.jeequan.jeepay.core.utils.SeqKit;
|
||||
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
|
||||
import com.jeequan.jeepay.db.entity.DivisionSubject;
|
||||
import com.jeequan.jeepay.db.entity.MchDivisionReceiver;
|
||||
import com.jeequan.jeepay.db.entity.MchDivisionReceiverGroup;
|
||||
import com.jeequan.jeepay.pay.ctrl.ApiController;
|
||||
import com.jeequan.jeepay.pay.util.ApiResKit;
|
||||
import com.jeequan.jeepay.service.impl.DivisionSubjectService;
|
||||
import com.jeequan.jeepay.service.impl.MchDivisionReceiverGroupService;
|
||||
import com.jeequan.jeepay.service.impl.MchDivisionReceiverService;
|
||||
import com.jeequan.jeepay.service.impl.PayInterfaceConfigService;
|
||||
import com.sun.org.apache.xpath.internal.operations.Div;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 分账相关操作接口
|
||||
* @author terrfly
|
||||
*
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController("/api/division")
|
||||
public class DivisionController extends ApiController {
|
||||
|
||||
@Autowired private IConfigContextQueryService configContextQueryService;
|
||||
@Autowired private PayInterfaceConfigService payInterfaceConfigService;
|
||||
@Autowired private DivisionSubjectService divisionSubjectService;
|
||||
@Autowired private MchDivisionReceiverGroupService mchDivisionReceiverGroupService;
|
||||
|
||||
/** 分账方创建 **/
|
||||
@MethodLog(remark = "绑定分账方")
|
||||
@PostMapping("/receiver/bind")
|
||||
public ApiRes receiverBind() {
|
||||
//获取参数 & 验签
|
||||
// DivisionReceiverBindRQ bizRQ = getRQByWithMchSign(DivisionReceiverBindRQ.class);
|
||||
// try {
|
||||
// //检查商户应用是否存在该接口
|
||||
// String ifCode = bizRQ.getIfCode();
|
||||
// // 商户配置信息
|
||||
// MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(bizRQ.getMchNo(), bizRQ.getAppId(),bizRQ.getMchExtNo());
|
||||
// if(mchAppConfigContext == null){
|
||||
// throw new BizException("获取商户应用信息失败");
|
||||
// }
|
||||
// MchInfo mchInfo = mchAppConfigContext.getMchInfo();
|
||||
// if(!payInterfaceConfigService.mchAppHasAvailableIfCode(bizRQ.getAppId(), ifCode)){
|
||||
// throw new BizException("商户应用的支付配置不存在或已关闭");
|
||||
// }
|
||||
//// MchDivisionReceiverGroup group = mchDivisionReceiverGroupService.findByIdAndMchNo(bizRQ.getReceiverGroupId(), bizRQ.getMchNo());
|
||||
//// if(group == null){
|
||||
//// throw new BizException("商户分账账号组不存在,请检查或进入商户平台进行创建操作");
|
||||
//// }
|
||||
//
|
||||
// BigDecimal divisionProfit = new BigDecimal(bizRQ.getDivisionProfit());
|
||||
// if(divisionProfit.compareTo(BigDecimal.ZERO) <= 0 || divisionProfit.compareTo(BigDecimal.ONE) > 1){
|
||||
// throw new BizException("账号分账比例有误, 配置值为[0.0001~1.0000]");
|
||||
// }
|
||||
//
|
||||
//
|
||||
// //生成数据库对象信息 (数据不完成, 暂时不可入库操作)
|
||||
// MchDivisionReceiver receiver = genRecord(bizRQ, group, mchInfo, divisionProfit);
|
||||
//
|
||||
// //调起上游接口
|
||||
//
|
||||
// IDivisionService divisionService = SpringBeansUtil.getBean(JeepayKit.getIfCodeOrigin(ifCode) + "DivisionService", IDivisionService.class);
|
||||
// if(divisionService == null){
|
||||
// throw new BizException("系统不支持该分账接口");
|
||||
// }
|
||||
//
|
||||
// // 同一个分账接收方账号, 不可以在同一个商户的同一个接口绑定多次! ( 避免出现类似微信: 分账对象重复 )
|
||||
// if(mchDivisionReceiverService.count(MchDivisionReceiver.gw()
|
||||
// .eq(MchDivisionReceiver::getMchNo, bizRQ.getMchNo()).eq(MchDivisionReceiver::getAppId, bizRQ.getAppId())
|
||||
// .eq(MchDivisionReceiver::getIfCode, bizRQ.getIfCode()).eq(MchDivisionReceiver::getAccNo, bizRQ.getAccNo())
|
||||
// .eq(MchDivisionReceiver::getAccType, receiver.getAccType())
|
||||
// ) > 0 ){
|
||||
// throw new BizException("分账接收账号["+ receiver.getAccNo() +"]已绑定,无需重复绑定。");
|
||||
// }
|
||||
//
|
||||
//
|
||||
// ChannelRetMsg retMsg = divisionService.bind(null, null);
|
||||
// if(retMsg.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_SUCCESS){
|
||||
//
|
||||
// receiver.setState(CS.YES);
|
||||
// receiver.setBindSuccessTime(new Date());
|
||||
// receiver.setChannelAccNo(retMsg.getChannelAttach()); // 在channelAttach数据中,写入 渠道用户编号
|
||||
// mchDivisionReceiverService.save(receiver);
|
||||
//
|
||||
// }else{
|
||||
//
|
||||
// receiver.setState(CS.NO);
|
||||
// receiver.setChannelBindResult(retMsg.getChannelErrMsg());
|
||||
// }
|
||||
//
|
||||
// DivisionReceiverBindRS bizRes = DivisionReceiverBindRS.buildByRecord(receiver);
|
||||
//
|
||||
// if(retMsg.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_SUCCESS){
|
||||
//
|
||||
// bizRes.setBindState(CS.YES);
|
||||
//
|
||||
// }else{
|
||||
// bizRes.setBindState(CS.NO);
|
||||
// bizRes.setErrCode(retMsg.getChannelErrCode());
|
||||
// bizRes.setErrMsg(retMsg.getChannelErrMsg());
|
||||
// }
|
||||
//
|
||||
// return ApiResKit.okWithSign(bizRes, mchAppConfigContext.getMchApp(), bizRQ.getSignType());
|
||||
//
|
||||
//
|
||||
//
|
||||
// } catch (BizException e) {
|
||||
// return ApiRes.customFail(e.getMessage());
|
||||
//
|
||||
// } catch (Exception e) {
|
||||
// log.error("系统异常:{}", e);
|
||||
// return ApiRes.customFail("系统异常");
|
||||
// }
|
||||
return null;
|
||||
}
|
||||
|
||||
@MethodLog(remark = "创建分账方")
|
||||
@PostMapping("/receiver/create")
|
||||
public ApiRes receiverCreate(){
|
||||
//获取参数 & 验签
|
||||
DivisionReceiverBindRQ bizRQ = getRQByWithMchSign(DivisionReceiverBindRQ.class);
|
||||
try {
|
||||
//检查商户应用是否存在该接口
|
||||
String ifCode = bizRQ.getIfCode();
|
||||
// 商户配置信息
|
||||
MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(bizRQ.getMchNo(), bizRQ.getAppId(),bizRQ.getMchExtNo());
|
||||
if(mchAppConfigContext == null){
|
||||
throw new BizException("获取商户应用信息失败");
|
||||
}
|
||||
MchInfo mchInfo = mchAppConfigContext.getMchInfo();
|
||||
if(!payInterfaceConfigService.mchAppHasAvailableIfCode(bizRQ.getAppId(), ifCode)){
|
||||
throw new BizException("商户应用的支付配置不存在或已关闭");
|
||||
}
|
||||
//生成数据库对象信息 (数据不完成, 暂时不可入库操作)
|
||||
DivisionSubject subject = genRecord(bizRQ, mchInfo);
|
||||
//调起上游接口
|
||||
IDivisionService divisionService = SpringBeansUtil.getBean(JeepayKit.getIfCodeOrigin(ifCode) + "DivisionService", IDivisionService.class);
|
||||
if(divisionService == null){
|
||||
throw new BizException("系统不支持该分账接口");
|
||||
}
|
||||
// 同一个分账接收方账号, 不可以在同一个商户的同一个接口绑定多次! ( 避免出现类似微信: 分账对象重复 )
|
||||
if(divisionSubjectService.count(DivisionSubject.gw().eq(DivisionSubject::getDivisionNo, bizRQ.getAccNo())) > 0 ){
|
||||
throw new BizException("分账接收账号["+ subject.getDivisionNo() +"]已绑定,无需重复绑定。");
|
||||
}
|
||||
DivisionSubjectEx subjectEx = new DivisionSubjectEx(null,subject.getUserType(),subject.getIfCode(),subject.getInfo(),subject.getRemark());
|
||||
ChannelRetMsg retMsg = divisionService.bind(subjectEx, bizRQ.getIsvNo());
|
||||
if(retMsg.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_SUCCESS){
|
||||
subject.setState(CS.YES);
|
||||
subject.setChannelDivisionNo(retMsg.getChannelAttach());
|
||||
divisionSubjectService.save(subject);
|
||||
}else{
|
||||
subject.setState(CS.NO);
|
||||
subject.setRemark(retMsg.getChannelErrMsg());
|
||||
}
|
||||
DivisionReceiverBindRS bizRes = DivisionReceiverBindRS.buildByRecord(subjectEx);
|
||||
if(retMsg.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_SUCCESS){
|
||||
bizRes.setBindState(CS.YES);
|
||||
}else{
|
||||
bizRes.setBindState(CS.NO);
|
||||
bizRes.setErrCode(retMsg.getChannelErrCode());
|
||||
bizRes.setErrMsg(retMsg.getChannelErrMsg());
|
||||
}
|
||||
return ApiResKit.okWithSign(bizRes, mchAppConfigContext.getMchApp(), bizRQ.getSignType());
|
||||
} catch (BizException e) {
|
||||
return ApiRes.customFail(e.getMessage());
|
||||
} catch (Exception e) {
|
||||
log.error("系统异常:{}", e);
|
||||
return ApiRes.customFail("系统异常");
|
||||
}
|
||||
}
|
||||
|
||||
private DivisionSubject genRecord(DivisionReceiverBindRQ bizRQ, MchInfo mchInfo){
|
||||
DivisionSubject subject = new DivisionSubject();
|
||||
subject.setDivisionNo(SeqKit.getDivisionNo());
|
||||
subject.setMchNo(mchInfo.getMchNo());
|
||||
subject.setIsvNo(mchInfo.getIsvNo());
|
||||
subject.setIfCode(bizRQ.getIfCode());
|
||||
subject.setUserType(bizRQ.getAccType());
|
||||
subject.setName(bizRQ.getAccName());
|
||||
if(bizRQ.getExtInfo() != null){
|
||||
subject.setInfo(bizRQ.getExtInfo().toJavaObject(DivisionSettleInfo.class));
|
||||
}
|
||||
return subject;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.division;
|
||||
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.ctrls.AbstractCtrl;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.exception.ResponseException;
|
||||
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
|
||||
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
|
||||
import com.jeequan.jeepay.core.model.rqrs.msg.DivisionChannelNotifyModel;
|
||||
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
|
||||
import com.jeequan.jeepay.db.entity.PayOrder;
|
||||
import com.jeequan.jeepay.db.entity.PayOrderDivisionRecord;
|
||||
import com.jeequan.jeepay.pay.service.PayOrderMarketReissueService;
|
||||
import com.jeequan.jeepay.service.impl.PayOrderDivisionRecordService;
|
||||
import com.jeequan.jeepay.service.impl.PayOrderService;
|
||||
import com.jeequan.jeepay.thirdparty.channel.AbstractDivisionRecordChannelNotifyService;
|
||||
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.MutablePair;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
* 分账渠道侧的通知入口Controller
|
||||
*
|
||||
* @author terrfly
|
||||
*
|
||||
* @date 2023/3/29 15:35
|
||||
*/
|
||||
@Slf4j
|
||||
@Controller
|
||||
public class DivisionRecordChannelNotifyController extends AbstractCtrl {
|
||||
|
||||
@Autowired private PayOrderDivisionRecordService payOrderDivisionRecordService;
|
||||
@Autowired private ConfigContextQueryService configContextQueryService;
|
||||
@Autowired private PayOrderService payOrderService;
|
||||
@Autowired private PayOrderMarketReissueService payOrderMarketReissueService;
|
||||
|
||||
|
||||
/** 异步回调入口 **/
|
||||
@ResponseBody
|
||||
@RequestMapping(value= {"/api/divisionRecordChannelNotify/{ifCode}"})
|
||||
public ResponseEntity doNotify(HttpServletRequest request, @PathVariable("ifCode") String ifCode){
|
||||
|
||||
String divisionBatchId = null;
|
||||
String logPrefix = "进入[" +ifCode+ "]分账回调";
|
||||
log.info("===== {} =====" , logPrefix);
|
||||
|
||||
try {
|
||||
|
||||
// 参数有误
|
||||
if(StringUtils.isEmpty(ifCode)){
|
||||
return ResponseEntity.badRequest().body("ifCode is empty");
|
||||
}
|
||||
|
||||
//查询支付接口是否存在
|
||||
AbstractDivisionRecordChannelNotifyService divisionNotifyService = SpringBeansUtil.getBean(ifCode + "DivisionRecordChannelNotifyService", AbstractDivisionRecordChannelNotifyService.class);
|
||||
|
||||
// 支付通道接口实现不存在
|
||||
if(divisionNotifyService == null){
|
||||
log.error("{}, interface not exists ", logPrefix);
|
||||
return ResponseEntity.badRequest().body("[" + ifCode + "] interface not exists");
|
||||
}
|
||||
|
||||
// 解析批次号 和 请求参数
|
||||
MutablePair<String, Object> mutablePair = divisionNotifyService.parseParams(request);
|
||||
if(mutablePair == null){ // 解析数据失败, 响应已处理
|
||||
log.error("{}, mutablePair is null ", logPrefix);
|
||||
throw new BizException("解析数据异常!"); //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
|
||||
}
|
||||
|
||||
//解析到订单号
|
||||
divisionBatchId = mutablePair.left;
|
||||
log.info("{}, 解析数据为:divisionBatchId:{}, params:{}", logPrefix, divisionBatchId, mutablePair.getRight());
|
||||
|
||||
// 通过 batchId 查询出列表( 注意: 需要按照ID 排序!!!! )
|
||||
List<PayOrderDivisionRecord> recordList = null;
|
||||
if(CS.IF_CODE.YSPAY.equals(ifCode)){
|
||||
recordList = payOrderDivisionRecordService.list(PayOrderDivisionRecord.gw()
|
||||
.eq(PayOrderDivisionRecord::getState, PayOrderDivisionRecord.STATE_ACCEPT)
|
||||
.eq(PayOrderDivisionRecord::getPayOrderId, divisionBatchId)
|
||||
.orderByAsc(PayOrderDivisionRecord::getRecordId));
|
||||
}else{
|
||||
recordList = payOrderDivisionRecordService.list(PayOrderDivisionRecord.gw()
|
||||
.eq(PayOrderDivisionRecord::getState, PayOrderDivisionRecord.STATE_ACCEPT)
|
||||
.eq(PayOrderDivisionRecord::getBatchOrderId, divisionBatchId)
|
||||
.orderByAsc(PayOrderDivisionRecord::getRecordId)
|
||||
);
|
||||
}
|
||||
// 订单不存在
|
||||
if(recordList == null || recordList.isEmpty()){
|
||||
log.error("{}, 待处理订单不存在. divisionBatchId={} ", logPrefix, divisionBatchId);
|
||||
return divisionNotifyService.doNotifyOrderNotExists(request);
|
||||
}
|
||||
//查询出商户应用的配置信息
|
||||
MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(recordList.get(0).getMchNo(), recordList.get(0).getAppId(),recordList.get(0).getMchExtNo());
|
||||
|
||||
//调起接口的回调判断
|
||||
DivisionChannelNotifyModel notifyResult = divisionNotifyService.doNotify(request, mutablePair.getRight(), recordList, mchAppConfigContext);
|
||||
|
||||
// 返回null 表明出现异常, 无需处理通知下游等操作。
|
||||
if(notifyResult == null || notifyResult.getApiRes() == null){
|
||||
log.error("{}, 处理回调事件异常 notifyResult data error, notifyResult ={} ",logPrefix, notifyResult);
|
||||
throw new BizException("处理回调事件异常!"); //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
|
||||
}
|
||||
|
||||
if(notifyResult.getRecordResultMap() != null && !notifyResult.getRecordResultMap().isEmpty()){
|
||||
|
||||
for (Long divisionId : notifyResult.getRecordResultMap().keySet()) {
|
||||
|
||||
// 单条结果
|
||||
ChannelRetMsg retMsgItem = notifyResult.getRecordResultMap().get(divisionId);
|
||||
|
||||
// 明确成功
|
||||
if(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS == retMsgItem.getChannelState()){
|
||||
|
||||
payOrderDivisionRecordService.updateRecordSuccessOrFailBySingleItem(divisionId, PayOrderDivisionRecord.STATE_SUCCESS, retMsgItem.getChannelOriginResponse());
|
||||
|
||||
} else if(ChannelRetMsg.ChannelState.CONFIRM_FAIL == retMsgItem.getChannelState()){ // 明确失败
|
||||
|
||||
payOrderDivisionRecordService.updateRecordSuccessOrFailBySingleItem(divisionId, PayOrderDivisionRecord.STATE_FAIL, StringUtils.defaultIfEmpty(retMsgItem.getChannelErrMsg(), retMsgItem.getChannelOriginResponse()));
|
||||
}
|
||||
}
|
||||
}
|
||||
payOrderService.updateDivState(recordList.get(0).getPayOrderId(), PayOrder.DIVISION_STATE_FINISH);
|
||||
PayOrder payOrder = payOrderService.getById(recordList.get(0).getPayOrderId());
|
||||
payOrderMarketReissueService.marketCashFeeReissue(payOrder,mchAppConfigContext);
|
||||
log.info("===== {}, 通知完成。 divisionBatchId={}, parseState = {} =====", logPrefix, divisionBatchId, notifyResult);
|
||||
return notifyResult.getApiRes();
|
||||
} catch (BizException e) {
|
||||
log.error("{}, divisionBatchId={}, BizException", logPrefix, divisionBatchId, e);
|
||||
return ResponseEntity.badRequest().body(e.getMessage());
|
||||
|
||||
} catch (ResponseException e) {
|
||||
log.error("{}, divisionBatchId={}, ResponseException", logPrefix, divisionBatchId, e);
|
||||
return e.getResponseEntity();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("{}, divisionBatchId={}, 系统异常", logPrefix, divisionBatchId, e);
|
||||
return ResponseEntity.badRequest().body(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.division;
|
||||
|
||||
import com.jeequan.jeepay.core.aop.MethodLog;
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.entity.MchInfo;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.interfaces.IConfigContextQueryService;
|
||||
import com.jeequan.jeepay.core.interfaces.paychannel.IDivisionService;
|
||||
import com.jeequan.jeepay.core.model.ApiRes;
|
||||
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
|
||||
import com.jeequan.jeepay.core.model.rqrs.division.DivisionReceiverBindRQ;
|
||||
import com.jeequan.jeepay.core.model.rqrs.division.DivisionReceiverBindRS;
|
||||
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
|
||||
import com.jeequan.jeepay.core.utils.JeepayKit;
|
||||
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
|
||||
import com.jeequan.jeepay.db.entity.MchDivisionReceiver;
|
||||
import com.jeequan.jeepay.db.entity.MchDivisionReceiverGroup;
|
||||
import com.jeequan.jeepay.pay.ctrl.ApiController;
|
||||
import com.jeequan.jeepay.pay.util.ApiResKit;
|
||||
import com.jeequan.jeepay.service.impl.MchDivisionReceiverGroupService;
|
||||
import com.jeequan.jeepay.service.impl.MchDivisionReceiverService;
|
||||
import com.jeequan.jeepay.service.impl.PayInterfaceConfigService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 分账账号绑定
|
||||
*
|
||||
* @author terrfly
|
||||
*
|
||||
* @date 2021/8/25 9:07
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
public class MchDivisionReceiverBindController extends ApiController {
|
||||
|
||||
@Autowired private IConfigContextQueryService configContextQueryService;
|
||||
@Autowired private PayInterfaceConfigService payInterfaceConfigService;
|
||||
@Autowired private MchDivisionReceiverService mchDivisionReceiverService;
|
||||
@Autowired private MchDivisionReceiverGroupService mchDivisionReceiverGroupService;
|
||||
|
||||
/** 分账账号绑定 **/
|
||||
// @MethodLog(remark = "绑定分账用户API")
|
||||
// @PostMapping("/api/division/receiver/bind")
|
||||
// public ApiRes bind(){
|
||||
//
|
||||
// //获取参数 & 验签
|
||||
// DivisionReceiverBindRQ bizRQ = getRQByWithMchSign(DivisionReceiverBindRQ.class);
|
||||
//
|
||||
// try {
|
||||
//
|
||||
// //检查商户应用是否存在该接口
|
||||
// String ifCode = bizRQ.getIfCode();
|
||||
//
|
||||
//
|
||||
// // 商户配置信息
|
||||
// MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(bizRQ.getMchNo(), bizRQ.getAppId(),bizRQ.getMchExtNo());
|
||||
// if(mchAppConfigContext == null){
|
||||
// throw new BizException("获取商户应用信息失败");
|
||||
// }
|
||||
//
|
||||
// MchInfo mchInfo = mchAppConfigContext.getMchInfo();
|
||||
//
|
||||
// if(!payInterfaceConfigService.mchAppHasAvailableIfCode(bizRQ.getAppId(), ifCode)){
|
||||
// throw new BizException("商户应用的支付配置不存在或已关闭");
|
||||
// }
|
||||
//
|
||||
// MchDivisionReceiverGroup group = mchDivisionReceiverGroupService.findByIdAndMchNo(bizRQ.getReceiverGroupId(), bizRQ.getMchNo());
|
||||
// if(group == null){
|
||||
// throw new BizException("商户分账账号组不存在,请检查或进入商户平台进行创建操作");
|
||||
// }
|
||||
//
|
||||
// BigDecimal divisionProfit = new BigDecimal(bizRQ.getDivisionProfit());
|
||||
// if(divisionProfit.compareTo(BigDecimal.ZERO) <= 0 || divisionProfit.compareTo(BigDecimal.ONE) > 1){
|
||||
// throw new BizException("账号分账比例有误, 配置值为[0.0001~1.0000]");
|
||||
// }
|
||||
//
|
||||
//
|
||||
// //生成数据库对象信息 (数据不完成, 暂时不可入库操作)
|
||||
// MchDivisionReceiver receiver = genRecord(bizRQ, group, mchInfo, divisionProfit);
|
||||
//
|
||||
// //调起上游接口
|
||||
//
|
||||
// IDivisionService divisionService = SpringBeansUtil.getBean(JeepayKit.getIfCodeOrigin(ifCode) + "DivisionService", IDivisionService.class);
|
||||
// if(divisionService == null){
|
||||
// throw new BizException("系统不支持该分账接口");
|
||||
// }
|
||||
//
|
||||
// // 同一个分账接收方账号, 不可以在同一个商户的同一个接口绑定多次! ( 避免出现类似微信: 分账对象重复 )
|
||||
// if(mchDivisionReceiverService.count(MchDivisionReceiver.gw()
|
||||
// .eq(MchDivisionReceiver::getMchNo, bizRQ.getMchNo()).eq(MchDivisionReceiver::getAppId, bizRQ.getAppId())
|
||||
// .eq(MchDivisionReceiver::getIfCode, bizRQ.getIfCode()).eq(MchDivisionReceiver::getAccNo, bizRQ.getAccNo())
|
||||
// .eq(MchDivisionReceiver::getAccType, receiver.getAccType())
|
||||
// ) > 0 ){
|
||||
// throw new BizException("分账接收账号["+ receiver.getAccNo() +"]已绑定,无需重复绑定。");
|
||||
// }
|
||||
//
|
||||
//
|
||||
// ChannelRetMsg retMsg = divisionService.bind(null, null);
|
||||
// if(retMsg.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_SUCCESS){
|
||||
//
|
||||
// receiver.setState(CS.YES);
|
||||
// receiver.setBindSuccessTime(new Date());
|
||||
// receiver.setChannelAccNo(retMsg.getChannelAttach()); // 在channelAttach数据中,写入 渠道用户编号
|
||||
// mchDivisionReceiverService.save(receiver);
|
||||
//
|
||||
// }else{
|
||||
//
|
||||
// receiver.setState(CS.NO);
|
||||
// receiver.setChannelBindResult(retMsg.getChannelErrMsg());
|
||||
// }
|
||||
//
|
||||
// DivisionReceiverBindRS bizRes = DivisionReceiverBindRS.buildByRecord(receiver);
|
||||
//
|
||||
// if(retMsg.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_SUCCESS){
|
||||
//
|
||||
// bizRes.setBindState(CS.YES);
|
||||
//
|
||||
// }else{
|
||||
// bizRes.setBindState(CS.NO);
|
||||
// bizRes.setErrCode(retMsg.getChannelErrCode());
|
||||
// bizRes.setErrMsg(retMsg.getChannelErrMsg());
|
||||
// }
|
||||
//
|
||||
// return ApiResKit.okWithSign(bizRes, mchAppConfigContext.getMchApp(), bizRQ.getSignType());
|
||||
//
|
||||
//
|
||||
//
|
||||
// } catch (BizException e) {
|
||||
// return ApiRes.customFail(e.getMessage());
|
||||
//
|
||||
// } catch (Exception e) {
|
||||
// log.error("系统异常:{}", e);
|
||||
// return ApiRes.customFail("系统异常");
|
||||
// }
|
||||
// }
|
||||
|
||||
// private MchDivisionReceiver genRecord(DivisionReceiverBindRQ bizRQ, MchDivisionReceiverGroup group, MchInfo mchInfo, BigDecimal divisionProfit){
|
||||
//
|
||||
// MchDivisionReceiver receiver = new MchDivisionReceiver();
|
||||
// receiver.setReceiverAlias(StringUtils.defaultIfEmpty(bizRQ.getReceiverAlias(), bizRQ.getAccNo())); //别名
|
||||
// receiver.setReceiverGroupId(bizRQ.getReceiverGroupId()); //分组ID
|
||||
// receiver.setReceiverGroupName(group.getReceiverGroupName()); //组名称
|
||||
// receiver.setMchNo(bizRQ.getMchNo()); //商户号
|
||||
//// receiver.setIsvNo(mchInfo.getIsvNo()); //isvNo
|
||||
// receiver.setAppId(bizRQ.getAppId()); //appId
|
||||
// receiver.setIfCode(bizRQ.getIfCode()); //接口代码
|
||||
// receiver.setAccType(bizRQ.getAccType()); //账号类型
|
||||
// receiver.setAccNo(bizRQ.getAccNo()); //账号
|
||||
// receiver.setAccName(bizRQ.getAccName()); //账号名称
|
||||
// receiver.setRelationType(bizRQ.getRelationType()); //关系
|
||||
//
|
||||
// receiver.setRelationTypeName(getRelationTypeName(bizRQ.getRelationType())); //关系名称
|
||||
//
|
||||
// if(receiver.getRelationTypeName() == null){
|
||||
// receiver.setRelationTypeName(bizRQ.getRelationTypeName());
|
||||
// }
|
||||
//
|
||||
// receiver.setDivisionProfit(divisionProfit); //分账比例
|
||||
// receiver.setChannelExtInfo(bizRQ.getChannelExtInfo()); //渠道信息
|
||||
//
|
||||
// return receiver;
|
||||
// }
|
||||
//
|
||||
// public String getRelationTypeName(String relationType){
|
||||
//
|
||||
// if("PARTNER".equals(relationType)){
|
||||
// return "合作伙伴";
|
||||
// }else if("SERVICE_PROVIDER".equals(relationType)){
|
||||
// return "服务商";
|
||||
// }else if("STORE".equals(relationType)){
|
||||
// return "门店";
|
||||
// }else if("STAFF".equals(relationType)){
|
||||
// return "员工";
|
||||
// }else if("STORE_OWNER".equals(relationType)){
|
||||
// return "店主";
|
||||
// }else if("HEADQUARTER".equals(relationType)){
|
||||
// return "总部";
|
||||
// }else if("BRAND".equals(relationType)){
|
||||
// return "品牌方";
|
||||
// }else if("DISTRIBUTOR".equals(relationType)){
|
||||
// return "分销商";
|
||||
// }else if("USER".equals(relationType)){
|
||||
// return "用户";
|
||||
// }else if("SUPPLIER".equals(relationType)){
|
||||
// return "供应商";
|
||||
// }else if("PAYMENT".equals(relationType)){
|
||||
// return "收款方";
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.division;
|
||||
|
||||
import com.jeequan.jeepay.core.aop.MethodLog;
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.interfaces.IConfigContextQueryService;
|
||||
import com.jeequan.jeepay.core.interfaces.paychannel.IDivisionService;
|
||||
import com.jeequan.jeepay.core.model.ApiRes;
|
||||
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
|
||||
import com.jeequan.jeepay.core.model.rqrs.division.DivisionReceiverChannelBalanceCashoutRQ;
|
||||
import com.jeequan.jeepay.core.model.rqrs.division.DivisionReceiverChannelBalanceCashoutRS;
|
||||
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
|
||||
import com.jeequan.jeepay.core.utils.JeepayKit;
|
||||
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
|
||||
import com.jeequan.jeepay.db.entity.MchDivisionReceiver;
|
||||
import com.jeequan.jeepay.pay.ctrl.ApiController;
|
||||
import com.jeequan.jeepay.pay.util.ApiResKit;
|
||||
import com.jeequan.jeepay.service.impl.MchDivisionReceiverService;
|
||||
import com.jeequan.jeepay.service.impl.PayInterfaceConfigService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 分账账号 渠道余额提现
|
||||
*
|
||||
* @author terrfly
|
||||
*
|
||||
* @date 2022/05/11 10:46
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
public class MchDivisionReceiverChannelBalanceCashoutController extends ApiController {
|
||||
|
||||
@Autowired private IConfigContextQueryService configContextQueryService;
|
||||
@Autowired private PayInterfaceConfigService payInterfaceConfigService;
|
||||
@Autowired private MchDivisionReceiverService mchDivisionReceiverService;
|
||||
|
||||
/** 分账账号绑定 **/
|
||||
@MethodLog(remark = "对分账用户的渠道余额发起提现API")
|
||||
@PostMapping("/api/division/receiver/channelBalanceCashout")
|
||||
public ApiRes channelBalanceCashout(){
|
||||
|
||||
//获取参数 & 验签
|
||||
DivisionReceiverChannelBalanceCashoutRQ bizRQ = getRQByWithMchSign(DivisionReceiverChannelBalanceCashoutRQ.class);
|
||||
|
||||
try {
|
||||
|
||||
MchDivisionReceiver mchDivisionReceiver = mchDivisionReceiverService.getOne(MchDivisionReceiver.gw()
|
||||
.eq(MchDivisionReceiver::getReceiverId, bizRQ.getReceiverId())
|
||||
.eq(MchDivisionReceiver::getMchNo, bizRQ.getMchNo())
|
||||
);
|
||||
|
||||
if(mchDivisionReceiver == null){
|
||||
throw new BizException("分账用户不存在");
|
||||
}
|
||||
|
||||
//检查商户应用是否存在该接口
|
||||
String ifCode = mchDivisionReceiver.getIfCode();
|
||||
|
||||
// 商户配置信息
|
||||
MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(bizRQ.getMchNo(), bizRQ.getAppId(),bizRQ.getMchExtNo());
|
||||
if(mchAppConfigContext == null){
|
||||
throw new BizException("获取商户应用信息失败");
|
||||
}
|
||||
|
||||
if(!payInterfaceConfigService.mchAppHasAvailableIfCode(bizRQ.getAppId(), ifCode)){
|
||||
throw new BizException("商户应用的支付配置不存在或已关闭");
|
||||
}
|
||||
|
||||
//调起上游接口
|
||||
IDivisionService divisionService = SpringBeansUtil.getBean(JeepayKit.getIfCodeOrigin(ifCode) + "DivisionService", IDivisionService.class);
|
||||
if(divisionService == null){
|
||||
throw new BizException("系统不支持该分账接口");
|
||||
}
|
||||
|
||||
DivisionReceiverChannelBalanceCashoutRS bizRes = new DivisionReceiverChannelBalanceCashoutRS();
|
||||
|
||||
ChannelRetMsg retMsg = divisionService.cashout(mchDivisionReceiver, bizRQ.getCashoutAmount(), mchAppConfigContext);
|
||||
if(retMsg.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_SUCCESS){
|
||||
bizRes.setState(CS.YES);
|
||||
}else{
|
||||
bizRes.setState(CS.NO);
|
||||
bizRes.setErrCode(retMsg.getChannelErrCode());
|
||||
bizRes.setErrMsg(retMsg.getChannelErrMsg());
|
||||
}
|
||||
|
||||
return ApiResKit.okWithSign(bizRes, mchAppConfigContext.getMchApp(), bizRQ.getSignType());
|
||||
|
||||
} catch (BizException e) {
|
||||
return ApiRes.customFail(e.getMessage());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("系统异常:{}", e);
|
||||
return ApiRes.customFail("系统异常");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.division;
|
||||
|
||||
import com.jeequan.jeepay.core.aop.MethodLog;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.interfaces.IConfigContextQueryService;
|
||||
import com.jeequan.jeepay.core.interfaces.paychannel.IDivisionService;
|
||||
import com.jeequan.jeepay.core.model.ApiRes;
|
||||
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
|
||||
import com.jeequan.jeepay.core.model.rqrs.division.DivisionReceiverChannelBalanceQueryRQ;
|
||||
import com.jeequan.jeepay.core.model.rqrs.division.DivisionReceiverChannelBalanceQueryRS;
|
||||
import com.jeequan.jeepay.core.utils.JeepayKit;
|
||||
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
|
||||
import com.jeequan.jeepay.db.entity.MchDivisionReceiver;
|
||||
import com.jeequan.jeepay.pay.ctrl.ApiController;
|
||||
import com.jeequan.jeepay.pay.util.ApiResKit;
|
||||
import com.jeequan.jeepay.service.impl.MchDivisionReceiverService;
|
||||
import com.jeequan.jeepay.service.impl.PayInterfaceConfigService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 分账账号 渠道余额查询
|
||||
*
|
||||
* @author terrfly
|
||||
*
|
||||
* @date 2022/05/11 10:46
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
public class MchDivisionReceiverChannelBalanceQueryController extends ApiController {
|
||||
|
||||
@Autowired private IConfigContextQueryService configContextQueryService;
|
||||
@Autowired private PayInterfaceConfigService payInterfaceConfigService;
|
||||
@Autowired private MchDivisionReceiverService mchDivisionReceiverService;
|
||||
|
||||
/** ctrl **/
|
||||
@MethodLog(remark = "查询分账用户可用余额API")
|
||||
@PostMapping("/api/division/receiver/channelBalanceQuery")
|
||||
public ApiRes query(){
|
||||
|
||||
//获取参数 & 验签
|
||||
DivisionReceiverChannelBalanceQueryRQ bizRQ = getRQByWithMchSign(DivisionReceiverChannelBalanceQueryRQ.class);
|
||||
|
||||
try {
|
||||
|
||||
MchDivisionReceiver mchDivisionReceiver = mchDivisionReceiverService.getOne(MchDivisionReceiver.gw()
|
||||
.eq(MchDivisionReceiver::getReceiverId, bizRQ.getReceiverId())
|
||||
.eq(MchDivisionReceiver::getMchNo, bizRQ.getMchNo())
|
||||
);
|
||||
|
||||
if(mchDivisionReceiver == null){
|
||||
throw new BizException("分账用户不存在");
|
||||
}
|
||||
|
||||
//检查商户应用是否存在该接口
|
||||
String ifCode = mchDivisionReceiver.getIfCode();
|
||||
|
||||
|
||||
// 商户配置信息
|
||||
MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(bizRQ.getMchNo(), bizRQ.getAppId(),bizRQ.getMchExtNo());
|
||||
if(mchAppConfigContext == null){
|
||||
throw new BizException("获取商户应用信息失败");
|
||||
}
|
||||
|
||||
if(!payInterfaceConfigService.mchAppHasAvailableIfCode(bizRQ.getAppId(), ifCode)){
|
||||
throw new BizException("商户应用的支付配置不存在或已关闭");
|
||||
}
|
||||
|
||||
//调起上游接口
|
||||
IDivisionService divisionService = SpringBeansUtil.getBean(JeepayKit.getIfCodeOrigin(ifCode) + "DivisionService", IDivisionService.class);
|
||||
if(divisionService == null){
|
||||
throw new BizException("系统不支持该分账接口");
|
||||
}
|
||||
|
||||
|
||||
Long balance = divisionService.queryBalanceAmount(mchDivisionReceiver, mchAppConfigContext);
|
||||
DivisionReceiverChannelBalanceQueryRS bizRes = new DivisionReceiverChannelBalanceQueryRS();
|
||||
bizRes.setReceiverId(bizRQ.getReceiverId());
|
||||
bizRes.setBalanceAmount(balance);
|
||||
|
||||
return ApiResKit.okWithSign(bizRes, mchAppConfigContext.getMchApp(), bizRQ.getSignType());
|
||||
|
||||
} catch (BizException e) {
|
||||
return ApiRes.customFail(e.getMessage());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("系统异常:{}", e);
|
||||
return ApiRes.customFail("系统异常");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.division;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.jeequan.jeepay.components.mq.model.PayOrderDivisionMQ;
|
||||
import com.jeequan.jeepay.core.aop.MethodLog;
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.interfaces.IConfigContextQueryService;
|
||||
import com.jeequan.jeepay.core.model.ApiRes;
|
||||
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
|
||||
import com.jeequan.jeepay.core.model.rqrs.division.PayOrderDivisionExecRQ;
|
||||
import com.jeequan.jeepay.core.model.rqrs.division.PayOrderDivisionExecRS;
|
||||
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
|
||||
import com.jeequan.jeepay.db.entity.MchDivisionReceiver;
|
||||
import com.jeequan.jeepay.db.entity.MchDivisionReceiverGroup;
|
||||
import com.jeequan.jeepay.db.entity.PayOrder;
|
||||
import com.jeequan.jeepay.db.entity.PayOrderDivisionRecord;
|
||||
import com.jeequan.jeepay.pay.ctrl.ApiController;
|
||||
import com.jeequan.jeepay.pay.service.PayOrderDivisionProcessService;
|
||||
import com.jeequan.jeepay.pay.util.ApiResKit;
|
||||
import com.jeequan.jeepay.service.impl.MchDivisionReceiverGroupService;
|
||||
import com.jeequan.jeepay.service.impl.MchDivisionReceiverService;
|
||||
import com.jeequan.jeepay.service.impl.PayOrderService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 发起分账请求
|
||||
*
|
||||
* @author terrfly
|
||||
*
|
||||
* @date 2021/8/27 8:01
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
public class PayOrderDivisionExecController extends ApiController {
|
||||
|
||||
@Autowired private IConfigContextQueryService configContextQueryService;
|
||||
@Autowired private PayOrderService payOrderService;
|
||||
@Autowired private MchDivisionReceiverService mchDivisionReceiverService;
|
||||
@Autowired private MchDivisionReceiverGroupService mchDivisionReceiverGroupService;
|
||||
@Autowired private PayOrderDivisionProcessService payOrderDivisionProcessService;
|
||||
|
||||
/** 分账执行 **/
|
||||
@MethodLog(remark = "发起订单分账API")
|
||||
@PostMapping("/api/division/exec")
|
||||
public ApiRes exec(){
|
||||
|
||||
//获取参数 & 验签
|
||||
PayOrderDivisionExecRQ bizRQ = getRQByWithMchSign(PayOrderDivisionExecRQ.class);
|
||||
|
||||
try {
|
||||
|
||||
if(StringUtils.isAllEmpty(bizRQ.getMchOrderNo(), bizRQ.getPayOrderId())){
|
||||
throw new BizException("mchOrderNo 和 payOrderId不能同时为空");
|
||||
}
|
||||
|
||||
PayOrder payOrder = payOrderService.queryMchOrder(bizRQ.getMchNo(), bizRQ.getPayOrderId(), bizRQ.getMchOrderNo());
|
||||
if(payOrder == null){
|
||||
throw new BizException("订单不存在");
|
||||
}
|
||||
|
||||
if(payOrder.getState() != PayOrder.STATE_SUCCESS || payOrder.getDivisionState() != PayOrder.DIVISION_STATE_UNHAPPEN || payOrder.getDivisionMode() != PayOrder.DIVISION_MODE_MANUAL){
|
||||
throw new BizException("当前订单状态不支持分账");
|
||||
}
|
||||
|
||||
List<PayOrderDivisionMQ.CustomerDivisionReceiver> receiverList = null;
|
||||
|
||||
//不使用默认分组, 需要转换每个账号信息
|
||||
if(bizRQ.getUseSysAutoDivisionReceivers() != CS.YES && !StringUtils.isEmpty(bizRQ.getReceivers())){
|
||||
receiverList = JSON.parseArray(bizRQ.getReceivers(), PayOrderDivisionMQ.CustomerDivisionReceiver.class);
|
||||
}
|
||||
|
||||
// 验证账号是否合法
|
||||
this.checkReceiverList(receiverList, payOrder.getIfCode(), bizRQ.getMchNo(), bizRQ.getAppId());
|
||||
|
||||
// 商户配置信息
|
||||
MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(bizRQ.getMchNo(), bizRQ.getAppId(),payOrder.getMchExtNo());
|
||||
if(mchAppConfigContext == null){
|
||||
throw new BizException("获取商户应用信息失败");
|
||||
}
|
||||
|
||||
//处理分账请求
|
||||
ChannelRetMsg channelRetMsg = payOrderDivisionProcessService.processPayOrderDivision(payOrder.getPayOrderId(), bizRQ.getUseSysAutoDivisionReceivers(), receiverList, false, false);
|
||||
|
||||
PayOrderDivisionExecRS bizRS = new PayOrderDivisionExecRS();
|
||||
|
||||
// 确认分账成功
|
||||
if(channelRetMsg.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_SUCCESS) {
|
||||
bizRS.setState(PayOrderDivisionRecord.STATE_SUCCESS);
|
||||
|
||||
}else if(channelRetMsg.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_FAIL){ //分账失败
|
||||
bizRS.setState(PayOrderDivisionRecord.STATE_FAIL);
|
||||
|
||||
}else if(channelRetMsg.getChannelState() == ChannelRetMsg.ChannelState.WAITING){ // 分账处理中
|
||||
bizRS.setState(PayOrderDivisionRecord.STATE_ING);
|
||||
}
|
||||
|
||||
bizRS.setChannelBatchOrderId(channelRetMsg.getChannelOrderId());
|
||||
bizRS.setErrCode(channelRetMsg.getChannelErrCode());
|
||||
bizRS.setErrMsg(channelRetMsg.getChannelErrMsg());
|
||||
|
||||
|
||||
return ApiResKit.okWithSign(bizRS, mchAppConfigContext.getMchApp(), bizRQ.getSignType());
|
||||
|
||||
} catch (BizException e) {
|
||||
return ApiRes.customFail(e.getMessage());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("系统异常:{}", e);
|
||||
return ApiRes.customFail("系统异常");
|
||||
}
|
||||
}
|
||||
|
||||
/** 检验账号是否合法 **/
|
||||
private void checkReceiverList(List<PayOrderDivisionMQ.CustomerDivisionReceiver> receiverList, String ifCode, String mchNo, String appId){
|
||||
|
||||
if(receiverList == null || receiverList.isEmpty()){
|
||||
return ;
|
||||
}
|
||||
|
||||
Set<Long> receiverIdSet = new HashSet<>();
|
||||
Set<Long> receiverGroupIdSet = new HashSet<>();
|
||||
|
||||
for (PayOrderDivisionMQ.CustomerDivisionReceiver receiver : receiverList) {
|
||||
|
||||
if(receiver.getReceiverId() != null){
|
||||
receiverIdSet.add(receiver.getReceiverId());
|
||||
}
|
||||
|
||||
if(receiver.getReceiverGroupId() != null){
|
||||
receiverGroupIdSet.add(receiver.getReceiverGroupId());
|
||||
}
|
||||
|
||||
if(receiver.getReceiverId() == null && receiver.getReceiverGroupId() == null){
|
||||
throw new BizException("分账用户组: receiverId 和 与receiverGroupId 必填一项");
|
||||
}
|
||||
|
||||
if(receiver.getDivisionProfit() != null){
|
||||
|
||||
if(receiver.getDivisionProfit().compareTo(BigDecimal.ZERO) < 0){
|
||||
throw new BizException("分账用户receiverId=["+ ( receiver.getReceiverId() == null ? "": receiver.getReceiverId() ) +"]," +
|
||||
"receiverGroupId=["+ (receiver.getReceiverGroupId() == null ? "": receiver.getReceiverGroupId() ) +"] 分账比例不得小于0%");
|
||||
}
|
||||
|
||||
if(receiver.getDivisionProfit().compareTo(BigDecimal.ONE) > 0){
|
||||
throw new BizException("分账用户receiverId=["+ ( receiver.getReceiverId() == null ? "": receiver.getReceiverId() ) +"]," +
|
||||
"receiverGroupId=["+ (receiver.getReceiverGroupId() == null ? "": receiver.getReceiverGroupId() ) +"] 分账比例不得高于100%");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(receiver.getDivisionAmount() != null && receiver.getDivisionAmount() <= 0){
|
||||
throw new BizException("分账用户receiverId=["+ ( receiver.getReceiverId() == null ? "": receiver.getReceiverId() ) + "]," +
|
||||
"receiverGroupId=["+ (receiver.getReceiverGroupId() == null ? "": receiver.getReceiverGroupId() ) +"] 分账金额请大于0");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(!receiverIdSet.isEmpty()){
|
||||
|
||||
long receiverCount = mchDivisionReceiverService.count(MchDivisionReceiver.gw()
|
||||
.in(MchDivisionReceiver::getReceiverId, receiverIdSet)
|
||||
.eq(MchDivisionReceiver::getMchNo, mchNo)
|
||||
.eq(MchDivisionReceiver::getAppId, appId)
|
||||
.eq(MchDivisionReceiver::getIfCode, ifCode)
|
||||
.eq(MchDivisionReceiver::getState, CS.YES)
|
||||
);
|
||||
|
||||
if(receiverCount != receiverIdSet.size()){
|
||||
throw new BizException("分账[用户]中包含不存在或渠道不可用账号,请更改");
|
||||
}
|
||||
}
|
||||
|
||||
if(!receiverGroupIdSet.isEmpty()){
|
||||
|
||||
long receiverGroupCount = mchDivisionReceiverGroupService.count(MchDivisionReceiverGroup.gw()
|
||||
.in(MchDivisionReceiverGroup::getReceiverGroupId, receiverGroupIdSet)
|
||||
.eq(MchDivisionReceiverGroup::getMchNo, mchNo)
|
||||
);
|
||||
|
||||
if(receiverGroupCount != receiverGroupIdSet.size()){
|
||||
throw new BizException("分账[账号组]中包含不存在或不可用组,请更改");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.openapi;
|
||||
|
||||
import com.jeequan.jeepay.pay.anno.OpenLabel;
|
||||
import com.jeequan.jeepay.pay.ctrl.OpenApiController;
|
||||
import com.jeequan.jeepay.pay.service.openapi.impl.DivisionApiService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
* 分账相关操作
|
||||
* @author crystal
|
||||
* @date 2024/5/17 14:56
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/api/open/division")
|
||||
public class DivisionController extends OpenApiController {
|
||||
|
||||
@Autowired
|
||||
private DivisionApiService divisionApiService;
|
||||
|
||||
@OpenLabel
|
||||
@PostMapping(value = "/receiver/create")
|
||||
public Object receiverCreate() {
|
||||
return divisionApiService.create(getRQ());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.openapi;
|
||||
|
||||
import com.jeequan.jeepay.pay.anno.OpenLabel;
|
||||
import com.jeequan.jeepay.pay.ctrl.OpenApiController;
|
||||
import com.jeequan.jeepay.pay.service.openapi.impl.MchApplymentApiService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @author deng
|
||||
* @since 2024/4/19
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/api/open/applyment")
|
||||
public class MchApplymentController extends OpenApiController {
|
||||
|
||||
private final MchApplymentApiService mchApplymentApiService;
|
||||
|
||||
/**
|
||||
* 创建草稿商户,返回编辑的H5链接
|
||||
* @return
|
||||
*/
|
||||
@OpenLabel
|
||||
@PostMapping(value = "/h5Data")
|
||||
public Object createApplymentUrl() {
|
||||
return mchApplymentApiService.createH5MchApplyment(getRQ());
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询商户信息
|
||||
* @return
|
||||
*/
|
||||
@OpenLabel
|
||||
@PostMapping(value = "/queryApplymentInfo")
|
||||
public Object queryApplymentInfo() {
|
||||
return mchApplymentApiService.queryApplymentInfo(getRQ());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.openapi;
|
||||
|
||||
import cn.hutool.core.date.DatePattern;
|
||||
import cn.hutool.core.date.DateTime;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.model.openapi.AccessReq;
|
||||
import com.jeequan.jeepay.core.model.openapi.AccessResp;
|
||||
import com.jeequan.jeepay.core.service.IOpenApiService;
|
||||
import com.jeequan.jeepay.core.utils.JeepayKit;
|
||||
import com.jeequan.jeepay.core.utils.SeqKit;
|
||||
import com.jeequan.jeepay.pay.anno.OpenLabel;
|
||||
import com.jeequan.jeepay.pay.ctrl.OpenApiController;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
|
||||
/**
|
||||
* TODO
|
||||
* 交易Api控制器
|
||||
* @author crystal
|
||||
* @date 2024/4/7 15:03
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/open")
|
||||
public class QueryOpenApiController extends OpenApiController {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("queryTradeOpenApiService")
|
||||
private IOpenApiService queryTradeService;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("queryRefundOpenApiService")
|
||||
private IOpenApiService queryRefundService;
|
||||
|
||||
/**
|
||||
* 交易订单查询
|
||||
* @return
|
||||
*/
|
||||
@OpenLabel
|
||||
@RequestMapping(value = "/query/trade")
|
||||
public AccessResp tradeQuery(){
|
||||
return queryTradeService.execute(getRQ());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 退款订单查询
|
||||
* @return
|
||||
*/
|
||||
@OpenLabel
|
||||
@RequestMapping(value = "/query/refund")
|
||||
public AccessResp refundQuery(){
|
||||
return queryRefundService.execute(getRQ());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.openapi;
|
||||
|
||||
import cn.hutool.core.date.DatePattern;
|
||||
import cn.hutool.core.date.DateTime;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.model.openapi.AccessReq;
|
||||
import com.jeequan.jeepay.core.model.openapi.AccessResp;
|
||||
import com.jeequan.jeepay.core.service.IOpenApiService;
|
||||
import com.jeequan.jeepay.core.utils.JeepayKit;
|
||||
import com.jeequan.jeepay.core.utils.SeqKit;
|
||||
import com.jeequan.jeepay.pay.anno.OpenLabel;
|
||||
import com.jeequan.jeepay.pay.ctrl.OpenApiController;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
|
||||
/**
|
||||
* TODO
|
||||
* 交易Api控制器
|
||||
* @author crystal
|
||||
* @date 2024/4/7 15:03
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/open")
|
||||
public class RefundOpenApiController extends OpenApiController {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("refundOpenApiService")
|
||||
private IOpenApiService refundService;
|
||||
|
||||
/**
|
||||
* 退款
|
||||
* @return
|
||||
*/
|
||||
@OpenLabel("")
|
||||
@RequestMapping(value = "/order/refund")
|
||||
public AccessResp refund(){
|
||||
return refundService.execute(getRQ());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.openapi;
|
||||
|
||||
import cn.hutool.core.date.DatePattern;
|
||||
import cn.hutool.core.date.DateTime;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.model.openapi.AccessReq;
|
||||
import com.jeequan.jeepay.core.model.openapi.AccessResp;
|
||||
import com.jeequan.jeepay.core.service.IOpenApiService;
|
||||
import com.jeequan.jeepay.core.utils.JeepayKit;
|
||||
import com.jeequan.jeepay.core.utils.SeqKit;
|
||||
import com.jeequan.jeepay.pay.anno.OpenLabel;
|
||||
import com.jeequan.jeepay.pay.ctrl.OpenApiController;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
|
||||
/**
|
||||
* TODO
|
||||
* 交易Api控制器
|
||||
* @author crystal
|
||||
* @date 2024/4/7 15:03
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/open")
|
||||
public class TradeOpenApiController extends OpenApiController {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("jsapiOpenApiService")
|
||||
private IOpenApiService jsapiService;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("microOpenApiService")
|
||||
private IOpenApiService microService;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("h5OpenApiService")
|
||||
private IOpenApiService h5Service;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("scanOpenApiService")
|
||||
private IOpenApiService scanService;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("appOpenApiService")
|
||||
private IOpenApiService appService;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("closeOpenApiService")
|
||||
private IOpenApiService closeService;
|
||||
|
||||
|
||||
@Autowired
|
||||
@Qualifier("cashierOpenApiService")
|
||||
private IOpenApiService cashierService;
|
||||
|
||||
/**
|
||||
* 聚合收银台
|
||||
* @return
|
||||
*/
|
||||
@OpenLabel(CS.MCH_APP_API_ENUM_V2.CASHIER_PAY)
|
||||
@RequestMapping(value = "/payment/chrpay")
|
||||
public AccessResp chrPay(){
|
||||
return cashierService.execute(getRQ());
|
||||
}
|
||||
|
||||
/**
|
||||
* 公众号/生活号支付
|
||||
* @return
|
||||
*/
|
||||
@OpenLabel(CS.MCH_APP_API_ENUM_V2.JSAPI_PAY)
|
||||
@RequestMapping(value = "/payment/jspay")
|
||||
public AccessResp jsPay(){
|
||||
return jsapiService.execute(getRQ());
|
||||
}
|
||||
|
||||
/**
|
||||
* 聚合扫码支付
|
||||
* @return
|
||||
*/
|
||||
@OpenLabel(CS.MCH_APP_API_ENUM_V2.SCAN_PAY)
|
||||
@RequestMapping(value = "/payment/scanpay")
|
||||
public AccessResp scanPay(){
|
||||
return scanService.execute(getRQ());
|
||||
}
|
||||
|
||||
/**
|
||||
* 小程序支付入口
|
||||
* @return
|
||||
*/
|
||||
@OpenLabel(CS.MCH_APP_API_ENUM_V2.APPLET_PAY)
|
||||
@RequestMapping(value = "/payment/ltpay")
|
||||
public AccessResp ltPay(){
|
||||
AccessReq rq = getRQ().setIsApplet(true);
|
||||
return jsapiService.execute(rq);
|
||||
}
|
||||
|
||||
/**
|
||||
* 反扫支付
|
||||
* @return
|
||||
*/
|
||||
@OpenLabel(CS.MCH_APP_API_ENUM_V2.MICRO_PAY)
|
||||
@RequestMapping(value = "/payment/micropay")
|
||||
public AccessResp microPay(){
|
||||
return microService.execute(getRQ());
|
||||
}
|
||||
|
||||
/**
|
||||
* H5支付入口
|
||||
* @return
|
||||
*/
|
||||
@OpenLabel(CS.MCH_APP_API_ENUM_V2.H5_PAY)
|
||||
@RequestMapping(value = {"/payment/h5pay"})
|
||||
public AccessResp h5Pay(){
|
||||
return h5Service.execute(getRQ());
|
||||
}
|
||||
|
||||
/**
|
||||
* APP支付入口
|
||||
* @return
|
||||
*/
|
||||
@OpenLabel(CS.MCH_APP_API_ENUM_V2.APP_PAY)
|
||||
@RequestMapping(value = "/payment/apppay")
|
||||
public AccessResp appPay(){
|
||||
return appService.execute(getRQ());
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单关闭
|
||||
* @return
|
||||
*/
|
||||
@OpenLabel()
|
||||
@RequestMapping(value = "/order/close")
|
||||
public AccessResp close(){
|
||||
return closeService.execute(getRQ());
|
||||
}
|
||||
|
||||
// result.put("payType", "wx");
|
||||
// result.put("openid", "oY6Es6Y52jNTArOpt-CTDP4ScKqs");
|
||||
// result.put("payType", "alipay");
|
||||
// result.put("userid", "2088502904759300");
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
String md5Key = "3oloC61eOXyFYLJiYom6HpvAkxYEj2ZZhaNUljSsRGLyRTuw2m77EQM2ouaK7okh3gwiYakPYFOKP2HzBKB3xp86d9RrCgOnl6D4mRVNJKWXOy8OtQpvRaEXrRMYDLY7";
|
||||
String storeId = "S2404236859";
|
||||
String mercOrderNo = SeqKit.genMhoOrderId();
|
||||
JSONObject bizData = new JSONObject();
|
||||
bizData.put("subject","测试支付1");
|
||||
bizData.put("amount",3);
|
||||
// bizData.put("payType","ALIPAY");
|
||||
// bizData.put("isScreen",true);
|
||||
// bizData.put("subAppId","wx9646c9a52da17c40");
|
||||
// bizData.put("userId","oY6Es6Y52jNTArOpt-CTDP4ScKqs");
|
||||
// bizData.put("userId","2088502904759300");
|
||||
bizData.put("clientIp","127.0.0.1");
|
||||
bizData.put("mchOrderNo",mercOrderNo);
|
||||
bizData.put("body","测试支付2");
|
||||
bizData.put("storeId",storeId);
|
||||
bizData.put("currency","cny");
|
||||
JSONObject params = new JSONObject();
|
||||
params.put("reqId",System.currentTimeMillis() + "");
|
||||
params.put("reqTime", DateUtil.format(new DateTime(),DatePattern.PURE_DATETIME_FORMAT));
|
||||
params.put("version", "1.0");
|
||||
params.put("signType", "MD5");
|
||||
params.put("appId", "2081240416099412");
|
||||
params.put("bizData",bizData);
|
||||
String strSort = JeepayKit.getStrSort(params) + "&appSecret="+md5Key;
|
||||
String sign = JeepayKit.md5(strSort, "utf-8");
|
||||
params.put("sign", sign);
|
||||
System.out.println(params.toJSONString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试1
|
||||
* @param args
|
||||
*/
|
||||
// public static void main(String[] args) {
|
||||
// String md5Key = "3oloC61eOXyFYLJiYom6HpvAkxYEj2ZZhaNUljSsRGLyRTuw2m77EQM2ouaK7okh3gwiYakPYFOKP2HzBKB3xp86d9RrCgOnl6D4mRVNJKWXOy8OtQpvRaEXrRMYDLY7";
|
||||
// String storeId = "S2404183032";
|
||||
// String mercOrderNo = SeqKit.genMhoOrderId();
|
||||
// JSONObject bizData = new JSONObject();
|
||||
// bizData.put("subject","测试支付1");
|
||||
// bizData.put("amount",3);
|
||||
// bizData.put("payType","ALIPAY");
|
||||
//// bizData.put("isScreen",true);
|
||||
//// bizData.put("subAppId","wx9646c9a52da17c40");
|
||||
//// bizData.put("userId","oY6Es6Y52jNTArOpt-CTDP4ScKqs");
|
||||
//// bizData.put("userId","2088502904759300");
|
||||
// bizData.put("clientIp","127.0.0.1");
|
||||
// bizData.put("mchOrderNo",mercOrderNo);
|
||||
// bizData.put("body","测试支付2");
|
||||
// bizData.put("storeId",storeId);
|
||||
// bizData.put("currency","cny");
|
||||
// JSONObject params = new JSONObject();
|
||||
// params.put("reqId",System.currentTimeMillis() + "");
|
||||
// params.put("reqTime", DateUtil.format(new DateTime(),DatePattern.PURE_DATETIME_FORMAT));
|
||||
// params.put("version", "1.0");
|
||||
// params.put("signType", "MD5");
|
||||
// params.put("appId", "2081240416099412");
|
||||
// params.put("bizData",bizData);
|
||||
// String strSort = JeepayKit.getStrSort(params) + "&appSecret="+md5Key;
|
||||
// String sign = JeepayKit.md5(strSort, "utf-8");
|
||||
// params.put("sign", sign);
|
||||
// System.out.println(params.toJSONString());
|
||||
// }
|
||||
|
||||
|
||||
// public static void main(String[] args) {
|
||||
// String md5Key = "O3kAhyaxYSbYhFg0CDiNflJ8eoNGpQ8Gn8m1vQKrcybE0tH8aB1WEggDT3NbRza6FItOoSDbj64WdxR0JG749c1jTTI4LQkKzZMAggPbhRB32Mf2lgnYDKJNs5sqlJLS";
|
||||
// String mercOrderNo = SeqKit.genMhoOrderId();
|
||||
// JSONObject bizData = new JSONObject();
|
||||
// bizData.put("mchOrderNo","1231231");
|
||||
// JSONObject params = new JSONObject();
|
||||
// params.put("reqId",System.currentTimeMillis() + "");
|
||||
// params.put("reqTime", DateUtil.format(new DateTime(),DatePattern.PURE_DATETIME_FORMAT));
|
||||
// params.put("version", "1.0");
|
||||
// params.put("signType", "MD5");
|
||||
// params.put("appId", "65d5abb9e4b0607c72ea5eaf");
|
||||
// params.put("bizData",bizData);
|
||||
// String strSort = JeepayKit.getStrSort(params) + "&appSecret="+md5Key;
|
||||
// String sign = JeepayKit.md5(strSort, "utf-8");
|
||||
// params.put("sign", sign);
|
||||
// System.out.println(params.toJSONString());
|
||||
// }
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.openapi;
|
||||
|
||||
import com.jeequan.jeepay.core.model.openapi.AccessReq;
|
||||
import com.jeequan.jeepay.pay.anno.OpenLabel;
|
||||
import com.jeequan.jeepay.pay.ctrl.OpenApiController;
|
||||
import com.jeequan.jeepay.pay.service.openapi.impl.TransferApiService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @author deng
|
||||
* @since 2024/5/14
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/open")
|
||||
public class TransferController extends OpenApiController {
|
||||
|
||||
@Autowired
|
||||
private TransferApiService transferApiService;
|
||||
|
||||
@OpenLabel
|
||||
@PostMapping("/transfer")
|
||||
public Object transfer() {
|
||||
AccessReq rq = getRQ();
|
||||
transferApiService.preCheck(rq);
|
||||
return transferApiService.transfer(rq);
|
||||
}
|
||||
|
||||
@OpenLabel
|
||||
@PostMapping("/transferResult")
|
||||
public Object transferResult() {
|
||||
AccessReq rq = getRQ();
|
||||
transferApiService.preCheck(rq);
|
||||
return transferApiService.transferResult(rq);
|
||||
}
|
||||
|
||||
@OpenLabel
|
||||
@PostMapping("/transferWallet")
|
||||
public Object transferWalletInfo() {
|
||||
AccessReq rq = getRQ();
|
||||
transferApiService.preCheck(rq);
|
||||
return transferApiService.transferWalletInfo(rq);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,154 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.payorder;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.entity.MchApp;
|
||||
import com.jeequan.jeepay.core.entity.MchInfo;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
|
||||
import com.jeequan.jeepay.core.model.rqrs.refund.RefundOrderRQ;
|
||||
import com.jeequan.jeepay.core.utils.SeqKit;
|
||||
import com.jeequan.jeepay.db.entity.*;
|
||||
import com.jeequan.jeepay.pay.ctrl.ApiController;
|
||||
import com.jeequan.jeepay.pay.service.PayMchNotifyService;
|
||||
import com.jeequan.jeepay.service.impl.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
* 创建支付订单抽象类
|
||||
*
|
||||
* @author terrfly
|
||||
*
|
||||
* @date 2021/6/8 17:26
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class AbstractRefundOrderController extends ApiController {
|
||||
|
||||
@Autowired private RefundOrderService refundOrderService;
|
||||
@Autowired private PayMchNotifyService payMchNotifyService;
|
||||
|
||||
protected RefundOrder genRefundOrder(RefundOrderRQ rq, PayOrder payOrder, MchInfo mchInfo, MchApp mchApp){
|
||||
|
||||
Date nowTime = new Date();
|
||||
RefundOrder refundOrder = new RefundOrder();
|
||||
refundOrder.setRefundOrderId(SeqKit.genRefundOrderId()); //退款订单号
|
||||
refundOrder.setPayOrderId(payOrder.getPayOrderId()); //支付订单号
|
||||
refundOrder.setChannelPayOrderNo(payOrder.getChannelOrderNo()); //渠道支付单号
|
||||
refundOrder.setMchNo(payOrder.getMchNo()); //商户号
|
||||
refundOrder.setAgentNo(payOrder.getAgentNo()); // 服务商号
|
||||
refundOrder.setTopAgentNo(payOrder.getTopAgentNo()); // 上级服务商号
|
||||
refundOrder.setIsvNo(payOrder.getIsvNo()); //服务商号
|
||||
refundOrder.setAppId(payOrder.getAppId()); //商户应用ID
|
||||
refundOrder.setMchName(mchInfo.getMchShortName()); //商户名称
|
||||
refundOrder.setStoreId(payOrder.getStoreId()); // 门店ID
|
||||
refundOrder.setStoreName(payOrder.getStoreName()); // 门店名称
|
||||
refundOrder.setStoreUserId(payOrder.getStoreUserId()); // 门店收银员ID
|
||||
refundOrder.setMchType(mchInfo.getType()); //商户类型
|
||||
refundOrder.setMchRefundNo(rq.getMchRefundNo()); //商户退款单号
|
||||
refundOrder.setWayCode(payOrder.getWayCode()); //支付方式代码
|
||||
refundOrder.setWayCodeType(payOrder.getWayCodeType()); //支付方式代码
|
||||
refundOrder.setIfCode(payOrder.getIfCode()); //支付接口代码
|
||||
refundOrder.setPayAmount(payOrder.getAmount()); //支付金额,单位分
|
||||
refundOrder.setRefundAmount(rq.getRefundAmount()); //退款金额,单位分
|
||||
refundOrder.setCurrency(rq.getCurrency()); //三位货币代码,人民币:cny
|
||||
refundOrder.setState(RefundOrder.STATE_INIT); //退款状态:0-订单生成,1-退款中,2-退款成功,3-退款失败
|
||||
refundOrder.setClientIp(StringUtils.defaultIfEmpty(rq.getClientIp(), getClientIp())); //客户端IP
|
||||
refundOrder.setDeviceNo(payOrder.getDeviceNo()); // 设备号
|
||||
refundOrder.setDeviceType(payOrder.getDeviceType()); // 设备类型
|
||||
refundOrder.setDeviceProvider(payOrder.getDeviceProvider()); // 设备厂商
|
||||
refundOrder.setRefundReason(rq.getRefundReason()); //退款原因
|
||||
refundOrder.setChannelOrderNo(null); //渠道订单号
|
||||
refundOrder.setErrCode(null); //渠道错误码
|
||||
refundOrder.setErrMsg(null); //渠道错误描述
|
||||
refundOrder.setChannelExtra(StringUtils.defaultString(rq.getChannelExtra(), payOrder.getChannelExtra())); //特定渠道发起时额外参数
|
||||
refundOrder.setNotifyUrl(rq.getNotifyUrl()); //通知地址
|
||||
refundOrder.setExtParam(rq.getExtParam()); //扩展参数
|
||||
refundOrder.setChannelBizData(StringUtils.defaultIfBlank(rq.getChannelBizData(), payOrder.getChannelBizData())); // 渠道特殊业务数据
|
||||
refundOrder.setRefundFeeAmount(0L); // 退还手续费,默认0 , 退款成功,再更新。
|
||||
|
||||
// 补单规则: 一小时内 每分钟/次 , 超过1小时 一小时/次 , 异步通知不变。 超过7天 订单失效。
|
||||
refundOrder.setExpiredTime(DateUtil.offsetDay(nowTime, 7)); // 订单超时关闭时间 默认7天
|
||||
refundOrder.setSuccessTime(null); //订单退款成功时间
|
||||
refundOrder.setCreatedAt(nowTime); //创建时间
|
||||
refundOrder.setMchExtNo(payOrder.getMchExtNo());
|
||||
if(rq.getRefundAmount() >= payOrder.getAmount()){
|
||||
refundOrder.setRefundType(RefundOrder.ALL_REFUND_TYPE);
|
||||
}else{
|
||||
refundOrder.setRefundType(RefundOrder.PART_REFUND_TYPE);
|
||||
}
|
||||
return refundOrder;
|
||||
}
|
||||
|
||||
|
||||
/** 处理返回的渠道信息,并更新退款单状态
|
||||
* payOrder将对部分信息进行 赋值操作。
|
||||
* **/
|
||||
protected void processChannelMsg(ChannelRetMsg channelRetMsg, RefundOrder refundOrder){
|
||||
|
||||
//对象为空 || 上游返回状态为空, 则无需操作
|
||||
if(channelRetMsg == null || channelRetMsg.getChannelState() == null){
|
||||
return ;
|
||||
}
|
||||
|
||||
|
||||
//明确成功
|
||||
if(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS == channelRetMsg.getChannelState()) {
|
||||
|
||||
this.updateInitOrderStateThrowException(RefundOrder.STATE_SUCCESS, refundOrder, channelRetMsg);
|
||||
payMchNotifyService.refundOrderNotify(refundOrder);
|
||||
|
||||
//明确失败
|
||||
}else if(ChannelRetMsg.ChannelState.CONFIRM_FAIL == channelRetMsg.getChannelState()) {
|
||||
|
||||
this.updateInitOrderStateThrowException(RefundOrder.STATE_FAIL, refundOrder, channelRetMsg);
|
||||
payMchNotifyService.refundOrderNotify(refundOrder);
|
||||
|
||||
// 上游处理中 || 未知 || 上游接口返回异常 退款单为退款中状态
|
||||
}else if( ChannelRetMsg.ChannelState.WAITING == channelRetMsg.getChannelState() ||
|
||||
ChannelRetMsg.ChannelState.UNKNOWN == channelRetMsg.getChannelState() ||
|
||||
ChannelRetMsg.ChannelState.API_RET_ERROR == channelRetMsg.getChannelState()
|
||||
|
||||
){
|
||||
this.updateInitOrderStateThrowException(RefundOrder.STATE_ING, refundOrder, channelRetMsg);
|
||||
|
||||
// 系统异常: 退款单不再处理。 为: 生成状态
|
||||
}else if( ChannelRetMsg.ChannelState.SYS_ERROR == channelRetMsg.getChannelState() ){
|
||||
|
||||
}else{
|
||||
|
||||
throw new BizException("ChannelState 返回异常!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** 更新退款单状态 --》 退款单生成--》 其他状态 (向外抛出异常) **/
|
||||
protected void updateInitOrderStateThrowException(byte orderState, RefundOrder refundOrder, ChannelRetMsg channelRetMsg){
|
||||
|
||||
refundOrder.setState(orderState);
|
||||
refundOrder.setChannelOrderNo(channelRetMsg.getChannelOrderId());
|
||||
refundOrder.setErrCode(channelRetMsg.getChannelErrCode());
|
||||
refundOrder.setErrMsg(channelRetMsg.getChannelErrMsg());
|
||||
//TODO如果是快钱通道 部分退款当日不到账 与次日到账
|
||||
if(CS.IF_CODE.KQPAY.equals(refundOrder.getIfCode()) && RefundOrder.PART_REFUND_TYPE == refundOrder.getRefundType()){
|
||||
refundOrder.setErrMsg("快钱通道部分退款类型的退款金额将于第二个工作日凌晨4-5点到账");
|
||||
}
|
||||
boolean isSuccess = refundOrderService.updateInit2Ing(refundOrder.getRefundOrderId(), channelRetMsg.getChannelOrderId());
|
||||
if(!isSuccess){
|
||||
throw new BizException("更新退款单异常!");
|
||||
}
|
||||
|
||||
isSuccess = refundOrderService.updateIng2SuccessOrFail(refundOrder.getRefundOrderId(), refundOrder.getState(),
|
||||
channelRetMsg.getChannelOrderId(), channelRetMsg.getChannelErrCode(), channelRetMsg.getChannelErrMsg());
|
||||
if(!isSuccess){
|
||||
throw new BizException("更新退款单异常!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,452 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.payorder;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.ctrls.AbstractCtrl;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.exception.ResponseException;
|
||||
import com.jeequan.jeepay.core.interfaces.IConfigContextQueryService;
|
||||
import com.jeequan.jeepay.core.interfaces.paychannel.IChannelNoticeService;
|
||||
import com.jeequan.jeepay.core.interfaces.paychannel.IIsvmchStoreApplymentNotifyService;
|
||||
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
|
||||
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
|
||||
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
|
||||
import com.jeequan.jeepay.db.entity.MchApplyment;
|
||||
import com.jeequan.jeepay.db.entity.MchStore;
|
||||
import com.jeequan.jeepay.db.entity.PayOrder;
|
||||
import com.jeequan.jeepay.db.entity.RouteManage;
|
||||
import com.jeequan.jeepay.pay.service.PayMchNotifyService;
|
||||
import com.jeequan.jeepay.pay.service.PayOrderMarketReissueService;
|
||||
import com.jeequan.jeepay.pay.service.PayOrderProcessService;
|
||||
import com.jeequan.jeepay.service.impl.*;
|
||||
import com.jeequan.jeepay.thirdparty.channel.ryxpay.model.ReqMethod;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.MutablePair;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 渠道侧的通知入口Controller 【分为同步跳转(doReturn)和异步回调(doNotify) 】
|
||||
*
|
||||
* @author terrfly
|
||||
*
|
||||
* @date 2021/6/8 17:26
|
||||
*/
|
||||
@Slf4j
|
||||
@Controller
|
||||
public class ChannelNoticeController extends AbstractCtrl {
|
||||
|
||||
@Autowired private PayOrderService payOrderService;
|
||||
@Autowired private IConfigContextQueryService configContextQueryService;
|
||||
@Autowired private PayMchNotifyService payMchNotifyService;
|
||||
@Autowired private PayOrderProcessService payOrderProcessService;
|
||||
@Autowired private MchApplymentService mchApplymentService;
|
||||
@Autowired private MchStoreService mchStoreService;
|
||||
@Autowired private SettleInfoService settleInfoService;
|
||||
@Autowired private PayOrderMarketReissueService payOrderMarketReissueService;
|
||||
@Autowired private RouteManageService routeManageService;
|
||||
|
||||
|
||||
/** 同步通知入口
|
||||
*
|
||||
* payOrderId动态参数, 可能是固定格式: 规则如下:
|
||||
* JEEPAYISV_V1000001 (CS.PAY_RETURNURL_FIX_ISV_PREFIX)
|
||||
* JEEPAYMCHAPP_M1000001
|
||||
* 如果是以上格式, 那么需要通道内自行解析, & 不校验 urlOrderId 是合法性。
|
||||
*
|
||||
* payOrderId 前缀为 ONLYJUMP_,表示直接跳转
|
||||
*
|
||||
* **/
|
||||
@RequestMapping(value= {"/api/pay/return/{ifCode}", "/api/pay/return/{ifCode}/{payOrderId}"})
|
||||
public String doReturn(HttpServletRequest request, @PathVariable("ifCode") String ifCode, @PathVariable(value = "payOrderId", required = false) String urlOrderId){
|
||||
|
||||
String payOrderId = null;
|
||||
String logPrefix = "进入[" +ifCode+ "]支付同步跳转:urlOrderId:["+ StringUtils.defaultIfEmpty(urlOrderId, "") + "] ";
|
||||
log.info("===== {} =====" , logPrefix);
|
||||
|
||||
try {
|
||||
|
||||
// 参数有误
|
||||
if(StringUtils.isEmpty(ifCode)){
|
||||
return this.toReturnPage("ifCode is empty");
|
||||
}
|
||||
|
||||
//查询支付接口是否存在
|
||||
IChannelNoticeService payNotifyService = SpringBeansUtil.getBean(ifCode + "ChannelNoticeService", IChannelNoticeService.class);
|
||||
|
||||
// 支付通道接口实现不存在
|
||||
if(payNotifyService == null){
|
||||
log.error("{}, interface not exists ", logPrefix);
|
||||
return this.toReturnPage("[" + ifCode + "] interface not exists");
|
||||
}
|
||||
|
||||
// 仅做跳转,直接跳转订单的returnUrl
|
||||
onlyJump(urlOrderId, logPrefix);
|
||||
|
||||
// 解析订单号 和 请求参数
|
||||
MutablePair<String, Object> mutablePair = payNotifyService.parseParams(request, urlOrderId, IChannelNoticeService.NoticeTypeEnum.DO_RETURN);
|
||||
if(mutablePair == null){ // 解析数据失败, 响应已处理
|
||||
log.error("{}, mutablePair is null ", logPrefix);
|
||||
throw new BizException("解析数据异常!"); //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
|
||||
}
|
||||
|
||||
//解析到订单号
|
||||
payOrderId = mutablePair.left;
|
||||
log.info("{}, 解析数据为:payOrderId:{}, params:{}", logPrefix, payOrderId, mutablePair.getRight());
|
||||
|
||||
// 判断url订单号是否合法
|
||||
this.judgeUrlOrderId(urlOrderId, payOrderId, logPrefix);
|
||||
|
||||
//获取订单号 和 订单数据
|
||||
PayOrder payOrder = payOrderService.getById(payOrderId);
|
||||
|
||||
// 订单不存在
|
||||
if(payOrder == null){
|
||||
log.error("{}, 订单不存在. payOrderId={} ", logPrefix, payOrderId);
|
||||
return this.toReturnPage("支付订单不存在");
|
||||
}
|
||||
|
||||
//查询出商户应用的配置信息
|
||||
MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(payOrder.getMchNo(), payOrder.getAppId(),payOrder.getMchExtNo());
|
||||
|
||||
//调起接口的回调判断
|
||||
ChannelRetMsg notifyResult = payNotifyService.doNotice(request, mutablePair.getRight(), payOrder, mchAppConfigContext, IChannelNoticeService.NoticeTypeEnum.DO_RETURN);
|
||||
|
||||
// 返回null 表明出现异常, 无需处理通知下游等操作。
|
||||
if(notifyResult == null || notifyResult.getChannelState() == null || notifyResult.getResponseEntity() == null){
|
||||
log.error("{}, 处理回调事件异常 notifyResult data error, notifyResult ={} ",logPrefix, notifyResult);
|
||||
throw new BizException("处理回调事件异常!"); //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
|
||||
}
|
||||
|
||||
//判断订单状态
|
||||
if(notifyResult.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_SUCCESS) {
|
||||
payOrder.setState(PayOrder.STATE_SUCCESS);
|
||||
}else if(notifyResult.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_FAIL) {
|
||||
payOrder.setState(PayOrder.STATE_FAIL);
|
||||
}
|
||||
|
||||
boolean hasReturnUrl = StringUtils.isNotBlank(payOrder.getReturnUrl());
|
||||
log.info("===== {}, 订单通知完成。 payOrderId={}, parseState = {}, hasReturnUrl={} =====", logPrefix, payOrderId, notifyResult.getChannelState(), hasReturnUrl);
|
||||
|
||||
//包含通知地址时
|
||||
if(hasReturnUrl){
|
||||
// 重定向
|
||||
response.sendRedirect(payMchNotifyService.createReturnUrl(payOrder, mchAppConfigContext.getMchApp().getAppSecret()));
|
||||
return null;
|
||||
}else{
|
||||
|
||||
//跳转到支付成功页面
|
||||
return this.toReturnPage(null);
|
||||
}
|
||||
|
||||
} catch (BizException e) {
|
||||
log.error("{}, payOrderId={}, BizException", logPrefix, payOrderId, e);
|
||||
return this.toReturnPage(e.getMessage());
|
||||
|
||||
} catch (ResponseException e) {
|
||||
log.error("{}, payOrderId={}, ResponseException", logPrefix, payOrderId, e);
|
||||
return this.toReturnPage(e.getMessage());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("{}, payOrderId={}, 系统异常", logPrefix, payOrderId, e);
|
||||
return this.toReturnPage(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** 异步回调入口 **/
|
||||
@ResponseBody
|
||||
@RequestMapping(value= {"/api/pay/notify/{ifCode}", "/api/pay/notify/{ifCode}/{payOrderId}"})
|
||||
public ResponseEntity doNotify(HttpServletRequest request,HttpServletResponse response, @PathVariable("ifCode") String ifCode, @PathVariable(value = "payOrderId", required = false) String urlOrderId){
|
||||
|
||||
String payOrderId = null;
|
||||
String logPrefix = "进入[" +ifCode+ "]支付回调:urlOrderId:["+ StringUtils.defaultIfEmpty(urlOrderId, "") + "] ";
|
||||
log.info("===== {} =====" , logPrefix);
|
||||
try {
|
||||
|
||||
// 参数有误
|
||||
if(StringUtils.isEmpty(ifCode)){
|
||||
return ResponseEntity.badRequest().body("ifCode is empty");
|
||||
}
|
||||
|
||||
//查询支付接口是否存在
|
||||
IChannelNoticeService payNotifyService = SpringBeansUtil.getBean(ifCode + "ChannelNoticeService", IChannelNoticeService.class);
|
||||
|
||||
// 支付通道接口实现不存在
|
||||
if(payNotifyService == null){
|
||||
log.error("{}, interface not exists ", logPrefix);
|
||||
return ResponseEntity.badRequest().body("[" + ifCode + "] interface not exists");
|
||||
}
|
||||
|
||||
// 解析订单号 和 请求参数
|
||||
MutablePair<String, Object> mutablePair = payNotifyService.parseParams(request, urlOrderId, IChannelNoticeService.NoticeTypeEnum.DO_NOTIFY);
|
||||
if(mutablePair == null){ // 解析数据失败, 响应已处理
|
||||
log.error("{}, mutablePair is null ", logPrefix);
|
||||
throw new BizException("解析数据异常!"); //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
|
||||
}
|
||||
|
||||
//解析到订单号
|
||||
payOrderId = mutablePair.left;
|
||||
log.info("{}, 解析数据为:payOrderId:{}, params:{}", logPrefix, payOrderId, mutablePair.getRight());
|
||||
|
||||
// 判断url订单号是否合法
|
||||
this.judgeUrlOrderId(urlOrderId, payOrderId, logPrefix);
|
||||
|
||||
//获取订单号 和 订单数据
|
||||
PayOrder payOrder = payOrderService.getById(payOrderId);
|
||||
|
||||
// 订单不存在
|
||||
if(payOrder == null){
|
||||
log.error("{}, 订单不存在. payOrderId={} ", logPrefix, payOrderId);
|
||||
//退款异步通知 由于瑞银信的特殊性 需要转发到退款回调去处理
|
||||
JSONObject right = (JSONObject) mutablePair.right;
|
||||
if(CS.IF_CODE.RYXPAY.equals(ifCode) && ReqMethod.ServiceCode.SMZF004.getValue().equals(right.getString("tranCode"))){
|
||||
request.getRequestDispatcher("/api/refund/notify/" + ifCode).forward(request, response);
|
||||
}
|
||||
return payNotifyService.doNotifyOrderNotExists(request);
|
||||
}
|
||||
|
||||
//查询出商户应用的配置信息
|
||||
MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(payOrder.getMchNo(), payOrder.getAppId(),payOrder.getMchExtNo());
|
||||
//调起接口的回调判断
|
||||
ChannelRetMsg notifyResult = payNotifyService.doNotice(request, mutablePair.getRight(), payOrder, mchAppConfigContext, IChannelNoticeService.NoticeTypeEnum.DO_NOTIFY);
|
||||
|
||||
// 返回null 表明出现异常, 无需处理通知下游等操作。
|
||||
if(notifyResult == null || notifyResult.getChannelState() == null || notifyResult.getResponseEntity() == null){
|
||||
log.error("{}, 处理回调事件异常 notifyResult data error, notifyResult ={} ",logPrefix, notifyResult);
|
||||
throw new BizException("处理回调事件异常!"); //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
|
||||
}
|
||||
|
||||
boolean updateOrderSuccess = true; //默认更新成功
|
||||
// 订单是 【支付中状态】
|
||||
if(payOrder.getState() == PayOrder.STATE_ING || payOrder.getState() == PayOrder.STATE_INIT) {
|
||||
// 渠道订单号
|
||||
payOrder.setChannelOrderNo(notifyResult.getChannelOrderId());
|
||||
//明确成功
|
||||
if(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS == notifyResult.getChannelState()) {
|
||||
|
||||
updateOrderSuccess = payOrderService.updateIng2Success(payOrderId, notifyResult);
|
||||
|
||||
//订单支付成功 其他业务逻辑
|
||||
if(updateOrderSuccess){
|
||||
payOrderProcessService.confirmSuccess(payOrder, mchAppConfigContext);
|
||||
}
|
||||
//TODO 初始化D0的结算数据
|
||||
log.info("处理D0交易初始化,订单号:{}",payOrder.getPayOrderId());
|
||||
settleInfoService.initD0Settle(payOrder);
|
||||
//营销活动相关处理
|
||||
payOrderMarketReissueService.reissue(payOrder);
|
||||
if(StringUtils.isNotBlank(payOrder.getRouteId())){
|
||||
routeManageService.upStoreRouteData(payOrder.getFindAmt(),payOrder.getStoreId(),payOrder.getRouteId(),true);
|
||||
}
|
||||
//明确失败
|
||||
}else if(ChannelRetMsg.ChannelState.CONFIRM_FAIL == notifyResult.getChannelState()) {
|
||||
|
||||
updateOrderSuccess = payOrderService.updateIng2Fail(payOrderId, notifyResult);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新订单 异常
|
||||
if(!updateOrderSuccess){
|
||||
log.error("{}, updateOrderSuccess = {} ",logPrefix, updateOrderSuccess);
|
||||
return payNotifyService.doNotifyOrderStateUpdateFail(request);
|
||||
}
|
||||
|
||||
log.info("===== {}, 订单通知完成。 payOrderId={}, parseState = {} =====", logPrefix, payOrderId, notifyResult.getChannelState());
|
||||
|
||||
return notifyResult.getResponseEntity();
|
||||
} catch (BizException e) {
|
||||
log.error("{}, payOrderId={}, BizException", logPrefix, payOrderId, e);
|
||||
return ResponseEntity.badRequest().body(e.getMessage());
|
||||
|
||||
} catch (ResponseException e) {
|
||||
log.error("{}, payOrderId={}, ResponseException", logPrefix, payOrderId, e);
|
||||
return e.getResponseEntity();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("{}, payOrderId={}, 系统异常", logPrefix, payOrderId, e);
|
||||
return ResponseEntity.badRequest().body(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** 异步回调入口 **/
|
||||
@ResponseBody
|
||||
@RequestMapping(value= {"/api/mchStoreApplyment/notify/{storeId}/{applyId}"})
|
||||
public ResponseEntity doNotifyStore(HttpServletRequest request, @PathVariable(value = "storeId") String storeId, @PathVariable(value = "applyId") String applyId){
|
||||
|
||||
String logPrefix = "进入[" +applyId+ "]门店入驻回调:storeId:["+ StringUtils.defaultIfEmpty(storeId, "") + "] ";
|
||||
log.info("===== {} =====" , logPrefix);
|
||||
try {
|
||||
|
||||
// 参数有误
|
||||
if(StringUtils.isEmpty(applyId)){
|
||||
return ResponseEntity.badRequest().body("applyId is empty");
|
||||
}
|
||||
// 参数有误
|
||||
if(StringUtils.isEmpty(storeId)){
|
||||
return ResponseEntity.badRequest().body("storeId is empty");
|
||||
}
|
||||
|
||||
// 进件记录查询
|
||||
MchApplyment tbMchApplyment = mchApplymentService.getById(applyId);
|
||||
if (tbMchApplyment == null) {
|
||||
return ResponseEntity.badRequest().body("进件记录不存在");
|
||||
}
|
||||
// 门店进件信息
|
||||
if (StringUtils.isEmpty(tbMchApplyment.getStoreSuccResParameter())) {
|
||||
return ResponseEntity.badRequest().body("门店入驻信息不存在");
|
||||
}
|
||||
// 支付接口代码
|
||||
String ifCode = tbMchApplyment.getIfCode();
|
||||
// 参数有误
|
||||
if(StringUtils.isEmpty(ifCode)){
|
||||
return ResponseEntity.badRequest().body("ifCode is empty");
|
||||
}
|
||||
//查询回调接口是否存在
|
||||
IIsvmchStoreApplymentNotifyService notifyService = SpringBeansUtil.getBean(ifCode + "MchStoreApplymentNoticeService", IIsvmchStoreApplymentNotifyService.class);
|
||||
|
||||
// 门店回调接口实现不存在
|
||||
if(notifyService == null){
|
||||
log.error("{}, interface not exists ", logPrefix);
|
||||
return ResponseEntity.badRequest().body("[" + ifCode + "] interface not exists");
|
||||
}
|
||||
// 门店信息是否存在
|
||||
MchStore mchStore = mchStoreService.getById(storeId);
|
||||
if (mchStore == null) {
|
||||
return ResponseEntity.badRequest().body("入驻门店不存在");
|
||||
}
|
||||
|
||||
// 获取当前入驻门店信息
|
||||
JSONArray jsonArray = JSONArray.parseArray(tbMchApplyment.getStoreSuccResParameter());
|
||||
JSONObject dbStoreApplyment = null;
|
||||
for (int i = 0; i < jsonArray.size(); i++) {
|
||||
JSONObject json = (JSONObject) jsonArray.get(i);
|
||||
if (storeId.equals(json.getString("storeId"))) {
|
||||
dbStoreApplyment = json;
|
||||
jsonArray.remove(i);
|
||||
}
|
||||
}
|
||||
|
||||
if(dbStoreApplyment == null){
|
||||
throw new BizException("处理回调事件异常!"); //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
|
||||
}
|
||||
|
||||
String storeNo = dbStoreApplyment.getString("store_no");
|
||||
|
||||
//调起接口的回调判断
|
||||
ChannelRetMsg notifyResult = notifyService.doNotice(request, storeNo, tbMchApplyment.getIsvNo(), IIsvmchStoreApplymentNotifyService.NoticeTypeEnum.DO_NOTIFY);
|
||||
|
||||
// 返回null 表明出现异常, 无需处理通知下游等操作。
|
||||
if(notifyResult == null || notifyResult.getChannelState() == null || notifyResult.getResponseEntity() == null){
|
||||
log.error("{}, 处理回调事件异常 notifyResult data error, notifyResult ={} ",logPrefix, notifyResult);
|
||||
throw new BizException("处理回调事件异常!"); //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
|
||||
}
|
||||
|
||||
if (notifyResult.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_SUCCESS) {
|
||||
// 门店审核通过
|
||||
dbStoreApplyment.put("state", MchApplyment.STATE_SUCCESS);
|
||||
jsonArray.add(dbStoreApplyment);
|
||||
}else if (notifyResult.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_FAIL) {
|
||||
// 审核失败
|
||||
dbStoreApplyment.put("state", MchApplyment.STATE_REJECT_WAIT_MODIFY);
|
||||
dbStoreApplyment.put("msg", notifyResult.getChannelErrMsg());
|
||||
jsonArray.add(dbStoreApplyment);
|
||||
}else {
|
||||
return notifyResult.getResponseEntity();
|
||||
}
|
||||
|
||||
// 更新入驻信息
|
||||
MchApplyment updateApplyment = new MchApplyment();
|
||||
updateApplyment.setApplyId(applyId);
|
||||
updateApplyment.setStoreSuccResParameter(jsonArray.toJSONString());
|
||||
boolean update = mchApplymentService.updateById(updateApplyment);
|
||||
|
||||
// 更新入驻信息 异常
|
||||
if(!update){
|
||||
log.error("{}, update = {} ",logPrefix, update);
|
||||
}
|
||||
|
||||
log.info("===== {}, 门店入驻通知完成。 storeId={}, parseState = {} =====", logPrefix, storeId, notifyResult.getChannelState());
|
||||
|
||||
return notifyResult.getResponseEntity();
|
||||
|
||||
} catch (BizException e) {
|
||||
log.error("{}, storeId={}, BizException", logPrefix, storeId, e);
|
||||
return ResponseEntity.badRequest().body(e.getMessage());
|
||||
|
||||
} catch (ResponseException e) {
|
||||
log.error("{}, storeId={}, ResponseException", logPrefix, storeId, e);
|
||||
return e.getResponseEntity();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("{}, storeId={}, 系统异常", logPrefix, storeId, e);
|
||||
return ResponseEntity.badRequest().body(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/* 跳转到支付成功页面 **/
|
||||
private String toReturnPage(String errInfo){
|
||||
|
||||
|
||||
return "cashier/returnPage";
|
||||
}
|
||||
|
||||
|
||||
/** 比较URL是否相等 */
|
||||
private void judgeUrlOrderId(String urlOrderId, String payOrderId, String logPrefix){
|
||||
|
||||
// URL参数中的 orderId为空
|
||||
if(StringUtils.isEmpty(urlOrderId)){
|
||||
return ;
|
||||
}
|
||||
|
||||
// 存在固定URL
|
||||
if(CS.parsePayReturnUrlFix(urlOrderId, CS.PAY_RETURNURL_FIX_MCHAPP_PREFIX) != null || CS.parsePayReturnUrlFix(urlOrderId, CS.PAY_RETURNURL_FIX_ISV_PREFIX) != null){
|
||||
return ;
|
||||
}
|
||||
|
||||
// 比较URL参数和解析的是否相等
|
||||
if(!urlOrderId.equals(payOrderId)){
|
||||
log.error("{}, 订单号不匹配. urlOrderId={}, payOrderId={} ", logPrefix, urlOrderId, payOrderId);
|
||||
throw new BizException("订单号不匹配!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void onlyJump(String urlOrderId, String logPrefix) throws IOException {
|
||||
|
||||
if (StringUtils.isNotBlank(urlOrderId) && urlOrderId.startsWith(CS.PAY_RETURNURL_FIX_ONLY_JUMP_PREFIX)) {
|
||||
|
||||
String payOrderId = urlOrderId.substring(CS.PAY_RETURNURL_FIX_ONLY_JUMP_PREFIX.length());
|
||||
|
||||
//获取订单号 和 订单数据
|
||||
PayOrder payOrder = payOrderService.getById(payOrderId);
|
||||
|
||||
// 订单不存在
|
||||
if(payOrder == null){
|
||||
log.error("{}, 订单不存在. payOrderId={} ", logPrefix, payOrderId);
|
||||
this.toReturnPage("支付订单不存在");
|
||||
}
|
||||
|
||||
//查询出商户应用的配置信息
|
||||
MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(payOrder.getMchNo(), payOrder.getAppId(),payOrder.getMchExtNo());
|
||||
|
||||
if (StringUtils.isBlank(payOrder.getReturnUrl())) {
|
||||
this.toReturnPage(null);
|
||||
}
|
||||
response.sendRedirect(payMchNotifyService.createReturnUrl(payOrder, mchAppConfigContext.getMchApp().getAppSecret()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,479 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.payorder;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.ctrls.AbstractCtrl;
|
||||
import com.jeequan.jeepay.core.entity.MchInfo;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.exception.ResponseException;
|
||||
import com.jeequan.jeepay.core.interfaces.IConfigContextQueryService;
|
||||
import com.jeequan.jeepay.core.interfaces.paychannel.IChannelOrderAcceptService;
|
||||
import com.jeequan.jeepay.core.interfaces.paychannel.IDivisionService;
|
||||
import com.jeequan.jeepay.core.model.applyment.PaywayFee;
|
||||
import com.jeequan.jeepay.core.model.autopos.ChannelOrderAcceptParams;
|
||||
import com.jeequan.jeepay.core.model.autopos.ChannelOrderAcceptRetMsg;
|
||||
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
|
||||
import com.jeequan.jeepay.core.model.mchconfig.MchDivisionConfig;
|
||||
import com.jeequan.jeepay.core.model.params.IsvParams;
|
||||
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
|
||||
import com.jeequan.jeepay.core.utils.JeepayKit;
|
||||
import com.jeequan.jeepay.core.utils.SeqKit;
|
||||
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
|
||||
import com.jeequan.jeepay.db.entity.*;
|
||||
import com.jeequan.jeepay.pay.service.PayOrderDivisionRefundOrderCommonService;
|
||||
import com.jeequan.jeepay.pay.service.PayOrderProcessService;
|
||||
import com.jeequan.jeepay.pay.service.RefundOrderProcessService;
|
||||
import com.jeequan.jeepay.service.impl.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.MutablePair;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
*
|
||||
* * 渠道订单接收 ctrl
|
||||
* * 渠道 --> 我方系统
|
||||
* * 作用: 将渠道订单信息同步到我方系统中
|
||||
* * 目前支持的有: 立码收
|
||||
*
|
||||
* 智能POS交易通知 POS直接收款,交易结果通知接口
|
||||
*
|
||||
* @author zx
|
||||
*
|
||||
* @date 2021/6/8 17:26
|
||||
*/
|
||||
@Slf4j
|
||||
@Controller
|
||||
public class ChannelOrderAcceptController extends AbstractCtrl {
|
||||
|
||||
@Autowired private PayOrderService payOrderService;
|
||||
@Autowired private RefundOrderService refundOrderService;
|
||||
@Autowired private IConfigContextQueryService configContextQueryService;
|
||||
@Autowired private PayOrderProcessService payOrderProcessService;
|
||||
@Autowired private RefundOrderProcessService refundOrderProcessService;
|
||||
@Autowired private MchStoreService mchStoreService;
|
||||
@Autowired private RateConfigService rateConfigService;
|
||||
@Autowired private MchConfigService mchConfigService;
|
||||
@Autowired private MchStoreDeviceService mchStoreDeviceService;
|
||||
@Autowired private PayOrderDivisionRefundOrderCommonService payOrderDivisionRefundOrderCommonService;
|
||||
|
||||
|
||||
/** 通知入口 **/
|
||||
@ResponseBody
|
||||
|
||||
// 联调 地址
|
||||
// @RequestMapping("/api/orderPush/{ifCode}")
|
||||
|
||||
// 正式 URL
|
||||
@RequestMapping(value= {
|
||||
"/api/pay/posNotify/{ifCode}", // 存量配置 ( 智能pos 订单推送模式(兼容历史数据) )
|
||||
"/api/channelOrderAccept/{ifCode}", // channelOrderAccept 新通用命名 不加密
|
||||
"/api/channelOrderAccept/{ifCode}/{isvNo}" // channelOrderAccept 新通用命名 加密数据
|
||||
})
|
||||
public ResponseEntity channelOrderAccept(HttpServletRequest request, @PathVariable("ifCode") String ifCode,
|
||||
@PathVariable(value = "isvNo", required = false) String isvNo){
|
||||
|
||||
String logPrefix = "[" +ifCode+ "]【接收订单推送】,isvNo=" + StringUtils.defaultIfBlank(isvNo, "");
|
||||
log.info("===== 进入{} =====" , logPrefix);
|
||||
String channelOrderNo = "";
|
||||
|
||||
try {
|
||||
// 参数有误
|
||||
if(StringUtils.isEmpty(ifCode)){
|
||||
return ResponseEntity.badRequest().body("ifCode is empty");
|
||||
}
|
||||
|
||||
//查询支付接口是否存在
|
||||
IChannelOrderAcceptService orderAcceptService= SpringBeansUtil.getBean(JeepayKit.getIfCodeOrigin(ifCode) + "ChannelOrderAcceptService", IChannelOrderAcceptService.class);
|
||||
|
||||
// 支付通道接口实现不存在
|
||||
if(orderAcceptService == null){
|
||||
log.error("{}, interface not exists", logPrefix);
|
||||
return ResponseEntity.badRequest().body("[" + ifCode + "] interface not exists");
|
||||
}
|
||||
|
||||
// 服务商号不为空,查询服务商参数,一般用于通知数据加密时,需要服务商参数解密
|
||||
IsvParams isvParams = null;
|
||||
if (StringUtils.isNotBlank(isvNo)) {
|
||||
isvParams = configContextQueryService.queryIsvParams(isvNo, ifCode);
|
||||
if (isvParams == null) {
|
||||
log.error("{}, 服务商参数未配置", logPrefix);
|
||||
return ResponseEntity.badRequest().body("[" + ifCode + "] system error");
|
||||
}
|
||||
}
|
||||
|
||||
// 解析请求参数
|
||||
ChannelOrderAcceptParams acceptParams = orderAcceptService.parseParams(request, isvParams);
|
||||
if(acceptParams == null || acceptParams.getDeviceType() == null || StringUtils.isBlank(acceptParams.getDeviceNo())){ // 解析数据失败, 响应已处理
|
||||
log.error("{}, acceptParams is null ", logPrefix);
|
||||
return ResponseEntity.accepted().body("[" + ifCode + "] data parse error");
|
||||
}
|
||||
|
||||
// 解析商户号、应用、门店
|
||||
MchStoreDevice mchStoreDevice = getDeviceByIfCodeAndDeviceNo(ifCode, acceptParams.getDeviceType(), acceptParams.getDeviceNo(), logPrefix);
|
||||
|
||||
// 查询出商户应用的配置信息
|
||||
MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfo(mchStoreDevice.getMchNo(), mchStoreDevice.getAppId());
|
||||
|
||||
// 接口处理订单数据
|
||||
ChannelOrderAcceptRetMsg acceptRetMsg;
|
||||
if (ChannelOrderAcceptParams.TRADE_TYPE_PAYMENT.equals(acceptParams.getTradeType())) {
|
||||
acceptRetMsg = orderAcceptService.payNotice(request, acceptParams.getAcceptData(), mchAppConfigContext, ifCode);
|
||||
} else if (ChannelOrderAcceptParams.TRADE_TYPE_REFUND.equals(acceptParams.getTradeType())) {
|
||||
acceptRetMsg = orderAcceptService.refundNotice(request, acceptParams.getAcceptData(), mchAppConfigContext, ifCode);
|
||||
} else {
|
||||
throw new BizException("订单类型错误!");
|
||||
}
|
||||
|
||||
// 返回null 表明出现异常。
|
||||
if(acceptRetMsg == null || acceptRetMsg.getChannelState() == null || acceptRetMsg.getResponseEntity() == null
|
||||
|| StringUtils.isBlank(acceptRetMsg.getChannelOrderId()) || acceptRetMsg.getChannelAmount() == null){
|
||||
log.error("{}, 处理回调事件异常 acceptRetMsg data error, acceptRetMsg ={} ",logPrefix, acceptRetMsg);
|
||||
throw new BizException("处理回调事件异常!");
|
||||
}
|
||||
channelOrderNo = acceptRetMsg.getChannelOrderId();
|
||||
|
||||
// 非支付成功状态,直接返回
|
||||
if (acceptRetMsg.getChannelState() != ChannelRetMsg.ChannelState.CONFIRM_SUCCESS) {
|
||||
return acceptRetMsg.getResponseEntity();
|
||||
}
|
||||
|
||||
if (ChannelOrderAcceptParams.TRADE_TYPE_PAYMENT.equals(acceptParams.getTradeType())) {
|
||||
|
||||
processPayOrder(acceptRetMsg, mchAppConfigContext, mchStoreDevice, ifCode, logPrefix);
|
||||
|
||||
} else if (ChannelOrderAcceptParams.TRADE_TYPE_REFUND.equals(acceptParams.getTradeType())) {
|
||||
|
||||
processRefundOrder(acceptRetMsg, mchAppConfigContext, ifCode, logPrefix);
|
||||
|
||||
}
|
||||
|
||||
log.info("===== {}, 订单通知完成。 channelOrderNo={}, parseState = {} =====", logPrefix, channelOrderNo, acceptRetMsg.getChannelState());
|
||||
|
||||
return acceptRetMsg.getResponseEntity();
|
||||
|
||||
} catch (BizException e) {
|
||||
log.error("{}, channelOrderNo={}, BizException", logPrefix, channelOrderNo, e);
|
||||
return ResponseEntity.badRequest().body(e.getMessage());
|
||||
|
||||
} catch (ResponseException e) {
|
||||
log.error("{}, channelOrderNo={}, ResponseException", logPrefix, channelOrderNo, e);
|
||||
return e.getResponseEntity();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("{}, channelOrderNo={}, 系统异常", logPrefix, channelOrderNo, e);
|
||||
return ResponseEntity.badRequest().body(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 处理订单
|
||||
private void processPayOrder(ChannelOrderAcceptRetMsg channelResult, MchAppConfigContext mchAppConfigContext,
|
||||
MchStoreDevice mchStoreDevice, String ifCode, String logPrefix) {
|
||||
|
||||
// 根据通道返回的支付订单号查询订单,存在则更新状态,不存在插入新订单
|
||||
if (StringUtils.isNotBlank(channelResult.getBizOrderId())) {
|
||||
PayOrder dbPayOrder = payOrderService.getById(channelResult.getBizOrderId());
|
||||
if (dbPayOrder != null) {
|
||||
payOrderProcessService.handlePayOrder4Channel(channelResult, dbPayOrder, mchAppConfigContext);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 查询当前支付接口 订单是否已创建
|
||||
long count = payOrderService.count(PayOrder.gw()
|
||||
.eq(PayOrder::getChannelOrderNo, channelResult.getChannelOrderId())
|
||||
.eq(PayOrder::getIfCode, ifCode)
|
||||
);
|
||||
if (count > 0) {
|
||||
log.error("{}, 当前订单已创建,渠道订单号:{},", logPrefix, channelResult.getChannelOrderId());
|
||||
throw new ResponseException(channelResult.getResponseEntity());
|
||||
}
|
||||
|
||||
PayOrder payOrder = genPayOrder(channelResult, mchAppConfigContext, mchStoreDevice, ifCode);
|
||||
boolean saveResult = payOrderService.save(payOrder);
|
||||
if (!saveResult) {
|
||||
log.error("{}, 数据库更新异常", logPrefix);
|
||||
throw new BizException("数据库更新异常!");
|
||||
}
|
||||
|
||||
// 订单支付成功 其他业务逻辑
|
||||
payOrderProcessService.confirmSuccess(payOrder, mchAppConfigContext);
|
||||
}
|
||||
|
||||
private void processRefundOrder(ChannelOrderAcceptRetMsg channelResult, MchAppConfigContext mchAppConfigContext, String ifCode, String logPrefix) {
|
||||
|
||||
// 支付订单,根据退款通知中的 原渠道订单号(订单表的渠道订单号)查询
|
||||
PayOrder payOrder = payOrderService.getOneByChannelOrderId(ifCode, channelResult.getChannelPayOrderId());
|
||||
if (payOrder == null) {
|
||||
throw new BizException("支付订单不存在");
|
||||
}
|
||||
|
||||
if(payOrder.getRefundState() == PayOrder.REFUND_STATE_ALL || payOrder.getRefundAmount() >= payOrder.getAmount()){
|
||||
throw new BizException("订单已全额退款");
|
||||
}
|
||||
|
||||
if(payOrder.getState() != PayOrder.STATE_SUCCESS){
|
||||
throw new BizException("仅支付成功状态的订单才可发起退款");
|
||||
}
|
||||
|
||||
Long refundAmount = channelResult.getChannelAmount();
|
||||
if(payOrder.getRefundAmount() + refundAmount > payOrder.getAmount()){
|
||||
throw new BizException("退款金额超过订单可退款金额!");
|
||||
}
|
||||
|
||||
// 全部退款金额 (退款订单表)
|
||||
Long sumSuccessRefundAmount = refundOrderService.getBaseMapper().sumSuccessRefundAmount(payOrder.getPayOrderId());
|
||||
if(sumSuccessRefundAmount >= payOrder.getAmount()){
|
||||
throw new BizException("退款单已完成全部订单退款,本次申请失败");
|
||||
}
|
||||
|
||||
if(sumSuccessRefundAmount + refundAmount > payOrder.getAmount()){
|
||||
throw new BizException("申请金额超出订单可退款余额,请检查退款金额");
|
||||
}
|
||||
|
||||
// 根据通道处理后 返回的退款订单号查询订单,存在则更新状态,不存在插入新订单
|
||||
RefundOrder dbRefundOrder = refundOrderService.getById(channelResult.getBizOrderId());
|
||||
if (dbRefundOrder != null) {
|
||||
// 更新退款订单为成功状态 && 处理退分
|
||||
updateRefundStateSuccessAndRefundDivision(true, channelResult, dbRefundOrder, mchAppConfigContext, logPrefix);
|
||||
return;
|
||||
}
|
||||
|
||||
// 查询当前支付接口 退款订单是否已创建
|
||||
long count = refundOrderService.count(RefundOrder.gw()
|
||||
.eq(RefundOrder::getChannelOrderNo, channelResult.getChannelOrderId())
|
||||
.eq(RefundOrder::getIfCode, ifCode)
|
||||
);
|
||||
if (count > 0) {
|
||||
log.error("{}, 当前退款订单已创建,渠道退款订单号:{},", logPrefix, channelResult.getChannelOrderId());
|
||||
throw new ResponseException(channelResult.getResponseEntity());
|
||||
}
|
||||
|
||||
RefundOrder refundOrder = genRefundOrder(channelResult, payOrder, mchAppConfigContext);
|
||||
boolean saveResult = refundOrderService.save(refundOrder);
|
||||
if (!saveResult) {
|
||||
log.error("{}, 数据库更新异常", logPrefix);
|
||||
throw new BizException("数据库更新异常!");
|
||||
}
|
||||
|
||||
// 更新退款订单为成功状态 && 处理退分
|
||||
updateRefundStateSuccessAndRefundDivision(false, channelResult, refundOrder, mchAppConfigContext, logPrefix);
|
||||
|
||||
}
|
||||
|
||||
private MchStoreDevice getDeviceByIfCodeAndDeviceNo(String ifCode, Byte deviceType, String deviceNo, String logPrefix) {
|
||||
|
||||
MchStoreDevice mchStoreDevice = mchStoreDeviceService.getOne(MchStoreDevice.gw()
|
||||
.eq(MchStoreDevice::getProvider, ifCode)
|
||||
.eq(MchStoreDevice::getDeviceType, deviceType)
|
||||
.eq(MchStoreDevice::getDeviceNo, deviceNo)
|
||||
.eq(MchStoreDevice::getBindState, CS.YES)
|
||||
.eq(MchStoreDevice::getState, CS.YES)
|
||||
);
|
||||
|
||||
if (mchStoreDevice == null) {
|
||||
log.error("{}, 设备不存在,deviceNo={} ", logPrefix, deviceNo);
|
||||
throw new BizException("解析数据异常!");
|
||||
}
|
||||
|
||||
return mchStoreDevice;
|
||||
}
|
||||
|
||||
|
||||
private PayOrder genPayOrder(ChannelOrderAcceptRetMsg channelResult, MchAppConfigContext mchAppConfigContext, MchStoreDevice mchStoreDevice, String ifCode){
|
||||
|
||||
MchInfo mchInfo = mchAppConfigContext.getMchInfo();
|
||||
com.jeequan.jeepay.core.entity.MchApp mchApp = mchAppConfigContext.getMchApp();
|
||||
|
||||
// 商户退款回调地址
|
||||
MchConfig payNotifyConfig = mchConfigService.getByMchNoAndConfigKey(mchInfo.getMchNo(), "mchPayNotifyUrl");
|
||||
if (payNotifyConfig == null || StringUtils.isBlank(payNotifyConfig.getConfigVal())) {
|
||||
log.error("商户支付回调地址未配置,商户号mchNo:{}", mchInfo.getMchNo());
|
||||
}
|
||||
|
||||
PayOrder payOrder = new PayOrder();
|
||||
payOrder.setPayOrderId(SeqKit.genPayOrderId()); //生成订单ID
|
||||
payOrder.setMchNo(mchInfo.getMchNo()); //商户号
|
||||
// payOrder.setIsvNo(mchInfo.getIsvNo()); //服务商号
|
||||
payOrder.setAgentNo(mchInfo.getAgentNo()); // 服务商ID
|
||||
payOrder.setTopAgentNo(mchInfo.getTopAgentNo()); // 上级服务商ID
|
||||
payOrder.setAppId(mchApp.getAppId()); //商户应用appId
|
||||
payOrder.setMchName(mchInfo.getMchShortName()); //商户名称(简称)
|
||||
payOrder.setMchType(mchInfo.getType()); //商户类型
|
||||
payOrder.setEpUserId(mchInfo.getCreatedUid()); // 商户拓展员
|
||||
payOrder.setMchOrderNo(SeqKit.genMhoOrderId()); //商户订单号
|
||||
payOrder.setIfCode(ifCode); //接口代码
|
||||
payOrder.setWayCode(channelResult.getChannelWayCode()); //支付方式
|
||||
payOrder.setWayCodeType(StringUtils.defaultString(channelResult.getChannelWayCodeType(), CS.PAY_WAY_CODE_TYPE.OTHER)); // 渠道返回的支付方式类型
|
||||
|
||||
payOrder.setAmount(channelResult.getChannelAmount()); //订单金额
|
||||
|
||||
MchStore mchStore;
|
||||
String storeId = mchStoreDevice.getStoreId();
|
||||
if (storeId != null) {
|
||||
mchStore = mchStoreService.getById(storeId);
|
||||
}else {
|
||||
mchStore = mchStoreService.queryDefaultStore(mchInfo.getMchNo()); //查询默认门店
|
||||
}
|
||||
payOrder.setStoreId(mchStore.getStoreId()); // 门店ID
|
||||
payOrder.setStoreName(mchStore.getStoreName()); // 门店名称
|
||||
payOrder.setLng(mchStore.getLng()); // 经度
|
||||
payOrder.setLat(mchStore.getLat()); // 纬度
|
||||
payOrder.setAddress(mchStore.getAddress()); // 详细地址
|
||||
|
||||
payOrder.setDeviceType((String) MchStoreDevice.covertDeviceType(mchStoreDevice.getDeviceType())); // 设备类型
|
||||
payOrder.setDeviceNo(mchStoreDevice.getDeviceNo()); // 设备号
|
||||
payOrder.setDeviceProvider(mchStoreDevice.getProvider()); // 设备厂商
|
||||
|
||||
|
||||
// 查询费率
|
||||
if (!CS.PAY_WAY_CODE.OUT_TRADE.equals(channelResult.getChannelWayCode())) {
|
||||
PaywayFee paywayFee = rateConfigService.queryCreateOrderPaywayFee(mchAppConfigContext.getMchApplyment().getApplyId(), mchAppConfigContext.getMchApplyment().getIsvNo(), mchAppConfigContext.getMchApplyment().getAutoConfigMchAppId(), ifCode, channelResult.getChannelWayCode());
|
||||
if(paywayFee == null){
|
||||
log.error("[{}]接收订单推送, 商户费率未配置", ifCode);
|
||||
throw new BizException("商户费率未配置!");
|
||||
}
|
||||
MutablePair<String, Long> feeAndSnapshot = paywayFee.calFeeAndSnapshot(payOrder.getAmount());
|
||||
payOrder.setMchFeeRate(feeAndSnapshot.left);
|
||||
payOrder.setMchFeeAmount(feeAndSnapshot.right); //商户手续费,单位分
|
||||
payOrder.setMchOrderFeeAmount(feeAndSnapshot.right); //商户手续费,单位分
|
||||
}else if (channelResult.getChannelFee() != null) {
|
||||
payOrder.setMchFeeAmount(channelResult.getChannelFee()); //商户手续费,单位分
|
||||
payOrder.setMchOrderFeeAmount(channelResult.getChannelFee()); //商户手续费,单位分
|
||||
}else {
|
||||
payOrder.setMchFeeAmount(0L); // 默认无手续费
|
||||
payOrder.setMchOrderFeeAmount(0L); // 默认无手续费
|
||||
}
|
||||
|
||||
|
||||
Date nowTime = new Date();
|
||||
payOrder.setCurrency("cny"); //币种
|
||||
payOrder.setState(PayOrder.STATE_SUCCESS); // 仅保存支付成功订单
|
||||
payOrder.setSuccessTime(nowTime);
|
||||
payOrder.setSubject("[" + mchStore.getStoreName() + "]收款"); //商品标题
|
||||
payOrder.setBody("[" + mchStore.getStoreName() + "]收款"); //商品描述信息
|
||||
payOrder.setChannelOrderNo(channelResult.getChannelOrderId()); // 渠道订单号
|
||||
payOrder.setChannelUser(channelResult.getChannelUserId()); //渠道用户标志
|
||||
|
||||
payOrder.setPlatformOrderNo(channelResult.getPlatformOrderNo());
|
||||
payOrder.setPlatformMchOrderNo(channelResult.getPlatformMchOrderNo());
|
||||
|
||||
// 渠道特殊参数,渠道自处理数据
|
||||
if (channelResult.getChannelBizData() != null) {
|
||||
payOrder.setChannelBizData(channelResult.getChannelBizData().toJSONString());
|
||||
}
|
||||
|
||||
if (payNotifyConfig != null && StringUtils.isNotBlank(payNotifyConfig.getConfigVal())) {
|
||||
payOrder.setNotifyUrl(payNotifyConfig.getConfigVal()); // 支付通知地址
|
||||
}
|
||||
|
||||
// 分账模式
|
||||
if (channelResult.getChannelDivisionMode() != null) {
|
||||
payOrder.setDivisionMode(channelResult.getChannelDivisionMode());
|
||||
} else {
|
||||
payOrder.setDivisionMode(PayOrder.DIVISION_MODE_FORBID); // 默认不分账
|
||||
|
||||
// 当前商户配置模式为: 全局自动分账 && 金额满足
|
||||
MchDivisionConfig mchDivisionConfig = mchConfigService.queryMchDivisionConfigByDefault(payOrder.getMchNo());
|
||||
if(mchDivisionConfig.getOverrideAutoFlag() == CS.YES && payOrder.getAmount() >= mchDivisionConfig.getAutoDivisionRules().getAmountLimit()){
|
||||
payOrder.setDivisionMode(PayOrder.DIVISION_MODE_AUTO);
|
||||
}
|
||||
}
|
||||
|
||||
payOrder.setCreatedAt(nowTime);
|
||||
return payOrder;
|
||||
}
|
||||
|
||||
private RefundOrder genRefundOrder(ChannelOrderAcceptRetMsg channelResult, PayOrder payOrder, MchAppConfigContext mchAppConfigContext){
|
||||
|
||||
MchInfo mchInfo = mchAppConfigContext.getMchInfo();
|
||||
com.jeequan.jeepay.core.entity.MchApp mchApp = mchAppConfigContext.getMchApp();
|
||||
|
||||
// 商户退款回调地址
|
||||
MchConfig refundNotifyConfig = mchConfigService.getByMchNoAndConfigKey(mchInfo.getMchNo(), "mchRefundNotifyUrl");
|
||||
if (refundNotifyConfig == null || StringUtils.isBlank(refundNotifyConfig.getConfigVal())) {
|
||||
log.error("商户退款回调地址未配置,商户号mchNo:{}", mchInfo.getMchNo());
|
||||
}
|
||||
|
||||
RefundOrder refundOrder = new RefundOrder();
|
||||
refundOrder.setRefundOrderId(SeqKit.genRefundOrderId()); //退款订单号
|
||||
refundOrder.setPayOrderId(payOrder.getPayOrderId()); //支付订单号
|
||||
refundOrder.setChannelPayOrderNo(payOrder.getChannelOrderNo()); //渠道支付单号
|
||||
refundOrder.setMchNo(mchInfo.getMchNo()); //商户号
|
||||
refundOrder.setAgentNo(mchInfo.getAgentNo()); // 服务商号
|
||||
refundOrder.setTopAgentNo(mchInfo.getTopAgentNo()); // 上级服务商号
|
||||
// refundOrder.setIsvNo(mchInfo.getIsvNo()); //服务商号
|
||||
refundOrder.setAppId(mchApp.getAppId()); //商户应用ID
|
||||
refundOrder.setMchName(mchInfo.getMchShortName()); //商户名称
|
||||
refundOrder.setStoreId(payOrder.getStoreId()); // 门店ID
|
||||
refundOrder.setStoreName(payOrder.getStoreName()); // 门店名称
|
||||
refundOrder.setMchType(mchInfo.getType()); //商户类型
|
||||
refundOrder.setMchRefundNo(SeqKit.genMhoRefundOrderId()); //商户退款单号
|
||||
refundOrder.setWayCode(payOrder.getWayCode()); //支付方式代码
|
||||
refundOrder.setWayCodeType(payOrder.getWayCodeType()); // 支付方式代码分类
|
||||
refundOrder.setIfCode(payOrder.getIfCode()); //支付接口代码
|
||||
refundOrder.setPayAmount(payOrder.getAmount()); //支付金额,单位分
|
||||
refundOrder.setRefundAmount(channelResult.getChannelAmount()); //退款金额,单位分
|
||||
refundOrder.setCurrency(payOrder.getCurrency()); //三位货币代码,人民币:cny
|
||||
refundOrder.setState(RefundOrder.STATE_ING); // 插入退款中订单,再更新成退款成功
|
||||
refundOrder.setClientIp(StringUtils.defaultIfEmpty(payOrder.getClientIp(), getClientIp())); //客户端IP
|
||||
refundOrder.setRefundReason("退款"); //退款原因
|
||||
refundOrder.setChannelOrderNo(channelResult.getChannelOrderId()); //渠道订单号
|
||||
refundOrder.setErrCode(channelResult.getChannelErrCode()); //渠道错误码
|
||||
refundOrder.setErrMsg(channelResult.getChannelErrMsg()); //渠道错误描述
|
||||
refundOrder.setRefundFeeAmount(0L); // 退还手续费,默认0 , 退款成功,再更新。
|
||||
|
||||
if (refundNotifyConfig != null && StringUtils.isNotBlank(refundNotifyConfig.getConfigVal())) {
|
||||
refundOrder.setNotifyUrl(refundNotifyConfig.getConfigVal()); // 退款通知地址
|
||||
}
|
||||
|
||||
|
||||
// 退款,不进行补单操作,超过7天 订单失效。
|
||||
Date nowTime = new Date();
|
||||
refundOrder.setExpiredTime(DateUtil.offsetDay(nowTime, 7)); // 订单超时关闭时间 默认7天
|
||||
refundOrder.setSuccessTime(nowTime); //订单退款成功时间
|
||||
refundOrder.setCreatedAt(nowTime); //创建时间
|
||||
|
||||
return refundOrder;
|
||||
}
|
||||
|
||||
// 更新退款订单为成功状态 && 处理退分
|
||||
private void updateRefundStateSuccessAndRefundDivision(boolean isUpdateRefundOrder, ChannelOrderAcceptRetMsg channelResult, RefundOrder refundOrder,
|
||||
MchAppConfigContext mchAppConfigContext, String logPrefix) {
|
||||
|
||||
IDivisionService divisionService = getDivisionService(refundOrder.getIfCode());
|
||||
|
||||
// 渠道是否自动退分账,true-自动退,无需系统调用
|
||||
boolean channelDivisionRefundMode = channelResult.getChannelDivisionRefundMode() != null &&
|
||||
channelResult.getChannelDivisionRefundMode() == 1;
|
||||
|
||||
// 退款业务发生事前 分账。
|
||||
// 直接更新
|
||||
if(!channelDivisionRefundMode && !isUpdateRefundOrder && divisionService != null && !divisionService.divisionRefundIsOrderRefundAfterProc()){
|
||||
// 处理分账回退逻辑
|
||||
payOrderDivisionRefundOrderCommonService.processDivisionRefund(refundOrder, mchAppConfigContext);
|
||||
}
|
||||
|
||||
// 处理退款订单
|
||||
boolean updateOrderSuccess = refundOrderProcessService.handleRefundOrder4Channel(channelResult, refundOrder);
|
||||
if(!updateOrderSuccess){
|
||||
log.error("{}, updateOrderSuccess = {} ",logPrefix, updateOrderSuccess);
|
||||
throw ResponseException.buildText("update status error");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private IDivisionService getDivisionService(String ifCode){
|
||||
IDivisionService divisionService = SpringBeansUtil.getBean(JeepayKit.getIfCodeOrigin(ifCode) + "DivisionService", IDivisionService.class);
|
||||
return divisionService;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.payorder;
|
||||
|
||||
import com.jeequan.jeepay.core.aop.MethodLog;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.interfaces.paychannel.IPayOrderCloseService;
|
||||
import com.jeequan.jeepay.core.model.ApiRes;
|
||||
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
|
||||
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
|
||||
import com.jeequan.jeepay.core.model.rqrs.payorder.ClosePayOrderRQ;
|
||||
import com.jeequan.jeepay.core.model.rqrs.payorder.ClosePayOrderRS;
|
||||
import com.jeequan.jeepay.core.utils.JeepayKit;
|
||||
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
|
||||
import com.jeequan.jeepay.db.entity.PayOrder;
|
||||
import com.jeequan.jeepay.pay.ctrl.ApiController;
|
||||
import com.jeequan.jeepay.pay.util.ApiResKit;
|
||||
import com.jeequan.jeepay.service.impl.PayOrderService;
|
||||
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/*
|
||||
* 关闭订单 controller
|
||||
*
|
||||
* @author xiaoyu
|
||||
*
|
||||
* @date 2022/1/25 9:19
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
public class CloseOrderController extends ApiController {
|
||||
|
||||
@Autowired private PayOrderService payOrderService;
|
||||
@Autowired private ConfigContextQueryService configContextQueryService;
|
||||
|
||||
/**
|
||||
* @author: xiaoyu
|
||||
* @date: 2022/1/25 9:19
|
||||
* @describe: 关闭订单
|
||||
*/
|
||||
@MethodLog(remark = "支付订单关闭API")
|
||||
@RequestMapping("/api/pay/close")
|
||||
public ApiRes queryOrder(){
|
||||
|
||||
//获取参数 & 验签
|
||||
ClosePayOrderRQ rq = getRQByWithMchSign(ClosePayOrderRQ.class);
|
||||
|
||||
if(StringUtils.isAllEmpty(rq.getPayOrderId(), rq.getMchOrderNo())){
|
||||
throw new BizException("payOrderId 和 mchOrderNo 不能同时为空");
|
||||
}
|
||||
|
||||
PayOrder payOrder = payOrderService.queryMchOrder(rq.getMchNo(), rq.getPayOrderId(), rq.getMchOrderNo());
|
||||
if(payOrder == null){
|
||||
throw new BizException("订单不存在");
|
||||
}
|
||||
|
||||
if (payOrder.getState() != PayOrder.STATE_INIT && payOrder.getState() != PayOrder.STATE_ING) {
|
||||
throw new BizException("当前订单不可关闭");
|
||||
}
|
||||
|
||||
ClosePayOrderRS bizRes = new ClosePayOrderRS();
|
||||
|
||||
// 订单生成状态 直接修改订单状态
|
||||
if (payOrder.getState() == com.jeequan.jeepay.core.entity.PayOrder.STATE_INIT) {
|
||||
payOrderService.updateInit2Close(payOrder.getPayOrderId());
|
||||
bizRes.setChannelRetMsg(ChannelRetMsg.confirmSuccess(null));
|
||||
return ApiResKit.okWithSign(bizRes, configContextQueryService.queryMchApp(rq.getMchNo(), rq.getAppId()), rq.getSignType());
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
String payOrderId = payOrder.getPayOrderId();
|
||||
|
||||
log.error("{} 关单操作!", payOrderId);
|
||||
|
||||
//查询支付接口是否存在
|
||||
IPayOrderCloseService closeService = SpringBeansUtil.getBean(JeepayKit.getIfCodeOrigin(payOrder.getIfCode()) + "PayOrderCloseService", IPayOrderCloseService.class);
|
||||
|
||||
ChannelRetMsg channelRetMsg = ChannelRetMsg.confirmSuccess(null);
|
||||
|
||||
// 支付通道接口实现不存在
|
||||
if(closeService == null){
|
||||
log.error("{} interface not exists, 直接修改为关单成功!", payOrder.getIfCode());
|
||||
}else{
|
||||
|
||||
//查询出商户应用的配置信息
|
||||
MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(payOrder.getMchNo(), payOrder.getAppId(),payOrder.getMchExtNo());
|
||||
|
||||
channelRetMsg = closeService.close(payOrder, mchAppConfigContext);
|
||||
if(channelRetMsg == null){
|
||||
log.error("channelRetMsg is null, 直接修改为关单成功!");
|
||||
channelRetMsg = ChannelRetMsg.confirmSuccess(null);
|
||||
}
|
||||
}
|
||||
|
||||
log.info("关闭订单[{}]结果为:{}", payOrderId, channelRetMsg);
|
||||
|
||||
// 关闭订单 成功
|
||||
if(channelRetMsg.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_SUCCESS) {
|
||||
payOrderService.updateIng2Close(payOrderId);
|
||||
}else {
|
||||
return ApiRes.customFail("渠道响应:" + channelRetMsg.getChannelErrMsg());
|
||||
}
|
||||
|
||||
bizRes.setChannelRetMsg(channelRetMsg);
|
||||
} catch (Exception e) { // 关闭订单异常
|
||||
log.error("error payOrderId = {}", payOrder.getPayOrderId(), e);
|
||||
throw new BizException(e.getMessage());
|
||||
}
|
||||
|
||||
return ApiResKit.okWithSign(bizRes, configContextQueryService.queryMchApp(rq.getMchNo(), rq.getAppId()), rq.getSignType());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.payorder;
|
||||
|
||||
import com.jeequan.jeepay.core.ctrls.AbstractCtrl;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.exception.ResponseException;
|
||||
import com.jeequan.jeepay.core.interfaces.IConfigContextQueryService;
|
||||
import com.jeequan.jeepay.core.interfaces.paychannel.IPosNoticeService;
|
||||
import com.jeequan.jeepay.core.model.autopos.ChannelOrderAcceptParams;
|
||||
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
|
||||
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
|
||||
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
|
||||
import com.jeequan.jeepay.db.entity.PayOrder;
|
||||
import com.jeequan.jeepay.db.entity.RefundOrder;
|
||||
import com.jeequan.jeepay.pay.service.PayOrderProcessService;
|
||||
import com.jeequan.jeepay.pay.service.RefundOrderProcessService;
|
||||
import com.jeequan.jeepay.service.impl.PayOrderService;
|
||||
import com.jeequan.jeepay.service.impl.RefundOrderService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.Triple;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 智能POS交易通知 用于将订单信息推送到POS,使用POS收款后的异步通知
|
||||
*
|
||||
* @author zx
|
||||
*
|
||||
* @date 2021/6/8 17:26
|
||||
*/
|
||||
@Slf4j
|
||||
@Controller
|
||||
public class PosNoticeController extends AbstractCtrl {
|
||||
|
||||
@Autowired private PayOrderService payOrderService;
|
||||
@Autowired private IConfigContextQueryService configContextQueryService;
|
||||
@Autowired private PayOrderProcessService payOrderProcessService;
|
||||
@Autowired private RefundOrderService refundOrderService;
|
||||
@Autowired private RefundOrderProcessService refundOrderProcessService;
|
||||
|
||||
|
||||
/** 智能POS通知入口 **/
|
||||
@ResponseBody
|
||||
@RequestMapping("/api/pos/pay/notify/{ifCode}")
|
||||
public ResponseEntity posNotify(HttpServletRequest request, @PathVariable("ifCode") String ifCode){
|
||||
|
||||
String orderId = null;
|
||||
String logPrefix = "进入[" +ifCode+ "]智能POS【订单推送至POS机收款模式】回调";
|
||||
log.info("===== {} =====" , logPrefix);
|
||||
|
||||
try {
|
||||
|
||||
// 参数有误
|
||||
if(StringUtils.isEmpty(ifCode)){
|
||||
return ResponseEntity.badRequest().body("ifCode is empty");
|
||||
}
|
||||
|
||||
//查询支付接口是否存在
|
||||
IPosNoticeService payNotifyService = SpringBeansUtil.getBean(ifCode + "PosNoticeService", IPosNoticeService.class);
|
||||
|
||||
// 支付通道接口实现不存在
|
||||
if(payNotifyService == null){
|
||||
log.error("{}, interface not exists ", logPrefix);
|
||||
return ResponseEntity.badRequest().body("[" + ifCode + "] interface not exists");
|
||||
}
|
||||
|
||||
// 解析订单号 和 请求参数 Triple => <交易类型(PAYMENT-支付 REFUND-退款),支付订单号,请求数据>
|
||||
Triple<String, String, Object> triple = payNotifyService.parseParams(request);
|
||||
if(triple == null || StringUtils.isBlank(triple.getMiddle())){ // 解析数据失败, 响应已处理
|
||||
log.error("{}, params is null ", logPrefix);
|
||||
throw new BizException("解析数据异常!"); //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
|
||||
}
|
||||
|
||||
// 解析到订单号
|
||||
orderId = triple.getMiddle();
|
||||
|
||||
// 接口验签 && 处理订单数据
|
||||
ChannelRetMsg notifyResult;
|
||||
// 支付订单
|
||||
if (StringUtils.equals(triple.getLeft(), ChannelOrderAcceptParams.TRADE_TYPE_PAYMENT)) {
|
||||
|
||||
PayOrder payOrder = getPayOrder(orderId, logPrefix);
|
||||
|
||||
MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(payOrder.getMchNo(), payOrder.getAppId(),payOrder.getMchExtNo());
|
||||
|
||||
notifyResult = payNotifyService.payNotice(request, triple.getRight(), payOrder, mchAppConfigContext);
|
||||
|
||||
// 返回null 表明出现异常, 无需处理通知下游等操作。
|
||||
if(notifyResult == null || notifyResult.getChannelState() == null || notifyResult.getResponseEntity() == null){
|
||||
log.error("{}, 处理回调事件异常 notifyResult data error, notifyResult ={} ",logPrefix, notifyResult);
|
||||
throw new BizException("处理回调事件异常!"); //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
|
||||
}
|
||||
|
||||
payOrderProcessService.handlePayOrder4Channel(notifyResult, payOrder, mchAppConfigContext);
|
||||
|
||||
// 退款订单
|
||||
} else if (StringUtils.equals(triple.getLeft(), ChannelOrderAcceptParams.TRADE_TYPE_REFUND)) {
|
||||
|
||||
RefundOrder refundOrder = getRefundOrder(orderId, logPrefix);
|
||||
|
||||
MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(refundOrder.getMchNo(), refundOrder.getAppId(),refundOrder.getMchExtNo());
|
||||
|
||||
notifyResult = payNotifyService.refundNotice(request, triple.getRight(), refundOrder, mchAppConfigContext);
|
||||
|
||||
// 返回null 表明出现异常, 无需处理通知下游等操作。
|
||||
if(notifyResult == null || notifyResult.getChannelState() == null || notifyResult.getResponseEntity() == null){
|
||||
log.error("{}, 处理回调事件异常 notifyResult data error, notifyResult ={} ",logPrefix, notifyResult);
|
||||
throw new BizException("处理回调事件异常!"); //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
|
||||
}
|
||||
// 处理退款订单
|
||||
boolean updateOrderSuccess = refundOrderProcessService.handleRefundOrder4Channel(notifyResult, refundOrder);
|
||||
|
||||
// 更新退款订单 异常
|
||||
if(!updateOrderSuccess){
|
||||
log.error("{}, updateOrderSuccess = {} ",logPrefix, updateOrderSuccess);
|
||||
throw ResponseException.buildText("update status error");
|
||||
}
|
||||
|
||||
} else {
|
||||
throw new BizException("通知类型错误!");
|
||||
}
|
||||
|
||||
log.info("===== {}, 订单通知完成。 orderId={}, parseState = {} =====", logPrefix, orderId, notifyResult.getChannelState());
|
||||
|
||||
return notifyResult.getResponseEntity();
|
||||
|
||||
} catch (BizException e) {
|
||||
log.error("{}, orderId={}, BizException", logPrefix, orderId, e);
|
||||
return ResponseEntity.badRequest().body(e.getMessage());
|
||||
|
||||
} catch (ResponseException e) {
|
||||
log.error("{}, orderId={}, ResponseException", logPrefix, orderId, e);
|
||||
return e.getResponseEntity();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("{}, orderId={}, 系统异常", logPrefix, orderId, e);
|
||||
return ResponseEntity.badRequest().body(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private PayOrder getPayOrder(String orderId, String logPrefix) {
|
||||
// 获取订单号 和 订单数据
|
||||
PayOrder payOrder = payOrderService.getById(orderId);
|
||||
|
||||
// 订单不存在
|
||||
if(payOrder == null){
|
||||
log.error("{}, 订单不存在. orderId={} ", logPrefix, orderId);
|
||||
throw ResponseException.buildText("order not exists");
|
||||
}
|
||||
|
||||
return payOrder;
|
||||
}
|
||||
|
||||
private RefundOrder getRefundOrder(String orderId, String logPrefix) {
|
||||
// 获取订单号 和 订单数据
|
||||
RefundOrder refundOrder = refundOrderService.getById(orderId);
|
||||
|
||||
// 订单不存在
|
||||
if(refundOrder == null){
|
||||
log.error("{}, 退款订单不存在. refundOrder={} ", logPrefix, refundOrder);
|
||||
throw ResponseException.buildText("refund order not exists");
|
||||
}
|
||||
|
||||
return refundOrder;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.payorder;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.jeequan.jeepay.core.aop.MethodLog;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.interfaces.IConfigContextQueryService;
|
||||
import com.jeequan.jeepay.core.model.ApiRes;
|
||||
import com.jeequan.jeepay.core.model.rqrs.payorder.QueryPayOrderRQ;
|
||||
import com.jeequan.jeepay.core.model.rqrs.payorder.QueryPayOrderRS;
|
||||
import com.jeequan.jeepay.db.entity.MchConfig;
|
||||
import com.jeequan.jeepay.db.entity.PayOrder;
|
||||
import com.jeequan.jeepay.pay.ctrl.ApiController;
|
||||
import com.jeequan.jeepay.pay.util.ApiResKit;
|
||||
import com.jeequan.jeepay.service.impl.MchConfigService;
|
||||
import com.jeequan.jeepay.service.impl.PayOrderService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
* 商户查单controller
|
||||
*
|
||||
* @author terrfly
|
||||
*
|
||||
* @date 2021/6/8 17:26
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
public class QueryOrderController extends ApiController {
|
||||
|
||||
@Autowired private PayOrderService payOrderService;
|
||||
@Autowired private MchConfigService mchConfigService;
|
||||
@Autowired private IConfigContextQueryService configContextQueryService;
|
||||
|
||||
/**
|
||||
* 查单接口
|
||||
* **/
|
||||
@MethodLog(remark = "查询支付订单API")
|
||||
@RequestMapping("/api/pay/query")
|
||||
public ApiRes queryOrder(){
|
||||
|
||||
//获取参数 & 验签
|
||||
QueryPayOrderRQ rq = getRQByWithMchSign(QueryPayOrderRQ.class);
|
||||
|
||||
if(StringUtils.isAllEmpty(rq.getMchOrderNo(), rq.getPayOrderId())){
|
||||
throw new BizException("mchOrderNo 和 payOrderId不能同时为空");
|
||||
}
|
||||
|
||||
PayOrder payOrder = payOrderService.queryMchOrder(rq.getMchNo(), rq.getPayOrderId(), rq.getMchOrderNo());
|
||||
if(payOrder == null){
|
||||
throw new BizException("订单不存在");
|
||||
}
|
||||
|
||||
QueryPayOrderRS bizRes = QueryPayOrderRS.buildByPayOrder(payOrder);
|
||||
|
||||
JSONObject jsonObject = (JSONObject)JSONObject.toJSON(bizRes);
|
||||
|
||||
// 查询扩展参数
|
||||
MchConfig mchConfig = mchConfigService.getByMchNoAndConfigKey(payOrder.getMchNo(), "payOrderNotifyExtParams");
|
||||
List<String> extParams = new ArrayList<>();
|
||||
if(mchConfig != null && StringUtils.isNotBlank(mchConfig.getConfigVal())){
|
||||
extParams = JSON.parseArray(mchConfig.getConfigVal(), String.class);
|
||||
}
|
||||
for (String key : QueryPayOrderRS.EXT_PRAMS) {
|
||||
if(!extParams.contains(key)){
|
||||
jsonObject.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
// 报文签名
|
||||
return ApiResKit.okWithSign(jsonObject, configContextQueryService.queryMchApp(rq.getMchNo(), rq.getAppId()), rq.getSignType());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.payorder;
|
||||
|
||||
import com.jeequan.jeepay.core.aop.MethodLog;
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.interfaces.IConfigContextQueryService;
|
||||
import com.jeequan.jeepay.core.model.ApiRes;
|
||||
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
|
||||
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRS;
|
||||
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.AutoBarOrderRQ;
|
||||
import com.jeequan.jeepay.core.utils.JeepayKit;
|
||||
import com.jeequan.jeepay.db.entity.PayOrder;
|
||||
import com.jeequan.jeepay.db.entity.PayWay;
|
||||
import com.jeequan.jeepay.pay.util.ApiResKit;
|
||||
import com.jeequan.jeepay.service.impl.PayWayService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/*
|
||||
* 统一下单 controller
|
||||
*
|
||||
* @author terrfly
|
||||
*
|
||||
* @date 2021/6/8 17:27
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
public class UnifiedOrderController extends AbstractPayOrderController {
|
||||
|
||||
@Autowired private PayWayService payWayService;
|
||||
@Autowired private IConfigContextQueryService configContextQueryService;
|
||||
|
||||
/**
|
||||
* 统一下单接口
|
||||
* **/
|
||||
@MethodLog(remark = "统一下单API")
|
||||
@PostMapping("/api/pay/unifiedOrder")
|
||||
public ApiRes unifiedOrder(){
|
||||
|
||||
//获取参数 & 验签
|
||||
UnifiedOrderRQ rq = getRQByWithMchSign(UnifiedOrderRQ.class);
|
||||
|
||||
UnifiedOrderRQ bizRQ = buildBizRQ(rq);
|
||||
|
||||
//实现子类的res
|
||||
ApiRes apiRes = unifiedOrder(bizRQ.getWayCode(), bizRQ);
|
||||
if(apiRes.getData() == null){
|
||||
return apiRes;
|
||||
}
|
||||
|
||||
UnifiedOrderRS bizRes = (UnifiedOrderRS)apiRes.getData();
|
||||
|
||||
//聚合接口,返回的参数
|
||||
UnifiedOrderRS res = new UnifiedOrderRS();
|
||||
BeanUtils.copyProperties(bizRes, res);
|
||||
|
||||
//只有 订单生成(QR_CASHIER) || 支付中 || 支付成功返回该数据
|
||||
if(bizRes.getOrderState() != null && (bizRes.getOrderState() == PayOrder.STATE_INIT || bizRes.getOrderState() == PayOrder.STATE_ING || bizRes.getOrderState() == PayOrder.STATE_SUCCESS) ){
|
||||
res.setPayDataType(bizRes.buildPayDataType());
|
||||
res.setPayData(bizRes.buildPayData());
|
||||
}
|
||||
|
||||
return ApiResKit.okWithSign(res, configContextQueryService.queryMchApp(rq.getMchNo(), rq.getAppId()), rq.getSignType());
|
||||
}
|
||||
|
||||
|
||||
private UnifiedOrderRQ buildBizRQ(UnifiedOrderRQ rq){
|
||||
|
||||
//支付方式 比如: ali_bar
|
||||
String wayCode = rq.getWayCode();
|
||||
|
||||
//jsapi 收银台聚合支付场景 (不校验是否存在payWayCode)
|
||||
if(CS.PAY_WAY_CODE.QR_CASHIER.equals(wayCode)){
|
||||
return rq.buildBizRQ();
|
||||
}
|
||||
|
||||
//web收银台聚合支付场景 (不校验是否存在payWayCode)
|
||||
if(CS.PAY_WAY_CODE.WEB_CASHIER.equals(wayCode)){
|
||||
return rq.buildBizRQ();
|
||||
}
|
||||
|
||||
//如果是自动分类条码
|
||||
if(CS.PAY_WAY_CODE.AUTO_BAR.equals(wayCode)){
|
||||
AutoBarOrderRQ bizRQ = (AutoBarOrderRQ)rq.buildBizRQ();
|
||||
wayCode = JeepayKit.getPayWayCodeByBarCode(bizRQ.getAuthCode());
|
||||
rq.setWayCode(wayCode.trim());
|
||||
}
|
||||
|
||||
if(payWayService.count(PayWay.gw().eq(PayWay::getWayCode, wayCode)) <= 0){
|
||||
throw new BizException("不支持的支付方式");
|
||||
}
|
||||
|
||||
//转换为 bizRQ
|
||||
return rq.buildBizRQ();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.payorder.payway;
|
||||
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.model.ApiRes;
|
||||
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.AliBarOrderRQ;
|
||||
import com.jeequan.jeepay.pay.ctrl.payorder.AbstractPayOrderController;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/*
|
||||
* 支付宝 条码支付 controller
|
||||
*
|
||||
* @author terrfly
|
||||
*
|
||||
* @date 2021/6/8 17:25
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
public class AliBarOrderController extends AbstractPayOrderController {
|
||||
|
||||
|
||||
/**
|
||||
* 统一下单接口
|
||||
* **/
|
||||
@PostMapping("/api/pay/aliBarOrder")
|
||||
public ApiRes aliBarOrder(){
|
||||
|
||||
//获取参数 & 验证
|
||||
AliBarOrderRQ bizRQ = getRQByWithMchSign(AliBarOrderRQ.class);
|
||||
|
||||
// 统一下单接口
|
||||
return unifiedOrder(CS.PAY_WAY_CODE.ALI_BAR, bizRQ);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.payorder.payway;
|
||||
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.model.ApiRes;
|
||||
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.AliJsapiOrderRQ;
|
||||
import com.jeequan.jeepay.pay.ctrl.payorder.AbstractPayOrderController;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/*
|
||||
* 支付宝 jspai controller
|
||||
*
|
||||
* @author terrfly
|
||||
*
|
||||
* @date 2021/6/8 17:25
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
public class AliJsapiOrderController extends AbstractPayOrderController {
|
||||
|
||||
|
||||
/**
|
||||
* 统一下单接口
|
||||
* **/
|
||||
@PostMapping("/api/pay/aliJsapiOrder")
|
||||
public ApiRes aliJsapiOrder(){
|
||||
|
||||
//获取参数 & 验证
|
||||
AliJsapiOrderRQ bizRQ = getRQByWithMchSign(AliJsapiOrderRQ.class);
|
||||
|
||||
// 统一下单接口
|
||||
return unifiedOrder(CS.PAY_WAY_CODE.ALI_JSAPI, bizRQ);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.payorder.payway;
|
||||
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.model.ApiRes;
|
||||
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.YsfBarOrderRQ;
|
||||
import com.jeequan.jeepay.pay.ctrl.payorder.AbstractPayOrderController;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/*
|
||||
* 云闪付 条码支付 controller
|
||||
*
|
||||
* @author pangxiaoyu
|
||||
*
|
||||
* @date 2021/6/8 17:25
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
public class YsfBarOrderController extends AbstractPayOrderController {
|
||||
|
||||
|
||||
/**
|
||||
* 统一下单接口
|
||||
* **/
|
||||
@PostMapping("/api/pay/ysfBarOrder")
|
||||
public ApiRes aliBarOrder(){
|
||||
|
||||
//获取参数 & 验证
|
||||
YsfBarOrderRQ bizRQ = getRQByWithMchSign(YsfBarOrderRQ.class);
|
||||
|
||||
// 统一下单接口
|
||||
return unifiedOrder(CS.PAY_WAY_CODE.YSF_BAR, bizRQ);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.payorder.payway;
|
||||
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.model.ApiRes;
|
||||
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.YsfJsapiOrderRQ;
|
||||
import com.jeequan.jeepay.pay.ctrl.payorder.AbstractPayOrderController;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/*
|
||||
* 云闪付 jsapi支付 controller
|
||||
*
|
||||
* @author pangxiaoyu
|
||||
*
|
||||
* @date 2021/6/8 17:25
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
public class YsfJsapiOrderController extends AbstractPayOrderController {
|
||||
|
||||
|
||||
/**
|
||||
* 统一下单接口
|
||||
* **/
|
||||
@PostMapping("/api/pay/ysfJsapiOrder")
|
||||
public ApiRes aliJsapiOrder(){
|
||||
|
||||
//获取参数 & 验证
|
||||
YsfJsapiOrderRQ bizRQ = getRQByWithMchSign(YsfJsapiOrderRQ.class);
|
||||
|
||||
// 统一下单接口
|
||||
return unifiedOrder(CS.PAY_WAY_CODE.YSF_JSAPI, bizRQ);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.qr;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.jeequan.jeepay.core.aop.MethodLog;
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.interfaces.IConfigContextQueryService;
|
||||
import com.jeequan.jeepay.core.interfaces.paychannel.IChannelUserService;
|
||||
import com.jeequan.jeepay.core.model.QRCodeParams;
|
||||
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
|
||||
import com.jeequan.jeepay.core.model.rqrs.ChannelUserIdRQ;
|
||||
import com.jeequan.jeepay.core.utils.JeepayKit;
|
||||
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
|
||||
import com.jeequan.jeepay.core.utils.StringKit;
|
||||
import com.jeequan.jeepay.pay.ctrl.payorder.AbstractPayOrderController;
|
||||
import com.jeequan.jeepay.service.impl.SysConfigService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/*
|
||||
* 商户获取渠道用户ID接口
|
||||
*
|
||||
* @author terrfly
|
||||
*
|
||||
* @date 2021/6/8 17:27
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/channelUserId")
|
||||
public class ChannelUserIdController extends AbstractPayOrderController {
|
||||
|
||||
@Autowired private IConfigContextQueryService configContextQueryService;
|
||||
@Autowired private SysConfigService sysConfigService;
|
||||
|
||||
/** 重定向到微信地址 **/
|
||||
@MethodLog(remark = "获取渠道用户IDAPI")
|
||||
@RequestMapping("/jump")
|
||||
public void jump() throws Exception {
|
||||
|
||||
//获取请求数据
|
||||
ChannelUserIdRQ rq = getRQByWithMchSign(ChannelUserIdRQ.class);
|
||||
|
||||
String ifCode = "AUTO".equalsIgnoreCase(rq.getIfCode()) ? getIfCodeByUA() : rq.getIfCode();
|
||||
|
||||
// 获取接口
|
||||
IChannelUserService channelUserService = SpringBeansUtil.getBean(JeepayKit.getIfCodeOrigin(ifCode) + "ChannelUserService", IChannelUserService.class);
|
||||
|
||||
if(channelUserService == null){
|
||||
throw new BizException("不支持的客户端");
|
||||
}
|
||||
|
||||
if(!StringKit.isAvailableUrl(rq.getRedirectUrl())){
|
||||
throw new BizException("跳转地址有误!");
|
||||
}
|
||||
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("mchNo", rq.getMchNo());
|
||||
jsonObject.put("appId", rq.getAppId());
|
||||
jsonObject.put("extParam", rq.getExtParam());
|
||||
jsonObject.put("ifCode", ifCode);
|
||||
jsonObject.put("redirectUrl", rq.getRedirectUrl());
|
||||
|
||||
//回调地址
|
||||
String callbackUrl = sysConfigService.getDBApplicationConfig().genMchChannelUserIdApiOauth2RedirectUrlEncode(jsonObject);
|
||||
|
||||
//获取商户配置信息
|
||||
MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(rq.getMchNo(), rq.getAppId(),null);
|
||||
String redirectUrl = channelUserService.buildUserRedirectUrl(callbackUrl, mchAppConfigContext).getRedirectUrl();
|
||||
response.sendRedirect(redirectUrl);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** 回调地址 **/
|
||||
@RequestMapping("/oauth2Callback/{aesData}")
|
||||
public void oauth2Callback(@PathVariable("aesData") String aesData) throws Exception {
|
||||
|
||||
JSONObject callbackData = JSON.parseObject(JeepayKit.aesDecode(aesData));
|
||||
|
||||
String mchNo = callbackData.getString("mchNo");
|
||||
String appId = callbackData.getString("appId");
|
||||
String ifCode = callbackData.getString("ifCode");
|
||||
String extParam = callbackData.getString("extParam");
|
||||
String redirectUrl = callbackData.getString("redirectUrl");
|
||||
|
||||
// 获取接口
|
||||
IChannelUserService channelUserService = SpringBeansUtil.getBean(JeepayKit.getIfCodeOrigin(ifCode) + "ChannelUserService", IChannelUserService.class);
|
||||
|
||||
//获取商户配置信息
|
||||
MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(mchNo, appId,null);
|
||||
|
||||
//获取渠道用户ID
|
||||
|
||||
String pageType = null;
|
||||
if(CS.IF_CODE.WXPAY.equals(ifCode)){
|
||||
pageType = QRCodeParams.PAGE_TYPE_WECHAT_H5;
|
||||
}
|
||||
if(CS.IF_CODE.ALIPAY.equals(ifCode)){
|
||||
pageType = QRCodeParams.PAGE_TYPE_ALIPAY_H5;
|
||||
}
|
||||
|
||||
String channelUserId = channelUserService.getChannelUserId(pageType, getReqParamJSON(), mchAppConfigContext, ifCode);
|
||||
|
||||
//同步跳转
|
||||
JSONObject appendParams = new JSONObject();
|
||||
appendParams.put("appId", appId);
|
||||
appendParams.put("channelUserId", channelUserId);
|
||||
appendParams.put("extParam", extParam);
|
||||
|
||||
// Fortify 扫描软件可忽略该问题: 因为所有的redirectUrl 全部来自三方(比如微信)固定格式。
|
||||
response.sendRedirect(StringKit.appendUrlQuery(redirectUrl, appendParams));
|
||||
}
|
||||
|
||||
|
||||
/** 根据UA获取支付接口 */
|
||||
private String getIfCodeByUA() {
|
||||
|
||||
String ua = request.getHeader("User-Agent");
|
||||
|
||||
// 无法识别扫码客户端
|
||||
if (StringUtils.isBlank(ua)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if(ua.contains("Alipay")) {
|
||||
return CS.IF_CODE.ALIPAY; //支付宝服务窗支付
|
||||
}else if(ua.contains("MicroMessenger")) {
|
||||
return CS.IF_CODE.WXPAY;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,102 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.qr;
|
||||
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.model.DBApplicationConfig;
|
||||
import com.jeequan.jeepay.core.utils.JeepayKit;
|
||||
import com.jeequan.jeepay.db.entity.MchStore;
|
||||
import com.jeequan.jeepay.db.entity.PayOrder;
|
||||
import com.jeequan.jeepay.db.entity.SysAdvertConfig;
|
||||
import com.jeequan.jeepay.pay.ctrl.payorder.AbstractPayOrderController;
|
||||
import com.jeequan.jeepay.service.impl.MchStoreService;
|
||||
import com.jeequan.jeepay.service.impl.PayOrderService;
|
||||
import com.jeequan.jeepay.service.impl.SysAdvertConfigService;
|
||||
import com.jeequan.jeepay.service.impl.SysConfigService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 微信点金计划controller
|
||||
*
|
||||
* @author zx
|
||||
*
|
||||
* @date 2022/7/1 17:27
|
||||
*/
|
||||
@Controller
|
||||
@RequestMapping("/api/goldplan")
|
||||
public class WxGoldPlanController extends AbstractPayOrderController {
|
||||
|
||||
@Autowired private PayOrderService payOrderService;
|
||||
@Autowired private MchStoreService mchStoreService;
|
||||
@Autowired private SysConfigService sysConfigService;
|
||||
@Autowired private SysAdvertConfigService advertConfigService;
|
||||
|
||||
/**
|
||||
* 点金计划 -- 商家小票链接地址
|
||||
**/
|
||||
@GetMapping("/custom")
|
||||
public String goldplan(){
|
||||
try {
|
||||
JSONObject paramJSON = getReqParamJSON();
|
||||
logger.info("点金计划微信请求参数:{}", paramJSON.toJSONString());
|
||||
|
||||
String platformMchOrderNo = paramJSON.getString("out_trade_no");
|
||||
String subMchId = paramJSON.getString("sub_mch_id");
|
||||
String checkCode = paramJSON.getString("check_code");
|
||||
|
||||
PayOrder payOrder = payOrderService.selectOneByPlatformMchOrderNo(platformMchOrderNo);
|
||||
String transactionId = payOrder.getPlatformOrderNo();
|
||||
String paySiteUrl = sysConfigService.getDBApplicationConfig().getPaySiteUrl();
|
||||
paySiteUrl = paySiteUrl.endsWith("/") ? paySiteUrl : paySiteUrl + "/";
|
||||
String md5Str = String.format(paySiteUrl + "api/goldplan/custom/?sub_mch_id=%s&out_trade_no=%s&transaction_id=%s", subMchId, platformMchOrderNo, transactionId);
|
||||
String myCheckCode = JeepayKit.md5(md5Str, CharsetUtil.UTF_8);
|
||||
if (!myCheckCode.equals(checkCode)) {
|
||||
logger.info("点金计划微信checkCode={},系统checkCode={}", checkCode, myCheckCode);
|
||||
throw new BizException("checkCode校验失败");
|
||||
}
|
||||
|
||||
MchStore mchStore = mchStoreService.getById(payOrder.getStoreId());
|
||||
|
||||
request.setAttribute("amount", payOrder.getAmount());
|
||||
request.setAttribute("mchShortName", mchStore.getStoreName());
|
||||
request.setAttribute("subject", payOrder.getSubject());
|
||||
|
||||
// 商家小票跳转URL优先级:默认取商户传的returnUrl;当为空时,取平台配置的点金计划支付后广告链接;若平台未配置,则隐藏页面上跳转按钮
|
||||
if (StringUtils.isNotBlank(payOrder.getReturnUrl())) {
|
||||
request.setAttribute("returnUrl", payOrder.getReturnUrl());
|
||||
}else {
|
||||
// 查询平台点金计划支付后广告
|
||||
LambdaQueryWrapper<SysAdvertConfig> wrapper = SysAdvertConfig.gw();
|
||||
wrapper.eq(SysAdvertConfig::getAdvertType, SysAdvertConfig.ADVERT_TYPE_AFTER);
|
||||
wrapper.eq(SysAdvertConfig::getReleaseState, CS.YES);
|
||||
wrapper.eq(SysAdvertConfig::getAppPlace, SysAdvertConfig.APP_PLACE_TOUCH);
|
||||
wrapper.orderByDesc(SysAdvertConfig::getCreatedAt);
|
||||
List<SysAdvertConfig> list = advertConfigService.list(wrapper);
|
||||
if (list.size() > 0) {
|
||||
request.setAttribute("returnUrl", list.get(0).getLinkUrl());
|
||||
}else {
|
||||
request.setAttribute("returnUrl", "");
|
||||
}
|
||||
}
|
||||
|
||||
return "goldplan/custom";
|
||||
|
||||
}catch (Exception e) {
|
||||
logger.error("点金计划解析错误", e);
|
||||
// 静态CDN地址
|
||||
DBApplicationConfig dbApplicationConfig = sysConfigService.getDBApplicationConfig();
|
||||
request.setAttribute("staticCdnHost", dbApplicationConfig.getStaticCdnHost());
|
||||
request.setAttribute("errMsg", "订单解析错误");
|
||||
return "goldplan/error";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.refund;
|
||||
|
||||
import com.jeequan.jeepay.core.ctrls.AbstractCtrl;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.exception.ResponseException;
|
||||
import com.jeequan.jeepay.core.interfaces.IConfigContextQueryService;
|
||||
import com.jeequan.jeepay.core.interfaces.paychannel.IChannelRefundNoticeService;
|
||||
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
|
||||
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
|
||||
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
|
||||
import com.jeequan.jeepay.db.entity.RefundOrder;
|
||||
import com.jeequan.jeepay.pay.service.RefundOrderProcessService;
|
||||
import com.jeequan.jeepay.service.impl.AccountInfoService;
|
||||
import com.jeequan.jeepay.service.impl.RefundOrderService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.MutablePair;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/*
|
||||
* 渠道侧的退款通知入口Controller 【异步回调(doNotify) 】
|
||||
*
|
||||
* @author jmdhappy
|
||||
*
|
||||
* @date 2021/9/25 22:35
|
||||
*/
|
||||
@Slf4j
|
||||
@Controller
|
||||
public class ChannelRefundNoticeController extends AbstractCtrl {
|
||||
|
||||
@Autowired private RefundOrderService refundOrderService;
|
||||
@Autowired private IConfigContextQueryService configContextQueryService;
|
||||
@Autowired private RefundOrderProcessService refundOrderProcessService;
|
||||
@Autowired private AccountInfoService accountInfoService;
|
||||
|
||||
/** 异步回调入口 **/
|
||||
@ResponseBody
|
||||
@RequestMapping(value= {"/api/refund/notify/{ifCode}", "/api/refund/notify/{ifCode}/{refundOrderId}"})
|
||||
public ResponseEntity doNotify(HttpServletRequest request, @PathVariable("ifCode") String ifCode, @PathVariable(value = "refundOrderId", required = false) String urlOrderId){
|
||||
|
||||
String refundOrderId = null;
|
||||
String logPrefix = "进入[" +ifCode+ "]退款回调:urlOrderId:["+ StringUtils.defaultIfEmpty(urlOrderId, "") + "] ";
|
||||
log.info("===== {} =====" , logPrefix);
|
||||
|
||||
try {
|
||||
|
||||
// 参数有误
|
||||
if(StringUtils.isEmpty(ifCode)){
|
||||
return ResponseEntity.badRequest().body("ifCode is empty");
|
||||
}
|
||||
|
||||
//查询退款接口是否存在
|
||||
IChannelRefundNoticeService refundNotifyService = SpringBeansUtil.getBean(ifCode + "ChannelRefundNoticeService", IChannelRefundNoticeService.class);
|
||||
|
||||
// 支付通道接口实现不存在
|
||||
if(refundNotifyService == null){
|
||||
log.error("{}, interface not exists ", logPrefix);
|
||||
return ResponseEntity.badRequest().body("[" + ifCode + "] interface not exists");
|
||||
}
|
||||
|
||||
// 解析订单号 和 请求参数
|
||||
MutablePair<String, Object> mutablePair = refundNotifyService.parseParams(request, urlOrderId, IChannelRefundNoticeService.NoticeTypeEnum.DO_NOTIFY);
|
||||
if(mutablePair == null){ // 解析数据失败, 响应已处理
|
||||
log.error("{}, mutablePair is null ", logPrefix);
|
||||
throw new BizException("解析数据异常!"); //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
|
||||
}
|
||||
|
||||
// 解析到订单号
|
||||
refundOrderId = mutablePair.left;
|
||||
log.info("{}, 解析数据为:refundOrderId:{}, params:{}", logPrefix, refundOrderId, mutablePair.getRight());
|
||||
|
||||
if(StringUtils.isNotEmpty(urlOrderId) && !urlOrderId.equals(refundOrderId)){
|
||||
log.error("{}, 订单号不匹配. urlOrderId={}, refundOrderId={} ", logPrefix, urlOrderId, refundOrderId);
|
||||
throw new BizException("退款单号不匹配!");
|
||||
}
|
||||
|
||||
//获取订单号 和 订单数据
|
||||
RefundOrder refundOrder = refundOrderService.getById(refundOrderId);
|
||||
|
||||
// 订单不存在
|
||||
if(refundOrder == null){
|
||||
log.error("{}, 退款订单不存在. refundOrder={} ", logPrefix, refundOrder);
|
||||
return refundNotifyService.doNotifyOrderNotExists(request);
|
||||
}
|
||||
|
||||
//查询出商户应用的配置信息
|
||||
MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(refundOrder.getMchNo(), refundOrder.getAppId(),refundOrder.getMchExtNo());
|
||||
|
||||
//调起接口的回调判断
|
||||
ChannelRetMsg notifyResult = refundNotifyService.doNotice(request, mutablePair.getRight(), refundOrder, mchAppConfigContext, IChannelRefundNoticeService.NoticeTypeEnum.DO_NOTIFY);
|
||||
|
||||
// 返回null 表明出现异常, 无需处理通知下游等操作。
|
||||
if(notifyResult == null || notifyResult.getChannelState() == null || notifyResult.getResponseEntity() == null){
|
||||
log.error("{}, 处理回调事件异常 notifyResult data error, notifyResult ={} ",logPrefix, notifyResult);
|
||||
throw new BizException("处理回调事件异常!"); //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
|
||||
}
|
||||
//如果是退款成功状态 则直接返回响应参数
|
||||
if(RefundOrder.STATE_SUCCESS == refundOrder.getState()){
|
||||
return notifyResult.getResponseEntity();
|
||||
}
|
||||
// 处理退款订单
|
||||
boolean updateOrderSuccess = refundOrderProcessService.handleRefundOrder4Channel(notifyResult, refundOrder);
|
||||
|
||||
// 更新退款订单 异常
|
||||
if(!updateOrderSuccess){
|
||||
log.error("{}, updateOrderSuccess = {} ",logPrefix, updateOrderSuccess);
|
||||
return refundNotifyService.doNotifyOrderStateUpdateFail(request);
|
||||
}
|
||||
log.info("===== {}, 订单通知完成。 refundOrderId={}, parseState = {} =====", logPrefix, refundOrderId, notifyResult.getChannelState());
|
||||
|
||||
return notifyResult.getResponseEntity();
|
||||
|
||||
} catch (BizException e) {
|
||||
log.error("{}, refundOrderId={}, BizException", logPrefix, refundOrderId, e);
|
||||
return ResponseEntity.badRequest().body(e.getMessage());
|
||||
|
||||
} catch (ResponseException e) {
|
||||
log.error("{}, refundOrderId={}, ResponseException", logPrefix, refundOrderId, e);
|
||||
return e.getResponseEntity();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("{}, refundOrderId={}, 系统异常", logPrefix, refundOrderId, e);
|
||||
return ResponseEntity.badRequest().body(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.refund;
|
||||
|
||||
import com.jeequan.jeepay.core.aop.MethodLog;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.interfaces.IConfigContextQueryService;
|
||||
import com.jeequan.jeepay.core.model.ApiRes;
|
||||
import com.jeequan.jeepay.core.model.rqrs.refund.QueryRefundOrderRQ;
|
||||
import com.jeequan.jeepay.core.model.rqrs.refund.QueryRefundOrderRS;
|
||||
import com.jeequan.jeepay.db.entity.RefundOrder;
|
||||
import com.jeequan.jeepay.pay.ctrl.ApiController;
|
||||
import com.jeequan.jeepay.pay.util.ApiResKit;
|
||||
import com.jeequan.jeepay.service.impl.RefundOrderService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 商户退款单查询controller
|
||||
*
|
||||
* @author terrfly
|
||||
*
|
||||
* @date 2021/6/17 15:20
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
public class QueryRefundOrderController extends ApiController {
|
||||
|
||||
@Autowired private RefundOrderService refundOrderService;
|
||||
@Autowired private IConfigContextQueryService configContextQueryService;
|
||||
|
||||
/**
|
||||
* 查单接口
|
||||
* **/
|
||||
@MethodLog(remark = "查询退款订单API")
|
||||
@RequestMapping("/api/refund/query")
|
||||
public ApiRes queryRefundOrder(){
|
||||
|
||||
//获取参数 & 验签
|
||||
QueryRefundOrderRQ rq = getRQByWithMchSign(QueryRefundOrderRQ.class);
|
||||
|
||||
if(StringUtils.isAllEmpty(rq.getMchRefundNo(), rq.getRefundOrderId())){
|
||||
throw new BizException("mchRefundNo 和 refundOrderId不能同时为空");
|
||||
}
|
||||
|
||||
RefundOrder refundOrder = refundOrderService.queryMchOrder(rq.getMchNo(), rq.getMchRefundNo(), rq.getRefundOrderId());
|
||||
if(refundOrder == null){
|
||||
throw new BizException("订单不存在");
|
||||
}
|
||||
|
||||
QueryRefundOrderRS bizRes = QueryRefundOrderRS.buildByRefundOrder(refundOrder);
|
||||
|
||||
return ApiResKit.okWithSign(bizRes, configContextQueryService.queryMchApp(rq.getMchNo(), rq.getAppId()), rq.getSignType());
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.refund;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import com.jeequan.jeepay.core.aop.MethodLog;
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.entity.MchApp;
|
||||
import com.jeequan.jeepay.core.entity.MchInfo;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.exception.ChannelException;
|
||||
import com.jeequan.jeepay.core.interfaces.IConfigContextQueryService;
|
||||
import com.jeequan.jeepay.core.interfaces.paychannel.IDivisionService;
|
||||
import com.jeequan.jeepay.core.interfaces.paychannel.IRefundService;
|
||||
import com.jeequan.jeepay.core.model.ApiRes;
|
||||
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
|
||||
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
|
||||
import com.jeequan.jeepay.core.model.rqrs.refund.RefundOrderRQ;
|
||||
import com.jeequan.jeepay.core.model.rqrs.refund.RefundOrderRS;
|
||||
import com.jeequan.jeepay.core.utils.JeepayKit;
|
||||
import com.jeequan.jeepay.core.utils.SeqKit;
|
||||
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
|
||||
import com.jeequan.jeepay.core.utils.StringKit;
|
||||
import com.jeequan.jeepay.db.entity.PayOrder;
|
||||
import com.jeequan.jeepay.db.entity.RefundOrder;
|
||||
import com.jeequan.jeepay.pay.ctrl.ApiController;
|
||||
import com.jeequan.jeepay.pay.ctrl.payorder.AbstractRefundOrderController;
|
||||
import com.jeequan.jeepay.pay.service.PayMchNotifyService;
|
||||
import com.jeequan.jeepay.pay.service.PayOrderDivisionRefundOrderCommonService;
|
||||
import com.jeequan.jeepay.pay.util.ApiResKit;
|
||||
import com.jeequan.jeepay.service.impl.PayOrderService;
|
||||
import com.jeequan.jeepay.service.impl.RefundOrderService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/*
|
||||
* 商户发起退款 controller
|
||||
*
|
||||
* @author terrfly
|
||||
*
|
||||
* @date 2021/6/16 15:54
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
public class RefundOrderController extends AbstractRefundOrderController {
|
||||
|
||||
@Autowired private PayOrderService payOrderService;
|
||||
@Autowired private RefundOrderService refundOrderService;
|
||||
@Autowired private IConfigContextQueryService configContextQueryService;
|
||||
@Autowired private PayOrderDivisionRefundOrderCommonService payOrderDivisionRefundOrderCommonService;
|
||||
|
||||
|
||||
/** 申请退款 **/
|
||||
@MethodLog(remark = "发起支付退款API")
|
||||
@PostMapping("/api/refund/refundOrder")
|
||||
public ApiRes refundOrder(){
|
||||
|
||||
//获取参数 & 验签
|
||||
RefundOrderRQ rq = getRQByWithMchSign(RefundOrderRQ.class);
|
||||
|
||||
return this.refund(rq);
|
||||
}
|
||||
|
||||
public ApiRes refund(RefundOrderRQ rq){
|
||||
RefundOrder refundOrder = null;
|
||||
|
||||
try {
|
||||
|
||||
if(StringUtils.isAllEmpty(rq.getMchOrderNo(), rq.getPayOrderId())){
|
||||
throw new BizException("mchOrderNo 和 payOrderId不能同时为空");
|
||||
}
|
||||
|
||||
PayOrder payOrder = payOrderService.queryMchOrder(rq.getMchNo(), rq.getPayOrderId(), rq.getMchOrderNo());
|
||||
if(payOrder == null){
|
||||
throw new BizException("退款订单不存在");
|
||||
}
|
||||
|
||||
if(payOrder.getRefundState() == PayOrder.REFUND_STATE_ALL || payOrder.getRefundAmount() >= payOrder.getAmount()){
|
||||
throw new BizException("订单已全额退款,本次申请失败");
|
||||
}
|
||||
|
||||
if(payOrder.getState() != PayOrder.STATE_SUCCESS && PayOrder.REFUND_STATE_ALL == payOrder.getRefundState()){
|
||||
throw new BizException("当前订单已退款完成或订单状态不正确");
|
||||
}
|
||||
|
||||
if(payOrder.getRefundAmount() + rq.getRefundAmount() > payOrder.getAmount()){
|
||||
throw new BizException("申请金额超出订单可退款余额,请检查退款金额");
|
||||
}
|
||||
|
||||
if(refundOrderService.count(RefundOrder.gw().eq(RefundOrder::getPayOrderId, payOrder.getPayOrderId()).eq(RefundOrder::getState, RefundOrder.STATE_ING)) > 0){
|
||||
throw new BizException("支付订单具有在途退款申请,请稍后再试");
|
||||
}
|
||||
|
||||
//全部退款金额 (退款订单表)
|
||||
Long sumSuccessRefundAmount = refundOrderService.getBaseMapper().sumSuccessRefundAmount(payOrder.getPayOrderId());
|
||||
if(sumSuccessRefundAmount >= payOrder.getAmount()){
|
||||
throw new BizException("退款单已完成全部订单退款,本次申请失败");
|
||||
}
|
||||
|
||||
if(sumSuccessRefundAmount + rq.getRefundAmount() > payOrder.getAmount()){
|
||||
throw new BizException("申请金额超出订单可退款余额,请检查退款金额");
|
||||
}
|
||||
|
||||
String mchNo = rq.getMchNo();
|
||||
String appId = rq.getAppId();
|
||||
|
||||
// 校验退款单号是否重复
|
||||
if(refundOrderService.count(RefundOrder.gw().eq(RefundOrder::getMchNo, mchNo).eq(RefundOrder::getMchRefundNo, rq.getMchRefundNo())) > 0){
|
||||
throw new BizException("商户退款订单号["+rq.getMchRefundNo()+"]已存在");
|
||||
}
|
||||
|
||||
if(StringUtils.isNotEmpty(rq.getNotifyUrl()) && !StringKit.isAvailableUrl(rq.getNotifyUrl())){
|
||||
throw new BizException("异步通知地址协议仅支持http:// 或 https:// !");
|
||||
}
|
||||
|
||||
//获取支付参数 (缓存数据) 和 商户信息
|
||||
MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(mchNo, appId,payOrder.getMchExtNo());
|
||||
if(mchAppConfigContext == null){
|
||||
throw new BizException("获取商户应用信息失败");
|
||||
}
|
||||
|
||||
MchInfo mchInfo = mchAppConfigContext.getMchInfo();
|
||||
MchApp mchApp = mchAppConfigContext.getMchApp();
|
||||
|
||||
// 是否有退款权限
|
||||
if (!mchInfo.getRefundMode().contains(MchInfo.REFUND_MODEL_API)) {
|
||||
throw new BizException("无退款权限!");
|
||||
}
|
||||
|
||||
//获取退款接口
|
||||
IRefundService refundService = SpringBeansUtil.getBean(JeepayKit.getIfCodeOrigin(payOrder.getIfCode()) + "RefundService", IRefundService.class);
|
||||
if(refundService == null){
|
||||
throw new BizException("当前通道不支持退款!");
|
||||
}
|
||||
refundOrder = genRefundOrder(rq, payOrder, mchInfo, mchApp);
|
||||
|
||||
refundService.preCheck(rq,refundOrder,payOrder);
|
||||
|
||||
//退款单入库 退款单状态:生成状态 此时没有和任何上游渠道产生交互。
|
||||
refundOrderService.save(refundOrder);
|
||||
|
||||
IDivisionService divisionService = getDivisionService(payOrder);
|
||||
// 退款业务发生事前 分账。
|
||||
if(divisionService != null && divisionService.isSupport() && !divisionService.divisionRefundIsOrderRefundAfterProc()){
|
||||
// 处理分账回退逻辑
|
||||
payOrderDivisionRefundOrderCommonService.processDivisionRefund(refundOrder, mchAppConfigContext);
|
||||
}
|
||||
// 调起退款接口
|
||||
ChannelRetMsg channelRetMsg = refundService.refund(rq, refundOrder, payOrder, mchAppConfigContext);
|
||||
|
||||
//处理退款单状态
|
||||
this.processChannelMsg(channelRetMsg, refundOrder);
|
||||
|
||||
// 退款业务发生 事后 分账。 明确成功再退分。
|
||||
if(divisionService != null && divisionService.isSupport() && divisionService.divisionRefundIsOrderRefundAfterProc()
|
||||
&& channelRetMsg.getChannelState() != null && channelRetMsg.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_SUCCESS){
|
||||
// 处理分账回退逻辑
|
||||
payOrderDivisionRefundOrderCommonService.processDivisionRefundByAfter(refundOrder);
|
||||
}
|
||||
|
||||
RefundOrderRS bizRes = RefundOrderRS.buildByRefundOrder(refundOrder);
|
||||
|
||||
return ApiResKit.okWithSign(bizRes, configContextQueryService.queryMchApp(rq.getMchNo(), rq.getAppId()), rq.getSignType());
|
||||
|
||||
|
||||
} catch (BizException e) {
|
||||
return ApiRes.customFail(e.getMessage());
|
||||
|
||||
} catch (ChannelException e) {
|
||||
|
||||
//处理上游返回数据
|
||||
this.processChannelMsg(e.getChannelRetMsg(), refundOrder);
|
||||
|
||||
if(e.getChannelRetMsg().getChannelState() == ChannelRetMsg.ChannelState.SYS_ERROR ){
|
||||
return ApiRes.customFail(e.getMessage());
|
||||
}
|
||||
|
||||
RefundOrderRS bizRes = RefundOrderRS.buildByRefundOrder(refundOrder);
|
||||
return ApiResKit.okWithSign(bizRes, configContextQueryService.queryMchApp(rq.getMchNo(), rq.getAppId()), rq.getSignType());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("系统异常:{}", e);
|
||||
return ApiRes.customFail("系统异常");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private IDivisionService getDivisionService(PayOrder payOrder){
|
||||
if(PayOrder.DIVISION_STATE_UNHAPPEN == payOrder.getDivisionMode()){
|
||||
return null;
|
||||
}
|
||||
IDivisionService divisionService = SpringBeansUtil.getBean(JeepayKit.getIfCodeOrigin(payOrder.getIfCode()) + "DivisionService", IDivisionService.class);
|
||||
return divisionService;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.repay;
|
||||
|
||||
import com.jeequan.jeepay.core.ctrls.AbstractCtrl;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.exception.ResponseException;
|
||||
import com.jeequan.jeepay.core.interfaces.paychannel.IRepayNoticeService;
|
||||
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
|
||||
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
|
||||
import com.jeequan.jeepay.db.entity.AccountChangeInfo;
|
||||
import com.jeequan.jeepay.db.entity.AccountFundInfo;
|
||||
import com.jeequan.jeepay.service.impl.AccountChangeInfoService;
|
||||
import com.jeequan.jeepay.service.impl.AccountFundInfoService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.MutablePair;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @author crystal
|
||||
* @date 2024/3/27 10:22
|
||||
*/
|
||||
@Controller
|
||||
@Slf4j
|
||||
public class ChannelRepayNoticeController extends AbstractCtrl {
|
||||
|
||||
@Autowired private AccountFundInfoService accountFundInfoService;
|
||||
|
||||
@ResponseBody
|
||||
@RequestMapping(value= {"/api/channel/payment/notify/{ifCode}", "/api/channel/payment/notify/{ifCode}/{rid}"})
|
||||
public ResponseEntity doNotify(HttpServletRequest request, @PathVariable("ifCode") String ifCode, @PathVariable(value = "rid", required = false) String urlRid){
|
||||
String rid = null;
|
||||
String logPrefix = "进入[" +ifCode+ "]打款回调:urlRid:["+ StringUtils.defaultIfEmpty(urlRid, "") + "] ";
|
||||
log.info("打款回调:{}" , logPrefix);
|
||||
try {
|
||||
// 参数有误
|
||||
if(StringUtils.isEmpty(ifCode)){
|
||||
return ResponseEntity.badRequest().body("ifCode is empty");
|
||||
}
|
||||
//查询通知接口是否存在
|
||||
IRepayNoticeService noticeService = SpringBeansUtil.getBean(ifCode + "RepayNoticeService", IRepayNoticeService.class);
|
||||
// 提现通知接口实现不存在
|
||||
if(noticeService == null){
|
||||
log.error("{}, interface not exists ", logPrefix);
|
||||
return ResponseEntity.badRequest().body("[" + ifCode + "] interface not exists");
|
||||
}
|
||||
// 解析提现单号 和 请求参数
|
||||
MutablePair<String, Object> mutablePair = noticeService.parseParams(request, urlRid);
|
||||
if(mutablePair == null){
|
||||
log.error("{}, mutablePair is null ", logPrefix);
|
||||
throw new BizException("解析数据异常!");
|
||||
}
|
||||
rid = mutablePair.left;
|
||||
AccountFundInfo fundInfo = accountFundInfoService.getById(rid);
|
||||
if(fundInfo == null){
|
||||
log.error("{}, 数据不存在. rid={} ", logPrefix, rid);
|
||||
return ResponseEntity.badRequest().body("data record not exists");
|
||||
}
|
||||
//解析到提现单号
|
||||
log.info("{}, 解析数据为:rid:{}, params:{}", logPrefix, rid, mutablePair.getRight());
|
||||
//调起接口的回调判断
|
||||
ChannelRetMsg retMsg = noticeService.doNotice(request,mutablePair.left, mutablePair.getRight());
|
||||
// 返回null 表明出现异常, 无需处理通知下游等操作。
|
||||
if(retMsg == null || retMsg.getChannelState() == null || retMsg.getResponseEntity() == null){
|
||||
log.error("{}, 处理回调事件异常 notifyResult data error, notifyResult ={} ",logPrefix, retMsg);
|
||||
//需要实现类自行抛出ResponseException, 不应该在这抛此异常。
|
||||
throw new BizException("处理回调事件异常!");
|
||||
}
|
||||
|
||||
boolean updateSuccess = true; //默认更新成功
|
||||
// 提现单是 【提现中状态】
|
||||
if(fundInfo.getState() == AccountFundInfo.PAYMENT) {
|
||||
// 渠道提现单号
|
||||
fundInfo.setChannelOrderId(retMsg.getChannelOrderId());
|
||||
// 明确成功
|
||||
if(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS == retMsg.getChannelState()) {
|
||||
updateSuccess = accountFundInfoService.updateIng2Success(rid, retMsg.getChannelOrderId());
|
||||
//明确失败
|
||||
}else if(ChannelRetMsg.ChannelState.CONFIRM_FAIL == retMsg.getChannelState()) {
|
||||
updateSuccess = accountFundInfoService.updateIng2Fail(rid, retMsg.getChannelErrMsg());
|
||||
}
|
||||
else if(ChannelRetMsg.ChannelState.WAITING == retMsg.getChannelState()) {
|
||||
updateSuccess = accountFundInfoService.updateChannelRemark(rid, retMsg.getChannelErrMsg());
|
||||
}
|
||||
}
|
||||
// 更新提现单 异常
|
||||
if(!updateSuccess){
|
||||
log.error("{}, updateOrderSuccess = {} ",logPrefix, updateSuccess);
|
||||
return ResponseEntity.badRequest().body("update error");
|
||||
}
|
||||
log.info("{}, 打款通知完成。 rid={}, parseState = {} =====", logPrefix, rid, retMsg.getChannelState());
|
||||
return retMsg.getResponseEntity();
|
||||
} catch (BizException e) {
|
||||
log.error("{}, rid={}, BizException", logPrefix, rid, e);
|
||||
return ResponseEntity.badRequest().body(e.getMessage());
|
||||
} catch (ResponseException e) {
|
||||
log.error("{}, rid={}, ResponseException", logPrefix, rid, e);
|
||||
return e.getResponseEntity();
|
||||
} catch (Exception e) {
|
||||
log.error("{}, rid={}, 系统异常", logPrefix, rid, e);
|
||||
return ResponseEntity.badRequest().body("error");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.scanimg;
|
||||
|
||||
import com.jeequan.jeepay.core.model.QRCodeParams;
|
||||
import com.jeequan.jeepay.core.utils.AmountUtil;
|
||||
import com.jeequan.jeepay.core.utils.JeepayKit;
|
||||
import com.jeequan.jeepay.core.utils.SeqKit;
|
||||
import com.jeequan.jeepay.db.entity.CacheTk;
|
||||
import com.jeequan.jeepay.pay.ctrl.payorder.AbstractPayOrderController;
|
||||
import com.jeequan.jeepay.pay.util.CodeImgUtil;
|
||||
import com.jeequan.jeepay.service.impl.CacheTkService;
|
||||
import com.jeequan.jeepay.service.impl.SysConfigService;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/*
|
||||
* jeepay 扫描图片生成器
|
||||
*
|
||||
* @author terrfly
|
||||
*
|
||||
* @date 2021/6/8 17:28
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/scan")
|
||||
public class ScanImgController extends AbstractPayOrderController {
|
||||
|
||||
@Resource
|
||||
private SysConfigService sysConfigService;
|
||||
@Resource
|
||||
private CacheTkService cacheTkService;
|
||||
|
||||
/** 返回 图片地址信息 **/
|
||||
@RequestMapping("/imgs/{aesStr}.png")
|
||||
public void qrImgs(@PathVariable("aesStr") String aesStr) throws Exception {
|
||||
if(aesStr.startsWith(SeqKit.TOKEN_PREFIX)){
|
||||
CacheTk cacheTk = cacheTkService.getById(aesStr);
|
||||
if(cacheTk.getType() == QRCodeParams.TYPE_ROUTE){
|
||||
aesStr = sysConfigService.getDBApplicationConfig().getPaySiteUrl() + "/pages/route/index?"+JeepayKit.TOKEN_KEY+"="+cacheTk.getTk();
|
||||
}else{
|
||||
String extParams = "&price="+ AmountUtil.convertCent2Dollar(cacheTk.getAmount()) + "&order_no="+ cacheTk.getSourceId() + "&env=scan";
|
||||
aesStr = sysConfigService.getDBApplicationConfig().getPaySiteUrl() + "/pages/payment/index?"+JeepayKit.TOKEN_KEY+"="+cacheTk.getTk() + extParams;
|
||||
}
|
||||
}else{
|
||||
aesStr = JeepayKit.aesDecode(aesStr);
|
||||
}
|
||||
int width = getValIntegerDefault("width", 200);
|
||||
int height = getValIntegerDefault("height", 200);
|
||||
CodeImgUtil.writeQrCode(response.getOutputStream(), aesStr, width, height);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,677 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.test;
|
||||
|
||||
import cn.hutool.core.date.DatePattern;
|
||||
import cn.hutool.core.date.DateTime;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.img.ImgUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.lang.UUID;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alipay.api.AlipayClient;
|
||||
import com.alipay.api.AlipayConfig;
|
||||
import com.alipay.api.DefaultAlipayClient;
|
||||
import com.alipay.api.domain.*;
|
||||
import com.alipay.api.request.*;
|
||||
import com.alipay.api.response.*;
|
||||
import com.jeequan.jeepay.components.mq.model.PayOrderDivisionMQ;
|
||||
import com.jeequan.jeepay.components.mq.model.PushWxMpMsgMQ;
|
||||
import com.jeequan.jeepay.components.mq.model.ZftSettOrderMQ;
|
||||
import com.jeequan.jeepay.components.mq.vender.IMQSender;
|
||||
import com.jeequan.jeepay.components.oss.ctrl.OssFileController;
|
||||
import com.jeequan.jeepay.components.oss.model.MockMultipartFile;
|
||||
import com.jeequan.jeepay.components.oss.model.OssFileConfig;
|
||||
import com.jeequan.jeepay.converter.MchInfoConverter;
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.interfaces.IConfigContextQueryService;
|
||||
import com.jeequan.jeepay.core.interfaces.paychannel.IPayOrderQueryService;
|
||||
import com.jeequan.jeepay.core.interfaces.paychannel.ITransferService;
|
||||
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
|
||||
import com.jeequan.jeepay.core.model.openapi.biz.transfer.TransferOrderParam;
|
||||
import com.jeequan.jeepay.core.model.params.alipay.AlipayIsvParams;
|
||||
import com.jeequan.jeepay.core.model.params.lklspay.LklspayIsvParams;
|
||||
import com.jeequan.jeepay.core.model.params.yspay.YspayIsvParams;
|
||||
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
|
||||
import com.jeequan.jeepay.core.service.ISysConfigService;
|
||||
import com.jeequan.jeepay.core.utils.SeqKit;
|
||||
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
|
||||
import com.jeequan.jeepay.db.entity.*;
|
||||
import com.jeequan.jeepay.pay.service.PayOrderDivisionProcessService;
|
||||
import com.jeequan.jeepay.pay.service.StatisticsService;
|
||||
import com.jeequan.jeepay.pay.service.payorderpush.impl.PushWxMpTemplateMsgService;
|
||||
import com.jeequan.jeepay.service.impl.*;
|
||||
import com.jeequan.jeepay.thirdparty.channel.lklspay.LklspayKit;
|
||||
import com.jeequan.jeepay.thirdparty.channel.lklspay.model.LklPayMethod;
|
||||
import com.jeequan.jeepay.thirdparty.channel.yspay.model.ReqMethod;
|
||||
import com.jeequan.jeepay.thirdparty.channel.yspay.utils.YspayKit;
|
||||
import com.jeequan.jeepay.thirdparty.device.speaker.lklsSpeaker.LklsSpeakerService;
|
||||
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.Image;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @author crystal
|
||||
* @date 2023/12/25 17:12
|
||||
*/
|
||||
@RequestMapping(value = "test")
|
||||
@RestController
|
||||
@Slf4j
|
||||
public class TestController {
|
||||
|
||||
@Autowired
|
||||
private ConfigContextQueryService queryService;
|
||||
|
||||
@Autowired private PayOrderDivisionProcessService payOrderDivisionProcessService;
|
||||
|
||||
@Autowired
|
||||
private TransferOrderService transferOrderService;
|
||||
|
||||
@Autowired
|
||||
private StatisticsService statisticsService;
|
||||
|
||||
@Autowired
|
||||
private MchStoreDeviceService mchStoreDeviceService;
|
||||
|
||||
@Autowired
|
||||
private PushWxMpTemplateMsgService pushWxMpTemplateMsgService;
|
||||
|
||||
@Autowired
|
||||
private PayOrderService payOrderService;
|
||||
|
||||
@Autowired private IConfigContextQueryService configContextQueryService;
|
||||
|
||||
@Autowired
|
||||
private MchApplymentService mchApplymentService;
|
||||
|
||||
@Autowired
|
||||
private LklsSpeakerService lklsSpeakerService;
|
||||
|
||||
@Autowired
|
||||
private MchInfoConverter mchInfoConverter;
|
||||
|
||||
@Autowired
|
||||
private DeviceProvideConfigService deviceProvideConfigService;
|
||||
|
||||
@Autowired
|
||||
private OssFileController ossFileController;
|
||||
|
||||
@Autowired private IMQSender mqSender;
|
||||
|
||||
|
||||
@RequestMapping("/lkl/bind")
|
||||
public void bind(){
|
||||
JSONObject request = new JSONObject();
|
||||
request.put("ver","1.0.0");
|
||||
request.put("reqId", SeqKit.genMhoOrderId());
|
||||
request.put("timestamp", DateUtil.format(new DateTime(), DatePattern.PURE_DATETIME_PATTERN));
|
||||
JSONObject reqData = new JSONObject();
|
||||
reqData.put("merId","8225210899900DS");
|
||||
reqData.put("termId","D4039820");
|
||||
reqData.put("sn","QR939200003565");
|
||||
reqData.put("businessType","1");
|
||||
reqData.put("shopName","测试门店");
|
||||
reqData.put("source","LOAP");
|
||||
reqData.put("merOrgNo","967080");
|
||||
request.put("reqData",reqData);
|
||||
String isvNo = "V1698312342";
|
||||
String ifCode = CS.IF_CODE.LKLSPAY;
|
||||
LklspayIsvParams isvParams = (LklspayIsvParams)queryService.queryIsvParams(isvNo, ifCode);
|
||||
JSONObject jsonObject = LklspayKit.reqApi(LklPayMethod.Api.DEVICE_SPEAKER_BOX_ACTIVATE_URL, isvParams, JSON.toJSONString(request));
|
||||
log.info("接口响应参数:{}",jsonObject);
|
||||
}
|
||||
|
||||
@RequestMapping("/lkl/unBind")
|
||||
public void unBind(){
|
||||
JSONObject request = new JSONObject();
|
||||
request.put("ver","1.0.0");
|
||||
request.put("reqId", SeqKit.genMhoOrderId());
|
||||
request.put("timestamp", DateUtil.format(new DateTime(), DatePattern.PURE_DATETIME_PATTERN));
|
||||
JSONObject reqData = new JSONObject();
|
||||
reqData.put("merId","8225210899900DS");
|
||||
reqData.put("termId","D4039820");
|
||||
reqData.put("sn","QR939200003565");
|
||||
reqData.put("source","KSAAS");
|
||||
request.put("reqData",reqData);
|
||||
String isvNo = "V1698312342";
|
||||
String ifCode = CS.IF_CODE.LKLSPAY;
|
||||
LklspayIsvParams isvParams = (LklspayIsvParams)queryService.queryIsvParams(isvNo, ifCode);
|
||||
JSONObject jsonObject = LklspayKit.reqApi(LklPayMethod.Api.DEVICE_SPEAKER_BOX_DEACTIVATE_URL, isvParams, JSON.toJSONString(request));
|
||||
log.info("接口响应参数:{}",jsonObject);
|
||||
}
|
||||
|
||||
@RequestMapping("/lkl/queryStatus")
|
||||
public void queryStatus(){
|
||||
JSONObject request = new JSONObject();
|
||||
request.put("ver","1.0.0");
|
||||
request.put("reqId", SeqKit.genMhoOrderId());
|
||||
request.put("timestamp", DateUtil.format(new DateTime(), DatePattern.PURE_DATETIME_PATTERN));
|
||||
JSONObject reqData = new JSONObject();
|
||||
reqData.put("merId","8225210899900DS");
|
||||
reqData.put("termId","D4039820");
|
||||
reqData.put("sn","QR939200003565");
|
||||
request.put("reqData",reqData);
|
||||
String isvNo = "V1698312342";
|
||||
String ifCode = CS.IF_CODE.LKLSPAY;
|
||||
LklspayIsvParams isvParams = (LklspayIsvParams)queryService.queryIsvParams(isvNo, ifCode);
|
||||
JSONObject jsonObject = LklspayKit.reqApi(LklPayMethod.Api.DEVICE_SPEAKER_BOX_ACTIVATE_STATUS_QUERY_URL, isvParams, JSON.toJSONString(request));
|
||||
log.info("接口响应参数:{}",jsonObject);
|
||||
}
|
||||
|
||||
@RequestMapping("/lkl/pushQrcode")
|
||||
public void pushQrCode(){
|
||||
JSONObject request = new JSONObject();
|
||||
request.put("ver","1.0.0");
|
||||
request.put("reqId", SeqKit.genMhoOrderId());
|
||||
request.put("timestamp", DateUtil.format(new DateTime(), DatePattern.PURE_DATETIME_PATTERN));
|
||||
JSONObject reqData = new JSONObject();
|
||||
reqData.put("merId","8225210899900DS");
|
||||
reqData.put("termId","D4039820");
|
||||
reqData.put("deviceSn","QR939200003565");
|
||||
reqData.put("qrcode","https://www.baidu.com");
|
||||
reqData.put("expireTime","60");
|
||||
reqData.put("ts",System.currentTimeMillis() + "");
|
||||
reqData.put("nonce",SeqKit.genTransferId());
|
||||
request.put("reqData",reqData);
|
||||
String isvNo = "V1698312342";
|
||||
String ifCode = CS.IF_CODE.LKLSPAY;
|
||||
LklspayIsvParams isvParams = (LklspayIsvParams)queryService.queryIsvParams(isvNo, ifCode);
|
||||
JSONObject jsonObject = LklspayKit.reqApi(LklPayMethod.Api.DEVICE_SPEAKER_BOX_PUSH_QRCODE_URL, isvParams, JSON.toJSONString(request));
|
||||
log.info("接口响应参数:{}",jsonObject);
|
||||
}
|
||||
|
||||
@RequestMapping("/lkl/getActiveSn")
|
||||
public void getActiveSn(){
|
||||
JSONObject request = new JSONObject();
|
||||
request.put("ver","1.0.0");
|
||||
request.put("reqId", SeqKit.genMhoOrderId());
|
||||
request.put("timestamp", DateUtil.format(new DateTime(), DatePattern.PURE_DATETIME_PATTERN));
|
||||
JSONObject reqData = new JSONObject();
|
||||
reqData.put("merId","8225210899900DS");
|
||||
reqData.put("termId","D4039820");
|
||||
reqData.put("source","TKSAAS");
|
||||
reqData.put("ts",System.currentTimeMillis() + "");
|
||||
reqData.put("nonce",SeqKit.genTransferId());
|
||||
request.put("reqData",reqData);
|
||||
String isvNo = "V1698312342";
|
||||
String ifCode = CS.IF_CODE.LKLSPAY;
|
||||
LklspayIsvParams isvParams = (LklspayIsvParams)queryService.queryIsvParams(isvNo, ifCode);
|
||||
JSONObject jsonObject = LklspayKit.reqApi(LklPayMethod.Api.DEVICE_SPEAKER_BOX_QUERY_SN_URL, isvParams, JSON.toJSONString(request));
|
||||
log.info("接口响应参数:{}",jsonObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分账test
|
||||
*/
|
||||
@RequestMapping("/division/{payOrderId}")
|
||||
public void divisionTest(@PathVariable("payOrderId") String payOrderId){
|
||||
payOrderDivisionProcessService.processPayOrderDivision(payOrderId,(byte)1,null,false,false);
|
||||
}
|
||||
|
||||
@RequestMapping("/state/count")
|
||||
public void stateCount(){
|
||||
String begin = "2024-01-29 00:00:00";
|
||||
String end = "2024-01-29 23:59:59";
|
||||
DateTime startDate = DateUtil.parse(begin, DatePattern.NORM_DATETIME_PATTERN);
|
||||
DateTime endDate = DateUtil.parse(end, DatePattern.NORM_DATETIME_PATTERN);
|
||||
statisticsService.doStats(startDate,endDate);
|
||||
}
|
||||
|
||||
|
||||
@RequestMapping("/transfer/{transferId}")
|
||||
public String transfer(@PathVariable("transferId") String transferId){
|
||||
ITransferService transferService = SpringBeansUtil.getBean("sybpayTransferService", ITransferService.class);
|
||||
if(transferService == null){
|
||||
throw new BizException("无此转账通道接口");
|
||||
}
|
||||
TransferOrderParam rq = new TransferOrderParam();
|
||||
TransferOrderEntity transferOrder = transferOrderService.getById(transferId);
|
||||
transferService.preCheck(rq,transferOrder);
|
||||
try {
|
||||
ChannelRetMsg transfer = transferService.transfer(rq, transferOrder);
|
||||
return JSON.toJSONString(transfer);
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
return "错误请求";
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping("/device/lkl/{startSn}/{endSn}")
|
||||
public String deviceInto(@PathVariable("startSn") String startSn,@PathVariable("endSn") String endSn){
|
||||
Long start = Long.valueOf(startSn.replace("QR", ""));
|
||||
Long end = Long.valueOf(endSn.replace("QR", ""));
|
||||
String batch_id = DateUtil.format(new DateTime(),DatePattern.PURE_DATE_PATTERN);
|
||||
List<MchStoreDevice> list = new ArrayList<>();
|
||||
for (long i = start; i <= end ; i++) {
|
||||
MchStoreDevice mchStoreDevice = new MchStoreDevice();
|
||||
mchStoreDevice.setDeviceName(RandomUtil.randomNumbers(4));
|
||||
mchStoreDevice.setBatchId(batch_id);
|
||||
mchStoreDevice.setDeviceType((byte)1);
|
||||
mchStoreDevice.setProvider("lkls");
|
||||
mchStoreDevice.setDeviceNo("QR"+ i);
|
||||
JSONObject deviceParams = new JSONObject();
|
||||
deviceParams.put("deviceNo","QR" + i);
|
||||
mchStoreDevice.setDeviceParams(deviceParams.toJSONString());
|
||||
list.add(mchStoreDevice);
|
||||
}
|
||||
mchStoreDeviceService.saveBatch(list);
|
||||
return "SUCCESS";
|
||||
}
|
||||
|
||||
|
||||
@RequestMapping("/send/order/msg/{orderNo}")
|
||||
public String sendOrderMsg(@PathVariable("orderNo") String orderNo){
|
||||
pushWxMpTemplateMsgService.send(orderNo, PushWxMpMsgMQ.ROUTE_NOTICE);
|
||||
return "SUCCESS";
|
||||
}
|
||||
|
||||
@RequestMapping("/send/zft/msg/{settleNo}")
|
||||
public String sendZftSettleMsg(@PathVariable("settleNo") String settleNo){
|
||||
mqSender.send(ZftSettOrderMQ.build(settleNo));
|
||||
return "SUCCESS";
|
||||
}
|
||||
|
||||
@RequestMapping("/send/division/msg/{payOrderId}")
|
||||
public String sendDivisionMsg(@PathVariable("payOrderId") String payOrderId){
|
||||
mqSender.send(PayOrderDivisionMQ.build(payOrderId, CS.YES, null, false, false), 3); //xx s后执行
|
||||
return "SUCCESS";
|
||||
}
|
||||
|
||||
|
||||
@RequestMapping("/statistics")
|
||||
public String statistics(){
|
||||
DateTime now = DateUtil.date();
|
||||
DateTime yesterday = DateUtil.offsetDay(now, -1);
|
||||
|
||||
Date beginDate = DateUtil.beginOfDay(yesterday);
|
||||
Date endDate = DateUtil.endOfDay(yesterday);
|
||||
log.info("【处理数据统计任务】统计日期: {}", DateUtil.formatDate(beginDate));
|
||||
statisticsService.doStats(beginDate, endDate);
|
||||
return "SUCCESS";
|
||||
}
|
||||
|
||||
@RequestMapping("/genTk")
|
||||
public String genTk(){
|
||||
ISysConfigService sysService = SpringBeansUtil.getBean("sysConfigService", ISysConfigService.class);
|
||||
return sysService.genQrToken((byte)1,"123123",1L);
|
||||
}
|
||||
|
||||
@RequestMapping("/tradeQuery/{orderId}")
|
||||
public String tradeQuery(@PathVariable("orderId") String payOrderId){
|
||||
IPayOrderQueryService queryService = SpringBeansUtil.getBean("yspayPayOrderQueryService", IPayOrderQueryService.class);
|
||||
if(queryService == null){
|
||||
throw new BizException("无此支付通道接口");
|
||||
}
|
||||
PayOrder payOrder = payOrderService.getById(payOrderId);
|
||||
//查询出商户应用的配置信息
|
||||
MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(payOrder.getMchNo(), payOrder.getAppId(),payOrder.getMchExtNo());
|
||||
try {
|
||||
ChannelRetMsg query = queryService.query(payOrder,mchAppConfigContext);
|
||||
return JSON.toJSONString(query);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "error";
|
||||
}
|
||||
|
||||
@RequestMapping("/div/query/{orderId}")
|
||||
public String divQuery(@PathVariable("orderId") String payOrderId){
|
||||
PayOrder payOrder = payOrderService.getById(payOrderId);
|
||||
JSONObject bizData = new JSONObject();
|
||||
bizData.put("mercId",payOrder.getChannelMchNo());
|
||||
bizData.put("orderId",payOrderId);
|
||||
MchApplyment applyment = mchApplymentService.getById(payOrder.getMchExtNo());
|
||||
YspayIsvParams yspayIsvParams = configContextQueryService.queryIsvParams(mchInfoConverter.toModel(applyment));
|
||||
try {
|
||||
JSONObject result = YspayKit.reqV3(ReqMethod.Method.QUERY_D0_DIVISION_SETTLE,yspayIsvParams, bizData);
|
||||
log.info("返回结果:{}",result);
|
||||
return result.toJSONString();
|
||||
}catch (BizException e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "ERROR";
|
||||
}
|
||||
|
||||
@RequestMapping("/get/box/market/{sn}")
|
||||
public String getBoxMarket(@PathVariable("sn") String sn){
|
||||
MchStoreDevice device = mchStoreDeviceService.getDeviceInfo(sn, (byte) 1, "lkls");
|
||||
DeviceProvideConfig provideConfig = deviceProvideConfigService.getById(device.getConfigId());
|
||||
JSONObject deviceParams = new JSONObject();
|
||||
deviceParams.put("deviceParams", device.getDeviceParams());
|
||||
deviceParams.put("bizConfigParams", device.getBizConfigParams());
|
||||
deviceParams.put("providerParams", provideConfig.getProviderParams());
|
||||
JSONObject resp = lklsSpeakerService.getMarketAreaAdvert(deviceParams);
|
||||
return resp.toString();
|
||||
}
|
||||
|
||||
@RequestMapping("/set/box/market/{sn}")
|
||||
public String setBoxMarket(@PathVariable("sn") String sn){
|
||||
MchStoreDevice device = mchStoreDeviceService.getDeviceInfo(sn, (byte) 1, "lkls");
|
||||
DeviceProvideConfig provideConfig = deviceProvideConfigService.getById(device.getConfigId());
|
||||
JSONObject deviceParams = new JSONObject();
|
||||
deviceParams.put("deviceParams", device.getDeviceParams());
|
||||
deviceParams.put("bizConfigParams", device.getBizConfigParams());
|
||||
deviceParams.put("providerParams", provideConfig.getProviderParams());
|
||||
JSONObject reqData = new JSONObject();
|
||||
reqData.put("translsNo",SeqKit.genFlowNo("F"));
|
||||
reqData.put("devSn",device.getDeviceNo());
|
||||
|
||||
|
||||
ArrayList markPicVos = new ArrayList();
|
||||
String url1 = "https://syb-jq-public.oss-cn-hangzhou.aliyuncs.com/notice/069507a2-e4a0-4a64-9122-0f40727aa60d.jpg";
|
||||
String url2 = "https://syb-jq-public.oss-cn-hangzhou.aliyuncs.com/notice/c55f884e-ad79-4daf-b710-6edef4c658b3.jpg";
|
||||
JSONObject item1 = new JSONObject();
|
||||
item1.put("displayTime",5);
|
||||
item1.put("enable",1);
|
||||
item1.put("picName",url1);
|
||||
item1.put("picSize",71693L);
|
||||
item1.put("picWidth",800);
|
||||
item1.put("picHeight",960);
|
||||
|
||||
JSONObject item2 = new JSONObject();
|
||||
item2.put("displayTime",5);
|
||||
item2.put("enable",1);
|
||||
item2.put("picName",url2);
|
||||
item2.put("picSize",87993L);
|
||||
item2.put("picWidth",800);
|
||||
item2.put("picHeight",960);
|
||||
markPicVos.add(item1);
|
||||
markPicVos.add(item2);
|
||||
reqData.put("markPicVos",markPicVos);
|
||||
JSONObject result = lklsSpeakerService.setMarketAreaAdvert(deviceParams,reqData);
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置跑马灯
|
||||
* @param sn
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping("/set/box/marquee/{sn}")
|
||||
public String setBoxMarquee(@PathVariable("sn") String sn){
|
||||
MchStoreDevice device = mchStoreDeviceService.getDeviceInfo(sn, (byte) 1, "lkls");
|
||||
DeviceProvideConfig provideConfig = deviceProvideConfigService.getById(device.getConfigId());
|
||||
JSONObject deviceParams = new JSONObject();
|
||||
deviceParams.put("deviceParams", device.getDeviceParams());
|
||||
deviceParams.put("bizConfigParams", device.getBizConfigParams());
|
||||
deviceParams.put("providerParams", provideConfig.getProviderParams());
|
||||
JSONObject bizData = new JSONObject();
|
||||
bizData.put("translsNo",SeqKit.genFlowNo("F"));
|
||||
bizData.put("devSn",device.getDeviceNo());
|
||||
JSONObject marqueeVo = new JSONObject();
|
||||
marqueeVo.put("inputText","快来玩啊小哥哥");
|
||||
marqueeVo.put("fontSize",20);
|
||||
marqueeVo.put("fontColor","#FFFFFF");
|
||||
marqueeVo.put("bgColor","#409EFF");
|
||||
marqueeVo.put("displayAreX",0);
|
||||
marqueeVo.put("displayAreY",300);
|
||||
marqueeVo.put("picWidth",840);
|
||||
marqueeVo.put("picHeight",40);
|
||||
bizData.put("marqueeVo",marqueeVo);
|
||||
JSONObject result = lklsSpeakerService.setMarqueeInfo(deviceParams,bizData);
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@RequestMapping("/set/split/image")
|
||||
public String setBoxMarquee(){
|
||||
// 源图片路径
|
||||
String sourceImagePath = "D:\\pic\\pic1.jpg";
|
||||
// 目标图片路径
|
||||
String targetImagePath = "D:\\pic\\223.jpg";
|
||||
// 目标宽度
|
||||
int targetWidth = 800;
|
||||
// 目标高度
|
||||
int targetHeight = 960;
|
||||
// 读取源图片
|
||||
BufferedImage image = ImgUtil.read(FileUtil.file(sourceImagePath));
|
||||
// 缩放图片
|
||||
Image scaledImage = ImgUtil.scale(image, targetWidth, targetHeight);
|
||||
// 将Image对象转换为BufferedImage
|
||||
BufferedImage bufferedImage = ImgUtil.castToBufferedImage(scaledImage,ImgUtil.IMAGE_TYPE_JPG);
|
||||
// 将BufferedImage转换为字节数组
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
try {
|
||||
ImageIO.write(bufferedImage, "jpg", os);
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(os.toByteArray());
|
||||
String url = ossFileController.singleFileUpload(new MockMultipartFile(UUID.fastUUID() + ".jpg", 1L, bis), OssFileConfig.BIZ_TYPE.MATERIAL);
|
||||
return url;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@RequestMapping("/set/aliapy/plym")
|
||||
public String alipayPlym(){
|
||||
String domain = "https://openapi.alipay.com/gateway.do";
|
||||
String path = "D:\\crystal\\cert\\2021004143669573\\";
|
||||
String alipayPublicCert = path + "alipayCertPublicKey_RSA2.crt";
|
||||
String alipayRootCert = path +"alipayRootCert.crt";
|
||||
String appPublicCert = path +"appCertPublicKey_2021004143669573.crt";
|
||||
String appid = "2021004143669573";
|
||||
String prikey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCPENaSv1YzGidYZF5wGbmwShV6SjwFgq3WSIgIFa9Ctaq/x4BmBFtf6pwAWnkYyVzRfUonh+leWD3ufT2fB6zRZkEPLY9zxUGDLCXR6r8+RhIZ0V9jKaVo2VP+j4XFn68mgUu07H1G4P8DsSper2DJ9FwDlxw96Vy8yOnvu31XYkEfh150PuU7PsiQlKHmskhU0+6aQnmmXZU6rPT9HHK8JDofUkotG5lRpe3sTk7tt5jK+PeXxAOK5SKImuO3kywsm5lUwzKSNEZfM6kVaHzsUojLL1FH1T7pACwuu7ot35GjopB8XhBza515zbOXiwzmOa0HFDXWVkvMGih3Ou2VAgMBAAECggEAP0GRrkbdZTDuLp4KR4SIXtMS1gRWr5UNKc7eHrz/xf4vJiaetfy9Lha8rUdTfcsiuBmunPXyEF3x3C4R/evV1KxincRmRij0g3wUqNEjh/RRcorpox253jchdU+eNTgaqGrIv5FE/v7ioo5GDqF6qTDbMlcxDIrPD0XCcdYzYFwOvx9d7DNymiM2XqvDww9KRs3H0XQvRMBY3lOYG1Bj9nEHaN//lVVsY0470e2wEJUKfofo8GuL+EcarJJ9C5OsKKYfwOjqNopB4RtFEI197qDVtaGtG5F4gtx/bu+2lLcULAMMpf59r3S7pVd5koQhSIDpxdQNi4eP5q+mUr6UIQKBgQDJKnkP+ojvILxaN+DqUE7TFjYCYIndJV0MMndoZF7yxv8snakhXIwbbvlfn/cNlAqtcQSM4BFtwv/A2mY0y31dO/GuymXB+Cm8vehx5MCyX7ioESFbGfIzmvNXah1fh5EgEvhM6zdqFWdKG2az3RX6FCUa+Hh+1FDCbFVEjibM/QKBgQC2EBa3eDSkbnCUp4Fh9I5UTLCce3xe5p74KlDBhEQecV42wYyIA2mROg0wAVJjMXfidW+DkP0sDw7aAG2LzdsPhub+t+kGfEOxmDOicY7q5EBKxep+OJJW5kmp3Qk9QM8MvnGp6BLEGt7Ln+RhV4MzqXvRZ0PTNivZM4eIsJdSeQKBgQCpbJghmQQTQiLgaQeI0LJSnfZG8j+qfXlseGSRC1fcaTUownBbpLJruH9WxkcwrGqBwRnv/BcxtnXoWwiRloCzMXVPnS8uum+MgvVuVxbkJDJFeof5TlTqmaoxCTJL7sWPmep9/ezh77zacVGCUsXbpXPKz1HMn8yUsuDtqDuNkQKBgFvbnjqPcbAM8Mxv/z2XEEDEOM+bFlMjdM2tO/WHcH8Tn/9shCUT462cZo7z+8eEbAJcUhlFjBcwucOQN7Uz9fTNkbquu+nDxLvUGSk6tmsZIr8XQIrSrDMgGcaUeVboXg8F/39EUkOTcxm14JqkdtQYDRrISVZFnLYH47g5sP+xAoGAD6EP/TyOHabSIxWqc/RbTs9xAgeMzC9r+Cra+QOaHmWCwNKXEeyDVATl8z8OJpurUn7X3T3YgCknS0tAmdc9MHs5SRiqW98qUtPNYHKzpHah/10YSGkfCzEqDfPrujXM6e2HdoWNFEi+NwqP9BwC834ad6Y5/xEEw8TA5EtcPyo=";
|
||||
AlipayConfig config = new AlipayConfig();
|
||||
config.setServerUrl(domain);
|
||||
config.setRootCertPath(alipayRootCert);
|
||||
config.setAppId(appid);
|
||||
config.setPrivateKey(prikey);
|
||||
config.setAlipayPublicCertPath(alipayPublicCert);
|
||||
config.setAppCertPath(appPublicCert);
|
||||
config.setFormat("json");
|
||||
config.setCharset("GBK");
|
||||
config.setSignType("RSA2");
|
||||
try {
|
||||
AlipayClient alipayClient = new DefaultAlipayClient(config);
|
||||
AlipayFundAuthorizeUniApplyRequest request = new AlipayFundAuthorizeUniApplyRequest();
|
||||
AlipayFundAuthorizeUniApplyModel model = new AlipayFundAuthorizeUniApplyModel();
|
||||
model.setProductCode("BATCH_API_TO_ACC");
|
||||
model.setBizScene("STANDARD_MESSAGE_BATCH_PAY");
|
||||
model.setOutBizNo(SeqKit.genFlowNo("AT"));
|
||||
model.setAuthorizeLinkType("SHORT_URL");
|
||||
AuthParticipantInfo participantInfo = new AuthParticipantInfo();
|
||||
participantInfo.setParticipantId("1@shouyinbei.com");
|
||||
participantInfo.setParticipantIdType("ALIPAY_LOGON_ID");
|
||||
participantInfo.setName("武汉融商创银科技有限公司");
|
||||
model.setPrincipalInfo(participantInfo);
|
||||
model.setSubBizScene("COMMON");
|
||||
model.setChannel("tinyapp");
|
||||
model.setApplyExpireTime("2024-05-22 22:00");
|
||||
// model.setAuthExpireTime("2024-05-22 18:00:00");
|
||||
request.setBizModel(model);
|
||||
AlipayFundAuthorizeUniApplyResponse response = alipayClient.certificateExecute(request);
|
||||
log.info("返回参数:{}",JSON.toJSONString(response));
|
||||
log.info("返回参数内容:{}",response.getBody());
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "SUCCESS";
|
||||
}
|
||||
|
||||
/**
|
||||
* 鲍志刚 AT1793199383804825601
|
||||
* agreement_no 20245322072022588330
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping("/set/aliapy/plym/createOrder")
|
||||
public String createOrder(){
|
||||
String domain = "https://openapi.alipay.com/gateway.do";
|
||||
String path = "D:\\crystal\\cert\\2021004143669573\\";
|
||||
String alipayPublicCert = path + "alipayCertPublicKey_RSA2.crt";
|
||||
String alipayRootCert = path +"alipayRootCert.crt";
|
||||
String appPublicCert = path +"appCertPublicKey_2021004143669573.crt";
|
||||
String appid = "2021004143669573";
|
||||
String prikey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCPENaSv1YzGidYZF5wGbmwShV6SjwFgq3WSIgIFa9Ctaq/x4BmBFtf6pwAWnkYyVzRfUonh+leWD3ufT2fB6zRZkEPLY9zxUGDLCXR6r8+RhIZ0V9jKaVo2VP+j4XFn68mgUu07H1G4P8DsSper2DJ9FwDlxw96Vy8yOnvu31XYkEfh150PuU7PsiQlKHmskhU0+6aQnmmXZU6rPT9HHK8JDofUkotG5lRpe3sTk7tt5jK+PeXxAOK5SKImuO3kywsm5lUwzKSNEZfM6kVaHzsUojLL1FH1T7pACwuu7ot35GjopB8XhBza515zbOXiwzmOa0HFDXWVkvMGih3Ou2VAgMBAAECggEAP0GRrkbdZTDuLp4KR4SIXtMS1gRWr5UNKc7eHrz/xf4vJiaetfy9Lha8rUdTfcsiuBmunPXyEF3x3C4R/evV1KxincRmRij0g3wUqNEjh/RRcorpox253jchdU+eNTgaqGrIv5FE/v7ioo5GDqF6qTDbMlcxDIrPD0XCcdYzYFwOvx9d7DNymiM2XqvDww9KRs3H0XQvRMBY3lOYG1Bj9nEHaN//lVVsY0470e2wEJUKfofo8GuL+EcarJJ9C5OsKKYfwOjqNopB4RtFEI197qDVtaGtG5F4gtx/bu+2lLcULAMMpf59r3S7pVd5koQhSIDpxdQNi4eP5q+mUr6UIQKBgQDJKnkP+ojvILxaN+DqUE7TFjYCYIndJV0MMndoZF7yxv8snakhXIwbbvlfn/cNlAqtcQSM4BFtwv/A2mY0y31dO/GuymXB+Cm8vehx5MCyX7ioESFbGfIzmvNXah1fh5EgEvhM6zdqFWdKG2az3RX6FCUa+Hh+1FDCbFVEjibM/QKBgQC2EBa3eDSkbnCUp4Fh9I5UTLCce3xe5p74KlDBhEQecV42wYyIA2mROg0wAVJjMXfidW+DkP0sDw7aAG2LzdsPhub+t+kGfEOxmDOicY7q5EBKxep+OJJW5kmp3Qk9QM8MvnGp6BLEGt7Ln+RhV4MzqXvRZ0PTNivZM4eIsJdSeQKBgQCpbJghmQQTQiLgaQeI0LJSnfZG8j+qfXlseGSRC1fcaTUownBbpLJruH9WxkcwrGqBwRnv/BcxtnXoWwiRloCzMXVPnS8uum+MgvVuVxbkJDJFeof5TlTqmaoxCTJL7sWPmep9/ezh77zacVGCUsXbpXPKz1HMn8yUsuDtqDuNkQKBgFvbnjqPcbAM8Mxv/z2XEEDEOM+bFlMjdM2tO/WHcH8Tn/9shCUT462cZo7z+8eEbAJcUhlFjBcwucOQN7Uz9fTNkbquu+nDxLvUGSk6tmsZIr8XQIrSrDMgGcaUeVboXg8F/39EUkOTcxm14JqkdtQYDRrISVZFnLYH47g5sP+xAoGAD6EP/TyOHabSIxWqc/RbTs9xAgeMzC9r+Cra+QOaHmWCwNKXEeyDVATl8z8OJpurUn7X3T3YgCknS0tAmdc9MHs5SRiqW98qUtPNYHKzpHah/10YSGkfCzEqDfPrujXM6e2HdoWNFEi+NwqP9BwC834ad6Y5/xEEw8TA5EtcPyo=";
|
||||
AlipayConfig config = new AlipayConfig();
|
||||
config.setServerUrl(domain);
|
||||
config.setRootCertPath(alipayRootCert);
|
||||
config.setAppId(appid);
|
||||
config.setPrivateKey(prikey);
|
||||
config.setAlipayPublicCertPath(alipayPublicCert);
|
||||
config.setAppCertPath(appPublicCert);
|
||||
config.setFormat("json");
|
||||
config.setCharset("GBK");
|
||||
config.setSignType("RSA2");
|
||||
try {
|
||||
AlipayClient alipayClient = new DefaultAlipayClient(config);
|
||||
AlipayFundBatchCreateRequest request = new AlipayFundBatchCreateRequest();
|
||||
AlipayFundBatchCreateModel model = new AlipayFundBatchCreateModel();
|
||||
model.setOutBatchNo(SeqKit.genFlowNo("OR"));
|
||||
model.setTotalTransAmount("1");
|
||||
model.setTotalCount("1");
|
||||
model.setProductCode("BATCH_API_TO_ACC");
|
||||
model.setBizScene("STANDARD_MESSAGE_BATCH_PAY");
|
||||
model.setOrderTitle("订单支付");
|
||||
model.setTimeExpire("2024-05-23 10:00");
|
||||
model.setRemark("测试支付");
|
||||
// model.setPassbackParams("");
|
||||
TransOrderDetail orderDetail = new TransOrderDetail();
|
||||
orderDetail.setOutBizNo(SeqKit.genMhoOrderId());
|
||||
orderDetail.setTransAmount("1");
|
||||
Participant participant = new Participant();
|
||||
participant.setName("刘韵杰");
|
||||
participant.setIdentityType("ALIPAY_LOGON_ID");
|
||||
participant.setIdentity("17764028120");
|
||||
orderDetail.setPayeeInfo(participant);
|
||||
List<TransOrderDetail> list = new ArrayList<>(1);
|
||||
list.add(orderDetail);
|
||||
model.setTransOrderList(list);
|
||||
|
||||
Participant pree = new Participant();
|
||||
pree.setIdentity("2088502904759300");
|
||||
pree.setIdentityType("ALIPAY_USER_ID");
|
||||
JSONObject extJson = new JSONObject();
|
||||
extJson.put("agreement_no","20245322072022588330");
|
||||
//TODO 个人需要签约协议号 否则无法支付
|
||||
pree.setExtInfo(extJson.toString());
|
||||
model.setPayerInfo(pree);
|
||||
request.setBizModel(model);
|
||||
AlipayFundBatchCreateResponse response = alipayClient.certificateExecute(request);
|
||||
if(response.isSuccess()){
|
||||
return response.getBatchTransId();
|
||||
}else{
|
||||
return response.getSubMsg();
|
||||
}
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "SUCCESS";
|
||||
}
|
||||
|
||||
@RequestMapping("/set/aliapy/plym/pay/{batchOrderId}")
|
||||
public String pay(@PathVariable("batchOrderId") String batchOrderId){
|
||||
String domain = "https://openapi.alipay.com/gateway.do";
|
||||
String path = "D:\\crystal\\cert\\2021004143669573\\";
|
||||
String alipayPublicCert = path + "alipayCertPublicKey_RSA2.crt";
|
||||
String alipayRootCert = path +"alipayRootCert.crt";
|
||||
String appPublicCert = path +"appCertPublicKey_2021004143669573.crt";
|
||||
String appid = "2021004143669573";
|
||||
String prikey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCPENaSv1YzGidYZF5wGbmwShV6SjwFgq3WSIgIFa9Ctaq/x4BmBFtf6pwAWnkYyVzRfUonh+leWD3ufT2fB6zRZkEPLY9zxUGDLCXR6r8+RhIZ0V9jKaVo2VP+j4XFn68mgUu07H1G4P8DsSper2DJ9FwDlxw96Vy8yOnvu31XYkEfh150PuU7PsiQlKHmskhU0+6aQnmmXZU6rPT9HHK8JDofUkotG5lRpe3sTk7tt5jK+PeXxAOK5SKImuO3kywsm5lUwzKSNEZfM6kVaHzsUojLL1FH1T7pACwuu7ot35GjopB8XhBza515zbOXiwzmOa0HFDXWVkvMGih3Ou2VAgMBAAECggEAP0GRrkbdZTDuLp4KR4SIXtMS1gRWr5UNKc7eHrz/xf4vJiaetfy9Lha8rUdTfcsiuBmunPXyEF3x3C4R/evV1KxincRmRij0g3wUqNEjh/RRcorpox253jchdU+eNTgaqGrIv5FE/v7ioo5GDqF6qTDbMlcxDIrPD0XCcdYzYFwOvx9d7DNymiM2XqvDww9KRs3H0XQvRMBY3lOYG1Bj9nEHaN//lVVsY0470e2wEJUKfofo8GuL+EcarJJ9C5OsKKYfwOjqNopB4RtFEI197qDVtaGtG5F4gtx/bu+2lLcULAMMpf59r3S7pVd5koQhSIDpxdQNi4eP5q+mUr6UIQKBgQDJKnkP+ojvILxaN+DqUE7TFjYCYIndJV0MMndoZF7yxv8snakhXIwbbvlfn/cNlAqtcQSM4BFtwv/A2mY0y31dO/GuymXB+Cm8vehx5MCyX7ioESFbGfIzmvNXah1fh5EgEvhM6zdqFWdKG2az3RX6FCUa+Hh+1FDCbFVEjibM/QKBgQC2EBa3eDSkbnCUp4Fh9I5UTLCce3xe5p74KlDBhEQecV42wYyIA2mROg0wAVJjMXfidW+DkP0sDw7aAG2LzdsPhub+t+kGfEOxmDOicY7q5EBKxep+OJJW5kmp3Qk9QM8MvnGp6BLEGt7Ln+RhV4MzqXvRZ0PTNivZM4eIsJdSeQKBgQCpbJghmQQTQiLgaQeI0LJSnfZG8j+qfXlseGSRC1fcaTUownBbpLJruH9WxkcwrGqBwRnv/BcxtnXoWwiRloCzMXVPnS8uum+MgvVuVxbkJDJFeof5TlTqmaoxCTJL7sWPmep9/ezh77zacVGCUsXbpXPKz1HMn8yUsuDtqDuNkQKBgFvbnjqPcbAM8Mxv/z2XEEDEOM+bFlMjdM2tO/WHcH8Tn/9shCUT462cZo7z+8eEbAJcUhlFjBcwucOQN7Uz9fTNkbquu+nDxLvUGSk6tmsZIr8XQIrSrDMgGcaUeVboXg8F/39EUkOTcxm14JqkdtQYDRrISVZFnLYH47g5sP+xAoGAD6EP/TyOHabSIxWqc/RbTs9xAgeMzC9r+Cra+QOaHmWCwNKXEeyDVATl8z8OJpurUn7X3T3YgCknS0tAmdc9MHs5SRiqW98qUtPNYHKzpHah/10YSGkfCzEqDfPrujXM6e2HdoWNFEi+NwqP9BwC834ad6Y5/xEEw8TA5EtcPyo=";
|
||||
AlipayConfig config = new AlipayConfig();
|
||||
config.setServerUrl(domain);
|
||||
config.setRootCertPath(alipayRootCert);
|
||||
config.setAppId(appid);
|
||||
config.setPrivateKey(prikey);
|
||||
config.setAlipayPublicCertPath(alipayPublicCert);
|
||||
config.setAppCertPath(appPublicCert);
|
||||
config.setFormat("json");
|
||||
config.setCharset("GBK");
|
||||
config.setSignType("RSA2");
|
||||
try {
|
||||
AlipayClient alipayClient = new DefaultAlipayClient(config);
|
||||
AlipayFundBatchAppPayRequest request = new AlipayFundBatchAppPayRequest();
|
||||
AlipayFundBatchAppPayModel model = new AlipayFundBatchAppPayModel();
|
||||
model.setBatchTransId(batchOrderId);
|
||||
request.setBizModel(model);
|
||||
AlipayFundBatchAppPayResponse response = alipayClient.pageExecute(request,"get");
|
||||
String url = response.getBody();
|
||||
String parm = url.replace("https://openapi.alipay.com/gateway.do?","");
|
||||
String encodeParm = URLEncoder.encode(parm, "utf-8");
|
||||
String pageEncode = URLEncoder.encode("pages/confirm/index/index?signParams=" + encodeParm,"utf-8");
|
||||
String payUrl = "alipays://platformapi/startapp?appId=2021002133661287&appClearTop=false&startMultApp=YES"
|
||||
+ "&page=" + pageEncode;
|
||||
String fullUrl = "https://render.alipay.com/p/s/ulink/?scheme=" + URLEncoder.encode(payUrl, "utf-8");
|
||||
log.info("付款url:{}",fullUrl);
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "SUCCESS";
|
||||
}
|
||||
|
||||
|
||||
// @RequestMapping("/set/aliapy/plym/createOrder")
|
||||
// public String createOrder(){
|
||||
// String domain = "https://openapi.alipay.com/gateway.do";
|
||||
// String path = "D:\\crystal\\cert\\2021004143669573\\";
|
||||
// String alipayPublicCert = path + "alipayCertPublicKey_RSA2.crt";
|
||||
// String alipayRootCert = path +"alipayRootCert.crt";
|
||||
// String appPublicCert = path +"appCertPublicKey_2021004143669573.crt";
|
||||
// String appid = "2021004143669573";
|
||||
// String prikey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCPENaSv1YzGidYZF5wGbmwShV6SjwFgq3WSIgIFa9Ctaq/x4BmBFtf6pwAWnkYyVzRfUonh+leWD3ufT2fB6zRZkEPLY9zxUGDLCXR6r8+RhIZ0V9jKaVo2VP+j4XFn68mgUu07H1G4P8DsSper2DJ9FwDlxw96Vy8yOnvu31XYkEfh150PuU7PsiQlKHmskhU0+6aQnmmXZU6rPT9HHK8JDofUkotG5lRpe3sTk7tt5jK+PeXxAOK5SKImuO3kywsm5lUwzKSNEZfM6kVaHzsUojLL1FH1T7pACwuu7ot35GjopB8XhBza515zbOXiwzmOa0HFDXWVkvMGih3Ou2VAgMBAAECggEAP0GRrkbdZTDuLp4KR4SIXtMS1gRWr5UNKc7eHrz/xf4vJiaetfy9Lha8rUdTfcsiuBmunPXyEF3x3C4R/evV1KxincRmRij0g3wUqNEjh/RRcorpox253jchdU+eNTgaqGrIv5FE/v7ioo5GDqF6qTDbMlcxDIrPD0XCcdYzYFwOvx9d7DNymiM2XqvDww9KRs3H0XQvRMBY3lOYG1Bj9nEHaN//lVVsY0470e2wEJUKfofo8GuL+EcarJJ9C5OsKKYfwOjqNopB4RtFEI197qDVtaGtG5F4gtx/bu+2lLcULAMMpf59r3S7pVd5koQhSIDpxdQNi4eP5q+mUr6UIQKBgQDJKnkP+ojvILxaN+DqUE7TFjYCYIndJV0MMndoZF7yxv8snakhXIwbbvlfn/cNlAqtcQSM4BFtwv/A2mY0y31dO/GuymXB+Cm8vehx5MCyX7ioESFbGfIzmvNXah1fh5EgEvhM6zdqFWdKG2az3RX6FCUa+Hh+1FDCbFVEjibM/QKBgQC2EBa3eDSkbnCUp4Fh9I5UTLCce3xe5p74KlDBhEQecV42wYyIA2mROg0wAVJjMXfidW+DkP0sDw7aAG2LzdsPhub+t+kGfEOxmDOicY7q5EBKxep+OJJW5kmp3Qk9QM8MvnGp6BLEGt7Ln+RhV4MzqXvRZ0PTNivZM4eIsJdSeQKBgQCpbJghmQQTQiLgaQeI0LJSnfZG8j+qfXlseGSRC1fcaTUownBbpLJruH9WxkcwrGqBwRnv/BcxtnXoWwiRloCzMXVPnS8uum+MgvVuVxbkJDJFeof5TlTqmaoxCTJL7sWPmep9/ezh77zacVGCUsXbpXPKz1HMn8yUsuDtqDuNkQKBgFvbnjqPcbAM8Mxv/z2XEEDEOM+bFlMjdM2tO/WHcH8Tn/9shCUT462cZo7z+8eEbAJcUhlFjBcwucOQN7Uz9fTNkbquu+nDxLvUGSk6tmsZIr8XQIrSrDMgGcaUeVboXg8F/39EUkOTcxm14JqkdtQYDRrISVZFnLYH47g5sP+xAoGAD6EP/TyOHabSIxWqc/RbTs9xAgeMzC9r+Cra+QOaHmWCwNKXEeyDVATl8z8OJpurUn7X3T3YgCknS0tAmdc9MHs5SRiqW98qUtPNYHKzpHah/10YSGkfCzEqDfPrujXM6e2HdoWNFEi+NwqP9BwC834ad6Y5/xEEw8TA5EtcPyo=";
|
||||
// AlipayConfig config = new AlipayConfig();
|
||||
// config.setServerUrl(domain);
|
||||
// config.setRootCertPath(alipayRootCert);
|
||||
// config.setAppId(appid);
|
||||
// config.setPrivateKey(prikey);
|
||||
// config.setAlipayPublicCertPath(alipayPublicCert);
|
||||
// config.setAppCertPath(appPublicCert);
|
||||
// config.setFormat("json");
|
||||
// config.setCharset("GBK");
|
||||
// config.setSignType("RSA2");
|
||||
// try {
|
||||
// AlipayClient alipayClient = new DefaultAlipayClient(config);
|
||||
// AlipayFundAuthorizeUniQueryRequest request = new AlipayFundAuthorizeUniQueryRequest();
|
||||
// AlipayFundAuthorizeUniQueryModel model = new AlipayFundAuthorizeUniQueryModel();
|
||||
// model.setProductCode("BATCH_API_TO_ACC");
|
||||
// model.setBizScene("STANDARD_MESSAGE_BATCH_PAY");
|
||||
// model.setOutBizNo("");
|
||||
// request.setBizModel(model);
|
||||
// AlipayFundAuthorizeUniQueryResponse response = alipayClient.certificateExecute(request);
|
||||
// if(response.isSuccess()){
|
||||
// return response.getAgreementNo();
|
||||
// }else{
|
||||
// return response.getSubMsg();
|
||||
// }
|
||||
// }catch (Exception e){
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// return "SUCCESS";
|
||||
// }
|
||||
|
||||
public static void main(String[] args) {
|
||||
// JSONObject params = new JSONObject();
|
||||
// params.put("alipay_root_cert_sn","687b59193f3f462dd5336e5abf83c5d8_02941eef3187dddf3d3b83462e1dfcf6");
|
||||
// params.put("alipay_sdk","alipay-sdk-java-4.38.61.ALL");
|
||||
// params.put("app_cert_sn","688411dca29b7c7edddb57c5f75c7414");
|
||||
// params.put("app_id","2021004143669573");
|
||||
// params.put("biz_content","%7B%22batch_trans_id%22%3A%222024052210881255135%22%7D");
|
||||
// params.put("charset","GBK");
|
||||
//
|
||||
// params.put("format","json");
|
||||
//
|
||||
// params.put("method","alipay.fund.batch.app.pay");
|
||||
//
|
||||
// params.put("charset","GBK");
|
||||
//
|
||||
// params.put("sign","FvSYtxPqCTMhH6OGPz%2F3Nt0eTc4SflPPymW9VQnUAvDuemUgM6eKsTz62iCUPvpJ7gUnAa01j%2BDUvq7%2FI6Tuzt7lwS13P2EaAD5RZ61zDxVwOZrznakCeQ2f1s4JngVO3NzZfWGwpXVJabyHiPXnXa8h69yKd%2FZN8MQMgsr%2B3L2g0YPRQ8fV8DaoHgaSpPB%2BuAmmb6mwjWC0EOSc%2FqOBrHoJvRF1wB6Hh9Ir3JGfC4rhmXtw%2BnWDevOT9uluDQrq4mRIuuptqVjQ6yeNCkhIfJcvZTjgNjqTzm451Jf09aXz2STSpCE3wE2jTD3Yl1Ku984E88a03lZ9JHfELNbUFw%3D%3D")
|
||||
String parm = "alipay_root_cert_sn=687b59193f3f462dd5336e5abf83c5d8_02941eef3187dddf3d3b83462e1dfcf6&alipay_sdk=alipay-sdk-java-4.38.61.ALL&app_cert_sn=688411dca29b7c7edddb57c5f75c7414&app_id=2021004143669573&biz_content=%7B%22batch_trans_id%22%3A%222024052210881255135%22%7D&charset=GBK&format=json&method=alipay.fund.batch.app.pay&sign=FvSYtxPqCTMhH6OGPz%2F3Nt0eTc4SflPPymW9VQnUAvDuemUgM6eKsTz62iCUPvpJ7gUnAa01j%2BDUvq7%2FI6Tuzt7lwS13P2EaAD5RZ61zDxVwOZrznakCeQ2f1s4JngVO3NzZfWGwpXVJabyHiPXnXa8h69yKd%2FZN8MQMgsr%2B3L2g0YPRQ8fV8DaoHgaSpPB%2BuAmmb6mwjWC0EOSc%2FqOBrHoJvRF1wB6Hh9Ir3JGfC4rhmXtw%2BnWDevOT9uluDQrq4mRIuuptqVjQ6yeNCkhIfJcvZTjgNjqTzm451Jf09aXz2STSpCE3wE2jTD3Yl1Ku984E88a03lZ9JHfELNbUFw%3D%3D&sign_type=RSA2×tamp=2024-05-22+19%3A17%3A35&version=1.0";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.transfer;
|
||||
|
||||
import com.jeequan.jeepay.core.aop.MethodLog;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.interfaces.IConfigContextQueryService;
|
||||
import com.jeequan.jeepay.core.model.ApiRes;
|
||||
import com.jeequan.jeepay.core.model.rqrs.transfer.QueryTransferOrderRQ;
|
||||
import com.jeequan.jeepay.core.model.rqrs.transfer.QueryTransferOrderRS;
|
||||
import com.jeequan.jeepay.db.entity.TransferOrderEntity;
|
||||
import com.jeequan.jeepay.pay.ctrl.ApiController;
|
||||
import com.jeequan.jeepay.pay.util.ApiResKit;
|
||||
import com.jeequan.jeepay.service.impl.PayInterfaceConfigService;
|
||||
import com.jeequan.jeepay.service.impl.TransferOrderService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 商户转账单查询controller
|
||||
*
|
||||
* @author terrfly
|
||||
*
|
||||
* @date 2021/8/13 15:20
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
public class QueryTransferOrderController extends ApiController {
|
||||
|
||||
@Autowired private TransferOrderService transferOrderService;
|
||||
@Autowired private IConfigContextQueryService configContextQueryService;
|
||||
@Autowired private PayInterfaceConfigService payInterfaceConfigService;
|
||||
|
||||
/**
|
||||
* 查单接口
|
||||
* **/
|
||||
@MethodLog(remark = "查询转账订单API")
|
||||
@RequestMapping("/api/transfer/query")
|
||||
public ApiRes queryTransferOrder(){
|
||||
|
||||
//获取参数 & 验签
|
||||
QueryTransferOrderRQ rq = getRQByWithMchSign(QueryTransferOrderRQ.class);
|
||||
|
||||
if(StringUtils.isAllEmpty(rq.getMchOrderNo(), rq.getTransferId())){
|
||||
throw new BizException("mchOrderNo 和 transferId不能同时为空");
|
||||
}
|
||||
|
||||
TransferOrderEntity refundOrder = transferOrderService.queryMchOrder(rq.getMchNo(), rq.getMchOrderNo(), rq.getTransferId());
|
||||
if(refundOrder == null){
|
||||
throw new BizException("订单不存在");
|
||||
}
|
||||
|
||||
QueryTransferOrderRS bizRes = QueryTransferOrderRS.buildByRecord(refundOrder);
|
||||
|
||||
return ApiResKit.okWithSign(bizRes, configContextQueryService.queryMchApp(rq.getMchNo(), rq.getAppId()), rq.getSignType());
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询转账账户余额接口
|
||||
* **/
|
||||
@MethodLog(remark = "查询转账可用余额API")
|
||||
@RequestMapping("/api/transfer/balance/query")
|
||||
public ApiRes queryTransferBalance(){
|
||||
|
||||
return ApiRes.ok();
|
||||
// //获取参数 & 验签
|
||||
// QueryTransferBalanceRQ bizRQ = getRQByWithMchSign(QueryTransferBalanceRQ.class);
|
||||
//
|
||||
// try {
|
||||
// String mchNo = bizRQ.getMchNo();
|
||||
// String appId = bizRQ.getAppId();
|
||||
// String ifCode = bizRQ.getIfCode();
|
||||
//
|
||||
// // 商户配置信息
|
||||
// MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(mchNo, appId,bizRQ.getMchExtNo());
|
||||
// if(mchAppConfigContext == null){
|
||||
// throw new BizException("获取商户应用信息失败");
|
||||
// }
|
||||
//
|
||||
// MchApp mchApp = mchAppConfigContext.getMchApp();
|
||||
//
|
||||
// // 是否已正确配置
|
||||
// if(!payInterfaceConfigService.mchAppHasAvailableIfCode(appId, ifCode)){
|
||||
// throw new BizException("应用未开通此接口配置!");
|
||||
// }
|
||||
//
|
||||
// ITransferService transferService = SpringBeansUtil.getBean(JeepayKit.getIfCodeOrigin(ifCode) + "TransferService", ITransferService.class);
|
||||
// if(transferService == null){
|
||||
// throw new BizException("无此转账通道接口");
|
||||
// }
|
||||
//
|
||||
// // 调起上游接口
|
||||
// MutablePair<String, Long> pair = transferService.queryBalanceAmount(mchAppConfigContext, bizRQ.getIfCode());
|
||||
//
|
||||
// QueryTransferBalanceRS bizRes = new QueryTransferBalanceRS();
|
||||
// bizRes.setBalanceAmount(pair.getRight());
|
||||
//
|
||||
// return ApiResKit.okWithSign(bizRes, mchApp, bizRQ.getSignType());
|
||||
//
|
||||
// } catch (BizException e) {
|
||||
// return ApiRes.customFail(e.getMessage());
|
||||
//
|
||||
// } catch (Exception e) {
|
||||
// log.error("系统异常:", e);
|
||||
// return ApiRes.customFail("系统异常");
|
||||
// }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.transfer;
|
||||
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import com.jeequan.jeepay.core.ctrls.AbstractCtrl;
|
||||
import com.jeequan.jeepay.core.entity.TransferOrder;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.exception.ResponseException;
|
||||
import com.jeequan.jeepay.core.interfaces.paychannel.ITransferNoticeService;
|
||||
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
|
||||
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
|
||||
import com.jeequan.jeepay.db.entity.OrderProfitSettRecord;
|
||||
import com.jeequan.jeepay.db.entity.TransferOrderEntity;
|
||||
import com.jeequan.jeepay.pay.service.PayMchNotifyService;
|
||||
import com.jeequan.jeepay.service.impl.OrderProfitSettRecordService;
|
||||
import com.jeequan.jeepay.service.impl.TransferOrderService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.MutablePair;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 转账异步通知入口Controller
|
||||
*
|
||||
* @author zx
|
||||
* @since 2022/12/30 10:26
|
||||
*/
|
||||
@Slf4j
|
||||
@Controller
|
||||
public class TransferNoticeController extends AbstractCtrl {
|
||||
|
||||
@Autowired
|
||||
private TransferOrderService transferOrderService;
|
||||
@Autowired
|
||||
private PayMchNotifyService payMchNotifyService;
|
||||
@Autowired
|
||||
private OrderProfitSettRecordService orderProfitSettRecordService;
|
||||
|
||||
/**
|
||||
* 异步回调入口
|
||||
**/
|
||||
@ResponseBody
|
||||
@RequestMapping(value = {"/api/transfer/notify/{ifCode}", "/api/transfer/notify/{ifCode}/{transferId}"})
|
||||
public ResponseEntity doNotify(HttpServletRequest request, @PathVariable("ifCode") String ifCode, @PathVariable(value = "transferId", required = false) String urlOrderId) {
|
||||
|
||||
String transferId = null;
|
||||
String logPrefix = "进入[" + ifCode + "]转账回调:urlOrderId:[" + StringUtils.defaultIfEmpty(urlOrderId, "") + "] ";
|
||||
log.info("===== {} =====", logPrefix);
|
||||
|
||||
try {
|
||||
|
||||
// 参数有误
|
||||
if (StringUtils.isEmpty(ifCode)) {
|
||||
return ResponseEntity.badRequest().body("ifCode is empty");
|
||||
}
|
||||
|
||||
//查询转账接口是否存在
|
||||
ITransferNoticeService transferNotifyService = SpringBeansUtil.getBean(ifCode + "TransferNoticeService", ITransferNoticeService.class);
|
||||
|
||||
// 支付通道转账接口实现不存在
|
||||
if (transferNotifyService == null) {
|
||||
log.error("{}, transfer interface not exists ", logPrefix);
|
||||
return ResponseEntity.badRequest().body("[" + ifCode + "] transfer interface not exists");
|
||||
}
|
||||
|
||||
// 解析转账单号 和 请求参数
|
||||
MutablePair<String, Object> mutablePair = transferNotifyService.parseParams(request, urlOrderId);
|
||||
if (mutablePair == null) { // 解析数据失败, 响应已处理
|
||||
log.error("{}, mutablePair is null ", logPrefix);
|
||||
throw new BizException("解析数据异常!"); //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
|
||||
}
|
||||
|
||||
// 解析到转账单号
|
||||
transferId = mutablePair.left;
|
||||
if (ObjUtil.isEmpty(transferId)) {
|
||||
log.error("解析回调数据转账单号为空, 转账单不存在");
|
||||
return ResponseEntity.ok(transferNotifyService.retOk(null));
|
||||
}
|
||||
log.info("{}, 解析数据为:transferId:{}, params:{}", logPrefix, transferId, mutablePair.getRight());
|
||||
|
||||
// 获取转账单号 和 转账单数据
|
||||
TransferOrderEntity transferOrder = transferOrderService.getById(transferId);
|
||||
|
||||
// 转账单不存在
|
||||
if (transferOrder == null) {
|
||||
log.error("{}, 转账单不存在. transferId={} ", logPrefix, transferId);
|
||||
return ResponseEntity.ok(transferNotifyService.retOk(null));
|
||||
}
|
||||
|
||||
//查询出商户应用的配置信息
|
||||
// MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(transferOrder.getMchNo(), transferOrder.getAppId(), transferOrder.getMchExtNo());
|
||||
|
||||
//调起接口的回调判断
|
||||
ChannelRetMsg notifyResult = transferNotifyService.doNotice(request, mutablePair.getRight(), transferOrder);
|
||||
|
||||
// 返回null 表明出现异常, 无需处理通知下游等操作。
|
||||
if (notifyResult == null || notifyResult.getChannelState() == null || notifyResult.getResponseEntity() == null) {
|
||||
log.error("{}, 处理回调事件异常 notifyResult data error, notifyResult ={} ", logPrefix, notifyResult);
|
||||
|
||||
return ResponseEntity.ok(transferNotifyService.retOk(null));
|
||||
}
|
||||
|
||||
// 转账单是 【转账中状态】
|
||||
if (transferOrder.getState() == TransferOrder.STATE_ING) {
|
||||
if (notifyResult.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_SUCCESS) {
|
||||
// 转账成功
|
||||
boolean isUpdateSuccess = transferOrderService.updateIng2Success(transferId, notifyResult);
|
||||
|
||||
if (isUpdateSuccess) {
|
||||
|
||||
|
||||
//更新营销活动补贴金额
|
||||
// payOrderService.updateMarketAmt(transferOrder.getFlowNo(), transferOrder.getAmount());
|
||||
|
||||
// 计算订单的分润记录 ( 若异常 不影响订单后续逻辑, service内的事务)
|
||||
try {
|
||||
boolean isRunTask = orderProfitSettRecordService.insertProfitByPayOrder(transferOrder.getTransferId(), OrderProfitSettRecord.CAL_TYPE_TRANS_ORDER);
|
||||
// 实时结算
|
||||
if (isRunTask) {
|
||||
orderProfitSettRecordService.profit2BalanceAmount(transferOrder.getTransferId(), OrderProfitSettRecord.CAL_TYPE_TRANS_ORDER);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("计算转账分润错误, transferId={}", transferOrder.getTransferId(), e);
|
||||
}
|
||||
|
||||
payMchNotifyService.transferOrderNotify(transferOrderService.getById(transferId));
|
||||
}
|
||||
} else if (notifyResult.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_FAIL) {
|
||||
// 转账失败
|
||||
ChannelRetMsg channelRetMsg = ChannelRetMsg.confirmFail(notifyResult.getChannelOrderId(), notifyResult.getChannelErrCode(), notifyResult.getChannelErrMsg());
|
||||
transferOrderService.updateIng2Fail(transferId, channelRetMsg);
|
||||
payMchNotifyService.transferOrderNotify(transferOrderService.getById(transferId));
|
||||
}
|
||||
}
|
||||
|
||||
log.info("===== {}, 转账单通知完成。 transferId={}, parseState = {} =====", logPrefix, transferId, notifyResult.getChannelState());
|
||||
|
||||
return notifyResult.getResponseEntity();
|
||||
|
||||
} catch (BizException e) {
|
||||
log.error("{}, transferId={}, BizException", logPrefix, transferId, e);
|
||||
return ResponseEntity.badRequest().body(e.getMessage());
|
||||
|
||||
} catch (ResponseException e) {
|
||||
log.error("{}, transferId={}, ResponseException", logPrefix, transferId, e);
|
||||
return e.getResponseEntity();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("{}, transferId={}, 系统异常", logPrefix, transferId, e);
|
||||
return ResponseEntity.badRequest().body(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.transfer;
|
||||
|
||||
import com.jeequan.jeepay.core.entity.MchApp;
|
||||
import com.jeequan.jeepay.core.entity.MchInfo;
|
||||
import com.jeequan.jeepay.core.interfaces.IConfigContextQueryService;
|
||||
import com.jeequan.jeepay.core.model.applyment.PaywayFee;
|
||||
import com.jeequan.jeepay.core.model.rqrs.transfer.TransferOrderRQ;
|
||||
import com.jeequan.jeepay.core.utils.SeqKit;
|
||||
import com.jeequan.jeepay.db.entity.TransferOrderEntity;
|
||||
import com.jeequan.jeepay.pay.ctrl.ApiController;
|
||||
import com.jeequan.jeepay.pay.service.TransferOrderReissueService;
|
||||
import com.jeequan.jeepay.service.impl.PayInterfaceConfigService;
|
||||
import com.jeequan.jeepay.service.impl.RateConfigService;
|
||||
import com.jeequan.jeepay.service.impl.TransferOrderService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.MutablePair;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 转账接口
|
||||
*
|
||||
* @author terrfly
|
||||
*
|
||||
* @date 2021/8/11 11:07
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
public class TransferOrderController extends ApiController {
|
||||
|
||||
@Autowired private IConfigContextQueryService configContextQueryService;
|
||||
@Autowired private TransferOrderService transferOrderService;
|
||||
@Autowired private PayInterfaceConfigService payInterfaceConfigService;
|
||||
@Autowired private TransferOrderReissueService transferOrderReissueService;
|
||||
@Autowired private RateConfigService rateConfigService;
|
||||
|
||||
|
||||
// /**
|
||||
// * 转账
|
||||
// * **/
|
||||
// @MethodLog(remark = "发起转账订单API")
|
||||
// @PostMapping("/api/transferOrder")
|
||||
// public ApiRes transferOrder(){
|
||||
//
|
||||
// TransferOrderEntity transferOrder = null;
|
||||
//
|
||||
// //获取参数 & 验签
|
||||
// TransferOrderRQ bizRQ = getRQByWithMchSign(TransferOrderRQ.class);
|
||||
//
|
||||
// try {
|
||||
//
|
||||
//
|
||||
// String mchNo = bizRQ.getMchNo();
|
||||
// String appId = bizRQ.getAppId();
|
||||
// String ifCode = bizRQ.getIfCode();
|
||||
//
|
||||
// // 商户订单号是否重复
|
||||
// if(transferOrderService.count(TransferOrderEntity.gw().eq(TransferOrderEntity::getMchNo, mchNo).eq(TransferOrderEntity::getMchOrderNo, bizRQ.getMchOrderNo())) > 0){
|
||||
// throw new BizException("商户订单["+bizRQ.getMchOrderNo()+"]已存在");
|
||||
// }
|
||||
//
|
||||
// if(StringUtils.isNotEmpty(bizRQ.getNotifyUrl()) && !StringKit.isAvailableUrl(bizRQ.getNotifyUrl())){
|
||||
// throw new BizException("异步通知地址协议仅支持http:// 或 https:// !");
|
||||
// }
|
||||
//
|
||||
// // 商户配置信息
|
||||
// MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(mchNo, appId,bizRQ.getMchNo());
|
||||
// if(mchAppConfigContext == null){
|
||||
// throw new BizException("获取商户应用信息失败");
|
||||
// }
|
||||
//
|
||||
// MchInfo mchInfo = mchAppConfigContext.getMchInfo();
|
||||
// MchApp mchApp = mchAppConfigContext.getMchApp();
|
||||
//
|
||||
// // 是否已正确配置
|
||||
// if(!payInterfaceConfigService.mchAppHasAvailableIfCode(appId, ifCode)){
|
||||
// throw new BizException("应用未开通此接口配置!");
|
||||
// }
|
||||
//
|
||||
//
|
||||
// ITransferService transferService = SpringBeansUtil.getBean(JeepayKit.getIfCodeOrigin(ifCode) + "TransferService", ITransferService.class);
|
||||
// if(transferService == null){
|
||||
// throw new BizException("无此转账通道接口");
|
||||
// }
|
||||
//
|
||||
// if(!transferService.isSupport(bizRQ.getEntryType())){
|
||||
// throw new BizException("该接口不支持该入账方式");
|
||||
// }
|
||||
//
|
||||
// // 查询商户的转账费率
|
||||
// PaywayFee paywayFee = rateConfigService.queryTransferCreateOrderPaywayFee(mchAppConfigContext.getMchApplyment().getApplyId(), mchAppConfigContext.getMchApplyment().getIsvNo(), mchAppConfigContext.getAppId(), ifCode, CS.PAY_WAY_CODE.TRANSFER);
|
||||
//
|
||||
// transferOrder = genTransferOrder(bizRQ, mchInfo, mchApp, ifCode, paywayFee);
|
||||
//
|
||||
// //预先校验
|
||||
// String errMsg = transferService.preCheck(bizRQ, transferOrder);
|
||||
// if(StringUtils.isNotEmpty(errMsg)){
|
||||
// throw new BizException(errMsg);
|
||||
// }
|
||||
//
|
||||
// // 入库
|
||||
// transferOrderService.save(transferOrder);
|
||||
//
|
||||
// // 调起上游接口
|
||||
// ChannelRetMsg channelRetMsg = transferService.transfer(bizRQ, transferOrder);
|
||||
//
|
||||
// //处理转账单状态
|
||||
// transferOrderReissueService.processChannelMsg(channelRetMsg, transferOrder);
|
||||
//
|
||||
// TransferOrderRS bizRes = TransferOrderRS.buildByRecord(transferOrder);
|
||||
//
|
||||
// return ApiResKit.okWithSign(bizRes, mchApp, bizRQ.getSignType());
|
||||
//
|
||||
// } catch (BizException e) {
|
||||
// return ApiRes.customFail(e.getMessage());
|
||||
//
|
||||
// } catch (ChannelException e) {
|
||||
//
|
||||
// //处理上游返回数据
|
||||
// transferOrderReissueService.processChannelMsg(e.getChannelRetMsg(), transferOrder);
|
||||
//
|
||||
// if(e.getChannelRetMsg().getChannelState() == ChannelRetMsg.ChannelState.SYS_ERROR ){
|
||||
// return ApiRes.customFail(e.getMessage());
|
||||
// }
|
||||
//
|
||||
// TransferOrderRS bizRes = TransferOrderRS.buildByRecord(transferOrder);
|
||||
//
|
||||
// return ApiResKit.okWithSign(bizRes, configContextQueryService.queryMchApp(bizRQ.getMchNo(), bizRQ.getAppId()), bizRQ.getSignType());
|
||||
//
|
||||
// } catch (Exception e) {
|
||||
// log.error("系统异常:{}", e);
|
||||
// return ApiRes.customFail("系统异常");
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
private TransferOrderEntity genTransferOrder(TransferOrderRQ rq, MchInfo mchInfo, MchApp mchApp, String ifCode, PaywayFee paywayFee){
|
||||
|
||||
TransferOrderEntity transferOrder = new TransferOrderEntity();
|
||||
transferOrder.setTransferId(SeqKit.genTransferId()); //生成转账订单号
|
||||
transferOrder.setMchNo(mchInfo.getMchNo()); //商户号
|
||||
// transferOrder.setIsvNo(mchInfo.getIsvNo()); //服务商号
|
||||
transferOrder.setAgentNo(mchInfo.getAgentNo()); // 服务商号
|
||||
transferOrder.setAppId(mchApp.getAppId()); //商户应用appId
|
||||
transferOrder.setMchName(mchInfo.getMchShortName()); //商户名称(简称)
|
||||
transferOrder.setMchType(mchInfo.getType()); //商户类型
|
||||
transferOrder.setMchOrderNo(rq.getMchOrderNo()); //商户订单号
|
||||
transferOrder.setIfCode(ifCode); //接口代码
|
||||
transferOrder.setEntryType(rq.getEntryType()); //入账方式
|
||||
transferOrder.setAmount(rq.getAmount()); //订单金额
|
||||
transferOrder.setCurrency(rq.getCurrency()); //币种
|
||||
transferOrder.setClientIp(StringUtils.defaultIfEmpty(rq.getClientIp(), getClientIp())); //客户端IP
|
||||
transferOrder.setState(TransferOrderEntity.STATE_INIT); //订单状态, 默认订单生成状态
|
||||
transferOrder.setAccountNo(rq.getAccountNo()); //收款账号
|
||||
transferOrder.setAccountName(rq.getAccountName()); //账户姓名
|
||||
transferOrder.setBankName(rq.getBankName()); //银行名称
|
||||
transferOrder.setTransferDesc(rq.getTransferDesc()); //转账备注
|
||||
transferOrder.setExtParam(rq.getExtParam()); //商户扩展参数
|
||||
transferOrder.setNotifyUrl(rq.getNotifyUrl()); //异步通知地址
|
||||
|
||||
// 转账手续费, 可以为空。
|
||||
if(paywayFee != null){
|
||||
MutablePair<String, Long> feeAndSnapshot = paywayFee.calFeeAndSnapshot(transferOrder.getAmount());
|
||||
transferOrder.setMchFeeRate(feeAndSnapshot.left);
|
||||
transferOrder.setMchOrderFeeAmount(feeAndSnapshot.right); //商户手续费,单位分
|
||||
}
|
||||
|
||||
transferOrder.setCreatedAt(new Date()); //订单创建时间
|
||||
return transferOrder;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.transfer;
|
||||
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.jeequan.jeepay.core.beans.RequestKitBean;
|
||||
import com.jeequan.jeepay.core.ctrls.AbstractCtrl;
|
||||
import com.jeequan.jeepay.core.interfaces.transfer.IChannelTransferSubjectService;
|
||||
import com.jeequan.jeepay.db.entity.TransferSubjectEntity;
|
||||
import com.jeequan.jeepay.service.impl.TransferSubjectService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.tuple.MutablePair;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 转账主体功能开通回调
|
||||
*
|
||||
* @author deng
|
||||
* @since 2024/5/8
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
public class TransferSubjectChannelNotifyController extends AbstractCtrl {
|
||||
|
||||
@Autowired
|
||||
private RequestKitBean requestKitBean;
|
||||
@Autowired
|
||||
private TransferSubjectService transferSubjectService;
|
||||
|
||||
@ResponseBody
|
||||
@RequestMapping(value = {"/api/transferSubject/notify/{ifCode}", "/api/transferSubject/notify/{ifCode}/{customApplyId}"})
|
||||
public ResponseEntity doNotify(HttpServletRequest request, @PathVariable("ifCode") String ifCode, @PathVariable(value = "customApplyId", required = false) String customApplyId) {
|
||||
|
||||
JSONObject reqParamJSON = requestKitBean.getReqParamJSON();
|
||||
|
||||
IChannelTransferSubjectService channelTransferSubjectService = SpringUtil.getBean(ifCode + "ChannelTransferSubjectService", IChannelTransferSubjectService.class);
|
||||
if (channelTransferSubjectService == null) {
|
||||
return ResponseEntity.ok("SUCCESS");
|
||||
}
|
||||
|
||||
MutablePair<String, Object> mutablePair = channelTransferSubjectService.parseParam(request, customApplyId);
|
||||
|
||||
//解析到订单号
|
||||
String applyId = mutablePair.left;
|
||||
log.info("解析数据为:applyId:{}, params:{}", applyId, mutablePair.getRight());
|
||||
log.info("代付参数, {}", reqParamJSON);
|
||||
|
||||
TransferSubjectEntity transferSubject = transferSubjectService.getById(applyId);
|
||||
if (Objects.equals(transferSubject.getSignState(), (int) TransferSubjectEntity.SIGN_STATE_SUCCESS)) {
|
||||
log.info("已经完成签约,本次不再处理");
|
||||
return ResponseEntity.ok(channelTransferSubjectService.retOk(mutablePair.right));
|
||||
}
|
||||
|
||||
Object result = channelTransferSubjectService.callback(mutablePair.right);
|
||||
return ResponseEntity.ok(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.webcashier;
|
||||
|
||||
import com.jeequan.jeepay.db.entity.ThirdAuthDataEntity;
|
||||
import com.jeequan.jeepay.service.impl.ThirdAuthDataService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/")
|
||||
public class CashierController {
|
||||
|
||||
@Autowired
|
||||
private ThirdAuthDataService wxThirdAuthDataService;
|
||||
|
||||
/**
|
||||
* 小程序的公用路径,这里可以根据服务商编号做特定区分
|
||||
*
|
||||
* @param agentNo 服务商编号
|
||||
* @param fileName 文件名称
|
||||
*/
|
||||
@GetMapping("/pages/hub/{agentNo}/{fileName}")
|
||||
public String getWxAuthData(@PathVariable("agentNo") String agentNo, @PathVariable("fileName") String fileName) {
|
||||
ThirdAuthDataEntity one = wxThirdAuthDataService.lambdaQuery()
|
||||
.eq(ThirdAuthDataEntity::getAgentNo, agentNo)
|
||||
.eq(ThirdAuthDataEntity::getSubType, ThirdAuthDataEntity.SUB_TYPE_LITE)
|
||||
.eq(ThirdAuthDataEntity::getType, ThirdAuthDataEntity.TYPE_WX)
|
||||
.eq(ThirdAuthDataEntity::getAuthFileName, fileName)
|
||||
.last("limit 1")
|
||||
.one();
|
||||
|
||||
if (one != null) {
|
||||
return one.getValue();
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 公众号根目录授权
|
||||
*
|
||||
* @param fileName 文件名称
|
||||
*/
|
||||
@GetMapping("/{fileName}")
|
||||
public String getWxAuthData(@PathVariable("fileName") String fileName) {
|
||||
ThirdAuthDataEntity one = wxThirdAuthDataService.lambdaQuery()
|
||||
.eq(ThirdAuthDataEntity::getSubType, ThirdAuthDataEntity.SUB_TYPE_H5)
|
||||
.eq(ThirdAuthDataEntity::getType, ThirdAuthDataEntity.TYPE_WX)
|
||||
.eq(ThirdAuthDataEntity::getAuthFileName, fileName)
|
||||
.last("limit 1")
|
||||
.one();
|
||||
|
||||
if (one != null) {
|
||||
return one.getValue();
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.webcashier;
|
||||
|
||||
import cn.hutool.http.useragent.UserAgent;
|
||||
import cn.hutool.http.useragent.UserAgentParser;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.jeequan.jeepay.ApiField;
|
||||
import com.jeequan.jeepay.JeepayClient;
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.model.*;
|
||||
import com.jeequan.jeepay.core.utils.JeepayKit;
|
||||
import com.jeequan.jeepay.core.utils.JsonKit;
|
||||
import com.jeequan.jeepay.core.utils.SeqKit;
|
||||
import com.jeequan.jeepay.db.entity.*;
|
||||
import com.jeequan.jeepay.exception.JeepayException;
|
||||
import com.jeequan.jeepay.model.DeviceInfo;
|
||||
import com.jeequan.jeepay.model.JeepayObject;
|
||||
import com.jeequan.jeepay.model.PayOrderCreateResModel;
|
||||
import com.jeequan.jeepay.pay.ctrl.payorder.AbstractPayOrderController;
|
||||
import com.jeequan.jeepay.pay.service.PayMchNotifyService;
|
||||
import com.jeequan.jeepay.request.PayOrderCreateRequest;
|
||||
import com.jeequan.jeepay.response.PayOrderCreateResponse;
|
||||
import com.jeequan.jeepay.service.impl.*;
|
||||
import lombok.Data;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 便捷收银台controller
|
||||
*
|
||||
* @author xiaoyu
|
||||
*
|
||||
* @date 2023/3/1 17:27
|
||||
*/
|
||||
@Controller
|
||||
@RequestMapping("/api/cashierPage")
|
||||
public class CashierPageController extends AbstractPayOrderController {
|
||||
|
||||
@Autowired private PayOrderService payOrderService;
|
||||
@Autowired private SysConfigService sysConfigService;
|
||||
@Autowired private PayMchNotifyService payMchNotifyService;
|
||||
@Autowired private MchAppService mchAppService;
|
||||
@Autowired private MchInfoService mchInfoService;
|
||||
@Autowired private MchPayPassageService mchPayPassageService;
|
||||
@Autowired private MchConfigService mchConfigService;
|
||||
@Autowired private MchStoreService mchStoreService;
|
||||
|
||||
|
||||
/** 跳转到页面 **/
|
||||
@GetMapping("/{appId}/{storeId}")
|
||||
public String toPage(@PathVariable("appId") String appId, @PathVariable("storeId") String storeId){
|
||||
|
||||
try {
|
||||
|
||||
request.setAttribute("appId", appId);
|
||||
request.setAttribute("storeId", storeId);
|
||||
// 应用ID、 门店ID 解码
|
||||
appId = JeepayKit.aesDecode(appId);
|
||||
storeId = JeepayKit.aesDecode(storeId);
|
||||
|
||||
MchAppEntity mchAppEntity = mchAppService.getById(appId);
|
||||
MchInfo mchInfo = mchInfoService.getById(mchAppEntity.getMchNo());
|
||||
MchStore mchStore = mchStoreService.getById(storeId);
|
||||
|
||||
// 查询商户收银台配置信息
|
||||
CashierConfig cashierConfig = mchConfigService.queryMchCashierConfigByDefault(mchAppEntity.getMchNo(), MchConfig.SELF_CASHIER_GROUP_KEY);
|
||||
|
||||
if(cashierConfig.getState() != CS.YES){
|
||||
throw new BizException("商户未开通[便捷收银台]功能");
|
||||
}
|
||||
|
||||
// 静态CDN地址
|
||||
setPublicCdnHost();
|
||||
|
||||
request.setAttribute("cashierConfig", cashierConfig);
|
||||
request.setAttribute("mchName", mchInfo.getMchName());
|
||||
request.setAttribute("storeName", mchStore != null ? mchStore.getStoreName():"");
|
||||
|
||||
DBOEMConfig oemConfig = sysConfigService.getOemConfig();
|
||||
|
||||
// 得到所有的支付方式
|
||||
Set<String> allWaycode = new HashSet<>();
|
||||
mchPayPassageService.list(MchPayPassage.gw().eq(MchPayPassage::getMchNo, mchAppEntity.getMchNo()).eq(MchPayPassage::getAppId, mchAppEntity.getAppId()).eq(MchPayPassage::getState, CS.YES))
|
||||
.stream().forEach( r -> allWaycode.add(r.getWayCode()));
|
||||
|
||||
// 处理 wxH5支付方式 and aliwap支付方式。
|
||||
cashierConfig.processWacodesWithWxH5AndALiwap(allWaycode);
|
||||
|
||||
// 放置jsAPI
|
||||
if(allWaycode.contains(CS.PAY_WAY_CODE.WX_JSAPI) || allWaycode.contains(CS.PAY_WAY_CODE.ALI_JSAPI) || allWaycode.contains(CS.PAY_WAY_CODE.YSF_JSAPI) ){
|
||||
allWaycode.add(CS.PAY_WAY_CODE.QR_CASHIER);
|
||||
}
|
||||
|
||||
request.setAttribute("allWaycode", JSON.toJSONString(allWaycode));
|
||||
|
||||
request.setAttribute("oemConfig", oemConfig);
|
||||
|
||||
UserAgent userAgent = UserAgentParser.parse(request.getHeader("User-Agent"));
|
||||
if(userAgent.getPlatform().isMobile()){
|
||||
return "cashierPage/cashierPageH5";
|
||||
}
|
||||
return "cashierPage/cashierPage";
|
||||
|
||||
} catch (BizException e) {
|
||||
logger.error("业务异常", e);
|
||||
request.setAttribute("errMsg", e.getMessage());
|
||||
return "cashierPage/error";
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("异常", e);
|
||||
request.setAttribute("errMsg", e.getMessage());
|
||||
return "cashierPage/error";
|
||||
}
|
||||
}
|
||||
|
||||
/* 跳转到支付成功页面 **/
|
||||
@GetMapping("/paySuccess")
|
||||
public String toReturnSuccessPage(){
|
||||
// 静态CDN地址
|
||||
setPublicCdnHost();
|
||||
return "cashier/returnPage";
|
||||
}
|
||||
|
||||
/** 支付方式的转换 **/
|
||||
@PostMapping ("/convertPayway/{appId}/{storeId}")
|
||||
@ResponseBody
|
||||
public ApiRes convertPayway(@PathVariable("appId") String appId, @PathVariable("storeId") String storeId){
|
||||
PayOrderCreateRequest request = new PayOrderCreateRequest();
|
||||
PayOrderCreateReqModelAddStoreId model = new PayOrderCreateReqModelAddStoreId();
|
||||
// 需要转换的支付方式
|
||||
appId = JeepayKit.aesDecode(appId);
|
||||
String wayCode = getValStringRequired("wayCode");
|
||||
// 需要的方式(url跳转 or 二维码展示)
|
||||
String payDataType = getValString("payDataType");
|
||||
// 买家备注
|
||||
String buyerRemark = getValString("buyerRemark");
|
||||
MchAppEntity mchAppEntity = mchAppService.getById(appId);
|
||||
|
||||
if(StringUtils.isNotBlank(payDataType)){ // payDataType
|
||||
model.setChannelExtra(JsonKit.newJson("payDataType", payDataType).toString());
|
||||
}
|
||||
// 任意一种签名方式
|
||||
// model.setSignType(appSignType.get(0));
|
||||
// 支付方式
|
||||
model.setWayCode(wayCode);
|
||||
// 订单金额
|
||||
model.setAmount(getRequiredAmountL("amount"));
|
||||
// 商户订单号
|
||||
model.setMchOrderNo(SeqKit.genMhoOrderId());
|
||||
// 门店ID
|
||||
model.setStoreId(JeepayKit.aesDecode(storeId));
|
||||
// appId
|
||||
model.setAppId(appId);
|
||||
// 商户号
|
||||
model.setMchNo(mchAppEntity.getMchNo());
|
||||
// 货币代码
|
||||
model.setCurrency("CNY");
|
||||
model.setSubject("收银台订单 [" + mchAppEntity.getMchNo() + "]");
|
||||
model.setBody("收银台订单 [" + mchAppEntity.getMchNo() + "]");
|
||||
// 失效时间 15分钟 60 * 15
|
||||
String expiredTime = "900";
|
||||
model.setExpiredTime(expiredTime);
|
||||
|
||||
model.setBuyerRemark(buyerRemark);
|
||||
DBApplicationConfig dbApplicationConfig = sysConfigService.getDBApplicationConfig();
|
||||
// model.setNotifyUrl(dbApplicationConfig.getMchSiteUrl() + "/api/anon/paytestNotify/payOrder"); //回调地址
|
||||
|
||||
model.setPas(SysConfigService.PLATFORM_API_SECRET); // 通信秘钥
|
||||
request.setBizModel(model);
|
||||
|
||||
|
||||
|
||||
// 判断是否 需要 h5 ==》 小程序
|
||||
|
||||
CashierConfig cashierConfig = mchConfigService.queryMchCashierConfigByDefault(mchAppEntity.getMchNo(), MchConfig.SELF_CASHIER_GROUP_KEY);
|
||||
|
||||
|
||||
// 得到所有的支付方式
|
||||
Set<String> allWaycode = new HashSet<>();
|
||||
mchPayPassageService.list(MchPayPassage.gw().select(MchPayPassage::getWayCode)
|
||||
.eq(MchPayPassage::getMchNo, mchAppEntity.getMchNo()).eq(MchPayPassage::getAppId, mchAppEntity.getAppId()).eq(MchPayPassage::getState, CS.YES))
|
||||
.stream().forEach( r -> allWaycode.add(r.getWayCode()));
|
||||
|
||||
// 商户可用 < 合并 > 配置
|
||||
List<String> wxH5PaywayListWithIntersectionDistinct = cashierConfig.getWxH5PaywayListWithIntersectionDistinct(allWaycode);
|
||||
List<String> aliWapPaywayListWithIntersectionDistinct = cashierConfig.getAliWapPaywayListWithIntersectionDistinct(allWaycode);
|
||||
|
||||
// 微信H5支付方式 && 配置的支付方式首先类型并不是 微信H5 , 那么需要做转换。
|
||||
if(CS.PAY_WAY_CODE.WX_H5.equals(wayCode) && !wxH5PaywayListWithIntersectionDistinct.isEmpty() && !wxH5PaywayListWithIntersectionDistinct.get(0).equals(CS.PAY_WAY_CODE.WX_H5)){
|
||||
|
||||
// 微信小程序URL
|
||||
model.setChannelExtra(JsonKit.newJson("entryLiteType", QRCodeParams.ENTRY_LITE_WXH5).toString());
|
||||
model.setWayCode(CS.PAY_WAY_CODE.QR_CASHIER);
|
||||
}
|
||||
|
||||
// 支付宝wap 支付方式 && 配置的支付方式首先类型并不是 支付宝wap , 那么需要做转换。
|
||||
if(CS.PAY_WAY_CODE.ALI_WAP.equals(wayCode) && !aliWapPaywayListWithIntersectionDistinct.isEmpty() && !aliWapPaywayListWithIntersectionDistinct.get(0).equals(CS.PAY_WAY_CODE.ALI_WAP)){
|
||||
|
||||
|
||||
// 生活号的形式
|
||||
if(aliWapPaywayListWithIntersectionDistinct.get(0).equals(CS.PAY_WAY_CODE.ALI_JSAPI)){
|
||||
|
||||
model.setChannelExtra(JsonKit.newJson("entryLiteType", QRCodeParams.ENTRY_LITE_ALIJSAPIH5).toString());
|
||||
model.setWayCode(CS.PAY_WAY_CODE.QR_CASHIER);
|
||||
|
||||
}else{ // 支付宝小程序URL
|
||||
model.setChannelExtra(JsonKit.newJson("entryLiteType", QRCodeParams.ENTRY_LITE_ALIH5).toString());
|
||||
model.setWayCode(CS.PAY_WAY_CODE.QR_CASHIER);
|
||||
}
|
||||
}
|
||||
|
||||
JeepayClient jeepayClient = new JeepayClient(dbApplicationConfig.getPaySiteUrl(), mchAppEntity.getAppSecret());
|
||||
|
||||
try {
|
||||
PayOrderCreateResponse response = jeepayClient.execute(request);
|
||||
if(response.getCode() != 0){
|
||||
throw new BizException(response.getMsg());
|
||||
}
|
||||
|
||||
// 存入下单超时时间
|
||||
PayOrderCreateResModel resModel = response.get();
|
||||
JSONObject resJson = (JSONObject) JSONObject.toJSON(resModel);
|
||||
resJson.put("expiredTime", expiredTime);
|
||||
return ApiRes.ok(resJson);
|
||||
} catch (JeepayException e) {
|
||||
throw new BizException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** 查单 **/
|
||||
@PostMapping("/orderState/{payOrderId}")
|
||||
@ResponseBody
|
||||
public ApiRes orderState(@PathVariable("payOrderId") String payOrderId){
|
||||
|
||||
PayOrder payOrder = payOrderService.getById(payOrderId);
|
||||
|
||||
JSONObject result = new JSONObject();
|
||||
result.put("state", payOrder.getState());
|
||||
|
||||
// 支付成功
|
||||
if(payOrder.getState() == PayOrder.STATE_SUCCESS){
|
||||
|
||||
// 包含同步回调地址
|
||||
if(StringUtils.isNotBlank(payOrder.getReturnUrl())){
|
||||
MchAppEntity mchAppEntity = mchAppService.getById(payOrder.getAppId());
|
||||
result.put("returnUrl", payMchNotifyService.createReturnUrl(payOrder, mchAppEntity.getAppSecret()));
|
||||
|
||||
}else{
|
||||
result.put("returnUrl", "/api/cashierPage/paySuccess");
|
||||
}
|
||||
}
|
||||
|
||||
return ApiRes.ok(result);
|
||||
}
|
||||
|
||||
/** 与阿里云sdk一样, 直接extends PayOrderCreateReqModel无法传入到接口中对应的参数。 需要把所有的参数放进来。。。 = = 。 **/
|
||||
@Data
|
||||
public static class PayOrderCreateReqModelAddStoreId extends JeepayObject {
|
||||
|
||||
@ApiField("pas")
|
||||
private String pas;
|
||||
|
||||
@ApiField("storeId")
|
||||
private String storeId;
|
||||
|
||||
private static final long serialVersionUID = -3998573128290306948L;
|
||||
|
||||
@ApiField("mchNo")
|
||||
private String mchNo; // 商户号
|
||||
@ApiField("appId")
|
||||
private String appId; // 应用ID
|
||||
@ApiField("mchOrderNo")
|
||||
String mchOrderNo; // 商户订单号
|
||||
@ApiField("wayCode")
|
||||
String wayCode; // 支付方式
|
||||
@ApiField("amount")
|
||||
Long amount; // 支付金额
|
||||
@ApiField("currency")
|
||||
String currency; // 货币代码,当前只支持cny
|
||||
@ApiField("clientIp")
|
||||
String clientIp; // 客户端IP
|
||||
@ApiField("subject")
|
||||
String subject; // 商品标题
|
||||
@ApiField("body")
|
||||
String body; // 商品描述
|
||||
@ApiField("channelBizData")
|
||||
String channelBizData; // 渠道业务参数
|
||||
@ApiField("notifyUrl")
|
||||
String notifyUrl; // 异步通知地址
|
||||
@ApiField("returnUrl")
|
||||
String returnUrl; // 跳转通知地址
|
||||
@ApiField("expiredTime")
|
||||
String expiredTime; // 订单失效时间
|
||||
@ApiField("channelExtra")
|
||||
String channelExtra; // 特定渠道额外支付参数
|
||||
@ApiField("channelUser")
|
||||
String channelUser; // 渠道用户标识,如微信openId,支付宝账号
|
||||
@ApiField("extParam")
|
||||
String extParam; // 商户扩展参数
|
||||
@ApiField("divisionMode")
|
||||
private Byte divisionMode; // 分账模式: 0-该笔订单不允许分账[默认], 1-支付成功按配置自动完成分账, 2-商户手动分账(解冻商户金额)
|
||||
@ApiField("sellerRemark")
|
||||
String sellerRemark; // 卖家备注
|
||||
@ApiField("buyerRemark")
|
||||
String buyerRemark; // 买家备注
|
||||
@ApiField("deviceInfo")
|
||||
private DeviceInfo deviceInfo; // 设备信息
|
||||
|
||||
}
|
||||
|
||||
public void setPublicCdnHost(){
|
||||
// 静态CDN地址
|
||||
DBApplicationConfig dbApplicationConfig = sysConfigService.getDBApplicationConfig();
|
||||
request.setAttribute("staticCdnHost", dbApplicationConfig.getStaticCdnHost());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.webcashier;
|
||||
|
||||
import cn.hutool.http.useragent.UserAgent;
|
||||
import cn.hutool.http.useragent.UserAgentParser;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.model.*;
|
||||
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
|
||||
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
|
||||
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRS;
|
||||
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.QrCashierOrderRQ;
|
||||
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.QrCashierOrderRS;
|
||||
import com.jeequan.jeepay.core.utils.AmountUtil;
|
||||
import com.jeequan.jeepay.core.utils.DateKit;
|
||||
import com.jeequan.jeepay.core.utils.JeepayKit;
|
||||
import com.jeequan.jeepay.core.utils.JsonKit;
|
||||
import com.jeequan.jeepay.db.entity.MchAppEntity;
|
||||
import com.jeequan.jeepay.db.entity.MchConfig;
|
||||
import com.jeequan.jeepay.db.entity.MchPayPassage;
|
||||
import com.jeequan.jeepay.db.entity.PayOrder;
|
||||
import com.jeequan.jeepay.pay.ctrl.payorder.AbstractPayOrderController;
|
||||
import com.jeequan.jeepay.pay.service.PayMchNotifyService;
|
||||
import com.jeequan.jeepay.service.impl.*;
|
||||
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* WEB收银台controller
|
||||
*
|
||||
* @author terrfly
|
||||
*
|
||||
* @date 2022/7/1 17:27
|
||||
*/
|
||||
@Controller
|
||||
@RequestMapping("/api/webCashier")
|
||||
public class WebCashierController extends AbstractPayOrderController {
|
||||
|
||||
@Autowired private PayOrderService payOrderService;
|
||||
@Autowired private SysConfigService sysConfigService;
|
||||
@Autowired private PayMchNotifyService payMchNotifyService;
|
||||
@Autowired private MchAppService mchAppService;
|
||||
@Autowired private MchPayPassageService mchPayPassageService;
|
||||
@Autowired private MchConfigService mchConfigService;
|
||||
@Autowired private ConfigContextQueryService configContextQueryService;
|
||||
|
||||
/** 跳转到页面 **/
|
||||
@GetMapping("/{payOrderToken}")
|
||||
public String toPage(@PathVariable("payOrderToken") String payOrderToken){
|
||||
|
||||
try {
|
||||
PayOrder payOrder = getPayOrder(payOrderToken);
|
||||
request.setAttribute("payOrder", payOrder);
|
||||
request.setAttribute("payOrderToken", payOrderToken);
|
||||
request.setAttribute("payAmount", AmountUtil.convertCent2Dollar(payOrder.getAmount()));
|
||||
|
||||
// 查询商户收银台配置信息
|
||||
CashierConfig cashierConfig = mchConfigService.queryMchCashierConfigByDefault(payOrder.getMchNo(), MchConfig.WEB_CASHIER_GROUP_KEY);
|
||||
|
||||
if(cashierConfig.getState() != CS.YES){
|
||||
throw new BizException("商户未开通[web收银台]功能");
|
||||
}
|
||||
|
||||
|
||||
DBOEMConfig oemConfig = sysConfigService.getOemConfig();
|
||||
|
||||
// 静态CDN地址
|
||||
setPublicCdnHost();
|
||||
|
||||
// 得到所有的支付方式
|
||||
Set<String> allWaycode = new HashSet<>();
|
||||
mchPayPassageService.list(MchPayPassage.gw().eq(MchPayPassage::getMchNo, payOrder.getMchNo()).eq(MchPayPassage::getAppId, payOrder.getAppId()).eq(MchPayPassage::getState, CS.YES))
|
||||
.stream().forEach( r -> allWaycode.add(r.getWayCode()));
|
||||
|
||||
// 处理 wxH5支付方式 and aliwap支付方式。
|
||||
cashierConfig.processWacodesWithWxH5AndALiwap(allWaycode);
|
||||
|
||||
// 放置jsAPI
|
||||
if(allWaycode.contains(CS.PAY_WAY_CODE.WX_JSAPI) || allWaycode.contains(CS.PAY_WAY_CODE.ALI_JSAPI) || allWaycode.contains(CS.PAY_WAY_CODE.YSF_JSAPI) ){
|
||||
allWaycode.add(CS.PAY_WAY_CODE.QR_CASHIER);
|
||||
}
|
||||
|
||||
request.setAttribute("allWaycode", JSON.toJSONString(allWaycode));
|
||||
|
||||
request.setAttribute("oemConfig", oemConfig);
|
||||
|
||||
request.setAttribute("cashierConfig", cashierConfig);
|
||||
|
||||
// 倒计时 时间, 单位: s (不存在负值的情况)
|
||||
long downSecond = (payOrder.getExpiredTime().getTime() - DateKit.currentTimeMillis()) / 1000;
|
||||
request.setAttribute("downSecond", downSecond < 0 ? 0L : downSecond);
|
||||
|
||||
UserAgent userAgent = UserAgentParser.parse(request.getHeader("User-Agent"));
|
||||
if(userAgent.getPlatform().isMobile()){
|
||||
return "webcashier/webCashierH5";
|
||||
}
|
||||
return "webcashier/webCashier";
|
||||
|
||||
} catch (BizException e) {
|
||||
logger.error("业务异常", e);
|
||||
request.setAttribute("errMsg", e.getMessage());
|
||||
return "webcashier/error";
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("异常", e);
|
||||
request.setAttribute("errMsg", e.getMessage());
|
||||
return "webcashier/error";
|
||||
}
|
||||
}
|
||||
|
||||
/* 跳转到支付成功页面 **/
|
||||
@GetMapping("/paySuccess")
|
||||
public String toReturnSuccessPage(){
|
||||
|
||||
// 静态CDN地址
|
||||
setPublicCdnHost();
|
||||
return "cashier/returnPage";
|
||||
}
|
||||
|
||||
/** 支付方式的转换 **/
|
||||
@PostMapping ("/convertPayway/{payOrderToken}")
|
||||
@ResponseBody
|
||||
public ApiRes convertPayway(@PathVariable("payOrderToken") String payOrderToken){
|
||||
|
||||
// 需要转换的支付方式
|
||||
String wayCode = getValStringRequired("wayCode");
|
||||
|
||||
// 需要的方式(url跳转 or 二维码展示)
|
||||
String payDataType = getValString("payDataType");
|
||||
|
||||
PayOrder payOrder = getPayOrder(payOrderToken);
|
||||
String payOrderId = payOrder.getPayOrderId();
|
||||
|
||||
DBApplicationConfig dbApplicationConfig = sysConfigService.getDBApplicationConfig();
|
||||
|
||||
MchAppEntity mchAppEntity = mchAppService.getById(payOrder.getAppId());
|
||||
List<String> appSignType = JSON.parseArray(mchAppEntity.getAppSignType(), String.class);
|
||||
|
||||
// 聚合扫码
|
||||
if(CS.PAY_WAY_CODE.QR_CASHIER.equals(wayCode)){
|
||||
|
||||
boolean updateflag = payOrderService.update(new LambdaUpdateWrapper<PayOrder>()
|
||||
.set(PayOrder::getWayCode, CS.PAY_WAY_CODE.QR_CASHIER)
|
||||
.eq(PayOrder::getPayOrderId, payOrder.getPayOrderId()).eq(PayOrder::getState, PayOrder.STATE_INIT));
|
||||
|
||||
if(!updateflag){
|
||||
throw new BizException("更新订单状态异常");
|
||||
}
|
||||
|
||||
QrCashierOrderRS result = new QrCashierOrderRS();
|
||||
String payUrl = dbApplicationConfig.genUniJsapiPayUrl(QRCodeParams.TYPE_PAY_ORDER, QRCodeParams.ENTRY_PAGE_H5, payOrderId, payOrder.getIsvNo());
|
||||
|
||||
// 需要二维码参数
|
||||
if(CS.PAY_DATA_TYPE.CODE_IMG_URL.equals(payDataType)){
|
||||
result.setCodeImgUrl(dbApplicationConfig.genScanImgUrl(payUrl));
|
||||
}else{
|
||||
result.setPayUrl(payUrl);
|
||||
}
|
||||
|
||||
result.setPayDataType(result.buildPayDataType());
|
||||
result.setPayData(result.buildPayData());
|
||||
return ApiRes.ok(result);
|
||||
|
||||
}else{ // 转换为: 其他的支付方式类型
|
||||
|
||||
|
||||
// 判断是否 需要 h5 ==》 小程序
|
||||
|
||||
CashierConfig cashierConfig = mchConfigService.queryMchCashierConfigByDefault(payOrder.getMchNo(), MchConfig.WEB_CASHIER_GROUP_KEY);
|
||||
|
||||
// 得到所有的支付方式
|
||||
Set<String> allWaycode = new HashSet<>();
|
||||
mchPayPassageService.list(MchPayPassage.gw().select(MchPayPassage::getWayCode)
|
||||
.eq(MchPayPassage::getMchNo, mchAppEntity.getMchNo()).eq(MchPayPassage::getAppId, mchAppEntity.getAppId()).eq(MchPayPassage::getState, CS.YES)).forEach(r -> allWaycode.add(r.getWayCode()));
|
||||
|
||||
// 商户可用 < 合并 > 配置
|
||||
List<String> wxH5PaywayListWithIntersectionDistinct = cashierConfig.getWxH5PaywayListWithIntersectionDistinct(allWaycode);
|
||||
List<String> aliWapPaywayListWithIntersectionDistinct = cashierConfig.getAliWapPaywayListWithIntersectionDistinct(allWaycode);
|
||||
|
||||
// 微信H5支付方式 && 配置的支付方式首先类型并不是 微信H5 , 那么需要做转换。
|
||||
if(CS.PAY_WAY_CODE.WX_H5.equals(wayCode) && !wxH5PaywayListWithIntersectionDistinct.isEmpty() && !wxH5PaywayListWithIntersectionDistinct.get(0).equals(CS.PAY_WAY_CODE.WX_H5)){
|
||||
|
||||
MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(mchAppEntity.getMchNo(), mchAppEntity.getAppId(),payOrder.getMchExtNo());
|
||||
|
||||
QrCashierOrderRQ bizRQ = new QrCashierOrderRQ();
|
||||
bizRQ.setSignType(appSignType.get(0)); // 任意一种签名方式
|
||||
bizRQ.setMchNo(payOrder.getMchNo()); //
|
||||
bizRQ.setAppId(payOrder.getAppId()); //
|
||||
|
||||
// 微信小程序URL
|
||||
bizRQ.setEntryLiteType(QRCodeParams.ENTRY_LITE_WXH5);
|
||||
|
||||
ApiRes apiRes = qrCashierPayWay(bizRQ, mchAppConfigContext, payOrderId,null);
|
||||
return commonAutoBuildRes(apiRes);
|
||||
|
||||
}
|
||||
|
||||
// 支付宝wap 支付方式 && 配置的支付方式首先类型并不是 支付宝wap , 那么需要做转换。
|
||||
if(CS.PAY_WAY_CODE.ALI_WAP.equals(wayCode) && !aliWapPaywayListWithIntersectionDistinct.isEmpty() && !aliWapPaywayListWithIntersectionDistinct.get(0).equals(CS.PAY_WAY_CODE.ALI_WAP)){
|
||||
|
||||
MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(mchAppEntity.getMchNo(), mchAppEntity.getAppId(),payOrder.getMchExtNo());
|
||||
|
||||
QrCashierOrderRQ bizRQ = new QrCashierOrderRQ();
|
||||
bizRQ.setSignType(appSignType.get(0)); // 任意一种签名方式
|
||||
bizRQ.setMchNo(payOrder.getMchNo()); //
|
||||
bizRQ.setAppId(payOrder.getAppId()); //
|
||||
|
||||
// 生活号的形式
|
||||
if(aliWapPaywayListWithIntersectionDistinct.get(0).equals(CS.PAY_WAY_CODE.ALI_JSAPI)){
|
||||
|
||||
bizRQ.setEntryLiteType(QRCodeParams.ENTRY_LITE_ALIJSAPIH5);
|
||||
|
||||
}else{ // 支付宝小程序URL
|
||||
|
||||
bizRQ.setEntryLiteType(QRCodeParams.ENTRY_LITE_ALIH5);
|
||||
}
|
||||
|
||||
ApiRes apiRes = this.qrCashierPayWay(bizRQ, mchAppConfigContext,payOrderId,null);
|
||||
return commonAutoBuildRes(apiRes);
|
||||
|
||||
}
|
||||
|
||||
// 明确支付方式的: 下单:
|
||||
UnifiedOrderRQ rq = new UnifiedOrderRQ();
|
||||
if(StringUtils.isNotBlank(payDataType)){ // payDataType
|
||||
rq.setChannelExtra(JsonKit.newJson("payDataType", payDataType).toString());
|
||||
}
|
||||
|
||||
rq.setSignType(appSignType.get(0)); // 任意一种签名方式
|
||||
rq.setWayCode(wayCode);
|
||||
ApiRes apiRes = this.unifiedOrder(wayCode, rq.buildBizRQ(), payOrder,null);
|
||||
return commonAutoBuildRes(apiRes);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** 查单 **/
|
||||
@PostMapping("orderState/{payOrderToken}")
|
||||
@ResponseBody
|
||||
public ApiRes orderState(@PathVariable("payOrderToken") String payOrderToken){
|
||||
|
||||
String payOrderId = JeepayKit.aesDecode(payOrderToken);
|
||||
PayOrder payOrder = payOrderService.getById(payOrderId);
|
||||
|
||||
JSONObject result = new JSONObject();
|
||||
result.put("state", payOrder.getState());
|
||||
|
||||
// 支付成功
|
||||
if(payOrder.getState() == PayOrder.STATE_SUCCESS){
|
||||
|
||||
// 包含同步回调地址
|
||||
if(StringUtils.isNotBlank(payOrder.getReturnUrl())){
|
||||
MchAppEntity mchAppEntity = mchAppService.getById(payOrder.getAppId());
|
||||
result.put("returnUrl", payMchNotifyService.createReturnUrl(payOrder, mchAppEntity.getAppSecret()));
|
||||
|
||||
}else{
|
||||
result.put("returnUrl", "/api/webCashier/paySuccess");
|
||||
}
|
||||
}
|
||||
|
||||
return ApiRes.ok(result);
|
||||
}
|
||||
|
||||
|
||||
private PayOrder getPayOrder(String payOrderToken){
|
||||
|
||||
String payOrderId = JeepayKit.aesDecode(payOrderToken);
|
||||
PayOrder payOrder = payOrderService.getById(payOrderId);
|
||||
|
||||
if(payOrder == null || payOrder.getState() != PayOrder.STATE_INIT){
|
||||
throw new BizException("收银台订单不存在或状态不正确");
|
||||
}
|
||||
|
||||
return payOrder;
|
||||
}
|
||||
|
||||
|
||||
/** 统一处理 buildPayData ( UnifiedOrderRS ) **/
|
||||
private ApiRes commonAutoBuildRes(ApiRes apiRes){
|
||||
|
||||
UnifiedOrderRS bizRes = (UnifiedOrderRS)apiRes.getData();
|
||||
if(bizRes == null){
|
||||
return apiRes;
|
||||
}
|
||||
bizRes.setPayDataType(bizRes.buildPayDataType());
|
||||
bizRes.setPayData(bizRes.buildPayData());
|
||||
return ApiRes.ok(bizRes);
|
||||
}
|
||||
|
||||
public void setPublicCdnHost(){
|
||||
// 静态CDN地址
|
||||
DBApplicationConfig dbApplicationConfig = sysConfigService.getDBApplicationConfig();
|
||||
request.setAttribute("staticCdnHost", dbApplicationConfig.getStaticCdnHost());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.wxmp;
|
||||
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.interfaces.wxmp.IWxMpTemplateMsgService;
|
||||
import com.jeequan.jeepay.core.model.DBWxMpConfig;
|
||||
import com.jeequan.jeepay.core.model.wx.wxmp.WxUserInfo;
|
||||
import com.jeequan.jeepay.pay.ctrl.CommonController;
|
||||
import com.jeequan.jeepay.service.impl.MchWxmpUserService;
|
||||
import com.jeequan.jeepay.service.impl.SysConfigService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
/**
|
||||
* 接收微信服务器消息
|
||||
*
|
||||
* @author zx
|
||||
* @site https://www.jeepay.vip
|
||||
* @date 2021/10/15 9:37
|
||||
*/
|
||||
@Controller
|
||||
@RequestMapping("/api/wxmpCallback/msg")
|
||||
public class WxmpServerController extends CommonController {
|
||||
|
||||
@Autowired private SysConfigService sysConfigService;
|
||||
@Autowired private MchWxmpUserService mchWxmpUserService;
|
||||
@Autowired private IWxMpTemplateMsgService wxMpMessageService;
|
||||
|
||||
/** 微信授权回调回的页面(保存商户接收消息用户)*/
|
||||
@RequestMapping("/wxAuthRedirect/{mchNo}/{sysUserId}")
|
||||
public String wxAuthRedirect(@PathVariable("mchNo") String mchNo, @PathVariable("sysUserId") Long sysUserId){
|
||||
|
||||
try {
|
||||
|
||||
String code = getValStringRequired("code");
|
||||
|
||||
DBWxMpConfig wxMpConfig = sysConfigService.getDBWxMpConfig();
|
||||
if (wxMpConfig == null) {
|
||||
throw new BizException("未正确配置微信公众号消息配置项");
|
||||
}
|
||||
|
||||
WxUserInfo wxUserInfo = wxMpMessageService.code2WxUserInfo(code, wxMpConfig);
|
||||
|
||||
// 初始化数据
|
||||
mchWxmpUserService.saveOrUpdateRecord(mchNo, sysUserId, wxMpConfig.getWxAppId(), wxUserInfo.getOpenid(), wxUserInfo.getNickname());
|
||||
|
||||
request.setAttribute("wxmpQrUrl", wxMpConfig.getWxmpUrl());
|
||||
|
||||
}catch (BizException e) {
|
||||
request.setAttribute("errMsg", e.getMessage());
|
||||
logger.error("wxAuthRedirect系统异常:", e);
|
||||
}catch (Exception e) {
|
||||
request.setAttribute("errMsg", StringUtils.defaultIfBlank(e.getMessage(), "系统异常"));
|
||||
logger.error("wxAuthRedirect系统异常:", e);
|
||||
}
|
||||
|
||||
return "mchauth/index";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,391 @@
|
||||
/**
|
||||
* cljpay.com
|
||||
* Copyright (C) 2015-2023 All Rights Reserved.
|
||||
*/package com.jeequan.jeepay.pay.ctrl.yly;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.jeequan.jeepay.core.constants.ApiCodeEnum;
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.entity.MchApp;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.model.ApiRes;
|
||||
import com.jeequan.jeepay.core.model.device.YlyParams;
|
||||
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
|
||||
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRS;
|
||||
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.AutoBarOrderRQ;
|
||||
import com.jeequan.jeepay.core.utils.AmountUtil;
|
||||
import com.jeequan.jeepay.core.utils.Base64;
|
||||
import com.jeequan.jeepay.core.utils.JeepayKit;
|
||||
import com.jeequan.jeepay.core.utils.SeqKit;
|
||||
import com.jeequan.jeepay.db.entity.*;
|
||||
import com.jeequan.jeepay.model.DeviceInfo;
|
||||
import com.jeequan.jeepay.pay.ctrl.payorder.AbstractPayOrderController;
|
||||
import com.jeequan.jeepay.pay.ctrl.yly.sdk.RequestMethod;
|
||||
import com.jeequan.jeepay.service.impl.DeviceProvideConfigService;
|
||||
import com.jeequan.jeepay.service.impl.MchStoreDeviceService;
|
||||
import com.jeequan.jeepay.service.impl.PayWayService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.GCMParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Signature;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
|
||||
/**
|
||||
* @author dingzhiwei
|
||||
* @version
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/yly")
|
||||
public class YlyController extends AbstractPayOrderController {
|
||||
|
||||
@Autowired private DeviceProvideConfigService deviceProvideConfigService;
|
||||
@Autowired private MchStoreDeviceService mchStoreDeviceService;
|
||||
@Autowired private PayWayService payWayService;
|
||||
|
||||
/**
|
||||
* 易联云K8通知
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping("/notify")
|
||||
public JSONObject ylNotify(HttpServletRequest request) {
|
||||
JSONObject object = new JSONObject();
|
||||
object.put("message", "ok");
|
||||
log.info("易联云K8消息:{}", getReqParamJSON());
|
||||
JSONObject reqObj = getReqParamJSON();
|
||||
if(reqObj == null || reqObj.isEmpty()) {
|
||||
return object;
|
||||
}
|
||||
String ciphertext = reqObj.getString("ciphertext");
|
||||
String nonce = reqObj.getString("nonce");
|
||||
String tag = reqObj.getString("tag");
|
||||
String iv = reqObj.getString("iv");
|
||||
String additional_data = reqObj.getString("additional_data");
|
||||
String signature = reqObj.getString("signature");
|
||||
// 解密key
|
||||
String key = "tfa62db9e93353f12ad94988dc9322em";
|
||||
try {
|
||||
String plaintext = decrypte(Base64.decodeBase64String(ciphertext), key.getBytes(), iv.getBytes(), Base64.decodeBase64String(tag));
|
||||
log.info("报文:{}", plaintext);
|
||||
} catch (Exception e) {
|
||||
log.error("解密异常,{}", e);
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
@RequestMapping("/oauth2")
|
||||
public JSONObject oauth2(HttpServletRequest request) {
|
||||
JSONObject object = new JSONObject();
|
||||
object.put("message", "ok");
|
||||
log.info("易联云K8消息:{}", getReqParamJSON());
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
// TODO 验证签名暂未实现
|
||||
|
||||
/**
|
||||
* AES-GCM-256对称解密
|
||||
* @param encryptedBytes
|
||||
* @param keyBytes
|
||||
* @param ivBytes
|
||||
* @param tagBytes
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
private String decrypte(byte[] encryptedBytes, byte[] keyBytes, byte[] ivBytes, byte[] tagBytes) throws Exception {
|
||||
SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");
|
||||
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
|
||||
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, ivBytes);
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKey, gcmParameterSpec);
|
||||
cipher.updateAAD("transaction".getBytes(StandardCharsets.UTF_8));
|
||||
cipher.update(encryptedBytes);
|
||||
byte[] decryptedBytes = cipher.doFinal(tagBytes);
|
||||
String decryptedString = new String(decryptedBytes, StandardCharsets.UTF_8);
|
||||
byte[] content = Base64.decodeBase64String(decryptedString);
|
||||
return new String(content);
|
||||
}
|
||||
|
||||
// 以下代码为临时测试
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
String clientId = "1071822737";
|
||||
String clientSecret = "9fa62db9e93353f09ad94988dc9322ee";
|
||||
|
||||
String machine_code = "4004849730";
|
||||
String secret = "623550139267";
|
||||
|
||||
String result;
|
||||
|
||||
RequestMethod.getInstance().init(clientId, clientSecret);
|
||||
|
||||
//result = RequestMethod.getInstance().getCodeOpen(URLEncoder.encode("https://jeepay.natapp4.cc/api/yly/oauth2"));
|
||||
//System.out.println("getCodeOpen = " + result);
|
||||
|
||||
//result = RequestMethod.getInstance().scanCodeModel(machine_code, secret);
|
||||
//System.out.println("scancodemodel = " + result);
|
||||
// String token = RequestMethod.getInstance().getAccessToken();
|
||||
|
||||
String accessToken = "40037cf4c0344b3f990a77eb9c116030"; // 自有 30天失效,到2023-10-19
|
||||
// String accessToken = "ca236d5254464d60b5b9832c3ef3fd92"; // 30天失效,到2023-10-19
|
||||
|
||||
// 状态获取
|
||||
result = RequestMethod.getInstance().printerGetPrintStatus(accessToken, machine_code);
|
||||
System.out.println("/printer/getprintstatus = " + result);
|
||||
|
||||
// 设置打印Logo
|
||||
//result = RequestMethod.getInstance().printSetIcon(accessToken, machine_code, "http://jeequan.oss-cn-beijing.aliyuncs.com/jeepay/img/jeepay_logo.png");
|
||||
//System.out.println("/printer/printSetIcon = " + result);
|
||||
|
||||
// 指令
|
||||
String content = "";
|
||||
// 支付下发
|
||||
// content = "<JSON>{\"order_payment\":{\"pay_amount\":\"0.03\",\"pay_amount_source\":3},\"processed_state\":0}</JSON><VI>040</VI><VI>043</VI><VD>0.03</VD><VI>032</VI>";
|
||||
|
||||
// 支付成功
|
||||
// content = "<VI>033</VI><VI>041</VI><VI>031</VI><VD>0.03</VD><VI>032</VI>"; // 微信成功收款0.03元
|
||||
content = "<VI>033</VI><VA>0,100</VA>"; // 微信收款100元
|
||||
// 文本打印
|
||||
result = RequestMethod.getInstance().printIndex(accessToken, machine_code, URLEncoder.encode(content), System.nanoTime()+"");
|
||||
System.out.println("/printer/printIndex = " + result);
|
||||
|
||||
// 图片打印
|
||||
// result = RequestMethod.getInstance().picturePrintIndex(accessToken, machine_code, "http://jeequan.oss-cn-beijing.aliyuncs.com/jeepay/img/jeepay_logo.png", SeqKit.genMhoRefundOrderId());
|
||||
// System.out.println("/pictureprint/index = " + result);
|
||||
|
||||
// 取消全部打印
|
||||
// result = RequestMethod.getInstance().printCancelAll(accessToken, machine_code);
|
||||
// System.out.println("/printer/cancelall = " + result);
|
||||
|
||||
// K8关键词设置
|
||||
JSONArray array = new JSONArray();
|
||||
array.add("计全支付");
|
||||
result = RequestMethod.getInstance().printerSetKeywords(accessToken, machine_code, "order_payment", "white_list", array.toJSONString());
|
||||
// System.out.println("/printer/setkeywords = " + result);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** 支付下单推送信息 **/
|
||||
@RequestMapping("/pay/{configId}")
|
||||
public JSONObject pay(@PathVariable("configId") String configId) {
|
||||
|
||||
String logPrefix = "易联云K8支付";
|
||||
JSONObject result = new JSONObject();
|
||||
result.put("message", "ok");
|
||||
JSONObject reqParamJSON = getReqParamJSON();
|
||||
|
||||
log.info("{} 推送消息:{}", logPrefix, reqParamJSON);
|
||||
if(reqParamJSON == null || reqParamJSON.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
String ciphertext = reqParamJSON.getString("ciphertext");
|
||||
String nonce = reqParamJSON.getString("nonce");
|
||||
String tag = reqParamJSON.getString("tag");
|
||||
String iv = reqParamJSON.getString("iv");
|
||||
String additional_data = reqParamJSON.getString("additional_data");
|
||||
String signature = reqParamJSON.getString("signature");
|
||||
|
||||
try {
|
||||
|
||||
// 查询对应打印设备厂商
|
||||
DeviceProvideConfig provideConfig = deviceProvideConfigService.getOne(DeviceProvideConfig.gw()
|
||||
.eq(DeviceProvideConfig::getConfigId, configId)
|
||||
.eq(DeviceProvideConfig::getDeviceType, DeviceProvideConfig.DEVICE_TYPE_PRINTER)
|
||||
.eq(DeviceProvideConfig::getState, CS.YES)
|
||||
.eq(DeviceProvideConfig::getProvider, CS.DEVICE_PROVIDER.YLY)
|
||||
);
|
||||
|
||||
if (provideConfig == null || StringUtils.isEmpty(provideConfig.getProviderParams())) {
|
||||
log.info("{} 厂商信息不存在:configId={}", logPrefix, configId);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 厂商信息获取
|
||||
YlyParams.ProviderParams providerParams = getParams(JSONObject.parseObject(provideConfig.getProviderParams()));
|
||||
|
||||
// 验签
|
||||
boolean verify = verify(providerParams.getPlatPublicKey(), ciphertext, signature);
|
||||
|
||||
if (!verify) {
|
||||
log.info("{} 验签失败:configId={}", logPrefix, configId);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 解密key
|
||||
String key = providerParams.getPushPublicKey();
|
||||
String plaintext = decrypte(Base64.decodeBase64String(ciphertext), key.getBytes(), iv.getBytes(), Base64.decodeBase64String(tag));
|
||||
log.info("{} 解密文本:{}", logPrefix, plaintext);
|
||||
|
||||
// 获取参数
|
||||
JSONObject payParams = JSONObject.parseObject(plaintext);
|
||||
// 终端号
|
||||
String machineCode = payParams.getString("machine_code");
|
||||
|
||||
// 设备验证是否存在
|
||||
MchStoreDevice device = mchStoreDeviceService.getOne(MchStoreDevice.gw()
|
||||
.eq(MchStoreDevice::getDeviceNo, machineCode)
|
||||
.eq(MchStoreDevice::getDeviceType, MchStoreDevice.DEVICE_TYPE_PRINTER)
|
||||
.eq(MchStoreDevice::getState, CS.YES)
|
||||
);
|
||||
if (device == null) {
|
||||
log.info("{} 设备信息不存在:deviceId={}", logPrefix, machineCode);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(device.getMchNo()) || StringUtils.isEmpty(device.getAppId()) || device.getStoreId() == null) {
|
||||
log.info("{} 设备未绑定,请先绑定:deviceId={}", logPrefix, machineCode);
|
||||
return result;
|
||||
}
|
||||
|
||||
ApiRes apiRes = doPayNew(payParams, device);
|
||||
log.info("{} >> pay >> 下单返回参数.ApiRes={}", logPrefix, apiRes);
|
||||
if (apiRes.getCode().equals(ApiCodeEnum.CUSTOM_FAIL.getCode())) {
|
||||
log.info("{} 下单失败:apiRes={}", logPrefix, apiRes);
|
||||
return result;
|
||||
}
|
||||
UnifiedOrderRS bizRs = (UnifiedOrderRS) apiRes.getData();
|
||||
log.info("{} >> pay >> 下单响应参数 bizRs = {}", logPrefix, JSON.toJSON(bizRs));
|
||||
|
||||
if (bizRs.getOrderState() == PayOrder.STATE_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
} catch (BizException e) {
|
||||
log.error("{} >> pos >> 打印机下单异常:", logPrefix, e);
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
log.error("{} >> pos >> 打印机下单异常:", logPrefix, e);
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private YlyParams.ProviderParams getParams(JSONObject deviceParams) {
|
||||
YlyParams.ProviderParams providerParams = JSON.parseObject(deviceParams.toJSONString(), YlyParams.ProviderParams.class);
|
||||
providerParams.setPlatPublicKey(deviceParams.getString("platPublicKey"));
|
||||
return providerParams;
|
||||
}
|
||||
|
||||
/** 构建下单参数 **/
|
||||
private ApiRes doPayNew(JSONObject payParams, MchStoreDevice device) {
|
||||
// 终端号
|
||||
String machineCode = payParams.getString("machine_code");
|
||||
// IP
|
||||
String ip = payParams.getString("ip");
|
||||
// 下单信息
|
||||
JSONObject orderPayment = payParams.getJSONObject("order_payment");
|
||||
// 付款码
|
||||
String authCode = orderPayment.getString("scanned_code");
|
||||
// 下单金额
|
||||
String payAmount = orderPayment.getString("pay_amount");
|
||||
// 来源参数 1:USB键盘 2:USB订单 3:网络金额
|
||||
String payAmountSource = orderPayment.getString("pay_amount_source");
|
||||
|
||||
// 下单参数
|
||||
String amount = AmountUtil.convertDollar2Cent(payAmount);
|
||||
String wayCode = JeepayKit.getPayWayCodeByBarCode(authCode);
|
||||
|
||||
UnifiedOrderRQ unifiedOrderRQ = new UnifiedOrderRQ();
|
||||
// 商户号
|
||||
unifiedOrderRQ.setMchNo(device.getMchNo());
|
||||
unifiedOrderRQ.setAppId(device.getAppId());
|
||||
unifiedOrderRQ.setStoreId(device.getStoreId());
|
||||
// 商户单号特殊处理
|
||||
unifiedOrderRQ.setMchOrderNo(SeqKit.genMhoOrderId());
|
||||
unifiedOrderRQ.setWayCode(wayCode);
|
||||
unifiedOrderRQ.setAmount(Long.valueOf(amount));
|
||||
unifiedOrderRQ.setCurrency("CNY");
|
||||
unifiedOrderRQ.setClientIp(getClientIp());
|
||||
unifiedOrderRQ.setSubject("标题");
|
||||
unifiedOrderRQ.setBody("商品信息");
|
||||
unifiedOrderRQ.setSignType(MchApp.SIGN_TYPE_MD5);
|
||||
// DBApplicationConfig dbApplicationConfig = sysConfigService.getDBApplicationConfig();
|
||||
// 回调地址
|
||||
// unifiedOrderRQ.setNotifyUrl(dbApplicationConfig.getMchSiteUrl() + "/api/anon/paytestNotify/payOrder");
|
||||
// 设置扩展参数
|
||||
JSONObject extParams = new JSONObject();
|
||||
extParams.put("authCode", authCode.trim());
|
||||
unifiedOrderRQ.setChannelExtra(extParams.toString());
|
||||
|
||||
DeviceInfo deviceInfo = new DeviceInfo();
|
||||
deviceInfo.setDeviceType(PayOrder.DEVICE_TYPE_PRINTER);
|
||||
deviceInfo.setDeviceNo(device.getDeviceNo());
|
||||
deviceInfo.setProvider(device.getProvider());
|
||||
// 设备信息
|
||||
unifiedOrderRQ.setDeviceInfo(JSON.toJSONString(deviceInfo));
|
||||
|
||||
try {
|
||||
UnifiedOrderRQ bizRQ = buildBizRQ(unifiedOrderRQ);
|
||||
|
||||
return this.unifiedOrder(bizRQ.getWayCode(), bizRQ);
|
||||
} catch (BizException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new BizException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private UnifiedOrderRQ buildBizRQ(UnifiedOrderRQ rq) {
|
||||
//支付方式 比如: ali_bar
|
||||
String wayCode = rq.getWayCode();
|
||||
|
||||
//jsapi 收银台聚合支付场景 (不校验是否存在payWayCode)
|
||||
if (CS.PAY_WAY_CODE.QR_CASHIER.equals(wayCode)) {
|
||||
return rq.buildBizRQ();
|
||||
}
|
||||
|
||||
//web收银台聚合支付场景 (不校验是否存在payWayCode)
|
||||
if (CS.PAY_WAY_CODE.WEB_CASHIER.equals(wayCode)) {
|
||||
return rq.buildBizRQ();
|
||||
}
|
||||
|
||||
//如果是自动分类条码
|
||||
if (CS.PAY_WAY_CODE.AUTO_BAR.equals(wayCode)) {
|
||||
|
||||
AutoBarOrderRQ bizRQ = (AutoBarOrderRQ) rq.buildBizRQ();
|
||||
wayCode = JeepayKit.getPayWayCodeByBarCode(bizRQ.getAuthCode());
|
||||
rq.setWayCode(wayCode.trim());
|
||||
}
|
||||
|
||||
if (payWayService.count(PayWay.gw().eq(PayWay::getWayCode, wayCode)) <= 0) {
|
||||
throw new BizException("不支持的支付方式");
|
||||
}
|
||||
|
||||
//转换为 bizRQ
|
||||
return rq.buildBizRQ();
|
||||
}
|
||||
|
||||
private static boolean verify(String publicKeyStr, String originStr, String encrypted) {
|
||||
try {
|
||||
byte[] decode = Base64.decodeBase64String(encrypted);
|
||||
byte[] data = originStr.getBytes();
|
||||
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.decodeBase64String(publicKeyStr));
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||
PublicKey publicKey = keyFactory.generatePublic(keySpec);
|
||||
Signature sha256withRSA = Signature.getInstance("SHA256withRSA");
|
||||
sha256withRSA.initVerify(publicKey);
|
||||
sha256withRSA.update(data);
|
||||
return sha256withRSA.verify(decode);
|
||||
}catch (Exception e) {
|
||||
log.error("设备验签失败:", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
//
|
||||
// Source code recreated from a .class file by IntelliJ IDEA
|
||||
// (powered by FernFlower decompiler)
|
||||
//
|
||||
package com.jeequan.jeepay.pay.ctrl.yly.sdk;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
class HttpRequest {
|
||||
HttpRequest() {
|
||||
}
|
||||
|
||||
public static String sendGet(String url, Map<String, String> paramMap) {
|
||||
String param = forMap(paramMap);
|
||||
String result = "";
|
||||
BufferedReader in = null;
|
||||
|
||||
try {
|
||||
String urlNameString = url + "?" + param;
|
||||
URLConnection connection = getUrlConnection(urlNameString);
|
||||
connection.connect();
|
||||
Map<String, List<String>> map = connection.getHeaderFields();
|
||||
Iterator var8 = map.keySet().iterator();
|
||||
|
||||
while(var8.hasNext()) {
|
||||
String key = (String)var8.next();
|
||||
System.out.println(key + "--->" + map.get(key));
|
||||
}
|
||||
|
||||
String line;
|
||||
for(in = new BufferedReader(new InputStreamReader(connection.getInputStream())); (line = in.readLine()) != null; result = result + line) {
|
||||
}
|
||||
} catch (Exception var18) {
|
||||
var18.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
if (in != null) {
|
||||
in.close();
|
||||
}
|
||||
} catch (Exception var17) {
|
||||
var17.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String sendPost(String url, Map<String, String> paramMap) {
|
||||
String param = forMap(paramMap);
|
||||
PrintWriter out = null;
|
||||
BufferedReader in = null;
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
try {
|
||||
URLConnection conn = getUrlConnection(url);
|
||||
conn.setDoOutput(true);
|
||||
conn.setDoInput(true);
|
||||
out = new PrintWriter(conn.getOutputStream());
|
||||
out.print(param);
|
||||
out.flush();
|
||||
in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
||||
|
||||
String line;
|
||||
while((line = in.readLine()) != null) {
|
||||
result.append(line);
|
||||
}
|
||||
} catch (Exception var16) {
|
||||
var16.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
if (out != null) {
|
||||
out.close();
|
||||
}
|
||||
|
||||
if (in != null) {
|
||||
in.close();
|
||||
}
|
||||
} catch (IOException var15) {
|
||||
var15.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private static URLConnection getUrlConnection(String url) throws IOException {
|
||||
URL realUrl = new URL(url);
|
||||
URLConnection conn = realUrl.openConnection();
|
||||
conn.setConnectTimeout(30000);
|
||||
conn.setRequestProperty("accept", "*/*");
|
||||
conn.setRequestProperty("connection", "Keep-Alive");
|
||||
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
|
||||
return conn;
|
||||
}
|
||||
|
||||
private static String forMap(Map<String, String> paramMap) {
|
||||
String reqStr = "";
|
||||
if (null != paramMap && !paramMap.isEmpty()) {
|
||||
Entry entry;
|
||||
for(Iterator var2 = paramMap.entrySet().iterator(); var2.hasNext(); reqStr = (String)entry.getKey() + "=" + (String)entry.getValue() + "&" + reqStr) {
|
||||
entry = (Entry)var2.next();
|
||||
System.out.println("key = " + (String)entry.getKey() + ", value = " + (String)entry.getValue());
|
||||
}
|
||||
|
||||
reqStr = reqStr.substring(0, reqStr.length() - 1);
|
||||
}
|
||||
|
||||
return reqStr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,614 @@
|
||||
//
|
||||
// Source code recreated from a .class file by IntelliJ IDEA
|
||||
// (powered by FernFlower decompiler)
|
||||
//
|
||||
package com.jeequan.jeepay.pay.ctrl.yly.sdk;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class RequestMethod {
|
||||
private static String example = "{\"error\":\"20\",\"error_description\":\"success\",\"body\":\"未完成初始化\"}";
|
||||
public String clientId;
|
||||
public String clientSecret;
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public String getClientSecret() {
|
||||
return clientSecret;
|
||||
}
|
||||
|
||||
public void setClientSecret(String clientSecret) {
|
||||
this.clientSecret = clientSecret;
|
||||
}
|
||||
|
||||
private static final RequestMethod singleton = new RequestMethod();
|
||||
|
||||
private RequestMethod() {
|
||||
}
|
||||
|
||||
public static RequestMethod getInstance() {
|
||||
return singleton;
|
||||
}
|
||||
|
||||
public void init(String client_id, String client_secret) {
|
||||
clientId = client_id;
|
||||
clientSecret = client_secret;
|
||||
}
|
||||
|
||||
private boolean CCIsNull(String client_id, String client_secret) {
|
||||
return clientId != null && clientSecret != null && !clientId.equals("") && !clientSecret.equals("");
|
||||
}
|
||||
|
||||
public String getCodeOpen(String redirect_uri) {
|
||||
return clientId != null && !clientId.equals("") ? UtilUrl.openType + "?response_type=code&client_id=" + clientId + "&redirect_uri=" + redirect_uri + "&state=1" : example;
|
||||
}
|
||||
|
||||
public String getOpenAccessToken(String code) throws Exception {
|
||||
if (CCIsNull(clientId, clientSecret)) {
|
||||
String timestamp = Utils.getTimestamp();
|
||||
String signMD5 = clientId + timestamp + clientSecret;
|
||||
String sign = Utils.getMD5Str(signMD5);
|
||||
Map<String, String> paramMap = new HashMap();
|
||||
paramMap.put("client_id", clientId);
|
||||
paramMap.put("grant_type", "authorization_code");
|
||||
paramMap.put("sign", sign);
|
||||
paramMap.put("code", code);
|
||||
paramMap.put("scope", "all");
|
||||
paramMap.put("timestamp", timestamp);
|
||||
paramMap.put("id", UUID.randomUUID().toString());
|
||||
return HttpRequest.sendPost(UtilUrl.freeType, paramMap);
|
||||
} else {
|
||||
return example;
|
||||
}
|
||||
}
|
||||
|
||||
public String getAccessToken() throws Exception {
|
||||
if (CCIsNull(clientId, clientSecret)) {
|
||||
String timestamp = Utils.getTimestamp();
|
||||
String signMD5 = clientId + timestamp + clientSecret;
|
||||
String sign = Utils.getMD5Str(signMD5);
|
||||
Map<String, String> paramMap = new HashMap();
|
||||
paramMap.put("client_id", clientId);
|
||||
paramMap.put("grant_type", "client_credentials");
|
||||
paramMap.put("sign", sign);
|
||||
paramMap.put("scope", "all");
|
||||
paramMap.put("timestamp", timestamp);
|
||||
paramMap.put("id", UUID.randomUUID().toString());
|
||||
return HttpRequest.sendPost(UtilUrl.freeType, paramMap);
|
||||
} else {
|
||||
return example;
|
||||
}
|
||||
}
|
||||
|
||||
public String getRefreshAccessToken(String refresh_token) throws Exception {
|
||||
if (CCIsNull(clientId, clientSecret)) {
|
||||
String timestamp = Utils.getTimestamp();
|
||||
String signMD5 = clientId + timestamp + clientSecret;
|
||||
String sign = Utils.getMD5Str(signMD5);
|
||||
Map<String, String> paramMap = new HashMap();
|
||||
paramMap.put("refresh_token", refresh_token);
|
||||
paramMap.put("grant_type", "refresh_token");
|
||||
paramMap.put("scope", "all");
|
||||
paramMap.put("client_id", clientId);
|
||||
paramMap.put("timestamp", timestamp);
|
||||
paramMap.put("id", UUID.randomUUID().toString());
|
||||
paramMap.put("sign", sign);
|
||||
return HttpRequest.sendPost(UtilUrl.freeType, paramMap);
|
||||
} else {
|
||||
return example;
|
||||
}
|
||||
}
|
||||
|
||||
public String addPrinter(String machine_code, String msign, String access_token) throws Exception {
|
||||
if (CCIsNull(clientId, clientSecret)) {
|
||||
String timestamp = Utils.getTimestamp();
|
||||
String signMD5 = clientId + timestamp + clientSecret;
|
||||
String sign = Utils.getMD5Str(signMD5);
|
||||
Map<String, String> paramMap = new HashMap();
|
||||
paramMap.put("machine_code", machine_code);
|
||||
paramMap.put("msign", msign);
|
||||
paramMap.put("access_token", access_token);
|
||||
paramMap.put("client_id", clientId);
|
||||
paramMap.put("timestamp", timestamp);
|
||||
paramMap.put("id", UUID.randomUUID().toString());
|
||||
paramMap.put("sign", sign);
|
||||
return HttpRequest.sendPost(UtilUrl.addPrinter, paramMap);
|
||||
} else {
|
||||
return example;
|
||||
}
|
||||
}
|
||||
|
||||
public String addPrinter(String machine_code, String msign, String access_token, String phone, String print_name) throws Exception {
|
||||
if (CCIsNull(clientId, clientSecret)) {
|
||||
String timestamp = Utils.getTimestamp();
|
||||
String signMD5 = clientId + timestamp + clientSecret;
|
||||
String sign = Utils.getMD5Str(signMD5);
|
||||
Map<String, String> paramMap = new HashMap();
|
||||
paramMap.put("machine_code", machine_code);
|
||||
paramMap.put("msign", msign);
|
||||
paramMap.put("access_token", access_token);
|
||||
paramMap.put("phone", phone);
|
||||
paramMap.put("print_name", print_name);
|
||||
paramMap.put("client_id", clientId);
|
||||
paramMap.put("timestamp", timestamp);
|
||||
paramMap.put("id", UUID.randomUUID().toString());
|
||||
paramMap.put("sign", sign);
|
||||
return HttpRequest.sendPost(UtilUrl.addPrinter, paramMap);
|
||||
} else {
|
||||
return example;
|
||||
}
|
||||
}
|
||||
|
||||
public String scanCodeModel(String machine_code, String msign) throws Exception {
|
||||
if (CCIsNull(clientId, clientSecret)) {
|
||||
String timestamp = Utils.getTimestamp();
|
||||
String signMD5 = clientId + timestamp + clientSecret;
|
||||
String sign = Utils.getMD5Str(signMD5);
|
||||
Map<String, String> paramMap = new HashMap();
|
||||
paramMap.put("machine_code", machine_code);
|
||||
paramMap.put("msign", msign);
|
||||
paramMap.put("scope", "all");
|
||||
paramMap.put("client_id", clientId);
|
||||
paramMap.put("timestamp", timestamp);
|
||||
paramMap.put("id", UUID.randomUUID().toString());
|
||||
paramMap.put("sign", sign);
|
||||
return HttpRequest.sendPost(UtilUrl.scanCodeModel, paramMap);
|
||||
} else {
|
||||
return example;
|
||||
}
|
||||
}
|
||||
|
||||
public String scanCodeModel_msign(String machine_code, String msign) throws Exception {
|
||||
if (CCIsNull(clientId, clientSecret)) {
|
||||
String timestamp = Utils.getTimestamp();
|
||||
String signMD5 = clientId + timestamp + clientSecret;
|
||||
String sign = Utils.getMD5Str(signMD5);
|
||||
Map<String, String> paramMap = new HashMap();
|
||||
paramMap.put("machine_code", machine_code);
|
||||
paramMap.put("msign", msign);
|
||||
paramMap.put("scope", "all");
|
||||
paramMap.put("client_id", clientId);
|
||||
paramMap.put("timestamp", timestamp);
|
||||
paramMap.put("id", UUID.randomUUID().toString());
|
||||
paramMap.put("sign", sign);
|
||||
return HttpRequest.sendPost(UtilUrl.scanCodeModel, paramMap);
|
||||
} else {
|
||||
return example;
|
||||
}
|
||||
}
|
||||
|
||||
public String printIndex(String access_token, String machine_code, String content, String origin_id) throws Exception {
|
||||
if (CCIsNull(clientId, clientSecret)) {
|
||||
String timestamp = Utils.getTimestamp();
|
||||
String signMD5 = clientId + timestamp + clientSecret;
|
||||
String sign = Utils.getMD5Str(signMD5);
|
||||
Map<String, String> paramMap = new HashMap();
|
||||
paramMap.put("access_token", access_token);
|
||||
paramMap.put("machine_code", machine_code);
|
||||
paramMap.put("content", content);
|
||||
paramMap.put("origin_id", origin_id);
|
||||
paramMap.put("client_id", clientId);
|
||||
paramMap.put("timestamp", timestamp);
|
||||
paramMap.put("id", UUID.randomUUID().toString());
|
||||
paramMap.put("idempotence", "0");
|
||||
paramMap.put("sign", sign);
|
||||
return HttpRequest.sendPost(UtilUrl.printIndex, paramMap);
|
||||
} else {
|
||||
return example;
|
||||
}
|
||||
}
|
||||
|
||||
public String picturePrintIndex(String access_token, String machine_code, String picture_url, String origin_id) throws Exception {
|
||||
if (CCIsNull(clientId, clientSecret)) {
|
||||
String timestamp = Utils.getTimestamp();
|
||||
String signMD5 = clientId + timestamp + clientSecret;
|
||||
String sign = Utils.getMD5Str(signMD5);
|
||||
Map<String, String> paramMap = new HashMap();
|
||||
paramMap.put("access_token", access_token);
|
||||
paramMap.put("machine_code", machine_code);
|
||||
paramMap.put("picture_url", URLEncoder.encode(picture_url, "UTF-8"));
|
||||
paramMap.put("origin_id", origin_id);
|
||||
paramMap.put("client_id", clientId);
|
||||
paramMap.put("timestamp", timestamp);
|
||||
paramMap.put("id", UUID.randomUUID().toString());
|
||||
paramMap.put("sign", sign);
|
||||
return HttpRequest.sendPost(UtilUrl.picturePrintIndex, paramMap);
|
||||
} else {
|
||||
return example;
|
||||
}
|
||||
}
|
||||
|
||||
public String expressPrintIndex(String access_token, String machine_code, String content, String origin_id) throws Exception {
|
||||
if (CCIsNull(clientId, clientSecret)) {
|
||||
String timestamp = Utils.getTimestamp();
|
||||
String signMD5 = clientId + timestamp + clientSecret;
|
||||
String sign = Utils.getMD5Str(signMD5);
|
||||
Map<String, String> paramMap = new HashMap();
|
||||
paramMap.put("access_token", access_token);
|
||||
paramMap.put("machine_code", machine_code);
|
||||
paramMap.put("content", content);
|
||||
paramMap.put("origin_id", origin_id);
|
||||
paramMap.put("client_id", clientId);
|
||||
paramMap.put("timestamp", timestamp);
|
||||
paramMap.put("id", UUID.randomUUID().toString());
|
||||
paramMap.put("sign", sign);
|
||||
return HttpRequest.sendPost(UtilUrl.expressPrintIndex, paramMap);
|
||||
} else {
|
||||
return example;
|
||||
}
|
||||
}
|
||||
|
||||
public String printerSetVoice(String access_token, String machine_code, String content, String is_file, String aid, String origin_id) throws Exception {
|
||||
if (CCIsNull(clientId, clientSecret)) {
|
||||
String timestamp = Utils.getTimestamp();
|
||||
String signMD5 = clientId + timestamp + clientSecret;
|
||||
String sign = Utils.getMD5Str(signMD5);
|
||||
Map<String, String> paramMap = new HashMap();
|
||||
paramMap.put("access_token", access_token);
|
||||
paramMap.put("machine_code", machine_code);
|
||||
paramMap.put("content", content);
|
||||
paramMap.put("is_file", is_file);
|
||||
paramMap.put("aid", aid);
|
||||
paramMap.put("origin_id", origin_id);
|
||||
paramMap.put("client_id", clientId);
|
||||
paramMap.put("timestamp", timestamp);
|
||||
paramMap.put("id", UUID.randomUUID().toString());
|
||||
paramMap.put("sign", sign);
|
||||
return HttpRequest.sendPost(UtilUrl.printerSetVoice, paramMap);
|
||||
} else {
|
||||
return example;
|
||||
}
|
||||
}
|
||||
|
||||
public String printerDeleteVoice(String access_token, String machine_code, String aid, String origin_id) throws Exception {
|
||||
if (CCIsNull(clientId, clientSecret)) {
|
||||
String timestamp = Utils.getTimestamp();
|
||||
String signMD5 = clientId + timestamp + clientSecret;
|
||||
String sign = Utils.getMD5Str(signMD5);
|
||||
Map<String, String> paramMap = new HashMap();
|
||||
paramMap.put("access_token", access_token);
|
||||
paramMap.put("machine_code", machine_code);
|
||||
paramMap.put("aid", aid);
|
||||
paramMap.put("origin_id", origin_id);
|
||||
paramMap.put("client_id", clientId);
|
||||
paramMap.put("timestamp", timestamp);
|
||||
paramMap.put("id", UUID.randomUUID().toString());
|
||||
paramMap.put("sign", sign);
|
||||
return HttpRequest.sendPost(UtilUrl.printerDeleteVoice, paramMap);
|
||||
} else {
|
||||
return example;
|
||||
}
|
||||
}
|
||||
|
||||
public String printerDeletePrinter(String access_token, String machine_code) throws Exception {
|
||||
if (CCIsNull(clientId, clientSecret)) {
|
||||
String timestamp = Utils.getTimestamp();
|
||||
String signMD5 = clientId + timestamp + clientSecret;
|
||||
String sign = Utils.getMD5Str(signMD5);
|
||||
Map<String, String> paramMap = new HashMap();
|
||||
paramMap.put("access_token", access_token);
|
||||
paramMap.put("machine_code", machine_code);
|
||||
paramMap.put("client_id", clientId);
|
||||
paramMap.put("timestamp", timestamp);
|
||||
paramMap.put("id", UUID.randomUUID().toString());
|
||||
paramMap.put("sign", sign);
|
||||
return HttpRequest.sendPost(UtilUrl.printerDeletePrinter, paramMap);
|
||||
} else {
|
||||
return example;
|
||||
}
|
||||
}
|
||||
|
||||
public String printMenuAddPrintMenu(String access_token, String machine_code, String content) throws Exception {
|
||||
if (CCIsNull(clientId, clientSecret)) {
|
||||
String timestamp = Utils.getTimestamp();
|
||||
String signMD5 = clientId + timestamp + clientSecret;
|
||||
String sign = Utils.getMD5Str(signMD5);
|
||||
Map<String, String> paramMap = new HashMap();
|
||||
paramMap.put("access_token", access_token);
|
||||
paramMap.put("machine_code", machine_code);
|
||||
paramMap.put("content", content);
|
||||
paramMap.put("client_id", clientId);
|
||||
paramMap.put("timestamp", timestamp);
|
||||
paramMap.put("id", UUID.randomUUID().toString());
|
||||
paramMap.put("sign", sign);
|
||||
return HttpRequest.sendPost(UtilUrl.printMenuAddPrintMenu, paramMap);
|
||||
} else {
|
||||
return example;
|
||||
}
|
||||
}
|
||||
|
||||
public String printShutdownRestart(String access_token, String machine_code, String response_type) throws Exception {
|
||||
if (CCIsNull(clientId, clientSecret)) {
|
||||
String timestamp = Utils.getTimestamp();
|
||||
String signMD5 = clientId + timestamp + clientSecret;
|
||||
String sign = Utils.getMD5Str(signMD5);
|
||||
Map<String, String> paramMap = new HashMap();
|
||||
paramMap.put("access_token", access_token);
|
||||
paramMap.put("machine_code", machine_code);
|
||||
paramMap.put("response_type", response_type);
|
||||
paramMap.put("client_id", clientId);
|
||||
paramMap.put("timestamp", timestamp);
|
||||
paramMap.put("id", UUID.randomUUID().toString());
|
||||
paramMap.put("sign", sign);
|
||||
return HttpRequest.sendPost(UtilUrl.printShutdownRestart, paramMap);
|
||||
} else {
|
||||
return example;
|
||||
}
|
||||
}
|
||||
|
||||
public String printSetSound(String access_token, String machine_code, String response_type, String voice) throws Exception {
|
||||
if (CCIsNull(clientId, clientSecret)) {
|
||||
String timestamp = Utils.getTimestamp();
|
||||
String signMD5 = clientId + timestamp + clientSecret;
|
||||
String sign = Utils.getMD5Str(signMD5);
|
||||
Map<String, String> paramMap = new HashMap();
|
||||
paramMap.put("access_token", access_token);
|
||||
paramMap.put("machine_code", machine_code);
|
||||
paramMap.put("response_type", response_type);
|
||||
paramMap.put("voice", voice);
|
||||
paramMap.put("client_id", clientId);
|
||||
paramMap.put("timestamp", timestamp);
|
||||
paramMap.put("id", UUID.randomUUID().toString());
|
||||
paramMap.put("sign", sign);
|
||||
return HttpRequest.sendPost(UtilUrl.printSetSound, paramMap);
|
||||
} else {
|
||||
return example;
|
||||
}
|
||||
}
|
||||
|
||||
public String printPrintInfo(String access_token, String machine_code) throws Exception {
|
||||
if (CCIsNull(clientId, clientSecret)) {
|
||||
String timestamp = Utils.getTimestamp();
|
||||
String signMD5 = clientId + timestamp + clientSecret;
|
||||
String sign = Utils.getMD5Str(signMD5);
|
||||
Map<String, String> paramMap = new HashMap();
|
||||
paramMap.put("access_token", access_token);
|
||||
paramMap.put("machine_code", machine_code);
|
||||
paramMap.put("client_id", clientId);
|
||||
paramMap.put("timestamp", timestamp);
|
||||
paramMap.put("id", UUID.randomUUID().toString());
|
||||
paramMap.put("sign", sign);
|
||||
return HttpRequest.sendPost(UtilUrl.printPrintInfo, paramMap);
|
||||
} else {
|
||||
return example;
|
||||
}
|
||||
}
|
||||
|
||||
public String printGetVersion(String access_token, String machine_code) throws Exception {
|
||||
if (CCIsNull(clientId, clientSecret)) {
|
||||
String timestamp = Utils.getTimestamp();
|
||||
String signMD5 = clientId + timestamp + clientSecret;
|
||||
String sign = Utils.getMD5Str(signMD5);
|
||||
Map<String, String> paramMap = new HashMap();
|
||||
paramMap.put("access_token", access_token);
|
||||
paramMap.put("machine_code", machine_code);
|
||||
paramMap.put("client_id", clientId);
|
||||
paramMap.put("timestamp", timestamp);
|
||||
paramMap.put("id", UUID.randomUUID().toString());
|
||||
paramMap.put("sign", sign);
|
||||
return HttpRequest.sendPost(UtilUrl.printGetVersion, paramMap);
|
||||
} else {
|
||||
return example;
|
||||
}
|
||||
}
|
||||
|
||||
public String printCancelAll(String access_token, String machine_code) throws Exception {
|
||||
if (CCIsNull(clientId, clientSecret)) {
|
||||
String timestamp = Utils.getTimestamp();
|
||||
String signMD5 = clientId + timestamp + clientSecret;
|
||||
String sign = Utils.getMD5Str(signMD5);
|
||||
Map<String, String> paramMap = new HashMap();
|
||||
paramMap.put("access_token", access_token);
|
||||
paramMap.put("machine_code", machine_code);
|
||||
paramMap.put("client_id", clientId);
|
||||
paramMap.put("timestamp", timestamp);
|
||||
paramMap.put("id", UUID.randomUUID().toString());
|
||||
paramMap.put("sign", sign);
|
||||
return HttpRequest.sendPost(UtilUrl.printCancelAll, paramMap);
|
||||
} else {
|
||||
return example;
|
||||
}
|
||||
}
|
||||
|
||||
public String printCancelOne(String access_token, String machine_code, String order_id) throws Exception {
|
||||
if (CCIsNull(clientId, clientSecret)) {
|
||||
String timestamp = Utils.getTimestamp();
|
||||
String signMD5 = clientId + timestamp + clientSecret;
|
||||
String sign = Utils.getMD5Str(signMD5);
|
||||
Map<String, String> paramMap = new HashMap();
|
||||
paramMap.put("access_token", access_token);
|
||||
paramMap.put("machine_code", machine_code);
|
||||
paramMap.put("order_id", order_id);
|
||||
paramMap.put("client_id", clientId);
|
||||
paramMap.put("timestamp", timestamp);
|
||||
paramMap.put("id", UUID.randomUUID().toString());
|
||||
paramMap.put("sign", sign);
|
||||
return HttpRequest.sendPost(UtilUrl.printCancelOne, paramMap);
|
||||
} else {
|
||||
return example;
|
||||
}
|
||||
}
|
||||
|
||||
public String printSetIcon(String access_token, String machine_code, String img_url) throws Exception {
|
||||
if (CCIsNull(clientId, clientSecret)) {
|
||||
String timestamp = Utils.getTimestamp();
|
||||
String signMD5 = clientId + timestamp + clientSecret;
|
||||
String sign = Utils.getMD5Str(signMD5);
|
||||
Map<String, String> paramMap = new HashMap();
|
||||
paramMap.put("access_token", access_token);
|
||||
paramMap.put("machine_code", machine_code);
|
||||
paramMap.put("img_url", img_url);
|
||||
paramMap.put("client_id", clientId);
|
||||
paramMap.put("timestamp", timestamp);
|
||||
paramMap.put("id", UUID.randomUUID().toString());
|
||||
paramMap.put("sign", sign);
|
||||
return HttpRequest.sendPost(UtilUrl.printSetIcon, paramMap);
|
||||
} else {
|
||||
return example;
|
||||
}
|
||||
}
|
||||
|
||||
public String printDeleteIcon(String access_token, String machine_code) throws Exception {
|
||||
if (CCIsNull(clientId, clientSecret)) {
|
||||
String timestamp = Utils.getTimestamp();
|
||||
String signMD5 = clientId + timestamp + clientSecret;
|
||||
String sign = Utils.getMD5Str(signMD5);
|
||||
Map<String, String> paramMap = new HashMap();
|
||||
paramMap.put("access_token", access_token);
|
||||
paramMap.put("machine_code", machine_code);
|
||||
paramMap.put("client_id", clientId);
|
||||
paramMap.put("timestamp", timestamp);
|
||||
paramMap.put("id", UUID.randomUUID().toString());
|
||||
paramMap.put("sign", sign);
|
||||
return HttpRequest.sendPost(UtilUrl.printDeleteIcon, paramMap);
|
||||
} else {
|
||||
return example;
|
||||
}
|
||||
}
|
||||
|
||||
public String printBtnPrint(String access_token, String machine_code, String response_type) throws Exception {
|
||||
if (CCIsNull(clientId, clientSecret)) {
|
||||
String timestamp = Utils.getTimestamp();
|
||||
String signMD5 = clientId + timestamp + clientSecret;
|
||||
String sign = Utils.getMD5Str(signMD5);
|
||||
Map<String, String> paramMap = new HashMap();
|
||||
paramMap.put("access_token", access_token);
|
||||
paramMap.put("machine_code", machine_code);
|
||||
paramMap.put("response_type", response_type);
|
||||
paramMap.put("client_id", clientId);
|
||||
paramMap.put("timestamp", timestamp);
|
||||
paramMap.put("id", UUID.randomUUID().toString());
|
||||
paramMap.put("sign", sign);
|
||||
return HttpRequest.sendPost(UtilUrl.printBtnPrint, paramMap);
|
||||
} else {
|
||||
return example;
|
||||
}
|
||||
}
|
||||
|
||||
public String printGetOrder(String access_token, String machine_code, String response_type) throws Exception {
|
||||
if (CCIsNull(clientId, clientSecret)) {
|
||||
String timestamp = Utils.getTimestamp();
|
||||
String signMD5 = clientId + timestamp + clientSecret;
|
||||
String sign = Utils.getMD5Str(signMD5);
|
||||
Map<String, String> paramMap = new HashMap();
|
||||
paramMap.put("access_token", access_token);
|
||||
paramMap.put("machine_code", machine_code);
|
||||
paramMap.put("response_type", response_type);
|
||||
paramMap.put("client_id", clientId);
|
||||
paramMap.put("timestamp", timestamp);
|
||||
paramMap.put("id", UUID.randomUUID().toString());
|
||||
paramMap.put("sign", sign);
|
||||
return HttpRequest.sendPost(UtilUrl.printGetOrder, paramMap);
|
||||
} else {
|
||||
return example;
|
||||
}
|
||||
}
|
||||
|
||||
public String oauthSetPushUrl(String access_token, String machine_code, String cmd, String url, String status) throws Exception {
|
||||
if (CCIsNull(clientId, clientSecret)) {
|
||||
String timestamp = Utils.getTimestamp();
|
||||
String signMD5 = clientId + timestamp + clientSecret;
|
||||
String sign = Utils.getMD5Str(signMD5);
|
||||
Map<String, String> paramMap = new HashMap();
|
||||
paramMap.put("access_token", access_token);
|
||||
paramMap.put("machine_code", machine_code);
|
||||
paramMap.put("cmd", cmd);
|
||||
paramMap.put("url", url);
|
||||
paramMap.put("status", status);
|
||||
paramMap.put("client_id", clientId);
|
||||
paramMap.put("timestamp", timestamp);
|
||||
paramMap.put("id", UUID.randomUUID().toString());
|
||||
paramMap.put("sign", sign);
|
||||
return HttpRequest.sendPost(UtilUrl.oauthSetPushUrl, paramMap);
|
||||
} else {
|
||||
return example;
|
||||
}
|
||||
}
|
||||
|
||||
public String printerGetOrderStatus(String access_token, String machine_code, String order_id) throws Exception {
|
||||
if (CCIsNull(clientId, clientSecret)) {
|
||||
String timestamp = Utils.getTimestamp();
|
||||
String signMD5 = clientId + timestamp + clientSecret;
|
||||
String sign = Utils.getMD5Str(signMD5);
|
||||
Map<String, String> paramMap = new HashMap();
|
||||
paramMap.put("access_token", access_token);
|
||||
paramMap.put("machine_code", machine_code);
|
||||
paramMap.put("order_id", order_id);
|
||||
paramMap.put("client_id", clientId);
|
||||
paramMap.put("timestamp", timestamp);
|
||||
paramMap.put("id", UUID.randomUUID().toString());
|
||||
paramMap.put("sign", sign);
|
||||
return HttpRequest.sendPost(UtilUrl.printerGetOrderStatus, paramMap);
|
||||
} else {
|
||||
return example;
|
||||
}
|
||||
}
|
||||
|
||||
public String printerGetOrderPagingList(String access_token, String machine_code, String page_index, String page_size) throws Exception {
|
||||
if (CCIsNull(clientId, clientSecret)) {
|
||||
String timestamp = Utils.getTimestamp();
|
||||
String signMD5 = clientId + timestamp + clientSecret;
|
||||
String sign = Utils.getMD5Str(signMD5);
|
||||
Map<String, String> paramMap = new HashMap();
|
||||
paramMap.put("access_token", access_token);
|
||||
paramMap.put("machine_code", machine_code);
|
||||
paramMap.put("page_index", page_index);
|
||||
paramMap.put("page_size", page_size);
|
||||
paramMap.put("client_id", clientId);
|
||||
paramMap.put("timestamp", timestamp);
|
||||
paramMap.put("id", UUID.randomUUID().toString());
|
||||
paramMap.put("sign", sign);
|
||||
return HttpRequest.sendPost(UtilUrl.printerGetOrderPagingList, paramMap);
|
||||
} else {
|
||||
return example;
|
||||
}
|
||||
}
|
||||
|
||||
public String printerGetPrintStatus(String access_token, String machine_code) throws Exception {
|
||||
if (CCIsNull(clientId, clientSecret)) {
|
||||
String timestamp = Utils.getTimestamp();
|
||||
String signMD5 = clientId + timestamp + clientSecret;
|
||||
String sign = Utils.getMD5Str(signMD5);
|
||||
Map<String, String> paramMap = new HashMap();
|
||||
paramMap.put("access_token", access_token);
|
||||
paramMap.put("machine_code", machine_code);
|
||||
paramMap.put("client_id", clientId);
|
||||
paramMap.put("timestamp", timestamp);
|
||||
paramMap.put("id", UUID.randomUUID().toString());
|
||||
paramMap.put("sign", sign);
|
||||
return HttpRequest.sendPost(UtilUrl.printerGetPrintStatus, paramMap);
|
||||
} else {
|
||||
return example;
|
||||
}
|
||||
}
|
||||
|
||||
public String printerSetKeywords(String access_token, String machine_code, String keys, String type, String content) throws Exception {
|
||||
if (CCIsNull(clientId, clientSecret)) {
|
||||
String timestamp = Utils.getTimestamp();
|
||||
String signMD5 = clientId + timestamp + clientSecret;
|
||||
String sign = Utils.getMD5Str(signMD5);
|
||||
Map<String, String> paramMap = new HashMap();
|
||||
paramMap.put("access_token", access_token);
|
||||
paramMap.put("machine_code", machine_code);
|
||||
paramMap.put("client_id", clientId);
|
||||
paramMap.put("timestamp", timestamp);
|
||||
paramMap.put("id", UUID.randomUUID().toString());
|
||||
paramMap.put("keys", keys);
|
||||
paramMap.put("type", type);
|
||||
paramMap.put("content", content);
|
||||
paramMap.put("sign", sign);
|
||||
return HttpRequest.sendPost(UtilUrl.printerSetKeywords, paramMap);
|
||||
} else {
|
||||
return example;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
//
|
||||
// Source code recreated from a .class file by IntelliJ IDEA
|
||||
// (powered by FernFlower decompiler)
|
||||
//
|
||||
package com.jeequan.jeepay.pay.ctrl.yly.sdk;
|
||||
|
||||
class UtilUrl {
|
||||
public static String baseUrl = "https://open-api.10ss.net/v2/";
|
||||
public static String openType;
|
||||
public static String freeType;
|
||||
public static String addPrinter;
|
||||
public static String scanCodeModel;
|
||||
public static String printIndex;
|
||||
public static String picturePrintIndex;
|
||||
public static String expressPrintIndex;
|
||||
public static String printerSetVoice;
|
||||
public static String printerDeleteVoice;
|
||||
public static String printerDeletePrinter;
|
||||
public static String printMenuAddPrintMenu;
|
||||
public static String printShutdownRestart;
|
||||
public static String printSetSound;
|
||||
public static String printPrintInfo;
|
||||
public static String printGetVersion;
|
||||
public static String printCancelAll;
|
||||
public static String printCancelOne;
|
||||
public static String printSetIcon;
|
||||
public static String printDeleteIcon;
|
||||
public static String printBtnPrint;
|
||||
public static String printGetOrder;
|
||||
public static String oauthSetPushUrl;
|
||||
public static String printerGetOrderStatus;
|
||||
public static String printerGetOrderPagingList;
|
||||
public static String printerGetPrintStatus;
|
||||
public static String printerSetKeywords;
|
||||
|
||||
UtilUrl() {
|
||||
}
|
||||
|
||||
static {
|
||||
openType = baseUrl + "oauth/authorize";
|
||||
freeType = baseUrl + "oauth/oauth";
|
||||
addPrinter = baseUrl + "printer/addprinter";
|
||||
scanCodeModel = baseUrl + "oauth/scancodemodel";
|
||||
printIndex = baseUrl + "print/index";
|
||||
picturePrintIndex = baseUrl + "pictureprint/index";
|
||||
expressPrintIndex = baseUrl + "expressprint/index";
|
||||
printerSetVoice = baseUrl + "printer/setvoice";
|
||||
printerDeleteVoice = baseUrl + "printer/deletevoice";
|
||||
printerDeletePrinter = baseUrl + "printer/deleteprinter";
|
||||
printMenuAddPrintMenu = baseUrl + "printmenu/addprintmenu";
|
||||
printShutdownRestart = baseUrl + "printer/shutdownrestart";
|
||||
printSetSound = baseUrl + "printer/setsound";
|
||||
printPrintInfo = baseUrl + "printer/printinfo";
|
||||
printGetVersion = baseUrl + "printer/getversion";
|
||||
printCancelAll = baseUrl + "printer/cancelall";
|
||||
printCancelOne = baseUrl + "printer/cancelone";
|
||||
printSetIcon = baseUrl + "printer/seticon";
|
||||
printDeleteIcon = baseUrl + "printer/deleteicon";
|
||||
printBtnPrint = baseUrl + "printer/btnprint";
|
||||
printGetOrder = baseUrl + "printer/getorder";
|
||||
oauthSetPushUrl = baseUrl + "oauth/setpushurl";
|
||||
printerGetOrderStatus = baseUrl + "printer/getorderstatus";
|
||||
printerGetOrderPagingList = baseUrl + "printer/getorderpaginglist";
|
||||
printerGetPrintStatus = baseUrl + "printer/getprintstatus";
|
||||
printerSetKeywords = baseUrl + "printer/setkeywords";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// Source code recreated from a .class file by IntelliJ IDEA
|
||||
// (powered by FernFlower decompiler)
|
||||
//
|
||||
package com.jeequan.jeepay.pay.ctrl.yly.sdk;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
public class Utils {
|
||||
public Utils() {
|
||||
}
|
||||
|
||||
public static String getMD5Str(String str) {
|
||||
String re = null;
|
||||
|
||||
try {
|
||||
byte[] tem = str.getBytes();
|
||||
MessageDigest md5 = MessageDigest.getInstance("md5");
|
||||
md5.reset();
|
||||
md5.update(tem);
|
||||
byte[] encrypt = md5.digest();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
byte[] var6 = encrypt;
|
||||
int var7 = encrypt.length;
|
||||
|
||||
for(int var8 = 0; var8 < var7; ++var8) {
|
||||
byte t = var6[var8];
|
||||
String s = Integer.toHexString(t & 255);
|
||||
if (s.length() == 1) {
|
||||
s = "0" + s;
|
||||
}
|
||||
|
||||
sb.append(s);
|
||||
}
|
||||
|
||||
re = sb.toString();
|
||||
} catch (NoSuchAlgorithmException var11) {
|
||||
var11.printStackTrace();
|
||||
}
|
||||
|
||||
return re.length() == 31 ? "0" + re : re;
|
||||
}
|
||||
|
||||
public static boolean isNull(String content) {
|
||||
return content == null || content.equals("");
|
||||
}
|
||||
|
||||
public static String getTimestamp() {
|
||||
return String.valueOf(System.currentTimeMillis() / 1000L);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,509 @@
|
||||
/**
|
||||
* cljpay.com
|
||||
* Copyright (C) 2015-2023 All Rights Reserved.
|
||||
*/package com.jeequan.jeepay.pay.ctrl.zw;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.aliyun.oss.ServiceException;
|
||||
import com.jeequan.jeepay.core.constants.ApiCodeEnum;
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.entity.MchApp;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.model.ApiRes;
|
||||
import com.jeequan.jeepay.core.model.device.ZwParams;
|
||||
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
|
||||
import com.jeequan.jeepay.core.model.rqrs.payorder.QueryPayOrderRS;
|
||||
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
|
||||
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRS;
|
||||
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.AutoBarOrderRQ;
|
||||
import com.jeequan.jeepay.core.utils.AmountUtil;
|
||||
import com.jeequan.jeepay.core.utils.JeepayKit;
|
||||
import com.jeequan.jeepay.core.utils.SeqKit;
|
||||
import com.jeequan.jeepay.db.entity.*;
|
||||
import com.jeequan.jeepay.model.DeviceInfo;
|
||||
import com.jeequan.jeepay.pay.ctrl.payorder.AbstractPayOrderController;
|
||||
import com.jeequan.jeepay.pay.service.ChannelOrderReissueService;
|
||||
import com.jeequan.jeepay.service.impl.*;
|
||||
import com.jeequan.jeepay.thirdparty.service.zw.model.request.PrintPdRequest;
|
||||
import com.jeequan.jeepay.thirdparty.service.zw.model.request.UpdatePayParamRequest;
|
||||
import com.jeequan.jeepay.thirdparty.service.zw.model.request.ZwPayParam;
|
||||
import com.jeequan.jeepay.thirdparty.service.zw.model.request.ZwPayQueryParam;
|
||||
import com.jeequan.jeepay.thirdparty.service.zw.uils.SignatureUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author yangjun
|
||||
* @version ZwController.java, v 0.1 2023-02-21 09:58 yangjun
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/zw")
|
||||
public class ZwController extends AbstractPayOrderController {
|
||||
|
||||
@Autowired
|
||||
private MchStoreDeviceService mchStoreDeviceService;
|
||||
@Autowired
|
||||
private MchInfoService mchInfoService;
|
||||
@Autowired
|
||||
private DeviceProvideConfigService deviceProvideConfigService;
|
||||
@Autowired
|
||||
private MchStoreService mchStoreService;
|
||||
@Autowired
|
||||
private MchAppService mchAppService;
|
||||
@Autowired
|
||||
private PayWayService payWayService;
|
||||
@Autowired
|
||||
private PayOrderService payOrderService;
|
||||
@Autowired
|
||||
private ChannelOrderReissueService channelOrderReissueService;
|
||||
|
||||
/**
|
||||
* 支付 校验数据 + 验签
|
||||
*
|
||||
* @param reqString
|
||||
* @param zwPayParam
|
||||
* @return
|
||||
*/
|
||||
private Object checkParamsWithSign(String reqString, ZwPayParam zwPayParam) {
|
||||
String deviceId = zwPayParam.getSn();
|
||||
if (StringUtils.isBlank(deviceId)) {
|
||||
return "设备sn不能为空";
|
||||
}
|
||||
String sign = zwPayParam.getMd5();
|
||||
if (StringUtils.isBlank(sign)) {
|
||||
return "签名参数不能为空";
|
||||
}
|
||||
|
||||
JSONObject respJson = JSONObject.parseObject(reqString);
|
||||
String termid = respJson.getString("termid");
|
||||
|
||||
// 打印机设备信息
|
||||
MchStoreDevice mchStoreDevice = mchStoreDeviceService.getOne(MchStoreDevice.gw()
|
||||
.eq(MchStoreDevice::getDeviceType, MchStoreDevice.DEVICE_TYPE_PRINTER)
|
||||
.eq(MchStoreDevice::getDeviceNo, deviceId)
|
||||
.eq(MchStoreDevice::getProvider, termid)
|
||||
);
|
||||
if (mchStoreDevice == null) {
|
||||
return "该设备没有入库";
|
||||
} else if (mchStoreDevice.getState() != CS.YES) {
|
||||
return "该设备已停用";
|
||||
} else if (mchStoreDevice.getBindState() != CS.YES) {
|
||||
return "该设备没有绑定商户";
|
||||
}
|
||||
|
||||
DeviceProvideConfig deviceProvideConfig = deviceProvideConfigService.getOne(DeviceProvideConfig.gw()
|
||||
.eq(DeviceProvideConfig::getConfigId, mchStoreDevice.getConfigId())
|
||||
.eq(DeviceProvideConfig::getDeviceType, MchStoreDevice.DEVICE_TYPE_PRINTER)
|
||||
.eq(DeviceProvideConfig::getProvider, CS.DEVICE_PROVIDER.ZW));
|
||||
if (deviceProvideConfig == null) {
|
||||
return "设备厂商异常";
|
||||
}
|
||||
|
||||
// 验签
|
||||
ZwParams.ProviderParams zwProviderParams = JSONObject.parseObject(deviceProvideConfig.getProviderParams(), ZwParams.ProviderParams.class);
|
||||
try {
|
||||
Boolean checkSign = SignatureUtil.checkIsSignValidFromResponseString(reqString, zwProviderParams.getMd5Key());
|
||||
if (!checkSign) {
|
||||
return "验签失败";
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
log.error("ZwController >> checkParamsWithSign >> 智网打印机支付请求验签异常:deviceProvideConfig ={}", deviceProvideConfig, ex);
|
||||
return "验签异常";
|
||||
}
|
||||
|
||||
|
||||
String provider = deviceProvideConfig.getProvider();
|
||||
String mchNo = mchStoreDevice.getMchNo();
|
||||
|
||||
MchInfo mchInfo = mchInfoService.getById(mchNo);
|
||||
if (mchInfo == null) {
|
||||
return "设备绑定商户不存在";
|
||||
} else if (mchInfo.getState() != CS.YES) {
|
||||
return "当前商户已停用";
|
||||
}
|
||||
|
||||
String mchAppId = mchStoreDevice.getAppId();
|
||||
MchAppEntity mchAppEntity = mchAppService.getById(mchAppId);
|
||||
if (mchAppEntity == null) {
|
||||
return "设备绑定应用不存在";
|
||||
} else if (mchAppEntity.getState() != CS.YES) {
|
||||
return "设备绑定应用已停用";
|
||||
}
|
||||
|
||||
String storeId = mchStoreDevice.getStoreId();
|
||||
MchStore mchStore = mchStoreService.getById(storeId);
|
||||
if (mchStore == null) {
|
||||
return "设备绑定门店不存在";
|
||||
}
|
||||
|
||||
JSONObject deviceParamsJSON = new JSONObject();
|
||||
deviceParamsJSON.put("mchInfo", mchInfo);
|
||||
deviceParamsJSON.put("mchStore", mchStore);
|
||||
deviceParamsJSON.put("mchApp", mchAppEntity);
|
||||
deviceParamsJSON.put("provider", provider);
|
||||
deviceParamsJSON.put("deviceNo", mchStoreDevice.getDeviceNo());
|
||||
return deviceParamsJSON;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建小票
|
||||
*
|
||||
* @param bizRes
|
||||
* @param deviceSn
|
||||
* @return
|
||||
*/
|
||||
private List<PrintPdRequest> buildTicket(QueryPayOrderRS bizRes, String deviceSn) {
|
||||
// 判断设备支付小票打印权限
|
||||
MchStoreDevice dbRecord = mchStoreDeviceService.getOne(MchStoreDevice.gw()
|
||||
.eq(MchStoreDevice::getDeviceType, MchStoreDevice.DEVICE_TYPE_PRINTER)
|
||||
.eq(MchStoreDevice::getDeviceNo, deviceSn));
|
||||
UpdatePayParamRequest updatePayParamRequest = JSONObject.parseObject(dbRecord.getDeviceParams(), UpdatePayParamRequest.class);
|
||||
if (updatePayParamRequest != null && updatePayParamRequest.getBill_pay_print() != null && updatePayParamRequest.getBill_pay_print() == 0) {
|
||||
return null;
|
||||
}
|
||||
List<PrintPdRequest> printPdRequests = new ArrayList<>();
|
||||
PrintPdRequest printPdRequest = new PrintPdRequest();
|
||||
printPdRequest.setTp("3");
|
||||
printPdRequests.add(printPdRequest);
|
||||
|
||||
printPdRequest = new PrintPdRequest();
|
||||
printPdRequest.setTp("1");
|
||||
printPdRequest.setCv("支付凭证");
|
||||
printPdRequest.setAl("1");
|
||||
printPdRequest.setFs("3");
|
||||
printPdRequest.setTb("1");
|
||||
printPdRequests.add(printPdRequest);
|
||||
|
||||
printPdRequest = new PrintPdRequest();
|
||||
printPdRequest.setTp("4");
|
||||
printPdRequests.add(printPdRequest);
|
||||
|
||||
printPdRequest = new PrintPdRequest();
|
||||
printPdRequest.setTp("1");
|
||||
printPdRequest.setCv("门店名称:" + bizRes.getStoreId());
|
||||
printPdRequests.add(printPdRequest);
|
||||
|
||||
printPdRequest = new PrintPdRequest();
|
||||
printPdRequest.setTp("1");
|
||||
printPdRequest.setCv("订 单 号:" + bizRes.getPayOrderId());
|
||||
printPdRequests.add(printPdRequest);
|
||||
|
||||
printPdRequest = new PrintPdRequest();
|
||||
printPdRequest.setTp("1");
|
||||
printPdRequest.setCv("商户单号:" + bizRes.getMchOrderNo());
|
||||
printPdRequests.add(printPdRequest);
|
||||
|
||||
printPdRequest = new PrintPdRequest();
|
||||
printPdRequest.setTp("1");
|
||||
printPdRequest.setCv("支付状态:" + "");
|
||||
printPdRequests.add(printPdRequest);
|
||||
|
||||
printPdRequest = new PrintPdRequest();
|
||||
printPdRequest.setTp("1");
|
||||
printPdRequest.setCv("支付方式:" + CS.PAY_WAY_CODE_TYPE_MAP.get(bizRes.getWayCodeType()));
|
||||
printPdRequests.add(printPdRequest);
|
||||
|
||||
printPdRequest = new PrintPdRequest();
|
||||
printPdRequest.setTp("1");
|
||||
printPdRequest.setCv("下单时间:" + "");
|
||||
printPdRequests.add(printPdRequest);
|
||||
|
||||
printPdRequest = new PrintPdRequest();
|
||||
printPdRequest.setTp("4");
|
||||
printPdRequests.add(printPdRequest);
|
||||
|
||||
printPdRequest = new PrintPdRequest();
|
||||
printPdRequest.setTp("1");
|
||||
printPdRequest.setCv("完成时间:" + "");
|
||||
printPdRequests.add(printPdRequest);
|
||||
|
||||
printPdRequest = new PrintPdRequest();
|
||||
printPdRequest.setTp("1");
|
||||
printPdRequest.setCv("交易金额:" + AmountUtil.convertCent2Dollar(bizRes.getAmount().toString()));
|
||||
printPdRequests.add(printPdRequest);
|
||||
|
||||
return printPdRequests;
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping("/pay")
|
||||
public ZwPosRes pay(HttpServletRequest request) {
|
||||
|
||||
String resultStr = getBody(request);
|
||||
log.info("ZwController >> pay >> 智网打印机支付请求:{}", resultStr);
|
||||
|
||||
ZwPayParam zwPayParam = JSONObject.parseObject(resultStr, ZwPayParam.class);
|
||||
|
||||
if (StringUtils.isBlank(zwPayParam.getAmount())) {
|
||||
return ZwPosRes.error(zwPayParam.getUuid(), "支付金额不能为空");
|
||||
}
|
||||
if (StringUtils.isBlank(zwPayParam.getPaycode())) {
|
||||
return ZwPosRes.error(zwPayParam.getUuid(), "条码不能为空");
|
||||
}
|
||||
|
||||
// 验证通用请求字段、签名true
|
||||
Object object = checkParamsWithSign(resultStr, zwPayParam);
|
||||
if (object instanceof String) {
|
||||
log.info("ZwController >> pay >> 智网打印机支付,验证不过.error={}", object);
|
||||
return ZwPosRes.error(zwPayParam.getUuid(), (String) object);
|
||||
}
|
||||
JSONObject deviceJSON = (JSONObject) object;
|
||||
try {
|
||||
ApiRes apiRes = doPayNew(zwPayParam, deviceJSON);
|
||||
log.info("ZwController >> pay >> 下单返回参数.ApiRes={}", apiRes);
|
||||
if (apiRes.getCode().equals(ApiCodeEnum.CUSTOM_FAIL.getCode())) {
|
||||
return ZwPosRes.error(zwPayParam.getUuid(), apiRes.getMsg());
|
||||
}
|
||||
UnifiedOrderRS bizRs = (UnifiedOrderRS) apiRes.getData();
|
||||
log.info("ZwController >> pay >> 下单响应参数 bizRs = {}", JSON.toJSON(bizRs));
|
||||
// 支付小票
|
||||
if (bizRs.getOrderState() == PayOrder.STATE_SUCCESS) {
|
||||
ZwPosRes zwPosRes = ZwPosRes.success(zwPayParam.getUuid(), "支付成功");
|
||||
log.info("ZwController >> pay >> 智网打印机支付 zwPosRes = {}", JSON.toJSON(zwPosRes));
|
||||
return zwPosRes;
|
||||
} else if (bizRs.getOrderState() == PayOrder.STATE_FAIL
|
||||
|| bizRs.getOrderState() == PayOrder.STATE_CANCEL
|
||||
|| bizRs.getOrderState() == PayOrder.STATE_REFUND
|
||||
|| bizRs.getOrderState() == PayOrder.STATE_CLOSED
|
||||
) {
|
||||
ZwPosRes zwPosRes = ZwPosRes.error(zwPayParam.getUuid(), bizRs.getErrCode() + bizRs.getErrMsg());
|
||||
log.info("ZwController >> pay >> 智网打印机支付 zwPosRes = {}", JSON.toJSON(zwPosRes));
|
||||
return zwPosRes;
|
||||
}
|
||||
ZwPosRes zwPosRes = ZwPosRes.ing(zwPayParam.getUuid(), "支付中");
|
||||
log.info("ZwController >> pay >> 智网打印机支付 zwPosRes = {}", JSON.toJSON(zwPosRes));
|
||||
return zwPosRes;
|
||||
} catch (BizException e) {
|
||||
log.error("PosController >> pos >> 打印机下单异常:", e);
|
||||
return ZwPosRes.error("StringPool.EMPTY", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付查单
|
||||
**/
|
||||
@RequestMapping("/query")
|
||||
public ZwPosRes query(HttpServletRequest request) {
|
||||
String resultStr = getBody(request);
|
||||
ZwPayParam zwPayParam = JSONObject.parseObject(resultStr, ZwPayParam.class);
|
||||
|
||||
log.info("ZwController >> pay >> 智网打印机支付查单请求:zwPayParam: {}", JSON.toJSON(zwPayParam));
|
||||
ZwPayQueryParam zwPayQueryParam = JSONObject.parseObject(resultStr, ZwPayQueryParam.class);
|
||||
if (StringUtils.isBlank(zwPayQueryParam.getUuid())) {
|
||||
return ZwPosRes.ing(zwPayQueryParam.getUuid(), "订单号不能为空");
|
||||
}
|
||||
|
||||
// 验证通用请求字段、签名true
|
||||
Object object = checkParamsWithSign(resultStr, zwPayParam);
|
||||
if (object instanceof String) {
|
||||
return ZwPosRes.error(zwPayParam.getUuid(), (String) object);
|
||||
}
|
||||
JSONObject deviceJSON = (JSONObject) object;
|
||||
String merchantId = deviceJSON.getJSONObject("mchInfo").getString("mchNo");
|
||||
|
||||
// 支持根据商户单号查询
|
||||
PayOrder payOrder = payOrderService.queryMchOrder(merchantId, null, zwPayQueryParam.getUuid());
|
||||
if (payOrder == null) {
|
||||
return ZwPosRes.ing(zwPayQueryParam.getUuid(), "订单信息错误");
|
||||
}
|
||||
|
||||
// 支付中主查
|
||||
if (PayOrder.STATE_ING == payOrder.getState()) {
|
||||
ChannelRetMsg channelRetMsg = channelOrderReissueService.processPayOrder(payOrder);
|
||||
if (channelRetMsg != null && channelRetMsg.getChannelState() != null && channelRetMsg.getChannelState().equals(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS)) {
|
||||
return ZwPosRes.success(zwPayQueryParam.getUuid(), null);
|
||||
}
|
||||
}
|
||||
|
||||
// 支付小票
|
||||
if (payOrder.getState() == PayOrder.STATE_SUCCESS) {
|
||||
List<PrintPdRequest> printPdRequests = buildTicket(QueryPayOrderRS.buildByPayOrder(payOrder), zwPayQueryParam.getSn());
|
||||
ZwPosRes zwPosRes = ZwPosRes.success(zwPayQueryParam.getUuid(), printPdRequests.toString());
|
||||
log.info("ZwController >> pay >> 智网打印机支付 zwPosRes = {}", JSON.toJSON(zwPosRes));
|
||||
return zwPosRes;
|
||||
} else if (payOrder.getState() == PayOrder.STATE_FAIL
|
||||
|| payOrder.getState() == PayOrder.STATE_CANCEL
|
||||
|| payOrder.getState() == PayOrder.STATE_REFUND
|
||||
|| payOrder.getState() == PayOrder.STATE_CLOSED
|
||||
) {
|
||||
return ZwPosRes.error(zwPayQueryParam.getUuid(), payOrder.getErrMsg());
|
||||
}
|
||||
return ZwPosRes.ing(zwPayQueryParam.getUuid(), "支付中");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 智网打印机 支付优化
|
||||
*
|
||||
* @param zwPayParam
|
||||
* @param deviceJSON
|
||||
* @return
|
||||
*/
|
||||
private ApiRes doPayNew(ZwPayParam zwPayParam, JSONObject deviceJSON) {
|
||||
// 下单参数
|
||||
String amount = AmountUtil.convertDollar2Cent(zwPayParam.getAmount());
|
||||
String wayCode = JeepayKit.getPayWayCodeByBarCode(zwPayParam.getPaycode());
|
||||
// 付款码
|
||||
String authCode = zwPayParam.getPaycode();
|
||||
|
||||
MchAppEntity mchAppEntity = deviceJSON.getObject("mchApp", MchAppEntity.class);
|
||||
MchStore mchStore = deviceJSON.getObject("mchStore", MchStore.class);
|
||||
UnifiedOrderRQ unifiedOrderRQ = new UnifiedOrderRQ();
|
||||
// 商户号
|
||||
unifiedOrderRQ.setMchNo(mchAppEntity.getMchNo());
|
||||
unifiedOrderRQ.setAppId(mchAppEntity.getAppId());
|
||||
unifiedOrderRQ.setStoreId(mchStore.getStoreId());
|
||||
// 商户单号特殊处理
|
||||
String deviceOrderNum = zwPayParam.getUuid();
|
||||
if (StringUtils.isNotBlank(deviceOrderNum)) {
|
||||
unifiedOrderRQ.setMchOrderNo(deviceOrderNum);
|
||||
} else {
|
||||
unifiedOrderRQ.setMchOrderNo(SeqKit.genMhoOrderId());
|
||||
}
|
||||
unifiedOrderRQ.setWayCode(wayCode);
|
||||
unifiedOrderRQ.setAmount(Long.valueOf(amount));
|
||||
unifiedOrderRQ.setCurrency("CNY");
|
||||
unifiedOrderRQ.setClientIp(getClientIp());
|
||||
unifiedOrderRQ.setSubject("标题");
|
||||
unifiedOrderRQ.setBody("商品信息");
|
||||
unifiedOrderRQ.setSignType(MchApp.SIGN_TYPE_MD5);
|
||||
// DBApplicationConfig dbApplicationConfig = sysConfigService.getDBApplicationConfig();
|
||||
// 回调地址
|
||||
// unifiedOrderRQ.setNotifyUrl(dbApplicationConfig.getMchSiteUrl() + "/api/anon/paytestNotify/payOrder");
|
||||
// 设置扩展参数
|
||||
JSONObject extParams = new JSONObject();
|
||||
extParams.put("authCode", authCode.trim());
|
||||
unifiedOrderRQ.setChannelExtra(extParams.toString());
|
||||
// 设备信息
|
||||
DeviceInfo deviceInfo = new DeviceInfo();
|
||||
deviceInfo.setDeviceType(PayOrder.DEVICE_TYPE_PRINTER);
|
||||
deviceInfo.setDeviceNo(deviceJSON.getString("deviceNo"));
|
||||
deviceInfo.setProvider(deviceJSON.getString("provider"));
|
||||
unifiedOrderRQ.setDeviceInfo(JSON.toJSONString(deviceInfo));
|
||||
|
||||
try {
|
||||
UnifiedOrderRQ bizRQ = buildBizRQ(unifiedOrderRQ);
|
||||
|
||||
return this.unifiedOrder(bizRQ.getWayCode(), bizRQ);
|
||||
} catch (BizException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new BizException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 统一下单
|
||||
*
|
||||
* @param rq
|
||||
* @return
|
||||
*/
|
||||
public ApiRes unifiedOrder(UnifiedOrderRQ rq) {
|
||||
UnifiedOrderRQ bizRQ = buildBizRQ(rq);
|
||||
//实现子类的res
|
||||
ApiRes apiRes = unifiedOrder(bizRQ.getWayCode(), bizRQ);
|
||||
if (apiRes.getData() == null) {
|
||||
return apiRes;
|
||||
}
|
||||
UnifiedOrderRS bizRes = (UnifiedOrderRS) apiRes.getData();
|
||||
//聚合接口,返回的参数
|
||||
UnifiedOrderRS res = new UnifiedOrderRS();
|
||||
BeanUtils.copyProperties(bizRes, res);
|
||||
//只有 订单生成(QR_CASHIER) || 支付中 || 支付成功返回该数据
|
||||
if (bizRes.getOrderState() != null
|
||||
&& (bizRes.getOrderState() == PayOrder.STATE_INIT
|
||||
|| bizRes.getOrderState() == PayOrder.STATE_ING
|
||||
|| bizRes.getOrderState() == PayOrder.STATE_SUCCESS)) {
|
||||
res.setPayDataType(bizRes.buildPayDataType());
|
||||
res.setPayData(bizRes.buildPayData());
|
||||
}
|
||||
return ApiRes.ok(res);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 支付参数构建
|
||||
*
|
||||
* @param rq
|
||||
* @return
|
||||
*/
|
||||
private UnifiedOrderRQ buildBizRQ(UnifiedOrderRQ rq) {
|
||||
//支付方式 比如: ali_bar
|
||||
String wayCode = rq.getWayCode();
|
||||
|
||||
//jsapi 收银台聚合支付场景 (不校验是否存在payWayCode)
|
||||
if (CS.PAY_WAY_CODE.QR_CASHIER.equals(wayCode)) {
|
||||
return rq.buildBizRQ();
|
||||
}
|
||||
|
||||
//web收银台聚合支付场景 (不校验是否存在payWayCode)
|
||||
if (CS.PAY_WAY_CODE.WEB_CASHIER.equals(wayCode)) {
|
||||
return rq.buildBizRQ();
|
||||
}
|
||||
|
||||
//如果是自动分类条码
|
||||
if (CS.PAY_WAY_CODE.AUTO_BAR.equals(wayCode)) {
|
||||
|
||||
AutoBarOrderRQ bizRQ = (AutoBarOrderRQ) rq.buildBizRQ();
|
||||
wayCode = JeepayKit.getPayWayCodeByBarCode(bizRQ.getAuthCode());
|
||||
rq.setWayCode(wayCode.trim());
|
||||
}
|
||||
|
||||
if (payWayService.count(PayWay.gw().eq(PayWay::getWayCode, wayCode)) <= 0) {
|
||||
throw new BizException("不支持的支付方式");
|
||||
}
|
||||
|
||||
//转换为 bizRQ
|
||||
return rq.buildBizRQ();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取请求数据
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
protected final String getBody(HttpServletRequest request) {
|
||||
InputStreamReader in = null;
|
||||
try {
|
||||
in = new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8);
|
||||
StringBuffer bf = new StringBuffer();
|
||||
int len;
|
||||
char[] chs = new char[1024];
|
||||
while ((len = in.read(chs)) != -1) {
|
||||
bf.append(new String(chs, 0, len));
|
||||
}
|
||||
return bf.toString();
|
||||
} catch (Exception e) {
|
||||
log.error("请求头部取数据异常:{}", e.getMessage());
|
||||
throw new ServiceException(e.getMessage());
|
||||
} finally {
|
||||
if (null != in) {
|
||||
try {
|
||||
in.close();
|
||||
} catch (Exception e) {
|
||||
log.error("流关闭异常:{}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.jeequan.jeepay.pay.ctrl.zw;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class ZwPosRes {
|
||||
|
||||
/** 业务响应码 **/
|
||||
private Integer code;
|
||||
|
||||
/** 业务响应信息 **/
|
||||
private String message;
|
||||
|
||||
/** 数据对象 **/
|
||||
private String uuid;
|
||||
|
||||
public static ZwPosRes ing(String uuid, String message) {
|
||||
return new ZwPosRes(2, message, uuid);
|
||||
}
|
||||
|
||||
public static ZwPosRes error(String uuid, String message) {
|
||||
return new ZwPosRes(1, message, uuid);
|
||||
}
|
||||
|
||||
public static ZwPosRes success(String uuid, String message) {
|
||||
return new ZwPosRes(0, message, uuid);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.jeequan.jeepay.pay.model;
|
||||
|
||||
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
|
||||
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
|
||||
import com.jeequan.jeepay.db.entity.RouteManage;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @author crystal
|
||||
* @date 2024/5/14 13:49
|
||||
*/
|
||||
@Data
|
||||
public class OpenApiExt {
|
||||
|
||||
private MchAppConfigContext configContext;
|
||||
|
||||
private RouteManage routeManage;
|
||||
|
||||
private UnifiedOrderRQ urq;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.jeequan.jeepay.pay.mq;
|
||||
|
||||
import com.jeequan.jeepay.components.mq.model.CleanTnDayCacheMQ;
|
||||
import com.jeequan.jeepay.core.cache.RedisUtil;
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 接收MQ消息
|
||||
* 业务: 清空所有T+N日期缓存
|
||||
* @author zx
|
||||
* @date 2023/9/22 9:23
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class CleanTnDayCacheMQReceiver implements CleanTnDayCacheMQ.IMQReceiver {
|
||||
|
||||
@Override
|
||||
public void receive(CleanTnDayCacheMQ.MsgPayload payload) {
|
||||
|
||||
log.info("成功接收【清空所有T+N日期缓存】的订阅通知, msg={}", payload);
|
||||
RedisUtil.delByPrefix(CS.CACHE_KEY_DATE_TN_PREFIX);
|
||||
log.info("系统已清空所有T+N日期缓存");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.jeequan.jeepay.pay.mq;
|
||||
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.jeequan.jeepay.components.mq.model.MchAuditMQ;
|
||||
import com.jeequan.jeepay.components.mq.model.MchAuditThirdNotifyMQ;
|
||||
import com.jeequan.jeepay.components.mq.vender.IMQSender;
|
||||
import com.jeequan.jeepay.converter.MchInfoConverter;
|
||||
import com.jeequan.jeepay.core.cache.RedisUtil;
|
||||
import com.jeequan.jeepay.db.entity.MchApplyment;
|
||||
import com.jeequan.jeepay.pay.mq.channel.IsvAuditReceiver;
|
||||
import com.jeequan.jeepay.service.impl.MchApplymentService;
|
||||
import com.jeequan.jeepay.thirdparty.channel.yspay.YspayApplymentApiService;
|
||||
import com.jeequan.jeepay.thirdparty.channel.yspay.YspayMchApplymentService;
|
||||
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Slf4j
|
||||
@Component
|
||||
public class MchAuditMQReceiver implements MchAuditMQ.IMQReceiver {
|
||||
|
||||
@Autowired
|
||||
private MchApplymentService mchApplymentService;
|
||||
|
||||
@Autowired
|
||||
private YspayApplymentApiService yspayApplymentApiService;
|
||||
|
||||
@Autowired
|
||||
private YspayMchApplymentService yspayMchApplymentService;
|
||||
|
||||
@Autowired
|
||||
private ConfigContextQueryService configContextQueryService;
|
||||
|
||||
@Autowired
|
||||
private IMQSender mqSender;
|
||||
|
||||
@Autowired
|
||||
private MchInfoConverter infoConverter;
|
||||
|
||||
@Override
|
||||
public void receive(MchAuditMQ.MsgPayload payload) {
|
||||
|
||||
String applyId = payload.getApplyId();
|
||||
|
||||
MchApplyment mchApplyment = mchApplymentService.getById(applyId);
|
||||
|
||||
if (mchApplyment.getState() != MchApplyment.STATE_AUDITING_WAIT) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mchApplyment.getRemainStep() <= 0) {
|
||||
// 没有剩余处理步骤了,不作处理
|
||||
return;
|
||||
}
|
||||
|
||||
// 添加正在进件的一个键值,防止进件时间过长状态,队列重复处理
|
||||
String redisKey = "auditApplyId_" + mchApplyment.getApplyId();
|
||||
|
||||
if (RedisUtil.hasKey(redisKey)) {
|
||||
// 正在处理,就将塞一条新的消息到rabbitmq中
|
||||
mqSender.send(MchAuditMQ.build(mchApplyment.getApplyId()), 20);
|
||||
return;
|
||||
} else {
|
||||
// 设置60秒超时,若上传图片速度实在太慢,还是需要升级服务器带宽
|
||||
RedisUtil.set(redisKey, "1", 120);
|
||||
}
|
||||
|
||||
IsvAuditReceiver isvAuditReceiver = SpringUtil.getBean(mchApplyment.getIfCode() + "AuditReceiver", IsvAuditReceiver.class);
|
||||
mchApplyment.setLastApplyAt(new Date());
|
||||
try {
|
||||
isvAuditReceiver.consume(mchApplyment);
|
||||
|
||||
if (mchApplyment.getState() == MchApplyment.STATE_AUDITING_WAIT) {
|
||||
return;
|
||||
}
|
||||
|
||||
RedisUtil.del(redisKey);
|
||||
|
||||
mqSender.send(MchAuditThirdNotifyMQ.build(mchApplyment.getApplyId(), MchAuditThirdNotifyMQ.TYPE_AUDIT));
|
||||
} catch (Exception e) {
|
||||
log.info("进件失败, 进件单号{}", applyId, e);
|
||||
MchApplyment result = new MchApplyment();
|
||||
result.setApplyId(applyId);
|
||||
result.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
|
||||
result.setApplyErrorInfo("[进件失败, 请联系客服处理]");
|
||||
mchApplymentService.updateById(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.jeequan.jeepay.pay.mq;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.jeequan.jeepay.components.mq.model.MchAuditNotifyMQ;
|
||||
import com.jeequan.jeepay.components.mq.model.MchAuditThirdNotifyMQ;
|
||||
import com.jeequan.jeepay.components.mq.vender.IMQSender;
|
||||
import com.jeequan.jeepay.converter.MchInfoConverter;
|
||||
import com.jeequan.jeepay.core.interfaces.paychannel.IIsvmchApplymentNotifyService;
|
||||
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
|
||||
import com.jeequan.jeepay.db.entity.MchApplyment;
|
||||
import com.jeequan.jeepay.service.impl.MchApplymentService;
|
||||
import com.jeequan.jeepay.thirdparty.channel.yspay.YspayApplymentApiService;
|
||||
import com.jeequan.jeepay.thirdparty.channel.yspay.YspayMchApplymentService;
|
||||
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.tuple.MutablePair;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Slf4j
|
||||
@Component
|
||||
public class MchAuditNotifyMQReceiver implements MchAuditNotifyMQ.IMQReceiver {
|
||||
|
||||
@Autowired
|
||||
private MchApplymentService mchApplymentService;
|
||||
|
||||
@Autowired
|
||||
private YspayApplymentApiService yspayApplymentApiService;
|
||||
|
||||
@Autowired
|
||||
private YspayMchApplymentService yspayMchApplymentService;
|
||||
|
||||
@Autowired
|
||||
private ConfigContextQueryService configContextQueryService;
|
||||
|
||||
@Autowired
|
||||
private IMQSender mqSender;
|
||||
|
||||
@Autowired
|
||||
private MchInfoConverter infoConverter;
|
||||
|
||||
@Override
|
||||
public void receive(MchAuditNotifyMQ.MsgPayload payload) {
|
||||
|
||||
JSONObject notifyJSON = payload.getNotifyData();
|
||||
String ifCode = payload.getIfCode();
|
||||
|
||||
IIsvmchApplymentNotifyService notifyService = SpringBeansUtil.getBean(ifCode + "IsvmchApplymentNotifyService", IIsvmchApplymentNotifyService.class);
|
||||
Assert.notNull(notifyService, "通知回调服务类缺失");
|
||||
MutablePair<com.jeequan.jeepay.core.entity.MchApplyment, ResponseEntity> responseEntityMutablePair = notifyService.doNotify(null, notifyJSON, null);
|
||||
|
||||
com.jeequan.jeepay.core.entity.MchApplyment result = responseEntityMutablePair.getLeft();
|
||||
mchApplymentService.updateById(infoConverter.toDbEntity(result));
|
||||
|
||||
if (result.getState() == MchApplyment.STATE_SUCCESS || result.getState() == MchApplyment.STATE_SUCCESS_NEED_SECOND_VERIFY) {
|
||||
mchApplymentService.onApplymentSuccess(result.getApplyId());
|
||||
mqSender.send(MchAuditThirdNotifyMQ.build(result.getApplyId(), MchAuditThirdNotifyMQ.TYPE_AUDIT));
|
||||
}
|
||||
|
||||
log.info("进件通知完成。 applyId={}, parseState = {} =====", result.getApplyId(), result.getState());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
package com.jeequan.jeepay.pay.mq;
|
||||
|
||||
import cn.hutool.core.net.url.UrlBuilder;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.http.ContentType;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.jeequan.jeepay.components.mq.model.MchAuditThirdNotifyMQ;
|
||||
import com.jeequan.jeepay.components.mq.vender.IMQSender;
|
||||
import com.jeequan.jeepay.converter.MchInfoConverter;
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.model.openapi.AccessError;
|
||||
import com.jeequan.jeepay.core.model.openapi.AccessReq;
|
||||
import com.jeequan.jeepay.core.model.openapi.AccessResp;
|
||||
import com.jeequan.jeepay.core.model.openapi.biz.AuditResultResp;
|
||||
import com.jeequan.jeepay.db.entity.MchAppEntity;
|
||||
import com.jeequan.jeepay.db.entity.MchApplyment;
|
||||
import com.jeequan.jeepay.db.entity.MchNotifyRecord;
|
||||
import com.jeequan.jeepay.db.entity.MchStore;
|
||||
import com.jeequan.jeepay.pay.util.ApiResKit;
|
||||
import com.jeequan.jeepay.service.impl.MchAppService;
|
||||
import com.jeequan.jeepay.service.impl.MchApplymentService;
|
||||
import com.jeequan.jeepay.service.impl.MchNotifyRecordService;
|
||||
import com.jeequan.jeepay.service.impl.MchStoreService;
|
||||
import com.jeequan.jeepay.thirdparty.channel.yspay.YspayApplymentApiService;
|
||||
import com.jeequan.jeepay.thirdparty.channel.yspay.YspayMchApplymentService;
|
||||
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Cleanup;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Slf4j
|
||||
@Component
|
||||
public class MchAuditThirdNotifyMQReceiver implements MchAuditThirdNotifyMQ.IMQReceiver {
|
||||
|
||||
@Autowired
|
||||
private MchApplymentService mchApplymentService;
|
||||
|
||||
@Autowired
|
||||
private YspayApplymentApiService yspayApplymentApiService;
|
||||
|
||||
@Autowired
|
||||
private YspayMchApplymentService yspayMchApplymentService;
|
||||
|
||||
@Autowired
|
||||
private ConfigContextQueryService configContextQueryService;
|
||||
|
||||
@Autowired
|
||||
private IMQSender mqSender;
|
||||
|
||||
@Autowired
|
||||
private MchInfoConverter infoConverter;
|
||||
|
||||
@Autowired
|
||||
private MchNotifyRecordService mchNotifyRecordService;
|
||||
|
||||
@Autowired
|
||||
private MchAppService mchAppService;
|
||||
|
||||
@Autowired
|
||||
private MchInfoConverter mchInfoConverter;
|
||||
@Autowired
|
||||
private MchStoreService mchStoreService;
|
||||
|
||||
@Override
|
||||
public void receive(MchAuditThirdNotifyMQ.MsgPayload payload) {
|
||||
|
||||
try {
|
||||
log.info("接收商户通知MQ, msg={}", payload.toString());
|
||||
|
||||
MchApplyment mchApplyment = mchApplymentService.getById(payload.getApplyId());
|
||||
|
||||
// 通知地址为空
|
||||
if (ObjUtil.isEmpty(mchApplyment.getNotifyUrl())) {
|
||||
return;
|
||||
}
|
||||
|
||||
String orderId = mchApplyment.getApplyId() + "_" + mchApplyment.getUpdatedAt().getTime();
|
||||
MchNotifyRecord auditOrder = mchNotifyRecordService.findAuditOrder(orderId);
|
||||
MchStore mchStore = mchStoreService.getByMchApplyId(mchApplyment.getApplyId());
|
||||
|
||||
if (auditOrder != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
AccessResp body = bulidNotifyData(mchApplyment, mchStore);
|
||||
|
||||
auditOrder = new MchNotifyRecord();
|
||||
auditOrder.setOrderId(orderId);
|
||||
auditOrder.setOrderType(MchNotifyRecord.TYPE_AUDIT);
|
||||
auditOrder.setMchNo(mchApplyment.getMchNo());
|
||||
auditOrder.setIsvNo(mchApplyment.getIsvNo());
|
||||
auditOrder.setNotifyBody(JSON.toJSONString(body));
|
||||
auditOrder.setAppId(mchApplyment.getAutoConfigMchAppId());
|
||||
auditOrder.setNotifyUrl(mchApplyment.getNotifyUrl());
|
||||
auditOrder.setNotifyPostType(CS.NOTIFY_POST_TYPE.POST_JSON);
|
||||
auditOrder.setResResult("");
|
||||
auditOrder.setNotifyCount(0);
|
||||
auditOrder.setMchOrderNo(mchApplyment.getSubApplyId());
|
||||
auditOrder.setState(MchNotifyRecord.STATE_ING);
|
||||
|
||||
try {
|
||||
mchNotifyRecordService.save(auditOrder);
|
||||
} catch (Exception e) {
|
||||
log.info("回调通知数据插入失败", e);
|
||||
log.info("数据库已存在[{}]消息,本次不再推送。", auditOrder.getOrderId());
|
||||
return;
|
||||
}
|
||||
|
||||
MchNotifyRecord record = mchNotifyRecordService.findAuditOrder(orderId);
|
||||
if(record == null || record.getState() != MchNotifyRecord.STATE_ING){
|
||||
log.info("查询通知记录不存在或状态不是通知中");
|
||||
return;
|
||||
}
|
||||
if( record.getNotifyCount() >= record.getNotifyCountLimit() ){
|
||||
log.info("已达到最大发送次数");
|
||||
return;
|
||||
}
|
||||
|
||||
Long notifyId = record.getNotifyId();
|
||||
|
||||
//1. (发送结果最多6次)
|
||||
int currentCount = record.getNotifyCount() + 1;
|
||||
|
||||
String notifyUrl = record.getNotifyUrl();
|
||||
String res = "";
|
||||
try {
|
||||
int timeout = 20000;
|
||||
// body形式
|
||||
@Cleanup HttpResponse httpResponse = HttpUtil.createPost(record.getNotifyUrl())
|
||||
.body(record.getNotifyBody(), ContentType.JSON.getValue())
|
||||
.timeout(timeout)
|
||||
.execute();
|
||||
res = httpResponse.body();
|
||||
} catch (Exception e) {
|
||||
log.error("http error", e);
|
||||
res = "连接["+ UrlBuilder.of(notifyUrl).getHost() +"]异常:【" + e.getMessage() + "】";
|
||||
}
|
||||
|
||||
//通知成功
|
||||
if("SUCCESS".equalsIgnoreCase(res)){
|
||||
mchNotifyRecordService.updateNotifyResult(notifyId, MchNotifyRecord.STATE_SUCCESS, res);
|
||||
return;
|
||||
}
|
||||
|
||||
//通知次数 >= 最大通知次数时, 更新响应结果为异常, 不在继续延迟发送消息
|
||||
if( currentCount >= record.getNotifyCountLimit() ){
|
||||
mchNotifyRecordService.updateNotifyResult(notifyId, MchNotifyRecord.STATE_FAIL, res);
|
||||
return;
|
||||
}
|
||||
|
||||
// 继续发送MQ 延迟发送
|
||||
mchNotifyRecordService.updateNotifyResult(notifyId, MchNotifyRecord.STATE_ING, res);
|
||||
// 通知延时次数
|
||||
// 1 2 3 4 5 6
|
||||
// 0 30 60 90 120 150
|
||||
mqSender.send(MchAuditThirdNotifyMQ.build(payload.getApplyId(), MchAuditThirdNotifyMQ.TYPE_AUDIT), currentCount * 30);
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知参数
|
||||
*
|
||||
* @param payOrder
|
||||
* @param mchStore
|
||||
* @return
|
||||
*/
|
||||
private AccessResp bulidNotifyData(MchApplyment payOrder, MchStore mchStore) {
|
||||
AccessReq req = new AccessReq();
|
||||
req.setAppId(payOrder.getAutoConfigMchAppId());
|
||||
MchAppEntity mchApp = mchAppService.getById(payOrder.getAutoConfigMchAppId());
|
||||
req.setAppInfo(mchInfoConverter.toModel(mchApp));
|
||||
req.setPlatPriKey(ApiResKit.getKey());
|
||||
|
||||
AuditResultResp bizResp = new AuditResultResp(mchInfoConverter.toModel(payOrder));
|
||||
bizResp.setMchStore(mchStore);
|
||||
// 生成通知
|
||||
return new AccessResp(AccessError.SUCCESS, bizResp, req);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.jeequan.jeepay.pay.mq;
|
||||
|
||||
import com.jeequan.jeepay.components.mq.model.PayOrderCashoutMQ;
|
||||
import com.jeequan.jeepay.pay.service.MchChannelCashoutService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 接收MQ消息
|
||||
* 业务: 订单支付成功自动提现通知
|
||||
* @author zx
|
||||
* @date 2021/7/27 9:23
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class PayOrderCashoutMQReceiver implements PayOrderCashoutMQ.IMQReceiver {
|
||||
|
||||
@Autowired private MchChannelCashoutService mchChannelCashoutService;
|
||||
|
||||
@Override
|
||||
public void receive(PayOrderCashoutMQ.MsgPayload payload) {
|
||||
|
||||
try {
|
||||
log.info("接收商户通知MQ, msg={}", payload.toString());
|
||||
|
||||
String payOrderId = payload.getPayOrderId();
|
||||
|
||||
// 发起提现
|
||||
mchChannelCashoutService.cashout(payOrderId);
|
||||
|
||||
}catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.jeequan.jeepay.pay.mq;
|
||||
|
||||
import com.jeequan.jeepay.components.mq.model.PayOrderDivisionMQ;
|
||||
import com.jeequan.jeepay.pay.service.PayOrderDivisionProcessService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 接收MQ消息
|
||||
* 业务: 支付订单分账处理逻辑
|
||||
* @author terrfly
|
||||
* @date 2021/8/22 8:23
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class PayOrderDivisionMQReceiver implements PayOrderDivisionMQ.IMQReceiver {
|
||||
|
||||
@Autowired private PayOrderDivisionProcessService payOrderDivisionProcessService;
|
||||
|
||||
@Override
|
||||
public void receive(PayOrderDivisionMQ.MsgPayload payload) {
|
||||
|
||||
try {
|
||||
log.info("接收订单分账通知MQ, msg={}", payload.toString());
|
||||
payOrderDivisionProcessService.processPayOrderDivision(payload.getPayOrderId(), payload.getUseSysAutoDivisionReceivers(), payload.getReceiverList(), payload.getIsResend(), payload.getIsResendAndRecalAmount());
|
||||
}catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.jeequan.jeepay.pay.mq;
|
||||
|
||||
import com.jeequan.jeepay.components.mq.model.PayOrderMarketMQ;
|
||||
import com.jeequan.jeepay.pay.service.PayOrderMarketReissueService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
* 订单营销活动消费类
|
||||
* @author crystal
|
||||
* @date 2024/1/29 16:10
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class PayOrderMarketMQReceiver implements PayOrderMarketMQ.IMQReceiver {
|
||||
|
||||
@Autowired
|
||||
private PayOrderMarketReissueService payOrderMarketReissueService;
|
||||
|
||||
@Override
|
||||
public void receive(PayOrderMarketMQ.MsgPayload payload) {
|
||||
try {
|
||||
log.info("接收订单营销转账通知MQ, msg={}", payload.toString());
|
||||
payOrderMarketReissueService.transferReissue(payload.getTransferId(), payload.getType());
|
||||
}catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.jeequan.jeepay.pay.mq;
|
||||
|
||||
import cn.hutool.core.net.url.UrlBuilder;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.jeequan.jeepay.components.mq.model.PayOrderMchNotifyMQ;
|
||||
import com.jeequan.jeepay.components.mq.vender.IMQSender;
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.db.entity.MchNotifyRecord;
|
||||
import com.jeequan.jeepay.service.impl.MchNotifyRecordService;
|
||||
import com.jeequan.jeepay.service.impl.PayOrderService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 接收MQ消息
|
||||
* 业务: 支付订单商户通知
|
||||
* @author terrfly
|
||||
* @date 2021/7/27 9:23
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class PayOrderMchNotifyMQReceiver implements PayOrderMchNotifyMQ.IMQReceiver {
|
||||
|
||||
@Autowired
|
||||
private PayOrderService payOrderService;
|
||||
@Autowired
|
||||
private MchNotifyRecordService mchNotifyRecordService;
|
||||
@Autowired
|
||||
private IMQSender mqSender;
|
||||
|
||||
@Override
|
||||
public void receive(PayOrderMchNotifyMQ.MsgPayload payload) {
|
||||
|
||||
try {
|
||||
log.info("接收商户通知MQ, msg={}", payload.toString());
|
||||
|
||||
Long notifyId = payload.getNotifyId();
|
||||
MchNotifyRecord record = mchNotifyRecordService.getById(notifyId);
|
||||
if(record == null || record.getState() != MchNotifyRecord.STATE_ING){
|
||||
log.info("查询通知记录不存在或状态不是通知中");
|
||||
return;
|
||||
}
|
||||
if( record.getNotifyCount() >= record.getNotifyCountLimit() ){
|
||||
log.info("已达到最大发送次数");
|
||||
return;
|
||||
}
|
||||
|
||||
//1. (发送结果最多6次)
|
||||
Integer currentCount = record.getNotifyCount() + 1;
|
||||
|
||||
String notifyUrl = record.getNotifyUrl();
|
||||
String res = "";
|
||||
try {
|
||||
int timeout = 20000;
|
||||
// body形式
|
||||
res = HttpUtil.createPost(record.getNotifyUrl()).body(record.getNotifyBody(), CharsetUtil.CHARSET_UTF_8.name())
|
||||
.contentType(MediaType.APPLICATION_JSON_VALUE).timeout(timeout).execute().body();
|
||||
} catch (Exception e) {
|
||||
log.error("http error", e);
|
||||
res = "连接["+ UrlBuilder.of(notifyUrl).getHost() +"]异常:【" + e.getMessage() + "】";
|
||||
}
|
||||
|
||||
//支付订单 & 第一次通知: 更新为已通知
|
||||
if(currentCount == 1 && MchNotifyRecord.TYPE_PAY_ORDER == record.getOrderType()){
|
||||
payOrderService.updateNotifySent(record.getOrderId());
|
||||
}
|
||||
|
||||
//通知成功
|
||||
if("SUCCESS".equalsIgnoreCase(res)){
|
||||
mchNotifyRecordService.updateNotifyResult(notifyId, MchNotifyRecord.STATE_SUCCESS, res);
|
||||
return;
|
||||
}
|
||||
|
||||
//通知次数 >= 最大通知次数时, 更新响应结果为异常, 不在继续延迟发送消息
|
||||
if( currentCount >= record.getNotifyCountLimit() ){
|
||||
mchNotifyRecordService.updateNotifyResult(notifyId, MchNotifyRecord.STATE_FAIL, res);
|
||||
return;
|
||||
}
|
||||
|
||||
// 继续发送MQ 延迟发送
|
||||
mchNotifyRecordService.updateNotifyResult(notifyId, MchNotifyRecord.STATE_ING, res);
|
||||
// 通知延时次数
|
||||
// 1 2 3 4 5 6
|
||||
// 0 30 60 90 120 150
|
||||
mqSender.send(PayOrderMchNotifyMQ.build(notifyId), currentCount * 30);
|
||||
|
||||
return;
|
||||
}catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.jeequan.jeepay.pay.mq;
|
||||
|
||||
import com.jeequan.jeepay.components.mq.model.PayOrderReissueMQ;
|
||||
import com.jeequan.jeepay.components.mq.vender.IMQSender;
|
||||
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
|
||||
import com.jeequan.jeepay.db.entity.PayOrder;
|
||||
import com.jeequan.jeepay.pay.service.ChannelOrderReissueService;
|
||||
import com.jeequan.jeepay.service.impl.PayOrderService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 接收MQ消息
|
||||
* 业务: 支付订单补单(一般用于没有回调的接口,比如微信的条码支付)
|
||||
* @author terrfly
|
||||
* @date 2021/7/27 9:23
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class PayOrderReissueMQReceiver implements PayOrderReissueMQ.IMQReceiver {
|
||||
|
||||
@Autowired
|
||||
private IMQSender mqSender;
|
||||
@Autowired
|
||||
private PayOrderService payOrderService;
|
||||
@Autowired
|
||||
private ChannelOrderReissueService channelOrderReissueService;
|
||||
|
||||
|
||||
@Override
|
||||
public void receive(PayOrderReissueMQ.MsgPayload payload) {
|
||||
try {
|
||||
String payOrderId = payload.getPayOrderId();
|
||||
int currentCount = payload.getCount();
|
||||
log.info("接收轮询查单通知MQ, payOrderId={}, count={}", payOrderId, currentCount);
|
||||
currentCount++ ;
|
||||
|
||||
PayOrder payOrder = payOrderService.getById(payOrderId);
|
||||
if(payOrder == null) {
|
||||
log.warn("查询支付订单为空,payOrderId={}", payOrderId);
|
||||
return;
|
||||
}
|
||||
|
||||
if(payOrder.getState() != PayOrder.STATE_ING) {
|
||||
log.warn("订单状态不是支付中,不需查询渠道.payOrderId={}", payOrderId);
|
||||
return;
|
||||
}
|
||||
|
||||
// 是否调用渠道独立的轮询查单接口
|
||||
payOrder.addExt("isReissueByPoll", true);
|
||||
// 轮询次数
|
||||
payOrder.addExt("pollCount", currentCount);
|
||||
|
||||
ChannelRetMsg channelRetMsg = channelOrderReissueService.processPayOrder(payOrder);
|
||||
|
||||
//返回null 可能为接口报错等, 需要再次轮询
|
||||
if(channelRetMsg == null || channelRetMsg.getChannelState() == null || channelRetMsg.getChannelState().equals(ChannelRetMsg.ChannelState.WAITING)){
|
||||
|
||||
//最多查询6次
|
||||
if(currentCount <= 6){
|
||||
mqSender.send(PayOrderReissueMQ.build(payOrderId, currentCount), 5); //延迟5s再次查询
|
||||
}else{
|
||||
|
||||
//TODO 调用【撤销订单】接口
|
||||
|
||||
}
|
||||
|
||||
}else{ //其他状态, 不需要再次轮询。
|
||||
}
|
||||
}catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.jeequan.jeepay.pay.mq;
|
||||
|
||||
import com.jeequan.jeepay.components.mq.model.PushPrinterMQ;
|
||||
import com.jeequan.jeepay.pay.service.payorderpush.impl.PushPrinterService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 接收MQ消息
|
||||
* 业务: 新订单提醒 通知
|
||||
* @author zx
|
||||
* @date 2021/01/04 15:25
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class PushPrinterMQReceiver implements PushPrinterMQ.IMQReceiver {
|
||||
|
||||
@Autowired private PushPrinterService pushPrinterService;
|
||||
|
||||
@Override
|
||||
public void receive(PushPrinterMQ.MsgPayload payload) {
|
||||
|
||||
log.info("接收支付成功云打印MQ, msg={}", payload.toString());
|
||||
|
||||
String orderId = payload.getOrderId();
|
||||
Byte orderType = payload.getOrderType();
|
||||
|
||||
// 云打印
|
||||
pushPrinterService.send(orderId, orderType);
|
||||
|
||||
log.info("云打印业务结束。");
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.jeequan.jeepay.pay.mq;
|
||||
|
||||
import com.jeequan.jeepay.components.mq.model.PushSpeakerMQ;
|
||||
import com.jeequan.jeepay.pay.service.payorderpush.impl.PushSpeakerService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 接收MQ消息
|
||||
* 业务: 新订单提醒 通知
|
||||
* @author zx
|
||||
* @date 2021/01/04 15:25
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class PushSpeakerMQReceiver implements PushSpeakerMQ.IMQReceiver {
|
||||
|
||||
@Autowired private PushSpeakerService pushSpeakerService;
|
||||
|
||||
@Override
|
||||
public void receive(PushSpeakerMQ.MsgPayload payload) {
|
||||
|
||||
log.info("接收支付成功云喇叭播报MQ, msg={}", payload.toString());
|
||||
|
||||
String orderId = payload.getOrderId();
|
||||
Byte orderType = payload.getOrderType();
|
||||
|
||||
// 云喇叭播报
|
||||
pushSpeakerService.send(orderId, orderType);
|
||||
|
||||
log.info("云喇叭播报业务结束。");
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.jeequan.jeepay.pay.mq;
|
||||
|
||||
import com.jeequan.jeepay.components.mq.model.PushWxMpMsgMQ;
|
||||
import com.jeequan.jeepay.pay.service.payorderpush.impl.PushWxMpTemplateMsgService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 接收MQ消息
|
||||
* 业务: 新订单提醒 通知
|
||||
* @author zx
|
||||
* @date 2021/01/04 15:25
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class PushWxMpMsgMQReceiver implements PushWxMpMsgMQ.IMQReceiver {
|
||||
|
||||
@Autowired private PushWxMpTemplateMsgService pushWxMpMessageService;
|
||||
|
||||
@Override
|
||||
public void receive(PushWxMpMsgMQ.MsgPayload payload) {
|
||||
|
||||
log.info("接收支付成功微信公众号提醒通知MQ, msg={}", payload.toString());
|
||||
|
||||
try {
|
||||
// 微信公众号消息提醒
|
||||
pushWxMpMessageService.send(payload.getPayOrderId(),payload.getNoticeType());
|
||||
|
||||
log.info("微信公众号提醒业务结束。");
|
||||
|
||||
}catch (Exception e) {
|
||||
log.error("微信公众号提醒异常" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user