@djangocfg/ext-payments 1.0.8 → 1.0.10
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.
- package/dist/config.cjs +3 -1
- package/dist/config.js +3 -1
- package/dist/hooks.cjs +198 -160
- package/dist/hooks.js +48 -10
- package/dist/index.cjs +198 -160
- package/dist/index.d.cts +36 -13
- package/dist/index.d.ts +36 -13
- package/dist/index.js +48 -10
- package/package.json +7 -5
- package/src/api/generated/ext_payments/CLAUDE.md +1 -8
- package/src/api/generated/ext_payments/_utils/schemas/PaymentDetail.schema.ts +1 -1
- package/src/api/generated/ext_payments/api-instance.ts +61 -13
- package/src/api/generated/ext_payments/client.ts +23 -2
- package/src/api/generated/ext_payments/http.ts +8 -2
- package/src/api/generated/ext_payments/index.ts +3 -1
- package/src/api/index.ts +6 -1
- package/src/layouts/PaymentsLayout/PaymentsLayout.tsx +1 -1
- package/src/layouts/PaymentsLayout/components/CreatePaymentDialog.tsx +1 -1
- package/src/layouts/PaymentsLayout/components/PaymentDetailsDialog.tsx +1 -1
- package/src/layouts/PaymentsLayout/views/overview/components/BalanceCard.tsx +1 -1
- package/src/layouts/PaymentsLayout/views/overview/components/RecentPayments.tsx +1 -1
- package/src/layouts/PaymentsLayout/views/payments/components/PaymentsList.tsx +4 -3
- package/src/layouts/PaymentsLayout/views/transactions/components/TransactionsList.tsx +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -279,6 +279,8 @@ interface HttpRequest {
|
|
|
279
279
|
params?: Record<string, any>;
|
|
280
280
|
/** FormData for file uploads (multipart/form-data) */
|
|
281
281
|
formData?: FormData;
|
|
282
|
+
/** Binary data for octet-stream uploads */
|
|
283
|
+
binaryBody?: Blob | ArrayBuffer;
|
|
282
284
|
}
|
|
283
285
|
interface HttpResponse<T = any> {
|
|
284
286
|
data: T;
|
|
@@ -531,11 +533,13 @@ declare class APIClient {
|
|
|
531
533
|
private httpClient;
|
|
532
534
|
private logger;
|
|
533
535
|
private retryConfig;
|
|
536
|
+
private tokenGetter;
|
|
534
537
|
ext_payments_payments: ExtPaymentsPayments;
|
|
535
538
|
constructor(baseUrl: string, options?: {
|
|
536
539
|
httpClient?: HttpClientAdapter;
|
|
537
540
|
loggerConfig?: Partial<LoggerConfig>;
|
|
538
541
|
retryConfig?: RetryConfig;
|
|
542
|
+
tokenGetter?: () => string | null;
|
|
539
543
|
});
|
|
540
544
|
/**
|
|
541
545
|
* Get CSRF token from cookies (for SessionAuthentication).
|
|
@@ -543,6 +547,15 @@ declare class APIClient {
|
|
|
543
547
|
* Returns null if cookie doesn't exist (JWT-only auth).
|
|
544
548
|
*/
|
|
545
549
|
getCsrfToken(): string | null;
|
|
550
|
+
/**
|
|
551
|
+
* Get the base URL for building streaming/download URLs.
|
|
552
|
+
*/
|
|
553
|
+
getBaseUrl(): string;
|
|
554
|
+
/**
|
|
555
|
+
* Get JWT token for URL authentication (used in streaming endpoints).
|
|
556
|
+
* Returns null if no token getter is configured or no token is available.
|
|
557
|
+
*/
|
|
558
|
+
getToken(): string | null;
|
|
546
559
|
/**
|
|
547
560
|
* Make HTTP request with Django CSRF and session handling.
|
|
548
561
|
* Automatically retries on network errors and 5xx server errors.
|
|
@@ -551,6 +564,7 @@ declare class APIClient {
|
|
|
551
564
|
params?: Record<string, any>;
|
|
552
565
|
body?: any;
|
|
553
566
|
formData?: FormData;
|
|
567
|
+
binaryBody?: Blob | ArrayBuffer;
|
|
554
568
|
headers?: Record<string, string>;
|
|
555
569
|
}): Promise<T>;
|
|
556
570
|
/**
|
|
@@ -715,7 +729,7 @@ declare const PaymentDetailSchema: z.ZodObject<{
|
|
|
715
729
|
status_display: z.ZodString;
|
|
716
730
|
pay_address: z.ZodNullable<z.ZodString>;
|
|
717
731
|
qr_code_url: z.ZodNullable<z.ZodString>;
|
|
718
|
-
payment_url: z.ZodNullable<z.ZodURL
|
|
732
|
+
payment_url: z.ZodNullable<z.ZodUnion<readonly [z.ZodURL, z.ZodLiteral<"">]>>;
|
|
719
733
|
transaction_hash: z.ZodNullable<z.ZodString>;
|
|
720
734
|
explorer_link: z.ZodNullable<z.ZodString>;
|
|
721
735
|
confirmations_count: z.ZodInt;
|
|
@@ -995,30 +1009,38 @@ declare namespace index {
|
|
|
995
1009
|
}
|
|
996
1010
|
|
|
997
1011
|
/**
|
|
998
|
-
* Global API Instance - Singleton configuration
|
|
1012
|
+
* Global API Instance - Singleton configuration with auto-configuration support
|
|
999
1013
|
*
|
|
1000
|
-
* This module provides a global API instance that
|
|
1001
|
-
*
|
|
1014
|
+
* This module provides a global API instance that auto-configures from
|
|
1015
|
+
* environment variables or can be configured manually.
|
|
1002
1016
|
*
|
|
1003
|
-
*
|
|
1017
|
+
* AUTO-CONFIGURATION (recommended):
|
|
1018
|
+
* Set one of these environment variables and the API will auto-configure:
|
|
1019
|
+
* - NEXT_PUBLIC_API_URL (Next.js)
|
|
1020
|
+
* - VITE_API_URL (Vite)
|
|
1021
|
+
* - REACT_APP_API_URL (Create React App)
|
|
1022
|
+
* - API_URL (generic)
|
|
1023
|
+
*
|
|
1024
|
+
* Then just use fetchers and hooks directly:
|
|
1025
|
+
* ```typescript
|
|
1026
|
+
* import { getUsers } from './_utils/fetchers'
|
|
1027
|
+
* const users = await getUsers({ page: 1 })
|
|
1028
|
+
* ```
|
|
1029
|
+
*
|
|
1030
|
+
* MANUAL CONFIGURATION:
|
|
1004
1031
|
* ```typescript
|
|
1005
|
-
* // Configure once (e.g., in your app entry point)
|
|
1006
1032
|
* import { configureAPI } from './api-instance'
|
|
1007
1033
|
*
|
|
1008
1034
|
* configureAPI({
|
|
1009
1035
|
* baseUrl: 'https://api.example.com',
|
|
1010
1036
|
* token: 'your-jwt-token'
|
|
1011
1037
|
* })
|
|
1012
|
-
*
|
|
1013
|
-
* // Then use fetchers and hooks anywhere without configuration
|
|
1014
|
-
* import { getUsers } from './fetchers'
|
|
1015
|
-
* const users = await getUsers({ page: 1 })
|
|
1016
1038
|
* ```
|
|
1017
1039
|
*
|
|
1018
1040
|
* For SSR or multiple instances:
|
|
1019
1041
|
* ```typescript
|
|
1020
1042
|
* import { API } from './index'
|
|
1021
|
-
* import { getUsers } from './fetchers'
|
|
1043
|
+
* import { getUsers } from './_utils/fetchers'
|
|
1022
1044
|
*
|
|
1023
1045
|
* const api = new API('https://api.example.com')
|
|
1024
1046
|
* const users = await getUsers({ page: 1 }, api)
|
|
@@ -1027,11 +1049,12 @@ declare namespace index {
|
|
|
1027
1049
|
|
|
1028
1050
|
/**
|
|
1029
1051
|
* Get the global API instance
|
|
1030
|
-
*
|
|
1052
|
+
* Auto-configures from environment variables on first call if not manually configured.
|
|
1053
|
+
* @throws Error if API is not configured and no env variable is set
|
|
1031
1054
|
*/
|
|
1032
1055
|
declare function getAPIInstance(): API;
|
|
1033
1056
|
/**
|
|
1034
|
-
* Check if API is configured
|
|
1057
|
+
* Check if API is configured (or can be auto-configured)
|
|
1035
1058
|
*/
|
|
1036
1059
|
declare function isAPIConfigured(): boolean;
|
|
1037
1060
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -279,6 +279,8 @@ interface HttpRequest {
|
|
|
279
279
|
params?: Record<string, any>;
|
|
280
280
|
/** FormData for file uploads (multipart/form-data) */
|
|
281
281
|
formData?: FormData;
|
|
282
|
+
/** Binary data for octet-stream uploads */
|
|
283
|
+
binaryBody?: Blob | ArrayBuffer;
|
|
282
284
|
}
|
|
283
285
|
interface HttpResponse<T = any> {
|
|
284
286
|
data: T;
|
|
@@ -531,11 +533,13 @@ declare class APIClient {
|
|
|
531
533
|
private httpClient;
|
|
532
534
|
private logger;
|
|
533
535
|
private retryConfig;
|
|
536
|
+
private tokenGetter;
|
|
534
537
|
ext_payments_payments: ExtPaymentsPayments;
|
|
535
538
|
constructor(baseUrl: string, options?: {
|
|
536
539
|
httpClient?: HttpClientAdapter;
|
|
537
540
|
loggerConfig?: Partial<LoggerConfig>;
|
|
538
541
|
retryConfig?: RetryConfig;
|
|
542
|
+
tokenGetter?: () => string | null;
|
|
539
543
|
});
|
|
540
544
|
/**
|
|
541
545
|
* Get CSRF token from cookies (for SessionAuthentication).
|
|
@@ -543,6 +547,15 @@ declare class APIClient {
|
|
|
543
547
|
* Returns null if cookie doesn't exist (JWT-only auth).
|
|
544
548
|
*/
|
|
545
549
|
getCsrfToken(): string | null;
|
|
550
|
+
/**
|
|
551
|
+
* Get the base URL for building streaming/download URLs.
|
|
552
|
+
*/
|
|
553
|
+
getBaseUrl(): string;
|
|
554
|
+
/**
|
|
555
|
+
* Get JWT token for URL authentication (used in streaming endpoints).
|
|
556
|
+
* Returns null if no token getter is configured or no token is available.
|
|
557
|
+
*/
|
|
558
|
+
getToken(): string | null;
|
|
546
559
|
/**
|
|
547
560
|
* Make HTTP request with Django CSRF and session handling.
|
|
548
561
|
* Automatically retries on network errors and 5xx server errors.
|
|
@@ -551,6 +564,7 @@ declare class APIClient {
|
|
|
551
564
|
params?: Record<string, any>;
|
|
552
565
|
body?: any;
|
|
553
566
|
formData?: FormData;
|
|
567
|
+
binaryBody?: Blob | ArrayBuffer;
|
|
554
568
|
headers?: Record<string, string>;
|
|
555
569
|
}): Promise<T>;
|
|
556
570
|
/**
|
|
@@ -715,7 +729,7 @@ declare const PaymentDetailSchema: z.ZodObject<{
|
|
|
715
729
|
status_display: z.ZodString;
|
|
716
730
|
pay_address: z.ZodNullable<z.ZodString>;
|
|
717
731
|
qr_code_url: z.ZodNullable<z.ZodString>;
|
|
718
|
-
payment_url: z.ZodNullable<z.ZodURL
|
|
732
|
+
payment_url: z.ZodNullable<z.ZodUnion<readonly [z.ZodURL, z.ZodLiteral<"">]>>;
|
|
719
733
|
transaction_hash: z.ZodNullable<z.ZodString>;
|
|
720
734
|
explorer_link: z.ZodNullable<z.ZodString>;
|
|
721
735
|
confirmations_count: z.ZodInt;
|
|
@@ -995,30 +1009,38 @@ declare namespace index {
|
|
|
995
1009
|
}
|
|
996
1010
|
|
|
997
1011
|
/**
|
|
998
|
-
* Global API Instance - Singleton configuration
|
|
1012
|
+
* Global API Instance - Singleton configuration with auto-configuration support
|
|
999
1013
|
*
|
|
1000
|
-
* This module provides a global API instance that
|
|
1001
|
-
*
|
|
1014
|
+
* This module provides a global API instance that auto-configures from
|
|
1015
|
+
* environment variables or can be configured manually.
|
|
1002
1016
|
*
|
|
1003
|
-
*
|
|
1017
|
+
* AUTO-CONFIGURATION (recommended):
|
|
1018
|
+
* Set one of these environment variables and the API will auto-configure:
|
|
1019
|
+
* - NEXT_PUBLIC_API_URL (Next.js)
|
|
1020
|
+
* - VITE_API_URL (Vite)
|
|
1021
|
+
* - REACT_APP_API_URL (Create React App)
|
|
1022
|
+
* - API_URL (generic)
|
|
1023
|
+
*
|
|
1024
|
+
* Then just use fetchers and hooks directly:
|
|
1025
|
+
* ```typescript
|
|
1026
|
+
* import { getUsers } from './_utils/fetchers'
|
|
1027
|
+
* const users = await getUsers({ page: 1 })
|
|
1028
|
+
* ```
|
|
1029
|
+
*
|
|
1030
|
+
* MANUAL CONFIGURATION:
|
|
1004
1031
|
* ```typescript
|
|
1005
|
-
* // Configure once (e.g., in your app entry point)
|
|
1006
1032
|
* import { configureAPI } from './api-instance'
|
|
1007
1033
|
*
|
|
1008
1034
|
* configureAPI({
|
|
1009
1035
|
* baseUrl: 'https://api.example.com',
|
|
1010
1036
|
* token: 'your-jwt-token'
|
|
1011
1037
|
* })
|
|
1012
|
-
*
|
|
1013
|
-
* // Then use fetchers and hooks anywhere without configuration
|
|
1014
|
-
* import { getUsers } from './fetchers'
|
|
1015
|
-
* const users = await getUsers({ page: 1 })
|
|
1016
1038
|
* ```
|
|
1017
1039
|
*
|
|
1018
1040
|
* For SSR or multiple instances:
|
|
1019
1041
|
* ```typescript
|
|
1020
1042
|
* import { API } from './index'
|
|
1021
|
-
* import { getUsers } from './fetchers'
|
|
1043
|
+
* import { getUsers } from './_utils/fetchers'
|
|
1022
1044
|
*
|
|
1023
1045
|
* const api = new API('https://api.example.com')
|
|
1024
1046
|
* const users = await getUsers({ page: 1 }, api)
|
|
@@ -1027,11 +1049,12 @@ declare namespace index {
|
|
|
1027
1049
|
|
|
1028
1050
|
/**
|
|
1029
1051
|
* Get the global API instance
|
|
1030
|
-
*
|
|
1052
|
+
* Auto-configures from environment variables on first call if not manually configured.
|
|
1053
|
+
* @throws Error if API is not configured and no env variable is set
|
|
1031
1054
|
*/
|
|
1032
1055
|
declare function getAPIInstance(): API;
|
|
1033
1056
|
/**
|
|
1034
|
-
* Check if API is configured
|
|
1057
|
+
* Check if API is configured (or can be auto-configured)
|
|
1035
1058
|
*/
|
|
1036
1059
|
declare function isAPIConfigured(): boolean;
|
|
1037
1060
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { createConsola, consola } from 'consola';
|
|
2
2
|
import pRetry, { AbortError } from 'p-retry';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
import { createExtensionAPI } from '@djangocfg/ext-base/api';
|
|
4
|
+
import { initializeExtensionAPI, createExtensionAPI } from '@djangocfg/ext-base/api';
|
|
5
5
|
import { Wallet, CreditCard, History, RefreshCw, Plus, XCircle, ExternalLink, Search, Filter, Clock, AlertCircle, CheckCircle2, ArrowDownLeft, ArrowUpRight } from 'lucide-react';
|
|
6
|
-
import { Tabs, TabsList, TabsTrigger, TabsContent, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Form, FormField, FormItem, FormLabel, FormControl, Input, FormDescription, FormMessage, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, TokenIcon, DialogFooter, Button, CopyButton, Card, CardHeader, CardTitle, Skeleton, CardContent, Badge,
|
|
6
|
+
import { Tabs, TabsList, TabsTrigger, TabsContent, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Form, FormField, FormItem, FormLabel, FormControl, Input, FormDescription, FormMessage, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, TokenIcon, DialogFooter, Button, CopyButton, Card, CardHeader, CardTitle, Skeleton, CardContent, Badge, Table, TableHeader, TableRow, TableHead, TableBody, TableCell } from '@djangocfg/ui-core';
|
|
7
7
|
import { createContext, useState, useMemo, useEffect, useContext } from 'react';
|
|
8
8
|
import useSWR, { SWRConfig, useSWRConfig } from 'swr';
|
|
9
9
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
10
10
|
import { useForm } from 'react-hook-form';
|
|
11
11
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
12
12
|
import moment from 'moment';
|
|
13
|
+
import { useDRFPagination, StaticPagination } from '@djangocfg/ui-nextjs/components';
|
|
13
14
|
import { createExtensionConfig } from '@djangocfg/ext-base';
|
|
14
15
|
|
|
15
16
|
var __defProp = Object.defineProperty;
|
|
@@ -120,7 +121,7 @@ var models_exports = {};
|
|
|
120
121
|
// src/api/generated/ext_payments/http.ts
|
|
121
122
|
var FetchAdapter = class {
|
|
122
123
|
async request(request) {
|
|
123
|
-
const { method, url, headers, body, params, formData } = request;
|
|
124
|
+
const { method, url, headers, body, params, formData, binaryBody } = request;
|
|
124
125
|
let finalUrl = url;
|
|
125
126
|
if (params) {
|
|
126
127
|
const searchParams = new URLSearchParams();
|
|
@@ -138,6 +139,9 @@ var FetchAdapter = class {
|
|
|
138
139
|
let requestBody;
|
|
139
140
|
if (formData) {
|
|
140
141
|
requestBody = formData;
|
|
142
|
+
} else if (binaryBody) {
|
|
143
|
+
finalHeaders["Content-Type"] = "application/octet-stream";
|
|
144
|
+
requestBody = binaryBody;
|
|
141
145
|
} else if (body) {
|
|
142
146
|
finalHeaders["Content-Type"] = "application/json";
|
|
143
147
|
requestBody = JSON.stringify(body);
|
|
@@ -462,11 +466,13 @@ var APIClient = class {
|
|
|
462
466
|
httpClient;
|
|
463
467
|
logger = null;
|
|
464
468
|
retryConfig = null;
|
|
469
|
+
tokenGetter = null;
|
|
465
470
|
// Sub-clients
|
|
466
471
|
ext_payments_payments;
|
|
467
472
|
constructor(baseUrl, options) {
|
|
468
473
|
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
469
474
|
this.httpClient = options?.httpClient || new FetchAdapter();
|
|
475
|
+
this.tokenGetter = options?.tokenGetter || null;
|
|
470
476
|
if (options?.loggerConfig !== void 0) {
|
|
471
477
|
this.logger = new APILogger(options.loggerConfig);
|
|
472
478
|
}
|
|
@@ -489,6 +495,19 @@ var APIClient = class {
|
|
|
489
495
|
}
|
|
490
496
|
return null;
|
|
491
497
|
}
|
|
498
|
+
/**
|
|
499
|
+
* Get the base URL for building streaming/download URLs.
|
|
500
|
+
*/
|
|
501
|
+
getBaseUrl() {
|
|
502
|
+
return this.baseUrl;
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Get JWT token for URL authentication (used in streaming endpoints).
|
|
506
|
+
* Returns null if no token getter is configured or no token is available.
|
|
507
|
+
*/
|
|
508
|
+
getToken() {
|
|
509
|
+
return this.tokenGetter ? this.tokenGetter() : null;
|
|
510
|
+
}
|
|
492
511
|
/**
|
|
493
512
|
* Make HTTP request with Django CSRF and session handling.
|
|
494
513
|
* Automatically retries on network errors and 5xx server errors.
|
|
@@ -519,7 +538,7 @@ var APIClient = class {
|
|
|
519
538
|
const headers = {
|
|
520
539
|
...options?.headers || {}
|
|
521
540
|
};
|
|
522
|
-
if (!options?.formData && !headers["Content-Type"]) {
|
|
541
|
+
if (!options?.formData && !options?.binaryBody && !headers["Content-Type"]) {
|
|
523
542
|
headers["Content-Type"] = "application/json";
|
|
524
543
|
}
|
|
525
544
|
if (this.logger) {
|
|
@@ -538,7 +557,8 @@ var APIClient = class {
|
|
|
538
557
|
headers,
|
|
539
558
|
params: options?.params,
|
|
540
559
|
body: options?.body,
|
|
541
|
-
formData: options?.formData
|
|
560
|
+
formData: options?.formData,
|
|
561
|
+
binaryBody: options?.binaryBody
|
|
542
562
|
});
|
|
543
563
|
const duration = Date.now() - startTime;
|
|
544
564
|
if (response.status >= 400) {
|
|
@@ -874,7 +894,7 @@ var PaymentDetailSchema = z.object({
|
|
|
874
894
|
status_display: z.string(),
|
|
875
895
|
pay_address: z.string().nullable(),
|
|
876
896
|
qr_code_url: z.string().nullable(),
|
|
877
|
-
payment_url: z.url().nullable(),
|
|
897
|
+
payment_url: z.union([z.url(), z.literal("")]).nullable(),
|
|
878
898
|
transaction_hash: z.string().nullable(),
|
|
879
899
|
explorer_link: z.string().nullable(),
|
|
880
900
|
confirmations_count: z.int(),
|
|
@@ -959,15 +979,28 @@ __export(fetchers_exports, {
|
|
|
959
979
|
|
|
960
980
|
// src/api/generated/ext_payments/api-instance.ts
|
|
961
981
|
var globalAPI = null;
|
|
982
|
+
var autoConfigAttempted = false;
|
|
983
|
+
function tryAutoConfigureFromEnv() {
|
|
984
|
+
if (autoConfigAttempted) return;
|
|
985
|
+
autoConfigAttempted = true;
|
|
986
|
+
if (globalAPI) return;
|
|
987
|
+
if (typeof process === "undefined" || !process.env) return;
|
|
988
|
+
const baseUrl = process.env.NEXT_PUBLIC_API_URL || process.env.VITE_API_URL || process.env.REACT_APP_API_URL || process.env.API_URL;
|
|
989
|
+
if (baseUrl) {
|
|
990
|
+
globalAPI = new API(baseUrl);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
962
993
|
function getAPIInstance() {
|
|
994
|
+
tryAutoConfigureFromEnv();
|
|
963
995
|
if (!globalAPI) {
|
|
964
996
|
throw new Error(
|
|
965
|
-
'API not configured. Call configureAPI() with your base URL before using fetchers or hooks.\n\nExample:\n import { configureAPI } from "./api-instance"\n configureAPI({ baseUrl: "https://api.example.com" })'
|
|
997
|
+
'API not configured. Call configureAPI() with your base URL before using fetchers or hooks.\n\nExample:\n import { configureAPI } from "./api-instance"\n configureAPI({ baseUrl: "https://api.example.com" })\n\nOr set environment variable: NEXT_PUBLIC_API_URL, VITE_API_URL, or REACT_APP_API_URL'
|
|
966
998
|
);
|
|
967
999
|
}
|
|
968
1000
|
return globalAPI;
|
|
969
1001
|
}
|
|
970
1002
|
function isAPIConfigured() {
|
|
1003
|
+
tryAutoConfigureFromEnv();
|
|
971
1004
|
return globalAPI !== null;
|
|
972
1005
|
}
|
|
973
1006
|
function configureAPI(config) {
|
|
@@ -1282,7 +1315,8 @@ var API = class {
|
|
|
1282
1315
|
this._loadTokensFromStorage();
|
|
1283
1316
|
this._client = new APIClient(this.baseUrl, {
|
|
1284
1317
|
retryConfig: this.options?.retryConfig,
|
|
1285
|
-
loggerConfig: this.options?.loggerConfig
|
|
1318
|
+
loggerConfig: this.options?.loggerConfig,
|
|
1319
|
+
tokenGetter: () => this.getToken()
|
|
1286
1320
|
});
|
|
1287
1321
|
this._injectAuthHeader();
|
|
1288
1322
|
this.ext_payments_payments = this._client.ext_payments_payments;
|
|
@@ -1294,7 +1328,8 @@ var API = class {
|
|
|
1294
1328
|
_reinitClients() {
|
|
1295
1329
|
this._client = new APIClient(this.baseUrl, {
|
|
1296
1330
|
retryConfig: this.options?.retryConfig,
|
|
1297
|
-
loggerConfig: this.options?.loggerConfig
|
|
1331
|
+
loggerConfig: this.options?.loggerConfig,
|
|
1332
|
+
tokenGetter: () => this.getToken()
|
|
1298
1333
|
});
|
|
1299
1334
|
this._injectAuthHeader();
|
|
1300
1335
|
this.ext_payments_payments = this._client.ext_payments_payments;
|
|
@@ -1385,6 +1420,7 @@ var API = class {
|
|
|
1385
1420
|
return "./schema.json";
|
|
1386
1421
|
}
|
|
1387
1422
|
};
|
|
1423
|
+
initializeExtensionAPI(configureAPI);
|
|
1388
1424
|
var apiPayments = createExtensionAPI(API);
|
|
1389
1425
|
function usePaymentsBalanceRetrieve(client) {
|
|
1390
1426
|
return useSWR(
|
|
@@ -2552,7 +2588,7 @@ var PaymentsLayout = () => {
|
|
|
2552
2588
|
// package.json
|
|
2553
2589
|
var package_default = {
|
|
2554
2590
|
name: "@djangocfg/ext-payments",
|
|
2555
|
-
version: "1.0.
|
|
2591
|
+
version: "1.0.10",
|
|
2556
2592
|
description: "Payments system extension for DjangoCFG",
|
|
2557
2593
|
keywords: [
|
|
2558
2594
|
"django",
|
|
@@ -2613,6 +2649,7 @@ var package_default = {
|
|
|
2613
2649
|
peerDependencies: {
|
|
2614
2650
|
"@djangocfg/api": "workspace:*",
|
|
2615
2651
|
"@djangocfg/ext-base": "workspace:*",
|
|
2652
|
+
"@djangocfg/ui-core": "workspace:*",
|
|
2616
2653
|
"@djangocfg/ui-nextjs": "workspace:*",
|
|
2617
2654
|
consola: "^3.4.2",
|
|
2618
2655
|
"lucide-react": "^0.545.0",
|
|
@@ -2627,6 +2664,7 @@ var package_default = {
|
|
|
2627
2664
|
"@djangocfg/api": "workspace:*",
|
|
2628
2665
|
"@djangocfg/ext-base": "workspace:*",
|
|
2629
2666
|
"@djangocfg/typescript-config": "workspace:*",
|
|
2667
|
+
"@djangocfg/ui-nextjs": "workspace:*",
|
|
2630
2668
|
"@types/node": "^24.7.2",
|
|
2631
2669
|
"@types/react": "^19.0.0",
|
|
2632
2670
|
consola: "^3.4.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/ext-payments",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.10",
|
|
4
4
|
"description": "Payments system extension for DjangoCFG",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"django",
|
|
@@ -59,9 +59,10 @@
|
|
|
59
59
|
"check": "tsc --noEmit"
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|
|
62
|
-
"@djangocfg/api": "^2.1.
|
|
62
|
+
"@djangocfg/api": "^2.1.107",
|
|
63
63
|
"@djangocfg/ext-base": "^1.0.8",
|
|
64
|
-
"@djangocfg/ui-
|
|
64
|
+
"@djangocfg/ui-core": "^2.1.107",
|
|
65
|
+
"@djangocfg/ui-nextjs": "^2.1.107",
|
|
65
66
|
"consola": "^3.4.2",
|
|
66
67
|
"lucide-react": "^0.545.0",
|
|
67
68
|
"next": "^15.5.7",
|
|
@@ -72,9 +73,10 @@
|
|
|
72
73
|
"moment": "^2.30.1"
|
|
73
74
|
},
|
|
74
75
|
"devDependencies": {
|
|
75
|
-
"@djangocfg/api": "^2.1.
|
|
76
|
+
"@djangocfg/api": "^2.1.107",
|
|
76
77
|
"@djangocfg/ext-base": "^1.0.8",
|
|
77
|
-
"@djangocfg/typescript-config": "^2.1.
|
|
78
|
+
"@djangocfg/typescript-config": "^2.1.107",
|
|
79
|
+
"@djangocfg/ui-nextjs": "^2.1.107",
|
|
78
80
|
"@types/node": "^24.7.2",
|
|
79
81
|
"@types/react": "^19.0.0",
|
|
80
82
|
"consola": "^3.4.2",
|
|
@@ -65,12 +65,5 @@ openapi_client = OpenAPIClientConfig(
|
|
|
65
65
|
)
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
-
**Copy to Next.js** (if `nextjs_admin` configured):
|
|
69
|
-
```python
|
|
70
|
-
nextjs_admin = NextJsAdminConfig(
|
|
71
|
-
project_path="../frontend/apps/...",
|
|
72
|
-
api_output_path="app/_lib/api/generated",
|
|
73
|
-
)
|
|
74
|
-
```
|
|
75
|
-
|
|
76
68
|
@see https://djangocfg.com/docs/features/api-generation
|
|
69
|
+
|
|
@@ -25,7 +25,7 @@ export const PaymentDetailSchema = z.object({
|
|
|
25
25
|
status_display: z.string(),
|
|
26
26
|
pay_address: z.string().nullable(),
|
|
27
27
|
qr_code_url: z.string().nullable(),
|
|
28
|
-
payment_url: z.url().nullable(),
|
|
28
|
+
payment_url: z.union([z.url(), z.literal('')]).nullable(),
|
|
29
29
|
transaction_hash: z.string().nullable(),
|
|
30
30
|
explorer_link: z.string().nullable(),
|
|
31
31
|
confirmations_count: z.int(),
|
|
@@ -1,29 +1,37 @@
|
|
|
1
1
|
// Auto-generated by DjangoCFG - see CLAUDE.md
|
|
2
2
|
/**
|
|
3
|
-
* Global API Instance - Singleton configuration
|
|
3
|
+
* Global API Instance - Singleton configuration with auto-configuration support
|
|
4
4
|
*
|
|
5
|
-
* This module provides a global API instance that
|
|
6
|
-
*
|
|
5
|
+
* This module provides a global API instance that auto-configures from
|
|
6
|
+
* environment variables or can be configured manually.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
8
|
+
* AUTO-CONFIGURATION (recommended):
|
|
9
|
+
* Set one of these environment variables and the API will auto-configure:
|
|
10
|
+
* - NEXT_PUBLIC_API_URL (Next.js)
|
|
11
|
+
* - VITE_API_URL (Vite)
|
|
12
|
+
* - REACT_APP_API_URL (Create React App)
|
|
13
|
+
* - API_URL (generic)
|
|
14
|
+
*
|
|
15
|
+
* Then just use fetchers and hooks directly:
|
|
16
|
+
* ```typescript
|
|
17
|
+
* import { getUsers } from './_utils/fetchers'
|
|
18
|
+
* const users = await getUsers({ page: 1 })
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* MANUAL CONFIGURATION:
|
|
9
22
|
* ```typescript
|
|
10
|
-
* // Configure once (e.g., in your app entry point)
|
|
11
23
|
* import { configureAPI } from './api-instance'
|
|
12
24
|
*
|
|
13
25
|
* configureAPI({
|
|
14
26
|
* baseUrl: 'https://api.example.com',
|
|
15
27
|
* token: 'your-jwt-token'
|
|
16
28
|
* })
|
|
17
|
-
*
|
|
18
|
-
* // Then use fetchers and hooks anywhere without configuration
|
|
19
|
-
* import { getUsers } from './fetchers'
|
|
20
|
-
* const users = await getUsers({ page: 1 })
|
|
21
29
|
* ```
|
|
22
30
|
*
|
|
23
31
|
* For SSR or multiple instances:
|
|
24
32
|
* ```typescript
|
|
25
33
|
* import { API } from './index'
|
|
26
|
-
* import { getUsers } from './fetchers'
|
|
34
|
+
* import { getUsers } from './_utils/fetchers'
|
|
27
35
|
*
|
|
28
36
|
* const api = new API('https://api.example.com')
|
|
29
37
|
* const users = await getUsers({ page: 1 }, api)
|
|
@@ -33,27 +41,67 @@
|
|
|
33
41
|
import { API, type APIOptions } from './index'
|
|
34
42
|
|
|
35
43
|
let globalAPI: API | null = null
|
|
44
|
+
let autoConfigAttempted = false
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Auto-configure from environment variable if available (Next.js pattern)
|
|
48
|
+
* This allows hooks and fetchers to work without explicit configureAPI() call
|
|
49
|
+
*
|
|
50
|
+
* Supported environment variables:
|
|
51
|
+
* - NEXT_PUBLIC_API_URL (Next.js)
|
|
52
|
+
* - VITE_API_URL (Vite)
|
|
53
|
+
* - REACT_APP_API_URL (Create React App)
|
|
54
|
+
* - API_URL (generic)
|
|
55
|
+
*/
|
|
56
|
+
function tryAutoConfigureFromEnv(): void {
|
|
57
|
+
// Only attempt once
|
|
58
|
+
if (autoConfigAttempted) return
|
|
59
|
+
autoConfigAttempted = true
|
|
60
|
+
|
|
61
|
+
// Skip if already configured
|
|
62
|
+
if (globalAPI) return
|
|
63
|
+
|
|
64
|
+
// Skip if process is not available (pure browser without bundler)
|
|
65
|
+
if (typeof process === 'undefined' || !process.env) return
|
|
66
|
+
|
|
67
|
+
// Try different environment variable patterns
|
|
68
|
+
const baseUrl =
|
|
69
|
+
process.env.NEXT_PUBLIC_API_URL ||
|
|
70
|
+
process.env.VITE_API_URL ||
|
|
71
|
+
process.env.REACT_APP_API_URL ||
|
|
72
|
+
process.env.API_URL
|
|
73
|
+
|
|
74
|
+
if (baseUrl) {
|
|
75
|
+
globalAPI = new API(baseUrl)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
36
78
|
|
|
37
79
|
/**
|
|
38
80
|
* Get the global API instance
|
|
39
|
-
*
|
|
81
|
+
* Auto-configures from environment variables on first call if not manually configured.
|
|
82
|
+
* @throws Error if API is not configured and no env variable is set
|
|
40
83
|
*/
|
|
41
84
|
export function getAPIInstance(): API {
|
|
85
|
+
// Try auto-configuration on first access (lazy initialization)
|
|
86
|
+
tryAutoConfigureFromEnv()
|
|
87
|
+
|
|
42
88
|
if (!globalAPI) {
|
|
43
89
|
throw new Error(
|
|
44
90
|
'API not configured. Call configureAPI() with your base URL before using fetchers or hooks.\n\n' +
|
|
45
91
|
'Example:\n' +
|
|
46
92
|
' import { configureAPI } from "./api-instance"\n' +
|
|
47
|
-
' configureAPI({ baseUrl: "https://api.example.com" })'
|
|
93
|
+
' configureAPI({ baseUrl: "https://api.example.com" })\n\n' +
|
|
94
|
+
'Or set environment variable: NEXT_PUBLIC_API_URL, VITE_API_URL, or REACT_APP_API_URL'
|
|
48
95
|
)
|
|
49
96
|
}
|
|
50
97
|
return globalAPI
|
|
51
98
|
}
|
|
52
99
|
|
|
53
100
|
/**
|
|
54
|
-
* Check if API is configured
|
|
101
|
+
* Check if API is configured (or can be auto-configured)
|
|
55
102
|
*/
|
|
56
103
|
export function isAPIConfigured(): boolean {
|
|
104
|
+
tryAutoConfigureFromEnv()
|
|
57
105
|
return globalAPI !== null
|
|
58
106
|
}
|
|
59
107
|
|
|
@@ -25,6 +25,7 @@ export class APIClient {
|
|
|
25
25
|
private httpClient: HttpClientAdapter;
|
|
26
26
|
private logger: APILogger | null = null;
|
|
27
27
|
private retryConfig: RetryConfig | null = null;
|
|
28
|
+
private tokenGetter: (() => string | null) | null = null;
|
|
28
29
|
|
|
29
30
|
// Sub-clients
|
|
30
31
|
public ext_payments_payments: ExtPaymentsPayments;
|
|
@@ -35,10 +36,12 @@ export class APIClient {
|
|
|
35
36
|
httpClient?: HttpClientAdapter;
|
|
36
37
|
loggerConfig?: Partial<LoggerConfig>;
|
|
37
38
|
retryConfig?: RetryConfig;
|
|
39
|
+
tokenGetter?: () => string | null;
|
|
38
40
|
}
|
|
39
41
|
) {
|
|
40
42
|
this.baseUrl = baseUrl.replace(/\/$/, '');
|
|
41
43
|
this.httpClient = options?.httpClient || new FetchAdapter();
|
|
44
|
+
this.tokenGetter = options?.tokenGetter || null;
|
|
42
45
|
|
|
43
46
|
// Initialize logger if config provided
|
|
44
47
|
if (options?.loggerConfig !== undefined) {
|
|
@@ -69,6 +72,21 @@ export class APIClient {
|
|
|
69
72
|
return null;
|
|
70
73
|
}
|
|
71
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Get the base URL for building streaming/download URLs.
|
|
77
|
+
*/
|
|
78
|
+
getBaseUrl(): string {
|
|
79
|
+
return this.baseUrl;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Get JWT token for URL authentication (used in streaming endpoints).
|
|
84
|
+
* Returns null if no token getter is configured or no token is available.
|
|
85
|
+
*/
|
|
86
|
+
getToken(): string | null {
|
|
87
|
+
return this.tokenGetter ? this.tokenGetter() : null;
|
|
88
|
+
}
|
|
89
|
+
|
|
72
90
|
/**
|
|
73
91
|
* Make HTTP request with Django CSRF and session handling.
|
|
74
92
|
* Automatically retries on network errors and 5xx server errors.
|
|
@@ -80,6 +98,7 @@ export class APIClient {
|
|
|
80
98
|
params?: Record<string, any>;
|
|
81
99
|
body?: any;
|
|
82
100
|
formData?: FormData;
|
|
101
|
+
binaryBody?: Blob | ArrayBuffer;
|
|
83
102
|
headers?: Record<string, string>;
|
|
84
103
|
}
|
|
85
104
|
): Promise<T> {
|
|
@@ -116,6 +135,7 @@ export class APIClient {
|
|
|
116
135
|
params?: Record<string, any>;
|
|
117
136
|
body?: any;
|
|
118
137
|
formData?: FormData;
|
|
138
|
+
binaryBody?: Blob | ArrayBuffer;
|
|
119
139
|
headers?: Record<string, string>;
|
|
120
140
|
}
|
|
121
141
|
): Promise<T> {
|
|
@@ -129,8 +149,8 @@ export class APIClient {
|
|
|
129
149
|
...(options?.headers || {})
|
|
130
150
|
};
|
|
131
151
|
|
|
132
|
-
// Don't set Content-Type for FormData (browser will set it with boundary)
|
|
133
|
-
if (!options?.formData && !headers['Content-Type']) {
|
|
152
|
+
// Don't set Content-Type for FormData/binaryBody (browser will set it with boundary)
|
|
153
|
+
if (!options?.formData && !options?.binaryBody && !headers['Content-Type']) {
|
|
134
154
|
headers['Content-Type'] = 'application/json';
|
|
135
155
|
}
|
|
136
156
|
|
|
@@ -157,6 +177,7 @@ export class APIClient {
|
|
|
157
177
|
params: options?.params,
|
|
158
178
|
body: options?.body,
|
|
159
179
|
formData: options?.formData,
|
|
180
|
+
binaryBody: options?.binaryBody,
|
|
160
181
|
});
|
|
161
182
|
|
|
162
183
|
const duration = Date.now() - startTime;
|