diff --git a/services/wallet-service/internal/service/wallet/service.go b/services/wallet-service/internal/service/wallet/service.go index 5271cf86..99387511 100644 --- a/services/wallet-service/internal/service/wallet/service.go +++ b/services/wallet-service/internal/service/wallet/service.go @@ -565,7 +565,7 @@ func (s *Service) ConfirmGooglePayment(ctx context.Context, command ledger.Googl if product.Platform != ledger.RechargeProductPlatformAndroid || product.Channel != ledger.RechargeChannelGoogle { return ledger.GooglePaymentReceipt{}, xerr.New(xerr.InvalidArgument, "recharge product is not google play product") } - if command.ProductCode != "" && command.ProductCode != product.ProductCode { + if command.ProductCode != "" && command.ProductCode != product.ProductName { return ledger.GooglePaymentReceipt{}, xerr.New(xerr.Conflict, "product_code does not match") } if !rechargeProductSupportsRegion(product, command.RegionID) { @@ -585,7 +585,7 @@ func (s *Service) ConfirmGooglePayment(ctx context.Context, command ledger.Googl if purchase.PurchaseState != ledger.GooglePurchaseStatePurchased { return ledger.GooglePaymentReceipt{}, xerr.New(xerr.Conflict, "google purchase is not purchased") } - if purchase.ProductID != "" && purchase.ProductID != product.ProductCode { + if purchase.ProductID != "" && purchase.ProductID != product.ProductName { return ledger.GooglePaymentReceipt{}, xerr.New(xerr.Conflict, "google product_id does not match recharge product") } if command.OrderID != "" && purchase.OrderID != "" && command.OrderID != purchase.OrderID { diff --git a/services/wallet-service/internal/service/wallet/service_test.go b/services/wallet-service/internal/service/wallet/service_test.go index f0965f0a..e3f504ff 100644 --- a/services/wallet-service/internal/service/wallet/service_test.go +++ b/services/wallet-service/internal/service/wallet/service_test.go @@ -2134,6 +2134,63 @@ func TestRechargeProductsAreConfiguredByPlatformRegionAndStatus(t *testing.T) { } } +// TestConfirmGooglePaymentUsesProductNameAsGoogleProductID 锁定后台 product_name 作为 Google Play 商品 ID。 +func TestConfirmGooglePaymentUsesProductNameAsGoogleProductID(t *testing.T) { + repository := mysqltest.NewRepository(t) + svc := walletservice.New(repository) + const googleProductID = "coins_1500_google" + product, err := svc.CreateRechargeProduct(context.Background(), ledger.RechargeProductCommand{ + AppCode: "lalu", + AmountMicro: 1500000, + CoinAmount: 1500, + ProductName: googleProductID, + Description: "google coin pack", + Platform: ledger.RechargeProductPlatformAndroid, + RegionIDs: []int64{1001}, + Enabled: true, + OperatorUserID: 90001, + }) + if err != nil { + t.Fatalf("CreateRechargeProduct failed: %v", err) + } + if product.ProductCode == googleProductID { + t.Fatalf("test requires internal product_code to differ from Google product ID: %+v", product) + } + svc.SetGooglePlayClient(&fakeGooglePlayClient{ + purchase: ledger.GooglePlayPurchase{ + ProductID: googleProductID, + OrderID: "GPA.1", + PurchaseState: ledger.GooglePurchaseStatePurchased, + ConsumptionState: "CONSUMPTION_STATE_YET_TO_BE_CONSUMED", + AcknowledgementState: "ACKNOWLEDGEMENT_STATE_ACKNOWLEDGED", + }, + }) + + receipt, err := svc.ConfirmGooglePayment(context.Background(), ledger.GooglePaymentCommand{ + AppCode: "lalu", + CommandID: "google-pay-product-name", + UserID: 53001, + RegionID: 1001, + ProductID: product.ProductID, + ProductCode: googleProductID, + PackageName: "com.org.laluparty", + PurchaseToken: "purchase-token-product-name", + OrderID: "GPA.1", + }) + if err != nil { + t.Fatalf("ConfirmGooglePayment failed: %v", err) + } + if receipt.Status != ledger.PaymentStatusCredited || receipt.CoinAmount != 1500 || receipt.ProductCode != product.ProductCode { + t.Fatalf("google payment receipt mismatch: %+v", receipt) + } + if got := repository.CountRows("payment_orders", "product_id = ? AND product_code = ?", product.ProductID, product.ProductCode); got != 1 { + t.Fatalf("google payment should write one payment order with internal product_code, got %d", got) + } + if got := repository.CountRows("wallet_entries", "transaction_id = ?", receipt.TransactionID); got != 1 { + t.Fatalf("google payment should write one wallet entry, got %d", got) + } +} + func TestApplyGameCoinChangeDebitCreditAndIdempotency(t *testing.T) { repository := mysqltest.NewRepository(t) repository.SetBalance(88001, 500) @@ -2283,3 +2340,25 @@ func (f *fakeActivityBadgeClient) ConsumeAchievementEvent(_ context.Context, req } return &activityv1.ConsumeAchievementEventResponse{EventId: req.GetEventId(), Status: status}, nil } + +type fakeGooglePlayClient struct { + purchase ledger.GooglePlayPurchase + err error + consumed []string +} + +func (f *fakeGooglePlayClient) GetProductPurchase(_ context.Context, packageName string, purchaseToken string) (ledger.GooglePlayPurchase, error) { + if f.err != nil { + return ledger.GooglePlayPurchase{}, f.err + } + purchase := f.purchase + if purchase.PackageName == "" { + purchase.PackageName = packageName + } + return purchase, nil +} + +func (f *fakeGooglePlayClient) ConsumeProduct(_ context.Context, _ string, productID string, _ string) error { + f.consumed = append(f.consumed, productID) + return nil +}