removed all "paid" features - no more stars, gifts, or grams
This commit is contained in:
parent
d4451d753c
commit
21d8e91756
165 changed files with 318 additions and 40948 deletions
|
|
@ -2,19 +2,19 @@ package rpc
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
"github.com/iamxvbaba/td/tg"
|
||||
"github.com/iamxvbaba/td/tgerr"
|
||||
|
||||
"github.com/iamxvbaba/td/tlprofile"
|
||||
"telesrv/internal/compat/tdesktop"
|
||||
"telesrv/internal/domain"
|
||||
)
|
||||
|
||||
// registerPayments 注册 payments.* RPC:Stars 本地账本(余额/流水真实化)+ 其余
|
||||
// gift/auction/revenue 第一阶段兼容桩。
|
||||
// registerPayments 注册 payments.* RPC。telesrv 不实现 Stars/Star Gift 经济:
|
||||
// 大部分曾经的 Stars/gift 动作 RPC 已不再注册;仍注册的几个只读状态 RPC
|
||||
// (getStarsStatus/Subscriptions/Transactions/RevenueStats)返回固定空值,
|
||||
// 因为部分客户端界面会无条件加载它们——错误返回会导致界面卡死或崩溃,
|
||||
// 空值则让界面正常渲染成"没有 Stars"。
|
||||
func (r *Router) registerPayments(d *tlprofile.Dispatcher) {
|
||||
registerRPC[*tg.PaymentsCanPurchaseStoreRequest](d, tlprofile.SemanticMethodPaymentsCanPurchaseStore, func(ctx context.Context, req *tg.PaymentsCanPurchaseStoreRequest) (any, error) {
|
||||
return r.onPaymentsCanPurchaseStore(ctx, req)
|
||||
|
|
@ -22,24 +22,6 @@ func (r *Router) registerPayments(d *tlprofile.Dispatcher) {
|
|||
registerRPC[*tg.PaymentsAssignPlayMarketTransactionRequest](d, tlprofile.SemanticMethodPaymentsAssignPlayMarketTransaction, func(ctx context.Context, req *tg.PaymentsAssignPlayMarketTransactionRequest) (any, error) {
|
||||
return r.onPaymentsAssignPlayMarketTransaction(ctx, req)
|
||||
})
|
||||
registerRPC[*tg.PaymentsGetStarsGiftOptionsRequest](d, tlprofile.SemanticMethodPaymentsGetStarsGiftOptions, func(ctx context.Context, req *tg.PaymentsGetStarsGiftOptionsRequest) (any, error) {
|
||||
return r.onPaymentsGetStarsGiftOptions(ctx, req)
|
||||
})
|
||||
registerRPC[*tg.PaymentsGetStarsGiveawayOptionsRequest](d, tlprofile.SemanticMethodPaymentsGetStarsGiveawayOptions, func(ctx context.Context, _ *tg.PaymentsGetStarsGiveawayOptionsRequest) (any, error) {
|
||||
return r.onPaymentsGetStarsGiveawayOptions(ctx)
|
||||
})
|
||||
registerRPC[*tg.PaymentsGetGiveawayInfoRequest](d, tlprofile.SemanticMethodPaymentsGetGiveawayInfo, func(ctx context.Context, req *tg.PaymentsGetGiveawayInfoRequest) (any, error) {
|
||||
return r.onPaymentsGetGiveawayInfo(ctx, req)
|
||||
})
|
||||
registerRPC[*tg.PaymentsGetStarsTopupOptionsRequest](d, tlprofile.SemanticMethodPaymentsGetStarsTopupOptions, func(ctx context.Context, layerRequest *tg.PaymentsGetStarsTopupOptionsRequest) (any,
|
||||
|
||||
// premium 订阅赠送 telesrv 不实现(无支付流),返回空选项。关键作用:TDesktop 送礼框
|
||||
// ShowStarGiftBox 的 ready() 门控要求 getPremiumGiftCodeOptions 成功返回(on_next)才置
|
||||
// premiumGiftsReady=true,否则整框不弹出——此前返 NOT_IMPLEMENTED 导致点生日礼物无反应。
|
||||
// 空列表即解门,星礼物正常发送;premium 区段另由 userFull.disallow_premium_gifts=true 隐藏。
|
||||
error) {
|
||||
return devStarsTopupOptions(), nil
|
||||
})
|
||||
registerRPC[*tg.PaymentsGetPremiumGiftCodeOptionsRequest](d, tlprofile.SemanticMethodPaymentsGetPremiumGiftCodeOptions, func(ctx context.Context, req *tg.PaymentsGetPremiumGiftCodeOptionsRequest) (any, error) {
|
||||
return []tg.PremiumGiftCodeOption{}, nil
|
||||
})
|
||||
|
|
@ -52,110 +34,6 @@ func (r *Router) registerPayments(d *tlprofile.Dispatcher) {
|
|||
registerRPC[*tg.PaymentsGetStarsTransactionsRequest](d, tlprofile.SemanticMethodPaymentsGetStarsTransactions, func(ctx context.Context, layerRequest *tg.PaymentsGetStarsTransactionsRequest) (any, error) {
|
||||
return r.onPaymentsGetStarsTransactions(ctx, layerRequest)
|
||||
})
|
||||
registerRPC[*tg.PaymentsCheckCanSendGiftRequest](d, tlprofile.SemanticMethodPaymentsCheckCanSendGift, func(ctx context.Context, req *tg.PaymentsCheckCanSendGiftRequest) (any, error) {
|
||||
return r.onPaymentsCheckCanSendGift(ctx, req)
|
||||
})
|
||||
registerRPC[*tg.PaymentsGetStarGiftActiveAuctionsRequest](d, tlprofile.SemanticMethodPaymentsGetStarGiftActiveAuctions, func(ctx context.Context, layerRequest *tg.PaymentsGetStarGiftActiveAuctionsRequest) (any, error) {
|
||||
return r.onPaymentsGetStarGiftActiveAuctions(ctx, layerRequest)
|
||||
})
|
||||
registerRPC[*tg.PaymentsGetStarGiftsRequest](d, tlprofile.SemanticMethodPaymentsGetStarGifts, func(ctx context.Context, layerRequest *tg.PaymentsGetStarGiftsRequest) (any, error) {
|
||||
return r.onPaymentsGetStarGifts(ctx, layerRequest.
|
||||
Hash)
|
||||
})
|
||||
registerRPC[*tg.PaymentsGetStarGiftUpgradePreviewRequest](d, tlprofile.SemanticMethodPaymentsGetStarGiftUpgradePreview, func(ctx context.Context, layerRequest *tg.PaymentsGetStarGiftUpgradePreviewRequest) (any, error) {
|
||||
return r.onPaymentsGetStarGiftUpgradePreview(ctx, layerRequest.
|
||||
GiftID)
|
||||
})
|
||||
registerRPC[*tg.PaymentsGetStarGiftUpgradeAttributesRequest](d, tlprofile.SemanticMethodPaymentsGetStarGiftUpgradeAttributes, func(ctx context.Context, layerRequest *tg.PaymentsGetStarGiftUpgradeAttributesRequest) (any, error) {
|
||||
return r.onPaymentsGetStarGiftUpgradeAttributes(ctx, layerRequest.GiftID)
|
||||
})
|
||||
registerRPC[*tg.PaymentsGetUniqueStarGiftRequest](d, tlprofile.SemanticMethodPaymentsGetUniqueStarGift, func(ctx context.Context, layerRequest *tg.PaymentsGetUniqueStarGiftRequest) (any, error) {
|
||||
return r.onPaymentsGetUniqueStarGift(ctx, layerRequest.
|
||||
Slug)
|
||||
})
|
||||
registerRPC[*tg.PaymentsGetUniqueStarGiftValueInfoRequest](d, tlprofile.SemanticMethodPaymentsGetUniqueStarGiftValueInfo, func(ctx context.Context, req *tg.PaymentsGetUniqueStarGiftValueInfoRequest) (any, error) {
|
||||
return r.onPaymentsGetUniqueStarGiftValueInfo(ctx, req)
|
||||
})
|
||||
registerRPC[*tg.PaymentsGetResaleStarGiftsRequest](d, tlprofile.SemanticMethodPaymentsGetResaleStarGifts, func(ctx context.Context, req *tg.PaymentsGetResaleStarGiftsRequest) (any, error) {
|
||||
return r.onPaymentsGetResaleStarGifts(ctx, req)
|
||||
})
|
||||
registerRPC[*tg.PaymentsGetPaymentFormRequest](d, tlprofile.SemanticMethodPaymentsGetPaymentForm, func(ctx context.Context, layerRequest *tg.PaymentsGetPaymentFormRequest) (any, error) {
|
||||
return r.onPaymentsGetPaymentForm(ctx, layerRequest)
|
||||
})
|
||||
registerRPC[*tg.PaymentsValidateRequestedInfoRequest](d, tlprofile.SemanticMethodPaymentsValidateRequestedInfo, func(ctx context.Context, req *tg.PaymentsValidateRequestedInfoRequest) (any, error) {
|
||||
return r.onPaymentsValidateRequestedInfo(ctx, req)
|
||||
})
|
||||
registerRPC[*tg.PaymentsSendStarsFormRequest](d, tlprofile.SemanticMethodPaymentsSendStarsForm, func(ctx context.Context, layerRequest *tg.PaymentsSendStarsFormRequest) (any, error) {
|
||||
return r.onPaymentsSendStarsForm(ctx, layerRequest)
|
||||
})
|
||||
registerRPC[*tg.PaymentsSendPaymentFormRequest](d, tlprofile.SemanticMethodPaymentsSendPaymentForm, func(ctx context.Context, req *tg.PaymentsSendPaymentFormRequest) (any, error) {
|
||||
return r.onPaymentsSendPaymentForm(ctx, req)
|
||||
})
|
||||
registerRPC[*tg.PaymentsGetSavedStarGiftsRequest](d, tlprofile.SemanticMethodPaymentsGetSavedStarGifts, func(ctx context.Context, layerRequest *tg.PaymentsGetSavedStarGiftsRequest) (any, error) {
|
||||
return r.onPaymentsGetSavedStarGifts(ctx, layerRequest)
|
||||
})
|
||||
registerRPC[*tg.PaymentsGetSavedStarGiftRequest](d, tlprofile.SemanticMethodPaymentsGetSavedStarGift, func(ctx context.Context, layerRequest *tg.PaymentsGetSavedStarGiftRequest) (any, error) {
|
||||
return r.onPaymentsGetSavedStarGift(ctx, layerRequest.
|
||||
Stargift)
|
||||
})
|
||||
registerRPC[*tg.PaymentsSaveStarGiftRequest](d, tlprofile.SemanticMethodPaymentsSaveStarGift, func(ctx context.Context, layerRequest *tg.PaymentsSaveStarGiftRequest) (any, error) {
|
||||
return r.onPaymentsSaveStarGift(ctx, layerRequest)
|
||||
})
|
||||
registerRPC[*tg.PaymentsConvertStarGiftRequest](d, tlprofile.SemanticMethodPaymentsConvertStarGift, func(ctx context.Context, layerRequest *tg.PaymentsConvertStarGiftRequest) (any, error) {
|
||||
return r.onPaymentsConvertStarGift(ctx, layerRequest.
|
||||
Stargift)
|
||||
})
|
||||
registerRPC[*tg.PaymentsUpgradeStarGiftRequest](d, tlprofile.SemanticMethodPaymentsUpgradeStarGift, func(ctx context.Context, layerRequest *tg.PaymentsUpgradeStarGiftRequest) (any, error) {
|
||||
return r.onPaymentsUpgradeStarGift(ctx, layerRequest)
|
||||
})
|
||||
registerRPC[*tg.PaymentsUpdateStarGiftPriceRequest](d, tlprofile.SemanticMethodPaymentsUpdateStarGiftPrice, func(ctx context.Context, req *tg.PaymentsUpdateStarGiftPriceRequest) (any, error) {
|
||||
return r.onPaymentsUpdateStarGiftPrice(ctx, req)
|
||||
})
|
||||
registerRPC[*tg.PaymentsTransferStarGiftRequest](d, tlprofile.SemanticMethodPaymentsTransferStarGift, func(ctx context.Context, req *tg.PaymentsTransferStarGiftRequest) (any, error) {
|
||||
return r.onPaymentsTransferStarGift(ctx, req)
|
||||
})
|
||||
registerRPC[*tg.PaymentsGetStarGiftWithdrawalURLRequest](d, tlprofile.SemanticMethodPaymentsGetStarGiftWithdrawalURL, func(ctx context.Context, req *tg.PaymentsGetStarGiftWithdrawalURLRequest) (any, error) {
|
||||
return r.onPaymentsGetStarGiftWithdrawalURL(ctx, req)
|
||||
})
|
||||
registerRPC[*tg.PaymentsSendStarGiftOfferRequest](d, tlprofile.SemanticMethodPaymentsSendStarGiftOffer, func(ctx context.Context, req *tg.PaymentsSendStarGiftOfferRequest) (any, error) {
|
||||
return r.onPaymentsSendStarGiftOffer(ctx, req)
|
||||
})
|
||||
registerRPC[*tg.PaymentsResolveStarGiftOfferRequest](d, tlprofile.SemanticMethodPaymentsResolveStarGiftOffer, func(ctx context.Context, req *tg.PaymentsResolveStarGiftOfferRequest) (any, error) {
|
||||
return r.onPaymentsResolveStarGiftOffer(ctx, req)
|
||||
})
|
||||
registerRPC[*tg.PaymentsGetCraftStarGiftsRequest](d, tlprofile.SemanticMethodPaymentsGetCraftStarGifts, func(ctx context.Context, req *tg.PaymentsGetCraftStarGiftsRequest) (any, error) {
|
||||
return r.onPaymentsGetCraftStarGifts(ctx, req)
|
||||
})
|
||||
registerRPC[*tg.PaymentsCraftStarGiftRequest](d, tlprofile.SemanticMethodPaymentsCraftStarGift, func(ctx context.Context, req *tg.PaymentsCraftStarGiftRequest) (any, error) {
|
||||
return r.onPaymentsCraftStarGift(ctx, req)
|
||||
})
|
||||
registerRPC[*tg.PaymentsGetStarGiftAuctionStateRequest](d, tlprofile.SemanticMethodPaymentsGetStarGiftAuctionState, func(ctx context.Context, req *tg.PaymentsGetStarGiftAuctionStateRequest) (any, error) {
|
||||
return r.onPaymentsGetStarGiftAuctionState(ctx, req)
|
||||
})
|
||||
registerRPC[*tg.PaymentsGetStarGiftAuctionAcquiredGiftsRequest](d, tlprofile.SemanticMethodPaymentsGetStarGiftAuctionAcquiredGifts, func(ctx context.Context, req *tg.PaymentsGetStarGiftAuctionAcquiredGiftsRequest) (any, error) {
|
||||
return r.onPaymentsGetStarGiftAuctionAcquiredGifts(ctx, req)
|
||||
})
|
||||
registerRPC[*tg.PaymentsToggleChatStarGiftNotificationsRequest](d, tlprofile.SemanticMethodPaymentsToggleChatStarGiftNotifications, func(ctx context.Context, req *tg.PaymentsToggleChatStarGiftNotificationsRequest) (any, error) {
|
||||
return r.onPaymentsToggleChatStarGiftNotifications(ctx, req)
|
||||
})
|
||||
registerRPC[*tg.PaymentsGetStarGiftCollectionsRequest](d, tlprofile.SemanticMethodPaymentsGetStarGiftCollections, func(ctx context.Context, layerRequest *tg.PaymentsGetStarGiftCollectionsRequest) (any, error) {
|
||||
return r.onPaymentsGetStarGiftCollections(ctx, layerRequest)
|
||||
})
|
||||
registerRPC[*tg.PaymentsCreateStarGiftCollectionRequest](d, tlprofile.SemanticMethodPaymentsCreateStarGiftCollection, func(ctx context.Context, layerRequest *tg.PaymentsCreateStarGiftCollectionRequest) (any, error) {
|
||||
return r.onPaymentsCreateStarGiftCollection(ctx, layerRequest)
|
||||
})
|
||||
registerRPC[*tg.PaymentsUpdateStarGiftCollectionRequest](d, tlprofile.SemanticMethodPaymentsUpdateStarGiftCollection, func(ctx context.Context, layerRequest *tg.PaymentsUpdateStarGiftCollectionRequest) (any, error) {
|
||||
return r.onPaymentsUpdateStarGiftCollection(ctx, layerRequest)
|
||||
})
|
||||
registerRPC[*tg.PaymentsDeleteStarGiftCollectionRequest](d, tlprofile.SemanticMethodPaymentsDeleteStarGiftCollection, func(ctx context.Context, layerRequest *tg.PaymentsDeleteStarGiftCollectionRequest) (any, error) {
|
||||
return r.onPaymentsDeleteStarGiftCollection(ctx, layerRequest)
|
||||
})
|
||||
registerRPC[*tg.PaymentsReorderStarGiftCollectionsRequest](d, tlprofile.SemanticMethodPaymentsReorderStarGiftCollections, func(ctx context.Context, layerRequest *tg.PaymentsReorderStarGiftCollectionsRequest) (any, error) {
|
||||
return r.onPaymentsReorderStarGiftCollections(ctx, layerRequest)
|
||||
})
|
||||
registerRPC[*tg.PaymentsToggleStarGiftsPinnedToTopRequest](d, tlprofile.SemanticMethodPaymentsToggleStarGiftsPinnedToTop, func(ctx context.Context, layerRequest *tg.PaymentsToggleStarGiftsPinnedToTopRequest) (any, error) {
|
||||
return r.onPaymentsToggleStarGiftsPinnedToTop(ctx, layerRequest)
|
||||
})
|
||||
registerRPC[*tg.PaymentsGetStarsRevenueAdsAccountURLRequest](d, tlprofile.SemanticMethodPaymentsGetStarsRevenueAdsAccountURL, func(ctx context.Context, layerRequest *tg.PaymentsGetStarsRevenueAdsAccountURLRequest) (any, error) {
|
||||
peer := layerRequest.
|
||||
Peer
|
||||
|
|
@ -194,10 +72,9 @@ func (r *Router) onPaymentsAssignPlayMarketTransaction(ctx context.Context, _ *t
|
|||
return nil, tgerr.New(400, "STORE_PAYMENT_UNAVAILABLE")
|
||||
}
|
||||
|
||||
// onPaymentsGetStarsRevenueStats exposes real channel Star Gift proceeds from
|
||||
// the same peer-scoped ledger as getStarsStatus/getStarsTransactions. Personal
|
||||
// and bot revenue remain the bounded compatibility response because their
|
||||
// revenue bucket is distinct from the general Stars balance and is not modeled.
|
||||
// onPaymentsGetStarsRevenueStats: telesrv has no Stars/gift economy, so every
|
||||
// peer gets the same zero-balance compatibility response (no channel ledger
|
||||
// branch left to read from).
|
||||
func (r *Router) onPaymentsGetStarsRevenueStats(ctx context.Context, req *tg.PaymentsGetStarsRevenueStatsRequest) (*tg.PaymentsStarsRevenueStats, error) {
|
||||
userID, _, err := r.currentUserID(ctx)
|
||||
if err != nil {
|
||||
|
|
@ -206,319 +83,43 @@ func (r *Router) onPaymentsGetStarsRevenueStats(ctx context.Context, req *tg.Pay
|
|||
if req == nil {
|
||||
return nil, peerIDInvalidErr()
|
||||
}
|
||||
owner, err := r.checkedDomainPeerFromInputPeer(ctx, userID, req.Peer)
|
||||
if err != nil {
|
||||
if _, err := r.checkedDomainPeerFromInputPeer(ctx, userID, req.Peer); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ton := req.GetTon()
|
||||
if owner.Type != domain.PeerTypeChannel {
|
||||
return tdesktop.StarsRevenueStats(ton), nil
|
||||
}
|
||||
if err := r.checkStarGiftOwnerPermission(ctx, userID, owner); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ledger, ok := r.deps.Gifts.(channelGiftLedgerReader)
|
||||
if !ok {
|
||||
return tdesktop.StarsRevenueStats(ton), nil
|
||||
}
|
||||
var balance int64
|
||||
if ton {
|
||||
balance, err = ledger.ChannelTonBalance(ctx, owner.ID)
|
||||
} else {
|
||||
balance, err = ledger.ChannelStarsBalance(ctx, owner.ID)
|
||||
}
|
||||
if err != nil {
|
||||
return tdesktop.StarsRevenueStats(req.GetTon()), nil
|
||||
}
|
||||
|
||||
// onPaymentsGetStarsStatus 无 Stars 账本,恒返回零余额(响应仍是合法的
|
||||
// payments.starsStatus——两端客户端无条件读取 balance 字段)。
|
||||
func (r *Router) onPaymentsGetStarsStatus(ctx context.Context, req *tg.PaymentsGetStarsStatusRequest) (*tg.PaymentsStarsStatus, error) {
|
||||
if _, _, err := r.currentUserID(ctx); err != nil {
|
||||
return nil, internalErr()
|
||||
}
|
||||
stats := tdesktop.StarsRevenueStats(ton)
|
||||
var amount tg.StarsAmountClass = &tg.StarsAmount{Amount: balance}
|
||||
if ton {
|
||||
amount = &tg.StarsTonAmount{Amount: balance}
|
||||
if req != nil && req.GetTon() {
|
||||
return emptyStarsStatus(&tg.StarsTonAmount{}), nil
|
||||
}
|
||||
// Channel ledgers currently only receive collectible conversion/marketplace
|
||||
// proceeds and have no withdrawal/debit path, so balance equals lifetime
|
||||
// revenue. Withdrawal stays disabled because no external payout exists.
|
||||
stats.Status.CurrentBalance = amount
|
||||
stats.Status.AvailableBalance = amount
|
||||
stats.Status.OverallRevenue = amount
|
||||
return stats, nil
|
||||
return emptyStarsStatus(&tg.StarsAmount{}), nil
|
||||
}
|
||||
|
||||
type channelGiftLedgerReader interface {
|
||||
ChannelStarsBalance(ctx context.Context, channelID int64) (int64, error)
|
||||
ChannelStarsTransactions(ctx context.Context, channelID int64, query domain.StarsTransactionQuery) (domain.StarsTransactionPage, error)
|
||||
ChannelTonBalance(ctx context.Context, channelID int64) (int64, error)
|
||||
ChannelTonTransactions(ctx context.Context, channelID int64, query domain.StarsTransactionQuery) (domain.TonTransactionPage, error)
|
||||
}
|
||||
|
||||
// onPaymentsGetStarsStatus 返回请求 peer 的 Stars/本地 TON 余额。个人与频道账本
|
||||
// 严格隔离;频道读取要求 Star Gift 管理权限,不能把频道收益投影到执行 RPC 的管理员。
|
||||
// 响应必须是 payments.starsStatus(balance/chats/users 都是必填,空 vector 即可)——
|
||||
// 两端客户端无条件读取 balance(DrKLO StarsAmount 反序列化 / TDesktop vbalance())。
|
||||
func (r *Router) onPaymentsGetStarsStatus(ctx context.Context, req *tg.PaymentsGetStarsStatusRequest) (*tg.PaymentsStarsStatus, error) {
|
||||
userID, owner, err := r.starGiftLedgerOwner(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ton := req != nil && req.GetTon()
|
||||
if owner.Type == domain.PeerTypeChannel {
|
||||
ledger, ok := r.deps.Gifts.(channelGiftLedgerReader)
|
||||
if !ok {
|
||||
if ton {
|
||||
return emptyStarsStatus(&tg.StarsTonAmount{}), nil
|
||||
}
|
||||
return emptyStarsStatus(&tg.StarsAmount{}), nil
|
||||
}
|
||||
var balance int64
|
||||
if ton {
|
||||
balance, err = ledger.ChannelTonBalance(ctx, owner.ID)
|
||||
} else {
|
||||
balance, err = ledger.ChannelStarsBalance(ctx, owner.ID)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, internalErr()
|
||||
}
|
||||
var amount tg.StarsAmountClass = &tg.StarsAmount{Amount: balance}
|
||||
if ton {
|
||||
amount = &tg.StarsTonAmount{Amount: balance}
|
||||
}
|
||||
out := emptyStarsStatus(amount)
|
||||
out.Chats = r.tgChatsForChannelIDs(ctx, userID, []int64{owner.ID})
|
||||
return out, nil
|
||||
}
|
||||
if ton {
|
||||
if r.deps.Gifts == nil {
|
||||
return emptyStarsStatus(&tg.StarsTonAmount{}), nil
|
||||
}
|
||||
balance, err := r.deps.Gifts.TonBalance(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, internalErr()
|
||||
}
|
||||
return emptyStarsStatus(&tg.StarsTonAmount{Amount: balance}), nil
|
||||
}
|
||||
if r.deps.Stars == nil {
|
||||
return emptyStarsStatus(&tg.StarsAmount{}), nil
|
||||
}
|
||||
bal, err := r.deps.Stars.GetBalance(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, starsErr(err)
|
||||
}
|
||||
return emptyStarsStatus(&tg.StarsAmount{Amount: bal.Balance}), nil
|
||||
}
|
||||
|
||||
// onPaymentsGetStarsSubscriptions returns the authoritative current balance
|
||||
// with an empty subscription page. telesrv does not create recurring Stars
|
||||
// subscriptions yet; returning a well-shaped terminal page lets both official
|
||||
// clients finish loading the Stars screen without inventing subscription state.
|
||||
// onPaymentsGetStarsSubscriptions returns a zero balance and no subscriptions
|
||||
// — telesrv never creates recurring Stars subscriptions.
|
||||
func (r *Router) onPaymentsGetStarsSubscriptions(ctx context.Context, req *tg.PaymentsGetStarsSubscriptionsRequest) (*tg.PaymentsStarsStatus, error) {
|
||||
if req == nil || len(req.Offset) > domain.MaxStarsTransactionsOffsetBytes {
|
||||
return nil, inputRequestInvalidErr()
|
||||
if _, _, err := r.currentUserID(ctx); err != nil {
|
||||
return nil, internalErr()
|
||||
}
|
||||
userID, owner, err := r.starGiftLedgerOwnerForPeer(ctx, req.Peer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if owner.Type != domain.PeerTypeUser || owner.ID != userID {
|
||||
return nil, peerIDInvalidErr()
|
||||
}
|
||||
if r.deps.Stars == nil {
|
||||
return emptyStarsStatus(&tg.StarsAmount{}), nil
|
||||
}
|
||||
balance, err := r.deps.Stars.GetBalance(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, starsErr(err)
|
||||
}
|
||||
return emptyStarsStatus(&tg.StarsAmount{Amount: balance.Balance}), nil
|
||||
return emptyStarsStatus(&tg.StarsAmount{}), nil
|
||||
}
|
||||
|
||||
// onPaymentsGetStarsTransactions 返回 keyset 分页的 Stars 流水(同 starsStatus 信封)。
|
||||
// 末页必须省略 next_offset(flag 不置),否则 DrKLO 会无限翻页。
|
||||
// onPaymentsGetStarsTransactions 无 Stars 账本,恒返回零余额、空流水(不设置
|
||||
// next_offset,避免客户端无限翻页)。
|
||||
func (r *Router) onPaymentsGetStarsTransactions(ctx context.Context, req *tg.PaymentsGetStarsTransactionsRequest) (*tg.PaymentsStarsStatus, error) {
|
||||
userID, owner, err := r.starGiftTransactionLedgerOwner(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if _, _, err := r.currentUserID(ctx); err != nil {
|
||||
return nil, internalErr()
|
||||
}
|
||||
query, err := starsTransactionQuery(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if req != nil && req.GetTon() {
|
||||
return emptyStarsStatus(&tg.StarsTonAmount{}), nil
|
||||
}
|
||||
ton := req != nil && req.GetTon()
|
||||
if owner.Type == domain.PeerTypeChannel {
|
||||
ledger, ok := r.deps.Gifts.(channelGiftLedgerReader)
|
||||
if !ok {
|
||||
if ton {
|
||||
return emptyStarsStatus(&tg.StarsTonAmount{}), nil
|
||||
}
|
||||
return emptyStarsStatus(&tg.StarsAmount{}), nil
|
||||
}
|
||||
if ton {
|
||||
page, err := ledger.ChannelTonTransactions(ctx, owner.ID, query)
|
||||
if err != nil {
|
||||
return nil, internalErr()
|
||||
}
|
||||
out := emptyStarsStatus(&tg.StarsTonAmount{Amount: page.Balance})
|
||||
if txns := tgTonTransactions(page.Transactions); len(txns) > 0 {
|
||||
out.SetHistory(txns)
|
||||
}
|
||||
if page.NextOffset != "" {
|
||||
out.SetNextOffset(page.NextOffset)
|
||||
}
|
||||
r.enrichChannelTonLedgerStatus(ctx, userID, owner.ID, page.Transactions, out)
|
||||
return out, nil
|
||||
}
|
||||
page, err := ledger.ChannelStarsTransactions(ctx, owner.ID, query)
|
||||
if err != nil {
|
||||
return nil, internalErr()
|
||||
}
|
||||
out := emptyStarsStatus(&tg.StarsAmount{Amount: page.Balance})
|
||||
if txns := tgStarsTransactions(page.Transactions); len(txns) > 0 {
|
||||
out.SetHistory(txns)
|
||||
}
|
||||
if page.NextOffset != "" {
|
||||
out.SetNextOffset(page.NextOffset)
|
||||
}
|
||||
r.enrichChannelStarsLedgerStatus(ctx, userID, owner.ID, page.Transactions, out)
|
||||
return out, nil
|
||||
}
|
||||
if ton {
|
||||
if r.deps.Gifts == nil {
|
||||
return emptyStarsStatus(&tg.StarsTonAmount{}), nil
|
||||
}
|
||||
page, err := r.deps.Gifts.TonTransactions(ctx, userID, query)
|
||||
if err != nil {
|
||||
return nil, internalErr()
|
||||
}
|
||||
out := emptyStarsStatus(&tg.StarsTonAmount{Amount: page.Balance})
|
||||
if txns := tgTonTransactions(page.Transactions); len(txns) > 0 {
|
||||
out.SetHistory(txns)
|
||||
}
|
||||
if page.NextOffset != "" {
|
||||
out.SetNextOffset(page.NextOffset)
|
||||
}
|
||||
ids := make([]int64, 0)
|
||||
for _, txn := range page.Transactions {
|
||||
if txn.Peer.Type == domain.PeerTypeUser {
|
||||
ids = append(ids, txn.Peer.ID)
|
||||
}
|
||||
}
|
||||
out.Users = tgUsersForViewer(userID, r.domainUsersForIDs(ctx, userID, uniqueInt64(ids)))
|
||||
return out, nil
|
||||
}
|
||||
if r.deps.Stars == nil {
|
||||
return emptyStarsStatus(&tg.StarsAmount{}), nil
|
||||
}
|
||||
page, err := r.deps.Stars.ListTransactions(ctx, userID, query)
|
||||
if err != nil {
|
||||
return nil, starsErr(err)
|
||||
}
|
||||
out := emptyStarsStatus(&tg.StarsAmount{Amount: page.Balance})
|
||||
if txns := tgStarsTransactions(page.Transactions); len(txns) > 0 {
|
||||
out.SetHistory(txns)
|
||||
}
|
||||
if page.NextOffset != "" {
|
||||
out.SetNextOffset(page.NextOffset)
|
||||
}
|
||||
// 富化流水中提到的用户对手方(频道对手方进 Chats 留待 paid reaction 阶段)。
|
||||
if ids := starsTransactionUserIDs(page.Transactions); len(ids) > 0 {
|
||||
out.Users = tgUsersForViewer(userID, r.domainUsersForIDs(ctx, userID, ids))
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func starsTransactionQuery(req *tg.PaymentsGetStarsTransactionsRequest) (domain.StarsTransactionQuery, error) {
|
||||
if req == nil {
|
||||
return domain.StarsTransactionQuery{}, inputRequestInvalidErr()
|
||||
}
|
||||
inbound, outbound := req.GetInbound(), req.GetOutbound()
|
||||
if inbound && outbound {
|
||||
return domain.StarsTransactionQuery{}, inputRequestInvalidErr()
|
||||
}
|
||||
if _, ok := req.GetSubscriptionID(); ok {
|
||||
// Stars subscriptions are not part of the current business model. Do not
|
||||
// silently return the unfiltered ledger for a requested subscription.
|
||||
return domain.StarsTransactionQuery{}, subscriptionIDInvalidErr()
|
||||
}
|
||||
direction := domain.StarsTransactionDirectionAll
|
||||
if inbound {
|
||||
direction = domain.StarsTransactionDirectionIncoming
|
||||
} else if outbound {
|
||||
direction = domain.StarsTransactionDirectionOutgoing
|
||||
}
|
||||
limit := req.Limit
|
||||
if limit <= 0 || limit > domain.MaxStarsTransactionsLimit {
|
||||
limit = domain.MaxStarsTransactionsLimit
|
||||
}
|
||||
return domain.StarsTransactionQuery{
|
||||
Offset: req.Offset,
|
||||
Limit: limit,
|
||||
Direction: direction,
|
||||
Ascending: req.GetAscending(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Router) starGiftLedgerOwner(ctx context.Context, req *tg.PaymentsGetStarsStatusRequest) (int64, domain.Peer, error) {
|
||||
if req == nil {
|
||||
return 0, domain.Peer{}, peerIDInvalidErr()
|
||||
}
|
||||
return r.starGiftLedgerOwnerForPeer(ctx, req.Peer)
|
||||
}
|
||||
|
||||
func (r *Router) starGiftTransactionLedgerOwner(ctx context.Context, req *tg.PaymentsGetStarsTransactionsRequest) (int64, domain.Peer, error) {
|
||||
if req == nil {
|
||||
return 0, domain.Peer{}, peerIDInvalidErr()
|
||||
}
|
||||
return r.starGiftLedgerOwnerForPeer(ctx, req.Peer)
|
||||
}
|
||||
|
||||
func (r *Router) starGiftLedgerOwnerForPeer(ctx context.Context, input tg.InputPeerClass) (int64, domain.Peer, error) {
|
||||
userID, _, err := r.currentUserID(ctx)
|
||||
if err != nil {
|
||||
return 0, domain.Peer{}, internalErr()
|
||||
}
|
||||
owner, err := r.checkedDomainPeerFromInputPeer(ctx, userID, input)
|
||||
if err != nil {
|
||||
return 0, domain.Peer{}, err
|
||||
}
|
||||
if owner.Type == domain.PeerTypeUser {
|
||||
if owner.ID != userID {
|
||||
return 0, domain.Peer{}, peerIDInvalidErr()
|
||||
}
|
||||
return userID, owner, nil
|
||||
}
|
||||
if err := r.checkStarGiftOwnerPermission(ctx, userID, owner); err != nil {
|
||||
return 0, domain.Peer{}, err
|
||||
}
|
||||
return userID, owner, nil
|
||||
}
|
||||
|
||||
func (r *Router) enrichChannelStarsLedgerStatus(ctx context.Context, viewerID, ownerChannelID int64, txns []domain.StarsTransaction, out *tg.PaymentsStarsStatus) {
|
||||
userIDs := make([]int64, 0, len(txns))
|
||||
channelIDs := []int64{ownerChannelID}
|
||||
for _, txn := range txns {
|
||||
switch txn.Peer.Type {
|
||||
case domain.PeerTypeUser:
|
||||
userIDs = append(userIDs, txn.Peer.ID)
|
||||
case domain.PeerTypeChannel:
|
||||
channelIDs = append(channelIDs, txn.Peer.ID)
|
||||
}
|
||||
}
|
||||
out.Users = tgUsersForViewer(viewerID, r.domainUsersForIDs(ctx, viewerID, uniqueInt64(userIDs)))
|
||||
out.Chats = r.tgChatsForChannelIDs(ctx, viewerID, uniqueInt64(channelIDs))
|
||||
}
|
||||
|
||||
func (r *Router) enrichChannelTonLedgerStatus(ctx context.Context, viewerID, ownerChannelID int64, txns []domain.TonTransaction, out *tg.PaymentsStarsStatus) {
|
||||
userIDs := make([]int64, 0, len(txns))
|
||||
channelIDs := []int64{ownerChannelID}
|
||||
for _, txn := range txns {
|
||||
switch txn.Peer.Type {
|
||||
case domain.PeerTypeUser:
|
||||
userIDs = append(userIDs, txn.Peer.ID)
|
||||
case domain.PeerTypeChannel:
|
||||
channelIDs = append(channelIDs, txn.Peer.ID)
|
||||
}
|
||||
}
|
||||
out.Users = tgUsersForViewer(viewerID, r.domainUsersForIDs(ctx, viewerID, uniqueInt64(userIDs)))
|
||||
out.Chats = r.tgChatsForChannelIDs(ctx, viewerID, uniqueInt64(channelIDs))
|
||||
return emptyStarsStatus(&tg.StarsAmount{}), nil
|
||||
}
|
||||
|
||||
// emptyStarsStatus 构造一个合法的最小 payments.starsStatus(chats/users 非空 vector 但可空)。
|
||||
|
|
@ -529,119 +130,3 @@ func emptyStarsStatus(balance tg.StarsAmountClass) *tg.PaymentsStarsStatus {
|
|||
Users: []tg.UserClass{},
|
||||
}
|
||||
}
|
||||
|
||||
// tgStarsTransactions 把账本流水投影为 tg.StarsTransaction(amount 带符号:借记为负)。
|
||||
func tgStarsTransactions(in []domain.StarsTransaction) []tg.StarsTransaction {
|
||||
out := make([]tg.StarsTransaction, 0, len(in))
|
||||
for _, t := range in {
|
||||
item := tg.StarsTransaction{
|
||||
ID: strconv.FormatInt(t.ID, 10),
|
||||
Amount: &tg.StarsAmount{Amount: t.Amount},
|
||||
Date: t.Date,
|
||||
Peer: tgStarsTransactionPeer(t),
|
||||
}
|
||||
if t.Title != "" {
|
||||
item.SetTitle(t.Title)
|
||||
}
|
||||
if t.Description != "" {
|
||||
item.SetDescription(t.Description)
|
||||
}
|
||||
switch t.Reason {
|
||||
case domain.StarsReasonReaction:
|
||||
item.Reaction = true
|
||||
case domain.StarsReasonPaidMessage:
|
||||
item.SetPaidMessages(1)
|
||||
case domain.StarsReasonGift:
|
||||
item.Gift = true
|
||||
case domain.StarsReasonGiftUpgrade:
|
||||
item.StargiftUpgrade = true
|
||||
case domain.StarsReasonGiftResale:
|
||||
item.StargiftResale = true
|
||||
case domain.StarsReasonGiftPrepaid:
|
||||
item.StargiftPrepaidUpgrade = true
|
||||
case domain.StarsReasonGiftDrop:
|
||||
item.StargiftDropOriginalDetails = true
|
||||
case domain.StarsReasonGiftAuction:
|
||||
item.StargiftAuctionBid = true
|
||||
case domain.StarsReasonGiftOffer:
|
||||
item.Offer = true
|
||||
}
|
||||
out = append(out, item)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func tgTonTransactions(in []domain.TonTransaction) []tg.StarsTransaction {
|
||||
out := make([]tg.StarsTransaction, 0, len(in))
|
||||
for _, t := range in {
|
||||
item := tg.StarsTransaction{ID: strconv.FormatInt(t.ID, 10), Amount: &tg.StarsTonAmount{Amount: t.Amount},
|
||||
Date: t.Date, Peer: tgStarsTransactionPeer(domain.StarsTransaction{Peer: t.Peer, Reason: t.Reason})}
|
||||
if t.Amount > 0 {
|
||||
item.Refund = true
|
||||
}
|
||||
if t.Title != "" {
|
||||
item.SetTitle(t.Title)
|
||||
}
|
||||
if t.Description != "" {
|
||||
item.SetDescription(t.Description)
|
||||
}
|
||||
switch t.Reason {
|
||||
case domain.StarsReasonGiftResale:
|
||||
item.StargiftResale = true
|
||||
case domain.StarsReasonGiftOffer:
|
||||
item.Offer = true
|
||||
case domain.StarsReasonGiftAuction:
|
||||
item.StargiftAuctionBid = true
|
||||
case domain.StarsReasonGiftPrepaid:
|
||||
item.StargiftPrepaidUpgrade = true
|
||||
case domain.StarsReasonGiftDrop:
|
||||
item.StargiftDropOriginalDetails = true
|
||||
}
|
||||
out = append(out, item)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// tgStarsTransactionPeer 选择对手方构造器:grant/topup 走 Fragment(站外充值轨),
|
||||
// 真实 peer 走 starsTransactionPeer,其余兜底 Unsupported(Peer 字段必填,不可为 nil)。
|
||||
func tgStarsTransactionPeer(t domain.StarsTransaction) tg.StarsTransactionPeerClass {
|
||||
switch t.Reason {
|
||||
case domain.StarsReasonGrant, domain.StarsReasonTopup:
|
||||
return &tg.StarsTransactionPeerFragment{}
|
||||
}
|
||||
if t.Peer.Type != "" && t.Peer.ID != 0 {
|
||||
if p := tgPeer(t.Peer); p != nil {
|
||||
return &tg.StarsTransactionPeer{Peer: p}
|
||||
}
|
||||
}
|
||||
return &tg.StarsTransactionPeerUnsupported{}
|
||||
}
|
||||
|
||||
// starsTransactionUserIDs 收集流水中去重的用户类对手方 id。
|
||||
func starsTransactionUserIDs(in []domain.StarsTransaction) []int64 {
|
||||
seen := make(map[int64]struct{}, len(in))
|
||||
ids := make([]int64, 0, len(in))
|
||||
for _, t := range in {
|
||||
if t.Peer.Type != domain.PeerTypeUser || t.Peer.ID == 0 {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[t.Peer.ID]; ok {
|
||||
continue
|
||||
}
|
||||
seen[t.Peer.ID] = struct{}{}
|
||||
ids = append(ids, t.Peer.ID)
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
// starsErr 把 Stars 账本领域错误映射为客户端可识别的 tgerr(仿 premiumBoostErr)。
|
||||
func starsErr(err error) error {
|
||||
switch {
|
||||
case errors.Is(err, domain.ErrStarsInsufficient):
|
||||
return balanceTooLowErr()
|
||||
case errors.Is(err, domain.ErrStarsInvalidAmount):
|
||||
return starsAmountInvalidErr()
|
||||
default:
|
||||
return internalErr()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue