Created Payment methods page
This commit is contained in:
parent
0149c0f341
commit
e723dde47f
@ -1,6 +1,6 @@
|
|||||||
// App component styles using BEM methodology
|
// App component styles using BEM methodology
|
||||||
|
|
||||||
.app {
|
.cashier-app {
|
||||||
// Root container
|
// Root container
|
||||||
max-width: 1280px;
|
max-width: 1280px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
@ -8,7 +8,7 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app__logo {
|
.cashier-app__logo {
|
||||||
height: 6em;
|
height: 6em;
|
||||||
padding: 1.5em;
|
padding: 1.5em;
|
||||||
will-change: filter;
|
will-change: filter;
|
||||||
@ -40,11 +40,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.app__card {
|
.cashier-app__card {
|
||||||
padding: 2em;
|
padding: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app__read-the-docs {
|
.cashier-app__read-the-docs {
|
||||||
color: #888;
|
color: #888;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
22
src/App.tsx
22
src/App.tsx
@ -1,25 +1,11 @@
|
|||||||
import PaymentMethodsList from '@/features/payment-methods/PaymentMethodsList/PaymentMethodsList'
|
import Cashier from '@/features/cashier/Cashier'
|
||||||
import EmptyPaymentMethods from '@/features/payment-methods/EmptyPaymentMethods/EmptyPaymentMethods'
|
import '@/App.scss'
|
||||||
import type { IPaymentMethod } from '@/features/payment-methods/types'
|
|
||||||
import { PAYMENT_METHODS } from '@/constants/payment'
|
|
||||||
import './App.scss'
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const handleMethodSelect = (method: IPaymentMethod) => {
|
|
||||||
console.log('Selected payment method:', method)
|
|
||||||
// Handle payment method selection here
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="app">
|
<div className="cashier-app">
|
||||||
{PAYMENT_METHODS.length === 0 ? (
|
<Cashier />
|
||||||
<EmptyPaymentMethods />
|
|
||||||
) : (
|
|
||||||
<PaymentMethodsList
|
|
||||||
methods={PAYMENT_METHODS}
|
|
||||||
onMethodSelect={handleMethodSelect}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
30
src/components/Error/Error.scss
Normal file
30
src/components/Error/Error.scss
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
.error {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3rem;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error__message {
|
||||||
|
color: #d32f2f;
|
||||||
|
font-size: 1rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error__retry {
|
||||||
|
padding: 0.5rem 1.5rem;
|
||||||
|
background-color: #646cff;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1rem;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #535bf2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
22
src/components/Error/Error.tsx
Normal file
22
src/components/Error/Error.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import './Error.scss'
|
||||||
|
|
||||||
|
interface IErrorProps {
|
||||||
|
message: string;
|
||||||
|
onRetry?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Error({ message, onRetry }: IErrorProps) {
|
||||||
|
return (
|
||||||
|
<div className="error">
|
||||||
|
<p className="error__message">{message}</p>
|
||||||
|
{onRetry && (
|
||||||
|
<button className="error__retry" onClick={onRetry}>
|
||||||
|
Retry
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Error;
|
||||||
|
|
||||||
28
src/components/Loading/Loading.scss
Normal file
28
src/components/Loading/Loading.scss
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
.loading {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3rem;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading__spinner {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border: 4px solid #f3f3f3;
|
||||||
|
border-top: 4px solid #646cff;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
0% { transform: rotate(0deg); }
|
||||||
|
100% { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading__text {
|
||||||
|
color: #666;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
13
src/components/Loading/Loading.tsx
Normal file
13
src/components/Loading/Loading.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import './Loading.scss'
|
||||||
|
|
||||||
|
function Loading() {
|
||||||
|
return (
|
||||||
|
<div className="loading">
|
||||||
|
<div className="loading__spinner"></div>
|
||||||
|
<p className="loading__text">Loading payment methods...</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Loading;
|
||||||
|
|
||||||
31
src/config/api.ts
Normal file
31
src/config/api.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Use proxy in development, direct URL in production
|
||||||
|
export const API_BASE_URL = import.meta.env.DEV
|
||||||
|
? '/api/v1'
|
||||||
|
: 'https://cashier-backend.brgoperations.com/api/v1';
|
||||||
|
|
||||||
|
export const TMerchant = {
|
||||||
|
DATA_SPIN: 'DATA_SPIN',
|
||||||
|
WIN_BOT: 'WIN_BOT',
|
||||||
|
VARK_SERVICES: 'VARK_SERVICES',
|
||||||
|
BETRISE: 'BETRISE',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type TMerchant = typeof TMerchant[keyof typeof TMerchant];
|
||||||
|
|
||||||
|
export const MERCHANT_API_KEYS: Record<TMerchant, string> = {
|
||||||
|
[TMerchant.DATA_SPIN]: '10000000-0000-0000-0000-000000000001',
|
||||||
|
[TMerchant.WIN_BOT]: '20000000-0000-0000-0000-000000000002',
|
||||||
|
[TMerchant.VARK_SERVICES]: '30000000-0000-0000-0000-000000000003',
|
||||||
|
[TMerchant.BETRISE]: '40000000-0000-0000-0000-000000000004',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MERCHANT_IDS: Record<TMerchant, string> = {
|
||||||
|
[TMerchant.DATA_SPIN]: '10000000-0000-0000-0000-000000000001',
|
||||||
|
[TMerchant.WIN_BOT]: '20000000-0000-0000-0000-000000000002',
|
||||||
|
[TMerchant.VARK_SERVICES]: '30000000-0000-0000-0000-000000000003',
|
||||||
|
[TMerchant.BETRISE]: '40000000-0000-0000-0000-000000000004',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Default merchant (can be changed based on requirements)
|
||||||
|
export const DEFAULT_MERCHANT = TMerchant.WIN_BOT;
|
||||||
|
|
||||||
0
src/features/cashier/Cashier.scss
Normal file
0
src/features/cashier/Cashier.scss
Normal file
38
src/features/cashier/Cashier.tsx
Normal file
38
src/features/cashier/Cashier.tsx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import PaymentMethodsList from '@/features/payment-methods/PaymentMethodsList/PaymentMethodsList'
|
||||||
|
import EmptyPaymentMethods from '@/features/payment-methods/EmptyPaymentMethods/EmptyPaymentMethods'
|
||||||
|
import Loading from '@/components/Loading/Loading'
|
||||||
|
import Error from '@/components/Error/Error'
|
||||||
|
import type { IPaymentMethod } from '@/features/payment-methods/types'
|
||||||
|
import { usePaymentMethods } from './hooks/usePaymentMethods'
|
||||||
|
|
||||||
|
function Cashier() {
|
||||||
|
const { methods: apiMethods, loading, error } = usePaymentMethods();
|
||||||
|
|
||||||
|
const handleMethodSelect = (method: IPaymentMethod) => {
|
||||||
|
console.log('Selected payment method:', method)
|
||||||
|
// Handle payment method selection here
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return <Loading />
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return <Error message={`Error loading payment methods: ${error.message}`} />
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{apiMethods.length === 0 ? (
|
||||||
|
<EmptyPaymentMethods />
|
||||||
|
) : (
|
||||||
|
<PaymentMethodsList
|
||||||
|
methods={apiMethods}
|
||||||
|
onMethodSelect={handleMethodSelect}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Cashier
|
||||||
30
src/features/cashier/hooks/useCurrencies.ts
Normal file
30
src/features/cashier/hooks/useCurrencies.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import { apiService } from '@/services/api';
|
||||||
|
import type { IApiCurrency } from '../types';
|
||||||
|
import type { TMerchant } from '@/config/api';
|
||||||
|
|
||||||
|
export function useCurrencies(merchant?: TMerchant) {
|
||||||
|
const [currencies, setCurrencies] = useState<IApiCurrency[]>([]);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [error, setError] = useState<Error | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchCurrencies = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
setError(null);
|
||||||
|
const data = await apiService.getCurrencies(merchant);
|
||||||
|
setCurrencies(data);
|
||||||
|
} catch (err) {
|
||||||
|
setError(err instanceof Error ? err : new Error('Failed to fetch currencies'));
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchCurrencies();
|
||||||
|
}, [merchant]);
|
||||||
|
|
||||||
|
return { currencies, loading, error };
|
||||||
|
}
|
||||||
|
|
||||||
44
src/features/cashier/hooks/usePayment.ts
Normal file
44
src/features/cashier/hooks/usePayment.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import { apiService } from '@/services/api';
|
||||||
|
import { MERCHANT_IDS, DEFAULT_MERCHANT, type TMerchant } from '@/config/api';
|
||||||
|
import type { IPaymentRequest, IPaymentResponse } from '../types';
|
||||||
|
|
||||||
|
export function usePayment() {
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [error, setError] = useState<Error | null>(null);
|
||||||
|
const [paymentUrl, setPaymentUrl] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const initiatePayment = async (
|
||||||
|
paymentRequest: Omit<IPaymentRequest, 'merchant_id'>,
|
||||||
|
merchant: TMerchant = DEFAULT_MERCHANT
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
setError(null);
|
||||||
|
setPaymentUrl(null);
|
||||||
|
|
||||||
|
const fullRequest: IPaymentRequest = {
|
||||||
|
...paymentRequest,
|
||||||
|
merchant_id: MERCHANT_IDS[merchant],
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await apiService.initiatePayment(fullRequest, merchant);
|
||||||
|
setPaymentUrl(response.payment_url);
|
||||||
|
return response;
|
||||||
|
} catch (err) {
|
||||||
|
const error = err instanceof Error ? err : new Error('Failed to initiate payment');
|
||||||
|
setError(error);
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
initiatePayment,
|
||||||
|
loading,
|
||||||
|
error,
|
||||||
|
paymentUrl,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
30
src/features/cashier/hooks/usePaymentMethods.ts
Normal file
30
src/features/cashier/hooks/usePaymentMethods.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import { apiService } from '@/services/api';
|
||||||
|
import type { TMerchant } from '@/config/api';
|
||||||
|
import type { IPaymentMethod } from '@/features/payment-methods/types';
|
||||||
|
|
||||||
|
export function usePaymentMethods(merchant?: TMerchant) {
|
||||||
|
const [methods, setMethods] = useState<IPaymentMethod[]>([]);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [error, setError] = useState<Error | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchMethods = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
setError(null);
|
||||||
|
const data = await apiService.getMethods(merchant);
|
||||||
|
setMethods(data);
|
||||||
|
} catch (err) {
|
||||||
|
setError(err instanceof Error ? err : new Error('Failed to fetch payment methods'));
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchMethods();
|
||||||
|
}, [merchant]);
|
||||||
|
|
||||||
|
return { methods, loading, error };
|
||||||
|
}
|
||||||
|
|
||||||
50
src/features/cashier/types.ts
Normal file
50
src/features/cashier/types.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
export interface IApiMethod {
|
||||||
|
code: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IApiMethodsResponse {
|
||||||
|
data: IApiMethod[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IApiCurrency {
|
||||||
|
currency: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IApiCurrenciesResponse {
|
||||||
|
data: IApiCurrency[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ICustomer {
|
||||||
|
id?: string;
|
||||||
|
name?: string;
|
||||||
|
email?: string;
|
||||||
|
phone?: string;
|
||||||
|
account?: string; // IBAN or similar - required on withdrawal
|
||||||
|
ip?: string;
|
||||||
|
country?: string;
|
||||||
|
city?: string;
|
||||||
|
zip?: string;
|
||||||
|
address?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IRedirect {
|
||||||
|
success: string; // URL where to redirect after payment success
|
||||||
|
cancel: string; // URL where to redirect after payment cancel
|
||||||
|
error: string; // URL where to redirect if error occur
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IPaymentRequest {
|
||||||
|
merchant_id: string; // same id as in header
|
||||||
|
payment_type: 'deposit' | 'withdrawal';
|
||||||
|
method: string;
|
||||||
|
customer: ICustomer;
|
||||||
|
currency: string;
|
||||||
|
amount: number; // float64
|
||||||
|
redirect: IRedirect;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IPaymentResponse {
|
||||||
|
payment_url: string; // URL where customer needs to continue with payment flow
|
||||||
|
}
|
||||||
|
|
||||||
26
src/features/cashier/utils/methodMapper.ts
Normal file
26
src/features/cashier/utils/methodMapper.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import type { IApiMethod } from '../types';
|
||||||
|
import type { IPaymentMethod } from '@/features/payment-methods/types';
|
||||||
|
|
||||||
|
const METHOD_TYPES: Record<string, string> = {
|
||||||
|
bankin: 'Banking',
|
||||||
|
bankpay: 'Banking',
|
||||||
|
papel: 'Digital Wallet',
|
||||||
|
mefete: 'Digital Wallet',
|
||||||
|
pep: 'Digital Wallet',
|
||||||
|
};
|
||||||
|
|
||||||
|
export function mapApiMethodToPaymentMethod(apiMethod: IApiMethod, index: number): IPaymentMethod {
|
||||||
|
const methodCode = apiMethod.code.toLowerCase();
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: `${methodCode}-${index}`,
|
||||||
|
name: apiMethod.name, // Use the name from API
|
||||||
|
type: METHOD_TYPES[methodCode] || 'Payment',
|
||||||
|
isActive: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mapApiMethodsToPaymentMethods(apiMethods: IApiMethod[]): IPaymentMethod[] {
|
||||||
|
return apiMethods.map((apiMethod, index) => mapApiMethodToPaymentMethod(apiMethod, index));
|
||||||
|
}
|
||||||
|
|
||||||
@ -1,10 +1,7 @@
|
|||||||
import { StrictMode } from 'react'
|
|
||||||
import { createRoot } from 'react-dom/client'
|
import { createRoot } from 'react-dom/client'
|
||||||
import App from '@/App.tsx'
|
import App from '@/App.tsx'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
||||||
createRoot(document.getElementById('root')!).render(
|
createRoot(document.getElementById('root')!).render(
|
||||||
<StrictMode>
|
|
||||||
<App />
|
<App />
|
||||||
</StrictMode>,
|
|
||||||
)
|
)
|
||||||
|
|||||||
75
src/services/api.ts
Normal file
75
src/services/api.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import { API_BASE_URL, MERCHANT_API_KEYS, TMerchant, DEFAULT_MERCHANT } from '@/config/api';
|
||||||
|
import type { IApiMethodsResponse, IApiCurrency, IApiCurrenciesResponse, IPaymentRequest, IPaymentResponse } from '@/features/cashier/types';
|
||||||
|
import { mapApiMethodsToPaymentMethods } from '@/features/cashier/utils/methodMapper';
|
||||||
|
import type { IPaymentMethod } from '@/features/payment-methods/types';
|
||||||
|
|
||||||
|
class ApiService {
|
||||||
|
|
||||||
|
private methods: IPaymentMethod[] = []
|
||||||
|
private currencies: IApiCurrency[] = []
|
||||||
|
|
||||||
|
private getAuthHeader(merchant: TMerchant = DEFAULT_MERCHANT): HeadersInit {
|
||||||
|
const apiKey = MERCHANT_API_KEYS[merchant];
|
||||||
|
return {
|
||||||
|
'Authorization': `Bearer ${apiKey}`,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async getMethods(merchant: TMerchant = DEFAULT_MERCHANT): Promise<IPaymentMethod[]> {
|
||||||
|
if (this.methods.length > 0) {
|
||||||
|
return this.methods
|
||||||
|
}
|
||||||
|
const response = await fetch(`${API_BASE_URL}/methods`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: this.getAuthHeader(merchant),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Failed to fetch methods: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data: IApiMethodsResponse = await response.json();
|
||||||
|
|
||||||
|
this.methods = mapApiMethodsToPaymentMethods(data.data);
|
||||||
|
|
||||||
|
return this.methods;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCurrencies(merchant: TMerchant = DEFAULT_MERCHANT): Promise<IApiCurrency[]> {
|
||||||
|
if (this.currencies.length > 0) {
|
||||||
|
return this.currencies
|
||||||
|
}
|
||||||
|
const response = await fetch(`${API_BASE_URL}/currencies`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: this.getAuthHeader(merchant),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Failed to fetch currencies: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data: IApiCurrenciesResponse = await response.json();
|
||||||
|
return data.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async initiatePayment(
|
||||||
|
paymentRequest: IPaymentRequest,
|
||||||
|
merchant: TMerchant = DEFAULT_MERCHANT
|
||||||
|
): Promise<IPaymentResponse> {
|
||||||
|
const response = await fetch(`${API_BASE_URL}/payment`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: this.getAuthHeader(merchant),
|
||||||
|
body: JSON.stringify(paymentRequest),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Failed to initiate payment: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const apiService = new ApiService();
|
||||||
|
|
||||||
@ -13,4 +13,13 @@ export default defineConfig({
|
|||||||
'@': path.resolve(__dirname, './src'),
|
'@': path.resolve(__dirname, './src'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
server: {
|
||||||
|
proxy: {
|
||||||
|
'/api': {
|
||||||
|
target: 'https://cashier-backend.brgoperations.com',
|
||||||
|
changeOrigin: true,
|
||||||
|
secure: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user