Initialer Laravel Commit für BetiX
This commit is contained in:
180
resources/js/composables/useVault.ts
Normal file
180
resources/js/composables/useVault.ts
Normal file
@@ -0,0 +1,180 @@
|
||||
import { ref } from 'vue';
|
||||
import { csrfFetch } from '@/utils/csrfFetch';
|
||||
|
||||
export interface VaultBalances {
|
||||
balance: string;
|
||||
vault_balance: string;
|
||||
vault_balances: Record<string, string>;
|
||||
currency: string | null;
|
||||
}
|
||||
|
||||
export function useVault() {
|
||||
const loading = ref(false);
|
||||
const error = ref<string | null>(null);
|
||||
const balances = ref<VaultBalances | null>(null);
|
||||
const transfers = ref<any>(null);
|
||||
const pinRequired = ref(false);
|
||||
const lockedUntil = ref<string | null>(null);
|
||||
|
||||
const sessionPin = ref<string | null>(null);
|
||||
|
||||
async function load(perPage = 10) {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
try {
|
||||
const resp = await fetch(`/api/wallet/vault?per_page=${perPage}`, {
|
||||
headers: { 'Accept': 'application/json' }
|
||||
});
|
||||
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
|
||||
const json = await resp.json();
|
||||
balances.value = {
|
||||
balance: json.balance,
|
||||
vault_balance: json.vault_balance,
|
||||
vault_balances: json.vault_balances ?? { BTX: json.vault_balance },
|
||||
currency: json.currency,
|
||||
};
|
||||
transfers.value = json.transfers;
|
||||
} catch (e: any) {
|
||||
error.value = e?.message || 'Failed to load vault';
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function uuidv4(): string {
|
||||
if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) {
|
||||
return (crypto as any).randomUUID();
|
||||
}
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
||||
const r = (Math.random() * 16) | 0;
|
||||
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
||||
return v.toString(16);
|
||||
});
|
||||
}
|
||||
|
||||
async function deposit(amount: string, currency = 'BTX', pin?: string) {
|
||||
return doAction('/api/wallet/vault/deposit', amount, currency, pin);
|
||||
}
|
||||
|
||||
async function withdraw(amount: string, currency = 'BTX', pin?: string) {
|
||||
return doAction('/api/wallet/vault/withdraw', amount, currency, pin);
|
||||
}
|
||||
|
||||
async function doAction(url: string, amount: string, currency: string, pin?: string) {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
pinRequired.value = false;
|
||||
lockedUntil.value = null;
|
||||
|
||||
try {
|
||||
const usePin = pin ?? sessionPin.value;
|
||||
if (!usePin) {
|
||||
pinRequired.value = true;
|
||||
throw new Error('PIN required');
|
||||
}
|
||||
|
||||
const body = { amount, currency, pin: usePin, idempotency_key: uuidv4() };
|
||||
const resp = await csrfFetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
||||
if (resp.status === 423) {
|
||||
const j = await resp.json().catch(() => ({}));
|
||||
pinRequired.value = true;
|
||||
lockedUntil.value = j?.locked_until || null;
|
||||
throw new Error(j?.message || 'PIN required');
|
||||
}
|
||||
|
||||
if (!resp.ok) {
|
||||
const j = await resp.json().catch(() => ({}));
|
||||
throw new Error(j?.message || `HTTP ${resp.status}`);
|
||||
}
|
||||
|
||||
const json = await resp.json();
|
||||
|
||||
if (json?.balances) {
|
||||
balances.value = {
|
||||
balance: json.balances.balance,
|
||||
vault_balance: json.balances.vault_balance,
|
||||
vault_balances: json.balances.vault_balances ?? { BTX: json.balances.vault_balance },
|
||||
currency: balances.value?.currency || 'BTX',
|
||||
};
|
||||
} else {
|
||||
await load();
|
||||
}
|
||||
return json;
|
||||
} catch (e: any) {
|
||||
error.value = e?.message || 'Vault action failed';
|
||||
throw e;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function verifyPin(pin: string) {
|
||||
error.value = null;
|
||||
try {
|
||||
const resp = await csrfFetch('/api/wallet/vault/pin/verify', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
|
||||
body: JSON.stringify({ pin }),
|
||||
});
|
||||
|
||||
if (!resp.ok) {
|
||||
const j = await resp.json().catch(() => ({}));
|
||||
if (resp.status === 423) lockedUntil.value = j?.locked_until || null;
|
||||
throw new Error(j?.message || `HTTP ${resp.status}`);
|
||||
}
|
||||
|
||||
sessionPin.value = pin;
|
||||
pinRequired.value = false;
|
||||
lockedUntil.value = null;
|
||||
|
||||
return await resp.json();
|
||||
} catch (e: any) {
|
||||
error.value = e?.message || 'PIN verification failed';
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
function clearSessionPin() {
|
||||
sessionPin.value = null;
|
||||
}
|
||||
|
||||
async function setPin(pin: string, current_pin?: string) {
|
||||
error.value = null;
|
||||
const payload: any = { pin };
|
||||
if (current_pin) payload.current_pin = current_pin;
|
||||
|
||||
const resp = await csrfFetch('/api/wallet/vault/pin/set', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
|
||||
if (!resp.ok) {
|
||||
const j = await resp.json().catch(() => ({}));
|
||||
throw new Error(j?.message || `HTTP ${resp.status}`);
|
||||
}
|
||||
|
||||
sessionPin.value = pin;
|
||||
return await resp.json();
|
||||
}
|
||||
|
||||
return {
|
||||
loading,
|
||||
error,
|
||||
balances,
|
||||
transfers,
|
||||
pinRequired,
|
||||
lockedUntil,
|
||||
load,
|
||||
deposit,
|
||||
withdraw,
|
||||
verifyPin,
|
||||
setPin,
|
||||
clearSessionPin,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user