Twin: synonymdev/bitkit-ios#849
Problem or use case
When a contact pays a creator's Shop order or unlocks one of their Locks posts, the creator's Bitkit records the on-chain receipt without the payer:
- the activity row reads "Received", and its detail offers "Assign";
- the receipt is missing from the payer's contact activity page.
A payment between the same two contacts made in Bitkit reads "Received from " and is listed on the contact page. In a regtest run on 25 Sep, the creator's contact page for the payer listed four Lightning payments and missed the Shop and Locks receipts ($120.14 and $5.47). The payer's contact page for the creator listed all six payments.
Proposed solution
- When an on-chain payment arrives at a server-derived address, Bitkit sets the contact who paid it, next to the private-address lookup in
handleOnchainTransactionReceived. The row then reads "Received from ", and the contact page lists the receipt.
- This needs a Paykit Server change first, because the creator's wallet has no way to learn who a server-derived address was handed to. No Paykit Server issue exists yet (I checked the issues of pubky/paykit-server and BitcoinErrorLog/paykit-server). The change: the server already derives each address for one reader (
invoices.reader_lookup_hash sits next to bitcoin_address_lookup_hash, paykit-server/migrations/0001_initial.sql:41-49 on 722ef26), so it can also hand the wallet the pair (address, reader pubky) when it derives the address, for example as a record the server writes for the wallet. Bitkit then looks the paying address up in those pairs.
Alternatives considered
- The payer's Bitkit tells the creator's wallet, over their existing wallet link, which transaction paid a server request. This needs no server change, but it relies on the payer's wallet reporting every payment.
- The creator assigns each receipt by hand with "Assign", which works today.
Additional context
Code on master (4d13a1d):
AppViewModel.kt:1655-1674 (handleOnchainTransactionReceived) sets a contact only when an output address is one the wallet reserved for a contact's private Paykit endpoint (:1661, lookup at PrivatePaykitRepo.kt:500-505):
val contactPublicKey = privatePaykitRepo.contactPublicKeyForPrivateOnchainAddresses(addresses)
A Shop or Locks payment goes to an address the Paykit Server derives from the creator's watch-only account, so it never matches.
- The contact page and the row title read the same field.
ActivityRepo.kt:459-467 keeps the activities where PubkyPublicKeyFormat.matches(it.contact(), normalizedKey), and ContactActivityTitle.kt:28-31 (contactForActivity) names the row from activity.contact(). The one missing attribution causes both gaps.
Twin: synonymdev/bitkit-ios#849
Problem or use case
When a contact pays a creator's Shop order or unlocks one of their Locks posts, the creator's Bitkit records the on-chain receipt without the payer:
A payment between the same two contacts made in Bitkit reads "Received from " and is listed on the contact page. In a regtest run on 25 Sep, the creator's contact page for the payer listed four Lightning payments and missed the Shop and Locks receipts ($120.14 and $5.47). The payer's contact page for the creator listed all six payments.
Proposed solution
handleOnchainTransactionReceived. The row then reads "Received from ", and the contact page lists the receipt.invoices.reader_lookup_hashsits next tobitcoin_address_lookup_hash,paykit-server/migrations/0001_initial.sql:41-49on722ef26), so it can also hand the wallet the pair (address, reader pubky) when it derives the address, for example as a record the server writes for the wallet. Bitkit then looks the paying address up in those pairs.Alternatives considered
Additional context
Code on
master(4d13a1d):AppViewModel.kt:1655-1674(handleOnchainTransactionReceived) sets a contact only when an output address is one the wallet reserved for a contact's private Paykit endpoint (:1661, lookup atPrivatePaykitRepo.kt:500-505):ActivityRepo.kt:459-467keeps the activities wherePubkyPublicKeyFormat.matches(it.contact(), normalizedKey), andContactActivityTitle.kt:28-31(contactForActivity) names the row fromactivity.contact(). The one missing attribution causes both gaps.