Skip to content

Commit 2a30890

Browse files
committed
retry logic
1 parent d4da338 commit 2a30890

File tree

1 file changed

+84
-20
lines changed

1 file changed

+84
-20
lines changed

frontend/src/services/paymentService.js

Lines changed: 84 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:5000
44

55
class PaymentService {
66
constructor() {
7+
// Cache for pricing data to avoid repeated API calls
8+
this.pricingCache = null
9+
this.pricingCacheExpiry = null
10+
this.CACHE_DURATION = 5 * 60 * 1000 // 5 minutes
11+
712
this.api = axios.create({
813
baseURL: API_BASE_URL,
914
headers: {
@@ -26,47 +31,106 @@ class PaymentService {
2631
return config
2732
})
2833

29-
// Handle response errors
34+
// Handle response errors with retry logic for rate limiting
3035
this.api.interceptors.response.use(
3136
(response) => response.data,
3237
(error) => {
33-
const message = error.response?.data?.message || error.message || 'An error occurred'
34-
throw new Error(message)
38+
const enhancedError = new Error(error.response?.data?.message || error.message || 'An error occurred')
39+
enhancedError.status = error.response?.status
40+
enhancedError.response = error.response
41+
enhancedError.retryAfter = error.response?.data?.retryAfter
42+
throw enhancedError
3543
}
3644
)
3745
}
3846

39-
// Create payment order
47+
// Helper method to handle retries for rate limiting
48+
async makeRequestWithRetry(requestFn, retryCount = 0, maxRetries = 3) {
49+
try {
50+
return await requestFn()
51+
} catch (error) {
52+
// Handle rate limiting (HTTP 429)
53+
if (error.status === 429 && retryCount < maxRetries) {
54+
const retryAfter = error.retryAfter || Math.pow(2, retryCount + 1) // Exponential backoff
55+
const delayMs = retryAfter * 1000
56+
57+
console.warn(`[PaymentService] Rate limited (429). Retrying after ${delayMs}ms... (attempt ${retryCount + 1}/${maxRetries})`)
58+
59+
await new Promise(resolve => setTimeout(resolve, delayMs))
60+
return this.makeRequestWithRetry(requestFn, retryCount + 1, maxRetries)
61+
}
62+
63+
// For 429 errors that exceed max retries, provide user-friendly message
64+
if (error.status === 429) {
65+
const userFriendlyError = new Error('Too many requests. Please wait a moment and try again.')
66+
userFriendlyError.status = 429
67+
userFriendlyError.isRateLimit = true
68+
throw userFriendlyError
69+
}
70+
71+
throw error
72+
}
73+
}
74+
75+
// Create payment order with retry logic
4076
async createOrder(amount, planId) {
41-
const response = await this.api.post('/payment/create-order', {
42-
amount,
43-
planId
77+
return this.makeRequestWithRetry(async () => {
78+
const response = await this.api.post('/payment/create-order', {
79+
amount,
80+
planId
81+
})
82+
return response
4483
})
45-
return response
4684
}
4785

48-
// Get international pricing based on user's location
86+
// Get international pricing with caching and retry logic
4987
async getInternationalPricing() {
50-
const response = await this.api.get('/payment/pricing')
51-
return response
88+
// Check cache first
89+
if (this.pricingCache && this.pricingCacheExpiry && Date.now() < this.pricingCacheExpiry) {
90+
console.log('[PaymentService] Returning cached pricing data')
91+
return this.pricingCache
92+
}
93+
94+
return this.makeRequestWithRetry(async () => {
95+
const response = await this.api.get('/payment/pricing')
96+
97+
// Cache the response
98+
this.pricingCache = response
99+
this.pricingCacheExpiry = Date.now() + this.CACHE_DURATION
100+
101+
return response
102+
})
52103
}
53104

54-
// Verify payment
105+
// Verify payment with retry logic
55106
async verifyPayment(paymentData) {
56-
const response = await this.api.post('/payment/verify', paymentData)
57-
return response
107+
return this.makeRequestWithRetry(async () => {
108+
const response = await this.api.post('/payment/verify', paymentData)
109+
return response
110+
})
58111
}
59112

60-
// Get payment history
113+
// Get payment history with retry logic
61114
async getPaymentHistory() {
62-
const response = await this.api.get('/payment/history')
63-
return response.payments
115+
return this.makeRequestWithRetry(async () => {
116+
const response = await this.api.get('/payment/history')
117+
return response.payments
118+
})
64119
}
65120

66-
// Cancel subscription
121+
// Cancel subscription with retry logic
67122
async cancelSubscription() {
68-
const response = await this.api.post('/payment/cancel-subscription')
69-
return response
123+
return this.makeRequestWithRetry(async () => {
124+
const response = await this.api.post('/payment/cancel-subscription')
125+
return response
126+
})
127+
}
128+
129+
// Clear pricing cache (useful for testing or manual refresh)
130+
clearPricingCache() {
131+
this.pricingCache = null
132+
this.pricingCacheExpiry = null
133+
console.log('[PaymentService] Pricing cache cleared')
70134
}
71135
}
72136

0 commit comments

Comments
 (0)