Skip to content

[EXPERIMENTAL]: Beta Galaxy Store Support#2903

Merged
fire-at-will merged 63 commits into
mainfrom
samsung-dev
Mar 11, 2026
Merged

[EXPERIMENTAL]: Beta Galaxy Store Support#2903
fire-at-will merged 63 commits into
mainfrom
samsung-dev

Conversation

@fire-at-will

@fire-at-will fire-at-will commented Dec 8, 2025

Copy link
Copy Markdown
Contributor

Description

Beta support for the Galaxy Store in the native Android SDK.


Note

High Risk
Introduces a new Galaxy billing integration (purchase, restore, product fetching, and subscription change flows) and wires it into CI/release packaging, which is core monetization logic and sensitive to store edge cases and dependency/versioning issues.

Overview
Adds an experimental feature:galaxy module to support Samsung Galaxy Store subscriptions, including a GalaxyConfiguration builder, a GalaxyBillingWrapper implementation, and conversion/handler layers for product details, purchases, acknowledges, restores, and subscription plan changes.

Updates build and CI/release tooling to conditionally include the Galaxy feature when the Samsung IAP AAR is available, download the AAR in CI via a new downloadSamsungIapAar task, run a new test-galaxy job, and publish a Galaxy artifact. Updates api-tester and the purchase-tester sample to optionally compile/run with Galaxy support (new store selection, persistence, and warning/reflective setup when the module/AAR isn’t present).

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

### Description
Introduces the scaffolding for several classes that will be foundational
to Samsung development as we move forward:
- `GalaxyBillingWrapper`: implementation of `BillingAbstract`. The class
currently satisfies all of the BillingAbstract's requirements, but all
functions are stubbed with `TODO`s
- `GalaxyPurchasingData`: will be used when processing samsung purchases
- `GalaxyStoreProduct`: represents a product from the Samsung store
### Description
Adds the `GALAXY` to the list of supported Store types. 

> Note: This PR will have one failing integration test for the Customer
Center's localized strings not containing the Galaxy Store string until
a backend PR has been merged.
### Description
This PR creates a new module for the Galaxy Store, and allows developers
to set up the Android SDK to use the Galaxy Store when they configure
the SDK, like so:

```kotlin
val configuration = GalaxyConfiguration.Builder(
     context,
     apiKey,
     GalaxyBillingMode.PRODUCTION,
).build()

Purchases.configure(configuration)
```

It also:
- Introduces `GalaxyBillingMode`, an enum required when configuring the
SDK to use the Galaxy Store, which determines the environment that the
Galaxy Store processes IAPs with (prod, test, etc.).
- When a Galaxy configuration is used, `BillingFactory` will wire up a
`GalaxyBillingWrapper`
- Adds unit tests :)
…er (#2906)

### Description
The Galaxy Store doesn't support fetching the current storefront/country
code or showing in-app messages, so this PR:
- Logs + throws an error when `GalaxyBillingWrapper.getStorefront()` is
called
- Adds docs explaining that the storefront functions/properties aren't
supported for the Galaxy Store
- Adds a comment to `GalaxyBillingWrapper` to explicitly call out that
in-app messages aren't supported
@RevenueCat RevenueCat deleted a comment from codecov Bot Dec 9, 2025
@emerge-tools

emerge-tools Bot commented Mar 9, 2026

Copy link
Copy Markdown

📸 Snapshot Test

571 unchanged

Name Added Removed Modified Renamed Unchanged Errored Approval
TestPurchasesUIAndroidCompatibility
com.revenuecat.testpurchasesuiandroidcompatibility
0 0 0 0 314 0 N/A
TestPurchasesUIAndroidCompatibility Paparazzi
com.revenuecat.testpurchasesuiandroidcompatibility.paparazzi
0 0 0 0 257 0 N/A

🛸 Powered by Emerge Tools

@fire-at-will fire-at-will changed the title [DO NOT MERGE]: Galaxy Store Support Initial Galaxy Store Support Mar 10, 2026
@fire-at-will fire-at-will marked this pull request as ready for review March 10, 2026 20:50
@fire-at-will fire-at-will requested review from a team as code owners March 10, 2026 20:50
@fire-at-will fire-at-will requested a review from tonidero March 10, 2026 20:53

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

Gave it a quick glance over. Looks good! One suggestion might be to add to the PR title that this feature is still experimental so it shows in the CHANGELOG.

@fire-at-will fire-at-will changed the title Initial Galaxy Store Support [EXPERIMENTAL]: Beta Galaxy Store Support Mar 11, 2026

@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 3 potential issues.

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

onGetProductsDetailsListener = this,
)

this.inFlightRequest = request

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.

inFlightRequest set after API call causes race condition

High Severity

ProductDataHandler sets inFlightRequest on line 88 after dispatching the request via iapHelper.getProductsDetails() on lines 83-86. If the callback onGetProducts fires before line 88 executes, inFlightRequest will be null, causing the response callbacks (onReceive/onError) to silently not be invoked. Every other handler (PurchaseHandler, AcknowledgePurchaseHandler, GetOwnedListHandler, ChangeSubscriptionPlanHandler, PromotionEligibilityHandler) correctly sets inFlightRequest before dispatching the request.

Fix in Cursor Fix in Web

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.

Will address in a follow-up PR 👍

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

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.

Addressed in #3213

clearInFlightRequest()
onError?.invoke(purchasesError)
return
}

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.

Empty promotion eligibility treated as error blocks products

High Severity

