You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Please describe the feature you'd like to see added.
I'd like to add Java bindings for payjoin-ffi.
There is no JVM target today. I'd follow the C# layout (payjoin-ffi/java/), generate with uniffi-bindgen-java, and keep the generator in that directory rather than adding a cargo feature on payjoin-ffi.
First PR: generate, tests including v2, CI on linux/mac/windows. Not in that PR: Maven or a wallet.
Does that match what you'd want? I can open a draft after generate works locally.
Is your feature related to a problem, if so please describe it.
Following up on this, I did some work and found a few things.
uniffi-bindgen-java 0.5.0 requires uniffi 0.32. payjoin-ffi is on 0.31.2, and 0.5.0 rejects the metadata with Unexpected metadata type code: 116. The previous release, 0.4.2, reads 0.31 metadata but generates Java that does not compile for this crate.
From there,
I backported the 0.5.0 template fixes onto the 0.31 line. I opened IronCoreLabs/uniffi-bindgen-java#68 asking about a 0.4.3 and offered to do the work. Fixes are on a fork meanwhile and I generated bindings against that fork here
A cleaner approach would be to move payjoin-ffi to uniffi 0.32, which 0.5.0 supports unmodified. That is not limited to payjoin-ffi. uniffi-bindgen-cs is pinned at v0.11.0+v0.31.0 and uniffi-dart at v0.2.1+v0.31.2, so both would need 0.32 tags first.
A third option avoids the bindgen: a hand-written JVM layer over the official Kotlin bindings, CompletableFuture rather than coroutines. I did some work here. Works, but hand-maintained, and JNA rather than FFM.
I can open a draft PR for whichever direction you prefer, or leave this if Java bindings are not wanted at present.
@ram0verflow Thanks for taking this on. I see you added Kotlin and are wrapping it. I'd have the Kotlin in its own draft; it seems like that could ship faster...
Also, do you have or know of a project demanding Java? I'd like to keep an eye on that
@chavic Thanks! I’ll separate the Kotlin work out and get a V2 integration test working first, then put up a draft PR.
For Java, my original motivation was to experiment with integrating Payjoin V2 into Sparrow Wallet. I’m not working on Sparrow officially, It’s just a personal project/interest. I needed JVM/Java support for that, and the Kotlin work came out of exploring the cross-platform JVM side, rather than Kotlin being the original goal.
For Java, the remaining blocker is "uniffi-bindgen-java": 0.5.x expects UniFFI 0.32, while "payjoin-ffi" is still on 0.31.x. The 0.4.x line can read the metadata but generates invalid Java for this crate without the template fixes I backported.
Before I continue, which direction would you prefer?
generated Java bindings, using a patched/pinned 0.4.x generator for now;
wait until the UniFFI 0.32 ecosystem upgrade and use upstream "uniffi-bindgen-java";
a thin Java-facing layer over the merged Kotlin/JVM bindings.
I’m happy to open the next PR once the preferred direction is clear.
Yeah, I think the merged Kotlin/JVM package should be enough to start the Sparrow experiment.
I’ll try the integration against that first. If the Java interop turns out to be awkward or the API would benefit from first-class Java bindings, I can then open the generated-Java PR using the patched 0.4.x generator and upgrade it later with UniFFI 0.32.
I tested the merged Kotlin/JVM bindings in the Sparrow experiment and got a full BIP77 sender round trip working.
It turns out full generated Java bindings aren't necessary for this path. Almost the entire sender API is directly callable from Java, including the synchronous persistence/state-machine APIs.
The one issue I hit is the three SenderBuilder build methods: their u64/u8 parameters become Kotlin ULong/UByte, so the JVM method names are mangled and can't be called normally from Java. I initially used reflection to prove the integration, then tested a small hand-written Kotlin shim exposing Long/Int? wrappers. With that in place, the Sparrow round trip works without reflection, and the existing Kotlin integration suite still passes.
Would you prefer a small PR adding that Java-interop shim to the Kotlin/JVM bindings, or would you still rather I proceed with the patched uniffi-bindgen-java 0.4.x route?
The paths depend on whether you're blocked on the Sparrow experiment and how much work it is to rewrite if you eventually stumble on something.
I lean towards patching the Java bindings generator, even though you have something working; that would make your implementation more durable in the long run.
One thing: if it's going to take too long to fix the Java generator, and given we're more excited to see something working in Sparrow, don't treat the Java generator as a blocker to that work...
Makes sense. I'll keep the Sparrow experiment moving with the Kotlin/JVM bindings for now so the Java generator doesn't block it.
In parallel I'll continue with the patched uniffi-bindgen-java 0.4.x route for the durable Java bindings, and if that lands cleanly I can switch the Sparrow integration over later.
@ram0verflow how much has been communicated of these plans to @craigraw? My understanding of that project is that they won't take on any new libraries, and this would be a major new library. Even large contributions from outsiders are usually rewritten by Craig from what I've seen. I think the best place for these bindings would be to demonstrate end to end experience demo of async page join as a sort of scaffold that would be replaced piece by piece in native Java. I for that reason I wouldn't want to introduce a bindings library in Rust Payjoin that we will maintain immediately, at least not until it was clear that there is demand for such a library.
The most interesting thing about this approach to me is that it's not unique. I think this would be the same type of thing that Bitcoin Core would want if it were integrated, for example: Step one is make the demo using the Payjoin dev kit, and then step two is replace each function there with native code.
Not much yet. I haven't discussed the implementation approach with Craig directly, and I'm not working on Sparrow officially this started as a personal integration experiment.
The useful result so far is that I have a real BIP77 sender round trip working through Sparrow's existing networking and signing path, so the bindings have been valuable as a scaffold for proving the flow.
It makes sense not to take on a new external bindings dependency without clear downstream demand. I can treat the Java bindings work as experimental for now and take the working Sparrow PoC to Craig/#1980 first, rather than assuming Sparrow would adopt the library as a dependency.
If the preferred Sparrow direction is native Java, the PDK-backed implementation can still serve as a reference/scaffold and be replaced piece by piece.
@DanGould I’ve taken the working sender PoC to Craig/Sparrow on #1980 and asked specifically about the dependency/native-Java direction. I’ll let that discussion determine whether the Java bindings work should go any further.
Activity
ram0verflow commentedon Sep 6, 2026
Following up on this, I did some work and found a few things.
uniffi-bindgen-java 0.5.0 requires uniffi 0.32. payjoin-ffi is on 0.31.2, and 0.5.0 rejects the metadata with Unexpected metadata type code: 116. The previous release, 0.4.2, reads 0.31 metadata but generates Java that does not compile for this crate.
From there,
I backported the 0.5.0 template fixes onto the 0.31 line. I opened IronCoreLabs/uniffi-bindgen-java#68 asking about a 0.4.3 and offered to do the work. Fixes are on a fork meanwhile and I generated bindings against that fork here
A cleaner approach would be to move payjoin-ffi to uniffi 0.32, which 0.5.0 supports unmodified. That is not limited to payjoin-ffi. uniffi-bindgen-cs is pinned at v0.11.0+v0.31.0 and uniffi-dart at v0.2.1+v0.31.2, so both would need 0.32 tags first.
A third option avoids the bindgen: a hand-written JVM layer over the official Kotlin bindings, CompletableFuture rather than coroutines. I did some work here. Works, but hand-maintained, and JNA rather than FFM.
I can open a draft PR for whichever direction you prefer, or leave this if Java bindings are not wanted at present.
chavic commentedon Sep 6, 2026
@ram0verflow Thanks for taking this on. I see you added Kotlin and are wrapping it. I'd have the Kotlin in its own draft; it seems like that could ship faster...
Also, do you have or know of a project demanding Java? I'd like to keep an eye on that
ram0verflow commentedon Sep 6, 2026
@chavic Thanks! I’ll separate the Kotlin work out and get a V2 integration test working first, then put up a draft PR.
For Java, my original motivation was to experiment with integrating Payjoin V2 into Sparrow Wallet. I’m not working on Sparrow officially, It’s just a personal project/interest. I needed JVM/Java support for that, and the Kotlin work came out of exploring the cross-platform JVM side, rather than Kotlin being the original goal.
ram0verflow commentedon Sep 8, 2026
opened a pr #1869
ram0verflow commentedon Sep 13, 2026
For Java, the remaining blocker is "uniffi-bindgen-java": 0.5.x expects UniFFI 0.32, while "payjoin-ffi" is still on 0.31.x. The 0.4.x line can read the metadata but generates invalid Java for this crate without the template fixes I backported.
Before I continue, which direction would you prefer?
I’m happy to open the next PR once the preferred direction is clear.
chavic commentedon Sep 14, 2026
@ram0verflow I would patch now then upgraded later...
Now that kotlin is merged, package incoming, do you have enough for your experimental integration? I saw you already tried the layer approach 🤔
ram0verflow commentedon Sep 14, 2026
Yeah, I think the merged Kotlin/JVM package should be enough to start the Sparrow experiment.
I’ll try the integration against that first. If the Java interop turns out to be awkward or the API would benefit from first-class Java bindings, I can then open the generated-Java PR using the patched 0.4.x generator and upgrade it later with UniFFI 0.32.
ram0verflow commentedon Sep 14, 2026
I tested the merged Kotlin/JVM bindings in the Sparrow experiment and got a full BIP77 sender round trip working.
It turns out full generated Java bindings aren't necessary for this path. Almost the entire sender API is directly callable from Java, including the synchronous persistence/state-machine APIs.
The one issue I hit is the three
SenderBuilderbuild methods: theiru64/u8parameters become KotlinULong/UByte, so the JVM method names are mangled and can't be called normally from Java. I initially used reflection to prove the integration, then tested a small hand-written Kotlin shim exposingLong/Int?wrappers. With that in place, the Sparrow round trip works without reflection, and the existing Kotlin integration suite still passes.Would you prefer a small PR adding that Java-interop shim to the Kotlin/JVM bindings, or would you still rather I proceed with the patched
uniffi-bindgen-java0.4.x route?chavic commentedon Sep 14, 2026
The paths depend on whether you're blocked on the Sparrow experiment and how much work it is to rewrite if you eventually stumble on something.
I lean towards patching the Java bindings generator, even though you have something working; that would make your implementation more durable in the long run.
One thing: if it's going to take too long to fix the Java generator, and given we're more excited to see something working in Sparrow, don't treat the Java generator as a blocker to that work...
ram0verflow commentedon Sep 14, 2026
Makes sense. I'll keep the Sparrow experiment moving with the Kotlin/JVM bindings for now so the Java generator doesn't block it.
In parallel I'll continue with the patched
uniffi-bindgen-java0.4.x route for the durable Java bindings, and if that lands cleanly I can switch the Sparrow integration over later.DanGould commentedon Sep 15, 2026
@ram0verflow how much has been communicated of these plans to @craigraw? My understanding of that project is that they won't take on any new libraries, and this would be a major new library. Even large contributions from outsiders are usually rewritten by Craig from what I've seen. I think the best place for these bindings would be to demonstrate end to end experience demo of async page join as a sort of scaffold that would be replaced piece by piece in native Java. I for that reason I wouldn't want to introduce a bindings library in Rust Payjoin that we will maintain immediately, at least not until it was clear that there is demand for such a library.
The most interesting thing about this approach to me is that it's not unique. I think this would be the same type of thing that Bitcoin Core would want if it were integrated, for example: Step one is make the demo using the Payjoin dev kit, and then step two is replace each function there with native code.
ram0verflow commentedon Sep 15, 2026
Not much yet. I haven't discussed the implementation approach with Craig directly, and I'm not working on Sparrow officially this started as a personal integration experiment.
The useful result so far is that I have a real BIP77 sender round trip working through Sparrow's existing networking and signing path, so the bindings have been valuable as a scaffold for proving the flow.
It makes sense not to take on a new external bindings dependency without clear downstream demand. I can treat the Java bindings work as experimental for now and take the working Sparrow PoC to Craig/#1980 first, rather than assuming Sparrow would adopt the library as a dependency.
If the preferred Sparrow direction is native Java, the PDK-backed implementation can still serve as a reference/scaffold and be replaced piece by piece.
ram0verflow commentedon Sep 15, 2026
@DanGould I’ve taken the working sender PoC to Craig/Sparrow on #1980 and asked specifically about the dependency/native-Java direction. I’ll let that discussion determine whether the Java bindings work should go any further.