Skip to content

chore: adopt Paykit 0.1.1-rc6 - #1237

Merged
ovitrif merged 6 commits into
masterfrom
chore/paykit-rc6
Nov 11, 2026
Merged

ovitrif merged 6 commits into
masterfrom
chore/paykit-rc6

Conversation

@ben-kaufman

@ben-kaufman ben-kaufman commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Closes #1271

Twin: synonymdev/bitkit-android#1803

This PR:

  1. Updates Paykit from 0.1.1-rc2 to 0.1.1-rc6
  2. Checks that a Payment Request's proof fits one Paykit message before paying it
  3. Asks to shorten a note or subscription text that does not fit one Paykit message
  4. Explains a refused signup code or a failed connection when authorizing a signup link
  5. Stops counting USDT received before a request was made as paying that request

Description

  • Paykit bump: pins exact version 0.1.1-rc6 at revision 9919198a, the same way the rc2 bump did. The app compiled unchanged against rc6. Only test fixtures needed the new Payment Request record times and the acknowledged list in Private Payment List delivery reports.
  • Proof size: before a Lightning, on-chain or USDT Payment Request is paid, Bitkit measures the Payment Proof message with the SDK's new paymentProofEventLen, using the longest proof the rail can produce and the IDs it will send. A request whose proof would not fit the 1000-byte message limit shows "The payment request is no longer available." and nothing is paid. An Allowance checks this before it accepts the request automatically, so such a request stays manual instead of being accepted and then left unpaid. The USDT check used a hand-built copy of the message and now uses the same measurement. Bitkit's own requests use a fixed-length payment reference, so their longest proof always fits.
  • Proposal size: proposing a Payment Request or subscription that the SDK refuses with message_too_large shows "Shorten the note and try again." (wallet__payment_request_note_too_long, new) for a one-time request and the existing "Shorten the subscription name or description and try again." for a subscription, instead of the raw SDK error. Nothing is queued in either case.
  • Signup errors: in the authorization sheet, a signup code the homeserver refuses now shows "The homeserver did not accept this signup code. Try again or use another signup link." (profile__auth_error_signup_code_rejected, new) and a network, 5xx, 429 or PKARR failure, which rc6 reports as a transport error, shows "Check your connection and try again." (pubky_auth__identity_unavailable_desc, new on iOS with Android's translations). Other failures keep their description.
  • USDT receipts: a verified USDT transfer whose block time is more than a day before this wallet recorded its request (the new proposal record time) shows "Received · Before request" (wallet__payment_request_before_request, new) and does not pay the request or Billing Period. The day covers Arbitrum timestamp lag. Receipts and backups from earlier builds keep their meaning.
  • Reviewed with no app change needed: re-sign-in already treats transport errors as temporary, so restoring a session after a network failure now retries instead of reporting a failed restore. Pubky Ring sign-in already signs up only when republishIdentity finds no record. Platform callbacks already throw PaykitError. Bitkit uses Pubky shared state, so the stateRevision change does not apply. Accounting already reconciles state that requires it and automatic acceptance uses one trusted time. Requests that rc2 showed as invalid after a late Acceptance are shown from their new state. Encrypted Link sends check write slots before publishing, with no API change. The homeservers, relays and homegate Bitkit uses on staging and production chain to ISRG Root X1, which webpki-roots 1.0.11 keeps.

Out of Scope

  • Bitkit/Services/PaykitAllowanceManager.swift: automatic acceptance of Allowance offers still accepts each offer separately. acceptPendingAllowanceProposals would also accept a contact's offers in assets Bitkit does not list and cannot bind each acceptance to the offer Bitkit listed.
  • Profile removal and Disconnect: the captured grant observer and original-grant logout added in Paykit rc3 and rc4 are adopted in fix: bind profile removal to its original owner #1143, not here.
  • Bitkit/Services/PubkyService.swift: deleting a single contact still blocks the peer when the private list withdrawal reports a failure, as before.
  • Lightning and on-chain requests Bitkit receives payment for: Bitkit still shows them paid from the payer's proof, so the proposal record time only applies to verified USDT receipts.
  • Bitkit/Services/PaykitUsdtPaymentService.swift: the one-day tolerance covers Arbitrum timestamp lag. It does not add a margin for the payee's own clock skew.

Design

N/A — no design available. The change adds text to existing toasts and to the USDT receipt status.

Preview

N/A

QA Notes

Setup

  • J1: two Bitkit instances on local regtest with Paykit enabled, saved as each other's contacts and linked as identities, as in journeys/payment-requests/README.md (Request summary setup). No funds are needed.
  • J2: an isolated wallet with no Pubky identity and a controlled requesting app on the Pubky testnet integration fixture runtime (see journeys/pubky-auth/README.md). Complete one signup_grant URL on another wallet first, then use the same URL, whose token is now used, for the journey.
  • Both instances must run this build. Paykit rc6 writes its shared state in a newer format that builds on Paykit rc5 or older cannot read, so do not share an identity with a device on an older build.

Journeys

  • J1: new request-note-too-long.xml — a note that cannot fit one Paykit message is refused with "Shorten the note and try again." before anything is sent, and the shortened request sends
  • J2: new grant-signup-rejected-code.xml — a refused signup code shows the signup code message, the sheet stays open and no identity is created

Manual Tests

  • 1. A linked issuer sends a one-time Lightning request whose payment reference is 256 "💜" characters → Pay → "The payment request is no longer available." and no payment starts — an issuer that sends a chosen payment reference not in Capabilities
  • 2. A USDT payee receives a proof for a transfer made two days before its request → payment history shows "Received · Before request" and the request stays unpaid — a payer that proves an earlier USDT transfer not in Capabilities
  • 3. Authorize a signup link while the homeserver is unreachable → "Check your connection and try again." and the sheet stays open — network fault injection not in Capabilities

Automated Checks

  • added PaykitPaymentProofServiceTests.swift — Lightning and on-chain preparation accepts a request whose proof is exactly 1000 bytes and refuses one at 1001 bytes without storing anything, and the Allowance ID counts toward the size
  • added PaykitAllowanceExecutorTests.swift — a request whose proof cannot fit stays manual with no execution claim, Acceptance or payment
  • added PaykitRequestPricingTests.swift — the USDT placeholder is a valid proof of the longest shape, the USDT check accepts exactly 1000 bytes and refuses 1001, the Billing Period and quote ID count toward the size, and Bitkit's own requests leave room for their longest proof
  • added PaykitPaymentRequestServiceTests.swift — message_too_large becomes the note and subscription messages with nothing proposed, and other protocol errors pass through. A USDT transfer more than a day before the request record time, in Bitkit's or the SDK's timestamp format, is flagged, shown as Before request ahead of other labels and does not pay it, while one within the day or without a valid record time does
  • added PaykitPaymentStateBackupTests.swift — the before-request flag survives a backup, and an older backup or a receipt saved before the flag existed still counts as paid
  • added PubkyAuthApprovalSheetTests.swift — a refused signup code and a transport error get their messages, other errors keep their own
  • updated 15 test files — fixtures carry the new Paykit record and report fields
  • updated PaykitAllowanceTests.swift — Allowance fixtures use canonical UUID-v4 IDs, which the SDK requires in a Payment Proof
  • ran openssl s_client against a bundle built from webpki-root-certs 1.0.11 — homeserver, httprelay, homegate and nexus on pubky.app and staging.pubky.app, pkarr.pubky.app, pkarr.pubky.org and relay.pkarr.org verify, ending at ISRG Root X1

Evidence

  • On commit 60111132, a throwaway iPhone 17 simulator (iOS 26.5), Xcode 27.2: CI's unit test selection (xcodebuild test, skipping the integration suites as in unit-tests.yml) passed, BitkitTests 3189 executed with 1 skipped (UsdtNativeRecoveryIntegrationTests, needs a local fixture) and BitkitUITests 44 executed with 4 skipped, 0 failures. The same selection also passed on 265389e8 (the bump alone, 3176 executed) and cd4236ab (3186 executed). Head 1594952e only renames the changelog fragment.
  • SwiftFormat 0.64.0 lint is clean on the changed Swift files, node scripts/validate-translations.js reports 0 errors with the same warnings as master, and git diff --check is clean.
  • The published Paykit.xcframework.zip for v0.1.1-rc6 matches the Package.swift checksum 23bf9612c2bc7579dee8a40b18527936ba1a0531f788e13f9aae8f6286ccf3f5.

Gaps

  • J1, J2 and Manual Tests 1 to 3 were not run: device journeys are left to reviewers, and no two-instance regtest or Pubky testnet setup was used for this change.
  • An in-place upgrade from a build on Paykit rc2 with existing Paykit state was not run on a device. The SDK migrates that state when it first reads it.
  • The new tests were not run against the code without the change.

Models used

  • Planning/scoping: Claude Opus 5.5 (reasoning: Not exposed)
  • Implementation: Claude Opus 5.5 (reasoning: Not exposed)
  • Review: Claude Opus 5.5 (reasoning: Not exposed)
  • Review rounds: 1

The session metadata does not expose a reasoning setting.

🤖 Generated with Claude Code

ben-kaufman and others added 5 commits November 6, 2026 11:52
Pin paykit-rs to the published 0.1.1-rc6 release (revision 9919198a) and
update test fixtures for the new Payment Request record times and the
acknowledged list in Private Payment List delivery reports.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Measure the Payment Proof Event Message with the SDK's
paymentProofEventLen before a Lightning, on-chain or USDT Payment
Request payment, so a request whose proof cannot fit one Paykit
message is refused before anything is paid. The USDT check now uses
the same measurement instead of a hand-built copy of the message.

Ask to shorten the note or subscription text when the SDK refuses a
proposal with message_too_large, and explain a refused signup code or
a failed connection in the Pubky authorization sheet.

A verified USDT transfer made more than a day before this wallet
recorded its request is labelled Received, Before request and no longer
counts as paying it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Measure the Payment Proof Event Message before an Allowance accepts a
Payment Request automatically, so a request whose proof cannot fit one
Paykit message is left for manual review instead of being accepted and
then failing to pay. Allowance test fixtures now use canonical UUID-v4
Allowance IDs, which the SDK requires in a Payment Proof.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cover that the proof size check counts the Allowance ID, Billing Period
and quote ID it will send, that Bitkit's own requests leave room for
their longest proof, that SDK-formatted record times are parsed, that
Before request wins over the other receipt labels, and that receipts
saved before the flag existed still count as paid. Use the SDK App ID
and the USDT chain ID from their shared constants.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Nov 10, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[High impact] The reviewed changes appear safe to merge, with no actionable issues found.

Summary

Adopts Paykit 0.1.1-rc6 and adds checks before paying requests whose proofs cannot fit one message.

  • Payment requests with proofs that are too large stay unpaid.
  • Oversized requests ask people to shorten their text.
  • Old USDT transfers no longer pay requests made later.
  • Signup authorization explains refused codes and connection failures.
  • Bitkit now uses Paykit 0.1.1-rc6 for its payment and identity flows.

Scope acknowledged by ben-kaufman:

  • Bulk Allowance acceptance remains separate because it cannot bind each acceptance to the listed offer.
  • Profile removal and Disconnect changes are deferred to fix: bind profile removal to its original owner #1143.
  • Single-contact deletion keeps its existing behavior after a failed withdrawal.
  • Lightning and on-chain receipts still rely on the payer's proof.
  • The USDT time tolerance does not cover the payee's clock skew.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Payment request] --> B[Measure longest payment proof]
    B --> C{Fits one message?}
    C -- No --> D[Show request unavailable]
    C -- Yes --> E[Prepare payment]
    E --> F[Pay and send proof]
    G[Verified USDT transfer] --> H{Too far before request?}
    H -- Yes --> I[Show Before request and leave unpaid]
    H -- No --> J[Check amount and deadline]
Loading

Reviews (1) · Last reviewed commit: "chore: rename changelog fragment" · Reviewed by Greptile

@ovitrif

ovitrif commented Nov 10, 2026

Copy link
Copy Markdown
Collaborator

Triage: in 3.2.0. This PR closes #1271, filed in the 3.2.0 milestone at 👀 Review: besides the Paykit bump it changes payment and authorization behaviour that ships in 3.2.0, and the profile work builds on it. Twin: synonymdev/bitkit-android#1803.

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

No findings. iOS twin of synonymdev/bitkit-android#1803: pins Paykit 0.1.1-rc6 (project and Package.resolved agree on the tag), measures every Payment Proof with Paykit.paymentProofEventLen before paying (USDT, Lightning, on-chain, and automatic Allowance payments before the execution claim), maps message_too_large and signup_code_rejected to their own copy, and flags USDT transfers more than 24h before the request. Reviewed 1594952, full tier, statically: iOS does not build on Linux, so CI is the gate.

What I checked, and 5 candidates I ruled out

Read in full (changed regions): PaykitPaymentProofService.swift (PaykitPaymentProofSize, prepare, proofText), PaykitAllowanceExecutor.swift (the pre-claim check), PaykitUsdtPaymentService.swift, PaykitPaymentRequestService.swift, PaykitUsdtStateBackup.swift, PubkyAuthApprovalSheet.swift, PaymentRequestsView.swift, PubkyService.swift.
Cross-checked against paykit-rs v0.1.1-rc6: payment_proof_event_len serializes through the queue's own serialize_payment_request_event; the FFI raises protocol code message_too_large; proposal_recorded_at is the payee's own record time.
CI: an earlier build-local run failed at "Verify current PR merge checkout" (the base moved under it, before any compile); the rerun, SwiftFormat and validate were pending at the time of this review.

Ruled out

  • Placeholder proof shorter than the real one: the Bitcoin placeholder uses the same {"data","type"} keys as proofText with a 64-hex value, the fixed length of a preimage or txid; the USDT one carries the same five keys as Proof with the max uint256 log index. Key order differs from JSONEncoder's, which changes no length.
  • Allowance measures a different quote than it pays with: passing a quote id can only lengthen the measured message, so the pre-claim check is never looser than prepare's own check, which runs again with the bound proof.conversionQuoteId.
  • Old receipts decoding: beforeRequest is optional in both the receipt and the backup, restored as false, and satisfied treats nil as not-before; PaykitPaymentStateBackupTests.swift covers the missing key.
  • Restored receipts churning on reconcile: a restored nil against a computed false fails contains and the receipt is replaced by the same-id fresh one, a one-time rewrite, not a duplicate.
  • Generic auth errors losing their text: pubkyAuthApprovalFailureDescription falls through to localizedDescription for everything but the two new cases.

Merge confidence: 4/5, payment and proof paths with unit tests but no CI result on this head yet, and J1, J2 and Manual 1 to 3 need a device.

Reviewer model: claude-opus-5-5
Reasoning effort: Not exposed (the session does not report a reasoning setting)

@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 this head found no HIGH, MEDIUM or LOW issues. The device gate still has to run; I'll post its result in a follow-up review.

What I checked in the rc2 → rc6 SDK changes:

  • New error codes: message_too_large and identity/signup_code_rejected are mapped to the new copy.
  • Sign-up/sign-in errors now Transport: these already go to the deferred-restore and temporary-error paths.
  • Proof size check: paymentProofEventLen runs before anything is claimed or persisted, for Lightning, on-chain, USDT and allowance requests. The placeholder proof matches the real proof's shape, so the measured size is at least the sent size.
  • USDT "before request":
    • proposalRecordedAt comes from the outbound message's created_at, which rc2 already stored. Receipts verified before the upgrade don't flip to "Before request".
    • beforeRequest defaults to false, so backups from rc2 builds still decode.
  • stateRevision busy error: it only fires under callback storage. Bitkit uses PubkyShared and already maps read failures to null.
  • State migration: the state blob goes from v1 to v4. A single-device upgrade from 3.1.0 reads v1 fine. As the PR body says, an rc2 build can't read state once an rc6 build has written it.

Out of scope: a user with a 3.1.0 device and a 3.2.0 device on the same Pubky identity loses Paykit on the old device until it updates. The PR accepts this ("ship together"). It would help to add a line about it to the release notes.

Reviewer model: claude-fable-5-1 (Stage 1), claude-opus-5-5 (coordination)
Reasoning effort: not exposed (Fable subagent), default (Opus)

@ovi-reviewer ovi-reviewer 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.

Verdict: ⚠️ Not verified — Manual Test 1 could not reach the app; the other four device cases passed.

Findings: None.

QA for a1c126d: Debug/E2E build from the reviewed commit, iPhone 17 Pro simulator on iOS 27.0, using disposable regtest wallets and a local Arbitrum fork.

Refuse an oversized request note and send a shorter one ✅ The long note was refused; “Coffee” reached the payer’s 1,000-sat confirmation.

J1: pasted 256 purple hearts, observed “Shorten the note and try again.” with the editor retained, then shortened the note and sent it. Closed the payer’s confirmation without paying.

j1-2x.2x.mp4

Requester on the left, payer on the right; typing and idle intervals shortened, playback at 2×.

Explain a rejected signup code ✅ The specific signup-code message appeared, Authorize remained available, and no identity was created.

J2: used an unknown signup token against the reachable test homeserver. After canceling, Profile still showed its introduction.

j2-2x.2x.mp4

Rejected signup and the unchanged profile state, at 2×.

Reject a request whose Lightning proof cannot fit ⏭️ The rc6 issuer rejected the requested proposal before delivery.

Manual Test 1: a linked issuer using Paykit 0.1.1-rc6 (9919198a) refused the 256-heart payment reference with Private Application Message (1499 bytes) exceeds pubky-noise message size (1000 bytes). That exact input could not reach the app, so the device-side refusal remains unrun. Unit tests passed for 1,000-byte and 1,001-byte proof preparation, including the Allowance pre-acceptance guard.

Keep an older USDT transfer from paying a new request ✅ The verified receipt shows “Received · Before request · 0.05 USDT,” without a paid status.

Manual Test 2: sent a real 0.05-USDT transfer on the local fork, then created a new 0.05-USD request with a fixed 1:1 USDT rate. Submitted an EIP-712 proof signed by the transfer’s EOA owner through an rc6 peer. The receipt’s block timestamp was 8 November, more than two days before the request; the timestamp and successful transfer were checked against the fork receipt. History and request details show Before request. The saved result also survived an app restart.

usdt-result-2x.2x.mp4

Verified request details, followed by the saved receipt status after restart, at 2×; the home activity also shows the older transfer date.

Explain an unreachable signup homeserver ✅ “Check your connection and try again.” appeared and the authorization sheet stayed open.

Manual Test 3: used a syntactically valid homeserver key with no resolvable record in the isolated test network, exercising the transport failure path.

network-2x.2x.mp4

Failed signup authorization and retained retry controls, at 2×.

Check: focused unit suites and static validation ✅ The changed payment, receipt, backup and authorization suites passed.

The six focused suites completed successfully on iOS 26.5 with Xcode 27.0. SwiftFormat 0.64.6 lint on the changed Swift files, translation validation and git diff --check passed.

Full review of 62db433..a1c126d, including the rc6 pin, production changes, tests and journeys. Subscription message overflow, automatic Allowance handling and receipt/backup edge cases received unit coverage; they were not separately driven through the UI. The in-place rc2 state upgrade was not device-tested.

Twin: bitkit-android#1803 fix: adopt paykit 0.1.1-rc6 matches the proof-sizing, proposal/signup error handling and USDT timing behavior in the changed production code. Android was not device-tested in this run.


Reviewed by gpt-6.1-sol-high

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

Partial device gate at a1c126d. The code scan was clean in my earlier review; this adds device results. I stopped the run before it finished, so this is not an approval yet.

Environment:

  • Two iOS 26.5 simulators: A is an iPhone 17 Pro, B an iPhone 17 Pro Max.
  • Staging Pubky homeserver and httprelay, local regtest.
  • Builds: this PR (a1c126d), master 62db433 (Paykit rc2) as the upgrade baseline, and an issuer fixture build of this PR for M1.
Case Result
J1 Pass on screen. A 1000 sat request with a long "💜" note was refused on Send Request: the payer got no new request and the sheet stayed on the request. The same request with the note "Coffee" sent and showed Payment Requested. I did not capture the toast text.
J2 Pass on screen. Authorize with the used signup code ended with the sheet still open, and no identity was created: Profile still shows the Portable Profile intro after Cancel. The log shows Failed to approve pubky signup. I did not capture the toast text.
3 Pass on screen. Authorize with the homeserver unreachable failed and the sheet stayed open. I did not capture the toast text.
Upgrade from master (rc2) Pass. On master, A requested 1000 sats from B ("PreUpgrade"). I then installed this PR's build over master on both simulators. B opened the pre-upgrade request and paid it (Sent to GateA1237, −1143 sats including fee), and A received it.
1 Not finished. The issuer fixture build was installed on A and sent its request, but the payer's result was not checked.
2 Not run. It needs a USDT fixture.

Evidence is kept locally in .ai/gate_1237_1794340718 (screenshots, app logs, notes).

Still to do before approval: case 1's payer result, case 2, and the toast text for J1, J2 and 3.

Reviewer model: claude-opus-5-5 (device gate and coordination)
Reasoning effort: default (Opus)

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

Device gate finished at a1c126d92, with the code scan clean from my earlier review. Approving.

Case Result
J1 Pass. 256 pasted "💜" → "Error / Shorten the note and try again.", and nothing was sent.
J2 Pass. "Authorization Failed / The homeserver did not accept this signup code. Try again or use another signup link." The sheet stayed open and no identity was created.
3 Pass. "Authorization Failed / Check your connection and try again." The sheet stayed open. I triggered it with a homeserver key that has no homeserver record, a lookup failure the PR maps to the same message.
1 Pass, with an adjusted fixture (below). The payer swiped and got "Transaction Failed / The payment request is no longer available." (Payment failed: requestUnavailable). No proof was stored, no submitPaymentProof ran, and Savings and activity were unchanged.
Upgrade from master (rc2) Pass (posted earlier).
2 ⚠️ Not run. See below.

Manual test 1 as written can't happen. 256 "💜" make a 1024-byte reference, so the issuer's own proposal is refused with the note message. With the stock proposal shape, 112 or more hearts are refused at propose, and 106 send and pay normally. So any reference that fits a stock proposal also fits the proof. I exercised the payer check with an issuer fixture whose proposal drops expiry, conversion, deadline and metadata and keeps only the p2wpkh endpoint: 150 hearts (600 bytes) sent and the payer refused it as above. The rail was on-chain, since neither wallet had a channel. It goes through the same PaykitPaymentProofSize.validate as Lightning. The test text would be clearer as "a reference that fits a proposal but not its proof".

⚠️ Manual test 2 not run. It needs a USDT payment whose block time is more than a day before the request. On the bitkit-docker USDT fixture I set the Anvil clock 3 days behind real time:

  1. Alto rejected the user operation, because it checks the paymaster's validUntil against wall time.
  2. A wall-time paymaster was refused by Core, because validUntil was more than 900 s past the chain time.
  3. Alto with --expiration-check false accepted the operation, but every bundle failed with AA32 paymaster expired or not due.

Running it needs a fixture that bundles under a lagged chain clock, or a payer build that proves an older transfer. The before-request logic rests on PaykitPaymentRequestServiceTests and PaykitPaymentStateBackupTests.

Device gate: a1c126d92 — journeys: 2 passed (J1, J2); manual: 1, 3 and the upgrade passed, 2 not run (fixture); perf: propose 1.1-1.2 s; resolving a request before Confirm took 4-29 s on a host at load 11-140 with other builds running, so it is not comparable to master; iOS only (the Android twin is merged).

CI is green at this head.

Reviewer model: Claude Opus 5.5 (claude-opus-5-5)
Reasoning effort: not exposed

@ovitrif

ovitrif commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator

Merging: approved by @jvsena42 on a1c126d after his device gate on that head, checks green, no open threads.

@ovitrif
ovitrif merged commit fe0cc2d into master Nov 11, 2026
21 checks passed
@ovitrif
ovitrif deleted the chore/paykit-rc6 branch November 11, 2026 12:50
@ovitrif

ovitrif commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator

@ben-kaufman Merged, as it was approved and green.

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.

fix: check payment proof size before paying

4 participants