diff --git a/app-android/tutashared/src/main/java/de/tutao/tutashared/generated_ipc/MobilePlanPrice.kt b/app-android/tutashared/src/main/java/de/tutao/tutashared/generated_ipc/MobilePlanPrice.kt index 63612c6bcae7..05e125d84353 100644 --- a/app-android/tutashared/src/main/java/de/tutao/tutashared/generated_ipc/MobilePlanPrice.kt +++ b/app-android/tutashared/src/main/java/de/tutao/tutashared/generated_ipc/MobilePlanPrice.kt @@ -10,7 +10,10 @@ import kotlinx.serialization.json.* @Serializable data class MobilePlanPrice( val name: String, - val monthlyPerMonth: String, - val yearlyPerYear: String, - val yearlyPerMonth: String, + val rawMonthlyPerMonth: String, + val rawYearlyPerYear: String, + val rawYearlyPerMonth: String, + val displayMonthlyPerMonth: String, + val displayYearlyPerYear: String, + val displayYearlyPerMonth: String, ) diff --git a/app-ios/TutanotaSharedFramework/GeneratedIpc/MobilePlanPrice.swift b/app-ios/TutanotaSharedFramework/GeneratedIpc/MobilePlanPrice.swift index 907e61e5376c..56c5e0636a62 100644 --- a/app-ios/TutanotaSharedFramework/GeneratedIpc/MobilePlanPrice.swift +++ b/app-ios/TutanotaSharedFramework/GeneratedIpc/MobilePlanPrice.swift @@ -4,17 +4,26 @@ public struct MobilePlanPrice : Codable { public init( name: String, - monthlyPerMonth: String, - yearlyPerYear: String, - yearlyPerMonth: String + rawMonthlyPerMonth: String, + rawYearlyPerYear: String, + rawYearlyPerMonth: String, + displayMonthlyPerMonth: String, + displayYearlyPerYear: String, + displayYearlyPerMonth: String ) { self.name = name - self.monthlyPerMonth = monthlyPerMonth - self.yearlyPerYear = yearlyPerYear - self.yearlyPerMonth = yearlyPerMonth + self.rawMonthlyPerMonth = rawMonthlyPerMonth + self.rawYearlyPerYear = rawYearlyPerYear + self.rawYearlyPerMonth = rawYearlyPerMonth + self.displayMonthlyPerMonth = displayMonthlyPerMonth + self.displayYearlyPerYear = displayYearlyPerYear + self.displayYearlyPerMonth = displayYearlyPerMonth } public let name: String - public let monthlyPerMonth: String - public let yearlyPerYear: String - public let yearlyPerMonth: String + public let rawMonthlyPerMonth: String + public let rawYearlyPerYear: String + public let rawYearlyPerMonth: String + public let displayMonthlyPerMonth: String + public let displayYearlyPerYear: String + public let displayYearlyPerMonth: String } diff --git a/app-ios/calendar/Sources/Payment/IosMobilePaymentsFacade.swift b/app-ios/calendar/Sources/Payment/IosMobilePaymentsFacade.swift index 4fdc8cce2caf..c27259007f54 100644 --- a/app-ios/calendar/Sources/Payment/IosMobilePaymentsFacade.swift +++ b/app-ios/calendar/Sources/Payment/IosMobilePaymentsFacade.swift @@ -27,39 +27,64 @@ public class IosMobilePaymentsFacade: MobilePaymentsFacade { return currentResult } - public func getPlanPrices() async throws -> [MobilePlanPrice] { - struct TempMobilePlanPrice { - var monthlyPerMonth: String? - var yearlyPerYear: String? - var yearlyPerMonth: String? - } - let plans: [String] = ALL_PURCHASEABLE_PLANS.flatMap { plan in - [self.formatPlanType(plan, withInterval: 1), self.formatPlanType(plan, withInterval: 12)] - } - let products: [Product] = try await Product.products(for: plans) - var result = [String: TempMobilePlanPrice]() - for product in products { - let productName = String(product.id.split(separator: ".")[2]) - var plan = result[productName] ?? TempMobilePlanPrice(monthlyPerMonth: nil, yearlyPerYear: nil, yearlyPerMonth: nil) - - let unit = product.subscription!.subscriptionPeriod.unit - switch unit { - case .year: - let formatStyle = product.priceFormatStyle - let priceDivided = product.price / 12 - let yearlyPerMonthPrice = priceDivided.formatted(formatStyle) - let yearlyPerYearPrice = product.displayPrice - plan.yearlyPerYear = yearlyPerYearPrice - plan.yearlyPerMonth = yearlyPerMonthPrice - case .month: plan.monthlyPerMonth = product.displayPrice - default: fatalError("unexpected subscription period unit \(unit)") - } - result[productName] = plan - } - return result.map { name, prices in - MobilePlanPrice(name: name, monthlyPerMonth: prices.monthlyPerMonth!, yearlyPerYear: prices.yearlyPerYear!, yearlyPerMonth: prices.yearlyPerMonth!) - } - } + public func getPlanPrices() async throws -> [MobilePlanPrice] { + // TODO: Handle promotions (first year discount etc.) + struct TempMobilePlanPrice { + var rawMonthlyPerMonth: String? + var rawYearlyPerYear: String? + var rawYearlyPerMonth: String? + var displayMonthlyPerMonth: String? + var displayYearlyPerYear: String? + var displayYearlyPerMonth: String? + } + let plans: [String] = ALL_PURCHASEABLE_PLANS.flatMap { plan in + [self.formatPlanType(plan, withInterval: 1), self.formatPlanType(plan, withInterval: 12)] + } + let products: [Product] = try await Product.products(for: plans) + var result = [String: TempMobilePlanPrice]() + for product in products { + let productName = String(product.id.split(separator: ".")[2]) + var plan = + result[productName] + ?? TempMobilePlanPrice( + rawMonthlyPerMonth: nil, + rawYearlyPerYear: nil, + rawYearlyPerMonth: nil, + displayMonthlyPerMonth: nil, + displayYearlyPerYear: nil, + displayYearlyPerMonth: nil + ) + + let unit = product.subscription!.subscriptionPeriod.unit + switch unit { + case .year: + let formatStyle = product.priceFormatStyle + let priceDivided = product.price / 12 + let yearlyPerMonthPrice = priceDivided.formatted(formatStyle) + let yearlyPerYearPrice = product.displayPrice + plan.rawYearlyPerYear = String(describing: product.price) + plan.rawYearlyPerMonth = String(describing: priceDivided) + plan.displayYearlyPerYear = yearlyPerYearPrice + plan.displayYearlyPerMonth = yearlyPerMonthPrice + case .month: + plan.rawMonthlyPerMonth = String(describing: product.price) + plan.displayMonthlyPerMonth = product.displayPrice + default: fatalError("unexpected subscription period unit \(unit)") + } + result[productName] = plan + } + return result.map { name, prices in + MobilePlanPrice( + name: name, + rawMonthlyPerMonth: prices.rawMonthlyPerMonth!, + rawYearlyPerYear: prices.rawYearlyPerYear!, + rawYearlyPerMonth: prices.rawYearlyPerMonth!, + displayMonthlyPerMonth: prices.displayMonthlyPerMonth!, + displayYearlyPerYear: prices.displayYearlyPerYear!, + displayYearlyPerMonth: prices.displayYearlyPerMonth! + ) + } + } public func showSubscriptionConfigView() async throws { let window = await UIApplication.shared.connectedScenes.first try await AppStore.showManageSubscriptions(in: window as! UIWindowScene) diff --git a/app-ios/tutanota/Sources/Payment/IosMobilePaymentsFacade.swift b/app-ios/tutanota/Sources/Payment/IosMobilePaymentsFacade.swift index 9f9f42c53939..e29b15caf428 100644 --- a/app-ios/tutanota/Sources/Payment/IosMobilePaymentsFacade.swift +++ b/app-ios/tutanota/Sources/Payment/IosMobilePaymentsFacade.swift @@ -28,10 +28,14 @@ public class IosMobilePaymentsFacade: MobilePaymentsFacade { } public func getPlanPrices() async throws -> [MobilePlanPrice] { + // TODO: Handle promotions (first year discount etc.) struct TempMobilePlanPrice { - var monthlyPerMonth: String? - var yearlyPerYear: String? - var yearlyPerMonth: String? + var rawMonthlyPerMonth: String? + var rawYearlyPerYear: String? + var rawYearlyPerMonth: String? + var displayMonthlyPerMonth: String? + var displayYearlyPerYear: String? + var displayYearlyPerMonth: String? } let plans: [String] = ALL_PURCHASEABLE_PLANS.flatMap { plan in [self.formatPlanType(plan, withInterval: 1), self.formatPlanType(plan, withInterval: 12)] @@ -40,7 +44,16 @@ public class IosMobilePaymentsFacade: MobilePaymentsFacade { var result = [String: TempMobilePlanPrice]() for product in products { let productName = String(product.id.split(separator: ".")[1]) - var plan = result[productName] ?? TempMobilePlanPrice(monthlyPerMonth: nil, yearlyPerYear: nil, yearlyPerMonth: nil) + var plan = + result[productName] + ?? TempMobilePlanPrice( + rawMonthlyPerMonth: nil, + rawYearlyPerYear: nil, + rawYearlyPerMonth: nil, + displayMonthlyPerMonth: nil, + displayYearlyPerYear: nil, + displayYearlyPerMonth: nil + ) let unit = product.subscription!.subscriptionPeriod.unit switch unit { @@ -49,15 +62,27 @@ public class IosMobilePaymentsFacade: MobilePaymentsFacade { let priceDivided = product.price / 12 let yearlyPerMonthPrice = priceDivided.formatted(formatStyle) let yearlyPerYearPrice = product.displayPrice - plan.yearlyPerYear = yearlyPerYearPrice - plan.yearlyPerMonth = yearlyPerMonthPrice - case .month: plan.monthlyPerMonth = product.displayPrice + plan.rawYearlyPerYear = String(describing: product.price) + plan.rawYearlyPerMonth = String(describing: priceDivided) + plan.displayYearlyPerYear = yearlyPerYearPrice + plan.displayYearlyPerMonth = yearlyPerMonthPrice + case .month: + plan.rawMonthlyPerMonth = String(describing: product.price) + plan.displayMonthlyPerMonth = product.displayPrice default: fatalError("unexpected subscription period unit \(unit)") } result[productName] = plan } return result.map { name, prices in - MobilePlanPrice(name: name, monthlyPerMonth: prices.monthlyPerMonth!, yearlyPerYear: prices.yearlyPerYear!, yearlyPerMonth: prices.yearlyPerMonth!) + MobilePlanPrice( + name: name, + rawMonthlyPerMonth: prices.rawMonthlyPerMonth!, + rawYearlyPerYear: prices.rawYearlyPerYear!, + rawYearlyPerMonth: prices.rawYearlyPerMonth!, + displayMonthlyPerMonth: prices.displayMonthlyPerMonth!, + displayYearlyPerYear: prices.displayYearlyPerYear!, + displayYearlyPerMonth: prices.displayYearlyPerMonth! + ) } } public func showSubscriptionConfigView() async throws { diff --git a/ipc-schema/types/MobilePlanPrice.json b/ipc-schema/types/MobilePlanPrice.json index 2873cde70f11..60e9af24bafd 100644 --- a/ipc-schema/types/MobilePlanPrice.json +++ b/ipc-schema/types/MobilePlanPrice.json @@ -3,8 +3,11 @@ "type": "struct", "fields": { "name": "string", - "monthlyPerMonth": "string", - "yearlyPerYear": "string", - "yearlyPerMonth": "string" + "rawMonthlyPerMonth": "string", + "rawYearlyPerYear": "string", + "rawYearlyPerMonth": "string", + "displayMonthlyPerMonth": "string", + "displayYearlyPerYear": "string", + "displayYearlyPerMonth": "string" } } diff --git a/src/common/native/common/generatedipc/MobilePlanPrice.ts b/src/common/native/common/generatedipc/MobilePlanPrice.ts index 3bb6689c9e64..f318a0d666cd 100644 --- a/src/common/native/common/generatedipc/MobilePlanPrice.ts +++ b/src/common/native/common/generatedipc/MobilePlanPrice.ts @@ -2,7 +2,10 @@ export interface MobilePlanPrice { readonly name: string - readonly monthlyPerMonth: string - readonly yearlyPerYear: string - readonly yearlyPerMonth: string + readonly rawMonthlyPerMonth: string + readonly rawYearlyPerYear: string + readonly rawYearlyPerMonth: string + readonly displayMonthlyPerMonth: string + readonly displayYearlyPerYear: string + readonly displayYearlyPerMonth: string } diff --git a/src/common/subscription/InvoiceAndPaymentDataPage.ts b/src/common/subscription/InvoiceAndPaymentDataPage.ts index 076acf61a2d4..9afbd3ffa3c9 100644 --- a/src/common/subscription/InvoiceAndPaymentDataPage.ts +++ b/src/common/subscription/InvoiceAndPaymentDataPage.ts @@ -159,7 +159,7 @@ export class InvoiceAndPaymentDataPage implements WizardPageN { if (success) { diff --git a/src/common/subscription/PriceUtils.ts b/src/common/subscription/PriceUtils.ts index e4ae78102b46..f44d0163f55b 100644 --- a/src/common/subscription/PriceUtils.ts +++ b/src/common/subscription/PriceUtils.ts @@ -121,7 +121,7 @@ export function getPriceFromPriceData(priceData: PriceData | null, featureType: export type SubscriptionPrice = { // The locale formatted price of a description in the local currency on iOS and in Euro elsewhere displayPrice: string - // The raw price in Euro as a float + // The raw price in the local currency on iOS and in Euro elsewhere as a float rawPrice: string } @@ -173,7 +173,9 @@ export class PriceAndConfigProvider { : this.getMonthlySubscriptionPrice(subscription, type) } - // Returns the subscription price with the currency formatting on iOS and as a plain period seperated number on other platforms + /** + * Returns the subscription price with the currency formatting on iOS and as a plain period seperated number on other platforms + */ getSubscriptionPriceWithCurrency(paymentInterval: PaymentInterval, subscription: PlanType, type: UpgradePriceType): SubscriptionPrice { const price = this.getSubscriptionPrice(paymentInterval, subscription, type) const rawPrice = price.toString() @@ -181,8 +183,8 @@ export class PriceAndConfigProvider { if (isIOSApp()) { return this.getAppStorePaymentsSubscriptionPrice(subscription, paymentInterval, rawPrice, type) } else { - const displayPrice = formatPrice(price, true) - return { displayPrice, rawPrice } + const price = this.getSubscriptionPrice(paymentInterval, subscription, type) + return { displayPrice: formatPrice(price, true), rawPrice: price.toString() } } } @@ -198,15 +200,9 @@ export class PriceAndConfigProvider { switch (paymentInterval) { case PaymentInterval.Monthly: - return { displayPrice: applePrices.monthlyPerMonth, rawPrice } + return { displayPrice: applePrices.displayMonthlyPerMonth, rawPrice: applePrices.rawMonthlyPerMonth } case PaymentInterval.Yearly: - if (isCyberMonday && subscription === PlanType.Legend && type === UpgradePriceType.PlanActualPrice) { - const revolutionaryYearlyPerYear = this.getMobilePrices().get(PlanTypeToName[PlanType.Revolutionary].toLowerCase())?.yearlyPerYear ?? NBSP - - return { displayPrice: revolutionaryYearlyPerYear, rawPrice } - } - - return { displayPrice: applePrices.yearlyPerYear, rawPrice } + return { displayPrice: applePrices.displayYearlyPerYear, rawPrice: applePrices.rawYearlyPerYear } } } diff --git a/src/common/subscription/SubscriptionSelector.ts b/src/common/subscription/SubscriptionSelector.ts index 62fcac40d247..5522c045d2b9 100644 --- a/src/common/subscription/SubscriptionSelector.ts +++ b/src/common/subscription/SubscriptionSelector.ts @@ -264,19 +264,19 @@ export class SubscriptionSelector implements Component if (prices != null) { if (isCyberMonday && targetSubscription === PlanType.Legend && interval == PaymentInterval.Yearly) { const revolutionaryPrice = priceAndConfigProvider.getMobilePrices().get(PlanTypeToName[PlanType.Revolutionary].toLowerCase()) - priceStr = revolutionaryPrice?.yearlyPerMonth ?? NBSP + priceStr = revolutionaryPrice?.displayYearlyPerMonth ?? NBSP // if there is a discount for this plan we show the original price as reference - referencePriceStr = prices?.yearlyPerMonth + referencePriceStr = prices?.displayYearlyPerMonth } else { switch (interval) { case PaymentInterval.Monthly: - priceStr = prices.monthlyPerMonth + priceStr = prices.displayMonthlyPerMonth break case PaymentInterval.Yearly: - priceStr = prices.yearlyPerMonth + priceStr = prices.displayYearlyPerMonth if (!isCyberMonday) { // if there is no discount for any plan then we show the monthly price as reference - referencePriceStr = prices.monthlyPerMonth + referencePriceStr = prices.displayMonthlyPerMonth } break } diff --git a/src/common/subscription/UpgradeConfirmSubscriptionPage.ts b/src/common/subscription/UpgradeConfirmSubscriptionPage.ts index 2477b21ae11e..57c1dd70ee54 100644 --- a/src/common/subscription/UpgradeConfirmSubscriptionPage.ts +++ b/src/common/subscription/UpgradeConfirmSubscriptionPage.ts @@ -168,8 +168,8 @@ export class UpgradeConfirmSubscriptionPage implements WizardPageN) { - return attrs.data.priceNextYear + return attrs.data.nextYearPrice ? m(TextField, { label: "priceForNextYear_label", - value: buildPriceString(attrs.data.priceNextYear, attrs.data.options), + value: buildPriceString(attrs.data.nextYearPrice.displayPrice, attrs.data.options), isReadOnly: true, }) : null diff --git a/src/common/subscription/UpgradeSubscriptionPage.ts b/src/common/subscription/UpgradeSubscriptionPage.ts index 74266b49a5f5..1a81b1c02d91 100644 --- a/src/common/subscription/UpgradeSubscriptionPage.ts +++ b/src/common/subscription/UpgradeSubscriptionPage.ts @@ -127,8 +127,8 @@ export class UpgradeSubscriptionPage implements WizardPageN