钱包
This commit is contained in:
parent
93e5cd0218
commit
09d5747cbd
@ -915,7 +915,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.ProductName {
|
||||
if command.ProductCode != "" && command.ProductCode != product.ProductName && command.ProductCode != product.ProductCode {
|
||||
return ledger.GooglePaymentReceipt{}, xerr.New(xerr.Conflict, "product_code does not match")
|
||||
}
|
||||
if !rechargeProductSupportsRegion(product, command.RegionID) {
|
||||
@ -935,6 +935,8 @@ 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")
|
||||
}
|
||||
// 旧 App 首充曾把钱包内部 product_code 当成确认参数;上面的兼容只负责放行历史入参,
|
||||
// 真正的 Google 商品事实仍以 purchase.ProductID 对齐 product_name,避免内部编码绕过支付商品校验。
|
||||
if purchase.ProductID != "" && purchase.ProductID != product.ProductName {
|
||||
return ledger.GooglePaymentReceipt{}, xerr.New(xerr.Conflict, "google product_id does not match recharge product")
|
||||
}
|
||||
|
||||
@ -4158,7 +4158,7 @@ func TestRechargeProductsAreConfiguredByPlatformRegionAndStatus(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("CreateRechargeProduct failed: %v", err)
|
||||
}
|
||||
if product.ProductID <= 0 || product.ProductCode == "" || product.Channel != ledger.RechargeChannelGoogle {
|
||||
if product.ProductID <= 0 || product.ProductCode != product.ProductName || product.Channel != ledger.RechargeChannelGoogle {
|
||||
t.Fatalf("created product identifiers mismatch: %+v", product)
|
||||
}
|
||||
if len(product.RegionIDs) != 2 || product.RegionIDs[0] != 1001 || product.RegionIDs[1] != 1002 {
|
||||
@ -4184,7 +4184,7 @@ func TestRechargeProductsAreConfiguredByPlatformRegionAndStatus(t *testing.T) {
|
||||
t.Fatalf("ios list must not include android product: %+v", iosProducts)
|
||||
}
|
||||
|
||||
_, err = svc.UpdateRechargeProduct(context.Background(), ledger.RechargeProductCommand{
|
||||
updatedProduct, err := svc.UpdateRechargeProduct(context.Background(), ledger.RechargeProductCommand{
|
||||
AppCode: "lalu",
|
||||
ProductID: product.ProductID,
|
||||
AmountMicro: 2500000,
|
||||
@ -4199,6 +4199,9 @@ func TestRechargeProductsAreConfiguredByPlatformRegionAndStatus(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("UpdateRechargeProduct failed: %v", err)
|
||||
}
|
||||
if updatedProduct.ProductCode != updatedProduct.ProductName {
|
||||
t.Fatalf("updated product_code should follow product_name: %+v", updatedProduct)
|
||||
}
|
||||
|
||||
inactiveProducts, _, err := svc.ListRechargeProducts(context.Background(), 53001, 1002, ledger.RechargeProductPlatformAndroid, ledger.RechargeAudienceNormal)
|
||||
if err != nil {
|
||||
@ -4333,8 +4336,8 @@ func TestConfirmGooglePaymentUsesProductNameAsGoogleProductID(t *testing.T) {
|
||||
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)
|
||||
if product.ProductCode != googleProductID {
|
||||
t.Fatalf("android product_code should match Google product ID: %+v", product)
|
||||
}
|
||||
svc.SetGooglePlayClient(&fakeGooglePlayClient{
|
||||
purchase: ledger.GooglePlayPurchase{
|
||||
@ -4377,6 +4380,58 @@ func TestConfirmGooglePaymentUsesProductNameAsGoogleProductID(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfirmGooglePaymentAcceptsLegacyProductCodeInput(t *testing.T) {
|
||||
repository := mysqltest.NewRepository(t)
|
||||
svc := walletservice.New(repository)
|
||||
const googleProductID = "coins_legacy_first_recharge"
|
||||
const legacyProductCode = "iap_android_legacy"
|
||||
product, err := svc.CreateRechargeProduct(context.Background(), ledger.RechargeProductCommand{
|
||||
AppCode: "lalu",
|
||||
AmountMicro: 1990000,
|
||||
CoinAmount: 160000,
|
||||
ProductName: googleProductID,
|
||||
Description: "legacy first recharge pack",
|
||||
Platform: ledger.RechargeProductPlatformAndroid,
|
||||
RegionIDs: []int64{1001},
|
||||
Enabled: true,
|
||||
OperatorUserID: 90001,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateRechargeProduct failed: %v", err)
|
||||
}
|
||||
repository.SetRechargeProductCode(product.ProductID, legacyProductCode)
|
||||
svc.SetGooglePlayClient(&fakeGooglePlayClient{
|
||||
purchase: ledger.GooglePlayPurchase{
|
||||
ProductID: googleProductID,
|
||||
OrderID: "GPA.legacy",
|
||||
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-legacy-product-code",
|
||||
UserID: 53002,
|
||||
RegionID: 1001,
|
||||
ProductID: product.ProductID,
|
||||
ProductCode: legacyProductCode,
|
||||
PackageName: "com.org.laluparty",
|
||||
PurchaseToken: "purchase-token-legacy-product-code",
|
||||
OrderID: "GPA.legacy",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("ConfirmGooglePayment should accept legacy product_code input: %v", err)
|
||||
}
|
||||
if receipt.Status != ledger.PaymentStatusCredited || receipt.ProductCode != legacyProductCode || receipt.CoinAmount != 160000 {
|
||||
t.Fatalf("legacy google payment receipt mismatch: %+v", receipt)
|
||||
}
|
||||
if got := repository.CountRows("payment_orders", "product_id = ? AND product_code = ? AND product_name = ?", product.ProductID, legacyProductCode, googleProductID); got != 1 {
|
||||
t.Fatalf("google payment should keep legacy product_code snapshot while validating product_name, got %d", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyGameCoinChangeDebitCreditAndIdempotency(t *testing.T) {
|
||||
repository := mysqltest.NewRepository(t)
|
||||
repository.SetBalance(88001, 500)
|
||||
|
||||
@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
@ -102,7 +101,7 @@ func (r *Repository) ListAdminRechargeProducts(ctx context.Context, query ledger
|
||||
return products, total, nil
|
||||
}
|
||||
|
||||
// CreateRechargeProduct 写入内购商品和区域映射,product_code 由商品 ID 派生保持稳定。
|
||||
// CreateRechargeProduct 写入内购商品和区域映射,product_code 和 Google 商品 ID 保持一致。
|
||||
func (r *Repository) CreateRechargeProduct(ctx context.Context, command ledger.RechargeProductCommand) (ledger.RechargeProduct, error) {
|
||||
if r == nil || r.db == nil {
|
||||
return ledger.RechargeProduct{}, xerr.New(xerr.Unavailable, "mysql repository is not configured")
|
||||
@ -136,7 +135,7 @@ func (r *Repository) CreateRechargeProduct(ctx context.Context, command ledger.R
|
||||
if err != nil {
|
||||
return ledger.RechargeProduct{}, err
|
||||
}
|
||||
productCode := fmt.Sprintf("iap_%s_%d", command.Platform, productID)
|
||||
productCode := command.ProductName
|
||||
if _, err := tx.ExecContext(ctx, `
|
||||
UPDATE wallet_recharge_products
|
||||
SET product_code = ?
|
||||
@ -154,7 +153,7 @@ func (r *Repository) CreateRechargeProduct(ctx context.Context, command ledger.R
|
||||
return r.GetRechargeProduct(ctx, command.AppCode, productID)
|
||||
}
|
||||
|
||||
// UpdateRechargeProduct 覆盖商品展示、价格、平台、上架状态和区域映射。
|
||||
// UpdateRechargeProduct 覆盖商品展示、价格、平台、上架状态和区域映射,并同步商品编码。
|
||||
func (r *Repository) UpdateRechargeProduct(ctx context.Context, command ledger.RechargeProductCommand) (ledger.RechargeProduct, error) {
|
||||
if r == nil || r.db == nil {
|
||||
return ledger.RechargeProduct{}, xerr.New(xerr.Unavailable, "mysql repository is not configured")
|
||||
@ -172,11 +171,11 @@ func (r *Repository) UpdateRechargeProduct(ctx context.Context, command ledger.R
|
||||
nowMs := time.Now().UnixMilli()
|
||||
result, err := tx.ExecContext(ctx, `
|
||||
UPDATE wallet_recharge_products
|
||||
SET audience_type = ?, product_name = ?, description = ?, platform = ?, channel = ?, currency_code = ?,
|
||||
SET audience_type = ?, product_code = ?, product_name = ?, description = ?, platform = ?, channel = ?, currency_code = ?,
|
||||
amount_micro = ?, coin_amount = ?, resource_asset_type = ?, status = ?,
|
||||
updated_by_user_id = ?, updated_at_ms = ?
|
||||
WHERE app_code = ? AND product_id = ?`,
|
||||
command.AudienceType, command.ProductName, command.Description, command.Platform, ledger.RechargeChannelForPlatform(command.Platform),
|
||||
command.AudienceType, command.ProductName, command.ProductName, command.Description, command.Platform, ledger.RechargeChannelForPlatform(command.Platform),
|
||||
ledger.PaidCurrencyUSDT, command.AmountMicro, command.CoinAmount, rechargeProductAssetType(command.AudienceType), productStatus(command.Enabled),
|
||||
command.OperatorUserID, nowMs, command.AppCode, command.ProductID,
|
||||
)
|
||||
@ -301,8 +300,8 @@ func scanRechargeProduct(scanner scanTarget) (ledger.RechargeProduct, error) {
|
||||
); err != nil {
|
||||
return ledger.RechargeProduct{}, err
|
||||
}
|
||||
if product.ProductCode == "" && product.ProductID > 0 {
|
||||
product.ProductCode = fmt.Sprintf("iap_%s_%d", product.Platform, product.ProductID)
|
||||
if product.ProductCode == "" {
|
||||
product.ProductCode = product.ProductName
|
||||
}
|
||||
if product.Channel == "" {
|
||||
product.Channel = ledger.RechargeChannelForPlatform(product.Platform)
|
||||
|
||||
@ -45,6 +45,19 @@ func (r *Repository) CountRows(table string, where string, args ...any) int {
|
||||
return count
|
||||
}
|
||||
|
||||
// SetRechargeProductCode overwrites product_code to model legacy rows created before code matched product_name.
|
||||
func (r *Repository) SetRechargeProductCode(productID int64, productCode string) {
|
||||
r.t.Helper()
|
||||
if _, err := r.schema.DB.ExecContext(context.Background(), `
|
||||
UPDATE wallet_recharge_products
|
||||
SET product_code = ?
|
||||
WHERE product_id = ?`,
|
||||
productCode, productID,
|
||||
); err != nil {
|
||||
r.t.Fatalf("set recharge product code failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Repository) ActiveResourceEntitlement(userID int64, resourceID int64) (expiresAtMS int64, quantity int64, remainingQuantity int64) {
|
||||
r.t.Helper()
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user