Skip to content

feat(clink): pay CLINK noffer offers from notes, profiles & wallet#588

Open
dmnyc wants to merge 3 commits into
barrydeen:mainfrom
dmnyc:feat/clink-noffer-payments
Open

feat(clink): pay CLINK noffer offers from notes, profiles & wallet#588
dmnyc wants to merge 3 commits into
barrydeen:mainfrom
dmnyc:feat/clink-noffer-payments

Conversation

@dmnyc

@dmnyc dmnyc commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Android port of the iOS CLINK Offers feature (barrydeen/wisp-ios#360). Adds noffer1… payment support so users can pay self-custodial Lightning offers advertised in notes, on profiles, and from the wallet.

  • DecoderNoffer: bech32 + TLV decoder, pricing types (fixed / variable-range / spontaneous), isNofferString / stripNostrPrefix / decodeOrNull.
  • RPCNofferClient: kind-21001 NIP-44 request → encrypted invoice response on the offer's relay (one-shot ephemeral connection), with code-3 "expired/moved" latest-retry per spec.
  • Pay sheetNofferPaySheet + NofferCard: full-height pay modal that pays via the active wallet, or falls back to a scannable bare-noffer QR for an external CLINK-aware wallet. The inline card resolves the recipient's name itself so it reads consistently across feed and thread.
  • NotesRichContent detects noffer tokens (incl. nostr: prefix) and renders the Pay-offer card.
  • Profilesclink_offer / noffer / offer parsed tolerantly from kind-0; Pay-offer button on the profile header; CLINK offer field in the profile editor; offer QR inside the QR sheet's Lightning tab with a contextual address/offer switch.
  • Wallet send — pasted/scanned noffer routes through the native amount → confirm flow; fixed-price offers skip straight to confirm. Payment hashes map to the offer's service pubkey so transaction history resolves the payee like zaps.
  • Cache migration — one-time profile-cache timestamp reset so pre-upgrade kind-0s re-parse with the new field.
  • Tests — decoder round-trip coverage mirroring the CLINK spec TLV layout.

Spec: https://github.com/shocknet/CLINK/blob/main/specs/clink-offers.md

A follow-up PR stacked on this branch aligns the profile/QR-sheet styling with iOS: #589.

Test plan

  • Note "Pay offer" card → in-app payment settles (Spark)
  • Wallet Send → pasted/scanned noffer opens the pay flow
  • Payee resolves in wallet transaction history
  • Profile editor round-trips clink_offer through kind-0
  • QR sheet Lightning tab shows the offer with address/offer switch
  • External bare-noffer QR fallback
  • Fixed-amount and variable-range pricing variants (only spontaneous exercised so far)

dmnyc added 3 commits June 11, 2026 15:36
Port of wisp-ios#360. Adds CLINK Offers (noffer1…) payment support:

- Noffer: bech32 + TLV decoder, pricing types (fixed / variable /
  spontaneous), isNofferString / stripNostrPrefix helpers
- NofferClient: kind-21001 NIP-44 RPC on the offer's relay with
  code-3 expired/moved latest-retry
- NofferPaySheet + NofferCard: shared pay sheet that pays via the
  active wallet or falls back to a scannable bare-noffer QR
- Notes: RichContent detects noffer tokens and renders a Pay-offer card
- Profiles: clink_offer parsed tolerantly from kind-0, editor field,
  Pay-offer header button, offer QR inside the Lightning tab with a
  contextual address/offer switch
- Wallet send: noffer detection routes through the native amount →
  confirm flow; fixed-price offers skip straight to confirm
- Tests: decoder round-trip coverage mirroring the CLINK spec TLVs

Spec: https://github.com/shocknet/CLINK/blob/main/specs/clink-offers.md
The NoteActions built for DM conversations, group DMs, group rooms,
and search results never set onPayInvoice, so the noffer pay sheet
(and lightning invoice cards) on those surfaces degraded to
"Connect a wallet to pay" even with an active wallet.
- Map noffer payment hashes to the offer's service pubkey via
  ZapSender.persistRecipient in both pay paths, so wallet history
  resolves the payee's profile (name + avatar) like zaps do
- Show the offer service's profile name on the wallet amount page and
  in the confirm-page description fallback
- Make NofferPaySheet full viewport height (status-bar inset so the
  drag handle clears camera cutouts), matching the iOS pay modal
- ProfileRepository schema migration: reset cached profile timestamps
  once so kind-0s re-parse with the new clinkOffer field — stale
  pre-upgrade cache entries otherwise never pick it up
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant