Skip to content

feat: support quoted lock prices and per-rail payment verification - #75

Merged
ben-kaufman merged 2 commits into
masterfrom
feat/quoted-lock-payments
Oct 12, 2026
Merged

ben-kaufman merged 2 commits into
masterfrom
feat/quoted-lock-payments

Conversation

@ben-kaufman

@ben-kaufman ben-kaufman commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Support BTC- or USD-priced locks using Paykit Server's fixed payment quotes, and verify each accepted payment method independently before unlocking content. The authenticated setup-status API and SDK now take the requested denomination; creator and reader examples retain that denomination throughout checkout.

Bitcoin-only setup remains the default. With Paykit Server's [usdt] configuration omitted, neither BTC nor USD pricing requests a USDT address, and both are payable in Bitcoin. This matches Bitkit iOS #887 and Android #1437. Optional Arbitrum USDT settlement requires an explicitly configured server and compatible wallets.

The matching client changes are in Pubky App #2782, which pins a built SDK archive from GitHub without an npm release.

Contract and risk impact

  • Public API or SDK contract: paykitSetupStatus(asset) and /creator/paykit/setup-status?asset=BTC|USD|USDT require the denomination. Deploy the matching SDK/client and Locks server together.
  • Payment or entitlement behavior: one rail must cover the full invoice on time. Bitcoin retains the configured confirmation threshold; USDT requires verified Arbitrum inclusion. Partial payments across rails do not combine, and terminal request states still deny access.
  • Runtime: the local demo pins merged Paykit Server #47 at 4cc8e7b836dba28446a8d821f593ee40bb4acbeb and its matching Paykit rc71 sources.

No storage migrations or new address-sharing permissions.

Verification

  • cargo fmt --check, workspace Clippy with all targets/features and -D warnings, and git diff --check: passed.
  • Core/SDK/server unit suites: 398 passed; service unit suite: 213 passed (43 PostgreSQL tests excluded).
  • Creator publishing, retrieval, production publishing, and creator-authority HTTP tests: 14 passed, including BTC and USDT status fixtures for BTC/USD prices.
  • JS/WASM: 58 native tests, wasm32 check, release build, generated API and demo smoke checks passed. The example smoke check passed in Linux Docker because it requires /usr/bin/flock.
  • JS syntax, denomination/rounding/range checks, Compose smoke, setup/connection contract checks against the pinned server, and staging/reader/Pubky example tests: passed.

These are local fixture tests, not live payment acceptance. Full PostgreSQL and live Bitkit checkout testing remain for CI/staging.

Documentation

API, SDK, runtime, payment-boundary ADR, and demo guides describe price denomination, server-owned conversion, independent settlement checks, and Bitcoin-only defaults.

Checklist

  • Focused and self-reviewed.
  • Payment, setup, and amount-validation coverage updated.
  • No credentials, private identities, wallet data, or local runtime state included.
  • Formatting and lint checks pass.

@piotr-iohk piotr-iohk left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

QA review

Scope: full PR diff against merge base f0cfc96c, at be3f1b4. Payment-to-entitlement verification, denomination API/SDK changes, examples, and pinned Paykit Server contracts were checked. The related Pubky App check covered its SDK/setup signature only.

1 actionable finding — LOW: Keep satoshi units visible in the creator price input.

The example creator pages replace “Amount (sats)” with “Bitcoin Amount”, but BTC input still represents integer satoshis. The currency-change handler restores the same ambiguous label. Source analysis plus an executed helper check: entering 1 produces an atomic BTC amount of 1 (one sat), not one BTC; fractional BTC input is rejected. Keep (sats) or an adjacent sats unit visible in both forms and when switching back to BTC. Changed label, amount conversion. No browser or payment reproduction is claimed.

Validation: 712 local Rust/native-binding/HTTP fixture tests passed across the final relevant runs, plus JS denomination, creator/reader staging-mode, reader-load-state, Pubky helper and syntax checks. Format/whitespace, wasm32 compilation and pinned setup/connection contract checks passed. The oversized-response test initially failed, then passed on isolation/final head run; the same failure reproduced at the base, so it is not identified as introduced here. Matching-head CI Checks passed, including broader PostgreSQL coverage.

The per-rail access guards require full amount and timely payment on the same rail, retain Bitcoin confirmation policy and terminal-state denial, and match the pinned server's Arbitrum-inclusion contract. No additional actionable payment-verification finding was identified.

Remaining staging validation: Run live BTC- and USD-priced Locks checkouts with Bitcoin-only setup, using the matching server/client. Verify the canonical denomination, exact wallet quote/output, correct-reader entitlement and guarded-content retrieval. Our earlier Bitkit fixed-price tests did not test this Locks access path. Optional USDT needs its explicit configuration and a compatible wallet. Full browser checkout/live settlement was not run; generated-JS/Compose smoke was not completed locally because the generated WASM package was absent.

@ben-kaufman

Copy link
Copy Markdown
Contributor Author

Fixed in b3a2fac: both creator forms now show “Bitcoin Amount (sats)”, and switching back from USD restores that label. I also reconciled the branch with current master, retaining the existing Paykit pin.

Validation: cargo fmt --check, strict workspace Clippy, JS syntax checks, payment-amount tests and the Paykit Compose smoke check all pass. The paired staging payment/unlock check remains pending.

@dzdidi dzdidi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed exact head b3a2facaed43754924a6bcf77553e77090a911df against current base bcadeb6df349fdb0719525018c5bc9e87c7cc87f.

No blocking correctness, security, persistence, API, or documentation findings.

Payment review:

  • Per-rail observations remain separate; one rail must independently be amount-matched and on time.
  • Bitcoin retains configured confirmation policy. USDT requires verified Arbitrum inclusion and does not borrow Bitcoin confirmations or combine partial amounts across rails.
  • Rejection, cancellation, proposal expiry, and payment-deadline expiry deny entitlement before settlement evidence is considered.
  • Malformed/unknown/oversized successful status responses fail closed; transport/body-read/non-200 failures remain retryable, with 409 preserved as conflict.
  • Signed status lookup remains creator + Bundle ID scoped. Timestamps remain closed and strictly ordered.

Contract/API review:

  • Required setup denomination is validated as closed BTC | USD | USDT across HTTP, Rust SDK, and JS/WASM.
  • Authenticated creator remains session-derived; caller cannot inject creator identity.
  • Current Locks fixtures are byte-identical to pinned Paykit Server revision 4cc8e7b836dba28446a8d821f593ee40bb4acbeb for all five payment-status cases.
  • Prior sats-label finding is fixed in both creator forms and denomination-switch behavior.

Verification evidence:

  • Exact-head GitHub Checks: success, including format, Clippy, unit, E2E, JS examples, setup contract, JS/WASM, PostgreSQL, integration, and whitespace gates.
  • Exact-head GitHub RustSec: success.
  • Local git diff --check bcadeb6...HEAD: passed.
  • Provider fixture byte comparisons: 5/5 matched.

Residual validation: no live BTC/USD/USDT wallet settlement was performed in this review. Staging paired checkout/unlock remains appropriate, especially optional USDT configuration. This does not block merge of fixture- and contract-covered code.

@ben-kaufman
ben-kaufman merged commit 41096a5 into master Oct 12, 2026
2 checks passed
@ben-kaufman
ben-kaufman deleted the feat/quoted-lock-payments branch October 12, 2026 14:00
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.

4 participants