From 5220a22cbde6d1d47584242208ad33371ae3d768 Mon Sep 17 00:00:00 2001 From: ziin Date: Fri, 3 Apr 2026 16:03:53 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8DgooglePlay=E8=B4=AD=E4=B9=B0?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../googleplay/GooglePlayApiClient.java | 31 +++++++++++++------ .../googleplay/GooglePlayStateService.java | 3 ++ src/main/resources/application-dev.yml | 4 +-- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/yolo/keyborad/googleplay/GooglePlayApiClient.java b/src/main/java/com/yolo/keyborad/googleplay/GooglePlayApiClient.java index bc98e26..a6d0e73 100644 --- a/src/main/java/com/yolo/keyborad/googleplay/GooglePlayApiClient.java +++ b/src/main/java/com/yolo/keyborad/googleplay/GooglePlayApiClient.java @@ -139,12 +139,20 @@ public class GooglePlayApiClient { } 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 googleOrderId = text(root, "latestOrderId"); + + // 修正:一次性购买的订单号字段名为 "orderId" + String googleOrderId = text(root, "orderId"); + return GooglePlayPurchaseSnapshot.builder() .packageName(packageName) - .productId(text(lineItem, "productId")) + // 修正:从第一个元素中获取 productId + .productId(text(firstLineItem, "productId")) .productType(GooglePlayConstants.PRODUCT_TYPE_ONE_TIME) .purchaseToken(purchaseToken) .orderKey(resolveOrderKey(googleOrderId, purchaseToken)) @@ -152,9 +160,10 @@ public class GooglePlayApiClient { .linkedPurchaseToken(null) .state(state) .acknowledgementState(mapAcknowledgementState(text(root, "acknowledgementState"))) - .consumptionState(mapConsumptionState(text(root, "consumptionState"))) - .quantity(number(lineItem, "quantity")) - .refundableQuantity(number(root, "refundableQuantity")) + // 修正:从 productOfferDetails 中获取以下字段 + .consumptionState(mapConsumptionState(text(offerDetails, "consumptionState"))) + .quantity(number(offerDetails, "quantity")) + .refundableQuantity(number(offerDetails, "refundableQuantity")) .autoRenewEnabled(false) .accessGranted(GooglePlayConstants.STATE_ACTIVE.equals(state)) .externalAccountId(text(root.path("externalAccountIdentifiers"), "obfuscatedExternalAccountId")) @@ -218,10 +227,14 @@ public class GooglePlayApiClient { } private String mapOneTimeState(String state) { + if (state == null) { + return GooglePlayConstants.STATE_UNKNOWN; + } return switch (state) { - case "PURCHASE_STATE_PURCHASED" -> GooglePlayConstants.STATE_ACTIVE; - case "PURCHASE_STATE_PENDING" -> GooglePlayConstants.STATE_PENDING; - case "PURCHASE_STATE_CANCELLED" -> GooglePlayConstants.STATE_CANCELED; + // productsv2 API 返回短枚举名 + case "PURCHASED", "PURCHASE_STATE_PURCHASED" -> GooglePlayConstants.STATE_ACTIVE; + case "PENDING", "PURCHASE_STATE_PENDING" -> GooglePlayConstants.STATE_PENDING; + case "CANCELLED", "PURCHASE_STATE_CANCELLED" -> GooglePlayConstants.STATE_CANCELED; default -> GooglePlayConstants.STATE_UNKNOWN; }; } diff --git a/src/main/java/com/yolo/keyborad/googleplay/GooglePlayStateService.java b/src/main/java/com/yolo/keyborad/googleplay/GooglePlayStateService.java index f5930fa..fa5b8df 100644 --- a/src/main/java/com/yolo/keyborad/googleplay/GooglePlayStateService.java +++ b/src/main/java/com/yolo/keyborad/googleplay/GooglePlayStateService.java @@ -35,11 +35,14 @@ public class GooglePlayStateService { KeyboardProductItems product = loadProduct(snapshot.getProductId()); GooglePlayOrder order = buildOrder(command, snapshot); GooglePlayPurchaseToken token = buildToken(command, snapshot); + // 先保存订单以确保 order.id 已生成,钱包充值等权益分发依赖 order.id 写入交易流水 + saveOrder(order); GooglePlayUserEntitlement entitlement = null; if (command.getUserId() != null) { entitlement = entitlementApplier.apply(command.getUserId(), product, snapshot, order); purchaseRecordService.recordSuccess(command.getUserId(), product, snapshot, order, entitlement); } + // 再次保存以持久化 apply() 中修改的 deliveryStatus/grantedQuantity saveOrder(order); token.setLatestOrderKey(order.getOrderKey()); token.setLatestOrderId(order.getGoogleOrderId()); diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index f0a33be..8c1ccb9 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -54,14 +54,14 @@ apple: google: play: - enabled: false + enabled: true package-name: "com.boshan.key.of.love" service-account-key-path: "classpath:keyboard-490601-ee503a425cc4.json" oauth-token-uri: "https://oauth2.googleapis.com/token" android-publisher-scope: "https://www.googleapis.com/auth/androidpublisher" pubsub-token-info-uri: "https://oauth2.googleapis.com/tokeninfo" validate-pubsub-jwt: true - require-obfuscated-account-id: true + require-obfuscated-account-id: false pubsub: expected-topic: "projects/keyboard-490601/topics/keyboard_topic" expected-subscription: "projects/keyboard-490601/subscriptions/keyboard_topic-sub"