b5-api-client 0.0.26 → 0.0.28

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.
@@ -1,10 +1,17 @@
1
- import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
2
- import { CreateOrderRequest, CreateUserRequest, Order, OrderLockedEvent, OrderResponse, ReleaseOrderEvent as OrderReleasedEvent, TakeOrderRequest, TestEventParams, UpdateOrderRequest, TransactionStatusResponse, RateUserRequest, OrderEventsResponse, PushNotificationsRegisterRequest, ConfigResponse, DashboardMetricsResponse, KioscoinUser, UsersResponse, PaymentMethod, GetOrdersParams, CreateDisputeRequest, DisputesResponse, OrderOffersResponse, TransactionRequest, KioscoinOperationResponse, UpdateUserSettingsRequest, NotificationsResponse } from './types';
1
+ import axios, { AxiosError, AxiosInstance, AxiosRequestConfig } from 'axios';
2
+ import { CreateOrderRequest, CreateUserRequest, Order, OrderResponse, TakeOrderRequest, TestEventParams, UpdateOrderRequest, TransactionStatusResponse, RateUserRequest, OrderEventsResponse, PushNotificationsRegisterRequest, ConfigResponse, DashboardMetricsResponse, UsersResponse, GetOrdersParams, CreateDisputeRequest, DisputesResponse, OrderOffersResponse, TransactionRequest, KioscoinOperationResponse, UpdateUserSettingsRequest, NotificationsResponse, VerifyEmailRequest } from './types';
3
+ import { AuthTokenProvider } from './auth/AuthTokenProvider';
3
4
  import { isPlainObject, camelCase, snakeCase, transform } from 'lodash';
4
5
 
