增加微信公众号的支付回调接口

This commit is contained in:
YunaiV 2021-10-25 23:29:42 +08:00
parent bac30d47b7
commit 75633aa84b
13 changed files with 173 additions and 11 deletions

View File

@ -120,6 +120,10 @@ public class PayOrderDO extends BaseDO {
* 订单支付成功时间 * 订单支付成功时间
*/ */
private Date successTime; private Date successTime;
/**
* 订单支付通知时间即支付渠道的通知时间
*/
private Date notifyTime;
/** /**
* 支付成功的订单拓展单编号 * 支付成功的订单拓展单编号
* *

View File

@ -77,6 +77,6 @@ public class PayOrderExtensionDO extends BaseDO {
* *
* 在支持成功后会记录回调的数据 * 在支持成功后会记录回调的数据
*/ */
private String channelCallbackData; private String channelNotifyData;
} }

View File

@ -1,6 +1,7 @@
package cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order; package cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order;
import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO; import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO;
import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderExtensionDO;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
@ -13,4 +14,9 @@ public interface PayOrderCoreMapper extends BaseMapperX<PayOrderDO> {
.eq("merchant_order_id", merchantOrderId)); .eq("merchant_order_id", merchantOrderId));
} }
default int updateByIdAndStatus(Long id, Integer status, PayOrderDO update) {
return update(update, new QueryWrapper<PayOrderDO>()
.eq("id", id).eq("status", status));
}
} }

View File

@ -1,6 +1,5 @@
package cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order; package cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order;
import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO;
import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderExtensionDO; import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderExtensionDO;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@ -9,4 +8,13 @@ import org.apache.ibatis.annotations.Mapper;
@Mapper @Mapper
public interface PayOrderExtensionCoreMapper extends BaseMapperX<PayOrderExtensionDO> { public interface PayOrderExtensionCoreMapper extends BaseMapperX<PayOrderExtensionDO> {
default PayOrderExtensionDO selectByOrderExtensionNo(String orderExtensionNo) {
return selectOne("order_extension_no", orderExtensionNo);
}
default int updateByIdAndStatus(Long id, Integer status, PayOrderExtensionDO update) {
return update(update, new QueryWrapper<PayOrderExtensionDO>()
.eq("id", id).eq("status", status));
}
} }

View File

@ -19,13 +19,13 @@ public interface PayErrorCodeCoreConstants {
ErrorCode PAY_CHANNEL_CLIENT_NOT_FOUND = new ErrorCode(1007001002, "支付渠道的客户端不存在"); ErrorCode PAY_CHANNEL_CLIENT_NOT_FOUND = new ErrorCode(1007001002, "支付渠道的客户端不存在");
// ========== ORDER 模块 1-007-002-000 ========== // ========== ORDER 模块 1-007-002-000 ==========
ErrorCode PAY_ORDER_NOT_FOUND = new ErrorCode(100401000, "支付订单不存在"); ErrorCode PAY_ORDER_NOT_FOUND = new ErrorCode(1007002000, "支付订单不存在");
ErrorCode PAY_ORDER_STATUS_IS_NOT_WAITING = new ErrorCode(100401001, "支付订单不处于待支付"); ErrorCode PAY_ORDER_STATUS_IS_NOT_WAITING = new ErrorCode(1007002001, "支付订单不处于待支付");
ErrorCode PAY_ORDER_STATUS_IS_NOT_SUCCESS = new ErrorCode(100401002, "支付订单不处于已支付"); ErrorCode PAY_ORDER_STATUS_IS_NOT_SUCCESS = new ErrorCode(1007002002, "支付订单不处于已支付");
ErrorCode PAY_ORDER_ERROR_USER = new ErrorCode(100401003, "支付订单用户不正确"); ErrorCode PAY_ORDER_ERROR_USER = new ErrorCode(1007002003, "支付订单用户不正确");
// ========== ORDER 模块(拓展单) 1-007-003-000 ==========
ErrorCode PAY_ORDER_EXTENSION_NOT_FOUND = new ErrorCode(100401050, "支付交易拓展单不存在"); ErrorCode PAY_ORDER_EXTENSION_NOT_FOUND = new ErrorCode(1007003000, "支付交易拓展单不存在");
ErrorCode PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING = new ErrorCode(100401051, "支付交易拓展单不处于待支付"); ErrorCode PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING = new ErrorCode(1007003001, "支付交易拓展单不处于待支付");
ErrorCode PAY_ORDER_EXTENSION_STATUS_IS_NOT_SUCCESS = new ErrorCode(100401052, "支付订单不处于已支付"); ErrorCode PAY_ORDER_EXTENSION_STATUS_IS_NOT_SUCCESS = new ErrorCode(1007003002, "支付订单不处于已支付");
} }

