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,42 +1589,57 @@ public final class WarplySDK { ...@@ -1589,42 +1589,57 @@ 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 - self.getCampaignsPersonalized(language: language, filters: filters, completion: { personalizedCampaigns in 1593 + if self.isUserAuthenticated() {
1594 - campaignsArray = campaignsArray + (personalizedCampaigns ?? []) 1594 + // User is authenticated - get full experience with personalized campaigns and coupon filtering
1595 + print("✅ [WarplySDK] User authenticated - loading personalized campaigns and coupon availability")
1595 1596
1596 - // Get available coupons (still using MyApi temporarily) 1597 + self.getCampaignsPersonalized(language: language, filters: filters, completion: { personalizedCampaigns in
1597 - self.getAvailableCoupons { availabilityData in 1598 + campaignsArray = campaignsArray + (personalizedCampaigns ?? [])
1598 - if let availabilityData = availabilityData { 1599 +
1599 - for tempCampaign in campaignsArray { 1600 + // Get available coupons for authenticated users
1600 - for item in availabilityData { 1601 + self.getAvailableCoupons { availabilityData in
1601 - if tempCampaign._couponset == item.key { 1602 + if let availabilityData = availabilityData {
1602 - tempCampaign._coupon_availability = item.value as? Int ?? 0 1603 + for tempCampaign in campaignsArray {
1604 + for item in availabilityData {
1605 + if tempCampaign._couponset == item.key {
1606 + tempCampaign._coupon_availability = item.value as? Int ?? 0
1607 + }
1603 } 1608 }
1604 } 1609 }
1605 } 1610 }
1611 +
1612 + // For authenticated users: use parseCampaigns (filters by coupon availability)
1613 + let parsedCampaigns = self.parseCampaigns(campaignsArray)
1614 + completion(parsedCampaigns)
1606 } 1615 }
1616 + }, failureCallback: { errorCode in
1617 + // If personalized campaigns fail, still try coupon availability
1618 + print("⚠️ [WarplySDK] Personalized campaigns failed for authenticated user - continuing with basic campaigns")
1607 1619
1608 - let parsedCampaigns = self.parseCampaigns(campaignsArray) 1620 + self.getAvailableCoupons { availabilityData in
1609 - completion(parsedCampaigns) 1621 + if let availabilityData = availabilityData {
1610 - } 1622 + for tempCampaign in campaignsArray {
1611 - }, failureCallback: { errorCode in 1623 + for item in availabilityData {
1612 - // If personalized campaigns fail, continue with regular campaigns 1624 + if tempCampaign._couponset == item.key {
1613 - self.getAvailableCoupons { availabilityData in 1625 + tempCampaign._coupon_availability = item.value as? Int ?? 0
1614 - if let availabilityData = availabilityData { 1626 + }
1615 - for tempCampaign in campaignsArray {
1616 - for item in availabilityData {
1617 - if tempCampaign._couponset == item.key {
1618 - tempCampaign._coupon_availability = item.value as? Int ?? 0
1619 } 1627 }
1620 } 1628 }
1621 } 1629 }
1630 +
1631 + // For authenticated users: use parseCampaigns (filters by coupon availability)
1632 + let parsedCampaigns = self.parseCampaigns(campaignsArray)
1633 + completion(parsedCampaigns)
1622 } 1634 }
1623 - 1635 + })
1624 - let parsedCampaigns = self.parseCampaigns(campaignsArray) 1636 + } else {
1625 - completion(parsedCampaigns) 1637 + // User not authenticated - return all basic campaigns without coupon filtering
1626 - } 1638 + print("ℹ️ [WarplySDK] User not authenticated - returning all basic campaigns without coupon filtering")
1627 - }) 1639 +
1640 + // Skip parseCampaigns entirely - return raw campaigns without filtering
1641 + completion(campaignsArray)
1642 + }
1628 } else { 1643 } else {
1629 let dynatraceEvent = LoyaltySDKDynatraceEventModel() 1644 let dynatraceEvent = LoyaltySDKDynatraceEventModel()
1630 dynatraceEvent._eventName = "custom_error_campaigns_loyalty" 1645 dynatraceEvent._eventName = "custom_error_campaigns_loyalty"
...@@ -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
......