6
+ export interface RequestConfigWithRetry extends AxiosRequestConfig {
7
+ _retry?: boolean;
8
+ }
9
+
5
10
  class P2PMarketplaceAPIClient {
6
11
  private readonly client: AxiosInstance;
7
12
  private readonly defaultHeaders: Record<string, string>;
13
+ private authTokenProvider?: AuthTokenProvider;
14
+ private refreshTokenPromise?: Promise<string | null>;
8
15
 
9
16
  constructor(baseURL: string, timeout: number = 30000) {
10
17
  this.client = axios.create({
@@ -18,11 +25,28 @@ class P2PMarketplaceAPIClient {
18
25
  };
19
26
 
20
27
  // Add a request interceptor to transform request data to snake_case
21
- this.client.interceptors.request.use((config) => {
28
+ this.client.interceptors.request.use(async (config) => {
22
29
  if (config.data) {
23
30
  config.data = toSnakeCase(config.data);
24
31
  }
25
- return config;
32
+ const provider = this.authTokenProvider;
33
+ if (!provider) {
34
+ return config;
35
+ }
36
+
37
+ return provider
38
+ .getToken()
39
+ .then((token) => {
40
+ if (token) {
41
+ config.headers = config.headers ?? {};
42
+ config.headers['Authorization'] = `Bearer ${token}`;
43
+ }
44
+ return config;
45
+ })
46
+ .catch((error) => {
47
+ console.warn('[P2PMarketplaceAPIClient] Failed to obtain auth token before request:', error);
48
+ return config;
49
+ });
26
50
  }, (error) => {
27
51
  return Promise.reject(error);
28
52
  });
@@ -33,11 +57,42 @@ class P2PMarketplaceAPIClient {
33
57
  response.data = toCamelCase(response.data);
34
58
  }
35
59
  return response;
36
- }, (error) => {
37
- return Promise.reject(error);
60
+ }, async (error: AxiosError) => {
61
+ const { response, config } = error;
62
+ if (!this.authTokenProvider || response?.status !== 401 || !config) {
63
+ return Promise.reject(error);
64
+ }
65
+
66
+ const originalRequest = config as RequestConfigWithRetry;
67
+ if (originalRequest._retry) {
68
+ return Promise.reject(error);
69
+ }
70
+
71
+ originalRequest._retry = true;
72
+
73
+ try {
74
+ const newToken = await this.getRefreshedToken();
75
+ if (!newToken) {
76
+ return Promise.reject(error);
77
+ }
78
+
79
+ originalRequest.headers = originalRequest.headers ?? {};
80
+ originalRequest.headers['Authorization'] = `Bearer ${newToken}`;
81
+ return this.client(originalRequest);
82
+ } catch (refreshError) {
83
+ if (this.authTokenProvider?.onRefreshFailure) {
84
+ this.authTokenProvider.onRefreshFailure(refreshError);
85
+ }
86
+ return Promise.reject(error);
87
+ }
38
88
  });
39
89
  }
40
90
 
91
+ setAuthTokenProvider(provider?: AuthTokenProvider) {
92
+ this.authTokenProvider = provider;
93
+ this.refreshTokenPromise = undefined;
94
+ }
95
+
41
96
  private mergeHeaders(customHeaders?: Record<string, string>): Record<string, string> {
42
97
  return { ...this.defaultHeaders, ...customHeaders };
43
98
  }
@@ -48,6 +103,28 @@ class P2PMarketplaceAPIClient {
48
103
  };
49
104
  }
50
105
 
106
+ private async getRefreshedToken(): Promise<string | null> {
107
+ if (!this.authTokenProvider) {
108
+ return null;
109
+ }
110
+
111
+ if (!this.refreshTokenPromise) {
112
+ this.refreshTokenPromise = this.authTokenProvider
113
+ .refreshToken()
114
+ .catch((error) => {
115
+ if (this.authTokenProvider?.onRefreshFailure) {
116
+ this.authTokenProvider.onRefreshFailure(error);
117
+ }
118
+ throw error;
119
+ })
120
+ .finally(() => {
121
+ this.refreshTokenPromise = undefined;
122
+ });
123
+ }
124
+
125
+ return this.refreshTokenPromise;
126
+ }
127
+
51
128
  async get<T>(url: string, headers?: Record<string, string>): Promise<T> {
52
129
  const config = this.createConfig(headers);
53
130
  try {
@@ -142,6 +219,11 @@ class P2PMarketplaceAPIClient {
142
219
  return this.post<UsersResponse>(url, request, headers);
143
220
  }
144
221
 
222
+ public async verifyUserEmail(request: VerifyEmailRequest, headers?: Record<string, string>): Promise<UsersResponse> {
223
+ const url = '/api/users/verify_email';
224
+ return this.post<UsersResponse>(url, request, headers);
225
+ }
226
+
145
227
  public async registerPushToken(registerRequest: PushNotificationsRegisterRequest, headers?: Record<string, string>): Promise<any> {
146
228
  const url = `/api/notifications/register-push`;
147
229
  return this.post<PushNotificationsRegisterRequest>(url, registerRequest, headers);
@@ -183,58 +265,6 @@ class P2PMarketplaceAPIClient {
183
265
  }
184
266
 
185
267
 
186
-
187
- public async testLockFunds(order: Order, testParams?: TestEventParams, headers?: Record<string, string>) {
188
- const orderEvent: OrderLockedEvent = {
189
- type: "LOCKED",
190
- orderId: order.id ?? "",
191
- buyerAddress: order.buyerAddress ?? "",
192
- amount: order.amount?.toString() ?? "0",
193
- transaction: {
194
- hash: "0x976d8c61b958e286d907a2e1c5ffde8c9316800ab4de1396a54927a39dc533a2",
195
- },
196
- tokenContractAddress: "0xee5e8291b551603a19ef41eea69ae49592ed14f8",
197
- buyerHash:
198
- "02809d5cc2ec09a7503ef77358654a5bfc958e08c04ecb33f412884c0933be68",
199
- sellerAddress: "0x2A4E89D18C2742FEDC65444d339cC5fAF3dE4dF1",
200
- sellerHash:
201
- "4b3560738b8e3cb8a0a30ac664b12de2ace977c62ade94bc08a37fe5b93bf34b",
202
- fee: "30000000000000000",
203
- timestamp: new Date().toISOString(),
204
- };
205
-
206
- const url = "api/events/simulate/lock"
207
- try {
208
- await this.post<undefined>(url, orderEvent, headers);
209
- } catch (error) {
210
- console.error("Error locking funds:", error);
211
- throw new Error("Failed to lock funds");
212
- }
213
- }
214
-
215
- public async testReleaseFunds(order: Order, testParams?: TestEventParams, headers?: Record<string, string>) {
216
- const releaseOrderEvent: OrderReleasedEvent = {
217
- type: "RELEASED",
218
- orderId: order.id ?? "",
219
- buyerAddress: order.buyerAddress ?? "",
220
- amount: order.amount?.toString() ?? "0",
221
- transaction: {
222
- hash: "0xeab4797bf0e13dff0bda481503a544698127c5f97dd826c465ebdb41bcfec3f5",
223
- },
224
- tokenContractAddress: "0xee5e8291b551603a19ef41eea69ae49592ed14f8",
225
- adminAction: true,
226
- timestamp: new Date().toISOString(),
227
- };
228
-
229
- const url = "api/events/simulate/release"
230
- try {
231
- await this.post<undefined>(url, releaseOrderEvent, headers);
232
- } catch (error) {
233
- console.error("Error releasing funds:", error);
234
- throw new Error("Failed to release funds");
235
- }
236
- }
237
-
238
268
  public async releaseFunds(order: Order, testParams?: TestEventParams, headers?: Record<string, string>): Promise<string> {
239
269
  const body = {
240
270
  orderId: order.id ?? "",
@@ -0,0 +1,5 @@
1
+ export interface AuthTokenProvider {
2
+ getToken(): Promise<string | null>;
3
+ refreshToken(): Promise<string | null>;
4
+ onRefreshFailure?(error: unknown): void;
5
+ }