|
| 1 | +# 📱 Frontend Integration Guide - Subscription Verification API |
| 2 | + |
| 3 | +## 🎯 Overview |
| 4 | + |
| 5 | +This guide explains how the subscription verification API works and how your frontend should integrate with it to handle subscription-based features like ad removal and premium content. |
| 6 | + |
| 7 | +## 🌐 API Endpoint Details |
| 8 | + |
| 9 | +### Primary Verification Endpoint |
| 10 | +- **URL**: `POST /api/v1/subscription/verify` |
| 11 | +- **Method**: `POST` |
| 12 | +- **Content-Type**: `application/json` |
| 13 | +- **Purpose**: Verify if an email has an active subscription |
| 14 | + |
| 15 | +### Request Format |
| 16 | +```json |
| 17 | +{ |
| 18 | + "email": "user@example.com" |
| 19 | +} |
| 20 | +``` |
| 21 | + |
| 22 | +### Response Format (Always HTTP 200 for valid requests) |
| 23 | +```json |
| 24 | +{ |
| 25 | + "hasActiveSubscription": boolean, |
| 26 | + "subscriptionType": string | null, |
| 27 | + "expiresAt": string | null, |
| 28 | + "features": string[], |
| 29 | + "message": string | null |
| 30 | +} |
| 31 | +``` |
| 32 | + |
| 33 | +## 📋 Response Scenarios |
| 34 | + |
| 35 | +### 1. ✅ Active Subscription Found |
| 36 | +**Scenario**: Email exists in database with `is_active: true` |
| 37 | + |
| 38 | +```json |
| 39 | +{ |
| 40 | + "hasActiveSubscription": true, |
| 41 | + "subscriptionType": "premium", |
| 42 | + "expiresAt": null, |
| 43 | + "features": ["ad_removal", "priority_support"], |
| 44 | + "message": null |
| 45 | +} |
| 46 | +``` |
| 47 | + |
| 48 | +**Frontend Action**: Enable premium features, hide ads |
| 49 | + |
| 50 | +### 2. ❌ No Active Subscription (Email Exists but Inactive) |
| 51 | +**Scenario**: Email exists in database with `is_active: false` |
| 52 | + |
| 53 | +```json |
| 54 | +{ |
| 55 | + "hasActiveSubscription": false, |
| 56 | + "subscriptionType": null, |
| 57 | + "expiresAt": null, |
| 58 | + "features": [], |
| 59 | + "message": "No active subscription found for this email" |
| 60 | +} |
| 61 | +``` |
| 62 | + |
| 63 | +**Frontend Action**: Show ads, disable premium features |
| 64 | + |
| 65 | +### 3. ❌ Email Doesn't Exist in Database |
| 66 | +**Scenario**: Email has never been registered for any subscription |
| 67 | + |
| 68 | +```json |
| 69 | +{ |
| 70 | + "hasActiveSubscription": false, |
| 71 | + "subscriptionType": null, |
| 72 | + "expiresAt": null, |
| 73 | + "features": [], |
| 74 | + "message": "No active subscription found for this email" |
| 75 | +} |
| 76 | +``` |
| 77 | + |
| 78 | +**Frontend Action**: Show ads, disable premium features |
| 79 | + |
| 80 | +### 4. ⚠️ Invalid Email Format |
| 81 | +**Scenario**: Malformed email address submitted |
| 82 | + |
| 83 | +```json |
| 84 | +{ |
| 85 | + "error": "Invalid request data", |
| 86 | + "details": { |
| 87 | + "email": ["Enter a valid email address."] |
| 88 | + } |
| 89 | +} |
| 90 | +``` |
| 91 | +**HTTP Status**: `400 Bad Request` |
| 92 | +**Frontend Action**: Show validation error to user |
| 93 | + |
| 94 | +## 🔧 Frontend Implementation Logic |
| 95 | + |
| 96 | +### Basic Integration Flow |
| 97 | + |
| 98 | +```javascript |
| 99 | +async function checkSubscriptionStatus(email) { |
| 100 | + try { |
| 101 | + const response = await fetch('/api/v1/subscription/verify', { |
| 102 | + method: 'POST', |
| 103 | + headers: { |
| 104 | + 'Content-Type': 'application/json', |
| 105 | + }, |
| 106 | + body: JSON.stringify({ email: email }) |
| 107 | + }); |
| 108 | + |
| 109 | + if (response.ok) { |
| 110 | + const data = await response.json(); |
| 111 | + |
| 112 | + // Check if response has error (400 status for invalid email) |
| 113 | + if (data.error) { |
| 114 | + console.error('Validation error:', data.details); |
| 115 | + return { hasSubscription: false, error: data.details }; |
| 116 | + } |
| 117 | + |
| 118 | + // Normal response - check subscription status |
| 119 | + return { |
| 120 | + hasSubscription: data.hasActiveSubscription, |
| 121 | + features: data.features || [], |
| 122 | + subscriptionType: data.subscriptionType, |
| 123 | + message: data.message |
| 124 | + }; |
| 125 | + } else { |
| 126 | + console.error('API request failed:', response.status); |
| 127 | + return { hasSubscription: false, error: 'API request failed' }; |
| 128 | + } |
| 129 | + } catch (error) { |
| 130 | + console.error('Network error:', error); |
| 131 | + return { hasSubscription: false, error: 'Network error' }; |
| 132 | + } |
| 133 | +} |
| 134 | +``` |
| 135 | + |
| 136 | +### Feature Control Logic |
| 137 | + |
| 138 | +```javascript |
| 139 | +function applySubscriptionFeatures(subscriptionData) { |
| 140 | + const { hasSubscription, features } = subscriptionData; |
| 141 | + |
| 142 | + if (hasSubscription) { |
| 143 | + // User has active subscription |
| 144 | + if (features.includes('ad_removal')) { |
| 145 | + hideAllAds(); |
| 146 | + } |
| 147 | + if (features.includes('priority_support')) { |
| 148 | + enablePrioritySupport(); |
| 149 | + } |
| 150 | + showPremiumBadge(); |
| 151 | + } else { |
| 152 | + // User doesn't have active subscription |
| 153 | + showAllAds(); |
| 154 | + disablePremiumFeatures(); |
| 155 | + hidePremiumBadge(); |
| 156 | + } |
| 157 | +} |
| 158 | + |
| 159 | +function hideAllAds() { |
| 160 | + document.querySelectorAll('.advertisement').forEach(ad => { |
| 161 | + ad.style.display = 'none'; |
| 162 | + }); |
| 163 | +} |
| 164 | + |
| 165 | +function showAllAds() { |
| 166 | + document.querySelectorAll('.advertisement').forEach(ad => { |
| 167 | + ad.style.display = 'block'; |
| 168 | + }); |
| 169 | +} |
| 170 | +``` |
| 171 | + |
| 172 | +### Complete Usage Example |
| 173 | + |
| 174 | +```javascript |
| 175 | +// Example usage in your app |
| 176 | +async function initializeUserExperience(userEmail) { |
| 177 | + // Show loading state |
| 178 | + showLoadingSpinner(); |
| 179 | + |
| 180 | + // Check subscription status |
| 181 | + const subscriptionResult = await checkSubscriptionStatus(userEmail); |
| 182 | + |
| 183 | + if (subscriptionResult.error) { |
| 184 | + // Handle errors (invalid email, network issues, etc.) |
| 185 | + showErrorMessage('Unable to verify subscription status'); |
| 186 | + // Default to showing ads on error |
| 187 | + showAllAds(); |
| 188 | + } else { |
| 189 | + // Apply subscription-based features |
| 190 | + applySubscriptionFeatures(subscriptionResult); |
| 191 | + |
| 192 | + // Optional: Show subscription status to user |
| 193 | + if (subscriptionResult.hasSubscription) { |
| 194 | + showSubscriptionStatus('Premium Active ✨'); |
| 195 | + } |
| 196 | + } |
| 197 | + |
| 198 | + hideLoadingSpinner(); |
| 199 | +} |
| 200 | +``` |
| 201 | + |
| 202 | +## ⚠️ Important Behavior Notes |
| 203 | + |
| 204 | +### 1. **No Errors for Non-Existent Emails** |
| 205 | +- ✅ Non-existent emails return `hasActiveSubscription: false` |
| 206 | +- ✅ HTTP status is always `200 OK` for valid email formats |
| 207 | +- ✅ No exceptions or 404 errors are thrown |
| 208 | +- **Frontend should treat this the same as inactive subscriptions** |
| 209 | + |
| 210 | +### 2. **Email Validation** |
| 211 | +- ❌ Invalid email formats return `400 Bad Request` |
| 212 | +- Check for `data.error` in response to handle validation errors |
| 213 | +- Display user-friendly validation messages |
| 214 | + |
| 215 | +### 3. **Features Array** |
| 216 | +- Current features: `["ad_removal", "priority_support"]` |
| 217 | +- Check for specific features instead of assuming all premium features |
| 218 | +- Future-proof for additional feature types |
| 219 | + |
| 220 | +### 4. **Subscription Types** |
| 221 | +- Currently only `"premium"` or `null` |
| 222 | +- Future versions may include different subscription tiers |
| 223 | + |
| 224 | +## 🔄 Recommended Error Handling |
| 225 | + |
| 226 | +```javascript |
| 227 | +function handleSubscriptionResponse(data, response) { |
| 228 | + // Case 1: Validation error (400 status) |
| 229 | + if (!response.ok && data.error) { |
| 230 | + return { |
| 231 | + status: 'validation_error', |
| 232 | + message: 'Please enter a valid email address', |
| 233 | + hasSubscription: false |
| 234 | + }; |
| 235 | + } |
| 236 | + |
| 237 | + // Case 2: Network/server error (500, etc.) |
| 238 | + if (!response.ok) { |
| 239 | + return { |
| 240 | + status: 'server_error', |
| 241 | + message: 'Unable to verify subscription. Please try again.', |
| 242 | + hasSubscription: false // Default to no subscription on errors |
| 243 | + }; |
| 244 | + } |
| 245 | + |
| 246 | + // Case 3: Successful response |
| 247 | + return { |
| 248 | + status: 'success', |
| 249 | + hasSubscription: data.hasActiveSubscription, |
| 250 | + features: data.features, |
| 251 | + subscriptionType: data.subscriptionType, |
| 252 | + message: data.message |
| 253 | + }; |
| 254 | +} |
| 255 | +``` |
| 256 | + |
| 257 | +## 🧪 Testing Your Frontend Integration |
| 258 | + |
| 259 | +### Test Cases to Verify |
| 260 | + |
| 261 | +1. **Active Subscription**: |
| 262 | + - Test with: `premium_user@saomiguelbus.com` |
| 263 | + - Expected: Ads hidden, premium features enabled |
| 264 | + |
| 265 | +2. **Inactive Subscription**: |
| 266 | + - Test with: `cancelled_user@example.com` |
| 267 | + - Expected: Ads shown, premium features disabled |
| 268 | + |
| 269 | +3. **Non-Existent Email**: |
| 270 | + - Test with: `nonexistent@test.com` |
| 271 | + - Expected: Ads shown, premium features disabled (same as inactive) |
| 272 | + |
| 273 | +4. **Invalid Email**: |
| 274 | + - Test with: `invalid-email` |
| 275 | + - Expected: Validation error message, ads shown |
| 276 | + |
| 277 | +5. **Network Error**: |
| 278 | + - Test with API offline |
| 279 | + - Expected: Error message, ads shown (fail-safe) |
| 280 | + |
| 281 | +## 📊 Sample Test Data |
| 282 | + |
| 283 | +The API currently has these test subscriptions available: |
| 284 | + |
| 285 | +```javascript |
| 286 | +// Active subscriptions (should hide ads) |
| 287 | +const activeEmails = [ |
| 288 | + 'premium_user@saomiguelbus.com', |
| 289 | + 'paid_user@example.com', |
| 290 | + 'vip_user@premium.com' |
| 291 | +]; |
| 292 | + |
| 293 | +// Inactive subscriptions (should show ads) |
| 294 | +const inactiveEmails = [ |
| 295 | + 'cancelled_user@example.com', |
| 296 | + 'inactive_user@test.com' |
| 297 | +]; |
| 298 | + |
| 299 | +// Non-existent emails (should show ads) |
| 300 | +const nonExistentEmails = [ |
| 301 | + 'random@test.com', |
| 302 | + 'user@nowhere.com' |
| 303 | +]; |
| 304 | +``` |
| 305 | + |
| 306 | +## 🎯 Key Frontend Checklist |
| 307 | + |
| 308 | +- [ ] Handle `hasActiveSubscription: false` for both non-existent and inactive emails |
| 309 | +- [ ] Check `features` array for specific capabilities |
| 310 | +- [ ] Handle validation errors for invalid email formats |
| 311 | +- [ ] Implement fail-safe behavior (show ads on API errors) |
| 312 | +- [ ] Test all scenarios with sample data |
| 313 | +- [ ] Show appropriate loading states during API calls |
| 314 | +- [ ] Provide user feedback for subscription status |
| 315 | + |
| 316 | +## 🚀 Performance Tips |
| 317 | + |
| 318 | +1. **Cache subscription status** for the session to avoid repeated API calls |
| 319 | +2. **Implement timeout** for API requests (5-10 seconds recommended) |
| 320 | +3. **Show ads immediately on timeout** for better user experience |
| 321 | +4. **Preload subscription check** when user email is available |
| 322 | + |
| 323 | +## 🔒 Security Considerations |
| 324 | + |
| 325 | +- API endpoints are public (no authentication required) |
| 326 | +- Only email verification, no sensitive data exposed |
| 327 | +- Rate limiting may apply (implement request throttling if needed) |
| 328 | +- Always validate email format client-side before API calls |
| 329 | + |
| 330 | +--- |
| 331 | + |
| 332 | +This API provides a simple, reliable way to verify subscription status and control premium features in your frontend application. The consistent response format and error handling make it easy to integrate and maintain. |
0 commit comments