parent
63cbf39a8a
commit
e21e11b022
|
@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Platform purchasers cannot upgrade to full version. [#464](https://github.com/passepartoutvpn/passepartout-apple/issues/464)
|
||||||
|
|
||||||
## 2.3.2 (2024-01-11)
|
## 2.3.2 (2024-01-11)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
|
@ -194,29 +194,8 @@ private extension PaywallView.PurchaseView {
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension PaywallView.PurchaseView {
|
private extension PaywallView.PurchaseView {
|
||||||
|
|
||||||
// hide full version if already bought the other platform version
|
|
||||||
var skFullVersion: InAppProduct? {
|
var skFullVersion: InAppProduct? {
|
||||||
guard !productManager.isFullVersion() else {
|
productManager.product(withIdentifier: .fullVersion)
|
||||||
return nil
|
|
||||||
}
|
|
||||||
#if targetEnvironment(macCatalyst)
|
|
||||||
guard !productManager.hasPurchased(.fullVersion_iOS) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
guard !productManager.hasPurchased(.fullVersion_macOS) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return productManager.product(withIdentifier: .fullVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
var skAppleTV: InAppProduct? {
|
|
||||||
guard feature == .appleTV else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return productManager.product(withIdentifier: .appleTV)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var fullFeatures: [FeatureModel] {
|
var fullFeatures: [FeatureModel] {
|
||||||
|
@ -230,8 +209,9 @@ private extension PaywallView.PurchaseView {
|
||||||
}
|
}
|
||||||
|
|
||||||
var productRowModels: [InAppProduct] {
|
var productRowModels: [InAppProduct] {
|
||||||
[skFullVersion, skAppleTV]
|
productManager
|
||||||
.compactMap { $0 }
|
.purchasableProducts(withFeature: feature)
|
||||||
|
.compactMap { productManager.product(withIdentifier: $0) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -293,6 +293,44 @@ extension ProductManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Purchasable products
|
||||||
|
|
||||||
|
extension ProductManager {
|
||||||
|
|
||||||
|
// no purchase -> full version or platform version
|
||||||
|
// purchased platform -> may only purchase other platform
|
||||||
|
|
||||||
|
public func purchasableProducts(withFeature feature: LocalProduct?) -> [LocalProduct] {
|
||||||
|
var products: [LocalProduct] = {
|
||||||
|
if hasPurchased(.fullVersion) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
#if targetEnvironment(macCatalyst)
|
||||||
|
if hasPurchased(.fullVersion_macOS) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
if hasPurchased(.fullVersion_iOS) {
|
||||||
|
return [.fullVersion_macOS]
|
||||||
|
}
|
||||||
|
return [.fullVersion, .fullVersion_macOS]
|
||||||
|
#else
|
||||||
|
if hasPurchased(.fullVersion_iOS) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
if hasPurchased(.fullVersion_macOS) {
|
||||||
|
return [.fullVersion_iOS]
|
||||||
|
}
|
||||||
|
return [.fullVersion, .fullVersion_iOS]
|
||||||
|
#endif
|
||||||
|
}()
|
||||||
|
if feature == .appleTV && !hasPurchased(.appleTV) {
|
||||||
|
products.append(.appleTV)
|
||||||
|
}
|
||||||
|
return products
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: Helpers
|
// MARK: Helpers
|
||||||
|
|
||||||
private extension ProductManager {
|
private extension ProductManager {
|
||||||
|
|
|
@ -33,6 +33,8 @@ final class ProductManagerTests: XCTestCase {
|
||||||
|
|
||||||
private let noBuildProducts = BuildProducts { _ in [] }
|
private let noBuildProducts = BuildProducts { _ in [] }
|
||||||
|
|
||||||
|
// MARK: Build products
|
||||||
|
|
||||||
func test_givenBuildProducts_whenOlder_thenFullVersion() {
|
func test_givenBuildProducts_whenOlder_thenFullVersion() {
|
||||||
let reader = MockReceiptReader()
|
let reader = MockReceiptReader()
|
||||||
reader.setReceipt(withBuild: 500, products: [])
|
reader.setReceipt(withBuild: 500, products: [])
|
||||||
|
@ -57,6 +59,8 @@ final class ProductManagerTests: XCTestCase {
|
||||||
XCTAssertFalse(sut.isEligible(forFeature: .fullVersion))
|
XCTAssertFalse(sut.isEligible(forFeature: .fullVersion))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Eligibility
|
||||||
|
|
||||||
func test_givenPurchase_whenReload_thenCredited() {
|
func test_givenPurchase_whenReload_thenCredited() {
|
||||||
let reader = MockReceiptReader()
|
let reader = MockReceiptReader()
|
||||||
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts)
|
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts)
|
||||||
|
@ -163,4 +167,70 @@ final class ProductManagerTests: XCTestCase {
|
||||||
|
|
||||||
XCTAssertTrue(sut.isEligible(forFeature: .appleTV))
|
XCTAssertTrue(sut.isEligible(forFeature: .appleTV))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Purchasable
|
||||||
|
|
||||||
|
func test_givenNoPurchase_thenCanBuyFullAndPlatformVersion() {
|
||||||
|
let reader = MockReceiptReader()
|
||||||
|
reader.setReceipt(withBuild: 1500, products: [])
|
||||||
|
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts)
|
||||||
|
|
||||||
|
#if targetEnvironment(macCatalyst)
|
||||||
|
XCTAssertEqual(sut.purchasableProducts(withFeature: nil), [.fullVersion, .fullVersion_macOS])
|
||||||
|
#else
|
||||||
|
XCTAssertEqual(sut.purchasableProducts(withFeature: nil), [.fullVersion, .fullVersion_iOS])
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_givenFullVersion_thenCannotPurchase() {
|
||||||
|
let reader = MockReceiptReader()
|
||||||
|
|
||||||
|
reader.setReceipt(withBuild: 1500, products: [.fullVersion])
|
||||||
|
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts)
|
||||||
|
XCTAssertEqual(sut.purchasableProducts(withFeature: nil), [])
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_givenPlatformVersion_thenCannotPurchaseSamePlatform() {
|
||||||
|
let reader = MockReceiptReader()
|
||||||
|
|
||||||
|
#if targetEnvironment(macCatalyst)
|
||||||
|
reader.setReceipt(withBuild: 1500, products: [.fullVersion_macOS])
|
||||||
|
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts)
|
||||||
|
XCTAssertEqual(sut.purchasableProducts(withFeature: nil), [])
|
||||||
|
#else
|
||||||
|
reader.setReceipt(withBuild: 1500, products: [.fullVersion_iOS])
|
||||||
|
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts)
|
||||||
|
XCTAssertEqual(sut.purchasableProducts(withFeature: nil), [])
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_givenOtherPlatformVersion_thenCanOnlyPurchaseMissingPlatform() {
|
||||||
|
let reader = MockReceiptReader()
|
||||||
|
|
||||||
|
#if targetEnvironment(macCatalyst)
|
||||||
|
reader.setReceipt(withBuild: 1500, products: [.fullVersion_iOS])
|
||||||
|
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts)
|
||||||
|
XCTAssertEqual(sut.purchasableProducts(withFeature: nil), [.fullVersion_macOS])
|
||||||
|
#else
|
||||||
|
reader.setReceipt(withBuild: 1500, products: [.fullVersion_macOS])
|
||||||
|
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts)
|
||||||
|
XCTAssertEqual(sut.purchasableProducts(withFeature: nil), [.fullVersion_iOS])
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_givenAppleTV_whenDidNotPurchase_thenCanPurchase() {
|
||||||
|
let reader = MockReceiptReader()
|
||||||
|
reader.setReceipt(withBuild: 1500, products: [])
|
||||||
|
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts)
|
||||||
|
|
||||||
|
XCTAssertTrue(sut.purchasableProducts(withFeature: .appleTV).contains(.appleTV))
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_givenAppleTV_whenDidPurchase_thenCannotPurchase() {
|
||||||
|
let reader = MockReceiptReader()
|
||||||
|
reader.setReceipt(withBuild: 1500, products: [.appleTV])
|
||||||
|
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts)
|
||||||
|
|
||||||
|
XCTAssertFalse(sut.purchasableProducts(withFeature: .appleTV).contains(.appleTV))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue