Jelajahi Sumber

Localize paywall pricing by storefront and clarify trial messaging.

Use StoreKit display prices in the paywall so currency follows region settings, update StoreKit test storefront/locale to Pakistan for local testing, and specify the monthly free trial duration in paywall copy.

Made-with: Cursor
huzaifahayat12 3 bulan lalu
induk
melakukan
70ea0cbfd0
2 mengubah file dengan 48 tambahan dan 22 penghapusan
  1. 6 6
      zoom_app/StoreKit.storekit
  2. 42 16
      zoom_app/ViewController.swift

+ 6 - 6
zoom_app/StoreKit.storekit

@@ -10,7 +10,7 @@
         {
           "description" : "Unlock premium features with weekly access.",
           "displayName" : "Premium Weekly",
-          "locale" : "en_US"
+          "locale" : "en_PK"
         }
       ],
       "productID" : "com.mqldev.zoomapp.premium.weekly",
@@ -25,7 +25,7 @@
         {
           "description" : "Unlock premium features with monthly access.",
           "displayName" : "Premium Monthly",
-          "locale" : "en_US"
+          "locale" : "en_PK"
         }
       ],
       "productID" : "com.mqldev.zoomapp.premium.monthly",
@@ -40,7 +40,7 @@
         {
           "description" : "Unlock premium features with yearly access.",
           "displayName" : "Premium Yearly",
-          "locale" : "en_US"
+          "locale" : "en_PK"
         }
       ],
       "productID" : "com.mqldev.zoomapp.premium.yearly",
@@ -55,7 +55,7 @@
         {
           "description" : "One-time premium purchase for lifetime access.",
           "displayName" : "Premium Lifetime",
-          "locale" : "en_US"
+          "locale" : "en_PK"
         }
       ],
       "productID" : "com.mqldev.zoomapp.premium.lifetime",
@@ -69,9 +69,9 @@
     "_disableDialogs" : false,
     "_failTransactionsEnabled" : false,
     "_lastSynchronizedDate" : 0,
-    "_locale" : "en_US",
+    "_locale" : "en_PK",
     "_renewalRate" : 0,
-    "_storefront" : "USA",
+    "_storefront" : "PAK",
     "_timeRate" : 0
   },
   "subscriptionGroups" : [],

+ 42 - 16
zoom_app/ViewController.swift

