Sent Tip vs Sent Cash is chosen by who the peer is, not by what the payment
was. A payment into a tip DM reports Sent Tip whether it came from a tip card
or from the money button inside the thread. Both platforms do this, and both do
it deliberately.
Android — ChatViewModel.kt:711-716:
// A payment into a tip DM is a tip; a contact DM is a plain cash send.
val transferEvent = if (stateFlow.value.participant is ChatParticipant.TipUser) {
Analytics.Transfer.SentTip(TipOrigin.CHAT)
} else {
Analytics.Transfer.SentCash
}
iOS — SendAmountViewModel.swift:198-203, same rule, with the intent stated outright:
// A payment into a tip DM reports as a tip; a contact DM is a plain
// cash send. This covers both the scanned-tipcard flow and the
// Send Cash action inside a tip thread, since both submit here —
// `Origin` is what tells the two apart.
let tipOrigin: TipOrigin? = if case .tip(let recipient) = target { recipient.origin } else { nil }
let transferEvent: Analytics.TransferEvent = tipOrigin == nil ? .sentCash : .sentTip
No data is lost: the event carries Origin: chat vs Origin: tipcard, so the
two are separable after the fact. The cost is that any count or funnel on Sent Tip that doesn't split on Origin reads in-chat sends as tips.
What's changed is that the product now draws the line the other way everywhere
else. The backend picks the activity verb from
ChatMetadata.TipDmPayment.Location, so a CHAT payment is a send and a
TIPCARD payment is a tip, and both clients now render history that way
(#1332, code-payments/code-ios-app#657). The
transfer events are the remaining place where the two are merged.
Three ways out, in increasing cost:
- Leave the events alone and split
Sent Tip on Origin in the dashboards.
- Emit
Sent Cash when Origin is chat, matching the history label. This
breaks continuity of the existing Sent Tip series.
- Keep both event names as they are but add
Origin to Sent Cash too, so one
property covers all four cases.
This wants a decision from whoever owns the Mixpanel dashboards before either
client moves, and whichever way it goes it needs doing on both platforms
together. Filing here for Android; happy to open the iOS twin if it should be
tracked separately.
Sent TipvsSent Cashis chosen by who the peer is, not by what the paymentwas. A payment into a tip DM reports
Sent Tipwhether it came from a tip cardor from the money button inside the thread. Both platforms do this, and both do
it deliberately.
Android —
ChatViewModel.kt:711-716:iOS —
SendAmountViewModel.swift:198-203, same rule, with the intent stated outright:No data is lost: the event carries
Origin: chatvsOrigin: tipcard, so thetwo are separable after the fact. The cost is that any count or funnel on
Sent Tipthat doesn't split onOriginreads in-chat sends as tips.What's changed is that the product now draws the line the other way everywhere
else. The backend picks the activity verb from
ChatMetadata.TipDmPayment.Location, so aCHATpayment is a send and aTIPCARDpayment is a tip, and both clients now render history that way(#1332, code-payments/code-ios-app#657). The
transfer events are the remaining place where the two are merged.
Three ways out, in increasing cost:
Sent TiponOriginin the dashboards.Sent CashwhenOriginischat, matching the history label. Thisbreaks continuity of the existing
Sent Tipseries.OrigintoSent Cashtoo, so oneproperty covers all four cases.
This wants a decision from whoever owns the Mixpanel dashboards before either
client moves, and whichever way it goes it needs doing on both platforms
together. Filing here for Android; happy to open the iOS twin if it should be
tracked separately.