Explorar el Código

Fix StoreKit subscription configuration and premium upgrade behavior.

Move recurring plans into a proper subscription group for StoreKit testing, route lifetime users to subscription management from the sidebar, and add a lifetime-upgrade warning to cancel existing subscriptions to avoid renewal charges.

Made-with: Cursor
huzaifahayat12 hace 1 mes
padre
commit
1b757d6d2b
Se han modificado 2 ficheros con 86 adiciones y 54 borrados
  1. 72 46
      meetings_app/StoreKit.storekit
  2. 14 8
      meetings_app/ViewController.swift

+ 72 - 46
meetings_app/StoreKit.storekit

@@ -2,51 +2,6 @@
2 2
   "identifier" : "7B5DA685-94A9-4A9B-86EA-F7D90A0D5249",
3 3
   "nonRenewingSubscriptions" : [],
4 4
   "products" : [
5
-    {
6
-      "displayPrice" : "1100.00",
7
-      "familyShareable" : false,
8
-      "internalID" : "F16C0A1F-5B83-41AB-A9F2-69157A11A11A",
9
-      "localizations" : [
10
-        {
11
-          "description" : "Unlock premium features with weekly access.",
12
-          "displayName" : "Premium Weekly",
13
-          "locale" : "en_US"
14
-        }
15
-      ],
16
-      "productID" : "com.mqldev.meetingsapp.premium.weekly",
17
-      "referenceName" : "Premium Weekly",
18
-      "type" : "NonConsumable"
19
-    },
20
-    {
21
-      "displayPrice" : "2500.00",
22
-      "familyShareable" : false,
23
-      "internalID" : "B2B57D59-AE2B-4953-BF03-5D4AFECAC6C1",
24
-      "localizations" : [
25
-        {
26
-          "description" : "Unlock premium features with monthly access.",
27
-          "displayName" : "Premium Monthly",
28
-          "locale" : "en_US"
29
-        }
30
-      ],
31
-      "productID" : "com.mqldev.meetingsapp.premium.monthly",
32
-      "referenceName" : "Premium Monthly",
33
-      "type" : "NonConsumable"
34
-    },
35
-    {
36
-      "displayPrice" : "9900.00",
37
-      "familyShareable" : false,
38
-      "internalID" : "C5694F51-47D8-4AFD-9D33-95A888527BB5",
39
-      "localizations" : [
40
-        {
41
-          "description" : "Unlock premium features with yearly access.",
42
-          "displayName" : "Premium Yearly",
43
-          "locale" : "en_US"
44
-        }
45
-      ],
46
-      "productID" : "com.mqldev.meetingsapp.premium.yearly",
47
-      "referenceName" : "Premium Yearly",
48
-      "type" : "NonConsumable"
49
-    },
50 5
     {
51 6
       "displayPrice" : "14900.00",
52 7
       "familyShareable" : false,
@@ -74,7 +29,78 @@
74 29
     "_storefront" : "PAK",
75 30
     "_timeRate" : 0
76 31
   },
77
-  "subscriptionGroups" : [],
32
+  "subscriptionGroups" : [
33
+    {
34
+      "id" : "A2D0B1D6",
35
+      "localizations" : [],
36
+      "name" : "Premium",
37
+      "subscriptions" : [
38
+        {
39
+          "adHocOffers" : [],
40
+          "codeOffers" : [],
41
+          "displayPrice" : "1100.00",
42
+          "familyShareable" : false,
43
+          "groupNumber" : 3,
44
+          "internalID" : "F16C0A1F-5B83-41AB-A9F2-69157A11A11A",
45
+          "introductoryOffer" : null,
46
+          "localizations" : [
47
+            {
48
+              "description" : "Unlock premium features with weekly access.",
49
+              "displayName" : "Premium Weekly",
50
+              "locale" : "en_US"
51
+            }
52
+          ],
53
+          "productID" : "com.mqldev.meetingsapp.premium.weekly",
54
+          "recurringSubscriptionPeriod" : "P1W",
55
+          "referenceName" : "Premium Weekly",
56
+          "subscriptionGroupID" : "A2D0B1D6",
57
+          "type" : "RecurringSubscription"
58
+        },
59
+        {
60
+          "adHocOffers" : [],
61
+          "codeOffers" : [],
62
+          "displayPrice" : "2500.00",
63
+          "familyShareable" : false,
64
+          "groupNumber" : 2,
65
+          "internalID" : "B2B57D59-AE2B-4953-BF03-5D4AFECAC6C1",
66
+          "introductoryOffer" : null,
67
+          "localizations" : [
68
+            {
69
+              "description" : "Unlock premium features with monthly access.",
70
+              "displayName" : "Premium Monthly",
71
+              "locale" : "en_US"
72
+            }
73
+          ],
74
+          "productID" : "com.mqldev.meetingsapp.premium.monthly",
75
+          "recurringSubscriptionPeriod" : "P1M",
76
+          "referenceName" : "Premium Monthly",
77
+          "subscriptionGroupID" : "A2D0B1D6",
78
+          "type" : "RecurringSubscription"
79
+        },
80
+        {
81
+          "adHocOffers" : [],
82
+          "codeOffers" : [],
83
+          "displayPrice" : "9900.00",
84
+          "familyShareable" : false,
85
+          "groupNumber" : 1,
86
+          "internalID" : "C5694F51-47D8-4AFD-9D33-95A888527BB5",
87
+          "introductoryOffer" : null,
88
+          "localizations" : [
89
+            {
90
+              "description" : "Unlock premium features with yearly access.",
91
+              "displayName" : "Premium Yearly",
92
+              "locale" : "en_US"
93
+            }
94
+          ],
95
+          "productID" : "com.mqldev.meetingsapp.premium.yearly",
96
+          "recurringSubscriptionPeriod" : "P1Y",
97
+          "referenceName" : "Premium Yearly",
98
+          "subscriptionGroupID" : "A2D0B1D6",
99
+          "type" : "RecurringSubscription"
100
+        }
101
+      ]
102
+    }
103
+  ],
78 104
   "version" : {
79 105
     "major" : 3,
80 106
     "minor" : 0

+ 14 - 8
meetings_app/ViewController.swift

@@ -550,7 +550,9 @@ private extension ViewController {
550 550
     }
551 551
 
552 552
     @objc private func premiumButtonClicked(_ sender: NSClickGestureRecognizer) {
553
-        if storeKitCoordinator.hasPremiumAccess {
553
+        if storeKitCoordinator.hasLifetimeAccess {
554
+            openManageSubscriptions()
555
+        } else if storeKitCoordinator.hasPremiumAccess {
554 556
             showPaywall(upgradeFlow: true, preferredPlan: .lifetime)
555 557
         } else {
556 558
             showPaywall()
@@ -1022,10 +1024,15 @@ private extension ViewController {
1022 1024
         DispatchQueue.main.asyncAfter(deadline: .now() + 2.0, execute: hideWorkItem)
1023 1025
     }
1024 1026
 
1025
-    private func confirmPremiumUpgrade() -> Bool {
1027
+    private func confirmPremiumUpgrade(for targetPlan: PremiumPlan) -> Bool {
1026 1028
         let alert = NSAlert()
1027
-        alert.messageText = "Already Premium"
1028
-        alert.informativeText = "You are already premium. Do you want to continue with this purchase?"
1029
+        if targetPlan == .lifetime, storeKitCoordinator.hasPremiumAccess, !storeKitCoordinator.hasLifetimeAccess {
1030
+            alert.messageText = "Switching to Lifetime"
1031
+            alert.informativeText = "You already have an active subscription. If you buy Lifetime, cancel your current subscription in App Store Subscriptions to avoid future renewal charges."
1032
+        } else {
1033
+            alert.messageText = "Already Premium"
1034
+            alert.informativeText = "You are already premium. Do you want to continue with this purchase?"
1035
+        }
1029 1036
         alert.addButton(withTitle: "Continue")
1030 1037
         alert.addButton(withTitle: "Cancel")
1031 1038
         return alert.runModal() == .alertFirstButtonReturn
@@ -1294,9 +1301,8 @@ private extension ViewController {
1294 1301
             let productID = PremiumStoreProduct.productID(for: plan)
1295 1302
             guard let product = storeKitCoordinator.productsByID[productID],
1296 1303
                   let period = product.subscription?.subscriptionPeriod else {
1297
-                if plan == .monthly {
1298
-                    label.stringValue = "3 days free trial"
1299
-                }
1304
+                // Show neutral fallback text when subscription metadata isn't available.
1305
+                label.stringValue = "Billed via App Store"
1300 1306
                 continue
1301 1307
             }
1302 1308
             let recurringText = "\(pkrDisplayPrice(product.displayPrice))/\(subscriptionUnitText(period.unit))"
@@ -1525,7 +1531,7 @@ private extension ViewController {
1525 1531
             }
1526 1532
             return
1527 1533
         }
1528
-        if paywallUpgradeFlowEnabled && storeKitCoordinator.hasPremiumAccess && !confirmPremiumUpgrade() {
1534
+        if paywallUpgradeFlowEnabled && storeKitCoordinator.hasPremiumAccess && !confirmPremiumUpgrade(for: selectedPremiumPlan) {
1529 1535
             return
1530 1536
         }
1531 1537
         paywallPurchaseTask?.cancel()