diff --git a/src/main/java/com/yolo/keyborad/controller/AppleReceiptController.java b/src/main/java/com/yolo/keyborad/controller/AppleReceiptController.java index 21160cc..a52175e 100644 --- a/src/main/java/com/yolo/keyborad/controller/AppleReceiptController.java +++ b/src/main/java/com/yolo/keyborad/controller/AppleReceiptController.java @@ -62,12 +62,12 @@ public class AppleReceiptController { if (body == null) { throw new BusinessException(ErrorCode.PARAMS_ERROR, "body 不能为空"); } - String receipt = body.get("receipt"); - if (receipt == null || receipt.isBlank()) { - throw new BusinessException(ErrorCode.PARAMS_ERROR, "receipt 不能为空"); + String signedPayload = body.get("signedPayload"); + if (signedPayload == null || signedPayload.isBlank()) { + throw new BusinessException(ErrorCode.PARAMS_ERROR, "signedPayload 不能为空"); } Long userId = StpUtil.getLoginIdAsLong(); - AppleReceiptValidationResult validationResult = appleReceiptService.validateReceipt(receipt); + AppleReceiptValidationResult validationResult = appleReceiptService.validateReceipt(signedPayload); applePurchaseService.processPurchase(userId, validationResult); return ResultUtils.success(Boolean.TRUE); } diff --git a/src/main/java/com/yolo/keyborad/service/AppleReceiptService.java b/src/main/java/com/yolo/keyborad/service/AppleReceiptService.java index 24c30c5..4b9db24 100644 --- a/src/main/java/com/yolo/keyborad/service/AppleReceiptService.java +++ b/src/main/java/com/yolo/keyborad/service/AppleReceiptService.java @@ -7,7 +7,7 @@ public interface AppleReceiptService { /** * 验证 base64 app receipt 是否有效,并返回解析结果。 * - * @param appReceipt Base64 的 app receipt(以 MI... 开头那串) + * @param signedPayload Base64 的 app receipt(以 MI... 开头那串) */ - AppleReceiptValidationResult validateReceipt(String appReceipt); + AppleReceiptValidationResult validateReceipt(String signedPayload); } diff --git a/src/main/java/com/yolo/keyborad/service/impl/AppleReceiptServiceImpl.java b/src/main/java/com/yolo/keyborad/service/impl/AppleReceiptServiceImpl.java index 0001e2d..4ebceee 100644 --- a/src/main/java/com/yolo/keyborad/service/impl/AppleReceiptServiceImpl.java +++ b/src/main/java/com/yolo/keyborad/service/impl/AppleReceiptServiceImpl.java @@ -1,11 +1,7 @@ package com.yolo.keyborad.service.impl; -import com.apple.itunes.storekit.client.APIException; -import com.apple.itunes.storekit.client.AppStoreServerAPIClient; -import com.apple.itunes.storekit.migration.ReceiptUtility; import com.apple.itunes.storekit.model.Environment; import com.apple.itunes.storekit.model.JWSTransactionDecodedPayload; -import com.apple.itunes.storekit.model.TransactionInfoResponse; import com.apple.itunes.storekit.verification.SignedDataVerifier; import com.apple.itunes.storekit.verification.VerificationException; import com.yolo.keyborad.model.dto.AppleReceiptValidationResult; @@ -27,102 +23,67 @@ import java.util.List; @Service public class AppleReceiptServiceImpl implements AppleReceiptService { - /** - * App Store 服务器 API 客户端 - *
- * 用于调用 Apple App Store Server API 获取交易信息 - *
- */ - private final AppStoreServerAPIClient client; - /** * 签名数据验证器 *- * 用于验证和解码 Apple 返回的 JWS 签名数据 + * 用于验证和解码客户端传来的 JWS 签名交易数据 *
*/ private final SignedDataVerifier signedDataVerifier; - - /** - * 收据工具类 - *- * 用于解析应用收据内容,提取交易 ID 等信息 - *
- */ - private final ReceiptUtility receiptUtility; /** * 构造函数 *- * 通过构造函数注入所需的依赖组件 + * 通过构造函数注入签名数据验证器 *
- * @param client App Store 服务器 API 客户端 * @param signedDataVerifier 签名数据验证器 - * @param receiptUtility 收据工具类 */ - public AppleReceiptServiceImpl(AppStoreServerAPIClient client, - SignedDataVerifier signedDataVerifier, - ReceiptUtility receiptUtility) { - this.client = client; + public AppleReceiptServiceImpl(SignedDataVerifier signedDataVerifier) { this.signedDataVerifier = signedDataVerifier; - this.receiptUtility = receiptUtility; } /** - * 验证 Apple 应用内购买收据 + * 验证 Apple 应用内购买交易 *- * 执行完整的收据验证流程,包括解析收据、获取交易信息、验证签名和业务逻辑校验 + * 直接验证从客户端传来的 JWS (JSON Web Signature) 交易数据 + * 使用 SignedDataVerifier 验证签名并解码交易信息,然后执行业务逻辑校验 *
- * @param appReceipt Base64 编码的应用内购买收据 + * @param signedTransaction 客户端传来的 JWS 格式的签名交易数据 * @return 验证结果对象,包含验证状态和交易信息 */ @Override - public AppleReceiptValidationResult validateReceipt(String appReceipt) { - // 检查收据是否为空 - if (appReceipt == null || appReceipt.isBlank()) { - return invalid("empty_receipt"); + public AppleReceiptValidationResult validateReceipt(String signedTransaction) { + // 检查 JWS 是否为空 + if (signedTransaction == null || signedTransaction.isBlank()) { + return invalid("empty_transaction"); } try { - // 1. 从收据里解析出 transactionId(不做验证,只是解析 ASN.1) - String transactionId = receiptUtility.extractTransactionIdFromAppReceipt(appReceipt); -// todo 验证服务器传输的transactionId -// String transactionId = receiptUtility.extractTransactionIdFromTransactionReceipt(appReceipt); - if (transactionId == null) { - return invalid("no_transaction_id_in_receipt"); - } + // 1. 使用 SignedDataVerifier 直接验证 JWS 并解码交易 payload + // 这会验证签名、证书链、bundle ID、环境等 + JWSTransactionDecodedPayload payload = + signedDataVerifier.verifyAndDecodeTransaction(signedTransaction); - // 2. 调用 App Store Server API 获取单笔交易信息 - TransactionInfoResponse infoResponse = client.getTransactionInfo(transactionId); + // 2. 记录交易信息用于调试 + log.info("Verified transaction: transactionId={}, productId={}, purchaseDate={}", + payload.getTransactionId(), + payload.getProductId(), + payload.getPurchaseDate()); - String signedTransactionInfo = infoResponse.getSignedTransactionInfo(); - if (signedTransactionInfo == null) { - return invalid("no_signed_transaction_info"); - } - - // 3. 使用 SignedDataVerifier 验证 JWS 并解码 payload - JWSTransactionDecodedPayload payload = - signedDataVerifier.verifyAndDecodeTransaction(signedTransactionInfo); - - // 4. 执行业务校验:检查交易是否仍然有效 + // 3. 执行业务校验:检查交易是否仍然有效 boolean stillActive = isTransactionActive(payload); - // 构建并返回验证结果 + // 4. 构建并返回验证结果 return getAppleReceiptValidationResult(stillActive, payload); + } catch (VerificationException e) { - // 验证异常处理 - log.warn("Apple receipt verification failed", e); + // 验证异常处理:签名无效、证书链问题、bundle ID 不匹配等 + log.warn("Apple transaction verification failed: status={}", e.getStatus(), e); return invalid("verification_exception:" + e.getStatus()); - } catch (APIException e) { - // API 异常处理,可以根据错误码进一步处理(如环境切换) - // 4040010 表示订单不存在,可能是环境不匹配导致 - long code = e.getApiError().errorCode(); - log.warn("App Store API error, code={}", code, e); - return invalid("api_exception:" + code); } catch (Exception e) { // 其他未预期异常处理 - log.error("Unexpected error when validating Apple receipt", e); - return invalid("unexpected_error"); + log.error("Unexpected error when validating Apple transaction", e); + return invalid("unexpected_error:" + e.getMessage()); } }