fix: sync StarGift lifecycle hardening
This commit is contained in:
parent
bfdfe825f6
commit
d69a34a4a8
30 changed files with 1883 additions and 132 deletions
|
|
@ -389,6 +389,7 @@ func (s *StarGiftLifecycleStore) TransferStarGift(ctx context.Context, req domai
|
|||
}},
|
||||
}
|
||||
var result domain.StarGiftTransferResult
|
||||
var sourceSaved domain.SavedStarGift
|
||||
hooks := privateSendTxHooks{
|
||||
before: func(ctx context.Context, tx pgx.Tx, send *domain.SendPrivateTextRequest) error {
|
||||
saved, unique, err := lockTransferableStarGift(ctx, tx, req.ActorUserID, req.Ref, req.Date)
|
||||
|
|
@ -414,6 +415,7 @@ func (s *StarGiftLifecycleStore) TransferStarGift(ctx context.Context, req domai
|
|||
if err := removeSavedGiftFromCollections(ctx, tx, saved.Owner, saved.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
sourceSaved = saved
|
||||
unique.Owner = req.To
|
||||
saved.Owner = req.To
|
||||
result.Saved, result.Unique, result.Balance = saved, unique, balance
|
||||
|
|
@ -433,6 +435,9 @@ func (s *StarGiftLifecycleStore) TransferStarGift(ctx context.Context, req domai
|
|||
can_transfer_at=0 WHERE id=$1`, result.Saved.ID, req.To.ID, req.ActorUserID, msgID, req.Date); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := registerUserStarGiftMessageRef(ctx, tx, req.To.ID, msgID, result.Saved.ID, result.Unique.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := tx.Exec(ctx, `INSERT INTO star_gift_transfer_commands(actor_user_id,command_key,unique_gift_id,
|
||||
from_peer_type,from_peer_id,to_peer_type,to_peer_id,charge_stars,balance_after,created_at)
|
||||
VALUES($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)`, req.ActorUserID, strings.TrimSpace(req.CommandKey), result.Unique.ID,
|
||||
|
|
@ -441,6 +446,10 @@ func (s *StarGiftLifecycleStore) TransferStarGift(ctx context.Context, req domai
|
|||
}
|
||||
result.Saved.MsgID, result.Saved.SavedID, result.Saved.UpgradeMsgID, result.Saved.Date = msgID, 0, msgID, req.Date
|
||||
result.Saved.FromUserID = req.ActorUserID
|
||||
if sourceSaved.Owner.Type == domain.PeerTypeUser {
|
||||
_, err := s.retireUserStarGiftMessagesTx(ctx, tx, sourceSaved, result.Unique, req.Date)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
|
@ -502,6 +511,7 @@ func (s *StarGiftLifecycleStore) PurchaseResaleStarGift(ctx context.Context, req
|
|||
}
|
||||
var result domain.StarGiftTransferResult
|
||||
var commissionAmount int64
|
||||
var sourceSaved domain.SavedStarGift
|
||||
hooks := privateSendTxHooks{
|
||||
before: func(ctx context.Context, tx pgx.Tx, send *domain.SendPrivateTextRequest) error {
|
||||
var listingCurrency, sellerType string
|
||||
|
|
@ -557,12 +567,15 @@ func (s *StarGiftLifecycleStore) PurchaseResaleStarGift(ctx context.Context, req
|
|||
gift.ResellAmount = nil
|
||||
gift.LastSaleDate = req.Date
|
||||
gift.LastSaleAmount = &domain.StarGiftAmount{Currency: req.Amount.Currency, Amount: req.Amount.Amount}
|
||||
sourceSaved = saved
|
||||
saved.Owner = req.To
|
||||
if req.To.Type == domain.PeerTypeChannel {
|
||||
saved.MsgID, saved.SavedID = 0, saved.ID
|
||||
}
|
||||
result.Saved, result.Unique, result.Balance = saved, gift, balance
|
||||
send.Media.ServiceAction.StarGiftUnique = transferUniqueAction(gift, messageSenderID, req.To, saved)
|
||||
resaleAmount := req.Amount
|
||||
send.Media.ServiceAction.StarGiftUnique.ResaleAmount = &resaleAmount
|
||||
return nil
|
||||
},
|
||||
after: func(ctx context.Context, tx pgx.Tx, sent domain.SendPrivateTextResult) error {
|
||||
|
|
@ -578,6 +591,11 @@ func (s *StarGiftLifecycleStore) PurchaseResaleStarGift(ctx context.Context, req
|
|||
WHERE id=$1`, result.Saved.ID, string(req.To.Type), req.To.ID, messageSenderID, msgID, savedID, req.Date); err != nil {
|
||||
return err
|
||||
}
|
||||
if req.To.Type == domain.PeerTypeUser {
|
||||
if err := registerUserStarGiftMessageRef(ctx, tx, req.To.ID, msgID, result.Saved.ID, result.Unique.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err := tx.Exec(ctx, `INSERT INTO star_gift_sales(unique_gift_id,seller_peer_type,seller_peer_id,
|
||||
buyer_peer_type,buyer_peer_id,currency,amount,commission_amount,sold_at,command_key)
|
||||
VALUES($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)`, result.Unique.ID, string(seller.Type), seller.ID,
|
||||
|
|
@ -601,6 +619,11 @@ func (s *StarGiftLifecycleStore) PurchaseResaleStarGift(ctx context.Context, req
|
|||
}
|
||||
result.Saved.MsgID, result.Saved.SavedID, result.Saved.UpgradeMsgID, result.Saved.Date = msgID, savedID, msgID, req.Date
|
||||
result.Saved.FromUserID = messageSenderID
|
||||
if sourceSaved.Owner.Type == domain.PeerTypeUser {
|
||||
if _, err := s.retireUserStarGiftMessagesTx(ctx, tx, sourceSaved, result.Unique, req.Date); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return updateStarGiftResaleProjection(ctx, tx, result.Unique.GiftID)
|
||||
},
|
||||
}
|
||||
|
|
@ -803,7 +826,7 @@ func (s *StarGiftLifecycleStore) ResolveStarGiftOffer(ctx context.Context, req d
|
|||
offer.Gift = gift
|
||||
actionKind := domain.MessageServiceActionStarGiftUnique
|
||||
action := &domain.MessageServiceAction{Kind: actionKind, StarGiftUnique: &domain.MessageStarGiftUniqueAction{
|
||||
Gift: gift, FromUserID: req.OwnerUserID, Peer: domain.Peer{Type: domain.PeerTypeUser, ID: offer.BuyerUserID},
|
||||
Gift: gift, FromUserID: req.OwnerUserID,
|
||||
Transferred: true, FromOffer: true, Saved: true,
|
||||
}}
|
||||
if req.Decline {
|
||||
|
|
@ -816,6 +839,7 @@ func (s *StarGiftLifecycleStore) ResolveStarGiftOffer(ctx context.Context, req d
|
|||
Media: &domain.MessageMedia{Kind: domain.MessageMediaKindService, ServiceAction: action}}
|
||||
var result domain.StarGiftOfferResult
|
||||
var commissionAmount int64
|
||||
var sourceSaved domain.SavedStarGift
|
||||
hooks := privateSendTxHooks{
|
||||
before: func(ctx context.Context, tx pgx.Tx, send *domain.SendPrivateTextRequest) error {
|
||||
locked, err := scanStarGiftOffer(tx.QueryRow(ctx, `SELECT id,buyer_user_id,owner_peer_type,owner_peer_id,unique_gift_id,
|
||||
|
|
@ -874,10 +898,15 @@ func (s *StarGiftLifecycleStore) ResolveStarGiftOffer(ctx context.Context, req d
|
|||
current.LastSaleDate = req.Date
|
||||
current.LastSaleAmount = &locked.Price
|
||||
locked.Status, locked.ResolvedAt, locked.Gift = "accepted", req.Date, current
|
||||
sourceSaved = saved
|
||||
saved.Owner = domain.Peer{Type: domain.PeerTypeUser, ID: locked.BuyerUserID}
|
||||
result.Offer = locked
|
||||
result.Unique = current
|
||||
result.Saved = saved
|
||||
send.Media.ServiceAction.StarGiftUnique.Gift = current
|
||||
send.Media.ServiceAction.StarGiftUnique = transferUniqueAction(current, req.OwnerUserID, saved.Owner, saved)
|
||||
send.Media.ServiceAction.StarGiftUnique.FromOffer = true
|
||||
resaleAmount := locked.Price
|
||||
send.Media.ServiceAction.StarGiftUnique.ResaleAmount = &resaleAmount
|
||||
return nil
|
||||
},
|
||||
after: func(ctx context.Context, tx pgx.Tx, sent domain.SendPrivateTextResult) error {
|
||||
|
|
@ -890,6 +919,10 @@ func (s *StarGiftLifecycleStore) ResolveStarGiftOffer(ctx context.Context, req d
|
|||
WHERE id=$1`, result.Saved.ID, result.Offer.BuyerUserID, req.OwnerUserID, msgID, req.Date); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := registerUserStarGiftMessageRef(ctx, tx, result.Offer.BuyerUserID, msgID,
|
||||
result.Saved.ID, result.Unique.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
result.Saved.Owner = domain.Peer{Type: domain.PeerTypeUser, ID: result.Offer.BuyerUserID}
|
||||
result.Saved.FromUserID, result.Saved.MsgID, result.Saved.SavedID, result.Saved.UpgradeMsgID, result.Saved.Date = req.OwnerUserID, msgID, 0, msgID, req.Date
|
||||
if _, err := tx.Exec(ctx, `INSERT INTO star_gift_sales(unique_gift_id,seller_peer_type,seller_peer_id,
|
||||
|
|
@ -899,6 +932,9 @@ func (s *StarGiftLifecycleStore) ResolveStarGiftOffer(ctx context.Context, req d
|
|||
req.Date, fmt.Sprintf("offer:%d", result.Offer.ID)); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := s.retireUserStarGiftMessagesTx(ctx, tx, sourceSaved, result.Unique, req.Date); err != nil {
|
||||
return err
|
||||
}
|
||||
return updateStarGiftResaleProjection(ctx, tx, result.Offer.Gift.GiftID)
|
||||
},
|
||||
}
|
||||
|
|
@ -1077,6 +1113,7 @@ func (s *StarGiftLifecycleStore) transferStarGiftWithoutPrivateMessage(ctx conte
|
|||
if err := removeSavedGiftFromCollections(ctx, tx, saved.Owner, saved.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
sourceSaved := saved
|
||||
if _, err := tx.Exec(ctx, `UPDATE unique_star_gifts SET owner_peer_type='channel',owner_peer_id=$2,updated_at=now() WHERE id=$1`, unique.ID, req.To.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -1100,6 +1137,11 @@ func (s *StarGiftLifecycleStore) transferStarGiftWithoutPrivateMessage(ctx conte
|
|||
return err
|
||||
}
|
||||
result.Saved, result.Unique, result.Balance = saved, unique, balance
|
||||
if sourceSaved.Owner.Type == domain.PeerTypeUser {
|
||||
if _, err := s.retireUserStarGiftMessagesTx(ctx, tx, sourceSaved, unique, req.Date); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return result, err
|
||||
|
|
@ -1148,17 +1190,23 @@ func ensureNoStarGiftMarketConflict(ctx context.Context, tx pgx.Tx, uniqueID int
|
|||
}
|
||||
|
||||
func transferUniqueAction(unique domain.UniqueStarGift, fromUserID int64, to domain.Peer, saved domain.SavedStarGift) *domain.MessageStarGiftUniqueAction {
|
||||
peer := to
|
||||
savedID := saved.SavedID
|
||||
canCraftAt := saved.CanCraftAt
|
||||
if to.Type == domain.PeerTypeUser {
|
||||
// For a user-owned transferred gift the action message itself becomes
|
||||
// inputSavedStarGiftUser.msg_id. A channel saved_id belongs to a different
|
||||
// identity namespace and must never leak into the recipient's user view.
|
||||
// peer and saved_id are a shared channel-only TL flag. A user-owned
|
||||
// transferred gift is managed by this action message's id.
|
||||
peer = domain.Peer{}
|
||||
savedID = 0
|
||||
} else {
|
||||
// Preserve the durable entitlement for a future transfer back to a user,
|
||||
// but keep channel Craft hidden until its write/update path exists.
|
||||
canCraftAt = 0
|
||||
}
|
||||
return &domain.MessageStarGiftUniqueAction{Gift: unique, FromUserID: fromUserID, Peer: to,
|
||||
return &domain.MessageStarGiftUniqueAction{Gift: unique, FromUserID: fromUserID, Peer: peer,
|
||||
SavedID: savedID, Transferred: true, Saved: true, CanExportAt: saved.CanExportAt,
|
||||
TransferStars: saved.TransferStars, CanTransferAt: saved.CanTransferAt, CanResellAt: saved.CanResellAt,
|
||||
DropOriginalDetailsStars: saved.DropOriginalDetailsStars, CanCraftAt: saved.CanCraftAt}
|
||||
DropOriginalDetailsStars: saved.DropOriginalDetailsStars, CanCraftAt: canCraftAt}
|
||||
}
|
||||
|
||||
func (s *StarGiftLifecycleStore) debitLifecycleAmount(ctx context.Context, tx pgx.Tx, userID int64, amount domain.StarGiftAmount,
|
||||
|
|
@ -1468,15 +1516,22 @@ WHERE provider_request_id=$1 FOR UPDATE`, providerRequestID).Scan(&uniqueID, &ow
|
|||
requestHash := sha256.Sum256([]byte(providerRequestID))
|
||||
giftAddress := fmt.Sprintf("telesrv-gift:%s:%x", unique.Slug, requestHash[:8])
|
||||
if _, err := tx.Exec(ctx, `UPDATE unique_star_gifts SET owner_peer_type=NULL,owner_peer_id=NULL,
|
||||
owner_address=$2,gift_address=$3,updated_at=now() WHERE id=$1`, uniqueID, ownerAddress, giftAddress); err != nil {
|
||||
owner_address=$2,gift_address=$3,craft_chance_permille=0,updated_at=now() WHERE id=$1`, uniqueID, ownerAddress, giftAddress); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := tx.Exec(ctx, `UPDATE peer_star_gifts SET lifecycle_status='exported',unsaved=true,pinned_order=0 WHERE id=$1`, saved.ID); err != nil {
|
||||
if _, err := tx.Exec(ctx, `UPDATE peer_star_gifts SET lifecycle_status='exported',unsaved=true,pinned_order=0,can_craft_at=0 WHERE id=$1`, saved.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := tx.Exec(ctx, `UPDATE star_gift_withdrawal_requests SET status='completed',completed_at=$2 WHERE provider_request_id=$1`, providerRequestID, date); err != nil {
|
||||
return err
|
||||
}
|
||||
unique.Owner = domain.Peer{}
|
||||
unique.OwnerAddress = ownerAddress
|
||||
unique.GiftAddress = giftAddress
|
||||
unique.CraftChancePermille = 0
|
||||
if _, err := s.retireUserStarGiftMessagesTx(ctx, tx, saved, unique, date); err != nil {
|
||||
return err
|
||||
}
|
||||
return updateStarGiftResaleProjection(ctx, tx, unique.GiftID)
|
||||
})
|
||||
if err != nil {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue