JAVA快速对接三方支付通道标准模版

1、支付通道枚举

java 复制代码
/**
 * @author Lucas
 * date 2025/2/3 17:20
 * description 支付通道类型
 */
@Getter
@AllArgsConstructor
public enum PayType {
    /**
     * TEST1
     */
    Test1Pay("Test1Pay"),
    /**
     * TEST2
     */
    Test2Pay("Test2Pay"),
    /**
     * TEST3
     */
    Test3Pay("Test3Pay"),
    ;
    private final String name;

}

2、充值或提现枚举

java 复制代码
/**
 * @author Lucas
 * date 2025/2/17 12:44
 * description 充值或提现
 */
public enum PaymentType {
    /**
     * 充值
     */
    Recharge,
    /**
     * 提现
     */
    Withdrawal
}

3、回调请求类型枚举

java 复制代码
/**
 * @author Lucas
 * date 2025/2/17 12:44
 * description 回调请求类型
 */
public enum RequestType {
    /**
     * body参数
     */
    json,
    /**
     * param参数
     */
    form
}

4、支付工厂类

java 复制代码
/**
 * 支付工厂类
 *
 * @author Lucas
 * date 2025/3/3 19:53
 */
@Component
public class PayFactory {

    private final Map<PayType, IPayService> serviceMap = new ConcurrentHashMap<>();

    public PayFactory(List<IPayService> serviceList) {
        for (IPayService paymentService : serviceList) {
            List<PayType> routes = paymentService.routes();
            for (PayType route : routes) {
                serviceMap.putIfAbsent(route, paymentService);
            }
        }
    }

    public IPayService getService(PayType payRoute) {
        IPayService paymentService = serviceMap.get(payRoute);
        if (paymentService == null) {
            throw new RuntimeException("the payRoute[" + payRoute + "] hasn't implement");
        }
        return paymentService;
    }
}

5、支付接口类

java 复制代码
/**
 * 支付接口类实现
 *
 * @author Lucas
 * date 2025/2/3 18:36
 */
public interface IPayService {
    /**
     * 支付渠道类型(路由)
     */
    List<PayType> routes();

    /**
     * 充值(钱从用户流向钱包)
     *
     * @param rechargeBO     充值信息
     * @param paymentChannel 支付渠道信息
     * @return PaymentResponse
     */
    Payment recharge(Recharge rechargeBO, PaymentChannelVo paymentChannel);

    /**
     * 提现(钱从钱包流向用户)
     *
     * @param withdrawalBO   提现信息
     * @param paymentChannel 支付渠道信息
     * @return PaymentResponse
     */
    Payment withdrawal(Withdrawal withdrawalBO, PaymentChannelVo paymentChannel);

    /**
     * 根据订单ID查询第三方支付状态
     *
     * @param merchantOrderNo 我方订单号
     * @param thirdOrderNo    第三方平台订单号
     * @return PaymentResponse
     */
    Payment queryRechargeOrder(String merchantOrderNo, String thirdOrderNo, PaymentChannelVo paymentChannel);

    /**
     * 根据订单ID查询第三方支付状态
     *
     * @param merchantOrderNo 我方订单号
     * @param thirdOrderNo    第三方平台订单号
     * @return PaymentResponse
     */
    Payment queryWithdrawalOrder(String merchantOrderNo, String thirdOrderNo, PaymentChannelVo paymentChannel);


    /**
     * 查询商户余额
     *
     * @return (元)
     */
    BigDecimal getBalance(PaymentChannelVo paymentChannel);

    /**
     * 将第三方订单状态转换为系统订单状态
     *
     * @param status 第三方订单状态
     * @param type   Recharge-充值 -Withdrawal-提现
     * @return 本系统订单状态
     */
    OrderStatus convertStatus(String status, PaymentType type);

    /**
     * 签名
     *
     * @param params         签名参数
     * @param paymentChannel 签名密钥
     * @return 签名
     */
    String sign(Map<String, Object> params, PaymentChannelVo paymentChannel);

    /**
     * 充值回调参数转换
     *
     * @param params 回调参数
     * @return 转换结果参数
     */
    RechargeCallbackRequest convertRecharge(Map<String, Object> params);

