新增了提现记录表,保存提现记录,定时器主动向Adapay查询提现状态

This commit is contained in:
HH 2023-03-21 01:03:47 +08:00
parent 9c5dda67aa
commit 9bd5811918
8 changed files with 283 additions and 22 deletions

View File

@ -179,10 +179,11 @@ public class OrderDetailServiceImpl implements OrderDetailService {
}
@Override
public String createCode() {
public synchronized String createCode() {
INDEX.compareAndSet(9999L, 1L);
LocalDateTime now = LocalDateTime.now();
return "od" + now.format(MINI_FORMATTER) + INDEX.getAndIncrement();
// 得到的CODE长度相同
return "od" + now.format(MINI_FORMATTER) + String.format("%04d", INDEX.getAndIncrement());
}
@Override

View File

@ -65,9 +65,10 @@ public class OrderMasterServiceImpl implements OrderMasterService {
private final static ThreadLocal<SimpleDateFormat> dateFormat = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMddHHmmss"));
@Override
public String createOrderCode() {
public synchronized String createOrderCode() {
INDEX.compareAndSet(9999L, 1L);
return "om" + dateFormat.get().format(new Date()) + INDEX.getAndIncrement();
// 得到的CODE长度相同
return "om" + dateFormat.get().format(new Date()) + String.format("%04d", INDEX.getAndIncrement());
}
@Override

View File

@ -0,0 +1,77 @@
package com.ghy.payment.domain;
import lombok.Data;
/**
* 提现记录
*/
@Data
public class DrawCashRecord {
/**
* 由Adapay生成的取现对象 id
*/
private String id;
private Long dept_id;
/**
* 控制台 主页面应用的app_id
*/
private String app_id;
/**
* 取现类型T1-T+1取现D1-D+1取现D0-即时取现
*/
private String cash_type;
/**
* 取现对象创建时的 10 位时间戳
*/
private String created_time;
/**
* 用户对象的member_id若是商户本身取现时为0
*/
private String member_id;
/**
* 取现对象cash
*/
private String object;
/**
* 请求订单号只能为英文数字或者下划线的一种或多种组合保证在app_id下唯一
*/
private String order_no;
/**
* 取现金额必须大于0保留两位小数点如0.10100.05等
*/
private String cash_amt;
/**
* 取现成功后的到账金额值为取现金额 - 取现手续费金额
*/
private String real_amt;
/**
* 取现手续费金额
*/
private String fee_amt;
/**
* 状态
* pending 交易处理中
* succeeded 交易成功
* failed 交易失败
*/
private String status;
/**
* 是否 prod模式true prod模式false mock模式
*/
private String prod_mode;
/**
* 错误码 https://docs.adapay.tech/api/errors.html#id6
*/
private String error_code;
/**
* 错误类型
* invalid_request_error 请求错误传入了不正确的地址参数或值
* api_error Adapay 服务器出现的异常错误
* channel_error 第三方支付渠道出现的错误导致请求出现错误通常您需要对这些可能出现的情况进行处理或者联系我们
*/
private String error_type;
private String error_msg;
}

View File

@ -0,0 +1,19 @@
package com.ghy.payment.mapper;
import com.ghy.payment.domain.DrawCashRecord;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface DrawCashRecordMapper {
int insert(DrawCashRecord record);
int update(DrawCashRecord record);
DrawCashRecord selectById(String id);
List<DrawCashRecord> selectByStatus(String status);
int updateStatus(@Param("id") String id, @Param("status") String status);
}

View File

@ -10,9 +10,9 @@ import com.ghy.payment.service.impl.PayReverseCallbackService;
import com.ghy.payment.service.impl.RefundCallbackService;
import com.huifu.adapay.core.exception.BaseAdaPayException;
import com.huifu.adapay.model.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
@ -25,11 +25,12 @@ import java.util.List;
/**
* @author HH 2022/3/25
*/
@Slf4j
@Service
public class AdapayService {
private static final Logger logger = LoggerFactory.getLogger(AdapayService.class);
@Resource
private ThreadPoolTaskExecutor executor;
@Resource
private PayCallbackService payCallbackService;
@Resource
@ -100,9 +101,9 @@ public class AdapayService {
confirmParams.put("div_members", divMembers);
confirmParams.put("fee_mode", feeMode);
confirmParams.put("description", description);
logger.info("发起支付确认 dept[{}] param:{}", deptId, confirmParams.toJSONString());
log.info("发起支付确认 dept[{}] param:{}", deptId, confirmParams.toJSONString());
JSONObject response = (JSONObject) PaymentConfirm.create(confirmParams, deptId.toString());
logger.info("支付确认结果 dept[{}] response:{}", deptId, response.toJSONString());
log.info("支付确认结果 dept[{}] response:{}", deptId, response.toJSONString());
return response;
}
@ -278,13 +279,30 @@ public class AdapayService {
cashParam.put("notify_url", adapayProperties.getNotifyUrl());
cashParam.put("remark", remark);
cashParam.put("fee_mode", feeMode);
logger.info("发起提现 dept[{}] param:{}", deptId, cashParam.toJSONString());
log.info("发起提现 dept[{}] param:{}", deptId, cashParam.toJSONString());
JSONObject response = (JSONObject) Drawcash.create(cashParam, deptId.toString());
logger.info("提现结果 dept[{}] response:{}", deptId, response.toJSONString());
drawCashCallbackService.onResponse(response);
log.info("提现结果 dept[{}] response:{}", deptId, response.toJSONString());
response.put("dept_id", deptId);
executor.execute(() -> drawCashCallbackService.onResponse(response));
return response;
}
/**
* 通过该功能可以查询已发起的取现交易状态
*
* @param deptId [必填]商户ID
* @param orderNo [必填项]请求订单号只能为英文数字或者下划线的一种或多种组合保证在app_id下唯一
* @return https://docs.adapay.tech/api/wallet.html#query-cash-response
* 失败示例: {"error_msg":"未查到相关取现信息","error_type":"invalid_request_error","prod_mode":"true","error_code":"cash_error","status":"failed"}
* 成功示例: {"cash_list":[{"cash_id":"0021110483925412449046528","trans_stat":"P","cash_amt":"19.87"}],"prod_mode":"true","status":"succeeded"}
*/
public JSONObject queryDrawCash(@NotNull Long deptId, @NotNull String orderNo) throws BaseAdaPayException {
Assert.hasText(orderNo, "orderNo is blank!");
JSONObject queryCashParam = new JSONObject();
queryCashParam.put("order_no", orderNo);
return (JSONObject) Drawcash.query(queryCashParam, deptId.toString());
}
/**
* 支付宝正扫支付
*/
@ -351,9 +369,9 @@ public class AdapayService {
paymentParams.put("div_members", divMembers);
paymentParams.put("device_info", deviceInfo);
paymentParams.put("expend", expend);
logger.debug("paymentParams: {}", paymentParams.toJSONString());
log.debug("paymentParams: {}", paymentParams.toJSONString());
JSONObject response = (JSONObject) Payment.create(paymentParams, deptId.toString());
payCallbackService.onResponse(response);
executor.execute(() -> payCallbackService.onResponse(response));
return response;
}
@ -380,7 +398,7 @@ public class AdapayService {
refundParams.put("refund_amt", refundAmt);
refundParams.put("refund_order_no", refundOrderNo);
JSONObject response = (JSONObject) Refund.create(paymentId, refundParams, deptId.toString());
refundCallbackService.onResponse(response);
executor.execute(() -> refundCallbackService.onResponse(response));
return response;
}
@ -444,7 +462,7 @@ public class AdapayService {
reverseParams.put("notify_url", adapayProperties.getNotifyUrl());
reverseParams.put("order_no", "PAYMENT_REVERSE" + System.currentTimeMillis());
JSONObject response = (JSONObject) PaymentReverse.create(reverseParams, deptId.toString());
payReverseCallbackService.onResponse(response);
executor.execute(() -> payReverseCallbackService.onResponse(response));
return response;
}

View File

@ -0,0 +1,76 @@
package com.ghy.payment.service;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ghy.common.adapay.model.AdapayStatusEnum;
import com.ghy.payment.domain.DrawCashRecord;
import com.ghy.payment.mapper.DrawCashRecordMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.List;
/**
* 定时器
* 主动与Adapay同步支付撤销支付提现订单的状态
*/
@Slf4j
@Component
@EnableScheduling
public class AdapaySyncTimer {
@Resource
private AdapayService adapayService;
@Resource
private DrawCashRecordMapper drawCashRecordMapper;
@Scheduled(fixedRate = 5 * 60 * 1000L)
public void syncDrawCash() {
List<DrawCashRecord> records = drawCashRecordMapper.selectByStatus("pending");
if (CollectionUtils.isEmpty(records)) {
log.debug("No pending drawCashRecord.");
return;
}
for (DrawCashRecord record : records) {
Long deptId = record.getDept_id();
String orderNo = record.getOrder_no();
if (deptId == null || StringUtils.isBlank(orderNo)) {
continue;
}
try {
JSONObject response = adapayService.queryDrawCash(deptId, orderNo);
// 这个status代表API调用状态 不代表提现状态
if (AdapayStatusEnum.succeeded.code.equals(response.getString("status"))) {
JSONArray cashList = response.getJSONArray("cash_list");
if (!CollectionUtils.isEmpty(cashList)) {
JSONObject cash = cashList.getJSONObject(0);
// 这个才是提现状态
String transStat = cash.getString("trans_stat");
switch (transStat) {
// 提现成功
case "S":
drawCashRecordMapper.updateStatus(record.getId(), "succeeded");
break;
// 提现失败
case "F":
drawCashRecordMapper.updateStatus(record.getId(), "failed");
break;
default:
break;
}
}
} else {
log.error("DrawCash.query请求失败: {}", response.toJSONString());
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}
}

View File

@ -2,29 +2,35 @@ package com.ghy.payment.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.ghy.common.adapay.model.Event;
import com.ghy.payment.domain.DrawCashRecord;
import com.ghy.payment.mapper.DrawCashRecordMapper;
import com.ghy.payment.service.CallBackService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* 提现回调
*
* @author HH 2022/5/30
*/
@Slf4j
@Service("drawCashCallbackService")
public class DrawCashCallbackService implements CallBackService {
private static final Logger logger = LoggerFactory.getLogger(DrawCashCallbackService.class);
@Resource
private DrawCashRecordMapper drawCashRecordMapper;
@Override
public void onCallback(Event event) {
logger.info("提现 callback: {}", event.toString());
log.info("提现 callback: {}", event.toString());
}
@Override
public void onResponse(JSONObject response) {
logger.info("提现 response: {}", response.toString());
DrawCashRecord record = response.toJavaObject(DrawCashRecord.class);
drawCashRecordMapper.insert(record);
}
}

View File

@ -0,0 +1,63 @@
<?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.ghy.payment.mapper.DrawCashRecordMapper">
<sql id="select_columns">
SELECT `id`,
`dept_id`,
`app_id`,
`cash_amt`,
`cash_type`,
`created_time`,
`fee_amt`,
`member_id`,
`object`,
`order_no`,
`real_amt`,
`status`,
`prod_mode`,
`error_code`,
`error_type`,
`error_msg`
FROM draw_cash_record
</sql>
<insert id="insert" parameterType="com.ghy.payment.domain.DrawCashRecord">
INSERT INTO draw_cash_record(`id`, `dept_id`, `app_id`, `cash_amt`, `cash_type`, `created_time`,
`fee_amt`, `member_id`, `object`, `order_no`,
`real_amt`, `status`, `prod_mode`, `error_code`, `error_type`, `error_msg`)
VALUES (#{id}, #{dept_id}, #{app_id}, #{cash_amt}, #{cash_type}, #{created_time},
#{fee_amt}, #{member_id}, #{object}, #{order_no},
#{real_amt}, #{status}, #{prod_mode}, #{error_code}, #{error_type}, #{error_msg})
</insert>
<update id="update" parameterType="com.ghy.payment.domain.DrawCashRecord">
UPDATE draw_cash_record
<set>
<if test="real_amt != null and real_amt != ''">`real_amt` = #{real_amt},</if>
<if test="fee_amt != null and fee_amt != ''">`fee_amt` = #{fee_amt},</if>
<if test="status != null and status != ''">`status` = #{status},</if>
<if test="error_code != null and error_code != ''">`error_code` = #{error_code},</if>
<if test="error_type != null and error_type != ''">`error_type` = #{error_type},</if>
<if test="error_msg != null and error_msg != ''">`error_msg` = #{error_msg},</if>
</set>
WHERE `id` = #{id}
</update>
<update id="updateStatus">
UPDATE draw_cash_record
SET `status` = #{status}
WHERE `id` = #{id}
</update>
<select id="selectById" resultType="com.ghy.payment.domain.DrawCashRecord">
<include refid="select_columns"/>
WHERE `id` = #{id}
</select>
<select id="selectByStatus" resultType="com.ghy.payment.domain.DrawCashRecord">
<include refid="select_columns"/>
WHERE `status` = #{status}
</select>
</mapper>