Skip to content

Add an internal wrapper over StoreKit's ExternalPurchaseCustomLink - #7594

Open
ajpallares wants to merge 6 commits into
mainfrom
pallares/SDK-4472-external-purchase-custom-link-wrapper
Open

Add an internal wrapper over StoreKit's ExternalPurchaseCustomLink#7594
ajpallares wants to merge 6 commits into
mainfrom
pallares/SDK-4472-external-purchase-custom-link-wrapper

Conversation

@ajpallares

@ajpallares ajpallares commented Sep 3, 2026

Copy link
Copy Markdown
Member

Checklist

  • If applicable, unit tests
  • If applicable, create follow-up issues for purchases-android and hybrids

Motivation

First step towards supporting Apple's external purchase custom links. Nothing calls it yet; later PRs build the token retrieval, disclosure notice and backend reporting on top.

Description

  • The protocol keeps StoreKit's types and availability out of its signature, so consumers up the stack — eventually a RevenueCatUI paywall button — don't inherit @available(iOS 18.1, *), and mocks stay usable on the iOS 15–17 simulators CI runs.
  • Gated on #if compiler(>=6.0.2): the API ships in the iOS 18.1 SDK (Xcode 16.1), and CI still builds with Xcode 14.3.1 and 15.4. Both branches of the gate compile.

Note

Low Risk
Internal-only scaffolding with no public API or production call sites yet; availability and compiler gating limit blast radius.

Overview
Introduces an internal ExternalPurchase layer under Purchasing as the first step toward Apple’s external purchase custom links. Nothing in the SDK calls it yet; follow-up work is expected to wire tokens, disclosure UI, and backend reporting.

The new ExternalPurchaseCustomLinkType protocol exposes eligibility, token retrieval, and disclosure notice flows using SDK-owned types so upper layers (e.g. future paywall UI) avoid @available(iOS 18.1, *) and tests can use mocks on older simulators. StoreKitExternalPurchaseCustomLink implements it via StoreKit’s ExternalPurchaseCustomLink, combining StoreKit eligibility with existing PaymentAuthorizationProvider checks. StoreKit usage is gated on #if compiler(>=6.0.2) and iOS 18.1+ (and aligned OS versions) so older Xcode CI toolchains still compile.

Supporting types include forward-compatible ExternalPurchaseTokenType (IN_APP / LINK_OUT), notice type/result enums, and ExternalPurchaseError.apiUnavailable. Unit tests cover mappings, availability behavior, and payment authorization; MockExternalPurchaseCustomLink is added for future callers.

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

@emerge-tools

emerge-tools Bot commented Sep 3, 2026

Copy link
Copy Markdown

⚠️ 3 new unused protocols, 4 builds increased size

Name Version Download Change Install Change Approval
RevenueCat
com.revenuecat.PaywallsTester
1.0 (1) 20.3 MB ⬆️ 13.5 kB (0.07%) 73.9 MB ⬆️ 50.7 kB (0.07%) N/A
BinarySizeTest
com.revenuecat.binary-size-test.local-source
1.0 (1) 4.9 MB ⬇️ 501 B (-0.01%) 14.6 MB ⬆️ 156 B N/A
BinarySizeTest
com.revenuecat.binary-size-test.cocoapods
1.0 (1) 7.3 MB ⬆️ 138 B 31.8 MB ⬆️ 156 B N/A
BinarySizeTest
com.revenuecat.binary-size-test.spm
1.0 (1) 4.9 MB ⬇️ 289 B 12.7 MB ⬆️ 156 B N/A

RevenueCat 1.0 (1)
com.revenuecat.PaywallsTester

⚠️ Found new unused protocol: ExternalPurchaseCustomLinkType
⚖️ Compare build
⏱️ Analyze build performance

Total install size change: ⬆️ 50.7 kB (0.07%)
Total download size change: ⬆️ 13.5 kB (0.07%)

Largest size changes

