apple支付部分逻辑
This commit is contained in:
@@ -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)) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user