
How to Implement PayJoin Transactions with PayJoin Dev Kit
A technical guide to integrating PayJoin (P2EP) into Bitcoin wallets using the PayJoin Dev Kit, covering setup, protocol flow, and production considerations.
Implementing PayJoin used to require developers to wrestle with complex protocol logic, HTTP endpoint management, and careful UTXO handling. The PayJoin Dev Kit (PDK) has changed that equation significantly. According to Dan Gould, the technical lead on PDK, developers can now implement full PayJoin support in under 2,000 lines of code.
This guide walks through what's involved in integrating PayJoin into a Bitcoin wallet or exchange, using PDK as the foundation.
Why PayJoin Matters for Privacy
Before diving into implementation, it's worth understanding what PayJoin actually accomplishes. Standard Bitcoin transactions make it easy for chain analysis to assume all inputs belong to the same entity. This "common-input-ownership heuristic" is one of the primary tools surveillance companies use to cluster addresses.
PayJoin breaks this assumption by having both sender and receiver contribute inputs to a single transaction. When a PayJoin transaction hits the blockchain, it looks like a normal payment, but the inputs come from two different parties. This creates uncertainty for any observer trying to trace fund flows.
There's also a practical benefit: receivers can consolidate UTXOs for free during the transaction, effectively batching their consolidation with an incoming payment.
Understanding the Protocol Versions
PDK supports two protocol versions, and choosing the right one depends on your use case.
BIP78 (PayJoin v1)
The original specification requires the receiver to present a BIP21 URI containing a payjoin endpoint parameter. The sender and receiver then negotiate through two iterations before broadcasting the final transaction. The catch: both parties must be online simultaneously.
For point-of-sale scenarios where both parties are present, v1 works fine. For asynchronous payments, it's a significant limitation.
BIP77 (PayJoin v2)
The newer asynchronous protocol uses Oblivious HTTP (OHTTP) to protect metadata, allowing sender and receiver to coordinate without being online at the same time. Bull Bitcoin shipped v2 support in December 2024, followed by Cake Wallet in May 2025. BDK-CLI's v3.0.0 release in early 2026 included PayJoin support as well.
For most new implementations in 2026, v2 is the recommended path.
Getting Started with PDK
PDK is a Rust-based reference implementation with FFI bindings for Python, Dart, Swift, Kotlin, JavaScript, and C#. This means you can integrate it regardless of your wallet's primary language.
Installation
For Rust projects, add the crate to your `Cargo.toml`:
```toml
[dependencies]
payjoin = "1.0-rc.2"
```
For other languages, PDK provides stable FFI bindings. The specific installation method varies by platform; consult the PDK documentation for your target language.
Core Concepts
PDK's architecture centers on a state machine that handles the protocol flow. The key components are:
- Sender: Initiates the PayJoin request by creating an original PSBT and sending it to the receiver's endpoint
- Receiver: Contributes inputs, modifies the transaction, and returns an updated PSBT
- Persistence: The state machine supports persistence, critical for v2's asynchronous flow
Implementing the Receiver Side
Most integration work happens on the receiver side, since receivers need to:
- Generate and expose a PayJoin endpoint
- Select appropriate UTXOs to contribute
- Validate incoming PSBTs against BIP78's receiver checklist
- Sign and return the modified transaction
Here's a simplified Rust example of initializing a receiver session:
```rust
use payjoin::receive::v2::Receiver;
let receiver = Receiver::new(
bitcoin_address,
directory_url,
ohttp_keys,
expiry,
)?;
// Generate the BIP21 URI to share with senders
let uri = receiver.pj_uri_builder()
.amount(amount)
.build();
```
The receiver then polls for incoming requests and processes them according to the protocol.
UTXO Selection Strategy
When contributing inputs, the receiver needs to balance privacy gains against practical concerns. Contributing UTXOs of similar value to the payment amount maximizes ambiguity. Contributing dust or very large UTXOs can actually reduce privacy by making the PayJoin structure more obvious.
Implementing the Sender Side
Senders have a somewhat simpler task:
- Detect PayJoin support in the recipient's BIP21 URI
- Create an original PSBT as if making a normal payment
- Send the PSBT to the receiver's endpoint
- Validate the returned PSBT (this is critical)
- Sign and broadcast
The sender validation step is essential. BIP78 specifies a detailed checklist to ensure the receiver hasn't manipulated the transaction maliciously. PDK handles most of this validation automatically, but developers should understand what's being checked.
Production Considerations
Error Handling
PayJoin adds failure modes that don't exist in standard transactions. The receiver might be offline (v1), the endpoint might be unreachable, or validation might fail. Your wallet should gracefully fall back to standard transactions when PayJoin fails.
Fee Handling
BIP78 specifies rules for fee contribution. In general, the receiver can reduce their output to contribute to fees, but within specified limits. PDK enforces these rules, but you should understand them when debugging fee-related issues.
Testing Against Real Implementations
Before shipping, test your implementation against other PayJoin-compatible wallets. Bull Bitcoin, Cake Wallet, and BTCPayServer (with the upcoming plugin) all provide real-world testing targets.
Current Ecosystem Status
As of May 2026, PayJoin adoption is accelerating. Beyond the production deployments mentioned earlier, pull requests are open for Liana, LDK-Node, Boltz, and LEXE integration. The Payjoin Foundation, launched in August 2025 with OpenSats funding, continues to support open-source development.
The rust-payjoin crate's progression from version 0.11 to 1.0-rc.2 reflects growing stability. The addition of persistence in the core state machine and stable FFI across all binding targets makes production deployment significantly more practical than it was even a year ago.
Trade-offs to Consider
PayJoin isn't a privacy silver bullet. It requires cooperation, meaning it only works when the receiver actively participates. It also requires the receiver to have UTXOs available to contribute, which may not always be the case.
For exchanges and high-volume merchants, maintaining a pool of appropriately-sized UTXOs for PayJoin contributions requires operational planning.
That said, the privacy and fee benefits are substantial enough that major wallets have found the implementation effort worthwhile. For developers building Bitcoin products in 2026, PDK makes that effort considerably more manageable than rolling a custom implementation.