Skip to content

Fix/payment request cancellation lifecycle - #27

Merged
dzdidi merged 9 commits into
feat/lock-payment-drainingfrom
fix/payment-request-cancellation-lifecycle
Oct 1, 2026
Merged

dzdidi merged 9 commits into
feat/lock-payment-drainingfrom
fix/payment-request-cancellation-lifecycle

Conversation

@dzdidi

@dzdidi dzdidi commented Sep 25, 2026 •

Copy link
Copy Markdown
Collaborator

fix: make Payment Request status the canonical Locks lifecycle contract

Prerequisite and scope

This PR is stacked on latest #3 (feat/lock-payment-draining) at 8d86af67f0a3639284c038bf67bc1faa02688ce0 through explicit merge commit 44ed37886122a201b2d5f73c9578ecabb48f72bd. It preserves both prior #27 head 9a266ef2df6a46c115a99e696fefdb4295bf814b and latest #3 head as direct parents; no rebase or force push was used. Keep this PR based on feat/lock-payment-draining until #3 merges.

PR #3 owns persisted invoice deadlines, immutable Paykit Rust rc56 request terms, replay validation, durable lifecycle projection, required-peer/path intake freshness, Creator-local SDK mutation serialization, same-fence target revalidation, payment observation, and destructive staging-only migration 0007.

This follow-up finalizes one canonical lifecycle boundary for Locks:

  • signed POST /payment-requests/status returns separate request_state and payment_state axes;
  • response retains invoice_created_at, exact protocol/persisted payment_deadline, confirmations, and amount_matched;
  • rejected, canceled, and proposal_expired remain distinct request states;
  • accepted/proof-submitted requests with payment_state: expired remain distinct payment-deadline expiry;
  • rejected/canceled/expired requests retain factual observed-funds fields;
  • RecoveryRequired returns typed 503 unavailable; InvalidConflict returns typed 409 conflict;
  • missing, partial, unrelated, or failed required-target intake returns unavailable rather than stale lifecycle state;
  • exact terminal response fixtures for Locks are published under docs/fixtures/payment-request-status/ and byte-checked against serializer.

Legacy signed POST /transactions/status remains Bitcoin-only compatibility with closed labels undetected, detected, and confirmed. Payment Request lifecycle no longer widens that old enum.

Dependency and version

  • paykit-lib / paykit-sdk: exact released rc56 revision 24162ebbcc703251d8038f2e176d1f4cfb117c6a
  • Paykit Server workspace packages: 0.1.0-rc6
  • No tag, release, merge, deployment, or migration was performed.

TDD and verification

RED evidence:

  • legacy rejected/canceled projection test failed because old code returned cancelled instead of factual confirmed;
  • fixture contract test failed to compile until exact Locks JSON fixtures existed.

Reviewer-remediation regression locks passed immediately against existing behavior: PostgreSQL now explicitly proves rejected + undetected retains undetected/0/false, canceled is independently selected as sole winning lifecycle state, and rejected + confirmed remains covered.

Passed locally on exact approved restacked candidate commit 44ed37886122a201b2d5f73c9578ecabb48f72bd (tree af6b0350a789ffc80aad9a0697195b362d108cef):

cargo fmt --all -- --check
cargo check --locked --workspace --all-targets
cargo test --locked -p paykit-server -- --test-threads=1
cargo test --locked -p paykit-server --example paykit-companion-auth -- --test-threads=1
cargo test --locked --workspace --no-run
TEST_DATABASE_URL=postgres://postgres:***@127.0.0.1:35591/postgres cargo test --locked -p paykit-server-e2e -- --test-threads=1
cargo clippy --locked --workspace --all-targets --all-features -- -D warnings
RUSTDOCFLAGS="-D warnings" cargo doc --locked --workspace --no-deps --all-features
git diff --check

PostgreSQL 16 full E2E passed with zero matching temporary databases before and after. Two live external-adapter smoke tests remain ignored by design. Original exact-tree closure review requested the two PostgreSQL status regressions added in 9a266ef; focused independent review approved that remediation. Second independent review reproduced and approved exact restacked commit 44ed37886122a201b2d5f73c9578ecabb48f72bd on latest #3 rc56-tag head 8d86af67f0a3639284c038bf67bc1faa02688ce0, including full Server, companion, PostgreSQL E2E, formatting, locked check, Clippy, rustdoc, dependency, migration, ancestry, and clean-tree gates. GitHub CI results below must be read against exact head 44ed37886122a201b2d5f73c9578ecabb48f72bd.

Out of scope

  • Locks repository changes;
  • graceful deletion orchestration;
  • refunds or late-payment entitlement restoration;
  • recurring scheduling;
  • compatibility aliases or dual lifecycle endpoints;
  • merge, tag, release, deployment, or force push.

Signed-off-by: dzdidi <dzdidi@protonmail.com>
Signed-off-by: dzdidi <dzdidi@protonmail.com>
@dzdidi
dzdidi changed the base branch from master to feat/lock-payment-draining September 27, 2026 17:17

@ben-kaufman ben-kaufman left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the changes relative to #3. No additional material issues found in this PR. The inherited Creator lifecycle refresh issue is tracked on #3 and still needs fixing in the stack.

@dzdidi
dzdidi merged commit cf41360 into feat/lock-payment-draining Oct 1, 2026
7 of 8 checks passed
@dzdidi
dzdidi deleted the fix/payment-request-cancellation-lifecycle branch October 1, 2026 21:55
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.

2 participants