feat(pay): 添加PingPong支付回调处理功能

- 新增CallbackController接收并处理PingPong支付回调通知
- 实现回调签名验证逻辑
- 添加支付结果页面展示功能,支持成功、失败、审核中等状态显示
- 创建CallbackService接口及实现类处理回调业务逻辑
- 新增账单地址DTO(BillingDTO)用于风险信息传输
- 添加航空信息DTO(AirlineDTO)和租车信息DTO(CarRentalDTO)作为扩展风险数据结构
- 完善Checkout请求DTO字段校验规则,增强数据安全性
- 实现订单状态映射与更新机制,确保支付状态同步准确
- 记录回调处理日志便于问题追踪与审计
This commit is contained in:
2025-12-18 17:47:35 +08:00
parent 723676ddb3
commit 1cf4914d8b
8 changed files with 467 additions and 0 deletions

View File

@@ -0,0 +1,80 @@
package com.mtkj.mtpay.dto.request;
import com.mtkj.mtpay.dto.RiskInfoDTO;
import jakarta.validation.Valid;
import jakarta.validation.constraints.*;
import lombok.Data;
import java.io.Serializable;
/**
* Checkout接口请求DTO
*/
@Data
public class CheckoutRequestDTO implements Serializable {
@NotBlank(message = "商户店铺编号不能为空")
@Size(max = 64, message = "商户店铺编号长度不能超过64")
private String accId;
@NotBlank(message = "交易金额不能为空")
@Pattern(regexp = "^\\d+(\\.\\d{2})?$", message = "交易金额格式不正确,需保留两位小数")
@Size(max = 12, message = "交易金额长度不能超过12")
private String amount;
@NotBlank(message = "交易币种不能为空")
@Size(min = 3, max = 3, message = "交易币种必须为3位ISO 4217币种代码")
private String currency;
@NotBlank(message = "商户订单号不能为空")
@Size(max = 64, message = "商户订单号长度不能超过64")
private String merchantTransactionId;
@NotBlank(message = "交易类型不能为空")
@Pattern(regexp = "^(SALE|AUTH)$", message = "交易类型必须为SALE或AUTH")
private String paymentType;
@Size(max = 64, message = "支付方式长度不能超过64")
private String paymentBrand;
@NotBlank(message = "结果重定向URL不能为空")
@Size(max = 255, message = "结果重定向URL长度不能超过255")
private String shopperResultUrl;
@NotBlank(message = "取消重定向URL不能为空")
@Size(max = 255, message = "取消重定向URL长度不能超过255")
private String shopperCancelUrl;
@NotBlank(message = "签名类型不能为空")
@Pattern(regexp = "^(MD5|SHA256)$", message = "签名类型必须为MD5或SHA256")
private String signType;
@NotBlank(message = "签名不能为空")
@Size(max = 255, message = "签名长度不能超过255")
private String sign;
@Size(max = 64, message = "商户用户ID长度不能超过64")
private String merchantUserId;
@Size(min = 2, max = 4, message = "语言代码长度必须在2-4之间")
private String language = "en";
@Pattern(regexp = "^(Y|N)$", message = "3DS参数必须为Y或N")
private String threeDSecure = "N";
@Size(max = 64, message = "原始商户交易订单号长度不能超过64")
private String primaryMerchantTransactionId;
private Integer periodsNum;
@NotNull(message = "风控信息不能为空")
@Valid
private RiskInfoDTO riskInfo;
@Size(max = 255, message = "异步通知地址长度不能超过255")
private String notificationUrl;
@Size(max = 500, message = "备注长度不能超过500")
private String remark;
}

View File

@@ -0,0 +1,37 @@
package com.mtkj.mtpay.dto.response;
import lombok.Data;
import java.io.Serializable;
/**
* Checkout接口响应DTO
*/
@Data
public class CheckoutResponseDTO implements Serializable {
private String clientId;
private String accId;
private String merchantTransactionId;
private String code;
private String description;
private String token;
private String paymentUrl;
private String innerJsUrl;
private String signType;
private String sign;
private String remark;
private String paymentHtml;
}

View File

@@ -0,0 +1,15 @@
package com.mtkj.mtpay.dto.risk;
import lombok.Data;
import java.io.Serializable;
/**
* 航空信息DTO
*/
@Data
public class AirlineDTO implements Serializable {
// 根据实际需求添加字段
}

View File

@@ -0,0 +1,31 @@
package com.mtkj.mtpay.dto.risk;
import lombok.Data;
import java.io.Serializable;
/**
* 账单地址DTO
*/
@Data
public class BillingDTO implements Serializable {
private String firstName;
private String lastName;
private String phone;
private String email;
private String street;
private String postcode;
private String city;
private String state;
private String country;
}

View File

@@ -0,0 +1,15 @@
package com.mtkj.mtpay.dto.risk;
import lombok.Data;
import java.io.Serializable;
/**
* 租车信息DTO
*/
@Data
public class CarRentalDTO implements Serializable {
// 根据实际需求添加字段
}