handleSuccessfulGetPromotionEligibilityResponse treats an empty promotionEligibilities list as an error. When users aren't eligible for any promotions (a perfectly valid state), this causes the entire product fetch in ProductDataHandler to fail via the onError callback. Products without promotions or for non-eligible users would never be returned. The error string also references "acknowledgement results," suggesting a copy-paste issue.

Fix in Cursor Fix in Web

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.

Will address in a follow-up PR 👍

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.

I just double checked to confirm, and if a product isn't eligible for a promotion, we still receive a PromotionEligibilityVo object for the product, where pricing = RegularPrice, so this isn't an issue. I'll add a comment to the code to document this.

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.

Added a code comment here: #3214

.filter {
// TO DO: Find out what this returns for OTPs when we support OTPs
it.subscriptionEndDate.parseDateFromGalaxyDateString() > dateProvider.now
}

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.

Uncaught exception in filter blocks serial executor permanently

Medium Severity

In queryPurchases, parseDateFromGalaxyDateString() is called inside a .filter block without a try-catch. This function throws IllegalArgumentException on unparseable dates. Unlike the .map block below which catches this exception, an uncaught throw here prevents finish() from ever being called on the SerialRequestExecutor, permanently blocking all future Galaxy Store operations.

Fix in Cursor Fix in Web

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.

Will address in a follow-up PR 👍

@fire-at-will fire-at-will added this pull request to the merge queue Mar 11, 2026
@fire-at-will fire-at-will removed this pull request from the merge queue due to a manual request Mar 11, 2026
@fire-at-will fire-at-will added this pull request to the merge queue Mar 11, 2026
@fire-at-will fire-at-will removed this pull request from the merge queue due to a manual request Mar 11, 2026
@fire-at-will fire-at-will added this pull request to the merge queue Mar 11, 2026
Merged via the queue into main with commit 3c9fc5d Mar 11, 2026
41 checks passed
@fire-at-will fire-at-will deleted the samsung-dev branch March 11, 2026 15:01
github-merge-queue Bot pushed a commit that referenced this pull request Mar 11, 2026
### Description
Adds a clarifying comment in response to this review comment raised by
Cursor:
#2903 (comment)

I double checked with a manual test to confirm the behavior :)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Comment-only change with no behavioral impact; low risk aside from
potential documentation drift if the upstream API behavior changes.
> 
> **Overview**
> Adds a clarifying comment in
`PromotionEligibilityHandler.handleSuccessfulGetPromotionEligibilityResponse`
explaining that Samsung’s API returns `PromotionEligibilityVo` entries
even for non-eligible products (with `"RegularPrice"`), so an *empty*
success response indicates an unexpected/error condition and links to
the relevant Samsung documentation.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
89a4bef. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
This was referenced Mar 11, 2026
github-merge-queue Bot pushed a commit that referenced this pull request Mar 12, 2026
**This is an automatic release.**

## RevenueCat SDK
### ✨ New Features
* [EXPERIMENTAL]: Beta Galaxy Store Support (#2903) via Will Taylor
(@fire-at-will)
### 🐞 Bugfixes
* Skip installation on GCP CLI in run-firebase-test (#3218) via Will
Taylor (@fire-at-will)
* Fix reduced timeouts being used for HTTP requests when a proxy URL is
configured (#3188) via Rick (@rickvdl)

## RevenueCatUI SDK
### 🐞 Bugfixes
* Fix missing ripple effect in View-based paywall wrappers (#3206) via
Toni Rico (@tonidero)
### Paywallv2
#### ✨ New Features
* Rules v0 Integration branch (#3117) via Cesar de la Vega (@vegaro)

### 🔄 Other Changes
* [Galaxy]: Add promotionEligibilities comment (#3214) via Will Taylor
(@fire-at-will)
* [EXTERNAL] Migrate deprecated buildDir to layout API (#3202)
contributed by @AlexanderTalledo (#3212) via Toni Rico (@tonidero)
* Remove automatic Claude code review workflow (#3211) via Cesar de la
Vega (@vegaro)
* Remove unused convention plugin (#3195) via Toni Rico (@tonidero)
* [EXTERNAL] Integrate convention plugins into Version Catalogs (#3181)
contributed by @AlexanderTalledo (#3194) via Toni Rico (@tonidero)
* [EXTERNAL] Migrate androidx cardview dependency to version catalogs
(#3192) contributed by @AlenxanderTalledo (#3193) via Toni Rico
(@tonidero)
* Improve AdMob adapter test coverage (#3204) via Pol Miro (@polmiro)
* Bump fastlane-plugin-revenuecat_internal from `f5c099b` to `e146447`
(#3197) via dependabot[bot] (@dependabot[bot])
* Fix integration tests (#3196) via Toni Rico (@tonidero)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Low risk release bookkeeping: version strings and deployment paths are
updated from `9.24.0-SNAPSHOT` to `9.24.0`, plus changelog/docs refresh.
Main risk is accidental publishing/docs deployment to the wrong
versioned location.
> 
> **Overview**
> Cuts the `9.24.0` release by updating all version references from
`9.24.0-SNAPSHOT` to `9.24.0` (root `.version`, `gradle.properties`, and
`Config.frameworkVersion`), and aligning sample/test app version
catalogs to consume the released artifact.
> 
> Updates documentation publishing to point at the `9.24.0` directory
(CircleCI S3 sync and `docs/index.html` redirect) and refreshes
`CHANGELOG.latest.md`/`CHANGELOG.md` with the `9.24.0` release notes.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
8e6d567. 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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants