Manos Chorianopoulos

getCampaigns unauthorized fix

...@@ -1121,6 +1121,81 @@ After these changes, the framework should: ...@@ -1121,6 +1121,81 @@ After these changes, the framework should:
1121 1121
1122 After the initial legacy credential removal, a comprehensive search revealed **2 additional problematic checks** that could still cause issues: 1122 After the initial legacy credential removal, a comprehensive search revealed **2 additional problematic checks** that could still cause issues:
1123 1123
1124 +#### **Issue #7: getCampaigns Authentication Logic (FINAL FIX)**
1125 +**File**: `SwiftWarplyFramework/SwiftWarplyFramework/Core/WarplySDK.swift`
1126 +**Problem**: `getCampaigns` method failed with ERROR: -1 when user not authenticated due to failed `getCampaignsPersonalized` and `getAvailableCoupons` calls
1127 +**Impact**: ❌ **CRITICAL** - Campaign loading completely failed for non-authenticated users
1128 +
1129 +**Root Cause**: The method tried to call authentication-required endpoints (`getCampaignsPersonalized` and `getAvailableCoupons`) even when user was not logged in, causing the entire flow to fail.
1130 +
1131 +**Solution**: **Authentication-First Approach**
1132 +```swift
1133 +// Check if user is authenticated to determine campaign processing approach
1134 +if self.isUserAuthenticated() {
1135 + // User is authenticated - get full experience with personalized campaigns and coupon filtering
1136 + print("✅ [WarplySDK] User authenticated - loading personalized campaigns and coupon availability")
1137 +
1138 + self.getCampaignsPersonalized(language: language, filters: filters, completion: { personalizedCampaigns in
1139 + // ... full authenticated flow with parseCampaigns filtering
1140 + })
1141 +} else {
1142 + // User not authenticated - return all basic campaigns without coupon filtering
1143 + print("ℹ️ [WarplySDK] User not authenticated - returning all basic campaigns without coupon filtering")
1144 +
1145 + // Skip parseCampaigns entirely - return raw campaigns without filtering
1146 + completion(campaignsArray)
1147 +}
1148 +```
1149 +
1150 +**Key Changes**:
1151 +1. **Added `isUserAuthenticated()` helper method** - checks database for valid, non-expired tokens
1152 +2. **Authentication-first logic** - check auth status before making dependent calls
1153 +3. **Graceful degradation** - non-authenticated users get basic campaigns without filtering
1154 +4. **Skip `parseCampaigns`** - for non-authenticated users to avoid empty results due to missing coupon availability
1155 +
1156 +**Result**: ✅ **No more ERROR: -1** - campaigns load successfully for both authenticated and non-authenticated users
1157 +
1158 +---
1159 +### **🔧 Additional Safety Fixes - June 27, 2025 (Session 2)**
1160 +
1161 +After the initial legacy credential removal, a comprehensive search revealed **3 additional problematic checks** that could still cause issues:
1162 +
1163 +#### **Issue #7: getCampaigns Authentication Logic (FINAL FIX)**
1164 +**File**: `SwiftWarplyFramework/SwiftWarplyFramework/Core/WarplySDK.swift`
1165 +**Problem**: `getCampaigns` method failed with ERROR: -1 when user not authenticated due to failed `getCampaignsPersonalized` and `getAvailableCoupons` calls
1166 +**Impact**: ❌ **CRITICAL** - Campaign loading completely failed for non-authenticated users
1167 +
1168 +**Root Cause**: The method tried to call authentication-required endpoints (`getCampaignsPersonalized` and `getAvailableCoupons`) even when user was not logged in, causing the entire flow to fail.
1169 +
1170 +**Solution**: **Authentication-First Approach**
1171 +```swift
1172 +// Check if user is authenticated to determine campaign processing approach
1173 +if self.isUserAuthenticated() {
1174 + // User is authenticated - get full experience with personalized campaigns and coupon filtering
1175 + print("✅ [WarplySDK] User authenticated - loading personalized campaigns and coupon availability")
1176 +
1177 + self.getCampaignsPersonalized(language: language, filters: filters, completion: { personalizedCampaigns in
1178 + // ... full authenticated flow with parseCampaigns filtering
1179 + })
1180 +} else {
1181 + // User not authenticated - return all basic campaigns without coupon filtering
1182 + print("ℹ️ [WarplySDK] User not authenticated - returning all basic campaigns without coupon filtering")
1183 +
1184 + // Skip parseCampaigns entirely - return raw campaigns without filtering
1185 + completion(campaignsArray)
1186 +}
1187 +```
1188 +
1189 +**Key Changes**:
1190 +1. **Added `isUserAuthenticated()` helper method** - checks database for valid, non-expired tokens
1191 +2. **Authentication-first logic** - check auth status before making dependent calls
1192 +3. **Graceful degradation** - non-authenticated users get basic campaigns without filtering
1193 +4. **Skip `parseCampaigns`** - for non-authenticated users to avoid empty results due to missing coupon availability
1194 +
1195 +**Result**: ✅ **No more ERROR: -1** - campaigns load successfully for both authenticated and non-authenticated users
1196 +
1197 +---
1198 +
1124 #### **Issue #5: NetworkService Signature Generation (CRITICAL)** 1199 #### **Issue #5: NetworkService Signature Generation (CRITICAL)**
1125 **File**: `SwiftWarplyFramework/SwiftWarplyFramework/Network/NetworkService.swift` 1200 **File**: `SwiftWarplyFramework/SwiftWarplyFramework/Network/NetworkService.swift`
1126 **Problem**: Conditional signature generation that skipped the `loyalty-signature` header when API key was empty 1201 **Problem**: Conditional signature generation that skipped the `loyalty-signature` header when API key was empty
......
...@@ -1589,11 +1589,15 @@ public final class WarplySDK { ...@@ -1589,11 +1589,15 @@ public final class WarplySDK {
1589 } 1589 }
1590 } 1590 }
1591 1591
1592 - // Get personalized campaigns (still using MyApi temporarily) 1592 + // Check if user is authenticated to determine campaign processing approach
1593 + if self.isUserAuthenticated() {
1594 + // User is authenticated - get full experience with personalized campaigns and coupon filtering
1595 + print("✅ [WarplySDK] User authenticated - loading personalized campaigns and coupon availability")
1596 +
1593 self.getCampaignsPersonalized(language: language, filters: filters, completion: { personalizedCampaigns in 1597 self.getCampaignsPersonalized(language: language, filters: filters, completion: { personalizedCampaigns in
1594 campaignsArray = campaignsArray + (personalizedCampaigns ?? []) 1598 campaignsArray = campaignsArray + (personalizedCampaigns ?? [])
1595 1599
1596 - // Get available coupons (still using MyApi temporarily) 1600 + // Get available coupons for authenticated users
1597 self.getAvailableCoupons { availabilityData in 1601 self.getAvailableCoupons { availabilityData in
1598 if let availabilityData = availabilityData { 1602 if let availabilityData = availabilityData {
1599 for tempCampaign in campaignsArray { 1603 for tempCampaign in campaignsArray {
...@@ -1605,11 +1609,14 @@ public final class WarplySDK { ...@@ -1605,11 +1609,14 @@ public final class WarplySDK {
1605 } 1609 }
1606 } 1610 }
1607 1611
1612 + // For authenticated users: use parseCampaigns (filters by coupon availability)
1608 let parsedCampaigns = self.parseCampaigns(campaignsArray) 1613 let parsedCampaigns = self.parseCampaigns(campaignsArray)
1609 completion(parsedCampaigns) 1614 completion(parsedCampaigns)
1610 } 1615 }
1611 }, failureCallback: { errorCode in 1616 }, failureCallback: { errorCode in
1612 - // If personalized campaigns fail, continue with regular campaigns 1617 + // If personalized campaigns fail, still try coupon availability
1618 + print("⚠️ [WarplySDK] Personalized campaigns failed for authenticated user - continuing with basic campaigns")
1619 +
1613 self.getAvailableCoupons { availabilityData in 1620 self.getAvailableCoupons { availabilityData in
1614 if let availabilityData = availabilityData { 1621 if let availabilityData = availabilityData {
1615 for tempCampaign in campaignsArray { 1622 for tempCampaign in campaignsArray {
...@@ -1621,11 +1628,19 @@ public final class WarplySDK { ...@@ -1621,11 +1628,19 @@ public final class WarplySDK {
1621 } 1628 }
1622 } 1629 }
1623 1630
1631 + // For authenticated users: use parseCampaigns (filters by coupon availability)
1624 let parsedCampaigns = self.parseCampaigns(campaignsArray) 1632 let parsedCampaigns = self.parseCampaigns(campaignsArray)
1625 completion(parsedCampaigns) 1633 completion(parsedCampaigns)
1626 } 1634 }
1627 }) 1635 })
1628 } else { 1636 } else {
1637 + // User not authenticated - return all basic campaigns without coupon filtering
1638 + print("ℹ️ [WarplySDK] User not authenticated - returning all basic campaigns without coupon filtering")
1639 +
1640 + // Skip parseCampaigns entirely - return raw campaigns without filtering
1641 + completion(campaignsArray)
1642 + }
1643 + } else {
1629 let dynatraceEvent = LoyaltySDKDynatraceEventModel() 1644 let dynatraceEvent = LoyaltySDKDynatraceEventModel()
1630 dynatraceEvent._eventName = "custom_error_campaigns_loyalty" 1645 dynatraceEvent._eventName = "custom_error_campaigns_loyalty"
1631 dynatraceEvent._parameters = nil 1646 dynatraceEvent._parameters = nil
...@@ -2967,6 +2982,19 @@ public final class WarplySDK { ...@@ -2967,6 +2982,19 @@ public final class WarplySDK {
2967 2982
2968 // MARK: - Private Helper Methods 2983 // MARK: - Private Helper Methods
2969 2984
2985 + /// Check if user is currently authenticated with valid tokens
2986 + private func isUserAuthenticated() -> Bool {
2987 + do {
2988 + if let tokenModel = try DatabaseManager.shared.getTokenModelSync() {
2989 + // Check if token exists and is not expired
2990 + return !tokenModel.accessToken.isEmpty && !tokenModel.isExpired
2991 + }
2992 + } catch {
2993 + print("⚠️ [WarplySDK] Failed to check authentication status: \(error)")
2994 + }
2995 + return false
2996 + }
2997 +
2970 private func parseCampaigns(_ campaigns: [CampaignItemModel]) -> [CampaignItemModel] { 2998 private func parseCampaigns(_ campaigns: [CampaignItemModel]) -> [CampaignItemModel] {
2971 let filteredCampaigns = campaigns.filter { $0._coupon_availability != 0 } 2999 let filteredCampaigns = campaigns.filter { $0._coupon_availability != 0 }
2972 3000
......