Fix addSuccessfullyPostedToken for new purchases in PostPendingTransactionsHelper#3239
Conversation
…PendingTransactionsHelper PR #3198 added an unconditional addSuccessfullyPostedToken call in transactionPostSuccess to persist isAutoRenewing status for auto-renewing change detection. However, this also fired for new purchases, which: 1. Broke integration tests that use a relaxed mock for BillingAbstract (consumeAndSave is a no-op), prematurely marking tokens as posted during SDK init so later syncs couldn't find pending purchases. 2. Changed production retry behavior: tokens that got a 500 (handled via offline entitlements) were marked as posted and never retried. Now addSuccessfullyPostedToken only fires for tokens reposted due to an auto-renewing status change. New purchases rely on billing.consumeAndSave to cache the token, as before #3198. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@RCGitBot please test |
Triggers run-firebase-tests-purchases-integration-tests (production) to validate the fix for the two failing integration tests. This commit should be removed before merging.
Adds a `run-firebase-tests` pipeline parameter that can be toggled from the CircleCI UI to run all Firebase integration test jobs on any branch, without requiring a release/* branch. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3239 +/- ##
==========================================
- Coverage 79.40% 79.40% -0.01%
==========================================
Files 356 356
Lines 14348 14345 -3
Branches 1959 1959
==========================================
- Hits 11393 11390 -3
Misses 2151 2151
Partials 804 804 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| transaction.purchaseToken, | ||
| transaction.isAutoRenewing, | ||
| ) | ||
| } |
There was a problem hiding this comment.
So I missed this in the previous PR... but I'm wondering now whether this is needed at all... As in, normally this path would already go through the PostReceiptHelper which adds the posted token to the cache. The exceptions are if the type of purchase is pending or unknown... (Since there is a safeguard before that path gets called) but in those scenarios we wouldn't want to add the token as posted... So I actually think we need to remove this entirely? Lmk if I missed something though 🙏
There was a problem hiding this comment.
You're right — removed it entirely in e1397bb. billing.consumeAndSave already calls addSuccessfullyPostedToken(token, purchase.isAutoRenewing) on the success path before transactionPostSuccess fires, so the call here was always redundant. And on the error path (500 + offline entitlements), consumeAndSave is intentionally not called, so the token stays uncached for retry.
billing.consumeAndSave already calls addSuccessfullyPostedToken (with isAutoRenewing) on the success path, before transactionPostSuccess fires. The call here was redundant for all cases — new purchases and auto-renewing changes alike. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
tonidero
left a comment
There was a problem hiding this comment.
Just reverting CI changes and I think it looks good.
Since #3198 hasn't been released, no users have tokens migrated from the legacy StringSet cache with isAutoRenewing = null. The call to saveAutoRenewingStatus(unchangedTokens) was only useful for that migration case — for tokens already cached with isAutoRenewing set, it's a no-op by definition (unchanged means same value). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
There are 3 total unresolved issues (including 1 from previous review).
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
The legacy StringSet cache predates #3198 — users upgrading from any older SDK version will have tokens with isAutoRenewing = null. This call populates that value on the first sync so auto-renewing change detection works for pre-existing subscriptions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@tonidero tests passed. I'll revert the temp thing in the CI |
**This is an automatic release.** ## RevenueCat SDK ### 🐞 Bugfixes * Fix & Standardize Galaxy Date Parsing Edge Cases (#3216) via Will Taylor (@fire-at-will) * Fix addSuccessfullyPostedToken for new purchases in PostPendingTransactionsHelper (#3239) via Facundo Menzella (@facumenzella) * [Galaxy]: Fix race condition when fetching Galaxy products (#3213) via Will Taylor (@fire-at-will) * Fixes double padding in PaywallActivity on Android 15+ when `edgeToEdge` parameter is false (#3227) via Cesar de la Vega (@vegaro) ## RevenueCatUI SDK ### 🐞 Bugfixes * Fix bold text not rendering in Markdown lists (#3228) via Cesar de la Vega (@vegaro) * Fix: Clear in-memory offerings cache on locale override to prevent stale paywall data (#3225) via Antonio Pallares (@ajpallares) ### Paywallv2 #### ✨ New Features * Feature: Update default paywall (#3133) via Jacob Rakidzich (@JZDesign) #### 🐞 Bugfixes * Fix V2 paywall safe area in landscape mode (#3221) via Cesar de la Vega (@vegaro) ### 🔄 Other Changes * Run integration tests on all branches (#3242) via Toni Rico (@tonidero) * Migrate Firebase Test Lab jobs to CircleCI emulators (#3238) via Toni Rico (@tonidero) * Run metalava on galaxy module in test-galaxy job (#3235) via Will Taylor (@fire-at-will) * Add offering_id to custom paywall impression event (#3230) via Rick (@rickvdl) * Cache isAutoRenewing to detect subscription changes without syncPurchases (#3198) via Facundo Menzella (@facumenzella) * Bump fastlane-plugin-revenuecat_internal from `e146447` to `3e8c384` (#3233) via dependabot[bot] (@dependabot[bot]) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Low risk release housekeeping: version string bumps and documentation/deployment path updates with no functional runtime logic changes beyond the exposed version constant. > > **Overview** > Publishes the `9.26.0` release by removing `-SNAPSHOT` across build/version metadata (root `VERSION_NAME`, `.version`, `Config.frameworkVersion`, and sample/test app dependency pins). > > Updates release documentation artifacts by adding the `9.26.0` notes to `CHANGELOG.md`/`CHANGELOG.latest.md`, switching docs deployment in CircleCI to sync `docs/9.26.0` to S3, and updating `docs/index.html` to redirect to the new version. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 0a30a45. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->

Summary
#3198 added an unconditional
addSuccessfullyPostedTokencall inPostPendingTransactionsHelper.transactionPostSuccessto persist theisAutoRenewingstatus for auto-renewing change detection. However, this call was redundant —billing.consumeAndSavealready callsaddSuccessfullyPostedToken(token, isAutoRenewing)on the success path beforetransactionPostSuccessfires. The extra call caused two issues:1. Integration test failures
The tests
entersOfflineEntitlementsModeIfCachedCustomerInfoAndPostingPendingPurchasesReturns500andpostsPurchasePerformedOnFallbackURLWhenRecoveringToMainServeruse a relaxed mock forBillingAbstractwhereconsumeAndSaveis a no-op. The unconditionaladdSuccessfullyPostedTokenprematurely marked tokens as posted during SDK init, so subsequent syncs found no pending purchases and couldn't enter offline entitlements mode.2. Production retry behavior change
Tokens that received a 500 error (handled via offline entitlements) were marked as posted and never retried on the next sync. This means the backend could permanently miss a purchase.
Fix
Removed the
addSuccessfullyPostedTokencall fromtransactionPostSuccessentirely. Token caching (includingisAutoRenewing) is already handled bybilling.consumeAndSaveon the success path. On the error path (500 + offline entitlements),consumeAndSaveis intentionally not called, so the token stays uncached for retry on the next sync. All auto-renewing change detection functionality from #3198 is preserved.Test plan
PostPendingTransactionsHelperTestauto-renewing tests passsynced transactions do not call addSuccessfullyPostedToken directlyDeviceCacheTestpassesentersOfflineEntitlementsModeIfCachedCustomerInfoAndPostingPendingPurchasesReturns500passespostsPurchasePerformedOnFallbackURLWhenRecoveringToMainServerpasses🤖 Generated with Claude Code
Note
Medium Risk
Touches purchase sync caching behavior by removing an eager
addSuccessfullyPostedTokencall; incorrect behavior could affect retry/sync reliability, but change is small and covered by updated tests.Overview
Removes the
deviceCache.addSuccessfullyPostedTokenside effect fromPostPendingTransactionsHelper’stransactionPostSuccess, relying onbilling.consumeAndSaveto persist posted tokens andisAutoRenewingmetadata.Clarifies the intent of
saveAutoRenewingStatusfor legacy-migrated tokens and updates tests to assertaddSuccessfullyPostedTokenis not called directly for either auto-renewing-change resyncs or newly-synced purchases.Written by Cursor Bugbot for commit b38bab1. This will update automatically on new commits. Configure here.