Skip to content

feat: accept allowance offers automatically on the allowee's wallet - #1385

Open
ovitrif wants to merge 19 commits into
feat/paykit-allowancesfrom
feat/allowance-auto-accept
Open

ovitrif wants to merge 19 commits into
feat/paykit-allowancesfrom
feat/allowance-auto-accept

Conversation

@ovitrif

@ovitrif ovitrif commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #1353
Twin: synonymdev/bitkit-ios#841
Stacked on #1340
Refs: #1351

This PR makes the allowee's wallet accept an allowance the allower offers, so the allowee taps nothing and the allowance is active as soon as the offer arrives.

Description

  • Adds automatic acceptance of a received allowance proposal that makes this wallet the allowee, so the allower's decision alone activates the allowance.
  • Adds an "Allowance Added" notification, "{name} set up an allowance for you", with a toast fallback, so the allowee learns about the new allowance.
  • Keeps the review sheet with Accept and Decline for an allowee's ask, because there the money is this wallet's.
  • Retries a failed acceptance on the next refresh, and never touches a proposal this wallet sent or one already answered.
  • Accepts only the offers it checked, and stops without announcing once the profile or the refresh it started for is gone.

Out of Scope

Design

N/A — no design available.

Preview

Payer: Set Allowance Payer: waiting for an answer Payer: Active
Allowee: Allowance Added notification Allowee: notification shade Allowee: row Active

ba1353-set-and-accept-1b61baa-2x.mp4

QA Notes

Journeys

  • updated set-and-accept.xml — the allowee's wallet accepts the offer with no review sheet, posts "Allowance Added", and both rows turn Active
    • Driven again on two fresh staging wallets at 1b61baa, Paykit 0.1.0, recorded side by side above (payer left, allowee right, 2x): no review sheet opened, "Allowance Added" posted once, the payer's row read "Waiting for an answer" and then "Active · $5 a payment", and the allowee's row read "Active · $5 a payment". The proposal went out 11 s after Save and the allowee's wallet accepted it and posted "Allowance Added" 17 s later (28 s from Save). The later head ba1c9bc only adds base changes outside the offer accept.
    • Journey drift, not edited here: Choose Contact shows "Add a contact first" for about 3 minutes after the contacts are saved, until Paykit discovery finishes (about 2.5 min at 1b61baa).
  • updated pin-gates-allowance-changes.xml — with a payment PIN on the payer, the payee's wallet still accepts the offer by itself: "Allowance Added" arrives with no review sheet, no tap and no PIN
    • Not driven on a device in this PR; the PIN gate itself comes from the base.

Manual Tests

N/A

Automated Checks

  • added PaykitAllowanceTest.kt — only a received proposal from the allower counts as an offer
  • added PaykitAllowanceRepoTest.kt — an allower's offer is accepted without the sheet and not announced twice; a failed accept is retried and announced once; an allowee's ask still goes to the sheet; sent and answered proposals are never accepted; an ask that joins an offer's entry mid-accept is never accepted; nothing is accepted or announced once the session or identity changed
  • updated AppViewModelSendFlowTest.kt — the send flow stubs the new acceptance call
  • ran ./gradlew testDevDebugUnitTest detekt at dea7822 (Paykit 0.1.0, merged with the updated feat/paykit-allowances): unit tests pass and detekt is clean

@ovitrif ovitrif self-assigned this Sep 30, 2026
@ovitrif

ovitrif commented Oct 1, 2026

Copy link
Copy Markdown
Collaborator Author

Pushed 528fb7b: merged the updated feat/paykit-allowances (Paykit rc59, allowances bound to the contact identity) into this branch.

  • The auto-accept code needed no change, since it already addresses an allowance by counterparty and allowance id.
  • The new tests now use the identity-bound acceptAllowance(counterparty, allowanceId) and processOutboundPrivateMessages(counterparty) calls (a3861ce).
  • The rule is unchanged: only a received proposal that makes this wallet the allowee is accepted automatically; an ask from the allowee still opens the review sheet.
  • testDevDebugUnitTest (3,220 tests) and detekt pass.
  • Device run of set-and-accept.xml on two emulators is still pending; screenshots and a recording follow in the description.

