Skip to content

FFI sender doesn' support v1 payjoin URIs #1849

Description

@spacebear21

Summary

payjoin::send::v2::SenderBuilder::new panics with
unimplemented!("V2 SenderBuilder only supports v2 URLs") when given a URI
whose pj= endpoint is v1-only (a plain HTTPS URL with no BIP 77 fragment,
as produced by any BIP 78 receiver such as BTCPay Server).

The panic is reachable with untrusted input. A v1 payjoin URI parses
successfully and passes check_pj_supported(), so nothing upstream of the
builder rejects it. Payjoin URIs are routinely scanned from QR codes, which
means any v2-only sender integration can be made to panic by paying a
BIP 78 receiver.

payjoin-ffi forwards URIs to this constructor unchecked, so the panic
crosses the FFI boundary. Through uniffi it surfaces as an opaque internal
panic exception in the host language (or a process abort in panic=abort
builds), with no way for bindings users to handle it as a typed error.
Since the FFI also exposes no v1 sender flow, bindings users currently have
no way to pay a v1-only receiver at all: the FFI V1Context and
RequestV1Context types exist but nothing can produce them.

Reproduction

Confirmed in the wild: Bull Bitcoin Mobile 6.13.0 fails at payjoin sender creation when payjoin
is enabled and the destination is a v1 payjoin URI. The panic is caught
by uniffi and rethrown as a generic internal error, so the send attempt
fails with an unactionable message.

Image

History

  • e59c1031 (2024-08-05, released in 0.20.0): extract_v2 gained a v1
    fallback, "Make backwards-compatible v2 to v1 sends possible."
  • 0.21.0: the unified sender exposed both extract_v1() and
    extract_v2(). The payjoin-ffi 0.21.0 changelog documented this as
    "v1 support is now only available through the V2 backwards-compatible
    APIs," which was accurate at the time.
  • 659740d6 (2025-08-05, Refactor sender to validate PjParam by version #901, released in 0.25.0): the v1 request path
    (create_v1_post_request) was removed from the v2 sender and from the
    FFI in the same commit, and the v1 match arm became unimplemented!.
    The changelog recorded this only as "Refactor sender to validate PjParam
    by version," so the loss of v2-to-v1 sending and the new panic path went
    unannounced. The receiver-side backwards compatibility (v2 receiver
    serving v1 senders) is unaffected and still works.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingffi

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions