Skip to content

[Billing Plans]: Include InstallmentsInfo in products in offering's packages#6784

Merged
fire-at-will merged 50 commits into
billing-plans-devfrom
include-billing-plans-in-offerings-response
May 14, 2026
Merged

[Billing Plans]: Include InstallmentsInfo in products in offering's packages#6784
fire-at-will merged 50 commits into
billing-plans-devfrom
include-billing-plans-in-offerings-response

Conversation

@fire-at-will

@fire-at-will fire-at-will commented May 13, 2026

Copy link
Copy Markdown
Contributor

Description

This PR updates the SDK's Offering fetching logic so that it now includes packages with compound billing identifiers, and populates the InstallmentsInfo object on the products in the packages. No new work was required in how the products themselves are fetched, we just needed to update how the SDK decides which products to use when hydrating the products for a given offering.


Note

Medium Risk
Changes how offerings map backend packages to fetched products by switching to compound product identifiers (productId:planId) and updating StoreProduct equality/hash semantics, which could affect product caching/lookups and missing-product handling. Behavior is covered by new/expanded unit tests but touches core purchasing/offering hydration paths.

Overview
Offerings fetching/hydration now treats package product IDs as compound identifiers when a backend platformProductPlanIdentifier is present, so the SDK requests and matches products using productIdentifier:planIdentifier (falling back to the base ID when absent).

This introduces a unified StoreProductType.id (SK1 = base ID, SK2 = compound ID) and updates StoreProduct equality/hash plus OfferingsManager product dictionaries to use id, enabling billing-plan-specific packages to resolve to the correct product (including InstallmentsInfo).

Adds InstallmentsInfo isEqual/hash implementations and expands test coverage with new TestStoreProductTests and additional StoreProductTests assertions around id behavior.

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

@fire-at-will fire-at-will changed the title Include billing plans in offerings response [Billing Plans]: Include billing plans products in offerings May 13, 2026

public override func isEqual(_ object: Any?) -> Bool {
return self.productIdentifier == (object as? StoreProductType)?.productIdentifier
guard let other = object as? StoreProductType else { return false }

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.

Another alternative here would be to perform == on the internal StoreProduct.compoundProductIdentifier, but somehow this felt more "complete" to me. Let me know if y'all disagree!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think comparing compoundProductIdentifier should be equivalent to comparing installmentsInfo, but a bit clearer?

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.

Makes sense. I've renamed compoundProductIdentifier to id to match what we're doing on Android and changed this equals to just compare on id here: c4773bb

@fire-at-will fire-at-will marked this pull request as ready for review May 13, 2026 21:58
@fire-at-will fire-at-will requested a review from a team as a code owner May 13, 2026 21:58
Comment thread Sources/Purchasing/StoreKitAbstractions/StoreProduct.swift Outdated
@fire-at-will fire-at-will changed the title [Billing Plans]: Include billing plans products in offerings [Billing Plans]: Include InstallmentsInfo in products in offering packages May 13, 2026
@fire-at-will fire-at-will changed the title [Billing Plans]: Include InstallmentsInfo in products in offering packages [Billing Plans]: Include InstallmentsInfo in products in offering's packages May 13, 2026
Comment thread Sources/Purchasing/StoreKitAbstractions/Test Data/TestStoreProduct.swift Outdated
Comment thread Sources/Purchasing/StoreKitAbstractions/StoreProduct.swift Outdated
Comment thread Tests/StoreKitUnitTests/StoreProductTests.swift Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

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, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 7fc6244. Configure here.

productPlanIdentifier: data.platformProductPlanIdentifier
)?.compoundProductIdentifier ?? data.platformProductIdentifier

guard let product = productsByID[compoundProductIdentifier] else {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Packages with billing plans silently dropped on older iOS

High Severity

On iOS < 26.4 (where billing plans aren't supported), packages with a platformProductPlanIdentifier will silently fail to be created. The productsByID dictionary is keyed by product.id which returns the base identifier (e.g., "com.app.sub") since populateSK2CompoundProductsIfSupported doesn't modify products on older OS. However, createPackage computes a compound lookup key (e.g., "com.app.sub:monthly") that won't match any dictionary entry. Before this PR, both sides used the base platformProductIdentifier, so they always matched.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 7fc6244. Configure here.

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.

This is okay - if the billing plan product isn't available, we shouldn't return a package for it

@fire-at-will

Copy link
Copy Markdown
Contributor Author

Got a verbal approval from @MarkVillacampa in Slack, will go ahead and merge into dev.

@fire-at-will fire-at-will merged commit 221ab69 into billing-plans-dev May 14, 2026
42 of 43 checks passed
@fire-at-will fire-at-will deleted the include-billing-plans-in-offerings-response branch May 14, 2026 17:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants