Skip to content

Fix & Standardize Galaxy Date Parsing Edge Cases#3216

Merged
fire-at-will merged 12 commits into
mainfrom
galaxy/fix-date-parsing-issue
Mar 16, 2026
Merged

Fix & Standardize Galaxy Date Parsing Edge Cases#3216
fire-at-will merged 12 commits into
mainfrom
galaxy/fix-date-parsing-issue

Conversation

@fire-at-will

@fire-at-will fire-at-will commented Mar 11, 2026

Copy link
Copy Markdown
Contributor

Description

Cursor pointed out that if subscription end date parsing fails, an exception is thrown and finish() is never called, which can halt execution. This PR fixes that by handling subscription end date parsing failures gracefully and ignoring the affected product instead of failing the whole flow.

Additionally, this PR updates the .toStoreTransaction() calls in queryPurchases() and queryAllPurchases() so that individual parsing failures cause only the affected transaction to be skipped, while successfully parsed transactions are still returned. The methods now fail only if parsing fails for all transactions.

This matches the existing partial-failure behavior used by the Amazon implementation of queryPurchases() and queryAllPurchases().


Note

Medium Risk
Changes purchase restore/query behavior to skip malformed Galaxy owned products and only error when all transactions are invalid, which could affect edge-case restore results. Also adds new date-parse error handling to avoid stalled serial execution on parsing exceptions.

Overview
Hardens Galaxy purchase restore/query flows against malformed store data. queryAllPurchases and queryPurchases now use mapNotNull to skip individual owned products that fail toStoreTransaction() parsing, and only return InvalidReceiptError when the owned list is non-empty but no valid transactions could be parsed.

queryPurchases additionally guards subscriptionEndDate.parseDateFromGalaxyDateString() with a try/catch, logging and filtering out entries with invalid end dates so serial execution always completes. New standardized strings were added for transaction and subscription-end-date parsing errors, and tests were updated/added to cover partial-failure filtering and the all-invalid error case.

Written by Cursor Bugbot for commit 6c69286. This will update automatically on new commits. Configure here.

@fire-at-will fire-at-will requested a review from a team as a code owner March 11, 2026 17:22
@fire-at-will fire-at-will added the pr:fix A bug fix label Mar 11, 2026
@codecov

codecov Bot commented Mar 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.41%. Comparing base (9768055) to head (6c69286).
⚠️ Report is 6 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #3216   +/-   ##
=======================================
  Coverage   79.41%   79.41%           
=======================================
  Files         356      356           
  Lines       14342    14342           
  Branches     1958     1958           
=======================================
  Hits        11389    11389           
  Misses       2149     2149           
  Partials      804      804           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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

This PR might be missing a part? Lmk if I'm missing something though! In any case, I think the changes in this PR make sense!

import java.util.Locale
import java.util.TimeZone

@Throws(IllegalArgumentException::class)

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.

Unfortunately kotlin doesn't force callers to handle exceptions :( But I think it's better to add it 👍

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed - better to have it than not!

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Comment thread feature/galaxy/src/main/kotlin/com/revenuecat/purchases/galaxy/GalaxyStrings.kt Outdated
@fire-at-will fire-at-will changed the title Fix Galaxy date parsing issue when fetching purchases Fix & Standardize Galaxy Date Parsing Edge Cases Mar 13, 2026
@fire-at-will

Copy link
Copy Markdown
Contributor Author

@tonidero Good callout on the finally block. I've removed it from the description. I decided not to use the finally pattern because if the onSuccess() callback threw any exception for any reason, the exception would be caught, and then the onError() callback would be executed, which would cause the function's onSuccess() and onError() callbacks to be executed, which definitely isn't the intended behavior.

I've also updated the parsing code in both queryPurchases() and queryAllPurchases() so that when parsing a transaction to a StoreTransaction fails, we log an error and then gracefully ignore that transaction when we return the results. We only error out if the parsing for all transactions fail. This mirrors the pattern used by the Amazon store.

Let me know what you think!

@fire-at-will fire-at-will requested a review from tonidero March 13, 2026 15:47

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

Makes sense!

@fire-at-will fire-at-will added this pull request to the merge queue Mar 16, 2026
Merged via the queue into main with commit 57ac08c Mar 16, 2026
30 checks passed
@fire-at-will fire-at-will deleted the galaxy/fix-date-parsing-issue branch March 16, 2026 15:58
github-merge-queue Bot pushed a commit that referenced this pull request Mar 16, 2026
**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 -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr:fix A bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants