From be314a68e6c9b77bc56d7d81678237782918f3f3 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Wed, 2 Sep 2026 03:05:50 +0200 Subject: [PATCH 1/5] test: add marketplace wallet journey --- .../PaymentRequestsScreenTest.kt | 7 +- .../paymentrequests/PaymentRequestsScreen.kt | 6 +- .../screens/profile/PubkyAuthApprovalSheet.kt | 5 +- .../to/bitkit/ui/settings/SettingsScreen.kt | 2 +- journeys/pubky-marketplace/README.md | 105 ++++++++++++++++++ journeys/pubky-marketplace/wallet-leg.xml | 39 +++++++ 6 files changed, 157 insertions(+), 7 deletions(-) create mode 100644 journeys/pubky-marketplace/README.md create mode 100644 journeys/pubky-marketplace/wallet-leg.xml diff --git a/app/src/androidTest/java/to/bitkit/ui/screens/paymentrequests/PaymentRequestsScreenTest.kt b/app/src/androidTest/java/to/bitkit/ui/screens/paymentrequests/PaymentRequestsScreenTest.kt index 35fd856b3d..18fdf8a079 100644 --- a/app/src/androidTest/java/to/bitkit/ui/screens/paymentrequests/PaymentRequestsScreenTest.kt +++ b/app/src/androidTest/java/to/bitkit/ui/screens/paymentrequests/PaymentRequestsScreenTest.kt @@ -47,7 +47,8 @@ class PaymentRequestsScreenTest { } } - composeTestRule.onNodeWithTag("PaymentRequestRowincoming").assertIsDisplayed() + composeTestRule.onNodeWithTag("PaymentRequestRow-incoming").assertIsDisplayed() + composeTestRule.onNodeWithTag("PaymentRequestPay-incoming").assertIsDisplayed() composeTestRule.onNodeWithTag("MoneyPrimary").assertIsDisplayed() composeTestRule.onNodeWithTag("MoneySecondary").assertIsDisplayed() composeTestRule.onNodeWithTag("PaymentRequestsSeeAll").assertIsDisplayed() @@ -105,8 +106,8 @@ class PaymentRequestsScreenTest { } } - composeTestRule.onNodeWithTag("PaymentRequestRowaccepted").assertIsDisplayed() - composeTestRule.onNodeWithTag("PaymentRequestRowoutgoing").assertIsDisplayed() + composeTestRule.onNodeWithTag("PaymentRequestRow-accepted").assertIsDisplayed() + composeTestRule.onNodeWithTag("PaymentRequestRow-outgoing").assertIsDisplayed() composeTestRule.onNodeWithText("Waiting for", substring = true).assertIsDisplayed() composeTestRule.onNodeWithText("PAYMENT REQUESTS").assertIsDisplayed() composeTestRule.onNodeWithText("TODAY").assertIsDisplayed() diff --git a/app/src/main/java/to/bitkit/ui/screens/paymentrequests/PaymentRequestsScreen.kt b/app/src/main/java/to/bitkit/ui/screens/paymentrequests/PaymentRequestsScreen.kt index abdab879f5..9ff28c241e 100644 --- a/app/src/main/java/to/bitkit/ui/screens/paymentrequests/PaymentRequestsScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/paymentrequests/PaymentRequestsScreen.kt @@ -490,7 +490,7 @@ internal fun PaymentRequestCard( Modifier } ) - .testTag("PaymentRequestRow${request.paymentRequestId}"), + .testTag("PaymentRequestRow-${request.paymentRequestId}") ) { Row( verticalAlignment = Alignment.CenterVertically, @@ -553,7 +553,9 @@ internal fun PaymentRequestCard( ) }, size = ButtonSize.Small, - modifier = Modifier.weight(1f), + modifier = Modifier + .weight(1f) + .testTag("PaymentRequestPay-${request.paymentRequestId}") ) } } diff --git a/app/src/main/java/to/bitkit/ui/screens/profile/PubkyAuthApprovalSheet.kt b/app/src/main/java/to/bitkit/ui/screens/profile/PubkyAuthApprovalSheet.kt index 97ec5eae3f..bff3611f44 100644 --- a/app/src/main/java/to/bitkit/ui/screens/profile/PubkyAuthApprovalSheet.kt +++ b/app/src/main/java/to/bitkit/ui/screens/profile/PubkyAuthApprovalSheet.kt @@ -348,7 +348,9 @@ private fun ColumnScope.AuthorizeContent( PrimaryButton( text = stringResource(R.string.profile__auth_approval_authorize), onClick = onAuthorize, - modifier = Modifier.weight(1f), + modifier = Modifier + .weight(1f) + .testTag("PubkyAuthAuthorize") ) } VerticalSpacer(16.dp) @@ -420,6 +422,7 @@ private fun ColumnScope.SuccessContent( PrimaryButton( text = stringResource(R.string.profile__auth_approval_ok), onClick = onDismiss, + modifier = Modifier.testTag("PubkyAuthOK") ) VerticalSpacer(16.dp) } diff --git a/app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt b/app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt index d8e88edde8..c037f21dc8 100644 --- a/app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt @@ -343,7 +343,7 @@ private fun GeneralTabContent( icon = { SettingsIcon(R.drawable.ic_coins) }, onClick = { onEvent(SettingsEvent.ContactPaymentsClick) }, enabled = !state.isUpdatingContactPayments, - switchTestTag = "ContactPaymentsSwitch", + switchTestTag = "ContactPaymentsToggle", modifier = Modifier.testTag("ContactPaymentsSettings") ) } diff --git a/journeys/pubky-marketplace/README.md b/journeys/pubky-marketplace/README.md new file mode 100644 index 0000000000..8fa1ae6ed0 --- /dev/null +++ b/journeys/pubky-marketplace/README.md @@ -0,0 +1,105 @@ +# Pubky marketplace wallet leg + +This suite covers the two-wallet Bitkit leg of a Pubky marketplace purchase: a seller grants a +watch-only account claim, a linked buyer receives the resulting Payment Request, and the buyer pays +the request on regtest through confirmation. It does not cover marketplace browsing, Locks content +delivery, fiat payment, or Hypercolor. + +## Required external fixture + +The journey needs a controlled marketplace fixture outside this repository. The fixture owns all +server-side state and must provide: + +- A fresh Pubky testnet or isolated staging namespace reachable by both wallets. +- A Paykit Server with the marketplace wallet-interop fixes and a `/setup` flow whose auth URL + carries `x-bitkit-claim=watch-only-account-v1`. +- A regtest bitcoind and Electrum/Fulcrum endpoint on the same chain. Configure the endpoint in both + wallets before their first launch so neither wallet retains a taller foreign regtest tip. +- A clean seller wallet, a separate clean funded buyer wallet, and the seller Pubky public key. +- A marketplace driver that can create one purchase for the buyer, expose its Payment Request id, + report Paykit delivery, return the derived on-chain address and expected amount, mine one block, + and report the transaction and purchase status. + +Android emulators reach host services through `10.0.2.2`. The reference Pubky testnet advertises +its homeserver endpoints as localhost, so map its TCP services into each emulator before creating a +profile: + +```sh +adb -s reverse tcp:6286 tcp:6286 +adb -s reverse tcp:6287 tcp:6287 +adb -s reverse tcp:6288 tcp:6288 +adb -s reverse tcp:15411 tcp:15411 +adb -s reverse tcp:15412 tcp:15412 +``` + +The request and endpoint must satisfy the issuer contract from Android issue +[#1208](https://github.com/synonymdev/bitkit-android/issues/1208): lowercase `btc`, a +network-correct `btc-regtest-*` endpoint identifier, and a JSON endpoint payload with a non-empty +string `value`. The fixture must keep watch-only account material and spending authority separate. +Evidence must show the claimed account xpub and account index while omitting wallet seed material +and tokens. + +The reference implementation is +[`BitcoinErrorLog/pubky-marketplace/payments-env`](https://github.com/BitcoinErrorLog/pubky-marketplace/tree/0259d994967961cb0b972eba2f11a567d7376dd7/payments-env). +Its `scripts/verify.sh` proves the Locks, Paykit, Pubky, bitcoind, and Fulcrum protocol path. For this +journey, the seller wallet replaces `paykit-companion-auth` and the buyer wallet replaces +`paykit-reader-demo`; the other fixture roles remain unchanged. + +## Required app changes + +The full journey depends on the sibling work from the parent epic: + +- [#1208](https://github.com/synonymdev/bitkit-android/issues/1208) defines the issuer interop + contract. +- [#1209](https://github.com/synonymdev/bitkit-android/issues/1209) preserves rejected incoming + requests as visible history. +- [#1210](https://github.com/synonymdev/bitkit-android/issues/1210) owns the approved Payment Request + intake policy. This journey does not implement or widen that policy. +- [#1211](https://github.com/synonymdev/bitkit-android/issues/1211) prevents an Electrum-rejected + broadcast from reaching `SendSuccess`. + +The linked-contact prerequisite is existing Paykit behavior: the buyer must save the seller before +Bitkit's private-message poll can receive the request. The seller must also save the buyer when the +fixture exercises bilateral private delivery. + +## Evidence contract + +Capture one timestamped evidence directory per run. Record the app commit, fixture commit, both +device identifiers, Payment Request id, transaction id, and regtest block height. Keep these +artifacts at each boundary: + +| Boundary | Bitkit evidence | Fixture evidence | +| --- | --- | --- | +| Watch-only claim | `PubkyAuthWatchOnlyConsent`, `PubkyAuthWatchOnlyApprove`, `PubkyAuthAuthorize`, and `PubkyAuthOK` snapshots | Setup completion and the claimed xpub/account index, with no spending key | +| Contact payments | `ContactPaymentsToggle` snapshots from both wallets | Public receiver markers for both wallet identities | +| Linked buyer | `Contact_` snapshot | Seller and buyer peer-link state | +| Incoming request | `PaymentRequestsSheet` and `PaymentRequestRow-` snapshots showing seller, note, and amount | Delivery record and exact Payment Request id | +| Payment approval | `PaymentRequestPay-`, `ReviewAmount`, and `ReviewUri` snapshots | Derived regtest address and expected amount | +| Broadcast | `SendSuccess` snapshot and buyer activity details | Transaction in the fixture mempool with an amount-matched output | +| Confirmation | Confirmed buyer activity snapshot | Transaction id at one or more confirmations and completed purchase status | + +`SendSuccess` is evidence of backend acceptance, not confirmation. The fixture's chain and purchase +status are the confirmation authority. + +## Release provenance + +The watch-only claim entered the repository in `6f3134e1`, and the incoming Payment Request surface +entered in `4ea3dd16` and `7385376d`. No shipped Android release or tag contains both surfaces as of +2026-09-02. The first intended shipped release is the open `2.6.0` milestone; record its final tag +here when it ships. + +## Baseline from 2026-09-02 + +The current implementation was built from `9182771b`. A deployed seller completed the unchanged +watch-only consent, approval, authorization, companion-claim delivery, and `/setup` completion +path. The pinned reference fixture passed all 15 protocol-verifier steps. An isolated Android +emulator created a local Pubky profile after the required localhost port mappings, linked the live +seller, and discovered a funded regtest UTXO through the fixture Fulcrum endpoint. + +The seller then issued a 15,000-sat request. Paykit reached terminal `delivered` state for Payment +Request `a90be1df-0a93-4077-a685-ccd8cb3142aa`, but the fixture record carried uppercase +`asset=BTC` and endpoint identifier `btc-bitcoin-p2wpkh`; the canonical wallet contract requires +lowercase `btc` and a network-correct `btc-regtest-*` identifier, so neither Android nor iOS +surfaced the request. +[Upstream issue #1](https://github.com/BitcoinErrorLog/pubky-marketplace/issues/1) blocks the +payment and confirmation boundaries. The wallet filters remain strict. diff --git a/journeys/pubky-marketplace/wallet-leg.xml b/journeys/pubky-marketplace/wallet-leg.xml new file mode 100644 index 0000000000..1516e89138 --- /dev/null +++ b/journeys/pubky-marketplace/wallet-leg.xml @@ -0,0 +1,39 @@ + + + Verifies a Bitkit seller grants a watch-only account claim and a separate linked Bitkit buyer + receives, approves, pays, and confirms the resulting marketplace Payment Request on regtest. + Precondition: two clean wallets and the external fixture described in this suite's README. + Configure the fixture Electrum endpoint, Android emulator port mappings, and Paykit UI before + either wallet's first launch. Start with the seller wallet open and the fixture's fresh setup + auth URL available. + + + Open the fixture setup auth URL in the seller wallet + Verify the watch-only consent screen (id "PubkyAuthWatchOnlyConsent") is visible + Capture the watch-only consent evidence, then tap Approve (id "PubkyAuthWatchOnlyApprove") + Verify the authorization screen shows only the requested Paykit capabilities + Tap Authorize (id "PubkyAuthAuthorize") + Verify authorization succeeds (id "PubkyAuthOK") + Verify the fixture completes setup with the seller account xpub and no spending authority + Open General Settings in both wallets and verify contact payments (id "ContactPaymentsToggle") are enabled + Open Contacts in the buyer wallet and save the fixture's seller public key + Verify the seller contact (id "Contact_<seller-public-key>") is visible + Open Contacts in the seller wallet and save the buyer public key + Verify the buyer contact (id "Contact_<buyer-public-key>") is visible + Verify the fixture reports the seller and buyer as linked peers + Have the fixture create one marketplace purchase for the buyer and record its Payment Request id + Verify the request uses lowercase "btc", a regtest endpoint identifier, and a JSON endpoint value + Wait for the incoming requests sheet (id "PaymentRequestsSheet") + Verify the request row (id "PaymentRequestRow-<payment-request-id>") shows the seller, note, and expected amount + Capture the incoming-request evidence, then tap Pay (id "PaymentRequestPay-<payment-request-id>") + Verify the review amount (id "ReviewAmount") matches the fixture amount + Show details and verify the payment URI (id "ReviewUri") contains the fixture's regtest address + Capture the payment-approval evidence, then swipe to send (id "GRAB") + Verify Bitcoin Sent (id "SendSuccess") appears after the backend accepts the transaction + Verify the fixture mempool contains the transaction with an amount-matched output + Mine one block with the fixture and wait for both Bitkit and the marketplace status to refresh + Open the latest sent on-chain activity and verify its amount (id "ActivityAmount") and transaction details (id "ActivityTxDetails") + Verify the fixture reports the transaction confirmed and the marketplace purchase completed + Capture the final activity, transaction id, confirmation height, and completed purchase evidence + + From b6da855274e3ad3ea57658345e081988ce098898 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Wed, 2 Sep 2026 10:17:06 +0200 Subject: [PATCH 2/5] test: record marketplace blocker --- journeys/pubky-marketplace/README.md | 45 ++++++++++++++--------- journeys/pubky-marketplace/wallet-leg.xml | 4 +- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/journeys/pubky-marketplace/README.md b/journeys/pubky-marketplace/README.md index 8fa1ae6ed0..d032a2f467 100644 --- a/journeys/pubky-marketplace/README.md +++ b/journeys/pubky-marketplace/README.md @@ -40,10 +40,11 @@ Evidence must show the claimed account xpub and account index while omitting wal and tokens. The reference implementation is -[`BitcoinErrorLog/pubky-marketplace/payments-env`](https://github.com/BitcoinErrorLog/pubky-marketplace/tree/0259d994967961cb0b972eba2f11a567d7376dd7/payments-env). -Its `scripts/verify.sh` proves the Locks, Paykit, Pubky, bitcoind, and Fulcrum protocol path. For this -journey, the seller wallet replaces `paykit-companion-auth` and the buyer wallet replaces -`paykit-reader-demo`; the other fixture roles remain unchanged. +[`BitcoinErrorLog/pubky-marketplace/payments-env`](https://github.com/BitcoinErrorLog/pubky-marketplace/tree/master/payments-env). +Fixture commit `ed03a32e` pins Paykit Server source `867fc883` and verifies the canonical request +contract. Its `scripts/verify.sh` proves the Locks, Paykit, Pubky, bitcoind, and Fulcrum protocol +path. For this journey, the seller wallet replaces `paykit-companion-auth` and the buyer wallet +replaces `paykit-reader-demo`; the other fixture roles remain unchanged. ## Required app changes @@ -73,8 +74,8 @@ artifacts at each boundary: | Watch-only claim | `PubkyAuthWatchOnlyConsent`, `PubkyAuthWatchOnlyApprove`, `PubkyAuthAuthorize`, and `PubkyAuthOK` snapshots | Setup completion and the claimed xpub/account index, with no spending key | | Contact payments | `ContactPaymentsToggle` snapshots from both wallets | Public receiver markers for both wallet identities | | Linked buyer | `Contact_` snapshot | Seller and buyer peer-link state | -| Incoming request | `PaymentRequestsSheet` and `PaymentRequestRow-` snapshots showing seller, note, and amount | Delivery record and exact Payment Request id | -| Payment approval | `PaymentRequestPay-`, `ReviewAmount`, and `ReviewUri` snapshots | Derived regtest address and expected amount | +| Incoming request | `PaymentRequestsSheet` and `PaymentRequestRow-` snapshots showing seller, amount, and note when present | Delivery record and exact Payment Request id | +| Payment approval | `PaymentRequestPay-`, `ReviewAmount`, and `ReviewContactRecipient` snapshots | Derived regtest address and expected amount | | Broadcast | `SendSuccess` snapshot and buyer activity details | Transaction in the fixture mempool with an amount-matched output | | Confirmation | Confirmed buyer activity snapshot | Transaction id at one or more confirmations and completed purchase status | @@ -90,16 +91,24 @@ here when it ships. ## Baseline from 2026-09-02 -The current implementation was built from `9182771b`. A deployed seller completed the unchanged +The current implementation was built from `423d4b2f`. A deployed seller completed the unchanged watch-only consent, approval, authorization, companion-claim delivery, and `/setup` completion -path. The pinned reference fixture passed all 15 protocol-verifier steps. An isolated Android -emulator created a local Pubky profile after the required localhost port mappings, linked the live -seller, and discovered a funded regtest UTXO through the fixture Fulcrum endpoint. - -The seller then issued a 15,000-sat request. Paykit reached terminal `delivered` state for Payment -Request `a90be1df-0a93-4077-a685-ccd8cb3142aa`, but the fixture record carried uppercase -`asset=BTC` and endpoint identifier `btc-bitcoin-p2wpkh`; the canonical wallet contract requires -lowercase `btc` and a network-correct `btc-regtest-*` identifier, so neither Android nor iOS -surfaced the request. -[Upstream issue #1](https://github.com/BitcoinErrorLog/pubky-marketplace/issues/1) blocks the -payment and confirmation boundaries. The wallet filters remain strict. +path. Corrected fixture commit `ed03a32e` passed its canonical verifier with lowercase `btc`, +endpoint identifier `btc-regtest-p2wpkh`, and a JSON `value`. An isolated Android emulator created +buyer `pubky9s1fboi8r1ft1ecnzpik1wwkiuxmd85hzu6w3wpigmwdyry7rjxy`, linked seller +`pubkyhbn4tahj71yzpmtarz5amtqqf5fmicdd7rs8ao448tzaujdapfiy`, and confirmed reciprocal contact +rows and enabled contact payments. + +The fixture delivered Locks bundle `YT3N7MNQ55PARNR6BK4H80MBDC`, Payment Request +`767ca32e-8f17-4763-9343-3b273f4fb699`, and event +`b19f4936-7b83-4a1d-a1ae-a571653a4b9e`. Android surfaced the exact incoming row with Seller and +15,000 sats, handled the absent note, and opened the payment confirmation. The app resolved +`bcrt1q8r8ryq9tv7yufr7gpszpgyw7lly7dl97przkv2`, retained the 15,000-sat amount, and showed Seller +as `ReviewContactRecipient` with a 141-sat fee. + +The confirmation slider remained disabled because the incoming on-chain scan path does not persist +its successful amount validation into `isAmountInputValid`. No transaction was broadcast and the +fixture mempool remained empty. Android issue +[#1218](https://github.com/synonymdev/bitkit-android/issues/1218) blocks the broadcast and +confirmation boundaries; [fixture issue #1](https://github.com/BitcoinErrorLog/pubky-marketplace/issues/1) +records the corrected upstream contract work. diff --git a/journeys/pubky-marketplace/wallet-leg.xml b/journeys/pubky-marketplace/wallet-leg.xml index 1516e89138..93b2860a1c 100644 --- a/journeys/pubky-marketplace/wallet-leg.xml +++ b/journeys/pubky-marketplace/wallet-leg.xml @@ -24,10 +24,10 @@ Have the fixture create one marketplace purchase for the buyer and record its Payment Request id Verify the request uses lowercase "btc", a regtest endpoint identifier, and a JSON endpoint value Wait for the incoming requests sheet (id "PaymentRequestsSheet") - Verify the request row (id "PaymentRequestRow-<payment-request-id>") shows the seller, note, and expected amount + Verify the request row (id "PaymentRequestRow-<payment-request-id>") shows the seller and expected amount, plus the fixture note when present Capture the incoming-request evidence, then tap Pay (id "PaymentRequestPay-<payment-request-id>") Verify the review amount (id "ReviewAmount") matches the fixture amount - Show details and verify the payment URI (id "ReviewUri") contains the fixture's regtest address + Show details and verify the recipient (id "ReviewContactRecipient") is the seller contact Capture the payment-approval evidence, then swipe to send (id "GRAB") Verify Bitcoin Sent (id "SendSuccess") appears after the backend accepts the transaction Verify the fixture mempool contains the transaction with an amount-matched output From e25180b291624423477f3cbaba59f6527fd03d42 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Wed, 2 Sep 2026 11:06:28 +0200 Subject: [PATCH 3/5] test: record marketplace acceptance --- journeys/pubky-marketplace/README.md | 55 +++++++++++++++++----------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/journeys/pubky-marketplace/README.md b/journeys/pubky-marketplace/README.md index d032a2f467..74ae1c53b7 100644 --- a/journeys/pubky-marketplace/README.md +++ b/journeys/pubky-marketplace/README.md @@ -89,26 +89,39 @@ entered in `4ea3dd16` and `7385376d`. No shipped Android release or tag contains 2026-09-02. The first intended shipped release is the open `2.6.0` milestone; record its final tag here when it ships. -## Baseline from 2026-09-02 +## Acceptance run from 2026-09-02 -The current implementation was built from `423d4b2f`. A deployed seller completed the unchanged +The successful replay used fixture commit `ed03a32e` and the production fix from Android issue +[#1218](https://github.com/synonymdev/bitkit-android/issues/1218) at `41b40818`. The installed E2E +APK had SHA-256 `cb494d870a255b96e3e289cbe7e659aa89fff1987308502b2fd55f121a0b0c42`, +used the local backend at `10.0.2.2`, and targeted homeserver +`8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo`. A deployed seller completed the unchanged watch-only consent, approval, authorization, companion-claim delivery, and `/setup` completion -path. Corrected fixture commit `ed03a32e` passed its canonical verifier with lowercase `btc`, -endpoint identifier `btc-regtest-p2wpkh`, and a JSON `value`. An isolated Android emulator created -buyer `pubky9s1fboi8r1ft1ecnzpik1wwkiuxmd85hzu6w3wpigmwdyry7rjxy`, linked seller -`pubkyhbn4tahj71yzpmtarz5amtqqf5fmicdd7rs8ao448tzaujdapfiy`, and confirmed reciprocal contact -rows and enabled contact payments. - -The fixture delivered Locks bundle `YT3N7MNQ55PARNR6BK4H80MBDC`, Payment Request -`767ca32e-8f17-4763-9343-3b273f4fb699`, and event -`b19f4936-7b83-4a1d-a1ae-a571653a4b9e`. Android surfaced the exact incoming row with Seller and -15,000 sats, handled the absent note, and opened the payment confirmation. The app resolved -`bcrt1q8r8ryq9tv7yufr7gpszpgyw7lly7dl97przkv2`, retained the 15,000-sat amount, and showed Seller -as `ReviewContactRecipient` with a 141-sat fee. - -The confirmation slider remained disabled because the incoming on-chain scan path does not persist -its successful amount validation into `isAmountInputValid`. No transaction was broadcast and the -fixture mempool remained empty. Android issue -[#1218](https://github.com/synonymdev/bitkit-android/issues/1218) blocks the broadcast and -confirmation boundaries; [fixture issue #1](https://github.com/BitcoinErrorLog/pubky-marketplace/issues/1) -records the corrected upstream contract work. +path. The fixture verifier passed with lowercase `btc`, endpoint identifier +`btc-regtest-p2wpkh`, and a JSON string `value`. + +Fresh Android buyer `pubkycecq8ssqnfgfwifioj7djoutnupmpjgomobistnz34zd9d5yyn4o` linked seller +`pubkyhbn4tahj71yzpmtarz5amtqqf5fmicdd7rs8ao448tzaujdapfiy`; both wallets saved the reciprocal +contact and enabled contact payments. The buyer received 1,000,000 sats at +`bcrt1q6mkkp26tu8zm4g78d58uksmvv3una04dryp7s0` in transaction +`3b48b259cd9aec8817b902a44c1609738268880cb16f25179ab87dea21a81a11`, confirmed at height +16,397 in block `5ad00a6d1d9e0e5a2348a255eae5bc83d507a759a4e9f377567af92fa3bacef6`. + +The fixture delivered Locks bundle `1GC8SBDYB2HHA2E0NZ51ZVEZX4`, server invoice +`92fdee57-6a46-40f2-9714-8a0e68d7e60e`, Payment Request +`ad1a8463-59e0-4cf8-b037-99ffb9d5b6ca`, and event +`4282bad8-270d-4e5c-a5f9-bca52d1583dd`. Android displayed the incoming Seller row for 15,000 +sats with an absent note, opened the payment review, resolved +`bcrt1qkuajc36azmaf9kk9ndwy9rdttl6vdlqwtvgyg5`, preserved the amount and Seller recipient, and +enabled the confirmation slider with a 141-sat fee. + +One swipe broadcast transaction `a3e2801d8cf3afa6a461a30fa38bc46680602311a7728b97e5d88f3767f3d9d2`. +The frozen zero-confirmation boundary contained only that mempool transaction, whose output zero +paid exactly 15,000 sats to the derived address; Paykit reported +`detected/0/amount_matched=true` and Locks remained pending. The fixture then mined exactly one +block. Android synced height 16,398 and showed a confirmed Seller activity with a 15,000-sat +payment and 141-sat fee. The transaction confirmed in block +`5f2a356cace276a17e0759d8d34e7c196c852b4b299901e592552fb8f8f0bb19`, Paykit reported +`confirmed/1/amount_matched=true`, the Locks bundle completed, and the paid request remained as +history without Pay or Dismiss actions. [Fixture issue #1](https://github.com/BitcoinErrorLog/pubky-marketplace/issues/1) +tracks the upstream environment used for this replay. From 3b27281fd42ee5c4cb98318517177eb7e353583c Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Wed, 2 Sep 2026 11:10:33 +0200 Subject: [PATCH 4/5] docs: pin marketplace fixture source --- journeys/pubky-marketplace/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/journeys/pubky-marketplace/README.md b/journeys/pubky-marketplace/README.md index 74ae1c53b7..dcd3f8ed6f 100644 --- a/journeys/pubky-marketplace/README.md +++ b/journeys/pubky-marketplace/README.md @@ -40,7 +40,7 @@ Evidence must show the claimed account xpub and account index while omitting wal and tokens. The reference implementation is -[`BitcoinErrorLog/pubky-marketplace/payments-env`](https://github.com/BitcoinErrorLog/pubky-marketplace/tree/master/payments-env). +[`BitcoinErrorLog/pubky-marketplace/payments-env`](https://github.com/BitcoinErrorLog/pubky-marketplace/tree/ed03a32ecfe02deab40ad10ae1bac7fa18465c10/payments-env). Fixture commit `ed03a32e` pins Paykit Server source `867fc883` and verifies the canonical request contract. Its `scripts/verify.sh` proves the Locks, Paykit, Pubky, bitcoind, and Fulcrum protocol path. For this journey, the seller wallet replaces `paykit-companion-auth` and the buyer wallet From 0a0f7c8b4b7153fb9f0d39d7016ec1c0b73bf96a Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Wed, 2 Sep 2026 11:25:18 +0200 Subject: [PATCH 5/5] docs: record selector replay --- journeys/pubky-marketplace/README.md | 67 +++++++++++++++-------- journeys/pubky-marketplace/wallet-leg.xml | 3 +- 2 files changed, 47 insertions(+), 23 deletions(-) diff --git a/journeys/pubky-marketplace/README.md b/journeys/pubky-marketplace/README.md index dcd3f8ed6f..e1ec7475fc 100644 --- a/journeys/pubky-marketplace/README.md +++ b/journeys/pubky-marketplace/README.md @@ -5,14 +5,14 @@ watch-only account claim, a linked buyer receives the resulting Payment Request, the request on regtest through confirmation. It does not cover marketplace browsing, Locks content delivery, fiat payment, or Hypercolor. -## Required external fixture +## Required integration fixture runtime -The journey needs a controlled marketplace fixture outside this repository. The fixture owns all -server-side state and must provide: +The journey needs a controlled integration fixture runtime. It must provide: - A fresh Pubky testnet or isolated staging namespace reachable by both wallets. -- A Paykit Server with the marketplace wallet-interop fixes and a `/setup` flow whose auth URL - carries `x-bitkit-claim=watch-only-account-v1`. +- A Paykit Server including the canonical request behavior from merged upstream + [`pubky/paykit-server#2`](https://github.com/pubky/paykit-server/pull/2), plus a `/setup` flow whose + auth URL carries `x-bitkit-claim=watch-only-account-v1`. - A regtest bitcoind and Electrum/Fulcrum endpoint on the same chain. Configure the endpoint in both wallets before their first launch so neither wallet retains a taller foreign regtest tip. - A clean seller wallet, a separate clean funded buyer wallet, and the seller Pubky public key. @@ -20,8 +20,8 @@ server-side state and must provide: report Paykit delivery, return the derived on-chain address and expected amount, mine one block, and report the transaction and purchase status. -Android emulators reach host services through `10.0.2.2`. The reference Pubky testnet advertises -its homeserver endpoints as localhost, so map its TCP services into each emulator before creating a +Android emulators reach host services through `10.0.2.2`. When the Pubky testnet runtime advertises +its homeserver endpoints as localhost, map its TCP services into each emulator before creating a profile: ```sh @@ -39,12 +39,11 @@ string `value`. The fixture must keep watch-only account material and spending a Evidence must show the claimed account xpub and account index while omitting wallet seed material and tokens. -The reference implementation is -[`BitcoinErrorLog/pubky-marketplace/payments-env`](https://github.com/BitcoinErrorLog/pubky-marketplace/tree/ed03a32ecfe02deab40ad10ae1bac7fa18465c10/payments-env). -Fixture commit `ed03a32e` pins Paykit Server source `867fc883` and verifies the canonical request -contract. Its `scripts/verify.sh` proves the Locks, Paykit, Pubky, bitcoind, and Fulcrum protocol -path. For this journey, the seller wallet replaces `paykit-companion-auth` and the buyer wallet -replaces `paykit-reader-demo`; the other fixture roles remain unchanged. +Use an isolated, disposable runtime that implements this contract. Paykit Server merge +`867fc883` supplies the canonical lowercase asset, network-correct endpoint identifier, JSON value +payload, and initial private-link retry behavior. Before running the wallets, verify the complete +Locks, Paykit, Pubky, bitcoind, and Fulcrum protocol path. The seller wallet fills the companion-auth +role and the buyer wallet fills the reader role; the runtime supplies the remaining services. ## Required app changes @@ -58,6 +57,9 @@ The full journey depends on the sibling work from the parent epic: intake policy. This journey does not implement or widen that policy. - [#1211](https://github.com/synonymdev/bitkit-android/issues/1211) prevents an Electrum-rejected broadcast from reaching `SendSuccess`. +- [#1218](https://github.com/synonymdev/bitkit-android/issues/1218) tracks the incoming on-chain + request swipe requirement. The behavior is supplied by merged + [#1178](https://github.com/synonymdev/bitkit-android/pull/1178) at `9698dea4`. The linked-contact prerequisite is existing Paykit behavior: the buyer must save the seller before Bitkit's private-message poll can receive the request. The seller must also save the buyer when the @@ -65,9 +67,9 @@ fixture exercises bilateral private delivery. ## Evidence contract -Capture one timestamped evidence directory per run. Record the app commit, fixture commit, both -device identifiers, Payment Request id, transaction id, and regtest block height. Keep these -artifacts at each boundary: +Capture one timestamped evidence directory per run. Record the app commit, fixture runtime +revisions, both device identifiers, Payment Request id, transaction id, and regtest block height. +Keep these artifacts at each boundary: | Boundary | Bitkit evidence | Fixture evidence | | --- | --- | --- | @@ -91,10 +93,11 @@ here when it ships. ## Acceptance run from 2026-09-02 -The successful replay used fixture commit `ed03a32e` and the production fix from Android issue -[#1218](https://github.com/synonymdev/bitkit-android/issues/1218) at `41b40818`. The installed E2E -APK had SHA-256 `cb494d870a255b96e3e289cbe7e659aa89fff1987308502b2fd55f121a0b0c42`, -used the local backend at `10.0.2.2`, and targeted homeserver +The initial successful payment replay used an isolated runtime including upstream Paykit Server +merge `867fc883` and a pre-merge copy of the incoming-request swipe behavior now supplied by merged +[#1178](https://github.com/synonymdev/bitkit-android/pull/1178). The installed E2E APK had SHA-256 +`cb494d870a255b96e3e289cbe7e659aa89fff1987308502b2fd55f121a0b0c42`, used the local backend at +`10.0.2.2`, and targeted homeserver `8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo`. A deployed seller completed the unchanged watch-only consent, approval, authorization, companion-claim delivery, and `/setup` completion path. The fixture verifier passed with lowercase `btc`, endpoint identifier @@ -123,5 +126,25 @@ block. Android synced height 16,398 and showed a confirmed Seller activity with payment and 141-sat fee. The transaction confirmed in block `5f2a356cace276a17e0759d8d34e7c196c852b4b299901e592552fb8f8f0bb19`, Paykit reported `confirmed/1/amount_matched=true`, the Locks bundle completed, and the paid request remained as -history without Pay or Dismiss actions. [Fixture issue #1](https://github.com/BitcoinErrorLog/pubky-marketplace/issues/1) -tracks the upstream environment used for this replay. +history without Pay or Dismiss actions. + +The selector-specific acceptance replay used Android buyer `emulator-5560` and seller iOS simulator +`B379B7A4-715A-427F-8CB6-A6479BC73050`. It ran a pre-merge integration build containing the journey +selectors and the incoming-request swipe behavior now supplied by merged #1178. The built and +device-installed APKs both had SHA-256 +`8d62881da626a6f6eab1e243c05079305ebd3efd2f9abb820a1a6b55d6454cf4`. The retained Buyer profile +was synced at height 16,398 with 984,859 sats before the fixture created Locks bundle +`J9AKW3TNASDM6MJB0SNE08RJ0M`, server invoice `8d810705-6eeb-46f6-9c57-dd3bc4bdc0cf`, Payment +Request `b7f67854-b052-4eed-a6e7-e1ac41c31a7a`, event +`cec538cb-57f5-4c89-9d80-7584699af1ec`, and payment reference +`94f212c9-ed8c-4b85-9b0a-e9eea09f0a73`. + +Android exposed the exact `PaymentRequestRow-b7f67854-b052-4eed-a6e7-e1ac41c31a7a` and +`PaymentRequestPay-b7f67854-b052-4eed-a6e7-e1ac41c31a7a` selectors, then preserved the 15,000-sat +amount, Seller recipient, and 141-sat fee with an enabled confirmation slider. One swipe broadcast +transaction `3dacd7591e0e9d1b7eab62f814f86017c41c1a1b8dda839a79b7244d9b20f997`, whose output zero paid +exactly 15,000 sats to `bcrt1qxluk70usejytg7ehgdhpsq657eshhmr8lmkcsv`. The frozen +zero-confirmation boundary contained that single mempool transaction. The fixture mined exactly one +block, `7cabfa65673a0472d70c0b1f217e93d6114e040a342381a446f9a50987d18f30`, at height 16,399. Paykit +reported `confirmed/1/amount_matched=true`, the Locks bundle completed, Android showed the Seller +payment as confirmed, and the paid request remained in history without Pay or Dismiss actions. diff --git a/journeys/pubky-marketplace/wallet-leg.xml b/journeys/pubky-marketplace/wallet-leg.xml index 93b2860a1c..19830ab9db 100644 --- a/journeys/pubky-marketplace/wallet-leg.xml +++ b/journeys/pubky-marketplace/wallet-leg.xml @@ -2,7 +2,8 @@ Verifies a Bitkit seller grants a watch-only account claim and a separate linked Bitkit buyer receives, approves, pays, and confirms the resulting marketplace Payment Request on regtest. - Precondition: two clean wallets and the external fixture described in this suite's README. + Precondition: two clean wallets and the integration fixture runtime described in this suite's + README. Configure the fixture Electrum endpoint, Android emulator port mappings, and Paykit UI before either wallet's first launch. Start with the seller wallet open and the fixture's fresh setup auth URL available.