Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,9 +202,10 @@ let store = TransactionStore(
## Unrecognized subscriptions

A non-upgraded product added to the same subscription group after this binary
ships can still arrive through another device or purchase path. By default, the
store keeps the verified raw transaction, grants no typed entitlement, and
leaves an unfinished delivery unfinished.
ships can still arrive after a plan change in Manage Subscriptions, an offer
code or win-back offer redeemed through the App Store, or a purchase on another
device. By default, the store keeps the verified raw transaction, grants no
typed entitlement, and leaves an unfinished delivery unfinished.

Supply a separate delegate only when the app knows how to handle that product:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,23 @@
Follow a transaction from admission through policy, finishing, entitlement
publication, failure ownership, and shutdown.

## App-owned purchase initiation

StoreTransactionKit starts handling a purchase when the app passes a
`Product.PurchaseResult` to ``TransactionStore/process(_:)`` or when the live
store receives a transaction through StoreKit's monitoring or reconciliation
paths. It doesn't observe `PurchaseIntent.intents` or initiate purchases.

On platforms where `PurchaseIntent` is available, StoreKit can emit an intent
for a supported App Store-initiated purchase, such as a promoted In-App Purchase
or a win-back offer when Streamlined Purchasing is disabled. The app owns
observing the sequence while it is running. Call `purchase(options:)` on the
intent's product, include any purchase options required by the intent, and pass
the resulting `Product.PurchaseResult` to
``TransactionStore/process(_:)``. Completed purchases that StoreKit instead
exposes through `Transaction.updates` or `Transaction.currentEntitlements`
enter the store's monitoring and reconciliation paths without app forwarding.

## Exact-revision processing

A direct `Product.PurchaseResult`, `Transaction.updates`, and
Expand Down Expand Up @@ -80,6 +97,11 @@ doesn't block publication or repeat its decision in the same store session.
Current entitlements that were finished by another process or device may still
require an unrecognized-subscription decision for typed projection.

The live store observes `Product.SubscriptionInfo.Status.updates`.
Subscription-status changes trigger current-entitlement reconciliation; status
isn't a second entitlement source and isn't copied into the app's entitlement
type.

``TransactionStore/entitlements``,
``TransactionStore/activeEntitlements``, and
``TransactionStore/entitlementStatus`` derive from one availability value:
Expand Down
16 changes: 9 additions & 7 deletions Sources/StoreTransactionKit/TransactionStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,12 @@ where Entitlement: Hashable & Sendable {

/// Creates the process's live StoreKit store for an auto-renewable subscription catalog.
///
/// Initialization starts transaction monitoring and the first entitlement
/// reconciliation. The store strongly retains both delegates until terminal
/// shutdown. Creating a second live store in the same process before the
/// first store finishes ``close()`` is a programmer error. Without
/// `unrecognizedSubscriptionDelegate`, valid non-upgraded undeclared
/// same-group subscriptions use
/// Initialization starts transaction and subscription-status monitoring,
/// along with the first entitlement reconciliation. The store strongly
/// retains both delegates until terminal shutdown. Creating a second live
/// store in the same process before the first store finishes ``close()`` is
/// a programmer error. Without `unrecognizedSubscriptionDelegate`, valid
/// non-upgraded undeclared same-group subscriptions use
/// ``UnrecognizedSubscriptionPolicy/leaveUnfinished``.
public convenience init(
subscriptionCatalog: AutoRenewableSubscriptionCatalog<Entitlement>,
Expand Down Expand Up @@ -257,8 +257,10 @@ where Entitlement: Hashable & Sendable {
activeEntitlements?.contains(entitlement) == true
}

/// Processes a direct result from custom purchase UI.
/// Processes a StoreKit purchase result supplied by the app.
///
/// Use this method for a result from `Product.purchase(options:)`, including
/// a purchase the app completes after observing `PurchaseIntent.intents`.
/// A verified purchase returns only after policy selection, the selected
/// finish or leave action, causal entitlement reconciliation, and main-actor
/// publication. Pending and user-cancelled results return their corresponding
Expand Down