    /**
     * 提现回调参数转换
     *
     * @param params 回调参数
     * @return 转换结果参数
     */
    WithdrawalCallbackRequest convertWithdrawal(Map<String, Object> params);

    /**
     * 回调返回结果
     *
     * @param response 成功或失败
     * @return 返回结果
     */
    String returnData(Response<Boolean> response);

    /**
     * 验证签名(回调和和拉单方式一样不需要实现)
     *
     * @param params         回调入参
     * @param paymentChannel 渠道信息
     * @param sign           入参签名
     * @return 签名校验结果 true:验签成功,false:验签不成功
     */
    default boolean verifySign(Map<String, Object> params, PaymentChannelVo paymentChannel, String sign) {
        if (ObjectUtils.anyNull(params, paymentChannel, sign)) {
            return false;
        }
        var newSign = sign(params, paymentChannel);
        return sign.equalsIgnoreCase(newSign);
    }
}

6、不同通道的支付三方实现

java 复制代码
/**
 * TEST支付ServiceImpl
 *
 * @author Lucas
 * date 2025/2/3 19:05
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class TestPayServiceImpl implements IPayService {


    @Override
    public List<PayType> routes() {
        return List.of(PayType.Test1Pay, PayType.Test2Pay);
    }

    @Override
    public Payment recharge(Recharge rechargeBO, PaymentChannelVo paymentChannel) {
        // 实现对应的业务
        return null;
    }

    @Override
    public Payment withdrawal(Withdrawal withdrawalBO, PaymentChannelVo paymentChannel) {
        // 实现对应的业务
        return null;
    }

    @Override
    public Payment queryRechargeOrder(String merchantOrderNo, String thirdOrderNo, PaymentChannelVo paymentChannel) {
        // 实现对应的业务
        return null;
    }

    @Override
    public Payment queryWithdrawalOrder(String merchantOrderNo, String thirdOrderNo, PaymentChannelVo paymentChannel) {
        // 实现对应的业务
        return null;
    }

    @Override
    public BigDecimal getBalance(PaymentChannelVo paymentChannel) {
        // 实现对应的业务
        return null;
    }

    @Override
    public OrderStatus convertStatus(String status, PaymentType type) {
        // 实现对应的业务
        return null;
    }

    @Override
    public String sign(Map<String, Object> params, PaymentChannelVo paymentChannel) {
        // 实现对应的业务
        return "";
    }

    @Override
    public RechargeCallbackRequest convertRecharge(Map<String, Object> params) {
        // 实现对应的业务
        return null;
    }

    @Override
    public WithdrawalCallbackRequest convertWithdrawal(Map<String, Object> params) {
        // 实现对应的业务
        return null;
    }

    @Override
    public String returnData(Response<Boolean> response) {
        return Boolean.TRUE.equals(response.ok()) ? "SUCCESS" : JSON.toJSONString(response);
    }

}

7、第三方支付回调-通用

java 复制代码
@Tag(name = "第三方支付回调-通用")
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/payment/callback")
public class PaymentCallbackController {

    private final TypeReference<Map<String, Object>> MAP_TYPE_REFERENCE = new TypeReference<>() {
    };
    private final JsonMapper jsonMapper;
    private final PayFactory payFactory;
    private final KeyLockTemplate keyLockTemplate;
    private final PaymentCallbackManager paymentCallbackManager;

    /**
     * 支付回调
     *
     * @param requestType 请求类型
     * @param type        支付种类
     * @param payType     支付平台
     * @param orderNo     订单号
     */
    @Operation(summary = "支付回调")
    @RequestMapping("/{requestType}/{type}/{payType}/{orderNo}")
    public String bodyRechargeCallback(
        @PathVariable("requestType") RequestType requestType,
        @PathVariable("type") PaymentType type,
        @PathVariable("payType") PayType payType,
        @PathVariable("orderNo") String orderNo,
        HttpServletRequest request
    ) {
        var params = RequestType.form == requestType ? convertMap(request.getParameterMap()) : getRequestBodyAsMap(request);
        log.info("支付回调,第三方渠道:{},回调类型:{}. params:{}", payType, type, params);
        return keyLockTemplate.tryLock(RedisKey.WalletCallback.key(orderNo), CommonConstants.DEFAULT_ORDER_LOCK_TIME, () -> {
            IPayService service = payFactory.getService(payType);
            Response<Boolean> resp;
            if (PaymentType.Recharge == type) {
                resp = paymentCallbackManager.rechargeCallback(payType, params, service.convertRecharge(params));
                if (resp.fail()) {
                    log.error("充值回调处理异常: params:{}, result:{}", params, resp);
                    return resp.getMsg();
                }
            } else {
                resp = paymentCallbackManager.withdrawCallback(payType, params, service.convertWithdrawal(params));
                if (resp.fail()) {
                    log.error("提现回调处理异常: params:{}, result:{}", params, resp);
                    return resp.getMsg();
                }
            }
            return service.returnData(resp);
        });
    }

    /**
     * FORM参数转换
     */
    private Map<String, Object> convertMap(Map<String, String[]> parameters) {
        if (parameters == null) {
            return Map.of();
        }
        Map<String, Object> params = new HashMap<>();
        parameters.forEach((key, values) -> {
            if (values != null && values.length > 0) {
                params.put(key, values[0]);
            }
        });
        return params;
    }

    /**
     * JSON参数转换
     */
    public Map<String, Object> getRequestBodyAsMap(HttpServletRequest request) {
        try {
            // 将请求体的内容转成Map
            String requestBody = IOUtils.toString(request.getReader());
            return jsonMapper.readValue(requestBody, MAP_TYPE_REFERENCE);
        } catch (Throwable e) {
            log.info("第三方回调获取Body获取异常");
        }
        return Map.of();
    }
}

