Repository navigation
feat(payments): persist payment requests for offline - #1169
Merged
Merged
Conversation
Store each request's PaymentRequestMetadata in a new paymentRequest table (schemaVersion 62 -> 63) so a chat's request cards and details draw offline and after a restart. The DB upsert applies the contract's merge rule in one statement: greater version wins, and at an equal version EXPIRED replaces OPEN. PaymentRequests writes through on ingest and applied updates, and hydrates stored copies ahead of the network read.
Store each private group's key envelope as the server returned it in a new groupKeyEnvelope table (schemaVersion 63 -> 64), so encrypted content opens offline and after a restart. GroupKeyEnvelopes opens the stored envelope first with the same wrapper and scheme checks as a fetched one, and falls back to the network when it is missing or fails. The row follows the in-memory state: replaced by the member's own envelope after a self-wrap, removed on ALREADY_SET, an untrusted wrapper, or an invite copy that fails a check, and deleted with the conversation. The unwrapped key is never written. EncryptedChatClient opens with the stored conversation when the chat has not passed through yet, so the seal no longer needs a GetChat.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Replaces #1166, which targeted
feat/chat-pins-contractafter #1165 had already squash-merged that branch intomain. Same three commits, rebased ontomainwith no changes.Payment requests lived only in memory in
PaymentRequests, so a chat's request cards were empty after a restart or offline until a refetch came back. This stores each request in the database and reads it before the network.62 → 63: newpaymentRequesttable, one row per request.id,conversationID,version,status,endsAtandcreatedAtare columns;metadatais the request serialized asPaymentRequestMetadata. The existingrequestedPaymenttable is unchanged.Database.upsertPaymentRequestsasINSERT … ON CONFLICT(id) DO UPDATE … WHERE: a row is written when there is none, when the incoming version is greater, or at an equal version when the incoming status is EXPIRED and the stored one is OPEN. A stale write can't overwrite a newer row.ingest(chat pages, fetch, refresh, create, close, mark paid,.createdupdates) is stored, and an applied update stores the whole request at its new version. A failed write is logged and doesn't fail the fetch.refreshChatandrefresh(_:)read stored rows before the network call, through the existingmerge. A network failure leaves the stored copies showing; a DENIED or NOT_FOUND still marks the card unavailable.63 → 64: newgroupKeyEnvelopetable, one row per conversation, holding theGetKeyEnvelopeResponseas fetched. The unwrapped key is never written. On an in-memory miss,GroupKeyEnvelopes.groupKey(for:creator:)opens the stored envelope with the same scheme and wrapper checks as a fetched one, so encrypted request text opens offline; nothing stored, or an envelope that fails a check, falls back to the network. A fetch,storeNewKeyor the member's own self-wrapped copy replaces the row.ALREADY_SET, an untrusted wrapper and an unusable key remove it, and so does deleting the conversation.EncryptedChatClientfalls back to the stored conversation when it has not seen one this launch, so the seal path reaches the stored envelope offline instead of returning.awaitingKey. The stored copy is used only to open; sends still follow the server's current encryption choice.Split.textis held encrypted and opened at read time, so the stored form is the ciphertext as received.deleteConversation, departed groups inreplaceGroupFeed, and conversationsreplaceConversationFeedno longer returns, in the same transaction as the envelope delete.PaymentRequests.forget(conversations:)drops departed groups' requests from memory. Logout doesn't delete the database, which is one file per owner, matching the other conversation tables.unavailable,inFlight, the failure sets, seals andchatRefreshes.GroupKeyEnvelopeStoreTests(offline open, replace, fallback, one case per invalidation point, key bytes absent from storage),DatabaseGroupKeyEnvelopeTests,EncryptedPaymentRequestOfflineTests(an encrypted request's text opens after a restart with the network down),PaymentRequestDatabaseTests(merge cases, read by chat, blob round trip, sealed text) andPaymentRequestsPersistenceTests(write-through, offline hydrate by chat and by id, applied and stale updates, stale ingest).