View File

@ -39,4 +39,13 @@ public interface PayOrderCoreService {
*/ */
PayOrderSubmitRespDTO submitPayOrder(@Valid PayOrderSubmitReqDTO reqDTO); PayOrderSubmitRespDTO submitPayOrder(@Valid PayOrderSubmitReqDTO reqDTO);
/**
* 通知支付单成功
*
* @param channelId 渠道编号
* @param channelCode 渠道编码
* @param notifyData 通知数据
*/
void notifyPayOrder(Long channelId, String channelCode, String notifyData) throws Exception;
} }

View File

@ -23,6 +23,7 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.pay.config.PayProperties; import cn.iocoder.yudao.framework.pay.config.PayProperties;
import cn.iocoder.yudao.framework.pay.core.client.PayClient; import cn.iocoder.yudao.framework.pay.core.client.PayClient;
import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderNotifyRespDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -99,7 +100,7 @@ public class PayOrderCoreServiceImpl implements PayOrderCoreService {
@Override @Override
public PayOrderSubmitRespDTO submitPayOrder(PayOrderSubmitReqDTO reqDTO) { public PayOrderSubmitRespDTO submitPayOrder(PayOrderSubmitReqDTO reqDTO) {
// 校验 App // 校验 App
PayAppDO app = payAppCoreService.validPayApp(reqDTO.getAppId()); payAppCoreService.validPayApp(reqDTO.getAppId());
// 校验支付渠道是否有效 // 校验支付渠道是否有效
PayChannelDO channel = payChannelCoreService.validPayChannel(reqDTO.getAppId(), reqDTO.getChannelCode()); PayChannelDO channel = payChannelCoreService.validPayChannel(reqDTO.getAppId(), reqDTO.getChannelCode());
// 校验支付客户端是否正确初始化 // 校验支付客户端是否正确初始化
@ -172,4 +173,61 @@ public class PayOrderCoreServiceImpl implements PayOrderCoreService {
; ;
} }
@Override
public void notifyPayOrder(Long channelId, String channelCode, String notifyData) throws Exception {
// TODO 芋艿记录回调日志
log.info("[notifyPayOrder][channelId({}) 回调数据({})]", channelId, notifyData);
// 校验支付渠道是否有效
PayChannelDO channel = payChannelCoreService.validPayChannel(channelId, channelCode);
// 校验支付客户端是否正确初始化
PayClient client = payClientFactory.getPayClient(channel.getId());
if (client == null) {
log.error("[notifyPayOrder][渠道编号({}) 找不到对应的支付客户端]", channel.getId());
throw exception(PAY_CHANNEL_CLIENT_NOT_FOUND);
}
// 解析支付结果
PayOrderNotifyRespDTO notifyRespDTO = client.parseOrderNotify(notifyData);
// TODO 芋艿先最严格的校验即使调用方重复调用实际哪个订单已经被重复回调的支付也返回 false 也没问题因为实际已经回调成功了
// 1.1 查询 PayOrderExtensionDO
PayOrderExtensionDO orderExtension = payOrderExtensionCoreMapper.selectByOrderExtensionNo(
notifyRespDTO.getOrderExtensionNo());
if (orderExtension == null) {
throw exception(PAY_ORDER_EXTENSION_NOT_FOUND);
}
if (!PayOrderStatusEnum.WAITING.getStatus().equals(orderExtension.getStatus())) { // 校验状态必须是待支付
throw exception(PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING);
}
// 1.2 更新 PayOrderExtensionDO
int updateCounts = payOrderExtensionCoreMapper.updateByIdAndStatus(orderExtension.getOrderId(),
PayOrderStatusEnum.WAITING.getStatus(), PayOrderExtensionDO.builder().id(orderExtension.getId())
.status(PayOrderStatusEnum.SUCCESS.getStatus()).channelNotifyData(notifyData).build());
if (updateCounts == 0) { // 校验状态必须是待支付
throw exception(PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING);
}
log.info("[notifyPayOrder][支付拓展单({}) 更新为已支付]", orderExtension.getId());
// 2.1 判断 PayOrderDO 是否处于待支付
PayOrderDO order = payOrderCoreMapper.selectById(orderExtension.getOrderId());
if (order == null) {
throw exception(PAY_ORDER_NOT_FOUND);
}
if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态必须是待支付
throw exception(PAY_ORDER_STATUS_IS_NOT_WAITING);
}
// 2.2 更新 PayOrderDO
updateCounts = payOrderCoreMapper.updateByIdAndStatus(order.getId(), PayOrderStatusEnum.WAITING.getStatus(),
PayOrderDO.builder().status(PayOrderStatusEnum.SUCCESS.getStatus()).channelId(channelId).channelCode(channelCode)
.successTime(notifyRespDTO.getSuccessTime()).notifyTime(new Date())
.successExtensionId(orderExtension.getId()).build());
if (updateCounts == 0) { // 校验状态必须是待支付
throw exception(PAY_ORDER_STATUS_IS_NOT_WAITING);
}
log.info("[notifyPayOrder][支付订单({}) 更新为已支付]", order.getId());
// 3. 插入支付通知记录
// payNotifyService.addPayTransactionNotifyTask(order, orderExtension);
}
} }

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.framework.pay.core.client; package cn.iocoder.yudao.framework.pay.core.client;
import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderNotifyRespDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO;
/** /**
@ -24,4 +25,13 @@ public interface PayClient {
*/ */
PayCommonResult<?> unifiedOrder(PayOrderUnifiedReqDTO reqDTO); PayCommonResult<?> unifiedOrder(PayOrderUnifiedReqDTO reqDTO);
/**
* 解析支付单的通知结果
*
* @param data 通知结果
* @return 解析结果
* @throws Exception 解析失败抛出异常
*/
PayOrderNotifyRespDTO parseOrderNotify(String data) throws Exception;
} }

