Skip to content

Don't log App Store / StoreKit messages when using a Test Store API key#6906

Merged
rickvdl merged 12 commits into
mainfrom
fix-simulated-store-appstore-logging
Jun 5, 2026
Merged

Don't log App Store / StoreKit messages when using a Test Store API key#6906
rickvdl merged 12 commits into
mainfrom
fix-simulated-store-appstore-logging

Conversation

@rickvdl

@rickvdl rickvdl commented Jun 4, 2026

Copy link
Copy Markdown
Member

When using a test store API key we were still making some StoreKit calls and logging it’s errors (which is quite likely when using test store, since chances are high that the product’s haven’t been configured on the ASC / StoreKit config side yet) .

This was confusing, as you’d use test store in cases where you actually don’t want to use StoreKit.

Changes made

  • When using test store we’re no longer looking at the StoreKit Transaction updates on configure and are no longer listening for transaction updates
  • Updated a bunch of info/warning/error logging strings to have variants that don’t mention the App Store / ASC / StoreKit when using test store

Note

Medium Risk
Touches configure-time transaction fetching and customer-info paths plus widespread offering error messages; behavior change is intentional for Test Store but affects core purchase/offering flows.

Overview
Test Store API keys now avoid StoreKit transaction plumbing and surface configuration messages that match how that mode works.

StoreKit bypass: Purchases wires in SimulatedStoreTransactionFetcher instead of StoreKit2TransactionFetcher when the key is simulated. That fetcher always returns empty/nil transaction data (with a warning if a receipt fetch is attempted). PurchasesOrchestrator skips starting StoreKit.Transaction.updates listening and SK2 purchase-intent listening in simulated-store mode. Customer info still goes through the backend when there are no unfinished transactions (covered by new tests).

Logging: Configure, offering, and purchase log strings now take apiKeyValidationResult and branch copy for Test Store vs App Store / legacy vs other platforms—e.g. simulator setup no longer tells you to use a StoreKit Configuration file when using a Test Store key; offering empty/missing-product errors point at RevenueCat Test Store config instead of App Store Connect; the iOS 18.4 simulator workaround text is suppressed for Test Store.

Offerings: OfferingsFactory holds SystemInfo so empty-offering warnings use the same API-key-aware messages. OfferingsManager passes validation result through empty-product and missing-product errors.

Tests: New/updated unit tests for string variants, transaction fetcher, orchestrator SK2 behavior, and customer info with simulated store.

Reviewed by Cursor Bugbot for commit 7d87d1f. Bugbot is set up for automated code reviews on this repo. Configure here.

@rickvdl rickvdl added the pr:fix A bug fix label Jun 4, 2026
@rickvdl rickvdl force-pushed the fix-simulated-store-appstore-logging branch from f1b01ab to eb4ac57 Compare June 4, 2026 08:50
"RevenueCat dashboard could be fetched from App Store Connect (or the StoreKit Configuration " +
"file if one is being used). \nMore information: https://rev.cat/why-are-offerings-empty"
case .simulatedStore:
return "There's a problem with your configuration. None of the Test Store products registered in " +

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This shouldn’t happen in theory, but updated it just in case

@rickvdl

rickvdl commented Jun 4, 2026

Copy link
Copy Markdown
Member Author

@RCGitBot please test

@rickvdl rickvdl marked this pull request as ready for review June 4, 2026 10:05
@rickvdl rickvdl requested review from a team as code owners June 4, 2026 10:05

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

Nice one! This is great, thank you!!


// The Simulated Store ("Test Store") never touches StoreKit, so skip reading
// StoreKit transactions and the storefront and fetch CustomerInfo from the backend directly.
if self.systemInfo.isSimulatedStoreAPIKey {

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.

Hmm I'm wondering if we would like to create a SimulatedStoreTransactionFetcher that can be used in these scenarios, and can be used to simulate cases when using the store down the line. As an initial version it could always return no transactions...

But this is just an idea and wouldn't add any value right now. Since this is probably easier for now, I think this is ok :)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good point! I did think about that, but didn’t end up implementing it. But I do agree that it’s a bit cleaner, so I made the change now :)

"\nThere is a problem with your configuration in App Store Connect. " +
"\nMore info here: https://errors.rev.cat/configuring-products"
case .simulatedStore:
return "Could not find products with identifiers: \(identifiers)." +

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.

I "believe" this shouldn't really ever happen... But it's ok to add it :)

@@ -2338,6 +2338,11 @@ extension PurchasesOrchestrator {

private func setSK2DelegateAndStartListening() async {
await storeKit2TransactionListener.set(delegate: self)

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.

Just to make sure. Is it fine to still set the delegate?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think so since we didn’t start listening. But you’re right in the sense that it isn’t guaranteed, so I changed it to not setting the delegate at all

@rickvdl rickvdl force-pushed the fix-simulated-store-appstore-logging branch from 4b81b79 to c0edc3f Compare June 4, 2026 12:48
@rickvdl

rickvdl commented Jun 4, 2026

Copy link
Copy Markdown
Member Author

@RCGitBot please test

1 similar comment
@rickvdl

rickvdl commented Jun 4, 2026

Copy link
Copy Markdown
Member Author

@RCGitBot please test

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

🚢

}

/// Unused in Simulated Store mode: no transactions are ever posted, so no receipt is fetched.
/// Returns an empty receipt to satisfy the protocol.

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.

Could be worth logging something in this case just in case?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Added some logging for this

@rickvdl rickvdl force-pushed the fix-simulated-store-appstore-logging branch from c98a641 to 7d87d1f Compare June 5, 2026 05:26
@rickvdl

rickvdl commented Jun 5, 2026

Copy link
Copy Markdown
Member Author

@RCGitBot please test

@rickvdl rickvdl merged commit 7144994 into main Jun 5, 2026
43 checks passed
@rickvdl rickvdl deleted the fix-simulated-store-appstore-logging branch June 5, 2026 06:20
@ajpallares

Copy link
Copy Markdown
Member

Great one! Thanks for doing this!

matteinn pushed a commit to matteinn/purchases-android that referenced this pull request Jun 5, 2026
… Store is used (RevenueCat#3538)

Android counterpart for
RevenueCat/purchases-ios#6906, making offering
product loading errors test-store aware. This prevents these errors from
pointing developers to the Play Store when Test Store is used.

These errors should rarely happen (if at all) but figured it was good to
add for consistency.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> String-only branching on configured store for logs and configuration
errors; behavior unchanged aside from messaging, with unit test
coverage.
> 
> **Overview**
> When the SDK is configured for **Test Store**, offering/product
configuration errors no longer tell developers to fix things in the
**Play Store**. The change threads `appConfig.store` into offerings
building and picks **Test Store–specific** copy from `OfferingStrings`
instead of the Play Store templates.
> 
> **`OfferingsFactory`** now uses Test Store strings for missing-product
warnings and for the configuration error when no dashboard products
resolve. **`OfferingParser.createOfferings`** gains a `configuredStore`
parameter (default Play Store) and routes empty-offering warnings
through a small helper that selects the right template. New constants
cover cannot-find-product, products-not-found, and empty-offering cases
for Test Store (RevenueCat dashboard wording). Tests assert string
content and that Test Store flows return the Test Store configuration
error without mentioning Play Store.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
4a1b9c3. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/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.

3 participants