This commit is contained in:
韩鹏辉
2024-05-23 15:23:08 +08:00
parent 49fe8fe877
commit d969550aa3
2477 changed files with 340990 additions and 0 deletions

View File

@@ -0,0 +1,257 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <!-- POM模型版本 -->
<groupId>com.jeequan</groupId> <!-- 组织名, 类似于包名 -->
<artifactId>jeepay-components-3rd</artifactId> <!-- 项目名称 -->
<packaging>jar</packaging> <!-- 项目的最终打包类型/发布形式, 可选[jar, war, pom, maven-plugin]等 -->
<version>${isys.version}</version> <!-- 项目当前版本号 -->
<description>Jeepay计全支付系统 [三方调用接口]</description> <!-- 项目描述 -->
<url>https://www.jeequan.com</url>
<parent>
<groupId>com.jeequan</groupId>
<artifactId>jeepay-components</artifactId>
<version>Final</version>
</parent>
<!-- 项目属性 -->
<properties>
<projectRootDir>${basedir}/../../</projectRootDir>
</properties>
<!-- 项目依赖声明 -->
<dependencies>
<!-- 依赖[ service ]包 (仅查询参数配置即可, 业务的处理不应该在该层进行 ) -->
<dependency>
<groupId>com.jeequan</groupId>
<artifactId>jeepay-components-db</artifactId>
</dependency>
<dependency>
<groupId>com.bill99</groupId>
<artifactId>crypto-sdk</artifactId>
</dependency>
<!-- 依赖[ oss ]包 -->
<dependency>
<groupId>com.jeequan</groupId>
<artifactId>jeepay-components-oss</artifactId>
</dependency>
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.14</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!-- 依赖 sping-boot-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>provided</scope>
</dependency>
<!-- 可选依赖 [spring-redis] -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<scope>provided</scope>
</dependency>
<!-- hibernate.validator插件 -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
<!--wx_pay https://github.com/wechat-group/WxJava -->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-miniapp</artifactId>
</dependency>
<!-- adapay -->
<!-- https://mvnrepository.com/artifact/com.huifu.adapay.core/adapay-core-sdk -->
<dependency>
<groupId>com.huifu.adapay.core</groupId>
<artifactId>adapay-core-sdk</artifactId>
<version>1.2.10</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.huifu.adapay/adapay-java-sdk -->
<dependency>
<groupId>com.huifu.adapay</groupId>
<artifactId>adapay-java-sdk</artifactId>
<version>1.2.10</version>
</dependency>
<!-- paypal 支付 -->
<dependency>
<groupId>com.paypal.sdk</groupId>
<artifactId>checkout-sdk</artifactId>
<version>1.0.5</version>
</dependency>
<!-- 生成二维码工具包 zxing -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alipay.sdk/alipay-sdk-java -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
</dependency>
<!-- mqtt 智谷物联使用mqtt连接 -->
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.2.2</version>
</dependency>
<!-- 阿里云OCR -->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>ocr_api20210707</artifactId>
<version>1.1.1</version>
</dependency>
<!-- 腾讯云OCR -->
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java</artifactId>
<version>3.1.322</version>
</dependency>
<!-- 个推 消息推送 -->
<dependency>
<groupId>com.getui.push</groupId>
<artifactId>restful-sdk</artifactId>
<version>1.0.0.4</version>
</dependency>
<!-- 杉德的河马付依赖包 -->
<dependency>
<groupId>cn.com.sand</groupId>
<artifactId>sandpay-hmpay-sdk</artifactId>
</dependency>
<!-- 富友FTP SFTP依赖包 -->
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.6</version>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.54</version>
</dependency>
<!-- 银盛支付sdk -->
<dependency>
<groupId>cfca.sadk</groupId>
<artifactId>cfca-sadk</artifactId>
</dependency>
<!-- 银盛支付sdk -->
<dependency>
<groupId>cfca.logback</groupId>
<artifactId>cfca-logback</artifactId>
</dependency>
<!-- 银盛支付sdk -->
<dependency>
<groupId>hikefa.core</groupId>
<artifactId>yspay-hikefa</artifactId>
</dependency>
<!-- 国密算法 BC库依赖 -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.61</version>
</dependency>
<dependency>
<groupId>com.payermax</groupId>
<artifactId>payermax-server-sdk</artifactId>
<version>1.0.3</version>
</dependency>
<!-- 建行龙支付SDK -->
<dependency>
<groupId>ccbpay.netpay</groupId>
<artifactId>ccbpay-netpay</artifactId>
</dependency>
<!-- 支付宝云支付 -->
<dependency>
<groupId>com.alipay.bpaas</groupId>
<artifactId>bpaas-bizapi-sdk</artifactId>
</dependency>
<!-- 交行支付SDK -->
<dependency>
<groupId>com.bocom.api</groupId>
<artifactId>bocom-openapi-sdk</artifactId>
</dependency>
<!-- 工行支付SDK -->
<dependency>
<groupId>com.icbc</groupId>
<artifactId>icbc-api-sdk-cop</artifactId>
</dependency>
<dependency>
<groupId>com.icbc.api</groupId>
<artifactId>icbc-api-sdk-cop-io</artifactId>
</dependency>
<dependency>
<groupId>cn.com.infosec</groupId>
<artifactId>icbc-ca</artifactId>
</dependency>
<dependency>
<groupId>com.icbc</groupId>
<artifactId>hsm-software-share</artifactId>
</dependency>
<dependency>
<groupId>com.clj</groupId>
<artifactId>leshuapay-sdk</artifactId>
</dependency>
<dependency>
<groupId>com.jeequan</groupId>
<artifactId>jeepay-components-core</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
</build>
</project>

View File

@@ -0,0 +1,79 @@
package com.jeequan.jeepay.thirdparty.channel;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.MchApplyment;
import com.jeequan.jeepay.core.interfaces.paychannel.IIsvmchWrapper;
import com.jeequan.jeepay.core.model.applyment.ApplymentBasicInfo;
import com.jeequan.jeepay.core.model.applyment.PaywayFee;
import com.jeequan.jeepay.db.entity.MchAppEntity;
import com.jeequan.jeepay.db.entity.RateConfig;
import com.jeequan.jeepay.service.impl.MchAppService;
import com.jeequan.jeepay.service.impl.RateConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
public abstract class AbsIsvmchWrapper<T extends ApplymentBasicInfo> implements IIsvmchWrapper {
@Autowired
private RateConfigService rateConfigService;
@Autowired
private MchAppService mchAppService;
/**
* 根据商户的上下级设置其缺省的费率值
*
* @param mchApplyment 商户信息
*/
@Override
public final void rateWrap(MchApplyment mchApplyment) {
JSONObject applyDetailInfo = JSON.parseObject(mchApplyment.getApplyDetailInfo());
Map<String, PaywayFee> mchApplyRateConfigMap = new HashMap<>();
Optional.ofNullable(applyDetailInfo.getJSONArray("paywayFeeList"))
.map(t -> t.toJavaList(PaywayFee.class))
.map(t -> {
t.forEach(t2 -> mchApplyRateConfigMap.put(t2.getWayCode(), t2));
return t;
});
MchAppEntity mchApp = mchAppService.getById(mchApplyment.getAutoConfigMchAppId());
String agentNo = mchApplyment.getAgentNo();
String isvNo = mchApplyment.getIsvNo();
// 获取商户费率, 一般来说这一步是没必要的,这里单纯做数据初始化吧
Map<String, PaywayFee> mchRateConfigMap = rateConfigService.queryPaywayFeeMap(agentNo + "_" + RateConfig.MCHRATE, CS.SYS_ROLE_TYPE.MCH_APP, mchApplyment.getIfCode()
, mchApp.getRange(), mchApp.getMccCode(), mchApplyment.getIsvNo());
setIfEmpty(mchApplyRateConfigMap, mchRateConfigMap);
// 获取直属上级费率
Map<String, PaywayFee> agentMchDefRateConfigMap = rateConfigService.queryPaywayFeeMap(agentNo + "_" + RateConfig.MCHAPPLYDEF, CS.SYS_ROLE_TYPE.AGENT, mchApplyment.getIfCode()
, mchApp.getRange(), mchApp.getMccCode(), mchApplyment.getIsvNo());
setIfEmpty(mchRateConfigMap, agentMchDefRateConfigMap);
// 获取服务商费率信息
Map<String, PaywayFee> isvRateConfigMap = rateConfigService.queryPaywayFeeMap(isvNo + "_" + RateConfig.MCHAPPLYDEF, CS.SYS_ROLE_TYPE.ISV, mchApplyment.getIfCode()
, mchApp.getRange(), mchApp.getMccCode(), mchApplyment.getIsvNo());
setIfEmpty(mchRateConfigMap, isvRateConfigMap);
mchRateConfigMap = channelInit(mchRateConfigMap, mchApplyment);
applyDetailInfo.put("paywayFeeList", mchRateConfigMap.values());
mchApplyment.setApplyDetailInfo(JSON.toJSONString(applyDetailInfo));
}
private void setIfEmpty(Map<String, PaywayFee> mchRate, Map<String, PaywayFee> targetConfigMap) {
targetConfigMap.forEach((wayCode, rateConfig) -> mchRate.putIfAbsent(wayCode, targetConfigMap.get(wayCode)));
}
public Map<String, PaywayFee> channelInit(Map<String, PaywayFee> mchRateConfigMap, MchApplyment mchApplyment) {
return mchRateConfigMap;
}
}

View File

@@ -0,0 +1,76 @@
package com.jeequan.jeepay.thirdparty.channel;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
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.core.constants.CS;
import com.jeequan.jeepay.core.entity.CheckBatch;
import com.jeequan.jeepay.core.interfaces.paychannel.IBillDownloadService;
import com.jeequan.jeepay.service.impl.SysConfigService;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import com.jeequan.jeepay.thirdparty.util.ChannelCertConfigKitBean;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.Date;
/*
* 渠道对账单下载 抽象类
*
* @author zx
*
* @date 2021/6/8 17:18
*/
public abstract class AbstractBillDownloadService implements IBillDownloadService {
@Autowired protected SysConfigService sysConfigService;
@Autowired protected ChannelCertConfigKitBean channelCertConfigKitBean;
@Autowired protected ConfigContextQueryService configContextQueryService;
@Autowired private OssFileController ossFileController;
// 上传文件至oss
public String upload2Oss(String ifCode, String channelMchNo, Date billDate, String suffix, InputStream inputStream){
return ossFileController.singleFileUpload(new MockMultipartFile(genOssFileName(ifCode, channelMchNo, billDate, suffix), 1L, inputStream), OssFileConfig.BIZ_TYPE.CHANNEL_BILL);
}
// 生成上传oss 账单文件名称、后缀
public String genOssFileName(String ifCode, String channelMchNo, Date billDate, String suffix){
return String.format("%s_%s_%s_%s", ifCode, channelMchNo, DateUtil.format(billDate, DatePattern.PURE_DATE_PATTERN), suffix);
}
/** 生成渠道账单 解析成功 的对账批次 */
public CheckBatch genReleaseSuccessCheckBatch(String ifCode, Date billDate, String channelMchNo, String path) {
return initCheckBatch(ifCode, billDate, channelMchNo, path, CS.YES, null);
}
/** 生成渠道账单 解析失败 的对账批次 */
public CheckBatch genReleaseFailCheckBatch(String ifCode, Date billDate, String channelMchNo, String releaseErrMsg) {
return initCheckBatch(ifCode, billDate, channelMchNo, null, CS.NO, releaseErrMsg);
}
/** 初始化对账批次 */
public CheckBatch initCheckBatch(String ifCode, Date billDate, String channelMchNo, String path, Byte releaseState, String releaseErrMsg) {
CheckBatch checkBatch = new CheckBatch();
checkBatch.setBatchNo(getBatchNo(ifCode, channelMchNo, billDate));
checkBatch.setIfCode(ifCode);
checkBatch.setBillDate(billDate);
checkBatch.setChannelMchNo(channelMchNo);
checkBatch.setState(CS.NO); //未处理状态
checkBatch.setOrgBillFilePath(path);
checkBatch.setReleaseState(releaseState);
checkBatch.setReleaseErrMsg(releaseErrMsg);
checkBatch.setChannelTotalAmount(BigDecimal.ZERO.longValue());
return checkBatch;
}
// 对账批次号 支付接口代码ifCode_渠道商户号_对账日期
public String getBatchNo(String ifCode, String channelMchNo, Date billDate) {
return ifCode + "_" + channelMchNo + "_" + DateUtil.format(billDate, DatePattern.PURE_DATE_PATTERN);
}
}

View File

@@ -0,0 +1,36 @@
package com.jeequan.jeepay.thirdparty.channel;
import com.jeequan.jeepay.core.interfaces.paychannel.IChannelAccountService;
import com.jeequan.jeepay.service.impl.SysConfigService;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import com.jeequan.jeepay.thirdparty.util.ChannelCertConfigKitBean;
import org.springframework.beans.factory.annotation.Autowired;
/*
* 提现接口抽象类
*
* @author zx
*
* @date 2021/6/8 17:18
*/
public abstract class AbstractChannelAccountService implements IChannelAccountService {
@Autowired protected SysConfigService sysConfigService;
@Autowired protected ChannelCertConfigKitBean channelCertConfigKitBean;
@Autowired protected ConfigContextQueryService configContextQueryService;
protected String getPaySiteUrl() {
return sysConfigService.getDBApplicationConfig().getPaySiteUrl();
}
protected String getNotifyUrl(){
return getPaySiteUrl() + "/api/channel/cashout/notify/" + getIfCode();
}
protected String getNotifyUrl(String rid){
return getPaySiteUrl() + "/api/channel/cashout/notify/" + getIfCode() + "/" + rid;
}
}

View File

@@ -0,0 +1,66 @@
package com.jeequan.jeepay.thirdparty.channel;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.beans.RequestKitBean;
import com.jeequan.jeepay.core.interfaces.cashout.IChannelCashoutNoticeService;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import com.jeequan.jeepay.thirdparty.util.ChannelCertConfigKitBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import java.io.File;
/*
* 实现提现回调接口抽象类
*
* @author zx
*
* @date 2021/6/8 17:18
*/
public abstract class AbstractChannelCashoutNoticeService implements IChannelCashoutNoticeService {
@Autowired private RequestKitBean requestKitBean;
@Autowired private ChannelCertConfigKitBean channelCertConfigKitBean;
@Autowired protected ConfigContextQueryService configContextQueryService;
/** 文本类型的响应数据 **/
protected ResponseEntity textResp(String text){
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_HTML);
return new ResponseEntity(text, httpHeaders, HttpStatus.OK);
}
/** json类型的响应数据 **/
protected ResponseEntity jsonResp(Object body){
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
return new ResponseEntity(body, httpHeaders, HttpStatus.OK);
}
/**request.getParameter 获取参数 并转换为JSON格式 **/
protected JSONObject getReqParamJSON() {
return requestKitBean.getReqParamJSON();
}
/**request.getParameter 获取参数 并转换为JSON格式 **/
protected String getReqParamFromBody() {
return requestKitBean.getReqParamFromBody();
}
/** 获取文件路径 **/
protected String getCertFilePath(String certFilePath) {
return channelCertConfigKitBean.getCertFilePath(certFilePath);
}
/** 获取文件File对象 **/
protected File getCertFile(String certFilePath) {
return channelCertConfigKitBean.getCertFile(certFilePath);
}
}

View File

@@ -0,0 +1,77 @@
package com.jeequan.jeepay.thirdparty.channel;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.beans.RequestKitBean;
import com.jeequan.jeepay.core.interfaces.paychannel.IChannelNoticeService;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import com.jeequan.jeepay.thirdparty.util.ChannelCertConfigKitBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
/*
* 实现回调接口抽象类
*
* @author terrfly
*
* @date 2021/6/8 17:18
*/
public abstract class AbstractChannelNoticeService implements IChannelNoticeService {
@Autowired private RequestKitBean requestKitBean;
@Autowired private ChannelCertConfigKitBean channelCertConfigKitBean;
@Autowired protected ConfigContextQueryService configContextQueryService;
@Override
public ResponseEntity doNotifyOrderNotExists(HttpServletRequest request) {
return textResp("order not exists");
}
@Override
public ResponseEntity doNotifyOrderStateUpdateFail(HttpServletRequest request) {
return textResp("update status error");
}
/** 文本类型的响应数据 **/
protected ResponseEntity textResp(String text){
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_HTML);
return new ResponseEntity(text, httpHeaders, HttpStatus.OK);
}
/** json类型的响应数据 **/
protected ResponseEntity jsonResp(Object body){
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
return new ResponseEntity(body, httpHeaders, HttpStatus.OK);
}
/**request.getParameter 获取参数 并转换为JSON格式 **/
protected JSONObject getReqParamJSON() {
return requestKitBean.getReqParamJSON();
}
/**request.getParameter 获取参数 并转换为JSON格式 **/
protected String getReqParamFromBody() {
return requestKitBean.getReqParamFromBody();
}
/** 获取文件路径 **/
protected String getCertFilePath(String certFilePath) {
return channelCertConfigKitBean.getCertFilePath(certFilePath);
}
/** 获取文件File对象 **/
protected File getCertFile(String certFilePath) {
return channelCertConfigKitBean.getCertFile(certFilePath);
}
}

View File

@@ -0,0 +1,67 @@
package com.jeequan.jeepay.thirdparty.channel;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.beans.RequestKitBean;
import com.jeequan.jeepay.core.interfaces.paychannel.IChannelOrderAcceptService;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import com.jeequan.jeepay.thirdparty.util.ChannelCertConfigKitBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import java.io.File;
/*
* 实现回调接口抽象类
*
* @author terrfly
*
* @date 2021/6/8 17:18
*/
public abstract class AbstractChannelOrderAcceptService implements IChannelOrderAcceptService {
@Autowired private RequestKitBean requestKitBean;
@Autowired private ChannelCertConfigKitBean channelCertConfigKitBean;
@Autowired protected ConfigContextQueryService configContextQueryService;
/** 文本类型的响应数据 **/
protected ResponseEntity textResp(String text){
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_HTML);
return new ResponseEntity(text, httpHeaders, HttpStatus.OK);
}
/** json类型的响应数据 **/
protected ResponseEntity jsonResp(Object body){
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
return new ResponseEntity(body, httpHeaders, HttpStatus.OK);
}
/**request.getParameter 获取参数 并转换为JSON格式 **/
protected JSONObject getReqParamJSON() {
return requestKitBean.getReqParamJSON();
}
/**request.getParameter 获取参数 并转换为JSON格式 **/
protected String getReqParamFromBody() {
return requestKitBean.getReqParamFromBody();
}
/** 获取文件路径 **/
protected String getCertFilePath(String certFilePath) {
return channelCertConfigKitBean.getCertFilePath(certFilePath);
}
/** 获取文件File对象 **/
protected File getCertFile(String certFilePath) {
return channelCertConfigKitBean.getCertFile(certFilePath);
}
}

View File

@@ -0,0 +1,77 @@
package com.jeequan.jeepay.thirdparty.channel;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.beans.RequestKitBean;
import com.jeequan.jeepay.core.interfaces.paychannel.IChannelRefundNoticeService;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import com.jeequan.jeepay.thirdparty.util.ChannelCertConfigKitBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
/*
* 实现退款回调接口抽象类
*
* @author jmdhappy
*
* @date 2021/9/25 23:18
*/
public abstract class AbstractChannelRefundNoticeService implements IChannelRefundNoticeService {
@Autowired private RequestKitBean requestKitBean;
@Autowired private ChannelCertConfigKitBean channelCertConfigKitBean;
@Autowired protected ConfigContextQueryService configContextQueryService;
@Override
public ResponseEntity doNotifyOrderNotExists(HttpServletRequest request) {
return textResp("order not exists");
}
@Override
public ResponseEntity doNotifyOrderStateUpdateFail(HttpServletRequest request) {
return textResp("update status error");
}
/** 文本类型的响应数据 **/
protected ResponseEntity textResp(String text){
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_HTML);
return new ResponseEntity(text, httpHeaders, HttpStatus.OK);
}
/** json类型的响应数据 **/
protected ResponseEntity jsonResp(Object body){
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
return new ResponseEntity(body, httpHeaders, HttpStatus.OK);
}
/**request.getParameter 获取参数 并转换为JSON格式 **/
protected JSONObject getReqParamJSON() {
return requestKitBean.getReqParamJSON();
}
/**request.getParameter 获取参数 并转换为JSON格式 **/
protected String getReqParamFromBody() {
return requestKitBean.getReqParamFromBody();
}
/** 获取文件路径 **/
protected String getCertFilePath(String certFilePath) {
return channelCertConfigKitBean.getCertFilePath(certFilePath);
}
/** 获取文件File对象 **/
protected File getCertFile(String certFilePath) {
return channelCertConfigKitBean.getCertFile(certFilePath);
}
}

View File

@@ -0,0 +1,95 @@
package com.jeequan.jeepay.thirdparty.channel;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.beans.RequestKitBean;
import com.jeequan.jeepay.core.entity.PayOrderDivisionRecord;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.rqrs.msg.DivisionChannelNotifyModel;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import com.jeequan.jeepay.thirdparty.util.ChannelCertConfigKitBean;
import org.apache.commons.lang3.tuple.MutablePair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.util.List;
/*
* 分账结果回调接口抽象类
*
* @author terrfly
*
* @date 2023/3/29 15:39
*/
public abstract class AbstractDivisionRecordChannelNotifyService {
@Autowired private RequestKitBean requestKitBean;
@Autowired private ChannelCertConfigKitBean channelCertConfigKitBean;
@Autowired protected ConfigContextQueryService configContextQueryService;
/** 获取到接口code **/
public abstract String getIfCode();
/** 解析参数: 批次号 和 请求参数
* 异常需要自行捕捉并返回null , 表示已响应数据。
* **/
public abstract MutablePair<String, Object> parseParams(HttpServletRequest request);
/**
* 返回需要更新的记录 <ID, 结果> 状态 和响应数据
*
* **/
public abstract DivisionChannelNotifyModel doNotify(HttpServletRequest request, Object params,
List<? extends PayOrderDivisionRecord> recordList, MchAppConfigContext mchAppConfigContext);
public ResponseEntity doNotifyOrderNotExists(HttpServletRequest request) {
return textResp("order not exists");
}
public ResponseEntity doNotifyOrderStateUpdateFail(HttpServletRequest request) {
return textResp("update status error");
}
/** 文本类型的响应数据 **/
protected ResponseEntity textResp(String text){
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_HTML);
return new ResponseEntity(text, httpHeaders, HttpStatus.OK);
}
/** json类型的响应数据 **/
protected ResponseEntity jsonResp(Object body){
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
return new ResponseEntity(body, httpHeaders, HttpStatus.OK);
}
/**request.getParameter 获取参数 并转换为JSON格式 **/
protected JSONObject getReqParamJSON() {
return requestKitBean.getReqParamJSON();
}
/**request.getParameter 获取参数 并转换为JSON格式 **/
protected String getReqParamFromBody() {
return requestKitBean.getReqParamFromBody();
}
/** 获取文件路径 **/
protected String getCertFilePath(String certFilePath) {
return channelCertConfigKitBean.getCertFilePath(certFilePath);
}
/** 获取文件File对象 **/
protected File getCertFile(String certFilePath) {
return channelCertConfigKitBean.getCertFile(certFilePath);
}
}

View File

@@ -0,0 +1,79 @@
package com.jeequan.jeepay.thirdparty.channel;
import com.jeequan.jeepay.core.entity.*;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.interfaces.paychannel.IDivisionService;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.service.impl.SysConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.HashMap;
import java.util.List;
/**
* abstract 分账实现
*
* @author terrfly
*
* @date 2022/6/13 11:09
*/
public abstract class AbstractDivisionService implements IDivisionService {
@Autowired
protected SysConfigService sysConfigService;
protected String getPaySiteUrl() {
return sysConfigService.getDBApplicationConfig().getPaySiteUrl();
}
@Override
public String getIfCode(MchDivisionReceiver mchDivisionReceiver) {
return mchDivisionReceiver.getIfCode();
}
@Override
public String getIfCode(PayOrder payOrder) {
return payOrder.getIfCode();
}
@Override
public String getIfCode(PayOrderDivisionRecord payOrderDivisionRecord) {
return payOrderDivisionRecord.getIfCode();
}
@Override
public boolean divisionRefundIsOrderRefundAfterProc() {
return false; // 默认分账业务前发起退分处理。
}
@Override
public HashMap<Long, ChannelRetMsg> queryDivision(PayOrder payOrder, List<? extends PayOrderDivisionRecord> recordList, MchAppConfigContext mchAppConfigContext){
throw new BizException("接口不支持");
}
@Override
public ChannelRetMsg divisionRefund(PayOrderDivisionRecord payOrderDivisionRecord, PayOrderDivisionRefundRecord payOrderDivisionRefundRecord, RefundOrder refundOrder, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) {
throw new BizException("接口不支持");
}
@Override
public Long queryBalanceAmount(MchDivisionReceiver mchDivisionReceiver, MchAppConfigContext mchAppConfigContext) {
throw new BizException("接口不支持");
}
@Override
public ChannelRetMsg cashout(MchDivisionReceiver mchDivisionReceiver, Long amount, MchAppConfigContext mchAppConfigContext) {
throw new BizException("接口不支持");
}
@Override
public String divisionFinishAfterDivision(PayOrderDivisionRecord divisionRecord, MchAppConfigContext mchAppConfigContext) {
throw new BizException("接口不支持");
}
protected String getNotifyUrl(){
return getPaySiteUrl() + "/api/divisionRecordChannelNotify/" + getIfCode();
}
}

View File

@@ -0,0 +1,28 @@
package com.jeequan.jeepay.thirdparty.channel;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.interfaces.paychannel.IMchApiService;
import com.jeequan.jeepay.core.model.params.IsvParams;
import com.jeequan.jeepay.core.model.rqrs.settle.ChannelSettleRq;
/**
* TODO
*
* @author crystal
* @date 2023/12/4 10:46
*/
public abstract class AbstractMchApiService<T extends IsvParams> implements IMchApiService<T> {
/**
* 通用结算参数校验
* @param settleRq
*/
protected void preSettleCheck(ChannelSettleRq settleRq){
if(settleRq.getPage() == null){
throw new BizException("当前页不能为空");
}
if(settleRq.getSize() == null){
throw new BizException("每页显示的条数不能为空");
}
}
}

View File

@@ -0,0 +1,66 @@
package com.jeequan.jeepay.thirdparty.channel;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.beans.RequestKitBean;
import com.jeequan.jeepay.core.interfaces.paychannel.IIsvmchStoreApplymentNotifyService;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import com.jeequan.jeepay.thirdparty.util.ChannelCertConfigKitBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import java.io.File;
/**
* 实现回调接口抽象类
*
* @author xiaoyu
*
* @date 2022/3/26 18:21
*/
public abstract class AbstractMchApplymentNoticeService implements IIsvmchStoreApplymentNotifyService {
@Autowired private RequestKitBean requestKitBean;
@Autowired private ChannelCertConfigKitBean channelCertConfigKitBean;
@Autowired protected ConfigContextQueryService configContextQueryService;
/** 文本类型的响应数据 **/
protected ResponseEntity textResp(String text){
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_HTML);
return new ResponseEntity(text, httpHeaders, HttpStatus.OK);
}
/** json类型的响应数据 **/
protected ResponseEntity jsonResp(Object body){
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
return new ResponseEntity(body, httpHeaders, HttpStatus.OK);
}
/**request.getParameter 获取参数 并转换为JSON格式 **/
protected JSONObject getReqParamJSON() {
return requestKitBean.getReqParamJSON();
}
/**request.getParameter 获取参数 并转换为JSON格式 **/
protected String getReqParamFromBody() {
return requestKitBean.getReqParamFromBody();
}
/** 获取文件路径 **/
protected String getCertFilePath(String certFilePath) {
return channelCertConfigKitBean.getCertFilePath(certFilePath);
}
/** 获取文件File对象 **/
protected File getCertFile(String certFilePath) {
return channelCertConfigKitBean.getCertFile(certFilePath);
}
}

View File

@@ -0,0 +1,120 @@
package com.jeequan.jeepay.thirdparty.channel;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.interfaces.paychannel.IPaymentService;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
import com.jeequan.jeepay.core.model.terminal.TerminalChannelModel;
import com.jeequan.jeepay.db.entity.MchStoreTerminal;
import com.jeequan.jeepay.service.impl.MchStoreTerminalService;
import com.jeequan.jeepay.service.impl.SysConfigService;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import com.jeequan.jeepay.thirdparty.util.ChannelCertConfigKitBean;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
/*
* 支付接口抽象类
*
* @author terrfly
*
* @date 2021/6/8 17:18
*/
public abstract class AbstractPaymentService implements IPaymentService {
@Autowired protected SysConfigService sysConfigService;
@Autowired protected ChannelCertConfigKitBean channelCertConfigKitBean;
@Autowired protected ConfigContextQueryService configContextQueryService;
@Autowired private MchStoreTerminalService mchStoreTerminalService;
@Override
public String getIfCode(PayOrder payOrder) {
return payOrder.getIfCode();
}
/** 订单分账(一般用作 如微信订单将在下单处做标记) */
protected boolean isDivisionOrder(PayOrder payOrder){
//订单分账, 将冻结商户资金。
if(payOrder.getDivisionMode() != null && (PayOrder.DIVISION_MODE_AUTO == payOrder.getDivisionMode() || PayOrder.DIVISION_MODE_MANUAL == payOrder.getDivisionMode() )){
return true;
}
return false;
}
protected String getPaySiteUrl() {
return sysConfigService.getDBApplicationConfig().getPaySiteUrl();
}
protected String getNotifyUrl(){
return getPaySiteUrl() + "/api/pay/notify/" + getIfCode();
}
protected String getNotifyUrl(String payOrderId){
return getPaySiteUrl() + "/api/pay/notify/" + getIfCode() + "/" + payOrderId;
}
protected String getNotifyUrlByFixMchApp(String mchAppId){
return getPaySiteUrl() + "/api/pay/notify/" + getIfCode() + "/" + CS.PAY_RETURNURL_FIX_MCHAPP_PREFIX + mchAppId;
}
protected String getPosNotifyUrl(){
return getPaySiteUrl() + "/api/pos/pay/notify/" + getIfCode();
}
protected String getReturnUrl(){
return getPaySiteUrl() + "/api/pay/return/" + getIfCode();
}
protected String getReturnUrl(String payOrderId){
return sysConfigService.getDBApplicationConfig().getPaySiteUrl() + "/api/pay/return/" + getIfCode() + "/" + payOrderId;
}
protected String getReturnUrlOnlyJump(String payOrderId){
return getPaySiteUrl() + "/api/pay/return/" + getIfCode() + "/" + CS.PAY_RETURNURL_FIX_ONLY_JUMP_PREFIX + payOrderId;
}
/**
* 查找出对应的设备信息
*
* @author terrfly
*
* @date 2022/4/29 15:20
*/
protected String queryStoreTerminalChannelTrmNo(String trmNo, PayOrder payOrder){
MchStoreTerminal mchStoreTerminal = null;
LambdaQueryWrapper<MchStoreTerminal> lambdaQueryWrapper = MchStoreTerminal.gw()
.eq(MchStoreTerminal::getMchNo, payOrder.getMchNo())
.eq(MchStoreTerminal::getAppId, payOrder.getAppId())
.eq(MchStoreTerminal::getStoreId, payOrder.getStoreId())
.eq(MchStoreTerminal::getState, CS.YES);
// 指定编号查询
if(StringUtils.isNotEmpty(trmNo)){
lambdaQueryWrapper.eq(MchStoreTerminal::getTrmNo, trmNo);
}else{ // 查询默认
lambdaQueryWrapper .eq(MchStoreTerminal::getDefaultFlag, CS.YES);
}
mchStoreTerminal = mchStoreTerminalService.getOne(lambdaQueryWrapper,false);
if(mchStoreTerminal == null || mchStoreTerminal.getChannelBindInfo() == null){
return null;
}
TerminalChannelModel model = mchStoreTerminal.getChannelBindInfo().getObject(payOrder.getIfCode(), TerminalChannelModel.class);
if(model != null && model.getState() != null && model.getState() == TerminalChannelModel.STATE_SUCCESS && StringUtils.isNotEmpty(model.getChannelTrmNo())){
return model.getChannelTrmNo();
}
return null;
}
}

View File

@@ -0,0 +1,55 @@
package com.jeequan.jeepay.thirdparty.channel;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.beans.RequestKitBean;
import com.jeequan.jeepay.core.interfaces.paychannel.IPosNoticeService;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import com.jeequan.jeepay.thirdparty.util.ChannelCertConfigKitBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
/*
* 实现回调接口抽象类
*
* @author terrfly
*
* @date 2021/6/8 17:18
*/
public abstract class AbstractPosNoticeService implements IPosNoticeService {
@Autowired private RequestKitBean requestKitBean;
@Autowired private ChannelCertConfigKitBean channelCertConfigKitBean;
@Autowired protected ConfigContextQueryService configContextQueryService;
/** 文本类型的响应数据 **/
protected ResponseEntity textResp(String text){
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_HTML);
return new ResponseEntity(text, httpHeaders, HttpStatus.OK);
}
/** json类型的响应数据 **/
protected ResponseEntity jsonResp(Object body){
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
return new ResponseEntity(body, httpHeaders, HttpStatus.OK);
}
/**request.getParameter 获取参数 并转换为JSON格式 **/
protected JSONObject getReqParamJSON() {
return requestKitBean.getReqParamJSON();
}
/**request.getParameter 获取参数 并转换为JSON格式 **/
protected String getReqParamFromBody() {
return requestKitBean.getReqParamFromBody();
}
}

View File

@@ -0,0 +1,115 @@
package com.jeequan.jeepay.thirdparty.channel;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.entity.RefundOrder;
import com.jeequan.jeepay.core.interfaces.paychannel.IRefundService;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRefundLimit;
import com.jeequan.jeepay.core.model.terminal.TerminalChannelModel;
import com.jeequan.jeepay.db.entity.AccountInfo;
import com.jeequan.jeepay.db.entity.MchStoreTerminal;
import com.jeequan.jeepay.service.impl.AccountInfoService;
import com.jeequan.jeepay.service.impl.MchStoreTerminalService;
import com.jeequan.jeepay.service.impl.SysConfigService;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import com.jeequan.jeepay.thirdparty.util.ChannelCertConfigKitBean;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
/*
* 退款接口抽象类
*
* @author terrfly
*
* @date 2021/6/17 9:37
*/
public abstract class AbstractRefundService implements IRefundService {
@Autowired protected SysConfigService sysConfigService;
@Autowired protected ChannelCertConfigKitBean channelCertConfigKitBean;
@Autowired protected ConfigContextQueryService configContextQueryService;
@Autowired private MchStoreTerminalService mchStoreTerminalService;
@Autowired protected AccountInfoService accountInfoService;
@Override
public String getIfCode(RefundOrder refundOrder) {
return refundOrder.getIfCode();
}
protected String getNotifyUrl(){
return sysConfigService.getDBApplicationConfig().getPaySiteUrl() + "/api/refund/notify/" + getIfCode();
}
protected String getNotifyUrl(String refundOrderId){
return sysConfigService.getDBApplicationConfig().getPaySiteUrl() + "/api/refund/notify/" + getIfCode() + "/" + refundOrderId;
}
protected String getPosNotifyUrl(){
return sysConfigService.getDBApplicationConfig().getPaySiteUrl() + "/api/pos/pay/notify/" + getIfCode();
}
/**
* 查找出对应的设备信息
*
* @author terrfly
*
* @date 2022/4/29 15:20
*/
protected String queryStoreTerminalChannelTrmNo(String trmNo, PayOrder payOrder){
MchStoreTerminal mchStoreTerminal = null;
LambdaQueryWrapper<MchStoreTerminal> lambdaQueryWrapper = MchStoreTerminal.gw()
.eq(MchStoreTerminal::getMchNo, payOrder.getMchNo())
.eq(MchStoreTerminal::getAppId, payOrder.getAppId())
.eq(MchStoreTerminal::getStoreId, payOrder.getStoreId())
.eq(MchStoreTerminal::getState, CS.YES);
// 指定编号查询
if(StringUtils.isNotEmpty(trmNo)){
lambdaQueryWrapper.eq(MchStoreTerminal::getTrmNo, trmNo);
}else{ // 查询默认
lambdaQueryWrapper .eq(MchStoreTerminal::getDefaultFlag, CS.YES);
}
mchStoreTerminal = mchStoreTerminalService.getOne(lambdaQueryWrapper,false);
if(mchStoreTerminal == null || mchStoreTerminal.getChannelBindInfo() == null){
return null;
}
TerminalChannelModel model = mchStoreTerminal.getChannelBindInfo().getObject(getIfCode(), TerminalChannelModel.class);
if(model != null && model.getState() != null && model.getState() == TerminalChannelModel.STATE_SUCCESS && StringUtils.isNotEmpty(model.getChannelTrmNo())){
return model.getChannelTrmNo();
}
return null;
}
/**
* 退款权限判断
* @param settleType
* @param applyId
* @return
*/
@Override
public ChannelRefundLimit isRefundLimit(String settleType,String applyId){
ChannelRefundLimit refundLimit = new ChannelRefundLimit(false,true);
return refundLimit;
}
/**
* 校验平台账户余额
* @param refundOrder
*/
@Override
public void checkPlatAccount(RefundOrder refundOrder){
if(RefundOrder.REFUND_ACCOUNT_PLAT.equals(refundOrder.getExtParam())){
accountInfoService.checkBalance(refundOrder.getMchNo(), AccountInfo.Type.REFUND,refundOrder.getRefundAmount());
}
}
}

View File

@@ -0,0 +1,46 @@
package com.jeequan.jeepay.thirdparty.channel;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.interfaces.paychannel.IRepayApiService;
import com.jeequan.jeepay.core.model.df.Account;
import com.jeequan.jeepay.service.impl.SysConfigService;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import com.jeequan.jeepay.thirdparty.util.ChannelCertConfigKitBean;
import org.springframework.beans.factory.annotation.Autowired;
/**
* TODO
*
* @author crystal
* @date 2024/3/25 10:43
*/
public abstract class AbstractRepayApiService implements IRepayApiService {
@Autowired
protected SysConfigService sysConfigService;
@Autowired protected ChannelCertConfigKitBean channelCertConfigKitBean;
@Autowired protected ConfigContextQueryService configContextQueryService;
protected String getPaySiteUrl() {
return sysConfigService.getDBApplicationConfig().getPaySiteUrl();
}
protected String getNotifyUrl(){
return getPaySiteUrl() + "/api/channel/payment/notify/" + getIfCode();
}
/**
* 校验账户余额
* @param mchNo
* @param accountType
* @param changeAmt
*/
@Override
public void checkAccountBalance(String mchNo,byte accountType, Long changeAmt) {
Account account = this.queryAccountBalance(mchNo, accountType);
if(account.getBalance() < changeAmt){
throw new BizException("账户不足,请先在后台充值后再操作");
}
}
}

View File

@@ -0,0 +1,64 @@
package com.jeequan.jeepay.thirdparty.channel;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.beans.RequestKitBean;
import com.jeequan.jeepay.core.interfaces.paychannel.IRepayNoticeService;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import com.jeequan.jeepay.thirdparty.util.ChannelCertConfigKitBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import java.io.File;
/*
* 实现回调接口抽象类
*
* @author terrfly
*
* @date 2021/6/8 17:18
*/
public abstract class AbstractRepayNoticeService implements IRepayNoticeService {
@Autowired private RequestKitBean requestKitBean;
@Autowired private ChannelCertConfigKitBean channelCertConfigKitBean;
@Autowired protected ConfigContextQueryService configContextQueryService;
/** 文本类型的响应数据 **/
protected ResponseEntity textResp(String text){
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_HTML);
return new ResponseEntity(text, httpHeaders, HttpStatus.OK);
}
/** json类型的响应数据 **/
protected ResponseEntity jsonResp(Object body){
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
return new ResponseEntity(body, httpHeaders, HttpStatus.OK);
}
/**request.getParameter 获取参数 并转换为JSON格式 **/
protected JSONObject getReqParamJSON() {
return requestKitBean.getReqParamJSON();
}
/**request.getParameter 获取参数 并转换为JSON格式 **/
protected String getReqParamFromBody() {
return requestKitBean.getReqParamFromBody();
}
/** 获取文件路径 **/
protected String getCertFilePath(String certFilePath) {
return channelCertConfigKitBean.getCertFilePath(certFilePath);
}
/** 获取文件File对象 **/
protected File getCertFile(String certFilePath) {
return channelCertConfigKitBean.getCertFile(certFilePath);
}
}

View File

@@ -0,0 +1,72 @@
package com.jeequan.jeepay.thirdparty.channel;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.beans.RequestKitBean;
import com.jeequan.jeepay.core.interfaces.paychannel.ITransferNoticeService;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import com.jeequan.jeepay.thirdparty.util.ChannelCertConfigKitBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
/*
* 实现回调接口抽象类
*
* @author zx
*
* @date 2022/12/30 10:18
*/
public abstract class AbstractTransferNoticeService implements ITransferNoticeService {
@Autowired private RequestKitBean requestKitBean;
@Autowired private ChannelCertConfigKitBean channelCertConfigKitBean;
@Autowired protected ConfigContextQueryService configContextQueryService;
@Override
public ResponseEntity doNotifyOrderNotExists(HttpServletRequest request) {
return textResp("order not exists");
}
/** 文本类型的响应数据 **/
protected ResponseEntity textResp(String text){
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_HTML);
return new ResponseEntity(text, httpHeaders, HttpStatus.OK);
}
/** json类型的响应数据 **/
protected ResponseEntity jsonResp(Object body){
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
return new ResponseEntity(body, httpHeaders, HttpStatus.OK);
}
/**request.getParameter 获取参数 并转换为JSON格式 **/
protected JSONObject getReqParamJSON() {
return requestKitBean.getReqParamJSON();
}
/**request.getParameter 获取参数 并转换为JSON格式 **/
protected String getReqParamFromBody() {
return requestKitBean.getReqParamFromBody();
}
/** 获取文件路径 **/
protected String getCertFilePath(String certFilePath) {
return channelCertConfigKitBean.getCertFilePath(certFilePath);
}
/** 获取文件File对象 **/
protected File getCertFile(String certFilePath) {
return channelCertConfigKitBean.getCertFile(certFilePath);
}
}

View File

@@ -0,0 +1,189 @@
package com.jeequan.jeepay.thirdparty.channel;
import cn.hutool.core.date.DateField;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.AccountOperate;
import com.jeequan.jeepay.core.entity.TransferOrder;
import com.jeequan.jeepay.core.entity.TransferWallet;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.interfaces.paychannel.ITransferService;
import com.jeequan.jeepay.core.model.rqrs.transfer.TransferOrderRQ;
import com.jeequan.jeepay.core.model.tranfer.TransferBasicInfo;
import com.jeequan.jeepay.core.utils.AmountUtil;
import com.jeequan.jeepay.db.entity.AccountFundInfo;
import com.jeequan.jeepay.db.entity.AccountInfo;
import com.jeequan.jeepay.db.entity.TransferSubjectEntity;
import com.jeequan.jeepay.db.entity.TransferWalletEntity;
import com.jeequan.jeepay.service.impl.*;
import org.apache.commons.lang3.tuple.MutablePair;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Date;
/**
* 支付接口抽象类
*
* @author terrfly
*
* @since 2021/6/8 17:18
*/
public abstract class AbstractTransferService implements ITransferService {
@Autowired protected TransferOrderService transferOrderService;
@Autowired protected TransferWalletService transferWalletService;
@Autowired protected TransferSubjectService transferSubjectService;
@Autowired protected SysConfigService sysConfigService;
@Autowired protected AccountInfoService accountInfoService;
protected String getPaySiteUrl() {
return sysConfigService.getDBApplicationConfig().getPaySiteUrl();
}
protected String getNotifyUrl(){
return getPaySiteUrl() + "/api/transfer/notify/" + getIfCode();
}
protected String getNotifyUrl(String payOrderId){
return getPaySiteUrl() + "/api/transfer/notify/" + getIfCode() + "/" + payOrderId;
}
@Override
public MutablePair<String, Long> queryBalanceAmount(TransferWallet transferWallet) {;
return MutablePair.of(null, null);
}
@Override
public String preCheck(TransferOrderRQ bizRQ, TransferOrder transferOrder) {
commonCheck(bizRQ, transferOrder);
return "";
}
@Override
public void commonCheck(TransferOrderRQ bizRQ, TransferOrder transferOrder) {
checkAmountLimit(bizRQ, transferOrder);
checkRateAccountFee(bizRQ, transferOrder);
}
/**
* 交易限额的校验
*/
protected final void checkAmountLimit(TransferOrderRQ bizRQ, TransferOrder transferOrder) {
String transferSubjectIdFq = transferOrder.getTransferSubjectIdFq();
TransferWalletEntity transferWallet = transferWalletService.getByApplyId(transferSubjectIdFq, null);
Long monthLimit;
Long dayLimit;
String transferTypeName;
switch (transferOrder.getEntryType()) {
case TransferOrder.ENTRY_BANK_CARD:
if (transferOrder.getAccountType().equals(TransferBasicInfo.ACCOUNT_TYPE_COR)) {
transferTypeName = "对公账户";
// 对公
if (transferOrder.getAmount() > transferWallet.getBankCardCorOnceMax()) {
throw new BizException("转账单笔超限额[" + AmountUtil.convertCent2Dollar(transferWallet.getBankCardCorOnceMax()) + "]");
}
} else {
transferTypeName = "个人银行卡";
// 对私
if (transferOrder.getAmount() > transferWallet.getBankCardPriOnceMax()) {
throw new BizException("转账个人银行卡单笔超限额[" + AmountUtil.convertCent2Dollar(transferWallet.getBankCardPriOnceMax()) + "]");
}
}
monthLimit = transferWallet.getBankCardMonthMax();
dayLimit = transferWallet.getBankCardDayMax();
break;
case TransferOrder.ENTRY_WX_CASH:
if (transferOrder.getAccountType().equals(TransferBasicInfo.ACCOUNT_TYPE_COR)) {
transferTypeName = "企业微信";
// 企业微信
if (transferOrder.getAmount() > transferWallet.getWxCorOnceMax()) {
throw new BizException("转账企业微信单笔超限额[" + AmountUtil.convertCent2Dollar(transferWallet.getWxCorOnceMax()) + "]");
}
} else {
transferTypeName = "个人微信";
// 对私
if (transferOrder.getAmount() > transferWallet.getWxPriOnceMax()) {
throw new BizException("转账个人微信单笔超限额[" + AmountUtil.convertCent2Dollar(transferWallet.getWxPriOnceMax()) + "]");
}
}
monthLimit = transferWallet.getWxMonthMax();
dayLimit = transferWallet.getWxDayMax();
break;
case TransferOrder.ENTRY_ALIPAY_CASH:
if (transferOrder.getAccountType().equals(TransferBasicInfo.ACCOUNT_TYPE_COR)) {
transferTypeName = "企业支付宝";
// 对公
if (transferOrder.getAmount() > transferWallet.getZfbCorOnceMax()) {
throw new BizException("转账企业支付宝单笔超限额[" + AmountUtil.convertCent2Dollar(transferWallet.getZfbCorOnceMax()) + "]");
}
} else {
transferTypeName = "个人支付宝";
// 对私
if (transferOrder.getAmount() > transferWallet.getZfbPriOnceMax()) {
throw new BizException("转账个人支付宝单笔超限额[" + AmountUtil.convertCent2Dollar(transferWallet.getZfbPriOnceMax()) + "]");
}
}
monthLimit = transferWallet.getZfbMonthMax();
dayLimit = transferWallet.getZfbDayMax();
break;
default:
throw new BizException("未知的打款方式");
}
Date current = new Date();
Long accumulatedMonth = transferOrderService.accumulated(DateField.MONTH, current,
TransferOrder.TRANSFER_TYPE_OUT, transferOrder.getEntryType(), transferSubjectIdFq,
TransferOrder.STATE_INIT, TransferOrder.STATE_ING, TransferOrder.STATE_SUCCESS);
if (accumulatedMonth + transferOrder.getAmount() > monthLimit) {;
throw new BizException("操作失败,转账" + transferTypeName + "已达到月限额[" + monthLimit + "]");
}
Long accumulatedDay = transferOrderService.accumulated(DateField.DAY_OF_YEAR, current,
TransferOrder.TRANSFER_TYPE_OUT, transferOrder.getEntryType(), transferSubjectIdFq,
TransferOrder.STATE_INIT, TransferOrder.STATE_ING, TransferOrder.STATE_SUCCESS);
if (accumulatedDay + transferOrder.getAmount() > dayLimit) {
throw new BizException("操作失败,转账" + transferTypeName + "已达到日限额[" + dayLimit + "]");
}
}
/**
* 手续费校验
*/
protected final void checkRateAccountFee(TransferOrderRQ bizRQ, TransferOrder transferOrder) {
// 手续费外扣,需要校验余额
if (transferOrder.getFeeCostType().equals(TransferOrder.COST_TYPE_ACCOUNT)) {
// 校验余额,
TransferSubjectEntity transferSubject = transferSubjectService.getById(transferOrder.getTransferSubjectIdFq());
String infoId;
if (transferSubject.getInfoType().equals(CS.SYS_ROLE_TYPE.MCH)) {
infoId = transferSubject.getMchNo();
} else {
infoId = transferSubject.getAgentNo();
}
AccountInfo accountInfo = accountInfoService.getAccountInfo(infoId, AccountInfo.Type.SERVICE_CHARGE.getType());
if (accountInfo.getBalacne() < transferOrder.getMchOrderFeeAmount()) {
throw new BizException("当前手续费余额不足");
}
// 预扣手续费
AccountOperate accountOperate = new AccountOperate(accountInfo.getAccountNo(), transferOrder.getAmount(),
AccountFundInfo.ChangeType.TRANSFER.getValue(), AccountFundInfo.ChangeMethod.DF.getValue(),
transferOrder.getTransferId(), "代付手续费");
accountOperate.setFinalAmt(transferOrder.getMchOrderFeeAmount());
accountOperate.setIsUpdateAccount(true);
accountInfoService.upAccount(accountOperate);
}
}
}

View File

@@ -0,0 +1,40 @@
package com.jeequan.jeepay.thirdparty.channel;
import com.jeequan.jeepay.core.interfaces.paychannel.*;
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
/**
* 返回各个通道的的
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class IsvFactory {
public static IIsvmchApplymentService getIsvMchApplymentService(String ifCode) {
return SpringBeansUtil.getBean(ifCode + "MchApplymentService", IIsvmchApplymentService.class);
}
public static IIsvmchWxConfigService getIsvMchWxConfigService(String ifCode) {
return SpringBeansUtil.getBean(ifCode + "IsvmchWxConfigService", IIsvmchWxConfigService.class);
}
public static IIsvmchAlipayConfigService getIsvMchAlipayConfigService(String ifCode) {
return SpringBeansUtil.getBean(ifCode + "IsvmchAlipayConfigService", IIsvmchAlipayConfigService.class);
}
public static IIsvmchModifyApplymentService getIsvMchModifyApplymentService(String ifCode) {
return SpringBeansUtil.getBean(ifCode + "IsvmchModifyApplymentService", IIsvmchModifyApplymentService.class);
}
public static IGetApplymentDataService getGetApplymentDataService(String ifCode) {
return SpringBeansUtil.getBean(ifCode + "GetApplymentDataService", IGetApplymentDataService.class);
}
public static IIsvmchWrapper getIsvMchWrapper(String ifCode) {
return SpringBeansUtil.getBean(ifCode + "IsvmchWrapper", IIsvmchWrapper.class);
}
public static ITransferService getTransferService(String ifCode) {
return SpringBeansUtil.getBean(ifCode + "TransferService", ITransferService.class);
}
}

View File

@@ -0,0 +1,92 @@
package com.jeequan.jeepay.thirdparty.channel.aliaqf;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.TransferSubject;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.interfaces.transfer.IChannelTransferSubjectService;
import com.jeequan.jeepay.core.model.params.aliaqf.AliaqfIsvParams;
import com.jeequan.jeepay.db.entity.TransferSubjectEntity;
import com.jeequan.jeepay.service.impl.TransferSubjectService;
import com.jeequan.jeepay.thirdparty.channel.aliaqf.model.AliAqfTransferBasicInfo;
import com.jeequan.jeepay.thirdparty.channel.alipay.AliAqfV2Service;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* TODO
*
* @author deng
* @since 2024/5/8
*/
@Slf4j
@Service
public class AliaqfChannelTransferSubjectService implements IChannelTransferSubjectService<AliAqfTransferBasicInfo> {
@Autowired
private TransferSubjectService transferSubjectService;
@Autowired
private AliAqfV2Service aliAqfV2Service;
@Autowired
private ConfigContextQueryService configContextQueryService;
@Override
public void addExporter(AliAqfTransferBasicInfo transferBasicInfo) {
}
@Override
public void addAcceptor(AliAqfTransferBasicInfo transferBasicInfo) {
throw new BizException("当前通道【安全发】不支持该操作");
}
@Override
public void callbackProcess(JSONObject bizContent) {
log.info("安全发的签约回调, 回调参数为{}", bizContent);
TransferSubjectEntity subjectEntity = transferSubjectService.getByApplyId(bizContent.getString("external_agreement_no"));
AliaqfIsvParams isvParams = (AliaqfIsvParams) configContextQueryService.queryTransferIsvParams(subjectEntity.getIsvNo(), CS.IF_CODE.ALIAQF);
try {
//验签
if (!AliaqfKit.checkSign(bizContent, isvParams)) {
log.error("安全发签约回调验签失败");
return;
}
subjectEntity.setTransChannelSignNo(bizContent.getString("agreement_no"));
subjectEntity.setSuccResParameter(bizContent.toJSONString());
switch (bizContent.getString("status")) {
case "NORMAL":
subjectEntity.setSignState((int) TransferSubject.SIGN_STATE_SUCCESS);
transferSubjectService.updateById(subjectEntity);
// 开通记账本
aliAqfV2Service.fundAccountbookCreate(subjectEntity.getId());
break;
case "TEMP":
subjectEntity.setSignState((int) TransferSubject.SIGN_STATE_ON_HOLD);
transferSubjectService.updateById(subjectEntity);
break;
case "STOP":
subjectEntity.setSignState((int) TransferSubject.SIGN_STATE_ON_PAUSE);
transferSubjectService.updateById(subjectEntity);
break;
default:
subjectEntity.setSignState((int) TransferSubject.SIGN_STATE_UNKNOWN);
transferSubjectService.updateById(subjectEntity);
break;
}
} catch (Exception e) {
log.error("安全发签约回调异常", e);
transferSubjectService.updateById(subjectEntity);
}
}
@Override
public Object retOk(JSONObject bizContent) {
return "SUCCESS";
}
}

View File

@@ -0,0 +1,45 @@
package com.jeequan.jeepay.thirdparty.channel.aliaqf;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.alipay.api.internal.util.AlipaySignature;
import com.jeequan.jeepay.core.model.params.aliaqf.AliaqfIsvParams;
import com.jeequan.jeepay.core.model.params.alipay.AlipayConfig;
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
import com.jeequan.jeepay.thirdparty.util.ChannelCertConfigKitBean;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
/**
* 安全发的一些通用请求,签名等
*
* @author deng
* @since 2024/5/9
*/
@Slf4j
public class AliaqfKit {
// alipay.fund.trans.uni.transfer 涉及的状态SUCCESS、FAIL、DEALING、REFUND
public static final String ORDER_SUCCESS = "SUCCESS";
/** 处理中 */
public static final String ORDER_DEALING = "DEALING";
/** 退票 */
public static final String ORDER_REFUND = "REFUND";
/** 失败 */
public static final String ORDER_FAIL = "FAIL";
public static boolean checkSign(JSONObject reqParamJSON, AliaqfIsvParams isvParams) {
try {
TypeReference<Map<String, String>> mapTypeReference = new TypeReference<Map<String, String>>() {
};
ChannelCertConfigKitBean channelCertConfigKitBean = SpringBeansUtil.getBean(ChannelCertConfigKitBean.class);
return AlipaySignature.rsaCertCheckV1(reqParamJSON.toJavaObject(mapTypeReference), channelCertConfigKitBean.getCertFilePath(isvParams.getAlipayPublicCert()),
AlipayConfig.CHARSET, "RSA2");
} catch (Exception e) {
log.info("验签失败", e);
}
return false;
}
}

View File

@@ -0,0 +1,251 @@
package com.jeequan.jeepay.thirdparty.channel.aliaqf;
import cn.hutool.core.lang.Assert;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayClient;
import com.alipay.api.CertAlipayRequest;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayFundTransCommonQueryModel;
import com.alipay.api.domain.AlipayFundTransUniTransferModel;
import com.alipay.api.domain.BankcardExtInfo;
import com.alipay.api.domain.Participant;
import com.alipay.api.request.AlipayFundTransCommonQueryRequest;
import com.alipay.api.request.AlipayFundTransUniTransferRequest;
import com.alipay.api.response.AlipayFundAccountbookQueryResponse;
import com.alipay.api.response.AlipayFundTransCommonQueryResponse;
import com.alipay.api.response.AlipayFundTransUniTransferResponse;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.AccountOperate;
import com.jeequan.jeepay.core.entity.TransferOrder;
import com.jeequan.jeepay.core.entity.TransferWallet;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.params.aliaqf.AliaqfIsvParams;
import com.jeequan.jeepay.core.model.params.alipay.AlipayConfig;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.model.rqrs.transfer.TransferOrderRQ;
import com.jeequan.jeepay.core.model.tranfer.TransferBasicInfo;
import com.jeequan.jeepay.core.utils.AmountUtil;
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
import com.jeequan.jeepay.db.entity.*;
import com.jeequan.jeepay.service.impl.AccountInfoService;
import com.jeequan.jeepay.service.impl.TransferInterfaceConfigService;
import com.jeequan.jeepay.service.impl.TransferSubjectService;
import com.jeequan.jeepay.thirdparty.channel.AbstractTransferService;
import com.jeequan.jeepay.thirdparty.channel.alipay.AliAqfV2Service;
import com.jeequan.jeepay.thirdparty.util.ChannelCertConfigKitBean;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.MutablePair;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
/**
* TODO
*
* @author deng
* @since 2024/5/11
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class AliaqfTransferService extends AbstractTransferService {
private final AccountInfoService accountInfoService;
private final TransferSubjectService transferSubjectService;
private final TransferInterfaceConfigService interfaceConfigService;
private final AliAqfV2Service aliAqfV2Service;
@Override
public String getIfCode() {
return CS.IF_CODE.ALIAQF;
}
@Override
public boolean isSupport(String entryType) {
return TransferBasicInfo.ENTRY_TYPE_BANK.equals(entryType) || TransferBasicInfo.ENTRY_TYPE_ZFB.equals(entryType);
}
public void rollBackServiceCharge(TransferOrder transferOrder) {
// 预扣手续费回退
TransferSubjectEntity transferSubject = transferSubjectService.getById(transferOrder.getTransferSubjectIdFq());
String infoId;
if (transferSubject.getInfoType().equals(CS.SYS_ROLE_TYPE.MCH)) {
infoId = transferSubject.getMchNo();
} else {
infoId = transferSubject.getAgentNo();
}
AccountInfo accountInfo = accountInfoService.getAccountInfo(infoId, AccountInfo.Type.SERVICE_CHARGE.getType());
// 预扣手续费
AccountOperate accountOperate = new AccountOperate(accountInfo.getAccountNo(), transferOrder.getAmount(),
AccountFundInfo.ChangeType.TRANSFER.getValue(), AccountFundInfo.ChangeMethod.DF.getValue(),
transferOrder.getTransferId(), "代付失败手续费回退");
accountOperate.setFinalAmt(-transferOrder.getMchOrderFeeAmount());
accountOperate.setIsUpdateAccount(true);
accountInfoService.upAccount(accountOperate);
}
@Override
public ChannelRetMsg transfer(TransferOrderRQ bizRQ, TransferOrder transferOrder) throws Exception {
log.info("进入了支付宝直付通代发!");
ChannelRetMsg retMsg = ChannelRetMsg.waiting();
CertAlipayRequest certAlipayRequest = getCertAliPayRequest(transferOrder.getIsvNo());
TransferSubjectEntity ersubjectEntity = transferSubjectService.getById(transferOrder.getTransferSubjectIdFq());
TransferSubjectEntity eesubjectEntity = transferSubjectService.getById(transferOrder.getTransferSubjectIdJs());
try {
AlipayClient alipayClient = new DefaultAlipayClient(certAlipayRequest);
AlipayFundTransUniTransferRequest request = new AlipayFundTransUniTransferRequest();
AlipayFundTransUniTransferModel model = new AlipayFundTransUniTransferModel();
model.setOutBizNo(transferOrder.getTransferId());
model.setTransAmount(AmountUtil.convertCent2Dollar(transferOrder.getAmount()));
model.setProductCode("SINGLE_TRANSFER_NO_PWD");
model.setBizScene("ENTRUST_TRANSFER");
model.setOrderTitle(StringUtils.isNotEmpty(transferOrder.getTransferDesc()) ? transferOrder.getTransferDesc() : "支付宝代发到户!");
//付款方信息
Participant participanter = new Participant();
participanter.setIdentity(ersubjectEntity.getTransChannelMchNo());
participanter.setIdentityType("ACCOUNT_BOOK_ID");
JSONObject erparm = new JSONObject();
erparm.put("agreement_no", ersubjectEntity.getTransChannelSignNo());
participanter.setExtInfo(erparm.toJSONString());
model.setPayerInfo(participanter);
//收款方信息
Participant participantee = new Participant();
participantee.setIdentity(eesubjectEntity.getAccount());
if (TransferOrderEntity.ENTRY_ALIPAY_CASH.equals(transferOrder.getEntryType())){
participantee.setIdentityType("ALIPAY_LOGON_ID");
participantee.setName(eesubjectEntity.getAccountName());
}else if (TransferOrderEntity.ENTRY_BANK_CARD.equals(transferOrder.getEntryType())){
participantee.setIdentityType("BANKCARD_ACCOUNT");
BankcardExtInfo bankcardExtInfo = new BankcardExtInfo();
participantee.setName(eesubjectEntity.getAccountName());
if (eesubjectEntity.getAccountType() == TransferBasicInfo.ACCOUNT_TYPE_COR) {
//对公
String accountMessage = eesubjectEntity.getAccountMessage();
if (StringUtils.isNotEmpty(accountMessage)) {
JSONObject jsonObject = JSONObject.parseObject(accountMessage);
bankcardExtInfo.setAccountType("1");
bankcardExtInfo.setInstName(jsonObject.getString("instName"));
bankcardExtInfo.setInstProvince(jsonObject.getString("instProvince"));
bankcardExtInfo.setInstCity(jsonObject.getString("instCity"));
bankcardExtInfo.setInstBranchName(jsonObject.getString("instBranchName"));
participantee.setBankcardExtInfo(bankcardExtInfo);
transferOrder.setBankName(jsonObject.getString("instName"));
}
} else {
//对私
bankcardExtInfo.setAccountType("2");
participantee.setBankcardExtInfo(bankcardExtInfo);
}
}
model.setPayeeInfo(participantee);
request.setBizModel(model);
AlipayFundTransUniTransferResponse response = alipayClient.certificateExecute(request);
retMsg.setChannelAttach(response.getBody());
if (response.isSuccess()) {
retMsg.setChannelOrderId(response.getOrderId());
retMsg.setPlatformOrderNo(response.getPayFundOrderId());
} else {
retMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
retMsg.setChannelErrMsg(response.getSubMsg());
retMsg.setChannelErrCode(response.getSubCode());
}
} catch (Exception e) {
log.info("代发异常!",e);
retMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
retMsg.setChannelErrMsg(e.getMessage());
rollBackServiceCharge(transferOrder);
}
return retMsg;
}
@Override
public ChannelRetMsg query(TransferOrder transferOrder, MchAppConfigContext mchAppConfigContext) {
log.info("进入了支付宝账单查询");
TransferSubjectEntity transferSubjectFq = transferSubjectService.getById(transferOrder.getTransferSubjectIdFq());
CertAlipayRequest certAlipayRequest = getCertAliPayRequest(transferSubjectFq.getIsvNo());
AlipayFundTransCommonQueryResponse response;
if ( transferSubjectFq.getTransChannelSignNo() == null || transferSubjectFq.getTransChannelMchNo() == null) {
throw new BizException("当前付款人签约或开通记账本!");
}
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.UNKNOWN);
try {
AlipayClient alipayClient = new DefaultAlipayClient(certAlipayRequest);
AlipayFundTransCommonQueryRequest request = new AlipayFundTransCommonQueryRequest();
AlipayFundTransCommonQueryModel model = new AlipayFundTransCommonQueryModel();
model.setProductCode(TransferOrder.ENTRY_BANK_CARD.equals(transferOrder.getEntryType()) ? "TRANS_BANKCARD_NO_PWD" : "TRANS_ACCOUNT_NO_PWD");
model.setBizScene("DIRECT_TRANSFER");
model.setOutBizNo(transferOrder.getTransferId());
request.setBizModel(model);
channelRetMsg.setChannelOrderId(transferOrder.getChannelOrderNo());
response = alipayClient.certificateExecute(request);
channelRetMsg.setChannelOriginResponse(response.getBody());
channelRetMsg.setResponseEntity(ResponseEntity.ok("SUCCESS"));
if (response.isSuccess()) {
log.info("安全发账单查询成功:" + response.getBody());
} else {
log.error("安全发账单查询失败!" + response.getSubMsg());
return channelRetMsg;
}
String status = response.getStatus();
if (AliaqfKit.ORDER_SUCCESS.equals(status)) {
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
channelRetMsg.setChannelOrderId(response.getOrderId());
channelRetMsg.setPlatformOrderNo(response.getPayFundOrderId());
} else if (AliaqfKit.ORDER_FAIL.equals(status)) {
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
rollBackServiceCharge(transferOrder);
} else if (AliaqfKit.ORDER_DEALING.equals(status)) {
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
}
return channelRetMsg;
} catch (Exception e) {
log.info("支付宝安全发查询订单状态异常, {}", transferOrder.getTransferId(), e);
}
return channelRetMsg;
}
@Override
public MutablePair<String, Long> queryBalanceAmount(TransferWallet transferWallet) {
AlipayFundAccountbookQueryResponse queryResultData = aliAqfV2Service.fundAccountbookQuery(transferWallet.getTransApplyId(), transferWallet.getWalletApplyId());
String availableAmount = queryResultData.getAvailableAmount();
return new MutablePair<>(transferWallet.getTransApplyId(), AmountUtil.convertDollar2CentLong(availableAmount));
}
public CertAlipayRequest getCertAliPayRequest(String isvNo) {
TransferInterfaceConfigEntity transferInterfaceConfigEntity = interfaceConfigService.getTransferConfig(CS.IF_CODE.ALIAQF, isvNo, CS.SYS_ROLE_TYPE.ISV);
Assert.notNull(transferInterfaceConfigEntity, "所选渠道配置不存在");
CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
ChannelCertConfigKitBean channelCertConfigKitBean = SpringBeansUtil.getBean(ChannelCertConfigKitBean.class);
AliaqfIsvParams isvParams = JSONObject.parseObject(transferInterfaceConfigEntity.getTransIfParams(), AliaqfIsvParams.class);
// 支付宝网关
certAlipayRequest.setServerUrl(AlipayConfig.PROD_SERVER_URL);
certAlipayRequest.setAppId(isvParams.getAppId());
certAlipayRequest.setPrivateKey(isvParams.getPrivateKey());
certAlipayRequest.setCertPath(channelCertConfigKitBean.getCertFilePath(isvParams.getAppPublicCert()));
certAlipayRequest.setAlipayPublicCertPath(channelCertConfigKitBean.getCertFilePath(isvParams.getAlipayPublicCert()));
certAlipayRequest.setRootCertPath(channelCertConfigKitBean.getCertFilePath(isvParams.getAlipayRootCert()));
certAlipayRequest.setSignType("RSA2");
certAlipayRequest.setFormat("json");
certAlipayRequest.setCharset("UTF-8");
return certAlipayRequest;
}
}

View File

@@ -0,0 +1,12 @@
package com.jeequan.jeepay.thirdparty.channel.aliaqf.model;
import com.jeequan.jeepay.core.model.tranfer.TransferBasicInfo;
/**
* TODO
*
* @author deng
* @since 2024/5/8
*/
public class AliAqfTransferBasicInfo extends TransferBasicInfo {
}

View File

@@ -0,0 +1,778 @@
package com.jeequan.jeepay.thirdparty.channel.alipay;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjUtil;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.CertAlipayRequest;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.diagnosis.DiagnosisUtils;
import com.alipay.api.domain.*;
import com.alipay.api.request.*;
import com.alipay.api.response.*;
import com.jeequan.jeepay.converter.TransferConverter;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.TransferOrder;
import com.jeequan.jeepay.core.entity.TransferSubject;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.interfaces.paychannel.aliaqfbiz.IAliAqfV2ApiService;
import com.jeequan.jeepay.core.model.applyment.PaywayFee;
import com.jeequan.jeepay.core.model.params.aliaqf.AliaqfIsvParams;
import com.jeequan.jeepay.core.model.params.alipay.AlipayConfig;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.model.tranfer.TransferBasicInfo;
import com.jeequan.jeepay.core.utils.AmountUtil;
import com.jeequan.jeepay.core.utils.SeqKit;
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
import com.jeequan.jeepay.db.entity.*;
import com.jeequan.jeepay.service.impl.*;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import com.jeequan.jeepay.thirdparty.util.ChannelCertConfigKitBean;
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.stereotype.Service;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@Service
@Slf4j
public class AliAqfV2Service implements IAliAqfV2ApiService {
@Autowired
private SysConfigService sysConfigService;
@Autowired
private TransferInterfaceConfigService transferInterfaceConfigService;
@Autowired
private TransferSubjectService transferSubjectService;
@Autowired
private TransferOrderService transferOrderService;
@Autowired
private TransferWalletService transferWalletService;
@Autowired
private TaskListService taskListService;
@Autowired
private ConfigContextQueryService configContextQueryService;
@Autowired
private RateConfigService rateConfigService;
@Autowired
private TransferConverter transferConverter;
@Autowired
private AccountInfoService accountInfoService;
private String getOpenCallbackUrl(String subjectId) {
return sysConfigService.getDBApplicationConfig().getPaySiteUrl() + "/api/transferSubject/notify/aliaqf/" + subjectId;
}
/**
* 签约
*/
@Override
public String userAgreementPageSign(String subId) {
log.info("进入了支付宝签约");
TransferSubjectEntity subjectEntity = getSubjectOne(subId);
CertAlipayRequest certAlipayRequest = getCertAliPayRequest(subjectEntity.getIsvNo());
if (subjectEntity.getState() == TransferSubject.STATE_DISABLE) {
throw new BizException("当前付款账户已被停用!");
}
Assert.notNull(subjectEntity.getTransApplyId(), "申请单号不能为空");
try {
AlipayClient alipayClient = new DefaultAlipayClient(certAlipayRequest);
AlipayUserAgreementPageSignRequest request = new AlipayUserAgreementPageSignRequest();
AlipayUserAgreementPageSignModel model = new AlipayUserAgreementPageSignModel();
model.setPersonalProductCode("FUND_SAFT_SIGN_WITHHOLDING_P");
AccessParams accessParams = new AccessParams();
accessParams.setChannel("QRCODE");
model.setAccessParams(accessParams);
model.setProductCode("FUND_SAFT_SIGN_WITHHOLDING");
model.setSignScene("INDUSTRY|SATF_ACC");
//唯一标识,新建收款账户时生成,
model.setExternalAgreementNo(subjectEntity.getTransApplyId());
model.setThirdPartyType("PARTNER");
request.setBizModel(model);
String openCallbackUrl = getOpenCallbackUrl(subjectEntity.getTransApplyId());
request.setNotifyUrl(openCallbackUrl);
log.info("安全发签约回调地址:" + openCallbackUrl);
log.info("请求报文:{}", JSONObject.toJSONString(request));
AlipayUserAgreementPageSignResponse response = alipayClient.pageExecute(request, "get");
String pageRedirectionData = response.getBody();
if (response.isSuccess()) {
log.info("安全发签约返回:" + response.getBody());
//设置为签约中
subjectEntity.setSignState((int) TransferSubject.SIGN_STATE_AUDITING);
subjectEntity.setChannelVar1(pageRedirectionData);
transferSubjectService.updateById(subjectEntity);
return pageRedirectionData;
} else {
log.info("安全发签约发起请求失败");
String diagnosisUrl = DiagnosisUtils.getDiagnosisUrl(response);
if (ObjUtil.isEmpty(diagnosisUrl)) {
throw new BizException(response.getSubMsg());
}
return diagnosisUrl;
}
} catch (AlipayApiException e) {
log.error("安全发签约请求异常!");
throw new BizException(e.getErrMsg());
}
}
/**
* 支付宝个人代扣协议查询接口
*
* @param
* @return
*/
@Override
public JSONObject userAgreementQuery(String subId) {
log.info("进入了支付宝个人代扣协议查询接口");
TransferSubjectEntity subjectEntity = getSubjectOne(subId);
CertAlipayRequest certAlipayRequest = getCertAliPayRequest(subjectEntity.getIsvNo());
JSONObject resut = new JSONObject();
if (certAlipayRequest == null || subjectEntity.getTransApplyId() == null) {
throw new BizException("未获取到当前用户相关信息!");
}
try {
AlipayClient alipayClient = new DefaultAlipayClient(certAlipayRequest);
AlipayUserAgreementQueryRequest request = new AlipayUserAgreementQueryRequest();
AlipayUserAgreementQueryModel model = new AlipayUserAgreementQueryModel();
model.setPersonalProductCode("FUND_SAFT_SIGN_WITHHOLDING_P");
model.setSignScene("INDUSTRY|SATF_ACC");
//签约时传入值
model.setExternalAgreementNo(subjectEntity.getTransApplyId());
model.setThirdPartyType("PARTNER");
request.setBizModel(model);
AlipayUserAgreementQueryResponse response = alipayClient.certificateExecute(request);
resut = JSONObject.parseObject(response.getBody()).getJSONObject("alipay_user_agreement_query_response");
} catch (Exception e) {
log.error("支付宝个人代扣协议查询异常!",e);
throw new BizException("支付宝个人代扣协议查询异常!");
}
return resut;
}
/**
* 解约
*
* @param
* @return
*/
@Override
public JSONObject userAgreementUnsign(String subId) {
log.info("进入了支付宝个人代扣协议解约接口");
TransferSubjectEntity subjectEntity = getSubjectOne(subId);
CertAlipayRequest certAlipayRequest = getCertAliPayRequest(subjectEntity.getIsvNo());
JSONObject resut = new JSONObject();
if (certAlipayRequest == null || subjectEntity.getTransApplyId() == null) {
throw new BizException("未获取到当前用户相关信息!");
}
try {
AlipayClient alipayClient = new DefaultAlipayClient(certAlipayRequest);
AlipayUserAgreementUnsignRequest request = new AlipayUserAgreementUnsignRequest();
AlipayUserAgreementUnsignModel model = new AlipayUserAgreementUnsignModel();
model.setPersonalProductCode("FUND_SAFT_SIGN_WITHHOLDING_P");
model.setSignScene("INDUSTRY|SATF_ACC");
//签约时传入值
model.setExternalAgreementNo(subjectEntity.getTransApplyId());
//注意:仅异步解约需传入,其余情况无需传递本参数。
// 解约确认: confirm
//解约作废: invalid
model.setOperateType("confirm");
request.setBizModel(model);
//request.setNotifyUrl(sysConfigService.getDBApplicationConfig().getPaySiteUrl() + "/api/anf/reqUnsign");
//log.info("安全发解约回调地址:" + sysConfigService.getDBApplicationConfig().getPaySiteUrl() + "/api/anf/reqNotify");
AlipayUserAgreementUnsignResponse response = alipayClient.certificateExecute(request);
log.info("支付宝个人代扣协议解约返回参数:" + response.getBody());
if (response.isSuccess()) {
//直接就是已解约,异步返回暂时不处理
subjectEntity.setSignState((int)TransferSubject.STATE_ENABLE_REVIEW);
transferSubjectService.updateById(subjectEntity);
} else {
log.info("支付宝个人代扣协议解约失败:" + response.getSubMsg());
}
resut = JSONObject.parseObject(response.getBody()).getJSONObject("alipay_user_agreement_unsign_response");
subjectEntity.setChannelVar1(response.getBody());
} catch (Exception e) {
log.error("支付宝个人代扣协议解约异常!",e);
throw new BizException("支付宝个人代扣协议解约异常!");
}
return resut;
}
/**
* 资金记账本开通
*
* @return
*/
@Override
public void fundAccountbookCreate(String subId) {
log.info("进入资金记账本开通方法!");
TransferSubjectEntity subjectEntity = getSubjectOne(subId);
AliaqfIsvParams isvParams = ((AliaqfIsvParams) configContextQueryService.queryTransferIsvParams(subjectEntity.getIsvNo(), CS.IF_CODE.ALIAQF));
CertAlipayRequest certAlipayRequest = getCertAliPayRequest(subjectEntity.getIsvNo());
JSONObject result = new JSONObject();
TransferSubjectEntity transferSubject = transferSubjectService.getById(subId);
TransferWalletEntity transferWalletEntity = new TransferWalletEntity();
AlipayFundAccountbookCreateResponse response;
String merchantUserId = UUID.randomUUID().toString().replace("-", "");
try {
AlipayClient alipayClient = new DefaultAlipayClient(certAlipayRequest);
AlipayFundAccountbookCreateRequest request = new AlipayFundAccountbookCreateRequest();
AlipayFundAccountbookCreateModel model = new AlipayFundAccountbookCreateModel();
//外部商户系统会员的唯一标识,自定义传入
model.setMerchantUserId(merchantUserId);
model.setMerchantUserType("BUSINESS_ORGANIZATION");
model.setSceneCode("SATF_FUND_BOOK");
//扩展信息: 支付宝协议号必传、企业营业执照认证号cert_no可不传。
//注意ext_info字段本质上是String类型所以传递的是转义后的json字符串。
//agreement_no 签约时回调返回的参数值
JSONObject param = new JSONObject();
param.put("agreement_no", transferSubject.getTransChannelSignNo());
model.setExtInfo(param.toJSONString());
request.setBizModel(model);
response = alipayClient.certificateExecute(request);
//处理记账本返回信息
log.info("支付宝安全发记账本开通请求返回参数, {}", response.getBody());
String responseBody = response.getBody();
if (response.isSuccess()) {
JSONObject jsonObject = JSONObject.parseObject(responseBody);
ExtCardInfo extCardInfo = response.getExtCardInfo();
transferSubject.setTransChannelMchNo(response.getAccountBookId());
//记账本信息
transferWalletEntity.setTransApplyId(transferSubject.getTransApplyId());
transferWalletEntity.setTransChannelMchNo(response.getAccountBookId());
transferWalletEntity.setTransIfCode(transferSubject.getTransIfCode());
transferWalletEntity.setWalletApplyId(model.getMerchantUserId());
transferWalletEntity.setExtResponse(jsonObject.toJSONString());
transferWalletEntity.setIsvNo(transferSubject.getIsvNo());
transferWalletEntity.setMchNo(transferSubject.getMchNo());
transferWalletEntity.setSubjectName(transferSubject.getSubjectName());
transferWalletEntity.setTransIfCode(CS.IF_CODE.ALIAQF);
transferWalletEntity.setWxMonthMax(0L);
transferWalletEntity.setWxDayMax(0L);
transferWalletEntity.setWxPriOnceMax(0L);
transferWalletEntity.setWxCorOnceMax(0L);
transferWalletEntity.setBankCardCorOnceMax(Long.parseLong(isvParams.getSubBankCardCorOnceMax()));
transferWalletEntity.setBankCardPriOnceMax(Long.parseLong(isvParams.getSubBankCardPriOnceMax()));
transferWalletEntity.setBankCardMonthMax(Long.parseLong(isvParams.getSubBankCardMonthMax()));
transferWalletEntity.setBankCardDayMax(Long.parseLong(isvParams.getSubBankCardDayMax()));
transferWalletEntity.setZfbCorOnceMax(Long.parseLong(isvParams.getSubZfbCorOnceMax()));
transferWalletEntity.setZfbPriOnceMax(Long.parseLong(isvParams.getSubZfbPriOnceMax()));
transferWalletEntity.setZfbMonthMax(Long.parseLong(isvParams.getSubZfbMonthMax()));
transferWalletEntity.setZfbDayMax(Long.parseLong(isvParams.getSubZfbDayMax()));
if (extCardInfo != null) {
transferWalletEntity.setExtCardInfo(JSONObject.toJSONString(response.getExtCardInfo()));
if ("A".equals(extCardInfo.getStatus())) {
transferWalletEntity.setCardStatus(TransferWalletEntity.CARD_STATUS_NORMAL);
} else {
transferWalletEntity.setCardStatus(TransferWalletEntity.CARD_STATUS_OTHER);
}
}
transferWalletService.save(transferWalletEntity);
} else {
log.info("资金记账本开通失败, {}", response.getSubMsg());
}
} catch (Exception e) {
log.info("资金记账本开通异常", e);
}
transferSubjectService.updateById(transferSubject);
}
/**
* 资金记账本的信息查询
*
* @return
*/
@Override
public AlipayFundAccountbookQueryResponse fundAccountbookQuery(String subId, String walletApplyId) {
log.info("进入了支付宝资金记账本的信息查询");
TransferSubjectEntity subjectEntity = getSubjectOne(subId);
CertAlipayRequest certAlipayRequest = getCertAliPayRequest(subjectEntity.getIsvNo());
AlipayFundAccountbookQueryResponse response;
if (certAlipayRequest == null || subjectEntity.getTransChannelSignNo() == null
|| subjectEntity.getTransChannelMchNo() == null) {
throw new BizException("当前用户暂未签约或配置!");
}
try {
TransferWalletEntity walletServiceOne = transferWalletService.getByApplyId(subjectEntity.getTransApplyId(), walletApplyId);
AlipayClient alipayClient = new DefaultAlipayClient(certAlipayRequest);
AlipayFundAccountbookQueryRequest request = new AlipayFundAccountbookQueryRequest();
AlipayFundAccountbookQueryModel model = new AlipayFundAccountbookQueryModel();
//资金记账本id开通时的返回值
model.setAccountBookId(subjectEntity.getTransChannelMchNo());
//商户会员的唯一标识 开通时自定义传入值
model.setMerchantUserId(walletServiceOne.getWalletApplyId());
model.setSceneCode("SATF_FUND_BOOK");
//扩展信息: 支付宝协议号必传
//注意ext_info字段本质上是String类型所以传递的是转义后的json字符串。
//agreement_no 签约时回调返回的参数值
JSONObject parm = new JSONObject();
parm.put("agreement_no", subjectEntity.getTransChannelSignNo());
model.setExtInfo(parm.toJSONString());
request.setBizModel(model);
response = alipayClient.certificateExecute(request);
if (response.isSuccess()) {
String responseBody = response.getBody();
JSONObject jsonObject = JSONObject.parseObject(responseBody);
JSONObject accountbook = jsonObject.getJSONObject("alipay_fund_accountbook_query_response");
walletServiceOne.setAccountBookBalance(AmountUtil.convertDollar2CentLong(accountbook.getString("available_amount")));
JSONObject extCardInfo = accountbook.getJSONObject("ext_card_info");
if (extCardInfo != null) {
walletServiceOne.setExtCardInfo(extCardInfo.toJSONString());
if ("A".equals(extCardInfo.getString("status"))) {
walletServiceOne.setCardStatus(1);
} else {
walletServiceOne.setCardStatus(2);
}
}
transferWalletService.updateById(walletServiceOne);
} else {
log.error("资金记账本的信息查询失败!" + response.getSubMsg());
}
} catch (Exception e) {
log.error("资金记账本的信息查询异常!",e);
throw new BizException("资金记账本的信息查询异常!");
}
return response;
}
/**
* 资金专款拨入(商户自身给记账本充值)
*/
@Override
public String transPage(String subId, String transAmount ,String transferDesc) {
log.info("进入了支付宝资金专款拨入(商户自身给记账本充值)");
TransferSubjectEntity subjectEntity = getSubjectOne(subId);
String resultUrl = null;
CertAlipayRequest certAlipayRequest = getCertAliPayRequest(subjectEntity.getIsvNo());
Assert.notNull(subjectEntity, "付款账户不存在");
//transferOrderService
TransferOrderEntity transferOrder = new TransferOrderEntity();
if (subjectEntity.getState() == TransferSubject.STATE_DISABLE) {
throw new BizException("当前付款账户已被停用");
}
//获取当前时间1小时后的时间作为过期时间
String formattedDateTime = LocalDateTime.now().plusHours(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
if (subjectEntity.getTransChannelMchNo() != null
&& subjectEntity.getTransChannelSignNo() != null) {
try {
AlipayClient alipayClient = new DefaultAlipayClient(certAlipayRequest);
AlipayFundTransPagePayRequest request = new AlipayFundTransPagePayRequest();
AlipayFundTransPagePayModel model = new AlipayFundTransPagePayModel();
//商户端的唯一订单号,对于同一笔转账请求,商户需保证该订单号唯一。
model.setOutBizNo("AQF_ZZ_" + SeqKit.genTransOrderId());
log.info("资金专款拨入outBizNo=====================" + model.getOutBizNo());
//拨入总金额,单位为元,精确到小数点后两位,取值范围[0.01,9999999999999.99]
model.setTransAmount(transAmount);
//
model.setProductCode("FUND_ACCOUNT_BOOK");
//
model.setBizScene("SATF_DEPOSIT");
//充值订单过期时间,默认会失败
model.setTimeExpire(formattedDateTime);
//收款方信息
Participant payeeInfo = new Participant();
//记账本 id
payeeInfo.setIdentity(subjectEntity.getTransChannelMchNo());
payeeInfo.setIdentityType("ACCOUNT_BOOK_ID");
//传入记账本id与场景码不传会校验失败导致充值失败。协议字段必传。
JSONObject parm = new JSONObject();
parm.put("agreement_no", subjectEntity.getTransChannelSignNo());
payeeInfo.setExtInfo(parm.toJSONString());
model.setPayeeInfo(payeeInfo);
request.setBizModel(model);
AlipayFundTransPagePayResponse response = alipayClient.pageExecute(request, "GET");
if (response.isSuccess()) {
log.info("资金专项拨入结果:" + response.getBody());
resultUrl = response.getBody();
//转账中,回调后改变状态
transferOrder.setState(TransferOrderEntity.STATE_ING);
} else {
log.error("资金专项拨入失败!" + response.getSubMsg());
resultUrl = response.getSubMsg();
//转账失败
transferOrder.setState(TransferOrderEntity.STATE_FAIL);
transferOrder.setErrCode(response.getSubCode());
transferOrder.setErrMsg(response.getSubMsg());
}
//将订单信息入库
// TODO: 2024/4/29 先做简单入库 后续完善
transferOrder.setAmount(AmountUtil.convertDollar2CentLong(transAmount));
transferOrder.setTransferId(model.getOutBizNo());
transferOrder.setMchOrderNo(model.getOutBizNo());
transferOrder.setTransferType(TransferOrderEntity.TRANSFER_TYPE_ZZ);
transferOrder.setTransferSubjectIdFq(subjectEntity.getId());
transferOrder.setTransferSubjectIdJs(subjectEntity.getId());
transferOrder.setMchNo(subjectEntity.getMchNo());
transferOrder.setMchExtNo(StringUtils.isNotEmpty(subjectEntity.getMchApplyId())?subjectEntity.getMchApplyId():null);
transferOrder.setAgentNo(subjectEntity.getAgentNo());
transferOrder.setAccountNo(subjectEntity.getAccount());
transferOrder.setAccountName(subjectEntity.getAccountName());
transferOrder.setTransferDesc(StringUtils.isNotEmpty(transferDesc)?transferDesc:null);
transferOrderService.save(transferOrder);
} catch (Exception e) {
log.error("资金专项拨入异常!",e);
throw new BizException("资金专项拨入异常!");
}
}
return resultUrl;
}
/**
* 基于记账本代发
*/
@Override
public ChannelRetMsg transUniTransfer(TransferOrder transferOrder) {
log.info("进入了支付宝代发!");
ChannelRetMsg retMsg = ChannelRetMsg.waiting();
CertAlipayRequest certAlipayRequest = getCertAliPayRequest(transferOrder.getIsvNo());
TransferSubjectEntity ersubjectEntity = transferSubjectService.getById(transferOrder.getTransferSubjectIdFq());
TransferSubjectEntity eesubjectEntity = transferSubjectService.getById(transferOrder.getTransferSubjectIdJs());
try {
AlipayClient alipayClient = new DefaultAlipayClient(certAlipayRequest);
AlipayFundTransUniTransferRequest request = new AlipayFundTransUniTransferRequest();
AlipayFundTransUniTransferModel model = new AlipayFundTransUniTransferModel();
model.setOutBizNo(transferOrder.getTransferId());
model.setTransAmount(AmountUtil.convertCent2Dollar(transferOrder.getAmount()));
model.setProductCode("SINGLE_TRANSFER_NO_PWD");
model.setBizScene("ENTRUST_TRANSFER");
model.setOrderTitle(StringUtils.isNotEmpty(transferOrder.getTransferDesc()) ? transferOrder.getTransferDesc() : "支付宝代发到户!");
//付款方信息
Participant participanter = new Participant();
participanter.setIdentity(ersubjectEntity.getTransChannelMchNo());
participanter.setIdentityType("ACCOUNT_BOOK_ID");
JSONObject erparm = new JSONObject();
erparm.put("agreement_no", ersubjectEntity.getTransChannelSignNo());
participanter.setExtInfo(erparm.toJSONString());
model.setPayerInfo(participanter);
//收款方信息
Participant participantee = new Participant();
participantee.setIdentity(eesubjectEntity.getAccount());
if (TransferOrderEntity.ENTRY_ALIPAY_CASH.equals(transferOrder.getEntryType())){
participantee.setIdentityType("ALIPAY_LOGON_ID");
participantee.setName(eesubjectEntity.getAccountName());
}else if (TransferOrderEntity.ENTRY_BANK_CARD.equals(transferOrder.getEntryType())){
participantee.setIdentityType("BANKCARD_ACCOUNT");
BankcardExtInfo bankcardExtInfo = new BankcardExtInfo();
participantee.setName(eesubjectEntity.getAccountName());
if (eesubjectEntity.getAccountType() == TransferBasicInfo.ACCOUNT_TYPE_COR) {
//对公
String accountMessage = eesubjectEntity.getAccountMessage();
if (StringUtils.isNotEmpty(accountMessage)) {
JSONObject jsonObject = JSONObject.parseObject(accountMessage);
bankcardExtInfo.setAccountType("1");
bankcardExtInfo.setInstName(jsonObject.getString("instName"));
bankcardExtInfo.setInstProvince(jsonObject.getString("instProvince"));
bankcardExtInfo.setInstCity(jsonObject.getString("instCity"));
bankcardExtInfo.setInstBranchName(jsonObject.getString("instBranchName"));
participantee.setBankcardExtInfo(bankcardExtInfo);
transferOrder.setBankName(jsonObject.getString("instName"));
}
} else {
//对私
bankcardExtInfo.setAccountType("2");
participantee.setBankcardExtInfo(bankcardExtInfo);
}
}
model.setPayeeInfo(participantee);
request.setBizModel(model);
AlipayFundTransUniTransferResponse response = alipayClient.certificateExecute(request);
retMsg.setChannelAttach(response.getBody());
if (response.isSuccess()) {
retMsg.setChannelOrderId(response.getOrderId());
retMsg.setPlatformOrderNo(response.getPayFundOrderId());
} else {
retMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
retMsg.setChannelErrMsg(response.getSubMsg());
retMsg.setChannelErrCode(response.getSubCode());
}
} catch (Exception e) {
log.info("代发异常!",e);
retMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
retMsg.setChannelErrMsg(e.getMessage());
}
return retMsg;
}
/**
* 账单查询
*/
@Override
public JSONObject bizfundagentQuery(String subId, String startTime, String endTime, String pageNo, String pageSize) {
log.info("进入了支付宝账单查询");
TransferSubjectEntity ersubjectEntity = getSubjectOne(subId);
CertAlipayRequest certAlipayRequest = getCertAliPayRequest(ersubjectEntity.getIsvNo());
JSONObject resut = new JSONObject();
AlipayDataBillBizfundagentQueryResponse response = new AlipayDataBillBizfundagentQueryResponse();
if ( ersubjectEntity.getTransChannelSignNo() == null || ersubjectEntity.getTransChannelMchNo() == null) {
throw new BizException("当前付款人签约或开通记账本!");
}
try {
AlipayClient alipayClient = new DefaultAlipayClient(certAlipayRequest);
AlipayDataBillBizfundagentQueryRequest request = new AlipayDataBillBizfundagentQueryRequest();
JSONObject jsonObject = new JSONObject();
jsonObject.put("start_time", startTime);
jsonObject.put("end_time", endTime);
jsonObject.put("page_no", StringUtils.isEmpty(pageNo) ? "1" : pageNo);
jsonObject.put("page_size", StringUtils.isEmpty(pageSize) ? "2000" : pageSize);
jsonObject.put("agreement_no", ersubjectEntity.getTransChannelSignNo());
jsonObject.put("account_book_id", ersubjectEntity.getTransChannelMchNo());
request.setBizContent(jsonObject.toJSONString());
response = alipayClient.certificateExecute(request);
if (response.isSuccess()) {
log.info("安全发账单查询成功:" + response.getBody());
} else {
log.error("安全发账单查询失败!" + response.getSubMsg());
}
resut = JSONObject.parseObject(response.getBody()).getJSONObject("alipay_data_bill_bizfundagent_query_response");
} catch (Exception e) {
resut.put("code", "99999");
resut.put("subMsg", "安全发账单查询异常!!");
log.error("安全发账单查询异常!",e);
}
return resut;
}
/**
* 申请电子回单
*/
@Override
public JSONObject applicationForm(String subId, String transferId) {
log.info("进入了支付宝申请电子回单");
TransferSubjectEntity ersubjectEntity = getSubjectOne(subId);
CertAlipayRequest certAlipayRequest = getCertAliPayRequest(ersubjectEntity.getIsvNo());
TransferOrderEntity transferOrder = transferOrderService.getById(transferId);
JSONObject resut = new JSONObject();
AlipayDataBillEreceiptagentApplyResponse response;
if (ersubjectEntity.getTransChannelSignNo() == null || transferOrder == null || transferOrder.getFlowNo() == null) {
throw new BizException("当前付款人不符合申请条件!");
}
try {
AlipayClient alipayClient = new DefaultAlipayClient(certAlipayRequest);
AlipayDataBillEreceiptagentApplyRequest request = new AlipayDataBillEreceiptagentApplyRequest();
JSONObject jsonObject = new JSONObject();
jsonObject.put("type", "FUND_DETAIL");
jsonObject.put("key", transferOrder.getFlowNo());
jsonObject.put("agreement_no", ersubjectEntity.getTransChannelSignNo());
request.setBizContent(jsonObject.toJSONString());
response = alipayClient.certificateExecute(request);
if (response.isSuccess()) {
log.info("申请电子回单:" + response.getBody());
transferOrder.setApplicationFormState(1);
transferOrder.setApplicationFileId(response.getFileId());
transferOrderService.updateById(transferOrder);
} else {
log.error("申请电子回单失败!" + response.getSubMsg());
}
resut = JSONObject.parseObject(response.getBody()).getJSONObject("alipay_data_bill_ereceiptagent_apply_response");
} catch (Exception e) {
resut.put("code", "99999");
resut.put("subMsg", "申请电子回单异常!!");
log.error("申请电子回单异常!",e);
}
return resut;
}
/**
* 回单申请状态和下载地址
*/
@Override
public JSONObject applicationFormDownload(String subId, String transferId) {
log.info("进入了支付宝回单申请状态和下载地址");
TransferSubjectEntity ersubjectEntity = getSubjectOne(subId);
CertAlipayRequest certAlipayRequest = getCertAliPayRequest(ersubjectEntity.getIsvNo());
TransferOrderEntity transferOrder = transferOrderService.getById(transferId);
JSONObject resut = new JSONObject();
AlipayDataBillAccountbookereceiptQueryResponse response;
if (ersubjectEntity.getTransChannelSignNo() == null || transferOrder == null || transferOrder.getApplicationFileId() == null) {
throw new BizException("当前付款人不符合申请条件!");
}
try {
AlipayClient alipayClient = new DefaultAlipayClient(certAlipayRequest);
AlipayDataBillAccountbookereceiptQueryRequest request = new AlipayDataBillAccountbookereceiptQueryRequest();
JSONObject jsonObject = new JSONObject();
jsonObject.put("file_id", transferOrder.getApplicationFileId());
jsonObject.put("agreement_no", ersubjectEntity.getTransChannelSignNo());
request.setBizContent(jsonObject.toJSONString());
response = alipayClient.certificateExecute(request);
if (response.isSuccess()) {
log.info("回单申请状态和下载地址返回:" + response.getBody());
} else {
log.error("回单申请状态和下载地址失败!" + response.getSubMsg());
}
resut = JSONObject.parseObject(response.getBody()).getJSONObject("alipay_data_bill_accountbookereceipt_query_response");
} catch (Exception e) {
resut.put("code", "99999");
resut.put("subMsg", "回单申请状态和下载地址异常!!");
log.error("回单申请状态和下载地址异常!",e);
}
return resut;
}
/**
* 生成待发待结算信息
*
* @return
*/
public String settlementData(String ersubId, List<TransferOrder> entityList, String taskId, String entryType, String accountType) {
String resut = "操作成功!";
TransferSubjectEntity transferSubject = transferSubjectService.getById(ersubId);
if (transferSubject.getState() == TransferSubject.STATE_DISABLE) {
throw new BizException("当前付款账户已停用!");
}
if (ObjUtil.isEmpty(entityList)) {
throw new BizException("收款人不能为空!");
}
if (transferSubject.getTransChannelMchNo() == null || transferSubject.getTransChannelSignNo() == null) {
throw new BizException("付款账户暂未签约或开通记账本!");
}
TaskList task = taskListService.getById(taskId);
//先进行手续费账户余额校验
long total = 0;
for (TransferOrder transferOrder : entityList) {
long transAmountInCent = AmountUtil.convertDollar2CentLong(transferOrder.getTransAmount()); // 将金额转换为以分为单位
total += transAmountInCent ;//累加转化后的金额
}
String infoId;
if (CS.SYS_ROLE_TYPE.AGENT.equals(transferSubject.getInfoType())) {
infoId = transferSubject.getAgentNo();
} else {
infoId = transferSubject.getMchNo();
}
accountInfoService.checkBalance(infoId, AccountInfo.Type.SERVICE_CHARGE, total);
Map<String, TransferSubject> map = new HashMap<>();
for (TransferOrder item: entityList) {
TransferSubject itemJsInfo = map.get(item.getTransferSubjectIdJs());
if (itemJsInfo == null) {
itemJsInfo = transferSubjectService.getById(item.getTransferSubjectIdJs());
map.put(item.getTransferSubjectIdJs(), itemJsInfo);
}
// ersubId:付款方id eesubId收款方id
// TODO: 2024/4/30 后续完善参数
TransferOrderEntity transferOrder = transferConverter.toDbEntity(item);
transferOrder.setTransferId("AQF_ZZ_" + SeqKit.genTransOrderId());
transferOrder.setMchNo(transferSubject.getMchNo());
transferOrder.setMchExtNo(transferSubject.getMchApplyId());
transferOrder.setIsvNo(transferSubject.getIsvNo());
transferOrder.setAgentNo(transferSubject.getAgentNo());
transferOrder.setTaskId(taskId);
transferOrder.setTransferSubjectIdFq(ersubId);
transferOrder.setTransferSubjectIdJs(item.getTransferSubjectIdJs());
transferOrder.setAccountType(accountType);
transferOrder.setTaskId(task.getId());
transferOrder.setTaskName(task.getTaskName());
transferOrder.setAccountNo(itemJsInfo.getAccount());
transferOrder.setAccountName(itemJsInfo.getAccountName());
transferOrder.setOriginAccountNo(transferSubject.getAccount());
transferOrder.setOriginAccountName(transferSubject.getAccountName());
Long transAmount = AmountUtil.convertDollar2CentLong(item.getTransAmount());
transferOrder.setIfCode(CS.IF_CODE.ALIAQF);
transferOrder.setAmount(transAmount);
transferOrder.setEntryType(entryType);
Map<String, PaywayFee> paywayFeeMap = rateConfigService.queryPaywayFeeMap(RateConfig.appendInfoByMchApp(transferSubject.getMchNo()), CS.SYS_ROLE_TYPE.MCH, transferSubject.getTransIfCode(), null, null, transferSubject.getIsvNo(), true);
if(paywayFeeMap == null){
throw new BizException("转账费率信息异常");
}
PaywayFee paywayFee = paywayFeeMap.get(transferOrder.getEntryType());
if(paywayFee == null){
throw new BizException("未获取到指定的打款方式的费率信息");
}
MutablePair<String, Long> feeAndSnapshot = paywayFee.calFeeAndSnapshot(transAmount);
transferOrder.setFeeCostType(TransferOrder.COST_TYPE_ACCOUNT);
transferOrder.setMchFeeRate(feeAndSnapshot.left);
transferOrder.setMchOrderFeeAmount(feeAndSnapshot.right);
transferOrder.setTransferDesc(item.getTransferDesc());
if (StringUtils.isNotEmpty(item.getBatchId())) {
transferOrder.setBatchId(item.getBatchId());
}
transferOrder.setTransferType(TransferOrderEntity.TRANSFER_TYPE_DF);
transferOrderService.save(transferOrder);
}
return resut;
}
public CertAlipayRequest getCertAliPayRequest(String isvNo) {
TransferInterfaceConfigEntity transferInterfaceConfigEntity = transferInterfaceConfigService.getTransferConfig(CS.IF_CODE.ALIAQF, isvNo, CS.SYS_ROLE_TYPE.ISV);
Assert.notNull(transferInterfaceConfigEntity, "所选渠道配置不存在");
CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
ChannelCertConfigKitBean channelCertConfigKitBean = SpringBeansUtil.getBean(ChannelCertConfigKitBean.class);
AliaqfIsvParams isvParams = JSONObject.parseObject(transferInterfaceConfigEntity.getTransIfParams(), AliaqfIsvParams.class);
// 支付宝网关
certAlipayRequest.setServerUrl(AlipayConfig.PROD_SERVER_URL);
certAlipayRequest.setAppId(isvParams.getAppId());
certAlipayRequest.setPrivateKey(isvParams.getPrivateKey());
certAlipayRequest.setCertPath(channelCertConfigKitBean.getCertFilePath(isvParams.getAppPublicCert()));
certAlipayRequest.setAlipayPublicCertPath(channelCertConfigKitBean.getCertFilePath(isvParams.getAlipayPublicCert()));
certAlipayRequest.setRootCertPath(channelCertConfigKitBean.getCertFilePath(isvParams.getAlipayRootCert()));
certAlipayRequest.setSignType("RSA2");
certAlipayRequest.setFormat("json");
certAlipayRequest.setCharset("UTF-8");
return certAlipayRequest;
}
/**
* 根据付款人信息id获取详情
*/
public TransferSubjectEntity getSubjectOne(String subId){
TransferSubjectEntity transferSubjectEntity = transferSubjectService.getById(subId);
if (transferSubjectEntity != null){
return transferSubjectEntity;
}else {
throw new BizException("未查询到当前付款人信息!");
}
}
}

View File

@@ -0,0 +1,108 @@
package com.jeequan.jeepay.thirdparty.channel.alipay;
import cn.hutool.core.util.ObjUtil;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.converter.TransferConverter;
import com.jeequan.jeepay.core.beans.RequestKitBean;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.TransferOrder;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.db.entity.TransferOrderEntity;
import com.jeequan.jeepay.service.impl.TransferOrderService;
import com.jeequan.jeepay.thirdparty.channel.AbstractTransferNoticeService;
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.Service;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
/**
* TODO
*
* @author deng
* @since 2024/5/8
*/
@Service
@Slf4j
public class AliaqfTransferNoticeService extends AbstractTransferNoticeService {
@Autowired
private RequestKitBean requestKitBean;
@Autowired
private TransferOrderService transferOrderService;
@Autowired
private TransferConverter transferConverter;
@Override
public String getIfCode() {
return CS.IF_CODE.ALIAQF;
}
@Override
public MutablePair<String, Object> parseParams(HttpServletRequest request, String urlOrderId) {
JSONObject reqParamJSON = requestKitBean.getReqParamJSON();
log.info("【支付宝安全发接口回调】, 原始参数为{}", reqParamJSON);
String input = reqParamJSON.getString("input");
if (ObjUtil.isEmpty(input)) {
return new MutablePair<>();
}
String[] params = input.split("&");
JSONObject decryptParam = new JSONObject();
for (String paramItem : params) {
String[] real = paramItem.split("=");
if (real.length != 2) {
log.info("回调参数格式不正确, {}", paramItem);
return new MutablePair<>();
}
try {
decryptParam.put(URLDecoder.decode(real[0], "UTF-8"),URLDecoder.decode(real[1], "UTF-8"));
} catch (UnsupportedEncodingException e) {
log.error("参数解析异常, {}", real[1], e);
}
}
JSONObject bizContent = decryptParam.getJSONObject("biz_content");
log.info("【支付宝安全发接口回调】, 解密后业务参数为{}", bizContent);
String outBizNo = bizContent.getString("out_biz_no");
return new MutablePair<>(outBizNo, bizContent);
}
@Override
public ChannelRetMsg doNotice(HttpServletRequest request, Object params, TransferOrder transferOrder) {
JSONObject bizContent = ((JSONObject) params);
ChannelRetMsg result = new ChannelRetMsg();
result.setChannelOrderId(bizContent.getString("order_id"));
result.setPlatformOrderNo(bizContent.getString("pay_fund_order_id"));
result.setChannelOriginResponse(bizContent.toJSONString());
switch (bizContent.getString("status")){
case "SUCCESS" :
result.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
break;
case "CLOSED" :
case "FAIL" :
result.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
result.setChannelErrMsg(bizContent.getString("sub_order_fail_reason"));
break;
default:
result.setChannelState(ChannelRetMsg.ChannelState.WAITING);
transferOrder.setState(TransferOrderEntity.STATE_ING);
break;
}
ResponseEntity<Object> ok = ResponseEntity.ok(retOk(null));
result.setResponseEntity(ok);
return result;
}
}

View File

@@ -0,0 +1,174 @@
package com.jeequan.jeepay.thirdparty.channel.alipay;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.text.csv.CsvData;
import cn.hutool.core.text.csv.CsvRow;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alipay.api.domain.AlipayDataDataserviceBillDownloadurlQueryModel;
import com.alipay.api.request.AlipayDataDataserviceBillDownloadurlQueryRequest;
import com.alipay.api.response.AlipayDataDataserviceBillDownloadurlQueryResponse;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.CheckBatch;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.model.bill.ChannelBill;
import com.jeequan.jeepay.core.model.bill.ChannelBillRQ;
import com.jeequan.jeepay.core.model.bill.ChannelBillRS;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.params.alipay.AlipayIsvsubMchParams;
import com.jeequan.jeepay.core.utils.AmountUtil;
import com.jeequan.jeepay.core.utils.ExcelUtil;
import com.jeequan.jeepay.db.entity.CheckChannelBill;
import com.jeequan.jeepay.thirdparty.channel.AbstractBillDownloadService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.MutablePair;
import org.springframework.stereotype.Service;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.zip.ZipEntry;
/*
* 支付宝 对账单下载
*
* @author zx
*
* @date 2021/6/8 17:20
*/
@Slf4j
@Service
public class AlipayBillDownloadService extends AbstractBillDownloadService {
@Override
public String getIfCode() {
return CS.IF_CODE.ALIPAY;
}
@Override
public ChannelBillRS convertStandardBill(ChannelBillRQ channelBillRQ, MchAppConfigContext mchAppConfigContext, Date billDate, String ifCode){
String logPrefix = "【支付宝对账单下载】";
ChannelBillRS channelBillRes = new ChannelBillRS();
AlipayDataDataserviceBillDownloadurlQueryRequest req = new AlipayDataDataserviceBillDownloadurlQueryRequest();
AlipayDataDataserviceBillDownloadurlQueryModel model = new AlipayDataDataserviceBillDownloadurlQueryModel();
req.setBizModel(model);
model.setBillDate(DateUtil.formatDate(billDate));
model.setBillType("trade");
String channelMchNo = channelBillRQ.getChannelMchNo(); // 渠道商户号
// 特约商户, 放置子商户token
if(mchAppConfigContext.isIsvsubMch()){
AlipayIsvsubMchParams isvsubMchParams = (AlipayIsvsubMchParams) channelBillRQ.getIsvsubMchParams();
req.putOtherTextParam("app_auth_token", isvsubMchParams.getAppAuthToken());
}
// 账单文件流
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try {
AlipayDataDataserviceBillDownloadurlQueryResponse resp = configContextQueryService.getAlipayClientWrapper(mchAppConfigContext).execute(req);
log.info("{},返回结果:{}", logPrefix, JSON.toJSONString(resp));
if (!resp.isSuccess()) {
throw new BizException(resp.getCode() + "[" + resp.getMsg() + "]" + resp.getSubCode() + "[" + resp.getSubMsg() + "]");
}
// 下载对账单,写入文件输出流
HttpUtil.download(resp.getBillDownloadUrl(), byteArrayOutputStream, false);
// 上传oss
String url = upload2Oss(ifCode, channelMchNo, billDate, ".zip", IoUtil.toStream(byteArrayOutputStream));
// 生成附带对账文件下载地址的解析成功的批次
CheckBatch checkBatch = genReleaseSuccessCheckBatch(ifCode, billDate, channelMchNo, url);
// 渠道账单列表
List<ChannelBill> channelBillList = new ArrayList<>();
// 渠道总退款金额
long channelTotalRefundAmount = 0;
// 读取对账单 csv文件
List<MutablePair<ZipEntry, CsvData>> mutablePairs = ExcelUtil.readCsvDataByZipInputStream(IoUtil.toStream(byteArrayOutputStream), CharsetUtil.CHARSET_GBK, CharsetUtil.CHARSET_GBK);
for (MutablePair<ZipEntry, CsvData> mutablePair : mutablePairs) {
CsvData data = mutablePair.right;
List<CsvRow> rows = data.getRows();
// 汇总数据
if (mutablePair.left.getName().contains("汇总")) {
if (rows.size() > 1) {
List<String> rawList = rows.get(1).getRawList();
if (rawList.size() > 10) {
checkBatch.setChannelTotalCount(Integer.valueOf(rawList.get(2).trim()));
checkBatch.setChannelTotalRefundCount(Integer.valueOf(rawList.get(3).trim()));
checkBatch.setChannelTotalAmount(AmountUtil.convertDollar2CentLong(rawList.get(4).trim()));
checkBatch.setChannelTotalFee(AmountUtil.convertDollar2CentLong(rawList.get(9).trim()));
}
}
// 明细数据
}else{
// 遍历行
for (int i = 1; i < rows.size(); i++) {
CsvRow csvRow = rows.get(i);
// getRawList返回一个List列表列表的每一项为CSV中的一个单元格
List<String> rawList = csvRow.getRawList();
if (rawList.size() > 22) {
ChannelBill channelBill = new ChannelBill();
channelBill.setBatchNo(checkBatch.getBatchNo());
channelBill.setBillDate(billDate);
channelBill.setIfCode(ifCode);
channelBill.setChannelMchNo(channelMchNo);
channelBill.setChannelOrderNo(rawList.get(0).trim());
channelBill.setChannelAmount(Math.abs(AmountUtil.convertDollar2CentLong(rawList.get(11).trim())));
channelBill.setChannelFeeAmount(Math.abs(AmountUtil.convertDollar2CentLong(rawList.get(22).trim())));
channelBill.setChannelSuccessAt(DateUtil.parseDateTime(rawList.get(5).trim()));
channelBill.setChannelUser(rawList.get(10).trim());
if (StringUtils.equals(rawList.get(2).trim(), "交易")) {
channelBill.setOrderId(rawList.get(1).trim());
channelBill.setBillType(CheckChannelBill.BILL_TYPE_PAY);
channelBill.setChannelState((byte) 2);
}else if (StringUtils.equals(rawList.get(2).trim(), "退款")) {
channelBill.setOrderId(rawList.get(21).trim()); // 平台退款订单号
channelBill.setBillType(CheckChannelBill.BILL_TYPE_REFUND);
channelBill.setChannelState((byte) 5);
channelBill.setOrgPayOrderId(rawList.get(1).trim());
channelTotalRefundAmount += channelBill.getChannelAmount();
}else {
log.error("支付宝账单类型错误!");
continue;
}
channelBillList.add(channelBill);
}
}
}
}
checkBatch.setChannelTotalRefundAmount(channelTotalRefundAmount);
checkBatch.setChannelTotalAmount(checkBatch.getChannelTotalAmount() + Math.abs(channelTotalRefundAmount));
channelBillRes.setCheckBatch(checkBatch);
channelBillRes.setChannelBillList(channelBillList);
return channelBillRes;
}catch (Exception e) {
log.error("{}, 解析对账文件失败,", logPrefix, e);
channelBillRes.setCheckBatch(genReleaseFailCheckBatch(ifCode, billDate, channelMchNo, e.getMessage()));
return channelBillRes;
}finally {
IoUtil.close(byteArrayOutputStream);
}
}
}

View File

@@ -0,0 +1,128 @@
package com.jeequan.jeepay.thirdparty.channel.alipay;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.internal.util.AlipaySignature;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.exception.ResponseException;
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.model.params.alipay.AlipayNormalMchParams;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.thirdparty.channel.AbstractChannelNoticeService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.MutablePair;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
/*
* 支付宝 回调接口实现类
*
* @author terrfly
*
* @date 2021/6/8 17:20
*/
@Service
@Slf4j
public class AlipayChannelNoticeService extends AbstractChannelNoticeService {
@Override
public String getIfCode() {
return CS.IF_CODE.ALIPAY;
}
@Override
public MutablePair<String, Object> parseParams(HttpServletRequest request, String urlOrderId, NoticeTypeEnum noticeTypeEnum) {
try {
JSONObject params = getReqParamJSON();
String payOrderId = params.getString("out_trade_no");
return MutablePair.of(payOrderId, params);
} catch (Exception e) {
log.error("error", e);
throw ResponseException.buildText("ERROR");
}
}
@Override
public ChannelRetMsg doNotice(HttpServletRequest request, Object params, PayOrder payOrder, MchAppConfigContext mchAppConfigContext, NoticeTypeEnum noticeTypeEnum) {
try {
//配置参数获取
Byte useCert = null;
String alipaySignType, alipayPublicCert, alipayPublicKey = null;
if(mchAppConfigContext.isIsvsubMch()){
// 获取支付参数
AlipayIsvParams alipayParams = (AlipayIsvParams) configContextQueryService.queryIsvParams(mchAppConfigContext.getMchApplyment().getIsvNo(), getIfCode());
useCert = alipayParams.getUseCert();
alipaySignType = alipayParams.getSignType();
alipayPublicCert = alipayParams.getAlipayPublicCert();
alipayPublicKey = alipayParams.getAlipayPublicKey();
}else{
// 获取支付参数
AlipayNormalMchParams alipayParams = (AlipayNormalMchParams)configContextQueryService.queryNormalMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), getIfCode());
useCert = alipayParams.getUseCert();
alipaySignType = alipayParams.getSignType();
alipayPublicCert = alipayParams.getAlipayPublicCert();
alipayPublicKey = alipayParams.getAlipayPublicKey();
}
// 获取请求参数
JSONObject jsonParams = (JSONObject) params;
boolean verifyResult;
if(useCert != null && useCert == CS.YES){ //证书方式
verifyResult = AlipaySignature.rsaCertCheckV1(jsonParams.toJavaObject(Map.class), getCertFilePath(alipayPublicCert),
AlipayConfig.CHARSET, alipaySignType);
}else{
verifyResult = AlipaySignature.rsaCheckV1(jsonParams.toJavaObject(Map.class), alipayPublicKey, AlipayConfig.CHARSET, alipaySignType);
}
//验签失败
if(!verifyResult){
throw ResponseException.buildText("ERROR");
}
//验签成功后判断上游订单状态
ResponseEntity okResponse = textResp("SUCCESS");
ChannelRetMsg result = new ChannelRetMsg();
result.setChannelOrderId(jsonParams.getString("trade_no")); //渠道订单号
result.setPlatformOrderNo(jsonParams.getString("trade_no"));
result.setPlatformMchOrderNo(payOrder.getPayOrderId());
result.setChannelUserId(jsonParams.getString("buyer_id")); //支付用户ID
result.setResponseEntity(okResponse); //响应数据
result.setChannelBizData(jsonParams);
result.setChannelState(ChannelRetMsg.ChannelState.WAITING); // 默认支付中
if("TRADE_SUCCESS".equals(jsonParams.getString("trade_status"))){
result.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
}else if("TRADE_CLOSED".equals(jsonParams.getString("trade_status"))){
result.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
}
return result;
} catch (Exception e) {
log.error("error", e);
throw ResponseException.buildText("ERROR");
}
}
}

View File

@@ -0,0 +1,214 @@
package com.jeequan.jeepay.thirdparty.channel.alipay;
import com.alibaba.fastjson.JSON;
import com.alipay.api.domain.*;
import com.alipay.api.request.AlipayTradeOrderSettleRequest;
import com.alipay.api.request.AlipayTradeRoyaltyRelationBindRequest;
import com.alipay.api.response.AlipayTradeOrderSettleResponse;
import com.alipay.api.response.AlipayTradeRoyaltyRelationBindResponse;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.*;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.exception.ChannelException;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.utils.AmountUtil;
import com.jeequan.jeepay.core.utils.RegKit;
import com.jeequan.jeepay.core.utils.SeqKit;
import com.jeequan.jeepay.thirdparty.channel.AbstractDivisionService;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 分账接口: 支付宝官方
*
* @author terrfly
*
* @date 2021/8/22 09:05
*/
@Slf4j
@Service
public class AlipayDivisionService extends AbstractDivisionService {
@Autowired private ConfigContextQueryService configContextQueryService;
@Override
public boolean divisionRefundIsOrderRefundAfterProc() {
return true; // 事后分账回退
}
// 分账回退, 默认成功。
@Override
public ChannelRetMsg divisionRefund(PayOrderDivisionRecord payOrderDivisionRecord, PayOrderDivisionRefundRecord payOrderDivisionRefundRecord, RefundOrder refundOrder, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) {
return ChannelRetMsg.confirmSuccess(null);
}
@Override
public String getIfCode() {
return CS.IF_CODE.ALIPAY;
}
@Override
public boolean isSupport() {
return false;
}
@Override
public ChannelRetMsg bind(MchDivisionReceiver mchDivisionReceiver, MchAppConfigContext mchAppConfigContext) {
try {
AlipayTradeRoyaltyRelationBindRequest request = new AlipayTradeRoyaltyRelationBindRequest();
AlipayTradeRoyaltyRelationBindModel model = new AlipayTradeRoyaltyRelationBindModel();
request.setBizModel(model);
model.setOutRequestNo(SeqKit.genDivisionBatchId());
//统一放置 isv接口必传信息
AlipayKit.putApiIsvInfo(mchAppConfigContext, request, model);
RoyaltyEntity royaltyEntity = new RoyaltyEntity();
royaltyEntity.setType("loginName");
if(RegKit.isAlipayUserId(mchDivisionReceiver.getAccNo())){
royaltyEntity.setType("userId");
}
royaltyEntity.setAccount(mchDivisionReceiver.getAccNo());
royaltyEntity.setName(mchDivisionReceiver.getAccName());
royaltyEntity.setMemo(mchDivisionReceiver.getRelationTypeName()); //分账关系描述
model.setReceiverList(Arrays.asList(royaltyEntity));
AlipayTradeRoyaltyRelationBindResponse alipayResp = configContextQueryService.getAlipayClientWrapper(mchAppConfigContext).execute(request);
if(alipayResp.isSuccess()){
return ChannelRetMsg.confirmSuccess(null);
}
//异常:
ChannelRetMsg channelRetMsg = ChannelRetMsg.confirmFail();
channelRetMsg.setChannelErrCode(AlipayKit.appendErrCode(alipayResp.getCode(), alipayResp.getSubCode()));
channelRetMsg.setChannelErrMsg(AlipayKit.appendErrMsg(alipayResp.getMsg(), alipayResp.getSubMsg()));
return channelRetMsg;
} catch (ChannelException e) {
ChannelRetMsg channelRetMsg = ChannelRetMsg.confirmFail();
channelRetMsg.setChannelErrCode(e.getChannelRetMsg().getChannelErrCode());
channelRetMsg.setChannelErrMsg(e.getChannelRetMsg().getChannelErrMsg());
return channelRetMsg;
} catch (Exception e) {
log.error("绑定支付宝账号异常", e);
ChannelRetMsg channelRetMsg = ChannelRetMsg.confirmFail();
channelRetMsg.setChannelErrMsg(e.getMessage());
return channelRetMsg;
}
}
@Override
public ChannelRetMsg singleDivision(PayOrder payOrder, List<PayOrderDivisionRecord> recordList, MchAppConfigContext mchAppConfigContext) {
try {
if(recordList.isEmpty()){ // 当无分账用户时, 支付宝不允许发起分账请求, 支付宝没有完结接口,直接响应成功即可。
return ChannelRetMsg.confirmSuccess(null);
}
AlipayTradeOrderSettleRequest request = new AlipayTradeOrderSettleRequest();
AlipayTradeOrderSettleModel model = new AlipayTradeOrderSettleModel();
request.setBizModel(model);
model.setOutRequestNo(recordList.get(0).getBatchOrderId()); //结算请求流水号由商家自定义。32个字符以内仅可包含字母、数字、下划线。需保证在商户端不重复。
model.setTradeNo(recordList.get(0).getPayOrderChannelOrderNo()); //支付宝订单号
//统一放置 isv接口必传信息
AlipayKit.putApiIsvInfo(mchAppConfigContext, request, model);
List<OpenApiRoyaltyDetailInfoPojo> reqReceiverList = new ArrayList<>();
for (int i = 0; i < recordList.size(); i++) {
PayOrderDivisionRecord record = recordList.get(i);
if(record.getCalDivisionAmount() <= 0){ //金额为 0 不参与分账处理
continue;
}
OpenApiRoyaltyDetailInfoPojo reqReceiver = new OpenApiRoyaltyDetailInfoPojo();
reqReceiver.setRoyaltyType("transfer"); //分账类型: 普通分账
// 出款信息
// reqReceiver.setTransOutType("loginName"); reqReceiver.setTransOut("xqxemt4735@sandbox.com");
// 入款信息
reqReceiver.setTransIn(record.getAccNo()); //收入方账号
reqReceiver.setTransInType("loginName");
if(RegKit.isAlipayUserId(record.getAccNo())){
reqReceiver.setTransInType("userId");
}
// 分账金额
reqReceiver.setAmount(AmountUtil.convertCent2Dollar(record.getCalDivisionAmount()));
reqReceiver.setDesc("[" + payOrder.getPayOrderId() + "]订单分账");
reqReceiverList.add(reqReceiver);
}
if(reqReceiverList.isEmpty()){ // 当无分账用户时, 支付宝不允许发起分账请求, 支付宝没有完结接口,直接响应成功即可。
return ChannelRetMsg.confirmSuccess(null);
}
model.setRoyaltyParameters(reqReceiverList); // 分账明细信息
// 完结
SettleExtendParams settleExtendParams = new SettleExtendParams();
settleExtendParams.setRoyaltyFinish("true");
model.setExtendParams(settleExtendParams);
//调起支付宝分账接口
if(log.isInfoEnabled()){
log.info("订单:[{}], 支付宝分账请求:{}", payOrder.getPayOrderId(), JSON.toJSONString(model));
}
AlipayTradeOrderSettleResponse alipayResp = configContextQueryService.getAlipayClientWrapper(mchAppConfigContext).execute(request);
log.info("订单:[{}], 支付宝分账响应:{}", payOrder.getPayOrderId(), alipayResp.getBody());
if(alipayResp.isSuccess()){
return ChannelRetMsg.confirmSuccess(alipayResp.getTradeNo());
}
//异常:
ChannelRetMsg channelRetMsg = ChannelRetMsg.confirmFail();
channelRetMsg.setChannelErrCode(AlipayKit.appendErrCode(alipayResp.getCode(), alipayResp.getSubCode()));
channelRetMsg.setChannelErrMsg(AlipayKit.appendErrMsg(alipayResp.getMsg(), alipayResp.getSubMsg()));
return channelRetMsg;
} catch (ChannelException e) {
ChannelRetMsg channelRetMsg = ChannelRetMsg.confirmFail();
channelRetMsg.setChannelErrCode(e.getChannelRetMsg().getChannelErrCode());
channelRetMsg.setChannelErrMsg(e.getChannelRetMsg().getChannelErrMsg());
return channelRetMsg;
} catch (Exception e) {
log.error("绑定支付宝账号异常", e);
ChannelRetMsg channelRetMsg = ChannelRetMsg.confirmFail();
channelRetMsg.setChannelErrMsg(e.getMessage());
return channelRetMsg;
}
}
@Override
public Long queryBalanceAmount(MchDivisionReceiver mchDivisionReceiver, MchAppConfigContext mchAppConfigContext) {
throw new BizException("接口不支持");
}
@Override
public ChannelRetMsg cashout(MchDivisionReceiver mchDivisionReceiver, Long amount, MchAppConfigContext mchAppConfigContext) {
throw new BizException("接口不支持");
}
}

View File

@@ -0,0 +1,100 @@
package com.jeequan.jeepay.thirdparty.channel.alipay;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.domain.AlipayCommerceIotDeviceBindModel;
import com.alipay.api.domain.AlipayCommerceIotDeviceBindQueryModel;
import com.alipay.api.domain.AlipayCommerceIotDeviceUnbindModel;
import com.alipay.api.request.AlipayCommerceIotDeviceBindQueryRequest;
import com.alipay.api.request.AlipayCommerceIotDeviceBindRequest;
import com.alipay.api.request.AlipayCommerceIotDeviceUnbindRequest;
import com.alipay.api.response.AlipayCommerceIotDeviceBindQueryResponse;
import com.alipay.api.response.AlipayCommerceIotDeviceBindResponse;
import com.alipay.api.response.AlipayCommerceIotDeviceUnbindResponse;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.interfaces.paychannel.alipaybiz.IAlipayIotDeviceBindService;
import com.jeequan.jeepay.core.model.params.alipay.AlipayIsvParams;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/*
* 支付宝Iot设备接口
*
* @author zx
*
* @date 2023/3/8 15:13
*/
@Slf4j
@Service
public class AlipayIotDeviceBindService implements IAlipayIotDeviceBindService {
@Override
public void bind(AlipayIsvParams alipayIsvParams, String mchNo, String alipayMerchantNo, String alipayShopId, String supplierId, JSONObject deviceJSON) {
AlipayCommerceIotDeviceBindRequest request = new AlipayCommerceIotDeviceBindRequest();
AlipayCommerceIotDeviceBindModel model = new AlipayCommerceIotDeviceBindModel();
model.setAppType("MINI_APP");
model.setMiniAppId("RUYI_LITE");
model.setDeviceIdType("SN");
model.setDeviceSn(deviceJSON.getString("deviceNo"));
model.setSupplierId(supplierId); // 设备供应商ID
model.setExternalId(mchNo); // jeepay商户号
model.setMerchantIdType("direct");
model.setMerchantId(alipayMerchantNo);
model.setShopId(alipayShopId);
model.setSource(alipayIsvParams.getPid()); // 受理商户的ISV在支付宝的pid
model.setSpiAppId(alipayIsvParams.getAppId()); // 表示ISV在开放平台注册的SPI服务应用的app_id
model.setTerminalBindInfo(deviceJSON.toJSONString()); // 由ISV自定义的扩展字段在支付宝侧向SPI服务发起请求时透传给ISV
request.setBizModel(model);
AlipayCommerceIotDeviceBindResponse response = AlipayKit.getAlipayClientWrapper(alipayIsvParams).getAlipayClient().execute(request);
log.info("alipayIot设备绑定响应deviceNo={}response={}", deviceJSON.getString("deviceNo"), JSONObject.toJSONString(response));
if (!response.isSuccess()) {
throw new BizException(AlipayKit.appendErrMsg(response.getMsg(), response.getSubMsg()));
}
}
@Override
public void unbind(AlipayIsvParams alipayIsvParams, String mchNo, String alipayMerchantNo, String alipayShopId, String supplierId, String deviceSn){
AlipayCommerceIotDeviceUnbindRequest request = new AlipayCommerceIotDeviceUnbindRequest();
AlipayCommerceIotDeviceUnbindModel model = new AlipayCommerceIotDeviceUnbindModel();
model.setAppType("MINI_APP");
model.setMiniAppId("RUYI_LITE");
model.setDeviceIdType("SN");
model.setDeviceSn(deviceSn);
model.setSupplierId(supplierId);
model.setSource(alipayIsvParams.getPid());
model.setExternalId(mchNo); // jeepay商户号 1678169795992
model.setMerchantIdType("direct");
model.setMerchantId(alipayMerchantNo);
model.setShopId(alipayShopId);
request.setBizModel(model);
AlipayCommerceIotDeviceUnbindResponse response = AlipayKit.getAlipayClientWrapper(alipayIsvParams).getAlipayClient().execute(request);
log.info("alipayIot设备解绑响应deviceNo{}response={}", deviceSn, JSONObject.toJSONString(response));
if ("NOT_BIND".equals(response.getSubCode())) {
return;
}
if (!response.isSuccess()) {
throw new BizException(AlipayKit.appendErrMsg(response.getMsg(), response.getSubMsg()));
}
}
@Override
public JSONObject query(AlipayIsvParams alipayIsvParams, String deviceSn){
AlipayCommerceIotDeviceBindQueryRequest request = new AlipayCommerceIotDeviceBindQueryRequest();
AlipayCommerceIotDeviceBindQueryModel model = new AlipayCommerceIotDeviceBindQueryModel();
model.setAppType("MINI_APP");
model.setMiniAppId("RUYI_LITE");
model.setDeviceIdType("SN");
model.setDeviceSn(deviceSn);
request.setBizModel(model);
AlipayCommerceIotDeviceBindQueryResponse response = AlipayKit.getAlipayClientWrapper(alipayIsvParams).getAlipayClient().execute(request);
log.info("alipayIot设备查询响应deviceNo{}response{}", deviceSn, JSONObject.toJSONString(response));
if (!response.isSuccess()) {
throw new BizException(AlipayKit.appendErrMsg(response.getMsg(), response.getSubMsg()));
}
return (JSONObject) JSONObject.toJSON(response);
}
}

View File

@@ -0,0 +1,108 @@
package com.jeequan.jeepay.thirdparty.channel.alipay;
import cn.hutool.core.text.CharSequenceUtil;
import com.alipay.api.AlipayObject;
import com.alipay.api.AlipayRequest;
import com.alipay.api.domain.*;
import com.alipay.api.request.*;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.params.alipay.AlipayIsvParams;
import com.jeequan.jeepay.core.model.params.alipay.AlipayIsvsubMchParams;
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
import com.jeequan.jeepay.thirdparty.model.AlipayClientWrapper;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import org.apache.commons.lang3.StringUtils;
/*
* 【支付宝】支付通道工具包
*
* @author terrfly
*
* @date 2021/6/8 17:19
*/
public class AlipayKit {
/** 放置 isv特殊信息 **/
public static void putApiIsvInfo(MchAppConfigContext mchAppConfigContext, AlipayRequest req, AlipayObject model){
//不是特约商户, 无需放置此值
if(!mchAppConfigContext.isIsvsubMch()){
return ;
}
ConfigContextQueryService configContextQueryService = SpringBeansUtil.getBean(ConfigContextQueryService.class);
// 获取支付参数
AlipayIsvParams isvParams = (AlipayIsvParams) configContextQueryService.queryIsvParams(mchAppConfigContext.getMchApplyment().getIsvNo(), CS.IF_CODE.ALIPAY);
AlipayIsvsubMchParams isvsubMchParams = (AlipayIsvsubMchParams)configContextQueryService.queryIsvsubMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), CS.IF_CODE.ALIPAY);
// 子商户信息
if(req instanceof AlipayTradePayRequest) {
((AlipayTradePayRequest)req).putOtherTextParam("app_auth_token", isvsubMchParams.getAppAuthToken());
} else if(req instanceof AlipayTradeAppPayRequest) {
((AlipayTradeAppPayRequest)req).putOtherTextParam("app_auth_token", isvsubMchParams.getAppAuthToken());
} else if(req instanceof AlipayTradeCreateRequest) {
((AlipayTradeCreateRequest)req).putOtherTextParam("app_auth_token", isvsubMchParams.getAppAuthToken());
} else if(req instanceof AlipayTradePagePayRequest) {
((AlipayTradePagePayRequest)req).putOtherTextParam("app_auth_token", isvsubMchParams.getAppAuthToken());
} else if(req instanceof AlipayTradePrecreateRequest) {
((AlipayTradePrecreateRequest)req).putOtherTextParam("app_auth_token", isvsubMchParams.getAppAuthToken());
} else if(req instanceof AlipayTradeWapPayRequest) {
((AlipayTradeWapPayRequest)req).putOtherTextParam("app_auth_token", isvsubMchParams.getAppAuthToken());
} else if(req instanceof AlipayTradeQueryRequest) {
((AlipayTradeQueryRequest)req).putOtherTextParam("app_auth_token", isvsubMchParams.getAppAuthToken());
} else if(req instanceof AlipayTradeRefundRequest) {
((AlipayTradeRefundRequest)req).putOtherTextParam("app_auth_token", isvsubMchParams.getAppAuthToken());
} else if(req instanceof AlipayTradeFastpayRefundQueryRequest) {
((AlipayTradeFastpayRefundQueryRequest)req).putOtherTextParam("app_auth_token", isvsubMchParams.getAppAuthToken());
} else if(req instanceof AlipayFundTransToaccountTransferRequest) {
((AlipayFundTransToaccountTransferRequest)req).putOtherTextParam("app_auth_token", isvsubMchParams.getAppAuthToken());
} else if(req instanceof AlipayTradeRoyaltyRelationBindRequest) {
((AlipayTradeRoyaltyRelationBindRequest)req).putOtherTextParam("app_auth_token", isvsubMchParams.getAppAuthToken());
} else if(req instanceof AlipayTradeOrderSettleRequest) {
((AlipayTradeOrderSettleRequest)req).putOtherTextParam("app_auth_token", isvsubMchParams.getAppAuthToken());
} else if(req instanceof AlipayDataDataserviceBillDownloadurlQueryRequest) {
((AlipayDataDataserviceBillDownloadurlQueryRequest)req).putOtherTextParam("app_auth_token", isvsubMchParams.getAppAuthToken());
}
// 服务商信息
ExtendParams extendParams = new ExtendParams();
extendParams.setSysServiceProviderId(isvParams.getPid());
if(model instanceof AlipayTradePayModel) {
((AlipayTradePayModel)model).setExtendParams(extendParams);
} else if(model instanceof AlipayTradeAppPayModel) {
((AlipayTradeAppPayModel)model).setExtendParams(extendParams);
} else if(model instanceof AlipayTradeCreateModel) {
((AlipayTradeCreateModel)model).setExtendParams(extendParams);
} else if(model instanceof AlipayTradePagePayModel) {
((AlipayTradePagePayModel)model).setExtendParams(extendParams);
} else if(model instanceof AlipayTradePrecreateModel) {
((AlipayTradePrecreateModel)model).setExtendParams(extendParams);
} else if(model instanceof AlipayTradeWapPayModel) {
((AlipayTradeWapPayModel)model).setExtendParams(extendParams);
}
}
public static AlipayClientWrapper getAlipayClientWrapper(AlipayIsvParams alipayIsvParams) {
return AlipayClientWrapper.buildAlipayClientWrapper(alipayIsvParams, null);
}
public static String appendErrCode(String code, String subCode){
return StringUtils.defaultIfEmpty(subCode, code); //优先: subCode
}
public static String appendErrMsg(String msg, String subMsg){
String result = null;
if(StringUtils.isNotEmpty(msg) && StringUtils.isNotEmpty(subMsg) ){
result = msg + "" + subMsg + "";
}else{
result = StringUtils.defaultIfEmpty(subMsg, msg);
}
return CharSequenceUtil.maxLength(result, 253);
}
}

View File

@@ -0,0 +1,299 @@
package com.jeequan.jeepay.thirdparty.channel.alipay;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.FileItem;
import com.alipay.api.domain.AlipayOpenAgentConfirmModel;
import com.alipay.api.domain.AlipayOpenAgentCreateModel;
import com.alipay.api.domain.ContactModel;
import com.alipay.api.domain.SignAddressInfo;
import com.alipay.api.request.AlipayOpenAgentConfirmRequest;
import com.alipay.api.request.AlipayOpenAgentCreateRequest;
import com.alipay.api.request.AlipayOpenAgentFacetofaceSignRequest;
import com.alipay.api.request.AlipayOpenAgentOrderQueryRequest;
import com.alipay.api.response.AlipayOpenAgentConfirmResponse;
import com.alipay.api.response.AlipayOpenAgentCreateResponse;
import com.alipay.api.response.AlipayOpenAgentFacetofaceSignResponse;
import com.alipay.api.response.AlipayOpenAgentOrderQueryResponse;
import com.jeequan.jeepay.core.entity.MchApplyment;
import com.jeequan.jeepay.core.entity.MchInfo;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.interfaces.paychannel.IIsvmchApplymentService;
import com.jeequan.jeepay.core.model.applyment.AlipayApplymentInfo;
import com.jeequan.jeepay.core.model.applyment.ApplymentSignInfo;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.params.alipay.AlipayIsvsubMchParams;
import com.jeequan.jeepay.service.impl.MchInfoService;
import com.jeequan.jeepay.service.impl.SysConfigService;
import com.jeequan.jeepay.thirdparty.model.AlipayClientWrapper;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.net.URL;
/**
* 支付宝接口进件
*
* @author xiaoyu
*
* @date 2022/1/17 9:58
*/
@Service
@Slf4j
public class AlipayMchApplymentService implements IIsvmchApplymentService<AlipayApplymentInfo> {
@Autowired private ConfigContextQueryService configContextQueryService;
@Autowired protected SysConfigService sysConfigService;
@Autowired protected MchInfoService mchInfoService;
@Override
public MchApplyment firstApplyment(MchApplyment mchApplyment) {
MchApplyment result = new MchApplyment();
// 发起商户是否为特约商户
if (StringUtils.isEmpty(mchApplyment.getIsvNo())) {
throw new BizException("当前商户非特约商户");
}
// 查询当前商户信息
MchInfo mchInfo = mchInfoService.getById(mchApplyment.getMchNo());
MchAppConfigContext context = new MchAppConfigContext();
context.setMchInfo(mchInfo);
context.setMchType(mchInfo.getType());
AlipayClientWrapper alipayClientWrapper = configContextQueryService.getAlipayClientWrapper(context);
if (alipayClientWrapper == null) {
throw new BizException("当前服务商未配置支付宝参数");
}
// 获取详细参数
AlipayApplymentInfo info = JSON.parseObject(mchApplyment.getApplyDetailInfo(), AlipayApplymentInfo.class);
/* 发起进件 */
if (AlipayApplymentInfo.PRODUCT_TYPE_OFFLINE.equals(info.getProductType())) { // 当面付
result = toApplyOffline(info, alipayClientWrapper);
}else {
result.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
result.setApplyErrorInfo("尚未支持该产品");
}
result.setApplyDetailInfo(JSON.toJSONString(info)); //重新更新全量参数包含imgCache
return result;
}
@Override
public MchApplyment rejectModify(MchApplyment mchApplyment) {
return firstApplyment(mchApplyment);
}
@Override
public MchApplyment replenishInfo(MchApplyment mchApplyment) {
return null;
}
@Override
public MchApplyment query(MchApplyment mchApplyment) {
// 查询当前商户信息
MchInfo mchInfo = mchInfoService.getById(mchApplyment.getMchNo());
MchAppConfigContext context = new MchAppConfigContext();
context.setMchInfo(mchInfo);
context.setMchType(mchInfo.getType());
AlipayClientWrapper alipayClientWrapper = configContextQueryService.getAlipayClientWrapper(context);
if (alipayClientWrapper == null) {
throw new BizException("当前服务商未配置支付宝参数");
}
// 调用查询接口
AlipayOpenAgentOrderQueryRequest queryRequest = new AlipayOpenAgentOrderQueryRequest();
// 调用进件接口
JSONObject reqJSON = new JSONObject();
reqJSON.put("batch_no", mchApplyment.getChannelApplyNo()); // 接入方商户号+申请编号 唯一
queryRequest.setBizContent(reqJSON.toJSONString());
log.info("请求支付宝进件查询接口参数: req={}", reqJSON);
AlipayOpenAgentOrderQueryResponse queryResponse = alipayClientWrapper.execute(queryRequest);
log.info("请求支付宝查询接口 code={}, msg={}, subCode={}, subMsg={}", queryResponse.getCode(), queryResponse.getMsg(), queryResponse.getSubCode(), queryResponse.getSubMsg());
// 创建返回对象
MchApplyment result = new MchApplyment();
// 业务异常
if(!queryResponse.isSuccess()){
result.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
result.setApplyErrorInfo(queryResponse.getCode() + "[" + queryResponse.getMsg() + "]" + queryResponse.getSubCode() + "[" + queryResponse.getSubMsg() + "]");
return result;
}
// 审核状态
if(queryResponse.isSuccess()){
if ("MERCHANT_CONFIRM".equals(queryResponse.getOrderStatus())) { // 待商户确认
result.setState(MchApplyment.STATE_WAIT_SIGN);
// 放置请求ID
result.setChannelApplyNo(queryResponse.getAgentAppId());
// 商户签约地址
result.setApplyErrorInfo(queryResponse.getConfirmUrl());
}else if ("MERCHANT_CONFIRM_SUCCESS".equals(queryResponse.getOrderStatus())) { // 商户确认成功
result.setState(MchApplyment.STATE_SUCCESS);
}else if ("MERCHANT_AUDITING".equals(queryResponse.getOrderStatus())) { // 审核中
result.setState(MchApplyment.STATE_AUDITING);
}else { // 异常失败
result.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
result.setApplyErrorInfo(queryResponse.getCode() + "[" + queryResponse.getMsg() + "]" + queryResponse.getSubCode() + "[" + queryResponse.getSubMsg() + "]" + "[" + queryResponse.getRejectReason() + "]");
}
// 封装支付参数
AlipayIsvsubMchParams mchParams = new AlipayIsvsubMchParams();
mchParams.setAppAuthToken(queryResponse.getMerchantPid());
result.setSuccResParameter(JSON.toJSONString(mchParams));
return result;
}else{
// 审核中
result.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
return result;
}
}
@Override
public ApplymentSignInfo signInfo(MchApplyment mchApplyment) {
return null;
}
private MchApplyment toApplyOffline(AlipayApplymentInfo info, AlipayClientWrapper alipayClientWrapper) {
// 创建返回对象
MchApplyment result = new MchApplyment();
String batchNo = "";
try {
/* 1.开启事务 */
AlipayOpenAgentCreateRequest openRequest = new AlipayOpenAgentCreateRequest();
AlipayOpenAgentCreateModel openModel = new AlipayOpenAgentCreateModel();
ContactModel contactModel = new ContactModel();
contactModel.setContactName(info.getContactName()); // 联系人名称
contactModel.setContactMobile(info.getContactPhone()); // 联系人手机号码
contactModel.setContactEmail(info.getContactEmail()); // 联系人邮箱
openModel.setContactInfo(contactModel);
openModel.setAccount(info.getAccountNo());
openRequest.setBizModel(openModel);
AlipayOpenAgentCreateResponse response = alipayClientWrapper.execute(openRequest);
log.info("【1】开启代商户签约开启事务:code={}, msg={}", response.getCode(), response.getMsg());
if (!response.isSuccess()) {
result.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
result.setApplyErrorInfo(response.getCode() + "[" + response.getMsg() + "]" + response.getSubCode() + "[" + response.getSubMsg() + "]");
log.error("支付宝【当面付】进件开启事务失败msg={}, subMsg={}", response.getMsg(), response.getSubMsg());
return result;
}
if (!"init".equals(response.getBatchStatus())) {
result.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
result.setApplyErrorInfo(response.getCode() + "[" + response.getMsg() + "]" + response.getSubCode() + "[" + response.getSubMsg() + "]");
log.error("支付宝【当面付】进件batchStatus状态不等于init提交事务失败");
return result;
}
batchNo = response.getBatchNo();
// 放置请求ID
result.setChannelApplyNo(batchNo);
/* 2.发起待签约 */
AlipayOpenAgentFacetofaceSignRequest offRequest = new AlipayOpenAgentFacetofaceSignRequest();
offRequest.setBatchNo(batchNo); // 代商户操作事务编号
offRequest.setMccCode(info.getMccCode()); // 所属经营类目编码
// 特殊资质证书
if (StringUtils.isNotEmpty(info.getSpecialLicenseImg())) {
//下载文件 & 转换为byte数组格式
byte[] imgFileByteArray = IOUtils.toByteArray(new URL(info.getSpecialLicenseImg()));
offRequest.setSpecialLicensePic(new FileItem("1.png", imgFileByteArray));
}
// 服务费率
offRequest.setRate(info.convertPaywayFeeList2String());
// 营业执照编号
offRequest.setBusinessLicenseNo(info.getLicenseNo());
// 营业执照照片
if (StringUtils.isNotEmpty(info.getLicenseImg())) {
//下载文件 & 转换为byte数组格式
byte[] imgFileByteArray = IOUtils.toByteArray(new URL(info.getLicenseImg()));
offRequest.setBusinessLicensePic(new FileItem("1.png", imgFileByteArray));
}
// 营业期限
if (StringUtils.isNotEmpty(info.getLicenseEffectEnd())) {
if ("长期".equals(info.getLicenseEffectEnd())) {
offRequest.setLongTerm(true);
}else {
offRequest.setDateLimitation(info.getLicenseEffectEnd());
}
}
// 店铺门头照
if (StringUtils.isNotEmpty(info.getStoreOuterImg())) {
//下载文件 & 转换为byte数组格式
byte[] imgFileByteArray = IOUtils.toByteArray(new URL(info.getStoreOuterImg()));
offRequest.setShopSignBoardPic(new FileItem("1.png", imgFileByteArray));
}
// 门店名称
offRequest.setShopName(info.getMchFullName());
// 店铺内景照
if (StringUtils.isNotEmpty(info.getStoreInnerImg())) {
//下载文件 & 转换为byte数组格式
byte[] imgFileByteArray = IOUtils.toByteArray(new URL(info.getStoreInnerImg()));
offRequest.setShopScenePic(new FileItem("1.png", imgFileByteArray));
}
// 营业执照法人手机号
offRequest.setBusinessLicenseMobile(info.getContactPhone());
// 店铺地址
SignAddressInfo addressInfo = new SignAddressInfo();
addressInfo.setCountryCode("156");
addressInfo.setProvinceCode(info.getAreaCode().getString(0)); // 省编码
addressInfo.setCityCode(info.getAreaCode().getString(1)); // 市编码
addressInfo.setDistrictCode(info.getAreaCode().getString(2)); // 区编码
addressInfo.setDetailAddress(info.getAddress()); // 详细地址
offRequest.setShopAddress(addressInfo);
AlipayOpenAgentFacetofaceSignResponse signResponse = alipayClientWrapper.execute(offRequest);
log.info("【2】待签约当面付code={}, msg={}, subCode={}, subMsg={}", signResponse.getCode(), signResponse.getMsg(), signResponse.getSubCode(), signResponse.getSubMsg());
if (!signResponse.isSuccess()) {
result.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
result.setApplyErrorInfo(signResponse.getCode() + "[" + signResponse.getMsg() + "]" + signResponse.getSubCode() + "[" + signResponse.getSubMsg() + "]");
log.error("发起待签约失败code={}, msg={}, subCode={}, subMsg={}", signResponse.getCode(), signResponse.getMsg(), signResponse.getSubCode(), signResponse.getSubMsg());
return result;
}
/* 3.提交待签约事务 */
AlipayOpenAgentConfirmRequest confirmRequest = new AlipayOpenAgentConfirmRequest();
AlipayOpenAgentConfirmModel confirmModel = new AlipayOpenAgentConfirmModel();
confirmModel.setBatchNo(batchNo);
confirmRequest.setBizModel(confirmModel);
AlipayOpenAgentConfirmResponse confirmResponse = alipayClientWrapper.execute(confirmRequest);
log.info("【3】提交待签约事务code={}, msg={}, subCode={}, subMsg={}", confirmResponse.getCode(), confirmResponse.getMsg(), confirmResponse.getSubCode(), confirmResponse.getSubMsg());
if (!confirmResponse.isSuccess()) {
result.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY); // 失败
result.setApplyErrorInfo(confirmResponse.getCode() + "[" + confirmResponse.getMsg() + "]" + confirmResponse.getSubCode() + "[" + confirmResponse.getSubMsg() + "]");
log.error("提交待签约事务失败code={}, msg={}, subCode={}, subMsg={}", confirmResponse.getCode(), confirmResponse.getMsg(), confirmResponse.getSubCode(), confirmResponse.getSubMsg());
return result;
}
result.setState(MchApplyment.STATE_AUDITING); // 审核中
}catch (Exception e) {
result.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
result.setApplyErrorInfo("[" + e.getMessage() + "]");
log.error("进件异常:{}", e.getMessage());
}
return result;
}
}

View File

@@ -0,0 +1,52 @@
package com.jeequan.jeepay.thirdparty.channel.alipay;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.domain.AlipayOpenAuthTokenAppModel;
import com.alipay.api.request.AlipayOpenAuthTokenAppRequest;
import com.alipay.api.response.AlipayOpenAuthTokenAppResponse;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.interfaces.paychannel.IMchAutoAuthService;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.thirdparty.model.AlipayClientWrapper;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/*
* 支付宝: 商户授权实现类
*
* @author terrfly
*
* @date 2021/6/8 17:21
*/
@Service
@Slf4j
public class AlipayMchAutoAuthService implements IMchAutoAuthService {
@Autowired private ConfigContextQueryService configContextQueryService;
@Override
public JSONObject mchAuth(MchAppConfigContext mchAppConfigContext, String appAuthCode) {
AlipayClientWrapper alipayClientWrapper = configContextQueryService.getAlipayClientWrapper(mchAppConfigContext);
AlipayOpenAuthTokenAppRequest request = new AlipayOpenAuthTokenAppRequest();
AlipayOpenAuthTokenAppModel model = new AlipayOpenAuthTokenAppModel();
model.setGrantType("authorization_code");
model.setCode(appAuthCode);
request.setBizModel(model);
// expiresIn: 该字段已作废,应用令牌长期有效,接入方不需要消费该字段
// reExpiresIn: 刷新令牌的有效时间(从接口调用时间作为起始时间),单位到秒
// DateUtil.offsetSecond(new Date(), Integer.parseInt(resp.getExpiresIn()));
AlipayOpenAuthTokenAppResponse resp = alipayClientWrapper.execute(request);
if(!resp.isSuccess()){
throw new BizException(AlipayKit.appendErrMsg(resp.getMsg(), resp.getSubMsg()));
}
return (JSONObject) JSON.toJSON(resp);
}
}

View File

@@ -0,0 +1,87 @@
package com.jeequan.jeepay.thirdparty.channel.alipay;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.domain.AlipayOpenSpOperationApplyModel;
import com.alipay.api.domain.AlipayOpenSpOperationQrcodeQueryModel;
import com.alipay.api.domain.AlipayOpenSpOperationResultQueryModel;
import com.alipay.api.request.AlipayOpenSpOperationApplyRequest;
import com.alipay.api.request.AlipayOpenSpOperationQrcodeQueryRequest;
import com.alipay.api.request.AlipayOpenSpOperationResultQueryRequest;
import com.alipay.api.response.AlipayOpenSpOperationApplyResponse;
import com.alipay.api.response.AlipayOpenSpOperationQrcodeQueryResponse;
import com.alipay.api.response.AlipayOpenSpOperationResultQueryResponse;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.interfaces.paychannel.alipaybiz.IAlipayOpenSpOperationService;
import com.jeequan.jeepay.core.model.alipay.AlipaySpOperationInfo;
import com.jeequan.jeepay.core.model.params.alipay.AlipayIsvParams;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/*
* 支付宝服务商代运营授权
*
* @author zx
*
* @date 2023/3/8 15:13
*/
@Slf4j
@Service
public class AlipayOpenSpOperationService implements IAlipayOpenSpOperationService {
@Override
public JSONObject querySpOperationQrcode(AlipayIsvParams alipayIsvParams, String alipayAccount) {
AlipayOpenSpOperationQrcodeQueryRequest request = new AlipayOpenSpOperationQrcodeQueryRequest();
AlipayOpenSpOperationQrcodeQueryModel model = new AlipayOpenSpOperationQrcodeQueryModel();
model.setOutBizNo(IdUtil.fastSimpleUUID()); // 外部操作流水,由服务商自定义,需确保每次操作唯一
model.setOperateType("OPERATION_AUTH"); // OPERATION_AUTH代运营授权支持间连、直连商户。ACCOUNT_BIND账号绑定仅支持间连商户。
model.setAlipayAccount(alipayAccount);
model.setAccessProductCode("OPENAPI_AUTH_DEFAULT");
request.setBizModel(model);
AlipayOpenSpOperationQrcodeQueryResponse response = AlipayKit.getAlipayClientWrapper(alipayIsvParams).getAlipayClient().execute(request);
if (!response.isSuccess()) {
throw new BizException(AlipayKit.appendErrMsg(response.getMsg(), response.getSubMsg()));
}
return (JSONObject) JSON.toJSON(response);
}
@Override
public JSONObject applySpOperation(AlipayIsvParams alipayIsvParams, String alipayAccount){
AlipayOpenSpOperationApplyRequest request = new AlipayOpenSpOperationApplyRequest();
AlipayOpenSpOperationApplyModel model = new AlipayOpenSpOperationApplyModel();
model.setOutBizNo(IdUtil.fastSimpleUUID());
model.setOperateType("OPERATION_AUTH");
model.setAlipayAccount(alipayAccount);
model.setAccessProductCode("OPENAPI_AUTH_DEFAULT");
request.setBizModel(model);
AlipayOpenSpOperationApplyResponse response = AlipayKit.getAlipayClientWrapper(alipayIsvParams).getAlipayClient().execute(request);
if (!response.isSuccess()) {
throw new BizException(AlipayKit.appendErrMsg(response.getMsg(), response.getSubMsg()));
}
return (JSONObject) JSON.toJSON(response);
}
@Override
public AlipaySpOperationInfo querySpOperationResult(AlipayIsvParams alipayIsvParams, String alipayAccount){
AlipayOpenSpOperationResultQueryRequest request = new AlipayOpenSpOperationResultQueryRequest();
AlipayOpenSpOperationResultQueryModel model = new AlipayOpenSpOperationResultQueryModel();
model.setOperateType("OPERATION_AUTH");
model.setAlipayAccount(alipayAccount);
model.setAccessProductCode("OPENAPI_AUTH_DEFAULT");
request.setBizModel(model);
AlipayOpenSpOperationResultQueryResponse response = AlipayKit.getAlipayClientWrapper(alipayIsvParams).getAlipayClient().execute(request);
AlipaySpOperationInfo operationInfo = new AlipaySpOperationInfo();
BeanUtil.copyProperties(response, operationInfo);
return operationInfo;
}
}

View File

@@ -0,0 +1,50 @@
package com.jeequan.jeepay.thirdparty.channel.alipay;
import com.alipay.api.domain.AlipayTradeCloseModel;
import com.alipay.api.request.AlipayTradeCloseRequest;
import com.alipay.api.response.AlipayTradeCloseResponse;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.interfaces.paychannel.IPayOrderCloseService;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 支付宝 关闭订单接口实现类
*
* @author xiaoyu
*
* @date 2022/1/25 11:17
*/
@Service
public class AlipayPayOrderCloseService implements IPayOrderCloseService {
@Autowired private ConfigContextQueryService configContextQueryService;
@Override
public ChannelRetMsg close(PayOrder payOrder, MchAppConfigContext mchAppConfigContext){
AlipayTradeCloseRequest req = new AlipayTradeCloseRequest();
// 商户订单号,商户网站订单系统中唯一订单号,必填
AlipayTradeCloseModel model = new AlipayTradeCloseModel();
model.setOutTradeNo(payOrder.getPayOrderId());
req.setBizModel(model);
//通用字段
AlipayKit.putApiIsvInfo(mchAppConfigContext, req, model);
AlipayTradeCloseResponse resp = configContextQueryService.getAlipayClientWrapper(mchAppConfigContext).execute(req);
// 返回状态成功
if (resp.isSuccess()) {
return ChannelRetMsg.confirmSuccess(resp.getTradeNo());
}else {
return ChannelRetMsg.sysError(resp.getSubMsg());
}
}
}

View File

@@ -0,0 +1,51 @@
package com.jeequan.jeepay.thirdparty.channel.alipay;
import com.alipay.api.domain.AlipayTradeQueryModel;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.interfaces.paychannel.IPayOrderQueryService;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/*
* 支付宝 查单接口实现类
*
* @author terrfly
*
* @date 2021/6/8 17:20
*/
@Service
public class AlipayPayOrderQueryService implements IPayOrderQueryService {
@Autowired private ConfigContextQueryService configContextQueryService;
@Override
public ChannelRetMsg query(PayOrder payOrder, MchAppConfigContext mchAppConfigContext){
AlipayTradeQueryRequest req = new AlipayTradeQueryRequest();
// 商户订单号,商户网站订单系统中唯一订单号,必填
AlipayTradeQueryModel model = new AlipayTradeQueryModel();
model.setOutTradeNo(payOrder.getPayOrderId());
req.setBizModel(model);
//通用字段
AlipayKit.putApiIsvInfo(mchAppConfigContext, req, model);
AlipayTradeQueryResponse resp = configContextQueryService.getAlipayClientWrapper(mchAppConfigContext).execute(req);
String result = resp.getTradeStatus();
if("TRADE_SUCCESS".equals(result)) {
return ChannelRetMsg.confirmSuccess(resp.getTradeNo(), resp.getTradeNo(), payOrder.getPayOrderId(),null); //支付成功
}else if("WAIT_BUYER_PAY".equals(result)) {
return ChannelRetMsg.waiting(); //支付中
}
return ChannelRetMsg.waiting(); //支付中
}
}

View File

@@ -0,0 +1,43 @@
package com.jeequan.jeepay.thirdparty.channel.alipay;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.rqrs.AbstractRS;
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
import com.jeequan.jeepay.thirdparty.channel.AbstractPaymentService;
import com.jeequan.jeepay.thirdparty.util.PaywayUtil;
import org.springframework.stereotype.Service;
/*
* 支付接口: 支付宝官方
* 支付方式: 自适应
*
* @author terrfly
*
* @date 2021/6/8 17:19
*/
@Service
public class AlipayPaymentService extends AbstractPaymentService {
@Override
public String getIfCode() {
return CS.IF_CODE.ALIPAY;
}
@Override
public boolean isSupport(String wayCode) {
return false;
}
@Override
public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) {
return PaywayUtil.getRealPaywayService(this, payOrder.getWayCode()).preCheck(rq, payOrder);
}
@Override
public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception {
return PaywayUtil.getRealPaywayService(this, payOrder.getWayCode()).pay(rq, payOrder, mchAppConfigContext);
}
}

View File

@@ -0,0 +1,118 @@
package com.jeequan.jeepay.thirdparty.channel.alipay;
import com.alipay.api.domain.AlipayTradeFastpayRefundQueryModel;
import com.alipay.api.domain.AlipayTradeRefundModel;
import com.alipay.api.request.AlipayTradeFastpayRefundQueryRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeFastpayRefundQueryResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.entity.RefundOrder;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRefundLimit;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.model.rqrs.refund.RefundOrderRQ;
import com.jeequan.jeepay.core.utils.AmountUtil;
import com.jeequan.jeepay.thirdparty.channel.AbstractRefundService;
import org.springframework.stereotype.Service;
/*
* 退款接口: 支付宝官方
*
* @author terrfly
*
* @date 2021/6/17 9:38
*/
@Service
public class AlipayRefundService extends AbstractRefundService {
@Override
public String getIfCode() {
return CS.IF_CODE.ALIPAY;
}
@Override
public String preCheck(RefundOrderRQ bizRQ, RefundOrder refundOrder, PayOrder payOrder) {
return null;
}
@Override
public ChannelRetMsg refund(RefundOrderRQ bizRQ, RefundOrder refundOrder, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception {
AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
AlipayTradeRefundModel model = new AlipayTradeRefundModel();
model.setOutTradeNo(refundOrder.getPayOrderId());
model.setTradeNo(refundOrder.getChannelPayOrderNo());
model.setOutRequestNo(refundOrder.getRefundOrderId());
model.setRefundAmount(AmountUtil.convertCent2Dollar(refundOrder.getRefundAmount().toString()));
model.setRefundReason(refundOrder.getRefundReason());
request.setBizModel(model);
//统一放置 isv接口必传信息
AlipayKit.putApiIsvInfo(mchAppConfigContext, request, model);
AlipayTradeRefundResponse response = configContextQueryService.getAlipayClientWrapper(mchAppConfigContext).execute(request);
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
channelRetMsg.setChannelAttach(response.getBody());
// 调用成功
if(response.isSuccess()){
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
}else{
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
channelRetMsg.setChannelErrCode(response.getSubCode());
channelRetMsg.setChannelErrMsg(response.getSubMsg());
}
return channelRetMsg;
}
@Override
public ChannelRetMsg query(RefundOrder refundOrder, MchAppConfigContext mchAppConfigContext) throws Exception {
AlipayTradeFastpayRefundQueryRequest request = new AlipayTradeFastpayRefundQueryRequest();
AlipayTradeFastpayRefundQueryModel model = new AlipayTradeFastpayRefundQueryModel();
model.setTradeNo(refundOrder.getChannelPayOrderNo());
model.setOutTradeNo(refundOrder.getPayOrderId());
model.setOutRequestNo(refundOrder.getRefundOrderId());
request.setBizModel(model);
//统一放置 isv接口必传信息
AlipayKit.putApiIsvInfo(mchAppConfigContext, request, model);
AlipayTradeFastpayRefundQueryResponse response = configContextQueryService.getAlipayClientWrapper(mchAppConfigContext).execute(request);
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
channelRetMsg.setChannelAttach(response.getBody());
// 调用成功 & 金额相等 传入不存在的outRequestNo支付宝仍然返回响应成功只是数据不存在 调用isSuccess() 仍是成功, 此处需判断金额是否相等)
Long channelRefundAmount = response.getRefundAmount() == null ? null : Long.parseLong(AmountUtil.convertDollar2Cent(response.getRefundAmount()));
if(response.isSuccess() && refundOrder.getRefundAmount().equals(channelRefundAmount)){
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
}else{
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING); //认为是处理中
}
return channelRetMsg;
}
@Override
public void checkPlatAccount(RefundOrder refundOrder) {
}
/**
* 退款权限
* @param settleType
* @param applyId
* @return
*/
@Override
public ChannelRefundLimit isRefundLimit(String settleType,String applyId) {
return super.isRefundLimit(settleType,applyId);
}
}

View File

@@ -0,0 +1,176 @@
package com.jeequan.jeepay.thirdparty.channel.alipay;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.domain.*;
import com.alipay.api.request.*;
import com.alipay.api.response.*;
import com.jeequan.jeepay.core.entity.MchStore;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.interfaces.paychannel.alipaybiz.IAlipayShopService;
import com.jeequan.jeepay.core.model.params.alipay.AlipayIsvParams;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
/*
* 蚂蚁店铺管理接口
*
* @author zx
*
* @date 2023/3/8 15:13
*/
@Slf4j
@Service
public class AlipayShopService implements IAlipayShopService {
@Override
public MchStore query(AlipayIsvParams alipayIsvParams, String storeId, String ipRoleId) {
AntMerchantExpandShopQueryRequest request = new AntMerchantExpandShopQueryRequest();
AntMerchantExpandShopQueryModel model = new AntMerchantExpandShopQueryModel();
model.setStoreId(storeId); // 系统门店ID
model.setIpRoleId(ipRoleId); // 直连开店场景填写商户pid
request.setBizModel(model);
AntMerchantExpandShopQueryResponse response = AlipayKit.getAlipayClientWrapper(alipayIsvParams).getAlipayClient().execute(request);
if (!response.isSuccess()) {
throw new BizException(AlipayKit.appendErrMsg(response.getMsg(), response.getSubMsg()));
}
// 转换为商户门店信息
MchStore mchStore = new MchStore();
mchStore.setAlipayShopId(response.getShopId());
mchStore.setStoreName(response.getShopName());
mchStore.setContactPhone(response.getContactMobile());
mchStore.addExt("shopCategory", response.getNewShopCategory());
mchStore.addExt("shopType", response.getShopType());
AddressInfo businessAddress = response.getBusinessAddress();
JSONArray jsonArray = new JSONArray();
jsonArray.add(businessAddress.getProvinceCode());
jsonArray.add(businessAddress.getCityCode());
jsonArray.add(businessAddress.getDistrictCode());
mchStore.setAreaCode(jsonArray);
mchStore.setAddress(businessAddress.getAddress());
mchStore.setLat(businessAddress.getLatitude());
mchStore.setLng(businessAddress.getLongitude());
return mchStore;
}
@Override
public MchStore create(AlipayIsvParams alipayIsvParams, MchStore mchStore, String ipRoleId){
AntMerchantExpandShopCreateRequest request = new AntMerchantExpandShopCreateRequest();
AntMerchantExpandShopCreateModel model = new AntMerchantExpandShopCreateModel();
// 经营地址
AddressInfo businessAddress = new AddressInfo();
businessAddress.setProvinceCode(mchStore.getAreaCode().getString(0));
businessAddress.setCityCode(mchStore.getAreaCode().getString(1));
businessAddress.setDistrictCode(mchStore.getAreaCode().getString(2));
businessAddress.setAddress(mchStore.getAddress());
model.setBusinessAddress(businessAddress);
if (mchStore.getExt() != null) {
if (StringUtils.isNotBlank(mchStore.getExt().getString("shopCategory"))) {
model.setShopCategory(mchStore.getExt().getString("shopCategory").split("_")[1]); // 新版门店类目标准二级类目code
}
model.setShopType(mchStore.getExt().getString("shopType")); // 店铺经营类型01表示直营02表示加盟
}
model.setStoreId(mchStore.getStoreId() + ""); // 外部门店ID
model.setIpRoleId(ipRoleId);
model.setShopName(mchStore.getStoreName());
model.setContactMobile(mchStore.getContactPhone());
request.setBizModel(model);
AntMerchantExpandShopCreateResponse response = AlipayKit.getAlipayClientWrapper(alipayIsvParams).getAlipayClient().execute(request);
log.info("蚂蚁店铺创建响应storeId={}response={}", mchStore.getStoreId(), JSONObject.toJSONString(response));
if (!response.isSuccess()) {
throw new BizException(AlipayKit.appendErrMsg(response.getMsg(), response.getSubMsg()));
}
MchStore result = new MchStore();
result.setAlipayShopCreateId(response.getOrderId());
return result;
}
@Override
public MchStore update(AlipayIsvParams alipayIsvParams, MchStore mchStore){
AntMerchantExpandShopModifyRequest request = new AntMerchantExpandShopModifyRequest();
AntMerchantExpandShopModifyModel model = new AntMerchantExpandShopModifyModel();
model.setShopId(mchStore.getAlipayShopId());
// 经营地址
AddressInfo businessAddress = new AddressInfo();
businessAddress.setProvinceCode(mchStore.getAreaCode().getString(0));
businessAddress.setCityCode(mchStore.getAreaCode().getString(1));
businessAddress.setDistrictCode(mchStore.getAreaCode().getString(2));
businessAddress.setAddress(mchStore.getAddress());
model.setBusinessAddress(businessAddress);
if (mchStore.getExt() != null) {
if (StringUtils.isNotBlank(mchStore.getExt().getString("shopCategory")) && mchStore.getExt().getString("shopCategory").contains("_")) {
model.setShopCategory(mchStore.getExt().getString("shopCategory").split("_")[1]); // 新版门店类目标准二级类目code
}else {
model.setShopCategory(mchStore.getExt().getString("shopCategory")); // 新版门店类目标准二级类目code
}
}
model.setShopName(mchStore.getStoreName());
model.setContactMobile(mchStore.getContactPhone());
request.setBizModel(model);
AntMerchantExpandShopModifyResponse response = AlipayKit.getAlipayClientWrapper(alipayIsvParams).getAlipayClient().execute(request);
log.info("蚂蚁店铺修改响应storeId={}response={}", mchStore.getStoreId(), JSONObject.toJSONString(response));
if (!response.isSuccess()) {
throw new BizException(AlipayKit.appendErrMsg(response.getMsg(), response.getSubMsg()));
}
MchStore result = new MchStore();
result.setAlipayShopCreateId(response.getOrderId());
return result;
}
@Override
public void close(AlipayIsvParams alipayIsvParams, String alipayShopId) {
AntMerchantExpandShopCloseRequest request = new AntMerchantExpandShopCloseRequest();
AntMerchantExpandShopCloseModel model = new AntMerchantExpandShopCloseModel();
model.setShopId(alipayShopId);
request.setBizModel(model);
AntMerchantExpandShopCloseResponse response = AlipayKit.getAlipayClientWrapper(alipayIsvParams).getAlipayClient().execute(request);
if (!response.isSuccess()) {
throw new BizException(AlipayKit.appendErrMsg(response.getMsg(), response.getSubMsg()));
}
}
@Override
public MchStore createResultQuery(AlipayIsvParams alipayIsvParams, String orderId){
AntMerchantExpandOrderQueryRequest request = new AntMerchantExpandOrderQueryRequest();
AntMerchantExpandOrderQueryModel model = new AntMerchantExpandOrderQueryModel();
model.setOrderId(orderId);
request.setBizModel(model);
AntMerchantExpandOrderQueryResponse response = AlipayKit.getAlipayClientWrapper(alipayIsvParams).getAlipayClient().execute(request);
log.info("蚂蚁店铺申请单查询接口响应orderId={}response={}", orderId, JSONObject.toJSONString(response));
if (!response.isSuccess()) {
throw new BizException(AlipayKit.appendErrMsg(response.getMsg(), response.getSubMsg()));
}
MchStore result = new MchStore();
if (StringUtils.isNotBlank(response.getExtInfo())) {
JSONObject extInfo = JSONObject.parseObject(response.getExtInfo());
result.setAlipayShopId(extInfo.getString("SHOP_ID"));
}
result.setAlipayShopStatus(response.getStatus());
return result;
}
}

View File

@@ -0,0 +1,113 @@
package com.jeequan.jeepay.thirdparty.channel.alipay;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.internal.util.AlipaySignature;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.TransferOrder;
import com.jeequan.jeepay.core.exception.ResponseException;
import com.jeequan.jeepay.core.model.params.alipay.AlipayConfig;
import com.jeequan.jeepay.core.model.params.alipay.AlipayIsvParams;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.thirdparty.channel.AbstractTransferNoticeService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.MutablePair;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
/*
* 支付宝 转账回调接口实现类
*
* @author zx
*
* @date 2021/21/01 17:16
*/
@Service
@Slf4j
public class AlipayTransferNoticeService extends AbstractTransferNoticeService {
@Override
public String getIfCode() {
return CS.IF_CODE.ALIPAY;
}
@Override
public MutablePair<String, Object> parseParams(HttpServletRequest request, String urlOrderId) {
try {
JSONObject params = getReqParamJSON();
log.info("【支付宝转账】回调通知参数:{}", params.toJSONString());
JSONObject bizContent = JSONObject.parseObject(params.getString("biz_content"));
String transferId = bizContent.getString("out_biz_no");
return MutablePair.of(transferId, params);
} catch (Exception e) {
log.error("error", e);
throw ResponseException.buildText("ERROR");
}
}
@Override
public ChannelRetMsg doNotice(HttpServletRequest request, Object params, TransferOrder transferOrder) {
String logPrefix = "【支付宝转账通知】";
try {
//配置参数获取
Byte useCert = null;
String alipaySignType, alipayPublicCert, alipayPublicKey;
// 获取支付参数
AlipayIsvParams alipayParams = (AlipayIsvParams) configContextQueryService.queryTransferIsvParams(transferOrder.getIsvNo(), getIfCode());
useCert = alipayParams.getUseCert();
alipaySignType = alipayParams.getSignType();
alipayPublicCert = alipayParams.getAlipayPublicCert();
alipayPublicKey = alipayParams.getAlipayPublicKey();
// 获取请求参数
JSONObject jsonParams = (JSONObject) params;
JSONObject bizContent = JSONObject.parseObject(jsonParams.getString("biz_content"));
boolean verifyResult;
if(useCert != null && useCert == CS.YES){ //证书方式
verifyResult = AlipaySignature.rsaCertCheckV1(jsonParams.toJavaObject(Map.class), getCertFilePath(alipayPublicCert),
AlipayConfig.CHARSET, alipaySignType);
}else{
verifyResult = AlipaySignature.rsaCheckV1(jsonParams.toJavaObject(Map.class), alipayPublicKey, AlipayConfig.CHARSET, alipaySignType);
}
//验签失败
if(!verifyResult){
log.error("{},验签失败", logPrefix);
throw ResponseException.buildText("ERROR");
}
//验签成功后判断上游订单状态
ResponseEntity okResponse = textResp("SUCCESS");
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
channelRetMsg.setResponseEntity(okResponse); // 响应数据
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING); // 默认转账中
// 成功SUCCESS
String status = bizContent.getString("status");
if("SUCCESS".equals(status)){
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
}
return channelRetMsg;
} catch (Exception e) {
log.error("error", e);
throw ResponseException.buildText("ERROR");
}
}
}

View File

@@ -0,0 +1,194 @@
package com.jeequan.jeepay.thirdparty.channel.alipay;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayFundTransCommonQueryModel;
import com.alipay.api.domain.AlipayFundTransUniTransferModel;
import com.alipay.api.domain.ExtendParams;
import com.alipay.api.domain.Participant;
import com.alipay.api.request.AlipayFundTransCommonQueryRequest;
import com.alipay.api.request.AlipayFundTransUniTransferRequest;
import com.alipay.api.response.AlipayFundTransCommonQueryResponse;
import com.alipay.api.response.AlipayFundTransUniTransferResponse;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.TransferOrder;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.params.alipay.AlipayIsvParams;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.model.rqrs.transfer.TransferOrderRQ;
import com.jeequan.jeepay.core.utils.AmountUtil;
import com.jeequan.jeepay.db.entity.TransferSubjectEntity;
import com.jeequan.jeepay.thirdparty.channel.AbstractTransferService;
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.stereotype.Service;
/**
* 转账接口: 支付宝官方
*
* @author terrfly
*
* @date 2021/8/11 14:05
*/
@Slf4j
@Service
public class AlipayTransferService extends AbstractTransferService {
@Autowired private ConfigContextQueryService configContextQueryService;
@Override
public String getIfCode() {
return CS.IF_CODE.ALIPAY;
}
@Override
public boolean isSupport(String entryType) {
// 支付宝账户
if(TransferOrder.ENTRY_ALIPAY_CASH.equals(entryType)){
return true;
}
return false;
}
@Override
public String preCheck(TransferOrderRQ bizRQ, TransferOrder transferOrder) {
super.preCheck(bizRQ, transferOrder);
/*
* 支付宝新版转账接口:不支持服务商模式 不存在 app_auth_token 参数了
* 旧版文档地址: https://opendocs.alipay.com/open/309/alipay.fund.trans.toaccount.transfer
* 新版文档地址: https://opendocs.alipay.com/open/02byuo?ref=api&scene=ca56bca529e64125a2786703c6192d41
*/
if(transferOrder.getMchType() == CS.MCH_TYPE_ISVSUB){
return "支付宝子商户暂不支持转账业务";
}
return null;
}
@Override
public ChannelRetMsg transfer(TransferOrderRQ bizRQ, TransferOrder transferOrder){
AlipayFundTransUniTransferRequest request = new AlipayFundTransUniTransferRequest();
AlipayFundTransUniTransferModel model = new AlipayFundTransUniTransferModel();
model.setTransAmount(AmountUtil.convertCent2Dollar(transferOrder.getAmount())); //转账金额,单位:元。
model.setOutBizNo(transferOrder.getTransferId()); //商户转账唯一订单号
model.setRemark(transferOrder.getTransferDesc()); //转账备注
model.setProductCode("TRANS_ACCOUNT_NO_PWD"); // 销售产品码。单笔无密转账固定为 TRANS_ACCOUNT_NO_PWD
model.setBizScene("DIRECT_TRANSFER"); // 业务场景 单笔无密转账固定为 DIRECT_TRANSFER。
model.setOrderTitle("转账"); // 转账业务的标题,用于在支付宝用户的账单里显示。
model.setBusinessParams(transferOrder.getChannelExtra()); // 转账业务请求的扩展参数 {\"payer_show_name_use_alias\":\"xx公司\"}
Participant accPayeeInfo = new Participant();
accPayeeInfo.setName(StringUtils.defaultString(transferOrder.getAccountName(), null)); //收款方真实姓名
accPayeeInfo.setIdentityType("ALIPAY_LOGON_ID"); //ALIPAY_USERID 支付宝用户ID ALIPAY_LOGONID:支付宝登录账号
accPayeeInfo.setIdentity(transferOrder.getAccountNo()); //收款方账户
model.setPayeeInfo(accPayeeInfo);
request.setBizModel(model);
TransferSubjectEntity transferSubjectFq = transferSubjectService.getById(transferOrder.getTransferSubjectIdFq());
//统一放置 isv接口必传信息
// 获取支付参数
AlipayIsvParams isvParams = (AlipayIsvParams) configContextQueryService.queryTransferIsvParams(transferSubjectFq);
// 服务商信息
ExtendParams extendParams = new ExtendParams();
extendParams.setSysServiceProviderId(isvParams.getPid());
// if(model instanceof AlipayTradePayModel) {
// ((AlipayTradePayModel)model).setExtendParams(extendParams);
// } else if(model instanceof AlipayTradeAppPayModel) {
// ((AlipayTradeAppPayModel)model).setExtendParams(extendParams);
// } else if(model instanceof AlipayTradeCreateModel) {
// ((AlipayTradeCreateModel)model).setExtendParams(extendParams);
// } else if(model instanceof AlipayTradePagePayModel) {
// ((AlipayTradePagePayModel)model).setExtendParams(extendParams);
// } else if(model instanceof AlipayTradePrecreateModel) {
// ((AlipayTradePrecreateModel)model).setExtendParams(extendParams);
// } else if(model instanceof AlipayTradeWapPayModel) {
// ((AlipayTradeWapPayModel)model).setExtendParams(extendParams);
// }
request.putOtherTextParam("app_auth_token", isvParams.getAppAuthToken());
// 调起支付宝接口
AlipayFundTransUniTransferResponse response;
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
try {
DefaultAlipayClient alipayClient = new DefaultAlipayClient(isvParams.getConfig());
response = alipayClient.certificateExecute(request);
channelRetMsg.setChannelAttach(response.getBody());
// 调用成功
if(response.isSuccess()){
if ("SUCCESS".equals(response.getStatus())) {
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
channelRetMsg.setChannelOrderId(response.getOrderId());
return channelRetMsg;
}else if ("FAIL".equals(response.getStatus())) {
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
channelRetMsg.setChannelErrCode(AlipayKit.appendErrCode(response.getCode(), response.getSubCode()));
channelRetMsg.setChannelErrMsg(AlipayKit.appendErrMsg(response.getMsg(), response.getSubMsg()));
return channelRetMsg;
}else {
return ChannelRetMsg.waiting();
}
}else{
//若 系统繁忙, 无法确认结果
if("SYSTEM_ERROR".equalsIgnoreCase(response.getSubCode())){
return ChannelRetMsg.waiting();
}
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
channelRetMsg.setChannelErrCode(response.getSubCode());
channelRetMsg.setChannelErrMsg(response.getSubMsg());
}
} catch (Exception e) {
log.info("支付宝请求异常", e);
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
channelRetMsg.setChannelErrMsg("支付宝请求异常");
}
return channelRetMsg;
}
@Override
public ChannelRetMsg query(TransferOrder transferOrder, MchAppConfigContext mchAppConfigContext) {
AlipayFundTransCommonQueryRequest request = new AlipayFundTransCommonQueryRequest();
AlipayFundTransCommonQueryModel model = new AlipayFundTransCommonQueryModel();
model.setProductCode(TransferOrder.ENTRY_BANK_CARD.equals(transferOrder.getEntryType()) ? "TRANS_BANKCARD_NO_PWD" : "TRANS_ACCOUNT_NO_PWD");
model.setBizScene("DIRECT_TRANSFER");
model.setOutBizNo(transferOrder.getTransferId()); // 商户转账唯一订单号
AlipayKit.putApiIsvInfo(mchAppConfigContext, request, model);
request.setBizModel(model);
// 调起支付宝接口
AlipayFundTransCommonQueryResponse response = configContextQueryService.getAlipayClientWrapper(mchAppConfigContext).execute(request);
if (response.isSuccess()) {
// SUCCESS明确成功
if("SUCCESS".equalsIgnoreCase(response.getStatus())){
return ChannelRetMsg.confirmSuccess(response.getOrderId());
}
// REFUND退票适用于"单笔转账到银行卡" FAIL失败适用于"单笔转账到银行卡"
else if ("REFUND".equalsIgnoreCase(response.getStatus()) || "FAIL".equalsIgnoreCase(response.getStatus())){
return ChannelRetMsg.confirmFail(response.getErrorCode(), response.getFailReason());
} else{
return ChannelRetMsg.waiting();
}
} else {
// 如果查询单号对应的数据不存在那么数据不存在的原因可能是1付款还在处理中2付款处理失败导致付款订单没有落地务必再次查询确认此次付款的结果。
if("ORDER_NOT_EXIST".equalsIgnoreCase(response.getSubCode())){
return ChannelRetMsg.waiting();
}
return ChannelRetMsg.confirmFail(AlipayKit.appendErrCode(response.getCode(), response.getSubCode()), AlipayKit.appendErrMsg(response.getMsg(), response.getSubMsg()));
}
}
}

View File

@@ -0,0 +1,74 @@
package com.jeequan.jeepay.thirdparty.channel.alipay.payway;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alipay.api.AlipayApiException;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.exception.ChannelException;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.rqrs.AbstractRS;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.AliAppOrderRS;
import com.jeequan.jeepay.core.utils.AmountUtil;
import com.jeequan.jeepay.thirdparty.channel.alipay.AlipayKit;
import com.jeequan.jeepay.thirdparty.channel.alipay.AlipayPaymentService;
import com.jeequan.jeepay.thirdparty.util.ApiResBuilder;
import org.springframework.stereotype.Service;
/*
* 支付宝 APP支付
*
* @author terrfly
*
* @date 2021/6/8 17:20
*/
@Service("alipayPaymentByAliAppService") //Service Name需保持全局唯一性
public class AliApp extends AlipayPaymentService {
@Override
public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) {
return null;
}
@Override
public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext){
AlipayTradeAppPayRequest req = new AlipayTradeAppPayRequest();
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setOutTradeNo(payOrder.getPayOrderId());
model.setSubject(payOrder.getSubject()); //订单标题
model.setBody(payOrder.getBody()); //订单描述信息
model.setTotalAmount(AmountUtil.convertCent2Dollar(payOrder.getFindAmt().toString())); //支付金额
model.setTimeExpire(DateUtil.format(payOrder.getExpiredTime(), DatePattern.NORM_DATETIME_FORMAT)); // 订单超时时间
req.setNotifyUrl(getNotifyUrl()); // 设置异步通知地址
req.setBizModel(model);
//统一放置 isv接口必传信息
AlipayKit.putApiIsvInfo(mchAppConfigContext, req, model);
String payData = null;
// sdk方式需自行拦截接口异常信息
try {
payData = configContextQueryService.getAlipayClientWrapper(mchAppConfigContext).getAlipayClient().getAlipayClient().sdkExecute(req).getBody();
} catch (AlipayApiException e) {
throw ChannelException.sysError(e.getMessage());
}
// 构造函数响应数据
AliAppOrderRS res = ApiResBuilder.buildSuccess(AliAppOrderRS.class);
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
res.setChannelRetMsg(channelRetMsg);
//放置 响应数据
channelRetMsg.setChannelAttach(payData);
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
res.setPayData(payData);
return res;
}
}

View File

@@ -0,0 +1,101 @@
package com.jeequan.jeepay.thirdparty.channel.alipay.payway;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alipay.api.domain.AlipayTradePayModel;
import com.alipay.api.request.AlipayTradePayRequest;
import com.alipay.api.response.AlipayTradePayResponse;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.rqrs.AbstractRS;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.AliBarOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.AliBarOrderRS;
import com.jeequan.jeepay.core.utils.AmountUtil;
import com.jeequan.jeepay.thirdparty.channel.alipay.AlipayKit;
import com.jeequan.jeepay.thirdparty.channel.alipay.AlipayPaymentService;
import com.jeequan.jeepay.thirdparty.util.ApiResBuilder;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
/*
* 支付宝 条码支付
*
* @author terrfly
*
* @date 2021/6/8 17:20
*/
@Service("alipayPaymentByAliBarService") //Service Name需保持全局唯一性
public class AliBar extends AlipayPaymentService {
@Override
public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) {
AliBarOrderRQ bizRQ = (AliBarOrderRQ) rq;
if(StringUtils.isEmpty(bizRQ.getAuthCode())){
throw new BizException("用户支付条码[authCode]不可为空");
}
return null;
}
@Override
public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext){
AliBarOrderRQ bizRQ = (AliBarOrderRQ) rq;
AlipayTradePayRequest req = new AlipayTradePayRequest();
AlipayTradePayModel model = new AlipayTradePayModel();
model.setOutTradeNo(payOrder.getPayOrderId());
model.setScene("bar_code"); //条码支付 bar_code ; 声波支付 wave_code
model.setAuthCode(bizRQ.getAuthCode().trim()); //支付授权码
model.setSubject(payOrder.getSubject()); //订单标题
model.setBody(payOrder.getBody()); //订单描述信息
model.setTotalAmount(AmountUtil.convertCent2Dollar(payOrder.getFindAmt().toString())); //支付金额
model.setTimeExpire(DateUtil.format(payOrder.getExpiredTime(), DatePattern.NORM_DATETIME_FORMAT)); // 订单超时时间
req.setNotifyUrl(getNotifyUrl()); // 设置异步通知地址
req.setBizModel(model);
//统一放置 isv接口必传信息
AlipayKit.putApiIsvInfo(mchAppConfigContext, req, model);
//调起支付宝 (如果异常, 将直接跑出 ChannelException
AlipayTradePayResponse alipayResp = configContextQueryService.getAlipayClientWrapper(mchAppConfigContext).execute(req);
// 构造函数响应数据
AliBarOrderRS res = ApiResBuilder.buildSuccess(AliBarOrderRS.class);
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
res.setChannelRetMsg(channelRetMsg);
//放置 响应数据
channelRetMsg.setChannelAttach(alipayResp.getBody());
channelRetMsg.setChannelOrderId(alipayResp.getTradeNo());
channelRetMsg.setPlatformOrderNo(alipayResp.getTradeNo());
channelRetMsg.setPlatformMchOrderNo(payOrder.getPayOrderId());
channelRetMsg.setChannelUserId(alipayResp.getBuyerUserId()); //渠道用户标识
// ↓↓↓↓↓↓ 调起接口成功后业务判断务必谨慎!! 避免因代码编写bug导致不能正确返回订单状态信息 ↓↓↓↓↓↓
//当条码重复发起时支付宝返回的code = 10003, subCode = null [等待用户支付], 此时需要特殊判断 = = 。
if("10000".equals(alipayResp.getCode()) && alipayResp.isSuccess()){ //支付成功, 更新订单成功 || 等待支付宝的异步回调接口
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
}else if("10003".equals(alipayResp.getCode())){ //10003 表示为 处理中, 例如等待用户输入密码
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
}else{ //其他状态, 表示下单失败
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
channelRetMsg.setChannelErrCode(AlipayKit.appendErrCode(alipayResp.getCode(), alipayResp.getSubCode()));
channelRetMsg.setChannelErrMsg(AlipayKit.appendErrMsg(alipayResp.getMsg(), alipayResp.getSubMsg()));
}
return res;
}
}

View File

@@ -0,0 +1,89 @@
package com.jeequan.jeepay.thirdparty.channel.alipay.payway;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alipay.api.domain.AlipayTradeCreateModel;
import com.alipay.api.request.AlipayTradeCreateRequest;
import com.alipay.api.response.AlipayTradeCreateResponse;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.rqrs.AbstractRS;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.AliJsapiOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.AliJsapiOrderRS;
import com.jeequan.jeepay.core.utils.AmountUtil;
import com.jeequan.jeepay.thirdparty.channel.alipay.AlipayKit;
import com.jeequan.jeepay.thirdparty.channel.alipay.AlipayPaymentService;
import com.jeequan.jeepay.thirdparty.util.ApiResBuilder;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
/*
* 支付宝 jsapi支付
*
* @author terrfly
*
* @date 2021/6/8 17:20
*/
@Service("alipayPaymentByJsapiService") //Service Name需保持全局唯一性
public class AliJsapi extends AlipayPaymentService {
@Override
public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) {
AliJsapiOrderRQ bizRQ = (AliJsapiOrderRQ) rq;
if(StringUtils.isEmpty(bizRQ.getBuyerUserId())){
throw new BizException("[buyerUserId]不可为空");
}
return null;
}
@Override
public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception{
AliJsapiOrderRQ bizRQ = (AliJsapiOrderRQ) rq;
AlipayTradeCreateRequest req = new AlipayTradeCreateRequest();
AlipayTradeCreateModel model = new AlipayTradeCreateModel();
model.setOutTradeNo(payOrder.getPayOrderId());
model.setSubject(payOrder.getSubject()); //订单标题
model.setBody(payOrder.getBody()); //订单描述信息
model.setTotalAmount(AmountUtil.convertCent2Dollar(payOrder.getFindAmt().toString())); //支付金额
model.setTimeExpire(DateUtil.format(payOrder.getExpiredTime(), DatePattern.NORM_DATETIME_FORMAT)); // 订单超时时间
model.setBuyerId(bizRQ.getBuyerUserId());
req.setNotifyUrl(getNotifyUrl()); // 设置异步通知地址
req.setBizModel(model);
//统一放置 isv接口必传信息
AlipayKit.putApiIsvInfo(mchAppConfigContext, req, model);
//调起支付宝 (如果异常, 将直接跑出 ChannelException
AlipayTradeCreateResponse alipayResp = configContextQueryService.getAlipayClientWrapper(mchAppConfigContext).execute(req);
// 构造函数响应数据
AliJsapiOrderRS res = ApiResBuilder.buildSuccess(AliJsapiOrderRS.class);
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
res.setChannelRetMsg(channelRetMsg);
//放置 响应数据
channelRetMsg.setChannelAttach(alipayResp.getBody());
// ↓↓↓↓↓↓ 调起接口成功后业务判断务必谨慎!! 避免因代码编写bug导致不能正确返回订单状态信息 ↓↓↓↓↓↓
res.setAlipayTradeNo(alipayResp.getTradeNo());
channelRetMsg.setChannelOrderId(alipayResp.getTradeNo());
if(alipayResp.isSuccess()){ //业务处理成功
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
}else{
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
channelRetMsg.setChannelErrCode(AlipayKit.appendErrCode(alipayResp.getCode(), alipayResp.getSubCode()));
channelRetMsg.setChannelErrMsg(AlipayKit.appendErrMsg(alipayResp.getMsg(), alipayResp.getSubMsg()));
}
return res;
}
}

View File

@@ -0,0 +1,99 @@
package com.jeequan.jeepay.thirdparty.channel.alipay.payway;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alipay.api.domain.AlipayTradeCreateModel;
import com.alipay.api.request.AlipayTradeCreateRequest;
import com.alipay.api.response.AlipayTradeCreateResponse;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.oauth2.AlipayOauth2Params;
import com.jeequan.jeepay.core.model.rqrs.AbstractRS;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.AliLiteOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.AliLiteOrderRS;
import com.jeequan.jeepay.core.utils.AmountUtil;
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
import com.jeequan.jeepay.thirdparty.channel.alipay.AlipayKit;
import com.jeequan.jeepay.thirdparty.channel.alipay.AlipayPaymentService;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import com.jeequan.jeepay.thirdparty.util.ApiResBuilder;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
/*
* 支付宝 小程序支付
*
* @author terrfly
*
* @date 2021/6/8 17:20
*/
@Service("alipayPaymentByLiteService") //Service Name需保持全局唯一性
public class AliLite extends AlipayPaymentService {
@Override
public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) {
AliLiteOrderRQ bizRQ = (AliLiteOrderRQ) rq;
if(StringUtils.isEmpty(bizRQ.getBuyerUserId())){
throw new BizException("[buyerUserId]不可为空");
}
return null;
}
@Override
public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception{
AliLiteOrderRQ bizRQ = (AliLiteOrderRQ) rq;
AlipayTradeCreateRequest req = new AlipayTradeCreateRequest();
AlipayTradeCreateModel model = new AlipayTradeCreateModel();
model.setOutTradeNo(payOrder.getPayOrderId());
model.setSubject(payOrder.getSubject()); //订单标题
model.setBody(payOrder.getBody()); //订单描述信息
model.setTotalAmount(AmountUtil.convertCent2Dollar(payOrder.getFindAmt().toString())); //支付金额
model.setTimeExpire(DateUtil.format(payOrder.getExpiredTime(), DatePattern.NORM_DATETIME_FORMAT)); // 订单超时时间
model.setBuyerId(bizRQ.getBuyerUserId());
ConfigContextQueryService configContextQueryService = SpringBeansUtil.getBean(ConfigContextQueryService.class);
AlipayOauth2Params oauth2Params = (AlipayOauth2Params) configContextQueryService.queryIsvOauth2Params(mchAppConfigContext.getMchApplyment().getIsvNo(), CS.IF_CODE.ALIPAY);
model.setProductCode("JSAPI_PAY");
model.setOpAppId(oauth2Params.getLiteParams().getAppId());
req.setNotifyUrl(getNotifyUrl()); // 设置异步通知地址
req.setBizModel(model);
//统一放置 isv接口必传信息
AlipayKit.putApiIsvInfo(mchAppConfigContext, req, model);
//调起支付宝 (如果异常, 将直接跑出 ChannelException
AlipayTradeCreateResponse alipayResp = configContextQueryService.getAlipayClientWrapper(mchAppConfigContext).execute(req);
// 构造函数响应数据
AliLiteOrderRS res = ApiResBuilder.buildSuccess(AliLiteOrderRS.class);
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
res.setChannelRetMsg(channelRetMsg);
//放置 响应数据
channelRetMsg.setChannelAttach(alipayResp.getBody());
// ↓↓↓↓↓↓ 调起接口成功后业务判断务必谨慎!! 避免因代码编写bug导致不能正确返回订单状态信息 ↓↓↓↓↓↓
res.setAlipayTradeNo(alipayResp.getTradeNo());
channelRetMsg.setChannelOrderId(alipayResp.getTradeNo());
if(alipayResp.isSuccess()){ //业务处理成功
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
}else{
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
channelRetMsg.setChannelErrCode(AlipayKit.appendErrCode(alipayResp.getCode(), alipayResp.getSubCode()));
channelRetMsg.setChannelErrMsg(AlipayKit.appendErrMsg(alipayResp.getMsg(), alipayResp.getSubMsg()));
}
return res;
}
}

View File

@@ -0,0 +1,80 @@
package com.jeequan.jeepay.thirdparty.channel.alipay.payway;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alipay.api.AlipayApiException;
import com.alipay.api.domain.AlipayTradePagePayModel;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.exception.ChannelException;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.rqrs.AbstractRS;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.AliPcOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.AliPcOrderRS;
import com.jeequan.jeepay.core.utils.AmountUtil;
import com.jeequan.jeepay.thirdparty.channel.alipay.AlipayKit;
import com.jeequan.jeepay.thirdparty.channel.alipay.AlipayPaymentService;
import com.jeequan.jeepay.thirdparty.util.ApiResBuilder;
import org.springframework.stereotype.Service;
/*
* 支付宝 PC支付
*
* @author terrfly
*
* @date 2021/6/8 17:21
*/
@Service("alipayPaymentByAliPcService") //Service Name需保持全局唯一性
public class AliPc extends AlipayPaymentService {
@Override
public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) {
return null;
}
@Override
public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext){
AliPcOrderRQ bizRQ = (AliPcOrderRQ) rq;
AlipayTradePagePayRequest req = new AlipayTradePagePayRequest();
AlipayTradePagePayModel model = new AlipayTradePagePayModel();
model.setOutTradeNo(payOrder.getPayOrderId());
model.setSubject(payOrder.getSubject()); //订单标题
model.setBody(payOrder.getBody()); //订单描述信息
model.setTotalAmount(AmountUtil.convertCent2Dollar(payOrder.getFindAmt().toString())); //支付金额
model.setTimeExpire(DateUtil.format(payOrder.getExpiredTime(), DatePattern.NORM_DATETIME_FORMAT)); // 订单超时时间
model.setProductCode("FAST_INSTANT_TRADE_PAY");
model.setQrPayMode("2"); //订单码-跳转模式
req.setNotifyUrl(getNotifyUrl()); // 设置异步通知地址
req.setReturnUrl(getReturnUrl()); // 同步跳转地址
req.setBizModel(model);
//统一放置 isv接口必传信息
AlipayKit.putApiIsvInfo(mchAppConfigContext, req, model);
// 构造函数响应数据
AliPcOrderRS res = ApiResBuilder.buildSuccess(AliPcOrderRS.class);
try {
if(CS.PAY_DATA_TYPE.FORM.equals(bizRQ.getPayDataType())){
res.setFormContent(configContextQueryService.getAlipayClientWrapper(mchAppConfigContext).getAlipayClient().getAlipayClient().pageExecute(req).getBody());
}else{
res.setPayUrl(configContextQueryService.getAlipayClientWrapper(mchAppConfigContext).getAlipayClient().getAlipayClient().pageExecute(req, "GET").getBody());
}
}catch (AlipayApiException e) {
throw ChannelException.sysError(e.getMessage());
}
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
res.setChannelRetMsg(channelRetMsg);
//放置 响应数据
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
return res;
}
}

View File

@@ -0,0 +1,88 @@
package com.jeequan.jeepay.thirdparty.channel.alipay.payway;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alipay.api.domain.AlipayTradePrecreateModel;
import com.alipay.api.request.AlipayTradePrecreateRequest;
import com.alipay.api.response.AlipayTradePrecreateResponse;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.rqrs.AbstractRS;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.AliQrOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.AliQrOrderRS;
import com.jeequan.jeepay.core.utils.AmountUtil;
import com.jeequan.jeepay.thirdparty.channel.alipay.AlipayKit;
import com.jeequan.jeepay.thirdparty.channel.alipay.AlipayPaymentService;
import com.jeequan.jeepay.thirdparty.util.ApiResBuilder;
import org.springframework.stereotype.Service;
/*
* 支付宝 QR支付
*
* @author terrfly
*
* @date 2021/6/8 17:21
*/
@Service("alipayPaymentByAliQrService") //Service Name需保持全局唯一性
public class AliQr extends AlipayPaymentService {
@Override
public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) {
return null;
}
@Override
public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext){
AliQrOrderRQ aliQrOrderRQ = (AliQrOrderRQ)rq;
AlipayTradePrecreateRequest req = new AlipayTradePrecreateRequest();
AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();
model.setOutTradeNo(payOrder.getPayOrderId());
model.setSubject(payOrder.getSubject()); //订单标题
model.setBody(payOrder.getBody()); //订单描述信息
model.setTotalAmount(AmountUtil.convertCent2Dollar(payOrder.getFindAmt().toString())); //支付金额
model.setTimeExpire(DateUtil.format(payOrder.getExpiredTime(), DatePattern.NORM_DATETIME_FORMAT)); // 订单超时时间
req.setNotifyUrl(getNotifyUrl()); // 设置异步通知地址
req.setBizModel(model);
//统一放置 isv接口必传信息
AlipayKit.putApiIsvInfo(mchAppConfigContext, req, model);
//调起支付宝 (如果异常, 将直接跑出 ChannelException
AlipayTradePrecreateResponse alipayResp = configContextQueryService.getAlipayClientWrapper(mchAppConfigContext).execute(req);
// 构造函数响应数据
AliQrOrderRS res = ApiResBuilder.buildSuccess(AliQrOrderRS.class);
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
res.setChannelRetMsg(channelRetMsg);
//放置 响应数据
channelRetMsg.setChannelAttach(alipayResp.getBody());
// ↓↓↓↓↓↓ 调起接口成功后业务判断务必谨慎!! 避免因代码编写bug导致不能正确返回订单状态信息 ↓↓↓↓↓↓
if(alipayResp.isSuccess()){ //处理成功
if(CS.PAY_DATA_TYPE.CODE_IMG_URL.equals(aliQrOrderRQ.getPayDataType())){ //二维码地址
res.setCodeImgUrl(sysConfigService.getDBApplicationConfig().genScanImgUrl(alipayResp.getQrCode()));
}else{ //默认都为跳转地址方式
res.setCodeUrl(alipayResp.getQrCode());
}
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
}else{ //其他状态, 表示下单失败
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
channelRetMsg.setChannelErrCode(AlipayKit.appendErrCode(alipayResp.getCode(), alipayResp.getSubCode()));
channelRetMsg.setChannelErrMsg(AlipayKit.appendErrMsg(alipayResp.getMsg(), alipayResp.getSubMsg()));
}
return res;
}
}

View File

@@ -0,0 +1,86 @@
package com.jeequan.jeepay.thirdparty.channel.alipay.payway;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alipay.api.AlipayApiException;
import com.alipay.api.domain.AlipayTradeWapPayModel;
import com.alipay.api.request.AlipayTradeWapPayRequest;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.exception.ChannelException;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.rqrs.AbstractRS;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.AliWapOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.AliWapOrderRS;
import com.jeequan.jeepay.core.utils.AmountUtil;
import com.jeequan.jeepay.thirdparty.channel.alipay.AlipayKit;
import com.jeequan.jeepay.thirdparty.channel.alipay.AlipayPaymentService;
import com.jeequan.jeepay.thirdparty.util.ApiResBuilder;
import org.springframework.stereotype.Service;
/*
* 支付宝 wap支付
*
* @author terrfly
*
* @date 2021/6/8 17:21
*/
@Service("alipayPaymentByAliWapService") //Service Name需保持全局唯一性
public class AliWap extends AlipayPaymentService {
@Override
public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) {
return null;
}
@Override
public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext){
AliWapOrderRQ bizRQ = (AliWapOrderRQ)rq;
AlipayTradeWapPayRequest req = new AlipayTradeWapPayRequest();
AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
model.setOutTradeNo(payOrder.getPayOrderId());
model.setSubject(payOrder.getSubject()); //订单标题
model.setBody(payOrder.getBody()); //订单描述信息
model.setTotalAmount(AmountUtil.convertCent2Dollar(payOrder.getFindAmt().toString())); //支付金额
model.setTimeExpire(DateUtil.format(payOrder.getExpiredTime(), DatePattern.NORM_DATETIME_FORMAT)); // 订单超时时间
model.setProductCode("QUICK_WAP_PAY");
req.setNotifyUrl(getNotifyUrl()); // 设置异步通知地址
req.setReturnUrl(getReturnUrl()); // 同步跳转地址
req.setBizModel(model);
//统一放置 isv接口必传信息
AlipayKit.putApiIsvInfo(mchAppConfigContext, req, model);
// 构造函数响应数据
AliWapOrderRS res = ApiResBuilder.buildSuccess(AliWapOrderRS.class);
try {
if(CS.PAY_DATA_TYPE.FORM.equals(bizRQ.getPayDataType())){ //表单方式
res.setFormContent(configContextQueryService.getAlipayClientWrapper(mchAppConfigContext).getAlipayClient().getAlipayClient().pageExecute(req).getBody());
}else if (CS.PAY_DATA_TYPE.CODE_IMG_URL.equals(bizRQ.getPayDataType())){ //二维码图片地址
String payUrl = configContextQueryService.getAlipayClientWrapper(mchAppConfigContext).getAlipayClient().getAlipayClient().pageExecute(req, "GET").getBody();
res.setCodeImgUrl(sysConfigService.getDBApplicationConfig().genScanImgUrl(payUrl));
}else{ // 默认都为 payUrl方式
res.setPayUrl(configContextQueryService.getAlipayClientWrapper(mchAppConfigContext).getAlipayClient().getAlipayClient().pageExecute(req, "GET").getBody());
}
}catch (AlipayApiException e) {
throw ChannelException.sysError(e.getMessage());
}
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
res.setChannelRetMsg(channelRetMsg);
//放置 响应数据
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
return res;
}
}

View File

@@ -0,0 +1,477 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.MchApplyment;
import com.jeequan.jeepay.core.entity.MchInfo;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.params.dgpay.DgpayIsvParams;
import com.jeequan.jeepay.core.model.params.dgpay.DgpayIsvsubMchParams;
import com.jeequan.jeepay.core.model.params.dgpay.DgpayNormalMchParams;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.utils.AmountUtil;
import com.jeequan.jeepay.core.utils.DateKit;
import com.jeequan.jeepay.core.utils.ImageUtils;
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import lombok.Cleanup;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;
/**
* 支付通道工具包
*
* @author xiaoyu
* @date 2022/6/6 11:00
*/
@Slf4j
public class DgPayKit {
public static final String STATE_SUCCESS = "00000000";
public static final String STATE_ING = "00000100";
public static final String BIZ_SUCCESS = "SUCCESS";
public static final String STATE_ING_P = "P";
public static final String STATE_SUCCESS_S = "S";
public static final String STATE_FAIL_F = "F";
public static final String BASE_REQ_URL = "https://api.huifu.com/v2";
/**
* 企业商户进件请求地址
**/
public static final String MCH_APPLY_ENT_URL = "/merchant/basicdata/ent";
/**
* 个人商户进件请求地址
**/
public static final String MCH_APPLY_INDV_URL = "/merchant/basicdata/indv";
/**
* 商户信息修改
**/
public static final String MCH_APPLY_MODIFY_URL = "/merchant/basicdata/modify";
public static final String MCH_APPLY_UPDATE_URL = "/merchant/integrate/update";
/**
* 进件状态查询
**/
public static final String MCH_APPLY_QUERY_URL = "/merchant/basicdata/status/query";
/**
* 商户详细信息查询
**/
public static final String MCH_INFO_QUERY_URL = "/merchant/basicdata/query";
/**
* 图片上传
**/
public static final String MCH_UPLOAD_PICTURE_URL = "/supplementary/picture";
/**
* 商户业务开通
**/
public static final String MCH_BUSI_OPEN_URL = "/merchant/busi/open";
/**
* 微信商户配置
**/
public static final String MCH_BUSI_CONFIG_URL = "/merchant/busi/config";
/**
* 结算信息查询
**/
public static final String MCH_BUSI_SETTLE_QUERY_URL = "/merchant/basicdata/settlement/query";
/**
* 智能POS webhook 支付推送事件类型
**/
public static List<String> POS_PAY_EVENT = new ArrayList<>();
static {
POS_PAY_EVENT.add("pay.union_scaned");
POS_PAY_EVENT.add("pay.wx_scaned");
POS_PAY_EVENT.add("pay.ali_scaned");
POS_PAY_EVENT.add("pay.digit_scaned");
POS_PAY_EVENT.add("pay.card_consume.pos");
}
/**
* 智能POS webhook 退款推送事件类型
**/
public static List<String> POS_REFUND_EVENT = new ArrayList<>();
static {
POS_REFUND_EVENT.add("refund.card_consume.pos");
POS_REFUND_EVENT.add("refund.standard");
}
/**
* 重写请求方法
*
* @param reqUrl
* @param bizData
* @return
*/
public static JSONObject req(String reqUrl, DgpayIsvParams isvParams, JSONObject bizData) {
JSONObject reqParams = initReq(isvParams, bizData);
// 生成签名
TreeMap treeMap = JSON.parseObject(bizData.toJSONString(), TreeMap.class);
JSONObject treeJson = (JSONObject) JSON.toJSON(treeMap);
String sign = sign(treeJson.toJSONString(), isvParams.getRsaPrivateKey());
reqParams.put("sign", sign);
String requestUrl = BASE_REQ_URL + reqUrl;
log.info("【斗拱】请求地址:{},请求参数:{}", requestUrl, reqParams);
HttpResponse response = post(requestUrl, reqParams.toJSONString());
log.info("【斗拱】响应结果:{}", response.body());
return initResult(response.body(), isvParams);
}
/**
* 处理统一响应结果
*
* @param result
* @return
*/
private static JSONObject initResult(String result, DgpayIsvParams isvParams) {
JSONObject respData = JSON.parseObject(result);
JSONObject bizData = respData.getJSONObject("data");
TreeMap signData = JSON.parseObject(JSON.toJSONString(bizData), TreeMap.class);
boolean verifyResult = DgPayKit.verify(JSON.toJSONString(signData), isvParams.getRsaPublicKey(), respData.getString("sign"));
if (!verifyResult) {
throw new BizException("签名校验异常");
}
if (!DgPayKit.STATE_SUCCESS.equals(bizData.getString("resp_code"))) {
throw new BizException(bizData.getString("resp_desc"));
}
return bizData;
}
public static JSONObject initReq(DgpayIsvParams isvParams, JSONObject bizData) {
JSONObject reqParams = new JSONObject();
reqParams.put("sys_id", isvParams.getSysId());
reqParams.put("product_id", isvParams.getProductId());
reqParams.put("data", bizData);
return reqParams;
}
/**
* 放置 请求信息
**/
public static JSONObject request(MchAppConfigContext mchAppConfigContext, JSONObject dataParams, String reqUrl, String ifCode) {
//
dataParams.put("req_date", DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN));
dataParams.put("req_seq_id", DateKit.currentTimeMillis());
try {
JSONObject configParams = getConfigParams(mchAppConfigContext, ifCode);
JSONObject reqParams = new JSONObject();
// 请求参数带参
reqParams.put("sys_id", configParams.getString("sysId"));
reqParams.put("product_id", configParams.getString("productId"));
if (dataParams.getBoolean("noMchId") != null && dataParams.getBoolean("noMchId")) {
} else {
if (StringUtils.isEmpty(dataParams.getString("huifu_id")) && StringUtils.isNotEmpty(configParams.getString("huifuId"))) {
dataParams.put("huifu_id", configParams.getString("huifuId"));
}
}
dataParams.remove("noMchId");
// 请求数据
reqParams.put("data", dataParams);
// 生成签名
TreeMap treeMap = JSON.parseObject(dataParams.toJSONString(), TreeMap.class);
JSONObject treeJson = (JSONObject) JSON.toJSON(treeMap);
String sign = sign(treeJson.toJSONString(), configParams.getString("rsaPrivateKey"));
reqParams.put("sign", sign);
String requestUrl = BASE_REQ_URL + reqUrl;
log.info("【斗拱】请求地址:{},请求参数:{}", requestUrl, reqParams);
HttpResponse response = post(requestUrl, reqParams.toJSONString());
String res = response.body();
log.info("【斗拱】响应结果:{}", res);
return JSON.parseObject(res);
} catch (BizException e) {
throw new BizException("[斗拱支付]参数请求异常:" + e.getMessage());
}
}
/**
* 支付请求
**/
public static JSONObject payRequest(MchAppConfigContext mchAppConfigContext, JSONObject dataParams, String reqUrl, String ifCode) {
JSONObject configParams = getConfigParams(mchAppConfigContext, ifCode);
dataParams.put("channel_no", configParams.getString("channelNo"));
dataParams.put("pay_scene", configParams.getString("payScene"));
JSONObject reqParams = new JSONObject();
// 请求参数带参
reqParams.put("sys_id", configParams.getString("sysId"));
reqParams.put("product_id", configParams.getString("productId"));
if (dataParams.getBoolean("noMchId") != null && dataParams.getBoolean("noMchId")) {
} else {
if (StringUtils.isEmpty(dataParams.getString("huifu_id")) && StringUtils.isNotEmpty(configParams.getString("huifuId"))) {
dataParams.put("huifu_id", configParams.getString("huifuId"));
}
}
dataParams.remove("noMchId");
// 请求数据
reqParams.put("data", dataParams);
// 生成签名
TreeMap treeMap = JSON.parseObject(dataParams.toJSONString(), TreeMap.class);
JSONObject treeJson = (JSONObject) JSON.toJSON(treeMap);
String sign = sign(treeJson.toJSONString(), configParams.getString("rsaPrivateKey"));
reqParams.put("sign", sign);
String requestUrl = BASE_REQ_URL + reqUrl;
log.info("【斗拱】请求地址:{},请求参数:{}", requestUrl, reqParams);
HttpResponse response = post(requestUrl, reqParams.toJSONString());
String res = response.body();
log.info("【斗拱】响应结果:{}", res);
JSONObject resp = JSON.parseObject(res);
JSONObject data = resp.getJSONObject("data");
if (!STATE_SUCCESS.equals(data.getString("resp_code")) && !STATE_ING.equals(data.getString("resp_code"))) {
String msg = StringUtils.isNotEmpty(data.getString("bank_message")) ? data.getString("bank_message") : data.getString("resp_desc");
throw new BizException(msg);
}
return resp;
}
/**
* 放置 请求信息
**/
public static String uploadFile(String isvNo, String fileType, String fileUrl, String ifCode) {
try {
if (StringUtils.isEmpty(fileUrl)) {
return "";
}
MchAppConfigContext mchAppConfigContext = new MchAppConfigContext();
mchAppConfigContext.setMchType(MchInfo.TYPE_ISVSUB);
mchAppConfigContext.setMchApplyment(new MchApplyment().setIsvNo(isvNo));
JSONObject configParams = getConfigParams(mchAppConfigContext, ifCode);
JSONObject reqParams = new JSONObject();
// 请求参数带参
reqParams.put("product_id", configParams.getString("productId"));
reqParams.put("sys_id", configParams.getString("sysId"));
JSONObject dataParams = new JSONObject();
dataParams.put("req_seq_id", DateKit.currentTimeMillis());
dataParams.put("req_date", DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN));
dataParams.put("file_type", fileType);
dataParams.put("picture", "1.jpg");
// 请求数据
reqParams.put("data", dataParams);
// 生成签名
TreeMap treeMap = JSON.parseObject(dataParams.toJSONString(), TreeMap.class);
JSONObject treeJson = (JSONObject) JSON.toJSON(treeMap);
String sign = sign(treeJson.toJSONString(), configParams.getString("rsaPrivateKey"));
reqParams.put("sign", sign);
String requestUrl = BASE_REQ_URL + MCH_UPLOAD_PICTURE_URL;
log.info("【斗拱】上传图片,请求地址:{},请求参数:{}", requestUrl, reqParams);
// 下载文件
byte[] imgFileByteArray = null;
try {
ByteArrayOutputStream baos = ImageUtils.compressNetPic(fileUrl, 2048 * 1024);
imgFileByteArray = baos.toByteArray();
} catch (IORuntimeException e) {
log.info("斗拱入网压缩图片异常, 图片链接为{}", fileUrl, e);
throw new BizException("银盛入网压缩图片失败, " + e.getMessage());
}
@Cleanup HttpResponse response = HttpUtil.createPost(requestUrl)
.form("picture", imgFileByteArray, "1.jpg")
.form("sys_id", configParams.getString("sysId"))
.form("product_id", configParams.getString("productId"))
.form("sign", sign)
.form("data", dataParams.toJSONString())
.execute();
String result = response.body();
log.info("【斗拱】上传图片,响应结果:{}", result);
JSONObject resJson = JSON.parseObject(result);
JSONObject resData = resJson.getJSONObject("data");
String respCode = resData.getString("resp_code");
String respDesc = resData.getString("resp_desc");
if (!DgPayKit.STATE_SUCCESS.equals(respCode)) {
throw new BizException(respDesc);
}
return resData.getString("file_id");
} catch (Exception e) {
throw new BizException("[斗拱支付]上传文件请求异常:" + e.getMessage());
}
}
/**
* 公共参数
**/
public static JSONObject dataParams(PayOrder payOrder, String notifyUrl) {
JSONObject dataParams = new JSONObject();
// 请求日期
dataParams.put("req_date", DateUtil.format(payOrder.getCreatedAt(), DatePattern.PURE_DATE_PATTERN));
// 请求流水号
dataParams.put("req_seq_id", payOrder.getPayOrderId());
// 支付金额 元 保留两位小数
dataParams.put("trans_amt", AmountUtil.convertCent2Dollar(payOrder.getFindAmt()));
// 商品描述
dataParams.put("goods_desc", payOrder.getBody());
// 安全信息
JSONObject riskJson = new JSONObject();
riskJson.put("ip_addr", payOrder.getClientIp());
dataParams.put("risk_check_data", riskJson.toJSONString());
// 订单失效时间
dataParams.put("time_expire", DateUtil.format(payOrder.getExpiredTime(), DatePattern.PURE_DATETIME_PATTERN));
// 异步回调地址
dataParams.put("notify_url", notifyUrl);
return dataParams;
}
public static JSONObject getConfigParams(MchAppConfigContext mchAppConfigContext, String ifCode) {
ConfigContextQueryService configContextQueryService = SpringBeansUtil.getBean(ConfigContextQueryService.class);
JSONObject configJson = null;
// 特约商户
if (mchAppConfigContext.isIsvsubMch()) {
DgpayIsvParams isvParams;
isvParams = (DgpayIsvParams) configContextQueryService.queryIsvParams(mchAppConfigContext.getMchApplyment().getIsvNo(), ifCode);
if (isvParams == null) {
throw new BizException("服务商参数未配置");
}
configJson = (JSONObject) JSON.toJSON(isvParams);
DgpayIsvsubMchParams mchParams = (DgpayIsvsubMchParams) configContextQueryService.queryIsvsubMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), ifCode);
if (mchParams != null) {
configJson.put("huifuId", mchAppConfigContext.getMchApplyment().getChannelMchNo());
configJson.put("payScene", StringUtils.defaultString(mchParams.getPayScene(), "02"));
}
configJson.put("productId", isvParams.getProductId());
configJson.put("channelNo", isvParams.getChannelNo());
} else {
// 普通商户
DgpayNormalMchParams mchParams = (DgpayNormalMchParams) configContextQueryService.queryNormalMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), ifCode);
if (mchParams == null) {
throw new BizException("商户参数未配置");
}
configJson = (JSONObject) JSON.toJSON(mchParams);
configJson.put("sysId", mchParams.getHuifuId());
configJson.put("productId", mchParams.getProductId());
configJson.put("payScene", StringUtils.defaultString(mchParams.getPayScene(), "02"));
}
return configJson;
}
/**
* @author: xiaoyu
* @date: 2022/4/13 15:43
* @describe: 发送请求
*/
public static HttpResponse post(String url, String params) {
HttpResponse response = HttpUtil.createPost(url)
.header("content-type", "application/json;charset=utf-8")
.timeout(20000)
.charset("utf-8")
.body(params)
.execute();
return response;
}
public static String sign(String data, String privateKeyBase64) {
try {
byte[] bytes = Base64.getDecoder().decode(privateKeyBase64);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
Signature signature = Signature.getInstance("SHA256WithRSA");
signature.initSign(privateKey);
signature.update(data.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(signature.sign());
} catch (Exception var7) {
var7.printStackTrace();
return null;
}
}
/**
* RSA公钥验签
*
* @param data 待签名字符串
* @param publicKeyBase64 公钥Base64编码
* @return 验签结果
* @throws Exception
*/
public static boolean verify(String data, String publicKeyBase64, String sign) {
// Base64 --> Key
try {
byte[] bytes = Base64.getDecoder().decode(publicKeyBase64);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);
KeyFactory keyFactory;
keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(keySpec);
// verify
Signature signature = Signature.getInstance("SHA256WithRSA");
signature.initVerify(publicKey);
signature.update(data.getBytes(StandardCharsets.UTF_8));
return signature.verify(Base64.getDecoder().decode(sign));
} catch (Exception e) {
log.error("Exception", e);
return false;
}
}
/**
* 转换
*/
public static String covertChannelId(String channelType) {
return StringUtils.defaultIfBlank(channelType, CS.AUTO_POS_PAY_TYPE.CARD).toLowerCase();
}
/**
* 转换
*/
public static String covertMobilePayType(String payType) {
switch (payType) {
case CS.AUTO_POS_PAY_CHANNEL.ALIPAY:
return "A";
case CS.AUTO_POS_PAY_CHANNEL.WECHAT:
return "W";
case CS.AUTO_POS_PAY_CHANNEL.QR_CASHIER:
return "J";
case CS.AUTO_POS_PAY_CHANNEL.DCEP:
return "D";
default:
return "U";
}
}
/**
* 异常信息返回描述处理
**/
public static void channelMsgError(JSONObject resData, ChannelRetMsg channelRetMsg) {
if (StringUtils.isEmpty(resData.getString("bank_message"))) {
channelRetMsg.setChannelErrMsg(resData.get("resp_desc").toString());
channelRetMsg.setChannelErrCode(resData.get("resp_code").toString());
} else {
channelRetMsg.setChannelErrMsg(resData.getString("bank_message"));
channelRetMsg.setChannelErrCode(resData.get("bank_code").toString());
}
}
}

View File

@@ -0,0 +1,301 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.entity.MchApplyment;
import com.jeequan.jeepay.core.entity.MchInfo;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.interfaces.paychannel.IDgApplymentApiService;
import com.jeequan.jeepay.core.model.applyment.DgpayApplymentInfo;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.params.dgpay.DgpayIsvParams;
import com.jeequan.jeepay.core.model.params.dgpay.DgpayIsvsubMchParams;
import com.jeequan.jeepay.core.utils.DateKit;
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
import com.jeequan.jeepay.service.impl.RateConfigService;
import com.jeequan.jeepay.service.impl.SysConfigService;
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.stereotype.Service;
import java.util.Date;
/**
* 斗拱 接口
*
* @author xiaoyu
*
* @date 2022/6/9 9:25
*/
@Service
@Slf4j
public class DgpayApplymentApiService implements IDgApplymentApiService {
@Autowired
protected SysConfigService sysConfigService;
@Autowired
protected RateConfigService rateConfigService;
@Override
public MchApplyment dgpayConfigOpen(MchApplyment mchApplyment, JSONObject paramJSON) {
String logPrefix = "【斗拱商户分账配置】";
MchApplyment result = new MchApplyment();
try {
MchAppConfigContext mchAppConfigContext = new MchAppConfigContext();
mchAppConfigContext.setMchType(MchInfo.TYPE_ISVSUB)
.setMchApplyment(mchApplyment);
JSONObject succResJson = JSON.parseObject(mchApplyment.getSuccResParameter());
if (succResJson == null || StringUtils.isEmpty(succResJson.getString("huifuId"))) {
result.setApplyErrorInfo("无汇付商户号huifu_id请确认是否进件成功");
return result;
}
// 最大分账比例
String applyRatio = paramJSON.getString("applyRatio");
if (StringUtils.isEmpty(applyRatio)) {
result.setApplyErrorInfo("请填写最大分账比例");
return result;
}
// 进件商户号
String huifuId = succResJson.getString("huifuId");
JSONObject reqParams = new JSONObject();
// 汇付ID
reqParams.put("huifu_id", huifuId);
// 请求日期
reqParams.put("req_date", DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN));
// 请求流水号
reqParams.put("req_seq_id", DateKit.currentTimeMillis() + "");
// 分账规则来源 01-接口发起 02-控台配置
reqParams.put("rule_origin", "01");
// 分账是否支持撤销交易
reqParams.put("repeal_flag", "Y");
// 分账是否支持退货交易
reqParams.put("refund_flag", "Y");
// 分账开关
reqParams.put("div_flag", "Y");
// 最大分账比例 未填写分账比例不请求开通分账
reqParams.put("apply_ratio", applyRatio);
// 生效类型
reqParams.put("start_type", "0");
// 发送请求
JSONObject response = DgPayKit.request(mchAppConfigContext, reqParams, "/merchant/split/config", mchApplyment.getIfCode());
JSONObject resData = response.getJSONObject("data");
log.info("{} 响应结果resData={}", logPrefix, resData);
if (!"00000000".equals(resData.getString("resp_code"))) {
result.setApplyErrorInfo(resData.getString("resp_desc"));
}
result.setChannelVar1(paramJSON.toJSONString());
} catch (BizException e) {
result.setApplyErrorInfo("[" + e.getMessage() + "]");
log.error("{} 操作失败:", logPrefix, e);
} catch (Exception e) {
result.setApplyErrorInfo("[" + e.getMessage() + "]");
log.error("{} 操作失败:", logPrefix, e);
}
return result;
}
@Override
public MchApplyment dgpayConfigOpenQuery(MchApplyment mchApplyment) {
String logPrefix = "【斗拱商户分账配置查询】";
MchApplyment result = new MchApplyment();
try {
MchAppConfigContext mchAppConfigContext = new MchAppConfigContext();
mchAppConfigContext.setMchType(MchInfo.TYPE_ISVSUB);
mchAppConfigContext.setMchApplyment(mchApplyment);
JSONObject succResJson = JSON.parseObject(mchApplyment.getSuccResParameter());
if (succResJson == null || StringUtils.isEmpty(succResJson.getString("huifuId"))) {
result.setApplyErrorInfo("无汇付商户号huifu_id请确认是否进件成功");
return result;
}
// 进件商户号
String huifuId = succResJson.getString("huifuId");
JSONObject reqParams = new JSONObject();
// 汇付ID
reqParams.put("huifu_id", huifuId);
// 请求日期
reqParams.put("req_date", DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN));
// 请求流水号
reqParams.put("req_seq_id", DateKit.currentTimeMillis() + "_" + huifuId);
// 发送请求
JSONObject response = DgPayKit.request(mchAppConfigContext, reqParams, "/merchant/split/query", mchApplyment.getIfCode());
JSONObject resData = response.getJSONObject("data");
log.info("{} 响应结果resData={}", logPrefix, resData);
result.setChannelVar2(resData.toJSONString());
} catch (BizException e) {
result.setApplyErrorInfo("[" + e.getMessage() + "]");
log.error("{} 操作失败:", logPrefix, e);
} catch (Exception e) {
result.setApplyErrorInfo("[" + e.getMessage() + "]");
log.error("{} 操作失败:", logPrefix, e);
}
return result;
}
@Override
public MchApplyment wechatConfigSet(String isvNo, String mchNo, String appId, String jsapiPath, String feeType, String configWxBindLiteAppId, String configWxBindAppId, String bankChannelNo, String ifCode) {
String logPrefix = "【斗拱商户微信配置】";
MchApplyment result = new MchApplyment();
try {
ConfigContextQueryService configContextQueryService = SpringBeansUtil.getBean(ConfigContextQueryService.class);
// 获取支付参数
DgpayIsvParams isvParams = (DgpayIsvParams) configContextQueryService.queryIsvParams(isvNo, ifCode);
MchAppConfigContext mchAppConfigContext = new MchAppConfigContext();
mchAppConfigContext.setMchType(MchInfo.TYPE_ISVSUB).setMchApplyment(new MchApplyment().setIsvNo(isvNo));
DgpayIsvsubMchParams mchParams = (DgpayIsvsubMchParams) configContextQueryService.queryIsvsubMchParams(mchNo, appId, ifCode);
if (mchParams == null || StringUtils.isEmpty(mchParams.getHuifuId())) {
result.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
result.setApplyErrorInfo("商户参数未配置,请先配置商户应用参数");
return result;
}
// 进件商户号
String huifuId = mchParams.getHuifuId();
// 请求参数
JSONObject reqParams = new JSONObject();
// 汇付ID
reqParams.put("huifu_id", huifuId);
// 产品编号
reqParams.put("product_id", isvParams.getProductId());
// 请求日期
reqParams.put("req_date", DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN));
// 请求流水号
reqParams.put("req_seq_id", DateKit.currentTimeMillis() + "");
// 业务开通类型
reqParams.put("fee_type", feeType);
// 授权目录
reqParams.put("wx_woa_path", jsapiPath);
// 微信小程序APPID
reqParams.put("wx_applet_app_id", configWxBindLiteAppId);
// 公众号APPID
reqParams.put("wx_woa_app_id", configWxBindAppId);
if (StringUtils.isNotEmpty(bankChannelNo)) {
// 渠道号
reqParams.put("bank_channel_no", bankChannelNo);
}
// 发送请求
JSONObject response = DgPayKit.request(mchAppConfigContext, reqParams, DgPayKit.MCH_BUSI_CONFIG_URL, ifCode);
JSONObject resData = response.getJSONObject("data");
String code = resData.getString("resp_code");
if (DgPayKit.STATE_SUCCESS.equals(code)) {
result.setState(MchApplyment.STATE_SUCCESS);
} else {
result.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
result.setApplyErrorInfo(code + "[" + resData.getString("resp_desc") + "]");
log.error("{} 请求失败code={}, msg={}", logPrefix, code, resData.getString("resp_desc"));
return result;
}
} catch (Exception e) {
result.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
result.setApplyErrorInfo("[" + e.getMessage() + "]");
log.error("{} 配置异常:", logPrefix, e);
}
return result;
}
@Override
public MchApplyment wechatRealName(MchApplyment mchApplyment) {
String logPrefix = "【斗拱商户微信实名认证】";
MchApplyment result = new MchApplyment();
try {
// 获取支付参数
MchAppConfigContext mchAppConfigContext = new MchAppConfigContext();
mchAppConfigContext.setMchType(MchInfo.TYPE_ISVSUB);
mchAppConfigContext.setMchApplyment(mchApplyment);
JSONObject succResJson = JSON.parseObject(mchApplyment.getSuccResParameter());
if (succResJson == null || StringUtils.isEmpty(succResJson.getString("huifuId"))) {
result.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
result.setApplyErrorInfo("商户参数未配置,请先配置商户应用参数");
return result;
}
// 进件商户号
String huifuId = succResJson.getString("huifuId");
// 获取详细参数
DgpayApplymentInfo info = JSON.parseObject(mchApplyment.getApplyDetailInfo(), DgpayApplymentInfo.class);
// 请求参数
JSONObject reqParams = new JSONObject();
// 汇付ID
reqParams.put("huifu_id", huifuId);
// 请求日期
reqParams.put("req_date", DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN));
// 请求流水号
reqParams.put("req_seq_id", DateKit.currentTimeMillis() + "");
// 联系人姓名
reqParams.put("name", info.getIdcardName());
// 联系人手机号
reqParams.put("mobile", info.getContactPhone());
// 联系人身份证号
reqParams.put("id_card_number", info.getIdcardNo());
// 法人身份证人像面照片
reqParams.put("identification_front_copy", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F02", info.getIdcard1Img(), mchApplyment.getIfCode()));
// 法人身份证国徽面照片
reqParams.put("identification_back_copy", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F03", info.getIdcard2Img(), mchApplyment.getIfCode()));
// 证书类型 【营业执照】
reqParams.put("cert_type", "CERTIFICATE_TYPE_2392");
// 证书编号
reqParams.put("cert_number", info.getLicenseNo());
// 证书照片
reqParams.put("cert_copy", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F07", info.getLicenseImg(), mchApplyment.getIfCode()));
// 小微经营类型
reqParams.put("micro_biz_type", info.getMicroBizType());
// 门店名称
reqParams.put("store_name", info.getMchShortName());
// 门店门头照
reqParams.put("store_header_copy", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F49", info.getStoreOuterImg(), mchApplyment.getIfCode()));
// 门店环境照
reqParams.put("store_indoor_copy", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F50", info.getStoreInnerImg(), mchApplyment.getIfCode()));
// 门店省市编码
reqParams.put("store_address_code", info.getAreaCode().get(1));
// 门店地址
reqParams.put("store_address", info.getMchShortName());
// 发送请求
JSONObject response = DgPayKit.request(mchAppConfigContext, reqParams, "/merchant/busi/realname", mchApplyment.getIfCode());
JSONObject resData = response.getJSONObject("data");
String code = resData.getString("resp_code");
if (DgPayKit.STATE_SUCCESS.equals(code)) {
// String channelOrderNo = resData.getString("applyment_id");
// result.setChannelApplyNo(channelOrderNo);
result.setState(MchApplyment.STATE_SUCCESS);
} else {
result.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
result.setApplyErrorInfo(code + "[" + resData.getString("resp_desc") + "]");
log.error("{} 请求失败code={}, msg={}", logPrefix, code, resData.getString("resp_desc"));
return result;
}
} catch (Exception e) {
result.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
result.setApplyErrorInfo("[" + e.getMessage() + "]");
log.error("{} 实名认证失败:", logPrefix, e);
}
return result;
}
}

View File

@@ -0,0 +1,204 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Pair;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.ChannelAccountCashoutRecord;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.model.cashout.CashoutRetMsg;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.params.dgpay.DgpayIsvParams;
import com.jeequan.jeepay.core.model.params.dgpay.DgpayIsvsubMchParams;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.utils.AmountUtil;
import com.jeequan.jeepay.core.utils.DateKit;
import com.jeequan.jeepay.thirdparty.channel.AbstractChannelAccountService;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Date;
/**
* 提现接口: 斗拱
*
* @author zx
*
* @date 2023/6/14 16:19
*/
@Slf4j
@Service
public class DgpayChannelAccountService extends AbstractChannelAccountService {
@Autowired private ConfigContextQueryService configContextQueryService;
@Override
public String getIfCode() {
return CS.IF_CODE.DGPAY;
}
@Override
public Pair<String, Long> queryBalanceAmount(MchAppConfigContext mchAppConfigContext, String ifCode) {
String logPrefix = "斗拱商户余额查询";
DgpayIsvsubMchParams subMchParams = (DgpayIsvsubMchParams) configContextQueryService.queryIsvsubMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), ifCode);
if (subMchParams == null) {
throw new BizException("斗拱商户余额查询,商户参数未配置");
}
JSONObject reqParams = new JSONObject();
// 请求日期
reqParams.put("req_date", DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN));
// 请求流水号
reqParams.put("req_seq_id", DateKit.currentTimeMillis());
// 商户号
reqParams.put("huifu_id", subMchParams.getHuifuId());
log.info(logPrefix);
JSONObject response = DgPayKit.request(mchAppConfigContext, reqParams, "/trade/acctpayment/balance/query", ifCode);
JSONObject jsonData = response.getJSONObject("data");
Long balanceAmt = 0L;
// 响应成功
if (DgPayKit.STATE_SUCCESS.equals(jsonData.getString("resp_code"))) {
// 账户信息列表
JSONArray infoList = jsonData.getJSONArray("acctInfo_list");
for (int i = 0; i < infoList.size(); i++) {
JSONObject infoJson = JSONObject.parseObject(infoList.get(i).toString());
// 查询基本账户类型
if ("01".equals(infoJson.getString("acct_type"))) {
balanceAmt = Long.parseLong(AmountUtil.convertDollar2Cent(infoJson.getString("balance_amt")));
}
}
}
return new Pair<>(subMchParams.getHuifuId(), balanceAmt);
}
@Override
public CashoutRetMsg cashout(ChannelAccountCashoutRecord cashoutRecord, MchAppConfigContext mchAppConfigContext, String ifCode) {
String logPrefix = "斗拱商户提现";
CashoutRetMsg cashoutRetMsg = new CashoutRetMsg();
DgpayIsvParams isvParams = (DgpayIsvParams) configContextQueryService.queryIsvParams(mchAppConfigContext.getMchApplyment().getIsvNo(), ifCode);
DgpayIsvsubMchParams subMchParams = (DgpayIsvsubMchParams) configContextQueryService.queryIsvsubMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), ifCode);
if (subMchParams == null) {
throw new BizException("斗拱商户提现,商户参数未配置");
}
try {
if ("0".equals(isvParams.getMchSettManual())) {
throw new BizException("斗拱商户提现,服务商未开启取现配置");
}
JSONObject reqParams = new JSONObject();
// 请求流水号
reqParams.put("req_seq_id", cashoutRecord.getRid());
// 请求日期
reqParams.put("req_date", DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN));
// 商户号
reqParams.put("huifu_id", subMchParams.getHuifuId());
// 到账类型
reqParams.put("into_acct_date_type", isvParams.getMchSettManual());
// 取现卡序列号
reqParams.put("token_no", subMchParams.getTokenNo());
// 取现金额
reqParams.put("cash_amt", AmountUtil.convertCent2Dollar(cashoutRecord.getCashoutAmount()));
// 异步通知地址
reqParams.put("notify_url", getNotifyUrl());
log.info(logPrefix);
JSONObject response = DgPayKit.request(mchAppConfigContext, reqParams, "/trade/settlement/enchashment", ifCode);
JSONObject dataJSON = response.getJSONObject("data");
// 响应成功
if (DgPayKit.STATE_SUCCESS.equals(dataJSON.getString("resp_code"))) {
// 交易状态 S成功 F失败 P处理中
String transStat = dataJSON.getString("trans_stat");
if (DgPayKit.STATE_SUCCESS_S.equals(transStat)) {
cashoutRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
cashoutRetMsg.setChannelOrderId(dataJSON.getString("hf_seq_id")); // 渠道提现单号
}else if (DgPayKit.STATE_FAIL_F.equals(transStat)) {
cashoutRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
cashoutRetMsg.setChannelErrCode(dataJSON.getString("resp_code"));
cashoutRetMsg.setChannelErrMsg(dataJSON.getString("resp_desc"));
}else {
cashoutRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
}
}else {
cashoutRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
cashoutRetMsg.setChannelErrMsg(dataJSON.getString("resp_desc"));
}
return cashoutRetMsg;
} catch (Exception e) {
cashoutRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
cashoutRetMsg.setChannelErrMsg(e.getMessage());
return cashoutRetMsg;
}
}
@Override
public CashoutRetMsg cashoutQuery(ChannelAccountCashoutRecord cashoutRecord, MchAppConfigContext mchAppConfigContext, String ifCode) {
String logPrefix = "斗拱商户提现结果查询";
CashoutRetMsg cashoutRetMsg = new CashoutRetMsg();
DgpayIsvsubMchParams subMchParams = (DgpayIsvsubMchParams) configContextQueryService.queryIsvsubMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), ifCode);
if (subMchParams == null) {
throw new BizException("斗拱商户提现结果查询失败,商户参数未配置");
}
try {
JSONObject reqParams = new JSONObject();
// 商户号
reqParams.put("huifu_id", subMchParams.getHuifuId());
// 原交易请求日期
reqParams.put("org_req_date", DateUtil.format(cashoutRecord.getCreatedAt(), DatePattern.PURE_DATE_PATTERN));
// 原交易请求流水号
reqParams.put("org_req_seq_id", cashoutRecord.getRid());
log.info(logPrefix);
JSONObject response = DgPayKit.request(mchAppConfigContext, reqParams, "/trade/settlement/query", ifCode);
JSONObject dataJSON = response.getJSONObject("data");
// 响应成功
if (DgPayKit.STATE_SUCCESS.equals(dataJSON.getString("resp_code"))) {
// 交易状态 S成功 F失败 P处理中
String transStat = dataJSON.getString("trans_status");
if (DgPayKit.STATE_SUCCESS_S.equals(transStat)) {
cashoutRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
cashoutRetMsg.setChannelOrderId(dataJSON.getString("hf_seq_id")); // 渠道提现单号
}else if (DgPayKit.STATE_FAIL_F.equals(transStat)) {
cashoutRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
cashoutRetMsg.setChannelErrCode(dataJSON.getString("resp_code"));
cashoutRetMsg.setChannelErrMsg(dataJSON.getString("resp_desc"));
}else {
cashoutRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
}
}else {
cashoutRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
cashoutRetMsg.setChannelErrMsg(dataJSON.getString("resp_desc"));
}
return cashoutRetMsg;
} catch (Exception e) {
cashoutRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
cashoutRetMsg.setChannelErrMsg(e.getMessage());
return cashoutRetMsg;
}
}
}

View File

@@ -0,0 +1,146 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.ChannelAccountCashoutRecord;
import com.jeequan.jeepay.core.exception.ResponseException;
import com.jeequan.jeepay.core.model.cashout.CashoutRetMsg;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.params.dgpay.DgpayIsvParams;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.utils.AmountUtil;
import com.jeequan.jeepay.thirdparty.channel.AbstractChannelCashoutNoticeService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.MutablePair;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
/**
* 斗拱提现回调
*
* @author zx
*
* @date 2023-06-14 07:15
*/
@Service
@Slf4j
public class DgpayChannelCashoutNoticeService extends AbstractChannelCashoutNoticeService {
@Override
public String getIfCode() {
return CS.IF_CODE.DGPAY;
}
@Override
public MutablePair<String, Object> parseParams(HttpServletRequest request, String urlRid) {
try {
// 回调参数body【回调参数顺序固定】
JSONObject reqParamJSON = getReqParamJSON();
log.info("【斗拱提现回调参数】{}", reqParamJSON);
JSONObject respData = reqParamJSON.getJSONObject("resp_data");
String rid = respData.getString("req_seq_id");
log.info("【斗拱提现回调参数】{}", respData);
return MutablePair.of(rid, reqParamJSON);
} catch (Exception e) {
log.error("error", e);
throw ResponseException.buildText("ERROR");
}
}
@Override
public CashoutRetMsg doNotice(HttpServletRequest request, Object params, ChannelAccountCashoutRecord cashoutRecord, MchAppConfigContext mchAppConfigContext) {
try {
String logPrefix = "处理斗拱提现回调";
CashoutRetMsg cashoutRetMsg = new CashoutRetMsg();
JSONObject jsonParams = (JSONObject) params;
log.info("{}参数:{}", logPrefix, jsonParams);
// 业务失败
if (!"10000".equals(jsonParams.getString("resp_code"))) {
log.error("{} 业务失败,原因:{}", logPrefix, jsonParams.getString("resp_desc"));
throw ResponseException.buildText("ERROR");
}
String respDataStr = jsonParams.getString("resp_data");
JSONObject respDataJSON = JSONObject.parseObject(respDataStr);
String sign = jsonParams.getString("sign");
// 交易状态 S成功 F失败 P处理中
String transStat = respDataJSON.getString("trans_status");
if (DgPayKit.STATE_SUCCESS_S.equals(transStat)) {
cashoutRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
cashoutRetMsg.setChannelOrderId(respDataJSON.getString("hf_seq_id")); // 渠道提现单号
}else if (DgPayKit.STATE_FAIL_F.equals(transStat)) {
cashoutRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
cashoutRetMsg.setChannelErrCode(respDataJSON.getString("sub_resp_code"));
cashoutRetMsg.setChannelErrMsg(respDataJSON.getString("sub_resp_desc"));
}else {
cashoutRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
}
// 返回上游内容
cashoutRetMsg.setResponseEntity(textResp("SUCCESS"));
// 校验提现回调
boolean verifyResult = verifyParams(respDataStr, cashoutRecord, mchAppConfigContext, sign);
// 验证参数失败
if(!verifyResult){
log.info("{}验证提现通知数据或签名失败", logPrefix);
throw ResponseException.buildText("ERROR");
}
cashoutRetMsg.setChannelOrderId(jsonParams.getString("acquireOrderNo"));
cashoutRetMsg.setBankName(jsonParams.getString("bankName"));
cashoutRetMsg.setBankAccount(jsonParams.getString("bankAccount"));
cashoutRetMsg.setBankAccountName(jsonParams.getString("bankAccountName"));
return cashoutRetMsg;
} catch (Exception e) {
log.error("error", e);
throw ResponseException.buildText("ERROR");
}
}
/**
* 验证斗拱提现通知参数
* @return
*/
public boolean verifyParams(String respDataStr, ChannelAccountCashoutRecord cashoutRecord, MchAppConfigContext mchAppConfigContext, String sign) {
// 提现金额
String txnAmt = JSONObject.parseObject(respDataStr).getString("cash_amt");
if (StringUtils.isEmpty(txnAmt)) {
log.info("金额参数为空 [txnAmt] :{}", txnAmt);
return false;
}
// 核对金额
long dbPayAmt = cashoutRecord.getCashoutAmount();
if (dbPayAmt != AmountUtil.convertDollar2CentLong(txnAmt)) {
log.info("订单金额与参数金额不符。 dbPayAmt={}, txnAmt={}, rid={}", dbPayAmt, txnAmt, cashoutRecord.getRid());
return false;
}
// 公钥参数
DgpayIsvParams isvParams = (DgpayIsvParams) configContextQueryService.queryIsvParams(mchAppConfigContext.getMchApplyment().getIsvNo(), cashoutRecord.getIfCode());
// 验证签名
boolean verifyResult = DgPayKit.verify(respDataStr, isvParams.getRsaPublicKey(), sign);
if(!verifyResult){
log.info("【斗拱提现回调】 验签失败! 回调参数respDataStr = {}", respDataStr);
return false;
}
return true;
}
}

View File

@@ -0,0 +1,115 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.exception.ResponseException;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.params.dgpay.DgpayIsvParams;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.thirdparty.channel.AbstractChannelNoticeService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.MutablePair;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
/**
* 斗拱 回调接口实现类
*
* @author xiaoyu
*
* @date 2022/6/13 15:58
*/
@Service
@Slf4j
public class DgpayChannelNoticeService extends AbstractChannelNoticeService {
@Override
public String getIfCode() {
return CS.IF_CODE.DGPAY;
}
@Override
public MutablePair<String, Object> parseParams(HttpServletRequest request, String urlOrderId, NoticeTypeEnum noticeTypeEnum) {
try {
// 回调参数body【回调参数顺序固定】
JSONObject reqParamJSON = getReqParamJSON();
JSONObject dataJson = reqParamJSON.getJSONObject("resp_data");
String payOrderId = dataJson.getString("req_seq_id");
return MutablePair.of(payOrderId, reqParamJSON);
} catch (Exception e) {
log.error("error", e);
throw ResponseException.buildText("ERROR");
}
}
@Override
public ChannelRetMsg doNotice(HttpServletRequest request, Object params, PayOrder payOrder, MchAppConfigContext mchAppConfigContext, NoticeTypeEnum noticeTypeEnum) {
try {
String logPrefix = "【处理[斗拱]支付回调】";
JSONObject jsonParams = (JSONObject) params;
String dataJson = jsonParams.getString("resp_data");
String sign = jsonParams.getString("sign");
log.info("{} 回调参数, dataJson{}, sign:{}", logPrefix, dataJson, sign);
// 公钥参数
DgpayIsvParams isvParams = (DgpayIsvParams) configContextQueryService.queryIsvParams(mchAppConfigContext.getMchApplyment().getIsvNo(), payOrder.getIfCode());
// 验证签名
boolean verifyResult = DgPayKit.verify(dataJson, isvParams.getRsaPublicKey(), sign);
//验签失败
if(!verifyResult){
throw ResponseException.buildText("ERROR");
}
//验签成功后判断上游订单状态
ResponseEntity okResponse = textResp("SUCCESS");
ChannelRetMsg result = new ChannelRetMsg();
result.setChannelBizData(jsonParams);
// 默认支付中
result.setChannelState(ChannelRetMsg.ChannelState.WAITING);
// 获取请求参数
JSONObject respData = jsonParams.getJSONObject("resp_data");
if(DgPayKit.STATE_SUCCESS_S.equals(respData.getString("trans_stat"))){
result.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
}else if(DgPayKit.STATE_FAIL_F.equals(respData.getString("trans_stat"))){
result.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
// 交易状态失败存储
DgPayKit.channelMsgError(respData, result);
}
/***
* D-借记卡C-贷记卡0-其他示例值D
*/
String debit_type = respData.getString("debit_type");
if(StringUtils.isEmpty(debit_type) || "0".equals(debit_type)){
result.setDrType(CS.DrType.OTHER.getType());
}else if("D".equals(debit_type)){
result.setDrType(CS.DrType.DEBIT.getType());
}else if("C".equals(debit_type)){
result.setDrType(CS.DrType.CREDIT.getType());
}else{
result.setDrType(CS.DrType.OTHER.getType());
}
result.setResponseEntity(okResponse);
result.setChannelOrderId(respData.getString("hf_seq_id"));
result.setPlatformOrderNo(respData.getString("out_trans_id"));
result.setPlatformMchOrderNo(respData.getString("party_order_id"));
return result;
} catch (Exception e) {
log.error("error", e);
throw ResponseException.buildText("ERROR");
}
}
}

View File

@@ -0,0 +1,161 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.exception.ResponseException;
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.params.IsvParams;
import com.jeequan.jeepay.core.model.params.dgpay.DgpayIsvParams;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.utils.AmountUtil;
import com.jeequan.jeepay.core.utils.JsonKit;
import com.jeequan.jeepay.thirdparty.channel.AbstractChannelOrderAcceptService;
import com.jeequan.jeepay.thirdparty.channel.dgpay.utils.MD5Utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
/**
* 斗拱智能POS回调
*
* @author zx
*
* @date 2021-06-07 07:15
*/
@Service
@Slf4j
public class DgpayChannelOrderAcceptService extends AbstractChannelOrderAcceptService {
@Override
public String getIfCode() {
return CS.IF_CODE.DGPAY;
}
@Override
public ChannelOrderAcceptParams parseParams(HttpServletRequest request, IsvParams isvParams) {
JSONObject params = getReqParamJSON();
// 获取事件类型
String eventDefineNo = params.getString("event_define_no");
String tradeType = ""; // 交易类型 收款-PAYMENT 退款-REFUND
if (DgPayKit.POS_PAY_EVENT.contains(eventDefineNo)) {
tradeType = ChannelOrderAcceptParams.TRADE_TYPE_PAYMENT;
} else if (DgPayKit.POS_REFUND_EVENT.contains(eventDefineNo)) {
tradeType = ChannelOrderAcceptParams.TRADE_TYPE_REFUND;
}
log.info("斗拱智能POS【订单通知模式】通知参数tradeType={}params={}", tradeType, params.toJSONString());
return ChannelOrderAcceptParams.buildAutoPosParams(tradeType, params.getString("devs_id"), params);
}
@Override
public ChannelOrderAcceptRetMsg payNotice(HttpServletRequest request, Object params, MchAppConfigContext mchAppConfigContext, String ifCode) throws Exception {
String logPrefix = "处理斗拱智能POS【订单通知模式】支付通知";
ChannelOrderAcceptRetMsg result = new ChannelOrderAcceptRetMsg();
result.setChannelState(ChannelRetMsg.ChannelState.WAITING);
// 获取请求参数
JSONObject jsonParams = (JSONObject) params;
// 验签
boolean verifyResult = validateSign(jsonParams, mchAppConfigContext, request.getParameterMap().get("sign")[0], ifCode);
if(!verifyResult){
log.info("{}验证通知数据及签名失败", logPrefix);
throw ResponseException.buildText4HttpStatus500("FAIL");
}
// 支付状态
String status = jsonParams.getString("trans_stat");
// 支付成功
if (StringUtils.equals(DgPayKit.STATE_SUCCESS_S, status)) {
result.setResponseEntity(textResp("SUCCESS")); // 通知上游处理成功
result.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
JSONObject transOrderInfo = jsonParams.getJSONObject("trans_order_info");
result.setChannelOrderId(transOrderInfo.getString("mer_ord_id")); // 渠道订单号,必填
result.setChannelBizData(JsonKit.newJson("reqSeqId", transOrderInfo.getString("req_seq_id"))); // 分账使用 渠道特殊参数
result.setChannelAmount(AmountUtil.convertDollar2CentLong(jsonParams.getString("trans_amt"))); // 订单金额,单位分,必填
result.setBizOrderId(transOrderInfo.getString("mer_ord_id"));
result.setChannelWayCode(CS.PAY_WAY_CODE.AUTO_POS); // 支付方式,必填
result.setChannelWayCodeType(covertWayCodeType(jsonParams.getString("event_define_no"))); // 支付方式类型,必填
result.setChannelDivisionMode(jsonParams.getByte("is_delay_acct"));
}
return result;
}
@Override
public ChannelOrderAcceptRetMsg refundNotice(HttpServletRequest request, Object params, MchAppConfigContext mchAppConfigContext, String ifCode) {
try {
String logPrefix = "处理斗拱智能POS【订单通知模式】退款通知";
ChannelOrderAcceptRetMsg result = new ChannelOrderAcceptRetMsg();
result.setChannelState(ChannelRetMsg.ChannelState.WAITING);
// 获取请求参数
JSONObject jsonParams = (JSONObject) params;
// 验签
boolean verifyResult = validateSign(jsonParams, mchAppConfigContext, request.getParameterMap().get("sign")[0], ifCode);
if(!verifyResult){
log.info("{}验证通知数据及签名失败", logPrefix);
throw ResponseException.buildText4HttpStatus500("FAIL");
}
// 退款状态
String status = jsonParams.getString("trans_stat");
// 退款成功
if (StringUtils.equals(DgPayKit.STATE_SUCCESS_S, status)) {
result.setResponseEntity(textResp("SUCCESS")); // 通知上游处理成功
result.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
result.setChannelOrderId(jsonParams.getString("mer_ord_id")); // 渠道订单号,必填
result.setChannelAmount(AmountUtil.convertDollar2CentLong(jsonParams.getString("ord_amt"))); // 订单金额,单位分,必填
result.setBizOrderId(jsonParams.getString("mer_ord_id"));
result.setChannelPayOrderId(jsonParams.getString("org_term_ord_id")); // 原渠道订单号(和支付订单渠道订单号一致),退款必填
result.setChannelDivisionRefundMode((byte) 1);
}
return result;
} catch (Exception e) {
log.error("error", e);
throw ResponseException.buildText4HttpStatus500("FAIL");
}
}
/**
* 验签
* @return
*/
public boolean validateSign(JSONObject jsonParams, MchAppConfigContext mchAppConfigContext, String sign, String ifCode) throws Exception {
DgpayIsvParams isvParams =
(DgpayIsvParams) configContextQueryService.queryIsvParams(mchAppConfigContext.getMchApplyment().getIsvNo(), ifCode);
return MD5Utils.verifySign(jsonParams.toJSONString(), isvParams.getWebhookPrivateKey(), sign);
}
private String covertWayCodeType(String eventDefineNo) {
switch (eventDefineNo) {
case "pay.ali_scaned": return CS.PAY_WAY_CODE_TYPE.ALIPAY;
case "pay.wx_scaned": return CS.PAY_WAY_CODE_TYPE.WECHAT;
case "pay.union_scaned":
case "pay.card_consume.pos":
case "pay.digit_scaned":
return CS.PAY_WAY_CODE_TYPE.UNIONPAY;
default: return CS.PAY_WAY_CODE_TYPE.OTHER;
}
}
}

View File

@@ -0,0 +1,137 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.RefundOrder;
import com.jeequan.jeepay.core.exception.ResponseException;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.params.dgpay.DgpayIsvParams;
import com.jeequan.jeepay.core.model.params.yspay.YspayIsvParams;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
import com.jeequan.jeepay.thirdparty.channel.AbstractChannelRefundNoticeService;
import com.jeequan.jeepay.thirdparty.channel.yspay.utils.SignRsaUtil;
import com.jeequan.jeepay.thirdparty.channel.yspay.utils.SignSmUtil;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import com.jeequan.jeepay.thirdparty.util.ChannelCertConfigKitBean;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.MutablePair;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.io.FileInputStream;
import java.util.Map;
/**
* 斗拱退款回调接口实现类
*
* @author xiaoyu
*
* @date 2021/12/24 11:31
*/
@Service
@Slf4j
public class DgpayChannelRefundNoticeService extends AbstractChannelRefundNoticeService {
@Override
public String getIfCode() {
return CS.IF_CODE.DGPAY;
}
@Override
public MutablePair<String, Object> parseParams(HttpServletRequest request, String urlOrderId, NoticeTypeEnum noticeTypeEnum) {
try {
// 回调参数body【回调参数顺序固定】
JSONObject reqParamJSON = getReqParamJSON();
JSONObject dataJson = reqParamJSON.getJSONObject("resp_data");
String payOrderId = dataJson.getString("req_seq_id");
return MutablePair.of(payOrderId, reqParamJSON);
} catch (Exception e) {
log.error("error", e);
throw ResponseException.buildText("ERROR");
}
}
@Override
public ChannelRetMsg doNotice(HttpServletRequest request, Object params, RefundOrder refundOrder, MchAppConfigContext mchAppConfigContext, NoticeTypeEnum noticeTypeEnum) {
try {
// 获取请求参数
JSONObject jsonParams = (JSONObject) params;
String dataJson = jsonParams.getString("resp_data");
String sign = jsonParams.getString("sign");
log.info("【斗拱】退款回调参数:{} ", jsonParams);
// 公钥参数
DgpayIsvParams isvParams = (DgpayIsvParams) configContextQueryService.queryIsvParams(mchAppConfigContext.getMchApplyment());
// 验证签名
boolean verifyResult = DgPayKit.verify(dataJson, isvParams.getRsaPublicKey(), sign);
//验签失败
if(!verifyResult){
throw ResponseException.buildText("ERROR");
}
//验签成功后判断上游订单状态
ResponseEntity okResponse = textResp("SUCCESS");
ChannelRetMsg result = new ChannelRetMsg();
// 默认退款中
result.setChannelState(ChannelRetMsg.ChannelState.WAITING);
// 获取请求参数
JSONObject respData = jsonParams.getJSONObject("resp_data");
if(DgPayKit.STATE_SUCCESS_S.equals(respData.getString("trans_stat"))){
result.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
}else if(DgPayKit.STATE_FAIL_F.equals(respData.getString("trans_stat"))){
result.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
// 交易状态失败存储
DgPayKit.channelMsgError(respData, result);
}
result.setResponseEntity(okResponse);
result.setChannelOrderId(respData.getString("hf_seq_id"));
result.setPlatformMchOrderNo(respData.getString("party_order_id"));
return result;
} catch (Exception e) {
log.error("error", e);
throw ResponseException.buildText("ERROR");
}
}
/**
* 验证银盛退款通知参数
* @return
*/
public boolean verifyParams(JSONObject jsonParams, MchAppConfigContext mchAppConfigContext) {
try {
log.info("【银盛】退款回调参数: jsonParams{}", jsonParams);
String sign = jsonParams.getString("sign");
Map<String, String> paramsMap = jsonParams.toJavaObject(Map.class);
if (StringUtils.isEmpty(sign)) {
log.info("验签参数为空sign{}", sign);
return false;
}
// 校验支付回调
ConfigContextQueryService configContextQueryService = SpringBeansUtil.getBean(ConfigContextQueryService.class);
YspayIsvParams isvParams = (YspayIsvParams) configContextQueryService.queryIsvParams(mchAppConfigContext.getMchApplyment());
// 证书地址
ChannelCertConfigKitBean channelCertConfigKitBean = SpringBeansUtil.getBean(ChannelCertConfigKitBean.class);
String certFilePath = channelCertConfigKitBean.getCertFilePath(isvParams.getPublicKeyFile());
boolean verifyResult = false;
if ("RSA".equals(isvParams.getSignType())) {
FileInputStream fileInputStream = new FileInputStream(channelCertConfigKitBean.getCertFile(isvParams.getPublicKeyFile()));
verifyResult = SignRsaUtil.asynVerifyYsRsa(fileInputStream, paramsMap);
}else if ("SM".equals(isvParams.getSignType())){
verifyResult = SignSmUtil.asynVerifyYsSm(certFilePath, paramsMap);
}else {
log.error("未识别的签名类型");
}
//验签失败
if(!verifyResult) {
log.info("【银盛】退款回调验签失败! 回调参数parameter = {} ", jsonParams);
return false;
}
return true;
}catch (Exception e) {
return false;
}
}
}

View File

@@ -0,0 +1,84 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.interfaces.paychannel.IChannelUserService;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.params.dgpay.DgpayIsvsubMchParams;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelUserInfoMsg;
import com.jeequan.jeepay.core.utils.DateKit;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Date;
/**
* 获取银联userId实现类
*
* @author jmdhapy
*
* @date 2022/3/17 16:51
*/
@Service
@Slf4j
public class DgpayChannelUserService implements IChannelUserService {
@Autowired protected ConfigContextQueryService configContextQueryService;
@Override
public String getIfCode() {
return CS.IF_CODE.DGPAY;
}
@Override
public ChannelUserInfoMsg buildUserRedirectUrl(String callbackUrlEncode, MchAppConfigContext mchAppConfigContext) {
//云闪付返回地址
String ysfRedirectUrl = String.format("https://qr.95516.com/qrcGtwWeb-web/api/userAuth?version=1.0.0&redirectUrl=%s", callbackUrlEncode);
log.info("【斗拱】ysfRedirectUrl={}", ysfRedirectUrl);
return ChannelUserInfoMsg.gen(ysfRedirectUrl, null);
}
@Override
public String getChannelUserId(String pageType, JSONObject reqParams, MchAppConfigContext mchAppConfigContext, String ifCode) {
String logPrefix = "【斗拱获取银联行业码用户ID】";
try {
String userAuthCode = reqParams.getString("userAuthCode"); //云闪付 userAuthCode
String appUpIdentifier = reqParams.getString("appUpIdentifier");
log.info("{} userAuthCode={}, appUpIdentifier={}", logPrefix, userAuthCode, appUpIdentifier);
DgpayIsvsubMchParams mchParams = (DgpayIsvsubMchParams)configContextQueryService.queryIsvsubMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), ifCode);
JSONObject reqJson = new JSONObject();
// 交易流水号
reqJson.put("req_seq_id", DateKit.currentTimeMillis()+"");
// 请求日期
reqJson.put("req_date", DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN));
// 汇付商户号
reqJson.put("huifu_id", mchParams.getHuifuId());
// 用户授权码
reqJson.put("auth_code", userAuthCode);
// 银联支付标识
reqJson.put("app_up_identifier", appUpIdentifier);
JSONObject response = DgPayKit.request(mchAppConfigContext, reqJson, "/trade/payment/usermark2/query", ifCode);
JSONObject resData = response.getJSONObject("data");
String code = resData.getString("resp_code");
String userId = "";
if (DgPayKit.STATE_SUCCESS.equals(code)) {
userId = resData.getString("user_id");
}else {
log.error("请求失败code={}, sub_msg={}", code, response.getString("resp_desc"));
return userId;
}
return userId;
} catch (Exception e) {
log.error("{}异常", logPrefix, e);
return null;
}
}
}

View File

@@ -0,0 +1,477 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.*;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.exception.ChannelException;
import com.jeequan.jeepay.core.model.applyment.DgpayApplymentInfo;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.params.dgpay.DgpayIsvsubMchParams;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.utils.AmountUtil;
import com.jeequan.jeepay.core.utils.DateKit;
import com.jeequan.jeepay.core.utils.JsonKit;
import com.jeequan.jeepay.core.utils.SeqKit;
import com.jeequan.jeepay.thirdparty.channel.AbstractDivisionService;
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.stereotype.Service;
import java.util.Date;
import java.util.List;
/**
* 分账接口
*
* @author xiaoyu
*
* @date 2022/8/1 15:39
*/
@Slf4j
@Service
public class DgpayDivisionService extends AbstractDivisionService {
@Autowired private ConfigContextQueryService configContextQueryService;
@Autowired private DgpayMchApplymentService dgpayMchApplymentService;
@Override
public String getIfCode() {
return CS.IF_CODE.DGPAY;
}
@Override
public boolean isSupport() {
return false;
}
@Override
public ChannelRetMsg bind(MchDivisionReceiver mchDivisionReceiver, MchAppConfigContext mchAppConfigContext) {
try {
String logPrefix = "[斗拱添加分账]";
// 新创建的分账接收者的mchId
String newDivisionReceiverSubMchId = null;
// 实际使用ifCode用于查询支付参数
String ifCode = mchDivisionReceiver.getIfCode();
// 当channelExtInfo 传入: {} 说明是subMchId直接绑定模式
JSONObject channelExtInfoJOSN = JSON.parseObject(mchDivisionReceiver.getChannelExtInfo());
if(channelExtInfoJOSN.isEmpty()){
throw new BizException("分账配置信息错误");
}
newDivisionReceiverSubMchId = mchDivisionReceiver.getAccNo();
Boolean isHasAccount = (Boolean) channelExtInfoJOSN.getOrDefault("isHasAccount", false);
// 新增的分账接收方:调起进件接口,并需要返回一个 subMchId
if(!isHasAccount){
// 1.基本信息开户
MchApplyment mchApplyment = new MchApplyment(); // 构造进件相关请求参数
mchApplyment.setApplyId(SeqKit.genMchApplyNo());
mchApplyment.setIsvNo(mchAppConfigContext.getMchApplyment().getIsvNo());
mchApplyment.setApplyDetailInfo(mchDivisionReceiver.getChannelExtInfo());
mchApplyment.setIfCode(ifCode);
// 进件参数
JSONObject reqParams = dgpayMchApplymentService.getApplymentParams(mchApplyment);
// 开户参数
dgpayMchApplymentService.setAccountInfo(mchApplyment, reqParams);
// 银行卡参数
JSONObject cardInfo = reqParams.getJSONObject("card_info");
reqParams.remove("card_info");
DgpayApplymentInfo applymentInfo = JSONObject.parseObject(mchApplyment.getApplyDetailInfo(), DgpayApplymentInfo.class);
JSONArray fileArr = new JSONArray();
// 门头照
String storeOuterImg = DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F49", applymentInfo.getStoreOuterImg(), ifCode);
if (StringUtils.isNotEmpty(storeOuterImg)) {
JSONObject json = JsonKit.newJson("file_type", "F49");
json.put("file_id", storeOuterImg);
fileArr.add(json);
}
// 内景照
String storeInnerImg = DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F50", applymentInfo.getStoreInnerImg(), ifCode);
if (StringUtils.isNotEmpty(storeInnerImg)) {
JSONObject json = JsonKit.newJson("file_type", "F50");
json.put("file_id", storeInnerImg);
fileArr.add(json);
}
// 收银台照
String storeCashierImg = DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F105", applymentInfo.getStoreCashierImg(), ifCode);
if (StringUtils.isNotEmpty(storeCashierImg)) {
JSONObject json = JsonKit.newJson("file_type", "F105");
json.put("file_id", storeCashierImg);
fileArr.add(json);
}
// 法人身份证人像面照片
String idcard1Img = DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F02", applymentInfo.getIdcard1Img(), ifCode);
if (StringUtils.isNotEmpty(idcard1Img)) {
JSONObject json = JsonKit.newJson("file_type", "F02");
json.put("file_id", idcard1Img);
fileArr.add(json);
}
// 法人身份证国徽面照片
String idcard2Img = DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F03", applymentInfo.getIdcard2Img(), ifCode);
if (StringUtils.isNotEmpty(idcard2Img)) {
JSONObject json = JsonKit.newJson("file_type", "F03");
json.put("file_id", idcard2Img);
fileArr.add(json);
}
// 营业执照
String licenseImg = DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F07", applymentInfo.getLicenseImg(), ifCode);
if (StringUtils.isNotEmpty(licenseImg)) {
JSONObject json = JsonKit.newJson("file_type", "F07");
json.put("file_id", licenseImg);
fileArr.add(json);
}
// 商务协议
String baPicImg = DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F116", applymentInfo.getBankPic(), ifCode);
if (StringUtils.isNotEmpty(baPicImg)) {
JSONObject json = JsonKit.newJson("file_type", "F116");
json.put("file_id", baPicImg);
fileArr.add(json);
}
reqParams.put("file_list", fileArr.toJSONString());
// 步骤1 请求地址 企业开户和个人开户地址
String step1Url = applymentInfo.getMerchantType() == 1?"/user/basicdata/indv":"/user/basicdata/ent";
// 发起开户请求
JSONObject responseStep1 = DgPayKit.request(mchAppConfigContext, reqParams, step1Url, ifCode);
JSONObject dataJson1 = responseStep1.getJSONObject("data");
// 响应失败
if (!DgPayKit.STATE_SUCCESS.equals(dataJson1.getString("resp_code"))) {
ChannelRetMsg channelRetMsg = ChannelRetMsg.confirmFail();
channelRetMsg.setChannelErrCode(dataJson1.getString("resp_code"));
channelRetMsg.setChannelErrMsg(dataJson1.getString("resp_desc"));
return channelRetMsg;
}
// 获取汇付ID号
String huifuId = dataJson1.getString("huifu_id");
// 2.业务入驻
DgpayIsvsubMchParams mchParams = (DgpayIsvsubMchParams)configContextQueryService.queryIsvsubMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), ifCode);
// 汇付ID
reqParams.put("huifu_id", huifuId);
// 请求流水号
reqParams.put("req_seq_id", SeqKit.genMchApplyNo());
// 商户编号
reqParams.put("upper_huifu_id", mchParams.getHuifuId());
// 延迟入账开关
reqParams.put("delay_flag", "Y");
// 银行卡参数
reqParams.put("card_info", cardInfo);
reqParams.remove("noMchId");
// 步骤2 请求地址 用户业务入驻
String step2Url = "/user/busi/open";
// 发起开户请求
JSONObject responseStep2 = DgPayKit.request(mchAppConfigContext, reqParams, step2Url, ifCode);
JSONObject dataJson2 = responseStep2.getJSONObject("data");
// 响应失败
if (!DgPayKit.STATE_SUCCESS.equals(dataJson2.getString("resp_code"))) {
ChannelRetMsg channelRetMsg = ChannelRetMsg.confirmFail();
// 状态失败
DgPayKit.channelMsgError(dataJson2, channelRetMsg);
return channelRetMsg;
}
JSONObject paramsJson = new JSONObject();
String tokenNo = dataJson2.getString("token_no");
paramsJson.put("tokenNo", tokenNo);
paramsJson.put("huifuId", huifuId);
newDivisionReceiverSubMchId = paramsJson.toJSONString();
}else {
JSONObject paramsJson = JsonKit.newJson("huifuId", newDivisionReceiverSubMchId);
// 取现卡序列号
if (StringUtils.isNotEmpty(mchDivisionReceiver.getChannelExtInfo())) {
String tokenNo = channelExtInfoJOSN.getString("tokenNo");
paramsJson.put("tokenNo", tokenNo);
}
newDivisionReceiverSubMchId = paramsJson.toJSONString();
}
//在channelAttach数据中写入 渠道用户编号
return ChannelRetMsg.confirmSuccess(null).setChannelAttach(newDivisionReceiverSubMchId);
} catch (ChannelException e) {
ChannelRetMsg channelRetMsg = ChannelRetMsg.confirmFail();
channelRetMsg.setChannelErrCode(e.getChannelRetMsg().getChannelErrCode());
channelRetMsg.setChannelErrMsg(e.getChannelRetMsg().getChannelErrMsg());
return channelRetMsg;
} catch (Exception e) {
log.error("添加斗拱分账异常", e);
ChannelRetMsg channelRetMsg = ChannelRetMsg.confirmFail();
channelRetMsg.setChannelErrMsg(e.getMessage());
return channelRetMsg;
}
}
@Override
public ChannelRetMsg singleDivision(PayOrder payOrder, List<PayOrderDivisionRecord> recordList, MchAppConfigContext mchAppConfigContext) {
try {
// 分账批次号
String batchOrderId = recordList.get(0).getBatchOrderId();
if(recordList.isEmpty()){ // 当无分账用户时,直接响应成功即可。
return ChannelRetMsg.confirmSuccess(null);
}
String logPrefix = "【斗拱分账】";
DgpayIsvsubMchParams isvsubMchParams = (DgpayIsvsubMchParams) configContextQueryService.queryIsvsubMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), payOrder.getIfCode());
String mchId = isvsubMchParams.getHuifuId();
JSONObject reqParams = new JSONObject();
// 请求日期
reqParams.put("req_date", DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN));
// 请求流水号
reqParams.put("req_seq_id", batchOrderId);
// 商户号
reqParams.put("huifu_id", mchId);
String orgReqSeqId = payOrder.getPayOrderId();
if (StringUtils.equals(CS.PAY_WAY_CODE.AUTO_POS, payOrder.getWayCode())) {
orgReqSeqId = payOrder.getChannelOrderNo();
if (payOrder.getChannelBizData() != null && StringUtils.isNotBlank(payOrder.getChannelBizData())) {
JSONObject channelBizData = JSONObject.parseObject(payOrder.getChannelBizData());
if (StringUtils.isNotBlank(channelBizData.getString("reqSeqId"))) {
orgReqSeqId = channelBizData.getString("reqSeqId");
}
}
}
// 原交易请求流水号
reqParams.put("org_req_seq_id", orgReqSeqId);
// 下单日期
reqParams.put("org_req_date", DateUtil.format(payOrder.getSuccessTime(), DatePattern.PURE_DATE_PATTERN));
JSONArray receivers = new JSONArray();
for (int i = 0; i < recordList.size(); i++) {
PayOrderDivisionRecord record = recordList.get(i);
if(record.getCalDivisionAmount() <= 0){ //金额为 0 不参与分账处理
continue;
}
if(StringUtils.isEmpty(record.getChannelAccNo())){ // 判断是否都包含渠道号码
throw new BizException(record.getAccName() + "账号没有分账渠道号, 本次分账失败");
}
JSONObject paramsJson = JSONObject.parseObject(record.getChannelAccNo());
JSONObject receiver = new JSONObject();
receiver.put("huifu_id", paramsJson.getString("huifuId"));
receiver.put("div_amt", AmountUtil.convertCent2Dollar(record.getCalDivisionAmount()));
receivers.add(receiver);
}
if(receivers.isEmpty()){
return ChannelRetMsg.confirmSuccess(null);
}
// 分账对象
JSONObject receiverJson = new JSONObject();
receiverJson.put("acct_infos", receivers);
reqParams.put("acct_split_bunch", receiverJson.toJSONString());
log.info("{}分账处理分账接收方", logPrefix);
// 发起分账请求
JSONObject response = DgPayKit.request(mchAppConfigContext, reqParams, "/trade/payment/delaytrans/confirm", payOrder.getIfCode());
JSONObject dataJson = response.getJSONObject("data");
// 响应失败
if (!DgPayKit.STATE_SUCCESS.equals(dataJson.getString("resp_code"))) {
ChannelRetMsg channelRetMsg = ChannelRetMsg.confirmFail();
// 状态失败
DgPayKit.channelMsgError(dataJson, channelRetMsg);
return channelRetMsg;
}
// 业务处理成功, 响应成功 交易状态为成功
if(DgPayKit.STATE_SUCCESS.equals(dataJson.getString("resp_code")) && "S".equals(dataJson.getString("trans_stat"))) {
return ChannelRetMsg.confirmSuccess(dataJson.getString("req_seq_id"));
}
// 订单处理中: 需要延迟到第二天5点进行结算
if("P".equals(dataJson.getString("trans_stat"))){
// 第二天的5点钟
Date nextDay5 = DateUtil.offset(DateKit.getBegin(DateUtil.offsetDay(new Date(), 1)), DateField.HOUR, 5);
// 触发MQ的时间 单位: 秒
Long delayTime = (nextDay5.getTime() - DateKit.currentTimeMillis()) / 1000;
return ChannelRetMsg.unknown().setChannelAttach(JsonKit.newJson("mqDelayTime", delayTime).toJSONString());
}else{ //其他情况认为: 失败
return ChannelRetMsg.confirmFail().setChannelErrCode(dataJson.getString("resp_code")).setChannelErrMsg(dataJson.getString("resp_desc"));
}
} catch (ChannelException e) {
ChannelRetMsg channelRetMsg = ChannelRetMsg.confirmFail();
channelRetMsg.setChannelErrCode(e.getChannelRetMsg().getChannelErrCode());
channelRetMsg.setChannelErrMsg(e.getChannelRetMsg().getChannelErrMsg());
return channelRetMsg;
} catch (Exception e) {
log.error("斗拱分账异常", e);
ChannelRetMsg channelRetMsg = ChannelRetMsg.confirmFail();
channelRetMsg.setChannelErrMsg(e.getMessage());
return channelRetMsg;
}
}
@Override
public ChannelRetMsg divisionRefund(PayOrderDivisionRecord payOrderDivisionRecord, PayOrderDivisionRefundRecord payOrderDivisionRefundRecord, RefundOrder refundOrder, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) {
String logPrefix = "【斗拱分账回退】";
try {
// 防止接口限流
Thread.sleep(1000);
DgpayIsvsubMchParams isvsubMchParams = (DgpayIsvsubMchParams) configContextQueryService.queryIsvsubMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), payOrderDivisionRecord.getIfCode());
JSONObject reqParams = new JSONObject();
// 请求日期
reqParams.put("req_date", DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN));
// 请求流水号
reqParams.put("req_seq_id", payOrderDivisionRefundRecord.getDivisionRefundId());
// 商户号
reqParams.put("huifu_id", isvsubMchParams.getHuifuId());
// 原交易请求日期
reqParams.put("org_req_date", DateUtil.format(payOrderDivisionRecord.getCreatedAt(), DatePattern.PURE_DATE_PATTERN));
// 原交易请求流水号
reqParams.put("org_req_seq_id", payOrderDivisionRecord.getChannelBatchOrderId());
JSONObject paramsJson = JSONObject.parseObject(payOrderDivisionRecord.getChannelAccNo());
JSONArray refundArr = new JSONArray();
JSONObject refundJson = new JSONObject();
refundJson.put("huifu_id", paramsJson.getString("huifuId"));
refundJson.put("div_amt", AmountUtil.convertCent2Dollar(payOrderDivisionRefundRecord.getDivisionRefundAmount()));
refundArr.add(refundJson);
JSONObject receiverJson = new JSONObject();
receiverJson.put("acct_infos", refundArr);
reqParams.put("acct_split_bunch", receiverJson.toJSONString());
log.info(logPrefix);
JSONObject response = DgPayKit.request(mchAppConfigContext, reqParams, "/trade/payment/delaytrans/confirmrefund", payOrderDivisionRecord.getIfCode());
JSONObject jsonData = response.getJSONObject("data");
// 响应失败
if (!DgPayKit.STATE_SUCCESS.equals(jsonData.getString("resp_code"))) {
ChannelRetMsg channelRetMsg = ChannelRetMsg.confirmFail();
// 状态失败
DgPayKit.channelMsgError(jsonData, channelRetMsg);
return channelRetMsg;
}
return ChannelRetMsg.confirmSuccess(jsonData.getString("hf_seq_id"));
} catch (Exception e) {
throw new BizException(e.getMessage());
}
}
@Override
public Long queryBalanceAmount(MchDivisionReceiver mchDivisionReceiver, MchAppConfigContext mchAppConfigContext) {
String logPrefix = "【斗拱分账余额查询】";
try {
JSONObject paramsJson = JSONObject.parseObject(mchDivisionReceiver.getChannelAccNo());
JSONObject reqParams = new JSONObject();
// 请求日期
reqParams.put("req_date", DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN));
// 请求流水号
reqParams.put("req_seq_id", DateKit.currentTimeMillis());
// 商户号
reqParams.put("huifu_id", paramsJson.getString("huifuId"));
log.info(logPrefix);
JSONObject response = DgPayKit.request(mchAppConfigContext, reqParams, "/trade/acctpayment/balance/query", mchDivisionReceiver.getIfCode());
JSONObject jsonData = response.getJSONObject("data");
Long balanceAmt = 0L;
// 响应成功
if (DgPayKit.STATE_SUCCESS.equals(jsonData.getString("resp_code"))) {
// 账户信息列表
JSONArray infoList = jsonData.getJSONArray("acctInfo_list");
for (int i = 0; i < infoList.size(); i++) {
JSONObject infoJson = JSONObject.parseObject(infoList.get(i).toString());
// 查询基本账户类型
if ("01".equals(infoJson.getString("acct_type"))) {
balanceAmt = Long.parseLong(AmountUtil.convertDollar2Cent(infoJson.getString("balance_amt")));
}
}
}
return balanceAmt;
} catch (Exception e) {
throw new BizException(e.getMessage());
}
}
@Override
public ChannelRetMsg cashout(MchDivisionReceiver mchDivisionReceiver, Long amount, MchAppConfigContext mchAppConfigContext) {
String logPrefix = "斗拱分账账号提现";
try {
Long thisBalanceAmount = queryBalanceAmount(mchDivisionReceiver, mchAppConfigContext);
if(thisBalanceAmount < amount){
throw new BizException("商户可用余额不足");
}
JSONObject paramsJson = JSONObject.parseObject(mchDivisionReceiver.getChannelAccNo());
JSONObject reqParams = new JSONObject();
// 请求流水号
reqParams.put("req_seq_id", DateKit.currentTimeMillis());
// 请求日期
reqParams.put("req_date", DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN));
// 商户号
reqParams.put("huifu_id", paramsJson.getString("huifuId"));
// 取现卡序列号
String tokenNo = paramsJson.getString("tokenNo");
if (StringUtils.isEmpty(tokenNo)) {
throw new BizException("取现卡序列号不能为空");
}
// 请求流水号
reqParams.put("req_seq_id", DateKit.currentTimeMillis() +"_"+ 1);
// 到账类型
reqParams.put("into_acct_date_type", "T0");
// 取现卡序列号
reqParams.put("token_no", tokenNo);
// 取现金额
reqParams.put("cash_amt", AmountUtil.convertCent2Dollar(amount));
log.info(logPrefix);
JSONObject response = DgPayKit.request(mchAppConfigContext, reqParams, "/trade/settlement/enchashment", mchDivisionReceiver.getIfCode());
JSONObject jsonData = response.getJSONObject("data");
// 响应成功
if (DgPayKit.STATE_SUCCESS.equals(jsonData.getString("resp_code"))) {
// 交易状态
String transStat = jsonData.getString("trans_stat");
if (DgPayKit.STATE_ING_P.equals(transStat)) {
String channelNo = jsonData.getString("hf_seq_id");
return ChannelRetMsg.confirmSuccess(channelNo);
}else if (DgPayKit.STATE_FAIL_F.equals(transStat)) {
return ChannelRetMsg.confirmFail(jsonData.getString("resp_code"), jsonData.getString("resp_desc"));
}
}
return ChannelRetMsg.waiting();
} catch (Exception e) {
throw new BizException(e.getMessage());
}
}
}

View File

@@ -0,0 +1,322 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.jeequan.jeepay.converter.MchInfoConverter;
import com.jeequan.jeepay.core.entity.MchApplyment;
import com.jeequan.jeepay.core.entity.MchInfo;
import com.jeequan.jeepay.core.entity.MchSubInfo;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.interfaces.paychannel.IIsvmchAlipayConfigService;
import com.jeequan.jeepay.core.model.applyment.ApplymentSignInfo;
import com.jeequan.jeepay.core.model.applyment.DgpayApplymentInfo;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.params.dgpay.DgpayIsvParams;
import com.jeequan.jeepay.core.utils.DateKit;
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
import com.jeequan.jeepay.db.entity.MchSubInfoEntity;
import com.jeequan.jeepay.service.impl.MchApplymentService;
import com.jeequan.jeepay.service.impl.MchSubInfoService;
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.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.Objects;
@Service
@Slf4j
public class DgpayIsvmchAlipayConfigService implements IIsvmchAlipayConfigService {
@Autowired
private MchSubInfoService mchSubInfoService;
@Autowired
private MchApplymentService mchApplymentService;
@Autowired
private MchInfoConverter mchInfoConverter;
private void realNameApply(MchApplyment mchApplyment, DgpayIsvParams isvParams) {
DgpayApplymentInfo applymentInfo = JSON.parseObject(mchApplyment.getApplyDetailInfo(), DgpayApplymentInfo.class);
JSONObject suJson = JSON.parseObject(mchApplyment.getSuccResParameter());
JSONObject reqParams = new JSONObject();
// 汇付ID
reqParams.put("huifu_id", suJson.getString("huifuId"));
// 请求日期
reqParams.put("req_date", DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN));
// 请求流水号
reqParams.put("req_seq_id", DateKit.currentTimeMillis() + "");
reqParams.put("pay_scene", "1");
// 主体信息
JSONObject authIdentityInfo = new JSONObject();
reqParams.put("auth_identity_info", authIdentityInfo);
authIdentityInfo.put("business_type", getBusinessType(mchApplyment.getMerchantType()));
authIdentityInfo.put("finance_institution_flag", "N");
if (mchApplyment.getMerchantType() == MchApplyment.MERCHANT_TYPE_PERSONAL) {
JSONObject supportCredentials = new JSONObject();
authIdentityInfo.put("support_credentials", supportCredentials);
supportCredentials.put("micro_biz_type", applymentInfo.getMicroBizType());
supportCredentials.put("store_name", applymentInfo.getMchShortName());
supportCredentials.put("province_code", applymentInfo.getAreaCode().getString(0));
if (applymentInfo.getAreaName() != null) {
supportCredentials.put("province", applymentInfo.getAreaName().getString(0));
}
supportCredentials.put("city_code", applymentInfo.getAreaCode().getString(1));
if (applymentInfo.getAreaName() != null) {
supportCredentials.put("city", applymentInfo.getAreaName().getString(1));
}
supportCredentials.put("district_code", applymentInfo.getAreaCode().getString(2));
if (applymentInfo.getAreaName() != null) {
supportCredentials.put("district", applymentInfo.getAreaName().getString(2));
}
if (applymentInfo.getMicroBizType().equals("MICRO_TYPE_MOBILE")) {
supportCredentials.put("store_address", "");
} else {
supportCredentials.put("store_address", applymentInfo.getAddress());
}
String f49 = DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F49", applymentInfo.getStoreOuterImg(), mchApplyment.getIfCode());
supportCredentials.put("store_door_img", f49);
String f50 = DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F50", applymentInfo.getStoreInnerImg(), mchApplyment.getIfCode());
supportCredentials.put("store_inner_img", f50);
} else {
authIdentityInfo.put("certificate_type", "BUSINESS_CERT");
JSONObject certificateInfo = new JSONObject();
authIdentityInfo.put("certificate_info", certificateInfo);
certificateInfo.put("cert_number", applymentInfo.getLicenseNo());
String f07 = DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F07", applymentInfo.getLicenseImg(), mchApplyment.getIfCode());
certificateInfo.put("cert_copy", f07);
certificateInfo.put("cert_merchant_name", applymentInfo.getMchFullName());
certificateInfo.put("cert_legal_person", applymentInfo.getIdcardName());
certificateInfo.put("cert_company_address", applymentInfo.getLicenseAddress());
certificateInfo.put("effect_time", applymentInfo.getLicenseEffectBegin().replace("-", ""));
certificateInfo.put("expire_time", applymentInfo.getLicenseEffectEnd().replace("-", ""));
}
JSONObject contactPersonInfo = new JSONObject();
reqParams.put("contact_person_info", contactPersonInfo);
contactPersonInfo.put("id_card_number", applymentInfo.getContactIdcardNo());
MchAppConfigContext mchAppConfigContext = new MchAppConfigContext();
mchAppConfigContext.setMchType(MchInfo.TYPE_ISVSUB);
mchAppConfigContext.setMchApplyment(mchApplyment);
JSONObject response = DgPayKit.request(mchAppConfigContext, reqParams, "/merchant/busi/ali/realname/apply", mchApplyment.getIfCode())
.getJSONObject("data");
if (!DgPayKit.STATE_SUCCESS.equals(response.getString("resp_code"))) {
throw new BizException(response.getString("resp_desc"));
}
String applymentId = response.getString("applyment_id");
String orderStatus = response.getString("order_status");
if (orderStatus.equals("AUDITING")) {
suJson.put("zfb_applyment_id", applymentId);
}
mchApplyment.setSuccResParameter(suJson.toJSONString());
mchApplymentService.updateById(mchInfoConverter.toDbEntity(mchApplyment));
}
private String getBusinessType(Byte merchantType) {
if (merchantType == MchApplyment.MERCHANT_TYPE_PERSONAL) {
return "0";
}
if (merchantType == MchApplyment.MERCHANT_TYPE_INDIVIDUAL) {
return "5";
}
if (merchantType == MchApplyment.MERCHANT_TYPE_ENTERPRISE) {
return "3";
}
throw new BizException("无法判定的其他主体类型");
}
@Override
public ApplymentSignInfo alipayOpenSignInfo(MchApplyment mchApplyment) {
// 服务商配置参数
ConfigContextQueryService configContextQueryService = SpringBeansUtil.getBean(ConfigContextQueryService.class);
DgpayIsvParams isvParams = (DgpayIsvParams) configContextQueryService.queryIsvParams(mchApplyment.getIsvNo(), mchApplyment.getIfCode());
ApplymentSignInfo result = new ApplymentSignInfo();
// 进件商户信息
JSONObject suJson = JSON.parseObject(mchApplyment.getSuccResParameter());
if (suJson == null || StringUtils.isEmpty(suJson.getString("huifuId"))) {
result.setState("查询失败");
result.setErrInfo("商户参数为空,该商户还未进件成功");
return result;
}
MchAppConfigContext mchAppConfigContext = new MchAppConfigContext();
mchAppConfigContext.setMchType(MchInfo.TYPE_ISVSUB);
mchAppConfigContext.setMchApplyment(mchApplyment);
JSONObject reqParams = new JSONObject();
// 汇付ID
reqParams.put("huifu_id", suJson.getString("huifuId"));
// 请求日期
reqParams.put("req_date", DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN));
// 请求流水号
reqParams.put("req_seq_id", DateKit.currentTimeMillis() + "");
// 发送请求
JSONObject response = DgPayKit.request(mchAppConfigContext, reqParams, "/merchant/busi/realname/query", mchApplyment.getIfCode())
.getJSONObject("data");
// 请求流水号
reqParams.put("req_seq_id", DateKit.currentTimeMillis() + "_info");
// 商户详情is_open_small_flag -> 0
JSONObject mchInfo = DgPayKit.request(mchAppConfigContext, reqParams, "/merchant/basicdata/query", mchApplyment.getIfCode());
JSONObject mchInfoData = mchInfo.getJSONObject("data");
String feeConfig = "";
JSONArray wxConfList = mchInfoData.getJSONArray("qry_wx_conf_list");
JSONArray aliConfList = mchInfoData.getJSONArray("qry_ali_conf_list");
JSONObject unionConf = mchInfoData.getJSONObject("qry_union_conf");
JSONObject bankCardConf = mchInfoData.getJSONObject("qry_bank_card_conf");
for (int aliIndex = 0; aliIndex < aliConfList.size(); aliIndex++) {
JSONArray aliMerInfos = aliConfList.getJSONObject(aliIndex).getJSONArray("ali_mer_infos");
if (!CollUtil.isEmpty(aliMerInfos)) {
for (int i = 0; i < aliMerInfos.size(); i++) {
JSONObject item = aliMerInfos.getJSONObject(i);
// 支付宝子商户号存在时
MchSubInfoEntity zfbSubInfo = new MchSubInfoEntity();
zfbSubInfo.setMchApplyId(mchApplyment.getApplyId());
String payChannelId = item.getString("pay_channel_id");
String bankMerCode = item.getString("bank_mer_code");
if (Objects.equals(isvParams.getChannelNo(), payChannelId)) {
zfbSubInfo.setMainUse(1);
result.setChannelSubMchId(bankMerCode);
} else {
zfbSubInfo.setMainUse(0);
}
zfbSubInfo.setSubMchWay("DG");
zfbSubInfo.setChannelMchNo(mchApplyment.getChannelMchNo());
zfbSubInfo.setChannelId(payChannelId);
zfbSubInfo.setAuthStatus(MchSubInfo.AUTH_STATUS_UNAUTHORIZED);
zfbSubInfo.setSubMchId(bankMerCode);
zfbSubInfo.setSubMchType("ZFB");
// 此处取1为正常
zfbSubInfo.setStatus("1");
zfbSubInfo.setExt(item);
try {
mchSubInfoService.save(zfbSubInfo);
} catch (DuplicateKeyException e) {
log.info("子商户信息已存储过");
}
}
}
}
// 支付宝申请单编号
// String applymentId = suJson.getString("zfb_applyment_id");
// if (applymentId == null) {
// realNameApply(mchApplyment, isvParams);
// }
reqParams = new JSONObject();
// 汇付ID
reqParams.put("huifu_id", suJson.getString("huifuId"));
// 请求日期
reqParams.put("req_date", DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN));
// 请求流水号
reqParams.put("req_seq_id", DateKit.currentTimeMillis() + "");
// reqParams.put("pay_channel_id", DateKit.currentTimeMillis()+"");
// reqParams.put("pay_scene", "1");
// 发送请求
JSONObject response2 = DgPayKit.request(mchAppConfigContext, reqParams, "/merchant/busi/ali/realname/query", mchApplyment.getIfCode())
.getJSONObject("data");
String respCode = response2.getString("resp_code");
String respDesc = response2.getString("resp_desc");
if (!respCode.equals("00000000")) {
result.setState("获取子商户状态异常");
result.setErrInfo("" + respDesc + "");
result.setImgType("qrContent");
result.setSignUrl(isvParams.getAliChannelExtUrl());
return result;
}
String orderStatus = response2.getString("order_status");
if ("AUDITING".equals(orderStatus)) {
result.setState("审核中");
} else if ("CONTACT_CONFIRM".equals(orderStatus)) {
result.setState("待联系人确认(扫码确认授权)");
result.setImgType("imgUrl");
result.setSignUrl(response2.getString("qrcode"));
} else if ("LEGAL_CONFIRM".equals(orderStatus)) {
result.setState("待法人确认(扫码确认授权)");
result.setImgType("imgUrl");
result.setSignUrl(response2.getString("qrcode"));
} else if ("AUDIT_PASS".equals(orderStatus)) {
result.setState("审核通过");
} else if ("AUDIT_REJECT".equals(orderStatus)) {
result.setState("审核失败");
} else if ("AUDIT_FREEZE".equals(orderStatus)) {
result.setState("已冻结(扫码确认授权)");
result.setImgType("imgUrl");
result.setSignUrl(response2.getString("qrcode"));
} else if ("CANCELED".equals(orderStatus)) {
result.setState("已撤回");
} else if ("NOT_APPLY".equals(orderStatus)) {
result.setState("未申请");
}
String authorizeStat = response2.getString("authorize_stat");
LambdaUpdateChainWrapper<MchSubInfoEntity> stateUpdate = mchSubInfoService.lambdaUpdate()
.eq(MchSubInfoEntity::getMchApplyId, mchApplyment.getApplyId())
.eq(MchSubInfoEntity::getMainUse, 1)
.eq(MchSubInfoEntity::getSubMchType, "ZFB");
if ("AUTHORIZED".equals(authorizeStat)) {
stateUpdate.set(MchSubInfoEntity::getAuthStatus, MchSubInfo.AUTH_STATUS_AUTHORIZED).update();
result.setState("已授权");
} else if ("UNAUTHORIZED".equals(authorizeStat)) {
stateUpdate.set(MchSubInfoEntity::getAuthStatus, MchSubInfo.AUTH_STATUS_UNAUTHORIZED).update();
result.setState("未授权");
} else if ("CLOSED".equals(authorizeStat)) {
result.setState("已销户");
} else {
result.setState("未报备");
}
if (ObjUtil.isEmpty(result.getSignUrl())) {
// 渠道拓展地址
result.setImgType("qrContent");
result.setSignUrl(isvParams.getAliChannelExtUrl());
}
return result;
}
}

View File

@@ -0,0 +1,205 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay;
import cn.hutool.core.text.CharSequenceUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.converter.MchInfoConverter;
import com.jeequan.jeepay.core.beans.RequestKitBean;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.MchApplyment;
import com.jeequan.jeepay.core.entity.MchModifyApplyment;
import com.jeequan.jeepay.core.entity.MchSubInfo;
import com.jeequan.jeepay.core.interfaces.paychannel.IIsvmchApplymentNotifyService;
import com.jeequan.jeepay.core.model.params.dgpay.DgpayIsvParams;
import com.jeequan.jeepay.db.entity.MchModifyApplymentEntity;
import com.jeequan.jeepay.service.impl.MchApplymentService;
import com.jeequan.jeepay.service.impl.MchModifyApplymentService;
import com.jeequan.jeepay.service.impl.MchSubInfoService;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
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.Service;
import javax.servlet.http.HttpServletRequest;
@Slf4j
@Service
public class DgpayIsvmchApplymentNotifyService implements IIsvmchApplymentNotifyService {
@Autowired
private RequestKitBean requestKitBean;
@Autowired
private ConfigContextQueryService configContextQueryService;
@Autowired
private MchApplymentService mchApplymentService;
@Autowired
private MchInfoConverter mchInfoConverter;
@Autowired
private MchSubInfoService mchSubInfoService;
@Autowired
private MchModifyApplymentService modifyApplymentService;
@Override
public MutablePair<String, Object> parseParams(HttpServletRequest request, String urlApplyId) {
JSONObject reqParamJSON = requestKitBean.getReqParamJSON();
JSONObject data = reqParamJSON.getJSONObject("data");
String applyNo = data.getString("apply_no");
com.jeequan.jeepay.db.entity.MchApplyment mchApplyment = mchApplymentService.getByOrderId(applyNo);
if (mchApplyment == null) {
// 此时查询变更的申请单
MchModifyApplymentEntity mchModifyApplyment = modifyApplymentService.getByOrderId(applyNo, CS.IF_CODE.DGPAY);
if (mchModifyApplyment == null) {
return MutablePair.of(null, data);
}
mchApplyment = mchApplymentService.getByChannelMchNo(mchModifyApplyment.getChannelMchNo());
}
if (mchApplyment == null) {
return MutablePair.of(null, data);
}
DgpayIsvParams isvParams = (DgpayIsvParams) configContextQueryService.queryIsvParams(mchInfoConverter.toModel(mchApplyment));
boolean verify = DgPayKit.verify(data.toJSONString(), isvParams.getRsaPublicKey(), reqParamJSON.getString("sign"));
if (!verify) {
log.info("斗拱支付回调参数验签不通过");
}
return MutablePair.of(mchApplyment.getApplyId(), data);
}
@Override
public MutablePair<MchApplyment, ResponseEntity> doNotify(HttpServletRequest request, Object params, MchApplyment mchApplyment) {
JSONObject response = ((JSONObject) params);
JSONObject resData = response.getJSONObject("data");
String notifyType = resData.getString("notify_type");
if (response.getString("apply_no").equals(mchApplyment.getChannelApplyNo())) {
// 入网回调
if (CharSequenceUtil.isEmpty(notifyType)) {
applyResult(resData, mchApplyment);
} else {
// 报备回调
reportResult(resData, mchApplyment);
return MutablePair.of(mchApplyment, retOk("RECV_ORD_ID_" + resData.getString("ord_id")));
}
} else {
MchModifyApplymentEntity mchModifyApplyment = modifyApplymentService.getByOrderId(response.getString("apply_no"), CS.IF_CODE.DGPAY);
// 变更回调
changeResult(resData, mchInfoConverter.toModel(mchModifyApplyment));
}
return MutablePair.of(mchApplyment, retOk(null));
}
private void changeResult(JSONObject resData, MchModifyApplyment modifyApplyment) {
String auditStatus = resData.getString("audit_status");
String auditDesc = resData.getString("audit_desc");
MchModifyApplymentEntity result = new MchModifyApplymentEntity();
result.setModifyApplyId(modifyApplyment.getModifyApplyId());
if ("Y".equals(auditStatus)) {
// 变更成功
modifyApplymentService.localModify(modifyApplyment);
} else if ("N".equals(auditStatus) || "F".equals(auditStatus)) {
// 变更失败
result.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
result.setApplyErrorInfo("[" + auditDesc + "]");
modifyApplymentService.updateById(result);
}
}
private void reportResult(JSONObject resData, MchApplyment mchApplyment) {
MchApplyment result = new MchApplyment();
result.setApplyId(mchApplyment.getApplyId());
String code = resData.getString("sub_resp_code");
String notifyType = resData.getString("notify_type");
String huifuId = resData.getString("huifu_id");
if (!"00000000".equals(code)) {
return;
}
MchSubInfo mchSubInfo = new MchSubInfo();
mchSubInfo.setChannelMchNo(huifuId);
mchSubInfo.setAuthStatus(MchSubInfo.AUTH_STATUS_UNAUTHORIZED);
switch (notifyType) {
case "W":
mchSubInfo.setSubMchType("WX");
break;
case "A":
mchSubInfo.setSubMchType("ZFB");
break;
case "U":
mchSubInfo.setSubMchType("BANK");
break;
default:
}
// 通知类型,没有则是申请单审核异步回调
JSONArray regResultList = resData.getJSONArray("reg_result_list");
if (regResultList == null || regResultList.isEmpty()) {
return;
}
for (int i = 0; i < regResultList.size(); i++) {
JSONObject item = regResultList.getJSONObject(i);
MchSubInfo newEntity = mchInfoConverter.clone(mchSubInfo);
newEntity.setExt(item);
newEntity.setStatus(item.getString("business_stat"));
newEntity.setSubMchId(item.getString("sub_mer_id"));
newEntity.setChannelId(item.getString("pay_channel_id"));
mchSubInfoService.save(mchInfoConverter.toDbEntity(newEntity));
}
}
private void applyResult(JSONObject resData, MchApplyment mchApplyment) {
MchApplyment result = new MchApplyment();
result.setApplyId(mchApplyment.getApplyId());
String code = resData.getString("sub_resp_code");
if ("00000000".equals(code)) {
return;
}
String auditStatus = resData.getString("audit_status");
String auditDesc = resData.getString("audit_desc");
// 通知类型,没有则是申请单审核异步回调
if ("Y".equals(auditStatus)) {
String huifuId = resData.getString("huifu_id");
// 进件查询响应参数
result.setApplyErrorInfo("[" + auditDesc + "]");
result.setState(MchApplyment.STATE_SUCCESS);
result.setChannelMchNo(huifuId);
result.setSuccResParameter(resData.toString());
mchApplymentService.updateById(mchInfoConverter.toDbEntity(result));
} else if ("N".equals(auditStatus) || "F".equals(auditStatus)) {
result.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
result.setApplyErrorInfo("[" + auditDesc + "]");
mchApplymentService.updateById(mchInfoConverter.toDbEntity(result));
}
}
@Override
public ResponseEntity<?> retOk(Object params) {
return ResponseEntity.ok("SUCCESS");
}
}

View File

@@ -0,0 +1,634 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.MchApplyment;
import com.jeequan.jeepay.core.entity.MchInfo;
import com.jeequan.jeepay.core.entity.MchModifyApplyment;
import com.jeequan.jeepay.core.interfaces.paychannel.IIsvmchModifyApplymentService;
import com.jeequan.jeepay.core.model.applyment.DgpayApplymentInfo;
import com.jeequan.jeepay.core.model.applyment.MchModifyApplymentModel;
import com.jeequan.jeepay.core.model.applyment.PaywayFee;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.params.dgpay.DgpayIsvParams;
import com.jeequan.jeepay.service.impl.SysConfigService;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.MutablePair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Date;
@Slf4j
@Service
public class DgpayIsvmchModifyApplymentService implements IIsvmchModifyApplymentService {
@Autowired
protected SysConfigService sysConfigService;
@Autowired
private ConfigContextQueryService configContextQueryService;
@Override
public MutablePair<MchModifyApplyment, MchApplyment> localModifyBase(MutablePair<MchModifyApplyment, MchApplyment> mchDataPair) {
MchModifyApplyment resultModifyApplyment = new MchModifyApplyment();
MchApplyment resultMchApplyment = new MchApplyment();
MchModifyApplyment mchModifyApplyment = mchDataPair.getKey();
resultModifyApplyment.setModifyApplyId(mchModifyApplyment.getModifyApplyId());
resultModifyApplyment.setState(MchApplyment.STATE_SUCCESS);
MchApplyment mchApplyment = mchDataPair.getValue();
resultMchApplyment.setApplyId(mchApplyment.getApplyId());
String applyDetailInfo = mchModifyApplyment.getApplyDetailInfo();
MchModifyApplymentModel mchModifyData = JSON.parseObject(applyDetailInfo, MchModifyApplymentModel.class);
String originApplyDetailInfo = mchApplyment.getApplyDetailInfo();
DgpayApplymentInfo originMchModifyData = JSON.parseObject(originApplyDetailInfo, DgpayApplymentInfo.class);
resultMchApplyment.setApplyDetailInfo(JSON.toJSONString(originMchModifyData));
originMchModifyData.setMchShortName(mchModifyData.getMchShortName());
resultMchApplyment.setMchShortName(mchModifyData.getMchShortName());
return new MutablePair<>(resultModifyApplyment, resultMchApplyment);
}
@Override
public MutablePair<MchModifyApplyment, MchApplyment> localModifySettlement(MutablePair<MchModifyApplyment, MchApplyment> mchDataPair) {
MchModifyApplyment resultModifyApplyment = new MchModifyApplyment();
MchApplyment resultMchApplyment = new MchApplyment();
MchModifyApplyment mchModifyApplyment = mchDataPair.getKey();
resultModifyApplyment.setModifyApplyId(mchModifyApplyment.getModifyApplyId());
resultModifyApplyment.setState(MchApplyment.STATE_SUCCESS);
MchApplyment mchApplyment = mchDataPair.getValue();
resultMchApplyment.setApplyId(mchApplyment.getApplyId());
String applyDetailInfo = mchModifyApplyment.getApplyDetailInfo();
MchModifyApplymentModel mchModifyData = JSON.parseObject(applyDetailInfo, MchModifyApplymentModel.class);
String originApplyDetailInfo = mchApplyment.getApplyDetailInfo();
DgpayApplymentInfo originMchModifyData = JSON.parseObject(originApplyDetailInfo, DgpayApplymentInfo.class);
resultMchApplyment.setApplyDetailInfo(JSON.toJSONString(originMchModifyData));
originMchModifyData.setSettAccountType(mchModifyData.getSettAccountType());
if ("Y".equals(mchModifyData.getIllegal())) {
originMchModifyData.setSettAccountIdcardNo(mchModifyData.getSettAccountIdcardNo());
originMchModifyData.setSettAccountIdcard1Img(mchModifyData.getSettAccountIdcard1Img());
originMchModifyData.setSettAccountIdcard2Img(mchModifyData.getSettAccountIdcard2Img());
originMchModifyData.setSettAccountIdcardEffectBegin(mchModifyData.getSettAccountIdcardEffectBegin());
originMchModifyData.setSettAccountIdcardEffectEnd(mchModifyData.getSettAccountIdcardEffectEnd());
}
originMchModifyData.setSettAccountLicenseImg(mchModifyData.getSettAccountLicenseImg());
originMchModifyData.setSettAccountName(mchModifyData.getSettAccountName());
originMchModifyData.setSettAccountNo(mchModifyData.getSettAccountNo());
originMchModifyData.setSettAccountBankName(mchModifyData.getSettAccountBankName());
originMchModifyData.setAuthEnturstPic(mchModifyData.getNonLegSettleAuthPic());
return new MutablePair<>(resultModifyApplyment, resultMchApplyment);
}
@Override
public MutablePair<MchModifyApplyment, MchApplyment> localModifyRate(MutablePair<MchModifyApplyment, MchApplyment> mchDataPair) {
return null;
}
@Override
public MutablePair<MchModifyApplyment, MchApplyment> syncChannelModifyBase(MutablePair<MchModifyApplyment, MchApplyment> mchDataPair) {
MchModifyApplyment resultModifyApplyment = new MchModifyApplyment();
MchModifyApplyment mchModifyApplyment = mchDataPair.getKey();
resultModifyApplyment.setModifyApplyId(mchModifyApplyment.getModifyApplyId());
resultModifyApplyment.setState(MchApplyment.STATE_AUDITING);
MchApplyment mchApplyment = mchDataPair.getValue();
String applyDetailInfo = mchModifyApplyment.getApplyDetailInfo();
MchModifyApplymentModel mchModifyData = JSON.parseObject(applyDetailInfo, MchModifyApplymentModel.class);
DgpayIsvParams isvParams = ((DgpayIsvParams) configContextQueryService.queryIsvParams(mchApplyment));
JSONObject param = new JSONObject();
Date today = new Date();
String formatDate = DateUtil.format(today, DatePattern.PURE_DATE_PATTERN);
param.put("req_seq_id", formatDate + mchModifyApplyment.getApplyId());
param.put("req_date", formatDate);
param.put("upper_huifu_id", isvParams.getSysId());
param.put("huifu_id", mchApplyment.getChannelMchNo());
param.put("short_name", mchModifyData.getMchShortName());
param.put("async_return_url", getCallbackUrl(mchApplyment.getIfCode()));
JSONObject agreementInfo = new JSONObject();
agreementInfo.put("agreement_type", "0");
param.put("agreement_info", agreementInfo.toJSONString());
JSONObject signUserInfo = new JSONObject();
param.put("sign_user_info", signUserInfo);
DgpayApplymentInfo applymentInfo = JSON.parseObject(mchApplyment.getApplyDetailInfo(), DgpayApplymentInfo.class);
switch (applymentInfo.getSettAccountType()) {
case "B":
case "C":
// 对公
signUserInfo.put("type", "LEGAL");
signUserInfo.put("mobile_no", applymentInfo.getIdCardPhone());
break;
case "D":
signUserInfo.put("type", "OTHER");
signUserInfo.put("name", applymentInfo.getSettAccountName());
signUserInfo.put("mobile_no", applymentInfo.getSettAccountBindPhone());
signUserInfo.put("cert_no", applymentInfo.getSettAccountIdcardNo());
break;
}
MchAppConfigContext mchAppConfigContext = new MchAppConfigContext();
mchAppConfigContext.setMchType(MchInfo.TYPE_ISVSUB);
mchAppConfigContext.setMchApplyment(mchApplyment);
try {
JSONObject applyResp = DgPayKit.request(mchAppConfigContext, param, DgPayKit.MCH_APPLY_MODIFY_URL, mchApplyment.getIfCode());
JSONObject applyRespData = applyResp.getJSONObject("data");
String code = applyRespData.getString("sub_resp_code");
String msg = applyRespData.getString("sub_resp_desc");
if ("00000000".equals(code)) {
// 请求成功
String applyNo = applyRespData.getString("apply_no");
resultModifyApplyment.setChannelApplyNo(applyNo);
resultModifyApplyment.setState(MchApplyment.STATE_SUCCESS);
return localModifyBase(new MutablePair<>(resultModifyApplyment, mchDataPair.right));
} else if ("90000000".equals(code)) {
// 审核中
String applyNo = applyRespData.getString("apply_no");
resultModifyApplyment.setChannelApplyNo(applyNo);
resultModifyApplyment.setState(MchApplyment.STATE_AUDITING);
} else {
// 其他暂定为失败
resultModifyApplyment.setApplyErrorInfo("[" + msg + "]");
resultModifyApplyment.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
}
resultModifyApplyment.setChannelVar1(applyRespData.toJSONString());
String remark = "斗拱支付基本信息变更已发起";
resultModifyApplyment.setRemark(remark);
log.debug("斗拱支付基本信息变更发起成功");
} catch (Exception e) {
resultModifyApplyment.setApplyErrorInfo("[" + e.getMessage() + "]");
resultModifyApplyment.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
}
return new MutablePair<>(resultModifyApplyment, null);
}
@Override
public MutablePair<MchModifyApplyment, MchApplyment> syncChannelModifySettlement(MutablePair<MchModifyApplyment, MchApplyment> mchDataPair) {
MchModifyApplyment resultModifyApplyment = new MchModifyApplyment();
MchModifyApplyment mchModifyApplyment = mchDataPair.getKey();
resultModifyApplyment.setModifyApplyId(mchModifyApplyment.getModifyApplyId());
resultModifyApplyment.setState(MchApplyment.STATE_AUDITING);
MchApplyment mchApplyment = mchDataPair.getValue();
String applyDetailInfo = mchModifyApplyment.getApplyDetailInfo();
MchModifyApplymentModel mchModifyData = JSON.parseObject(applyDetailInfo, MchModifyApplymentModel.class);
DgpayIsvParams isvParams = ((DgpayIsvParams) configContextQueryService.queryIsvParams(mchApplyment));
JSONObject param = new JSONObject();
Date today = new Date();
String formatDate = DateUtil.format(today, DatePattern.PURE_DATE_PATTERN);
param.put("req_seq_id", formatDate + mchModifyApplyment.getApplyId());
param.put("req_date", formatDate);
param.put("upper_huifu_id", isvParams.getSysId());
param.put("huifu_id", mchApplyment.getChannelMchNo());
param.put("async_return_url", getCallbackUrl(mchApplyment.getIfCode()));
JSONObject cardInfo = new JSONObject();
if ("B".equals(mchModifyData.getSettAccountType())) {
String f13 = DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F13", mchModifyData.getSettAccountLicenseImg(), mchApplyment.getIfCode());
cardInfo.put("settle_card_front_pic", f13);
} else {
String f08 = DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F08", mchModifyData.getChangeFormPic(), mchApplyment.getIfCode());
cardInfo.put("reg_acct_pic", f08);
}
String f55 = DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F55", mchModifyData.getSettAccountIdcard1Img(), mchApplyment.getIfCode());
cardInfo.put("settle_cert_front_pic", f55);
String f56 = DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F56", mchModifyData.getSettAccountIdcard2Img(), mchApplyment.getIfCode());
cardInfo.put("settle_cert_back_pic", f56);
String f15 = DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F15", mchModifyData.getNonLegSettleAuthPic(), mchApplyment.getIfCode());
cardInfo.put("auth_enturst_pic", f15);
param.put("card_info", cardInfo);
if ("Y".equals(mchModifyData.getIllegal())) {
cardInfo.put("card_type", "2");
} else {
if ("B".equals(mchModifyData.getSettAccountType())) {
cardInfo.put("card_type", "0");
} else {
cardInfo.put("card_type", "1");
}
}
if (!"B".equals(mchModifyData.getSettAccountType())) {
cardInfo.put("prov_id", mchModifyData.getSettAccountBankBranchAreaCode().getString(0));
cardInfo.put("area_id", mchModifyData.getSettAccountBankBranchAreaCode().getString(1));
} else {
cardInfo.put("branch_code", mchModifyData.getBankSubCode());
cardInfo.put("branch_name", mchModifyData.getSettAccountBankBranchName());
}
cardInfo.put("card_name", mchModifyData.getSettAccountName());
cardInfo.put("card_no", mchModifyData.getSettAccountNo());
cardInfo.put("bank_code", mchModifyData.getBankCode());
if ("Y".equals(mchModifyData.getIllegal())) {
cardInfo.put("cert_validity_type", mchModifyData.getSettAccountIdcardEffectEnd().equals("长期")? "1": "0");
cardInfo.put("cert_begin_date", mchModifyData.getSettAccountIdcardEffectBegin().replace("-", ""));
if (!mchModifyData.getSettAccountIdcardEffectEnd().equals("长期")) {
cardInfo.put("cert_end_date", mchModifyData.getSettAccountIdcardEffectEnd().replace("-", ""));
}
cardInfo.put("cert_no", mchModifyData.getSettAccountIdcardNo());
cardInfo.put("cert_type", "00");
cardInfo.put("mp", mchModifyData.getPhone());
}
if ("Y".equals(mchModifyData.getIllegal())) {
cardInfo.put("card_type", "2");
}
cardInfo.put("card_type", mchModifyData.getMchShortName());
cardInfo.put("receipt_name", mchModifyData.getMchShortName());
param.put("deal_type", "1");
JSONObject agreementInfo = new JSONObject();
agreementInfo.put("agreement_type", "0");
param.put("agreement_info", agreementInfo.toJSONString());
MchAppConfigContext mchAppConfigContext = new MchAppConfigContext();
mchAppConfigContext.setMchType(MchInfo.TYPE_ISVSUB);
mchAppConfigContext.setMchApplyment(mchApplyment);
try {
JSONObject applyResp = DgPayKit.request(mchAppConfigContext, param, DgPayKit.MCH_APPLY_MODIFY_URL, mchApplyment.getIfCode());
JSONObject applyRespData = applyResp.getJSONObject("data");
String code = applyRespData.getString("sub_resp_code");
String msg = applyRespData.getString("sub_resp_desc");
if ("00000000".equals(code)) {
// 请求成功
String applyNo = applyRespData.getString("apply_no");
resultModifyApplyment.setChannelApplyNo(applyNo);
resultModifyApplyment.setState(MchApplyment.STATE_AUDITING);
} else if ("90000000".equals(code)) {
// 审核中
String applyNo = applyRespData.getString("apply_no");
resultModifyApplyment.setChannelApplyNo(applyNo);
resultModifyApplyment.setState(MchApplyment.STATE_AUDITING);
} else {
// 其他暂定为失败
resultModifyApplyment.setApplyErrorInfo("[" + msg + "]");
resultModifyApplyment.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
}
String changeSysFlowId = applyRespData.getString("changeSysFlowId");
resultModifyApplyment.setChannelApplyNo(changeSysFlowId);
resultModifyApplyment.setChannelVar1(applyRespData.toJSONString());
resultModifyApplyment.setState(MchApplyment.STATE_AUDITING);
String remark = "斗拱支付结算信息变更已发起";
resultModifyApplyment.setRemark(remark);
log.debug("斗拱支付结算信息变更发起成功");
} catch (Exception e) {
resultModifyApplyment.setApplyErrorInfo("[" + e.getMessage() + "]");
resultModifyApplyment.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
}
return new MutablePair<>(resultModifyApplyment, null);
}
@Override
public MutablePair<MchModifyApplyment, MchApplyment> syncChannelModifyRate(MutablePair<MchModifyApplyment, MchApplyment> mchDataPair) {
return null;
}
@Override
public MutablePair<MchModifyApplyment, MchApplyment> syncChannelModifySettlementType(MutablePair<MchModifyApplyment, MchApplyment> mchDataPair) {
MchModifyApplyment resultModifyApplyment = new MchModifyApplyment();
MchModifyApplyment mchModifyApplyment = mchDataPair.getKey();
resultModifyApplyment.setModifyApplyId(mchModifyApplyment.getModifyApplyId());
resultModifyApplyment.setState(MchApplyment.STATE_AUDITING);
MchApplyment mchApplyment = mchDataPair.getValue();
String applyDetailInfo = mchModifyApplyment.getApplyDetailInfo();
MchModifyApplymentModel mchModifyData = JSON.parseObject(applyDetailInfo, MchModifyApplymentModel.class);
DgpayApplymentInfo applymentInfo = JSON.parseObject(mchDataPair.right.getApplyDetailInfo(), DgpayApplymentInfo.class);
DgpayIsvParams isvParams = ((DgpayIsvParams) configContextQueryService.queryIsvParams(mchApplyment));
JSONObject param = new JSONObject();
Date today = new Date();
String formatDate = DateUtil.format(today, DatePattern.PURE_DATE_PATTERN);
param.put("req_seq_id", formatDate + mchModifyApplyment.getApplyId());
param.put("req_date", formatDate);
param.put("upper_huifu_id", isvParams.getSysId());
param.put("huifu_id", mchApplyment.getChannelMchNo());
JSONObject settleConfig = new JSONObject();
settleConfig.put("settle_status", "1");
settleConfig.put("settle_cycle", mchModifyData.getSettlementType());
PaywayFee d0 = null;
PaywayFee d1 = null;
for (PaywayFee paywayFee : applymentInfo.getPaywayFeeList()) {
if (CS.PAY_WAY_CODE.D0.equals(paywayFee.getFeeType())) {
d0 = paywayFee;
}
if (CS.PAY_WAY_CODE.D1.equals(paywayFee.getFeeType())) {
d1 = paywayFee;
}
}
if (d1 != null) {
settleConfig.put("fixed_ratio", DgpayApplymentInfo.convertRate(d1.getFeeRate()));
} else {
// 默认D1提现费率为0.01%
settleConfig.put("fixed_ratio", "0.01");
}
param.put("settle_config", settleConfig.toJSONString());
JSONObject signUserInfo = new JSONObject();
signUserInfo.put("type", "LEGAL");
signUserInfo.put("mobile_no", mchApplyment.getContactPhone());
param.put("sign_user_info", signUserInfo);
param.put("async_return_url", getCallbackUrl(mchApplyment.getIfCode()));
JSONObject cashConfig = new JSONObject();
param.put("deal_type", "2");
cashConfig.put("switch_state", "0");
cashConfig.put("cash_type", mchModifyData.getSettlementType());
if (d0 != null) {
cashConfig.put("fee_rate", DgpayApplymentInfo.convertRate(d0.getFeeRate()));
} else {
// 默认D0提现费率设置为 0.1%
cashConfig.put("fee_rate", "0.10");
}
// 电子协议
JSONObject agreementInfo = new JSONObject();
agreementInfo.put("agreement_type", "0");
param.put("agreement_info", agreementInfo.toJSONString());
// 结算卡信息
JSONObject cardInfoJson = new JSONObject();
DgpayApplymentInfo info = JSON.parseObject(mchApplyment.getApplyDetailInfo(), DgpayApplymentInfo.class);
String settAccountType = info.getSettAccountType();
if ("B".equals(settAccountType)) {
// 对公
cardInfoJson.put("card_type", "0");
// 持卡人证件类型 默认为身份证
cardInfoJson.put("cert_type", "");
// 银行编码
cardInfoJson.put("bank_code", info.getBankCode());
// 联行号
cardInfoJson.put("branch_code", info.getBranchCode());
// 支行名称
cardInfoJson.put("branch_name", info.getBranchName());
cardInfoJson.put("mp", "");
// 签约人类型
// signUserInfoJson.put("type", "LEGAL");
} else if ("C".equals(settAccountType)) {
cardInfoJson.put("mp", info.getSettAccountBindPhone());
// 对私
cardInfoJson.put("card_type", "1");
// 持卡人证件类型 默认为身份证
cardInfoJson.put("cert_type", "00");
// 持卡人证件号码
cardInfoJson.put("cert_no", info.getSettAccountIdcardNo());
// 持卡人证件有效期类型
cardInfoJson.put("cert_validity_type", info.getCertValidityType(info.getSettAccountIdcardEffectEnd()));
// 持卡人证件有效期 起始
cardInfoJson.put("cert_begin_date", info.getSettAccountIdcardEffectBegin().replace("-", ""));
// 持卡人证件有效期 截止
if (!"长期".equals(info.getSettAccountIdcardEffectEnd())) {
cardInfoJson.put("cert_end_date", info.getSettAccountIdcardEffectEnd().replace("-", ""));
}
// 结算卡正面
// fileJson.put("settle_card_front_pic", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F13", info.getSettAccountLicenseImg(), mchApplyment.getIfCode()));
// // 结算卡反面
// fileJson.put("settle_card_back_pic", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F14", info.getSettBankCardBackImg(), mchApplyment.getIfCode()));
// // 结算人身份证人像面
// fileJson.put("settle_cert_front_pic", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F55", info.getSettAccountIdcard1Img(), mchApplyment.getIfCode()));
// // 结算人身份证国徽面
// fileJson.put("settle_cert_back_pic", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F56", info.getSettAccountIdcard2Img(), mchApplyment.getIfCode()));
// // 授权委托书
// fileJson.put("auth_enturst_pic", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F15", info.getAuthEnturstPic(), mchApplyment.getIfCode()));
//
// // 签约人类型
// signUserInfoJson.put("type", "LEGAL");
} else {
cardInfoJson.put("mp", info.getSettAccountBindPhone());
// 对私非法人
cardInfoJson.put("card_type", "2");
// 持卡人证件类型 默认为身份证
cardInfoJson.put("cert_type", "00");
// 持卡人证件号码
cardInfoJson.put("cert_no", info.getSettAccountIdcardNo());
// 持卡人证件有效期类型
cardInfoJson.put("cert_validity_type", info.getCertValidityType(info.getSettAccountIdcardEffectEnd()));
// 持卡人证件有效期 起始
cardInfoJson.put("cert_begin_date", info.getSettAccountIdcardEffectBegin().replace("-", ""));
// 持卡人证件有效期 截止
if (!"长期".equals(info.getSettAccountIdcardEffectEnd())) {
cardInfoJson.put("cert_end_date", info.getSettAccountIdcardEffectEnd().replace("-", ""));
}
// // 结算卡正面
// fileJson.put("settle_card_front_pic", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F13", info.getSettAccountLicenseImg(), mchApplyment.getIfCode()));
// // 结算卡反面
// fileJson.put("settle_card_back_pic", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F14", info.getSettBankCardBackImg(), mchApplyment.getIfCode()));
// // 结算人身份证人像面
// fileJson.put("settle_cert_front_pic", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F55", info.getSettAccountIdcard1Img(), mchApplyment.getIfCode()));
// // 结算人身份证国徽面
// fileJson.put("settle_cert_back_pic", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F56", info.getSettAccountIdcard2Img(), mchApplyment.getIfCode()));
// // 授权委托书
// fileJson.put("auth_enturst_pic", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F15", info.getAuthEnturstPic(), mchApplyment.getIfCode()));
// // 签约人法人授权书
// fileJson.put("sign_auth_file_id", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F245", info.getSignAuthFileId(), mchApplyment.getIfCode()));
//
// // 签约人类型
// signUserInfoJson.put("type", "OTHER");
}
// 卡户名
cardInfoJson.put("card_name", info.getSettAccountName());
// 银行卡号
cardInfoJson.put("card_no", info.getSettAccountNo());
// 银行所在省
cardInfoJson.put("prov_id", info.getSettAccountBankBranchAreaCode().get(0));
// 银行所在市
cardInfoJson.put("area_id", info.getSettAccountBankBranchAreaCode().get(1));
// 银行卡绑定手机号
cardInfoJson.put("mp", info.getSettAccountBindPhone());
param.put("card_info", cardInfoJson.toJSONString());
JSONArray casArr = new JSONArray();
casArr.add(cashConfig);
param.put("cash_config", casArr.toJSONString());
MchAppConfigContext mchAppConfigContext = new MchAppConfigContext();
mchAppConfigContext.setMchType(MchInfo.TYPE_ISVSUB);
mchAppConfigContext.setMchApplyment(mchApplyment);
try {
JSONObject applyResp = DgPayKit.request(mchAppConfigContext, param, DgPayKit.MCH_APPLY_MODIFY_URL, mchApplyment.getIfCode());
JSONObject applyRespData = applyResp.getJSONObject("data");
String code = applyRespData.getString("resp_code");
String msg = applyRespData.getString("resp_desc");
if ("00000000".equals(code)) {
// 请求通过
String applyNo = applyRespData.getString("apply_no");
resultModifyApplyment.setChannelApplyNo(applyNo);
resultModifyApplyment.setState(MchApplyment.STATE_SUCCESS);
return localModifySettlementType(mchDataPair);
} else if ("90000000".equals(code)) {
String applyNo = applyRespData.getString("apply_no");
resultModifyApplyment.setChannelApplyNo(applyNo);
resultModifyApplyment.setState(MchApplyment.STATE_AUDITING);
} else {
resultModifyApplyment.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
resultModifyApplyment.setApplyErrorInfo("[" + msg + "]");
}
resultModifyApplyment.setChannelVar1(applyRespData.toJSONString());
String remark = "斗拱支付结算方式变更已发起";
resultModifyApplyment.setRemark(remark);
log.info("斗拱支付结算方式变更发起成功");
} catch (Exception e) {
resultModifyApplyment.setApplyErrorInfo("[" + e.getMessage() + "]");
resultModifyApplyment.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
}
return new MutablePair<>(resultModifyApplyment, null);
}
@Override
public MutablePair<MchModifyApplyment, MchApplyment> queryModifyResult(MutablePair<MchModifyApplyment, MchApplyment> mchDataPair) {
MchModifyApplyment resultModifyApplyment = new MchModifyApplyment();
MchModifyApplyment modifyApplyment = mchDataPair.getLeft();
resultModifyApplyment.setModifyApplyId(modifyApplyment.getModifyApplyId());
MchApplyment mchApplyment = mchDataPair.getValue();
JSONObject reqParams = new JSONObject();
reqParams.put("huifu_id", mchApplyment.getChannelMchNo());
reqParams.put("apply_no", modifyApplyment.getChannelApplyNo());
MchAppConfigContext mchAppConfigContext = new MchAppConfigContext();
mchAppConfigContext.setMchType(MchInfo.TYPE_ISVSUB);
mchAppConfigContext.setMchApplyment(mchApplyment);
// 发送请求
JSONObject response = DgPayKit.request(mchAppConfigContext, reqParams, DgPayKit.MCH_APPLY_QUERY_URL, mchApplyment.getIfCode());
JSONObject bizData = response.getJSONObject("data");
String code = bizData.getString("resp_code");
if ("00000000".equals(code)) {
String applStatus = bizData.getString("apply_status");
if ("Y".equals(applStatus)) {
// 审核通过
resultModifyApplyment.setChannelVar1(processChannelValue(modifyApplyment.getChannelVar1(), bizData));
return handleSuccessCase(modifyApplyment, mchDataPair);
}
if ("N".equals(applStatus) || "F".equals(applStatus)) {
resultModifyApplyment.setChannelVar1(processChannelValue(modifyApplyment.getChannelVar1(), bizData));
resultModifyApplyment.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
resultModifyApplyment.setApplyErrorInfo("[" + bizData.getString("note") + "]");
return new MutablePair<>(resultModifyApplyment, null);
}
if ("P".equals(applStatus)) {
// 审核中,需要查询签约状态
JSONObject signReqParams = new JSONObject();
signReqParams.put("huifu_id", mchApplyment.getChannelMchNo());
JSONObject signResp = DgPayKit.request(mchAppConfigContext, signReqParams, DgPayKit.MCH_INFO_QUERY_URL, mchApplyment.getIfCode());
JSONObject signRespBizData = signResp.getJSONObject("data");
String respCode = signRespBizData.getString("resp_code");
if ("00000000".equals(respCode)) {
JSONArray agreementInfoList = signRespBizData.getJSONArray("agreement_info_list");
JSONObject item = agreementInfoList.getJSONObject(0);
String conStat = item.getString("con_stat");
if ("3".equals(conStat)) {
// 待签约
resultModifyApplyment.setState(MchApplyment.STATE_WAIT_SIGN);
return new MutablePair<>(resultModifyApplyment, null);
}
if ("5".equals(conStat)) {
// 签约完成
resultModifyApplyment.setState(MchApplyment.STATE_FINISH_SIGN);
return new MutablePair<>(resultModifyApplyment, null);
}
}
}
}
return mchDataPair;
}
private MutablePair<MchModifyApplyment, MchApplyment> handleSuccessCase(MchModifyApplyment modifyApplyment, MutablePair<MchModifyApplyment, MchApplyment> mchDataPair) {
switch (modifyApplyment.getModifyApplyType()) {
case MchModifyApplyment.MODIFY_TYPE_BASE:
return localModifyBase(mchDataPair);
case MchModifyApplyment.MODIFY_TYPE_SETTLEMENT:
return localModifySettlement(mchDataPair);
case MchModifyApplyment.MODIFY_TYPE_SETTLEMENT_TYPE:
return localModifySettlementType(mchDataPair);
default:
return mchDataPair;
}
}
private String processChannelValue(String channelVal, JSONObject bizData) {
JSONObject channelValJSON = StrUtil.isEmptyIfStr(channelVal) ? new JSONObject() : JSON.parseObject(channelVal);
channelValJSON.putAll(bizData);
return channelValJSON.toJSONString();
}
protected String getCallbackUrl(String ifCode) {
return sysConfigService.getDBApplicationConfig().getPaySiteUrl() + "/api/mchApplyment/notify/" + ifCode;
}
}

View File

@@ -0,0 +1,227 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.jeequan.jeepay.core.entity.MchApplyment;
import com.jeequan.jeepay.core.entity.MchInfo;
import com.jeequan.jeepay.core.entity.MchSubInfo;
import com.jeequan.jeepay.core.interfaces.paychannel.IIsvmchWxConfigService;
import com.jeequan.jeepay.core.model.applyment.ApplymentSignInfo;
import com.jeequan.jeepay.core.model.applyment.PaywayFee;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.params.dgpay.DgpayIsvParams;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.utils.DateKit;
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
import com.jeequan.jeepay.db.entity.MchSubInfoEntity;
import com.jeequan.jeepay.service.impl.MchSubInfoService;
import com.jeequan.jeepay.service.impl.SysConfigService;
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.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.List;
import java.util.Objects;
@Service
@Slf4j
public class DgpayIsvmchWxConfigService implements IIsvmchWxConfigService {
@Autowired
protected SysConfigService sysConfigService;
@Autowired
private MchSubInfoService mchSubInfoService;
@Override
public ChannelRetMsg configPayBaseUrl(String configBaseUrl, String isvNo, String mchNo, String mchAppId, String ifCode) {
return null;
}
@Override
public ChannelRetMsg applyModifyMchRate(List<PaywayFee> paywayFeeList, String isvNo, String mchNo, String mchAppId, String ifCode) {
return null;
}
@Override
public String queryConfiguredInfo(String isvNo, String mchNo, String mchAppId, String ifCode) {
return null;
}
@Override
public ChannelRetMsg configBindAppId(String configAppId, String isvNo, String mchNo, String mchAppId, String ifCode) {
return null;
}
@Override
public ChannelRetMsg configSubscribeAppId(String configAppId, String isvNo, String mchNo, String mchAppId, String ifCode) {
return ChannelRetMsg.confirmSuccess(null);
}
@Override
public ChannelRetMsg applyModifyMchShortName(String mchShortName, String isvNo, String mchNo, String mchAppId, String ifCode) {
return ChannelRetMsg.confirmSuccess(null);
}
@Override
public ChannelRetMsg applyModifyMchAppPublicKey(String appPublicKey, String isvNo, String mchNo, String mchAppId, String ifCode) {
return ChannelRetMsg.confirmSuccess(null);
}
@Override
public ApplymentSignInfo wxOpenSignInfo(MchApplyment mchApplyment) {
ApplymentSignInfo result = new ApplymentSignInfo();
JSONObject suJson = JSON.parseObject(mchApplyment.getSuccResParameter());
MchAppConfigContext mchAppConfigContext = new MchAppConfigContext();
mchAppConfigContext.setMchType(MchInfo.TYPE_ISVSUB);
mchAppConfigContext.setMchApplyment(mchApplyment);
// 服务商配置参数
ConfigContextQueryService configContextQueryService = SpringBeansUtil.getBean(ConfigContextQueryService.class);
DgpayIsvParams isvParams = (DgpayIsvParams) configContextQueryService.queryIsvParams(mchApplyment.getIsvNo(), mchApplyment.getIfCode());
if (suJson == null || StringUtils.isEmpty(suJson.getString("huifuId"))) {
result.setState("查询失败");
result.setErrInfo("商户参数为空,该商户还未进件成功");
return result;
}
// 微信开通意愿二维码地址
result.setSignUrl(isvParams.getWxOpenUrl());
result.setImgType(ApplymentSignInfo.IMG_TYPE_QRCONTENT);
// 请求参数
JSONObject reqParams = new JSONObject();
// 汇付ID
reqParams.put("huifu_id", suJson.getString("huifuId"));
// 请求日期
reqParams.put("req_date", DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN));
// 请求流水号
reqParams.put("req_seq_id", DateKit.currentTimeMillis() + "");
// 发送请求
JSONObject response = DgPayKit.request(mchAppConfigContext, reqParams, "/merchant/busi/realname/query", mchApplyment.getIfCode());
// 请求流水号
reqParams.put("req_seq_id", DateKit.currentTimeMillis() + "_info");
// 商户详情is_open_small_flag -> 0
JSONObject mchInfoData = DgPayKit.request(mchAppConfigContext, reqParams, "/merchant/basicdata/query", mchApplyment.getIfCode()).getJSONObject("data");
String respCode = mchInfoData.getString("resp_code");
String respDesc = mchInfoData.getString("resp_desc");
if (!respCode.equals("00000000")) {
result.setState("获取子商户状态异常");
result.setErrInfo("" + respDesc + "");
result.setImgType("qrContent");
result.setSignUrl(isvParams.getAliChannelExtUrl());
return result;
}
String feeConfig = "";
JSONArray wxConfList = mchInfoData.getJSONArray("qry_wx_conf_list");
JSONArray aliConfList = mchInfoData.getJSONArray("qry_ali_conf_list");
JSONObject unionConf = mchInfoData.getJSONObject("qry_union_conf");
JSONObject bankCardConf = mchInfoData.getJSONObject("qry_bank_card_conf");
log.info("【商户信息】微信配置:{}, 支付宝配置:{}, 银联配置:{}, 银行卡配置:{}", wxConfList, aliConfList, unionConf, bankCardConf);
if (!CollUtil.isEmpty(wxConfList)) {
feeConfig = "【微信费率配置成功】";
}
if (!CollUtil.isEmpty(aliConfList)) {
feeConfig = feeConfig + "【支付宝费率配置成功】";
}
if (!CollUtil.isEmpty(unionConf)) {
feeConfig = feeConfig + "【银联费率配置成功】";
}
if (!CollUtil.isEmpty(bankCardConf)) {
feeConfig = feeConfig + "【银行卡支付配置成功】";
}
for (int wxIndex = 0; wxIndex < wxConfList.size(); wxIndex++) {
JSONArray aliMerInfos = wxConfList.getJSONObject(wxIndex).getJSONArray("wx_mer_infos");
if (!CollUtil.isEmpty(aliMerInfos)) {
for (int i = 0; i < aliMerInfos.size(); i++) {
JSONObject item = aliMerInfos.getJSONObject(i);
// 支付宝子商户号存在时
MchSubInfoEntity wxSubInfo = new MchSubInfoEntity();
wxSubInfo.setMchApplyId(mchApplyment.getApplyId());
String payChannelId = item.getString("pay_channel_id");
String bankMerCode = item.getString("bank_mer_code");
if (Objects.equals(isvParams.getChannelNo(), payChannelId)) {
wxSubInfo.setMainUse(1);
result.setChannelSubMchId(bankMerCode);
} else {
wxSubInfo.setMainUse(0);
}
wxSubInfo.setSubMchWay("DG");
wxSubInfo.setChannelMchNo(mchApplyment.getChannelMchNo());
wxSubInfo.setChannelId(payChannelId);
wxSubInfo.setAuthStatus(MchSubInfo.AUTH_STATUS_UNAUTHORIZED);
wxSubInfo.setSubMchId(bankMerCode);
wxSubInfo.setSubMchType("WX");
// 此处取1为正常
wxSubInfo.setStatus("1");
wxSubInfo.setExt(item);
try {
mchSubInfoService.save(wxSubInfo);
} catch (DuplicateKeyException e) {
log.info("子商户信息已存储过");
}
}
}
}
result.setOriginData(feeConfig);
JSONObject resData = response.getJSONObject("data");
String code = resData.getString("resp_code");
// 驳回原因
String rejectMsg = resData.getString("reject_reason");
// 请求异常
if (!DgPayKit.STATE_SUCCESS.equals(code)) {
result.setErrInfo(resData.getString("resp_desc"));
return result;
}
// 申请单状态
String applymentStat = resData.getString("applyment_stat");
// 授权状态
String authorizeStat = resData.getString("authorize_stat");
// // 小程序码
// result.setSignUrl(resData.getString("qrcode_data"));
LambdaUpdateChainWrapper<MchSubInfoEntity> stateUpdate = mchSubInfoService.lambdaUpdate()
.eq(MchSubInfoEntity::getMchApplyId, mchApplyment.getApplyId())
.eq(MchSubInfoEntity::getMainUse, 1)
.eq(MchSubInfoEntity::getSubMchType, "WX");
// 覆写状态
if (StringUtils.isNotEmpty(authorizeStat)) {
if ("0".equals(authorizeStat)) {
stateUpdate.set(MchSubInfoEntity::getAuthStatus, MchSubInfo.AUTH_STATUS_UNAUTHORIZED).update();
result.setState("未授权");
} else if ("1".equals(authorizeStat)) {
result.setState("已授权"); // 覆写状态 其他不考虑
stateUpdate.set(MchSubInfoEntity::getAuthStatus, MchSubInfo.AUTH_STATUS_AUTHORIZED).update();
}
}
return result;
}
}

View File

@@ -0,0 +1,174 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.MchApplyment;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.model.applyment.PaywayFee;
import com.jeequan.jeepay.core.model.params.dgpay.DgpayIsvParams;
import com.jeequan.jeepay.core.model.rqrs.mch.ChannelMchRq;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.model.rqrs.settle.ChannelSettleRq;
import com.jeequan.jeepay.db.entity.SettleInfo;
import com.jeequan.jeepay.thirdparty.channel.AbstractMchApiService;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import lombok.AllArgsConstructor;
import lombok.Getter;
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.stereotype.Service;
import java.math.BigDecimal;
import java.util.*;
/**
* TODO
*
* @author crystal
* @date 2023/12/4 10:47
*/
@Service
@Slf4j
public class DgpayMchApiService extends AbstractMchApiService<DgpayIsvParams> {
@Autowired
private ConfigContextQueryService configContextQueryService;
@Override
public String getIfCode() {
return CS.IF_CODE.DGPAY;
}
@Override
public boolean preCheck() {
return true;
}
/**
*
* @param applyment
* @param mchRq
* 01-标准费率线上(支持统一进件页面版)
* 02-标准费率线下(支持统一进件页面版)
* 03-非盈利行业费率
* 04-缴费行业费率
* 05-保险行汪费率
* 06-行业(蓝海绿洲)活动场景费率
* 07-校园餐饮费率
* 08-K12中小幼费率
* 09-非在线教培行业费率
* @return
*/
@Override
public ChannelRetMsg appidAndPath(MchApplyment applyment, ChannelMchRq mchRq) {
JSONObject bizData = new JSONObject();
bizData.put("req_seq_id", getIfCode() + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_MS_PATTERN));
bizData.put("req_date", DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN));
bizData.put("huifu_id",applyment.getChannelMchNo());
if(ChannelMchRq.Type.PATH.getType().equals(mchRq.getType())){
bizData.put("wx_woa_path",mchRq.getJsapiPath());
}else if(ChannelMchRq.Type.PUBLIC.getType().equals(mchRq.getType())){
bizData.put("wx_woa_app_id",mchRq.getAppid());
}else if(ChannelMchRq.Type.APPLET.getType().equals(mchRq.getType())){
bizData.put("wx_applet_app_id",mchRq.getAppid());
}
//默认写死 还不知道作用是什么 传线下
bizData.put("fee_type","02");
DgpayIsvParams isvParams = ((DgpayIsvParams) configContextQueryService.queryIsvParams(applyment));
JSONObject respBizData = DgPayKit.req(DgPayKit.MCH_BUSI_CONFIG_URL, isvParams, bizData);
ChannelRetMsg retMsg = new ChannelRetMsg();
retMsg.setChannelBizData(respBizData);
retMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
return retMsg;
}
/**
* 斗拱结算信息查询接口
* @param settleRq
* @param isvNo
* @return
*/
@Override
public List<JSONObject> querySettleInfo(ChannelSettleRq settleRq, String isvNo, DgpayIsvParams isvParams) {
if(StringUtils.isBlank(settleRq.getMercNo())){
throw new BizException("缺失商户号");
}
JSONObject bizData = new JSONObject();
bizData.put("req_seq_id",getIfCode() + DateUtil.format(new DateTime(),DatePattern.PURE_DATETIME_MS_FORMATTER));
bizData.put("req_date",DateUtil.format(new DateTime(),DatePattern.PURE_DATE_PATTERN));
bizData.put("huifu_id",settleRq.getMercNo());
bizData.put("begin_date",DateUtil.format(settleRq.getStartDate(),DatePattern.PURE_DATE_PATTERN));
bizData.put("end_date",DateUtil.format(settleRq.getEndDate(),DatePattern.PURE_DATE_PATTERN));
bizData.put("page_num",settleRq.getPage());
bizData.put("page_size",settleRq.getSize());
JSONObject respBizData = DgPayKit.req(DgPayKit.MCH_BUSI_SETTLE_QUERY_URL, isvParams, bizData);
JSONArray infos = respBizData.getJSONArray("trans_log_result_list");
if(infos == null || infos.isEmpty()){
return null;
}
List<JSONObject> result = new ArrayList<>(infos.size());
infos.forEach(item -> {
JSONObject itemData = ((JSONObject) item);
JSONObject stlData = new JSONObject();
stlData.put("billNo",itemData.getString("trans_id"));
stlData.put("isvNo", isvNo);
stlData.put("settleDate",DateUtil.format(DateUtil.parse(itemData.getString("trans_date"),DatePattern.PURE_DATE_PATTERN),DatePattern.NORM_DATE_PATTERN));
stlData.put("accountNo",itemData.getString("card_no"));
stlData.put("accountName",itemData.getString("card_name"));
stlData.put("settleAmt",itemData.getBigDecimal("trans_amt").multiply(BigDecimal.valueOf(100)).longValue());
// long feeAmt = itemData.getBigDecimal("fee_amt").multiply(BigDecimal.valueOf(100)).longValue();
// if( feeAmt > 0L){
// stlData.put("fee",feeAmt);
// }
stlData.put("remark",itemData.getString("settle_desc"));
SettleState val = SettleState.getVal(itemData.getString("trans_stat"));
switch (val){
case PAY_SUCCESS:
stlData.put("state", SettleInfo.SUCCESS);
break;
case PAY_FAIL:
stlData.put("state", SettleInfo.FAIL);
break;
case PAY_PROGRESS:
stlData.put("state", SettleInfo.PROGRESS);
break;
}
stlData.put("channelState",val.getState());
result.add(stlData);
});
return result;
}
@Getter
@AllArgsConstructor
public enum SettleState{
PAY_PROGRESS("I","付款中"),
PAY_SUCCESS("S","付款成功"),
PAY_FAIL("F","付款失败");
private final String state;
private final String desc;
public static SettleState getVal(String state){
SettleState[] values = values();
for (SettleState val:values) {
if(val.getState().equals(state)){
return val;
}
}
return PAY_PROGRESS;
}
}
}

View File

@@ -0,0 +1,815 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.MchApplyment;
import com.jeequan.jeepay.core.entity.MchInfo;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.interfaces.paychannel.IIsvmchApplymentService;
import com.jeequan.jeepay.core.model.DBApplicationConfig;
import com.jeequan.jeepay.core.model.applyment.ApplymentSignInfo;
import com.jeequan.jeepay.core.model.applyment.DgpayApplymentInfo;
import com.jeequan.jeepay.core.model.applyment.PaywayFee;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.oauth2.WxpayOauth2Params;
import com.jeequan.jeepay.core.model.params.dgpay.DgpayIsvParams;
import com.jeequan.jeepay.core.model.params.dgpay.DgpayIsvsubMchParams;
import com.jeequan.jeepay.core.utils.DateKit;
import com.jeequan.jeepay.core.utils.JsonKit;
import com.jeequan.jeepay.service.impl.RateConfigService;
import com.jeequan.jeepay.service.impl.SysConfigService;
import com.jeequan.jeepay.thirdparty.channel.dgpay.utils.DgUtils;
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.stereotype.Service;
import java.util.Date;
import java.util.List;
/**
* 斗拱接口进件
*
* @author xiaoyu
* @date 2022/6/7 10:23
*/
@Service
@Slf4j
public class DgpayMchApplymentService implements IIsvmchApplymentService<DgpayApplymentInfo> {
@Autowired
protected SysConfigService sysConfigService;
@Autowired
private ConfigContextQueryService configContextQueryService;
@Autowired
private RateConfigService rateConfigService;
@Override
public MchApplyment firstApplyment(MchApplyment mchApplyment) {
// 直接丢到异步处理进件逻辑, 通用请求可照此逻辑
MchApplyment result = new MchApplyment();
result.setApplyId(mchApplyment.getApplyId());
result.setState(com.jeequan.jeepay.core.entity.MchApplyment.STATE_AUDITING_WAIT);
result.setRemainStep((byte) 1);
DgpayApplymentInfo applymentInfo = JSON.parseObject(mchApplyment.getApplyDetailInfo(), DgpayApplymentInfo.class);
result.setSettlementType(applymentInfo.getSettleCycle());
return result;
}
@Override
public MchApplyment rejectModify(MchApplyment mchApplyment) {
String logPrefix = "【斗拱商户进件】";
MchApplyment result = new MchApplyment();
try {
// 如果未进件成功执行第一次进件方法
String succResParameter = mchApplyment.getSuccResParameter();
if (StringUtils.isEmpty(succResParameter)) {
return firstApplyment(mchApplyment);
}
JSONObject succJson = JSON.parseObject(succResParameter);
String huifuId = succJson.getString("huifuId");
// 缺少必要参数 执行第一次进件
if (StringUtils.isEmpty(huifuId)) {
return firstApplyment(mchApplyment);
}
// 获取支付参数
MchAppConfigContext mchAppConfigContext = new MchAppConfigContext();
mchAppConfigContext.setMchType(MchInfo.TYPE_ISVSUB);
mchAppConfigContext.setMchApplyment(mchApplyment);
// 获取详细参数
JSONObject reqParams = getApplymentParams(mchApplyment);
reqParams.put("huifu_id", huifuId);
// 业务处理类型 1基本信息修改 2业务开通 3业务修改 4审核材料补充审核材料补充除必填字段外仅需要传入file_info字段
reqParams.put("deal_type", "3");
// 发送请求
JSONObject response = DgPayKit.request(mchAppConfigContext, reqParams, DgPayKit.MCH_APPLY_UPDATE_URL, mchApplyment.getIfCode());
JSONObject resData = response.getJSONObject("data");
String code = resData.getString("resp_code");
if ("00000000".equals(code)) {
// 渠道号
String channelOrderNo = resData.getString("apply_no");
result.setChannelApplyNo(channelOrderNo);
// 进件响应参数
result.setChannelVar1(resData.toJSONString());
result.setState(MchApplyment.STATE_AUDITING);
} else if ("90000000".equals(code)) {
result.setState(MchApplyment.STATE_AUDITING);
} else {
result.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
result.setApplyErrorInfo(code + "[" + resData.getString("resp_desc") + "]");
log.error("{} 请求失败code={}, msg={}", logPrefix, code, resData.getString("resp_desc"));
return result;
}
} catch (Exception e) {
result.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
result.setApplyErrorInfo("[" + e.getMessage() + "]");
log.error("{} 进件失败:", logPrefix, e);
}
return result;
}
@Override
public MchApplyment replenishInfo(MchApplyment mchApplyment) {
return null;
}
@Override
public MchApplyment query(MchApplyment mchApplyment) {
String logPrefix = "【斗拱商户进件状态查询】";
MchApplyment result = new MchApplyment();
try {
String succResParameter = mchApplyment.getSuccResParameter();
JSONObject succJson = JSON.parseObject(succResParameter);
String huifuId = succJson.getString("huifuId");
// 获取支付参数
MchAppConfigContext mchAppConfigContext = new MchAppConfigContext();
mchAppConfigContext.setMchType(MchInfo.TYPE_ISVSUB)
.setMchApplyment(mchApplyment);
// 请求参数
JSONObject reqParams = new JSONObject();
reqParams.put("huifu_id", huifuId);
reqParams.put("apply_no", mchApplyment.getChannelApplyNo());
reqParams.put("req_date", DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN));
reqParams.put("req_seq_id", DateKit.currentTimeMillis());
// 发送请求
JSONObject response = DgPayKit.request(mchAppConfigContext, reqParams, DgPayKit.MCH_APPLY_QUERY_URL, mchApplyment.getIfCode());
JSONObject resData = response.getJSONObject("data");
String code = resData.getString("resp_code");
if ("00000000".equals(code)) {
String applStatus = resData.getString("apply_status");
// 入驻状态
String regStatus = resData.getString("reg_status");
if ("Y".equals(applStatus)) {
// 进件查询响应参数
// result.setChannelVar1(resData.toJSONString());
// 更新错误信息
result.setApplyErrorInfo("");
result.setState(MchApplyment.STATE_WAIT_SIGN);
if ("S".equals(regStatus)) {
// 入驻成功
// result.setChannelVar2(resData.toJSONString());
result.setState(MchApplyment.STATE_SUCCESS);
result.setChannelMchNo(huifuId);
// 参数重写 商户号+绑卡序列号
queryMchInfo(result, mchAppConfigContext, huifuId, mchApplyment.getIfCode());
} else {
result.setApplyErrorInfo(code + "[" + resData.getString("apply_reason") + "]");
}
} else if ("P".equals(applStatus)) {
if ("S".equals(regStatus)) {
result.setState(MchApplyment.STATE_WAIT_SIGN);
} else {
result.setState(MchApplyment.STATE_AUDITING);
}
} else {
result.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
result.setApplyErrorInfo(code + "[" + resData.getString("apply_reason") + "]");
}
} else if ("90000000".equals(code)) {
result.setState(MchApplyment.STATE_AUDITING);
} else {
result.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
result.setApplyErrorInfo(code + "[" + resData.getString("resp_desc") + "]");
log.error("{} 请求失败code={}, msg={}", logPrefix, code, resData.getString("resp_desc"));
return result;
}
} catch (BizException e) {
result.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
result.setApplyErrorInfo("[" + e.getMessage() + "]");
log.error("{} 进件失败:", logPrefix, e);
} catch (Exception e) {
log.info("未知异常", e);
throw new BizException(e.getMessage());
}
return result;
}
@Override
public ApplymentSignInfo signInfo(MchApplyment mchApplyment) {
return null;
}
public JSONObject getApplymentParams(MchApplyment mchApplyment) {
// 获取支付参数
DgpayIsvParams isvParams = (DgpayIsvParams) configContextQueryService.queryIsvParams(mchApplyment);
// 获取详细参数
DgpayApplymentInfo info = JSON.parseObject(mchApplyment.getApplyDetailInfo(), DgpayApplymentInfo.class);
// 请求参数
JSONObject reqParams = new JSONObject();
// 文件信息
JSONObject fileJson = new JSONObject();
// 认证信息
JSONObject realNameInfoJson = new JSONObject();
// 请求日期
reqParams.put("req_date", DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN));
// 业务申请编号
reqParams.put("req_seq_id", DateKit.currentTimeMillis());
// 渠道商户号
reqParams.put("upper_huifu_id", isvParams.getSysId());
// ONLINE线上场景OFFLINE线下场景ALL线上线下 场景类型
reqParams.put("scene_type", "1".equals(isvParams.getPayScene()) ? "OFFLINE" : "ONLINE");
// 实名认证类型
realNameInfoJson.put("realname_info_type", "AW");
// 支付场景 -- 线下场景
realNameInfoJson.put("pay_scene", "1");
if (CollUtil.isEmpty(info.getAreaCode())) {
throw new BizException("请选择店铺经营省市区地址");
}
boolean bankAllow = DgUtils.checkBankLimitArea(info.getAreaCode().getString(0));
// 经营地址 省
reqParams.put("prov_id", info.getAreaCode().get(0));
// 经营地址 市
reqParams.put("area_id", info.getAreaCode().get(1));
// 经营地址 区
reqParams.put("district_id", info.getAreaCode().get(2));
// 详细经营地址
reqParams.put("detail_addr", info.getAddress());
// 签约人
JSONObject signUserInfoJson = new JSONObject();
// 签约人类型
signUserInfoJson.put("type", "LEGAL");
// 异步通知地址
String notifyUrl = sysConfigService.getDBApplicationConfig().getPaySiteUrl() + "/api/mchApplyment/notify/" + CS.IF_CODE.DGPAY;
reqParams.put("async_return_url", notifyUrl);
reqParams.put("busi_async_return_url", notifyUrl);
// 是否发送短信通知到商户 设置为发送
reqParams.put("sms_send_flag", "A");
// 卡信息
JSONObject cardInfoJson = new JSONObject();
String settAccountType = info.getSettAccountType();
if ("B".equals(settAccountType)) {
// 对公
cardInfoJson.put("card_type", "0");
// 持卡人证件类型 默认为身份证
cardInfoJson.put("cert_type", "");
// 银行编码
cardInfoJson.put("bank_code", info.getBankCode());
// 联行号
cardInfoJson.put("branch_code", info.getBranchCode());
// 支行名称
cardInfoJson.put("branch_name", info.getBranchName());
// 签约人类型
signUserInfoJson.put("type", "LEGAL");
} else if ("C".equals(settAccountType)) {
// 对私
cardInfoJson.put("card_type", "1");
// 持卡人证件类型 默认为身份证
cardInfoJson.put("cert_type", "00");
// 持卡人证件号码
cardInfoJson.put("cert_no", info.getSettAccountIdcardNo());
// 持卡人证件有效期类型
cardInfoJson.put("cert_validity_type", info.getCertValidityType(info.getSettAccountIdcardEffectEnd()));
// 持卡人证件有效期 起始
cardInfoJson.put("cert_begin_date", info.getSettAccountIdcardEffectBegin().replace("-", ""));
// 持卡人证件有效期 截止
if (!"长期".equals(info.getSettAccountIdcardEffectEnd())) {
cardInfoJson.put("cert_end_date", info.getSettAccountIdcardEffectEnd().replace("-", ""));
}
// 结算卡正面
fileJson.put("settle_card_front_pic", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F13", info.getSettAccountLicenseImg(), mchApplyment.getIfCode()));
// 结算卡反面
fileJson.put("settle_card_back_pic", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F14", info.getSettBankCardBackImg(), mchApplyment.getIfCode()));
// 结算人身份证人像面
fileJson.put("settle_cert_front_pic", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F55", info.getSettAccountIdcard1Img(), mchApplyment.getIfCode()));
// 结算人身份证国徽面
fileJson.put("settle_cert_back_pic", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F56", info.getSettAccountIdcard2Img(), mchApplyment.getIfCode()));
// 授权委托书
fileJson.put("auth_enturst_pic", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F15", info.getAuthEnturstPic(), mchApplyment.getIfCode()));
// 签约人类型
signUserInfoJson.put("type", "LEGAL");
} else {
// 对私非法人
cardInfoJson.put("card_type", "2");
// 持卡人证件类型 默认为身份证
cardInfoJson.put("cert_type", "00");
// 持卡人证件号码
cardInfoJson.put("cert_no", info.getSettAccountIdcardNo());
// 持卡人证件有效期类型
cardInfoJson.put("cert_validity_type", info.getCertValidityType(info.getSettAccountIdcardEffectEnd()));
// 持卡人证件有效期 起始
cardInfoJson.put("cert_begin_date", info.getSettAccountIdcardEffectBegin().replace("-", ""));
// 持卡人证件有效期 截止
if (!"长期".equals(info.getSettAccountIdcardEffectEnd())) {
cardInfoJson.put("cert_end_date", info.getSettAccountIdcardEffectEnd().replace("-", ""));
}
// 结算卡正面
fileJson.put("settle_card_front_pic", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F13", info.getSettAccountLicenseImg(), mchApplyment.getIfCode()));
// 结算卡反面
fileJson.put("settle_card_back_pic", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F14", info.getSettBankCardBackImg(), mchApplyment.getIfCode()));
// 结算人身份证人像面
fileJson.put("settle_cert_front_pic", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F55", info.getSettAccountIdcard1Img(), mchApplyment.getIfCode()));
// 结算人身份证国徽面
fileJson.put("settle_cert_back_pic", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F56", info.getSettAccountIdcard2Img(), mchApplyment.getIfCode()));
// 授权委托书
fileJson.put("auth_enturst_pic", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F15", info.getAuthEnturstPic(), mchApplyment.getIfCode()));
// 签约人法人授权书
fileJson.put("sign_auth_file_id", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F245", info.getSignAuthFileId(), mchApplyment.getIfCode()));
// 签约人类型
signUserInfoJson.put("type", "OTHER");
}
// 卡户名
cardInfoJson.put("card_name", info.getSettAccountName());
// 银行卡号
cardInfoJson.put("card_no", info.getSettAccountNo());
// 银行所在省
cardInfoJson.put("prov_id", info.getSettAccountBankBranchAreaCode().get(0));
// 银行所在市
cardInfoJson.put("area_id", info.getSettAccountBankBranchAreaCode().get(1));
// 银行卡绑定手机号
cardInfoJson.put("mp", info.getSettAccountBindPhone());
// 结算卡信息
reqParams.put("card_info", cardInfoJson);
// 结算配置
JSONObject settConfig = new JSONObject();
// 结算周期
settConfig.put("settle_cycle", info.getSettleCycle());
// 起结金额
settConfig.put("min_amt", "0.00");
// 留存金额
settConfig.put("remained_amt", "0.00");
if ("D1".equals(info.getSettleCycle())) {
// D1费率 [0.00 - 100.00]
List<PaywayFee> paywayFeeList = info.getPaywayFeeList();
for (PaywayFee paywayFee : paywayFeeList) {
if (CS.PAY_WAY_CODE.D1.equals(paywayFee.getWayCode())) {
settConfig.put("fixed_ratio", DgpayApplymentInfo.convertRate(paywayFee.getFeeRate()));
}
}
settConfig.put("fixed_ratio", isvParams.getSettleFee());
// D1结算协议图片文件
if (StringUtils.isNotEmpty(info.getSettleAgreePic())) {
fileJson.put("settle_agree_pic", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F116", info.getSettleAgreePic(), mchApplyment.getIfCode()));
}
}
reqParams.put("settle_config", settConfig.toJSONString());
// 取现配置
if (!"0".equals(isvParams.getMchSettManual())) {
JSONObject cashJson = new JSONObject();
// 取现类型
cashJson.put("cash_type", isvParams.getMchSettManual());
if (StringUtils.isNotEmpty(isvParams.getCashFee())) {
cashJson.put("fee_rate", isvParams.getCashFee());
} else {
cashJson.put("fix_amt", "0.00");
cashJson.put("fee_rate", "0.00");
}
JSONArray casArr = new JSONArray();
casArr.add(cashJson);
reqParams.put("cash_config", casArr.toJSONString());
}
if (StringUtils.isNotEmpty(info.getLoginUserName())) {
// 管理员账号
reqParams.put("login_name", info.getLoginUserName().trim());
}
// 所属行业MCC
reqParams.put("mcc", info.getMccCode());
// 个人商户
if (info.getMerchantType() == 1) {
// 个人商户
reqParams.put("ent_type", "0");
// 商户名
reqParams.put("reg_name", info.getContactName());
// 商户简称
reqParams.put("short_name", info.getMchFullName());
JSONObject contactInfoJson = new JSONObject();
// 联系人姓名
contactInfoJson.put("contact_name", info.getContactName());
// 联系人手机号
contactInfoJson.put("contact_mobile_no", info.getContactPhone());
// 联系人邮箱
contactInfoJson.put("contact_email", info.getContactEmail());
// 联系人身份证号
contactInfoJson.put("contact_cert_no", info.getContactIdcardNo());
// 联系人信息
reqParams.put("contact_info", contactInfoJson);
// 签约人手机号
signUserInfoJson.put("sign_mobile_no", info.getContactPhone());
// 签约人姓名
signUserInfoJson.put("name", info.getContactName());
// 签约人手机号
signUserInfoJson.put("mobile_no", info.getContactPhone());
// 签约人身份证号
signUserInfoJson.put("cert_no", info.getContactIdcardNo());
// TODO **** 兼容 旧接口 ****
// 签约人姓名
signUserInfoJson.put("sign_name", info.getContactName());
// 签约人身份证号
signUserInfoJson.put("sign_cert_no", info.getContactIdcardNo());
reqParams.put("sign_user_info", signUserInfoJson);
// 联系人姓名
realNameInfoJson.put("name", info.getContactName());
// 联系人手机号
realNameInfoJson.put("mobile", info.getContactPhone());
// 联系人证件号
realNameInfoJson.put("contact_id_card_number", info.getContactIdcardNo());
// 法人身份证人像面照片
fileJson.put("identification_front_pic", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F40", info.getIdcard1Img(), mchApplyment.getIfCode()));
// 法人身份证国徽面照片
fileJson.put("identification_back_pic", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F41", info.getIdcard2Img(), mchApplyment.getIfCode()));
} else {
// 公司类型
reqParams.put("ent_type", info.getEntType());
// 商户名称
reqParams.put("reg_name", info.getMchFullName());
// 商户英文名称
reqParams.put("mer_en_name", info.getMerEnName());
// 经营名称
reqParams.put("short_name", info.getMchShortName());
// 小票名称
reqParams.put("receipt_name", info.getReceiptName());
// 经营类型
reqParams.put("busi_type", info.getBusiType());
// 注册资本
reqParams.put("reg_capital", info.getRegCapital());
// 执照类型 NATIONAL_LEGAL-营业执照 NATIONAL_LEGAL_MERGE-多证合一
reqParams.put("license_type", "NATIONAL_LEGAL");
// 营业执照编号
reqParams.put("license_code", info.getLicenseNo());
// 经营范围
reqParams.put("business_scope", info.getLicenseBusiness());
// 营业执照有效期类型
reqParams.put("license_validity_type", info.getCertValidityType(info.getLicenseEffectEnd()));
// 营业执照有效期开始日期
reqParams.put("license_begin_date", info.getLicenseEffectBegin().replace("-", ""));
// 营业执照有效期截止日期
if (!"长期".equals(info.getLicenseEffectEnd())) {
reqParams.put("license_end_date", info.getLicenseEffectEnd().replace("-", ""));
}
JSONObject contactInfoJson = new JSONObject();
// 联系人姓名
contactInfoJson.put("contact_name", info.getIdcardName());
// 联系人手机号
contactInfoJson.put("contact_mobile_no", info.getIdCardPhone());
// 联系人邮箱
contactInfoJson.put("contact_email", info.getIdCardEmail());
// 联系人身份证号
contactInfoJson.put("contact_cert_no", info.getIdcardNo());
// 联系人信息
reqParams.put("contact_info", contactInfoJson);
// 非法人结算签约人信息
if (StringUtils.isNotEmpty(info.getSettAccountType()) && "D".equals(info.getSettAccountType())) {
// 签约人手机号
signUserInfoJson.put("sign_mobile_no", info.getSettAccountBindPhone());
// 签约人姓名
signUserInfoJson.put("name", info.getSettAccountName());
// 签约人手机号
signUserInfoJson.put("mobile_no", info.getSettAccountBindPhone());
// 签约人身份证号
signUserInfoJson.put("cert_no", info.getSettAccountIdcardNo());
// TODO **** 兼容 旧接口 ****
// 签约人姓名
signUserInfoJson.put("sign_name", info.getSettAccountName());
// 签约人身份证号
signUserInfoJson.put("sign_cert_no", info.getSettAccountIdcardNo());
} else {
// 签约人手机号
signUserInfoJson.put("sign_mobile_no", info.getIdCardPhone());
signUserInfoJson.put("mobile_no", info.getIdCardPhone());
// 签约人姓名
signUserInfoJson.put("name", info.getIdcardName());
// 签约人身份证号
signUserInfoJson.put("cert_no", info.getIdcardNo());
// TODO **** 兼容 旧接口 ****
// 签约人姓名
signUserInfoJson.put("sign_name", info.getIdcardName());
// 签约人身份证号
signUserInfoJson.put("sign_cert_no", info.getIdcardNo());
}
reqParams.put("sign_user_info", signUserInfoJson);
// 联系人姓名
realNameInfoJson.put("name", info.getIdcardName());
// 联系人手机号
realNameInfoJson.put("mobile", info.getIdCardPhone());
// 联系人证件号
realNameInfoJson.put("contact_id_card_number", info.getIdcardNo());
// 注册 省
reqParams.put("reg_prov_id", info.getAreaCode().get(0));
// 注册 市
reqParams.put("reg_area_id", info.getAreaCode().get(1));
// 注册 区
reqParams.put("reg_district_id", info.getAreaCode().get(2));
// 注册地址
reqParams.put("reg_detail", info.getAddress());
// 法人信息
JSONObject legalInfoJson = new JSONObject();
// 法人姓名
legalInfoJson.put("legal_name", info.getIdcardName());
// 法人证件类型 默认身份证
legalInfoJson.put("legal_cert_type", "00");
// 法人证件号码
legalInfoJson.put("legal_cert_no", info.getIdcardNo());
// 法人手机号
legalInfoJson.put("legal_mobile_no", info.getIdCardPhone());
// 法人证件居住地
legalInfoJson.put("legal_identification_address", info.getIdcardAddress());
// 法人证件有效期类型
legalInfoJson.put("legal_cert_validity_type", info.getCertValidityType(info.getIdcardEffectEnd()));
// 法人证件有效期开始日期
legalInfoJson.put("legal_cert_begin_date", info.getIdcardEffectBegin().replace("-", ""));
if ("0".equals(info.getCertValidityType(info.getIdcardEffectEnd()))) {
// 法人证件有效期截止日期
legalInfoJson.put("legal_cert_end_date", info.getIdcardEffectEnd().replace("-", ""));
}
reqParams.put("legal_info", legalInfoJson);
// 客服电话
reqParams.put("service_phone", info.getServicePhone());
// 门头照
reqParams.put("store_header_pic", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F18", info.getStoreOuterImg(), mchApplyment.getIfCode()));
// 内景照
reqParams.put("store_indoor_pic", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F19", info.getStoreInnerImg(), mchApplyment.getIfCode()));
// 收银台照
reqParams.put("store_cashier_desk_pic", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F20", info.getStoreCashierImg(), mchApplyment.getIfCode()));
// 开户许可证
fileJson.put("reg_acct_pic", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F08", info.getCompanyAccountLicenseImg(), mchApplyment.getIfCode()));
// 法人身份证人像面照片
fileJson.put("legal_cert_front_pic", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F02", info.getIdcard1Img(), mchApplyment.getIfCode()));
// 法人身份证国徽面照片
fileJson.put("legal_cert_back_pic", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F03", info.getIdcard2Img(), mchApplyment.getIfCode()));
// 营业执照
fileJson.put("license_pic", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F07", info.getLicenseImg(), mchApplyment.getIfCode()));
// 组织机构代码证
// fileJson.put("org_code_pic", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F57", info.getOrgCodePic()));
// 签约人身份证照片-人像照
fileJson.put("sign_identity_front_file_id", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F244", info.getIdcard1Img(), mchApplyment.getIfCode()));
// 签约人身份证照片-国徽照
fileJson.put("sign_identity_back_file_id", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F247", info.getIdcard2Img(), mchApplyment.getIfCode()));
}
// 扩展资料包
JSONArray extendedList = new JSONArray();
if (StringUtils.isNotEmpty(info.getServiceAgreement())) {
// 【商户】斗拱平台综合支付服务协议(纸质合同版)
JSONObject paramJson = JsonKit.newJson("file_id", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F479", info.getServiceAgreement(), mchApplyment.getIfCode()));
paramJson.put("file_type", "F479");
extendedList.add(paramJson);
}
if (StringUtils.isNotEmpty(info.getPriceConfirmation())) {
// 【商户】斗拱平台综合支付服务功能及价格确认表(纸质合同版)
JSONObject paramJson = JsonKit.newJson("file_id", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F480", info.getPriceConfirmation(), mchApplyment.getIfCode()));
paramJson.put("file_type", "F480");
extendedList.add(paramJson);
}
if (StringUtils.isNotEmpty(info.getStoreInnerImg())) {
// (线上场景)办公场所照片-工作区域
JSONObject paramJson = JsonKit.newJson("file_id", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F371", info.getStoreInnerImg(), mchApplyment.getIfCode()));
paramJson.put("file_type", "F371");
extendedList.add(paramJson);
}
if (StringUtils.isNotEmpty(info.getStoreOuterImg())) {
// (线上场景)办公场所照片-公司前台
JSONObject paramJson = JsonKit.newJson("file_id", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F478", info.getStoreOuterImg(), mchApplyment.getIfCode()));
paramJson.put("file_type", "F478");
extendedList.add(paramJson);
}
if (StringUtils.isNotEmpty(info.getStoreOuterImg())) {
// 门头照
JSONObject paramJson = JsonKit.newJson("file_id", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F22", info.getStoreOuterImg(), mchApplyment.getIfCode()));
paramJson.put("file_type", "F22");
extendedList.add(paramJson);
}
if (StringUtils.isNotEmpty(info.getStoreInnerImg())) {
// 内景照
JSONObject paramJson = JsonKit.newJson("file_id", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F24", info.getStoreInnerImg(), mchApplyment.getIfCode()));
paramJson.put("file_type", "F24");
extendedList.add(paramJson);
}
if (StringUtils.isNotEmpty(info.getStoreCashierImg())) {
// 收银台照
JSONObject paramJson = JsonKit.newJson("file_id", DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F105", info.getStoreCashierImg(), mchApplyment.getIfCode()));
paramJson.put("file_type", "F105");
extendedList.add(paramJson);
}
reqParams.put("extended_material_list", extendedList.toJSONString());
JSONObject agreementJson = new JSONObject();
// 如果配置了协议信息 走电子协议 未配置则纸质协议
if (StringUtils.isNotEmpty(isvParams.getAgreementModel())) {
// 协议类型
agreementJson.put("agreement_type", "0");
// 协议模板号
agreementJson.put("agreement_model", isvParams.getAgreementModel());
// 协议模板名称
agreementJson.put("agreement_name", isvParams.getAgreementName());
} else {
// 协议类型
agreementJson.put("agreement_type", "1");
}
reqParams.put("agreement_info", agreementJson.toJSONString());
// 业务开关配置
JSONObject bizConfJson = new JSONObject();
// 延迟入账开关
bizConfJson.put("delay_flag", "Y");
// 是否开通补贴支付
bizConfJson.put("combine_pay_flag", "Y");
// 是否开通余额支付
bizConfJson.put("balance_pay_flag", "Y");
// 是否开通结算
bizConfJson.put("settle_flag", "Y");
// // 商户开通强制延迟标记
// bizConfJson.put("forced_delay_flag", "Y");
// // 是否开通网银
// bizConfJson.put("online_flag", "Y");
// // 是否开通快捷
// bizConfJson.put("quick_flag", "Y");
// // 是否开通代扣
// bizConfJson.put("withhold_flag", "Y");
// // 是否开通微信预授权
// bizConfJson.put("wechatpay_pre_auth_flag", "Y");
// // 是否开通支付宝预授权
// bizConfJson.put("alipay_pre_auth_flag", "Y");
reqParams.put("biz_conf", bizConfJson);
// 支付宝配置对象
List<JSONObject> aliPayFeeList = info.convertAliPayFee(isvParams);
if (CollUtil.isNotEmpty(aliPayFeeList)) {
reqParams.put("ali_conf_list", JSON.toJSONString(aliPayFeeList));
}
WxpayOauth2Params wxpayOauth2Params = (WxpayOauth2Params) configContextQueryService.queryIsvOauth2Params(mchApplyment.getIsvNo(), CS.IF_CODE.WXPAY);
DBApplicationConfig dbApplicationConfig = sysConfigService.getDBApplicationConfig();
// 微信配置对象
List<JSONObject> wxPayFeeList = info.convertWxPayFee(isvParams, wxpayOauth2Params, dbApplicationConfig);
if (CollUtil.isNotEmpty(wxPayFeeList)) {
reqParams.put("wx_conf_list", JSON.toJSONString(wxPayFeeList));
}
if (bankAllow) {
// 银联配置对象
List<JSONObject> unionList = info.convertUniPayFee();
if (CollUtil.isNotEmpty(unionList)) {
reqParams.put("union_conf_list", unionList.toString());
}
// 银行卡业务配置
JSONObject bankPayFee = info.convertBankPayFee();
if (bankPayFee != null && bankPayFee.size() > 1) {
reqParams.put("bank_card_conf", bankPayFee.toJSONString());
}
// 银联小微参数配置
if (CollUtil.isNotEmpty(unionList)) {
reqParams.put("union_micro_info", info.getMicroInfo());
}
}
// 文件集
reqParams.put("file_info", fileJson);
// 实名认证信息
reqParams.put("wx_realname_info", realNameInfoJson);
return reqParams;
}
public void setAccountInfo(MchApplyment mchApplyment, JSONObject reqParams) {
DgpayApplymentInfo info = JSON.parseObject(mchApplyment.getApplyDetailInfo(), DgpayApplymentInfo.class);
if (info.getMerchantType() == 1) {
reqParams.put("name", info.getContactName());
// 银行卡绑定手机号
reqParams.put("mobile_no", info.getSettAccountBindPhone());
// 持卡人证件类型 默认为身份证
reqParams.put("cert_type", "00");
// 持卡人证件号码
reqParams.put("cert_no", info.getSettAccountIdcardNo());
// 持卡人证件有效期类型
reqParams.put("cert_validity_type", info.getCertValidityType(info.getSettAccountIdcardEffectEnd()));
// 持卡人证件有效期 起始
reqParams.put("cert_begin_date", info.getSettAccountIdcardEffectBegin().replace("-", ""));
// 持卡人证件有效期 截止
if (!"长期".equals(info.getSettAccountIdcardEffectEnd())) {
reqParams.put("cert_end_date", info.getSettAccountIdcardEffectEnd().replace("-", ""));
}
} else {
// 联系人姓名
reqParams.put("contact_name", info.getIdcardName());
// 联系人手机号
reqParams.put("contact_mobile", info.getIdCardPhone());
// 联系人邮箱
reqParams.put("contact_email", info.getIdCardEmail());
// 联系人身份证号
reqParams.put("contact_cert_no", info.getIdcardNo());
// 法人姓名
reqParams.put("legal_name", info.getIdcardName());
// 法人证件类型 默认身份证
reqParams.put("legal_cert_type", "00");
// 法人证件号码
reqParams.put("legal_cert_no", info.getIdcardNo());
// 法人证件有效期类型
reqParams.put("legal_cert_validity_type", info.getCertValidityType(info.getIdcardEffectEnd()));
// 法人证件有效期开始日期
reqParams.put("legal_cert_begin_date", info.getIdcardEffectBegin().replace("-", ""));
if ("0".equals(info.getCertValidityType(info.getIdcardEffectEnd()))) {
// 法人证件有效期截止日期
reqParams.put("legal_cert_end_date", info.getIdcardEffectEnd().replace("-", ""));
}
}
// 发送短信标识
reqParams.put("sms_send_flag", "Y");
reqParams.remove("area_id");
reqParams.remove("detail_addr");
reqParams.remove("prov_id");
reqParams.remove("district_id");
reqParams.remove("busi_type");
reqParams.remove("mcc");
reqParams.remove("receipt_name");
reqParams.remove("ent_type");
reqParams.remove("upper_huifu_id");
reqParams.put("noMchId", true);
}
/**
* 查询商户详细信息
**/
public void queryMchInfo(MchApplyment result, MchAppConfigContext mchAppConfigContext, String huifuId, String ifCode) {
String logPrefix = "【斗拱】查询商户详细信息";
try {
// 请求参数
JSONObject reqParams = new JSONObject();
reqParams.put("huifu_id", huifuId);
// 发送请求
log.info("{}请求开始", logPrefix);
JSONObject response = DgPayKit.request(mchAppConfigContext, reqParams, DgPayKit.MCH_INFO_QUERY_URL, ifCode);
log.info("{}请求结束", logPrefix);
JSONObject resData = response.getJSONObject("data");
String code = resData.getString("resp_code");
if ("00000000".equals(code)) {
JSONArray infoList = resData.getJSONArray("qry_cash_card_info_list");
if (CollUtil.isNotEmpty(infoList)) {
// 取第一个卡信息
JSONObject infoJson = (JSONObject) JSON.toJSON(infoList.get(0));
// 绑卡序列号
String tokenNo = infoJson.getString("token_no");
DgpayIsvsubMchParams mchParams = new DgpayIsvsubMchParams();
mchParams.setHuifuId(huifuId);
mchParams.setTokenNo(tokenNo);
result.setSuccResParameter(JSON.toJSONString(mchParams));
}
}
} catch (Exception e) {
log.error("{} 异常:", logPrefix, e);
}
}
}

View File

@@ -0,0 +1,67 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.interfaces.paychannel.IPayOrderCloseService;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.utils.DateKit;
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import org.springframework.stereotype.Service;
import java.util.Date;
/**
* 订单关闭
*
* @author xiaoyu
*
* @date 2023/2/28 16:30
*/
@Service
public class DgpayPayOrderCloseService implements IPayOrderCloseService {
@Override
public ChannelRetMsg close(PayOrder payOrder, MchAppConfigContext mchAppConfigContext){
try {
ConfigContextQueryService configContextQueryService = SpringBeansUtil.getBean(ConfigContextQueryService.class);
JSONObject reqParams = new JSONObject();
// 请求日期
reqParams.put("req_date", DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN));
// 请求流水号
reqParams.put("req_seq_id", DateKit.currentTimeMillis()+"");
// 交易流水号
reqParams.put("org_hf_seq_id", payOrder.getChannelOrderNo());
// 商户号
reqParams.put("huifu_id", payOrder.getChannelMchNo());
// 原机构请求日期
reqParams.put("org_req_date", DateUtil.format(payOrder.getCreatedAt(), DatePattern.PURE_DATE_PATTERN));
JSONObject response = DgPayKit.request(mchAppConfigContext, reqParams, "/trade/payment/scanpay/close", payOrder.getIfCode());
JSONObject resData = response.getJSONObject("data");
String code = resData.getString("resp_code");
//如果查询请求成功
if(DgPayKit.STATE_SUCCESS.equals(code)){
String transStat = resData.getString("trans_stat");
if (DgPayKit.STATE_SUCCESS_S.equals(transStat)) {
return ChannelRetMsg.confirmSuccess(null);
}else if (DgPayKit.STATE_FAIL_F.equals(transStat)) {
// 失败
return ChannelRetMsg.confirmFail(resData.getString("resp_desc"));
}
}else if(DgPayKit.STATE_ING.equals(code)) {
return ChannelRetMsg.confirmSuccess(null);
}
//支付中
return ChannelRetMsg.waiting();
}catch (Exception e) {
//支付中
return ChannelRetMsg.waiting();
}
}
}

View File

@@ -0,0 +1,74 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.interfaces.paychannel.IPayOrderQueryService;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
/**
* 斗拱 查询订单
*
* @author xiaoyu
*
* @date 2022/6/9 17:05
*/
@Service
@Slf4j
public class DgpayPayOrderQueryService implements IPayOrderQueryService {
@Override
public ChannelRetMsg query(PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception {
// 智能POS 直接返回
if (StringUtils.equals(payOrder.getWayCode(), CS.PAY_WAY_CODE.AUTO_POS)) {
return ChannelRetMsg.waiting();
}
try {
JSONObject reqParams = new JSONObject();
// 交易流水号
reqParams.put("mer_ord_id", payOrder.getPayOrderId());
// 商户号
reqParams.put("huifu_id", payOrder.getChannelMchNo());
// 原机构请求日期
reqParams.put("org_req_date", DateUtil.format(payOrder.getCreatedAt(), DatePattern.PURE_DATE_PATTERN));
JSONObject response = DgPayKit.request(mchAppConfigContext, reqParams, "/trade/payment/scanpay/query", payOrder.getIfCode());
JSONObject resData = response.getJSONObject("data");
String code = resData.getString("resp_code");
//如果查询请求成功
if(DgPayKit.STATE_SUCCESS.equals(code)){
String tradeState = resData.getString("trans_stat");
if (DgPayKit.STATE_SUCCESS_S.equals(tradeState)) {
String channelOrderNo = resData.getString("org_hf_seq_id");
// 渠道订单号
String outTransId = resData.getString("out_trans_id");
// 商户订单号
String partyOrderId = resData.getString("party_order_id");
String drType = CS.DrType.OTHER.getType();
String debit_type = resData.getString("debit_type");
if("D".equals(debit_type)){
drType = CS.DrType.DEBIT.getType();
}else if("C".equals(debit_type)){
drType = CS.DrType.CREDIT.getType();
}
return ChannelRetMsg.confirmSuccess(channelOrderNo, outTransId, partyOrderId,drType);
}else if (DgPayKit.STATE_FAIL_F.equals(tradeState)) {
// 失败
return ChannelRetMsg.confirmFail();
}
}
//支付中
return ChannelRetMsg.waiting();
}catch (Exception e) {
//支付中
return ChannelRetMsg.waiting();
}
}
}

View File

@@ -0,0 +1,42 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.rqrs.AbstractRS;
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
import com.jeequan.jeepay.thirdparty.channel.AbstractPaymentService;
import com.jeequan.jeepay.thirdparty.util.PaywayUtil;
import org.springframework.stereotype.Service;
/**
* * 支付接口: 斗拱
*
* @author xiaoyu
*
* @date 2022/5/13 15:02
*/
@Service
public class DgpayPaymentService extends AbstractPaymentService {
@Override
public String getIfCode() {
return CS.IF_CODE.DGPAY;
}
@Override
public boolean isSupport(String wayCode) {
return false;
}
@Override
public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) {
return PaywayUtil.getRealPaywayService(this, payOrder.getWayCode()).preCheck(rq, payOrder);
}
@Override
public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception {
return PaywayUtil.getRealPaywayService(this, payOrder.getWayCode()).pay(rq, payOrder, mchAppConfigContext);
}
}

View File

@@ -0,0 +1,169 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.entity.RefundOrder;
import com.jeequan.jeepay.core.exception.ResponseException;
import com.jeequan.jeepay.core.model.autopos.ChannelOrderAcceptParams;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.params.dgpay.DgpayIsvParams;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.utils.AmountUtil;
import com.jeequan.jeepay.thirdparty.channel.AbstractPosNoticeService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Triple;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
/**
* 富友智能POS回调
*
* @author zx
*
* @date 2021-06-07 07:15
*/
@Service
@Slf4j
public class DgpayPosNoticeService extends AbstractPosNoticeService {
@Override
public String getIfCode() {
return CS.IF_CODE.DGPAY;
}
@Override
public Triple<String, String, Object> parseParams(HttpServletRequest request) {
JSONObject params = getReqParamJSON();
String respCode = params.getString("resp_code");
if (!StringUtils.equals(DgPayKit.STATE_SUCCESS, respCode)) {
log.info("汇付斗拱智能POS【订单推送模式】回调处理失败resp_code={}", respCode);
throw ResponseException.buildText("ERROR");
}
// 交易数据
JSONObject respData = params.getJSONObject("resp_data");
String subRespCode = respData.getString("sub_resp_code");
if (!StringUtils.equals(DgPayKit.STATE_SUCCESS, subRespCode)) {
log.info("汇付斗拱智能POS【订单推送模式】回调处理失败sub_resp_code={}", subRespCode);
throw ResponseException.buildText("ERROR");
}
String payOrderId = respData.getString("mer_ord_id");
String transType = respData.getString("trans_type"); // TRANS_REFUND: 交易退款 SCAN_REFUND: 终端扫码退款
String tradeType = "";
if (StringUtils.isNotBlank(transType)) {
tradeType = ChannelOrderAcceptParams.TRADE_TYPE_PAYMENT;
if (StringUtils.equalsAny(transType, "TRANS_REFUND", "SCAN_REFUND")) {
tradeType = ChannelOrderAcceptParams.TRADE_TYPE_REFUND;
}
}
log.info("斗拱智能POS【订单推送模式】通知参数tradeType={}params={}", tradeType, params.toJSONString());
return Triple.of(tradeType, payOrderId, params);
}
@Override
public ChannelRetMsg payNotice(HttpServletRequest request, Object params, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) {
String logPrefix = "处理斗拱智能POS【订单推送模式】支付回调";
JSONObject jsonParams = (JSONObject) params;
// 验签
validate(jsonParams, mchAppConfigContext, payOrder.getIfCode(), logPrefix);
// 获取请求参数
JSONObject respData = jsonParams.getJSONObject("resp_data");
String transAmt = AmountUtil.convertDollar2Cent(respData.getString("trans_amt"));
if (!String.valueOf(payOrder.getAmount()).equals(transAmt)) {
log.error("{} 金额不符trans_amt={}amount={}", logPrefix, transAmt, payOrder.getAmount());
throw ResponseException.buildText("ERROR");
}
// 判断上游订单状态
ResponseEntity okResponse = textResp("RECV_ORD_ID_" + payOrder.getPayOrderId());
ChannelRetMsg result = new ChannelRetMsg();
// 默认支付中
result.setChannelState(ChannelRetMsg.ChannelState.WAITING);
String transStat = respData.getString("trans_stat"); // 支付状态 I初始、P处理中、S成功、F失败
if(DgPayKit.STATE_SUCCESS_S.equals(transStat)){
result.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
}else if(DgPayKit.STATE_FAIL_F.equals(transStat)){
result.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
}
result.setResponseEntity(okResponse);
result.setChannelOrderId(respData.getString("req_seq_id"));
return result;
}
@Override
public ChannelRetMsg refundNotice(HttpServletRequest request, Object params, RefundOrder refundOrder, MchAppConfigContext mchAppConfigContext) {
String logPrefix = "处理斗拱智能POS【订单推送模式】退款通知";
JSONObject jsonParams = (JSONObject) params;
// 验签
validate(jsonParams, mchAppConfigContext, refundOrder.getIfCode(), logPrefix);
// 获取请求参数
JSONObject respData = jsonParams.getJSONObject("resp_data");
String transAmt = AmountUtil.convertDollar2Cent(respData.getString("ord_amt"));
if (!String.valueOf(refundOrder.getRefundAmount()).equals(transAmt)) {
log.error("{} 金额不符ord_amt={}amount={}", logPrefix, transAmt, refundOrder.getRefundAmount());
throw ResponseException.buildText("ERROR");
}
// 判断上游订单状态
ResponseEntity okResponse = textResp("RECV_ORD_ID_" + refundOrder.getRefundOrderId());
ChannelRetMsg result = new ChannelRetMsg();
// 默认支付中
result.setChannelState(ChannelRetMsg.ChannelState.WAITING);
String transStat = respData.getString("trans_stat"); // 支付状态 I初始、P处理中、S成功、F失败
if(DgPayKit.STATE_SUCCESS_S.equals(transStat)){
result.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
}else if(DgPayKit.STATE_FAIL_F.equals(transStat)){
result.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
}
result.setResponseEntity(okResponse);
result.setChannelOrderId(respData.getString("hf_seq_id"));
return result;
}
/**
* 验签
* @return
*/
private void validate(JSONObject jsonParams, MchAppConfigContext mchAppConfigContext, String ifCode, String logPrefix) {
String sign = jsonParams.getString("sign");
// 公钥参数
DgpayIsvParams isvParams = (DgpayIsvParams) configContextQueryService.queryIsvParams(mchAppConfigContext.getMchApplyment().getIsvNo(), ifCode);
// 验证签名
boolean verifyResult = DgPayKit.verify(jsonParams.getString("resp_data"), isvParams.getPosPublicKey(), sign);
// 验签失败
if(!verifyResult){
log.error("{} 验签失败", logPrefix);
throw ResponseException.buildText("SIGN ERROR");
}
}
}

View File

@@ -0,0 +1,239 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.entity.RefundOrder;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.exception.ChannelException;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.params.dgpay.DgpayIsvsubMchParams;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRefundLimit;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.model.rqrs.refund.RefundOrderRQ;
import com.jeequan.jeepay.core.utils.AmountUtil;
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
import com.jeequan.jeepay.thirdparty.channel.AbstractRefundService;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
/**
* 退款接口: 斗拱
*
* @author xiaoyu
*
* @date 2022/6/9 17:43
*/
@Slf4j
@Service
public class DgpayRefundService extends AbstractRefundService {
@Override
public String getIfCode() {
return CS.IF_CODE.DGPAY;
}
@Override
public String preCheck(RefundOrderRQ bizRQ, RefundOrder refundOrder, PayOrder payOrder) {
return null;
}
@Override
public ChannelRetMsg refund(RefundOrderRQ bizRQ, RefundOrder refundOrder, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception {
// 智能pos收款走独立退款接口
if (StringUtils.equals(payOrder.getWayCode(), CS.PAY_WAY_CODE.AUTO_POS)) {
return autoPosRefund(bizRQ, refundOrder, payOrder, mchAppConfigContext);
}
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
JSONObject reqParams = new JSONObject();
// 交易流水号
reqParams.put("req_seq_id", refundOrder.getRefundOrderId());
// 原交易请求订单号
reqParams.put("org_req_seq_id", payOrder.getPayOrderId());
// 商户号
reqParams.put("huifu_id", payOrder.getChannelMchNo())
;
reqParams.put("notify_url", getNotifyUrl());
// 请求日期
reqParams.put("req_date", DateUtil.format(refundOrder.getCreatedAt(), DatePattern.PURE_DATE_PATTERN));
// 退款金额
reqParams.put("ord_amt", AmountUtil.convertCent2Dollar(refundOrder.getRefundAmount()));
// 原交易请求日期
reqParams.put("org_req_date", DateUtil.format(payOrder.getCreatedAt(), DatePattern.PURE_DATE_PATTERN));
JSONObject response = DgPayKit.request(mchAppConfigContext, reqParams, "/trade/payment/scanpay/refund", refundOrder.getIfCode());
JSONObject resData = response.getJSONObject("data");
String code = resData.getString("resp_code");
// 调用成功
if(DgPayKit.STATE_SUCCESS.equals(code)){
String tradeState = resData.getString("trans_stat");
if (DgPayKit.STATE_SUCCESS_S.equals(tradeState)) {
String channelOrderNo = resData.getString("hf_seq_id");
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
return ChannelRetMsg.confirmSuccess(channelOrderNo);
}else if (DgPayKit.STATE_FAIL_F.equals(tradeState)) {
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
// 状态失败
DgPayKit.channelMsgError(resData, channelRetMsg);
}
}else if (DgPayKit.STATE_ING.equals(code)){
return channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
}else{
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
// 状态失败
DgPayKit.channelMsgError(resData, channelRetMsg);
}
return channelRetMsg;
}
private ChannelRetMsg autoPosRefund(RefundOrderRQ bizRQ, RefundOrder refundOrder, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) {
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
// 智能POS bizParams参数
JSONObject bizParams = JSONObject.parseObject(payOrder.getChannelBizData());
if (bizParams == null || StringUtils.isBlank(bizParams.getString("deviceNo"))) {
throw new BizException("该订单不支持接口退款请在POS机完成退款");
}
try {
// 请求数据data
JSONObject dataParams = new JSONObject();
dataParams.put("device_id", bizParams.getString("deviceNo"));
// 退款信息
JSONObject jsonData = new JSONObject();
jsonData.put("interfaceType", "refund");
jsonData.put("channelId", DgPayKit.covertChannelId(bizParams.getString("channelType"))); // 支付渠道
jsonData.put("ordAmt", refundOrder.getRefundAmount().toString());
jsonData.put("oriOutOrderId", payOrder.getPayOrderId()); // 原三方订单号
jsonData.put("outOrdId", refundOrder.getRefundOrderId()); // 外部订单号
jsonData.put("bgRetUrl", "virgo://" + getPosNotifyUrl());
dataParams.put("json_data", jsonData.toJSONString());
JSONObject response = DgPayKit.request(mchAppConfigContext, dataParams, "/trade/cloudmis/device/information", refundOrder.getIfCode());
JSONObject resData = response.getJSONObject("data");
String code = resData.getString("resp_code");
if(!DgPayKit.STATE_SUCCESS.equals(code)){
log.error("斗拱智能POS退款失败错误码{},错误信息:{}", code, resData.getString("resp_desc"));
}
channelRetMsg.setChannelOrderId(resData.getString("termOrdId"));
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
return channelRetMsg;
}catch (BizException e) {
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
channelRetMsg.setChannelErrCode(e.getApiRes().getCode()+"");
channelRetMsg.setChannelErrMsg(e.getApiRes().getMsg());
}catch (Exception e) {
throw ChannelException.sysError(e.getMessage());
}
return channelRetMsg;
}
@Override
public ChannelRetMsg query(RefundOrder refundOrder, MchAppConfigContext mchAppConfigContext) throws Exception {
if (StringUtils.equals(refundOrder.getWayCode(), CS.PAY_WAY_CODE.AUTO_POS)) {
return ChannelRetMsg.waiting();
}
try {
ConfigContextQueryService configContextQueryService = SpringBeansUtil.getBean(ConfigContextQueryService.class);
DgpayIsvsubMchParams mchParams = (DgpayIsvsubMchParams)configContextQueryService.queryIsvsubMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getMchApplyment().getAutoConfigMchAppId(), refundOrder.getIfCode());
JSONObject reqParams = new JSONObject();
// 交易流水号
reqParams.put("org_req_seq_id", refundOrder.getRefundOrderId());
// 商户号
reqParams.put("huifu_id", mchParams.getHuifuId());
// 原机构请求日期
reqParams.put("org_req_date", DateUtil.format(refundOrder.getCreatedAt(), DatePattern.PURE_DATE_PATTERN));
JSONObject response = DgPayKit.request(mchAppConfigContext, reqParams, "/trade/payment/scanpay/refundquery", refundOrder.getIfCode());
JSONObject resData = response.getJSONObject("data");
String code = resData.getString("resp_code");
//如果查询请求成功
if(DgPayKit.STATE_SUCCESS.equals(code)){
String tradeState = resData.getString("trans_stat");
if (DgPayKit.STATE_SUCCESS_S.equals(tradeState)) {
return ChannelRetMsg.confirmSuccess(null);
}else if (DgPayKit.STATE_FAIL_F.equals(tradeState)) {
// 失败
return ChannelRetMsg.confirmFail();
}
}
//支付中
return ChannelRetMsg.waiting();
}catch (Exception e) {
//支付中
return ChannelRetMsg.waiting();
}
}
private ChannelRetMsg autoPosQuery(RefundOrder refundOrder, MchAppConfigContext mchAppConfigContext) {
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
// 智能POS bizParams参数
JSONObject bizParams = JSONObject.parseObject(refundOrder.getChannelBizData());
if (bizParams == null) {
throw new BizException("不支持退款补单");
}
try {
// 请求数据data
JSONObject dataParams = new JSONObject();
dataParams.put("device_id", bizParams.getString("deviceNo"));
// 退款信息
JSONObject jsonData = new JSONObject();
jsonData.put("interfaceType", "queryOrder");
jsonData.put("tradeNo", refundOrder.getChannelPayOrderNo()); // 外部订单号
jsonData.put("outOrdId", refundOrder.getPayOrderId()); // 外部订单号
jsonData.put("isSale", "0");
jsonData.put("channelId", DgPayKit.covertChannelId(bizParams.getString("channelType")));
dataParams.put("json_data ", jsonData.toJSONString());
JSONObject response = DgPayKit.request(mchAppConfigContext, dataParams, "/trade/cloudmis/device/information", refundOrder.getIfCode());
JSONObject resData = response.getJSONObject("data");
String code = resData.getString("resp_code");
if(!DgPayKit.STATE_SUCCESS.equals(code)){
log.error("斗拱智能POS退款查单失败错误码{},错误信息:{}", code, resData.getString("resp_desc"));
}
return channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
}catch (BizException e) {
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
channelRetMsg.setChannelErrCode(e.getApiRes().getCode()+"");
channelRetMsg.setChannelErrMsg(e.getApiRes().getMsg());
}catch (Exception e) {
throw ChannelException.sysError(e.getMessage());
}
return channelRetMsg;
}
/**
* 退款权限
* @param settleType
* @param applyId
* @return
*/
@Override
public ChannelRefundLimit isRefundLimit(String settleType,String applyId) {
return super.isRefundLimit(settleType,applyId);
}
@Override
public void checkPlatAccount(RefundOrder refundOrder) {
}
}

View File

@@ -0,0 +1,105 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay.payway;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.exception.ChannelException;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.rqrs.AbstractRS;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.AliBarOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.AliBarOrderRS;
import com.jeequan.jeepay.thirdparty.channel.dgpay.DgPayKit;
import com.jeequan.jeepay.thirdparty.channel.dgpay.DgpayPaymentService;
import com.jeequan.jeepay.thirdparty.util.ApiResBuilder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
/*
* 斗拱 支付宝 条码支付
*
* @author pangxiaoyu
*
* @date 2021/6/8 18:11
*/
@Slf4j
@Service("dgpayPaymentByAliBarService") //Service Name需保持全局唯一性
public class AliBar extends DgpayPaymentService {
@Override
public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) {
AliBarOrderRQ bizRQ = (AliBarOrderRQ) rq;
if(StringUtils.isEmpty(bizRQ.getAuthCode())){
throw new BizException("用户支付条码[authCode]不可为空");
}
return null;
}
@Override
public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception {
AliBarOrderRQ bizRQ = (AliBarOrderRQ) rq;
AliBarOrderRS res = ApiResBuilder.buildSuccess(AliBarOrderRS.class);
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
res.setChannelRetMsg(channelRetMsg);
try {
JSONObject dataParams = DgPayKit.dataParams(payOrder, getNotifyUrl());
dataParams.put("auth_code", bizRQ.getAuthCode());
// 设置订单是否需要分账
if(isDivisionOrder(payOrder)) {
dataParams.put("delay_acct_flag", "Y");
}
JSONObject response = DgPayKit.payRequest(mchAppConfigContext, dataParams, "/trade/payment/micropay", payOrder.getIfCode());
JSONObject resData = response.getJSONObject("data");
channelRetMsg.setChannelBizData(resData);
String code = resData.getString("resp_code");
if(DgPayKit.STATE_SUCCESS.equals(code)){
String transStat = resData.getString("trans_stat");
String channelOrderNo = resData.get("hf_seq_id").toString();
String debit_type = resData.getString("debit_type");
if(StringUtils.isEmpty(debit_type) || "0".equals(debit_type)){
channelRetMsg.setDrType(CS.DrType.OTHER.getType());
}else if("D".equals(debit_type)){
channelRetMsg.setDrType(CS.DrType.DEBIT.getType());
}else if("C".equals(debit_type)){
channelRetMsg.setDrType(CS.DrType.CREDIT.getType());
}else{
channelRetMsg.setDrType(CS.DrType.OTHER.getType());
}
channelRetMsg.setChannelOrderId(channelOrderNo);
if (DgPayKit.STATE_SUCCESS_S.equals(transStat)) {
// 交易状态成功
channelRetMsg.setPlatformOrderNo(resData.get("out_trans_id").toString());
channelRetMsg.setPlatformMchOrderNo(resData.get("party_order_id").toString());
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
res.setPayData(resData.toString());
}else if (DgPayKit.STATE_FAIL_F.equals(transStat)) {
// 交易状态失败
DgPayKit.channelMsgError(resData, channelRetMsg);
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
}else {
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
// 开启轮询查单
channelRetMsg.setNeedQuery(true);
}
} else if (DgPayKit.STATE_ING.equals(code)) {
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
// 开启轮询查单
channelRetMsg.setNeedQuery(true);
} else{
DgPayKit.channelMsgError(resData, channelRetMsg);
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
}
}catch (Exception e) {
log.error(e.getMessage());
throw ChannelException.sysError(e.getMessage());
}
return res;
}
}

View File

@@ -0,0 +1,88 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay.payway;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.exception.ChannelException;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.rqrs.AbstractRS;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelJsapiMsg;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.AliJsapiOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.AliJsapiOrderRS;
import com.jeequan.jeepay.thirdparty.channel.dgpay.DgPayKit;
import com.jeequan.jeepay.thirdparty.channel.dgpay.DgpayPaymentService;
import com.jeequan.jeepay.thirdparty.util.ApiResBuilder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
/**
* 斗拱 支付宝 jsapi
*
* @author xiaoyu
*
* @date 2022/6/13 17:20
*/
@Slf4j
@Service("dgpayPaymentByAliJsapiService") //Service Name需保持全局唯一性
public class AliJsapi extends DgpayPaymentService {
@Override
public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) {
AliJsapiOrderRQ bizRQ = (AliJsapiOrderRQ) rq;
if(StringUtils.isEmpty(bizRQ.getBuyerUserId())){
throw new BizException("[buyerUserId]不可为空");
}
return null;
}
@Override
public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext){
AliJsapiOrderRS res = ApiResBuilder.buildSuccess(AliJsapiOrderRS.class);
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
res.setChannelRetMsg(channelRetMsg);
try {
AliJsapiOrderRQ bizRQ = (AliJsapiOrderRQ) rq;
JSONObject dataParams = DgPayKit.dataParams(payOrder, getNotifyUrl());
dataParams.put("trade_type", "A_JSAPI");
JSONObject aliJson = new JSONObject();
aliJson.put("buyer_id", bizRQ.getBuyerUserId());
dataParams.put("alipay_data", aliJson.toJSONString());
// 设置订单是否需要分账
if(isDivisionOrder(payOrder)) {
dataParams.put("delay_acct_flag", "Y");
}
JSONObject response = DgPayKit.payRequest(mchAppConfigContext, dataParams, "/trade/payment/jspay", payOrder.getIfCode());
JSONObject resData = response.getJSONObject("data");
channelRetMsg.setChannelBizData(resData);
String code = resData.getString("resp_code");
if(DgPayKit.STATE_ING.equals(code)){
String channelOrderNo = resData.get("hf_seq_id").toString();
//付款信息
JSONObject aliResJson = resData.getJSONObject("pay_info");
channelRetMsg.setChannelOrderId(channelOrderNo);
// 获取payInfo
String tradeNo = aliResJson.getString("tradeNO");
res.setAlipayTradeNo(tradeNo);
res.setPayData(JSON.toJSONString(new ChannelJsapiMsg(tradeNo)));
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
}else { //其他状态, 表示下单失败
DgPayKit.channelMsgError(resData, channelRetMsg);
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
}
}catch (Exception e) {
log.error(e.getMessage());
throw ChannelException.sysError(e.getMessage());
}
return res;
}
}

View File

@@ -0,0 +1,92 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay.payway;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.exception.ChannelException;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.rqrs.AbstractRS;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelJsapiMsg;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.AliJsapiOrderRS;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.AliLiteOrderRQ;
import com.jeequan.jeepay.thirdparty.channel.dgpay.DgPayKit;
import com.jeequan.jeepay.thirdparty.channel.dgpay.DgpayPaymentService;
import com.jeequan.jeepay.thirdparty.util.ApiResBuilder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
/**
* 支付宝小程序
*
* @author xiaoyu
*
* @date 2023/1/6 16:53
*/
@Slf4j
@Service("dgpayPaymentByAliLiteService") //Service Name需保持全局唯一性
public class AliLite extends DgpayPaymentService {
@Override
public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) {
AliLiteOrderRQ bizRQ = (AliLiteOrderRQ) rq;
if (StringUtils.isEmpty(bizRQ.getBuyerUserId())) {
throw new BizException("[buyerUserId]不可为空");
}
return null;
}
@Override
public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception {
AliLiteOrderRQ bizRQ = (AliLiteOrderRQ) rq;
// 构造函数响应数据
AliJsapiOrderRS res = ApiResBuilder.buildSuccess(AliJsapiOrderRS.class);
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
res.setChannelRetMsg(channelRetMsg);
try {
JSONObject dataParams = DgPayKit.dataParams(payOrder, getNotifyUrl());
dataParams.put("trade_type", "A_JSAPI");
JSONObject aliJson = new JSONObject();
aliJson.put("buyer_id", bizRQ.getBuyerUserId());
dataParams.put("alipay_data", aliJson.toJSONString());
// 设置订单是否需要分账
if(isDivisionOrder(payOrder)) {
dataParams.put("delay_acct_flag", "Y");
}
JSONObject response = DgPayKit.payRequest(mchAppConfigContext, dataParams, "/trade/payment/jspay", payOrder.getIfCode());
JSONObject resData = response.getJSONObject("data");
String code = resData.getString("resp_code");
channelRetMsg.setChannelBizData(resData);
if(DgPayKit.STATE_ING.equals(code)){
String channelOrderNo = resData.get("hf_seq_id").toString();
//付款信息
JSONObject aliResJson = resData.getJSONObject("pay_info");
String tradeNo = aliResJson.getString("tradeNO");
res.setAlipayTradeNo(tradeNo);
res.setPayData(JSON.toJSONString(new ChannelJsapiMsg(tradeNo)));
// 支付中
channelRetMsg.setChannelOrderId(channelOrderNo);
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
// 开启轮询查单
channelRetMsg.setNeedQuery(true);
}else {
DgPayKit.channelMsgError(resData, channelRetMsg);
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
}
}catch (Exception e) {
log.error(e.getMessage());
throw ChannelException.sysError(e.getMessage());
}
return res;
}
}

View File

@@ -0,0 +1,77 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay.payway;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.rqrs.AbstractRS;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelJsapiMsg;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.AliQrOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.AliQrOrderRS;
import com.jeequan.jeepay.thirdparty.channel.dgpay.DgPayKit;
import com.jeequan.jeepay.thirdparty.channel.dgpay.DgpayPaymentService;
import com.jeequan.jeepay.thirdparty.util.ApiResBuilder;
import org.springframework.stereotype.Service;
/**
* 斗拱 支付宝扫码
*
* @author xiaoyu
*
* @date 2022/5/24 17:38
*/
@Service("dgpayPaymentByAliQrService") //Service Name需保持全局唯一性
public class AliQr extends DgpayPaymentService {
@Override
public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) {
return null;
}
@Override
public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext){
AliQrOrderRQ aliQrOrderRQ = (AliQrOrderRQ)rq;
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
// 构造函数响应数据
AliQrOrderRS res = ApiResBuilder.buildSuccess(AliQrOrderRS.class);
res.setChannelRetMsg(channelRetMsg);
JSONObject dataParams = DgPayKit.dataParams(payOrder, getNotifyUrl());
dataParams.put("trade_type", "A_NATIVE");
// 设置订单是否需要分账
if(isDivisionOrder(payOrder)) {
dataParams.put("delay_acct_flag", "Y");
}
JSONObject response = DgPayKit.payRequest(mchAppConfigContext, dataParams, "/trade/payment/jspay", payOrder.getIfCode());
JSONObject resData = response.getJSONObject("data");
String code = resData.getString("resp_code");
channelRetMsg.setChannelBizData(resData);
if (DgPayKit.STATE_ING.equals(code)) {
String channelOrderNo = resData.get("hf_seq_id").toString();
//付款信息
String qrCdLink = resData.getString("qr_code");
channelRetMsg.setChannelOrderId(channelOrderNo);
ChannelJsapiMsg jsapiMsg = new ChannelJsapiMsg();
if(CS.PAY_DATA_TYPE.CODE_IMG_URL.equals(aliQrOrderRQ.getPayDataType())){ //二维码地址
res.setCodeImgUrl(sysConfigService.getDBApplicationConfig().genScanImgUrl(qrCdLink));
jsapiMsg.setQrCodeUrl(sysConfigService.getDBApplicationConfig().genScanImgUrl(qrCdLink));
}else{ //默认都为跳转地址方式
jsapiMsg.setQrCodeUrl(qrCdLink);
res.setCodeUrl(qrCdLink);
}
res.setPayData(JSON.toJSONString(jsapiMsg));
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
}else {
DgPayKit.channelMsgError(resData, channelRetMsg);
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
}
return res;
}
}

View File

@@ -0,0 +1,94 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay.payway;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.exception.ChannelException;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.rqrs.AbstractRS;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.AutoPosOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.AutoPosOrderRS;
import com.jeequan.jeepay.thirdparty.channel.dgpay.DgPayKit;
import com.jeequan.jeepay.thirdparty.channel.dgpay.DgpayPaymentService;
import com.jeequan.jeepay.thirdparty.util.ApiResBuilder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
/**
* 斗拱 智能POS
*
* @author zx
*
* @date 2022/8/2 18:11
*/
@Slf4j
@Service("dgpayPaymentByAutoPosService") //Service Name需保持全局唯一性
public class AutoPos extends DgpayPaymentService {
@Override
public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) {
if(StringUtils.isEmpty(payOrder.getDeviceNo())){
throw new BizException("智能POS[deviceNo]不可为空");
}
return null;
}
@Override
public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception {
String logPrefix = "【斗拱 智能POS支付】";
AutoPosOrderRS res = ApiResBuilder.buildSuccess(AutoPosOrderRS.class);
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
res.setChannelRetMsg(channelRetMsg);
AutoPosOrderRQ bizRQ = (AutoPosOrderRQ) rq;
try {
// 请求数据data
JSONObject dataParams = new JSONObject();
dataParams.put("device_id", payOrder.getDeviceNo());
// 交易信息
JSONObject tradeJSON = new JSONObject();
tradeJSON.put("interfaceType", "payment");
tradeJSON.put("channelId", DgPayKit.covertChannelId(bizRQ.getChannelType()));
tradeJSON.put("mobilePayType", DgPayKit.covertMobilePayType(bizRQ.getPayType()));
tradeJSON.put("ordAmt", payOrder.getFindAmt().toString());
tradeJSON.put("outOrdId", payOrder.getPayOrderId());
tradeJSON.put("goodsDesc", payOrder.getBody());
tradeJSON.put("bgRetUrl", "virgo://" + getPosNotifyUrl());
tradeJSON.put("ordRemark", payOrder.getSellerRemark());
// 设置订单是否需要分账
if(isDivisionOrder(payOrder)) {
tradeJSON.put("isDelayAcct", isDivisionOrder(payOrder) ? "1" : "0");
}
dataParams.put("json_data", tradeJSON.toJSONString());
JSONObject response = DgPayKit.request(mchAppConfigContext, dataParams, "/trade/cloudmis/device/information", payOrder.getIfCode());
JSONObject resData = response.getJSONObject("data");
channelRetMsg.setChannelBizData(resData);
String code = resData.getString("resp_code");
if(!DgPayKit.STATE_SUCCESS.equals(code)){
log.error("{}推送订单失败,错误码:{},错误信息:{}", logPrefix, code, resData.getString("resp_desc"));
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
channelRetMsg.setChannelErrCode(resData.getString("result_code"));
channelRetMsg.setChannelErrMsg(resData.getString("result_msg"));
return res;
}
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING); // 调用成功,改为支付中
}catch (Exception e) {
log.error(e.getMessage());
throw ChannelException.sysError(e.getMessage());
}
return res;
}
}

View File

@@ -0,0 +1,105 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay.payway;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.rqrs.AbstractRS;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.DcepBarOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.DcepBarOrderRS;
import com.jeequan.jeepay.thirdparty.channel.dgpay.DgPayKit;
import com.jeequan.jeepay.thirdparty.channel.dgpay.DgpayPaymentService;
import com.jeequan.jeepay.thirdparty.util.ApiResBuilder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
/**
* 斗拱 微信 条码支付
*
* @author xiaoyu
*
* @date 2022/6/13 18:03
*/
@Slf4j
@Service("dgpayPaymentByDcepBarService") //Service Name需保持全局唯一性
public class DcepBar extends DgpayPaymentService {
@Override
public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) {
DcepBarOrderRQ bizRQ = (DcepBarOrderRQ) rq;
if(StringUtils.isEmpty(bizRQ.getAuthCode())){
throw new BizException("用户支付条码[authCode]不可为空");
}
return null;
}
@Override
public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception {
DcepBarOrderRQ bizRQ = (DcepBarOrderRQ) rq;
DcepBarOrderRS res = ApiResBuilder.buildSuccess(DcepBarOrderRS.class);
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
res.setChannelRetMsg(channelRetMsg);
JSONObject dataParams = DgPayKit.dataParams(payOrder, getNotifyUrl());
dataParams.put("auth_code", bizRQ.getAuthCode());
// 设置订单是否需要分账
if(isDivisionOrder(payOrder)) {
dataParams.put("delay_acct_flag", "Y");
}
JSONObject response = DgPayKit.payRequest(mchAppConfigContext, dataParams, "/trade/payment/micropay", payOrder.getIfCode());
JSONObject resData = response.getJSONObject("data");
channelRetMsg.setChannelBizData(resData);
String code = resData.getString("resp_code");
if(DgPayKit.STATE_SUCCESS.equals(code)){
String transStat = resData.getString("trans_stat");
String channelOrderNo = resData.get("hf_seq_id").toString();
channelRetMsg.setChannelOrderId(channelOrderNo);
if (DgPayKit.STATE_SUCCESS_S.equals(transStat)) {
// 交易状态成功
channelRetMsg.setPlatformOrderNo(resData.get("out_trans_id").toString());
channelRetMsg.setPlatformMchOrderNo(resData.get("party_order_id").toString());
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
res.setPayData(resData.toString());
String debit_type = resData.getString("debit_type");
if(StringUtils.isEmpty(debit_type) || "0".equals(debit_type)){
channelRetMsg.setDrType(CS.DrType.OTHER.getType());
}else if("D".equals(debit_type)){
channelRetMsg.setDrType(CS.DrType.DEBIT.getType());
}else if("C".equals(debit_type)){
channelRetMsg.setDrType(CS.DrType.CREDIT.getType());
}else{
channelRetMsg.setDrType(CS.DrType.OTHER.getType());
}
}else if (DgPayKit.STATE_FAIL_F.equals(transStat)) {
// 交易状态失败
DgPayKit.channelMsgError(resData, channelRetMsg);
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
}else {
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
// 开启轮询查单
channelRetMsg.setNeedQuery(true);
}
} else if (DgPayKit.STATE_ING.equals(code)){
String channelOrderNo = resData.get("hf_seq_id").toString();
channelRetMsg.setChannelOrderId(channelOrderNo);
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
// 开启轮询查单
channelRetMsg.setNeedQuery(true);
}else{ //其他状态, 表示下单失败
DgPayKit.channelMsgError(resData, channelRetMsg);
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
}
return res;
}
}

View File

@@ -0,0 +1,77 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay.payway;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.rqrs.AbstractRS;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelJsapiMsg;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.DcepQrOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.DcepQrOrderRS;
import com.jeequan.jeepay.thirdparty.channel.dgpay.DgPayKit;
import com.jeequan.jeepay.thirdparty.channel.dgpay.DgpayPaymentService;
import com.jeequan.jeepay.thirdparty.util.ApiResBuilder;
import org.springframework.stereotype.Service;
/**
* 斗拱 支付宝扫码
*
* @author xiaoyu
*
* @date 2022/5/24 17:38
*/
@Service("dgpayPaymentByDcepQrService") //Service Name需保持全局唯一性
public class DcepQr extends DgpayPaymentService {
@Override
public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) {
return null;
}
@Override
public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext){
DcepQrOrderRQ aliQrOrderRQ = (DcepQrOrderRQ)rq;
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
// 构造函数响应数据
DcepQrOrderRS res = ApiResBuilder.buildSuccess(DcepQrOrderRS.class);
res.setChannelRetMsg(channelRetMsg);
JSONObject dataParams = DgPayKit.dataParams(payOrder, getNotifyUrl());
dataParams.put("trade_type", "D_NATIVE");
// 设置订单是否需要分账
if(isDivisionOrder(payOrder)) {
dataParams.put("delay_acct_flag", "Y");
}
JSONObject response = DgPayKit.payRequest(mchAppConfigContext, dataParams, "/trade/payment/jspay", payOrder.getIfCode());
JSONObject resData = response.getJSONObject("data");
String code = resData.getString("resp_code");
channelRetMsg.setChannelBizData(resData);
if (DgPayKit.STATE_ING.equals(code)) {
String channelOrderNo = resData.get("hf_seq_id").toString();
//付款信息
String qrCdLink = resData.getString("qr_code");
channelRetMsg.setChannelOrderId(channelOrderNo);
ChannelJsapiMsg jsapiMsg = new ChannelJsapiMsg();
if(CS.PAY_DATA_TYPE.CODE_IMG_URL.equals(aliQrOrderRQ.getPayDataType())){ //二维码地址
jsapiMsg.setQrCodeUrl(sysConfigService.getDBApplicationConfig().genScanImgUrl(qrCdLink));
res.setCodeImgUrl(sysConfigService.getDBApplicationConfig().genScanImgUrl(qrCdLink));
}else{ //默认都为跳转地址方式
jsapiMsg.setQrCodeUrl(qrCdLink);
res.setCodeUrl(qrCdLink);
}
res.setPayData(JSON.toJSONString(jsapiMsg));
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
}else {
DgPayKit.channelMsgError(resData, channelRetMsg);
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
}
return res;
}
}

View File

@@ -0,0 +1,89 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay.payway;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.exception.ChannelException;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.rqrs.AbstractRS;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelJsapiMsg;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.UpQrOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.UpQrOrderRS;
import com.jeequan.jeepay.thirdparty.channel.dgpay.DgPayKit;
import com.jeequan.jeepay.thirdparty.channel.dgpay.DgpayPaymentService;
import com.jeequan.jeepay.thirdparty.util.ApiResBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 银联扫码支付
*
* @author zx
*
* @date 2022/06/22 20:09
*/
@Service("dgpayPaymentByUpQrService") //Service Name需保持全局唯一性
@Slf4j
public class UpQr extends DgpayPaymentService {
@Override
public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) {
return null;
}
@Override
public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext){
UpQrOrderRQ bizRQ = (UpQrOrderRQ)rq;
// 构造函数响应数据
UpQrOrderRS res = ApiResBuilder.buildSuccess(UpQrOrderRS.class);
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
res.setChannelRetMsg(channelRetMsg);
try {
JSONObject dataParams = DgPayKit.dataParams(payOrder, getNotifyUrl());
dataParams.put("trade_type", "U_NATIVE");
// 设置订单是否需要分账
if(isDivisionOrder(payOrder)) {
dataParams.put("delay_acct_flag", "Y");
}
JSONObject response = DgPayKit.payRequest(mchAppConfigContext, dataParams, "/trade/payment/jspay", payOrder.getIfCode());
JSONObject resData = response.getJSONObject("data");
String code = resData.getString("resp_code");
channelRetMsg.setChannelBizData(resData);
if (DgPayKit.STATE_ING.equals(code)) {
String channelOrderNo = resData.get("hf_seq_id").toString();
//付款信息
String qrCdLink = resData.getString("qr_code");
channelRetMsg.setChannelOrderId(channelOrderNo);
ChannelJsapiMsg jsapiMsg = new ChannelJsapiMsg();
if(CS.PAY_DATA_TYPE.CODE_IMG_URL.equals(bizRQ.getPayDataType())){ //二维码地址
res.setCodeImgUrl(sysConfigService.getDBApplicationConfig().genScanImgUrl(qrCdLink));
jsapiMsg.setQrCodeUrl(sysConfigService.getDBApplicationConfig().genScanImgUrl(qrCdLink));
}else{ //默认都为跳转地址方式
res.setCodeUrl(qrCdLink);
jsapiMsg.setQrCodeUrl(qrCdLink);
}
res.setPayData(JSON.toJSONString(jsapiMsg));
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
}else {
DgPayKit.channelMsgError(resData, channelRetMsg);
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
}
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
return res;
}catch (Exception e) {
log.error(e.getMessage());
throw ChannelException.sysError(e.getMessage());
}
}
}

View File

@@ -0,0 +1,105 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay.payway;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.rqrs.AbstractRS;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.WxBarOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.WxBarOrderRS;
import com.jeequan.jeepay.thirdparty.channel.dgpay.DgPayKit;
import com.jeequan.jeepay.thirdparty.channel.dgpay.DgpayPaymentService;
import com.jeequan.jeepay.thirdparty.util.ApiResBuilder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
/**
* 斗拱 微信 条码支付
*
* @author xiaoyu
*
* @date 2022/6/13 18:03
*/
@Slf4j
@Service("dgPaymentByWxBarService") //Service Name需保持全局唯一性
public class WxBar extends DgpayPaymentService {
@Override
public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) {
WxBarOrderRQ bizRQ = (WxBarOrderRQ) rq;
if(StringUtils.isEmpty(bizRQ.getAuthCode())){
throw new BizException("用户支付条码[authCode]不可为空");
}
return null;
}
@Override
public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception {
WxBarOrderRQ bizRQ = (WxBarOrderRQ) rq;
WxBarOrderRS res = ApiResBuilder.buildSuccess(WxBarOrderRS.class);
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
res.setChannelRetMsg(channelRetMsg);
JSONObject dataParams = DgPayKit.dataParams(payOrder, getNotifyUrl());
dataParams.put("auth_code", bizRQ.getAuthCode());
// 设置订单是否需要分账
if(isDivisionOrder(payOrder)) {
dataParams.put("delay_acct_flag", "Y");
}
JSONObject response = DgPayKit.payRequest(mchAppConfigContext, dataParams, "/trade/payment/micropay", payOrder.getIfCode());
JSONObject resData = response.getJSONObject("data");
String code = resData.getString("resp_code");
channelRetMsg.setChannelBizData(resData);
if(DgPayKit.STATE_SUCCESS.equals(code)){
String transStat = resData.getString("trans_stat");
String channelOrderNo = resData.get("hf_seq_id").toString();
channelRetMsg.setChannelOrderId(channelOrderNo);
if (DgPayKit.STATE_SUCCESS_S.equals(transStat)) {
// 交易状态成功
channelRetMsg.setPlatformOrderNo(resData.get("out_trans_id").toString());
channelRetMsg.setPlatformMchOrderNo(resData.get("party_order_id").toString());
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
res.setPayData(resData.toString());
String debit_type = resData.getString("debit_type");
if(StringUtils.isEmpty(debit_type) || "0".equals(debit_type)){
channelRetMsg.setDrType(CS.DrType.OTHER.getType());
}else if("D".equals(debit_type)){
channelRetMsg.setDrType(CS.DrType.DEBIT.getType());
}else if("C".equals(debit_type)){
channelRetMsg.setDrType(CS.DrType.CREDIT.getType());
}else{
channelRetMsg.setDrType(CS.DrType.OTHER.getType());
}
}else if (DgPayKit.STATE_FAIL_F.equals(transStat)) {
// 交易状态失败
DgPayKit.channelMsgError(resData, channelRetMsg);
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
}else {
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
// 开启轮询查单
channelRetMsg.setNeedQuery(true);
}
} else if (DgPayKit.STATE_ING.equals(code)){
String channelOrderNo = resData.get("hf_seq_id").toString();
channelRetMsg.setChannelOrderId(channelOrderNo);
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
// 开启轮询查单
channelRetMsg.setNeedQuery(true);
}else{ //其他状态, 表示下单失败
DgPayKit.channelMsgError(resData, channelRetMsg);
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
}
return res;
}
}

View File

@@ -0,0 +1,98 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay.payway;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.exception.ChannelException;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.oauth2.WxpayOauth2Params;
import com.jeequan.jeepay.core.model.rqrs.AbstractRS;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.WxJsapiOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.WxJsapiOrderRS;
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
import com.jeequan.jeepay.thirdparty.channel.dgpay.DgPayKit;
import com.jeequan.jeepay.thirdparty.channel.dgpay.DgpayPaymentService;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import com.jeequan.jeepay.thirdparty.util.ApiResBuilder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
/**
* 斗拱 微信jsapi
*
* @author xiaoyu
*
* @date 2022/6/13 17:40
*/
@Slf4j
@Service("dgpayPaymentByWxJsapiService") //Service Name需保持全局唯一性
public class WxJsapi extends DgpayPaymentService {
@Override
public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) {
WxJsapiOrderRQ bizRQ = (WxJsapiOrderRQ) rq;
if(StringUtils.isEmpty(bizRQ.getOpenid())){
throw new BizException("[openId]不可为空");
}
return null;
}
@Override
public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception {
WxJsapiOrderRS res = ApiResBuilder.buildSuccess(WxJsapiOrderRS.class);
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
res.setChannelRetMsg(channelRetMsg);
try {
ConfigContextQueryService configContextQueryService = SpringBeansUtil.getBean(ConfigContextQueryService.class);
WxpayOauth2Params oauth2Params = (WxpayOauth2Params)configContextQueryService.queryIsvOauth2Params(mchAppConfigContext.getMchApplyment().getIsvNo(), CS.IF_CODE.WXPAY);
// 设置请求参数
WxJsapiOrderRQ bizRQ = (WxJsapiOrderRQ) rq;
JSONObject dataParams = DgPayKit.dataParams(payOrder, getNotifyUrl());
dataParams.put("trade_type", "T_JSAPI");
JSONObject wxJson = new JSONObject();
// wxJson.put("sub_appid", oauth2Params.getAppId());
wxJson.put("openid", bizRQ.getChannelUserId());
// 接口上传的appId
if (StringUtils.isNotEmpty(bizRQ.getSubAppid())) {
wxJson.put("sub_appid", bizRQ.getSubAppid());
}else{
wxJson.put("sub_appid", oauth2Params.getAppId());
}
dataParams.put("wx_data", wxJson.toJSONString());
// 设置订单是否需要分账
if(isDivisionOrder(payOrder)) {
dataParams.put("delay_acct_flag", "Y");
}
JSONObject response = DgPayKit.payRequest(mchAppConfigContext, dataParams, "/trade/payment/jspay", payOrder.getIfCode());
JSONObject resData = response.getJSONObject("data");
String code = resData.getString("resp_code");
channelRetMsg.setChannelBizData(resData);
if(DgPayKit.STATE_ING.equals(code)){
String channelOrderNo = resData.get("hf_seq_id").toString();
//付款信息
String payInfo = resData.getString("pay_info");
channelRetMsg.setChannelOrderId(channelOrderNo);
// 获取payInfo
res.setPayInfo(payInfo);
res.setPayData(payInfo);
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
}else { //其他状态, 表示下单失败
DgPayKit.channelMsgError(resData, channelRetMsg);
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
}
}catch (Exception e) {
log.error(e.getMessage());
throw ChannelException.sysError(e.getMessage());
}
return res;
}
}

View File

@@ -0,0 +1,103 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay.payway;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.exception.ChannelException;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.oauth2.WxpayOauth2Params;
import com.jeequan.jeepay.core.model.rqrs.AbstractRS;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.WxLiteOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.WxLiteOrderRS;
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
import com.jeequan.jeepay.thirdparty.channel.dgpay.DgPayKit;
import com.jeequan.jeepay.thirdparty.channel.dgpay.DgpayPaymentService;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import com.jeequan.jeepay.thirdparty.util.ApiResBuilder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
/**
* 斗拱 微信jsapi
*
* @author xiaoyu
*
* @date 2022/6/13 17:52
*/
@Slf4j
@Service("dgpayPaymentByWxLiteService") //Service Name需保持全局唯一性
public class WxLite extends DgpayPaymentService {
@Override
public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) {
WxLiteOrderRQ bizRQ = (WxLiteOrderRQ) rq;
if(StringUtils.isEmpty(bizRQ.getOpenid())){
throw new BizException("[openId]不可为空");
}
return null;
}
@Override
public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception {
WxLiteOrderRS res = ApiResBuilder.buildSuccess(WxLiteOrderRS.class);
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
res.setChannelRetMsg(channelRetMsg);
try {
ConfigContextQueryService configContextQueryService = SpringBeansUtil.getBean(ConfigContextQueryService.class);
WxpayOauth2Params oauth2Params = (WxpayOauth2Params) configContextQueryService.queryIsvOauth2Params(mchAppConfigContext.getMchApplyment().getIsvNo(), CS.IF_CODE.WXPAY);
// 设置请求参数
WxLiteOrderRQ bizRQ = (WxLiteOrderRQ) rq;
JSONObject dataParams = DgPayKit.dataParams(payOrder, getNotifyUrl());
dataParams.put("trade_type", "T_MINIAPP");
JSONObject wxJson = new JSONObject();
wxJson.put("sub_appid", oauth2Params.getLiteAppId());
wxJson.put("sub_openid", bizRQ.getChannelUserId());
// 接口上传的appId
if (StringUtils.isNotEmpty(bizRQ.getSubAppid())) {
wxJson.put("sub_appid", bizRQ.getSubAppid());
}
dataParams.put("wx_data", wxJson.toJSONString());
// 设置订单是否需要分账
if(isDivisionOrder(payOrder)) {
dataParams.put("delay_acct_flag", "Y");
}
JSONObject response = DgPayKit.payRequest(mchAppConfigContext, dataParams, "/trade/payment/jspay", payOrder.getIfCode());
JSONObject resData = response.getJSONObject("data");
channelRetMsg.setChannelBizData(resData);
String code = resData.getString("resp_code");
if(DgPayKit.STATE_ING.equals(code)){
String channelOrderNo = resData.get("hf_seq_id").toString();
//付款信息
String payInfo = resData.getString("pay_info");
channelRetMsg.setChannelOrderId(channelOrderNo);
// 获取payInfo
res.setPayInfo(payInfo);
res.setPayData(payInfo);
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
// 开启轮询查单
channelRetMsg.setNeedQuery(true);
}else { //其他状态, 表示下单失败
DgPayKit.channelMsgError(resData, channelRetMsg);
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
}
}catch (Exception e) {
log.error(e.getMessage());
throw ChannelException.sysError(e.getMessage());
}
return res;
}
}

View File

@@ -0,0 +1,93 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay.payway;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.rqrs.AbstractRS;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelJsapiMsg;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.YsfJsapiOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.YsfJsapiOrderRS;
import com.jeequan.jeepay.thirdparty.channel.dgpay.DgPayKit;
import com.jeequan.jeepay.thirdparty.channel.dgpay.DgpayPaymentService;
import com.jeequan.jeepay.thirdparty.util.ApiResBuilder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
/*
* 银联 jsapi
*
* @author zx
*
* @date 2021/6/8 18:11
*/
@Slf4j
@Service("dgpayPaymentByYsfJsapiService") //Service Name需保持全局唯一性
public class YsfJsapi extends DgpayPaymentService {
@Override
public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) {
YsfJsapiOrderRQ bizRQ = (YsfJsapiOrderRQ) rq;
if(StringUtils.isEmpty(bizRQ.getUserId())){
throw new BizException("[userId]不可为空");
}
return null;
}
@Override
public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception {
YsfJsapiOrderRS res = ApiResBuilder.buildSuccess(YsfJsapiOrderRS.class);
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
res.setChannelRetMsg(channelRetMsg);
try {
YsfJsapiOrderRQ bizRQ = (YsfJsapiOrderRQ) rq;
JSONObject dataParams = DgPayKit.dataParams(payOrder, getNotifyUrl());
dataParams.put("trade_type", "U_JSAPI");
JSONObject jsapiJson = new JSONObject();
jsapiJson.put("customer_ip", payOrder.getClientIp());
jsapiJson.put("user_id", bizRQ.getUserId());
dataParams.put("unionpay_data", jsapiJson.toJSONString());
// 设置订单是否需要分账
if(isDivisionOrder(payOrder)) {
dataParams.put("delay_acct_flag", "Y");
}
JSONObject response = DgPayKit.payRequest(mchAppConfigContext, dataParams, "/trade/payment/jspay", payOrder.getIfCode());
JSONObject resData = response.getJSONObject("data");
channelRetMsg.setChannelBizData(resData);
String code = resData.getString("resp_code");
if (DgPayKit.STATE_ING.equals(code)) {
String channelOrderNo = resData.getString("hf_seq_id");
//付款信息
String payInfo = resData.getString("pay_info");
channelRetMsg.setChannelOrderId(channelOrderNo);
res.setRedirectUrl(payInfo);
ChannelJsapiMsg channelJsapiMsg = new ChannelJsapiMsg();
channelJsapiMsg.setRedirectUrl(payInfo);
res.setPayData(JSON.toJSONString(channelJsapiMsg));
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
}else {
DgPayKit.channelMsgError(resData, channelRetMsg);
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
}
return res;
}catch (Exception e) {
log.error(e.getMessage());
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
channelRetMsg.setChannelErrMsg("系统异常");
return res;
}
}
}

View File

@@ -0,0 +1,103 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay.utils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DgUtils {
private static List<Map<String, String>> limitList = new ArrayList<>();
static {
Map<String, String> item = new HashMap<>();
item.put("name", "贵州省");
item.put("code", "520000");
limitList.add(item);
item = new HashMap<>();
item.put("name", "湖南省");
item.put("code", "430000");
limitList.add(item);
item = new HashMap<>();
item.put("name", "陕西省");
item.put("code", "610000");
limitList.add(item);
item = new HashMap<>();
item.put("name", "河南省");
item.put("code", "410000");
limitList.add(item);
item = new HashMap<>();
item.put("name", "浙江省");
item.put("code", "330000");
limitList.add(item);
item = new HashMap<>();
item.put("name", "重庆市");
item.put("code", "500100");
limitList.add(item);
item = new HashMap<>();
item.put("name", "云南省");
item.put("code", "530000");
limitList.add(item);
item = new HashMap<>();
item.put("name", "湖北省");
item.put("code", "420000");
limitList.add(item);
item = new HashMap<>();
item.put("name", "福建省");
item.put("code", "350000");
limitList.add(item);
item = new HashMap<>();
item.put("name", "宁夏回族自治区");
item.put("code", "640000");
limitList.add(item);
item = new HashMap<>();
item.put("name", "吉林省");
item.put("code", "220000");
limitList.add(item);
item = new HashMap<>();
item.put("name", "黑龙江省");
item.put("code", "230000");
limitList.add(item);
item = new HashMap<>();
item.put("name", "江苏省");
item.put("code", "320000");
limitList.add(item);
item = new HashMap<>();
item.put("name", "海南省");
item.put("code", "460000");
limitList.add(item);
item = new HashMap<>();
item.put("name", "青海省");
item.put("code", "630000");
limitList.add(item);
}
/**
* 检验银联限制
* @param provinceCode
* @return
*/
public static boolean checkBankLimitArea(String provinceCode) {
for (Map<String, String> item : limitList) {
if (provinceCode.equals(item.get("code"))) {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,86 @@
package com.jeequan.jeepay.thirdparty.channel.dgpay.utils;
import java.security.MessageDigest;
import java.util.HashMap;
import java.util.Map;
/**
* @ClassName MD5Utils
* @Description
* @Author boy
* @Date 2019/8/30 8:29 PM
*/
public class MD5Utils {
static char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
static String MD5 = "MD5";//加签方式MD5
/*
* @Author boy
* @Description 数据签名
* @Date 2019/8/31 1:57 PM
* @Param [data, key]
* @return java.lang.String
*/
public static String sign(String data, String key) throws Exception {
//得到明文的字节数组
byte[] btInput = (data + key).getBytes();
// 创建一个提供信息摘要算法的对象(MD5摘要算法)
MessageDigest messageDigest = MessageDigest.getInstance(MD5);
// 使用指定的字节更新摘要
messageDigest.update(btInput);
// 得到二进制的密文
byte[] encryptData = messageDigest.digest();
// 把密文转换成十六进制的字符串形式
String encryptDataStr = bytesToHex(encryptData);
return encryptDataStr;
}
/*
* @Author boy
* @Description 验签
* @Date 2019/8/31 1:57 PM
* @Param [data, key, sign][明文数据,签名key,接收到的签名]
* @return boolean
*/
public static boolean verifySign(String data, String key, String sign) throws Exception {
//调用加签方法,看加签后的签名是否和接收到的一致
String encryptData = sign(data, key);
if (encryptData.equals(sign)) {
return true;
} else {
return false;
}
}
/*
* @Author boy
* @Description 将byte数组转化为16进制字符串
* @Date 2019/8/31 1:58 PM
* @Param [bytes]
* @return java.lang.String
*/
public static String bytesToHex(byte[] bytes) {
int k = 0;
char[] hexChars = new char[bytes.length * 2];
for (int i = 0; i < bytes.length; i++) {
byte byte0 = bytes[i];
hexChars[k++] = hexDigits[byte0 >>> 4 & 0xf];
hexChars[k++] = hexDigits[byte0 & 0xf];
}
return new String(hexChars);
}
public static void main(String[] args) throws Exception {
Map<String, String> hashMap = new HashMap<>();
String data = "你好MD你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!你好MD5!5!";
String key = "1234567890abcdef";//密钥
String dataSign = MD5Utils.sign(data, key);
hashMap.put("data", data);
hashMap.put("dataSign", dataSign);
System.out.println("明文:" + hashMap.get("data"));
System.out.println("签名:" + hashMap.get("dataSign"));
System.out.println("验签结果:" + MD5Utils.verifySign(data, key, dataSign));
}
}

View File

@@ -0,0 +1,100 @@
package com.jeequan.jeepay.thirdparty.channel.flpay;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.crypto.digest.HmacAlgorithm;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.exception.ChannelException;
import com.jeequan.jeepay.core.model.DBPaymentConfig;
import com.jeequan.jeepay.core.utils.JeepayKit;
import com.jeequan.jeepay.thirdparty.channel.flpay.model.ReqEntity;
import com.jeequan.jeepay.thirdparty.channel.flpay.model.ReqMethod;
import com.jeequan.jeepay.thirdparty.channel.flpay.model.RespEntity;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import javax.xml.bind.DatatypeConverter;
import java.nio.charset.StandardCharsets;
/**
* TODO
* 福禄打款
* @author crystal
* @date 2024/3/25 11:09
*/
@Slf4j
public class FlKit {
/**
* 发起请求
* @param method
* @param config
* @param bizData
* @return
*/
public static JSONObject reqApi(ReqMethod.Method method, DBPaymentConfig config, JSONObject bizData) {
ReqEntity reqEntity = initSign(config,bizData);
String url = ReqMethod.getUrl(config,method);
log.info("【福禄】请求参数:{}",JSON.toJSONString(reqEntity));
HttpResponse response = HttpRequest.post(url).body(JSON.toJSONString(reqEntity)).execute();
if(!response.isOk()){
throw ChannelException.sysError("付款接口请求异常");
}
log.info("【福禄】返回参数:{}",response.body());
RespEntity respEntity = JSON.parseObject(response.body(), RespEntity.class);
return initResult(respEntity);
}
/**
* 公共返回参数处理
* @param resp
* @return
*/
public static JSONObject initResult(RespEntity resp) {
if(!resp.isSuccess()){
String msg = StringUtils.isNotEmpty(resp.getMsg()) ? resp.getMsg() : resp.getSubMsg();
throw ChannelException.sysError(msg);
}
return JSON.parseObject(resp.getData());
}
public static ReqEntity initSign(DBPaymentConfig config,JSONObject bizData){
bizData.put("taskNo", config.getTaskNo());
bizData.put("appId", config.getAppId());
ReqEntity reqEntity = new ReqEntity(config.getAppId(),bizData);
reqEntity.setSign(genSign(reqEntity,config));
return reqEntity;
}
/**
* 校验回调签名
* @param reqEntity
* @param config
* @return
*/
public static boolean checkResultNotify(ReqEntity reqEntity, DBPaymentConfig config) {
String sign = genSign(reqEntity,config);
return !sign.equalsIgnoreCase(reqEntity.getSign());
}
private static String genSign(ReqEntity reqEntity,DBPaymentConfig config) {
String signStr = JeepayKit.getStrSort(reqEntity.toMap());
//前5位
String prefix = config.getSecret().substring(0,5);
//后5位
String suffix = config.getSecret().substring(config.getSecret().length() - 5);
//拼接在首尾
String signContent = prefix + signStr + suffix;
try {
String signText = DigestUtil.sha256Hex(signContent);
byte[] secretSigning = DigestUtil.hmac(HmacAlgorithm.HmacSHA256, config.getSecret().getBytes(StandardCharsets.UTF_8)).digest(reqEntity.getTimestamp());
String sign = DatatypeConverter.printHexBinary(DigestUtil.hmac(HmacAlgorithm.HmacSHA256,secretSigning).digest(signText)).toLowerCase();
return sign;
} catch (Exception e) {
throw new BizException("生成签名异常");
}
}
}

View File

@@ -0,0 +1,173 @@
package com.jeequan.jeepay.thirdparty.channel.flpay;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.CashoutRecord;
import com.jeequan.jeepay.core.exception.ChannelException;
import com.jeequan.jeepay.core.model.DBPaymentConfig;
import com.jeequan.jeepay.core.model.df.Account;
import com.jeequan.jeepay.core.model.df.PaymentSign;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.utils.AmountUtil;
import com.jeequan.jeepay.db.entity.AccountFundInfo;
import com.jeequan.jeepay.db.entity.AccountInfo;
import com.jeequan.jeepay.service.impl.AccountInfoService;
import com.jeequan.jeepay.thirdparty.channel.flpay.model.ReqMethod;
import com.jeequan.jeepay.thirdparty.channel.AbstractRepayApiService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Locale;
/**
* TODO
* 福禄打款
* @author crystal
* @date 2024/3/25 10:38
*/
@Service
@Slf4j
public class FlpayRepayApiService extends AbstractRepayApiService {
@Autowired
private AccountInfoService accountInfoService;
@Override
public String getIfCode() {
return CS.IF_CODE.FLPAY;
}
/**
* 是否需要签约
* @return
*/
@Override
public boolean isSign() {
return true;
}
@Override
public void preCheck(String mchNo,byte accountType,Long changeAmt) {
this.checkAccountBalance(mchNo,accountType,changeAmt);
}
/**
* 查询账户余额
* @param mchNo 用户号
* @param accountType 账户类型
* @return
*/
@Override
public Account queryAccountBalance(String mchNo, byte accountType) {
AccountInfo accountInfo = accountInfoService.getAccountInfo(mchNo, accountType);
return new Account(accountInfo.getBalacne(),accountInfo.getFreeze());
}
/**
* 签约正常 签约状态(0-待处理 1-校验失败 2-待签约 3-签约成功 4-校验错误)
* @param sign
* @param config
* @return
*/
@Override
public ChannelRetMsg sign(PaymentSign sign, DBPaymentConfig config) {
JSONObject bizData = new JSONObject();
bizData.put("userName",sign.getUserName());
bizData.put("idCard",sign.getIdCard().toUpperCase(Locale.ROOT));
bizData.put("mobile",sign.getMobile());
JSONObject respBizData = FlKit.reqApi(ReqMethod.Method.SIGN, config, bizData);
Integer signStatus = respBizData.getInteger("signStatus");
switch (signStatus){
case 3:
return ChannelRetMsg.confirmSuccess(respBizData.getString("peopleId"));
}
return ChannelRetMsg.confirmFail(null,respBizData.getString("failReason"));
}
/**
* 签约结果查询
* @param sign
* @param config
* @return
*/
@Override
public ChannelRetMsg querySign(PaymentSign sign, DBPaymentConfig config) {
JSONObject bizData = new JSONObject();
bizData.put("userName",sign.getUserName());
bizData.put("idCard",sign.getIdCard().toUpperCase(Locale.ROOT));
JSONObject respBizData = FlKit.reqApi(ReqMethod.Method.SIGN_QUERY, config, bizData);
Integer signStatus = respBizData.getInteger("signStatus");
switch (signStatus){
case 3:
return ChannelRetMsg.confirmSuccess(respBizData.getString("peopleId"));
}
return ChannelRetMsg.confirmFail(null,respBizData.getString("failReason"));
}
/**
* 付款
* @return
*/
@Override
public ChannelRetMsg payment(CashoutRecord record,DBPaymentConfig config) {
JSONObject bizData = new JSONObject();
bizData.put("accountNumber",record.getSettAccountNo());
bizData.put("afterTaxAmount", AmountUtil.convertCent2Dollar(AmountUtil.abs(record.getSettAmount())));
String grantType = AccountFundInfo.WECHAT.equals(record.getSettAccountType()) ? "微信支付" : AccountFundInfo.ALIPAY == record.getSettAccountType() ? "支付宝" : "银行卡";
bizData.put("grantType", grantType);
bizData.put("idCard",record.getIdCard());
bizData.put("outBizNo",record.getTransferOrderId());
bizData.put("mobile",record.getBankPhone());
bizData.put("transRemark","账户结算处理");
bizData.put("remark",record.getAuditRemark());
bizData.put("userName",record.getSettAccountName());
bizData.put("notifyUrl",getNotifyUrl());
try {
JSONObject respBizData = FlKit.reqApi(ReqMethod.Method.PAYMENT, config, bizData);
ChannelRetMsg channelRetMsg = ChannelRetMsg.waiting();
channelRetMsg.setChannelOrderId(respBizData.getString("bizNo"));
channelRetMsg.setChannelBizData(respBizData);
return channelRetMsg;
}catch (ChannelException e){
throw ChannelException.sysError(e.getMessage());
}catch (Exception e){
return ChannelRetMsg.confirmFail(e.getMessage());
}
}
/**
* 付款结果查询
* @param outBizNo 外部请求流水号
* @param config
* @return
*/
@Override
public ChannelRetMsg queryPayment(String outBizNo, DBPaymentConfig config) {
JSONObject bizData = new JSONObject();
bizData.put("outBizNo",outBizNo);
try {
JSONObject respBizData = FlKit.reqApi(ReqMethod.Method.PAYMENT_QUERY, config, bizData);
String failCause = respBizData.getString("failCause");
Integer auditStatus = respBizData.getInteger("auditStatus");
Integer payStatus = respBizData.getInteger("payStatus");
String payFailCause = respBizData.getString("payFailCause");
ChannelRetMsg retMsg = ChannelRetMsg.waiting();
retMsg.setChannelErrMsg(failCause);
switch (auditStatus){
case 2:
return retMsg;
}
retMsg.setChannelErrMsg(payFailCause);
switch (payStatus){
case 3:
return ChannelRetMsg.confirmSuccess(respBizData.getString("bizNo"));
}
return retMsg;
}catch (ChannelException e){
throw ChannelException.sysError(e.getMessage());
}catch (Exception e){
return ChannelRetMsg.confirmFail(e.getMessage());
}
}
}

View File

@@ -0,0 +1,115 @@
package com.jeequan.jeepay.thirdparty.channel.flpay;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.exception.ResponseException;
import com.jeequan.jeepay.core.interfaces.paychannel.IRepayNoticeService;
import com.jeequan.jeepay.core.model.DBPaymentConfig;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.db.entity.AccountChangeInfo;
import com.jeequan.jeepay.service.impl.AccountChangeInfoService;
import com.jeequan.jeepay.service.impl.SysConfigService;
import com.jeequan.jeepay.thirdparty.channel.AbstractRepayNoticeService;
import com.jeequan.jeepay.thirdparty.channel.flpay.model.ReqEntity;
import com.jeequan.jeepay.thirdparty.channel.kqpay.KqpayKit;
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.Service;
import javax.servlet.http.HttpServletRequest;
/**
* TODO
*
* @author crystal
* @date 2024/3/27 10:43
*/
@Service
@Slf4j
public class FlpayRepayNoticeService extends AbstractRepayNoticeService {
@Autowired private SysConfigService sysConfigService;
@Override
public String getIfCode() {
return CS.IF_CODE.FLPAY;
}
/**
* 参数解析
* @param request
* @param urlOrderId
* @return
*/
@Override
public MutablePair<String, Object> parseParams(HttpServletRequest request, String urlOrderId) {
JSONObject reqParamJSON = getReqParamJSON();
JSONObject bizContent = reqParamJSON.getJSONObject("bizContent");
return MutablePair.of(bizContent.getString("bizOutNo"), reqParamJSON);
}
/**
*
* @param request
* @param urlOrderId
* @param params
* @return
*/
@Override
public ChannelRetMsg doNotice(HttpServletRequest request, String urlOrderId, Object params) {
JSONObject respJson = (JSONObject) JSON.toJSON(params);
boolean verifyResult = verifyParams(respJson);
if(!verifyResult){
throw ResponseException.buildText("sign check error");
}
JSONObject biz = respJson.getJSONObject("bizContent");
String channelRemark = "【渠道备注】";
log.info("【灵工】打款状态回调,打款流水号:{},业务参数:{}", urlOrderId, biz);
//失败原因
String failCause = biz.getString("failCause");
//审核状态 审核状态 0-待审核 1-审核通过 2-人工审核拒绝
int auditStatus = biz.getIntValue("auditStatus");
//发放状态 发放(推送)状态 0-待发放 1-发放通过 2-发放拒绝
int grantStatus = biz.getIntValue("grantStatus");
//支付状态 0-未处理 1-待打款 2-打款中 3-打款成功 4-打款失败 5-退票退款
int payStatus = biz.getIntValue("payStatus");
//支付失败原因
String payFailCause = biz.getString("payFailCause");
String bizNo = biz.getString("bizNo");
ChannelRetMsg retMsg = ChannelRetMsg.waiting();
ResponseEntity okResponse = textResp("success");
retMsg.setResponseEntity(okResponse);
retMsg.setChannelOrderId(bizNo);
retMsg.setChannelErrMsg(channelRemark + failCause);
if(grantStatus == 2 || auditStatus == 2){
return retMsg;
}
retMsg.setChannelErrMsg(channelRemark + payFailCause);
switch (payStatus){
case 3:
return ChannelRetMsg.confirmSuccess(bizNo);
}
return retMsg;
}
/**
* 校验参数
* @param noticeData
* @return
*/
public boolean verifyParams(JSONObject noticeData) {
DBPaymentConfig config = sysConfigService.getDbPaymentConfig();
if(CS.IF_CODE.FLPAY.equals(config.getPaymentType())){
try {
return FlKit.checkResultNotify(noticeData.toJavaObject(ReqEntity.class),config);
}catch (Exception e) {
log.error("验证签名异常", e);
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,69 @@
package com.jeequan.jeepay.thirdparty.channel.flpay.model;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.Getter;
import lombok.Setter;
import java.util.HashMap;
import java.util.Map;
/**
* TODO
* 福禄公共参数请求封装
* @author crystal
* @date 2024/2/29 11:53
*/
@Setter
@Getter
public class ReqEntity {
/**
*appid
*/
private String appId;
/**
* 时间戳
*/
private String timestamp;
/**
* 随机字符串
*/
private String nonce;
/**
* 请求唯一表示,代表订单号 长度不能超过12
*/
private String sign;
/**
* 请求回调地址
*/
private String bizContent;
public ReqEntity(String appId, JSONObject bizData) {
this.appId = appId;
this.nonce = RandomUtil.randomString(20);
if(bizData != null){
this.bizContent = bizData.toJSONString();
}else{
this.bizContent = "{}";
}
String timestamp = System.currentTimeMillis() + "";
this.timestamp = timestamp;
}
public Map<String, Object> toMap() {
Map<String, Object> params = new HashMap<>(5);
params.put("appId",this.appId);
params.put("timestamp",this.timestamp);
params.put("nonce",this.nonce);
params.put("bizContent",this.bizContent);
return params;
}
}

View File

@@ -0,0 +1,49 @@
package com.jeequan.jeepay.thirdparty.channel.flpay.model;
import com.jeequan.jeepay.core.model.DBPaymentConfig;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
/**
* TODO
*
* @author crystal
* @date 2024/3/25 11:40
*/
@Data
public class ReqMethod {
@Getter
@AllArgsConstructor
public enum Method{
SIGN("/api/open/uploadPeople","自由职业者签约"),
SIGN_QUERY("/api/open/queryPeople","自由职业者签约状态查询"),
PAYMENT("/api/open/uploadPlan","付款接口"),
ACCOUNT_QUERY("/api/open/queryPayMethod","账户类型查询接口"),
AUDIT("/api/open/grantPlan","审核下发结算"),
PAYMENT_QUERY("/api/open/queryPlan","结算发放结果查询接口"),
;
private final String method;
private final String desc;
}
/** 获取请求接口地址
* @param method
* @return
*/
public static String getUrl(DBPaymentConfig config,Method method){
return config.getDomain() + method.getMethod();
}
}

View File

@@ -0,0 +1,48 @@
package com.jeequan.jeepay.thirdparty.channel.flpay.model;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* TODO
* 福禄通用返回参数封装
* @author crystal
* @date 2024/3/25 13:51
*/
@Data
@NoArgsConstructor
public class RespEntity {
public static final int SUCCESS_CODE = 0;
public static final int SUCCESS_EXT_CODE = 1;
public static final int NETWORK_ERROR_CODE = 500;
private int code;
private String data;
private String msg;
private String subCode;
private String subMsg;
public RespEntity(int code, String msg) {
this.code = code;
this.msg = msg;
}
public static RespEntity fail(String msg) {
RespEntity result = new RespEntity(NETWORK_ERROR_CODE,msg);
return result;
}
/**
* 判断是否返回成功
*/
public boolean isSuccess(){
return SUCCESS_CODE == this.code;
}
}

View File

@@ -0,0 +1,32 @@
package com.jeequan.jeepay.thirdparty.channel.kqpay;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.interfaces.paychannel.IGetApplymentDataService;
import com.jeequan.jeepay.db.entity.BankBranch;
import com.jeequan.jeepay.service.impl.BankBranchService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
@Service("dgpayGetApplymentDataService")
@Slf4j
public class DgpayGetApplymentDataService implements IGetApplymentDataService {
@Autowired
private BankBranchService bankBranchService;
@Override
public List<JSONObject> getBankBranchInfo(String isvNo, String bankAliasCode, String cityCode, String bankName, String branchName) throws BizException {
// Assert.notBlank(bankName, "银行名称不能为空");
Page<BankBranch> page = new Page<>();
bankBranchService.pageByArea(page, cityCode, bankName, branchName);
return page.getRecords().stream().map( t -> ((JSONObject) JSON.toJSON(t))).collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,92 @@
package com.jeequan.jeepay.thirdparty.channel.kqpay;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrder;
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.thirdparty.channel.AbstractChannelNoticeService;
import com.jeequan.jeepay.thirdparty.channel.kqpay.model.KqpayOrderStatus;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.MutablePair;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
/**
* 快钱支付 回调接口实现类
*
* @author xiaoyu
*
* @date 2022/4/15 10:23
*/
@Service
@Slf4j
public class KqpayChannelNoticeService extends AbstractChannelNoticeService {
@Override
public String getIfCode() {
return CS.IF_CODE.KQPAY;
}
@Override
public MutablePair<String, Object> parseParams(HttpServletRequest request, String urlOrderId, NoticeTypeEnum noticeTypeEnum) {
JSONObject reqParamJSON = getReqParamJSON();
JSONObject notifyHead = reqParamJSON.getJSONObject("head");
return MutablePair.of(notifyHead.getString("externalRefNumber"), reqParamJSON);
}
@Override
public ChannelRetMsg doNotice(HttpServletRequest request, Object params, PayOrder payOrder, MchAppConfigContext mchAppConfigContext, NoticeTypeEnum noticeTypeEnum) {
JSONObject respJson = (JSONObject) JSON.toJSON(params);
log.info("快钱回调参数:{}",respJson);
boolean verifyResult = verifyParams(respJson, mchAppConfigContext);
log.info("快前参数解密结果:{}",respJson);
// 验证参数失败
if(!verifyResult){
throw ResponseException.buildText("签名校验失败");
}
//验签成功后判断上游订单状态
ResponseEntity okResponse = textResp(KqpayKit.resSuccess(respJson));
ChannelRetMsg result = new ChannelRetMsg();
result.setResponseEntity(okResponse);
result.setChannelBizData(respJson);
JSONObject requestBody = respJson.getJSONObject("requestBody");
KqpayOrderStatus orderStatus = KqpayOrderStatus.getVal(requestBody.getString("orderStatus"));
result.setChannelOrderId(requestBody.getString("idOrderCtrl"));
result.setPlatformMchOrderNo(requestBody.getString("idTxnCtrl"));
result.setChannelErrCode(requestBody.getString("bizResponseCode"));
result.setChannelErrMsg(requestBody.getString("bizResponseMessage"));
JSONObject equityInfo = requestBody.getJSONObject("equityInfo");
result.setPlatformOrderNo(requestBody.getString("channelTradeNo"));
result.setChannelUserId(requestBody.getString("thirdPartyBuyerId"));
switch (orderStatus){
case SUCCESS:
result.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
break;
case FAIL:
result.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
break;
}
return result;
}
/**
* 验证银盛支付通知参数
* @return
*/
public boolean verifyParams(JSONObject jsonParams, MchAppConfigContext mchAppConfigContext) {
try {
return KqpayKit.checkResultNotify(jsonParams,mchAppConfigContext);
}catch (Exception e) {
log.error("验证签名异常", e);
return false;
}
}
}

View File

@@ -0,0 +1,102 @@
package com.jeequan.jeepay.thirdparty.channel.kqpay;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.RefundOrder;
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.thirdparty.channel.AbstractChannelRefundNoticeService;
import com.jeequan.jeepay.thirdparty.channel.kqpay.model.KqpayOrderStatus;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.MutablePair;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
/**
* 拉卡拉退款回调接口实现类
*
* @author xiaoyu
*
* @date 2021/12/24 11:31
*/
@Service
@Slf4j
public class KqpayChannelRefundNoticeService extends AbstractChannelRefundNoticeService {
@Override
public String getIfCode() {
return CS.IF_CODE.KQPAY;
}
@Override
public MutablePair<String, Object> parseParams(HttpServletRequest request, String urlOrderId, NoticeTypeEnum noticeTypeEnum) {
try {
JSONObject reqParamJSON = getReqParamJSON();
JSONObject notifyHead = reqParamJSON.getJSONObject("head");
return MutablePair.of(notifyHead.getString("externalRefNumber"), reqParamJSON);
} catch (Exception e) {
log.error("error", e);
throw ResponseException.buildText("ERROR");
}
}
@Override
public ChannelRetMsg doNotice(HttpServletRequest request, Object params, RefundOrder refundOrder, MchAppConfigContext mchAppConfigContext, NoticeTypeEnum noticeTypeEnum) {
String logPrefix = "【处理快钱退款回调】";
try {
JSONObject respJson = (JSONObject) JSON.toJSON(params);
log.info("快钱回调参数:{}",respJson);
boolean verifyResult = verifyParams(respJson, mchAppConfigContext);
// 验证参数失败
if(!verifyResult){
throw ResponseException.buildText("签名校验失败");
}
log.info("快前参数解密结果:{}",respJson);
//验签成功后判断上游订单状态
ResponseEntity okResponse = textResp(KqpayKit.resSuccess(respJson));
ChannelRetMsg result = new ChannelRetMsg();
//验签成功后判断上游订单状态
result.setResponseEntity(okResponse);
JSONObject requestBody = respJson.getJSONObject("requestBody");
KqpayOrderStatus orderStatus = KqpayOrderStatus.getVal(requestBody.getString("orderStatus"));
result.setChannelOrderId(requestBody.getString("idOrderCtrl"));
result.setPlatformMchOrderNo(requestBody.getString("idTxnCtrl"));
result.setChannelErrCode(requestBody.getString("bizResponseCode"));
result.setChannelErrMsg(requestBody.getString("bizResponseMessage"));
result.setPlatformOrderNo(requestBody.getString("channelTradeNo"));
result.setChannelUserId(requestBody.getString("thirdPartyBuyerId"));
switch (orderStatus){
case SUCCESS:
result.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
break;
case FAIL:
result.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
break;
default:
result.setChannelState(ChannelRetMsg.ChannelState.WAITING);
break;
}
return result;
} catch (Exception e) {
log.error("error", e);
throw ResponseException.buildText("ERROR");
}
}
/**
* 验证快钱退款通知参数
* @return
*/
public boolean verifyParams(JSONObject jsonParams, MchAppConfigContext mchAppConfigContext) {
try {
return KqpayKit.checkResultNotify(jsonParams,mchAppConfigContext);
}catch (Exception e) {
log.error("验证签名异常", e);
return false;
}
}
}

View File

@@ -0,0 +1,43 @@
package com.jeequan.jeepay.thirdparty.channel.kqpay;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.interfaces.paychannel.IGetApplymentDataService;
import com.jeequan.jeepay.db.entity.BankBranch;
import com.jeequan.jeepay.service.impl.BankBranchService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
/***
*
* @author zx
*
* @date 2022/1/4 14:54
*/
@Service
@Slf4j
public class KqpayGetApplymentDataService implements IGetApplymentDataService {
@Autowired
private BankBranchService bankBranchService;
/**
* @cityCode 这里取城市名称.
*/
@Override
public List<JSONObject> getBankBranchInfo(String isvNo, String bankAliasCode, String cityCode, String bankName, String branchName) throws BizException {
// Assert.notBlank(bankName, "银行名称不能为空");
Page<BankBranch> page = new Page<>();
bankBranchService.pageByArea(page, cityCode, bankName, branchName);
return page.getRecords().stream().map( t -> ((JSONObject) JSON.toJSON(t))).collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,62 @@
package com.jeequan.jeepay.thirdparty.channel.kqpay;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.entity.MchApplyment;
import com.jeequan.jeepay.core.entity.MchInfo;
import com.jeequan.jeepay.core.interfaces.paychannel.IIsvmchAlipayConfigService;
import com.jeequan.jeepay.core.model.applyment.ApplymentSignInfo;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.params.kqpay.KqpayIsvParams;
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Service;
@Service
@Slf4j
public class KqpayIsvmchAlipayConfigService implements IIsvmchAlipayConfigService {
@Override
public ApplymentSignInfo alipayOpenSignInfo(MchApplyment mchApplyment) {
ApplymentSignInfo result = new ApplymentSignInfo();
// 进件商户信息
// JSONObject suJson = JSON.parseObject(mchApplyment.getSuccResParameter());
// if (suJson == null || StringUtils.isEmpty(suJson.getString("huifuId"))) {
// result.setState("查询失败");
// result.setErrInfo("商户参数为空,该商户还未进件成功");
// return result;
// }
MchAppConfigContext mchAppConfigContext = new MchAppConfigContext();
mchAppConfigContext.setMchType(MchInfo.TYPE_ISVSUB);
mchAppConfigContext.setMchApplyment(mchApplyment);
// 服务商配置参数
ConfigContextQueryService configContextQueryService = SpringBeansUtil.getBean(ConfigContextQueryService.class);
KqpayIsvParams isvParams = (KqpayIsvParams) configContextQueryService.queryIsvParams(mchApplyment.getIsvNo(), mchApplyment.getIfCode());
// 渠道拓展地址
result.setSignUrl(isvParams.getAliChannelExtUrl());
// 图片类型
result.setImgType(ApplymentSignInfo.IMG_TYPE_QRCONTENT);
result.setState("当前通道无法确定认证状态");
String succResParameter = mchApplyment.getSuccResParameter();
JSONObject succRes = JSON.parseObject(succResParameter);
JSONArray alipayList = succRes.getJSONArray("alipayList");
if (alipayList.isEmpty()) {
result.setState("未报备");
} else {
JSONObject aliReportData = alipayList.getJSONObject(0);
String subMchId = aliReportData.getString("subMchId");
if (ObjectUtils.isEmpty(subMchId)) {
result.setChannelSubMchId(aliReportData.getString("errorMsg"));
} else {
result.setChannelSubMchId(subMchId);
}
}
return result;
}
}

View File

@@ -0,0 +1,160 @@
package com.jeequan.jeepay.thirdparty.channel.kqpay;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.jeequan.jeepay.core.beans.RequestKitBean;
import com.jeequan.jeepay.core.entity.MchApplyment;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.interfaces.paychannel.IIsvmchApplymentNotifyService;
import com.jeequan.jeepay.core.model.params.kqpay.KqpayIsvParams;
import com.jeequan.jeepay.db.entity.PayInterfaceConfig;
import com.jeequan.jeepay.service.impl.MchApplymentService;
import com.jeequan.jeepay.service.impl.PayInterfaceConfigService;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.MutablePair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
@Slf4j
@Service("kqpayIsvmchApplymentNotifyService")
public class KqpayIsvmchApplymentNotifyService implements IIsvmchApplymentNotifyService {
@Autowired
private KqpayMchApplymentService kqpayMchApplymentService;
@Autowired
private PayInterfaceConfigService payInterfaceConfigService;
@Autowired @Lazy
private MchApplymentService mchApplymentService;
@Autowired
protected RequestKitBean requestKitBean;
@Autowired
private ConfigContextQueryService configContextQueryService;
@Override
public ResponseEntity<?> retOk(Object params) {
JSONObject reqParamJSON = requestKitBean.getReqParamJSON();
String s = KqpayKit.resSuccess((reqParamJSON));
return ResponseEntity.ok(JSON.parseObject(s));
}
@Override
public MutablePair<String, Object> parseParams(HttpServletRequest request, String urlApplyId) {
JSONObject reqParamJSON = requestKitBean.getReqParamJSON();
String url = "https://reg.shouyinbeipay.com/auditCallback/kq";
HttpResponse execute = null;
try {
execute = HttpRequest.post(url)
.body(reqParamJSON.toJSONString())
.execute();
if (execute.isOk()) {
log.info("回调给{}成功", url);
} else {
log.info("回调给{}异常", url);
}
} catch (Exception e) {
log.info("回调给{}异常", url);
if (execute != null) {
execute.close();
}
}
String orderId = reqParamJSON.getJSONObject("head").getString("externalRefNumber");
com.jeequan.jeepay.db.entity.MchApplyment mchApplyment = mchApplymentService.getByOrderId(orderId);
String memberCode = reqParamJSON.getJSONObject("head").getString("memberCode");
QueryWrapper<PayInterfaceConfig> qWrapper = Wrappers.query();
qWrapper.eq("if_params->>'$.memberCode'", memberCode);
qWrapper.eq("if_code", "kqpay");
List<PayInterfaceConfig> list = payInterfaceConfigService.list(qWrapper);
if (list.isEmpty()) {
throw new BizException("没找到配置信息");
}
KqpayIsvParams isvParams = JSON.parseObject(list.get(0).getIfParams(), KqpayIsvParams.class);
log.info("快钱回调: {}", reqParamJSON);
KqpayKit.checkResultNotify(reqParamJSON, isvParams);
return new MutablePair<>(mchApplyment.getApplyId(), reqParamJSON);
}
@Override
public MutablePair<MchApplyment, ResponseEntity> doNotify(HttpServletRequest request, Object params, MchApplyment mchApplyment) {
MchApplyment resultMch = new MchApplyment();
JSONObject bizParam = ((JSONObject) params).getJSONObject("requestBody");
String status = bizParam.getString("status");
JSONObject openResult = bizParam.getJSONObject("openResult");
JSONObject param = new JSONObject();
param.put("isReceived", "1");
ResponseEntity<JSONObject> ok = ResponseEntity.ok(param);
String comments = bizParam.getString("comments");
resultMch.setApplyErrorInfo(comments);
if ("REJECTED".equals(status)) {
resultMch.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
return new MutablePair<>(resultMch, ok);
}
if ("FINISHED".equals(status)) {
resultMch.setChannelMchNo(bizParam.getString("subMerchantId"));
resultMch.setSuccResParameter(bizParam.toString());
// 收集子商户信息
kqpayMchApplymentService.subMchColl(mchApplyment, bizParam);
return new MutablePair<>(resultMch, ok);
}
if ("TO_BE_SIGNED".equals(status) ) {
KqpayIsvParams isvParams = (KqpayIsvParams) configContextQueryService.queryIsvParams(mchApplyment);
int allowSignResult = kqpayMchApplymentService.isAllowSign(mchApplyment);
if (allowSignResult == 2) {
resultMch.setState(MchApplyment.STATE_WAIT_SIGN);
return new MutablePair<>(resultMch, ok);
}
if (allowSignResult == 1) {
kqpayMchApplymentService.signApply(mchApplyment);
resultMch = kqpayMchApplymentService.query(mchApplyment);
return new MutablePair<>(resultMch, ok);
}
}
if ("TO_BE_REAUDITED".equals(status)) {
if (openResult != null) {
resultMch.setChannelMchNo(openResult.getString("subMerchantId"));
resultMch.setSuccResParameter(openResult.toString());
kqpayMchApplymentService.subMchColl(mchApplyment, openResult);
}
resultMch.setState(MchApplyment.STATE_SUCCESS_NEED_SECOND_VERIFY);
return new MutablePair<>(resultMch, ok);
}
return new MutablePair<>(mchApplyment, ok);
}
}

View File

@@ -0,0 +1,267 @@
package com.jeequan.jeepay.thirdparty.channel.kqpay;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.entity.MchApplyment;
import com.jeequan.jeepay.core.entity.MchModifyApplyment;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.interfaces.paychannel.IIsvmchModifyApplymentService;
import com.jeequan.jeepay.core.model.applyment.KqpayApplymentInfo;
import com.jeequan.jeepay.core.model.applyment.MchModifyApplymentModel;
import com.jeequan.jeepay.core.model.params.kqpay.KqpayIsvParams;
import com.jeequan.jeepay.thirdparty.channel.kqpay.model.KqpayResp;
import com.jeequan.jeepay.thirdparty.channel.kqpay.util.KqpayConst;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.MutablePair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
@Slf4j
@Service
public class KqpayIsvmchModifyApplymentService implements IIsvmchModifyApplymentService {
@Autowired
private ConfigContextQueryService configContextQueryService;
@Override
public MutablePair<MchModifyApplyment, MchApplyment> localModifyBase(MutablePair<MchModifyApplyment, MchApplyment> mchDataPair) {
MchModifyApplyment resultModifyApplyment = new MchModifyApplyment();
MchApplyment resultMchApplyment = new MchApplyment();
MchModifyApplyment mchModifyApplyment = mchDataPair.getKey();
resultModifyApplyment.setModifyApplyId(mchModifyApplyment.getModifyApplyId());
resultModifyApplyment.setState(MchApplyment.STATE_SUCCESS);
MchApplyment mchApplyment = mchDataPair.getValue();
resultMchApplyment.setApplyId(mchApplyment.getApplyId());
String applyDetailInfo = mchModifyApplyment.getApplyDetailInfo();
MchModifyApplymentModel mchModifyData = JSON.parseObject(applyDetailInfo, MchModifyApplymentModel.class);
String originApplyDetailInfo = mchApplyment.getApplyDetailInfo();
KqpayApplymentInfo originMchModifyData = JSON.parseObject(originApplyDetailInfo, KqpayApplymentInfo.class);
resultMchApplyment.setApplyDetailInfo(JSON.toJSONString(originMchModifyData));
originMchModifyData.setMchShortName(mchModifyData.getMchShortName());
resultMchApplyment.setMchShortName(mchModifyData.getMchShortName());
return new MutablePair<>(resultModifyApplyment, resultMchApplyment);
}
@Override
public MutablePair<MchModifyApplyment, MchApplyment> localModifySettlement(MutablePair<MchModifyApplyment, MchApplyment> mchDataPair) {
MchModifyApplyment resultModifyApplyment = new MchModifyApplyment();
MchApplyment resultMchApplyment = new MchApplyment();
MchModifyApplyment mchModifyApplyment = mchDataPair.getKey();
resultModifyApplyment.setModifyApplyId(mchModifyApplyment.getModifyApplyId());
resultModifyApplyment.setState(MchApplyment.STATE_SUCCESS);
MchApplyment mchApplyment = mchDataPair.getValue();
resultMchApplyment.setApplyId(mchApplyment.getApplyId());
String applyDetailInfo = mchModifyApplyment.getApplyDetailInfo();
MchModifyApplymentModel mchModifyData = JSON.parseObject(applyDetailInfo, MchModifyApplymentModel.class);
String originApplyDetailInfo = mchApplyment.getApplyDetailInfo();
KqpayApplymentInfo originMchModifyData = JSON.parseObject(originApplyDetailInfo, KqpayApplymentInfo.class);
resultMchApplyment.setApplyDetailInfo(JSON.toJSONString(originMchModifyData));
originMchModifyData.setSettAccountType(mchModifyData.getSettAccountType());
originMchModifyData.setIsUncrpSett(mchModifyData.getIllegal());
originMchModifyData.setSettAccountLicenseImg(mchModifyData.getSettAccountLicenseImg());
originMchModifyData.setSettAccountName(mchModifyData.getSettAccountName());
originMchModifyData.setSettAccountNo(mchModifyData.getSettAccountNo());
originMchModifyData.setSettAccountBankName(mchModifyData.getSettAccountBankName());
originMchModifyData.setPersonalInformationAttorney(mchModifyData.getNonLegSettleAuthPic());
originMchModifyData.setSettAccountIdcardNo(mchModifyData.getSettAccountIdcardNo());
originMchModifyData.setSettAccountIdcard1Img(mchModifyData.getSettAccountIdcard1Img());
originMchModifyData.setSettAccountIdcard2Img(mchModifyData.getSettAccountIdcard2Img());
originMchModifyData.setSettAccountIdcardEffectBegin(mchModifyData.getSettAccountIdcardEffectBegin());
originMchModifyData.setSettAccountIdcardEffectEnd(mchModifyData.getSettAccountIdcardEffectEnd());
return new MutablePair<>(resultModifyApplyment, resultMchApplyment);
}
@Override
public MutablePair<MchModifyApplyment, MchApplyment> localModifyRate(MutablePair<MchModifyApplyment, MchApplyment> mchDataPair) {
return null;
}
@Override
public MutablePair<MchModifyApplyment, MchApplyment> syncChannelModifyBase(MutablePair<MchModifyApplyment, MchApplyment> mchDataPair) {
throw new BizException("暂不支持该操作");
// MchModifyApplyment modifyApplyment = mchDataPair.getLeft();
// MchApplyment mchApplyment = mchDataPair.getRight();
//
// KqpayApplymentInfo kqpayApplymentInfo = JSON.parseObject(mchApplyment.getApplyDetailInfo(), KqpayApplymentInfo.class);
//
// JSONObject param = new JSONObject();
// param.fluentPut("operator", "admin")
// .fluentPut("subBillAccount", kqpayApplymentInfo.getContactEmail());
//
// // 套餐信息
// JSONObject packageInfo = new JSONObject();
// param.put("packageInfo", packageInfo);
// packageInfo.put("packageCode", KqpayConst.Package.COMPANY_BASE_EDIT.getCode());
//
// String openAccountWillingnessLetter = "https://syb-jq-public.oss-cn-hangzhou.aliyuncs.com/applyment/38835122-55b6-48be-884e-35e4aa856b99.jpg";
//
// KqpayIsvParams isvParams = (KqpayIsvParams) configContextQueryService.queryIsvParams(mchApplyment);
// String openAccountWillingnessLetterFssId = KqpayKit.uploadRequest(openAccountWillingnessLetter, isvParams);
//
// List<Object> additionList = new ArrayList<>();
// param.put("additionList", additionList);
// Map<String, Object> addition = new HashMap<>();
// additionList.add(addition);
// addition.put("additionFssId", openAccountWillingnessLetterFssId);
// addition.put("additionFileType", "OPEN_ACCOUNT_WILLINGNESS_LETTER");
// addition.put("additionRemark", "开户意愿确认函");
// addition.put("extName", "jpg");
//
// KqpayResp kqpayResp = KqpayKit.applymentRequest(KqpayConst.ReqMethod.BS005, isvParams, param);
//
// return mchDataPair;
}
@Override
public MutablePair<MchModifyApplyment, MchApplyment> syncChannelModifySettlement(MutablePair<MchModifyApplyment, MchApplyment> mchDataPair) {
MchModifyApplyment resultModifyApplyment = new MchModifyApplyment();
MchApplyment resultMchApplyment = new MchApplyment();
MchModifyApplyment modifyApplyment = mchDataPair.getLeft();
resultModifyApplyment.setModifyApplyId(modifyApplyment.getModifyApplyId());
MchApplyment mchApplyment = mchDataPair.getRight();
resultMchApplyment.setApplyId(mchApplyment.getApplyId());
KqpayIsvParams isvParams = (KqpayIsvParams) configContextQueryService.queryIsvParams(mchApplyment);
KqpayApplymentInfo kqpayApplymentInfo = JSON.parseObject(mchApplyment.getApplyDetailInfo(), KqpayApplymentInfo.class);
MchModifyApplymentModel modifyApplyBiz = JSON.parseObject(modifyApplyment.getApplyDetailInfo(), MchModifyApplymentModel.class);
JSONObject param = new JSONObject();
param.fluentPut("operator", "admin")
.fluentPut("subBillAccount", kqpayApplymentInfo.getContactEmail());
// 套餐信息
JSONObject packageInfo = new JSONObject();
param.put("packageInfo", packageInfo);
packageInfo.put("packageCode", KqpayConst.Package.COMPANY_BASE_EDIT.getCode());
JSONObject packageParam = new JSONObject();
packageInfo.put("packageParameter", packageParam);
// 定向付款信息域
JSONObject settlementInfo = new JSONObject();
packageParam.put("packageParam", settlementInfo);
settlementInfo.put("bankMobilePhone", modifyApplyBiz.getPhone());
if (modifyApplyBiz.getSettAccountType().equals("B")) {
settlementInfo.put("accountType", "0");
} else {
if (modifyApplyBiz.getIllegal().equals("Y")) {
settlementInfo.put("accountType", "2");
} else {
settlementInfo.put("accountType", "1");
}
}
settlementInfo.put("accountNo", modifyApplyBiz.getSettAccountNo());
settlementInfo.put("accountName", modifyApplyBiz.getSettAccountName());
settlementInfo.put("bankName", modifyApplyBiz.getSettAccountBankName());
settlementInfo.put("bankBranch", modifyApplyBiz.getSettAccountBankBranchName());
Assert.notNull(modifyApplyBiz.getSettAccountBankBranchAreaCode(), "支行地区编码信息不能为空");
Assert.isTrue(modifyApplyBiz.getSettAccountBankBranchAreaCode().size() >= 2, "支行地区编码信息不能为空");
settlementInfo.put("areaCode", modifyApplyBiz.getSettAccountBankBranchAreaCode().getString(1));
if (kqpayApplymentInfo.getMerchantType() != MchApplyment.MERCHANT_TYPE_PERSONAL) {
JSONObject accOpenInfo = new JSONObject();
packageParam.put("accOpenInfo", accOpenInfo);
String accOpenFssId = KqpayKit.uploadRequest(modifyApplyBiz.getAccOpenCertImg(), isvParams);
}
if (modifyApplyBiz.getIllegal().equals("Y")) {
JSONObject authorizerInfo = new JSONObject();
packageParam.put("authorizerInfo", authorizerInfo);
String authFssId = KqpayKit.uploadRequest(modifyApplyBiz.getNonLegSettleAuthPic(), isvParams);
authorizerInfo.put("authFssId", authFssId);
String idCard1Img = KqpayKit.uploadRequest(modifyApplyBiz.getSettAccountIdcard1Img(), isvParams);
authorizerInfo.put("authNationalFssId", idCard1Img);
String idCard2Img = KqpayKit.uploadRequest(modifyApplyBiz.getSettAccountIdcard2Img(), isvParams);
authorizerInfo.put("authPersonFssId", idCard2Img);
authorizerInfo.put("beginDate", modifyApplyBiz.getSettAccountIdcardEffectBegin());
authorizerInfo.put("expireDate", modifyApplyBiz.getSettAccountIdcardEffectEnd());
authorizerInfo.put("authIdCard", modifyApplyBiz.getSettAccountIdcardNo());
authorizerInfo.put("authTel", modifyApplyBiz.getPhone());
authorizerInfo.put("authName", modifyApplyBiz.getSettAccountName());
String authHandFssId = KqpayKit.uploadRequest(modifyApplyBiz.getAuthHandImg(), isvParams);
authorizerInfo.put("authHandFssId", authHandFssId);
}
try {
KqpayResp kqpayResp = KqpayKit.applymentRequest(KqpayConst.ReqMethod.BS005, isvParams, param);
JSONObject responseBody = kqpayResp.getResponseBody();
String bizResponseCode = responseBody.getString("bizResponseCode");
String bizResponseMessage = responseBody.getString("bizResponseMessage");
resultModifyApplyment.setChannelVar1(responseBody.toJSONString());
if (!KqpayConst.SUCCESS.equals(bizResponseCode)) {
resultModifyApplyment.setApplyDetailInfo("[" + bizResponseMessage + "]");
resultModifyApplyment.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
return new MutablePair<>(resultModifyApplyment, null);
}
String orderId = responseBody.getString("orderId");
resultModifyApplyment.setChannelApplyNo(orderId);
resultModifyApplyment.setState(MchApplyment.STATE_AUDITING);
return new MutablePair<>(resultModifyApplyment, null);
} catch (Exception e) {
resultModifyApplyment.setApplyDetailInfo("[" + e.getMessage() + "]");
resultModifyApplyment.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
return new MutablePair<>(resultModifyApplyment, null);
}
}
@Override
public MutablePair<MchModifyApplyment, MchApplyment> syncChannelModifyRate(MutablePair<MchModifyApplyment, MchApplyment> mchDataPair) {
return null;
}
@Override
public MutablePair<MchModifyApplyment, MchApplyment> syncChannelModifySettlementType(MutablePair<MchModifyApplyment, MchApplyment> mchDataPair) {
return null;
}
@Override
public MutablePair<MchModifyApplyment, MchApplyment> queryModifyResult(MutablePair<MchModifyApplyment, MchApplyment> mchDataPair) {
MchModifyApplyment resultMchModifyApplyment = mchDataPair.left;
MchApplyment resultMch = mchDataPair.right;
KqpayIsvParams isvParams = (KqpayIsvParams) configContextQueryService.queryIsvParams(mchDataPair.right);
JSONObject reqParam = new JSONObject();
reqParam.put("orderId", resultMchModifyApplyment.getChannelApplyNo());
KqpayResp kqpayResp = KqpayKit.applymentRequest(KqpayConst.ReqMethod.BS002, isvParams, reqParam);
JSONObject responseBody = kqpayResp.getResponseBody();
String bizResponseCode = responseBody.getString("bizResponseCode");
String bizResponseMessage = responseBody.getString("bizResponseMessage");
if (!KqpayConst.SUCCESS.equals(bizResponseCode)) {
log.info("快钱查询商户变更审核状态失败, {}", bizResponseMessage);
return new MutablePair<>();
}
String status = responseBody.getString("status");
if ("FINISHED".equals(status)) {
return localModifySettlement(mchDataPair);
}
return new MutablePair<>();
}
}

View File

@@ -0,0 +1,89 @@
package com.jeequan.jeepay.thirdparty.channel.kqpay;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.entity.MchApplyment;
import com.jeequan.jeepay.core.interfaces.paychannel.IIsvmchWxConfigService;
import com.jeequan.jeepay.core.model.applyment.ApplymentSignInfo;
import com.jeequan.jeepay.core.model.applyment.PaywayFee;
import com.jeequan.jeepay.core.model.params.kqpay.KqpayIsvParams;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
@Slf4j
public class KqpayIsvmchWxConfigService implements IIsvmchWxConfigService {
@Override
public String queryConfiguredInfo(String isvNo, String mchNo, String mchAppId, String ifCode) {
return null;
}
@Override
public ChannelRetMsg configBindAppId(String configAppId, String isvNo, String mchNo, String mchAppId, String ifCode) {
return null;
}
@Override
public ChannelRetMsg configSubscribeAppId(String configAppId, String isvNo, String mchNo, String mchAppId, String ifCode) {
return null;
}
@Override
public ChannelRetMsg applyModifyMchShortName(String mchShortName, String isvNo, String mchNo, String mchAppId, String ifCode) {
return null;
}
@Override
public ChannelRetMsg applyModifyMchAppPublicKey(String appPublicKey, String isvNo, String mchNo, String mchAppId, String ifCode) {
return null;
}
@Override
public ApplymentSignInfo wxOpenSignInfo(MchApplyment mchApplyment) {
ApplymentSignInfo result = new ApplymentSignInfo();
// 获取到 服务商配置信息
ConfigContextQueryService configContextQueryService = SpringBeansUtil.getBean(ConfigContextQueryService.class);
KqpayIsvParams isvParams = (KqpayIsvParams)configContextQueryService.queryIsvParams(mchApplyment);
// 微信开通意愿二维码地址
result.setSignUrl(isvParams.getWxOpenUrl());
result.setImgType(ApplymentSignInfo.IMG_TYPE_QRCONTENT);
result.setState("当前通道无法确定认证状态");
result.setErrInfo("");
String succResParameter = mchApplyment.getSuccResParameter();
JSONObject succRes = JSON.parseObject(succResParameter);
JSONArray alipayList = succRes.getJSONArray("wechatList");
if (alipayList.isEmpty()) {
result.setState("未报备");
} else {
JSONObject aliReportData = alipayList.getJSONObject(0);
String subMchId = aliReportData.getString("subMchId");
if (ObjectUtils.isEmpty(subMchId)) {
result.setErrInfo(aliReportData.getString("errorMsg"));
} else {
result.setChannelSubMchId(subMchId);
}
}
return result;
}
@Override
public ChannelRetMsg configPayBaseUrl(String configBaseUrl, String isvNo, String mchNo, String mchAppId, String ifCode) {
return null;
}
@Override
public ChannelRetMsg applyModifyMchRate(List<PaywayFee> paywayFeeList, String isvNo, String mchNo, String mchAppId, String ifCode) {
return null;
}
}

View File

@@ -0,0 +1,303 @@
package com.jeequan.jeepay.thirdparty.channel.kqpay;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.date.DateUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bill99.crypto.entity.Bill99CertConfig;
import com.bill99.crypto.service.CryptoService;
import com.bill99.crypto.service.processor.P7CryptoServiceProcessor;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.params.kqpay.KqpayIsvParams;
import com.jeequan.jeepay.core.model.params.kqpay.KqpayIsvsubMchParams;
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
import com.jeequan.jeepay.thirdparty.channel.kqpay.model.KqpayReqHead;
import com.jeequan.jeepay.thirdparty.channel.kqpay.model.KqpayResp;
import com.jeequan.jeepay.thirdparty.channel.kqpay.util.HttpClientCert;
import com.jeequan.jeepay.thirdparty.channel.kqpay.util.HttpsClientFactory;
import com.jeequan.jeepay.thirdparty.channel.kqpay.util.KqpayConst;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import com.jeequan.jeepay.thirdparty.util.ChannelCertConfigKitBean;
import lombok.AccessLevel;
import lombok.Cleanup;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.util.Assert;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
/**
* 快钱请求参数
*/
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class KqpayKit {
private static final CryptoService cryptoService = P7CryptoServiceProcessor.getInstance();
/**
* 快钱入网请求网关
*/
public static final String APPLYMENTS_GATE_WAY = KqpayConst.APPLYMENTS_GATE_WAY;
public static final String TRADE_GATE_WAY = KqpayConst.TRADE_GATE_WAY;
public static String getExternalRefNumber(String memberCode) {
return memberCode + DateUtil.format(new Date(), DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"));
}
public static String uploadRequest(String imgUrl, KqpayIsvParams isvParams) {
byte[] imgFileByteArray = HttpUtil.downloadBytes(imgUrl);
String base64 = Base64.encode(imgFileByteArray);
JSONObject reqParam = new JSONObject();
reqParam.put("fileBuffer", base64);
reqParam.put("fileName", "test.jpg");
return KqpayKit.applymentRequest(KqpayConst.ReqMethod.BS000, isvParams, reqParam).getResponseBody().getString("fssId");
}
public static KqpayResp applymentRequest(KqpayConst.ReqMethod reqMethod, KqpayIsvParams isvParams, JSONObject bizContent) {
String externalRefNumber = ((String) bizContent.remove("externalRefNumber"));
ConfigContextQueryService configContextQueryService = SpringBeansUtil.getBean(ConfigContextQueryService.class);
Assert.notNull(configContextQueryService, "无法获取快钱入网配置信息");
KqpayReqHead head = init(reqMethod, isvParams, externalRefNumber);
head.setVendorMemberCode(null);
try {
return reqApi(APPLYMENTS_GATE_WAY, head, bizContent, isvParams);
} catch (Exception e) {
log.info("快钱入网请求异常,", e);
throw new BizException("快钱入网请求异常, " + e.getMessage());
}
}
/**
* 初始化
*
* @return
*/
public static KqpayReqHead init(KqpayConst.ReqMethod reqMethod, KqpayIsvParams isvParams, String outFlowNo) {
initCert(isvParams);
KqpayReqHead head = new KqpayReqHead();
head.setVersion("1.0.0");
head.setVendorMemberCode(isvParams.getVendorMemberCode());
head.setMemberCode(head.getVendorMemberCode());
head.setMessageType(reqMethod.getMessageType());
if (StringUtils.isNotEmpty(outFlowNo)) {
head.setExternalRefNumber(outFlowNo);
} else {
head.setExternalRefNumber(getExternalRefNumber(isvParams.getVendorMemberCode()));
}
return head;
}
public static KqpayResp payRequest(KqpayConst.ReqMethod reqMethod, MchAppConfigContext mchAppConfigContext, JSONObject bizContent, String outFlowNo) {// 配置参数
ConfigContextQueryService configContextQueryService = SpringBeansUtil.getBean(ConfigContextQueryService.class);
Assert.notNull(configContextQueryService, "无法获取快钱入网配置信息");
KqpayIsvParams isvParams = (KqpayIsvParams) configContextQueryService.queryIsvParams(mchAppConfigContext.getMchApplyment().getIsvNo(), CS.IF_CODE.KQPAY);
KqpayReqHead head = init(reqMethod, isvParams, outFlowNo);
head.setMemberCode(bizContent.getString("kqCode"));
try {
return reqApi(TRADE_GATE_WAY, head, bizContent, isvParams);
} catch (Exception e) {
throw new BizException(e.getMessage());
}
}
/**
* 设置参数
*
* @param mchAppConfigContext
* @param bizContent
*/
public static void setPayParams(MchAppConfigContext mchAppConfigContext, JSONObject bizContent) {
KqpayIsvsubMchParams mchParams = JSON.parseObject(mchAppConfigContext.getMchApplyment().getSuccResParameter(),KqpayIsvsubMchParams.class);
bizContent.put("merchantId", mchParams.getSubMerchantId());
List<JSONObject> terminalInfos = mchParams.getTerminalInfos();
JSONObject item = terminalInfos.stream().filter(e -> "terminalQkf".equals(e.getString("type"))).findFirst().orElse(new JSONObject());
JSONArray terminalIds = item.getJSONArray("terminalIds");
bizContent.put("terminalId", terminalIds.get(0));
bizContent.put("kqCode", mchParams.getSubMemberCode());
bizContent.put("cur", "CNY");
}
public static KqpayResp reqApi(String url, KqpayReqHead head, JSONObject bizData, KqpayIsvParams isvParams) throws Exception {
JSONObject encryptBody = null;
try {
encryptBody = cryptoService.seal(bizData.toString());
} catch (Exception e) {
log.error("请求快钱,加密加签出错。", e);
}
JSONObject requestString = new JSONObject();
requestString.put("head", head);
requestString.put("requestBody", encryptBody);
log.info("快钱接口请求地址:{}", url);
log.info("快钱接口请求业务参数:{}", bizData);
log.info("快钱接口请求参数:{}", requestString);
@Cleanup CloseableHttpClient httpClient = url.startsWith("https") ? createHttpsClient(isvParams) : HttpClients.createDefault();
HttpPost httpPost = new HttpPost(url);
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(60000)
.setConnectTimeout(10000).build();
httpPost.setConfig(requestConfig);
httpPost.setEntity(new StringEntity(requestString.toJSONString(), Consts.UTF_8));
return httpClient.execute(httpPost, httpresponse -> {
int statusCode = httpresponse.getStatusLine().getStatusCode();
if (200 != statusCode) {
return null;
} else {
String entityStr;
HttpEntity entity = httpresponse.getEntity();
if (null != entity) {
entityStr = EntityUtils.toString(entity, Consts.UTF_8);
log.info("快钱请求返回待解密报文:{}", entityStr);
JSONObject respEncryptObject = JSON.parseObject(entityStr);
JSONObject respHead = respEncryptObject.getJSONObject("head");
JSONObject respEncryptBody = respEncryptObject.getJSONObject("responseBody");
String respSignedData = respEncryptBody.getString("signedData");
String respEnvelopedData = respEncryptBody.getString("envelopedData");
KqpayResp kqpayResp = new KqpayResp(respHead);
if (KqpayConst.SUCCESS.equals(respHead.getString("responseCode"))) {
String respDecryptBody = null;
try {
respDecryptBody = cryptoService.unSeal(respEnvelopedData, respSignedData);
log.info("快钱请求返回报文:{}", respDecryptBody);
} catch (Exception e) {
log.info("快钱入网解密异常", e);
}
kqpayResp.setResponseBody(JSON.parseObject(respDecryptBody));
return kqpayResp;
} else {
log.error("请求快钱出错, head报错信息 = {}", respHead.toJSONString());
throw new BizException(respHead.getString("responseTextMessage"));
}
} else {
throw new BizException("接口请求异常");
}
}
});
}
private static CloseableHttpClient createHttpsClient(KqpayIsvParams kqpayIsvParams) throws Exception {
ChannelCertConfigKitBean channelCertConfigKitBean = SpringBeansUtil.getBean(ChannelCertConfigKitBean.class);
String certFilePath = channelCertConfigKitBean.getCertFilePath(kqpayIsvParams.getSslPublicPath());
@Cleanup InputStream keystoreFileInputStream = new FileInputStream(certFilePath);
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(keystoreFileInputStream, kqpayIsvParams.getStorePwd().toCharArray());
HttpClientCert httpClientCert = new HttpClientCert();
httpClientCert.setKeyStore(keyStore);
httpClientCert.setKeyStorePwd(kqpayIsvParams.getStorePwd());
httpClientCert.setConnTimeout(60000);
httpClientCert.setSoTimeout(10000);
httpClientCert.setSSLVersion("TLSv1.2");
HttpsClientFactory factory = new HttpsClientFactory();
return factory.createSSLClient(httpClientCert);
}
/**
* 解析异步通知参数
*
* @param resNotify
* @return
*/
public static boolean checkResultNotify(JSONObject resNotify, KqpayIsvParams isvParams) {
try {
initCert(isvParams);
JSONObject requestBody = resNotify.getJSONObject("requestBody");
String signedData = requestBody.getString("signedData");
String envelopedData = requestBody.getString("envelopedData");
String data = cryptoService.unSeal(envelopedData, signedData);
resNotify.put("requestBody", JSON.parseObject(data));
return true;
} catch (Exception e) {
return false;
}
}
/**
* 解析异步通知参数
*
* @param resNotify
* @return
*/
public static boolean checkResultNotify(JSONObject resNotify, MchAppConfigContext mchAppConfigContext) {
try {
initCert(mchAppConfigContext);
JSONObject requestBody = resNotify.getJSONObject("requestBody");
String signedData = requestBody.getString("signedData");
String envelopedData = requestBody.getString("envelopedData");
String data = cryptoService.unSeal(envelopedData, signedData);
resNotify.put("requestBody", JSON.parseObject(data));
return true;
} catch (Exception e) {
return false;
}
}
/**
* 组装快钱异步通知返回参数
*
* @param notifyParams
* @return
*/
public static String resSuccess(JSONObject notifyParams) {
JSONObject responseBody = new JSONObject(1);
responseBody.put("isReceived", "1");
try {
JSONObject respEncryptBody = cryptoService.seal(responseBody.toJSONString());
JSONObject respMessage = new JSONObject();
respMessage.put("head", notifyParams.getJSONObject("head"));
respMessage.put("responseBody", respEncryptBody);
return StringEscapeUtils.unescapeJson(respMessage.toJSONString());
} catch (Exception e) {
e.printStackTrace();
}
return "error";
}
public static KqpayIsvParams initCert(MchAppConfigContext configContext) {
ConfigContextQueryService configContextQueryService = SpringBeansUtil.getBean(ConfigContextQueryService.class);
Assert.notNull(configContextQueryService, "无法获取快钱入网配置信息");
KqpayIsvParams isvParams = (KqpayIsvParams) configContextQueryService.queryIsvParams(configContext.getMchApplyment().getIsvNo(), CS.IF_CODE.KQPAY);
return initCert(isvParams);
}
public static KqpayIsvParams initCert(KqpayIsvParams isvParams) {
ChannelCertConfigKitBean channelCertConfigKitBean = SpringBeansUtil.getBean(ChannelCertConfigKitBean.class);
Assert.notNull(channelCertConfigKitBean, "无法获取快钱入网配置文件");
String bill99DefaultPublicPath = channelCertConfigKitBean.getCertFilePath(isvParams.getBill99DefaultPublicPath());
String merchantDefaultPrivatePath = channelCertConfigKitBean.getCertFilePath(isvParams.getMerchantDefaultPrivatePath());
Bill99CertConfig bill99CertConfig = new Bill99CertConfig();
bill99CertConfig.setBill99DefaultPublicPath(bill99DefaultPublicPath);
bill99CertConfig.setMerchantDefaultPrivatePassword(isvParams.getMerchantDefaultPrivatePassword());
bill99CertConfig.setMerchantDefaultPrivatePath(merchantDefaultPrivatePath);
cryptoService.setBill99CertConfig(bill99CertConfig);
return isvParams;
}
}

View File

@@ -0,0 +1,68 @@
package com.jeequan.jeepay.thirdparty.channel.kqpay;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.MchApplyment;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.model.applyment.PaywayFee;
import com.jeequan.jeepay.core.model.params.IsvParams;
import com.jeequan.jeepay.core.model.rqrs.mch.ChannelMchRq;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.model.rqrs.settle.ChannelSettleRq;
import com.jeequan.jeepay.thirdparty.channel.AbstractMchApiService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.MutablePair;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* TODO
*
* @author crystal
* @date 2023/12/4 10:47
*/
@Service
@Slf4j
public class KqpayMchApiService extends AbstractMchApiService {
@Override
public String getIfCode() {
return CS.IF_CODE.KQPAY;
}
/**
* 是否支持当前接口 false 不支持
* @return
*/
@Override
public boolean preCheck() {
return false;
}
/**
*
* @param applyment
* @param mchRq
* @return
*/
@Override
public ChannelRetMsg appidAndPath(MchApplyment applyment, ChannelMchRq mchRq) {
throw new BizException("快钱通道暂不支持线上操作方式,请联系客服报备处理");
}
/**
* 结算记录查询
* @param settleRq
* @param isvNo
* @param isvParams
* @return
*/
@Override
public List<JSONObject> querySettleInfo(ChannelSettleRq settleRq, String isvNo, IsvParams isvParams) {
return null;
}
}

View File

@@ -0,0 +1,772 @@
package com.jeequan.jeepay.thirdparty.channel.kqpay;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.converter.MchInfoConverter;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.MchApplyment;
import com.jeequan.jeepay.core.entity.MchInfo;
import com.jeequan.jeepay.core.entity.MchSubInfo;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.interfaces.paychannel.IIsvmchApplymentService;
import com.jeequan.jeepay.core.model.applyment.ApplymentBasicInfo;
import com.jeequan.jeepay.core.model.applyment.ApplymentSignInfo;
import com.jeequan.jeepay.core.model.applyment.KqpayApplymentInfo;
import com.jeequan.jeepay.core.model.applyment.PaywayFee;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.oauth2.AlipayOauth2Params;
import com.jeequan.jeepay.core.model.oauth2.WxpayOauth2Params;
import com.jeequan.jeepay.core.model.params.kqpay.KqpayIsvParams;
import com.jeequan.jeepay.db.entity.MchSubInfoEntity;
import com.jeequan.jeepay.service.impl.MchSubInfoService;
import com.jeequan.jeepay.service.impl.SysConfigService;
import com.jeequan.jeepay.thirdparty.channel.kqpay.model.KqpayResp;
import com.jeequan.jeepay.thirdparty.channel.kqpay.util.KqpayConst;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.Future;
import java.util.function.Consumer;
/**
* 快钱商户入网审批流程
* 调用BS008获取个人信息同意授权书等官方电子版数据
* 新商户进件申请成功此时调BS002返的是 审批中
* 审批完成后查询到的是 合同待签约不能直接签约需要激活快钱签约前可以调合同查询接口如果是0-待签约 才能调签约接口)
* 签约完成后调BS002 返的是 已完成,至此进件完成
*/
@Service
@Slf4j
public class KqpayMchApplymentService implements IIsvmchApplymentService<KqpayApplymentInfo> {
@Autowired
private SysConfigService sysConfigService;
@Autowired
private ConfigContextQueryService configContextQueryService;
@Autowired
private MchSubInfoService mchSubInfoService;
@Autowired
private MchInfoConverter mchInfoConverter;
/**
* 开户意愿确认, 入网前的操作
*
* @param mchApplyment
*/
public void willingnessConfirm(MchApplyment mchApplyment, KqpayApplymentInfo applymentInfo, KqpayIsvParams isvParams) {
JSONObject bizContent = new JSONObject();
bizContent.put("legalId", applymentInfo.getIdcardNo());
bizContent.put("legalName", applymentInfo.getIdcardName());
if (applymentInfo.getMerchantType() != MchApplyment.MERCHANT_TYPE_PERSONAL) {
// 营业执照信息
bizContent.put("businessLicense", applymentInfo.getLicenseNo());
bizContent.put("merchantType", "1");
if (applymentInfo.getIsUncrpSett().equals("Y")) {
bizContent.put("settleToAuth", "1");
bizContent.put("authName", applymentInfo.getSettAccountName());
bizContent.put("authIdCard", applymentInfo.getSettAccountIdcardNo());
}
} else {
bizContent.put("merchantType", "2");
}
bizContent.put("merchantName", applymentInfo.getMchFullName());
List<String> fileTypeList = new ArrayList<>();
bizContent.put("fileTypeList", fileTypeList);
fileTypeList.add("OPEN_ACCOUNT_WILLINGNESS_LETTER");
fileTypeList.add("SPE_MERCH_SQSDZRGZS");
fileTypeList.add("PERSONAL_INFORMATION_ATTORNEY");
KqpayResp kqpayResp = KqpayKit.applymentRequest(KqpayConst.ReqMethod.BS008, isvParams, bizContent);
JSONObject responseBody = kqpayResp.getResponseBody();
String bizResponseCode = responseBody.getString("bizResponseCode");
String bizResponseMessage = responseBody.getString("bizResponseMessage");
if (!KqpayConst.SUCCESS.equals(bizResponseCode)) {
log.info("快钱开户意愿确认接口调用失败, {}", bizResponseMessage);
throw new BizException("快钱开户意愿确认失败, " + bizResponseMessage);
}
JSONArray data = responseBody.getJSONArray("data");
mchApplyment.setChannelVar1(data.toJSONString());
}
/**
* 上传图片,入网时候
*
* @param uploadResultMap 上传图片返回结果
* @param imgUrl 图片链接
* @param isvParams 通道服务商配置参数
*/
public void uploadRequest(Map<String, Future<String>> uploadResultMap, String key, String imgUrl, KqpayIsvParams isvParams, Consumer<String> consumer) {
// 下载文件
if (ObjectUtils.isEmpty(imgUrl)) {
return;
}
// 改为同步请求
String fssId = KqpayKit.uploadRequest(imgUrl, isvParams);
consumer.accept(fssId);
// Future<String> submit = executorService.submit(() -> {
// String fssId = KqpayKit.uploadRequest(imgUrl, isvParams);
// consumer.accept(fssId);
// return fssId;
// });
//
// uploadResultMap.put(key, submit);
}
@Override
public com.jeequan.jeepay.core.entity.MchApplyment firstApplyment(com.jeequan.jeepay.core.entity.MchApplyment mchApplyment) {
// 直接丢到异步处理进件逻辑
mchApplyment.setState(com.jeequan.jeepay.core.entity.MchApplyment.STATE_AUDITING_WAIT);
mchApplyment.setRemainStep((byte) 1);
mchApplyment.setSettlementType(CS.SETTLEMENT_TYPE.T1);
return mchApplyment;
}
public JSONObject getApplymentParams(com.jeequan.jeepay.core.entity.MchApplyment mchApplyment, KqpayIsvParams isvParams) {
String applyDetailInfo = mchApplyment.getApplyDetailInfo();
Map<String, Future<String>> uploadResultMap = new HashMap<>();
log.info("商户信息: {}", applyDetailInfo);
if (applyDetailInfo == null || applyDetailInfo.isEmpty()) {
throw new BizException("无法获取到商户信息");
}
KqpayApplymentInfo mchInfoSrc = JSON.parseObject(applyDetailInfo, KqpayApplymentInfo.class);
willingnessConfirm(mchApplyment, mchInfoSrc, isvParams);
JSONObject mchInfoDes = new JSONObject();
mchInfoDes.put("operator", "admin");
Assert.notNull(mchInfoSrc.getContactEmail(), "缺少电子邮箱");
mchInfoDes.put("subBillAccount", mchInfoSrc.getContactEmail());
// 结算信息
mchInfoDes.put("settlementInfo", getSettlement(uploadResultMap, mchInfoSrc, isvParams));
// 主体信息
JSONObject subjectInfo = getSubjectInfo(uploadResultMap, mchInfoSrc, isvParams);
mchInfoDes.put("subjectInfo", subjectInfo);
// 经营信息
mchInfoDes.put("storeInfo", getStoreInfo(uploadResultMap, mchInfoSrc, isvParams));
// 套餐信息
mchInfoDes.put("packageInfo", getPackageInfo(mchInfoSrc, mchApplyment));
List<JSONObject> additionList = new ArrayList<>();
mchInfoDes.put("additionList", additionList);
JSONArray jsonArray = JSON.parseArray(mchApplyment.getChannelVar1());
for (int i = 0; i < jsonArray.size(); i++) {
JSONObject item = jsonArray.getJSONObject(i);
String fileName = item.getString("fileName");
String fssId = item.getString("fssId");
String fileType = item.getString("fileType");
JSONObject finalAddition = new JSONObject();
finalAddition.put("additionFssId", fssId);
finalAddition.put("additionFileType", fileType);
finalAddition.put("additionRemark", fileName);
finalAddition.put("extName", "jpg");
additionList.add(finalAddition);
}
// 开户意愿函走电子协议
// if (!ObjectUtils.isEmpty(mchInfoSrc.getOpenAccountWillingnessLetter())) {
// JSONObject finalAddition = new JSONObject();
// uploadRequest(uploadResultMap, "OPEN_ACCOUNT_WILLINGNESS_LETTER", mchInfoSrc.getOpenAccountWillingnessLetter(), isvParams, s -> {
// finalAddition.put("additionFssId", s);
// });
// finalAddition.put("additionFileType", "OPEN_ACCOUNT_WILLINGNESS_LETTER");
// finalAddition.put("additionRemark", "开户意愿确认函");
// finalAddition.put("extName", "jpg");
// additionList.add(finalAddition);
// }
// 电子协议
// if ("C".equals(mchInfoSrc.getSettAccountType())) {
// // 个人信息单独同意授权书
// JSONObject finalAddition2 = new JSONObject();
// uploadRequest(uploadResultMap, "PERSONAL_INFORMATION_ATTORNEY", mchInfoSrc.getPersonalInformationAttorney(), isvParams, s -> {
// finalAddition2.put("additionFssId", s);
// });
// finalAddition2.put("additionFileType", "PERSONAL_INFORMATION_ATTORNEY");
// finalAddition2.put("additionRemark", "个人信息单独同意授权书");
// finalAddition2.put("extName", "jpg");
// additionList.add(finalAddition2);
// }
// uploadResultMap.forEach((key, value) -> {
// try {
// value.get();
// } catch (InterruptedException | ExecutionException e) {
// log.info("上传图片异常", e);
// throw new BizException("上传图片异常");
// }
// });
return mchInfoDes;
}
/**
* 主体信息
*/
private JSONObject getSubjectInfo(Map<String, Future<String>> uploadResultMap, KqpayApplymentInfo mchInfoSrc, KqpayIsvParams isvParams) {
JSONObject subjectInfo = new JSONObject();
subjectInfo.put("merchantShortName", mchInfoSrc.getMchShortName());
if (mchInfoSrc.getMerchantType() == 1) {
subjectInfo.put("subjectType", "0");
} else if (mchInfoSrc.getMerchantType() == 2) {
subjectInfo.put("subjectType", "5");
} else {
subjectInfo.put("subjectType", "1");
}
// 主体信息——营业执照信息
if (mchInfoSrc.getMerchantType() != com.jeequan.jeepay.core.entity.MchApplyment.MERCHANT_TYPE_PERSONAL) {
subjectInfo.put("businessLicenseInfo", getBusinessLicenseInfo(uploadResultMap, mchInfoSrc, isvParams));
}
if (com.jeequan.jeepay.core.entity.MchApplyment.MERCHANT_TYPE_ENTERPRISE == mchInfoSrc.getMerchantType()) {
subjectInfo.put("accOpenInfo", getAccOpenInfo(uploadResultMap, mchInfoSrc, isvParams));
}
// 法人信息
subjectInfo.put("identityInfo", getIdentityInfo(uploadResultMap, mchInfoSrc, isvParams));
return subjectInfo;
}
private JSONObject getAccOpenInfo(Map<String, Future<String>> uploadResultMap, KqpayApplymentInfo mchInfoSrc, KqpayIsvParams isvParams) {
JSONObject accOpenInfo = new JSONObject();
Assert.notEmpty(mchInfoSrc.getCompanyAccountLicenseImg(), "对公结算, 开户许可证不能为空");
// 开户凭证
uploadRequest(uploadResultMap, "accOpenFssId", mchInfoSrc.getCompanyAccountLicenseImg(), isvParams, s -> accOpenInfo.put("accOpenFssId", s));
accOpenInfo.put("accOpenCode", mchInfoSrc.getAccOpenCode());
accOpenInfo.put("accOpenPeriodBegin", mchInfoSrc.getAccOpenPeriodBegin());
accOpenInfo.put("accOpenPeriodEnd", mchInfoSrc.getAccOpenPeriodEnd());
return accOpenInfo;
}
private JSONObject getBusinessLicenseInfo(Map<String, Future<String>> uploadResultMap, KqpayApplymentInfo mchInfoSrc, KqpayIsvParams isvParams) {
JSONObject businessLicenseInfo = new JSONObject();
businessLicenseInfo.put("businessRegno", mchInfoSrc.getLicenseNo());
businessLicenseInfo.put("merchantName", mchInfoSrc.getMchFullName());
businessLicenseInfo.put("legalName", mchInfoSrc.getIdcardName());
uploadRequest(uploadResultMap, "fssId", mchInfoSrc.getLicenseImg(), isvParams, s -> businessLicenseInfo.put("fssId", s));
businessLicenseInfo.put("constraintBusiness", mchInfoSrc.getLicenseScope());
businessLicenseInfo.put("areaCode", mchInfoSrc.getAreaCode().getString(2));
businessLicenseInfo.put("address", mchInfoSrc.getLicenseAddress());
businessLicenseInfo.put("handleDate", mchInfoSrc.getLicenseEffectBegin());
if (ApplymentBasicInfo.DATE_FOREVER_VAL.equals(mchInfoSrc.getLicenseEffectEnd())) {
businessLicenseInfo.put("cancelDate", "9999-12-31");
} else {
businessLicenseInfo.put("cancelDate", mchInfoSrc.getLicenseEffectEnd());
}
return businessLicenseInfo;
}
/**
* 经营信息
*/
private JSONObject getStoreInfo(Map<String, Future<String>> uploadResultMap, KqpayApplymentInfo mchInfoSrc, KqpayIsvParams isvParams) {
JSONObject storeInfo = new JSONObject();
storeInfo.put("storeName", mchInfoSrc.getMchFullName());
storeInfo.put("areaCode", mchInfoSrc.getAreaCode().getString(2));
storeInfo.put("address", mchInfoSrc.getAddress());
storeInfo.put("outStoreName", mchInfoSrc.getMchShortName());
// 经营图片
uploadRequest(uploadResultMap, "headFssId", mchInfoSrc.getStoreOuterImg(), isvParams, s -> storeInfo.put("headFssId", s));
uploadRequest(uploadResultMap, "cashierFssId", mchInfoSrc.getStoreCashierImg(), isvParams, s -> storeInfo.put("cashierFssId", s));
uploadRequest(uploadResultMap, "inDoorFssId", mchInfoSrc.getStoreInnerImg(), isvParams, s -> storeInfo.put("inDoorFssId", s));
// String cashierFssId = uploadRequest(mchInfoSrc.getStoreOuterImg(), mchApplyment.getIsvNo());
// storeInfo.put("headFssId", cashierFssId);
// String cashierFssId = uploadRequest(mchInfoSrc.getStoreCashierImg(), mchApplyment.getIsvNo());
// storeInfo.put("cashierFssId", cashierFssId);
// String inDoorFssId = uploadRequest(mchInfoSrc.getStoreInnerImg(), mchApplyment.getIsvNo());
// storeInfo.put("inDoorFssId", inDoorFssId);
return storeInfo;
}
/**
* 法人信息
*/
private JSONObject getIdentityInfo(Map<String, Future<String>> uploadResultMap, KqpayApplymentInfo mchInfoSrc, KqpayIsvParams isvParams) {
JSONObject identityInfo = new JSONObject();
identityInfo.put("telephone", mchInfoSrc.getContactPhone());
identityInfo.put("idType", "1");
if (mchInfoSrc.getMerchantType() == com.jeequan.jeepay.core.entity.MchApplyment.MERCHANT_TYPE_PERSONAL) {
identityInfo.put("merchantName", mchInfoSrc.getAddress() + mchInfoSrc.getIdcardName() + mchInfoSrc.getMchShortName());
identityInfo.put("address", mchInfoSrc.getAddress());
identityInfo.put("areaCode", mchInfoSrc.getAreaCode().getString(2));
identityInfo.put("constraintBusiness", mchInfoSrc.getLicenseScope());
}
JSONObject idCardInfo = new JSONObject();
identityInfo.put("idCardInfo", idCardInfo);
idCardInfo.put("idCardName", mchInfoSrc.getIdcardName());
idCardInfo.put("idCardNumber", mchInfoSrc.getIdcardNo());
idCardInfo.put("beginDate", mchInfoSrc.getIdcardEffectBegin());
if (ApplymentBasicInfo.DATE_FOREVER_VAL.equals(mchInfoSrc.getIdcardEffectEnd())) {
idCardInfo.put("expireDate", "9999-12-31");
} else {
idCardInfo.put("expireDate", mchInfoSrc.getIdcardEffectEnd());
}
// 法人身份证图片
uploadRequest(uploadResultMap, "cardPersonFssId", mchInfoSrc.getIdcard1Img(), isvParams, s -> idCardInfo.put("cardPersonFssId", s));
uploadRequest(uploadResultMap, "cardNationalFssId", mchInfoSrc.getIdcard2Img(), isvParams, s -> idCardInfo.put("cardNationalFssId", s));
// String cardPersonFssId = uploadRequest(mchInfoSrc.getIdcard1Img(), mchApplyment.getIsvNo());
// idCardInfo.put("cardPersonFssId", cardPersonFssId);
// String cardNationalFssId = uploadRequest(mchInfoSrc.getIdcard2Img(), mchApplyment.getIsvNo());
// idCardInfo.put("cardNationalFssId", cardNationalFssId);
if ("Y".equals(mchInfoSrc.getIsUncrpSett())) {
JSONObject authorizerInfo = new JSONObject();
identityInfo.put("authorizerInfo", authorizerInfo);
authorizerInfo.put("authName", mchInfoSrc.getSettAccountName());
authorizerInfo.put("authIdCard", mchInfoSrc.getSettAccountIdcardNo());
authorizerInfo.put("beginDate", mchInfoSrc.getSettAccountIdcardEffectBegin());
if (ApplymentBasicInfo.DATE_FOREVER_VAL.equals(mchInfoSrc.getSettAccountIdcardEffectEnd())) {
authorizerInfo.put("expireDate", "9999-12-31");
} else {
authorizerInfo.put("expireDate", mchInfoSrc.getSettAccountIdcardEffectEnd());
}
authorizerInfo.put("authName", mchInfoSrc.getSettAccountName());
// 上传结算人身份证
uploadRequest(uploadResultMap, "authPersonFssId", mchInfoSrc.getSettAccountIdcard1Img(), isvParams, s -> authorizerInfo.put("authPersonFssId", s));
uploadRequest(uploadResultMap, "authNationalFssId", mchInfoSrc.getSettAccountIdcard2Img(), isvParams, s -> authorizerInfo.put("authNationalFssId", s));
// String authPersonFssId = uploadRequest(mchInfoSrc.getSettAccountIdcard1Img(), mchApplyment.getIsvNo());
// authorizerInfo.put("cardPersonFssId", authPersonFssId);
// String authNationalFssId = uploadRequest(mchInfoSrc.getSettAccountIdcard2Img(), mchApplyment.getIsvNo());
// authorizerInfo.put("cardNationalFssId", authNationalFssId);
authorizerInfo.put("authTel", mchInfoSrc.getBankMobile());
// 授权证明
uploadRequest(uploadResultMap, "authFssId", mchInfoSrc.getPersonalInformationAttorney(), isvParams, s -> authorizerInfo.put("authFssId", s));
}
return identityInfo;
}
private JSONObject getSettlement(Map<String, Future<String>> uploadResultMap, KqpayApplymentInfo mchInfoSrc, KqpayIsvParams isvParams) {
JSONObject settlementInfo = new JSONObject();
settlementInfo.put("settlePan", mchInfoSrc.getSettAccountNo());
settlementInfo.put("bankName", mchInfoSrc.getSettAccountBankName());
settlementInfo.put("branchName", mchInfoSrc.getSettAccountBankBranchName());
settlementInfo.put("areaCode", mchInfoSrc.getSettAccountBankBranchAreaCode().getString(2));
if ("Y".equals(mchInfoSrc.getIsUncrpSett())) {
// 非法人结算
settlementInfo.put("accountName", mchInfoSrc.getSettAccountName());
settlementInfo.put("bankMobilePhone", mchInfoSrc.getBankMobile());
} else {
settlementInfo.put("bankMobilePhone", mchInfoSrc.getLegalPersonPhone());
String accountName = "C".equals(mchInfoSrc.getSettAccountType()) ? mchInfoSrc.getIdcardName() : mchInfoSrc.getMchFullName();
settlementInfo.put("accountName", accountName);
}
if ("C".equals(mchInfoSrc.getSettAccountType())) {
if ("Y".equals(mchInfoSrc.getIsUncrpSett())) {
// 授权人结算
settlementInfo.put("settleAcctType", "2");
} else {
// 法人对私结算
settlementInfo.put("settleAcctType", "1");
}
// 对私上传结算卡
String cardFssId = KqpayKit.uploadRequest(mchInfoSrc.getSettAccountLicenseImg(), isvParams);
settlementInfo.put("cardFssId", cardFssId);
// uploadRequest(uploadResultMap, "cardFssId", mchInfoSrc.getSettAccountLicenseImg(), isvParams, s -> settlementInfo.put("cardFssId", s));
} else {
// 对公,开户许可证在主体信息中已经上传
settlementInfo.put("settleAcctType", "0");
}
return settlementInfo;
}
/**
* 【微信】
* 微信商户号IDwechatMchID1502807381快钱固定值
* 微信公众号IDwechatAppIDwxc81c4ae33fe525f0快钱固定值
* 微信渠道号IDwechatChannelID销售前缘申请提供示例178354903
* 微信关联appIdwechatSubAppId商户微信应用id示例wx9a9e46c300ca733e
* 微信支付目录(wechatJsapi)商户支付目录地址示例https://www.99bill.com/
* 【支付宝】
* 支付宝APPIDaliAppID1266000048120000 (快钱固定值)
* 支付宝PIDaliSource2088821475893827快钱固定值
* 支付宝SOURCEaliPid2088821475893827可以使用快钱的SOURCE或商户自己的
*/
private JSONObject getPackageInfo(KqpayApplymentInfo kqpayApplymentInfo, com.jeequan.jeepay.core.entity.MchApplyment mchApplyment) {
Byte merchantType = kqpayApplymentInfo.getMerchantType();
JSONObject packInfo = new JSONObject();
KqpayConst.Package usePackage = merchantType == 1 ? KqpayConst.Package.MICRO : KqpayConst.Package.COMPANY_AUTO_ACTIVATION;
packInfo.put("packageCode", usePackage.getCode());
JSONObject parameter = new JSONObject();
packInfo.put("packageParameter", parameter);
parameter.put("mcc", kqpayApplymentInfo.getMccCode());
BigDecimal bigDecimal100 = new BigDecimal("100");
List<PaywayFee> paywayFeeList = kqpayApplymentInfo.getPaywayFeeList();
if (CollUtil.isEmpty(paywayFeeList)) {
throw new BizException("缺少商户费率信息");
}
paywayFeeList.forEach(item -> {
if (item.getWayCode().equals(CS.PAY_WAY_CODE.ALI_JSAPI)) {
parameter.put("zhifubaoRate", item.getFeeRate().multiply(bigDecimal100).stripTrailingZeros().toPlainString());
}
if (item.getWayCode().equals(CS.PAY_WAY_CODE.ALI_JSAPI)) {
parameter.put("weixinRate", item.getFeeRate().multiply(bigDecimal100).stripTrailingZeros().toPlainString());
}
if (item.getWayCode().equals(CS.PAY_WAY_CODE.YSF_JSAPI)) {
String feeType = item.getFeeType();
Long maxFee = item.getMaxFee();
// 借记卡费率上限
parameter.put("cuDebitMaxFee", BigDecimal.valueOf(maxFee).divide(bigDecimal100, 2, RoundingMode.DOWN).stripTrailingZeros().toPlainString());
if (PaywayFee.FEE_TYPE_LEVEL.equals(feeType)) {
// 分级
item.getLevelList().forEach(levelItem -> {
if (Objects.equals(levelItem.getMinFee(), 100000L)) {
// 银联借记卡大额费率
BigDecimal feeRate = levelItem.getFeeRate();
parameter.put("cuRateDebit", feeRate.multiply(bigDecimal100).stripTrailingZeros().toPlainString());
}
});
// 贷记卡, 不支持小微
item.getCreditCardPaywayFee().getLevelList().forEach(levelItem -> {
BigDecimal feeRate = levelItem.getFeeRate();
if (Objects.equals(levelItem.getMinFee(), 100000L)) {
// 银联贷记卡大额费率
parameter.put("cuRateCredit", feeRate.multiply(bigDecimal100).stripTrailingZeros().toPlainString());
} else {
// 银联贷记卡小额费率
parameter.put("smallDiscountRate", feeRate.multiply(bigDecimal100).stripTrailingZeros().toPlainString());
}
});
}
}
// 线上费率
if (item.getWayCode().equals(CS.PAY_WAY_CODE.SCAN)) {
BigDecimal feeRate = item.getFeeRate();
parameter.put("zhifubaoRate", feeRate.multiply(bigDecimal100).stripTrailingZeros().toPlainString());
parameter.put("weixinRate", feeRate.multiply(bigDecimal100).stripTrailingZeros().toPlainString());
// 银联借记卡大额费率
parameter.put("cuRateDebit", feeRate.multiply(bigDecimal100).stripTrailingZeros().toPlainString());
// 小额优惠费率
parameter.put("smallDiscountRate", feeRate.multiply(bigDecimal100).stripTrailingZeros().toPlainString());
//
parameter.put("cuRateCredit", feeRate.multiply(bigDecimal100).stripTrailingZeros().toPlainString());
parameter.put("cuDebitMaxFee", "20.0");
}
});
// 轻快付
parameter.put("terminalQkf", "1");
parameter.put("isWechatAppletPay", "1");
String paySiteUrl = sysConfigService.getDBApplicationConfig().getPaySiteUrl();
;
if (paySiteUrl == null || paySiteUrl.isEmpty()) {
throw new BizException("未获取到支付网关信息");
}
// 微信配置
WxpayOauth2Params wxpayOauth2Params = (WxpayOauth2Params) configContextQueryService.queryIsvOauth2Params(mchApplyment.getIsvNo(), CS.IF_CODE.WXPAY);
if (wxpayOauth2Params == null) {
throw new BizException("服务商未配置微信oauth信息");
}
parameter.put("wechatAppID", "wxc81c4ae33fe525f0");
parameter.put("wechatMchID", "1502807381");
parameter.put("wechatChannelID", "191695524");
String liteAppId = wxpayOauth2Params.getLiteAppId();
if (ObjectUtils.isEmpty(liteAppId)) {
parameter.put("wechatSubAppId", wxpayOauth2Params.getAppId());
} else {
parameter.put("wechatSubAppId", wxpayOauth2Params.getAppId() + ";" + wxpayOauth2Params.getLiteAppId());
}
parameter.put("wechatJsapi", paySiteUrl + "/");
// 支付宝配置
AlipayOauth2Params alipayOauth2Params = (AlipayOauth2Params) configContextQueryService.queryIsvOauth2Params(mchApplyment.getIsvNo(), CS.IF_CODE.ALIPAY);
if (alipayOauth2Params == null) {
throw new BizException("服务商未配置微信oauth信息");
}
parameter.put("isAlipayAppletPay", "1");
parameter.put("aliAppID", "1266000048120000");
parameter.put("aliSource", "2088821475893827");
parameter.put("aliPid", "2088821475893827");
return packInfo;
}
@Override
public com.jeequan.jeepay.core.entity.MchApplyment rejectModify(com.jeequan.jeepay.core.entity.MchApplyment mchApplyment) {
return firstApplyment(mchApplyment);
}
@Override
public com.jeequan.jeepay.core.entity.MchApplyment replenishInfo(com.jeequan.jeepay.core.entity.MchApplyment mchApplyment) {
return null;
}
@Override
public com.jeequan.jeepay.core.entity.MchApplyment query(com.jeequan.jeepay.core.entity.MchApplyment mchApplyment) {
log.info("快钱商户入网状态查询, 商户号为{}", mchApplyment.getApplyId());
com.jeequan.jeepay.core.entity.MchApplyment result = new com.jeequan.jeepay.core.entity.MchApplyment();
result.setApplyId(mchApplyment.getApplyId());
result.setState(mchApplyment.getState());
JSONObject bizJSON = new JSONObject();
String orderId = mchApplyment.getChannelApplyNo();
bizJSON.put("orderId", orderId);
KqpayIsvParams isvParams = (KqpayIsvParams) configContextQueryService.queryIsvParams(mchApplyment);
MchAppConfigContext mchAppConfigContext = new MchAppConfigContext();
mchAppConfigContext.setMchType(MchInfo.TYPE_ISVSUB);
mchAppConfigContext.setMchApplyment(mchApplyment);
KqpayResp kqpayResp = KqpayKit.applymentRequest(KqpayConst.ReqMethod.BS002, isvParams, bizJSON);
JSONObject responseBody = kqpayResp.getResponseBody();
String bizResponseCode = responseBody.getString("bizResponseCode");
String bizResponseMessage = responseBody.getString("bizResponseMessage");
if (!KqpayConst.SUCCESS.equals(bizResponseCode)) {
log.info("快钱查询商户审核状态失败, {}", bizResponseMessage);
return mchApplyment;
}
String status = responseBody.getString("status");
String comments = responseBody.getString("comments");
JSONObject openResult = responseBody.getJSONObject("openResult");
if ("AUDITING".equals(status)) {
return result;
}
result.setApplyErrorInfo(comments);
if ("TO_BE_SIGNED".equals(status)) {
int allowSign = isAllowSign(mchApplyment);
// 待签约
if (allowSign == 1) {
// 可以签约的话,直接签约之后再查询
signApply(mchApplyment);
return query(mchApplyment);
}
// 待激活
if (allowSign == 2) {
result.setState(MchApplyment.STATE_WAIT_SIGN);
return result;
}
return result;
}
if ("TO_BE_REAUDITED".equals(status)) {
if (openResult != null) {
result.setChannelMchNo(openResult.getString("subMerchantId"));
result.setSuccResParameter(openResult.toString());
subMchColl(mchApplyment, openResult);
}
result.setState(MchApplyment.STATE_SUCCESS_NEED_SECOND_VERIFY);
return result;
}
if ("REJECTED".equals(status)) {
result.setState(com.jeequan.jeepay.core.entity.MchApplyment.STATE_REJECT_WAIT_MODIFY);
return result;
}
if ("FINISHED".equals(status)) {
if (openResult != null) {
result.setChannelMchNo(openResult.getString("subMerchantId"));
result.setSuccResParameter(openResult.toString());
subMchColl(mchApplyment, openResult);
}
result.setState(com.jeequan.jeepay.core.entity.MchApplyment.STATE_SUCCESS);
return result;
}
return result;
}
@Override
public ApplymentSignInfo signInfo(com.jeequan.jeepay.core.entity.MchApplyment mchApplyment) {
return null;
}
@Override
public com.jeequan.jeepay.core.entity.MchApplyment signApply(com.jeequan.jeepay.core.entity.MchApplyment mchApplyment) {
String orderId = mchApplyment.getChannelVar1();
JSONObject param = new JSONObject();
param.put("orderId", mchApplyment.getChannelApplyNo());
KqpayIsvParams isvParams = (KqpayIsvParams) configContextQueryService.queryIsvParams(mchApplyment);
KqpayResp kqpayResp = KqpayKit.applymentRequest(KqpayConst.ReqMethod.BS004, isvParams, param);
JSONObject responseBody = kqpayResp.getResponseBody();
String bizResponseCode = responseBody.getString("bizResponseCode");
String bizResponseMessage = responseBody.getString("bizResponseMessage");
if (!KqpayConst.SUCCESS.equals(bizResponseCode)) {
log.info("发起签约失败, {}", bizResponseMessage);
throw new BizException(bizResponseMessage);
}
String authStatus = responseBody.getString("authStatus");
if (!"1".equals(authStatus)) {
throw new BizException("签约失败");
} else {
mchApplyment.setState(com.jeequan.jeepay.core.entity.MchApplyment.STATE_FINISH_SIGN);
}
return mchApplyment;
}
/**
*
* @param mchApplyment
* @return 返回1可以使用接口签约2需要快钱官网手动签约
*/
public int isAllowSign(com.jeequan.jeepay.core.entity.MchApplyment mchApplyment) {
String orderId = mchApplyment.getChannelApplyNo();
JSONObject param = new JSONObject();
param.put("orderId", orderId);
MchAppConfigContext mchAppConfigContext = new MchAppConfigContext();
mchAppConfigContext.setMchType(MchInfo.TYPE_ISVSUB);
mchAppConfigContext.setMchApplyment(mchApplyment);
KqpayIsvParams isvParams = (KqpayIsvParams) configContextQueryService.queryIsvParams(mchApplyment);
KqpayResp kqpayResp = KqpayKit.applymentRequest(KqpayConst.ReqMethod.BS003, isvParams, param);
JSONObject responseBody = kqpayResp.getResponseBody();
String bizResponseCode = responseBody.getString("bizResponseCode");
String bizResponseMessage = responseBody.getString("bizResponseMessage");
if (!KqpayConst.SUCCESS.equals(bizResponseCode)) {
//
log.info("查询签约状态失败, {}", bizResponseMessage);
return 0;
}
JSONArray econtractList = responseBody.getJSONArray("econtractList");
for (int i = 0; i < econtractList.size(); i++) {
JSONObject econtract = econtractList.getJSONObject(i);
/*
* 0待签约
* 1签约成功
* 2签约拒绝
* 3快钱账户待激活
* 4合同生成待转 PDF
* 6签约中
* 7签约失败
* -1未生成合同
*/
if (econtract.getString("authStatus").equals("0")) {
return 1;
}
if (econtract.getString("authStatus").equals("3")) {
return 3;
}
}
return 0;
}
@Override
public void subMchColl(com.jeequan.jeepay.core.entity.MchApplyment mchApplyment, Object openResult1) {
JSONObject openResult = (JSONObject) openResult1;
String subMerchantId = openResult.getString("subMerchantId");
List<MchSubInfoEntity> mchSubInfoEntityList = new ArrayList<>();
JSONArray alipayList = openResult.getJSONArray("alipayList");
if (!CollUtil.isEmpty(alipayList)) {
getByOpenResultItem(mchApplyment.getApplyId(), subMerchantId, "ZFB", alipayList);
}
JSONArray wechatList = openResult.getJSONArray("wechatList");
if (!CollUtil.isEmpty(wechatList)) {
getByOpenResultItem(mchApplyment.getApplyId(), subMerchantId, "WX", wechatList);
}
}
public void getByOpenResultItem(String mchApplyId, String channelMchNo, String subChannelType, JSONArray subMchArr) {
List<MchSubInfoEntity> mchSubInfoEntityList = new ArrayList<>();
for (int i = 0; i < subMchArr.size(); i++) {
JSONObject openResultItem = subMchArr.getJSONObject(i);
MchSubInfoEntity entity = new MchSubInfoEntity();
entity.setChannelMchNo(channelMchNo);
entity.setRemark(openResultItem.getString("errorMsg"));
entity.setSubMchId(openResultItem.getString("subMchId"));
entity.setStatus(openResultItem.getString("status"));
entity.setSubMchType(subChannelType);
entity.setSubMchWay("KQ");
entity.setMchApplyId(mchApplyId);
entity.setMainUse(1);
entity.setAuthStatus(MchSubInfo.AUTH_STATUS_UNKNOWN);
try {
mchSubInfoService.saveBatch(mchSubInfoEntityList);
} catch (DuplicateKeyException e) {
log.info("快钱子商户信息已存在");
}
}
}
@Override
public void subMchColl(com.jeequan.jeepay.core.entity.MchApplyment mchApplyment) {
query(mchApplyment);
}
}

View File

@@ -0,0 +1,51 @@
package com.jeequan.jeepay.thirdparty.channel.kqpay;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.interfaces.paychannel.IPayOrderQueryService;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.thirdparty.channel.kqpay.model.KqpayOrderStatus;
import com.jeequan.jeepay.thirdparty.channel.kqpay.model.KqpayResp;
import com.jeequan.jeepay.thirdparty.channel.kqpay.util.KqpayConst;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 查询订单 快钱支付
*
* @author xiaoyu
*
* @date 2022/4/15 14:29
*/
@Service
@Slf4j
public class KqpayPayOrderQueryService implements IPayOrderQueryService {
@Override
public ChannelRetMsg query(PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception {
JSONObject bizContent = new JSONObject();
KqpayKit.setPayParams(mchAppConfigContext,bizContent);
bizContent.put("idOrderCtrl",payOrder.getChannelOrderNo());
KqpayResp resp = KqpayKit.payRequest(KqpayConst.ReqMethod.A7006,mchAppConfigContext,bizContent,payOrder.getPayOrderId());
JSONObject responseBody = resp.getResponseBody();
KqpayOrderStatus txnStatus = KqpayOrderStatus.getVal(responseBody.getString("txnStatus"));
String transNo = responseBody.getString("idOrderCtrl");
String channelTradeNo = responseBody.getString("channelTradeNo");
String channelSendSn = responseBody.getString("idTxnCtrl");
String bizResponseCode = responseBody.getString("bizResponseCode");
String bizResponseMessage = responseBody.getString("bizResponseMessage");
ChannelRetMsg retMsg = ChannelRetMsg.confirmSuccess(transNo, channelTradeNo, channelSendSn,null);
retMsg.setChannelUserId(responseBody.getString("thirdPartyBuyerId"));
switch (txnStatus){
case PROCESS:
return ChannelRetMsg.waiting();
case FAIL:
return ChannelRetMsg.confirmFail(bizResponseCode,bizResponseMessage);
case CORRECT:
return ChannelRetMsg.waiting();
default:
return retMsg;
}
}
}

Some files were not shown because too many files have changed in this diff Show More