完成 pingxx 支付回调接口
This commit is contained in:
parent
0285bf50fb
commit
63b4c27c8f
@ -14,18 +14,6 @@ import java.util.List;
|
|||||||
|
|
||||||
public interface PayTransactionService {
|
public interface PayTransactionService {
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新交易支付成功
|
|
||||||
*
|
|
||||||
* 该接口用于不同支付平台,支付成功后,回调该接口
|
|
||||||
*
|
|
||||||
* @param payChannel 支付渠道
|
|
||||||
* @param params 回调参数。
|
|
||||||
* 因为不同平台,能够提供的参数不同,所以使用 String 类型统一接收,然后在使用不同的 AbstractPaySDK 进行处理。
|
|
||||||
* @return 是否支付成功
|
|
||||||
*/
|
|
||||||
Boolean updateTransactionPaySuccess(Integer payChannel, String params);
|
|
||||||
|
|
||||||
List<PayTransactionBO> getTransactionList(Collection<Integer> ids);
|
List<PayTransactionBO> getTransactionList(Collection<Integer> ids);
|
||||||
|
|
||||||
PayTransactionPageBO getTransactionPage(PayTransactionPageDTO payTransactionPageDTO);
|
PayTransactionPageBO getTransactionPage(PayTransactionPageDTO payTransactionPageDTO);
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
package cn.iocoder.mall.pay.api.constant;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 支付通知类型
|
|
||||||
*/
|
|
||||||
public enum PayNotifyType {
|
|
||||||
|
|
||||||
TRANSACTION(1, "支付"),
|
|
||||||
REFUND(2, "退款"),
|
|
||||||
;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 类型
|
|
||||||
*/
|
|
||||||
private Integer value;
|
|
||||||
/**
|
|
||||||
* 名字
|
|
||||||
*/
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
PayNotifyType(Integer value, String name) {
|
|
||||||
this.value = value;
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PayNotifyType setValue(Integer value) {
|
|
||||||
this.value = value;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PayNotifyType setName(String name) {
|
|
||||||
this.name = name;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
package cn.iocoder.mall.pay.api.constant;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 支付交易通知状态枚举
|
|
||||||
*/
|
|
||||||
public enum PayTransactionNotifyStatusEnum {
|
|
||||||
|
|
||||||
WAITING(1, "等待通知"),
|
|
||||||
SUCCESS(2, "通知成功"),
|
|
||||||
FAILURE(3, "通知失败"), // 多次尝试,彻底失败
|
|
||||||
REQUEST_SUCCESS(4, "请求成功,但是结果失败"),
|
|
||||||
REQUEST_FAILURE(5, "请求失败"),
|
|
||||||
|
|
||||||
;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 状态
|
|
||||||
*/
|
|
||||||
private Integer value;
|
|
||||||
/**
|
|
||||||
* 名字
|
|
||||||
*/
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
PayTransactionNotifyStatusEnum(Integer value, String name) {
|
|
||||||
this.value = value;
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
package cn.iocoder.mall.pay.application.controller.users;
|
|
||||||
|
|
||||||
import cn.iocoder.common.framework.vo.CommonResult;
|
|
||||||
import cn.iocoder.mall.pay.api.PayRefundService;
|
|
||||||
import cn.iocoder.mall.pay.api.constant.PayChannelEnum;
|
|
||||||
import org.apache.dubbo.config.annotation.Reference;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.http.MediaType;
|
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("users/refund") // TODO 芋艿,理论来说,是用户无关的。这里先酱紫先~
|
|
||||||
public class UsersPayRefundController {
|
|
||||||
|
|
||||||
private Logger logger = LoggerFactory.getLogger(getClass());
|
|
||||||
|
|
||||||
@Reference(validation = "true", version = "${dubbo.provider.PayRefundService.version}")
|
|
||||||
private PayRefundService payRefundService;
|
|
||||||
|
|
||||||
@PostMapping(value = "pingxx_refund_success", consumes = MediaType.APPLICATION_JSON_VALUE)
|
|
||||||
public String pingxxRefundSuccess(HttpServletRequest request) throws IOException {
|
|
||||||
logger.info("[pingxxRefundSuccess][被回调]");
|
|
||||||
// 读取 webhook
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
try (BufferedReader reader = request.getReader()) {
|
|
||||||
String line;
|
|
||||||
while ((line = reader.readLine()) != null) {
|
|
||||||
sb.append(line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CommonResult<Boolean> result = payRefundService.updateRefundSuccess(PayChannelEnum.PINGXX.getId(), sb.toString());
|
|
||||||
if (result.isError()) {
|
|
||||||
logger.error("[pingxxRefundSuccess][message({}) result({})]", sb, result);
|
|
||||||
return "failure";
|
|
||||||
}
|
|
||||||
return "success";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
package cn.iocoder.mall.pay.biz.convert;
|
|
||||||
|
|
||||||
import cn.iocoder.mall.pay.api.message.PayRefundSuccessMessage;
|
|
||||||
import cn.iocoder.mall.pay.api.message.PayTransactionSuccessMessage;
|
|
||||||
import cn.iocoder.mall.pay.biz.dataobject.PayNotifyTaskDO;
|
|
||||||
import org.mapstruct.Mapper;
|
|
||||||
import org.mapstruct.Mapping;
|
|
||||||
import org.mapstruct.Mappings;
|
|
||||||
import org.mapstruct.factory.Mappers;
|
|
||||||
|
|
||||||
@Mapper
|
|
||||||
public interface PayNotifyConvert {
|
|
||||||
|
|
||||||
PayNotifyConvert INSTANCE = Mappers.getMapper(PayNotifyConvert.class);
|
|
||||||
|
|
||||||
@Mappings({
|
|
||||||
@Mapping(source = "transaction.transactionId", target = "transactionId"),
|
|
||||||
@Mapping(source = "transaction.orderId", target = "orderId"),
|
|
||||||
})
|
|
||||||
PayTransactionSuccessMessage convertTransaction(PayNotifyTaskDO payTransactionNotifyTaskDO);
|
|
||||||
|
|
||||||
@Mappings({
|
|
||||||
@Mapping(source = "refund.transactionId", target = "transactionId"),
|
|
||||||
@Mapping(source = "refund.orderId", target = "orderId"),
|
|
||||||
@Mapping(source = "refund.refundId", target = "refundId"),
|
|
||||||
})
|
|
||||||
PayRefundSuccessMessage convertRefund(PayNotifyTaskDO payTransactionNotifyTaskDO);
|
|
||||||
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
package cn.iocoder.mall.pay.biz.dao;
|
|
||||||
|
|
||||||
import cn.iocoder.mall.pay.biz.dataobject.PayNotifyLogDO;
|
|
||||||
import org.springframework.stereotype.Repository;
|
|
||||||
|
|
||||||
@Repository
|
|
||||||
public interface PayNotifyLogMapper {
|
|
||||||
|
|
||||||
void insert(PayNotifyLogDO entity);
|
|
||||||
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
package cn.iocoder.mall.pay.biz.dao;
|
|
||||||
|
|
||||||
import cn.iocoder.mall.pay.biz.dataobject.PayNotifyTaskDO;
|
|
||||||
import org.springframework.stereotype.Repository;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Repository
|
|
||||||
public interface PayNotifyTaskMapper {
|
|
||||||
|
|
||||||
void insert(PayNotifyTaskDO entity);
|
|
||||||
|
|
||||||
int update(PayNotifyTaskDO entity);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得需要通知的 PayTransactionNotifyTaskDO 记录。需要满足如下条件:
|
|
||||||
*
|
|
||||||
* 1. status 非成功
|
|
||||||
* 2. nextNotifyTime 小于当前时间
|
|
||||||
* 3. lastExecuteTime > nextNotifyTime
|
|
||||||
*
|
|
||||||
* @return PayTransactionNotifyTaskDO 数组
|
|
||||||
*/
|
|
||||||
List<PayNotifyTaskDO> selectByNotify();
|
|
||||||
|
|
||||||
}
|
|
@ -11,9 +11,6 @@ import java.util.List;
|
|||||||
@Repository
|
@Repository
|
||||||
public interface PayTransactionMapper {
|
public interface PayTransactionMapper {
|
||||||
|
|
||||||
int update(@Param("entity") PayTransactionDO entity,
|
|
||||||
@Param("whereStatus") Integer whereStatus);
|
|
||||||
|
|
||||||
int updateForRefundTotal(@Param("id") Integer id,
|
int updateForRefundTotal(@Param("id") Integer id,
|
||||||
@Param("refundTotalIncr") Integer refundTotalIncr);
|
@Param("refundTotalIncr") Integer refundTotalIncr);
|
||||||
|
|
||||||
|
@ -1,75 +0,0 @@
|
|||||||
package cn.iocoder.mall.pay.biz.service;
|
|
||||||
|
|
||||||
import cn.iocoder.common.framework.util.DateUtil;
|
|
||||||
import cn.iocoder.mall.pay.api.constant.PayNotifyType;
|
|
||||||
import cn.iocoder.mall.pay.api.constant.PayTransactionNotifyStatusEnum;
|
|
||||||
import cn.iocoder.mall.pay.api.message.PayRefundSuccessMessage;
|
|
||||||
import cn.iocoder.mall.pay.api.message.PayTransactionSuccessMessage;
|
|
||||||
import cn.iocoder.mall.pay.biz.convert.PayNotifyConvert;
|
|
||||||
import cn.iocoder.mall.pay.biz.dao.PayNotifyTaskMapper;
|
|
||||||
import cn.iocoder.mall.pay.biz.dataobject.PayNotifyTaskDO;
|
|
||||||
import cn.iocoder.mall.pay.biz.dataobject.PayRefundDO;
|
|
||||||
import cn.iocoder.mall.pay.biz.dataobject.PayTransactionDO;
|
|
||||||
import cn.iocoder.mall.pay.biz.dataobject.PayTransactionExtensionDO;
|
|
||||||
import org.apache.rocketmq.spring.core.RocketMQTemplate;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import java.util.Calendar;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
public class PayNotifyServiceImpl {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private PayNotifyTaskMapper payTransactionNotifyTaskMapper;
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private RocketMQTemplate rocketMQTemplate;
|
|
||||||
|
|
||||||
@Deprecated // 参见 PayRefundSuccessConsumer 类的说明
|
|
||||||
public void addRefundNotifyTask(PayRefundDO refund) {
|
|
||||||
PayNotifyTaskDO payTransactionNotifyTask = this.createBasePayNotifyTaskDO(refund.getAppId(), refund.getNotifyUrl())
|
|
||||||
.setType(PayNotifyType.REFUND.getValue());
|
|
||||||
// 设置 Refund 属性
|
|
||||||
payTransactionNotifyTask.setRefund(new PayNotifyTaskDO.Refund().setRefundId(refund.getId())
|
|
||||||
.setTransactionId(refund.getTransactionId()).setOrderId(refund.getOrderId()));
|
|
||||||
// 保存到数据库
|
|
||||||
payTransactionNotifyTaskMapper.insert(payTransactionNotifyTask);
|
|
||||||
// 发送 MQ 消息
|
|
||||||
sendNotifyMessage(payTransactionNotifyTask);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addTransactionNotifyTask(PayTransactionDO transaction, PayTransactionExtensionDO extension) {
|
|
||||||
PayNotifyTaskDO payTransactionNotifyTask = this.createBasePayNotifyTaskDO(transaction.getAppId(), transaction.getNotifyUrl())
|
|
||||||
.setType(PayNotifyType.TRANSACTION.getValue());
|
|
||||||
// 设置 Transaction 属性
|
|
||||||
payTransactionNotifyTask.setTransaction(new PayNotifyTaskDO.Transaction().setOrderId(transaction.getOrderId())
|
|
||||||
.setTransactionId(extension.getTransactionId()).setTransactionExtensionId(extension.getId()));
|
|
||||||
payTransactionNotifyTaskMapper.insert(payTransactionNotifyTask);
|
|
||||||
// 3.2 发送 MQ
|
|
||||||
sendNotifyMessage(payTransactionNotifyTask);
|
|
||||||
}
|
|
||||||
|
|
||||||
private PayNotifyTaskDO createBasePayNotifyTaskDO(String appId, String notifyUrl) {
|
|
||||||
return new PayNotifyTaskDO()
|
|
||||||
.setAppId(appId)
|
|
||||||
.setStatus(PayTransactionNotifyStatusEnum.WAITING.getValue())
|
|
||||||
.setNotifyTimes(0).setMaxNotifyTimes(PayNotifyTaskDO.NOTIFY_FREQUENCY.length + 1)
|
|
||||||
.setNextNotifyTime(DateUtil.addDate(Calendar.SECOND, PayNotifyTaskDO.NOTIFY_FREQUENCY[0]))
|
|
||||||
.setNotifyUrl(notifyUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendNotifyMessage(PayNotifyTaskDO notifyTask) {
|
|
||||||
if (PayNotifyType.TRANSACTION.getValue().equals(notifyTask.getType())) {
|
|
||||||
rocketMQTemplate.convertAndSend(PayTransactionSuccessMessage.TOPIC,
|
|
||||||
PayNotifyConvert.INSTANCE.convertTransaction(notifyTask));
|
|
||||||
} else if (PayNotifyType.REFUND.getValue().equals(notifyTask.getType())) {
|
|
||||||
rocketMQTemplate.convertAndSend(PayRefundSuccessMessage.TOPIC,
|
|
||||||
PayNotifyConvert.INSTANCE.convertRefund(notifyTask));
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException(String.format("通知任务(%s) 无法发送通知消息", notifyTask.toString()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -50,63 +50,6 @@ public class PayTransactionServiceImpl implements PayTransactionService {
|
|||||||
return payTransactionExtensionMapper.selectById(id);
|
return payTransactionExtensionMapper.selectById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
@Transactional
|
|
||||||
public Boolean updateTransactionPaySuccess(Integer payChannel, String params) {
|
|
||||||
// TODO 芋艿,记录回调日志
|
|
||||||
// 解析传入的参数,成 TransactionSuccessBO 对象
|
|
||||||
AbstractPaySDK paySDK = PaySDKFactory.getSDK(payChannel);
|
|
||||||
CommonResult<TransactionSuccessBO> paySuccessResult = paySDK.parseTransactionSuccessParams(params);
|
|
||||||
if (paySuccessResult.isError()) {
|
|
||||||
throw ServiceExceptionUtil.exception(paySuccessResult.getCode(), paySuccessResult.getMessage());
|
|
||||||
}
|
|
||||||
// TODO 芋艿,先最严格的校验。即使调用方重复调用,实际哪个订单已经被重复回调的支付,也返回 false 。也没问题,因为实际已经回调成功了。
|
|
||||||
// 1.1 查询 PayTransactionExtensionDO
|
|
||||||
PayTransactionExtensionDO extension = payTransactionExtensionMapper.selectByTransactionCode(paySuccessResult.getData().getTransactionCode());
|
|
||||||
if (extension == null) {
|
|
||||||
throw ServiceExceptionUtil.exception(PayErrorCodeEnum.PAY_TRANSACTION_EXTENSION_NOT_FOUND.getCode());
|
|
||||||
}
|
|
||||||
if (!PayTransactionStatusEnum.WAITING.getValue().equals(extension.getStatus())) { // 校验状态,必须是待支付
|
|
||||||
throw ServiceExceptionUtil.exception(PayErrorCodeEnum.PAY_TRANSACTION_EXTENSION_STATUS_IS_NOT_WAITING.getCode());
|
|
||||||
}
|
|
||||||
// 1.2 更新 PayTransactionExtensionDO
|
|
||||||
PayTransactionExtensionDO updatePayTransactionExtension = new PayTransactionExtensionDO()
|
|
||||||
.setId(extension.getId())
|
|
||||||
.setStatus(PayTransactionStatusEnum.SUCCESS.getValue())
|
|
||||||
.setExtensionData(params);
|
|
||||||
int updateCounts = payTransactionExtensionMapper.update(updatePayTransactionExtension, PayTransactionStatusEnum.WAITING.getValue());
|
|
||||||
if (updateCounts == 0) { // 校验状态,必须是待支付
|
|
||||||
throw ServiceExceptionUtil.exception(PayErrorCodeEnum.PAY_TRANSACTION_EXTENSION_STATUS_IS_NOT_WAITING.getCode());
|
|
||||||
}
|
|
||||||
logger.info("[updateTransactionPaySuccess][PayTransactionExtensionDO({}) 更新为已支付]", extension.getId());
|
|
||||||
// 2.1 判断 PayTransactionDO 是否处于待支付
|
|
||||||
PayTransactionDO transaction = payTransactionMapper.selectById(extension.getTransactionId());
|
|
||||||
if (transaction == null) {
|
|
||||||
throw ServiceExceptionUtil.exception(PayErrorCodeEnum.PAY_TRANSACTION_NOT_FOUND.getCode());
|
|
||||||
}
|
|
||||||
if (!PayTransactionStatusEnum.WAITING.getValue().equals(transaction.getStatus())) { // 校验状态,必须是待支付
|
|
||||||
throw ServiceExceptionUtil.exception(PayErrorCodeEnum.PAY_TRANSACTION_STATUS_IS_NOT_WAITING.getCode());
|
|
||||||
}
|
|
||||||
// 2.2 更新 PayTransactionDO
|
|
||||||
PayTransactionDO updatePayTransaction = new PayTransactionDO()
|
|
||||||
.setId(transaction.getId())
|
|
||||||
.setStatus(PayTransactionStatusEnum.SUCCESS.getValue())
|
|
||||||
.setExtensionId(extension.getId())
|
|
||||||
.setPayChannel(payChannel)
|
|
||||||
.setPaymentTime(paySuccessResult.getData().getPaymentTime())
|
|
||||||
.setNotifyTime(new Date())
|
|
||||||
.setTradeNo(paySuccessResult.getData().getTradeNo());
|
|
||||||
updateCounts = payTransactionMapper.update(updatePayTransaction, PayTransactionStatusEnum.WAITING.getValue());
|
|
||||||
if (updateCounts == 0) { // 校验状态,必须是待支付 TODO 这种类型,需要思考下。需要返回错误,但是又要保证事务回滚
|
|
||||||
throw ServiceExceptionUtil.exception(PayErrorCodeEnum.PAY_TRANSACTION_STATUS_IS_NOT_WAITING.getCode());
|
|
||||||
}
|
|
||||||
logger.info("[updateTransactionPaySuccess][PayTransactionDO({}) 更新为已支付]", transaction.getId());
|
|
||||||
// 3 新增 PayNotifyTaskDO 注释原因,参见 PayRefundSuccessConsumer 类。
|
|
||||||
// payNotifyService.addTransactionNotifyTask(transaction, extension);
|
|
||||||
// 返回结果
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<PayTransactionBO> getTransactionList(Collection<Integer> ids) {
|
public List<PayTransactionBO> getTransactionList(Collection<Integer> ids) {
|
||||||
return PayTransactionConvert.INSTANCE.convertList(payTransactionMapper.selectListByIds(ids));
|
return PayTransactionConvert.INSTANCE.convertList(payTransactionMapper.selectListByIds(ids));
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
<?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="cn.iocoder.mall.pay.biz.dao.PayNotifyLogMapper">
|
|
||||||
|
|
||||||
<!--<sql id="FIELDS">-->
|
|
||||||
<!--id, transaction_id, transaction_extension_id, app_id, order_id,-->
|
|
||||||
<!--status, next_notify_time, last_execute_time, notify_times, max_notify_times,-->
|
|
||||||
<!--create_time-->
|
|
||||||
<!--</sql>-->
|
|
||||||
|
|
||||||
<insert id="insert" parameterType="PayNotifyLogDO" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
|
|
||||||
INSERT INTO notify_log (
|
|
||||||
notify_id, request, response, status
|
|
||||||
) VALUES (
|
|
||||||
#{notifyId}, #{request}, #{response}, #{status}
|
|
||||||
)
|
|
||||||
</insert>
|
|
||||||
|
|
||||||
<!--<update id="update" parameterType="PayTransactionNotifyTaskDO">-->
|
|
||||||
<!--UPDATE transaction_notify_task-->
|
|
||||||
<!--<set>-->
|
|
||||||
<!--<if test="status != null">-->
|
|
||||||
<!--, status = #{status}-->
|
|
||||||
<!--</if>-->
|
|
||||||
<!--<if test="nextNotifyTime != null">-->
|
|
||||||
<!--, last_notify_time = #{nextNotifyTime}-->
|
|
||||||
<!--</if>-->
|
|
||||||
<!--<if test="lastExecuteTime != null">-->
|
|
||||||
<!--, last_execute_time = #{lastExecuteTime}-->
|
|
||||||
<!--</if>-->
|
|
||||||
<!--<if test="notifyTimes != null">-->
|
|
||||||
<!--, notify_times = #{notifyTimes}-->
|
|
||||||
<!--</if>-->
|
|
||||||
<!--</set>-->
|
|
||||||
<!--WHERE id = #{id}-->
|
|
||||||
<!--</update>-->
|
|
||||||
|
|
||||||
<!--<select id="selectByTransactionCode" parameterType="String" resultType="PayTransactionExtensionDO">-->
|
|
||||||
<!--SELECT-->
|
|
||||||
<!--<include refid="FIELDS"/>-->
|
|
||||||
<!--FROM transaction_extension-->
|
|
||||||
<!--WHERE transaction_code = #{transactionCode}-->
|
|
||||||
<!--LIMIT 1-->
|
|
||||||
<!--</select>-->
|
|
||||||
|
|
||||||
</mapper>
|
|
@ -1,61 +0,0 @@
|
|||||||
<?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="cn.iocoder.mall.pay.biz.dao.PayNotifyTaskMapper">
|
|
||||||
|
|
||||||
<sql id="FIELDS">
|
|
||||||
id, app_id, type,
|
|
||||||
status, next_notify_time, last_execute_time, notify_times, max_notify_times,
|
|
||||||
create_time
|
|
||||||
</sql>
|
|
||||||
|
|
||||||
<resultMap id="PayNotifyTaskResultMap" type="PayNotifyTaskDO">
|
|
||||||
<result property="transaction" column="transaction"
|
|
||||||
javaType="cn.iocoder.mall.pay.biz.dataobject.PayNotifyTaskDO$Transaction"
|
|
||||||
typeHandler="cn.iocoder.mall.mybatis.core.type.JSONTypeHandler"/>
|
|
||||||
<result property="refund" column="refund"
|
|
||||||
javaType="cn.iocoder.mall.pay.biz.dataobject.PayNotifyTaskDO$Refund"
|
|
||||||
typeHandler="cn.iocoder.mall.mybatis.core.type.JSONTypeHandler"/>
|
|
||||||
</resultMap>
|
|
||||||
|
|
||||||
<insert id="insert" parameterType="PayNotifyTaskDO" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
|
|
||||||
INSERT INTO notify_task (
|
|
||||||
app_id, type,
|
|
||||||
status, next_notify_time, notify_times, max_notify_times,
|
|
||||||
`transaction`, refund
|
|
||||||
) VALUES (
|
|
||||||
#{appId}, #{type},
|
|
||||||
#{status}, #{nextNotifyTime}, #{notifyTimes}, #{maxNotifyTimes},
|
|
||||||
#{transaction, typeHandler=cn.iocoder.common.framework.mybatis.JSONTypeHandler},
|
|
||||||
#{refund, typeHandler=cn.iocoder.common.framework.mybatis.JSONTypeHandler}
|
|
||||||
)
|
|
||||||
</insert>
|
|
||||||
|
|
||||||
<update id="update" parameterType="PayNotifyTaskDO">
|
|
||||||
UPDATE notify_task
|
|
||||||
<set>
|
|
||||||
<if test="status != null">
|
|
||||||
, status = #{status}
|
|
||||||
</if>
|
|
||||||
<if test="nextNotifyTime != null">
|
|
||||||
, next_notify_time = #{nextNotifyTime}
|
|
||||||
</if>
|
|
||||||
<if test="lastExecuteTime != null">
|
|
||||||
, last_execute_time = #{lastExecuteTime}
|
|
||||||
</if>
|
|
||||||
<if test="notifyTimes != null">
|
|
||||||
, notify_times = #{notifyTimes}
|
|
||||||
</if>
|
|
||||||
</set>
|
|
||||||
WHERE id = #{id}
|
|
||||||
</update>
|
|
||||||
|
|
||||||
<select id="selectByNotify" resultMap="PayNotifyTaskResultMap">
|
|
||||||
SELECT
|
|
||||||
<include refid="FIELDS"/>
|
|
||||||
FROM notify_task
|
|
||||||
WHERE status IN (1, 4, 5)
|
|
||||||
AND next_notify_time <![CDATA[ <= ]]> NOW()
|
|
||||||
AND last_execute_time > next_notify_time
|
|
||||||
</select>
|
|
||||||
|
|
||||||
</mapper>
|
|
@ -7,32 +7,6 @@
|
|||||||
create_ip, status, create_time
|
create_ip, status, create_time
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<insert id="insert" parameterType="PayTransactionExtensionDO" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
|
|
||||||
INSERT INTO transaction_extension (
|
|
||||||
transaction_id, pay_channel, transaction_code, extension_data,
|
|
||||||
create_ip, status
|
|
||||||
) VALUES (
|
|
||||||
#{transactionId}, #{payChannel}, #{transactionCode}, #{extensionData},
|
|
||||||
#{createIp}, #{status}
|
|
||||||
)
|
|
||||||
</insert>
|
|
||||||
|
|
||||||
<update id="update">
|
|
||||||
UPDATE transaction_extension
|
|
||||||
<set>
|
|
||||||
<if test="entity.extensionData != null">
|
|
||||||
, extension_data = #{entity.extensionData}
|
|
||||||
</if>
|
|
||||||
<if test="entity.status != null">
|
|
||||||
, status = #{entity.status}
|
|
||||||
</if>
|
|
||||||
</set>
|
|
||||||
WHERE id = #{entity.id}
|
|
||||||
<if test="whereStatus != null">
|
|
||||||
AND status = #{whereStatus}
|
|
||||||
</if>
|
|
||||||
</update>
|
|
||||||
|
|
||||||
<select id="selectByTransactionCode" parameterType="String" resultType="PayTransactionExtensionDO">
|
<select id="selectByTransactionCode" parameterType="String" resultType="PayTransactionExtensionDO">
|
||||||
SELECT
|
SELECT
|
||||||
<include refid="FIELDS"/>
|
<include refid="FIELDS"/>
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
package cn.iocoder.mall.payservice.enums.notify;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付通知状态枚举
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public enum PayNotifyStatusEnum {
|
||||||
|
|
||||||
|
WAITING(1, "等待通知"),
|
||||||
|
SUCCESS(2, "通知成功"),
|
||||||
|
FAILURE(3, "通知失败"), // 多次尝试,彻底失败
|
||||||
|
REQUEST_SUCCESS(4, "请求成功,但是结果失败"),
|
||||||
|
REQUEST_FAILURE(5, "请求失败"),
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态
|
||||||
|
*/
|
||||||
|
private final Integer status;
|
||||||
|
/**
|
||||||
|
* 名字
|
||||||
|
*/
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
PayNotifyStatusEnum(Integer status, String name) {
|
||||||
|
this.status = status;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package cn.iocoder.mall.payservice.enums.notify;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付通知类型
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public enum PayNotifyType {
|
||||||
|
|
||||||
|
TRANSACTION(1, "支付"),
|
||||||
|
REFUND(2, "退款"),
|
||||||
|
;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型
|
||||||
|
*/
|
||||||
|
private final Integer type;
|
||||||
|
/**
|
||||||
|
* 名字
|
||||||
|
*/
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
PayNotifyType(Integer type, String name) {
|
||||||
|
this.type = type;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,8 +1,11 @@
|
|||||||
package cn.iocoder.mall.payservice.enums.transaction;
|
package cn.iocoder.mall.payservice.enums.transaction;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 支付交易状态枚举
|
* 支付交易状态枚举
|
||||||
*/
|
*/
|
||||||
|
@Getter
|
||||||
public enum PayTransactionStatusEnum {
|
public enum PayTransactionStatusEnum {
|
||||||
|
|
||||||
WAITING(1, "等待支付"),
|
WAITING(1, "等待支付"),
|
||||||
@ -13,33 +16,15 @@ public enum PayTransactionStatusEnum {
|
|||||||
/**
|
/**
|
||||||
* 状态
|
* 状态
|
||||||
*/
|
*/
|
||||||
private Integer value;
|
private final Integer status;
|
||||||
/**
|
/**
|
||||||
* 名字
|
* 名字
|
||||||
*/
|
*/
|
||||||
private String name;
|
private final String name;
|
||||||
|
|
||||||
PayTransactionStatusEnum(Integer value, String name) {
|
PayTransactionStatusEnum(Integer status, String name) {
|
||||||
this.value = value;
|
this.status = status;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PayTransactionStatusEnum setValue(Integer value) {
|
|
||||||
this.value = value;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PayTransactionStatusEnum setName(String name) {
|
|
||||||
this.name = name;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -32,4 +32,12 @@ public interface PayTransactionRpc {
|
|||||||
*/
|
*/
|
||||||
CommonResult<PayTransactionRespDTO> getPayTransaction(PayTransactionGetReqDTO getReqDTO);
|
CommonResult<PayTransactionRespDTO> getPayTransaction(PayTransactionGetReqDTO getReqDTO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新交易支付成功
|
||||||
|
*
|
||||||
|
* @param successReqDTO 支付成功信息
|
||||||
|
* @return 是否成功
|
||||||
|
*/
|
||||||
|
CommonResult<Boolean> updatePayTransactionSuccess(PayTransactionSuccessReqDTO successReqDTO);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,14 @@ import lombok.Data;
|
|||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
import javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 支付交易获得 Request DTO
|
* 支付交易获得 Request DTO
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@Accessors(chain = true)
|
@Accessors(chain = true)
|
||||||
public class PayTransactionGetReqDTO {
|
public class PayTransactionGetReqDTO implements Serializable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 应用编号
|
* 应用编号
|
||||||
|
@ -3,6 +3,7 @@ package cn.iocoder.mall.payservice.rpc.transaction.dto;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -10,7 +11,7 @@ import java.util.Date;
|
|||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@Accessors(chain = true)
|
@Accessors(chain = true)
|
||||||
public class PayTransactionRespDTO {
|
public class PayTransactionRespDTO implements Serializable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 编号,自增
|
* 编号,自增
|
||||||
|
@ -7,13 +7,14 @@ import lombok.experimental.Accessors;
|
|||||||
|
|
||||||
import javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotEmpty;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 支付交易提交 Request VO
|
* 支付交易提交 Request VO
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@Accessors(chain = true)
|
@Accessors(chain = true)
|
||||||
public class PayTransactionSubmitReqDTO {
|
public class PayTransactionSubmitReqDTO implements Serializable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 应用编号
|
* 应用编号
|
||||||
|
@ -3,12 +3,14 @@ package cn.iocoder.mall.payservice.rpc.transaction.dto;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 支付交易提交 Response DTO
|
* 支付交易提交 Response DTO
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@Accessors(chain = true)
|
@Accessors(chain = true)
|
||||||
public class PayTransactionSubmitRespDTO {
|
public class PayTransactionSubmitRespDTO implements Serializable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 支付交易拓展单编号
|
* 支付交易拓展单编号
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
package cn.iocoder.mall.payservice.rpc.transaction.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 交易支付成功 Request DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class PayTransactionSuccessReqDTO implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付渠道
|
||||||
|
*/
|
||||||
|
private Integer payChannel;
|
||||||
|
/**
|
||||||
|
* 支付渠道的回调参数
|
||||||
|
*/
|
||||||
|
private String params;
|
||||||
|
|
||||||
|
}
|
@ -31,6 +31,12 @@
|
|||||||
<artifactId>spring-boot-starter-web</artifactId> <!-- 需要开启 Web 容器,因为 Actuator 需要使用到 -->
|
<artifactId>spring-boot-starter-web</artifactId> <!-- 需要开启 Web 容器,因为 Actuator 需要使用到 -->
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- MQ 相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.mall</groupId>
|
||||||
|
<artifactId>mall-spring-boot-starter-rocketmq</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Registry 和 Config 相关 -->
|
<!-- Registry 和 Config 相关 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.alibaba.cloud</groupId>
|
<groupId>com.alibaba.cloud</groupId>
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
package cn.iocoder.mall.payservice.convert.notify;
|
||||||
|
|
||||||
|
import cn.iocoder.mall.payservice.dal.mysql.dataobject.notify.PayNotifyTaskDO;
|
||||||
|
import cn.iocoder.mall.payservice.mq.producer.message.PayRefundSuccessMessage;
|
||||||
|
import cn.iocoder.mall.payservice.mq.producer.message.PayTransactionSuccessMessage;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface PayNotifyConvert {
|
||||||
|
|
||||||
|
PayNotifyConvert INSTANCE = Mappers.getMapper(PayNotifyConvert.class);
|
||||||
|
|
||||||
|
PayTransactionSuccessMessage convertTransaction(PayNotifyTaskDO payTransactionNotifyTaskDO);
|
||||||
|
|
||||||
|
PayRefundSuccessMessage convertRefund(PayNotifyTaskDO payTransactionNotifyTaskDO);
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package cn.iocoder.mall.pay.biz.dataobject;
|
package cn.iocoder.mall.payservice.dal.mysql.dataobject.log;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
@ -1,7 +1,10 @@
|
|||||||
package cn.iocoder.mall.pay.biz.dataobject;
|
package cn.iocoder.mall.payservice.dal.mysql.dataobject.notify;
|
||||||
|
|
||||||
import cn.iocoder.common.framework.dataobject.DeletableDO;
|
import cn.iocoder.mall.mybatis.core.dataobject.DeletableDO;
|
||||||
|
import cn.iocoder.mall.payservice.enums.notify.PayNotifyStatusEnum;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -9,7 +12,9 @@ import lombok.experimental.Accessors;
|
|||||||
*
|
*
|
||||||
* 通过该表,记录通知 App 时,产生的日志
|
* 通过该表,记录通知 App 时,产生的日志
|
||||||
*/
|
*/
|
||||||
|
@TableName("pay_notify_log")
|
||||||
@Data
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@Accessors(chain = true)
|
@Accessors(chain = true)
|
||||||
public class PayNotifyLogDO extends DeletableDO {
|
public class PayNotifyLogDO extends DeletableDO {
|
||||||
|
|
||||||
@ -32,7 +37,7 @@ public class PayNotifyLogDO extends DeletableDO {
|
|||||||
/**
|
/**
|
||||||
* 状态
|
* 状态
|
||||||
*
|
*
|
||||||
* @see cn.iocoder.mall.pay.api.constant.PayTransactionNotifyStatusEnum
|
* 外键 {@link PayNotifyStatusEnum}
|
||||||
*/
|
*/
|
||||||
private Integer status;
|
private Integer status;
|
||||||
|
|
@ -1,8 +1,16 @@
|
|||||||
package cn.iocoder.mall.pay.biz.dataobject;
|
package cn.iocoder.mall.payservice.dal.mysql.dataobject.notify;
|
||||||
|
|
||||||
import cn.iocoder.common.framework.dataobject.DeletableDO;
|
import cn.iocoder.mall.mybatis.core.dataobject.DeletableDO;
|
||||||
import cn.iocoder.mall.pay.biz.service.PayTransactionServiceImpl;
|
import cn.iocoder.mall.payservice.dal.mysql.dataobject.transaction.PayTransactionDO;
|
||||||
|
import cn.iocoder.mall.payservice.dal.mysql.dataobject.transaction.PayTransactionExtensionDO;
|
||||||
|
import cn.iocoder.mall.payservice.enums.notify.PayNotifyStatusEnum;
|
||||||
|
import cn.iocoder.mall.payservice.enums.notify.PayNotifyType;
|
||||||
|
import cn.iocoder.mall.payservice.service.transaction.PayTransactionService;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@ -12,7 +20,9 @@ import java.util.Date;
|
|||||||
*
|
*
|
||||||
* 目前包括支付通知、退款通知。
|
* 目前包括支付通知、退款通知。
|
||||||
*/
|
*/
|
||||||
|
@TableName("pay_notify_task")
|
||||||
@Data
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@Accessors(chain = true)
|
@Accessors(chain = true)
|
||||||
public class PayNotifyTaskDO extends DeletableDO {
|
public class PayNotifyTaskDO extends DeletableDO {
|
||||||
|
|
||||||
@ -37,13 +47,13 @@ public class PayNotifyTaskDO extends DeletableDO {
|
|||||||
/**
|
/**
|
||||||
* 类型
|
* 类型
|
||||||
*
|
*
|
||||||
* @see cn.iocoder.mall.pay.api.constant.PayNotifyType
|
* 外键 {@link PayNotifyType}
|
||||||
*/
|
*/
|
||||||
private Integer type;
|
private Integer type;
|
||||||
/**
|
/**
|
||||||
* 通知状态
|
* 通知状态
|
||||||
*
|
*
|
||||||
* @see cn.iocoder.mall.pay.api.constant.PayTransactionNotifyStatusEnum
|
* 外键 {@link PayNotifyStatusEnum}
|
||||||
*/
|
*/
|
||||||
private Integer status;
|
private Integer status;
|
||||||
/**
|
/**
|
||||||
@ -55,7 +65,7 @@ public class PayNotifyTaskDO extends DeletableDO {
|
|||||||
*
|
*
|
||||||
* 这个字段,需要结合 {@link #nextNotifyTime} 一起使用。
|
* 这个字段,需要结合 {@link #nextNotifyTime} 一起使用。
|
||||||
*
|
*
|
||||||
* 1. 初始时,{@link PayTransactionServiceImpl#updateTransactionPaySuccess(Integer, String)}
|
* 1. 初始时,{@link PayTransactionService#updateTransactionPaySuccess(Integer, String)}
|
||||||
* nextNotifyTime 为当前时间 + 15 秒
|
* nextNotifyTime 为当前时间 + 15 秒
|
||||||
* lastExecuteTime 为空
|
* lastExecuteTime 为空
|
||||||
* 并发送给 MQ ,执行执行
|
* 并发送给 MQ ,执行执行
|
||||||
@ -83,10 +93,12 @@ public class PayNotifyTaskDO extends DeletableDO {
|
|||||||
/**
|
/**
|
||||||
* 支付数据
|
* 支付数据
|
||||||
*/
|
*/
|
||||||
|
@TableField(typeHandler = FastjsonTypeHandler.class)
|
||||||
private Transaction transaction;
|
private Transaction transaction;
|
||||||
/**
|
/**
|
||||||
* 退款数据
|
* 退款数据
|
||||||
*/
|
*/
|
||||||
|
@TableField(typeHandler = FastjsonTypeHandler.class)
|
||||||
private Refund refund;
|
private Refund refund;
|
||||||
|
|
||||||
@Data
|
@Data
|
@ -0,0 +1,10 @@
|
|||||||
|
package cn.iocoder.mall.payservice.dal.mysql.mapper.notify;
|
||||||
|
|
||||||
|
import cn.iocoder.mall.payservice.dal.mysql.dataobject.notify.PayNotifyLogDO;
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface PayNotifyLogMapper extends BaseMapper<PayNotifyLogDO> {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package cn.iocoder.mall.payservice.dal.mysql.mapper.notify;
|
||||||
|
|
||||||
|
import cn.iocoder.mall.payservice.dal.mysql.dataobject.notify.PayNotifyTaskDO;
|
||||||
|
import cn.iocoder.mall.payservice.enums.notify.PayNotifyStatusEnum;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface PayNotifyTaskMapper extends BaseMapper<PayNotifyTaskDO> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得需要通知的 PayTransactionNotifyTaskDO 记录。需要满足如下条件:
|
||||||
|
*
|
||||||
|
* 1. status 非成功
|
||||||
|
* 2. nextNotifyTime 小于当前时间
|
||||||
|
* 3. lastExecuteTime > nextNotifyTime
|
||||||
|
*
|
||||||
|
* @return PayTransactionNotifyTaskDO 数组
|
||||||
|
*/
|
||||||
|
default List<PayNotifyTaskDO> selectListByNotify() {
|
||||||
|
return selectList(new QueryWrapper<PayNotifyTaskDO>()
|
||||||
|
.in("status", PayNotifyStatusEnum.WAITING.getName(), PayNotifyStatusEnum.REQUEST_SUCCESS.getName(),
|
||||||
|
PayNotifyStatusEnum.REQUEST_FAILURE.getName())
|
||||||
|
.le("next_notify_time", "NOW()")
|
||||||
|
.gt("last_execute_time", "next_notify_time"));
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// <select id="selectByNotify" resultMap="PayNotifyTaskResultMap">
|
||||||
|
// SELECT
|
||||||
|
// <include refid="FIELDS"/>
|
||||||
|
// FROM notify_task
|
||||||
|
// WHERE status IN (1, 4, 5)
|
||||||
|
// AND next_notify_time <![CDATA[ <= ]]> NOW()
|
||||||
|
// AND last_execute_time > next_notify_time
|
||||||
|
// </select>
|
||||||
|
|
||||||
|
}
|
@ -13,6 +13,11 @@ public interface PayTransactionMapper extends BaseMapper<PayTransactionDO> {
|
|||||||
// new QueryWrapperX<PayTransactionDO>());
|
// new QueryWrapperX<PayTransactionDO>());
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
default int update(PayTransactionDO entity, Integer whereStatus) {
|
||||||
|
return update(entity, new QueryWrapper<PayTransactionDO>()
|
||||||
|
.eq("id", entity.getId()).eq("status", whereStatus));
|
||||||
|
}
|
||||||
|
|
||||||
default PayTransactionDO selectByAppIdAndOrderId(String appId, String orderId) {
|
default PayTransactionDO selectByAppIdAndOrderId(String appId, String orderId) {
|
||||||
return selectOne(new QueryWrapper<PayTransactionDO>().eq("app_id", appId)
|
return selectOne(new QueryWrapper<PayTransactionDO>().eq("app_id", appId)
|
||||||
.eq("order_id", orderId));
|
.eq("order_id", orderId));
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
package cn.iocoder.mall.payservice.mq.producer;
|
||||||
|
|
||||||
|
import cn.iocoder.mall.payservice.mq.producer.message.PayRefundSuccessMessage;
|
||||||
|
import cn.iocoder.mall.payservice.mq.producer.message.PayTransactionSuccessMessage;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.rocketmq.client.producer.SendResult;
|
||||||
|
import org.apache.rocketmq.client.producer.SendStatus;
|
||||||
|
import org.apache.rocketmq.spring.core.RocketMQTemplate;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
// TODO 芋艿:后续优化下,考虑下一致性
|
||||||
|
public class PayMQProducer {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RocketMQTemplate template;
|
||||||
|
|
||||||
|
public void sendPayRefundNotifyTaskMessage(PayRefundSuccessMessage message, Integer refundId, Integer transactionId, String orderId) {
|
||||||
|
message.setRefundId(refundId).setTransactionId(transactionId).setOrderId(orderId);
|
||||||
|
try {
|
||||||
|
SendResult sendResult = template.syncSend(PayTransactionSuccessMessage.TOPIC, message);
|
||||||
|
if (!SendStatus.SEND_OK.equals(sendResult.getSendStatus())) {
|
||||||
|
log.error("[sendPayRefundNotifyTaskMessage][消息({}) 发送更新消息失败,结果为({})]", message, sendResult);
|
||||||
|
}
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
log.error("[sendPayRefundNotifyTaskMessage][消息({}) 发送更新消息失败,发生异常]", message, throwable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendPayTransactionNotifyTaskMessage(PayTransactionSuccessMessage message, Integer transactionId, String orderId) {
|
||||||
|
message.setTransactionId(transactionId).setOrderId(orderId);
|
||||||
|
try {
|
||||||
|
SendResult sendResult = template.syncSend(PayTransactionSuccessMessage.TOPIC, message);
|
||||||
|
if (!SendStatus.SEND_OK.equals(sendResult.getSendStatus())) {
|
||||||
|
log.error("[sendPayTransactionNotifyTaskMessage][消息({}) 发送更新消息失败,结果为({})]", message, sendResult);
|
||||||
|
}
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
log.error("[sendPayTransactionNotifyTaskMessage][消息({}) 发送更新消息失败,发生异常]", message, throwable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package cn.iocoder.mall.pay.api.message;
|
package cn.iocoder.mall.payservice.mq.producer.message;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
|
|
@ -1,6 +1,7 @@
|
|||||||
package cn.iocoder.mall.pay.api.message;
|
package cn.iocoder.mall.payservice.mq.producer.message;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -8,6 +9,7 @@ import lombok.experimental.Accessors;
|
|||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@Accessors(chain = true)
|
@Accessors(chain = true)
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
public class PayRefundSuccessMessage extends AbstractPayNotifySuccessMessage {
|
public class PayRefundSuccessMessage extends AbstractPayNotifySuccessMessage {
|
||||||
|
|
||||||
public static final String TOPIC = "PAY_REFUND_SUCCESS";
|
public static final String TOPIC = "PAY_REFUND_SUCCESS";
|
@ -1,6 +1,7 @@
|
|||||||
package cn.iocoder.mall.pay.api.message;
|
package cn.iocoder.mall.payservice.mq.producer.message;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -8,6 +9,7 @@ import lombok.experimental.Accessors;
|
|||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@Accessors(chain = true)
|
@Accessors(chain = true)
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
public class PayTransactionSuccessMessage extends AbstractPayNotifySuccessMessage {
|
public class PayTransactionSuccessMessage extends AbstractPayNotifySuccessMessage {
|
||||||
|
|
||||||
public static final String TOPIC = "PAY_TRANSACTION_SUCCESS";
|
public static final String TOPIC = "PAY_TRANSACTION_SUCCESS";
|
@ -29,4 +29,10 @@ public class PayTransactionRpcImpl implements PayTransactionRpc {
|
|||||||
return success(payTransactionService.getPayTransaction(getReqDTO));
|
return success(payTransactionService.getPayTransaction(getReqDTO));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommonResult<Boolean> updatePayTransactionSuccess(PayTransactionSuccessReqDTO successReqDTO) {
|
||||||
|
return success(payTransactionService.updateTransactionPaySuccess(successReqDTO.getPayChannel(),
|
||||||
|
successReqDTO.getParams()));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
package cn.iocoder.mall.payservice.service.notify;
|
||||||
|
|
||||||
|
import cn.iocoder.mall.payservice.dal.mysql.dataobject.refund.PayRefundDO;
|
||||||
|
import cn.iocoder.mall.payservice.dal.mysql.dataobject.transaction.PayTransactionDO;
|
||||||
|
import cn.iocoder.mall.payservice.dal.mysql.dataobject.transaction.PayTransactionExtensionDO;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付通知 Service 接口
|
||||||
|
*/
|
||||||
|
public interface PayNotifyService {
|
||||||
|
|
||||||
|
// TODO 芋艿:后续优化下,不要暴露 entity 出来
|
||||||
|
void addPayRefundNotifyTask(PayRefundDO refund);
|
||||||
|
|
||||||
|
// TODO 芋艿:后续优化下,不要暴露 entity 出来
|
||||||
|
void addPayTransactionNotifyTask(PayTransactionDO transaction, PayTransactionExtensionDO extension);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
package cn.iocoder.mall.payservice.service.notify.impl;
|
||||||
|
|
||||||
|
import cn.iocoder.common.framework.util.DateUtil;
|
||||||
|
import cn.iocoder.mall.payservice.convert.notify.PayNotifyConvert;
|
||||||
|
import cn.iocoder.mall.payservice.dal.mysql.dataobject.notify.PayNotifyTaskDO;
|
||||||
|
import cn.iocoder.mall.payservice.dal.mysql.dataobject.refund.PayRefundDO;
|
||||||
|
import cn.iocoder.mall.payservice.dal.mysql.dataobject.transaction.PayTransactionDO;
|
||||||
|
import cn.iocoder.mall.payservice.dal.mysql.dataobject.transaction.PayTransactionExtensionDO;
|
||||||
|
import cn.iocoder.mall.payservice.dal.mysql.mapper.notify.PayNotifyTaskMapper;
|
||||||
|
import cn.iocoder.mall.payservice.enums.notify.PayNotifyStatusEnum;
|
||||||
|
import cn.iocoder.mall.payservice.enums.notify.PayNotifyType;
|
||||||
|
import cn.iocoder.mall.payservice.mq.producer.PayMQProducer;
|
||||||
|
import cn.iocoder.mall.payservice.service.notify.PayNotifyService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Calendar;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付通知 Service 实现类
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class PayNotifyServiceImpl implements PayNotifyService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PayNotifyTaskMapper payNotifyTaskMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PayMQProducer payMQProducer;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addPayRefundNotifyTask(PayRefundDO refund) {
|
||||||
|
PayNotifyTaskDO payNotifyTaskDO = this.createBasePayNotifyTaskDO(refund.getAppId(), refund.getNotifyUrl())
|
||||||
|
.setType(PayNotifyType.REFUND.getType());
|
||||||
|
// 设置 Refund 属性
|
||||||
|
payNotifyTaskDO.setRefund(new PayNotifyTaskDO.Refund().setRefundId(refund.getId())
|
||||||
|
.setTransactionId(refund.getTransactionId()).setOrderId(refund.getOrderId()));
|
||||||
|
// 保存到数据库
|
||||||
|
payNotifyTaskMapper.insert(payNotifyTaskDO);
|
||||||
|
|
||||||
|
// 发送 MQ 消息
|
||||||
|
payMQProducer.sendPayRefundNotifyTaskMessage(PayNotifyConvert.INSTANCE.convertRefund(payNotifyTaskDO),
|
||||||
|
refund.getId(), refund.getTransactionId(), refund.getOrderId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addPayTransactionNotifyTask(PayTransactionDO transaction, PayTransactionExtensionDO extension) {
|
||||||
|
PayNotifyTaskDO payNotifyTaskDO = this.createBasePayNotifyTaskDO(transaction.getAppId(), transaction.getNotifyUrl())
|
||||||
|
.setType(PayNotifyType.TRANSACTION.getType());
|
||||||
|
// 设置 Transaction 属性
|
||||||
|
payNotifyTaskDO.setTransaction(new PayNotifyTaskDO.Transaction().setOrderId(transaction.getOrderId())
|
||||||
|
.setTransactionId(extension.getTransactionId()).setTransactionExtensionId(extension.getId()));
|
||||||
|
// 保存到数据库
|
||||||
|
payNotifyTaskMapper.insert(payNotifyTaskDO);
|
||||||
|
|
||||||
|
// 发送 MQ 消息
|
||||||
|
payMQProducer.sendPayTransactionNotifyTaskMessage(PayNotifyConvert.INSTANCE.convertTransaction(payNotifyTaskDO),
|
||||||
|
transaction.getId(), transaction.getOrderId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private PayNotifyTaskDO createBasePayNotifyTaskDO(String appId, String notifyUrl) {
|
||||||
|
return new PayNotifyTaskDO()
|
||||||
|
.setAppId(appId)
|
||||||
|
.setStatus(PayNotifyStatusEnum.WAITING.getStatus())
|
||||||
|
.setNotifyTimes(0).setMaxNotifyTimes(PayNotifyTaskDO.NOTIFY_FREQUENCY.length + 1)
|
||||||
|
.setNextNotifyTime(DateUtil.addDate(Calendar.SECOND, PayNotifyTaskDO.NOTIFY_FREQUENCY[0]))
|
||||||
|
.setNotifyUrl(notifyUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -31,4 +31,16 @@ public interface PayTransactionService {
|
|||||||
*/
|
*/
|
||||||
PayTransactionRespDTO getPayTransaction(PayTransactionGetReqDTO getReqDTO);
|
PayTransactionRespDTO getPayTransaction(PayTransactionGetReqDTO getReqDTO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新交易支付成功
|
||||||
|
*
|
||||||
|
* 该接口用于不同支付平台,支付成功后,回调该接口
|
||||||
|
*
|
||||||
|
* @param payChannel 支付渠道
|
||||||
|
* @param params 回调参数。
|
||||||
|
* 因为不同平台,能够提供的参数不同,所以使用 String 类型统一接收,然后在使用不同的 AbstractThirdPayClient 进行处理。
|
||||||
|
* @return 是否支付成功
|
||||||
|
*/
|
||||||
|
Boolean updateTransactionPaySuccess(Integer payChannel, String params);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import cn.iocoder.common.framework.util.MathUtil;
|
|||||||
import cn.iocoder.common.framework.vo.CommonResult;
|
import cn.iocoder.common.framework.vo.CommonResult;
|
||||||
import cn.iocoder.mall.payservice.client.thirdpay.AbstractThirdPayClient;
|
import cn.iocoder.mall.payservice.client.thirdpay.AbstractThirdPayClient;
|
||||||
import cn.iocoder.mall.payservice.client.thirdpay.ThirdPayClientFactory;
|
import cn.iocoder.mall.payservice.client.thirdpay.ThirdPayClientFactory;
|
||||||
|
import cn.iocoder.mall.payservice.client.thirdpay.dto.ThirdPayTransactionSuccessRespDTO;
|
||||||
import cn.iocoder.mall.payservice.convert.transaction.PayTransactionConvert;
|
import cn.iocoder.mall.payservice.convert.transaction.PayTransactionConvert;
|
||||||
import cn.iocoder.mall.payservice.dal.mysql.dataobject.transaction.PayTransactionDO;
|
import cn.iocoder.mall.payservice.dal.mysql.dataobject.transaction.PayTransactionDO;
|
||||||
import cn.iocoder.mall.payservice.dal.mysql.dataobject.transaction.PayTransactionExtensionDO;
|
import cn.iocoder.mall.payservice.dal.mysql.dataobject.transaction.PayTransactionExtensionDO;
|
||||||
@ -15,16 +16,17 @@ import cn.iocoder.mall.payservice.enums.transaction.PayTransactionStatusEnum;
|
|||||||
import cn.iocoder.mall.payservice.rpc.app.dto.PayAppRespDTO;
|
import cn.iocoder.mall.payservice.rpc.app.dto.PayAppRespDTO;
|
||||||
import cn.iocoder.mall.payservice.rpc.transaction.dto.*;
|
import cn.iocoder.mall.payservice.rpc.transaction.dto.*;
|
||||||
import cn.iocoder.mall.payservice.service.app.PayAppService;
|
import cn.iocoder.mall.payservice.service.app.PayAppService;
|
||||||
|
import cn.iocoder.mall.payservice.service.notify.PayNotifyService;
|
||||||
import cn.iocoder.mall.payservice.service.transaction.PayTransactionService;
|
import cn.iocoder.mall.payservice.service.transaction.PayTransactionService;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import static cn.iocoder.mall.payservice.enums.PayErrorCodeConstants.PAY_TRANSACTION_NOT_FOUND;
|
import static cn.iocoder.mall.payservice.enums.PayErrorCodeConstants.*;
|
||||||
import static cn.iocoder.mall.payservice.enums.PayErrorCodeConstants.PAY_TRANSACTION_STATUS_IS_NOT_WAITING;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 支付交易单 Service 实现类
|
* 支付交易单 Service 实现类
|
||||||
@ -41,6 +43,8 @@ public class PayTransactionServiceImpl implements PayTransactionService {
|
|||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private PayAppService payAppService;
|
private PayAppService payAppService;
|
||||||
|
@Autowired
|
||||||
|
private PayNotifyService payNotifyService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Integer createPayTransaction(PayTransactionCreateReqDTO createReqDTO) {
|
public Integer createPayTransaction(PayTransactionCreateReqDTO createReqDTO) {
|
||||||
@ -58,7 +62,7 @@ public class PayTransactionServiceImpl implements PayTransactionService {
|
|||||||
|
|
||||||
// 创建支付交易单
|
// 创建支付交易单
|
||||||
payTransaction = PayTransactionConvert.INSTANCE.convert(createReqDTO)
|
payTransaction = PayTransactionConvert.INSTANCE.convert(createReqDTO)
|
||||||
.setStatus(PayTransactionStatusEnum.WAITING.getValue())
|
.setStatus(PayTransactionStatusEnum.WAITING.getStatus())
|
||||||
.setNotifyUrl(payAppRespDTO.getPayNotifyUrl());
|
.setNotifyUrl(payAppRespDTO.getPayNotifyUrl());
|
||||||
payTransactionMapper.insert(payTransaction);
|
payTransactionMapper.insert(payTransaction);
|
||||||
// 最终返回
|
// 最终返回
|
||||||
@ -77,14 +81,14 @@ public class PayTransactionServiceImpl implements PayTransactionService {
|
|||||||
if (payTransaction == null) { // 是否存在
|
if (payTransaction == null) { // 是否存在
|
||||||
throw ServiceExceptionUtil.exception(PAY_TRANSACTION_NOT_FOUND);
|
throw ServiceExceptionUtil.exception(PAY_TRANSACTION_NOT_FOUND);
|
||||||
}
|
}
|
||||||
if (!PayTransactionStatusEnum.WAITING.getValue().equals(payTransaction.getStatus())) { // 校验状态,必须是待支付
|
if (!PayTransactionStatusEnum.WAITING.getStatus().equals(payTransaction.getStatus())) { // 校验状态,必须是待支付
|
||||||
throw ServiceExceptionUtil.exception(PAY_TRANSACTION_STATUS_IS_NOT_WAITING);
|
throw ServiceExceptionUtil.exception(PAY_TRANSACTION_STATUS_IS_NOT_WAITING);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 插入 PayTransactionExtensionDO
|
// 插入 PayTransactionExtensionDO
|
||||||
PayTransactionExtensionDO payTransactionExtensionDO = PayTransactionConvert.INSTANCE.convert(submitReqDTO)
|
PayTransactionExtensionDO payTransactionExtensionDO = PayTransactionConvert.INSTANCE.convert(submitReqDTO)
|
||||||
.setTransactionId(payTransaction.getId()).setTransactionCode(generateTransactionCode())
|
.setTransactionId(payTransaction.getId()).setTransactionCode(generateTransactionCode())
|
||||||
.setStatus(PayTransactionStatusEnum.WAITING.getValue());
|
.setStatus(PayTransactionStatusEnum.WAITING.getStatus());
|
||||||
payTransactionExtensionMapper.insert(payTransactionExtensionDO);
|
payTransactionExtensionMapper.insert(payTransactionExtensionDO);
|
||||||
|
|
||||||
// 调用三方接口
|
// 调用三方接口
|
||||||
@ -103,6 +107,64 @@ public class PayTransactionServiceImpl implements PayTransactionService {
|
|||||||
getReqDTO.getAppId(), getReqDTO.getOrderId()));
|
getReqDTO.getAppId(), getReqDTO.getOrderId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public Boolean updateTransactionPaySuccess(Integer payChannel, String params) {
|
||||||
|
// TODO 芋艿,记录回调日志
|
||||||
|
// 解析传入的参数,成 ThirdPayTransactionSuccessRespDTO 对象
|
||||||
|
AbstractThirdPayClient thirdPayClient = ThirdPayClientFactory.getThirdPayClient(payChannel);
|
||||||
|
CommonResult<ThirdPayTransactionSuccessRespDTO> paySuccessResult = thirdPayClient.parseTransactionSuccessParams(params);
|
||||||
|
paySuccessResult.checkError();
|
||||||
|
|
||||||
|
// TODO 芋艿,先最严格的校验。即使调用方重复调用,实际哪个订单已经被重复回调的支付,也返回 false 。也没问题,因为实际已经回调成功了。
|
||||||
|
// 1.1 查询 PayTransactionExtensionDO
|
||||||
|
PayTransactionExtensionDO extension = payTransactionExtensionMapper.selectByTransactionCode(paySuccessResult.getData().getTransactionCode());
|
||||||
|
if (extension == null) {
|
||||||
|
throw ServiceExceptionUtil.exception(PAY_TRANSACTION_EXTENSION_NOT_FOUND);
|
||||||
|
}
|
||||||
|
if (!PayTransactionStatusEnum.WAITING.getStatus().equals(extension.getStatus())) { // 校验状态,必须是待支付
|
||||||
|
throw ServiceExceptionUtil.exception(PAY_TRANSACTION_EXTENSION_STATUS_IS_NOT_WAITING);
|
||||||
|
}
|
||||||
|
// 1.2 更新 PayTransactionExtensionDO
|
||||||
|
PayTransactionExtensionDO updatePayTransactionExtension = new PayTransactionExtensionDO()
|
||||||
|
.setId(extension.getId())
|
||||||
|
.setStatus(PayTransactionStatusEnum.SUCCESS.getStatus())
|
||||||
|
.setExtensionData(params);
|
||||||
|
int updateCounts = payTransactionExtensionMapper.update(updatePayTransactionExtension, PayTransactionStatusEnum.WAITING.getStatus());
|
||||||
|
if (updateCounts == 0) { // 校验状态,必须是待支付
|
||||||
|
throw ServiceExceptionUtil.exception(PAY_TRANSACTION_EXTENSION_STATUS_IS_NOT_WAITING);
|
||||||
|
}
|
||||||
|
log.info("[updateTransactionPaySuccess][PayTransactionExtensionDO({}) 更新为已支付]", extension.getId());
|
||||||
|
|
||||||
|
// 2.1 判断 PayTransactionDO 是否处于待支付
|
||||||
|
PayTransactionDO transaction = payTransactionMapper.selectById(extension.getTransactionId());
|
||||||
|
if (transaction == null) {
|
||||||
|
throw ServiceExceptionUtil.exception(PAY_TRANSACTION_NOT_FOUND);
|
||||||
|
}
|
||||||
|
if (!PayTransactionStatusEnum.WAITING.getStatus().equals(transaction.getStatus())) { // 校验状态,必须是待支付
|
||||||
|
throw ServiceExceptionUtil.exception(PAY_TRANSACTION_STATUS_IS_NOT_WAITING);
|
||||||
|
}
|
||||||
|
// 2.2 更新 PayTransactionDO
|
||||||
|
PayTransactionDO updatePayTransaction = new PayTransactionDO()
|
||||||
|
.setId(transaction.getId())
|
||||||
|
.setStatus(PayTransactionStatusEnum.SUCCESS.getStatus())
|
||||||
|
.setExtensionId(extension.getId())
|
||||||
|
.setPayChannel(payChannel)
|
||||||
|
.setPaymentTime(paySuccessResult.getData().getPaymentTime())
|
||||||
|
.setNotifyTime(new Date())
|
||||||
|
.setTradeNo(paySuccessResult.getData().getTradeNo());
|
||||||
|
updateCounts = payTransactionMapper.update(updatePayTransaction, PayTransactionStatusEnum.WAITING.getStatus());
|
||||||
|
if (updateCounts == 0) { // 校验状态,必须是待支付
|
||||||
|
throw ServiceExceptionUtil.exception(PAY_TRANSACTION_STATUS_IS_NOT_WAITING);
|
||||||
|
}
|
||||||
|
log.info("[updateTransactionPaySuccess][PayTransactionDO({}) 更新为已支付]", transaction.getId());
|
||||||
|
|
||||||
|
// 3 新增 PayNotifyTaskDO 注释原因,参见 PayRefundSuccessConsumer 类。
|
||||||
|
payNotifyService.addPayTransactionNotifyTask(transaction, extension);
|
||||||
|
// 返回结果
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private String generateTransactionCode() {
|
private String generateTransactionCode() {
|
||||||
// wx
|
// wx
|
||||||
// 2014
|
// 2014
|
||||||
|
@ -2,10 +2,7 @@ package cn.iocoder.mall.shopweb.client.pay;
|
|||||||
|
|
||||||
import cn.iocoder.common.framework.vo.CommonResult;
|
import cn.iocoder.common.framework.vo.CommonResult;
|
||||||
import cn.iocoder.mall.payservice.rpc.transaction.PayTransactionRpc;
|
import cn.iocoder.mall.payservice.rpc.transaction.PayTransactionRpc;
|
||||||
import cn.iocoder.mall.payservice.rpc.transaction.dto.PayTransactionGetReqDTO;
|
import cn.iocoder.mall.payservice.rpc.transaction.dto.*;
|
||||||
import cn.iocoder.mall.payservice.rpc.transaction.dto.PayTransactionRespDTO;
|
|
||||||
import cn.iocoder.mall.payservice.rpc.transaction.dto.PayTransactionSubmitReqDTO;
|
|
||||||
import cn.iocoder.mall.payservice.rpc.transaction.dto.PayTransactionSubmitRespDTO;
|
|
||||||
import org.apache.dubbo.config.annotation.DubboReference;
|
import org.apache.dubbo.config.annotation.DubboReference;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@ -35,4 +32,10 @@ public class PayTransactionClient {
|
|||||||
return submitPayTransactionResult.getData();
|
return submitPayTransactionResult.getData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updatePayTransactionSuccess(Integer payChannel, String params) {
|
||||||
|
CommonResult<Boolean> updatePayTransactionSuccessResult = payTransactionRpc.updatePayTransactionSuccess(
|
||||||
|
new PayTransactionSuccessReqDTO().setPayChannel(payChannel).setParams(params));
|
||||||
|
updatePayTransactionSuccessResult.checkError();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package cn.iocoder.mall.shopweb.controller.pay;
|
|||||||
|
|
||||||
import cn.iocoder.common.framework.util.HttpUtil;
|
import cn.iocoder.common.framework.util.HttpUtil;
|
||||||
import cn.iocoder.common.framework.vo.CommonResult;
|
import cn.iocoder.common.framework.vo.CommonResult;
|
||||||
|
import cn.iocoder.mall.payservice.enums.PayChannelEnum;
|
||||||
import cn.iocoder.mall.security.user.core.context.UserSecurityContextHolder;
|
import cn.iocoder.mall.security.user.core.context.UserSecurityContextHolder;
|
||||||
import cn.iocoder.mall.shopweb.controller.pay.vo.transaction.PayTransactionRespVO;
|
import cn.iocoder.mall.shopweb.controller.pay.vo.transaction.PayTransactionRespVO;
|
||||||
import cn.iocoder.mall.shopweb.controller.pay.vo.transaction.PayTransactionSubmitReqVO;
|
import cn.iocoder.mall.shopweb.controller.pay.vo.transaction.PayTransactionSubmitReqVO;
|
||||||
@ -14,10 +15,13 @@ import io.swagger.annotations.ApiImplicitParams;
|
|||||||
import io.swagger.annotations.ApiOperation;
|
import io.swagger.annotations.ApiOperation;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
import static cn.iocoder.common.framework.vo.CommonResult.success;
|
import static cn.iocoder.common.framework.vo.CommonResult.success;
|
||||||
|
|
||||||
@ -31,6 +35,7 @@ public class PayTransactionController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private PayTransactionService payTransactionService;
|
private PayTransactionService payTransactionService;
|
||||||
|
|
||||||
|
// TODO 芋艿:这个 API 定义可能不太太合适,应该改成支付交易单号
|
||||||
@GetMapping("/get")
|
@GetMapping("/get")
|
||||||
@ApiOperation("获得支付交易")
|
@ApiOperation("获得支付交易")
|
||||||
@ApiImplicitParams({
|
@ApiImplicitParams({
|
||||||
@ -43,6 +48,7 @@ public class PayTransactionController {
|
|||||||
return success(payTransactionService.getPayTransaction(UserSecurityContextHolder.getUserId(), appId, orderId));
|
return success(payTransactionService.getPayTransaction(UserSecurityContextHolder.getUserId(), appId, orderId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO 芋艿:这个 API 定义可能不太太合适,应该改成支付交易单号
|
||||||
@PostMapping("/submit")
|
@PostMapping("/submit")
|
||||||
@ApiOperation("提交支付交易")
|
@ApiOperation("提交支付交易")
|
||||||
@RequiresAuthenticate
|
@RequiresAuthenticate
|
||||||
@ -51,24 +57,25 @@ public class PayTransactionController {
|
|||||||
return success(payTransactionService.submitPayTransaction(submitReqVO, HttpUtil.getIp(request)));
|
return success(payTransactionService.submitPayTransaction(submitReqVO, HttpUtil.getIp(request)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// @PostMapping(value = "pingxx_pay_success", consumes = MediaType.APPLICATION_JSON_VALUE)
|
@PostMapping(value = "pingxx_pay_success", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||||
//// @GetMapping(value = "pingxx_pay_success")
|
@ApiOperation("Pingxx 支付成功回调")
|
||||||
// public String pingxxPaySuccess(HttpServletRequest request) throws IOException {
|
// @GetMapping(value = "pingxx_pay_success")
|
||||||
// logger.info("[pingxxPaySuccess][被回调]");
|
public String updatePayTransactionSuccess(HttpServletRequest request) throws IOException {
|
||||||
// // 读取 webhook
|
log.info("[pingxxPaySuccess][被回调]");
|
||||||
// StringBuilder sb = new StringBuilder();
|
// 读取 webhook
|
||||||
// try (BufferedReader reader = request.getReader()) {
|
StringBuilder sb = new StringBuilder();
|
||||||
// String line;
|
try (BufferedReader reader = request.getReader()) {
|
||||||
// while ((line = reader.readLine()) != null) {
|
String line;
|
||||||
// sb.append(line);
|
while ((line = reader.readLine()) != null) {
|
||||||
// }
|
sb.append(line);
|
||||||
// }
|
}
|
||||||
//
|
}
|
||||||
//// JSONObject bodyObj = JSON.parseObject(sb.toString());
|
|
||||||
//// bodyObj.put("webhookId", bodyObj.remove("id"));
|
// JSONObject bodyObj = JSON.parseObject(sb.toString());
|
||||||
//// String body = bodyObj.toString();
|
// bodyObj.put("webhookId", bodyObj.remove("id"));
|
||||||
// payTransactionService.updateTransactionPaySuccess(PayChannelEnum.PINGXX.getId(), sb.toString());
|
// String body = bodyObj.toString();
|
||||||
// return "success";
|
payTransactionService.updatePayTransactionSuccess(PayChannelEnum.PINGXX.getId(), sb.toString());
|
||||||
// }
|
return "success";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ public class PayTransactionService {
|
|||||||
|
|
||||||
public PayTransactionSubmitRespVO submitPayTransaction(PayTransactionSubmitReqVO submitReqVO, String ip) {
|
public PayTransactionSubmitRespVO submitPayTransaction(PayTransactionSubmitReqVO submitReqVO, String ip) {
|
||||||
PayTransactionSubmitRespDTO submitPayTransaction = payTransactionClient.submitPayTransaction(
|
PayTransactionSubmitRespDTO submitPayTransaction = payTransactionClient.submitPayTransaction(
|
||||||
PayTransactionConvert.INSTANCE.convert(submitReqVO));
|
PayTransactionConvert.INSTANCE.convert(submitReqVO).setCreateIp(ip));
|
||||||
return PayTransactionConvert.INSTANCE.convert(submitPayTransaction);
|
return PayTransactionConvert.INSTANCE.convert(submitPayTransaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,4 +25,8 @@ public class PayTransactionService {
|
|||||||
return PayTransactionConvert.INSTANCE.convert(payTransactionClient.getPayTransaction(userId, appId, orderId));
|
return PayTransactionConvert.INSTANCE.convert(payTransactionClient.getPayTransaction(userId, appId, orderId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updatePayTransactionSuccess(Integer payChannel, String params) {
|
||||||
|
payTransactionClient.updatePayTransactionSuccess(payChannel, params);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user