@ovitrif

ovitrif commented Oct 1, 2026

Copy link
Copy Markdown
Collaborator Author

Added the device result and media to the description.

  • set-and-accept.xml passes on two fresh staging wallets with the a3861ce build (Paykit rc59): the allowee accepted with no tap and no review sheet, "Allowance Added" posted once, and both rows turned Active.
  • Save to the allowee's row took about 3 min 16 s on a heavily loaded host.
  • The build was a3861ce; head 528fb7b only adds a base merge that touches PaykitAllowanceRepo.kt (one line) and PaykitPaymentRequestRepo.kt.
  • Preview now has the payer and allowee screenshots plus the recording.
  • Journey drift is noted under QA Notes and the journey is not edited here.

@ovitrif

ovitrif commented Oct 2, 2026

Copy link
Copy Markdown
Collaborator Author

Pushed e7a8bd3: merged the updated feat/paykit-allowances (b929cce, Ben's newer shared-runtime sync and Paykit session retry) into this branch.

  • The merge was clean and changes none of this PR's code.
  • testDevDebugUnitTest (3,227 tests) and detekt pass on the merged head.
  • The device run was on the a3861ce build, before this merge, and has not been repeated.

@jvsena42
jvsena42 added this pull request to stack #1415 October 2, 2026 11:50
@ovitrif
ovitrif removed this pull request from stack #1415 October 5, 2026 15:39
@ovitrif

ovitrif commented Oct 19, 2026

Copy link
Copy Markdown
Collaborator Author

Pushed df5105a: merged the updated feat/paykit-allowances (now synced with master, Paykit 0.1.0) into this branch.

  • AppViewModel.kt: the base now runs allowance payments inside a payment submission (processAllowanceRequests()). The offer accept stays just before it, outside that gate, because accepting an allowance sends no payment.
  • PaykitAllowanceRepoTest.kt: Paykit 0.1.0's acceptAllowance takes the proposal's stream item id. The tests now stub and verify the three-argument call. Auto-accept passes the id through the existing accept path, so the app code needed no change.
  • The rule is unchanged: only a received proposal that makes this wallet the allowee is accepted automatically; an ask from the allowee still opens the review sheet.
  • testDevDebugUnitTest and detekt pass on the merged head.
  • A device rerun of set-and-accept.xml on this head follows, with the time from proposal to the payee's Active row in QA Notes.

@ovitrif ovitrif added this to the 3.1.0 milestone Oct 19, 2026
…t/allowance-auto-accept

# Conflicts:
#	journeys/allowances/README.md
…t/allowance-auto-accept

# Conflicts:
#	app/src/test/java/to/bitkit/repositories/PaykitAllowanceRepoTest.kt
@ovitrif

ovitrif commented Oct 19, 2026

Copy link
Copy Markdown
Collaborator Author

Pushed 211d4c2: merged the updated feat/paykit-allowances (PIN gate for the allower, bounded-limits fix, master sync) into this branch.

  • pin-gates-allowance-changes.xml: the new journey expected the payee to open the review sheet and tap Accept. On this branch the payee's wallet accepts the offer by itself, so the journey now checks that "Allowance Added" arrives with no review sheet, no tap and no PIN, and that a cancelled PIN on the payer sends nothing.
  • journeys/allowances/README.md: kept the base's PIN paragraph and this branch's auto-accept paragraph.
  • PaykitAllowanceRepoTest.kt: kept the auto-accept tests next to the base's new test for a proposal without limits.
  • The PIN gate covers only the allower's own Save, End and Accept; the allowee's automatic accept goes through the repository and never asks, as the journey README states.
  • testDevDebugUnitTest and detekt pass on the merged head.
  • set-and-accept.xml passed on df5105a (proposal to the payee's Active row in about 20 s, recording in the description); a rerun on this head follows.

@ovitrif

ovitrif commented Oct 19, 2026

Copy link
Copy Markdown
Collaborator Author

Pushed 1b61baa: merged the updated feat/paykit-allowances into this branch.

  • The base now pays allowance requests automatically only over Lightning; on-chain requests stay manual. The automatic accept sends no payment, so it is unchanged.
  • The allowee's row now shows the per-payment limit in whole dollars like the payer's ("Active · $5 a payment"), from the base.
  • No conflicts; testDevDebugUnitTest and detekt pass on the merged head.
  • The set-and-accept.xml rerun on this head follows.

@ovitrif

ovitrif commented Oct 19, 2026

Copy link
Copy Markdown
Collaborator Author

Pushed ba1c9bc: merged the updated feat/paykit-allowances (master sync, a routing-fee cap for automatic payments, and dropping allowance terms whose limits cannot be read) into this branch.

  • No conflicts. The new allowance fixes apply only when this wallet pays, and the master changes (USDT screens, timed sheets, send amounts) leave the offer accept in AppViewModel.kt as it was.
  • testDevDebugUnitTest and detekt pass on the merged head.
  • set-and-accept.xml passed on 1b61baa with both rows reading "Active · $5 a payment" (recording and timing in the description). This merge adds nothing on that path, so it was not driven again.

@ovitrif
ovitrif marked this pull request as ready for review October 19, 2026 20:10
@ovitrif
ovitrif requested review from a team and ben-kaufman October 19, 2026 20:10
@ovitrif
ovitrif requested review from pwltr and removed request for a team October 19, 2026 20:10
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-19T20:16:01.454626Z ba1c9bc Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@greptile-apps

greptile-apps Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 4/5

[Medium impact] The changes affect the Paykit allowance workflow between linked contacts.

The PR appears safe to merge, with a non-blocking issue in fallback announcements for multiple offers.

Findings

  1. P2 Earlier allowance notices disappear ▶

Summary

Automatically accepts received allowance offers when this wallet is the receiver.

  • The allowee’s wallet accepts an allower’s offer without a tap.
  • The allowee gets an “Allowance Added” notice after an offer is accepted.

English-only strings were explicitly declared out of scope by ovitrif until the next translation sync.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Refresh allowance records] --> B{Received unanswered proposal?}
    B -->|No| C[Leave unchanged]
    B -->|Yes| D{This wallet receives the allowance?}
    D -->|Yes| E[Accept automatically]
    E --> F{Acceptance succeeded?}
    F -->|Yes| G[Post notification or toast]
    F -->|No| H[Retry on a later refresh]
    D -->|No| I[Show review with Accept and Decline]
Loading

Reviews (1) · Last reviewed commit: "Merge remote-tracking branch 'origin/fea..." · Reviewed by Greptile

Comment thread app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ba1c9bc209

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread app/src/main/java/to/bitkit/repositories/PaykitAllowanceRepo.kt
Comment thread app/src/main/java/to/bitkit/repositories/PaykitAllowanceRepo.kt Outdated
@github-actions

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Regtest APK

Built from dea7822 (run).

Download bitkit-dev-debug universal APK (expires in 30 days).

@talosmachina talosmachina 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.

No findings. The allowee's wallet now accepts a received proposal where the proposer took the allower role, posts "Allowance Added", and keeps the review sheet for an allowee's ask. Reviewed ba1c9bc, full tier, reasoned from the code and CI: on this track the branch is not built here.

What I checked, and 5 candidates I ruled out

Read: PaykitAllowance.kt, PaykitAllowanceRepo.kt (lifecycle, accept/respond, presentation, publish), AppViewModel.kt around refreshIncomingPaykitPaymentRequests and presentNextIncomingPaykitPaymentRequest, the new tests, and the twin bitkit-ios#841 for parity.
Call sites traced: acceptOffersFromAllowers (one caller, after paykitAllowanceRepo.refresh()), proposalForPresentation, isAnswerable (the Subscriptions row tap), every refreshIncomingPaykitPaymentRequests caller (10 s poll, foreground, stored refresh).
CI: build with testDevDebugUnitTest, lint and detekt green; e2e shards still running at review time.

Ruled out

  • Double accept from two overlapping refreshes: the isAcceptingOffers CAS lets one run through, and a run that slips past it on a stale snapshot still goes through respond, which re-reads entry(entryId) and throws Unavailable once the record is no longer answerable, so acceptAllowance is not called twice and nothing is announced twice.
  • Mixed entry (an offer and an ask in one group): excluded from auto-accept by the all { isOfferFromAllower } check and still presented by proposalForPresentation, so the user decides the ask.
  • Unbounded terms auto-accepted: PaykitAllowance.from drops unbounded terms only for the allower, but on an offer this wallet is the allowee and pays nothing, so accepting one exposes no funds.
  • Accept during profile deletion: unlike iOS, Android passes no canContinue into the accept loop, so a deletion that starts mid-poll does not stop it; processAllowanceRequests right after it has the same shape on the base branch, so this is not new to this diff.
  • Identity switch mid-accept, row tap opening Review for an offer, toasts replacing each other: already raised inline by other reviewers on this head; not repeated.

Merge confidence: 4/5, the e2e shards had not finished and pin-gates-allowance-changes.xml was not driven on a device.

@jvsena42 jvsena42 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Code scan: no HIGH/MEDIUM. One LOW is inline, with a failing test.

Checked and clean:

  • This wallet never pays an offer. isOfferFromAllower requires role == ALLOWEE from the SDK's localRole on a received proposal, and coversRequest pays only on isAllower.
  • Removed contacts. removeContact blocks the peer, and list_allowances drops blocked peers.
  • Cancellation. isAcceptingOffers resets in finally, including on cancellation.
  • No shared progress flag, so the isWorking issue from synonymdev/bitkit-ios#841 doesn't apply.
  • Identity switch. An identity-A offer isn't accepted under B, because respond looks it up in B's entries and fails Unavailable. Announcing A's already-accepted offers in B's profile is codex's open thread at :252, so I'm not repeating it. The iOS canContinue fence (c5ac982) is still worth porting.

set-and-accept.xml and pin-gates-allowance-changes.xml match iOS #841 apart from ids.

Perf (repeated work): acceptOffersFromAllowers runs on every request refresh, including the 10 s poll. An offer whose accept keeps failing retries every poll, costing one SDK call and a warn line each time.

Device gate (partial): not run yet. Pending: the two allowance journeys, cross-device with #841.

Comment thread app/src/main/java/to/bitkit/repositories/PaykitAllowanceRepo.kt Outdated
@ovitrif

ovitrif commented Oct 19, 2026

Copy link
Copy Markdown
Collaborator Author

Pushed 6d5010f: the automatic offer accept now answers only what it checked, and only while its session lasts.

  • PaykitAllowanceRepo.kt: the accept answers the offer entry it checked rather than re-reading the entry, so an ask that joins the same entry mid-accept is never answered with this wallet as the payer.
  • PaykitAllowanceRepo.kt: acceptOffersFromAllowers(canContinue) stops, and returns nothing to announce, once the identity it started with changes or the refresh's session check fails; respond records presented proposals only for the identity it started with. This matches feat: accept allowance offers automatically on the allowee's wallet bitkit-ios#841.
  • AppViewModel.kt: the refresh passes its own check (no profile deletion in progress, polling not stopped) into the accept.
  • PaykitAllowanceRepoTest.kt: covers the mid-accept ask, a paused or ended session, an identity change mid-accept, and a failed accept retried and announced once.
  • testDevDebugUnitTest and detekt pass.

@ovitrif
ovitrif requested a review from jvsena42 October 20, 2026 04:54

@pwltr pwltr 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 6d5010f. I ran the 51 targeted allowance unit tests, all passing. On two Android emulators, I verified automatic offer acceptance and notifications, PIN confirmation/cancellation when saving and ending allowances, and a $2 regtest automatic payment with matching receipts, history and allowance totals.

One observation: the automatic-payment success notification appeared roughly 40 seconds after sending the request, with a pending-request bell visible during processing. I haven’t isolated the cause of that delay or compared its timing against the base branch.

No additional blocking findings from my review. Greptile’s deferred fallback-toast issue is tracked in #1599.

@jvsena42 jvsena42 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Code scan at 6d5010f: no issues found in 6d5010f. Reviewed as a funds change. My earlier LOW is fixed: acceptOffersFromAllowers calls respond(offer) on the checked snapshot (:270), and respond iterates that snapshot (:305).

Checked and clean:

  • The snapshot also pins proposalStreamItemId (:245).
  • isCurrent runs before each offer and again before returning (:258, :268, :274).
  • presentedProposalIds is written only while the identity is unchanged (:312), which matches iOS c5ac982.
  • canContinue (AppViewModel.kt:1161-1164) applies the same profile-deletion and polling-stopped checks as the refresh (:1140-1147).
  • isAcceptingOffers is still released in finally.

Inherited from the base: the manual Accept answers every member of an entry, including another contact's proposal that shares the id. It is the same bug as synonymdev/bitkit-ios#799 (review) and has an Android reproduction on #1340's branch. This PR doesn't make it worse, so I'll raise it on #1340 once it's rebased.

Perf: not relevant.

Device gate (partial): 6d5010f — not run yet. Pending: allowances/set-and-accept.xml and allowances/pin-gates-allowance-changes.xml cross-device with synonymdev/bitkit-ios#841. Device results will follow in a separate review.

…t/allowance-auto-accept

# Conflicts:
#	app/src/main/java/to/bitkit/repositories/PaykitAllowanceRepo.kt
#	app/src/test/java/to/bitkit/repositories/PaykitAllowanceRepoTest.kt
#	journeys/allowances/README.md
@ovitrif

ovitrif commented Oct 20, 2026

Copy link
Copy Markdown
Collaborator Author

Pushed 53540d2: merged the updated feat/paykit-allowances (allowances kept per contact, payments counted per contact, master sync with the stricter lint) into this branch.

  • PaykitAllowanceRepo.kt: kept the base's contact-scoped keys for presented proposals, still written only for the identity the answer started with; the review sheet still skips offers from the allower, which are accepted automatically.
  • PaykitAllowanceRepoTest.kt: the base's new test that an accept never answers another contact's proposal with the same id now expects the sheet to show that contact's ask, since the offer is accepted automatically here; this branch's ask test uses the contact-scoped entry id.
  • journeys/allowances/README.md: took the base's row id wording and kept this branch's offer toast and review sheet lines.
  • testDevDebugUnitTest, detekt, lintDevDebug and assembleDevDebug pass.

@jvsena42 jvsena42 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Delta at 53540d2: no issues found. The merge of feat/paykit-allowances takes the contact-scoped it.key in respond (keeping the activeIdentity guard), proposalForPresentation (keeping the !isOfferFromAllower filter), markProposalPresented and the grouping. acceptOffersFromAllowers still answers only the entry it checked. The re-aimed cross-contact test still proves the original contact is never answered.

Minor, no behaviour impact: the merge left a duplicated verify(sdk, never()).acceptAllowance(any(), any(), any()) at PaykitAllowanceRepoTest.kt:505-506.

Device gate (partial): 53540d2 — not run yet. Pending: allowances/set-and-accept.xml and allowances/pin-gates-allowance-changes.xml in the auto-accept form, plus the other five allowance journeys, cross-device with synonymdev/bitkit-ios#841. Device results will follow in a separate review.

@ovitrif

ovitrif commented Oct 20, 2026

Copy link
Copy Markdown
Collaborator Author

Pushed 235c636: dropped the duplicated verify in PaykitAllowanceRepoTest.kt that @jvsena42 spotted after the base merge. Test-only; PaykitAllowanceRepoTest and detekt pass.

@ovitrif

ovitrif commented Oct 21, 2026

Copy link
Copy Markdown
Collaborator Author

Pushed dea7822: merged the updated feat/paykit-allowances (a master sync with no allowance changes) into this branch. No conflicts; testDevDebugUnitTest, detekt, lintDevDebug and assembleDevDebug pass.

This branch has not been deployed

No deployments
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