修复googlePlay购买验证问题

This commit is contained in:
2026-04-03 16:03:53 +08:00
parent 83cb65a31f
commit 5220a22cbd
3 changed files with 27 additions and 11 deletions

View File

@@ -139,12 +139,20 @@ public class GooglePlayApiClient {
} }
private GooglePlayPurchaseSnapshot mapOneTimeSnapshot(String packageName, String purchaseToken, JsonNode root) { private GooglePlayPurchaseSnapshot mapOneTimeSnapshot(String packageName, String purchaseToken, JsonNode root) {
JsonNode lineItem = root.path("productLineItem"); // 1. 获取 productLineItem 数组的第一个元素 (index 0)
JsonNode firstLineItem = root.path("productLineItem").path(0);
// 2. 获取嵌套的 productOfferDetails 对象
JsonNode offerDetails = firstLineItem.path("productOfferDetails");
String state = mapOneTimeState(text(root.path("purchaseStateContext"), "purchaseState")); String state = mapOneTimeState(text(root.path("purchaseStateContext"), "purchaseState"));
String googleOrderId = text(root, "latestOrderId");
// 修正:一次性购买的订单号字段名为 "orderId"
String googleOrderId = text(root, "orderId");
return GooglePlayPurchaseSnapshot.builder() return GooglePlayPurchaseSnapshot.builder()
.packageName(packageName) .packageName(packageName)
.productId(text(lineItem, "productId")) // 修正:从第一个元素中获取 productId
.productId(text(firstLineItem, "productId"))
.productType(GooglePlayConstants.PRODUCT_TYPE_ONE_TIME) .productType(GooglePlayConstants.PRODUCT_TYPE_ONE_TIME)
.purchaseToken(purchaseToken) .purchaseToken(purchaseToken)
.orderKey(resolveOrderKey(googleOrderId, purchaseToken)) .orderKey(resolveOrderKey(googleOrderId, purchaseToken))
@@ -152,9 +160,10 @@ public class GooglePlayApiClient {
.linkedPurchaseToken(null) .linkedPurchaseToken(null)
.state(state) .state(state)
.acknowledgementState(mapAcknowledgementState(text(root, "acknowledgementState"))) .acknowledgementState(mapAcknowledgementState(text(root, "acknowledgementState")))
.consumptionState(mapConsumptionState(text(root, "consumptionState"))) // 修正:从 productOfferDetails 中获取以下字段
.quantity(number(lineItem, "quantity")) .consumptionState(mapConsumptionState(text(offerDetails, "consumptionState")))
.refundableQuantity(number(root, "refundableQuantity")) .quantity(number(offerDetails, "quantity"))
.refundableQuantity(number(offerDetails, "refundableQuantity"))
.autoRenewEnabled(false) .autoRenewEnabled(false)
.accessGranted(GooglePlayConstants.STATE_ACTIVE.equals(state)) .accessGranted(GooglePlayConstants.STATE_ACTIVE.equals(state))
.externalAccountId(text(root.path("externalAccountIdentifiers"), "obfuscatedExternalAccountId")) .externalAccountId(text(root.path("externalAccountIdentifiers"), "obfuscatedExternalAccountId"))
@@ -218,10 +227,14 @@ public class GooglePlayApiClient {
} }
private String mapOneTimeState(String state) { private String mapOneTimeState(String state) {
if (state == null) {
return GooglePlayConstants.STATE_UNKNOWN;
}
return switch (state) { return switch (state) {
case "PURCHASE_STATE_PURCHASED" -> GooglePlayConstants.STATE_ACTIVE; // productsv2 API 返回短枚举名
case "PURCHASE_STATE_PENDING" -> GooglePlayConstants.STATE_PENDING; case "PURCHASED", "PURCHASE_STATE_PURCHASED" -> GooglePlayConstants.STATE_ACTIVE;
case "PURCHASE_STATE_CANCELLED" -> GooglePlayConstants.STATE_CANCELED; case "PENDING", "PURCHASE_STATE_PENDING" -> GooglePlayConstants.STATE_PENDING;
case "CANCELLED", "PURCHASE_STATE_CANCELLED" -> GooglePlayConstants.STATE_CANCELED;
default -> GooglePlayConstants.STATE_UNKNOWN; default -> GooglePlayConstants.STATE_UNKNOWN;
}; };
} }

View File

@@ -35,11 +35,14 @@ public class GooglePlayStateService {
KeyboardProductItems product = loadProduct(snapshot.getProductId()); KeyboardProductItems product = loadProduct(snapshot.getProductId());
GooglePlayOrder order = buildOrder(command, snapshot); GooglePlayOrder order = buildOrder(command, snapshot);
GooglePlayPurchaseToken token = buildToken(command, snapshot); GooglePlayPurchaseToken token = buildToken(command, snapshot);
// 先保存订单以确保 order.id 已生成,钱包充值等权益分发依赖 order.id 写入交易流水
saveOrder(order);
GooglePlayUserEntitlement entitlement = null; GooglePlayUserEntitlement entitlement = null;
if (command.getUserId() != null) { if (command.getUserId() != null) {
entitlement = entitlementApplier.apply(command.getUserId(), product, snapshot, order); entitlement = entitlementApplier.apply(command.getUserId(), product, snapshot, order);
purchaseRecordService.recordSuccess(command.getUserId(), product, snapshot, order, entitlement); purchaseRecordService.recordSuccess(command.getUserId(), product, snapshot, order, entitlement);
} }
// 再次保存以持久化 apply() 中修改的 deliveryStatus/grantedQuantity
saveOrder(order); saveOrder(order);
token.setLatestOrderKey(order.getOrderKey()); token.setLatestOrderKey(order.getOrderKey());
token.setLatestOrderId(order.getGoogleOrderId()); token.setLatestOrderId(order.getGoogleOrderId());

View File

@@ -54,14 +54,14 @@ apple:
google: google:
play: play:
enabled: false enabled: true
package-name: "com.boshan.key.of.love" package-name: "com.boshan.key.of.love"
service-account-key-path: "classpath:keyboard-490601-ee503a425cc4.json" service-account-key-path: "classpath:keyboard-490601-ee503a425cc4.json"
oauth-token-uri: "https://oauth2.googleapis.com/token" oauth-token-uri: "https://oauth2.googleapis.com/token"
android-publisher-scope: "https://www.googleapis.com/auth/androidpublisher" android-publisher-scope: "https://www.googleapis.com/auth/androidpublisher"
pubsub-token-info-uri: "https://oauth2.googleapis.com/tokeninfo" pubsub-token-info-uri: "https://oauth2.googleapis.com/tokeninfo"
validate-pubsub-jwt: true validate-pubsub-jwt: true
require-obfuscated-account-id: true require-obfuscated-account-id: false
pubsub: pubsub:
expected-topic: "projects/keyboard-490601/topics/keyboard_topic" expected-topic: "projects/keyboard-490601/topics/keyboard_topic"
expected-subscription: "projects/keyboard-490601/subscriptions/keyboard_topic-sub" expected-subscription: "projects/keyboard-490601/subscriptions/keyboard_topic-sub"