react-native-fpay 0.4.6 → 0.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -8,6 +8,8 @@ import axios from 'axios';
|
|
|
8
8
|
import { getFPStore } from "../../store/FPStore.js";
|
|
9
9
|
const DEFAULT_BASE_URL = 'https://kenisha-happiest-nan.ngrok-free.dev/sdk/v1/fpip/';
|
|
10
10
|
let _client = null;
|
|
11
|
+
let _isRefreshing = false;
|
|
12
|
+
let _retryQueue = [];
|
|
11
13
|
export function initClient(apiKey, options = {}) {
|
|
12
14
|
_client = axios.create({
|
|
13
15
|
baseURL: options.baseUrl ?? DEFAULT_BASE_URL,
|
|
@@ -32,7 +34,61 @@ export function initClient(apiKey, options = {}) {
|
|
|
32
34
|
}
|
|
33
35
|
return config;
|
|
34
36
|
});
|
|
35
|
-
_client.interceptors.response.use(r => r, err => {
|
|
37
|
+
_client.interceptors.response.use(r => r, async err => {
|
|
38
|
+
const originalRequest = err.config;
|
|
39
|
+
|
|
40
|
+
// Handle 401 — token expired
|
|
41
|
+
if (err.response?.status === 401 && !originalRequest._retry) {
|
|
42
|
+
originalRequest._retry = true; // prevent infinite loop
|
|
43
|
+
|
|
44
|
+
if (_isRefreshing) {
|
|
45
|
+
// Another request already refreshing — queue this one
|
|
46
|
+
return new Promise(resolve => {
|
|
47
|
+
_retryQueue.push(newToken => {
|
|
48
|
+
originalRequest.headers.Authorization = `Bearer ${newToken}`;
|
|
49
|
+
resolve(_client.request(originalRequest));
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
_isRefreshing = true;
|
|
54
|
+
try {
|
|
55
|
+
console.log('[FPay HTTP] Token expired — re-authenticating...');
|
|
56
|
+
|
|
57
|
+
// Clear expired token first
|
|
58
|
+
getFPStore().clearAccessToken();
|
|
59
|
+
|
|
60
|
+
// Re-authenticate with the stored API key
|
|
61
|
+
const apiKey = originalRequest.headers['x-api-key'];
|
|
62
|
+
const response = await _client.post('/auth', {
|
|
63
|
+
api_key: apiKey
|
|
64
|
+
});
|
|
65
|
+
if (response.data?.status && response.data?.payload?.access_token) {
|
|
66
|
+
const newToken = response.data.payload.access_token;
|
|
67
|
+
const expiresIn = response.data.payload.expires_in;
|
|
68
|
+
|
|
69
|
+
// Save new token to store
|
|
70
|
+
getFPStore().setAccessToken(newToken, expiresIn);
|
|
71
|
+
console.log('[FPay HTTP] Re-authenticated successfully.');
|
|
72
|
+
|
|
73
|
+
// Retry queued requests with new token
|
|
74
|
+
_retryQueue.forEach(cb => cb(newToken));
|
|
75
|
+
_retryQueue = [];
|
|
76
|
+
|
|
77
|
+
// Retry original request with new token
|
|
78
|
+
originalRequest.headers.Authorization = `Bearer ${newToken}`;
|
|
79
|
+
return _client.request(originalRequest);
|
|
80
|
+
}
|
|
81
|
+
} catch (refreshErr) {
|
|
82
|
+
console.error('[FPay HTTP] Re-authentication failed:', refreshErr);
|
|
83
|
+
_retryQueue = [];
|
|
84
|
+
getFPStore().clearAccessToken();
|
|
85
|
+
getFPStore().setAuthenticated(false, 'Session expired. Please restart the app.');
|
|
86
|
+
} finally {
|
|
87
|
+
_isRefreshing = false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// All other errors — normalize and reject
|
|
36
92
|
const e = {
|
|
37
93
|
code: 'UNKNOWN',
|
|
38
94
|
message: 'An error occurred'
|
|
@@ -45,10 +101,6 @@ export function initClient(apiKey, options = {}) {
|
|
|
45
101
|
e.code = d.code ?? `HTTP_${err.response.status}`;
|
|
46
102
|
e.message = d.message ?? err.message;
|
|
47
103
|
e.statusCode = err.response.status;
|
|
48
|
-
if (err.response.status === 401) {
|
|
49
|
-
e.code = 'INVALID_API_KEY';
|
|
50
|
-
e.message = 'Invalid API key.';
|
|
51
|
-
}
|
|
52
104
|
}
|
|
53
105
|
return Promise.reject(e);
|
|
54
106
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["axios","getFPStore","DEFAULT_BASE_URL","_client","initClient","apiKey","options","create","baseURL","baseUrl","timeout","headers","environment","interceptors","request","use","config","accessToken","console","log","Authorization","response","r","err","
|
|
1
|
+
{"version":3,"names":["axios","getFPStore","DEFAULT_BASE_URL","_client","_isRefreshing","_retryQueue","initClient","apiKey","options","create","baseURL","baseUrl","timeout","headers","environment","interceptors","request","use","config","accessToken","console","log","Authorization","response","r","err","originalRequest","status","_retry","Promise","resolve","push","newToken","clearAccessToken","post","api_key","data","payload","access_token","expiresIn","expires_in","setAccessToken","forEach","cb","refreshErr","error","setAuthenticated","e","code","message","d","statusCode","reject","http","Error"],"sourceRoot":"../../../../src","sources":["core/api/client.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../../../src/core/api/client.ts"],"names":[],"mappings":"AAIA,OAAc,EAAE,KAAK,aAAa,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../../../src/core/api/client.ts"],"names":[],"mappings":"AAIA,OAAc,EAAE,KAAK,aAAa,EAAE,MAAM,OAAO,CAAC;AAWlD,wBAAgB,UAAU,CACxB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAO,GACvD,IAAI,CAgGN;AAED,wBAAgB,IAAI,IAAI,aAAa,CAMpC"}
|
package/package.json
CHANGED
package/src/core/api/client.ts
CHANGED
|
@@ -10,6 +10,8 @@ const DEFAULT_BASE_URL =
|
|
|
10
10
|
'https://kenisha-happiest-nan.ngrok-free.dev/sdk/v1/fpip/';
|
|
11
11
|
|
|
12
12
|
let _client: AxiosInstance | null = null;
|
|
13
|
+
let _isRefreshing = false;
|
|
14
|
+
let _retryQueue: Array<(token: string) => void> = [];
|
|
13
15
|
|
|
14
16
|
export function initClient(
|
|
15
17
|
apiKey: string,
|
|
@@ -41,7 +43,62 @@ export function initClient(
|
|
|
41
43
|
|
|
42
44
|
_client.interceptors.response.use(
|
|
43
45
|
(r) => r,
|
|
44
|
-
(err) => {
|
|
46
|
+
async (err) => {
|
|
47
|
+
const originalRequest = err.config;
|
|
48
|
+
|
|
49
|
+
// Handle 401 — token expired
|
|
50
|
+
if (err.response?.status === 401 && !originalRequest._retry) {
|
|
51
|
+
originalRequest._retry = true; // prevent infinite loop
|
|
52
|
+
|
|
53
|
+
if (_isRefreshing) {
|
|
54
|
+
// Another request already refreshing — queue this one
|
|
55
|
+
return new Promise((resolve) => {
|
|
56
|
+
_retryQueue.push((newToken: string) => {
|
|
57
|
+
originalRequest.headers.Authorization = `Bearer ${newToken}`;
|
|
58
|
+
resolve(_client!.request(originalRequest));
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
_isRefreshing = true;
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
console.log('[FPay HTTP] Token expired — re-authenticating...');
|
|
67
|
+
|
|
68
|
+
// Clear expired token first
|
|
69
|
+
getFPStore().clearAccessToken();
|
|
70
|
+
|
|
71
|
+
// Re-authenticate with the stored API key
|
|
72
|
+
const apiKey = originalRequest.headers['x-api-key'];
|
|
73
|
+
const response = await _client!.post('/auth', { api_key: apiKey });
|
|
74
|
+
|
|
75
|
+
if (response.data?.status && response.data?.payload?.access_token) {
|
|
76
|
+
const newToken = response.data.payload.access_token;
|
|
77
|
+
const expiresIn = response.data.payload.expires_in;
|
|
78
|
+
|
|
79
|
+
// Save new token to store
|
|
80
|
+
getFPStore().setAccessToken(newToken, expiresIn);
|
|
81
|
+
console.log('[FPay HTTP] Re-authenticated successfully.');
|
|
82
|
+
|
|
83
|
+
// Retry queued requests with new token
|
|
84
|
+
_retryQueue.forEach(cb => cb(newToken));
|
|
85
|
+
_retryQueue = [];
|
|
86
|
+
|
|
87
|
+
// Retry original request with new token
|
|
88
|
+
originalRequest.headers.Authorization = `Bearer ${newToken}`;
|
|
89
|
+
return _client!.request(originalRequest);
|
|
90
|
+
}
|
|
91
|
+
} catch (refreshErr) {
|
|
92
|
+
console.error('[FPay HTTP] Re-authentication failed:', refreshErr);
|
|
93
|
+
_retryQueue = [];
|
|
94
|
+
getFPStore().clearAccessToken();
|
|
95
|
+
getFPStore().setAuthenticated(false, 'Session expired. Please restart the app.');
|
|
96
|
+
} finally {
|
|
97
|
+
_isRefreshing = false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// All other errors — normalize and reject
|
|
45
102
|
const e: FPError = { code: 'UNKNOWN', message: 'An error occurred' };
|
|
46
103
|
if (!err.response) {
|
|
47
104
|
e.code = 'NETWORK_ERROR';
|
|
@@ -51,10 +108,6 @@ export function initClient(
|
|
|
51
108
|
e.code = d.code ?? `HTTP_${err.response.status}`;
|
|
52
109
|
e.message = d.message ?? err.message;
|
|
53
110
|
e.statusCode = err.response.status;
|
|
54
|
-
if (err.response.status === 401) {
|
|
55
|
-
e.code = 'INVALID_API_KEY';
|
|
56
|
-
e.message = 'Invalid API key.';
|
|
57
|
-
}
|
|
58
111
|
}
|
|
59
112
|
return Promise.reject(e);
|
|
60
113
|
}
|