apple支付部分逻辑

This commit is contained in:
menxipeng
2025-11-06 23:26:02 +08:00
parent 5ac6929d2e
commit 3319343ca3
16 changed files with 737 additions and 494 deletions

View File

@@ -13,6 +13,7 @@ import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.mapper.OrderInfoMapper;
import com.ruoyi.system.service.IOrderInfoService;
import com.ruoyi.system.util.AppleyPay;
import com.ruoyi.system.util.PaymentUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -22,22 +23,22 @@ import java.util.*;
/**
* 【请填写功能名称】Service业务层处理
*
*
* @author ruoyi
* @date 2025-08-03
*/
@Service
public class OrderInfoServiceImpl implements IOrderInfoService
public class OrderInfoServiceImpl implements IOrderInfoService
{
@Autowired
private OrderInfoMapper orderInfoMapper;
@Autowired
private PaymentUtil paymentUtil;
/**
* 查询【请填写功能名称】
*
*
* @param id 【请填写功能名称】主键
* @return 【请填写功能名称】
*/
@@ -49,7 +50,7 @@ public class OrderInfoServiceImpl implements IOrderInfoService
/**
* 查询【请填写功能名称】列表
*
*
* @param orderInfo 【请填写功能名称】
* @return 【请填写功能名称】
*/
@@ -61,7 +62,7 @@ public class OrderInfoServiceImpl implements IOrderInfoService
/**
* 新增【请填写功能名称】
*
*
* @param orderInfo 【请填写功能名称】
* @return 结果
*/
@@ -74,7 +75,7 @@ public class OrderInfoServiceImpl implements IOrderInfoService
/**
* 修改【请填写功能名称】
*
*
* @param orderInfo 【请填写功能名称】
* @return 结果
*/
@@ -87,7 +88,7 @@ public class OrderInfoServiceImpl implements IOrderInfoService
/**
* 批量删除【请填写功能名称】
*
*
* @param ids 需要删除的【请填写功能名称】主键
* @return 结果
*/
@@ -99,7 +100,7 @@ public class OrderInfoServiceImpl implements IOrderInfoService
/**
* 删除【请填写功能名称】信息
*
*
* @param id 【请填写功能名称】主键
* @return 结果
*/
@@ -117,13 +118,13 @@ public class OrderInfoServiceImpl implements IOrderInfoService
if (StringUtils.isEmpty(request.getOrderName()) || request.getAmount() == null || request.getAmount() <= 0) {
return AjaxResult.error("订单信息不完整");
}
// 获取当前用户
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser == null) {
return AjaxResult.error(401,"用户未登录");
}
// 创建订单
OrderInfo orderInfo = new OrderInfo();
orderInfo.setId(UUID.randomUUID().toString().replace("-", ""));
@@ -139,7 +140,7 @@ public class OrderInfoServiceImpl implements IOrderInfoService
orderInfo.setIdDel(0L);
orderInfo.setVersion(1L);
orderInfo.setCreateTime(DateUtils.getNowDate());
// 保存订单
int result = insertOrderInfo(orderInfo);
if (result > 0) {
@@ -160,29 +161,34 @@ public class OrderInfoServiceImpl implements IOrderInfoService
if (StringUtils.isEmpty(request.getOrderId())) {
return AjaxResult.error("订单ID不能为空");
}
// 查询订单
OrderInfo orderInfo = selectOrderByOrderId(request.getOrderId());
if (orderInfo == null) {
return AjaxResult.error("订单不存在");
}
// 检查订单状态
if (orderInfo.getPayStatus() != PayStatusEnum.CREATE.getStatus()) {
return AjaxResult.error("订单状态不正确");
}
// 检查用户权限
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser == null || !loginUser.getUserId().equals(orderInfo.getUserId())) {
return AjaxResult.error("无权限操作此订单");
// if (loginUser == null || !loginUser.getUserId().equals(orderInfo.getUserId())) {
// return AjaxResult.error("无权限操作此订单");
// }
// 调用第三方支付接口 TODO// 待修改
processPayment(orderInfo, request);
boolean paymentResult = true;
if (paymentResult){
return AjaxResult.success("支付请求成功");
}else {
return AjaxResult.error("支付请求失败");
}
// 调用第三方支付接口
Map<String, Object> paymentResult = processPayment(orderInfo, request);
return AjaxResult.success("支付请求成功", paymentResult);
} catch (Exception e) {
return AjaxResult.error("支付异常: " + e.getMessage());
}
@@ -197,12 +203,12 @@ public class OrderInfoServiceImpl implements IOrderInfoService
if (orderInfo == null) {
return AjaxResult.error("订单不存在");
}
// 检查订单状态
if (orderInfo.getPayStatus() != PayStatusEnum.CREATE.getStatus()) {
return AjaxResult.error("订单状态不正确");
}
// 更新订单状态
orderInfo.setPayStatus(PayStatusEnum.PENDING.getStatus()); // 2-待出货
orderInfo.setPayTime(DateUtils.getNowDate());
@@ -210,10 +216,10 @@ public class OrderInfoServiceImpl implements IOrderInfoService
orderInfo.setCallTime(DateUtils.getNowDate());
orderInfo.setCallbackContent("支付成功回调");
orderInfo.setUpdateTime(DateUtils.getNowDate());
// 计算服务时间
calculateServiceTime(orderInfo);
// 保存订单
int result = updateOrderInfo(orderInfo);
if (result > 0) {
@@ -221,7 +227,7 @@ public class OrderInfoServiceImpl implements IOrderInfoService
} else {
return AjaxResult.error("支付完成处理失败");
}
} catch (Exception e) {
return AjaxResult.error("支付完成处理异常: " + e.getMessage());
}
@@ -235,44 +241,44 @@ public class OrderInfoServiceImpl implements IOrderInfoService
if (StringUtils.isEmpty(request.getOrderId()) || request.getRefundAmount() == null || request.getRefundAmount() <= 0) {
return AjaxResult.error("退款信息不完整");
}
// 查询订单
OrderInfo orderInfo = selectOrderByOrderId(request.getOrderId());
if (orderInfo == null) {
return AjaxResult.error("订单不存在");
}
// 检查订单状态
if (orderInfo.getPayStatus() != PayStatusEnum.PENDING.getStatus() && orderInfo.getPayStatus() != PayStatusEnum.WAIT_REFUND.getStatus()) {
return AjaxResult.error("订单状态不允许退款");
}
// 检查退款金额
if (request.getRefundAmount() > orderInfo.getAmount()) {
return AjaxResult.error("退款金额不能大于订单金额");
}
// 检查是否已退款
if (orderInfo.getRefundAmount() != null && orderInfo.getRefundAmount() > 0) {
return AjaxResult.error("订单已退款");
}
// 调用第三方退款接口
boolean refundResult = paymentUtil.processRefund(orderInfo.getOrderId().toString(), request.getRefundAmount(), orderInfo.getPayType());
if (!refundResult) {
return AjaxResult.error("第三方退款失败");
}
// 更新订单退款信息
orderInfo.setRefundAmount(request.getRefundAmount());
orderInfo.setRefundTime(DateUtils.getNowDate());
orderInfo.setUpdateTime(DateUtils.getNowDate());
// 如果是全额退款,更新订单状态
if (request.getRefundAmount().equals(orderInfo.getAmount())) {
orderInfo.setPayStatus(PayStatusEnum.REFUND.getStatus()); // 4-已退款
}
// 保存订单
int result = updateOrderInfo(orderInfo);
if (result > 0) {
@@ -280,7 +286,7 @@ public class OrderInfoServiceImpl implements IOrderInfoService
} else {
return AjaxResult.error("退款申请失败");
}
} catch (Exception e) {
return AjaxResult.error("退款异常: " + e.getMessage());
}
@@ -313,16 +319,16 @@ public class OrderInfoServiceImpl implements IOrderInfoService
if (orderInfo == null) {
return AjaxResult.error("订单不存在");
}
// 检查订单状态
if (orderInfo.getPayStatus() != PayStatusEnum.PENDING.getStatus()) {
return AjaxResult.error("订单状态不正确,只有已支付的订单才能确认出货");
}
// 更新订单状态为已完成
orderInfo.setPayStatus(PayStatusEnum.COMPLETE.getStatus()); // 3-已完成
orderInfo.setUpdateTime(DateUtils.getNowDate());
// 保存订单
int result = updateOrderInfo(orderInfo);
if (result > 0) {
@@ -330,28 +336,35 @@ public class OrderInfoServiceImpl implements IOrderInfoService
} else {
return AjaxResult.error("确认出货失败");
}
} catch (Exception e) {
return AjaxResult.error("确认出货异常: " + e.getMessage());
}
}
@Autowired
private AppleyPay appleyPay;
/**
* 处理支付过程
*/
private Map<String, Object> processPayment(OrderInfo orderInfo, PaymentRequest request) {
private boolean processPayment(OrderInfo orderInfo, PaymentRequest request) {
String payType = orderInfo.getPayType();
// 根据支付方式调用相应的支付接口
switch (request.getPayType()) {
switch (payType) {
case "aliPay":
return paymentUtil.processAliPay(orderInfo, request);
//return paymentUtil.processAliPay(orderInfo, request);
return false;
case "wechatPay":
return paymentUtil.processWechatPay(orderInfo, request);
//return paymentUtil.processWechatPay(orderInfo, request);
return false;
case "applePay":
return paymentUtil.processApplePay(orderInfo, request);
return appleyPay.setIapCertificate(String.valueOf(orderInfo.getUserId()), request.getPaymentToken(), false);
default:
Map<String, Object> errorResult = new HashMap<>();
errorResult.put("error", "不支持的支付方式");
return errorResult;
return false;
}
}
@@ -361,7 +374,7 @@ public class OrderInfoServiceImpl implements IOrderInfoService
private void calculateServiceTime(OrderInfo orderInfo) {
Date now = DateUtils.getNowDate();
orderInfo.setStartTime(now);
// 根据套餐类型计算结束时间
String packageType = orderInfo.getPackageType();
if ("1".equals(packageType)) {

View File

@@ -0,0 +1,226 @@
package com.ruoyi.system.util;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class AppleNotificationProcessor {
@Autowired
private AppleSignedPayloadDecoder payloadDecoder;
/**
* 完整的通知处理流程
*/
public void processSignedPayload(String signedPayload) {
try {
// 1. 解码
JSONObject payload = payloadDecoder.decodeSignedPayload(signedPayload);
if (payload == null) {
log.error("Payload解码失败");
return;
}
// 2. 提取各种信息
NotificationInfo notificationInfo = payloadDecoder.extractNotificationInfo(payload);
TransactionInfo transactionInfo = payloadDecoder.extractTransactionInfo(notificationInfo.getData());
RenewalInfo renewalInfo = payloadDecoder.extractRenewalInfo(notificationInfo.getData());
// 3. 记录日志
logNotificationDetails(notificationInfo, transactionInfo, renewalInfo);
// 4. 业务处理
processBusinessLogic(notificationInfo, transactionInfo, renewalInfo);
} catch (Exception e) {
log.error("处理通知完整流程异常: {}", e.getMessage(), e);
}
}
/**
* 记录详细的通知信息
*/
private void logNotificationDetails(NotificationInfo notificationInfo,
TransactionInfo transactionInfo,
RenewalInfo renewalInfo) {
log.info("=== 苹果通知详情 ===");
log.info("通知类型: {}, 子类型: {}",
notificationInfo.getNotificationType(),
notificationInfo.getSubtype());
log.info("通知UUID: {}, 版本: {}",
notificationInfo.getNotificationUUID(),
notificationInfo.getVersion());
if (transactionInfo != null) {
log.info("交易信息 - 原始ID: {}, 产品: {}",
transactionInfo.getOriginalTransactionId(),
transactionInfo.getProductId());
log.info("购买时间: {}, 过期时间: {}",
transactionInfo.getPurchaseDate(),
transactionInfo.getExpiresDate());
}
if (renewalInfo != null) {
log.info("续订信息 - 自动续订状态: {}, 续订产品: {}",
renewalInfo.getAutoRenewStatus(),
renewalInfo.getAutoRenewProductId());
}
log.info("=== 通知详情结束 ===");
}
/**
* 业务逻辑处理
*/
private void processBusinessLogic(NotificationInfo notificationInfo,
TransactionInfo transactionInfo,
RenewalInfo renewalInfo) {
String notificationType = notificationInfo.getNotificationType();
String originalTransactionId = getOriginalTransactionId(transactionInfo, renewalInfo);
// 根据通知类型路由到不同的处理方法
switch (notificationType) {
case "SUBSCRIBED":
handleSubscriptionEvent(notificationInfo, transactionInfo, renewalInfo);
break;
case "DID_RENEW":
handleRenewalEvent(notificationInfo, transactionInfo, renewalInfo);
break;
case "DID_FAIL_TO_RENEW":
handleRenewalFailureEvent(notificationInfo, transactionInfo, renewalInfo);
break;
case "DID_CHANGE_RENEWAL_PREF":
handleRenewalPreferenceChange(notificationInfo, transactionInfo, renewalInfo);
break;
case "DID_CHANGE_RENEWAL_STATUS":
handleRenewalStatusChange(notificationInfo, transactionInfo, renewalInfo);
break;
case "OFFER_REDEEMED":
handleOfferRedeemed(notificationInfo, transactionInfo, renewalInfo);
break;
case "EXPIRED":
handleExpiredEvent(notificationInfo, transactionInfo, renewalInfo);
break;
case "REFUND":
handleRefundEvent(notificationInfo, transactionInfo, renewalInfo);
break;
case "REVOKE":
handleRevokeEvent(notificationInfo, transactionInfo, renewalInfo);
break;
case "GRACE_PERIOD_EXPIRED":
handleGracePeriodExpired(notificationInfo, transactionInfo, renewalInfo);
break;
default:
log.warn("未处理的通知类型: {}", notificationType);
}
}
private String getOriginalTransactionId(TransactionInfo transactionInfo, RenewalInfo renewalInfo) {
if (transactionInfo != null && StringUtils.isNotEmpty(transactionInfo.getOriginalTransactionId())) {
return transactionInfo.getOriginalTransactionId();
}
if (renewalInfo != null && StringUtils.isNotEmpty(renewalInfo.getOriginalTransactionId())) {
return renewalInfo.getOriginalTransactionId();
}
return "Unknown";
}
// 具体的业务处理方法
private void handleSubscriptionEvent(NotificationInfo notificationInfo,
TransactionInfo transactionInfo,
RenewalInfo renewalInfo) {
log.info("处理订阅事件 - 子类型: {}", notificationInfo.getSubtype());
if (transactionInfo != null) {
}
}
private void handleRenewalEvent(NotificationInfo notificationInfo,
TransactionInfo transactionInfo,
RenewalInfo renewalInfo) {
log.info("处理续订事件 - 子类型: {}", notificationInfo.getSubtype());
if (transactionInfo != null) {
}
}
private void handleRenewalFailureEvent(NotificationInfo notificationInfo,
TransactionInfo transactionInfo,
RenewalInfo renewalInfo) {
log.warn("处理续订失败事件 - 子类型: {}", notificationInfo.getSubtype());
String transactionId = getOriginalTransactionId(transactionInfo, renewalInfo);
}
private void handleRenewalPreferenceChange(NotificationInfo notificationInfo,
TransactionInfo transactionInfo,
RenewalInfo renewalInfo) {
log.info("处理续订偏好变更 - 子类型: {}", notificationInfo.getSubtype());
if (renewalInfo != null) {
}
}
private void handleRenewalStatusChange(NotificationInfo notificationInfo,
TransactionInfo transactionInfo,
RenewalInfo renewalInfo) {
log.info("处理续订状态变更 - 子类型: {}", notificationInfo.getSubtype());
if (renewalInfo != null) {
}
}
private void handleOfferRedeemed(NotificationInfo notificationInfo,
TransactionInfo transactionInfo,
RenewalInfo renewalInfo) {
log.info("处理优惠兑换 - 子类型: {}", notificationInfo.getSubtype());
// TODO: 处理优惠兑换逻辑
}
private void handleExpiredEvent(NotificationInfo notificationInfo,
TransactionInfo transactionInfo,
RenewalInfo renewalInfo) {
log.warn("处理过期事件 - 子类型: {}", notificationInfo.getSubtype());
String transactionId = getOriginalTransactionId(transactionInfo, renewalInfo);
}
private void handleRefundEvent(NotificationInfo notificationInfo,
TransactionInfo transactionInfo,
RenewalInfo renewalInfo) {
log.warn("处理退款事件 - 子类型: {}", notificationInfo.getSubtype());
String transactionId = getOriginalTransactionId(transactionInfo, renewalInfo);
}
private void handleRevokeEvent(NotificationInfo notificationInfo,
TransactionInfo transactionInfo,
RenewalInfo renewalInfo) {
log.warn("处理权益撤销事件 - 子类型: {}", notificationInfo.getSubtype());
String transactionId = getOriginalTransactionId(transactionInfo, renewalInfo);
}
private void handleGracePeriodExpired(NotificationInfo notificationInfo,
TransactionInfo transactionInfo,
RenewalInfo renewalInfo) {
log.warn("处理宽限期过期事件 - 子类型: {}", notificationInfo.getSubtype());
String transactionId = getOriginalTransactionId(transactionInfo, renewalInfo);
}
}

View File

@@ -0,0 +1,152 @@
package com.ruoyi.system.util;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
@Service
public class AppleSignedPayloadDecoder {
private static final Logger log = LoggerFactory.getLogger(AppleSignedPayloadDecoder.class);
/**
* 直接解码Signed Payload无需公钥验证
*/
public JSONObject decodeSignedPayload(String signedPayload) {
try {
log.info("开始解码Signed Payload");
// 1. 直接解码JWT不验证签名
String[] parts = signedPayload.split("\\.");
if (parts.length != 3) {
log.error("Invalid JWT format");
return null;
}
// 2. 解码Payload部分
String payloadJson = decodeBase64Url(parts[1]);
JSONObject payload = JSON.parseObject(payloadJson);
log.info("解码成功 - 通知类型: {}", payload.getString("notificationType"));
return payload;
} catch (Exception e) {
log.error("解码Signed Payload异常: {}", e.getMessage(), e);
return null;
}
}
/**
* Base64URL解码
*/
private String decodeBase64Url(String base64Url) {
try {
// 将Base64URL转换为标准Base64
String base64 = base64Url.replace('-', '+').replace('_', '/');
// 添加padding
switch (base64.length() % 4) {
case 2: base64 += "=="; break;
case 3: base64 += "="; break;
}
byte[] decodedBytes = Base64.getDecoder().decode(base64);
return new String(decodedBytes, StandardCharsets.UTF_8);
} catch (Exception e) {
log.error("Base64URL解码失败: {}", e.getMessage());
throw new RuntimeException("Base64解码失败", e);
}
}
/**
* 提取关键通知信息
*/
public NotificationInfo extractNotificationInfo(JSONObject payload) {
NotificationInfo info = new NotificationInfo();
info.setNotificationType(payload.getString("notificationType"));
info.setSubtype(payload.getString("subtype"));
info.setNotificationUUID(payload.getString("notificationUUID"));
info.setVersion(payload.getString("version"));
info.setData(payload.getJSONObject("data"));
return info;
}
/**
* 从data中提取交易信息
*/
public TransactionInfo extractTransactionInfo(JSONObject data) {
try {
if (data == null) return null;
String signedTransactionInfo = data.getString("signedTransactionInfo");
if (StringUtils.isEmpty(signedTransactionInfo)) {
return null;
}
// 解码交易信息JWT
String[] transactionParts = signedTransactionInfo.split("\\.");
if (transactionParts.length != 3) return null;
String transactionPayload = decodeBase64Url(transactionParts[1]);
JSONObject transactionJson = JSON.parseObject(transactionPayload);
TransactionInfo transactionInfo = new TransactionInfo();
transactionInfo.setOriginalTransactionId(transactionJson.getString("originalTransactionId"));
transactionInfo.setTransactionId(transactionJson.getString("transactionId"));
transactionInfo.setProductId(transactionJson.getString("productId"));
transactionInfo.setPurchaseDate(transactionJson.getString("purchaseDate"));
transactionInfo.setExpiresDate(transactionJson.getString("expiresDate"));
transactionInfo.setOriginalPurchaseDate(transactionJson.getString("originalPurchaseDate"));
return transactionInfo;
} catch (Exception e) {
log.error("提取交易信息异常: {}", e.getMessage());
return null;
}
}
/**
* 从data中提取续订信息
*/
public RenewalInfo extractRenewalInfo(JSONObject data) {
try {
if (data == null) return null;
String signedRenewalInfo = data.getString("signedRenewalInfo");
if (StringUtils.isEmpty(signedRenewalInfo)) {
return null;
}
// 解码续订信息JWT
String[] renewalParts = signedRenewalInfo.split("\\.");
if (renewalParts.length != 3) return null;
String renewalPayload = decodeBase64Url(renewalParts[1]);
JSONObject renewalJson = JSON.parseObject(renewalPayload);
RenewalInfo renewalInfo = new RenewalInfo();
renewalInfo.setOriginalTransactionId(renewalJson.getString("originalTransactionId"));
renewalInfo.setAutoRenewStatus(renewalJson.getString("autoRenewStatus"));
renewalInfo.setProductId(renewalJson.getString("productId"));
renewalInfo.setAutoRenewProductId(renewalJson.getString("autoRenewProductId"));
return renewalInfo;
} catch (Exception e) {
log.error("提取续订信息异常: {}", e.getMessage());
return null;
}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,108 @@
package com.ruoyi.system.util;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 描述:
*
* @author MXP by 2025/11/6
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class InAppTransaction {
/**
* 购买数量
*/
private String quantity;
/**
* 产品ID
*/
private String productId;
/**
* 交易ID
*/
private String transactionId;
/**
* 原始交易ID对于订阅整个订阅周期保持不变
*/
private String originalTransactionId;
/**
* 购买日期(字符串格式)
*/
private String purchaseDate;
/**
* 购买日期(时间戳毫秒)
*/
private String purchaseDateMs;
/**
* 购买日期(太平洋时间)
*/
private String purchaseDatePst;
/**
* 原始购买日期(字符串格式)
*/
private String originalPurchaseDate;
/**
* 原始购买日期(时间戳毫秒)
*/
private String originalPurchaseDateMs;
/**
* 原始购买日期(太平洋时间)
*/
private String originalPurchaseDatePst;
/**
* 过期日期(字符串格式)- 仅订阅有效
*/
private String expiresDate;
/**
* 过期日期(时间戳毫秒)- 仅订阅有效
*/
private String expiresDateMs;
/**
* 过期日期(太平洋时间)- 仅订阅有效
*/
private String expiresDatePst;
/**
* Web订单行项目ID
*/
private String webOrderLineItemId;
/**
* 是否试用期
*/
private String isTrialPeriod;
/**
* 是否在介绍优惠期
*/
private String isInIntroOfferPeriod;
/**
* 应用内购买所有权类型
*/
private String inAppOwnershipType;
/**
* 订阅组标识符
*/
private String subscriptionGroupIdentifier;
}

View File

@@ -0,0 +1,22 @@
package com.ruoyi.system.util;
/**
* 描述:
*
* @author MXP by 2025/11/6
*/
import com.alibaba.fastjson2.JSONObject;
import lombok.Data;
/**
* 通知信息DTO
*/
@Data
class NotificationInfo {
private String notificationType;
private String subtype;
private String notificationUUID;
private String version;
private JSONObject data;
}

View File

@@ -0,0 +1,20 @@
package com.ruoyi.system.util;
/**
* 描述:
*
* @author MXP by 2025/11/6
*/
import lombok.Data;
/**
* 续订信息DTO
*/
@Data
class RenewalInfo {
private String originalTransactionId;
private String autoRenewStatus;
private String productId;
private String autoRenewProductId;
}

View File

@@ -0,0 +1,22 @@
package com.ruoyi.system.util;
/**
* 描述:
*
* @author MXP by 2025/11/6
*/
import lombok.Data;
/**
* 交易信息DTO
*/
@Data
class TransactionInfo {
private String originalTransactionId;
private String transactionId;
private String productId;
private String purchaseDate;
private String expiresDate;
private String originalPurchaseDate;
}