Repository navigation
Conversation
Payment scanners read requests from NFC tags (reader mode) and from nearby Bitkit wallets over Bluetooth LE. The Receive screen offers its request as an emulated NFC Forum Type 4 tag and over a BLE GATT service while it is on screen. Adds the TapPay (NFC) intro sheet, the scanner source states from Figma and a Nearby payments setting. Protocol in docs/nearby-payments.md. Refs #1740 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
28 tasks
Contributor
Regtest APKDownload bitkit-dev-debug universal APK (expires in 30 days). |
- Accept only bitcoin:, lightning: invoice/offer and ethereum: requests over Bluetooth; skip the device otherwise. - Stop offering and listening while the app is locked. - Show the Scan to pay panel on every payment scanner when the camera is denied. - Android 9-11: report location services off for scanning, and advertise without the location permission. - Recreate the scanner/advertiser when Bluetooth or location is switched. - Keep scanning after a delivered request, retry failed scans, and ignore callbacks from stale read sessions. - Register the NDEF tag AID only while Receive is shown and present no tag when nothing is published; owner tokens for the emitter and reader mode. - Translations, journeys, and repo conventions (nowMillis, Duration, Context.bluetoothManager, narrow lint suppressions, log wording). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Square Register serves lightning:<uppercase BOLT11> as a long-format NDEF URI record; Numo serves BIP 321 without an address. Add a real 447-char signet fixture and tests for record decoding, byte-exact encoding, chunked tag reads and the Bluetooth allowlist. bitkit-core rejects bitcoin:?lightning=<invoice> (synonymdev/bitkit-core#212), so requests read over NFC or Bluetooth are rewritten to lightning:<invoice>. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1740
Twin: synonymdev/bitkit-ios#1302
This PR adds NFC and Bluetooth nearby payments. Payment scanners read requests from NFC tags, payment terminals and nearby Bitkit wallets. The Receive screen offers its request to nearby phones. The protocol is in
docs/nearby-payments.mdand is shared with iOS.Platform support
Description
lightning:, BIP21bitcoin:(withlightning=), LNURL, and USDTethereum:.NdefTagService(host card emulation) serves the current request as a read-only NFC Forum Type 4 tag (categoryother, device unlock required). It also covers the USDT receive screen.D2760000850101is registered for the service only while Receive is shown, and the service is the preferred one meanwhile.bitcoin:, alightning:invoice or offer, and USDTethereum:. Anything else the scanner understands (Pubky auth, LNURL auth/withdraw/channel, wallet connection setups, contact keys) is ignored, and that device is skipped.NFC,BLUETOOTH_ADVERTISE, optionalnfcandnfc.hcefeatures, and the HCE service.POS request formats
lightning:<uppercase BOLT11>as one long-format NDEF URI record (header0xC1, identifier code0x00). Bitkit decodes it as is. Tests cover it with a real 447-character signet invoice, a 465-byte message laid out like Square's.bitcoin:?lightning=<BOLT11>. bitkit-core rejects that form (Decode BIP 321 URIs without an address (bitcoin:?lightning=…) bitkit-core#212), so requests read over NFC or Bluetooth are rewritten tolightning:<invoice>before decoding. The rewrite can go once core supports BIP 321.Out of Scope
nfc: being read by Bolt Card terminals (Blink, BTCPay, LNbits TPoS, Swiss Bitcoin Pay, Wallet of Satoshi merchant mode). They read anlnurlwcard and pull the payment, which needs a server-side LNURL-withdraw relay for a self-custodial wallet.nfc: USDT over NFC terminals. None exist on any chain, Arbitrum included. A USDTethereum:request read from another Bitkit is supported.nearby: requesting the Nearby devices permission from Receive. Receive advertises only once the permission was granted from the scanner, because the design has no Receive permission state.settings: the design's "QRs + NFC" QuickPay row value. The row keeps On/Off.Design
NFC compatibility (research, Nov 2026):
lightning:Type 4 taglightning:/bitcoin:/LNURL tagslnurlwcardPreview
Screenshots are from an emulator, which has no NFC hardware. For these captures only, the NFC availability checks were forced on in a local build. That override is not in this branch. The Bluetooth states are real.
QA Notes
Setup
devDebug) from this branch on two NFC-capable Android phones, a third phone or a tag writer for static tags, and an iPhone running feat: nfc and bluetooth nearby payments bitkit-ios#1302 for the cross-platform cases.Journeys
nearby-payments-toggle.xml— Settings shows Nearby payments on by default, and the choice persistsscanner-nearby-states.xml— the Send scanner shows Scan to pay when the camera is denied, a Bluetooth button and "Scanning QR" without the Nearby devices permission, then "Scanning QR & Bluetooth"NFC and Bluetooth between devices are not in the Capabilities table; see Manual Tests.
Manual Tests
lightning:tag, abitcoin:?lightning=tag and an LNURL-pay tag from the main scanner → each opens the matching send flow — NFC tagspubkyauth://URI from a test build) → B's scanner ignores it and opens nothing — Bluetooth between devicesAutomated Checks
NdefCodecTest.kt— URI/Text/absolute-URI records, prefix codes, long records, malformed input, and single-request selectionType4TagEmulatorTest.kt— the Type 4 APDU read sequence, chunked reads, no tag while nothing is published, read-only, and errorsNfcPaymentUriTest.kt— schemes added to bare invoices and addressesNearbyProtocolTest.kt— UUIDs, meta encoding, chunking and reassembly, offset reads, and limitsNfcTimedSheetTest.kt— shows once and only with NFC, and marks both intros seenNearbyRequestFilterTest.kt— Bluetooth accepts bitcoin, lightning invoice/offer and ethereum requests, and refuses auth, LNURL, setup and contact payloadsNfcTagEmitterTest.kt— an older publisher cannot clear a newer requestAppViewModelSendFlowTest.kt— new timed sheet dependencyEvidence
On
28639c4e7:just compile,just testandjust lint(detekt and Android Lint) all pass.Read nearby request, thenHandling decoded scan data).On
40b2a250b:devDebug:dumpsys bluetooth_manager.Read nearby requestand thenHandling decoded scan data: OnChain(... lightning=lnbcrt1...). That covers case 5's transport, but not a completed payment, because B had no funds.Gaps
3e377fbf9, and its findings were addressed in28639c4e7. One finding was not taken: it flagged thatshowsTapToPaycomes aftermodifierinReceiveQrView, but AGENTS.md requiresmodifierto be the first optional parameter.Models used
claude-opus-5-5(reasoning:Not exposed)claude-opus-5-5(reasoning:Not exposed)claude-opus-5-5(reasoning:Not exposed)Metadata note: the session exposes the model ID but not the reasoning setting.
🤖 Generated with Claude Code