Skip to content

feat: add allowances for automatic paykit payments - #799

Draft
ovitrif wants to merge 13 commits into
feat/demo-clock-subscriptionsfrom
feat/paykit-allowances
Draft

ovitrif wants to merge 13 commits into
feat/demo-clock-subscriptionsfrom
feat/paykit-allowances

Conversation

@ovitrif

@ovitrif ovitrif commented Sep 24, 2026

Copy link
Copy Markdown
Collaborator

Depends on pubky/paykit-rs#161 (open; head 8b893f0, the top of the #158 → #159 → #160 → #161 stack). The Xcode project points at a local checkout of pubky/paykit-rs beside this one (../paykit-rs) that builds the Swift bindings and xcframework from that head, instead of the published 0.1.0-rc55 tag, so CI cannot build this branch until #161 is published.

Stacked on #798: the allowance tests prove that admission ignores the demo clock, so the branch starts from that commit and GitHub retargets this PR to master when it merges.

Android port: synonymdev/bitkit-android#1340

This PR adds allowances: a payer sets a per-payment and a monthly limit for a Paykit contact, and that contact's requests within the limits are paid without asking.

Description

  • Adds an Allowances tab to Subscriptions, with an empty state, the list and a Set Allowance sheet with per-payment and monthly USD limits, so a payer can let a contact's requests pay themselves
  • Sends the allowance over the existing private Paykit link and opens a review sheet on the receiver, who accepts or declines; either side can end an allowance from its detail sheet
  • Pays an incoming request within the limits automatically over Lightning: the request is reserved in the SDK's allowance accounting before the send and reported back afterwards, so a kill and relaunch mid-payment never pays twice
  • Leaves a request above the per-payment limit, over the monthly cap, or after an end as an ordinary Payment Request, with a Limit Reached notification for the payer and a Payment Executed notification for an automatic one; requests that arrived before the allowance stay manual
  • Attributes automatically paid activity to the contact, tags those rows "Auto-paid" in the Payments tab and shows the amount paid automatically on the allowance detail
  • Keeps Paykit state written by rc55 readable: the stored blob is probed on first launch and converted between the rc55 and allowance layouts, so an upgrade or a downgrade keeps the profile, contacts and requests, and a wallet without an allowance ledger is left untouched at launch
  • Counts allowance months from the original anchor, so the monthly cap resets on the same day each month
  • Waits for the payee's next private payment list instead of asking the payer when the previous list was used up, and holds automatic payments until the node has a usable channel, because both cases fell back to manual on regtest

Out of Scope

  • Allowance detail on the payee: "Paid automatically" counts only the payer's own journal, so the payee sees $0.00; SDK follow-up
  • Automatic payments: Lightning only, with no capacity check before choosing bolt11 and no on-chain fallback, so a payer without a usable channel falls back to a manual request
  • Failed automatic send: the request is already accepted, so it falls back to a manual Payment Request and is not retried automatically
  • Paykit dependency: stays on the local build of Expose Allowance accounting to Swift and Kotlin pubky/paykit-rs#161 until it is published

Design

Figma "Bitkit - Experimental New": Allowances empty state, Set Allowance, Allowances list. The receiver's review sheet and the detail sheet have no frame and follow the drawn film. Deliberate differences: the first tab stays "Overview", the list row's right column reads the monthly limit, and the copy typos are fixed.

Preview

Screenshots follow before review.

QA Notes

Journeys

  • new set-and-accept.xml — the offer opens on the payee by itself and both rows turn Active
  • new auto-pay-under-limit.xml — a $2 request pays itself with only a Payment Executed notification
  • new above-limit-asks.xml — a $20 request arrives as an ordinary Payment Request
  • new monthly-cap-reached.xml — the third $4 request on a $10 cap raises Limit Reached and asks
  • new end-stops-auto-pay.xml — after either side ends it, the next request asks
  • new restart-never-pays-twice.xml — a kill right after the hand-off never leads to a second payment

Manual Tests

  • 1. regression: install the rc55 build, create a profile and a contact, then install this build → Profile and Contacts: intact, log reads "Resolved the stored Paykit state layout as 'RC55'"
  • 2. restart-never-pays-twice.xml and the payee half of end-stops-auto-pay.xml on two simulators: the source run drove them on Android only; iOS covered set, accept, auto-pay, ask above the limit and end by the payer

Automated Checks

  • added PaykitAllowanceTests.swift — limits, terms, capacity, month anchoring and admission decisions, including that admission ignores the demo clock
  • added PaykitAllowanceExecutorTests.swift — reserve, hand-off, outcome recording, deferral, channel readiness and restart recovery of automatic payments
  • updated PaykitPaymentProofServiceTests.swift — payment proofs carry the allowance id
  • updated PaykitPaymentRequestServiceTests.swift — pre-allowance requests stay manual
  • ran xcodebuild test -only-testing:BitkitTests on an iPhone simulator against the local Paykit build — 1,411 tests pass, 12 skipped; CI cannot resolve the Paykit build until Expose Allowance accounting to Swift and Kotlin pubky/paykit-rs#161 is published

@ovitrif ovitrif self-assigned this Sep 24, 2026
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.

1 participant