View File

@ -0,0 +1,39 @@
package cn.iocoder.yudao.framework.pay.core.client.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* 支付通知 Response DTO
*
* @author 芋道源码
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PayOrderNotifyRespDTO {
/**
* 支付订单号支付模块的
*/
private String orderExtensionNo;
/**
* 支付渠道
*/
private String channelOrderNo;
/**
* 支付渠道
*/
private Date successTime;
/**
* 通知的原始数据
*
* 主要用于持久化方便后续修复数据或者排错
*/
private String data;
}

View File

@ -12,6 +12,8 @@ import java.util.Map;
/** /**
* 统一下单 Request DTO * 统一下单 Request DTO
*
* @author 芋道源码
*/ */
@Data @Data
public class PayOrderUnifiedReqDTO { public class PayOrderUnifiedReqDTO {

View File

@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult; import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult;
import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderNotifyRespDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO;
import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
@ -65,4 +66,9 @@ public class AlipayQrPayClient extends AbstractPayClient<AlipayPayClientConfig>
return PayCommonResult.build(response.getCode(), response.getMsg(), response, codeMapping); return PayCommonResult.build(response.getCode(), response.getMsg(), response, codeMapping);
} }
@Override
public PayOrderNotifyRespDTO parseOrderNotify(String data) throws Exception {
// TODO 芋艿待完成
return null;
}
} }

View File

@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult; import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult;
import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderNotifyRespDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO;
import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
@ -61,4 +62,9 @@ public class AlipayWapPayClient extends AbstractPayClient<AlipayPayClientConfig>
return PayCommonResult.build(response.getCode(), response.getMsg(), response, codeMapping); return PayCommonResult.build(response.getCode(), response.getMsg(), response, codeMapping);
} }
@Override
public PayOrderNotifyRespDTO parseOrderNotify(String data) throws Exception {
// TODO 芋艿待完成
return null;
}
} }

View File

@ -2,14 +2,17 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.wx;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.util.io.FileUtils; import cn.iocoder.yudao.framework.common.util.io.FileUtils;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult; import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult;
import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderNotifyRespDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO;
import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult; import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest; import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request; import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
@ -22,6 +25,8 @@ import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl; import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.util.Objects;
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
import static cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXCodeMapping.CODE_SUCCESS; import static cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXCodeMapping.CODE_SUCCESS;
import static cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXCodeMapping.MESSAGE_SUCCESS; import static cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXCodeMapping.MESSAGE_SUCCESS;
@ -126,4 +131,13 @@ public class WXPubPayClient extends AbstractPayClient<WXPayClientConfig> {
return openid; return openid;
} }
@Override
public PayOrderNotifyRespDTO parseOrderNotify(String data) throws WxPayException {
WxPayOrderNotifyResult notifyResult = client.parseOrderNotifyResult(data);
Assert.isTrue(Objects.equals(notifyResult.getResultCode(), "SUCCESS"), "支付结果非 SUCCESS");
// 转换结果
return new PayOrderNotifyRespDTO(notifyResult.getOutTradeNo(), notifyResult.getTransactionId(),
DateUtil.parse(notifyResult.getTimeEnd(), "yyyyMMddHHmmss"), data);
}
} }