Backend services: - targo-hub: extract deepGetValue to helpers.js, DRY disconnect reasons lookup map, compact CAPABILITIES, consolidate vision.js prompts/schemas, extract dispatch scoring weights, trim section dividers across 9 files - modem-bridge: extract getSession() helper (6 occurrences), resetIdleTimer(), consolidate DM query factory, fix duplicate username fill bug, trim headers (server.js -36%, tplink-session.js -47%, docker-compose.yml -57%) Frontend: - useWifiDiagnostic: extract THRESHOLDS const, split processDiagnostic into 6 focused helpers (processOnlineStatus, processWanIPs, processRadios, processMeshNodes, processClients, checkRadioIssues) - EquipmentDetail: merge duplicate ROLE_LABELS, remove verbose comments Documentation (17 → 13 files, -1,400 lines): - New consolidated README.md (architecture, services, dependencies, auth) - Merge ECOSYSTEM-OVERVIEW into ARCHITECTURE.md - Merge MIGRATION-PLAN + ARCHITECTURE-COMPARE + FIELD-GAP + CHANGELOG → MIGRATION.md - Merge COMPETITIVE-ANALYSIS into PLATFORM-STRATEGY.md - Update ROADMAP.md with current phase status - Delete CONTEXT.md (absorbed into README) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
177 lines
5.4 KiB
JavaScript
177 lines
5.4 KiB
JavaScript
/**
|
|
* Payment actions composable — agent-facing payment operations via targo-hub
|
|
*/
|
|
import { ref } from 'vue'
|
|
import { Notify } from 'quasar'
|
|
import { HUB_SSE_URL } from 'src/config/dispatch'
|
|
|
|
const HUB = HUB_SSE_URL
|
|
|
|
async function hubFetch (path, opts = {}) {
|
|
const res = await fetch(HUB + path, {
|
|
method: opts.method || 'GET',
|
|
headers: { 'Content-Type': 'application/json', ...opts.headers },
|
|
...(opts.body ? { body: JSON.stringify(opts.body) } : {}),
|
|
})
|
|
if (!res.ok) {
|
|
const err = await res.json().catch(() => ({ error: 'Request failed' }))
|
|
throw new Error(err.error || `HTTP ${res.status}`)
|
|
}
|
|
return res.json()
|
|
}
|
|
|
|
export function usePaymentActions (customer) {
|
|
const loading = ref(false)
|
|
const sendingLink = ref(false)
|
|
const chargingCard = ref(false)
|
|
const togglingPpa = ref(false)
|
|
|
|
async function fetchBalance () {
|
|
if (!customer.value?.name) return null
|
|
try {
|
|
return await hubFetch(`/payments/balance/${encodeURIComponent(customer.value.name)}`)
|
|
} catch (e) {
|
|
console.error('Balance fetch error:', e)
|
|
return null
|
|
}
|
|
}
|
|
|
|
async function fetchMethods () {
|
|
if (!customer.value?.name) return []
|
|
try {
|
|
const res = await hubFetch(`/payments/methods/${encodeURIComponent(customer.value.name)}`)
|
|
return res.methods || []
|
|
} catch (e) {
|
|
console.error('Methods fetch error:', e)
|
|
return []
|
|
}
|
|
}
|
|
|
|
async function sendPaymentLink (channel = 'both') {
|
|
sendingLink.value = true
|
|
try {
|
|
const res = await hubFetch('/payments/send-link', {
|
|
method: 'POST',
|
|
body: { customer: customer.value.name, channel },
|
|
})
|
|
const sentVia = res.sent?.join(' + ') || 'aucun'
|
|
Notify.create({ type: 'positive', message: `Lien de paiement envoyé (${sentVia}) — ${res.amount}$`, position: 'top' })
|
|
return res
|
|
} catch (e) {
|
|
Notify.create({ type: 'negative', message: `Erreur: ${e.message}`, position: 'top' })
|
|
} finally {
|
|
sendingLink.value = false
|
|
}
|
|
}
|
|
|
|
async function chargeCard (amount) {
|
|
chargingCard.value = true
|
|
try {
|
|
const res = await hubFetch('/payments/charge', {
|
|
method: 'POST',
|
|
body: { customer: customer.value.name, ...(amount ? { amount } : {}) },
|
|
})
|
|
if (res.ok) {
|
|
Notify.create({ type: 'positive', message: `Paiement de ${res.amount}$ prélevé avec succès`, position: 'top' })
|
|
} else {
|
|
Notify.create({ type: 'warning', message: `Statut: ${res.status} — vérifier Stripe`, position: 'top' })
|
|
}
|
|
return res
|
|
} catch (e) {
|
|
Notify.create({ type: 'negative', message: `Erreur prélèvement: ${e.message}`, position: 'top' })
|
|
} finally {
|
|
chargingCard.value = false
|
|
}
|
|
}
|
|
|
|
async function togglePpa (enabled) {
|
|
togglingPpa.value = true
|
|
try {
|
|
await hubFetch('/payments/toggle-ppa', {
|
|
method: 'POST',
|
|
body: { customer: customer.value.name, enabled },
|
|
})
|
|
Notify.create({
|
|
type: 'positive',
|
|
message: enabled ? 'PPA activé' : 'PPA désactivé',
|
|
position: 'top',
|
|
})
|
|
return true
|
|
} catch (e) {
|
|
Notify.create({ type: 'negative', message: `Erreur PPA: ${e.message}`, position: 'top' })
|
|
return false
|
|
} finally {
|
|
togglingPpa.value = false
|
|
}
|
|
}
|
|
|
|
async function openPortal () {
|
|
loading.value = true
|
|
try {
|
|
const res = await hubFetch('/payments/portal', {
|
|
method: 'POST',
|
|
body: { customer: customer.value.name },
|
|
})
|
|
if (res.url) window.open(res.url, '_blank')
|
|
} catch (e) {
|
|
Notify.create({ type: 'negative', message: `Erreur: ${e.message}`, position: 'top' })
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
async function createCheckout () {
|
|
loading.value = true
|
|
try {
|
|
const res = await hubFetch('/payments/checkout', {
|
|
method: 'POST',
|
|
body: { customer: customer.value.name },
|
|
})
|
|
// Copy URL to clipboard for the agent to share
|
|
if (res.url) {
|
|
await navigator.clipboard?.writeText(res.url)
|
|
Notify.create({ type: 'info', message: `Lien copié — ${res.amount}$. Ouvre dans un nouvel onglet.`, position: 'top', timeout: 4000 })
|
|
window.open(res.url, '_blank')
|
|
}
|
|
return res
|
|
} catch (e) {
|
|
Notify.create({ type: 'negative', message: `Erreur: ${e.message}`, position: 'top' })
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
const refunding = ref(false)
|
|
|
|
async function refundPayment (paymentEntry, amount, reason) {
|
|
refunding.value = true
|
|
try {
|
|
const res = await hubFetch('/payments/refund', {
|
|
method: 'POST',
|
|
body: { payment_entry: paymentEntry, amount: amount || undefined, reason },
|
|
})
|
|
if (res.ok) {
|
|
const msg = res.stripe_refund
|
|
? `Remboursement Stripe ${res.amount}$ (${res.stripe_refund.id})`
|
|
: `Remboursement ${res.amount}$ enregistré`
|
|
Notify.create({ type: 'positive', message: msg, position: 'top' })
|
|
if (res.warning) {
|
|
Notify.create({ type: 'warning', message: res.warning, position: 'top', timeout: 8000 })
|
|
}
|
|
}
|
|
return res
|
|
} catch (e) {
|
|
Notify.create({ type: 'negative', message: `Erreur remboursement: ${e.message}`, position: 'top' })
|
|
} finally {
|
|
refunding.value = false
|
|
}
|
|
}
|
|
|
|
return {
|
|
loading, sendingLink, chargingCard, togglingPpa, refunding,
|
|
fetchBalance, fetchMethods,
|
|
sendPaymentLink, chargeCard, togglePpa, openPortal, createCheckout,
|
|
refundPayment,
|
|
}
|
|
}
|