8、充值/提现回调处理

java 复制代码
/**
 * 充值/提现回调处理
 *
 * @author Lucas
 * date 2025/2/5 16:39
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class PaymentCallbackManager {

    private final PayFactory payFactory;
    private final ResponseFactory responseFactory;

    /**
     * 充值回调
     *
     * @param signMap 充值回调参数
     * @return R<Boolean>
     */
    public Response<Boolean> rechargeCallback(PayType payType, Map<String, Object> signMap, RechargeCallbackRequest request) {
        // 必要参数判断是否存在
        if (ObjectUtils.anyNull(payType, request.getMerchantOrderNo(), request.getOrderNo(), request.getPayType(), request.getAmount(), request.getSign())) {
            return responseFactory.error(ErrorCode.PayParamsError, request);
        }
        // 校验支付方式及签名信息
        Response<OrderStatus> validate = validateParams(payType, signMap, request.getSign(), request.getStatus(), request, PaymentType.Recharge);
        if (validate.fail()) {
            return responseFactory.error(validate);
        }
        RechargeOrder rechargeOrder = rechargeOrderManager.adminDetail(request.getMerchantOrderNo());
        if (Objects.isNull(rechargeOrder)) {
            log.error("充值回调,未找到该笔订单. params:{}", signMap);
        }
        //如果订单数是最终状态(成功/失败),则不再处理回调
        if (!(OrderStatus.WaitingPay.equals(rechargeOrder.getStatus()) || OrderStatus.Paying.equals(rechargeOrder.getStatus()))) {
            return responseFactory.success();
        }
        if (StrUtil.isBlank(rechargeOrder.getThirdOrderId())) {
            rechargeOrder.setThirdOrderId(request.getOrderNo());
        }
        // 验证订单号、金额是否正确

        rechargeOrder.setStatus(validate.getData());
        rechargeOrder.setRemark(StrUtil.isNotBlank(rechargeOrder.getRemark()) ? rechargeOrder.getRemark() + " -> 回调接口" : "回调接口");
        // 业务逻辑处理
        return rechargeOrderService.processCallBack(rechargeOrder);
    }

    /**
     * 提现回调
     *
     * @param signMap 提现回调参数
     * @return R<Boolean>
     */
    public Response<Boolean> withdrawCallback(PayType payType, Map<String, Object> signMap, WithdrawalCallbackRequest request) {
        // 必要参数判断是否存在
        if (ObjectUtils.anyNull(request.getMerchantOrderNo(), request.getOrderNo(), request.getAmount(), request.getSign())) {
            return responseFactory.error(ErrorCode.PayParamsError, request);
        }
        // 校验支付方式及签名信息
        var validateR = validateParams(payType, signMap, request.getSign(), request.getStatus(), request, PaymentType.Withdrawal);
        if (validateR.fail()) {
            return responseFactory.error(validateR);
        }
        // 提现回调逻辑处理
        WithdrawalOrder withdrawalOrder = withdrawalOrderManager.queryWithdrawOrderByOrderId(request.getMerchantOrderNo());
        if (null == withdrawalOrder) {
            log.error("提现回调,未找到该笔订单. params:{}", signMap);
            return responseFactory.error(ErrorCode.WithdrawalOrderIsEmpty);
        }
        // 如果订单数是最终状态(成功/失败),则不再处理回调
        if (!(OrderStatus.WaitingPay.equals(withdrawalOrder.getStatus()) || OrderStatus.Paying.equals(withdrawalOrder.getStatus()))) {
            return responseFactory.success();
        }
        if (StrUtil.isBlank(withdrawalOrder.getThirdOrderNo())) {
            withdrawalOrder.setThirdOrderNo(request.getOrderNo());
        }
        // 验证订单号、金额是否正确

        withdrawalOrder.setStatus(validateR.getData());
        withdrawalOrder.setRemark(StrUtil.isNotBlank(withdrawalOrder.getRemark()) ? withdrawalOrder.getRemark() + " -> 回调接口" : "回调接口");
        // 业务逻辑处理
        return withdrawalOrderService.processCallBack(withdrawalOrder));
    }

    private Response<OrderStatus> validateParams(PayType payType, Map<String, Object> signMap, String signStr, String statusStr, Object requestData, PaymentType requestType) {
        // 1. 校验支付方式
        var paymentChannel = paymentChannelService.info(payType);
        if (paymentChannel == null) {
            log.error("当前系统不支持的支付渠道. params:{}, payType:{}", signMap, payType);
            return responseFactory.error(ErrorCode.PayCbChannelNotSupport, requestData);
        }
        // 2. IP验证
        List<String> ipList = Arrays.stream(paymentChannel.getCallbackIp().split(",")).map(String::trim).toList();
        String ip = IpUtil.getRequestIp(ServletUtil.getRequest());
        if (!ipList.contains(ip)) {
            log.error("IP校验失败,访问IP:{}, params:{}", ip, signMap);
            return responseFactory.error(ErrorCode.PayCbIpNotMatch, signMap);
        }
        // 3. 校验签名
        var service = payFactory.getService(payType);
        if (!service.verifySign(signMap, paymentChannel, signStr)) {
            log.error("签名校验失败. params:{}", signMap);
            return responseFactory.error(ErrorCode.PayCbSignNotMatch, signMap);
        }
        // 4. 校验获取订单状态
        OrderStatus status = service.convertStatus(statusStr, requestType);
        if (status == null) {
            log.error("当前系统不支持的status状态. params:{}", signMap);
            return responseFactory.error(ErrorCode.PayCbStatusNotSupport, signMap);
        }
        return responseFactory.success(status);
    }
相关推荐
Renhao-Wan1 小时前
Java 算法实践(八):贪心算法思路
java·算法·贪心算法
w***71101 小时前
常见的 Spring 项目目录结构
java·后端·spring
xyq20241 小时前
C# 判断语句详解与应用
开发语言
野犬寒鸦2 小时前
深入解析HashMap核心机制(底层数据结构及扩容机制详解剖析)
java·服务器·开发语言·数据库·后端·面试
##学无止境##3 小时前
从0到1吃透Java负载均衡:原理与算法大揭秘
java·开发语言·负载均衡
梵得儿SHI3 小时前
Spring Cloud 核心组件精讲:负载均衡深度对比 Spring Cloud LoadBalancer vs Ribbon(原理 + 策略配置 + 性能优化)
java·spring cloud·微服务·负载均衡·架构原理·对比单体与微服务架构·springcloud核心组件
Desirediscipline3 小时前
#define _CRT_SECURE_NO_WARNINGS 1
开发语言·数据结构·c++·算法·c#·github·visual studio
知识即是力量ol3 小时前
多线程并发篇(八股)
java·开发语言·八股·多线程并发
尘缘浮梦3 小时前
协程asyncio入门案例 1
开发语言·python