@@ -262,6 +262,9 @@ class ViewController: NSViewController {
     private var selectedPremiumPlan: PremiumPlan = .monthly
     private var paywallPlanViews: [PremiumPlan: NSView] = [:]
     private var premiumPlanByView = [ObjectIdentifier: PremiumPlan]()
+    private var paywallPlanPriceLabels: [PremiumPlan: NSTextField] = [:]
+    private var paywallPlanSubtitleLabels: [PremiumPlan: NSTextField] = [:]
+    private var paywallPlanStrikeLabels: [PremiumPlan: NSTextField] = [:]
     private weak var paywallOfferLabel: NSTextField?
     private weak var paywallContinueLabel: NSTextField?
     private weak var paywallContinueButton: NSView?
@@ -3169,6 +3172,9 @@ class ViewController: NSViewController {
 
     private func makePaywallContent() -> NSView {
         paywallPlanViews.removeAll()
+        paywallPlanPriceLabels.removeAll()
+        paywallPlanSubtitleLabels.removeAll()
+        paywallPlanStrikeLabels.removeAll()
         premiumPlanByView.removeAll()
         paywallOfferLabel = nil
         paywallContinueLabel = nil
@@ -3223,10 +3229,10 @@ class ViewController: NSViewController {
 
         let weeklyCard = paywallPlanCard(
             title: "Weekly",
-            price: "PKR 1,100.00",
+            price: paywallDisplayPrice(for: .weekly),
             badge: "Basic Deal",
             badgeColor: NSColor(calibratedRed: 1.0, green: 0.60, blue: 0.20, alpha: 1),
-            subtitle: nil,
+            subtitle: "Billed weekly",
             plan: .weekly,
             strikePrice: nil
         )
@@ -3234,10 +3240,10 @@ class ViewController: NSViewController {
 
         let monthlyCard = paywallPlanCard(
             title: "Monthly",
-            price: "PKR 2,500.00",
+            price: paywallDisplayPrice(for: .monthly),
             badge: "Free Trial",
             badgeColor: NSColor(calibratedRed: 0.19, green: 0.82, blue: 0.39, alpha: 1),
-            subtitle: "625.00/week",
+            subtitle: "7-day free trial, then monthly",
             plan: .monthly,
             strikePrice: nil
         )
@@ -3245,10 +3251,10 @@ class ViewController: NSViewController {
 
         let yearlyCard = paywallPlanCard(
             title: "Yearly",
-            price: "PKR 9,900.00",
+            price: paywallDisplayPrice(for: .yearly),
             badge: "Best Deal",
             badgeColor: NSColor(calibratedRed: 1.0, green: 0.60, blue: 0.20, alpha: 1),
-            subtitle: "190.38/week",
+            subtitle: "Billed yearly",
             plan: .yearly,
             strikePrice: nil
         )
@@ -3256,12 +3262,12 @@ class ViewController: NSViewController {
 
         let lifetimeCard = paywallPlanCard(
             title: "Lifetime",
-            price: "PKR 14,900.00",
+            price: paywallDisplayPrice(for: .lifetime),
             badge: "Save 50%",
             badgeColor: NSColor(calibratedRed: 1.0, green: 0.60, blue: 0.20, alpha: 1),
-            subtitle: nil,
+            subtitle: "One-time purchase",
             plan: .lifetime,
-            strikePrice: "PKR 29,800.00"
+            strikePrice: nil
         )
         contentStack.addArrangedSubview(lifetimeCard)
         updatePaywallPlanSelection()
@@ -3453,6 +3459,7 @@ class ViewController: NSViewController {
         card.addSubview(titleLabel)
         let priceLabel = textLabel(price, font: NSFont.systemFont(ofSize: 12, weight: .bold), color: primaryText)
         card.addSubview(priceLabel)
+        paywallPlanPriceLabels[plan] = priceLabel
 
         NSLayoutConstraint.activate([
             badgeWrap.centerXAnchor.constraint(equalTo: card.centerXAnchor),
@@ -3468,6 +3475,7 @@ class ViewController: NSViewController {
         if let subtitle {
             let sub = textLabel(subtitle, font: NSFont.systemFont(ofSize: 10, weight: .semibold), color: secondaryText)
             card.addSubview(sub)
+            paywallPlanSubtitleLabels[plan] = sub
             NSLayoutConstraint.activate([
                 sub.trailingAnchor.constraint(equalTo: priceLabel.trailingAnchor),
                 sub.topAnchor.constraint(equalTo: priceLabel.bottomAnchor, constant: 0)
@@ -3477,6 +3485,7 @@ class ViewController: NSViewController {
         if let strikePrice {
             let strike = textLabel(strikePrice, font: NSFont.systemFont(ofSize: 12, weight: .medium), color: NSColor.systemRed)
             card.addSubview(strike)
+            paywallPlanStrikeLabels[plan] = strike
             NSLayoutConstraint.activate([
                 strike.trailingAnchor.constraint(equalTo: priceLabel.trailingAnchor),
                 strike.topAnchor.constraint(equalTo: priceLabel.bottomAnchor, constant: 4)
@@ -3510,22 +3519,39 @@ class ViewController: NSViewController {
         if storeKitCoordinator.hasPremiumAccess {
             return "Premium is active on this Apple ID."
         }
-        if let product = storeKitCoordinator.productsByID[plan.rawValue] {
-            return "\(product.displayPrice) purchase"
-        }
+        let price = paywallDisplayPrice(for: plan)
         switch plan {
-        case .weekly: return "PKR 1,100.00/week"
-        case .monthly: return "PKR 2,500.00/month"
-        case .yearly: return "PKR 9,900.00/year"
-        case .lifetime: return "PKR 14,900.00 one-time purchase"
+        case .weekly: return "\(price)/week"
+        case .monthly: return "7-day free trial, then \(price)/month"
+        case .yearly: return "\(price)/year"
+        case .lifetime: return "\(price) one-time purchase"
         }
     }
 
     private func refreshPaywallStoreUI() {
+        refreshPaywallPlanPrices()
         updatePaywallPlanSelection()
         updatePaywallContinueState(isLoading: false)
     }
 
+    private func refreshPaywallPlanPrices() {
+        for plan in PremiumPlan.allCases {
+            paywallPlanPriceLabels[plan]?.stringValue = paywallDisplayPrice(for: plan)
+        }
+    }
+
+    private func paywallDisplayPrice(for plan: PremiumPlan) -> String {
+        if let product = storeKitCoordinator.productsByID[plan.rawValue] {
+            return product.displayPrice
+        }
+        switch plan {
+        case .weekly: return "1,100.00"
+        case .monthly: return "2,500.00"
+        case .yearly: return "9,900.00"
+        case .lifetime: return "14,900.00"
+        }
+    }
+
     @objc private func paywallContinueClicked(_ sender: Any?) {
         startSelectedPlanPurchase()
     }