From 83cb65a31fc92f278cc4f91729cd565cd086f9f1 Mon Sep 17 00:00:00 2001 From: ziin Date: Thu, 19 Mar 2026 08:53:22 +0800 Subject: [PATCH] =?UTF-8?q?feat(googleplay):=20=E6=96=B0=E5=A2=9E=E8=B4=AD?= =?UTF-8?q?=E4=B9=B0=E6=88=90=E5=8A=9F=E8=AE=B0=E5=BD=95=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=B9=B6=E6=B3=A8=E5=85=A5=E4=BD=BF=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../GooglePlayPurchaseRecordService.java | 137 ++++++++++++++++++ .../googleplay/GooglePlayStateService.java | 2 + .../KeyboardUserPurchaseRecordsService.java | 2 - 3 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/yolo/keyborad/googleplay/GooglePlayPurchaseRecordService.java diff --git a/src/main/java/com/yolo/keyborad/googleplay/GooglePlayPurchaseRecordService.java b/src/main/java/com/yolo/keyborad/googleplay/GooglePlayPurchaseRecordService.java new file mode 100644 index 0000000..ceb7b00 --- /dev/null +++ b/src/main/java/com/yolo/keyborad/googleplay/GooglePlayPurchaseRecordService.java @@ -0,0 +1,137 @@ +package com.yolo.keyborad.googleplay; + +import com.yolo.keyborad.googleplay.model.GooglePlayPurchaseSnapshot; +import com.yolo.keyborad.model.entity.KeyboardProductItems; +import com.yolo.keyborad.model.entity.KeyboardUserPurchaseRecords; +import com.yolo.keyborad.model.entity.googleplay.GooglePlayOrder; +import com.yolo.keyborad.model.entity.googleplay.GooglePlayUserEntitlement; +import com.yolo.keyborad.service.KeyboardUserPurchaseRecordsService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.Objects; + +@Component +@RequiredArgsConstructor +public class GooglePlayPurchaseRecordService { + + private static final String PURCHASE_STATUS_PAID = "PAID"; + + private final KeyboardUserPurchaseRecordsService purchaseRecordsService; + + public void recordSuccess(Long userId, + KeyboardProductItems product, + GooglePlayPurchaseSnapshot snapshot, + GooglePlayOrder order, + GooglePlayUserEntitlement entitlement) { + if (!shouldRecord(userId, order, entitlement)) { + return; + } + String transactionId = resolveTransactionId(order); + if (alreadyRecorded(transactionId)) { + return; + } + purchaseRecordsService.save(buildRecord(userId, product, snapshot, order, transactionId)); + } + + private boolean shouldRecord(Long userId, + GooglePlayOrder order, + GooglePlayUserEntitlement entitlement) { + return userId != null + && order != null + && entitlement != null + && Boolean.TRUE.equals(entitlement.getActive()) + && !GooglePlayConstants.DELIVERY_REVOKED.equals(order.getDeliveryStatus()) + && !GooglePlayConstants.DELIVERY_MANUAL_REVIEW.equals(order.getDeliveryStatus()); + } + + private boolean alreadyRecorded(String transactionId) { + return purchaseRecordsService.lambdaQuery() + .eq(KeyboardUserPurchaseRecords::getTransactionId, transactionId) + .eq(KeyboardUserPurchaseRecords::getPaymentMethod, GooglePlayConstants.PAYMENT_METHOD_GOOGLE_PLAY) + .exists(); + } + + private KeyboardUserPurchaseRecords buildRecord(Long userId, + KeyboardProductItems product, + GooglePlayPurchaseSnapshot snapshot, + GooglePlayOrder order, + String transactionId) { + KeyboardUserPurchaseRecords record = new KeyboardUserPurchaseRecords(); + record.setUserId(Math.toIntExact(userId)); + record.setProductId(product.getProductId()); + record.setPurchaseQuantity(resolvePurchaseQuantity(product, snapshot, order)); + record.setPrice(product.getPrice()); + record.setCurrency(product.getCurrency()); + record.setPurchaseType(resolvePurchaseType(product, snapshot)); + Date purchaseTime = resolvePurchaseTime(snapshot, order); + record.setPurchaseTime(purchaseTime); + record.setStatus(PURCHASE_STATUS_PAID); + record.setPaymentMethod(GooglePlayConstants.PAYMENT_METHOD_GOOGLE_PLAY); + record.setTransactionId(transactionId); + record.setOriginalTransactionId(order.getPurchaseToken()); + record.setProductIds(new String[]{product.getProductId()}); + record.setPurchaseDate(purchaseTime); + record.setExpiresDate(snapshot.getExpiryTime()); + return record; + } + + private Integer resolvePurchaseQuantity(KeyboardProductItems product, + GooglePlayPurchaseSnapshot snapshot, + GooglePlayOrder order) { + Integer configured = product.getDurationValue(); + if (configured != null) { + return configured; + } + Integer parsed = parseInteger(product.getName()); + if (parsed != null) { + return parsed; + } + if (snapshot.getQuantity() != null) { + return snapshot.getQuantity(); + } + return Objects.requireNonNullElse(order.getQuantity(), 1); + } + + private Integer parseInteger(String raw) { + if (raw == null || raw.isBlank()) { + return null; + } + String digits = raw.replaceAll("[^\\d]", ""); + if (digits.isBlank()) { + return null; + } + return Integer.parseInt(digits); + } + + private String resolvePurchaseType(KeyboardProductItems product, GooglePlayPurchaseSnapshot snapshot) { + if (product.getType() != null && !product.getType().isBlank()) { + return product.getType(); + } + if (GooglePlayConstants.PRODUCT_TYPE_SUBSCRIPTION.equals(snapshot.getProductType())) { + return "subscription"; + } + return "in-app-purchase"; + } + + private Date resolvePurchaseTime(GooglePlayPurchaseSnapshot snapshot, GooglePlayOrder order) { + if (snapshot.getStartTime() != null) { + return snapshot.getStartTime(); + } + if (order.getLastEventTime() != null) { + return order.getLastEventTime(); + } + if (snapshot.getLastSyncedAt() != null) { + return snapshot.getLastSyncedAt(); + } + return new Date(); + } + + private String resolveTransactionId(GooglePlayOrder order) { + if (order.getGoogleOrderId() != null && !order.getGoogleOrderId().isBlank()) { + return order.getGoogleOrderId(); + } + return order.getOrderKey(); + } +} diff --git a/src/main/java/com/yolo/keyborad/googleplay/GooglePlayStateService.java b/src/main/java/com/yolo/keyborad/googleplay/GooglePlayStateService.java index 3378677..f5930fa 100644 --- a/src/main/java/com/yolo/keyborad/googleplay/GooglePlayStateService.java +++ b/src/main/java/com/yolo/keyborad/googleplay/GooglePlayStateService.java @@ -28,6 +28,7 @@ public class GooglePlayStateService { private final GooglePlayPurchaseTokenMapper purchaseTokenMapper; private final KeyboardProductItemsService productItemsService; private final GooglePlayEntitlementApplier entitlementApplier; + private final GooglePlayPurchaseRecordService purchaseRecordService; @Transactional(rollbackFor = Exception.class) public GooglePlaySyncResult sync(GooglePlaySyncCommand command, GooglePlayPurchaseSnapshot snapshot) { @@ -37,6 +38,7 @@ public class GooglePlayStateService { GooglePlayUserEntitlement entitlement = null; if (command.getUserId() != null) { entitlement = entitlementApplier.apply(command.getUserId(), product, snapshot, order); + purchaseRecordService.recordSuccess(command.getUserId(), product, snapshot, order, entitlement); } saveOrder(order); token.setLatestOrderKey(order.getOrderKey()); diff --git a/src/main/java/com/yolo/keyborad/service/KeyboardUserPurchaseRecordsService.java b/src/main/java/com/yolo/keyborad/service/KeyboardUserPurchaseRecordsService.java index 80f557a..f0786d8 100644 --- a/src/main/java/com/yolo/keyborad/service/KeyboardUserPurchaseRecordsService.java +++ b/src/main/java/com/yolo/keyborad/service/KeyboardUserPurchaseRecordsService.java @@ -8,6 +8,4 @@ import com.baomidou.mybatisplus.extension.service.IService; */ public interface KeyboardUserPurchaseRecordsService extends IService{ - - }