添加转账API接口的实现;
This commit is contained in:
parent
267d5dfb80
commit
3ad69e9a29
|
|
@ -365,6 +365,43 @@ CREATE TABLE `t_refund_order` (
|
|||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='退款订单表';
|
||||
|
||||
|
||||
-- 转账订单表
|
||||
DROP TABLE IF EXISTS t_transfer_order;
|
||||
CREATE TABLE `t_transfer_order` (
|
||||
`transfer_id` VARCHAR(32) NOT NULL COMMENT '转账订单号',
|
||||
`mch_no` VARCHAR(64) NOT NULL COMMENT '商户号',
|
||||
`isv_no` VARCHAR(64) COMMENT '服务商号',
|
||||
`app_id` VARCHAR(64) NOT NULL COMMENT '应用ID',
|
||||
`mch_name` VARCHAR(30) NOT NULL COMMENT '商户名称',
|
||||
`mch_type` TINYINT(6) NOT NULL COMMENT '类型: 1-普通商户, 2-特约商户(服务商模式)',
|
||||
`mch_order_no` VARCHAR(64) NOT NULL COMMENT '商户订单号',
|
||||
`if_code` VARCHAR(20) NOT NULL COMMENT '支付接口代码',
|
||||
`entry_type` VARCHAR(20) NOT NULL COMMENT '入账方式: WX_CASH-微信零钱; ALIPAY_CASH-支付宝转账; BANK_CARD-银行卡',
|
||||
`amount` BIGINT(20) NOT NULL COMMENT '转账金额,单位分',
|
||||
`currency` VARCHAR(3) NOT NULL DEFAULT 'cny' COMMENT '三位货币代码,人民币:cny',
|
||||
`account_no` VARCHAR(64) NOT NULL COMMENT '收款账号',
|
||||
`account_name` VARCHAR(64) COMMENT '收款人姓名',
|
||||
`bank_name` VARCHAR(32) COMMENT '收款人开户行名称',
|
||||
`transfer_desc` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '转账备注信息',
|
||||
`client_ip` VARCHAR(32) DEFAULT NULL COMMENT '客户端IP',
|
||||
`state` TINYINT(6) NOT NULL DEFAULT '0' COMMENT '支付状态: 0-订单生成, 1-转账中, 2-转账成功, 3-转账失败, 4-订单关闭',
|
||||
`notify_state` TINYINT(6) NOT NULL DEFAULT '0' COMMENT '向下游回调状态, 0-未发送, 1-已发送',
|
||||
`channel_extra` VARCHAR(512) DEFAULT NULL COMMENT '特定渠道发起额外参数',
|
||||
`channel_order_no` VARCHAR(64) DEFAULT NULL COMMENT '渠道订单号',
|
||||
`err_code` VARCHAR(128) DEFAULT NULL COMMENT '渠道支付错误码',
|
||||
`err_msg` VARCHAR(256) DEFAULT NULL COMMENT '渠道支付错误描述',
|
||||
`ext_param` VARCHAR(128) DEFAULT NULL COMMENT '商户扩展参数',
|
||||
`notify_url` VARCHAR(128) NOT NULL default '' COMMENT '异步通知地址',
|
||||
`success_time` DATETIME DEFAULT NULL COMMENT '转账成功时间',
|
||||
`created_at` TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '创建时间',
|
||||
`updated_at` TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6) COMMENT '更新时间',
|
||||
PRIMARY KEY (`transfer_id`),
|
||||
UNIQUE KEY `Uni_MchNo_MchOrderNo` (`mch_no`, `mch_order_no`),
|
||||
INDEX(`created_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='转账订单表';
|
||||
|
||||
|
||||
|
||||
##### ↑↑↑↑↑↑↑↑↑↑ 表结构DDL ↑↑↑↑↑↑↑↑↑↑ #####
|
||||
|
||||
##### ↓↓↓↓↓↓↓↓↓↓ 初始化DML ↓↓↓↓↓↓↓↓↓↓ #####
|
||||
|
|
|
|||
|
|
@ -41,9 +41,10 @@ import java.util.Date;
|
|||
@TableName("t_mch_notify_record")
|
||||
public class MchNotifyRecord extends BaseModel implements Serializable {
|
||||
|
||||
//订单类型:1-支付,2-退款
|
||||
//订单类型:1-支付,2-退款, 3-转账
|
||||
public static final byte TYPE_PAY_ORDER = 1;
|
||||
public static final byte TYPE_REFUND_ORDER = 2;
|
||||
public static final byte TYPE_TRANSFER_ORDER = 3;
|
||||
|
||||
//通知状态
|
||||
public static final byte STATE_ING = 1;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jeequan.jeepay.core.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 转账订单表
|
||||
* </p>
|
||||
*
|
||||
* @author [mybatis plus generator]
|
||||
* @since 2021-08-11
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@Accessors(chain = true)
|
||||
@TableName("t_transfer_order")
|
||||
public class TransferOrder implements Serializable {
|
||||
|
||||
/** 入账方式 **/
|
||||
public static final String ENTRY_WX_CASH = "WX_CASH";
|
||||
public static final String ENTRY_ALIPAY_CASH = "ALIPAY_CASH";
|
||||
public static final String ENTRY_BANK_CARD = "BANK_CARD";
|
||||
|
||||
public static final byte STATE_INIT = 0; //订单生成
|
||||
public static final byte STATE_ING = 1; //转账中
|
||||
public static final byte STATE_SUCCESS = 2; //转账成功
|
||||
public static final byte STATE_FAIL = 3; //转账失败
|
||||
public static final byte STATE_CLOSED = 4; //转账关闭
|
||||
|
||||
|
||||
public static final LambdaQueryWrapper<TransferOrder> gw(){
|
||||
return new LambdaQueryWrapper<>();
|
||||
}
|
||||
|
||||
|
||||
private static final long serialVersionUID=1L;
|
||||
|
||||
/**
|
||||
* 转账订单号
|
||||
*/
|
||||
private String transferId;
|
||||
|
||||
/**
|
||||
* 商户号
|
||||
*/
|
||||
private String mchNo;
|
||||
|
||||
/**
|
||||
* 服务商号
|
||||
*/
|
||||
private String isvNo;
|
||||
|
||||
/**
|
||||
* 应用ID
|
||||
*/
|
||||
private String appId;
|
||||
|
||||
/**
|
||||
* 商户名称
|
||||
*/
|
||||
private String mchName;
|
||||
|
||||
/**
|
||||
* 类型: 1-普通商户, 2-特约商户(服务商模式)
|
||||
*/
|
||||
private Byte mchType;
|
||||
|
||||
/**
|
||||
* 商户订单号
|
||||
*/
|
||||
private String mchOrderNo;
|
||||
|
||||
/**
|
||||
* 支付接口代码
|
||||
*/
|
||||
private String ifCode;
|
||||
|
||||
/**
|
||||
* 入账方式: WX_CASH-微信零钱; ALIPAY_CASH-支付宝转账; BANK_CARD-银行卡
|
||||
*/
|
||||
private String entryType;
|
||||
|
||||
/**
|
||||
* 转账金额,单位分
|
||||
*/
|
||||
private Long amount;
|
||||
|
||||
/**
|
||||
* 三位货币代码,人民币:cny
|
||||
*/
|
||||
private String currency;
|
||||
|
||||
/**
|
||||
* 收款账号
|
||||
*/
|
||||
private String accountNo;
|
||||
|
||||
/**
|
||||
* 收款人姓名
|
||||
*/
|
||||
private String accountName;
|
||||
|
||||
/**
|
||||
* 收款人开户行名称
|
||||
*/
|
||||
private String bankName;
|
||||
|
||||
/**
|
||||
* 转账备注信息
|
||||
*/
|
||||
private String transferDesc;
|
||||
|
||||
/**
|
||||
* 客户端IP
|
||||
*/
|
||||
private String clientIp;
|
||||
|
||||
/**
|
||||
* 支付状态: 0-订单生成, 1-转账中, 2-转账成功, 3-转账失败, 4-订单关闭
|
||||
*/
|
||||
private Byte state;
|
||||
|
||||
/**
|
||||
* 向下游回调状态, 0-未发送, 1-已发送
|
||||
*/
|
||||
private Byte notifyState;
|
||||
|
||||
/**
|
||||
* 特定渠道发起额外参数
|
||||
*/
|
||||
private String channelExtra;
|
||||
|
||||
/**
|
||||
* 渠道订单号
|
||||
*/
|
||||
private String channelOrderNo;
|
||||
|
||||
/**
|
||||
* 渠道支付错误码
|
||||
*/
|
||||
private String errCode;
|
||||
|
||||
/**
|
||||
* 渠道支付错误描述
|
||||
*/
|
||||
private String errMsg;
|
||||
|
||||
/**
|
||||
* 商户扩展参数
|
||||
*/
|
||||
private String extParam;
|
||||
|
||||
/**
|
||||
* 异步通知地址
|
||||
*/
|
||||
private String notifyUrl;
|
||||
|
||||
/**
|
||||
* 转账成功时间
|
||||
*/
|
||||
private Date successTime;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createdAt;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private Date updatedAt;
|
||||
|
||||
}
|
||||
|
|
@ -34,9 +34,11 @@ public class SeqKit {
|
|||
private static final AtomicLong PAY_ORDER_SEQ = new AtomicLong(0L);
|
||||
private static final AtomicLong REFUND_ORDER_SEQ = new AtomicLong(0L);
|
||||
private static final AtomicLong MHO_ORDER_SEQ = new AtomicLong(0L);
|
||||
private static final AtomicLong TRANSFER_ID_SEQ = new AtomicLong(0L);
|
||||
private static final String PAY_ORDER_SEQ_PREFIX = "P";
|
||||
private static final String REFUND_ORDER_SEQ_PREFIX = "R";
|
||||
private static final String MHO_ORDER_SEQ_PREFIX = "M";
|
||||
private static final String TRANSFER_ID_SEQ_PREFIX = "T";
|
||||
|
||||
/** 生成支付订单号 **/
|
||||
public static String genPayOrderId() {
|
||||
|
|
@ -60,4 +62,11 @@ public class SeqKit {
|
|||
(int) MHO_ORDER_SEQ.getAndIncrement() % 10000);
|
||||
}
|
||||
|
||||
/** 模拟生成商户订单号 **/
|
||||
public static String genTransferId() {
|
||||
return String.format("%s%s%04d", TRANSFER_ID_SEQ_PREFIX,
|
||||
DateUtil.format(new Date(), DatePattern.PURE_DATETIME_MS_PATTERN),
|
||||
(int) TRANSFER_ID_SEQ.getAndIncrement() % 10000);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import com.jeequan.jeepay.core.entity.PayOrder;
|
|||
import com.jeequan.jeepay.pay.rqrs.msg.ChannelRetMsg;
|
||||
import com.jeequan.jeepay.pay.model.MchAppConfigContext;
|
||||
|
||||
/*
|
||||
/**
|
||||
* 查单(渠道侧)接口定义
|
||||
*
|
||||
* @author terrfly
|
||||
|
|
@ -28,7 +28,7 @@ import com.jeequan.jeepay.pay.model.MchAppConfigContext;
|
|||
*/
|
||||
public interface IPayOrderQueryService {
|
||||
|
||||
/* 获取到接口code **/
|
||||
/** 获取到接口code **/
|
||||
String getIfCode();
|
||||
|
||||
/** 查询订单 **/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jeequan.jeepay.pay.channel;
|
||||
|
||||
import com.jeequan.jeepay.core.entity.TransferOrder;
|
||||
import com.jeequan.jeepay.pay.model.MchAppConfigContext;
|
||||
import com.jeequan.jeepay.pay.rqrs.msg.ChannelRetMsg;
|
||||
import com.jeequan.jeepay.pay.rqrs.transfer.TransferOrderRQ;
|
||||
|
||||
/**
|
||||
* 转账接口
|
||||
*
|
||||
* @author terrfly
|
||||
* @site https://www.jeepay.vip
|
||||
* @date 2021/8/11 13:59
|
||||
*/
|
||||
public interface ITransferService {
|
||||
|
||||
/* 获取到接口code **/
|
||||
String getIfCode();
|
||||
|
||||
/** 是否支持该支付入账方式 */
|
||||
boolean isSupport(String entryType);
|
||||
|
||||
/** 前置检查如参数等信息是否符合要求, 返回错误信息或直接抛出异常即可 */
|
||||
String preCheck(TransferOrderRQ bizRQ, TransferOrder refundOrder);
|
||||
|
||||
/** 调起退款接口,并响应数据; 内部处理普通商户和服务商模式 **/
|
||||
ChannelRetMsg transfer(TransferOrderRQ bizRQ, TransferOrder refundOrder, MchAppConfigContext mchAppConfigContext) throws Exception;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jeequan.jeepay.pay.channel.wxpay;
|
||||
|
||||
import com.github.binarywang.wxpay.bean.entpay.EntPayRequest;
|
||||
import com.github.binarywang.wxpay.bean.entpay.EntPayResult;
|
||||
import com.github.binarywang.wxpay.exception.WxPayException;
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.entity.TransferOrder;
|
||||
import com.jeequan.jeepay.core.model.params.wxpay.WxpayNormalMchParams;
|
||||
import com.jeequan.jeepay.pay.channel.ITransferService;
|
||||
import com.jeequan.jeepay.pay.channel.wxpay.kits.WxpayKit;
|
||||
import com.jeequan.jeepay.pay.model.MchAppConfigContext;
|
||||
import com.jeequan.jeepay.pay.rqrs.msg.ChannelRetMsg;
|
||||
import com.jeequan.jeepay.pay.rqrs.transfer.TransferOrderRQ;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 转账接口: 微信官方
|
||||
*
|
||||
* @author terrfly
|
||||
* @site https://www.jeepay.vip
|
||||
* @date 2021/8/11 14:05
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class WxpayTransferService implements ITransferService {
|
||||
|
||||
@Override
|
||||
public String getIfCode() {
|
||||
return CS.IF_CODE.WXPAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSupport(String entryType) {
|
||||
|
||||
// 微信仅支持 零钱 和 银行卡入账方式
|
||||
if(TransferOrder.ENTRY_WX_CASH.equals(entryType) || TransferOrder.ENTRY_BANK_CARD.equals(entryType)){
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String preCheck(TransferOrderRQ bizRQ, TransferOrder refundOrder) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelRetMsg transfer(TransferOrderRQ bizRQ, TransferOrder transferOrder, MchAppConfigContext mchAppConfigContext){
|
||||
|
||||
try {
|
||||
|
||||
WxpayNormalMchParams params = mchAppConfigContext.getNormalMchParamsByIfCode(getIfCode(), WxpayNormalMchParams.class);
|
||||
|
||||
EntPayRequest request = new EntPayRequest();
|
||||
request.setMchAppid(params.getAppId()); // 商户账号appid
|
||||
request.setMchId(params.getMchId()); //商户号
|
||||
|
||||
request.setPartnerTradeNo(transferOrder.getTransferId()); //商户订单号
|
||||
request.setOpenid(transferOrder.getAccountNo()); //openid
|
||||
request.setAmount(transferOrder.getAmount().intValue()); //付款金额,单位为分
|
||||
request.setSpbillCreateIp(transferOrder.getClientIp());
|
||||
request.setDescription(transferOrder.getTransferDesc()); //付款备注
|
||||
if(StringUtils.isNotEmpty(transferOrder.getAccountName())){
|
||||
request.setReUserName(transferOrder.getAccountName());
|
||||
request.setCheckName("FORCE_CHECK");
|
||||
}else{
|
||||
request.setCheckName("NO_CHECK");
|
||||
}
|
||||
|
||||
EntPayResult entPayResult = mchAppConfigContext.getWxServiceWrapper().getWxPayService().getEntPayService().entPay(request);
|
||||
|
||||
// SUCCESS/FAIL,注意:当状态为FAIL时,存在业务结果未明确的情况。如果状态为FAIL,请务必关注错误代码(err_code字段),通过查询接口确认此次付款的结果。
|
||||
if("SUCCESS".equalsIgnoreCase(entPayResult.getResultCode())){
|
||||
return ChannelRetMsg.confirmSuccess(entPayResult.getPaymentNo());
|
||||
}else{
|
||||
return ChannelRetMsg.waiting();
|
||||
}
|
||||
|
||||
} catch (WxPayException e) {
|
||||
|
||||
//出现未明确的错误码时(SYSTEMERROR等),请务必用原商户订单号重试,或通过查询接口确认此次付款的结果。
|
||||
if("SYSTEMERROR".equalsIgnoreCase(e.getErrCode())){
|
||||
return ChannelRetMsg.waiting();
|
||||
}
|
||||
|
||||
return ChannelRetMsg.confirmFail(null,
|
||||
WxpayKit.appendErrCode(e.getReturnMsg(), e.getErrCode()),
|
||||
WxpayKit.appendErrMsg(e.getReturnMsg(), StringUtils.defaultIfEmpty(e.getErrCodeDes(), e.getCustomErrorMsg())));
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("转账异常:", e);
|
||||
return ChannelRetMsg.waiting();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,248 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jeequan.jeepay.pay.ctrl.transfer;
|
||||
|
||||
import com.jeequan.jeepay.core.entity.MchApp;
|
||||
import com.jeequan.jeepay.core.entity.MchInfo;
|
||||
import com.jeequan.jeepay.core.entity.TransferOrder;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.model.ApiRes;
|
||||
import com.jeequan.jeepay.core.utils.SeqKit;
|
||||
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
|
||||
import com.jeequan.jeepay.core.utils.StringKit;
|
||||
import com.jeequan.jeepay.pay.channel.ITransferService;
|
||||
import com.jeequan.jeepay.pay.ctrl.ApiController;
|
||||
import com.jeequan.jeepay.pay.exception.ChannelException;
|
||||
import com.jeequan.jeepay.pay.model.MchAppConfigContext;
|
||||
import com.jeequan.jeepay.pay.rqrs.msg.ChannelRetMsg;
|
||||
import com.jeequan.jeepay.pay.rqrs.transfer.TransferOrderRQ;
|
||||
import com.jeequan.jeepay.pay.rqrs.transfer.TransferOrderRS;
|
||||
import com.jeequan.jeepay.pay.service.ConfigContextService;
|
||||
import com.jeequan.jeepay.pay.service.PayMchNotifyService;
|
||||
import com.jeequan.jeepay.service.impl.PayInterfaceConfigService;
|
||||
import com.jeequan.jeepay.service.impl.TransferOrderService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 转账接口
|
||||
*
|
||||
* @author terrfly
|
||||
* @site https://www.jeepay.vip
|
||||
* @date 2021/8/11 11:07
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
public class TransferOrderController extends ApiController {
|
||||
|
||||
@Autowired private ConfigContextService configContextService;
|
||||
@Autowired private TransferOrderService transferOrderService;
|
||||
@Autowired private PayInterfaceConfigService payInterfaceConfigService;
|
||||
@Autowired private PayMchNotifyService payMchNotifyService;
|
||||
|
||||
/**
|
||||
* 转账
|
||||
* **/
|
||||
@PostMapping("/api/transferOrder")
|
||||
public ApiRes transferOrder(){
|
||||
|
||||
TransferOrder transferOrder = null;
|
||||
|
||||
//获取参数 & 验签
|
||||
TransferOrderRQ bizRQ = getRQByWithMchSign(TransferOrderRQ.class);
|
||||
|
||||
try {
|
||||
|
||||
|
||||
String mchNo = bizRQ.getMchNo();
|
||||
String appId = bizRQ.getAppId();
|
||||
String ifCode = bizRQ.getIfCode();
|
||||
|
||||
// 商户订单号是否重复
|
||||
if(transferOrderService.count(TransferOrder.gw().eq(TransferOrder::getMchNo, mchNo).eq(TransferOrder::getMchOrderNo, bizRQ.getMchOrderNo())) > 0){
|
||||
throw new BizException("商户订单["+bizRQ.getMchOrderNo()+"]已存在");
|
||||
}
|
||||
|
||||
if(StringUtils.isNotEmpty(bizRQ.getNotifyUrl()) && !StringKit.isAvailableUrl(bizRQ.getNotifyUrl())){
|
||||
throw new BizException("异步通知地址协议仅支持http:// 或 https:// !");
|
||||
}
|
||||
|
||||
// 商户配置信息
|
||||
MchAppConfigContext mchAppConfigContext = configContextService.getMchAppConfigContext(mchNo, appId);
|
||||
if(mchAppConfigContext == null){
|
||||
throw new BizException("获取商户应用信息失败");
|
||||
}
|
||||
|
||||
MchInfo mchInfo = mchAppConfigContext.getMchInfo();
|
||||
MchApp mchApp = mchAppConfigContext.getMchApp();
|
||||
|
||||
// 是否已正确配置
|
||||
if(!payInterfaceConfigService.mchAppHasAvailableIfCode(appId, ifCode)){
|
||||
throw new BizException("应用未开通此接口配置!");
|
||||
}
|
||||
|
||||
|
||||
ITransferService transferService = SpringBeansUtil.getBean(ifCode + "TransferService", ITransferService.class);
|
||||
if(transferService == null){
|
||||
throw new BizException("无此转账通道接口");
|
||||
}
|
||||
|
||||
if(!transferService.isSupport(bizRQ.getEntryType())){
|
||||
throw new BizException("该接口不支持该入账方式");
|
||||
}
|
||||
|
||||
transferOrder = genTransferOrder(bizRQ, mchInfo, mchApp, ifCode);
|
||||
|
||||
//预先校验
|
||||
String errMsg = transferService.preCheck(bizRQ, transferOrder);
|
||||
if(StringUtils.isNotEmpty(errMsg)){
|
||||
throw new BizException(errMsg);
|
||||
}
|
||||
|
||||
// 入库
|
||||
transferOrderService.save(transferOrder);
|
||||
|
||||
// 调起上游接口
|
||||
ChannelRetMsg channelRetMsg = transferService.transfer(bizRQ, transferOrder, mchAppConfigContext);
|
||||
|
||||
//处理退款单状态
|
||||
this.processChannelMsg(channelRetMsg, transferOrder);
|
||||
|
||||
TransferOrderRS bizRes = TransferOrderRS.buildByRecord(transferOrder);
|
||||
return ApiRes.okWithSign(bizRes, mchApp.getAppSecret());
|
||||
|
||||
} catch (BizException e) {
|
||||
return ApiRes.customFail(e.getMessage());
|
||||
|
||||
} catch (ChannelException e) {
|
||||
|
||||
//处理上游返回数据
|
||||
this.processChannelMsg(e.getChannelRetMsg(), transferOrder);
|
||||
|
||||
if(e.getChannelRetMsg().getChannelState() == ChannelRetMsg.ChannelState.SYS_ERROR ){
|
||||
return ApiRes.customFail(e.getMessage());
|
||||
}
|
||||
|
||||
TransferOrderRS bizRes = TransferOrderRS.buildByRecord(transferOrder);
|
||||
return ApiRes.okWithSign(bizRes, configContextService.getMchAppConfigContext(bizRQ.getMchNo(), bizRQ.getAppId()).getMchApp().getAppSecret());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("系统异常:{}", e);
|
||||
return ApiRes.customFail("系统异常");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private TransferOrder genTransferOrder(TransferOrderRQ rq, MchInfo mchInfo, MchApp mchApp, String ifCode){
|
||||
|
||||
TransferOrder transferOrder = new TransferOrder();
|
||||
transferOrder.setTransferId(SeqKit.genTransferId()); //生成转账订单号
|
||||
transferOrder.setMchNo(mchInfo.getMchNo()); //商户号
|
||||
transferOrder.setIsvNo(mchInfo.getIsvNo()); //服务商号
|
||||
transferOrder.setAppId(mchApp.getAppId()); //商户应用appId
|
||||
transferOrder.setMchName(mchInfo.getMchShortName()); //商户名称(简称)
|
||||
transferOrder.setMchType(mchInfo.getType()); //商户类型
|
||||
transferOrder.setMchOrderNo(rq.getMchOrderNo()); //商户订单号
|
||||
transferOrder.setIfCode(ifCode); //接口代码
|
||||
transferOrder.setEntryType(rq.getEntryType()); //入账方式
|
||||
transferOrder.setAmount(rq.getAmount()); //订单金额
|
||||
transferOrder.setCurrency(rq.getCurrency()); //币种
|
||||
transferOrder.setClientIp(StringUtils.defaultIfEmpty(rq.getClientIp(), getClientIp())); //客户端IP
|
||||
transferOrder.setState(TransferOrder.STATE_INIT); //订单状态, 默认订单生成状态
|
||||
transferOrder.setAccountNo(rq.getAccountNo()); //收款账号
|
||||
transferOrder.setAccountName(rq.getAccountName()); //账户姓名
|
||||
transferOrder.setBankName(rq.getBankName()); //银行名称
|
||||
transferOrder.setTransferDesc(rq.getTransferDesc()); //转账备注
|
||||
transferOrder.setExtParam(rq.getExtParam()); //商户扩展参数
|
||||
transferOrder.setNotifyUrl(rq.getNotifyUrl()); //异步通知地址
|
||||
transferOrder.setCreatedAt(new Date()); //订单创建时间
|
||||
return transferOrder;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 处理返回的渠道信息,并更新订单状态
|
||||
* TransferOrder将对部分信息进行 赋值操作。
|
||||
* **/
|
||||
private void processChannelMsg(ChannelRetMsg channelRetMsg, TransferOrder transferOrder){
|
||||
|
||||
//对象为空 || 上游返回状态为空, 则无需操作
|
||||
if(channelRetMsg == null || channelRetMsg.getChannelState() == null){
|
||||
return ;
|
||||
}
|
||||
|
||||
String transferId = transferOrder.getTransferId();
|
||||
|
||||
//明确成功
|
||||
if(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS == channelRetMsg.getChannelState()) {
|
||||
|
||||
this.updateInitOrderStateThrowException(TransferOrder.STATE_SUCCESS, transferOrder, channelRetMsg);
|
||||
payMchNotifyService.transferOrderNotify(transferOrder);
|
||||
|
||||
//明确失败
|
||||
}else if(ChannelRetMsg.ChannelState.CONFIRM_FAIL == channelRetMsg.getChannelState()) {
|
||||
|
||||
this.updateInitOrderStateThrowException(TransferOrder.STATE_FAIL, transferOrder, channelRetMsg);
|
||||
payMchNotifyService.transferOrderNotify(transferOrder);
|
||||
|
||||
// 上游处理中 || 未知 || 上游接口返回异常 订单为支付中状态
|
||||
}else if( ChannelRetMsg.ChannelState.WAITING == channelRetMsg.getChannelState() ||
|
||||
ChannelRetMsg.ChannelState.UNKNOWN == channelRetMsg.getChannelState() ||
|
||||
ChannelRetMsg.ChannelState.API_RET_ERROR == channelRetMsg.getChannelState()
|
||||
|
||||
){
|
||||
this.updateInitOrderStateThrowException(TransferOrder.STATE_ING, transferOrder, channelRetMsg);
|
||||
|
||||
// 系统异常: 订单不再处理。 为: 生成状态
|
||||
}else if( ChannelRetMsg.ChannelState.SYS_ERROR == channelRetMsg.getChannelState()){
|
||||
|
||||
}else{
|
||||
|
||||
throw new BizException("ChannelState 返回异常!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** 更新订单状态 --》 订单生成--》 其他状态 (向外抛出异常) **/
|
||||
private void updateInitOrderStateThrowException(byte orderState, TransferOrder transferOrder, ChannelRetMsg channelRetMsg){
|
||||
|
||||
transferOrder.setState(orderState);
|
||||
transferOrder.setChannelOrderNo(channelRetMsg.getChannelOrderId());
|
||||
transferOrder.setErrCode(channelRetMsg.getChannelErrCode());
|
||||
transferOrder.setErrMsg(channelRetMsg.getChannelErrMsg());
|
||||
|
||||
|
||||
boolean isSuccess = transferOrderService.updateInit2Ing(transferOrder.getTransferId());
|
||||
if(!isSuccess){
|
||||
throw new BizException("更新转账订单异常!");
|
||||
}
|
||||
|
||||
isSuccess = transferOrderService.updateIng2SuccessOrFail(transferOrder.getTransferId(), transferOrder.getState(),
|
||||
channelRetMsg.getChannelOrderId(), channelRetMsg.getChannelErrCode(), channelRetMsg.getChannelErrMsg());
|
||||
if(!isSuccess){
|
||||
throw new BizException("更新转账订单异常!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jeequan.jeepay.pay.rqrs.transfer;
|
||||
|
||||
import com.jeequan.jeepay.pay.rqrs.AbstractMchAppRQ;
|
||||
import lombok.Data;
|
||||
|
||||
/*
|
||||
* 查询转账单请求参数对象
|
||||
*
|
||||
* @author terrfly
|
||||
* @site https://www.jeequan.com
|
||||
* @date 2021/6/17 14:07
|
||||
*/
|
||||
@Data
|
||||
public class QueryTransferOrderRQ extends AbstractMchAppRQ {
|
||||
|
||||
/** 商户转账单号 **/
|
||||
private String mchRefundNo;
|
||||
|
||||
/** 支付系统转账单号 **/
|
||||
private String transferId;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jeequan.jeepay.pay.rqrs.transfer;
|
||||
|
||||
import com.jeequan.jeepay.core.entity.TransferOrder;
|
||||
import com.jeequan.jeepay.pay.rqrs.AbstractRS;
|
||||
import lombok.Data;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
|
||||
/*
|
||||
* 查询转账订单 响应参数
|
||||
*
|
||||
* @author terrfly
|
||||
* @site https://www.jeequan.com
|
||||
* @date 2021/6/17 14:08
|
||||
*/
|
||||
@Data
|
||||
public class QueryTransferOrderRS extends AbstractRS {
|
||||
|
||||
/**
|
||||
* 转账订单号
|
||||
*/
|
||||
private String transferId;
|
||||
|
||||
/**
|
||||
* 商户号
|
||||
*/
|
||||
private String mchNo;
|
||||
|
||||
/**
|
||||
* 应用ID
|
||||
*/
|
||||
private String appId;
|
||||
|
||||
/**
|
||||
* 商户订单号
|
||||
*/
|
||||
private String mchOrderNo;
|
||||
|
||||
/**
|
||||
* 支付接口代码
|
||||
*/
|
||||
private String ifCode;
|
||||
|
||||
/**
|
||||
* 入账方式: WX_CASH-微信零钱; ALIPAY_CASH-支付宝转账; BANK_CARD-银行卡
|
||||
*/
|
||||
private String entryType;
|
||||
|
||||
/**
|
||||
* 转账金额,单位分
|
||||
*/
|
||||
private Long amount;
|
||||
|
||||
/**
|
||||
* 三位货币代码,人民币:cny
|
||||
*/
|
||||
private String currency;
|
||||
|
||||
/**
|
||||
* 收款账号
|
||||
*/
|
||||
private String accountNo;
|
||||
|
||||
/**
|
||||
* 收款人姓名
|
||||
*/
|
||||
private String accountName;
|
||||
|
||||
/**
|
||||
* 收款人开户行名称
|
||||
*/
|
||||
private String bankName;
|
||||
|
||||
/**
|
||||
* 转账备注信息
|
||||
*/
|
||||
private String transferDesc;
|
||||
|
||||
/**
|
||||
* 支付状态: 0-订单生成, 1-转账中, 2-转账成功, 3-转账失败, 4-订单关闭
|
||||
*/
|
||||
private Byte state;
|
||||
|
||||
/**
|
||||
* 特定渠道发起额外参数
|
||||
*/
|
||||
private String channelExtra;
|
||||
|
||||
/**
|
||||
* 渠道订单号
|
||||
*/
|
||||
private String channelOrderNo;
|
||||
|
||||
/**
|
||||
* 渠道支付错误码
|
||||
*/
|
||||
private String errCode;
|
||||
|
||||
/**
|
||||
* 渠道支付错误描述
|
||||
*/
|
||||
private String errMsg;
|
||||
|
||||
/**
|
||||
* 商户扩展参数
|
||||
*/
|
||||
private String extParam;
|
||||
|
||||
/**
|
||||
* 转账成功时间
|
||||
*/
|
||||
private Long successTime;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Long createdAt;
|
||||
|
||||
|
||||
public static QueryTransferOrderRS buildByRecord(TransferOrder record){
|
||||
|
||||
if(record == null){
|
||||
return null;
|
||||
}
|
||||
|
||||
QueryTransferOrderRS result = new QueryTransferOrderRS();
|
||||
BeanUtils.copyProperties(record, result);
|
||||
result.setSuccessTime(record.getSuccessTime() == null ? null : record.getSuccessTime().getTime());
|
||||
result.setCreatedAt(record.getCreatedAt() == null ? null : record.getCreatedAt().getTime());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jeequan.jeepay.pay.rqrs.transfer;
|
||||
|
||||
import com.jeequan.jeepay.pay.rqrs.AbstractMchAppRQ;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/*
|
||||
* 申请转账 请求参数
|
||||
*
|
||||
* @author terrfly
|
||||
* @site https://www.jeequan.com
|
||||
* @date 2021/8/10 11:31
|
||||
*/
|
||||
@Data
|
||||
public class TransferOrderRQ extends AbstractMchAppRQ {
|
||||
|
||||
/** 商户订单号 **/
|
||||
@NotBlank(message="商户订单号不能为空")
|
||||
private String mchOrderNo;
|
||||
|
||||
/** 支付接口代码 **/
|
||||
@NotBlank(message="支付接口代码不能为空")
|
||||
private String ifCode;
|
||||
|
||||
/** 入账方式 **/
|
||||
@NotBlank(message="入账方式不能为空")
|
||||
private String entryType;
|
||||
|
||||
/** 支付金额, 单位:分 **/
|
||||
@NotNull(message="转账金额不能为空")
|
||||
@Min(value = 1, message = "转账金额不能小于1分")
|
||||
private Long amount;
|
||||
|
||||
/** 货币代码 **/
|
||||
@NotBlank(message="货币代码不能为空")
|
||||
private String currency;
|
||||
|
||||
/** 收款账号 **/
|
||||
@NotBlank(message="收款账号不能为空")
|
||||
private String accountNo;
|
||||
|
||||
/** 收款人姓名 **/
|
||||
private String accountName;
|
||||
|
||||
/** 收款人开户行名称 **/
|
||||
private String bankName;
|
||||
|
||||
/** 客户端IP地址 **/
|
||||
private String clientIp;
|
||||
|
||||
/** 转账备注信息 **/
|
||||
@NotBlank(message="转账备注信息不能为空")
|
||||
private String transferDesc;
|
||||
|
||||
/** 异步通知地址 **/
|
||||
private String notifyUrl;
|
||||
|
||||
/** 特定渠道发起额外参数 **/
|
||||
private String channelExtra;
|
||||
|
||||
/** 商户扩展参数 **/
|
||||
private String extParam;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jeequan.jeepay.pay.rqrs.transfer;
|
||||
|
||||
import com.jeequan.jeepay.core.entity.TransferOrder;
|
||||
import com.jeequan.jeepay.pay.rqrs.AbstractRS;
|
||||
import lombok.Data;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
|
||||
/*
|
||||
* 创建订单(统一订单) 响应参数
|
||||
*
|
||||
* @author terrfly
|
||||
* @site https://www.jeequan.com
|
||||
* @date 2021/6/8 17:34
|
||||
*/
|
||||
@Data
|
||||
public class TransferOrderRS extends AbstractRS {
|
||||
|
||||
|
||||
/** 转账单号 **/
|
||||
private String transferId;
|
||||
|
||||
/** 商户单号 **/
|
||||
private String mchOrderNo;
|
||||
|
||||
/** 转账金额 **/
|
||||
private Long amount;
|
||||
|
||||
/**
|
||||
* 收款账号
|
||||
*/
|
||||
private String accountNo;
|
||||
|
||||
/**
|
||||
* 收款人姓名
|
||||
*/
|
||||
private String accountName;
|
||||
|
||||
/**
|
||||
* 收款人开户行名称
|
||||
*/
|
||||
private String bankName;
|
||||
|
||||
/** 状态 **/
|
||||
private Byte state;
|
||||
|
||||
/** 渠道退款单号 **/
|
||||
private String channelOrderNo;
|
||||
|
||||
/** 渠道返回错误代码 **/
|
||||
private String errCode;
|
||||
|
||||
/** 渠道返回错误信息 **/
|
||||
private String errMsg;
|
||||
|
||||
public static TransferOrderRS buildByRecord(TransferOrder record){
|
||||
|
||||
if(record == null){
|
||||
return null;
|
||||
}
|
||||
|
||||
TransferOrderRS result = new TransferOrderRS();
|
||||
BeanUtils.copyProperties(record, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -21,10 +21,12 @@ import com.jeequan.jeepay.components.mq.vender.IMQSender;
|
|||
import com.jeequan.jeepay.core.entity.MchNotifyRecord;
|
||||
import com.jeequan.jeepay.core.entity.PayOrder;
|
||||
import com.jeequan.jeepay.core.entity.RefundOrder;
|
||||
import com.jeequan.jeepay.core.entity.TransferOrder;
|
||||
import com.jeequan.jeepay.core.utils.JeepayKit;
|
||||
import com.jeequan.jeepay.core.utils.StringKit;
|
||||
import com.jeequan.jeepay.pay.rqrs.payorder.QueryPayOrderRS;
|
||||
import com.jeequan.jeepay.pay.rqrs.refund.QueryRefundOrderRS;
|
||||
import com.jeequan.jeepay.pay.rqrs.transfer.QueryTransferOrderRS;
|
||||
import com.jeequan.jeepay.service.impl.MchNotifyRecordService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
|
@ -138,6 +140,51 @@ public class PayMchNotifyService {
|
|||
}
|
||||
|
||||
|
||||
/** 商户通知信息,转账订单的通知接口 **/
|
||||
public void transferOrderNotify(TransferOrder dbTransferOrder){
|
||||
|
||||
try {
|
||||
// 通知地址为空
|
||||
if(StringUtils.isEmpty(dbTransferOrder.getNotifyUrl())){
|
||||
return ;
|
||||
}
|
||||
|
||||
//获取到通知对象
|
||||
MchNotifyRecord mchNotifyRecord = mchNotifyRecordService.findByTransferOrder(dbTransferOrder.getTransferId());
|
||||
|
||||
if(mchNotifyRecord != null){
|
||||
log.info("当前已存在通知消息, 不再发送。");
|
||||
return ;
|
||||
}
|
||||
|
||||
//商户app私钥
|
||||
String appSecret = configContextService.getMchAppConfigContext(dbTransferOrder.getMchNo(), dbTransferOrder.getAppId()).getMchApp().getAppSecret();
|
||||
|
||||
// 封装通知url
|
||||
String notifyUrl = createNotifyUrl(dbTransferOrder, appSecret);
|
||||
mchNotifyRecord = new MchNotifyRecord();
|
||||
mchNotifyRecord.setOrderId(dbTransferOrder.getTransferId());
|
||||
mchNotifyRecord.setOrderType(MchNotifyRecord.TYPE_TRANSFER_ORDER);
|
||||
mchNotifyRecord.setMchNo(dbTransferOrder.getMchNo());
|
||||
mchNotifyRecord.setMchOrderNo(dbTransferOrder.getMchOrderNo()); //商户订单号
|
||||
mchNotifyRecord.setIsvNo(dbTransferOrder.getIsvNo());
|
||||
mchNotifyRecord.setAppId(dbTransferOrder.getAppId());
|
||||
mchNotifyRecord.setNotifyUrl(notifyUrl);
|
||||
mchNotifyRecord.setResResult("");
|
||||
mchNotifyRecord.setNotifyCount(0);
|
||||
mchNotifyRecord.setState(MchNotifyRecord.STATE_ING); // 通知中
|
||||
mchNotifyRecordService.save(mchNotifyRecord);
|
||||
|
||||
//推送到MQ
|
||||
Long notifyId = mchNotifyRecord.getNotifyId();
|
||||
mqSender.send(PayOrderMchNotifyMQ.build(notifyId));
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("推送失败!", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建响应URL
|
||||
*/
|
||||
|
|
@ -172,6 +219,23 @@ public class PayMchNotifyService {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建响应URL
|
||||
*/
|
||||
public String createNotifyUrl(TransferOrder transferOrder, String appSecret) {
|
||||
|
||||
QueryTransferOrderRS rs = QueryTransferOrderRS.buildByRecord(transferOrder);
|
||||
JSONObject jsonObject = (JSONObject)JSONObject.toJSON(rs);
|
||||
jsonObject.put("reqTime", System.currentTimeMillis()); //添加请求时间
|
||||
|
||||
// 报文签名
|
||||
jsonObject.put("sign", JeepayKit.getSign(jsonObject, appSecret));
|
||||
|
||||
// 生成通知
|
||||
return StringKit.appendUrlQuery(transferOrder.getNotifyUrl(), jsonObject);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建响应URL
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -49,6 +49,11 @@ public class MchNotifyRecordService extends ServiceImpl<MchNotifyRecordMapper, M
|
|||
return findByOrderAndType(orderId, MchNotifyRecord.TYPE_REFUND_ORDER);
|
||||
}
|
||||
|
||||
/** 查询退款订单订单 */
|
||||
public MchNotifyRecord findByTransferOrder(String transferId){
|
||||
return findByOrderAndType(transferId, MchNotifyRecord.TYPE_TRANSFER_ORDER);
|
||||
}
|
||||
|
||||
public Integer updateNotifyResult(Long notifyId, Byte state, String resResult){
|
||||
return baseMapper.updateNotifyResult(notifyId, state, resResult);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,10 +19,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.jeequan.jeepay.core.constants.ApiCodeEnum;
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.entity.MchApp;
|
||||
import com.jeequan.jeepay.core.entity.MchInfo;
|
||||
import com.jeequan.jeepay.core.entity.PayInterfaceConfig;
|
||||
import com.jeequan.jeepay.core.entity.PayInterfaceDefine;
|
||||
import com.jeequan.jeepay.core.entity.*;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.service.mapper.PayInterfaceConfigMapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
|
@ -155,4 +152,21 @@ public class PayInterfaceConfigService extends ServiceImpl<PayInterfaceConfigMap
|
|||
}
|
||||
return defineList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** 查询商户app使用已正确配置了通道信息 */
|
||||
public boolean mchAppHasAvailableIfCode(String appId, String ifCode){
|
||||
|
||||
return this.count(
|
||||
PayInterfaceConfig.gw()
|
||||
.eq(PayInterfaceConfig::getIfCode, ifCode)
|
||||
.eq(PayInterfaceConfig::getState, CS.PUB_USABLE)
|
||||
.eq(PayInterfaceConfig::getInfoId, appId)
|
||||
.eq(PayInterfaceConfig::getInfoType, CS.INFO_TYPE_MCH_APP)
|
||||
) > 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jeequan.jeepay.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.jeequan.jeepay.core.entity.TransferOrder;
|
||||
import com.jeequan.jeepay.service.mapper.TransferOrderMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 转账订单表 服务实现类
|
||||
* </p>
|
||||
*
|
||||
* @author [mybatis plus generator]
|
||||
* @since 2021-08-11
|
||||
*/
|
||||
@Service
|
||||
public class TransferOrderService extends ServiceImpl<TransferOrderMapper, TransferOrder> {
|
||||
|
||||
|
||||
/** 更新转账订单状态 【转账订单生成】 --》 【转账中】 **/
|
||||
public boolean updateInit2Ing(String transferId){
|
||||
|
||||
TransferOrder updateRecord = new TransferOrder();
|
||||
updateRecord.setState(TransferOrder.STATE_ING);
|
||||
|
||||
return update(updateRecord, new LambdaUpdateWrapper<TransferOrder>()
|
||||
.eq(TransferOrder::getTransferId, transferId).eq(TransferOrder::getState, TransferOrder.STATE_INIT));
|
||||
}
|
||||
|
||||
|
||||
/** 更新转账订单状态 【转账中】 --》 【转账成功】 **/
|
||||
@Transactional
|
||||
public boolean updateIng2Success(String transferId, String channelOrderNo){
|
||||
|
||||
TransferOrder updateRecord = new TransferOrder();
|
||||
updateRecord.setState(TransferOrder.STATE_SUCCESS);
|
||||
updateRecord.setChannelOrderNo(channelOrderNo);
|
||||
updateRecord.setSuccessTime(new Date());
|
||||
|
||||
//更新转账订单表数据
|
||||
if(! update(updateRecord, new LambdaUpdateWrapper<TransferOrder>()
|
||||
.eq(TransferOrder::getTransferId, transferId).eq(TransferOrder::getState, TransferOrder.STATE_ING))
|
||||
){
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** 更新转账订单状态 【转账中】 --》 【转账失败】 **/
|
||||
@Transactional
|
||||
public boolean updateIng2Fail(String transferId, String channelOrderNo, String channelErrCode, String channelErrMsg){
|
||||
|
||||
TransferOrder updateRecord = new TransferOrder();
|
||||
updateRecord.setState(TransferOrder.STATE_FAIL);
|
||||
updateRecord.setErrCode(channelErrCode);
|
||||
updateRecord.setErrMsg(channelErrMsg);
|
||||
updateRecord.setChannelOrderNo(channelOrderNo);
|
||||
|
||||
return update(updateRecord, new LambdaUpdateWrapper<TransferOrder>()
|
||||
.eq(TransferOrder::getTransferId, transferId).eq(TransferOrder::getState, TransferOrder.STATE_ING));
|
||||
}
|
||||
|
||||
|
||||
/** 更新转账订单状态 【转账中】 --》 【转账成功/转账失败】 **/
|
||||
@Transactional
|
||||
public boolean updateIng2SuccessOrFail(String transferId, Byte updateState, String channelOrderNo, String channelErrCode, String channelErrMsg){
|
||||
|
||||
if(updateState == TransferOrder.STATE_ING){
|
||||
return true;
|
||||
}else if(updateState == TransferOrder.STATE_SUCCESS){
|
||||
return updateIng2Success(transferId, channelOrderNo);
|
||||
}else if(updateState == TransferOrder.STATE_FAIL){
|
||||
return updateIng2Fail(transferId, channelOrderNo, channelErrCode, channelErrMsg);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jeequan.jeepay.service.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.jeequan.jeepay.core.entity.TransferOrder;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 转账订单表 Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author [mybatis plus generator]
|
||||
* @since 2021-08-11
|
||||
*/
|
||||
public interface TransferOrderMapper extends BaseMapper<TransferOrder> {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.jeequan.jeepay.service.mapper.TransferOrderMapper">
|
||||
|
||||
<!-- 通用查询映射结果 -->
|
||||
<resultMap id="BaseResultMap" type="com.jeequan.jeepay.core.entity.TransferOrder">
|
||||
<id column="transfer_id" property="transferId" />
|
||||
<result column="mch_no" property="mchNo" />
|
||||
<result column="isv_no" property="isvNo" />
|
||||
<result column="app_id" property="appId" />
|
||||
<result column="mch_name" property="mchName" />
|
||||
<result column="mch_type" property="mchType" />
|
||||
<result column="mch_order_no" property="mchOrderNo" />
|
||||
<result column="if_code" property="ifCode" />
|
||||
<result column="entry_type" property="entryType" />
|
||||
<result column="amount" property="amount" />
|
||||
<result column="currency" property="currency" />
|
||||
<result column="account_no" property="accountNo" />
|
||||
<result column="account_name" property="accountName" />
|
||||
<result column="bank_name" property="bankName" />
|
||||
<result column="transfer_desc" property="transferDesc" />
|
||||
<result column="client_ip" property="clientIp" />
|
||||
<result column="state" property="state" />
|
||||
<result column="notify_state" property="notifyState" />
|
||||
<result column="channel_extra" property="channelExtra" />
|
||||
<result column="channel_order_no" property="channelOrderNo" />
|
||||
<result column="err_code" property="errCode" />
|
||||
<result column="err_msg" property="errMsg" />
|
||||
<result column="ext_param" property="extParam" />
|
||||
<result column="notify_url" property="notifyUrl" />
|
||||
<result column="success_time" property="successTime" />
|
||||
<result column="created_at" property="createdAt" />
|
||||
<result column="updated_at" property="updatedAt" />
|
||||
</resultMap>
|
||||
|
||||
</mapper>
|
||||
Loading…
Reference in New Issue