Item Install Size Change
DYLD.String Table ⬆️ 16.8 kB
DYLD.Exports ⬆️ 1.7 kB
Code Signature ⬆️ 1.2 kB
DYLD.Fixups ⬆️ 1.1 kB
Other ⬆️ 29.8 kB
View Treemap

Image of diff

BinarySizeTest 1.0 (1)
com.revenuecat.binary-size-test.local-source

⚖️ Compare build
📦 Install build
⏱️ Analyze build performance

Total install size change: ⬆️ 156 B
Total download size change: ⬇️ 501 B (-0.01%)

Largest size changes

Item Install Size Change
Other ⬆️ 156 B
View Treemap

Image of diff

BinarySizeTest 1.0 (1)
com.revenuecat.binary-size-test.cocoapods

⚠️ Found new unused protocol: ExternalPurchaseCustomLinkType
⚖️ Compare build
📦 Install build
⏱️ Analyze build performance

Total install size change: ⬆️ 156 B
Total download size change: ⬆️ 138 B

Largest size changes

Item Install Size Change
DYLD.String Table ⬆️ 608 B
Other ⬇️ -452 B
View Treemap

Image of diff

BinarySizeTest 1.0 (1)
com.revenuecat.binary-size-test.spm

⚠️ Found new unused protocol: ExternalPurchaseCustomLinkType
⚖️ Compare build
📦 Install build
⏱️ Analyze build performance

Total install size change: ⬆️ 156 B
Total download size change: ⬇️ 289 B

Largest size changes

Item Install Size Change
Other ⬆️ 156 B
View Treemap

Image of diff


🛸 Powered by Emerge Tools

@ajpallares
ajpallares force-pushed the pallares/SDK-4472-external-purchase-custom-link-wrapper branch from 9d25064 to 7e985ea Compare September 3, 2026 15:00
@ajpallares
ajpallares marked this pull request as ready for review September 3, 2026 15:00
@ajpallares
ajpallares requested a review from a team as a code owner September 3, 2026 15:00
@ajpallares
ajpallares requested a review from a team September 3, 2026 15:00
@ajpallares
ajpallares force-pushed the pallares/SDK-4472-external-purchase-custom-link-wrapper branch from 7e985ea to 649afe0 Compare September 3, 2026 15:50
ajpallares and others added 6 commits September 4, 2026 09:07
Keeps StoreKit's types and their availability out of the protocol's
signature, so the token retrieval, disclosure notice and backend
reporting built on top of it in later PRs do not each inherit
@available(iOS 18.1, *), and mocks stay usable on the older simulators
CI runs.

The API ships in the iOS 18.1 SDK, so every use of it is gated on
compiler(>=6.0.2) as well as the OS version.

Nothing calls this yet.

Co-authored-by: Cursor <cursoragent@cursor.com>
Apple's implementation checklists for every external purchase flow name
isEligible as the runtime gate and never mention canMakePayments; it
appears once, as a "for more information, see" cross-reference.

AppStore.canMakePayments reports whether the account may pay through
Apple's payment system, which an external purchase deliberately does not
use, so gating on it would hide a legitimate payment path from a
customer whose only restriction is on in-app purchases.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
They asserted that the mock returns what it was stubbed with, so they
covered no production code. The mock stays for the token manager built
on it in a later PR.

Co-authored-by: Cursor <cursoragent@cursor.com>
Apple requires both ExternalPurchaseCustomLink.isEligible and
AppStore.canMakePayments before an app may request an external purchase
token. The two are orthogonal: the first accounts for the storefront, the
second for parental controls, Screen Time and MDM.

Rename isEligible() to canMakeExternalPurchases() so it is not mistaken for
StoreKit's own property, and have it answer both.

Co-authored-by: Cursor <cursoragent@cursor.com>
@ajpallares
ajpallares force-pushed the pallares/SDK-4472-external-purchase-custom-link-wrapper branch from 649afe0 to 666e3d8 Compare September 4, 2026 07:07
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.

2 participants