Skip to content
This repository was archived by the owner on Oct 16, 2025. It is now read-only.

fix(hot): skip revoked transactions on subs upgrade#19

Merged
hyochan merged 1 commit intomainfrom
fix/skip-revoked-transaction-upgrade
Oct 9, 2025
Merged

fix(hot): skip revoked transactions on subs upgrade#19
hyochan merged 1 commit intomainfrom
fix/skip-revoked-transaction-upgrade

Conversation

@hyochan
Copy link
Member

@hyochan hyochan commented Oct 9, 2025

@hyochan hyochan added the 🔥 hotfix Hot urgent pr label Oct 9, 2025
@coderabbitai
Copy link

coderabbitai bot commented Oct 9, 2025

Walkthrough

Adds conditional checks in the transaction processing loop to skip revoked transactions and already-processed transactions, with debug logging. When skipping already-processed transactions, the code also removes them from the processed set. Unchanged behavior for unprocessed transactions.

Changes

Cohort / File(s) Summary
Transaction processing guardrails
Sources/OpenIapModule.swift
Added checks to skip transactions with non-nil revocationDate; added skip path for already-processed transactions with debug log and removal from processed set; preserved existing flow for unprocessed transactions.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant App
  participant OpenIapModule
  participant StoreKit as StoreKit Transaction

  App->>OpenIapModule: processTransactions()
  loop For each transaction
    OpenIapModule->>StoreKit: read transaction
    alt Revoked (revocationDate != nil)
      OpenIapModule-->>App: skip with debug log
    else Already processed
      OpenIapModule->>OpenIapModule: remove from processed set
      OpenIapModule-->>App: skip with debug log
    else Unprocessed
      OpenIapModule->>OpenIapModule: process transaction (existing flow)
      OpenIapModule-->>App: continue
    end
  end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10–15 minutes

Poem

I hop through queues where purchases flow,
Sniff revoked trails, then let them go.
If marked as done, I tidy tracks—
Uncheck, log, and bounce right back.
For fresh carrots, onward I prance—
Process, nibble, next advance. 🥕🐇

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title correctly summarizes the main change, indicating that revoked transactions are now skipped during subscription upgrades, which directly reflects the core modification described in the diff. It is concise and focused on the primary fix without extraneous details.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/skip-revoked-transaction-upgrade

Comment @coderabbitai help to get the list of available commands and usage tips.

@hyochan hyochan merged commit 08b1894 into main Oct 9, 2025
2 checks passed
@hyochan hyochan deleted the fix/skip-revoked-transaction-upgrade branch October 9, 2025 19:11
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f510abe and dd83c29.

📒 Files selected for processing (1)
  • Sources/OpenIapModule.swift (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
Sources/OpenIapModule.swift (1)
Sources/Helpers/IapState.swift (1)
  • isProcessed (26-26)
🔇 Additional comments (1)
Sources/OpenIapModule.swift (1)

841-845: LGTM! Correct handling of revoked transactions.

The check for revocationDate appropriately skips revoked transactions that occur during subscription upgrades. This prevents processing obsolete transactions and the debug logging aids troubleshooting.

Comment on lines 847 to 852
if await self.state.isProcessed(transactionId) {
OpenIapLog.debug("⏭️ Skipping already processed transaction: \(transactionId)")
// Remove from processed set for future updates (e.g., subscription renewals)
await self.state.unmarkProcessed(transactionId)
continue
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify the unmarking logic for already-processed transactions.

Unmarking a transaction immediately when skipping it (line 850) could lead to duplicate processing if the same transaction appears again shortly after. The flow becomes:

  1. First appearance: Transaction processed and marked
  2. Second appearance: Found processed → unmarked and skipped
  3. Third appearance: Not marked anymore → processed again (duplicate)

The comment mentions "subscription renewals," but renewals generate new transaction IDs rather than reusing existing ones. Additionally, lines 859-862 already handle unmarking after a 5-second delay for legitimate reprocessing scenarios.

Expected behavior: If a transaction is already processed, it should be skipped without unmarking (the 5-second timer handles unmarking). Alternatively, if the intent is to reprocess on subsequent appearances, the transaction should be unmarked and then processed (not skipped).

Consider one of these approaches:

Option A: Remove the unmarkProcessed call to preserve the original skip behavior:

 if await self.state.isProcessed(transactionId) {
     OpenIapLog.debug("⏭️ Skipping already processed transaction: \(transactionId)")
-    // Remove from processed set for future updates (e.g., subscription renewals)
-    await self.state.unmarkProcessed(transactionId)
     continue
 }

Option B: If reprocessing is intentional, unmark and process (don't skip):

 if await self.state.isProcessed(transactionId) {
     OpenIapLog.debug("⏭️ Reprocessing transaction: \(transactionId)")
     await self.state.unmarkProcessed(transactionId)
-    continue
+    // Fall through to process the transaction again
 }

Please verify which behavior is correct for your use case. Run the following script to check if there are any existing tests that document the expected behavior:


🏁 Script executed:

#!/bin/bash
# Search for tests related to transaction processing and duplicate handling
rg -n --type swift -C 3 'isProcessed|markProcessed|unmarkProcessed|processUnfinished'

Length of output: 3986


📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if await self.state.isProcessed(transactionId) {
OpenIapLog.debug("⏭️ Skipping already processed transaction: \(transactionId)")
// Remove from processed set for future updates (e.g., subscription renewals)
await self.state.unmarkProcessed(transactionId)
continue
}
if await self.state.isProcessed(transactionId) {
OpenIapLog.debug("⏭️ Skipping already processed transaction: \(transactionId)")
continue
}
🤖 Prompt for AI Agents
In Sources/OpenIapModule.swift around lines 847 to 852, remove the immediate
call to await self.state.unmarkProcessed(transactionId) inside the if-block that
skips already processed transactions; simply keep the skip logging and continue,
relying on the existing 5‑second timer to unmark processed IDs instead of
unmarking before continue to avoid re-processing duplicates.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

🔥 hotfix Hot urgent pr

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant