Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Add support for StoreKit 2 full flow #3529

Closed
wants to merge 4 commits into from
Closed
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"StoreKit2JWSIntegrationTests",
"StoreKit2JWSObserverModeIntegrationTests",
"StoreKit2JWSObserverModeWithExistingPurchasesTests",
"StoreKit2NotEnabledObserverModeIntegrationTests",
"StoreKit2ObserverModeIntegrationTests",
"StoreKit2ObserverModeWithExistingPurchasesTests",
"SubscriberAttributesManagerIntegrationTests",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"StoreKit2JWSIntegrationTests",
"StoreKit2JWSObserverModeIntegrationTests",
"StoreKit2JWSObserverModeWithExistingPurchasesTests",
"StoreKit2NotEnabledObserverModeIntegrationTests",
"StoreKit2ObserverModeIntegrationTests",
"StoreKit2ObserverModeWithExistingPurchasesTests",
"TestCase"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"StoreKit2JWSIntegrationTests",
"StoreKit2JWSObserverModeIntegrationTests",
"StoreKit2JWSObserverModeWithExistingPurchasesTests",
"StoreKit2NotEnabledObserverModeIntegrationTests",
"StoreKit2ObserverModeIntegrationTests",
"StoreKit2ObserverModeWithExistingPurchasesTests",
"SubscriberAttributesManagerIntegrationTests",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {

Purchases.configure(
with: Configuration.Builder(withAPIKey: Constants.apiKey)
.with(usesStoreKit2IfAvailable: true)
.with(storeKitVersion: .storeKit2)
.build()
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ struct MagicWeatherApp: App {

Purchases.configure(
with: Configuration.Builder(withAPIKey: Constants.apiKey)
.with(usesStoreKit2IfAvailable: true)
.with(storeKitVersion: .storeKit2)
.build()
)

Expand Down
12 changes: 4 additions & 8 deletions RevenueCat.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@
4D6ABB0E2AF13FB100BB2A08 /* StoreEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D6ABB0D2AF13FB100BB2A08 /* StoreEnvironment.swift */; };
4D6ABB102AF13FBD00BB2A08 /* SK2AppTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D6ABB0F2AF13FBD00BB2A08 /* SK2AppTransaction.swift */; };
4D72E8622B221EA600BF9EFE /* StoreEnvironmentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D24EF3F2B04EA6000E586D2 /* StoreEnvironmentTests.swift */; };
4DBC30962B1DFA97001D33C7 /* StoreKitVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DBC30952B1DFA97001D33C7 /* StoreKitVersion.swift */; };
4DBF1F362B4D572400D52354 /* LocalReceiptFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DBF1F352B4D572400D52354 /* LocalReceiptFetcher.swift */; };
4DBF1F372B4D572400D52354 /* LocalReceiptFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DBF1F352B4D572400D52354 /* LocalReceiptFetcher.swift */; };
4DC546272AD44BBE005CDB35 /* EncodedAppleReceipt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DC546262AD44BBE005CDB35 /* EncodedAppleReceipt.swift */; };
Expand Down Expand Up @@ -347,7 +348,6 @@
4FFFE6C82AA9467800B2955C /* PaywallEventsManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FFFE6C72AA9467800B2955C /* PaywallEventsManagerTests.swift */; };
4FFFE6CA2AA946A700B2955C /* MockInternalAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FFFE6C92AA946A700B2955C /* MockInternalAPI.swift */; };
4FFFE6E72AA948A600B2955C /* PaywallEventsIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FFFE6E62AA948A600B2955C /* PaywallEventsIntegrationTests.swift */; };
57032ABF28C13CE4004FF47A /* StoreKit2SettingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57032ABE28C13CE4004FF47A /* StoreKit2SettingTests.swift */; };
57045B3829C514A8001A5417 /* ProductEntitlementMappingDecodingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57045B3729C514A8001A5417 /* ProductEntitlementMappingDecodingTests.swift */; };
57045B3A29C51751001A5417 /* GetProductEntitlementMappingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57045B3929C51751001A5417 /* GetProductEntitlementMappingOperation.swift */; };
57045B3C29C51AF7001A5417 /* ProductEntitlementMappingCallback.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57045B3B29C51AF7001A5417 /* ProductEntitlementMappingCallback.swift */; };
Expand Down Expand Up @@ -530,7 +530,6 @@
57CD86E6291C344000768DE1 /* UserDefaultsDefaultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57CD86E5291C344000768DE1 /* UserDefaultsDefaultTests.swift */; };
57CFB96C27FE0E79002A6730 /* MockCurrentUserProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57CFB96B27FE0E79002A6730 /* MockCurrentUserProvider.swift */; };
57CFB96D27FE0E79002A6730 /* MockCurrentUserProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57CFB96B27FE0E79002A6730 /* MockCurrentUserProvider.swift */; };
57CFB98427FE2258002A6730 /* StoreKit2Setting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57CFB98327FE2258002A6730 /* StoreKit2Setting.swift */; };
57D04BB827D947C6006DAC06 /* HTTPResponseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57D04BB727D947C6006DAC06 /* HTTPResponseTests.swift */; };
57D5412E27F6311C004CC35C /* OfferingsResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57D5412D27F6311C004CC35C /* OfferingsResponse.swift */; };
57D5414227F656D9004CC35C /* NetworkError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57D5414127F656D9004CC35C /* NetworkError.swift */; };
Expand Down Expand Up @@ -1002,6 +1001,7 @@
4D6ABB0B2AF13F9400BB2A08 /* StoreKit2Receipt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoreKit2Receipt.swift; sourceTree = "<group>"; };
4D6ABB0D2AF13FB100BB2A08 /* StoreEnvironment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoreEnvironment.swift; sourceTree = "<group>"; };
4D6ABB0F2AF13FBD00BB2A08 /* SK2AppTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SK2AppTransaction.swift; sourceTree = "<group>"; };
4DBC30952B1DFA97001D33C7 /* StoreKitVersion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreKitVersion.swift; sourceTree = "<group>"; };
4DBF1F352B4D572400D52354 /* LocalReceiptFetcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalReceiptFetcher.swift; sourceTree = "<group>"; };
4DC546262AD44BBE005CDB35 /* EncodedAppleReceipt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EncodedAppleReceipt.swift; sourceTree = "<group>"; };
4F0201C32A13C85500091612 /* Assertions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Assertions.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1109,7 +1109,6 @@
4FFFE6C72AA9467800B2955C /* PaywallEventsManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaywallEventsManagerTests.swift; sourceTree = "<group>"; };
4FFFE6C92AA946A700B2955C /* MockInternalAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockInternalAPI.swift; sourceTree = "<group>"; };
4FFFE6E62AA948A600B2955C /* PaywallEventsIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaywallEventsIntegrationTests.swift; sourceTree = "<group>"; };
57032ABE28C13CE4004FF47A /* StoreKit2SettingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreKit2SettingTests.swift; sourceTree = "<group>"; };
57045B3729C514A8001A5417 /* ProductEntitlementMappingDecodingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductEntitlementMappingDecodingTests.swift; sourceTree = "<group>"; };
57045B3929C51751001A5417 /* GetProductEntitlementMappingOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetProductEntitlementMappingOperation.swift; sourceTree = "<group>"; };
57045B3B29C51AF7001A5417 /* ProductEntitlementMappingCallback.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductEntitlementMappingCallback.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1284,7 +1283,6 @@
57CD86D9291C1E2300768DE1 /* UserDefaults+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaults+Extensions.swift"; sourceTree = "<group>"; };
57CD86E5291C344000768DE1 /* UserDefaultsDefaultTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsDefaultTests.swift; sourceTree = "<group>"; };
57CFB96B27FE0E79002A6730 /* MockCurrentUserProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockCurrentUserProvider.swift; sourceTree = "<group>"; };
57CFB98327FE2258002A6730 /* StoreKit2Setting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreKit2Setting.swift; sourceTree = "<group>"; };
57D04BB727D947C6006DAC06 /* HTTPResponseTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPResponseTests.swift; sourceTree = "<group>"; };
57D5412D27F6311C004CC35C /* OfferingsResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OfferingsResponse.swift; sourceTree = "<group>"; };
57D5414127F656D9004CC35C /* NetworkError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkError.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1801,10 +1799,10 @@
57ABA76C28F08DDA003D9181 /* Either.swift */,
F530E4FE275646EF001AF6BD /* MacDevice.swift */,
57EAE52A274332830060EB74 /* Obsoletions.swift */,
4DBC30952B1DFA97001D33C7 /* StoreKitVersion.swift */,
2CB8CF9227BF538F00C34DE3 /* PlatformInfo.swift */,
F5FCD3E927DA0D0B003BDC04 /* PriceFormatterProvider.swift */,
57FDAAB9284937A0009A48F1 /* SandboxEnvironmentDetector.swift */,
57CFB98327FE2258002A6730 /* StoreKit2Setting.swift */,
B3AA6237268B926F00894871 /* SystemInfo.swift */,
4FEF41AA2B4F2F3400CD699F /* MapAppStoreDetector.swift */,
);
Expand Down Expand Up @@ -2052,7 +2050,6 @@
57ACB12328174B9F000DCC9F /* CustomerInfo+TestExtensions.swift */,
57FDAABD28493A29009A48F1 /* SandboxEnvironmentDetectorTests.swift */,
579189E828F47E8D00BF4963 /* PurchasesDiagnosticsTests.swift */,
57032ABE28C13CE4004FF47A /* StoreKit2SettingTests.swift */,
5712BE9129241F7900A83F15 /* TimingUtilTests.swift */,
4F8DDB682AAA9189000188F2 /* OperationDispatcherTests.swift */,
4FEF41AC2B4F301800CD699F /* MacAppStoreDetectorTests.swift */,
Expand Down Expand Up @@ -3404,7 +3401,6 @@
4F6BEDE22A26B69500CD9322 /* DebugContentViews.swift in Sources */,
B3B5FBBC269D121B00104A0C /* Offerings.swift in Sources */,
9A65E03B25918B0900DE00B0 /* CustomerInfoStrings.swift in Sources */,
57CFB98427FE2258002A6730 /* StoreKit2Setting.swift in Sources */,
5721360F28B4602C006C46BE /* Purchases+nonasync.swift in Sources */,
57DE806D28074976008D6C6F /* Storefront.swift in Sources */,
B3B5FBB6269CED6400104A0C /* ErrorDetails.swift in Sources */,
Expand Down Expand Up @@ -3642,6 +3638,7 @@
57C2931528BFEF4F0054EDFC /* PurchasesError.swift in Sources */,
57FD7B1528DA4037009CA4E4 /* PurchasesType.swift in Sources */,
57C381DA2796153D009E3940 /* SK1StoreProductDiscount.swift in Sources */,
4DBC30962B1DFA97001D33C7 /* StoreKitVersion.swift in Sources */,
57DE807328074C76008D6C6F /* SK2Storefront.swift in Sources */,
57A17727276A721D0052D3A8 /* Set+Extensions.swift in Sources */,
4DC546272AD44BBE005CDB35 /* EncodedAppleReceipt.swift in Sources */,
Expand Down Expand Up @@ -3823,7 +3820,6 @@
57CFB96C27FE0E79002A6730 /* MockCurrentUserProvider.swift in Sources */,
57ACB13728184CF1000DCC9F /* DecoderExtensionTests.swift in Sources */,
351B516126D44BEB00BD2BD7 /* IdentityManagerTests.swift in Sources */,
57032ABF28C13CE4004FF47A /* StoreKit2SettingTests.swift in Sources */,
579D2E3A28F0BF5A0094B36F /* BackendInternalTests.swift in Sources */,
351B51C126D450E800BD2BD7 /* OfferingsManagerTests.swift in Sources */,
5796A39927D6C1E000653165 /* BackendPostSubscriberAttributesTests.swift in Sources */,
Expand Down
5 changes: 2 additions & 3 deletions Sources/Error Handling/ErrorCode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,8 @@ extension ErrorCode: DescribableError {
return "The receipt is in use by other subscriber."
case .invalidAppleSubscriptionKeyError:
return """
Apple Subscription Key is invalid or not present. In order to provide subscription offers, you must
first generate a subscription key.
Please see https://docs.revenuecat.com/docs/ios-subscription-offers for more info.
Apple In-App Purchase Key is invalid or not present. You must configure an In-App Purchase Key.
Please see https://rev.cat/in-app-purchase-key-configuration for more info.
"""
case .ineligibleError:
return "The User is ineligible for that action."
Expand Down
33 changes: 24 additions & 9 deletions Sources/Logging/Strings/ConfigureStrings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,12 @@ enum ConfigureStrings {

case debug_enabled

case store_kit_2_enabled

case observer_mode_enabled

case observer_mode_with_storekit2

case response_verification_mode(Signing.ResponseVerificationMode)

case storekit_version(StoreKitVersion)

case delegate_set

case purchase_instance_already_set
Expand Down Expand Up @@ -75,6 +73,12 @@ enum ConfigureStrings {

case sk2_required_for_swiftui_paywalls

case handle_transaction_observer_mode_required

case sk2_required

case observer_mode_with_storekit2

}

extension ConfigureStrings: LogMessage {
Expand All @@ -96,12 +100,8 @@ extension ConfigureStrings: LogMessage {
"with URL: \(url)"
case .debug_enabled:
return "Debug logging enabled"
case .store_kit_2_enabled:
return "StoreKit 2 support enabled"
case .observer_mode_enabled:
return "Purchases is configured in observer mode"
case .observer_mode_with_storekit2:
return "Observer mode is not currently compatible with StoreKit 2"
case let .response_verification_mode(mode):
switch mode {
case .disabled:
Expand All @@ -111,6 +111,8 @@ extension ConfigureStrings: LogMessage {
case .enforced:
return "Purchases is configured with enforced response verification"
}
case let .storekit_version(version):
return "Purchases is configured with StoreKit version \(version)"
case .delegate_set:
return "Delegate set"
case .purchase_instance_already_set:
Expand Down Expand Up @@ -185,8 +187,21 @@ extension ConfigureStrings: LogMessage {

case .sk2_required_for_swiftui_paywalls:
return "Purchases is not configured with StoreKit 2 enabled. This is required in order to detect " +
"transactions coming from SwiftUI paywalls. You must use `.with(usesStoreKit2IfAvailable: true)` " +
"transactions coming from SwiftUI paywalls. You must use `.with(storeKitVersion: .storeKit2)` " +
"when configuring the SDK."

case .handle_transaction_observer_mode_required:
return "Attempted to manually handle transactions with observer mode not enabled. " +
"You must use `.with(observerMode: true, storeKitVersion: .storeKit2)` when configuring the SDK."

case .sk2_required:
return "StoreKit 2 must be enabled. You must use `.with(storeKitVersion: .storeKit2)` " +
"when configuring the SDK."

case .observer_mode_with_storekit2:
return "StoreKit 2 Observer Mode is enabled. You must manually handle newly purchased transactions " +
"calling `Purchases.shared.handleObserverModeTransaction()`."

}
}

Expand Down
3 changes: 3 additions & 0 deletions Sources/Logging/Strings/PurchaseStrings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ enum PurchaseStrings {
case caching_presented_offering_identifier(offeringID: String, productID: String)
case payment_queue_wrapper_delegate_call_sk1_enabled
case restorepurchases_called_with_allow_sharing_appstore_account_false
case sk2_observer_mode_error_processing_transaction(Error)

}

Expand Down Expand Up @@ -329,6 +330,8 @@ extension PurchaseStrings: LogMessage {
case .restorepurchases_called_with_allow_sharing_appstore_account_false:
return "allowSharingAppStoreAccount is set to false and restorePurchases has been called. " +
"Are you sure you want to do this?"
case let .sk2_observer_mode_error_processing_transaction(error):
return "Obserber mode could not process transaction: \(error)"
}
}

Expand Down
5 changes: 5 additions & 0 deletions Sources/Logging/Strings/StoreKitStrings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ enum StoreKitStrings {

case sk2_purchasing_added_promotional_offer_option(String)

case sk2_purchasing_added_uuid_option(UUID)

case sk2_unknown_product_type(String)

case sk1_no_known_product_type
Expand Down Expand Up @@ -122,6 +124,9 @@ extension StoreKitStrings: LogMessage {
case let .sk2_purchasing_added_promotional_offer_option(discountIdentifier):
return "Adding Product.PurchaseOption for discount '\(discountIdentifier)'"

case let .sk2_purchasing_added_uuid_option(uuid):
return "Adding Product.PurchaseOption for .appAccountToken '\(uuid)'"

case let .sk2_unknown_product_type(type):
return "Product.ProductType '\(type)' unknown, the product type will be undefined."

Expand Down
13 changes: 1 addition & 12 deletions Sources/Misc/DangerousSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import Foundation
internal struct Internal: InternalDangerousSettingsType {

let enableReceiptFetchRetry: Bool
let usesStoreKit2JWS: Bool

#if DEBUG
let forceServerErrors: Bool
Expand All @@ -26,26 +25,22 @@ import Foundation

init(
enableReceiptFetchRetry: Bool = false,
usesStoreKit2JWS: Bool = false,
forceServerErrors: Bool = false,
forceSignatureFailures: Bool = false,
disableHeaderSignatureVerification: Bool = false,
testReceiptIdentifier: String? = nil
) {
self.enableReceiptFetchRetry = enableReceiptFetchRetry
self.usesStoreKit2JWS = usesStoreKit2JWS
self.forceServerErrors = forceServerErrors
self.forceSignatureFailures = forceSignatureFailures
self.disableHeaderSignatureVerification = disableHeaderSignatureVerification
self.testReceiptIdentifier = testReceiptIdentifier
}
#else
init(
enableReceiptFetchRetry: Bool = false,
usesStoreKit2JWS: Bool = false
enableReceiptFetchRetry: Bool = false
) {
self.enableReceiptFetchRetry = enableReceiptFetchRetry
self.usesStoreKit2JWS = usesStoreKit2JWS
}

#endif
Expand Down Expand Up @@ -125,12 +120,6 @@ internal protocol InternalDangerousSettingsType: Sendable {
/// Whether `ReceiptFetcher` can retry fetching receipts.
var enableReceiptFetchRetry: Bool { get }

/**
* Controls whether StoreKit 2 JWS tokens are sent to RevenueCat instead of StoreKit 1 receipts.
* Must be used in conjunction with the `usesStoreKit2IfAvailable configuration` option.
*/
var usesStoreKit2JWS: Bool { get }

#if DEBUG
/// Whether `HTTPClient` will fake server errors
var forceServerErrors: Bool { get }
Expand Down
Loading