Repository navigation
Description
Each language binding reimplements InMemoryPersisters for the receiver and sender in tests, we could do a big code deletion there now that it's exposed.
Originally posted by @spacebear21 in #1528 (comment)
Fed this comment into my meta context for #1528 to produce this issue which should be a sufficient prompt for the run that closes it. I sanity checked the below prompt and find the direction, acceptable. Per-language commits were not my idea but sound sane and easier to review without conflating one problem for another.
In terms of sequencing this makes sense to do before payjoin-ffi-1.0, before someone hand-rolls another JsonSenderSessionpersister or if someone ~junior is exceedingly confident confident they can earn the glory of getting it merged it in one-shot review-then-merge cycle (feeling free to ask clarifying questions before requesting review).
PR #1528 promoted payjoin::persist::InMemoryPersister to public, unconditional Rust
API and re-exported it from payjoin-ffi (payjoin-ffi/src/lib.rs:14). However,
payjoin-ffi exposes session persistence to bindings via the
JsonReceiverSessionPersister / JsonSenderSessionPersister traits (defined in
payjoin-ffi/src/receive/mod.rs and payjoin-ffi/src/send/mod.rs with
#[uniffi::export(with_foreign)]). Bindings implement those traits in their own
language, and each one currently reimplements an in-memory event log:
- Dart —
InMemoryReceiverPersister,InMemorySenderPersisterin
payjoin-ffi/dart/test/test_payjoin_integration_test.dart - Python —
InMemoryReceiverSessionEventLog,InMemorySenderPersisterin
payjoin-ffi/python/test/test_payjoin_integration_test.py - C# —
InMemoryReceiverPersister,InMemorySenderPersisterin
payjoin-ffi/csharp/IntegrationTests.cs
These are functionally identical: append events to a list, return the list on load,
set a closed flag.
Proposed work (stage)
-
payjoin-ffi— add uniffi-exposed in-memory persister types that implement
JsonReceiverSessionPersister,JsonReceiverSessionPersisterAsync,
JsonSenderSessionPersister,JsonSenderSessionPersisterAsyncand wrap the Rust
payjoin::persist::InMemoryPersister. Suggested names:InMemoryReceiverPersister,
InMemoryReceiverPersisterAsync,InMemorySenderPersister,
InMemorySenderPersisterAsync. Gate them behind the existing
payjoin-ffi/_test-utilsfeature so they ship to binding test scripts but don't bloat
the production FFI surface. -
Per-language cleanup PRs (one each, mergeable independently after step 1):
- Dart — delete the two test classes, regenerate uniffi bindings, swap call sites
- Python — same
- C# — same
Each binding PR is pure deletion + regen + import-swap once step 1 lands. Mechanical.
Why split
Step 1 needs core/FFI review attention (uniffi annotations, feature gating, async
variant). Steps 2a/b/c need per-language test verification (Dart hooks, Python uv +
bindgen, C# dotnet + bindgen). Splitting also lets the binding owners review their
own surface.
Out of scope
- Deduping anything else across bindings (this issue is scoped strictly to the
InMemoryPersisterreimplementations spacebear flagged) - Production code changes — the in-memory variant is for tests/examples/prototyping only
Activity
spacebear21 commentedon May 13, 2026
Superseded by #1553