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);
}